ReactNextCentral

렌더링과 커밋

Published on
리액트에서 렌더링 및 DOM 업데이트 과정을 이해하고, 초기 렌더링과 상태 업데이트를 통한 재렌더링, 그리고 DOM 커밋 단계를 파악합니다.
Table of Contents

컴포넌트가 화면에 표시되기 전에 리액트에 의해 렌더링되어야 합니다. 이 과정에서의 단계를 이해하면 코드가 실행되는 방식을 생각하고 그 동작을 설명할 수 있습니다.

배울 내용

  • 리액트에서 렌더링이란 무엇인지
  • 컴포넌트가 언제 그리고 왜 렌더링되는지
  • 컴포넌트를 화면에 표시하는 과정
  • 렌더링이 항상 DOM 업데이트를 유발하지 않는 이유

상상해보세요. 컴포넌트는 주방에서 재료를 사용하여 맛있는 요리를 조리하는 요리사입니다. 이 시나리오에서 리액트는 손님의 요청을 받고 주문을 전달하는 웨이터입니다. UI에 대한 요청과 제공이 이루어지는 과정은 세 가지 단계로 이루어집니다.

  1. 렌더를 트리거합니다(손님의 주문을 주방에 전달합니다).
  2. 컴포넌트를 렌더링합니다(주문을 주방에서 처리합니다).
  3. DOM에 커밋합니다(주문을 테이블에 올려놓습니다).

단계 1: 렌더 트리거

컴포넌트가 렌더링되는 이유는 두 가지입니다.

  1. 컴포넌트의 초기 렌더링입니다.
  2. 컴포넌트(또는 그 조상 중 하나)의 상태가 업데이트되었습니다.

초기 렌더링

앱이 시작될 때 초기 렌더링을 트리거해야 합니다. 프레임워크나 샌드박스는 때로 이 코드를 숨깁니다. 그러나 대상 DOM 노드로 createRoot를 호출하고 컴포넌트와 함께 렌더 메서드를 호출함으로써 이를 수행합니다.

Edit gallant-waterfall-mnfdp5

root.render() 호출을 주석 처리하고 컴포넌트가 사라지는 것을 확인해보세요!

상태 업데이트에 따른 재렌더링

컴포넌트가 초기 렌더링된 후에는 상태를 업데이트하여 추가적인 렌더링을 트리거할 수 있습니다. set 함수를 사용하여 컴포넌트의 상태를 업데이트하면 자동으로 렌더링이 대기열에 추가됩니다. (각 상태 업데이트마다 고객의 목마름이나 배고픔에 따라 차, 디저트 및 다른 다양한 주문을 넣는 고객처럼 상상해볼 수 있습니다.)

단계 2: 리액트가 컴포넌트를 렌더링

렌더링을 트리거한 후에 리액트는 화면에 표시할 내용을 결정하기 위해 컴포넌트를 호출합니다. "렌더링"은 리액트가 컴포넌트를 호출하는 것입니다.

  • 초기 렌더링에서 리액트는 루트 컴포넌트를 호출합니다.
  • 추가적인 렌더링에서 리액트는 렌더링을 트리거한 함수 컴포넌트를 호출합니다.

이 프로세스는 재귀적으로 진행됩니다. 업데이트된 컴포넌트가 다른 컴포넌트를 반환하면 리액트는 해당 컴포넌트를 다음으로 렌더링하고, 그 컴포넌트도 무언가를 반환하면 다음 컴포넌트를 렌더링하고, 이런 식으로 계속 진행됩니다. 이 프로세스는 중첩된 컴포넌트가 더 이상 없을 때까지 계속되며, 리액트는 정확히 어떤 내용을 화면에 표시해야 하는지 알게 됩니다.

다음 예제에서 리액트는 Gallery()Image()를 여러 번 호출합니다.

Edit practical-mccarthy-1p08zd

  • 초기 렌더링 중 리액트는 <section>, <h1>, 그리고 세 개의 <img> 태그에 대한 DOM 노드를 생성합니다.
  • 재렌더링 중 리액트는 이전 렌더링과 비교하여 변경된 속성을 계산합니다. 이 정보를 다음 단계인 커밋 단계에서 사용하기 전까지는 아무 작업도 수행하지 않습니다.

주의점

렌더링은 항상 순수한 계산이어야 합니다.

  • 동일한 입력에 대해 동일한 출력을 반환합니다. 동일한 입력이 주어지면 컴포넌트는 항상 동일한 JSX를 반환해야 합니다. (샐러드에 토마토를 주문한 사람은 양파가 들어간 샐러드를 받아서는 안 됩니다!)
  • 자신의 업무에만 집중해야 합니다. 렌더링 이전에 존재한 객체나 변수를 변경해서는 안 됩니다. (한 주문이 다른 사람의 주문을 변경해서는 안 됩니다.)

그렇지 않으면 코드베이스가 복잡해짐에 따라 혼란스러운 버그와 예측할 수 없는 동작을 경험할 수 있습니다. "Strict 모드"에서 개발할 때 리액트는 각 컴포넌트 함수를 두 번 호출하여 비순수 함수에 의한 오류를 파악하는 데 도움이 됩니다.

자세히 알아보기: 성능 최적화

업데이트된 컴포넌트의 모든 중첩된 컴포넌트를 렌더링하는 기본 동작은 트리에서 상위에 위치한 업데이트된 컴포넌트의 성능에는 적합하지 않습니다. 성능 문제가 발생하는 경우, 성능 섹션에 설명된 몇 가지 최적화 방법을 선택적으로 사용하여 문제를 해결할 수 있습니다. 너무 일찍 최적화하지 마세요!

단계 3: 리액트가 DOM에 변경 사항을 커밋

컴포넌트를 렌더링(호출)한 후에 리액트는 DOM을 수정합니다.

  • 초기 렌더링에서 리액트는 생성한 모든 DOM 노드를 appendChild() DOM API를 사용하여 화면에 배치합니다.
  • 재렌더링에서 리액트는 (렌더링 중 계산된) 최소한의 작업을 수행하여 DOM을 최신 렌더링 결과와 일치하도록 합니다.

리액트는 렌더링 결과가 이전과 동일한 경우에는 DOM 노드를 변경하지 않습니다. 예를 들어, 다음은 부모로부터 전달되는 다른 속성으로 재렌더링되는 컴포넌트입니다. <input>에 텍스트를 추가하여 값을 업데이트할 수 있지만, 컴포넌트가 재렌더링되어도 텍스트가 사라지지 않습니다.

Edit modern-snowflake-y5b4hi

이 작동하는 이유는 마지막 단계에서 리액트가 <h1>의 내용만 새로운 시간으로 업데이트하기 때문입니다. JSX에서 <input>이 동일한 위치에 있음을 알 수 있으므로 리액트는 <input> 또는 그 값을 변경하지 않습니다!

에필로그: 브라우저 페인팅

렌더링이 완료되고 리액트가 DOM을 업데이트한 후에 브라우저는 화면을 다시 그립니다. 이 과정을 "브라우저 렌더링"이라고 알려져 있지만 이 문서에서는 혼동을 피하기 위해 "페인팅"으로 표현합니다.

요약

  • 리액트 앱에서 화면 업데이트는 세 단계로 진행됩니다.
    1. 트리거
    2. 렌더
    3. 커밋
  • Strict 모드를 사용하여 컴포넌트의 오류를 찾을 수 있습니다.
  • 렌더링 결과가 이전과 동일한 경우 리액트는 DOM을 변경하지 않습니다.