티스토리 뷰
React를 공부하다 보면 useState 다음으로 가장 많이 막히는 Hook이 바로 useEffect입니다. 문법 자체는 짧아 보이는데, 막상 써보면 “이게 왜 두 번 실행되지?”, “의존성 배열을 비우면 뭐가 달라지지?”, “API 호출은 어디에 넣어야 하지?” 같은 질문이 한꺼번에 생깁니다.
특히 입문자 입장에서는 아래 포인트에서 많이 헷갈립니다.
- useEffect는 정확히 언제 실행되는가?
- 의존성 배열은 왜 필요한가?
- 빈 배열
[]은 무슨 의미인가? - 무한 렌더링은 왜 생기고 어떻게 막아야 하는가?
이번 글에서는 React 입문자가 가장 자주 찾는 useEffect 사용법 정리를 기준으로, 실행 시점 → 의존성 배열 → API 호출 → cleanup → 자주 하는 실수까지 한 번에 정리하겠습니다.
React 컴포넌트는 먼저 화면을 그립니다. 그런데 어떤 작업은 화면을 그리는 도중이 아니라, 화면이 그려진 다음에 실행해야 합니다.
예를 들면 아래 같은 것들입니다.
- 서버에서 데이터 가져오기
- 이벤트 리스너 등록하기
- setInterval 같은 타이머 시작하기
- props나 state 변화에 맞춰 외부 동작 연결하기
이런 작업을 처리하는 대표적인 Hook이 useEffect입니다.
useEffect는 렌더링 이후 필요한 부수 작업을 실행하는 Hook입니다.
즉 useEffect를 이해할 때는 “언제 화면이 바뀌는가?”보다 “렌더링 후 어떤 작업을 연결해야 하는가?”로 보면 훨씬 쉽습니다.
가장 기본적인 형태는 아래와 같습니다.
import { useEffect } from 'react';
useEffect(() => {
console.log('effect 실행');
}, []);
핵심은 두 번째 인자인 의존성 배열입니다.
function Example({ userId }) {
useEffect(() => {
console.log('처음 렌더링되거나 userId가 바뀔 때 실행');
}, [userId]);
return <div>예제</div>;
}
의존성 배열은 effect 안에서 사용하는 값 중 어떤 값이 바뀌면 다시 effect를 실행할지 정하는 역할을 합니다.
쉽게 보면 이렇게 이해할 수 있습니다.
렌더링 후 effect 실행
↓
배열 안 값 변화 감시
↓
값이 바뀌면 다시 실행
예를 들어 검색어가 바뀔 때마다 데이터를 다시 불러오고 싶다면 아래처럼 쓸 수 있습니다.
function SearchResult({ keyword }) {
useEffect(() => {
console.log(`${keyword}로 다시 검색`);
}, [keyword]);
return <div>검색 결과</div>;
}
여기서 keyword가 바뀌면 effect도 다시 실행됩니다.
의존성 배열은 “이 값이 바뀌면 effect를 다시 실행해라”라고 React에 알려주는 목록입니다.
대표적으로 많이 나오는 예시는 아래와 같습니다.
import { useEffect, useState } from 'react';
function UserList() {
const [users, setUsers] = useState([]);
useEffect(() => {
fetch('/api/users')
.then((res) => res.json())
.then((data) => setUsers(data));
}, []);
return <div>{users.length}명</div>;
}
useEffect(() => {
const handleResize = () => {
console.log(window.innerWidth);
};
window.addEventListener('resize', handleResize);
return () => {
window.removeEventListener('resize', handleResize);
};
}, []);
useEffect(() => {
const timer = setInterval(() => {
console.log('3초마다 실행');
}, 3000);
return () => clearInterval(timer);
}, []);
useEffect(() => {
setCount(count + 1);
}, [count]);
이 코드는 count가 바뀔 때마다 effect가 다시 실행되고, 그 안에서 또 setCount를 호출하므로 반복될 수 있습니다.
- effect가 언제 실행돼야 하는지 먼저 말로 설명할 수 있는지 확인
- effect 안에서 사용하는 값이 의존성 배열에 들어갔는지 확인
- 이벤트 / 타이머 / 구독이 있다면 cleanup이 있는지 확인
- state 변경이 effect 재실행을 계속 유발하지 않는지 확인
useEffect는 return 함수로 cleanup 로직을 넣을 수 있습니다.
useEffect(() => {
console.log('effect 시작');
return () => {
console.log('이전 effect 정리');
};
}, [value]);
이 cleanup은 보통 아래 상황에서 중요합니다.
- 컴포넌트가 사라질 때 정리해야 하는 이벤트
- 반복 실행되는 interval 정리
- 이전 요청/구독 상태 해제
즉 useEffect는 단순히 "실행" Hook이 아니라, 시작과 정리를 같이 다루는 Hook으로 이해하는 편이 더 정확합니다.
- useEffect는 렌더링을 직접 만드는 Hook이 아니라 렌더링 이후 부수 작업을 처리하는 Hook입니다.
- 의존성 배열은 다시 실행될 조건을 정하는 목록입니다.
- 빈 배열은 보통 처음 한 번 실행하려는 의도에서 사용합니다.
- 이벤트 리스너나 interval은 cleanup까지 같이 써야 안전합니다.
- Q. useEffect는 왜 두 번 실행되는 것처럼 보이나요?
→ 개발 환경과 Strict Mode 설정에 따라 effect가 한 번 더 확인용으로 실행되는 것처럼 보일 수 있습니다. 무조건 배포 환경 문제라고 보진 말고 실행 환경을 같이 확인해야 합니다. - Q. 의존성 배열에 무엇을 넣어야 하나요?
→ effect 안에서 실제로 사용하는 값 중, 변경되면 다시 실행돼야 하는 값을 기준으로 생각하면 됩니다. - Q. API 호출은 무조건 useEffect에 넣어야 하나요?
→ 전통적인 패턴에서는 자주 그렇게 처리하지만, 상황에 따라 데이터 패칭 라이브러리나 서버 컴포넌트 구조를 함께 고려할 수도 있습니다.
React의 useEffect는 처음 보면 어렵지만, 핵심은 의외로 단순합니다.
- 렌더링 이후 실행해야 하는 작업을 넣고
- 어떤 값이 바뀌면 다시 실행할지 정하고
- 필요하면 cleanup으로 정리한다
이 흐름만 잡히면 useEffect는 훨씬 덜 어렵게 느껴집니다.
특히 useEffect를 잘 쓰려면 문법 자체보다 "언제 실행돼야 하는가"와 "무엇이 바뀌면 다시 실행돼야 하는가"를 먼저 말로 설명할 수 있어야 합니다.
useEffect는 렌더링 이후 부수 작업을 관리하는 Hook이고, 의존성 배열은 그 작업의 재실행 조건을 정하는 장치입니다.
※ 이 글은 React useEffect를 처음 이해하기 위한 입문 가이드입니다. 실제 프로젝트에서는 Strict Mode, 비동기 처리, 데이터 패칭 라이브러리, cleanup 전략까지 함께 보는 것이 좋습니다.
'Front > React' 카테고리의 다른 글
| React API 연동 방법 | Axios로 GET, POST 요청하고 화면에 출력하기 (1) | 2026.04.06 |
|---|---|
| React useState 사용법 정리 | 상태 관리, 값 변경, 배열·객체 업데이트까지 (0) | 2026.04.04 |
| React 리눅스 서버 배포 방법 | build 파일 업로드부터 실제 서비스 적용까지 (0) | 2026.04.03 |
| React Router 사용법 | 페이지 이동, Link, 기본 라우팅 적용 방법 (2) | 2026.04.02 |
| React JSX 문법 정리 | 기본 문법, 조건부 렌더링, 리스트 렌더링까지 (0) | 2026.04.02 |

