리액트 컴포넌트는 오직 '입력(Props/State)을 받아 UI(JSX)를 반환하는' 순수한 역할에 집중해야 합니다. 하지만 실제 앱에서는 화면을 그리는 일 외에 서버에서 데이터 가져오기(Fetch), 브라우저 타이머 세팅, DOM 직접 조작 같은 부수 효과(Side Effect)가 반드시 필요합니다.
useEffect는 렌더링이 화면에 반영된 직후에 비동기적으로 이러한 작업들을 수행할 수 있게 해주는 훅입니다.
컴포넌트 생명주기(Lifecycle)와 useEffect
1. Mount (생성)
브라우저에 실제 HTML(DOM)이 그려진 직후에 useEffect가 딱 한 번 실행됩니다. 주로 백엔드 API 데이터를 불러오거나 이벤트를 등록할 때 쓰입니다.
2. Update (업데이트)
상태(State)가 바뀌어 화면이 다시 그려지면, 이전 상태의 찌꺼기를 지우기 위해 Cleanup 함수가 먼저 실행되고, 그다음 새로운 Setup 함수가 실행됩니다.
3. Unmount (소멸)
다른 페이지로 이동하거나 조건부 렌더링에 의해 컴포넌트가 사라질 때, 메모리 누수를 막기 위해 마지막으로 Cleanup 함수가 한 번 실행됩니다.
의존성 배열(Dependency Array) 완벽 이해
useEffect(() => { ... }) : 렌더링될 때마다 매번 실행됩니다. (거의 안 씀)
useEffect(() => { ... }, []) : 빈 배열. 컴포넌트가 처음 화면에 나타날 때(Mount) 단 한 번만 실행됩니다. API 초기 호출에 주로 씁니다.
useEffect(() => { ... }, [상태]) : 배열 안의 상태가 변경될 때마다 실행됩니다.
또한 컴포넌트가 사라질 때(Unmount) 리소스를 해제(Cleanup)하려면 return으로 정리 함수를 반환하면 됩니다.
useRef의 두 가지 핵심 용도
useRef는 리액트 컴포넌트 생애주기 동안 유지되는 변경 가능한 객체(.current)를 생성합니다. useState와 비슷해 보이지만 가장 큰 차이점은 값이 변경되어도 컴포넌트가 리렌더링되지 않는다는 점입니다.
DOM 요소 직접 선택하기 특정 input에 포커스를 주거나, 스크롤 위치를 계산하는 등 바닐라 JS의 getElementById처럼 실제 DOM 노드에 직접 접근해야 할 때 사용합니다.
리렌더링을 유발하지 않는 변수 저장 타이머 ID, 이전 상태값 기억 등 값이 바뀌어도 굳이 화면을 다시 그릴 필요가 없는 내부 데이터를 보관할 때 사용합니다.