🐯
경민민 IT 핸드북
  • Orientation
    • 전달사항
    • 복습방법
    • 수료한 선배의 한마디
    • 간단 자기소개
    • 스터디
  • 백엔드
    • Java
      • 1장 프로그래밍 기초
      • 2장 자바 메모리구조
        • 1. Stack
        • 2. Heap
      • 6장 객체
      • 8장 상속
      • 9장 다형성
      • 10장 추상클래스와 인터페이스
      • 13장 Generic
      • 14장 Thread
      • 15장 Network
      • 16장 Lamda
        • 1. 내부 클래스 (Inner Class)
          • DTO , VO, Builder Pattern
        • 2. 람다 표현식 (Lambda Expression)
        • 3. 스트림 API (Stream API)
          • Optional
      • 17장 Enum
  • 프론트
    • Node.js
    • Java Script
      • ES6+
        • Node.js로 자바스크립트 실행
        • let , const , var
        • Destructuring문법
          • Rest(...) 문법
        • Arrow Function
        • 모듈
        • ETC
    • Type Script
      • 개요
      • TS설치 및 환경설정
      • 타입스크립트 기본
        • 기본 자료형들과 타입추론
        • Object, Array , Tuple
        • Any, Unknown, Union Type
        • Function Type
          • Type Assertion && Narrowing
          • Never type
        • Type Aliases와 Interface
        • 리터럴 타입
        • 함수 추가 문법
        • Class문법
        • 객체 타입 추가 문법
        • 실습문제 1차
        • 실습문제 2차
        • 실습문제 3차
    • React
      • 개요
      • SPA 와 MPA
        • SEO(작성예정)
      • 리액트 프로젝트 생성(18.3.1.ver)
        • HTML + react 샘플
        • CRA 와 Vite 비교
      • 리액트 개념들
        • Component
          • 클래스 컴포넌트(작성예정)
          • 함수형 컴포넌트(작성예정)
        • JSX
        • React Virtual Dom
          • Reconciliation
        • hook
          • useState
        • 리액트 데이터 전달
          • FLUX
      • 백엔드 서버 연동
        • 비동기요청
        • 웹소켓
        • Promise(작성예정)
      • 실습문제 1
      • 실습문제 2
      • 실습문제3
      • 실습문제4
  • 프레임워크
    • Spring
      • Spring 개발환경 구축
        • 프로젝트 환경설정
        • 프로젝트 생성
          • MVC Project 생성이슈
        • Maven 설정
        • web.xml 설정
        • Spring Bean Configuration.xml 설정
      • Spring Legacy Project
        • Spring 요청 및 응답 흐름
        • Spring 주요 Annotation
          • 의존성 주입방식의 차이점
          • @ModelAttribute와 유효성검사
          • 비동기처리
          • 스프링 예외처리
        • Logging
        • Pagination
        • Spring File 업로드 및 다운로드
        • Spring WebSocket
        • Spring AOP
      • Spring 라이브러리들(작성예정)
        • Lombok
        • Maven
        • MyBatis
      • Spring 구성 모듈(작성예정)
      • 스프링 과제
    • Spring Boot
      • Spring Boot 개발환경 구축
      • 스프링 부트 프로젝트 생성방법들
        • 프로젝트에서 사용하는 의존성들
      • 스프링 프로젝트 구조
        • SpringBootApplication
      • application.properties
      • Cross Origin
        • CORS
      • WebSocket
        • Stomp(작성중)
      • 로그인(작성중)
      • Spring Security(작성중)
      • 실습문제 Select
      • 실습문제 Update
      • 실습문제 Delete
  • 형상관리(Git)
    • GitHub설정
    • SourceTree를 활용한 깃허브 연동
      • 소스트리 설치
      • Clone
      • Branch
        • Branch Protection rules
          • Branch Protection Rules 상세규칙
        • Rebase 와 Squash (작성예정)
      • Team Project 설정
        • 팀장 프로젝트 셋팅
          • Collaborator
          • .gitignore 설정
        • 팀원 프로젝트 셋팅
        • 공통 프로젝트 진행
  • 프로젝트
    • 진행순서
      • 요구사항 분석 단계
        • 유용한 사이트
      • 프로그램 설계 단계
        • 유용한 사이트
      • 프로그램 구현단계
        • SourceTree를 활용한 Team Project설정
      • 테스트 단계
  • 배포
    • AWS-EC2 배포 연습
    • DevOps
      • IT시스템의 변화와 DevOps
      • DevOps 라이프사이클
    • 젠킨스
      • 도커
        • 도커 설치 방법
        • 도커 기본 명령어들
      • 젠킨스 설치
      • 젠킨스 프로젝트 생성
      • 젠킨스 소스코드 통합 - Github
      • 젠킨스 빌드 설정 - Maven
      • 배포 서버 구축하기
      • 파이프라인 구축
      • AWS 서버 생성
        • AWS 인스턴스 생성
        • AWS - Zenkins 연동
        • AWS - 배포서버 연동
        • AWS - Jenkins CI/CD파이프라인 구축
  • 유용한 사이트 모음
  • SQL
    • SQLD
      • 데이터 모델링의 이해 - 스키마
      • 데이터 모델링의 이해 - ERD
      • 데이터 모델링의 이해 - 정규화
      • 데이터 모델링의 이해 - NULL
      • SQL 기본 및 활용 - WINDOW FUNCTION
    • Oracle
      • 1장 개요
      • 2장 SQL
  • LLM 서비스
    • 1장 LLM에 대한 이해
    • 2장 프롬프트 엔지니어링
      • 프롬프트와 프롬프트 엔지니어링
      • GPT PlayGround
      • 프롬프트 작문 유형
      • 기본 프롬프트 엔지니어링 태크닉
      • 고급 프롬프트 엔지니어링 태크닉
        • ReAct Prompting
        • Active-Prompt
        • Reflexion
        • Graph Prompt
      • OpenAI API설정
      • OpenAI를 활용한 프롬프트 엔지니어링 실습
        • 실습 프롬프트
    • 3장 Lang Chain 프레임워크
      • LangSmith 프레임워크
        • LangSmith를 활용한 LangChain 모니터링 설정
      • LangChain 실습 1 - Prompt
        • 실습 코드
      • LangChain 실습 2 - LLM 캐시와 메모리
    • 4장 RAG
      • Document Loader - 문서 로더
      • Text Splitter - 텍스트 분할
      • Embedding - 임베딩
      • Vector Store - 벡터 저장소
      • Retriever - 검색기
      • ReRanker - 재평가자
      • RAG
Powered by GitBook
On this page
  • Intro - 선언적 프로그래밍
  • Reconciliation
  • Diffing 알고리즘
  • 1) 컴포넌트의 루트타입이 바뀐 경우
  • 2) 컴포넌트의 루트타입 외 다른 부분이 바뀐 경우
  • 3) 자식요소 검사시
  • 4) 자식요소에 key속성 적용
  • 일괄 업데이트(batch update)
  • 마무리
  1. 프론트
  2. React
  3. 리액트 개념들
  4. React Virtual Dom

Reconciliation

Reconciliation프로세스에 대해 기술할 페이지. 어렵지만 이해하면 어디가서 리액트 작동방식 안다고 떠들고 다닐 수 있다

PreviousReact Virtual DomNexthook

Last updated 10 months ago

Intro - 선언적 프로그래밍

리액트는 선언적 프로그래밍(declarative programing)을 지향하기 때문에 사용하기는 쉽지만 실제 작동방식에 대해서는 파악하기 어렵습니다. 빠른 개발이 목적이라면 몰라도 되겠지만, 실제 작동방식에 대해서 파악하지 못한 채로 기능만 만져보는건 리액트를 제대로 공부했다고 할 수 없으므로 리액트는 내부적으로 어떤식으로 작동하길래 렌더링이 빠르고 성능이 좋다고 불리는지에 대해 알아보도록 하겠습니다.

Reconciliation

리액트는 Real DOM에게 수정사항을 전달하여 렌더링이 이루어지기 전에 Virtual DOM을 활용하여 렌더링상에 불필요한 부분이 있는지 검사하고 그 과정에서 최소한의 변경사항만 찾아낸 후 Real Dom에 전달하여 효율적인 렌더링작업을 수행합니다. 이 프로세스 전체를 reconciliation이라고 부르는데 실제 수행과정에 대해서 하나하나 짚고 넘어가도록 하겠습니다.

Diffing 알고리즘

리액트는 Real Dom을 본딴 Virtual Dom을 생성합니다. 단 이 Virtual Dom은 항상 1개만 존재하지 않습니다. 리액트는 Component의 상태가 바뀌었을 때 변경사항을 적용시킨 Virtual Dom을 새롭게 생성합니다. 즉 Component의 상태가 막 바뀌었을 때는 Old Virtual Dom과 변경사항이 반영된 New Virtual Dom 2개가 동시에 존재하게 됩니다. 2개의 Virtual Dom을 비교(diff)하는 하여 변경사항을 찾아내는 알고리즘을 Diffing 알고리즘이라고 부릅니다.

아래 문서에 나와있는 Diff알고리즘의 작동방식에 대해서 정리해 보겠습니다.

  • Diff는 2가지 상황을 가정하고 상황별로 다른 작동방식을 가진 알고리즘을 구현하습니다.

1) 컴포넌트의 루트타입이 바뀐 경우

function CounterParent(){
    return(
        <div> // root 
            <Counter/> // root 하위 컴포넌트
        </div>
    )
}

위 코드에서 <div>가 CounterParent컴포넌트에서 root 요소입니다. root요소 아래에는 <Counter/>컴포넌트가 존재하는데 어떤 상황에 의해 <Counter/>컴포넌트를 감싸고 있는 root요소가 다음과 같이 변경되는 경우

function CounterParent(){
    return(
        <span> // <div> -> <span>. Component에 변경사항 발생
            <Counter/> 
        </span>
    )
}

리액트는 기존 루트요소와 하위 요소들을 전부 삭제한 후 새로운 루트요소와 하위요소를 생성시키는 방식으로 동작합니다.

  1. Real Dom에 존재하던 <div>노드와 <Counter>노드는 삭제됩니다.(unmount)

  2. New Virtual Dom에서 변경사항(<span>노드와 <Counter>노드)에 해당하는 노드를 새롭게 생성합니다. (mount)

  3. 생성된 노드를 삭제된 위치에 추가합니다.

  4. 작업 완료후 Old Virtual Dom은 완전히 제거된 후 , New Virtural Dom만 남습니다.

위 처리과정에서 이전 <Counter>에서 유지중이던 상태(state)값이 있다면 소멸하게 됩니다. 따라서 루트 컴포넌트를 변경할 때는 자식 컴포넌트에게 끼칠 영향도 고려해줘야 합니다.

2) 컴포넌트의 루트타입 외 다른 부분이 바뀐 경우

두 리액트 돔을 비교하는 과정에서 각 요소의 루트타입이 같은 경우 리액트는 두 요소의 속성을 검사합니다. 속성은 className, style등을 의미합니다.

function SameParent(){
    return (
        <div className="before">
            <Counter/>
        </div>
    )
}
// 클래스명 변경
function SameParent(){
    return (
        <div className="after">
            <Counter/>
        </div>
    )
}

위 코드에서 루트는 똑같은 <div>이지만 className이 "before"에서 "after"로 바뀌는 경우

리액트는 기존 요소는 그대로 유지한채로 className에만 update를 실행합니다. 또 다른 예시를 보자면

function SameParent(){
    return (
        <div style={{color:'green', fontWeight:'bold'}}>
            <Counter/>
        </div>
    )
}
// color 변경
function SameParent(){
    return (
        <div style={{color:'red', fontWeight:'bold'}}>
            <Counter/>
        </div>
    )
}

다음과 같이 style에서 color만 변경된 경우 기존요소를 유지하면서fontWeight의 변경 없이 color에만 변경을 수행합니다. 이렇게 루트 타입에 대한 변경사항을 반영 했으면 이제 자식 요소들에 대해서도 동일한 검사를 수행합니다.

3) 자식요소 검사시

두 리액트 돔은 자식요소들을 한번에 하나씩 꺼내서 검사를 수행합니다. <ul>태그의 자식요소로 새로운 <li>태그가 추가되었다고 가정을 해보겠습니다.

-- Old Virtual Dom --
<ul> --> root
  <li>first-child</li>
  <li>second-child</li>
</ul>
--------------------------------------------------------------
-- New Virtual Dom --
<ul>--> root
  <li>first-child</li>
  <li>second-child</li>
  <li>third-child</li> --> 추가된 자식요소소
</ul>

위 코드는 다음과 같은 검사를 수행합니다.

  1. root노드가 같은 경우 이므로 root노드의 속성들이 같은지 검사합니다. 변경된 부분이 있다면 수정을 진행한 후 자식 요소들에 대해서도 동일한 검사를 수행합니다.

  2. Old Virtual Dom의 첫번째 자식요소<li>first-child</li>와 New Virtual Dom의 첫번째 자식요소 <li>first-child</li>를 비교합니다. 변경점이 없으므로 넘어갑니다.

  3. Old Virtual Dom의 첫번째 자식요소<li>second-child</li>와 New Virtual Dom의 첫번째 자식요소 <li>second-child</li>를 비교합니다. 변경점이 없으므로 넘어갑니다.

  4. 마지막 <li>third-child</li>는 Old Virtual Dom에 존재하지 않는 요소이므로 새롭게 생성하여 Real Dom에 넣어줍니다.

  5. 작업이 완료된 Old Virtual Dom은 제거됩니다.

위 작업은 first-child와 second는 그대로 유지한 채 최소한의 변경점만 Real Dom에 반영했으므로 성능이 습니다. 하지만 자식요소가 부모의 맨 뒤가 아닌 장소에 추가된 경우에는 성능이 매우 떨어지는데 아래 예시를 통해 확인해 보겠습니다.

-- Old Virtual Dom --
<ul> --> root
  <li>mkm</li>
  <li>kh</li>
</ul>
--------------------------------------------------------------
-- New Virtual Dom --
<ul>--> root
  <li>mkm-kh</li> --> 추가된 자식요소소
  <li>mkm</li>
  <li>kh</li> 
</ul>

위 코드에서 <li>mkm-kh</li>요소는 부모의 제일 첫번째 자식으로 추가 되었습니다. 이런 경우 리액트 돔은 Old Virtual Dom에서는 <li>mkm</li> , New Virtual Dom에서는 <li>mkm-kh</li>를 두고 비교를 수행하기 떄문에 모든 요소가 바뀌었다고 판단을 하고 변경 및 추가작업을 실행합니다. <li>mkm</li>와<li>kh</li>는 사실상 바뀐게 없음에도 불구하고 말이죠.

이러한 문제를 해결할수 있는 방법으로 key 속성을 사용하는 것을 권장하고 있습니다.

4) 자식요소에 key속성 적용

리액트는 자식요소들을 검사할때 자식요소에 key속성이 존재하는 경우 key속석을 기준으로 일치하는 요소들끼리 diff알고리즘을 적용합니다. 아래 코드를 통해 확인해 보도록 하겠습니다.

-- Old Virtual Dom --
<ul> --> root
  <li key='1'>mkm</li>
  <li key='2'>kh</li>
</ul>
--------------------------------------------------------------
-- New Virtual Dom --
<ul>--> root
  <li key='3'>mkm-kh</li> --> 추가된 자식요소소
  <li key='1'>mkm</li>
  <li key='2'>kh</li> 
</ul>

두 리액트 돔은 이제 key값을 기준으로 일치하는 요소들끼리 검사를 수행하게 되었습니다. 즉 Old Dom에서 <li key='1'>mkm</li>인 요소와 검사할 New Dom의 요소는 마찬가지로 key값이 1인 요소를 검사하게 되는 거죠. 즉 리액트는 key값이 1인 요소와 2인 요소는 위치만 변경되었음을 이해하고 변경 해야할 요소는 <li key='3'>mkm-kh</li>뿐 임을 알 수 있는 것입니다.

  • key값에는 해당 자식을 식별할 수 있는 고유한 식별자를 사용하는 것이 가장 좋습니다.

일괄 업데이트(batch update)

위에서 설명 드린 diff알고리즘은 모든 변경사항을 찾았을 때 Real Dom을 즉시 변경시키지 않습니다. 모든 변경 사항들은 하나의 저장공간안에 쌓아 두었다가 모든 자식요소들에 대한 검사를 마친 후에 일괄 업데이트(batch update)를 수행합니다. 이는 업데이트를 좀더 효율적으로 수행하기 위한 리액트의 처리방식입니다.

마무리

개요에 설명했듯 리액트는 선언형 프로그래밍을 지향하기 때문에 각종 리액트 API들을 디자인할때 구현 상세기능들에 대해서는 감춰두었습니다. 이는 개발자가 리액트의 구현 상세 기능에 집중하는 것보다 리액트를 활용하여 화면구현을 하는 것을 더 중요한 가치로 판단했기 때문입니다. 실제로 리액트는 굉장히 버전업이 많이 되고 코드가 많이 바뀌기 때문에 코드 하나하나를 뜯어 보는 건 의미 없는 일일지도 모릅니다.

하지만 정말 리액트를 고성능으로 잘 사용하기 위해서는 상세기능에 대한 이해가 필요합니다. 당연하지만 현재 문서에서 리액트의 모든 Reconciliation과정을 다룬것 도 아니고 리액트의 모든 기능에 대해 설명하진 않았습니다. 다만 진정한 리액트 고수가 되고자 한다면 리액트 메뉴얼을 하나하나 읽어보시고, 최신업데이트내용을 살펴보시고 어떤 기능이 추가되었나 꼼꼼한 확인이 필요할 겁니다. 아래 리액트 공식 커뮤니티 주소를 남기니 기회가 될때마다 잘 살펴보시길 바랍니다.

root노드가 서로 같은지 검사합니다. 서로 다르다면 를 수행합니다.

컴포넌트의 루트타입이 바뀐 경우
React Blog – Reactreactjs
공부는 끝이 없다
Reconciliation – React
Logo
Logo