티스토리 뷰

앞서, 리액트 다시 생각해보기 글을 시작으로 리액트의 특성을 이해하고 사용하기만 했지, 정확히 어떤 형식과 매커니즘으로 동작하는지에 대해서 구체적으로 알아보는 시간이 없었던 것 같았습니다.
Rodrigo Pombo의 Build your own React 블로그 글을 참고해서 리액트의 기본 개념, 기능들을 직접 따라 만들어 보면서, 신경쓰지 못한 부분을 알고 가고자 합니다.
알아가는 과정 속에서 원글을 응용하여 직접 돌려보며 확인해볼 수 있는 실습환경을 구성 및 번역 내용이 들어간 프로젝트를 만들었습니다. learn-react-by-building-ko

Rodrigo Pombo의 'Build your own React'를 바탕으로 리액트의 핵심 메커니즘을 직접 구현해보며 깊이 있게 이해하는 과정을 공유합니다.
구조
해당 원글을 번역하여 Step마다 폴더로 README.md를 만들어 두었으며, 직접 실습을 만들어 제공하고 있습니다.
실습 환경
실습 환경을 위해서 Node.js와 pnpm이 필요합니다. Node.js 16.13 이상에는 corepack이 함께 제공되므로 별도의 pnpm 전역 설치 없이 사용할 수 있습니다.
1. Node.js 설치
nodejs.org에서 LTS 버전을 받거나, 버전 매니저 사용을 권장합니다.
# macOS / Linux — nvm 사용 시
nvm install --lts
nvm use --lts
# 설치 확인
node -v
2. pnpm 활성화 (corepack)
Node.js에 내장된 corepack으로 pnpm을 활성화합니다.
corepack enable pnpm
# 설치 확인
pnpm -v
3. 프로젝트 의존성 설치
레포지토리 루트에서 다음 명령으로 babel, live-server 등 실습에 필요한 패키지를 설치합니다.
pnpm install
실습 방법
pnpm step{n}을 진행하면서 결과를 확인해 볼 수 있습니다.
Step Zero: 복습
먼저 몇 가지 기본 개념을 복습해봅시다. React, JSX, DOM 요소가 어떻게 동작하는지 이미 잘 알고 있다면 이 단계는 건너뛰어도 됩니다.
이제 단 세 줄짜리 React 앱을 살펴보겠습니다. 첫 번째 줄은 React 엘리먼트를 정의합니다. 두 번째 줄은 DOM에서 노드를 가져옵니다. 마지막 줄은 React 엘리먼트를 컨테이너 안에 렌더링합니다.
const element = <h1 title="foo">Hello</h1>;
const container = document.getElementById('root');
ReactDOM.render(element, container);
이제 React 관련 코드를 모두 제거하고 바닐라 JavaScript로 대체해봅시다.
첫 번째 줄에는 JSX로 정의된 엘리먼트가 있습니다. 이건 유효한 JavaScript조차 아니기 때문에, 바닐라 JS로 대체하려면 먼저 유효한 JS로 바꿔야 합니다.
JSX는 Babel 같은 빌드 도구에 의해 JS로 변환됩니다.
const element = React.createElement(
"h1",
{ title: "foo" },
"Hello"
)
변환 과정은 보통 단순합니다: 태그 안의 코드를 createElement 호출로 바꾸고, 태그 이름과 props, children을 인자로 전달하는 방식입니다.
React.createElement는 인자들로부터 객체를 만들어냅니다. 몇 가지 검증을 제외하면 그게 전부입니다. 따라서 이 함수 호출을 그 결과값으로 안전하게 대체할 수 있습니다.
그리고 이게 바로 엘리먼트의 정체입니다 — type과 props라는 두 개의 속성을 가진 객체입니다 (사실 더 많은 속성이 있긴 하지만, 우리가 신경 쓸 건 이 두 개뿐입니다).
const element = {
type: 'h1',
props: {
title: 'foo',
children: 'Hello',
},
};
type은 우리가 만들려는 DOM 노드의 타입을 지정하는 문자열입니다. HTML 엘리먼트를 만들 때 document.createElement에 넘기는 tagName과 같습니다. 함수가 될 수도 있지만, 그건 Step VII에서 다루겠습니다.
props는 또 다른 객체로, JSX 속성(attribute)의 모든 키와 값을 담고 있습니다. 또한 특별한 속성 하나를 가지고 있는데, 바로 children입니다. 이 경우 children은 문자열이지만, 보통은 더 많은 엘리먼트가 담긴 배열입니다. 엘리먼트가 트리 구조이기도 한 이유가 바로 이것입니다.
우리가 대체해야 할 또 다른 React 코드는 ReactDOM.render 호출입니다.
ReactDOM.render(element, container);
render는 React가 DOM을 변경하는 지점이니, 그 업데이트를 우리가 직접 해봅시다. 먼저 엘리먼트의 type을 사용해 노드*를 만듭니다. 이 경우엔 h1입니다. 그다음 엘리먼트의 모든 props를 그 노드에 할당합니다. 여기서는 title뿐입니다.
- 혼동을 피하기 위해, "엘리먼트(element)"는 React 엘리먼트를 가리키고 "노드(node)"는 DOM 엘리먼트를 가리키는 데 쓰겠습니다.
const element = {
type: 'h1',
props: {
title: 'foo',
children: 'Hello',
},
};
const node = document.createElement(element.type);
node['title'] = element.props.title;
그다음 children을 위한 노드들을 만듭니다. children이 문자열 하나뿐이니 텍스트 노드를 만들면 됩니다. innerText를 설정하는 대신 textNode를 사용하면, 나중에 모든 엘리먼트를 같은 방식으로 다룰 수 있게 됩니다. 또한 h1의 title을 설정할 때와 마찬가지로 nodeValue를 설정하는 방식에 주목해보세요. 마치 그 문자열이 props: {nodeValue: "hello"}를 가진 것처럼 다루는 것입니다.
const element = {
type: "h1",
props: {
title: "foo",
children: "Hello",
},
}
const text = document.createTextNode("")
text["nodeValue"] = element.props.children
마지막으로 textNode를 h1에 붙이고, h1을 container에 붙입니다. 이제 React를 사용하지 않고도 이전과 똑같은 앱을 만들었습니다.
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)
현재 프로젝트에서는 pnpm step0 를 진행해보세요.

'Dev > React' 카테고리의 다른 글
- Total
- Today
- Yesterday
- project
- Firebase
- nodejs
- RUBY
- html
- redux-toolkit
- instagram CSS
- TypeScirpt
- Django
- 드림코딩
- JavaScript
- Class
- 프론트엔드
- object
- css
- hooks
- 트위터 클론
- 오버라이딩
- github
- 그림판 만들기
- React
- frontend
- 바닐라js
- Python
- nrc
- NomadCoder
- 생활코딩
- Build your own React
- 기능추가
| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | |||
| 5 | 6 | 7 | 8 | 9 | 10 | 11 |
| 12 | 13 | 14 | 15 | 16 | 17 | 18 |
| 19 | 20 | 21 | 22 | 23 | 24 | 25 |
| 26 | 27 | 28 | 29 | 30 | 31 |