의도: 지원자가 React의 Virtual DOM의 개념과 그 동작 방식을 이해하고 있는지 평가
팁
나의 답안
리액트에서 사용하는 Virtual DOM은 말 그대로 실제 DOM을 메모리상에 가볍게 만들어 둔 가상의 DOM 트리입니다.
브라우저의 실제 DOM은 조작 비용이 크기 때문에, 상태가 자주 변하는 SPA에서 직접 조작하면 성능이 크게 떨어질 수 있습니다.
이를 해결하기 위해 리액트는 직접 DOM을 건드리는 대신 Virtual DOM을 이용해 변경 사항을 계산하고 최소한으로만 실제 DOM에 반영하는 방식을 사용합니다.state나 props가 변경되면 리액트는 새로운 Virtual DOM을 다시 생성합니다.
그리고 이전 Virtual DOM과 새로운 Virtual DOM을 비교해서 어떤 부분이 바뀌었는지 계산합니다.
바뀐 부분만 실제 DOM에 패치하여 브라우저에 반영합니다.이렇게 하면 전체 DOM을 다시 그리는 대신 필요한 부분만 업데이트하기 때문에 성능이 훨씬 효율적이고, 렌더링 최적화에도 유리합니다.
즉, Virtual DOM은 리액트가 변경 최소화 전략으로 UI를 빠르게 업데이트할 수 있도록 돕는 핵심 메커니즘이라고 할 수 있습니다.
주어진 답안 (모범 답안)
Virtual DOM은 실제 DOM의 가벼운 복사본으로, React에서 상태 변화에 따른 UI 업데이트를 효율적으로 처리하기 위해 사용됩니다.
Virtual DOM은 메모리에서 작동하며, 변경 사항을 실제 DOM에 반영하기 전에 비교(diffing)하고 최소한의 변경만 실제 DOM에 적용(patching)합니다.
이를 통해 성능을 최적화하고 빠른 렌더링을 제공합니다.
Virtual DOM(V-DOM)은 React와 같은 라이브러리에서 사용되는 개념으로, UI 업데이트를 효율적으로 처리하기 위해 실제 DOM 대신 메모리에 가상의 DOM을 만들어 변경 사항을 최소화하는 방식이다. 이를 통해 브라우저 성능을 최적화하고 렌더링 비용을 줄일 수 있다.
Virtual DOM은 실제 DOM의 가벼운 사본(객체 형태)이다. 이 사본은 JavaScript 객체로 표현되며, UI의 현재 상태를 나타낸다.
React에서는 UI를 변경할 때 직접 DOM을 조작하지 않고, Virtual DOM을 수정한 후 이를 실제 DOM과 비교하여 변경이 필요한 부분만 반영한다.
Virtual DOM은 일반적으로 다음 3단계로 동작한다.
Render(렌더링) - Virtual DOM 생성
컴포넌트가 렌더링될 때, React는 render() 메서드를 실행하여 Virtual DOM을 생성한다.
이 Virtual DOM은 메모리에 존재하며, 실제 DOM과 연결되지 않는다.
React.createElement()를 통해 Virtual DOM 객체로 변환됨const element = <h1>Hello, world!</h1>;
console.log(element);
// 결과 (실제 React 내부 변환 구조)
{
type: "h1",
props: { children: "Hello, world!" }
}Diffing(비교) - Virtual DOM 간 변경 사항 확인 (Reconciliation 과정)
Virtual DOM을 갱신해야 할 상황이 오면, React는 새롭게 생성된 Virtual DOM과 기존 Virtual DOM을 비교하여 달라진 부분을 찾는다.
이 과정은 Diffing 알고리즘을 사용하여 수행된다.
React의 Diffing 알고리즘 최적화 방식
div → div)이면 속성만 비교하여 변경된 부분만 업데이트div → p)이면 기존 요소를 삭제하고 새로운 요소로 교체Diffing 예제
const element1 = <h1>Hello, world!</h1>;
const element2 = <h1>Hello, React!</h1>;
1) h1 태그는 그대로 유지된다.
2) Hello, world! → Hello, React!로 텍스트가 변경된다.
React는 전체 h1을 리렌더링하는 것이 아니라, 텍스트 노드만 변경하는 방식으로 최소한의 업데이트를 수행한다.
Patching(업데이트) - 실제 DOM 반영
변경 사항이 확인되면, React는 Batching(일괄 처리) 기법을 이용하여 필요한 부분만 실제 DOM에 반영한다.
React는 ReactDOM.render() 또는 useState(), useReducer() 등을 통해 상태가 변경될 때 Virtual DOM을 새롭게 만들고, 이를 비교하여 실제 DOM을 수정한다.
실제 DOM 업데이트는 React의 Batch Update(일괄 업데이트) 기법을 사용하여 성능을 최적화한다.
React.createElement()를 통해 UI를 Virtual DOM으로 쉽게 변환이 가능하다.ReactDOM.createRoot()와 useTransition()을 활용하여 성능을 조절할 수 있다.| 항목 | Virtual DOM | Real DOM |
|---|---|---|
| 렌더링 속도 | 빠름 (최소한의 변경) | 느림 (전체 업데이트) |
| 변경 감지 방식 | Diffing 알고리즘 활용 | 직접 조작 |
| 업데이트 방식 | 변경된 부분만 적용 | 모든 요소 업데이트 |
| 브라우저 성능 | 최적화됨 | 성능 저하 가능 |
복잡한 애니메이션 성능 문제
CSS 애니메이션과 비교하면 Virtual DOM을 통한 애니메이션 구현이 느릴 수 있다.
requestAnimationFrame()을 활용하여 성능을 높이는 방식이 필요하다.
⭐
requestAnimationFrame()이란?
requestAnimationFrame()은 브라우저에게 애니메이션을 수행할 것을 요청하는 함수로, 브라우저의 화면 새로고침 주기(Frame Rate, FPS)에 맞춰 최적화된 렌더링을 수행하도록 도와준다.- 즉, 자바스크립트의 애니메이션을 부드럽게 실행하면서, 불필요한 Reflow/Repaint를 줄이는 최적화 기법이다.
⭐ 기존의
setTimeout()vs.requestAnimationFrame()차이
- 과거에는 애니메이션을 만들 때
setTimeout()또는setInterval()을 사용했다.- 하지만
setTimeout()을 사용하면 프레임이 일정하지 않아 애니메이션이 끊기는 문제가 발생한다.function moveBox() { box.style.left = box.offsetLeft + 5 + "px"; setTimeout(moveBox, 16); // 16ms마다 실행 (약 60FPS) } moveBox();
- 문제점
💖setTimeout()은 브라우저가 렌더링하는 시점을 고려하지 않는다.
💖 브라우저가 화면을 그리는 주기(보통 60FPS, 즉 16.67ms)와 동기화되지 않아 프레임이 밀리거나 끊길 수 있다.- 해결 방법:
requestAnimationFrame()function moveBox() { box.style.left = box.offsetLeft + 5 + "px"; requestAnimationFrame(moveBox); // 브라우저의 프레임 속도에 맞춰 실행 } moveBox();
- 장점
💖 브라우저의 리프레시 속도(보통 60FPS)에 맞춰 실행된다. → 부드러운 애니메이션
💖 백그라운드 탭에서 자동으로 중지된다. → CPU/GPU 자원이 절약된다.(성능 최적화)
⭐
requestAnimationFrame()의 동작 방식
- 브라우저가 화면을 새로고침할 때(refresh rate, 보통 1초에 60번)마다 콜백 함수를 실행할 기회를 준다.
- 브라우저가 최적의 타이밍에 애니메이션을 실행하도록 보장한다.
- 백그라운드에서 실행 중이면 자동으로 멈춰 자원 낭비를 방지한다.
- 기본 사용법
function animate() { console.log("애니메이션 실행!"); requestAnimationFrame(animate); // 다음 프레임에서 실행 } requestAnimationFrame(animate);
useMemo(), useCallback(), React.memo() 등을 사용하여 불필요한 렌더링을 방지해야 한다.React.memo()를 사용한다.useMemo()와 useCallback()을 사용하여 컴포넌트 리렌더링을 방지한다.useTransition()을 사용하여 렌더링 우선순위를 조절한다.Virtual DOM은 UI 변경을 효율적으로 처리하기 위해 메모리에서 변경 사항을 감지하고, 최소한의 실제 DOM 조작을 수행하는 방식이다.
이를 통해 React는 높은 성능을 유지하면서도 선언형 프로그래밍 스타일을 유지할 수 있다.
하지만 Diffing 과정에서 연산 비용이 발생할 수 있으므로 React.memo(), useMemo(), useCallback() 등을 활용하여 최적화하는 것이 중요하다.