
컴포넌트 생명주기(Lifecycle)란?
리액트 컴포넌트는 태어나고 → 살고 → 사라지는 흐름을 가집니다.
이 세 순간마다 특정 코드를 실행하고 싶을 때 생명주기 메서드 또는 useEffect를 사용합니다.
| 단계 | 설명 |
| Mount(마운트) | 화면에 처음 등장하는 시점 |
| Update(업데이트) | state 또는 props가 변경되어 다시 렌더링되는 시점 |
| Unmount(언마운트) | 화면에서 사라지는 시점 |
useEffect를 사용하는 이유
리액트는 UI = state의 결과물이라는 철학이 있어서 “렌더링 과정”은 아주 순수하게 유지합니다.
- state로 화면을 만들고
- state가 바뀌면 다시 화면을 만들고
-> 이 과정에서는 부수효과(side-effect)를 절대 섞으면 안됩니다! 그래서 React가 만든 해결책이 바로 useEffect.
1. 렌더링과 부수효과(side-effects)를 분리하기 위해
console.log, fetch, scroll event, setTimeout, DOM 직접 변경 등… 이런 것들은 화면을 그리는 로직과 상관없는 부수효과라고합니다.
React 철학상
👉 렌더링 과정에는 순수한 계산만 있어야 합니다.
그래서 랜더링 과정에서 순수한 계산 그 외의 부수효과는 전부 useEffect 안에 넣어야 합니다.
이렇게 분리함:
function Component() {
// 렌더링: 화면을 만드는 순수한 로직
return <div>...</div>;
}
useEffect(() => {
// 부수효과: 외부와 상호작용하는 모든 작업
});
2. 렌더링 이후에 실행해야 하는 작업을 처리하기 위해
리액트 렌더링 순서 : render → 화면 출력 → useEffect 실행
-> useEffect는 렌더링 이후에 실행됨.
그래서 이런 작업들을 useEffect에서 처리해야 합니다
- API 호출(fetch, axios)
- DOM 크기 측정
- document.title 변경
- 이벤트 리스너 등록(scroll, resize)
- 타이머(setInterval)
이런 것들은 “화면이 그려지고 난 뒤”에 해야 하기 때문입니다.!
3. 특정 값이 변경될 때만 실행되는 동작을 만들기 위해
예를 들어:
- count 값이 변할 때 API 요청 다시 보내기
- searchTerm이 바뀔 때마다 검색 결과 최신화
- props로 받은 userId가 바뀌면 유저 정보 새로 불러오기
이런 동작은 특정 값 변화에 따라 제어해야 합니다. useEffect의 [의존성 배열]이 이걸 맡아요!
useEffect(() => {
console.log("count 변경됨");
}, [count]); // count가 변할 때만 실행
-> [의존성 배열] 덕분에 원하는 시점에만 원하는 effect를 실행할 수 있음.
4. 컴포넌트가 사라질 때 정리(cleanup)하기 위해
이건 특히 “실무에서 매우 중요한 이유”입니다.
컴포넌트가 사라질 때:
- 이벤트 리스너 제거
- setInterval 정리
- 서버 구독(WebSocket) 해제
- 메모리 누수 방지
이런 정리(cleaning) 작업이 반드시 필요해요. 그걸 useEffect 안에서 이렇게 처리합니다.
useEffect(() => {
window.addEventListener("scroll", onScroll);
return () => {
window.removeEventListener("scroll", onScroll);
};
}, []);
-> return 내부의 함수가 바로 정리 함수입니다. 이 cleanup 기능은 class 컴포넌트의 componentWillUnmount 역할과 동일해요

간단하게 말하자면 useEffect는
렌더링이 끝난 뒤 부수효과를 안전하게 실행하고, 생명주기를 제어하는 도구입니다.!
Class 컴포넌트의 생명주기 메서드
리액트의 전통적인 방식은 클래스형 컴포넌트를 사용하는 것이었습니다.
현재 대부분의 프로젝트는 함수형 컴포넌트를 쓰지만, 생명주기 개념을 이해하는 데 도움되므로 가장 핵심적인 3가지 메서드를 알아봅시다.
① componentDidMount() — “마운트 직후 실행”
componentDidMount() {
console.log("처음 화면에 등장!");
// API 호출, 이벤트 등록, DOM 접근 등
}
② componentDidUpdate(prevProps, prevState) — “업데이트 직후 실행”
componentDidUpdate(prevProps, prevState) {
console.log("업데이트됨!");
}
③ componentWillUnmount() — “언마운트 직전 실행 (정리)"
componentWillUnmount() {
console.log("사라지기 직전!");
// 타이머 제거, 리스너 제거 등 정리 작업
}
함수형 컴포넌트에서의 생명주기 — useEffect
함수형 컴포넌트는 생명주기 메서드가 없습니다.
대신 useEffect가 이 모든 역할을 통합해서 담당합니다.
useEffect 기본 형태
useEffect(() => {
// 실행할 코드
return () => {
// cleanup (정리) 코드 — 선택
};
}, [의존성 배열]);
1. 컴포넌트가 최초로 렌더링 될 때에만 조작을 하고싶다. !
useEffect(() => {
console.log("맨 처음 렌더링 될 때");
}, []);
-> 두 번째 인자로 오는 [의존선 배열]이 비어있는 상태로 코드가 작성됩니다.
2. 컴포넌트가 리렌더링 될 때 조작하고 싶다. !
useEffect(() => {
console.log("리렌더링...");
},);
-> 두 번째 인자로 오는 [의존성 배열] 없이 코드가 작성됩니다.
3. 특정 상태값이 변할 때에만 조작하고 싶다. !
useEffect(() => {
console.log("counter의 값이 변할 때");
}, [conter]);
useEffect(() => {
console.log("counter2의 값이 변할 때");
}, [conter2]);
-> 두 번째 인자로 오는 [의존성 배열]자리에 들어온 counter와 counter2는 useState명을 가져온겁니다 !
4. 컴포넌트가 최종적으로 언마운트 될 때 조작하고 싶다. !
useEffect(() => {
return () => {
console.log("컴포넌트 언마운트");
};
}, []);
-> useEffect() 함수 안에 return 안에 작성되면 언마운트 될 떄 조작이 가능합니다 !
| 역할 | 클래스형 컴포넌트 | 함수형 컴포넌트(useEffect) |
| 마운트 시 한 번 실행 | componentDidMount | useEffect(() => {}, []) |
| 특정 값 업데이트 시 실행 | componentDidUpdate | useEffect(() => {}, [value]) |
| 언마운트 직전 실행 | componentWillUnmount | return () => { ... } |
초보자 입장에서는 이렇게 외워도 됩니다.
빈 배열 = 처음 한 번
배열에 값 넣기 = 그 값이 변할 때마다
cleanup = 사라질 때
예제로 이해하는 useEffect
✔ 예제: count 값이 바뀔 때마다 페이지 제목 변경
count 변경 → 재렌더링 → useEffect 실행 → 브라우저 제목 변경
import { useState, useEffect } from "react";
function TitleCounter() {
const [count, setCount] = useState(0);
useEffect(() => {
document.title = `클릭 횟수: ${count}`;
}, [count]); // count가 바뀔 때마다 실행
return (
<div>
<p>카운트: {count}</p>
<button onClick={() => setCount(count + 1)}>+</button>
</div>
);
}
export default TitleCounter;
조금 더 응용된 내용 : 이벤트 리스너 + useEffect + cleanup 쉽게 이해하기
왜 cleanup(정리)이 필요할까?
예를 들어 컴포넌트가 이렇게 있다고 가정해볼게요~
- 컴포넌트가 처음 보일 때 → window.addEventListener("scroll") 등록
- 컴포넌트가 사라질 때 → 스크롤 이벤트 제거해야 함
왜냐하면…
컴포넌트는 화면에서 사라졌는데 리스너는 계속 남아 있으면
“메모리 낭비 + 이상한 오류"가 생길 수 있기 때문!
그래서 useEffect에서 이렇게 작성합니다.
기본 패턴 : 이 패턴이 “언마운트 시 정리하는” 정석 패턴입니다.
useEffect(() => {
// 1. 등록
window.addEventListener("scroll", handleScroll);
// 2. 언마운트되기 직전 정리
return () => {
window.removeEventListener("scroll", handleScroll);
};
}, []);
- [의존성 배열] : 마운트 시 한 번만 등록됨, 언마운트 시 한 번만 정리됨
- 여기서 return() 부분이 cleanup 함수입니다.
🐣 Mini Quiz 🐣
“아~ 이렇게 이해하면 되는구나!” 하고 정리할 수 있도록 useEffect 핵심 개념을 완전히 파악할 수 있는 4지선다형 퀴즈를 가져왔어요!
정답은 맨 댓글에 남겨둘게요 ^_^!
퀴즈 풀어보고 댓글남겨주세요~
🧩 React useEffect 완전 정복! 4지선다형 퀴즈 10문제 🧩
Q1. useEffect(() => {}, []); 의 실행 시점은?
A. 모든 렌더링마다 실행된다
B. 컴포넌트가 처음 마운트될 때 단 한 번 실행된다
C. state가 바뀔 때만 실행된다
D. 컴포넌트가 사라질 때만 실행된다
Q2. 의존성 배열(dependency array)에 count를 넣으면 언제 실행될까?
useEffect(() => { console.log(count); }, [count]);
A. count가 바뀔 때에만 실행된다
B. 렌더링마다 실행된다
C. 컴포넌트가 처음 렌더될 때만 실행된다
D. 부모 컴포넌트가 렌더링될 때마다 실행된다
Q3. 언마운트(컴포넌트 사라짐) 시 어떤 코드가 실행될까?
A. useEffect의 콜백 함수
B. 의존성 배열 내부의 변수
C. useEffect의 return() 내부 함수
D. 아무 것도 실행되지 않는다
Q4. 다음 중 useEffect를 사용하는 이유로 ‘올바른 것’은?
A. 렌더링 전에 실행하기 위해서
B. 비동기 작업을 지연시키기 위해서
C. 렌더링 이후 부수효과(side-effect)를 실행하기 위해서
D. return문을 대신하기 위해서
Q5. 다음 중 “마운트 시 한 번만 실행되는” 패턴은?
A. useEffect(() => {}, [value])
B. useEffect(() => {})
C. useEffect(() => {}, [])
D. useEffect(() => { return cleanup })
Q6. 이벤트 리스너 등록 → 언마운트 시 제거하려면 어떤 패턴을 사용해야 할까?
A. 의존성 배열 없이 useEffect 실행
B. 빈 의존성 배열 + cleanup 함수
C. setTimeout 내부에 이벤트 리스너 제거
D. useState로 이벤트 제거
Q7. 클래스형 컴포넌트의 componentWillUnmount에 해당하는 부분은?
A. useEffect의 첫 번째 인자
B. useEffect의 두 번째 인자
C. useEffect의 return() 내부 함수
D. 렌더 함수 내부 코드
Q8. 다음 useEffect가 무한 렌더링을 일으키는 이유는?
useEffect(() => {
setCount(count + 1);
});
A. 의존성 배열이 비어 있어서
B. 렌더링마다 setCount가 실행되기 때문에
C. useState가 없기 때문에
D. cleanup 함수가 없기 때문에
Q9. 다음 중 “업데이트 시에만 실행되는” useEffect는?
A. useEffect(() => {}, [])
B. useEffect(() => {})
C. useEffect(() => {}, [value])
D. useEffect(() => { return () => {} })
Q10. 의존성 배열에 아무것도 넣지 않는 경우(useEffect(() => {})) 어떤 일이 일어날까?
A. 단 한 번 실행된다
B. 렌더링마다 매번 실행된다
C. 언마운트될 때 실행된다
D. cleanup 함수가 자동으로 추가된다
'프론트엔드 공부 > React' 카테고리의 다른 글
| 리액트 데이터 흐름 핵심 정리 : State는 올리고, Props로 내린다 (0) | 2025.11.10 |
|---|---|
| React Props 완전 정복 — 개념, 구조분해, 실습 예제로 쉽게 이해하기 (0) | 2025.11.10 |
| 리액트 State 선언 방식 완전 이해하기 (useState vs this.setState) (0) | 2025.11.10 |
| React 문법 기초 — JSX란 무엇이고 왜 필요한가? 초보자 쉽게 이해 (0) | 2025.11.10 |
| React 컴포넌트 정리 — Class 컴포넌트 vs 함수형 컴포넌트 차이 쉽게 이해하기 (0) | 2025.11.10 |