컴포넌트가 화면에 표시되기 전에 React로 렌더링이 진행됩니다. 이 프로세스의 단계를 이해하면 코드가 어떻게 실행되고 동작을 설명하는지 생각하는데 도움이 됩니다.
UI를 요청하고 제공하는 프로세스는 세 단계로 구성됩니다.
- 렌더링 트리거
- 컴포넌트 렌더링
- DOM에 커밋
컴포넌트가 렌더링되는 데는 두 가지 이유가 있습니다.
- 컴포넌트의 초기 렌더링
- 컴포넌트(또는 상위 컴포넌트 중 하나)의 상태 업데이트
앱이 시작되면 초기 렌더를 트리거해야 합니다. 프레임워크와 샌드박스는 때때로 이 코드를 숨기지만 대상 DOM 노드로 createRoot를 호출한 다음 구성 요소로 render 메서드를 호출하여 수행합니다
// index.js
import Image from './Image.js';
import { createRoot } from 'react-dom/client';
const root = createRoot(document.getElementById('root'))
root.render(<Image />);
// image.js
export default 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"
/>
);
}
컴포넌트가 처음 렌더링되면 해당 set 함수를 통해 상태를 업데이트하여 추가 렌더링을 트리거할 수 있습니다. 컴포넌트의 상태를 업데이트하면 렌더링이 자동으로 대기열에 추가됩니다.
렌더링을 트리거한 후 React는 컴포넌트를 호출하여 화면에 표시할 내용을 파악합니다. “렌더링”은 React가 컴포넌트를 호출하는 것입니다.
- 초기 렌더링 시 React는 루트 컴포넌트를 호출합니다.
- 리렌더링의 경우 React는 상태 업데이트가 렌더링을 트리거한 함수 컴포넌트를 호출합니다.
해당 프로세스는 재귀적입니다. 업데이트된 컴포넌트가 다른 컴포넌트를 반환하면 React는 해당 컴포넌트를 다음에 렌더링하고 해당 컴포넌트도 무언가를 반환하면 해당 컴포넌트를 다음에 렌더링하는 식입니다. 더 이상 중첩된 컴포넌트가 없고 React가 화면에 표시해야 할 내용을 정확히 알 때까지 프로세스가 진행됩니다.
다음 예에서는 React는 Gallery() 및 Image() 를 여러 번 호출합니다.
// index.js
import Gallery from './Gallery.js';
import { createRoot } from 'react-dom/client';
const root = createRoot(document.getElementById('root'))
root.render(<Gallery />);
// Gallery.js
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는 이전 렌더링 이후 변경된 속성이 있는지 계산합니다. 다음 단계인 커밋 단계까지 해당 정보로 아무 작업도 수행하지 않습니다.
렌더링은 항상 순수한 계산이어야 합니다.
- 동일 입력, 동일 출력. 동일한 입력이 주어지면 컴포넌트는 항상 동일한 JSX를 반환해야 합니다.
- 자신의 일만 생각합니다. 렌더링 전에 존재했던 객체나 변수를 변경해서는 안됩니다.
그렇지 않으면 코드베이스가 복잡해지면서 혼란스러운 버그와 예측할 수 없는 동작이 발생할 수 있습니다. “Strict Mode”로 개발할 때 React는 각 컴포넌트의 함수를 두 번 호출하여 불순한 함수로 인한 실수를 표면화하는 데 도움이 될 수 있습니다.
업데이트된 컴포넌트 내에 중첩된 모든 컴포넌트를 렌더링하는 기본 동작은 업데이트된 컴포넌트가 트리에서 매우 상위 컴포넌트인 경우 성능이 좋지 않습니다.
컴포넌트를 렌더링한 후 React는 DOM을 수정합니다.
- 초기 렌더링의 경우 React는
appendChild()DOM API를 사용하여 생성된 모든 노드를 화면에 배치합니다.- 재렌더링의 경우 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을 업데이트한 후 브라우저는 화면을 다시 칠합니다. 이 프로세스를 “브라우저 렌더링”이라고 하지만 문서 전체에서 혼동을 파하기 위해 “페인팅”이라고 칭하겠습니다.