1 useEffect란
useEffect는 함수형 컴포넌트에서 **부수 효과(side effect)**를 선언할 때 쓰는 훅입니다.- 렌더링 결과(JSX)와 직접 관계없는 동작을 "렌더가 반영된 뒤" 실행할 수 있게 합니다.
- 데이터 구독, API 호출, DOM 수동 조작, 타이머 등은
useEffect안에서 처리하는 것이 일반적입니다.
2 기본 사용법
2.1 시그니처
useEffect(() => {
// 부수 효과 로직
return () => { /* 클린업 (선택) */ };
}, [deps]);
- 첫 번째 인자: 실행할 함수(effect 함수).
- 두 번째 인자: 의존성 배열. 생략하거나,
[],[a, b]형태로 넘깁니다.
2.2 의존성 배열에 따른 실행 시점
| 의존성 배열 | 실행 시점 |
|---|---|
| 생략 | 매 렌더 후 실행 |
[] | 마운트 후 한 번만 실행 |
[a, b] | 마운트 시 + a 또는 b가 바뀐 렌더 후 실행 |
// 매 렌더 후 실행 (의존성 배열 생략)
useEffect(() => {
console.log('렌더 완료 후 실행');
});
// 마운트 시 한 번만 (컴포넌트 등장 시)
useEffect(() => {
fetchData();
}, []);
// userId가 바뀔 때마다
useEffect(() => {
fetchUser(userId);
}, [userId]);
3 클린업 함수
- effect 함수에서 함수를 반환하면, 그 함수는 다음 effect 실행 전·또는 컴포넌트 언마운트 시 React가 호출합니다(클린업).
useEffect(() => {
const id = setInterval(() => setCount((c) => c + 1), 1000);
return () => clearInterval(id); // 클린업: 타이머 해제
}, []);
- 구독(subscription), 이벤트 리스너, 타이머는 클린업에서 해제하지 않으면 메모리 누수나 중복 실행의 원인이 됩니다.
4 자주 쓰는 패턴
4.1 API 호출
useEffect(() => {
let cancelled = false;
async function load() {
const data = await fetchSomething(id);
if (!cancelled) setData(data);
}
load();
return () => { cancelled = true; }; // 요청 후 응답 전 언마운트 시 상태 갱신 방지
}, [id]);
id가 바뀌면 새 요청을 보내고, 응답이 오기 전에 컴포넌트가 사라지면setState를 호출하지 않도록 플래그로 막는 패턴입니다.
4.2 구독(Subscription)
useEffect(() => {
const sub = eventSource.subscribe(handler);
return () => sub.unsubscribe();
}, []);
- 구독은 보통 마운트 시 시작하고, 클린업에서 해제합니다.
4.3 DOM 측정·조작
useEffect(() => {
const rect = ref.current.getBoundingClientRect();
setDimensions(rect);
}, []);
- DOM이 렌더된 뒤에만 접근해야 하므로
useEffect안에서 처리합니다.
5 주의사항
5.1 훅 규칙
useEffect는 컴포넌트 최상위에서만 호출합니다. 조건문·반복문·중첩 함수 안에서 호출하면 안 됩니다.
5.2 의존성 누락
- effect 안에서 사용하는 props·state는 의존성 배열에 넣는 것이 좋습니다. 누락하면 오래된 클로저 값을 참조할 수 있습니다.
- ESLint
react-hooks/exhaustive-deps규칙을 켜 두면 도움이 됩니다.