OI7MI
[React] useEffect() 와 Cleanup 함수 본문
useEffect란?
실행 시점을 내 마음대로 결정하게 해주는 함수
리액트 컴포넌트는 state가 변할 때마다 전체 코드가 다시 실행(Re-render)되는 특징이 있다.
하지만 서버에서 데이터를 받아오는 API 호출처럼
딱 한 번만 실행되어야 하거나, 특정 값이 변할 때만 실행되어야 하는 코드가 있다면?
이걸 내 마음대로 제어할 수 있게 해주는 게 바로 useEffect다.
useEffect(() => {
// 1. 실행하고 싶은 코드 (첫 번째 인자)
}, [/* 2. 지켜볼 대상들 */]); // (두 번째 인자)
첫 번째 인자: 실행하고 싶은 코드(함수)
두 번째 인자: 리액트가 지켜볼 대상이 담긴 배열 (의존성 배열, [])
useEffect는 'Side Effect'를 위한 Hook이다
리액트에서 Side effect(부작용, 영향)란 렌더링 결과물과는 직접적인 상관이 없지만
컴포넌트 외부와 상호작용하는 작업들을 말한다
예시: 서버에서 데이터 받아오기(API), 수동으로 DOM 조작하기, 타이머 설정 등
특징: 이런 작업들은 렌더링이 다 끝난 뒤에 실행되어야 성능에 방해가 안 된다. 그래서 useEffect라는 Hook(훅)을 사용해서 실행 시점을 관리하는 것.
의존성 배열([])에 따른 마법
두 번째 인자인 배열을 어떻게 쓰느냐에 따라 실행 타이밍이 완전히 달라진다
컴포넌트가 처음 나타날 때 딱 한 번만 실행하고
그 뒤로는 어떤 일이 생겨도 실행하고 싶지 않을 때 두 번째 인자에 빈 배열을 넣어서 사용하고,
배열 안에 특정 변수를 넣어주면, 리액트가 그 값을 감시하다가 값이 바뀔 때만 코드를 다시 실행해준다.
- 빈 배열일 때 []
동작: 컴포넌트가 처음 화면에 나타날 때(Mount) 딱 한 번만 실행하고 다시는 안 함.
용도: API 딱 한 번만 호출하고 싶을 때 사용. 리액트한테 "지켜볼 게 아무것도 없으니까 처음 말고는 다시 실행하지 마"라고 알려주는 것.
- 특정 값이 있을 때 [keyword]
동작: 처음 나타날 때 + 값이 바뀔 때마다 실행.
용도: 특정 데이터가 변할 때마다 반응해야 하는 로직에 사용.
코드
import { useState, useEffect } from "react";
function App() {
const [counter, setCounter] = useState(0);
const [keyword, setKeyword] = useState("");
const onClick = () => setCounter((prev) => prev + 1);
const onChange = (e) => setKeyword(e.target.value);
// 1. 처음 렌더링될 때 딱 한 번만 실행
useEffect(() => {
console.log("나는 한번만 실행돼요.");
}, []);
// 2. keyword가 변화할 때만 실행
useEffect(() => {
console.log(`나는 ${keyword} 가 변화할 때 실행돼요.`);
}, [keyword]);
// 3. counter가 변화할 때만 실행
useEffect(() => {
console.log(`나는 ${counter} 가 변화할 때 실행돼요.`);
}, [counter]);
// 4. 둘 중 하나라도 변화하면 실행
useEffect(() => {
console.log(`나는 ${counter}, ${keyword} 둘 다 변화할 때 실행돼요.`);
}, [counter, keyword]);
return (
<div>
<input type="text" placeholder="Search here..." value={keyword} onChange={onChange} />
<h1>{counter}</h1>
<button onClick={onClick}>click me</button>
</div>
);
}
useEffect 정리
| 의존성 배열 | 실행 타이밍 |
| 빈 배열 [] | 컴포넌트가 처음 나타날 때(Mount) 딱 한 번 |
| 값이 있는 배열 [a] | 처음 나타날 때 + a가 변할 때마다 |
| 여러 개 있는 배열 [a, b] | 처음 나타날 때 + a 혹은 b가 변할 때마다 |
useEffect는 무분별한 코드 실행을 막아주는 방어막이자, 내가 원하는 시점을 정확히 조준하는 트리거다.
Cleanup 함수란?
useEffect는 컴포넌트가 처음 나타날 때 코드를 실행할 수 있게 해주지만
반대로 컴포넌트가 화면에서 사라질 때(Destroy/Unmount) 코드를 실행하는 방법도 제공한다.
이걸 Cleanup 함수라고 부른다.
Cleanup 함수는 useEffect 안에서 함수를 return 하면, 그 리턴된 함수는 컴포넌트가 파괴될 때 실행된다
아래 두 코드는 같다
// 1
function Hello() {
useEffect(() => {
console.log("created :)"); // 컴포넌트가 나타날 때 실행
return () => console.log("destroyed :("); // 컴포넌트가 사라질 때 실행 (Cleanup)
}, []);
return <h1>Hello</h1>;
}
// 2
function Hello() {
function byFn(){
console.log("destroyed");
}
function hiFn(){
console.log("created");
return byFn;
}
useEffect(hiFn, []);
return <h1>Hello</h1>;
}
2. 작동 원리: 왜 return을 할까?
리액트의 이펙트 라이프사이클은 아래와 같이 흘러간다
리렌더링/생성: 컴포넌트가 화면에 그려짐.
이펙트 실행: useEffect의 첫 번째 인자인 함수가 실행됨.클린업: 다음 이펙트가 실행되기 전이나, 컴포넌트가 파괴될 때 이전에 return 했던 함수를 실행해서 청소함.
3. 두 코드는 왜 결과가 같을까?
두 코드는 작성 방식만 다를 뿐, 자바스크립트의 함수 리턴 원리를 이용한 같은 로직
첫 번째: 익명 함수를 바로 리턴함. (가장 흔히 쓰는 방식)
두 번째: hiFn을 실행하면 byFn이라는 함수 자체를 결과값으로 리턴함.
결국 리액트 입장에서는 어쨌든 나한테 함수 하나를 리턴해줬네? 그럼 나중에 파괴될 때 이거 실행해 줄게! 가 되는 것.
4. 언제 사용할까?
컴포넌트가 사라지는데 왜 코드를 실행해야 할까? 보통 '정리'가 필요할 때 쓴다.
타이머 제거:
setTimeout이나 setInterval을 켰다면 사라질 때 꺼줘야 메모리 낭비가 없다.
이벤트 리스너 해제:
윈도우 스크롤 감지 같은 걸 등록했다면 사라질 때 지워줘야 한다.
API 요청 취소:
데이터를 가져오다가 컴포넌트가 닫히면 더 이상 필요 없는 요청을 취소할 때 쓴다.
Cleanup 정리
- Cleanup 함수: useEffect 안에서 return 하는 함수.
- 실행 시점: 컴포넌트가 파괴(Unmount)될 때, 혹은 다음 이펙트가 실행되기 직전에 실행됨.
- 의존성 배열이 []일 때: 컴포넌트가 아예 사라질 때 딱 한 번 실행됨.
'프론트엔드 > React' 카테고리의 다른 글
| [React] API 호출 .then() 대신 async-await 처리 (0) | 2026.01.13 |
|---|---|
| [React] useState 상태 업데이트와 배열에서 특정 항목 삭제하기 (0) | 2026.01.09 |
| [React] CSS Module 사용 이유와 사용 방법 (1) | 2026.01.06 |
| [React] useState로 input의 disabled 속성 제어하기 (0) | 2025.12.27 |
| [React] 컴포넌트의 상태 관리: state와 useState (0) | 2025.12.22 |