하나의 리액트 요소 트리는 시간에 따라 변화하는 화면의 한순간을 나타낸다.
리액트는 렌더링된 컴포넌트로 렌더 트리를 구성할 수 있습니다. 렌더 트리란 컴포넌트가 렌더링될 때 실제 컴포넌트 계층 구조를 나타낸 것으로 부모, 자식 컴포넌트의 구조를 트리로 표현한 것이라고 할 수 있습니다 ! 이 때 렌더 트리는 리액트 컴포넌트로만 구성되고 HTML 태그와 같은 플랫폼 UI 요소는 포함하지 않습니다.
React, as a UI framework, is platform agnostic.
공식 문서에 이런 문구가 있는데, 이 말은 곧 리액트가 플랫폼에 독립적이라는 말 입니다. 앱이 렌더링 되는 플랫폼에 상관없이 리액트에서 렌더링 할 수 있다고 부연 설명이 되어있는데 이 부분을 NoteBook LLM 과 대화해본 결과 다음과 같은 결론이 나왔습니다.
1) 리액트 자체는 사용자 인터페이스를 구성하는 컴포넌트의 구조와 관계를 관리하는데 집중한다. 실제 플랫폼에서 사용되는 UI 기본 요소(웹의 HTML 태그) 등은 포함하지 않는다.
2) 리액트는 HTML 마크업을 통해 웹 UI 를 렌더링 하지만 다른 플랫폼에서도 사용할 수 있도록 설계 되었다.
즉, 리액트로 작성된 컴포넌트 코드와 UI 구조 정의는 웹, 모바일 등 특정 플랫폼에 묶여있지 않다는 얘기였습니다 (이 문서에서 이야기 하고 있는 '플랫폼' 은 모바일 , 데스크톱, 웹 어플리케이션 등을 말하고 있는 거였어요!)
예시 코드로 렌더 트리와 그 작동 구조를 한번 살펴봅시다. 다음과 같이 App 이라는 컴포넌트 하위에 Header, Main, Footer 컴포넌트가 각각 있고, Main은 App 으로부터 count 라는 props를 받아서 사용하고 있는 구조 입니다.
function Header() {
console.log('🟦 Header 렌더링됨');
return <header>Header</header>;
}
function Main({ count }: { count: number }) {
console.log('🟨 Main 렌더링됨');
return <main>Count: {count}</main>;
}
function Footer() {
console.log('🟩 Footer 렌더링됨');
return <footer>Footer</footer>;
}
export default function App() {
const [count, setCount] = useState(0);
console.log('🏠 App 렌더링됨');
return (
<div>
<Header />
<Main count={count} />
<Footer />
<button onClick={() => setCount(count + 1)}>+</button>
</div>
);
}
코드가 그렇게 구조가 복잡하지 않아 아래 그려진 렌더 트리를 보면 굉장히 간단하게 만들어진 걸 알 수 있습니다.

리액트는 렌더링을 할 때 렌더 트리를 기준으로 위에서 아래로 렌더링을 하게 되는데, 만약 이때 '상태' 나 'props'가 변화되면 그 변경된 컴포넌트와 자식 컴포넌트만 다시 렌더링 됩니다. 위 코드를 예시로 들면 App -> Header, Main, Footer 순으로 트리를 따라 내려가며 렌더링이 되겠네요.! 실행 결과는 아래와 같이 나오게 됩니다
🏠 App 렌더링됨
🟦 Header 렌더링됨
🟨 Main 렌더링됨
🟩 Footer 렌더링됨
이떄 버튼을 클릭해서 count의 상태를 바꿔볼까요? 그렇다면 어떻게 될까요?
🏠 App 렌더링됨
🟦 Header 렌더링됨
🟨 Main 렌더링됨
🟩 Footer 렌더링됨
상태가 바뀔 경우 count라는 props를 받는 Main 이라는 자식 컴포넌트의 count 값은 변경됩니다. 그러나 상태가 바뀌면서 App이 다시 렌더링 되기 때문에 그 자식 컴포넌트들인 Header, Main, Footer은 결국 모두 함수 호출로 인해 콘솔에 찍히게 됩니다.
* 이 때 Header과 Footer의 경우 props의 영향을 받지 않음에도 불필요한 리렌더링이 일어났다는 문제가 있어 React.memo 등으로 props가 바뀌지 않는 이상은 아예 호출되지 않도록 최적화를 할 수 있겠죠?
왜 DOM 처럼 트리 구조로 되어있나요?
렌더 트리는 Top-level 컴포넌트부터 leaf 컴포넌트까지 구조를 파악하기 쉽도록 해준다는 장점이 있는데, 이때 Top-level 컴포넌트는 root에 가까운 컴포넌트이고 가장 복잡한 구조로 하위의 컴포넌트의 렌더링에 영향을 미치는 것을 의미하고, leaf 컴포넌트의 경우 자식 컴포넌트는 없지만 자주 리렌더링 되는 컴포넌트를 의미합니다.
추가 질문! 리렌더링은 어떤 순간에 발생하나요?
1. 부모 컴포넌트가 리렌더링 될 때 (같은 계층 컴포넌트끼리는 영향 X - Header만 재렌더링 된다고 해도 Main이나 Footer에는 영향을 미치지 않음)
2. 부모 컴포넌트에서 전달받은 props가 변경되었을 때
3. 내부 상태가 변경되었을 때
* 리액트의 가상 DOM 조작을 간단히 알아보자면 ,,
리액트는 Virtual DOM (가상 DOM, 실제 DOM과 내용은 동일 하나 메모리에 저장되어 있는 JS 객체) 을 기반으로 DOM 을 조작하게 되는데, 리렌더링의 순간에 새로운 내용이 담긴 가상 DOM 을 생성합니다. 이때 렌더링 이전의 화면 내용을 담는 첫 번째 가상 DOM 과 조작 이후에 발생할 두 번째 가상 DOM 을 비교하여 어떤 것이 달라졌는지 비교하게 됩니다 (Diffing)
-> 차이가 발생한 부분만을 실제 DOM 에 적용 (Reconciliation)
렌더 트리랑 DOM 트리랑 무슨 차이일까요?
React에서의 렌더 트리는 컴포넌트의 계층 구조를 나타낸 것이고, 상태 변경을 감지하여 차이를 비교한 뒤 가상 DOM 에 전달합니다!
DOM 트리는 React가 아닌, 브라우저가 화면에 요소들을 출력하기 위해 만드는 구조를 이야기 해요.
정리하자면, 리액트의 공식 문서에서 이야기 하는 'Understanding Your UI as a Tree' 라는건 리액트에서의 컴포넌트 구조를 렌더링 하기 전 렌더 트리를 통해 구조를 파악할 수 있고, 이 때 렌더 트리는 부모 요소와 자식 요소, 즉 중첩된 컴포넌트의 구조를 나타낼 수 있다는 것을 의미합니다.
'리액트 톺아보기' 카테고리의 다른 글
| Memoization, 최적화 (0) | 2025.05.22 |
|---|---|
| [React] 제어 컴포넌트 vs. 비제어 컴포넌트 (0) | 2025.05.08 |
| [Hook] useImperativeHandle (0) | 2025.05.01 |