렌더링 및 커밋
컴포넌트가 화면에 표시되기 전에 React에서 렌더링해야 합니다. 이 프로세스의 단계를 이해하면 코드가 어떻게 실행되는지 생각하고 그 동작을 설명하는 데 도움이 됩니다.
학습 내용
- React에서 렌더링이 의미하는 것
- React가 컴포넌트를 렌더링하는 시기와 이유
- 컴포넌트를 화면에 표시하는 단계
- 렌더링이 항상 DOM 업데이트를 생성하지 않는 이유
컴포넌트가 주방에서 재료로 맛있는 요리를 만드는 요리사라고 상상해 보세요. 이 시나리오에서 React는 고객의 요청을 접수하고 주문을 가져오는 웨이터 역할을 합니다. UI를 요청하고 제공하는 이 과정은 세 단계로 이루어집니다:
1. 렌더링 트리거(손님의 주문을 주방에 전달)
2. 컴포넌트 렌더링(주방에서 주문 준비)
3. DOM에 커밋(주문을 테이블에 배치)
1단계: 렌더링 트리거
컴포넌트가 렌더링되는 이유는 두 가지입니다:
1. 컴포넌트의 초기 렌더링.
2. 컴포넌트(또는 그 상위 컴포넌트 중 하나)의 상태 업데이트.
초기의 렌더링
앱이 시작되면 초기 렌더링을 트리거해야 합니다. 프레임워크와 샌드박스는 때때로 이 코드를 숨기기도 하지만, 대상 DOM 노드로 createRoot를 호출한 다음 컴포넌트로 해당 렌더링 메서드를 호출하면 됩니다:
import Image from './Image.js';
import { createRoot } from 'react-dom/client';
const root = createRoot(document.getElementById('root'))
root.render(<Image />);
root.render() 호출을 주석 처리하면 컴포넌트가 사라지는 것을 확인할 수 있습니다!
상태 업데이트 시 재렌더링
컴포넌트가 처음 렌더링된 후에는 setter 함수로 상태를 업데이트하여 추가 렌더링을 트리거할 수 있습니다. 컴포넌트의 상태를 업데이트하면 렌더링이 자동으로 대기열에 추가됩니다. (레스토랑에서 손님이 첫 주문을 한 후 갈증이나 배고픔의 상태에 따라 차, 디저트 등 다양한 음식을 주문한다고 생각하면 됩니다.)
2단계: React가 컴포넌트를 렌더링
렌더링을 트리거한 후 React는 컴포넌트를 호출하여 화면에 표시할 내용을 파악합니다. "렌더링"은 React가 컴포넌트를 호출하는 것입니다.
초기 렌더링에서 React는 루트 컴포넌트를 호출합니다.
이후 렌더링에서, React는 상태 업데이트가 렌더링을 트리거한 함수 컴포넌트를 호출합니다.
업데이트된 컴포넌트가 다른 컴포넌트를 반환하면 React는 그 컴포넌트를 다음에 렌더링하고, 그 컴포넌트 역시 무언가를 반환하면 그 컴포넌트를 다음에 렌더링하는 식으로 재귀적인 프로세스를 수행합니다. 이 프로세스는 중첩된 컴포넌트가 더 이상 존재하지 않을 때까지, 그리고 React가 화면에 표시해야 할 내용을 정확히 파악할 때까지 계속됩니다.
다음 예제에서 React는 Gallery()와 Image()를 여러 번 호출합니다:
export default function Gallery() {
return (
<section>
<h1>Inspiring Sculptures</h1>
<Image />
<Image />
<Image />
</section>
);
}
function Image() {
return (
<img
src="https://i.imgur.com/ZF6s192.jpg"
alt="'Floralis Genérica' by Eduardo Catalano: a gigantic metallic flower sculpture with reflective petals"
/>
);
}
초기 렌더링 중에 React는 <section>, <h1>, 세 개의 <img> 태그에 대한 DOM 노드를 생성합니다.
다시 렌더링하는 동안 React는 이전 렌더링 이후 변경된 props가 있다면 어떤 것인지 계산합니다. 다음 단계인 커밋 단계가 될 때까지 해당 정보로 아무 작업도 하지 않습니다.
함정
렌더링은 항상 순수한 계산이어야 합니다:
동일한 입력, 동일한 출력. 동일한 입력이 주어지면 컴포넌트는 항상 동일한 JSX를 반환해야 합니다. (토마토가 들어간 샐러드를 주문했는데 양파가 들어간 샐러드가 나오면 안 됩니다!).
컴포넌트는 자기 일에만 신경씁니다. 렌더링 전에 존재했던 객체나 변수를 변경하지 않아야 합니다. (하나의 주문이 다른 사람의 주문을 변경해서는 안 됩니다.)
그렇지 않으면 코드베이스가 복잡해지면서 혼란스러운 버그와 예측할 수 없는 동작이 발생할 수 있습니다. "엄격한 모드"에서 개발할 때 React는 각 컴포넌트의 함수를 두 번 호출하므로 불순한 함수로 인한 실수를 발견하는 데 도움이 될 수 있습니다.
Deep Dive - 성능 최적화
업데이트된 컴포넌트가 트리에서 매우 높은 위치에 있는 경우 업데이트된 컴포넌트 내에 중첩된 모든 컴포넌트를 렌더링하는 기본 동작은 성능에 최적이 아닙니다. 성능 문제가 발생하는 경우 성능 섹션에 설명된 몇 가지 옵트인 방식으로 해결할 수 있습니다. 조기에 최적화하지 마세요!
3단계: React가 DOM에 변경사항 커밋하기
컴포넌트를 렌더링(호출)한 후 React는 DOM을 수정합니다.
- 초기 렌더링에서 React는 appendChild() DOM API를 사용해 생성한 모든 DOM 노드를 화면에 배치합니다.
- 재렌더링의 경우, React는 DOM이 최신 렌더링 출력과 일치하도록 하기 위해 필요한 최소한의 연산(렌더링하는 동안 계산!)을 적용합니다.
React는 렌더링 간에 차이가 있을 때만 DOM 노드를 변경합니다. 예를 들어, 다음은 부모로부터 매초마다 다른 props를 전달받아 다시 렌더링하는 컴포넌트입니다. <input>에 텍스트를 추가하고 값을 업데이트해도 컴포넌트가 다시 렌더링될 때 텍스트가 사라지지 않는 것을 확인할 수 있습니다:
export default function Clock({ time }) {
return (
<>
<h1>{time}</h1>
<input />
</>
);
}
이 마지막 단계에서 React는 <h1>의 콘텐츠만 새로운 시간으로 업데이트하기 때문에 작동합니다. <input>이 지난번과 같은 위치의 JSX에 나타나는 것을 확인하므로 React는 <input>이나 그 값을 건드리지 않습니다!
에필로그: 브라우저 페인트
렌더링이 완료되고 React가 DOM을 업데이트하면 브라우저는 화면을 다시 칠합니다. 이 프로세스를 "브라우저 렌더링"이라고 부르지만, 이 문서의 나머지 부분에서는 혼동을 피하기 위해 "페인팅"이라고 부를 것입니다.
요약
React 앱의 모든 화면 업데이트는 세 단계로 진행됩니다:
1. 트리거
2. 렌더
3. 커밋
Strict 모드를 사용하여 컴포넌트에서 실수를 찾을 수 있습니다.
렌더링 결과가 지난번과 동일한 경우 React는 DOM을 건드리지 않습니다.
발표요약)
1. 순수함수로 컴포넌트를 짜야한다.(중요). 같은입력, 같은출력.
2. 리액트의 화면 업데이트는 3단계로 진행된다. 트리거 > 렌더 > 커밋
3. 리액트는 렌더링 결과가 이전과 다른 부분만 변경한다.
'[STUDY] 스터디 > React Docs' 카테고리의 다른 글
React docs beta 한글 번역 : Queueing a Series of State Updates - 일련의 상태 업데이트 대기 (0) | 2023.03.01 |
---|---|
React docs beta 한글 번역 : State as a Snapshot - 스냅샷으로서의 상태 (0) | 2023.02.27 |
React docs beta 한글 번역 : state: A Component's Memory - 상태: 컴포넌트의 메모리 (0) | 2023.02.23 |
React docs beta 한글 번역 : Responding to Events - 이벤트에 응답하기 (0) | 2023.02.22 |
React docs beta 한글 번역 : Keeping Components Pure - 컴포넌트 순수성 유지 (0) | 2023.02.21 |