렌더링이란 우리가 코드를 통해 작성한 DOM 객체가 화면에 그려지는 것을 의미한다. React 공식 문서를 통해 React 에서 렌더링이 발생하는 조건과, 렌더링 주기에 대해 알아보자.
배울 것
- 컴포넌트는 언제, 어떻게 렌더링 되는지
- 컴포넌트가 스크린에 나타나기까지의 과정
- 렌더링이 DOM 업데이트를 매번 생성하지는 않는 이유
컴포넌트를 재료들로 주방에서 요리하는 음식이라고 생각해보자. 이 경우 리액트는 고객의 요구를 전달해주고 음식을 내놓는 웨이터와 같다. 리액트는 컴포넌트를 요리하기 위해 다음과 같은 일을 한다.
컴포넌트는 다음 이유로 렌더링 된다.
createRoot 메서드를 사용하여 DOM 노드를 가져오고, render 메서드로 컴포넌트를 호출하면 렌더링된다.import Image from './Image.js';
import { createRoot } from 'react-dom/client';
const root = createRoot(document.querySelector('root'));
root.render(<Image />);set 메서드로 state 를 업데이트 하여 렌더링을 추가로 수행할 수 있다. 컴포넌트의 상태를 업데이트하면 렌더링이 자동으로 대기열에 추가된다.컴포넌트가 렌더링되면 React 는 컴포넌트의 render 메서드를 호출하여 가상 DOM 을 생성한다.
Rendering 이란 리액트가 컴포넌트를 호출하는 것을 의미한다.
이 과정은 재귀적으로 이뤄진다. 업데이트된 컴포넌트가 다른 컴포넌트를 반환하고 있으면 해당 컴포넌트를 렌더링 하고, 또 그 컴포넌트가 다른 컴포넌트를 반환하면 해당 컴포넌트를 렌더링하는 것을 반복한다.
// 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"
/>
);
}
<section> <h1> 3개의 <img> 를 DOM 노드로 생성한다.렌더링 이후 생성한 가상 DOM 과 이전의 DOM 을 비교하여 변경사항을 확인한다.
appendChild() 메서드를 이용하여 DOM 노드를 스크린에 생성한다.리액트가 DOM 을 업데이트 하면, 브라우저는 스크린에 repaint 를 한다. 이 작업을 browser rendering 으로 불리기도 하지만 React 문서에서는 혼란을 피하기 위해 painting 이라는 용어를 사용할 것이다.
클래스 컴포넌트의 라이프 사이클과 함수 컴포넌트의 라이프 사이클에 대해서 알아보자.
클래스형 컴포넌트에서는 컴포넌트의 생명주기를 직접 컨트롤 할 수 있는 다음과 같은 메서드들을 제공해주었다.
componentWillMount → (16.3v 이후) constructor getDerivedStateFromPropscomponentDidMountcomponentWillReceiveProps → (16.3v 이후) getDerivedStateFromPropsshouldComponentUpdatecomponentWillUpdate → (16.3v 이후) getSnapshotBeforeUpdatecomponentDidUpdatecomponentWillUnmount이제 각 키워드에 대한 정의를 하나씩 보며 라이프 사이클에 대해 이해해보자.
16.3v 이전
컴포넌트가 처음 실행되는 것을 의미한다. 컴포넌트가 처음 실행되면 context, defaultProps, state 를 지정하게 된다. 이후 componentWillMount 메서드를 호출한다.
이후 render 로 컴포넌트를 DOM 에 연결하고, mount 가 완료되고 난 뒤에 componentDidMount 가 호출된다.
📝 componentWillMount → componentDidMount
constructor 가 가장 먼저 실행된다. 컴포넌트에 필요한 state 및 초기값 등을 바인딩 하기 위해 사용한다.getDerivedStateFromProps 메서드로 부모에게서 받은 prop 을 처음으로 초기화시킨다.render 함수를 호출하여 처음 렌더링이 발생한다.componentDidMount 메서드가 호출되고 렌더링이 완료된 뒤 외부 자원 요청 등의 처리를 한다.마운트가 되고 있는 도중에는 props 나 state 를 바꿀 수 없고, DOM 이 렌더링 되기 이전의 시점이기 때문에 DOM 에도 접근할 수 없다.
prop 및 state 가 변경되어 컴포넌트가 업데이트 될 때의 과정이다.
16.3v 이전
업데이트가 되기 전, 업데이트가 발생하는 것을 감지하고 componentWillReceiveProps 메서드가 호출되어 prop 을 업데이트 한다.
이후 shouldComponentUpdate, componentWilUpdate 가 차례대로 호출된다. 각 메서드는 업데이트할 prop 에 대한 정보를 가지고 있다. componentWillMount 와 마찬가지로 componentWillUpdate 도중엔 state 를 바꿀 수 없다.
업데이트가 완료되어 리렌더링이 되고 난 이후에 componentDidUpdate 가 실행된다.
📝 props 이 업데이트 되는 경우 : componentWillReceiveProps → shouldComponentUpdate → componentWillUpdate → render → componentDidUpdate
📝 state 가 업데이트 되는 경우 : shouldComponentUpdate, componentWillUpdate, render, componentDidUpdate
getDerivedStateFromProps 가 호출되어 변경된 prop 을 가지고 온다.componentShouldUpdate 메서드가 호출되는데, 아직 렌더링이 일어나기 전이라 false 를 반환함으로써 렌더링을 취소할 수 있다. 주로 성능 최적화를 위해 사용한다.render 함수를 호출하여 리렌더링을 발생시킨다.getSnapShotBeforeUpdate 를 통해 이전 DOM 의 정보를 가져올 수 있다. 해당 메서드의 반환값은 componentDidUpdate 의 세번째인자로 전달된다. 이를 통해 업데이트 전후의 차이를 계산할 수 있다. (스크롤 위치를 기억하는 등)componentDidUpdate 는 이전의 prop 정보와 새로운 prop 에 대한 정보를 둘 다 가지고 있다. 리렌더링이 완료된 다음 외부 자원 요청등의 처리를 할 수 있다.컴포넌트가 제거되어 더 이상 사용하지 않을 때 발생하는 이벤트이다.
componentWillUnmount 가 실행된다.
클래스형 컴포넌트는 라이프 사이클의 중심이 컴포넌트이다. 하지만 함수형 컴포넌트에선 특정 데이터에 대하여 라이프 사이클이 진행된다. 바로 useEffect 를 사용해서!
useEffect(() => {
console.log('userState is changed!');
}, [userState]);
userState is changed! 라는 문장은 컴포넌트가 첫 렌더링 될 때, 그리고 userState 가 변경될 때 마다 실행된다.
즉, componentDidMount 와 componentDidUpdate 가 합쳐진 셈이 된다. 또한 return 문을 사용하여 컴포넌트가 언마운트 될 때, (componentWillUnmount) 를 처리할 수도 있다.
useEffect(() => {
return () => {
console.log('userState will change');
};
}, [hidden]);
함수형 컴포넌트에서는 특정 데이터에 대해 라이프 사이클이 진행되므로, 데이터가 언마운트될 때 userState will change 라는 콘솔이 출력된다.
componentWillMount 와 componentWillUpdate 는 useEffect 에선 사용하지 않는다.
❓ 그렇다면 componentDidMount 와 componentDidUpdate 중 하나만 사용하고 싶다면요?
componentDidMount
useEffect(() => {
console.log('component did Mount');
}, []);
useRef 를 사용하여 반응성은 가지고 있지 않지만, 렌더링이 되어도 초기화되지 않는 변수를 생성한다.componentDidUpdate
조건문과 useRef 를 사용하여 데이터가 업데이트 된 경우를 처리할 수 있다.