useEffect 완전 정복: 의존성 배열과 클린업 함수
·7분 읽기
useEffect란?#
useEffect는 React 함수형 컴포넌트에서 사이드 이펙트(side effect)를 처리하기 위한 훅입니다. 사이드 이펙트란 컴포넌트 렌더링 이외에 발생하는 작업을 말합니다 — 데이터 페칭, 이벤트 리스너 등록, DOM 조작 등이 여기에 해당합니다.
import { useEffect, useState } from 'react'
function UserProfile({ userId }: { userId: string }) {
const [user, setUser] = useState(null)
useEffect(() => {
// 사이드 이펙트: API 호출
fetch(`/api/users/${userId}`)
.then(res => res.json())
.then(data => setUser(data))
}, [userId]) // userId가 변경될 때마다 실행
return <div>{user?.name}</div>
}
의존성 배열의 동작 원리#
useEffect의 두 번째 인자인 의존성 배열은 이펙트가 언제 실행될지를 결정합니다.
세 가지 패턴#
1. 의존성 배열 없음 — 매 렌더링마다 실행
useEffect(() => {
console.log('매 렌더링마다 실행됩니다')
})
2. 빈 배열 — 마운트 시 한 번만 실행
useEffect(() => {
console.log('컴포넌트가 마운트될 때 한 번만 실행됩니다')
}, [])
3. 값이 있는 배열 — 해당 값이 변경될 때 실행
useEffect(() => {
console.log(`count가 ${count}로 변경되었습니다`)
}, [count])
클린업 함수#
클린업 함수는 useEffect 내에서 반환하는 함수로, 컴포넌트가 언마운트되거나 다음 이펙트가 실행되기 전에 호출됩니다.
useEffect(() => {
// 이벤트 리스너 등록
const handleResize = () => {
console.log('창 크기:', window.innerWidth)
}
window.addEventListener('resize', handleResize)
// 클린업: 이벤트 리스너 제거
return () => {
window.removeEventListener('resize', handleResize)
}
}, [])
흔한 실수와 해결책#
무한 루프 문제#
// ❌ 잘못된 예: 객체를 의존성으로 사용
function BadExample() {
const options = { page: 1 } // 매 렌더링마다 새 객체 생성
useEffect(() => {
fetchData(options) // options는 항상 새 참조 → 무한 루프
}, [options])
}
// ✅ 올바른 예: 원시값을 의존성으로 사용
function GoodExample() {
const page = 1
useEffect(() => {
fetchData({ page })
}, [page]) // 원시값은 참조가 동일
}
비동기 함수 사용#
// ❌ useEffect 콜백을 직접 async로 만들면 안 됨
useEffect(async () => {
const data = await fetchData() // 이렇게 하면 안 됩니다
}, [])
// ✅ 내부에서 async 함수 선언 후 호출
useEffect(() => {
async function loadData() {
const data = await fetchData()
setData(data)
}
loadData()
}, [])
React 18의 Strict Mode와 useEffect#
React 18의 Strict Mode에서는 개발 환경에서 useEffect가 두 번 실행됩니다. 이는 의도적인 동작으로, 클린업 함수가 올바르게 구현되었는지 확인하기 위해서입니다. 프로덕션 환경에서는 한 번만 실행됩니다.
관련 포스트
개발#성능 최적화#Web Vitals#Next.js#프론트엔드
웹 성능 최적화 실전 가이드: Core Web Vitals와 최적화 기법
LCP, FID, CLS 등 Core Web Vitals의 의미와 실제 프론트엔드 성능을 개선하는 실전 기법을 정리했습니다.
·9분 읽기
개발#Docker#DevOps#컨테이너#인프라
Docker 입문 가이드: 개발자가 꼭 알아야 할 컨테이너 기초
Docker가 왜 필요한지부터 이미지, 컨테이너, Docker Compose까지 개발자 관점에서 실용적으로 정리했습니다.
·8분 읽기
개발#API#REST#GraphQL#백엔드
REST API vs GraphQL: 실무에서 뭘 선택해야 할까?
REST와 GraphQL의 핵심 차이를 이해하고, 프로젝트 상황에 따른 올바른 선택 기준을 정리했습니다.
·8분 읽기