버츄얼 돔에 먼저 변화를 처리하고, 이 변경 사항을 적용하기 전에 이전 버츄얼 돔과 비교하여 필요한 부분만 찾아내 실제 DOM 에 적용함 ( Diffing, Reconciliation )
리액트에서 Virtual DOM
리액트에서 핵심 기능 중 하나임
리액트는 렌더링이 발생될 상황에 놓이게 되면 새로운 화면에 들어갈 내용이 담긴 가상 돔을 생성
리액트는 이전 화면 구조와 렌더링 이후 화면 구조 2개의 가상 돔 객체 유지
diffing 알고리즘 동작 방식
이전 가상돔 트리와 새로운 가상돔 트리를 비교하고 루트 노드에서 시작해서 이전과 새로운 노드를 비교
두 노드가 다른 유형이면 새 노드를 생성하여 기존 노드를 대체
두 노드가 같은 유형이면 속성을 비교해서 변경된 것이 있는지 확인함
변경 된 속성이 있으면 속성 업데이트
변경 된 속성이 없으면 그대로 사용
리스트 항목의 경우 key를 사용해서 효율적으로 비교s
자식노드를 재귀적으로 비교
Reconciliation 단계 (재조정)
diffing 알고리즘을 포함한 더 넓은 개념
Virtual DOM의 변경사항을 실제 DOM에 효율적으로 적용하는 전체 프로세스를 의미
Diffing에서 식별된 변경사항을 바탕으로 실제 DOM 업데이트 계획을 수립
변경사항을 배치(batch)로 그룹화하여 처리
실제 DOM 업데이트를 최적화하여 수행
컴포넌트 생명주기 메서드를 적절한 시점에 호출
이벤트 핸들러 재설정
Vanilla JS 로 Virtual DOM 구현 시 고려사항
1. DOM 표현 구조 설계
노드 표현 방식
각 DOM 노드를 JS 객체로 표현하는 방법 설계
속성 및 이벤트 처리
요소의 속성, 스타일, 이벤트 리스너 등을 어떻게 표현할지 결정
텍스트 노드 처리
텍스트 노드를 어떻게 표현할지 결정
// 가상 DOM 노드 예시{type:'div',props:{id:'container',className:'wrapper',onClick:()=>console.log('clicked')},children:[{type:'h1',props:{},children:['제목']},{type:'p',props:{},children:['내용']}]}
2. Reconciliation 알고리즘
차이점 감지 ( Diffing )
이전 가상 DOM과 새 가상 DOM 간의 차이를 효율적으로 찾는 알고리즘 구현
키 (Key) 관리
리스트 항목의 효율적인 재사용과 재정렬을 위한 고유 식별자 관리
트리 비교 최적화
전체 트리를 비교하는 대신 변경 가능성이 있는 부분만 비교하는 전략
3. DOM 업데이트 최적화
최소 조작 : 필요한 DOM 조작을 최소화하는 방법
배치 처리 : 여러 DOM 업데이트를 그룹화하여 한 번에 처리
비동기 업데이트 : 렌더링 성능을 위해 DOM 업데이트를 비동기적으로 처리
4. 컴포넌트 시스템 설계
컴포넌트 생명주기 : 마운트, 업데이트, 언마운트 등의 생명주기 이벤트 관리
상태 관리 : 컴포넌트 상태를 관리하고 변경 시 리렌더링 트리거 방법
속성 (Props) 전달 : 부모에서 자식 컴포넌트로 데이터를 전달하는 매커니즘
5. 이벤트 처리
이벤트 위임 ( Event Delegation ) : 효율적인 이벤트 처리를 위한 이벤트 위임 구현
여러 요소에 각각 이벤트 리스너를 붙이는 대신, 공통 부모 요소에 하나의 이벤트 리스너를 붙이는 기법
이벤트가 버블링되는 특성을 활용한 패턴
버블링 : DOM 트리를 타고 위로 올라가는 이벤트 버블링
// 🚫 이벤트 위임 없이 - 각 버튼마다 리스너 추가document.querySelectorAll('.button').forEach(button=>{button.addEventListener('click',function(){console.log('버튼 클릭됨:',this.textContent);});});// ✅ 이벤트 위임 사용 - 부모 요소에 하나의 리스너만 추가document.querySelector('.button-container').addEventListener('click',function(e){// 클릭된 요소가 버튼인지 확인if(e.target.classList.contains('button')){console.log('버튼 클릭됨:',e.target.textContent);}});
합성 이벤트 ( Synthetic Events ) : 브라우저 간 일관된 이벤트 처리를 위한 이벤트 래퍼
6. 성능 고려사항
메모리 관리 : 가비지 컬렉션과 메모리 누수 방지
렌더링 최적화 : 불필요한 리렌더링 방지
큰 리스트 처리 : 대량의 데이터를 효율적으로 렌더링하는 방법
// 가상 DOM 노드 생성 함수functioncreateElement(type,props,...children){return{type,props:props||{},children:children.flat().map(child=>typeofchild==='string'||typeofchild==='number'?{type:'TEXT_ELEMENT',props:{nodeValue:child}}:child)};}// DOM 노드 생성 함수functioncreateDomNode(vNode){if(vNode.type==='TEXT_ELEMENT'){returndocument.createTextNode(vNode.props.nodeValue);}constdomNode=document.createElement(vNode.type);// 속성 설정Object.keys(vNode.props).filter(key=>key!=='children').forEach(name=>{if(name.startsWith('on')){consteventType=name.toLowerCase().substring(2);domNode.addEventListener(eventType,vNode.props[name]);}else{domNode[name]=vNode.props[name];}});// 자식 노드 처리vNode.children.forEach(childVNode=>{domNode.appendChild(createDomNode(childVNode));});returndomNode;}// 차이점 비교 및 DOM 업데이트 함수functionupdateDom(dom,oldVNode,newVNode){// 구현 생략 - 속성 비교 및 업데이트 로직// 자식 노드 비교 및 업데이트 로직}// 렌더링 함수functionrender(vNode,container){// 초기 렌더링 또는 업데이트 로직container.innerHTML='';container.appendChild(createDomNode(vNode));}
Leave a comment