웹 개발
useEffect 의존성 배열 체크리스트
useEffect에서 의존성 배열을 잘못 설정해서 발생하는 버그들을 미리 잡아내는 방법.
useEffect 버그는 타이밍에서 나온다
useEffect의 의존성 배열을 잘못 설정하면 정말 찾기 어려운 버그가 생긴다. "처음 로드할 때는 잘 나오는데 페이지를 왔다 갔다 하면 데이터가 없어져"같은 현상이 자주 생긴다.
가장 흔한 실수:
- 의존성 배열을 아예 빠뜨림 (매번 렌더링될 때마다 실행)
- 필요한 의존성을 빠뜨림 (이전 값으로 계속 실행)
- 오브젝트나 배열을 의존성으로 사용 (매번 새로 생성되어서 계속 실행)
확인해야 할 항목들
1. 의존성 배열이 있는가?
// 위험: 의존성 배열이 없으면 매번 실행
useEffect(() => {
fetch('/api/data').then(r => setData(r));
});
// 올바름
useEffect(() => {
fetch('/api/data').then(r => setData(r));
}, []);
2. 사용하는 변수를 모두 의존성에 포함했는가?
const userId = props.userId;
// 위험: userId를 쓰지만 의존성에 없음
useEffect(() => {
fetch(`/api/user/${userId}`).then(r => setUser(r));
}, []);
// 올바름
useEffect(() => {
fetch(`/api/user/${userId}`).then(r => setUser(r));
}, [userId]);
3. 오브젝트나 배열을 직접 의존성으로 쓰지 않았는가?
// 위험: options은 매번 새로 생성되어 무한 루프
const options = { sort: 'name' };
useEffect(() => {
fetchData(options);
}, [options]);
// 올바름: 필요한 값만 의존성에
useEffect(() => {
fetchData(options);
}, [options.sort]);
// 또는 useMemo로 options을 메모이제이션
const options = useMemo(() => ({ sort: 'name' }), []);
useEffect(() => {
fetchData(options);
}, [options]);
코드 리뷰 체크리스트
ESLint의 exhaustive-deps 규칙을 켜둔다.
{
"extends": ["react-app"],
"rules": {
"react-hooks/exhaustive-deps": "warn"
}
}
이 규칙이 경고하는 건 대부분 실제 버그일 가능성이 높다.
실전 팁
클린업 함수를 사용하자:
useEffect(() => {
const timer = setTimeout(() => {
// 작업
}, 1000);
return () => clearTimeout(timer);
}, []);
이렇게 하면 컴포넌트가 언마운트될 때나 의존성이 바뀔 때 타이머를 정리한다.
비동기 작업은 함수로 분리:
useEffect(() => {
let mounted = true;
const fetchData = async () => {
const response = await fetch('/api/data');
if (mounted) {
setData(await response.json());
}
};
fetchData();
return () => {
mounted = false;
};
}, []);
컴포넌트가 언마운트되면 setState를 하지 않도록 한다.
배포 전 확인
- ESLint 경고를 모두 해결했는가?
- 페이지를 왔다 갔다 했을 때 데이터가 제대로 로드되는가?
- 콘솔에 경고나 에러가 없는가?
- 개발자 도구에서 Strict Mode를 켜도 정상 작동하는가?