✨ useEffect 훅 (Hook)
useEffect는 리액트(React) 함수형 컴포넌트 내에서 사이드 이펙트(Side Effects) 를 수행하기 위해 사용되는 핵심 훅(Hook) 이다.
🤔 사이드 이펙트(Side Effects)란?
리액트의 주된 역할은 상태(State)와 속성(Props)을 받아 UI를 렌더링하는 것이다. 이 렌더링 과정 외에 애플리케이션의 상태에 영향을 주거나 외부 세계와 상호작용하는 모든 작업을 사이드 이펙트라고 부른다.
일반적으로 사용하는 부정적인 부작용(negative side effect)이나 프로그래밍 과정에서 나오는 의도하지 않은 부작용 (Unintended Side Effects)과는 다른 개념이다.
대표적인 예시는 다음과 같다:
- 🌐 데이터 가져오기 (Data Fetching): API 서버에 데이터를 요청하고 받아오는 작업.
- <sub>subscribe</sub> 구독 설정 및 해제 (Subscriptions): 웹소켓(WebSocket) 연결, 이벤트 리스너(Event Listener) 등록 등 외부 소스로부터 데이터를 구독하고, 컴포넌트가 사라질 때 구독을 해제하는 작업.
- DOM 직접 조작: 리액트의 제어 범위를 벗어나 직접 DOM(Document Object Model)을 변경하는 작업 (권장되지는 않음).
- ⏰ 타이머 설정 및 해제 (Timers): setTimeout 이나 setInterval 을 사용하여 특정 시간 후에 코드를 실행하거나 반복 실행하고, 필요 없을 때 해제하는 작업.
- 🪵 로깅 (Logging): 특정 이벤트나 상태 변화를 콘솔이나 외부 로깅 서비스에 기록하는 작업.
사이드 이펙트는 컴포넌트 렌더링 중에 직접 수행하면 예측 불가능한 동작이나 성능 문제를 일으킬 수 있으므로, useEffect 를 사용하여 렌더링 이후에 안전하게 처리하는 것이 권장된다.
⚙️ useEffect 의 역할과 기본 구조
useEffect는 컴포넌트가 렌더링된 후에 특정 코드(사이드 이펙트 함수)를 실행하도록 예약한다. 또한, 필요하다면 다음 이펙트가 실행되기 전이나 컴포넌트가 사라지기(unmount) 전에 정리(cleanup) 작업을 수행할 수도 있다.
기본적인 구조는 다음과 같다:
useEffect(() => {
// 사이드 이펙트 로직 (콜백 함수)
// 예: API 호출, 이벤트 리스너 등록, 타이머 설정 등
return () => {
// 정리(Cleanup) 로직 (선택적 반환 함수)
// 예: 이벤트 리스너 제거, 타이머 해제, 구독 취소 등
};
}, [dependency1, dependency2]); // 의존성 배열 (선택적)
🔄 의존성 배열 (Dependency Array)
useEffect(callback, [dependencies]) 의 두 번째 인자인 의존성 배열은 이펙트 함수가 언제 다시 실행될지를 결정하는 매우 중요한 부분이다.
- 배열 생략 (useEffect(callback)):
- 컴포넌트가 최초 렌더링될 때 실행된다.
- 컴포넌트가 리렌더링 될 때마다 (props나 state가 변경되어) 항상 다시 실행된다.
- 주의: 이펙트 함수 내에서 상태를 업데이트하면 무한 루프에 빠질 수 있으므로 신중하게 사용해야 한다.
- 빈 배열 (useEffect(callback, [])):
- 컴포넌트가 최초 렌더링될 때 한 번만 실행된다. (클래스 컴포넌트의 componentDidMount 와 유사)
- 이후 리렌더링 시에는 다시 실행되지 않는다.
- 용도: 컴포넌트가 처음 나타날 때 필요한 초기 설정 (최초 데이터 로딩, 전역 이벤트 리스너 등록 등)에 주로 사용된다.
- 값이 포함된 배열 (useEffect(callback, [dep1, dep2])):
- 컴포넌트가 최초 렌더링될 때 실행된다.
- 이후 리렌더링 시, 의존성 배열 안의 값 (dep1 또는 dep2) 중 하나라도 이전 렌더링과 비교했을 때 변경되었으면 다시 실행된다. (클래스 컴포넌트의 componentDidUpdate 의 특정 조건부 실행과 유사)
- 용도: 특정 props나 state 값에 의존하여 동작하는 사이드 이펙트를 구현할 때 가장 흔하게 사용된다. (예: 사용자 ID가 바뀌면 해당 사용자의 프로필 데이터 다시 가져오기)
🧹 정리 함수 (Cleanup Function)
useEffect의 콜백 함수는 선택적으로 또 다른 함수를 반환할 수 있다. 이 반환된 함수를 정리 함수라고 부른다.
- 실행 시점:
- 컴포넌트가 화면에서 사라지기 직전 (unmount) 에 실행된다. (클래스 컴포넌트의 componentWillUnmount 와 유사)
- 다음 이펙트 함수가 실행되기 직전에 이전 이펙트를 정리하기 위해 실행된다. (의존성 배열이 변경되어 이펙트가 다시 실행될 때)
- 목적: 이펙트 함수에서 설정했던 외부 자원(구독, 타이머, 이벤트 리스너 등)을 깨끗하게 정리하여 메모리 누수(Memory Leak) 나 예상치 못한 버그를 방지한다.
💡 주요 활용 사례
- 데이터 가져오기: 컴포넌트 마운트 시([]) 또는 특정 ID 변경 시([id]) API 호출.
- 이벤트 리스너 관리: 마운트 시([]) window 등에 이벤트 리스너 추가하고, 언마운트 시(정리 함수) 제거.
- 타이머/인터벌 관리: 마운트 시([]) setInterval 설정하고, 언마운트 시(정리 함수) clearInterval 호출.
- 외부 라이브러리 연동: 리액트 생명주기에 맞춰 외부 라이브러리 초기화 및 해제.
useEffect는 함수형 컴포넌트에서 클래스 컴포넌트의 생명주기 메소드(componentDidMount, componentDidUpdate, componentWillUnmount)의 역할을 통합적으로 수행하며, 사이드 이펙트를 선언적으로 관리할 수 있게 해주는 강력한 도구다.