둘 다 effect를 실행하지만 브라우저 페인트를 기준으로 서로 다른 시점에 실행되며 — 그 타이밍 차이가 핵심입니다.
- **
useEffect**는 브라우저가 페인트한 후에 실행됩니다. 비동기적이고 논블로킹입니다. 거의 모든 것에 사용하세요(데이터, 구독, 로깅). - **
useLayoutEffect**는 DOM이 변경된 후이지만 브라우저가 페인트하기 전에 동기적으로 실행됩니다. 보이는 깜빡임을 피하기 위해 동일한 프레임에서 레이아웃을 읽고 DOM을 변경해야 할 때 사용하세요.
text
render → DOM 업데이트 → [useLayoutEffect 실행] → 브라우저 페인트 → [useEffect 실행]
왜 useLayoutEffect가 필요할 때가 있는가
요소를 측정하고 그 크기에 기반하여 툴팁을 재배치하는 경우를 상상해 보세요. useEffect를 사용하면 사용자가 잠시 잘못된 위치의 툴팁을 본 다음 점프하는 것을 보게 됩니다:
jsx
useLayoutEffect(() => {
const { height } = ref.current.getBoundingClientRect();
setTooltipTop(-height); // 페인트 전에 적용됨 → 보이는 점프 없음
}, []);
useEffect로 같은 코드를 실행하면 페인트 후에 실행됨 → 한 프레임의 깜빡임.
왜 기본으로 사용하면 안 되는가
- 이것은 페인팅을 차단합니다:
useLayoutEffect의 무거운 작업은 끝날 때까지 브라우저가 페인트할 수 없어 UI가 끊기는 느낌을 줍니다. - SSR 중에 경고합니다(레이아웃이 없는 서버에서는 실행될 수 없습니다).
경험 법칙: 기본은 useEffect이고, 그렇지 않으면 깜빡일 동기적 DOM 측정/변경에만 useLayoutEffect를 사용하세요.
