useEffect
1. useEffect
useEffect는 React 컴포넌트에서 부수 효과(side effects)를 처리하기 위한 Hook입니다. 부수 효과란 컴포넌트의 주요 기능인 화면 렌더링 외에 필요한 작업들을 의미합니다.
1.1 기본 구조
useEffect(콜백함수, 의존성배열);
useEffect는 두 개의 인자를 받습니다. 첫 번째는 실행할 콜백 함수, 두 번째는 의존성 배열입니다. 의존성 배열의 값이 변경되면 콜백 함수가 실행됩니다.
1.2 의존성 배열
useEffect의 실행 시점은 의존성 배열의 형태에 따라 달라집니다.
1) 빈 배열 [] - 마운트 시 1회 실행
import { useState, useEffect } from 'react';
function App() {
const [count1, setCount1] = useState(0);
const [count2, setCount2] = useState(0);
useEffect(() => {
console.log('처음에 한 번 실행되는 함수야!');
}, []);
return (
<div>
<h1>count1 : {count1}</h1>
<button onClick={() => setCount1(count1 + 1)}>count1 증가</button>
<h1>count2 : {count2}</h1>
<button onClick={() => setCount2(count2 + 1)}>count2 증가</button>
</div>
);
}
컴포넌트가 처음 렌더링될 때 한 번만 실행됩니다. 초기 데이터 로딩 등에 적합합니다.
2) 특정 상태 감시 [state]
import { useState, useEffect } from 'react';
function App() {
const [count1, setCount1] = useState(0);
const [count2, setCount2] = useState(0);
useEffect(() => {
console.log('count1만 감시하는 함수야!');
}, [count1]);
return (
<div>
<h1>count1 : {count1}</h1>
<button onClick={() => setCount1(count1 + 1)}>count1 증가</button>
<h1>count2 : {count2}</h1>
<button onClick={() => setCount2(count2 + 1)}>count2 증가</button>
</div>
);
}
count1 버튼을 클릭하면 콘솔에 메시지가 출력되지만, count2 버튼을 클릭하면 출력되지 않습니다.
3) 의존성 배열 생략 - 매 렌더링마다 실행
useEffect(() => {
console.log('매 렌더링마다 실행');
});
모든 상태 변화에 반응합니다. 단, 무한 루프 위험이 있으므로 주의가 필요합니다.
1.3 클린업 함수
클린업 함수는 useEffect 내에서 반환되는 함수입니다. 컴포넌트가 언마운트되거나 다음 effect가 실행되기 전에 호출됩니다.
function Timer() {
const [count, setCount] = useState(0);
useEffect(() => {
const timer = setInterval(() => {
setCount((c) => c + 1);
}, 1000);
// 클린업 함수: 타이머 정리
return () => {
clearInterval(timer);
};
}, []);
return <h1>{count}</h1>;
}
클린업 함수가 없다면 컴포넌트가 사라져도 타이머가 계속 실행되어 메모리 누수가 발생합니다. clearInterval을 호출하여 타이머를 정리합니다.
1.4 함수형 업데이트
의존성 배열을 최적화하려면 함수형 업데이트를 활용합니다.
// ❌ count가 변경될 때마다 interval 재설정
useEffect(() => {
const interval = setInterval(() => {
setCount(count + 1);
}, 1000);
return () => clearInterval(interval);
}, [count]);
// ✅ 함수형 업데이트로 의존성 제거
useEffect(() => {
const interval = setInterval(() => {
setCount((prev) => prev + 1);
}, 1000);
return () => clearInterval(interval);
}, []);
함수형 업데이트 (prev) => prev + 1를 사용하면 항상 최신 값을 참조할 수 있어 의존성 배열을 비울 수 있습니다.
2. [실습] useEffect 사용해보기
SNS 게시글 작성 폼을 구현해보세요. 글자 수 제한과 임시저장 기능이 있는 게시글 작성 컴포넌트를 만들어봅시다.
[기본 코드]
function PostForm() {
const [content, setContent] = useState('');
const [charCount, setCharCount] = useState(0);
const [warningMessage, setWarningMessage] = useState('');
const maxLength = 150;
// 여기에 코드를 작성하세요
// 1. 글자 수 계산과 경고 메시지 표시를 위한 useEffect
// 2. 임시저장을 위한 useEffect
return (
<div className="post-form">
<h2>게시글 작성하기</h2>
<textarea
value={content}
onChange={(e) => setContent(e.target.value)}
placeholder="오늘 어떤 일이 있었나요?"
maxLength={maxLength}
/>
<div className="char-count">
{charCount}/{maxLength}
</div>
{warningMessage && <p className="warning">{warningMessage}</p>}
<button disabled={charCount === 0 || charCount > maxLength}>
게시하기
</button>
</div>
);
}
[요구사항]
-
실시간 글자 수 표시
- 글자 수에 따른 경고 메시지 (0자: "내용을 입력해주세요", 150자: "최대 글자 수 입니다")
-
임시저장 기능
- 내용 변경 시 localStorage에 자동 저장
- 페이지 새로고침 시 저장된 내용 불러오기
3. [실습] 클린업 함수 사용해보기
검색어를 입력하면 일정 시간 후에 결과를 필터링하는 디바운싱을 구현하세요.
[기본 코드]
const items = [
{ id: 1, name: 'MacBook Pro' },
{ id: 2, name: 'MacBook Air' },
{ id: 3, name: 'iMac' },
{ id: 4, name: 'Mac Mini' },
{ id: 5, name: 'Mac Pro' },
];
function SearchComponent() {
const [searchTerm, setSearchTerm] = useState('');
const [results, setResults] = useState(items);
// 여기에 코드를 작성하세요
return (
<div>
<input
type="text"
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
placeholder="검색어를 입력하세요"
/>
<ul>
{results.map((item) => (
<li key={item.id}>{item.name}</li>
))}
</ul>
</div>
);
}
[고려 사항]
- 타이핑할 때마다 즉시 검색하면 성능 저하 발생
- 타이핑 종료 후 500ms 후에 검색 실행
- 새로운 검색어 입력 시 이전 타이머 정리 (클린업 함수 활용)