React 리렌더링 조건과 렌더링 과정

개미·2023년 11월 10일
0
post-thumbnail

프로젝트를 반년 간 했지만 기능을 개발하는 것에 급해서 리액트의 작동원리를 제대로 모르고 있다는 생각이 들었다. 이번 기회에 한번 정리를 하고자 한다. (내가 헷갈렸던 것만 모아모아 정리)

리액트 리렌더링 조건

  1. state 변경이 있을 경우
  2. 새로운 props가 들어올 경우
  3. 기존 props가 업데이트될 경우
  4. 부모 컴포넌트가 리렌더링될 경우
    • 이때 자식 컴포넌트의 정의는 부모 컴포넌트의 JSX 안에서 사용된 모든 컴포넌트들이다.
    • 하지만, props, children으로 받은 컴포넌트는 자식 컴포넌트가 아니다.

props, children으로 받은 컴포넌트는 자식 컴포넌트가 아닌 점을 이용하여 리액트 렌더링 최적화를 해줄 수 있다는 점!

렌더링 과정?

리액트는 리렌더링을 할 때 변경된 부분만 리렌더링한다는 정도만 알고 있었다. 하지만 렌더링의 과정에는 리액트 렌더링과 브라우저 렌더링으로 구분된다.

리액트 렌더링

Step 1. Trigger a render

렌더링을 하는 두가지 이유가 있다.

  1. 가장 처음 렌더링
  • createRoot를 부른다.
  1. 리렌더링 조건이 발생한 경우
  • 렌더링이 자동으로 대기된다.

Step2. React renders your components

  1. 가장 처음 렌더링
  • root 컴포넌트를 호출한다
  • DOM node를 생성한다
  1. 리렌더링 조건이 발생한 경우
  • 업데이트 된 컴포넌트를 호출한다.
  • 변경 된 부분만 계산을 하고 다른 아무런 작업을 하지 않는다.

⇒ 만약 자식 컴포넌트가 존재한다면 recursive하게 계속 호출

Step3. React commits changes to the DOM

리액트는 DOM을 수정한다.

  1. 가장 처음 렌더링
  • appendChild() DOM API을 사용하여 작성한 모든 DOM Node를 화면에 표시한다.
  1. 리렌더링 조건이 발생한 경우
  • 이전 스텝에서 계산한 것을 바탕으로 최소한으로 필요한 작업을 이미 존재하는 최신 DOM에 하게 된다.
  • 이 과정에서 fiber (전 Virtual DOM)를 구성 및 업데이트 하고, 재조정(reconciliation) 과정을 거쳐서 변경된 부분만 DOM에 업데이트하게 되는 것이다.

DOM이 왜 필요한가?
DOM은 브라우저 상에서 자바스크립트가 HTML 문서를 조작하기 위한 API
자바스크립트가 직접 HTML을 동적으로 바꾸는데에는 한계가 있으니 JS가 조작하기 편하게 DOM이라는 형태로 바꾸어 놓은 것

Virtual Dom의 기본적인 원리
1. real dom으로 부터 virtual dom을 만든다 (virtual dom은 메모리 상에 존재하는 하나의 객체이다)
2. 변화가 생기면 새로운 버전의 virtual dom을 만든다
3. 구 버전의 virtual dom과 새로운 버전의 virtual dom을 비교한다
4. 차이가 있으면 차이점을 real dom에 적용한다

왜 React는 Virtual Dom을 만들었는가
리액트는 상태가 변하면 관찰자에게 알림을 보내주는 방식이다. 효율적이지만, 전체를 렌더링시키기 때문에 비용이 커진다. 그래서 나온 것이 virtual dom이다.
브라우저를 렌더링 시키는 비용 vs 객체를 새로 만드는 비용
후자가 더 저렴하기 때문이다.

react fiber이 나온 이유?
virtual dom은 구 버전의 virtual dom과 새로운 virtual dom을 비교할 때 재귀적으로 비교한다. 즉 한번 시작하면 끝날 때까지 순회를 무조건 해야한다. 이때 이 시간이 길어지면 프레임 드롭이 발생하고 유저 이벤트에 즉각적으로 대응하는 것이 어려워진다.
따라서 react fiber는 이 문제를 해결한다.
1. 작업을 멈추고, 나중에 다시 시작한다
2. 다양한 종류의 작업에 따라 우선순위를 부여한다
3. 더 이상 필요하지 않은 작업물이면 버릴 수 있다.

Next

이 세가지 스텝이 끝나고 DOM이 업데이트되면 브라우저 렌더링이 시작되게 된다.

브라우저 렌더링은 예전에 정리해두었다. https://velog.io/@coddingyun/브라우저-렌더링과-최적화

리액트 렌더링 최적화

state나 props가 변경되면 리렌더링이 발생한다.
리렌더링할 때, 바뀌지 않은 props를 자식 컴포넌트에게 전달해주어도 해당 자식 컴포넌트는 리렌더링 된다.
그 이유는, 함수형 컴포넌트가 다시 호출될때 다시 모든 것이 호출되기 때문에, 바뀐 것으로 인식되는 것이다.
어떻게 렌더링을 최적화할 수 있을까?

useCallback

  • 함수의 메모이제이션을 제공하는 리액트 훅
  • 의존성 배열이 변하지 않는 이상 리렌더링 될 때마다 변수에 같은 함수가 할당 된다.
  • 하지만.. 리액트 렌더링 과정의 step 3만 하지 않게 된다. step1, 2는 그대로 진행한다.

React.memo

  • 전달 받은 props가 이전의 props와 동일하면 컴포넌트의 리렌더링을 막아주고, 마지막으로 리렌더링된 결과를 재사용하는 고차 컴포넌트이다.
  • 리액트 렌더링 과정의 step1, 2 또한 막아준다.
  • 하지만 객체를 props로 전달하는 경우에는 리렌더링이 된다.

왜?
객체가 매번 새로 생성 → 매번 다른 참조값을 가진 객체를 props로 전달 → React.memo 작동 x

useMemo

  • 값에 대한 메모이제이션을 제공하는 리액트 훅
  • 의존성 배열에 들어있는 값이 변하지 않는 이상 리렌더링 될 때마다 같은 값을 반환한다.

더하여 알게 된 사실

const app = () => {
	console.log('yes')
	return (
		<div>
		</div>
	)
}
  • 위의 코드에서 console.log(’yes’)는 컴포넌트가 리렌더링 될때마다 출력된다. 즉, 부분적으로 리렌더링된다는 의미는 return 반환 값인 JSX에 해당되는 이야기이다. return 위의 부분은 리렌더링 될때마다 모두 실행된다. 그래서 실행하고 싶지 않는 코드들은 useEffect를 사용하는 것이다.
  • 위처럼 리렌더링을 확인해보려고 콘솔을 찍어보다가 애니메이션도 리렌더링된다는 사실을 알았다.

참고

https://react.dev/learn/render-and-commit
https://velog.io/@mogulist/understanding-react-rerender-easily
https://velog.io/@yesbb/virtual-dom의-성능이-더-좋은이유#4render-함수-내부에서-일어나는-일--react-fiber
https://www.youtube.com/watch?v=1YAWshEGU6g

profile
개발자

0개의 댓글