React의 useEffect는 함수형 컴포넌트에서 부작용(side effects)을 처리하기 위해 사용되는 Hook입니다. 하지만 useEffect를 올바르게 사용하려면 의존성 배열(dependency array)의 동작 방식을 제대로 이해해야 합니다. 의존성 배열은 useEffect의 강력한 기능을 제공하지만 잘못 사용하면 성능 문제나 예기치 않은 동작을 초래할 수 있습니다. 이번 글에서는 의존성 배열의 역할과 사용 방법, 그리고 이를 둘러싼 주요 개념들을 자세히 살펴보겠습니다.
1. useEffect란 무엇인가?
기본 개념
useEffect는 컴포넌트가 렌더링된 이후에 특정 작업을 수행하도록 설정하는 Hook입니다. 일반적으로 다음과 같은 작업에 사용됩니다:
- 데이터 가져오기(API 호출)
- DOM 조작
- 구독(subscription) 설정 및 해제
- 타이머 설정
기본 사용법
import React, { useEffect } from 'react';
function ExampleComponent() {
useEffect(() => {
console.log('컴포넌트가 렌더링되었습니다.');
return () => {
console.log('컴포넌트가 언마운트되었습니다.');
};
});
return <div>안녕하세요!</div>;
}
위 코드에서 useEffect는 컴포넌트가 마운트될 때와 언마운트될 때 실행됩니다.
2. 의존성 배열(dependency array)이란 무엇인가?
정의
의존성 배열은 useEffect의 두 번째 매개변수로 전달되는 배열입니다. 이 배열은 useEffect가 언제 실행될지를 결정합니다.
의존성 배열의 역할
의존성 배열에는 useEffect가 의존하는 값들이 나열됩니다. React는 이 배열에 포함된 값들이 변경되었을 때만 useEffect를 다시 실행합니다.
예제
import React, { useState, useEffect } from 'react';
function Counter() {
const [count, setCount] = useState(0);
useEffect(() => {
console.log(`현재 카운트: ${count}`);
}, [count]);
return (
<div>
<p>{count}</p>
<button onClick={() => setCount(count + 1)}>증가</button>
</div>
);
}
위 코드에서 useEffect는 count 값이 변경될 때만 실행됩니다. 즉, 버튼을 클릭하여 count가 증가할 때마다 콘솔 로그가 출력됩니다.
3. 의존성 배열의 다양한 패턴
1. 의존성 배열 없음
의존성 배열을 생략하면, useEffect는 매 렌더링마다 실행됩니다.
useEffect(() => {
console.log('렌더링마다 실행됩니다.');
});
2. 빈 배열
의존성 배열을 빈 배열로 설정하면, useEffect는 컴포넌트가 처음 마운트될 때만 실행됩니다.
useEffect(() => {
console.log('마운트 시에만 실행됩니다.');
}, []);
3. 특정 값 포함
의존성 배열에 특정 값을 포함하면, 해당 값이 변경될 때만 useEffect가 실행됩니다.
useEffect(() => {
console.log('의존성이 변경되었습니다.');
}, [dependency]);
4. 의존성 배열 관련 주요 이슈
1. 의존성 배열 누락
의존성 배열에서 의존성을 누락하면 React가 값을 감지하지 못해 의도치 않은 동작이 발생할 수 있습니다.
useEffect(() => {
console.log(dependency); // dependency가 의존성 배열에 없으면 문제가 발생할 수 있음
}, []); // 잘못된 사용
React 개발 도구에서는 이러한 누락을 경고로 표시합니다.
2. 불필요한 의존성 추가
모든 값을 의존성 배열에 추가하면 성능 문제가 발생할 수 있습니다. 예를 들어, 변경되지 않는 값을 의존성 배열에 포함시키는 것은 불필요한 리렌더링을 초래할 수 있습니다.
3. 클로저 문제
useEffect 내부에서 의존성 배열을 잘못 관리하면 클로저 문제가 발생할 수 있습니다.
useEffect(() => {
const timer = setInterval(() => {
console.log(count); // 오래된 count 값 참조
}, 1000);
return () => clearInterval(timer);
}, []);
이 문제를 해결하려면 의존성 배열에 최신 상태를 포함하거나, useRef를 사용해 최신 값을 참조해야 합니다.
5. 의존성 배열 관리 모범 사례
- 모든 의존성을 명시적으로 추가: React는 의존성 배열을 기반으로 최적화를 수행하므로 모든 필요한 값을 배열에 포함시키는 것이 중요합니다.
- useEffect(() => { fetchData(dataId); // dataId를 의존성 배열에 추가 }, [dataId]);
- 함수 메모이제이션 사용: 콜백 함수는 useCallback으로 메모이제이션하여 불필요한 리렌더링을 방지할 수 있습니다.
- const fetchData = useCallback(() => { // 데이터 가져오기 로직 }, [dependency]); useEffect(() => { fetchData(); }, [fetchData]);
- React 경고에 주의: React가 제공하는 경고를 무시하지 말고, 의존성 배열을 적절히 수정하세요.
결론
useEffect의 의존성 배열은 React의 데이터 흐름을 관리하는 데 핵심적인 역할을 합니다. 이를 올바르게 사용하면 성능을 최적화하고, 컴포넌트가 예상대로 동작하도록 보장할 수 있습니다. 하지만 의존성을 잘못 관리하면 성능 문제와 디버깅 어려움을 초래할 수 있습니다. 따라서 useEffect의 의존성 배열을 이해하고, 이를 적절히 사용하는 것이 React 개발에 있어 매우 중요합니다.
'React' 카테고리의 다른 글
React에서 Controlled vs Uncontrolled 컴포넌트 (0) | 2024.12.12 |
---|---|
React에서 useState와 useReducer 비교하기 (0) | 2024.12.12 |
React에서 Hooks와 클래스형 컴포넌트의 차이 (0) | 2024.12.12 |
React에서 Suspense 사용법 (0) | 2024.12.12 |
React에서 Error Boundary 사용법 (0) | 2024.12.12 |