React 톺아보기 - 01. Preview

모든 설명은 v16.12.0 버전 함수형 컴포넌트와 브라우저 환경을 기준으로 합니다. 버전에 따라 코드는 변경될 수 있으며 클래스 컴포넌트는 설명에서 제외됨을 알려 드립니다.

1. 들어가기에 앞서

먼저 이 시리즈는 리액트 사용법에 대한 글이 아닙니다.
리액트를 통해 프로젝트를 진행한 경험이 있고 동작 방식을 어렴풋이 이해하기는 하지만 여전히 리액트가 마법처럼 느껴지고 응용, 에러 디버깅 등에 어려움을 겪는 사람들을 위해 작성하였습니다.

시리즈의 내용은 사용자의 상호작용으로부터 훅을 통해 컴포넌트가 업데이트되고 DOM에 마운트되기 까지의 일련의 과정을 실제 코드를 통해 분석해 나가게 될 것입니다. 친절히 요약해서 전달해 드리지 않습니다. 코드에 변형을 가하지 않고 코드 그대로 변수 하나하나 무엇을 위해 선언되었는지 A - Z까지 알아볼 것입니다.

시리즈를 끝내고 나면 Virtual DOM이 무엇인지, 컴포넌트 상태가 바뀌었을 때 리액트는 어떻게 해당 컴포넌트를 리-렌더링시키는지, 어떻게 변경된 부분만 DOM에 마운트 되는지, hook은 컴포넌트와 어떤 방식으로 매핑되어 사용되는지, 이벤트 구현은 어떻게 되어 있는지 등 리액트의 전반적인 큰 줄기의 흐름을 알게 될 것입니다.

들어가기에 앞서 당부 드리고 싶은 말씀은 코드를 분석할 때 필히 분석 대상이 어떤 패키지 밑에 있는 것이고 모듈과 함수 이름은 무엇인지 먼저 보고 머리에 담아둔 상태로 코드를 보시기 바랍니다. 리액트는 역할별로 많은 모듈과 동작에 맞는 함수들을 만들어 놓았습니다. 코드만 보고 넘어가게 된다면 리액트의 구조로 코드를 바라보기가 어려우며 이는 추후에 지금 내가 어디를 보고 있으며 어떠한 흐름으로 여기까지 왔는지 이해하기가 힘듭니다. 이해도 제대로 못 한 상태로 시간은 시간대로 날리고 가장 큰 걱정은 혼자 분석하고 싶을 때 많은 어려움을 겪게 될 것입니다.

여기서 언급되는 내용을 몰라도 리액트 프로젝트를 진행하는데 아무런 영향은 없지만, 이 시리즈를 통해 리액트에 대한 막연한 인식의 허들을 낮추고 궁금증을 가지고 있지만 않고 스스로 분석해보려는 의지를 심어 드리기 위하여 이 글을 시작합니다.

리액트 공식 홈페이지의 Docs을 정독하지 않으신 분이 계신다면 먼저 읽고 오시길 바랍니다. 리액트 뿐만 아니라 모든 것은 공식 홈페이지를 보고 이해하고 난 후에 시작하는 게 순서입니다.

2. 패키지 구조

리액트는 react 코어, 플랫폼과 관련된 renderer, VDOM과 관련된 reconciler, 비동기 실행기인 scheduler 그리고 event로 나눌 수 있습니다.

react

컴포넌트 정의와 관련된 패키지입니다.
대표적으로 React element를 만드는 createElement()와 개발자에게 다른 패키지의 모듈을 제공하는 중간 다리 역할을 하고 있습니다. 해당 패키지는 다른 패키지에 의존도을 가지고 있지 않기 때문에 여러 플랫폼(브라우저, 모바일)에 올려서 사용할 수 있습니다.

renderer

react-dom, react-native-renderer 등 호스트 렌더링 환경에 의존적인 패키지입니다. 호스트와 react를 연결하는 역할을 합니다. reconciler와 legacy-event 패키지에 의존도를 가지고 있습니다.

event(legacy-events)

SyntheticEvent라는 명칭으로 내부적으로 개발된 이벤트 시스템입니다. 개발자가 event를 사용하기 전 리액트에서 추가적인 제어를 하기 위해 호스트 event를 wrapping하며 이벤트 풀링, 이벤트 위임 등을 사용하여 구현되어 있습니다.

scheduler

리액트는 여러 가지 이유로 작업을 비동기로 실행시켜야 합니다. 이 작업은 Task란 이름으로 우선순위에 따라 스케줄링 됩니다. Task를 실행하기에 가장 적기인 때를 알고 있는 것이 scheduler이며 리액트는 비동기 실행의 책임을 전문가인 scheduler에게 위임합니다. 이 패키지는 호스트 환경에 의존적입니다.

reconciler

리액트의 핵심 패키지입니다.
그만큼 여러 고민과 노력들이 묻어 있으며 이 시리즈에서 가장 많은 시간을 보내게 될 것입니다.
v15 이전에는 스택 기반 구현이었다면 v16부터는 오랜 기간 연구 끝에 fiber architecture를 도입했습니다.

package
리액트 패키지

Hook, Scheduler, Reconciler 는 서로 연관된 부분이 많습니다. 그래서 각 챕터별로 진행하는데 많은 어려움을 겪게 되실 겁니다. 추천해 드리는 방법은 처음부터 욕심부려서 모든 걸 이해하려 하지 마시고 시리즈 전체를 빠르게 한번 훑어 전체적인 그림을 익히고 난 후에 시작하신다면 처음에는 보이지 않던 부분들이 눈에 들어오면서 무리 없이 진행하실 거라 생각됩니다.

3. 혼동되는 용어 정리

컴포넌트 구분

리액트에서 자체 제공하는 컴포넌트, 사용자 정의 컴포넌트, 플랫폼 컴포넌트로 구분할 수 있으며 아래와 같이 명칭하도록 하겠습니다.

  • 자체 제공하는 컴포넌트를 스태틱 컴포넌트
  • 플랫폼 컴포넌트를 호스트 컴포넌트
  • 사용자 정의 컴포넌트를 커스텀 컴포넌트

렌더링

일반적으로 컴포넌트 렌더링이라 하면 컴포넌트 호출을 떠올립니다. 하지만 세부 구현 사항으로 내려가면 여러 단계가 더 존재하기 때문에 용어 혼동을 방지하기 위해 좀 더 세분화하도록 하겠습니다.

컴포넌트 호출은 reconciler에서 합니다. 그 후 VDOM 재조정 작업이 들어가고 renderer를 이용하여 DOM에 마운트합니다.
컴포넌트 호출과 DOM 삽입은 별개입니다. 그리고 리액트에서는 DOM 삽입과 화면에 그려지는 것 또한 별개로 다루고 있습니다.

앞으로 렌더링은 컴포넌트가 호출되어 자식을 반환하고 VDOM에 적용하는 일련의 과정을 일컫는다고 하겠습니다. DOM 작업은 제외 입니다. 컴포넌트 호출은 그저 함수 ‘호출’로만 생각하세요. ‘React element를 반환한다.’ 그 이상 그 이하도 아닙니다.
또한 컴포넌트를 DOM에 삽입하는 것을 마운트, 브라우저가 화면에 그리는 걸 페인트라 정의하겠습니다.

Virtual DOM

이하 VDOM으로 명칭

  1. reactElement , fiber

    • reactElement는 컴포넌트의 정보를 담은 모델 객체입니다. 컴포넌트가 반환하는 것은 JSX가 아닌 이 React element입니다.
    • fiber는 VDOM의 노드 객체입니다. 이 객체는 React element를 VDOM에 올리기 위해 확장한 객체입니다. fiber를 통해 컴포넌트의 상태, 훅, 라이프 사이클 등 대부분이 관리됩니다.
  2. current, workInProgress
    리액트는 더블 버퍼링 형태로 VDOM을 설계했습니다.
    current는 마운트가 끝난 트리이며 workInProgress는 업데이트가 적용 중인 트리입니다.

더 많은 용어들이 있지만 크게 혼동되는 부분만 짚어 보았습니다.


오픈소스를 톺아보며 매직 코드라 생각했던 부분들의 동작 원리와 의미, 의도를 파악해보고 서로의 생각을 나누기 위한 블로그