토이 프로젝트를 하기 전 따로 공부하기 위해 작성한 글입니다.
Elements란?
요소 성분이란 의미
리액트 앱을 구성하는 가장 작은 블록들을 의미.
웹사이트에 대한 모든 정보를 담고 있는 객체인 DOM에서 사용하는 용어.
const element = <h1>Hello, world</h1>;
대입 연산자 왼쪽 부분에 나오는 변수의 이름이 엘리먼트이다.
이 코드가 실행될 때 대입 연산자의 오른쪽 부분은 React의 createElement 함수를 사용하여 엘리먼트를 생성하게 된다.
결국 이렇게 생성된 것이 리액트 엘리먼트가 되는 것이다.
그리고 리액트는 이 엘리먼트를 이용해서 실제 우리가 화면에서 보게 될 DOM 엘리먼트를 생성한다.
그렇다면 React 엘리트는 실제로 어떻게 생겼을까?
React 엘리먼트는 자바스크립트 객체 형태로 존재한다.
엘리먼트는 컴포넌트 유형과 속성 및 내부의 모든 자식에 대한 정보를 포함하고 있는 일반적인 자바스크립트 객체이다.
이 객체는 마음대로 변경할 수 없는 불변성을 갖고 있다. 한 번 생성되면 바꿀 수 없다.
{
type: 'button',
props: {
className: 'bg-green',
children: {
type: 'b',
props: {
children: 'Hello, element!'
}
}
}
}
타입에 html 태그 이름이 문자열로 들어가는 경우 엘리먼트는 해당 태그 이름을 가진 DOM노드를 나타내고
props는 속성에 해당한다.
(props는 뒤에서 더 자세히 배울 예정)
위의 엘리먼트가 실제로 렌더링이 된다면 그림과 같은 도움 엘리먼트가 될 것이다.
<button class='bg-green'>
<b>
Hello, element!
</b>
</button>
그렇다면 엘리먼트의 타입의 HTML 태그 이름이 문자열로 들어가는 것이 아닌 경우에는 어떻게 될까?
{
type: Button,
props: {
color: 'green',
children: 'Hello, element!'
}
}
이 JavaScript 코드는 React의 컴포넌트 엘리먼트를 나타낸 것이다.
이 역시도 일반적인 JavaScript 객체이다.
다만 위에 나왔던 엘리먼트와 한가지 다른 점은 타입의 문자열로 된 HTML 태그가 아닌 React 컴포넌트의 이름이 들어갔다는 점이다. 이처럼 react-element는 JavaScript 객체 형태로 존재한다.
그리고 이 객체를 만드는 역할을 하는 것이 바로 앞에서 나왔던 createElement 함수이다. 앞에서 createElement함수를 호출할 때 3가지의 파라미터를 넣었는데 그 부분을 다시 보도록 하자.
React.createElement(
type,
[props],
[...children]
)
첫 번째 파라미터
- 타입이 들어간다.
- html 태그 이름이 문자열로 들어가거나 또 다른 react 컴포넌트가 들어가게 된다.
- 모든 React 컴포넌트는 최종적으로 HTML 태그를 사용하게 되어있다.
- 하나의 컴포넌트는 여러 개의 자식 컴포넌트를 포함할 수 있고 자식 컴포넌트를 모두 쭉 분해해보면 결국 HTML태그가 나오는 것이다.
두 번째 파라미터
- props라는 것이 들어간다.
- 간단하게 엘리먼트의 속성이라는 의미.
- html태그 안에 클래스나 스타일 같은 것들. 이런 속성을 attribute라고 한다.
- props는 attributes보다는 좀 더 상위에 있는 복잡한 개념이지만 일단 엘리먼트의 속성이라고만 이해하고 넘어가자.
세번째 파라미터
- children이 들어가게 된다.
- 해당 엘리먼트의 자식 엘리먼트들이 이 부분에 들어가게 된다.
- 실제 개발자 도구의 그림에서는 하나의 html 태그 하위에 다시 여러 개의 html 태그가 나오는 것을 볼 수 있었다.
이러한 html 태그들이 자식 엘리먼트들이 되는 것이다.
실제로 create element 함수가 동작하는 과정을 코드와 함께 살펴보도록 하자
function Button(props) {
return (
<button className={`bg-${props.color}`}>
<b>
{props.children}
</b>
</button>
)
}
function ConfirmDialog(props) {
return (
<div>
<p>내용을 확인하셨으며 확인 버튼을 눌러주세요.</p>
<Button color='green'>확인</Button>
</div>
)
}
이 코드에는 Button 컴포넌트와 confirmDialog 컴포넌트가 있으며 confirmDialog 컴포넌트가 Button 컴포넌트를 포함하고 있다. 여기에서 confirmDialog 컴포넌트의 엘리먼트는 어떤 모습이 될까?
{
type: 'div',
props: {
children: [
{
type: 'p',
props: {
children: '내용을 확인하셨으면 확인 버튼을 눌러주세요.'
}
},
{
type: Button,
props: {
color: 'green',
children: '확인'
}
}
]
}
}
여기에서 첫 번째 칠드런은 type이 html 태그 중 하나인 p 태그이기 때문에 곧바로 렌더링이 될 수 있는 상태이다.
하지만 두번째 칠드런의 타입은 HTML 태그가 아니라 React 컴포넌트의 이름인 버튼이다. 이 경우에 React는 버튼 컴포넌트의 엘리먼트를 생성해서 합치게 된다.
그래서 최종적으로는 엘리먼트는 다음과 같은 모습이 될 것이다.
{
type: 'div',
props: {
children: [
{
type: 'p',
props: {
children: '내용을 확인하셨으면 확인 버튼을 눌러주세요.'
}
},
{
type: 'button',
props: {
className: 'bg-green',
children: {
type: 'b',
props: {
children: '확인'
}
}
}
}
]
}
}
이처럼 컴포넌트 렌더링을 위해서 모든 컴포넌트가 CreateElement 함수를 통해 Element로 변환된다는 것을 기억하자.
엘리먼트의 불변성
엘리먼트는 불변성의 특징을 가진다.
엘리먼트 생성 후에는 Children이나 Attributes를 바꿀 수없다는 의미
리액트의 엘리먼트라는 것은 우리 눈에 보이는 것을 기술한다고 했는데 엘리먼트가 변할 수 없다면 화면 갱신이 안되는 것 아닌가? 라는 의문이 들 수 있다.
엘리먼트 생성 후에는 Children이나 Attirutes를 바꿀 수 없다
여기에 우리가 놓친 부분이 있다.
엘리먼트 생성 후에는 Children이나 Attirutes를 바꿀 수 없다
즉 엘리먼트는 다양한 모습으로 존재할 수 있지만 한번 생성된 다음에는 변경이 불가능하다는 뜻이다.
한가지 예시로 붕어빵을 들 수 있는데 구워져 나온 붕어빵의 속 내용은 바꿀 수 없는 것과 같은 이치라고 생각하면 된다.
이 그림에는 React의 컴포넌트와 엘리먼트의 관계가 나타나 있다.
컴포넌트는 일종의 붕어빵 틀이라고 보면 된다.
붕어빵이 구워져서 밖으로 나오는 과정은 엘리먼트를 생성하는 것이고 완성된 붕어빵은 엘리먼트가 되는 것이다.
완성이 되면 변경할 수 없다.
리액트 엘리먼트의 불변성이라는 특징을 잘 기억해둬야한다.
실제로 리액트를 사용해서 개발하다 보면 상태 관리와 더불어 화면이 얼마나 자주 갱신되는지가 성능에 큰 영향을 미치게 된다.
이 과정에서 엘리먼트가 새롭게 생성된다는 것을 이해하고 있으면 좀 더 원리를 잘 이해하고 효율적으로 개발할 수 있다.
Elements 렌더링
엘리먼트를 생성한 이후에 실제로 화면에 보여주기 위한 과정
<div id="root"></div>
이 HTML 코드는 root라는 아이디를 가진 div 태그이다.
보기에 단순하지만 이 코드는 모든 리액트 앱에 필수적으로 들어가는 아주 중요한 코드이다.
실제로 이 div 태그 안에 리액트 엘리먼트들이 렌더링 되며 이것을 루트 돔노드라고 부른다.
이 div 태그 안에 있는 모든 것이 리액트 돔에 의해서 관리되기 때문이다.
오직 리액트만으로 만들어진 모든 웹사이트들은 단 하나의 루트 돔노드를 가지게 된다.
반면에 기존에 있던 웹사이트에 추가적으로 리액트를 연동하게 되면 여러 개의 분리된 수많은 루트 돔노드를 가질 수 있다.
렌더링된 Elements를 업데이트
엘리먼트는 한번 생성되면 바꿀 수 없기 때문에 엘리먼트를 업데이트하기 위해서는 다시 생성해야 한다.
function tick() {
const element = (
<div>
<h1>안녕, 리액트!</h1>
<h2>현재 시간 : {new Date().toLocaleTimeString()}</h2>
</div>
);
ReactDOM.render(element, document.getElementById('root'));
}
setInterval(tick, 1000);
이 코드에서 tick 함수는 현재 시간을 포함하고 있는 엘리먼트를 생성하여 root.div에 렌더링하는 역할을 한다.
내부적으로는 tick 함수가 호출될 때마다 기존 엘리먼트를 변경하는 것이 아니라 새로운 엘리먼트를 생성해서 바꿔치기 하는 것이다.
매초 새로운 엘리먼트가 생성되어 기존 엘리먼트와 교체되면서 내용이 변경되고 변경된 부분에 반짝이는 효과가 나타나는 것이다.
리액트 엘리먼트의 불변성 때문에 엘리먼트를 업데이트하기 위해서는 새로 만들어야 한다는 중요한 사실을 꼭 기억하자!
실습
my-app>src>chapter_04 폴더 생성 후 clock.jsx 파일 생성
import React from "react";
function Clock(props) {
return (
<div>
<h1>안녕, 리액트!</h1>
<h2>현재 시간: {new Date().toLocaleTimeString()}</h2>
</div>
);
}
export default Clock;
clock.jsx에 해당 코드 입력.
import React from 'react';
import ReactDOM from 'react-dom/client'; // React 18 업데이트 이후 '/client' 경로 추가
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import Library from './chapter_03/Library';
import Clock from './chapter_04/clock';
// React 18에서 새로운 root API 사용
// const root = ReactDOM.createRoot(document.getElementById('root'));
// root.render(
// <React.StrictMode>
// <Library />
// </React.StrictMode>
// );
setInterval(() => {
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<Clock />
</React.StrictMode>
);
}, 1000);
// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();
index.js에 새로운 컴포넌트 등록.
새로운 터미널을 열어 로컬 환경에서 리액트 실행.
(해당 명령어 : npm start)
1초마다 페이지가 갱신되면 완료!
참고 강의 : 처음 만난 리액트(React)
'React' 카테고리의 다른 글
[리액트] Hooks의 정의와 사용법 (0) | 2024.05.10 |
---|---|
[리액트] Props 정의 실습 (0) | 2024.05.08 |
[리액트] JSX란? (정의, 역할, 장점 및 사용법) (4) | 2024.05.07 |
[리액트] npx create-react-app 가 실행되지 않을 때 (0) | 2024.05.06 |
[리액트] HTML란 무엇인가 (0) | 2024.05.06 |