이 글은 단순히 리액트 공식 문서를 읽고 베꼈을 뿐이다!! 학습 목적이라면 공식 문서를 보는 걸 추천한다.

공식 문서
https://react.dev/learn/lifecycle-of-reactive-effects
Lifecycle of Reactive Effects – React
The library for web and native user interfaces
react.dev
이펙트의 생명 주기
- 모든 리액트 컴포넌트는 같은 생명 주기를 가짐: mount, update, unmount
- '컴포넌트'의 생명 주기는 맞는데 '이펙트'는 이거랑 좀 다름
- 이펙트는 컴포넌트의 생명 주기와 독립적임: 이펙트는 외부 시스템과의 동기화를 위한 것
useEffect
는 언제, 그리고 어떻게 동기화를 시작하고 끝낼 것인지를 나타냄: 단순히 컴포넌트의 mount, unmount에만 하는 게 아님
동기화를 여러 번 해야 하는 이유
- 이미 있는 컴포넌트를 update할 때 기존의 이펙트 결과와 현재 UI가 일치하지 않을 수 있기 때문
리액트는 이펙트를 어떻게 다시 동기화할까?
- 컴포넌트 mount
- 이펙트를 실행해 동기화 시작
- 컴포넌트 update
- 이전에 실행된 이펙트의 cleanup 함수를 써서 동기화를 멈춤
- 새로운 정보들을 가지고 이번 이펙트를 실행해 동기화 시작
- 컴포넌트 unmount
- 이전에 실행된 이펙트의 cleanup 함수를 써서 동기화를 멈춤
이펙트 감수성으로 생각하기
- 컴포넌트의 관점에서 보면 이펙트는 컴포넌트의 렌더 직후, unmount 직전과 같은 컴포넌트의 특정 생명 주기에 실행되는 콜백이라고 생각할 수 있음
- 하지만 그런 관점보다는 이펙트의 관점에서 보는 게 나음
- 하나의 시작-종료 주기에만 집중하기
- 컴포넌트의 mount, update, unmount는 신경쓰지 말고 어떻게 동기화를 시작하고 끝낼지만 잘 표현하면 됨
- 이 관점으로 코드를 짜면 언제든지 동기화를 시작하고 끝낼 수 있는 말랑말랑한 코드를 만들 수 있음
리액트는 어떻게 이펙트의 동기화 주기를 검증할까?
- 개발 단계의 Strict Mode에서 컴포넌트를 두 번 mount하면서 확인함
- 강제적으로 시작-종료 주기를 한 번은 겪도록 해서 잠재적인 문제를 볼 수 있게 도와줌
- cleanup 함수를 잘 만들었다면 아무 문제 없을 것
리액트는 어떻게 동기화를 다시 해야 한다는 걸 알까?
- 내가 코드에 의존성 배열 넣었으니까 ㅇㅇ
- 배열의 각 값이 같은지는
Object.is
로 비교함
각 이펙트는 서로 다른 동기화 과정을 나타낸다
- '하나의 함수는 하나의 역할만' 처럼 하나의 이펙트는 하나의 역할만
- 의존성 배열이 같아서 같은 시기에 동작하는 로직이라도 역할이 다르면 분리해서 서로 다른 이펙트에 넣기
이펙트는 반응형(리액트스러운) 값에 반응한다
- 반응형 값? 리렌더링 때 바뀔 수 있는 값들: props, 상태, 컴포넌트 내부에서 정의된 변수 등 렌더링 도중에 계산되어 리액트의 흐름에 탑승하는 친구들
- 렌더링과 아예 상관없이 돌아가는 외부 상수같은 값들은 이펙트의 의존성 배열에 넣을 필요 없음
컴포넌트 내부에 선언된 모든 변수는 리액트스럽다
- props나 상태는 당연히 반응형 값들임
- 얘네로부터 계산되어 나오는 값들도 반응형 값들임
- 모든 반응형 값들은 리렌더링 시 값이 바뀔 수 있으므로 이펙트에서 사용할 경우 의존성 배열에 넣어야 함
전역 변수나 변형 가능한(mutable) 값은 의존성 배열에 넣을 수 있을까?
- ㄴㄴ
location.pathname
처럼 변형 가능한 값- 이걸 바꾼다고 해도 리액트가 관리하는 상태가 아님 -> 리렌더링이 일어나지 않음
- 렌더링 도중에 변형 가능한 값을 읽는 것 자체가 렌더링 과정의 순수성을 해침
- 외부의 변형 가능한 값을 읽거나 구독하려면
useSyncExternalStore
를 쓰는 걸 권장
ref.current
와 그 내부의 값useRef
로부터 반환되는ref
객체 자체는 의존성 배열에 넣을 수 있음- 하지만 그 내부의 값은 안 됨: 위와 같은 이유
- 애초에
ref
를 쓰는 이유가 리렌더링을 유발하지 않고 어떤 정보를 기억하기 위함임
이펙트와 빈 의존성 배열
- 컴포넌트의 관점: 이펙트는 컴포넌트 mount에 동기화 시작, unmount에 동기화 종료
- 이펙트의 관점: 컴포넌트 생명 주기는 생각할 필요 없고 이펙트 내부 로직을 고칠 필요도 없음. 그냥 이펙트 내부에서 쓰는 값이 반응형 값이고 거기에 반응하기 원한다면 의존성 배열에 넣으면 그만임
리액트는 이펙트에서 쓰는 모든 반응형 값들이 의존성 배열에 잘 들어있는지 확인한다
- 리액트 전용 linter를 설정했으면 에러가 나올 것
- 몇몇 값은 리액트스럽지 않다는 걸 이미 알고 있음:
useRef
의ref
객체,useState
의 상태 설정 함수
다시 동기화하기 싫은데 어떡하죠?
- linter에게 의존성 배열의 값들이 리액트스럽지 않음을 증명하면 됨
- 아예 컴포넌트 밖의 상수로 빼기
- 아예 이펙트 안의 상수로 넣기
- 의존성 배열은 내 맘대로 정할 수 없음: 의존성 배열은 이펙트 내부에서 사용하는 모든 반응형 값을 담아야 함
- 하나의 이펙트가 하나의 동기화 작업을 하는지 살펴보기: 동기화를 안 하면 불필요한 이펙트, 여러 개를 하면 이펙트 쪼개기
- 새로운 props나 상태값은 읽되 거기에 반응하거나 다시 동기화하길 원하지 않으면? 반응형인 부분과 그렇지 않은 부분을 분리해보기 (다음 장에 자세히)
- 렌더링 도중에 선언하는 객체나 함수를 의존성 배열에 넣는 건 피하기: 선언 -> 새로운 참조 -> 의존성 배열 내부의 값이 변함 -> 이펙트 실행
'리액트 공식 문서 읽기' 카테고리의 다른 글
29화: Removing Effect Dependencies (0) | 2023.06.22 |
---|---|
28화: Separating Events from Effects (0) | 2023.06.22 |
26화: You Might Not Need an Effect (0) | 2023.06.22 |
25화: Synchronizing with Effects (0) | 2023.06.20 |
24화: Manipulating the DOM with Refs (0) | 2023.06.19 |