STUDY/[ React ]
useEffect
Lim임
2025. 11. 22. 04:00
useEffect란?
컴포넌트가 렌더링된 후에 실행되는 “사이드 이펙트(side effect)” 전용 함수.
리액트는 렌더링 = 화면을 그리는 과정인데,
이 렌더링 과정에서는 순수한 계산만 있어야 해.
하지만 실제 앱에서는 이런 게 필요함:
- API 요청
- DOM 직접 조작
- 네트워크 / 타이머 / 이벤트 등록
- 데이터 저장/불러오기(localStorage)
- 외부 라이브러리 초기화 등등…
이런 “부수효과(=side effect)”를 수행하는 공간이 useEffect야.
기본 형태
useEffect(() => { // 실행할 코드 }, [deps]);
deps = dependency array(의존성 배열)
어떤 상황에 effect를 실행할지 결정하는 “조건” 역할.
상황별로 정리한 useEffect의 3가지 동작
의존성 배열이 없는 경우
useEffect(() => { console.log("컴포넌트가 렌더링될 때마다 실행"); });
↳ 언제 쓰냐?
- 매 렌더링마다 동작해야 하는 로그, 디버깅
- 렌더링 기반으로 DOM을 계산해야 하는 상황
거의 사용 안 함
렌더링마다 실행되면 성능상 손해라 대부분 피함.
빈 배열인 경우: 첫 렌더링만 실행
useEffect(() => { console.log("마운트될 때 딱 1번 실행!"); }, []);
↳ 언제 쓰냐?
- 처음 페이지 열릴 때 한 번만 실행해야 하는 작업
✔ API 데이터 fetch
✔ WebSocket / Firebase 연결
✔ setInterval 등록
✔ 외부 라이브러리 초기화
React에서 componentDidMount 역할.
특정 값이 변경될 때 실행
useEffect(() => { console.log(`count가 ${count}로 바뀔 때마다 실행`); }, [count]);
↳ 언제 쓰냐?
- 상태가 바뀔 때 특정 작업 수행
- 검색어가 바뀌면 API 요청
- 어떤 props가 바뀌면 로직 다시 실행
- 폼 입력값 변화 감지 후 debouncing
useEffect의 또 하나의 핵심: cleanup(정리 함수)
useEffect(() => { const timer = setInterval(() => { console.log("Tick"); }, 1000); return () => { clearInterval(timer); // 정리 }; }, []);
왜 필요?
컴포넌트가 사라지거나 다시 실행될 때
이전에 사용하던 리소스를 “정리해주어야” 메모리 누수가 안 발생함.
cleanup이 필요한 경우
- setInterval, setTimeout
- 이벤트 리스너(window.addEventListener)
- WebSocket 연결 종료
- 외부 라이브러리 instance 해제
useEffect로 만들 수 있는 주요 효과들 정리
1) API 데이터 가져오기
useEffect(() => { fetch('/api/todos') .then(res => res.json()) .then(data => setTodos(data)); }, []);
2) 타이머 / 인터벌 만들기
useEffect(() => { const id = setInterval(() => { setTime(t => t + 1); }, 1000); return () => clearInterval(id); }, []);
3) 이벤트 리스너 등록
useEffect(() => { const handleResize = () => setWidth(window.innerWidth); window.addEventListener("resize", handleResize); return () => window.removeEventListener("resize", handleResize); }, []);
4) 상태 변경 시 추가 로직 실행
useEffect(() => { console.log("검색어:", keyword); // debounce 후 API 요청 등 }, [keyword]);
5) props 변경 기반 로직
useEffect(() => { console.log("props로 받은 userId가 바뀌었어!"); }, [userId]);
6) localStorage 저장/불러오기
useEffect(() => { const saved = localStorage.getItem("todo"); if (saved) setTodo(JSON.parse(saved)); }, []); useEffect(() => { localStorage.setItem("todo", JSON.stringify(todo)); }, [todo]);
useEffect를 쓸 때 고려해야 할 것
1) “언제 실행되어야 하는지”가 가장 중요
동작 조건 → 의존성 배열.
2) 불필요한 effect 금지
- 상태 업데이트가 또 effect를 불러오면 무한 루프 가능.
3) 상태를 읽어야 하면 의존성 배열에 반드시 넣기
리액트 strict모드에서는 더 엄격하게 검사함.
useEffect는 언제 쓰나? (한 줄 요약)
렌더링과 직접 관련 없는 작업을 하고 싶을 때.
≠ 화면을 그리는 로직
= 외부 세계와 상호작용하는 로직(API, 이벤트, 타이머 등)