컴포넌트를 화면에 표시하기 이전에 React에서 렌더링을 해야 합니다. 해당 과정의 단계를 이해하면 코드가 어떻게 실행되는지 이해할 수 있고 리액트 렌더링 동작에 관해 설명하는데 도움이 됩니다.
학습 내용
- React에서 렌더링의 의미
- React가 컴포넌트를 언제, 왜 렌더링 하는지
- 화면에 컴포넌트를 표시하는 단계
- 렌더링이 항상 DOM 업데이트를 하지 않는 이유
주방에서 요리사가 컴포넌트를 재료로 맛있는 요리를 한다고 상상해보세요. 이 시나리오에서 React는 고객들의 요청을 받고 주문을 가져오는 웨이터입니다. 이 과정에는 UI를 요청하고 제공하는 세 가지 단계가 있습니다.
- 렌더링 트리거 (손님의 주문을 주방으로 전달)
- 컴포넌트 렌더링 (주방에서 주문 준비하기)
- DOM에 커밋 (테이블에 주문한 요리 내놓기)
Illustrated by Rachel Lee Nabors
1단계: 렌더링 트리거
컴포넌트 렌더링이 일어나는 데에는 두 가지 이유가 있습니다.
- 컴포넌트의 초기 렌더링인 경우
- 컴포넌트의 state가 업데이트된 경우
초기 렌더링
앱을 시작할 때 초기 렌더링을 트리거해야 합니다. 프레임워크와 샌드박스는 때때로 이 코드를 숨기곤 하지만, 대상 DOM 노드와 함께 createRoot
를 호출한 다음 해당 컴포넌트로 render
메서드를 호출하면 이 작업이 완료됩니다.
import Image from './Image.js'; import { createRoot } from 'react-dom/client'; const root = createRoot(document.getElementById('root')) root.render(<Image />);
root.render()
호출을 주석 처리하고 컴포넌트가 사라지는 것을 확인하세요!
State 업데이트 시 리렌더링
컴포넌트가 처음으로 렌더링 된 후에는 set
함수를 통해 상태를 업데이트하여 추가적인 렌더링을 트리거할 수 있습니다. 컴포넌트의 상태를 업데이트하면 자동으로 렌더링 대기열에 추가됩니다. (이것은 레스토랑의 손님이 첫 주문 이후에 갈증이나 배고픔의 상태에 따라 차, 디저트 등의 메뉴를 주문하는 것으로 상상해 볼 수 있습니다.)
Illustrated by Rachel Lee Nabors
2단계: React 컴포넌트 렌더링
렌더링을 트리거한 후 React는 컴포넌트를 호출하여 화면에 표시할 내용을 파악합니다. “렌더링”은 React에서 컴포넌트를 호출하는 것입니다.
- 초기 렌더링에서 React는 루트 컴포넌트를 호출합니다.
- 이후 렌더링에서 React는 state 업데이트가 일어나 렌더링을 트리거한 컴포넌트를 호출합니다.
재귀적 단계: 업데이트된 컴포넌트가 다른 컴포넌트를 반환하면 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>
그리고 3개의<img>
태그에 대한 DOM 노드를 생성합니다. - 리렌더링하는 동안 React는 이전 렌더링 이후 변경된 속성을 계산합니다. 다음 단계인 커밋 단계까지는 해당 정보로 아무런 작업도 수행하지 않습니다.
Deep Dive
업데이트된 컴포넌트 내에 중첩된 모든 컴포넌트를 렌더링하는 기본 동작은 업데이트된 컴포넌트가 트리에서 매우 높은 곳에 있는 경우 성능 최적화되지 않습니다. 성능 문제가 발생하는 경우 성능 섹션에 설명된 몇 가지 옵트인 방식으로 문제를 해결 할 수 있습니다. 성급하게 최적화하지 마세요!
3단계: React가 DOM에 변경사항을 커밋
컴포넌트를 렌더링(호출)한 후 React는 DOM을 수정합니다.
- 초기 렌더링의 경우 React는
appendChild()
DOM API를 사용하여 생성한 모든 DOM 노드를 화면에 표시합니다. - 리렌더링의 경우 React는 필요한 최소한의 작업(렌더링하는 동안 계산된 것!)을 적용하여 DOM이 최신 렌더링 출력과 일치하도록 합니다.
React는 렌더링 간에 차이가 있는 경우에만 DOM 노드를 변경합니다. 예를 들어 매초 부모로부터 전달된 다른 props로 다시 렌더링하는 컴포넌트가 있습니다. <input>
에 텍스트를 입력하여 value
를 업데이트 하지만 컴포넌트가 리렌더링될 때 텍스트가 사라지지 않습니다.
export default function Clock({ time }) { return ( <> <h1>{time}</h1> <input /> </> ); }
마지막 단계에서 React가 <h1>
의 내용만 새로운 time
으로 업데이트하기 때문입니다. <input>
이 JSX에서 이전과 같은 위치로 확인되므로 React는 <input>
또는 value
를 건드리지 않습니다!
에필로그: 브라우저 페인트
렌더링이 완료되고 React가 DOM을 업데이트한 후 브라우저는 화면을 다시 그립니다. 이 단계를 “브라우저 렌더링”이라고 하지만 이 문서의 나머지 부분에서 혼동을 피하고자 “페인팅”이라고 부를 것입니다.
Illustrated by Rachel Lee Nabors