[React] 컴포넌트 렌더링 과정 정리(useLayoutEffect vs. useEffect)
# React 컴포넌트 재렌더링 케이스
- React 컴포넌트는 아래의 2가지 상황에서 재렌더링(Re-rendering) 된다.
1. 내부 상태값(state)이나 중앙 상태값(redux store 등)이 변경되는 경우
2. 부모 컴포넌트가 재렌더링되는 경우, 자식 컴포넌트도 재렌더링.
- 컴포넌트가 렌더링되면, 해당 컴포넌트 함수가 호출되어 화면을 다시 그리는데, 이 과정을 정리해둔다.
# React 마운트 과정
1. 함수 컴포넌트 호출
2. 구현부 실행
- props 취득, hook 실행, 내부 변수 및 함수 생성
- 단, hook 에 등록해둔 상태값, 부수함수 효과 등은 별도 메모리에 저장되어 관리된다.
3. return 실행
- 렌더링 시작
4. 렌더 단계(Render Phase)
- 가상DOM을 생성한다.
5. 커밋 단계(Commit Phase)
- 실제 DOM에 반영한다.
6. useLayoutEffect
- 브라우저가 화면에 Paint 하기 전에, useLayoutEffect에 등록해둔 effect(부수효과함수)가 '동기'로 실행된다.
- 이 때, state, redux store 등의 변경이 있다면 한번 더 재렌더링 된다.
7. Paint
- 브라우저가 실제 DOM을 화면에 그린다. didMount가 완료된다.
8. useEffect
- Mount되어 화면이 그려진 직후, useEffect에 등록해둔 effect(부수효과함수)가 '비동기'로 실행된다.
# React 재렌더링 과정
1. 함수 컴포넌트 재호출
2. 구현부 실행
- props 취득, hook 실행, 내부 변수 및 함수 재생성
- 단, 각 hook의 특성에 따라 기존에 메모리에 저장한 내용을 적절히 활용한다.
3. return 실행
- 렌더링 시작
4. 렌더 단계(Render Phase)
- 새로운 가상DOM 생성 후, 이전 가상 DOM과 비교하여, 달라진 부분을 탐색하고, 실제 DOM에 반영할 부분을 결정한다.
5. 커밋 단계(Commit Phase)
- 달라진 부분만 실제 DOM에 반영한다.
6. useLayoutEffect
- 브라우저가 화면에 Paint 하기 전에, useLayoutEffect에 등록해둔 effect(부수효과함수)가 '동기'로 실행된다.
- 이 때, state, redux store 등의 변경이 있다면 한번 더 재렌더링 된다.
7. Paint
- 브라우저가 실제 DOM을 화면에 그린다. didUpdate가 완료된다.
8. useEffect
- update되어 화면이 그려진 직후, useEffect에 등록해둔 effect(부수효과함수)가 '비동기'로 실행된다.
- effect에 return부분이 있다면, 구현부보다 먼저 실행된다.
# useEffect와 useLayoutEffect
1. useEffect( 비동기effect )
- useEffect에 등록된 effect는 화면이 그려진 직후, "비동기"로 실행된다.
1) (재)렌더링 시작
2) 실제DOM에 반영
3) 화면에 Paint(didMount || didUpdate)
4) effect 함수 비동기 실행
2. useLayoutEffect ( 동기effect )
- useLayoutEffect에 등록된 effect는 실제DOM 반영 후, 화면이 그려지기 직전, "동기"로 실행된다.
1) (재)렌더링 시작
2) 실제DOM에 반영
3) effect 함수 동기 실행 : state 등 변경시, 다시 재렌더링 된다.
4) 화면에 Paint(didMount || didUpdate)
* 참고: effect "구현부"와 "return 함수" 실행 순서
- didMount => effect "구현부"만 실행
- didUpdate => effect "return 함수" 실행 => effect "구현부" 실행
- willUnmount => effect "return 함수"만 실행
# Redux store 변경시, 자동으로 재렌더링 되는 이유.
리덕스 스토어의 경우,
<Provider store={store}> 로 컴포넌트들을 감싸주면,
스토어 상태가 변경될 때마다 이를 참조하는 컴포넌트들이 재렌더링 되도록
react-redux 라이브러리가 자동으로 컴포넌트들의 렌더 함수들을 subscribe 처리 해준다.
(개발자가 직접 subscribe 처리를 할 수도 있다.)
# 자식 컴포넌트의 불필요한 재렌더링 막는 방법
- 부모 컴포넌트가 재렌더링되면, 자식 컴포넌트들도 모두 자동으로 재렌더링된다.
- React.memo 를 사용하면, 자식이 전달받는 props가 변경되었을 때만 재렌더링 되도록 최적화할 수 있다.
# 재렌더링시, 불필요한 지역변수/함수 재생성 막는 방법
- 컴포넌트가 재렌더링되면, 해당 함수 컴포넌트가 재호출되면서 내부 변수와 함수가 다시 새로 생성된다.
- useMemo와 useCallback 훅을 사용하면, 최초 한번 생성 후 재사용 가능하다.
- 훅 사용시, 의존성배열을 추가하여 재생성 여부를 결정할 수도 있다.