![[React] React Hook Flow - Mount 부터 Unmount까지](https://img1.daumcdn.net/thumb/R750x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdna%2Fb1uxfv%2FbtsMzPkCU69%2FAAAAAAAAAAAAAAAAAAAAADzWZ8bEysq4koRpleCEUA8oisiNwhPI7rwl48RamqnE%2Fimg.png%3Fcredential%3DyqXZFxpELC7KVnFOS48ylbz2pIh7yKj8%26expires%3D1753973999%26allow_ip%3D%26allow_referer%3D%26signature%3DrM9Km8Hssev5l8pVaXNAs3C9WEc%253D)
React Hook Flow - Mount 부터 Unmount까지
이번 포스팅에서는 React Hook Flow Diagram을 통해 리액트 컴포넌트의 생명주기동안 훅이 언제 실행되는지 공부한 내용을 포스팅 해보려고 한다.
추가적으로 useEffect 와 useLayoutEffect를 비교하면서 언제 해당 훅을 사용하는게 좋을지도 같이 고민한 내용도 추가했다.
Mount, Update, Unmount?
- Mount: 컴포넌트가 페이지에 처음 렌더링 될 때를 의미
- Update: 부모 컴포넌트가 리렌더링 or state, props가 변경되어 리렌더링 될 때를 의미
- Unmount: 컴포넌트가 DOM에서 제거될 때를 의미
이제 React Hook Flow를 보면서 Mount, Update, Unmout 마다 어떠한 일이 일어나는지 알아보겠다.
1. Mount
1.1 Run Lazy Initializers
- useEffect, useReducer와 같이 상태 초기화가 필요한 훅들이 초기화 되는 로직이 실행되고.
1.2 Render
- 초기화 된 상태를 기반으로 첫 렌더링 작업이 실행된다. (JSX를 통한 가상 DOM 생성)
1.3 React updates DOM
+) 이 단계는 커밋 단계라고도 불린다 - 이전 Render 단계에서 계산된 가상 DOM을 실제 브라우저 DOM에 반영하는 단계입니다
- 이후 커밋단계에서 React는 DOM의 해당 변경사항들을 반영하게 되고 주의해야 할 점은 현재 React가 메인 스레드를 차지하고 있는 상태이기에 DOM에는 반영되었지만 브라우저에 렌더링되는 단계는 아직 아님
1.4 Run LayoutEffects
- 위 상태로 화면이 그려지기 전에 useLayoutEffect에 작성된 로직이 실행되게 되고
1.5. Browser paints screen
- 그 다음에 브라우저가 화면을 그리게 된다.
1.6 Run Effects
- 마지막으로 useEffect에 작성된 로직이 실행되게 된다.
2. Update
2.1 Render
- 변경된 상태를 기반으로 React가 렌더링을 실시한다.
!!) 주의 해야할 점은 useEffect, useReducer의 상태 초기화 로직은 update 단계에서는 실행되지 않음
2.2 React updates DOM
- 렌더링 결과 이전 트리와 변경사항이 있다면 DOM에 업데이트를 실행하게 된다.
2.3 Cleanup LayoutEffects
- cleanup useLayoutEffect가 있다면 먼저 실행
2.4 Run LayoutEffects
- 이어서 useLayoutEffect에 작성된 로직 실행
2.5 Browser paints screen
- 브라우저가 화면을 그리게 되고
2.6 Cleanup Effects
- cleanup useEffect가 있다면 먼저 실행
2.7 Run Effects
- 이어서 useEffect에 작성된 로직 실행
3. Unmount
3.1 Cleanup LayoutEffects
- useLayoutEffect의 훅 정리 함수가 실행되고
3.2 Cleanup Effects
- 이어서 useEffect의 훅 정리 함수가 실행된다.
컴포넌트의 생명주기 동안 훅이 언제 실행되는지 명확히 이해하는데 도움이 됐다면 좋겠다.
이제 앞선 내용을 바탕으로
useEffect VS useLayoutEffect 공부해보자
제목의 내용을 공부하기 전 왜 이러한 훅들을 사용하는지 알기 위해 하나의 개념을 이해하고 넘어가자
Side Effect (부수 효과) 란?
Side Effect란 함수나 컴포넌트가 실행될 때 외부 상태를 변경하거나 외부와 상호작용하는 모든 작업을 의미한다.
쉽게 말해, 어떤 작업의 결과로 예상치 못한 부수적인 효과라고 생각하면 된다.
예를 들어 함수의 내부에서 Math.random(), Date.now()를 사용하여 실행할 때마다 결과가 달라지는 것이
대표적인 side effect이다. 그 외에도 함수 내부에서 인자로 받지 않은 외부의 상태를 참조하거나
API로 서버의 데이터를 가져오는 등의 작업도 side effect에 포함된다.
useEffect와 useLayoutEffect 모두 컴포넌트에서 side effect가 있는 작업을 수행할 때 사용할 수 있다.
차이점
useEffect와 useLayoutEffect의 차이는 실행 시점과 동작 방식(동기/비동기)에 있다.
useEffect 훅은 컴포넌트 렌더링과 브라우저 페인팅이 끝난 후 비동기적으로 실행된다.
반면 useLayoutEffect 훅은 컴포넌트 렌더링 후 동기적으로 실행되어 작업이 완료되면 브라우저 페인팅이 시작된다.
useLayoutEffect의 실행 시점과 동작 방식은 componentDidMount, componentDidUpdate의 스케쥴링과 동일하다.
useEffect
1. 리액트 앱에서 렌더링이 트리거 됨 (dependency 수정됨)
2. 리액트가 컴포넌트를 렌더링함
3. 브라우저가 화면을 새로 그림
4. useEffect가 백그라운드에서 실행됨
5. useEffect 안에서 DOM 노드 ref를 조작함
6. 리렌더링, 이펙트가 DOM을 수정하는 동안 잠시 화면이 빔.
7. 브라우저가 화면을 새로 그림
useLayoutEffect
1. 리액트 앱에서 렌더링이 트리거 됨 (dependency 수정됨)
2. 리액트가 컴포넌트를 렌더링함
3. useLayoutEffect가 실행됨
4. useLayoutEffect가 끝나기를 기다림
5. 브라우저가 화면을 새로 그림
useLayoutEffect는 언제 사용해야 할까?
- 브라우저 페인트 전 DOM 조작, 혹은 레이아웃 정보를 읽어야 할 때
주요 사용 예시
1. 스크롤 위치 조정
2. 화면 깜빡이는 현상
3. DOM 요소의 크기/위치 측정
4. DOM 기반 애니메이션
그 외: ref가 업데이트 되기 전 이전 값 확인
const ref = useRef()
useEffect(() => {
ref.current = "some value"
})
// 다른 hook 등에서 다음 실행
useLayoutEffect(() => {
console.log(ref.current) // <-- 이전 값 로그
})
하나 궁금한 점
useLayoutEffect가 useEffect보다 먼저 실행되니까 Data Fetching을 useLayoutEffect를 이용하면 더 좋지 않을까?
하지만 여기에 문제가있다.
useLayoutEffect가 동기적으로 작동한다는 것이다.
useLayoutEffect가 실행되는 동안 브라우저 페인팅을 블로킹하면서 웹앱이 일시중지 되는데 이펙트 내부에 느린 작업이 있는 경우 그만큼 버벅임과 같은 이슈를 발생시킬 수 있기 때문이다.
즉, 더 빠른 Data Fetching에서 얻는 이점보다 브라우저 페인팅이 블로킹되면서 생기는 성능 이슈가 더 심각하다.
프론트엔드 공부일지 입니다.
포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!