useEffect와 useLayoutEffect
React를 사용하다 보면 가장 자주 마주치는 훅 중 하나가 useEffect이다.
하지만 가끔은 React 문서나 커뮤니티에서 useLayoutEffect를 함께 언급하는 것을 볼 수 있다.
두 훅은 모두 “렌더링 이후”에 실행된다는 공통점이 있지만, 실행 타이밍과 용도가 완전히 다르다.
⚡ 1. useEffect — 렌더링 후 비동기적으로 실행
useEffect는 렌더링이 완전히 끝나고, 실제로 브라우저가 화면을 그린 이후에 비동기적으로 실행된다.
즉,
- 컴포넌트가 렌더됨
- DOM이 그려짐
- 브라우저가 화면 업데이트 완료
- 그 다음에 useEffect 실행
이 순서로 동작합니다.
따라서 useEffect는 다음과 같은 화면에 직접적인 영향을 주지 않는 작업에 적합하다.
- API 호출 (데이터 패칭)
- 로깅, 분석 이벤트 전송
- DOM 조작이 필요 없는 이벤트 리스너 등록
- setTimeout / setInterval
- 전역 객체(window, document) 관련 부수 효과 관리
✔️ 예시: API 데이터 패칭에 적합
1
2
3
useEffect(() => {
fetchData().then(response => setData(response));
}, []);
이런 작업은 화면이 먼저 렌더링되어도 무방하고, 렌더링 이후 로직이 실행되어도 문제되지 않기 때문에 useEffect가 자연스럽다.
⚡ 2. useLayoutEffect — 렌더링 후 DOM이 그려지기 직전 동기 실행
반면 useLayoutEffect는 조금 다르다.
- React가 렌더링을 완료
- DOM 업데이트 직전(페인트 직전)에 동기적으로 실행
- 이 함수가 끝날 때까지 브라우저는 화면을 그리지 않음
- 모든 레이아웃 관련 작업이 끝난 뒤 브라우저가 화면을 그림
즉, 사용자가 화면을 보기 전에 레이아웃 작업을 강제로 완료시키는 역할을 합니다.
어떤 경우에 필요한가?
화면이 그려지기 전에 DOM의 크기, 위치 정보를 수집해야 할 때 또는 그 정보에 맞춰 DOM을 즉시 조정해야 할 때.
- 요소의 height/width 측정
- 스크롤 위치 조정
- 화면 깜빡임(Flickering) 방지
- 애니메이션 초기 위치 계산
- 포커스 위치 조정 등
✔️ 예시: DOM 높이를 계산해 레이아웃에 반영
1
2
3
4
useLayoutEffect(() => {
const height = ref.current.offsetHeight;
setHeight(height);
}, []);
이 작업을 useEffect로 하면?
- 화면이 먼저 그려짐
- 이후 useEffect가 실행됨
- setState → 다시 렌더링
- 화면이 두 번 깜빡일 수 있음
그래서 반드시 useLayoutEffect가 필요하다.
⚠️ useLayoutEffect는 성능에 영향을 준다
useLayoutEffect는 동기적으로 실행되기 때문에, 여기에 많은 작업이 들어가면 렌더링을 “막아버린다.” 브라우저가 화면을 그리기 전에 기다려야 하므로 성능이 떨어질 수 있다.
언제 어떤 훅을 써야 할까?
| 비교 항목 | useEffect | useLayoutEffect |
|---|---|---|
| 실행 시점 | 렌더 → 브라우저 페인트 후 | 렌더 → 브라우저 페인트 직전 |
| 실행 방식 | 비동기 | 동기 |
| 화면 영향 | 화면 변경 ❌ | 화면 변경 ⭕ (깜빡임 방지) |
| 주 사용 목적 | 데이터 패칭, 이벤트 등록, 비동기 작업 | DOM 측정, 레이아웃 조정, 스크롤/포커스 |
| 성능 영향 | 적음 | 높음(남용 금지) |
기본은 useEffect 레이아웃 계산이나 화면 깜빡임을 막아야 하는 경우에만 useLayoutEffect 사용
Summary
렌더링 후 “비동기적” 작업 → useEffect 렌더링 후 화면이 그려지기 전 “동기적” 작업 → useLayoutEffect
