React
의 코드를 리뷰한 내용은 깃허브에 적어두었다.
https://github.com/yonghyeun/React-Deepdive/tree/main/docs/3.%20ReactElement%20deppdive
JSX
를 이용해 함수들을 반환 할 때 반환되는 반환 값우리는 이전 docs
에서 React
는 여러 컴포넌트들을
선언형 방식을 이용해 Virtual DOM
을 생성하여 웹에서 렌더링 되는 Actual DOM
을 조작한다는 사실을 알았다.
...
const root = ReactDOM.createRoot(
document.getElementById('root') as HTMLElement,
);
root.render(
<React.StrictMode>
<App />
</React.StrictMode>,
);
root.render
는 ReactDom.render()
에 해당하는데 이는 Virtual DOM
에 해당하는 ReactDom
을 생성한다.
root
이하에 생성되는 App
컴포넌트를 살펴보면 다음과 같이 생겼다.
const Container: React.FC<{ children: ReactNode }> = ({ children }) => {
return <div>{children}</div>;
};
const Title: React.FC<{ text: string }> = ({ text }) => {
return <h1>{text}</h1>;
};
function App() {
return (
<Container>
<Title text='안녕하세요~!' />
</Container>
);
}
여러 함수형 컴포넌트들의 조합으로 이뤄진 App
컴포넌트가 호출되었을 때
어떤 일이 벌어지는지 살펴보자
console.log(App());
App
함수가 호출될 때의 반환 값을 살펴보자
App
함수가 반환하는 객체 형태를 갖는 모습을 볼 수 있다.
우리는 다음처럼 함수들을 JSX
문법을 통해 반환하도록 했는데 말이다.
function App() {
return (
<Container>
<Title text='안녕하세요~!' />
</Container>
);
}
이는 JSX
를 이용해 HTML
과 유사한 문법으로 갖춘 코드들을
Babel
컴파일러가 리액트가 사용하는 자바스크립트 객체를 만드는 코드로 컴파일 해주기 때문이다.
React.createElement
바벨 컴파일러가 JSX
파일을 컴파일 하여 리액트에서 사용하는 자바스크립트 객체인
엘리먼트를 생성하는 메소드로 컴파일 해준다.
<Container>
<Title text='Hello~!' />
</Container>
다음과 같이 생긴 JSX
파일을 Babel
이 컴파일 할 때는
React.createElement(
Container,
null,
React.createElement(Title, { text: 'Hello~!' }),
);
다음과 같이 생성한다.
그렇다면 React.createElement
메소드가 의미하는 것은 무엇일까 ?
공식문서에 정의된 React.createElement
의 설명은 다음과 같다.
function createElement<P extends {}>(
type: FunctionComponent<P> | ComponentClass<P> | string,
props?: (Attributes & P) | null,
...children: ReactNode[]
): ReactElement<P>;
React.createElement
는React.Element
를 생성한다.
type
: 유효한React
컴포넌트를 받는다. 예를 들어HTMLElement
태그 이름 문자열 또는React
컴포넌트가 될 수 있다.props
:props
는 객체 또는null
이여야 한다.children
:children
은 선택 사항이며 0개 이상의 자식 노드 , 엘리먼트 , 문자열 , 숫자 ... 등이 들어 올 수 있으며React
노드 배열을 포함한 모든React
노드가 될 수 있다.
아 ! React.createElement
는 React.Element
를 생성하고
type
에는 컴포넌트들 , props
엔 해당 컴포넌트에서 사용할 Attributes
객체가 들어오고
children
엔 리액트 노드들이 담긴 배열을 받는구나
React.Element
그렇다면 React.Element
는 무엇일까 ?
interface ReactElement<
P = any,
T extends string | JSXElementConstructor<any> =
| string
| JSXElementConstructor<any>,
> {
type: T;
props: P;
key: string | null;
}
타입스크립트로 정의된 React.Element
는 다음과 같이 생겼다.
React.createElement
로 생성되는 React.Element
는 type , children , props
를 프로퍼티로 갖는 자바스크립트 객체이다.
React.Element
는 children
이란 프로퍼티를 통해 트리구조의 모습을 갖는다.
<Container>
<Title text='Hello~!' />
</Container>
해당 JSX
가 컴파일 되어 React.createElement
로 생성된 React.Element
객체는 다음과 같은 모습을 갖는다.
React.createElement(
Container,
null,
React.createElement(Title, { text: 'Hello~!' }),
);
React.createElement(Title, { text: 'Hello~!' });
내부에 존재하는 React.createElement (Title , ..)
는
다음과 같은 자바스크립트 객체를 생성한다.
{
"type": Title,
"props": {
"text": "Hello~!",
"children": null
},
"key": null,
"ref": null
}
자바스크립트 객체로 변환 이후
외부에 존재하는 React.createElement(Container , ..)
는 다음과 같이 변경된다.
React.createElement(Container, null, {
type: Title,
props: {
text: 'Hello~!',
children: null,
},
key: null,
ref: null,
});
최종적으로 생성되는 React.Element
는 다음과 같이 생겼다.
{
"type": Container,
"props": {
"children": {
"type": Title,
"props": {
"text": "Hello~!",
"children": null
},
"key": null,
"ref": null
}
},
"key": null,
"ref": null
}
Actual DOM
을 생성하는 동작에 비해
재귀적으로 React.createElement
를 호출하여 React.Element
를 생성하는 과정은
Actual DOM
과 유사한 구조의 트리 자료구조 객체를 생성하지만
paint , layout
을 거치지 않고 메모리 상의 객체를 생성하는 것이기 때문에
비용이 매우 저렴하다는 점을 기억하자
React.Element
가 그러면 Virtual DOM
의 노드가 되는걸까 ?React.Element
는 단순히 UI
에 렌더링 하기 위한 컴포넌트의
모습을 선언해둔 것이다.
React
는 React.Element
로 생성된 자바스크립트 객체를 활용하여
Virtual DOM
을 구성하는 것은 맞지만
Virtual DOM
을 구성 할 때 각 컴포넌트들의 state , lifeCycle
등을 고려하여
노드를 생성한다.
JSX
문법을 통해 정의한 컴포넌트들은 호출 될 때
React.createElement(..)
로 변환되고
재귀적인 React.createElement
호출을 통해 최종적인 자바스크립트 객체인
React.Element
를 반환한다.
생성된 React.Element
들은 React.DOM
에서 생성할 Virtual DOM
이 렌더링 할 모습을
정의해둔 자료구조이며
React
는 이렇게 생성된 React.Element
에 state , lifeCycle
등을 고려하여
Virtual DOM
의 노드를 생성한다.