시리즈에 대한 설명과 관련 글은 이 글에서 모아보실 수 있습니다.
Step 0: Review
이 글에서는 React.js(이하 React)의 내부 동작을 살펴보기 앞서 기본 개념에 대해 다룹니다. React, JSX, DOM 요소가 기본적으로 어떻게 상호작용하며 동작하는지 알고 있다면 건너뛰어도 좋습니다.
const element = <h1 title="foo">Hello</h1>;
const container = document.getElementById("root");
ReactDOM.render(element, container);
위 세 줄의 코드로 작성된 React 앱으로 React, JSX, DOM에 대해 가볍게 살펴봅시다.
첫 번째 줄은 React 요소(element)를 정의하고 두 번째 줄은 DOM으로부터 컨테이너로 사용할 노드(node)를 가져옵니다. 마지막 줄은 React 요소를 컨테이너 노드에 그립니다(render). 이 간단한 React 앱에서 React에서 제공하는 기능을 vanilla JS로 대체해 해당 기능을 조금 더 자세히 이해해 봅시다.
JSX and React element
React 요소를 정의하는 첫 번째 줄은 javascript 문법이 아닙니다. 이는 JSX 문법이며 JS 런타임에서 실행 가능하도록 Babel과 같은 빌드툴에 의해 JS로 변환됩니다. React를 Vanilla JS로 치환하기 전에 JSX를 JS로 변환해 봅시다. JSX를 JS로 변환하는 과정은 단순합니다. 코드를 파싱하여 태그 이름(`h1`), 속성(`title="foo"`), 자식(`Hello`)을 추출하고 추출한 값을 인자로 `createElement` 함수를 호출하면 됩니다. 이름, 속성, 값 등에 대해 validation과 같은 추가적인 과정이 있지만 글의 맥락과 맞지 않아 생략하겠습니다. JSX를 걷어낸 코드는 아래와 같습니다.
const element = React.createElement(
"h1",
{ title: "foo" },
"Hello"
);
const container = document.getElementById("root");
ReactDOM.render(element, container);
`React.createElement`는 React 요소를 반환합니다. 이렇게 생성된 React 요소는 아래와 같이 전달된 인자를 속성으로 갖는 객체입니다. 실제로는 더 많은 속성을 갖지만 이 역시 글의 주제에서 벗어나 생략했습니다.
const element = {
type: "h1",
props: {
title: "foo",
children: "Hello",
},
};
const container = document.getElementById("root");
ReactDOM.render(element, container);
위 React 요소 객체에서 `type` 속성은 `document.createElement`에 전달되는 `tagName`으로 사용됩니다. `document.createElement`를 래핑하는 `React.createElement`함수는 Step VII에서 다룹니다.
`props`는 JSX 태그의 attributes에서 추출된 키-값 쌍을 가지며 `children`이라는 특별한 속성도 갖고 있습니다. 위 예시에서 `children`은 문자열 타입이지만 다른 React 요소로 이루어진 배열도 가능합니다. 따라서 `children`속성에 의해 React 요소들은 단방향 계층 구조로 연결되고 트리 구조를 갖습니다.
Render
마지막 줄 또한 React에서 제공하는 함수입니다. `render`는 React가 DOM을 조작하는 부분입니다. 이 부분을 vanilla JS로 변경해 보겠습니다.
가장 먼저 DOM에 삽입할 DOM 노드를 생성해야 합니다. 그 뒤, 요소의 `props`를 생성된 노드에 추가하면 됩니다.
(node와 element가 많이 혼용되어 사용되곤 합니다. 오해를 줄이기 위해 React 요소는 element, DOM 요소는 node를 사용하겠습니다.)
const element = {
type: "h1",
props: {
title: "foo",
children: "Hello",
},
};
const container = document.getElementById("root");
const node = document.createElement(element.type);
node["title"] = element.props.title;
// ... 생략 ...
이제 생성된 노드에 자식 노드들을 생성해야 합니다. 위 예시에서는 자식으로 문자열을 가지므로 text node를 생성합니다.
const element = {
type: "h1",
props: {
title: "foo",
children: "Hello",
},
};
const container = document.getElementById("root");
const node = document.createElement(element.type);
node["title"] = element.props.title;
const text = document.createTextNode("");
text["nodeValue"] = element.props.children;
// ... 생략 ...
`innerText`대신 `textNode`를 사용한 이유는 나중에 모든 요소들에 대해 같은 방법으로 처리하기 위함입니다. 비슷한 이유로 `h1` 노드에서 `title`속성을 추가한 것과 같은 방식으로 `text` 노드에 `nodeValue`를 설정합니다.
마지막으로 `textNode`를 `node`에, `node`를 `container`에 추가(append)합니다.
const element = {
type: "h1",
props: {
title: "foo",
children: "Hello",
},
};
const container = document.getElementById("root");
const node = document.createElement(element.type);
node["title"] = element.props.title;
const text = document.createTextNode("");
text["nodeValue"] = element.props.children;
node.appendChild(text);
container.appendChild(node);
이렇게 처음 세 줄의 React 앱을 React를 사용하지 않은 vanilla JS로 변환하며 JSX와 React 그리고 DOM이 어떻게 상호작용 하는지 간단하게 알아봤습니다.
이제 React에서 제공하는 함수들을 직접 제작하며 React를 이해해 봅시다.
'🎨 Frontend' 카테고리의 다른 글
[Build your own React] README (0) | 2024.10.20 |
---|