노트

react와 렌더러의 분리

React Core and Renderers

프론트엔드#react · 연결된 개념 5개

쉽게 말하면

react와 react-dom은 악보와 연주자 관계예요. react 패키지는 컴포넌트라는 악보를 쓰는 규칙만 주고, 실제 소리는 브라우저용 react-dom이나 모바일용 react-native 같은 연주자가 각자 악기에 맞게 내요.

비유의 한계 연주자는 악보를 읽기만 하지 않아요. 무엇이 바뀌었는지 비교하는 재조정자도 react 패키지가 아니라 각 렌더러 안에 들어 있어요. 렌더러마다 플랫폼 조작 방법만 host config로 갈아 끼워요.

React는 컴포넌트를 정의하는 API(Application Programming Interface)를 담은 react 패키지와, 그 컴포넌트 트리를 특정 플랫폼의 화면으로 바꾸는 렌더러(renderer) 패키지로 나뉜다. 브라우저 DOM(Document Object Model)용이 react-dom, iOS·Android 네이티브 뷰용이 react-native다.

import { useState } from 'react'               // 컴포넌트를 정의하는 API
import { createRoot } from 'react-dom/client'  // 그 트리를 브라우저 DOM에 그리는 렌더러
 
createRoot(document.getElementById('root')!).render(<App />)

누가 무엇을 하나

  • react(코어): createElement·JSX가 만드는 엘리먼트, 훅, Context처럼 컴포넌트를 정의하는 데 필요한 API만 있다. 예전 React 코드베이스 문서는 코어에 재조정 알고리즘과 플랫폼별 코드가 없다고 설명한다
  • 재조정자(reconciler): setState 같은 업데이트가 예약되면 컴포넌트를 다시 렌더하고, 이전 트리와 비교해 바꿀 점을 찾는다(재조정(Reconciliation)). React 16부터 이 일은 Fiber 재조정자가 맡는다. DOM과 네이티브처럼 전혀 다른 렌더러도 이 로직은 같이 쓴다
  • 렌더러: 재조정자가 찾은 변경을 플랫폼 호출로 옮긴다. DOM 노드를 만들고, 속성을 바꾸고, 이벤트를 연결한다. react-dom은 서버 렌더링(react-dom/server)과 하이드레이션도 맡는다

흔한 오해는 "react가 비교를 계산하고 react-dom은 결과를 칠하기만 한다"는 그림이다. 실제로는 재조정자가 각 렌더러 번들 안에 들어간다. react 패키지는 어느 렌더러에서든 같은 코드로 컴포넌트를 쓰게 해 주는 얇은 층이다.

렌더러를 갈아 끼우는 지점

재조정자는 플랫폼을 직접 알지 못한다. 대신 렌더러가 host config라는 객체로 "이 환경에서 노드를 어떻게 만들고 붙이고 고치는지"를 알려 준다.

const HostConfig = {
  supportsMutation: true,               // DOM처럼 노드를 직접 고치는 방식
  createInstance(type, props) { /* 플랫폼 노드를 만들어 반환 */ },
  appendChild(parentInstance, child) { /* 자식 붙이기 */ },
  commitUpdate(instance, /* ... */ nextProps) { /* nextProps에 맞게 고치기 */ },
  // ...
}
const MyRenderer = Reconciler(HostConfig)

react-reconciler 패키지로 이런 커스텀 렌더러를 만들 수 있다. 다만 README가 실험적 패키지이고 API가 React 본체만큼 안정적이지 않으며 일반 버전 규칙도 따르지 않는다고 밝힌다(2026 기준). 예전 React 코드베이스 문서는 재조정자를 따로 배포하지 않는다고 적었는데, 지금은 이 실험적 패키지로 공개돼 있다.

그래서 생기는 일

  • react-dom의 API(createRoot, createPortal, flushSync 등)는 브라우저 환경 전용이라 React Native에서는 쓸 수 없다(React Portal)
  • React Native 컴포넌트는 div가 아니라 View·Text 같은 기본 요소를 쓴다. 이 요소는 실행 중에 Android·iOS의 네이티브 뷰로 만들어진다
  • 하이브리드 앱의 WebView 안 화면은 브라우저 페이지라 DOM이 있다. 그 화면을 React로 만들면 react-dom을 쓰고, 네이티브 렌더러와는 다른 길이다

출처: React(구 문서) — Codebase Overview: React Core · React(구 문서) — Codebase Overview: Renderers · React(구 문서) — Codebase Overview: Reconcilers · react-reconciler README · React — React DOM APIs · React Native — Core Components and Native Components

연결된 개념

이 노트를 가리키는 문서

뜻이 가까운 노트

  • React의 규칙과 렌더 순수성

    Rules of React는 React가 올바르게 동작하고 최적화하기 위해 코드가 지켜야 하는 규칙으로, 공식 문서가 세 가지로 정리한다. 핵심은 렌더가 순수한 계산이어야 한다는 것이다.

  • React 서버 컴포넌트(RSC)

    React 서버 컴포넌트(React Server Components, RSC)는 서버에서만 실행되고, 컴포넌트 코드가 아니라 실행 결과(직렬화된 엘리먼트 트리)만 클라이언트로 보내는 컴포넌트다. Next.js App Router에서는 'use client'를 붙이지 않은 컴포넌트가 기본으로 서버 컴포넌트다.

  • Virtual DOM

    Virtual DOM(가상 DOM, Document Object Model)은 화면이 어떻게 생겨야 하는지를 메모리에 적어 둔 가벼운 JavaScript 객체 트리다. React에서는 React 엘리먼트 트리가 그것이다. 상태가 바뀌면 새 트리를 만들어 이전 트리와 비교하고, 달라진 부분만 실제 DOM에 반영한다.

  • Suspense

    Suspense는 아직 준비되지 않은 하위 트리의 렌더를 잠시 보류(suspend)하고, 그동안 가장 가까운 경계의 fallback을 보여 주는 React의 비동기 렌더링 메커니즘이다. 로딩 상태를 컴포넌트마다 들고 있지 않고 경계에 위임한다.

  • 서버 렌더에서 훅이 하는 일

    서버 렌더(Server-Side Rendering, SSR)에서도 컴포넌트 함수는 실제로 호출되고, 그 안의 훅도 호출은 된다. 다만 서버에는 커밋(DOM 반영)이 없어서, 렌더 중 동기적으로 값을 계산하는 훅만 일하고 커밋 뒤에 도는 훅은 아무것도 하지 않는다. "이 훅은 클라이언트에서만 동작한다"는 말은 대개 "Effect 안의 일은 클라이언트에서만 일어난다"는 뜻이다.

보기 옵션