useEffect: 렌더링 바깥의 세계와 연결하기
React 컴포넌트의 역할은 UI를 그리는 것입니다. 그런데 실제 앱을 만들다 보면 렌더링 외에도 해야 할 일들이 생깁니다. API 데이터 가져오기, 이벤트 리스너 등록, 타이머 설정 등이 그 예입니다. 이런 “부수 효과(side effect)“를 처리하는 것이 useEffect의 역할입니다.
기본 사용법
import { useEffect } from 'react';
function MyComponent() {
useEffect(() => {
// 렌더링 이후 실행되는 코드
console.log('렌더링 완료!');
});
return <div>안녕하세요</div>;
}useEffect에 전달하는 함수는 렌더링이 완료된 후 실행됩니다. 화면이 먼저 그려지고, 그 다음에 이 함수가 실행됩니다.
의존성 배열 (Dependency Array)
useEffect의 두 번째 인자로 배열을 넘기면, 그 값들이 바뀔 때만 효과가 다시 실행됩니다.
경우 1: 두 번째 인자 없음 → 매 렌더링마다 실행
useEffect(() => {
console.log('매 렌더링마다 실행');
});경우 2: 빈 배열 [] → 마운트 시 단 한 번만 실행
useEffect(() => {
console.log('컴포넌트가 처음 나타날 때 한 번만 실행');
}, []);API를 한 번 호출하거나 이벤트 리스너를 한 번 등록할 때 자주 사용합니다.
경우 3: 특정 값 → 그 값이 바뀔 때만 실행
useEffect(() => {
console.log(`userId가 ${userId}로 바뀌었을 때 실행`);
}, [userId]);클린업 (Cleanup)
useEffect 함수가 함수를 반환하면, 그 함수는 효과가 정리될 때 실행됩니다.
- 컴포넌트가 화면에서 사라질 때 (언마운트)
- 다음 효과가 실행되기 전
useEffect(() => {
const timer = setInterval(() => {
console.log('1초마다 실행');
}, 1000);
// 클린업 함수: 타이머를 정리합니다
return () => {
clearInterval(timer);
};
}, []);클린업을 빠뜨리면 컴포넌트가 사라져도 타이머나 이벤트 리스너가 계속 남아 **메모리 누수(memory leak)**가 발생합니다.
useEffect(() => {
window.addEventListener('resize', handleResize);
return () => {
window.removeEventListener('resize', handleResize); // 반드시 정리
};
}, []);실전 예제: 데이터 가져오기
function UserProfile({ userId }) {
const [user, setUser] = useState(null);
useEffect(() => {
fetch(`/api/users/${userId}`)
.then(res => res.json())
.then(data => setUser(data));
}, [userId]); // userId가 바뀔 때마다 새로 가져옴
if (!user) return <div>로딩 중...</div>;
return <div>{user.name}</div>;
}무한 루프 주의
의존성 배열을 잘못 관리하면 무한 루프가 발생합니다.
// 위험: count를 의존성에 포함시켰는데, 안에서 count를 바꿈
useEffect(() => {
setCount(count + 1); // count 변경 → 렌더링 → effect 재실행 → 무한 반복!
}, [count]);
// 해결: 함수형 업데이트 사용
useEffect(() => {
setCount(prev => prev + 1);
}, []); // count에 의존하지 않아도 됨또한 객체나 함수를 의존성에 넣을 때도 주의가 필요합니다. 매 렌더링마다 새로 생성되므로 “바뀐 것”으로 인식됩니다.
// 위험: options 객체가 매 렌더링마다 새로 생성됨
const options = { page: 1 };
useEffect(() => {
fetchData(options);
}, [options]); // 매 렌더링마다 실행됨!정리
| 의존성 배열 | 실행 시점 |
|---|---|
| 없음 | 매 렌더링 후 |
[] | 마운트 시 한 번 |
[a, b] | a 또는 b가 바뀔 때 |
useEffect는 React 세계와 외부 세계(API, DOM, 타이머 등)를 연결하는 다리입니다. 클린업을 통해 연결을 제대로 끊는 것이 안정적인 앱을 만드는 핵심입니다.