본문으로 건너뛰기
개발 머꼬
개발 노트React
hohyeon.dev17

툴팁이 잘못된 위치에서 깜빡였다가 제자리로 간 이유

  • #Engineering Note
  • #Performance
  • #React

문제 발생

툴팁 위치를 재서 화면 밖으로 나가지 않게 조정했는데, 열릴 때마다 잘못된 위치에서 깜빡였다가 제자리로 갔습니다.

useEffect(() => {
  const { height } = ref.current.getBoundingClientRect();
  setTooltipHeight(height);
}, []);

useLayoutEffect로 바꿨더니 깜빡임은 사라졌는데, 이번엔 서버 렌더링에서 경고가 떴습니다.

Warning: useLayoutEffect does nothing on the server

원인 분석

둘의 차이는 브라우저가 화면을 다시 그리기 전인가 후인가 하나입니다.

useLayoutEffectuseEffect
실행 시점브라우저가 페인트하기 페인트한
페인트 차단차단함차단하지 않음
서버실행되지 않음 (경고)실행되지 않음

useEffect로 측정하면 이미 그려진 뒤에 위치를 고치므로 사용자 눈에 한 번 잘못된 위치가 보입니다. useLayoutEffect는 그리기 전에 측정과 조정을 끝내므로 깜빡임이 없습니다.

대가도 분명합니다. React 공식 문서가 함정(Pitfall) 으로 표시합니다 — useLayoutEffect는 성능을 해칠 수 있으니 가능하면 useEffect를 쓰십시오. 이 안의 코드와 여기서 예약된 모든 state 갱신은 브라우저가 화면을 다시 그리는 것을 막습니다. 과하게 쓰면 앱이 느려집니다.

서버 경고도 당연한 결과입니다. 문서의 설명대로 useLayoutEffect의 목적은 브라우저가 페인트하기 전에 레이아웃 정보를 측정하는 것인데, 레이아웃 자체가 없는 서버에서는 불가능합니다.

해결 방안

  1. 측정이 정말 필요한 경우에만 씁니다. 툴팁·팝오버 위치, 스크롤 위치 복원처럼 "그리기 전에 알아야 하는" 값에 한정합니다. 그 외에는 useEffect가 맞습니다.
useLayoutEffect(() => {
  const { height } = ref.current.getBoundingClientRect();
  setTooltipHeight(height);   // 페인트 전에 반영됨
}, []);
  1. SSR 경고는 상황에 맞는 방법으로 없앱니다. 문서가 제시하는 선택지는 여럿이고, 무엇을 고르느냐는 "서버에서 무엇을 보여줄 것인가"에 달려 있습니다.
  • useEffect로 바꾼다 — 초기 렌더를 페인트 차단 없이 보여줘도 괜찮다면 이게 가장 간단합니다. 원래 HTML이 Effect보다 먼저 보이게 됩니다.
  • 브라우저 전용으로 표시한다 — 서버 렌더링 동안 가장 가까운 <Suspense> 경계까지를 로딩 폴백으로 대체합니다.
  • isMounted 상태를 쓴다false로 시작해 useEffect에서 true로 바꾸고, 서버에서는 대체 콘텐츠를 렌더합니다.
  • useSyncExternalStore를 쓴다 — 외부 데이터 저장소와 동기화하는 경우라면 이쪽이 서버 렌더링을 지원합니다.
  1. isMounted 패턴은 이런 형태입니다.
const [mounted, setMounted] = useState(false);
useEffect(() => setMounted(true), []);

if (!mounted) return <TooltipSkeleton />;
  1. 조건부로 훅을 부르지 않습니다. typeof window === "undefined" ? useEffect : useLayoutEffect 같은 분기는 훅 규칙을 어깁니다. 그렇게 만든 useIsomorphicLayoutEffect가 널리 퍼져 있지만, 실제로는 "서버에서 무엇을 보여줄지"를 정하지 않고 경고만 숨기는 경우가 많습니다.

  2. 여기서 무거운 일을 하지 않습니다. 페인트를 막고 있는 동안의 모든 작업이 사용자에게는 멈춤으로 보입니다. 네트워크 요청, 큰 배열 순회는 여기 두지 않습니다.

공식 문서

마지막 수정

좋아요북마크

댓글0

아직 댓글이 없어요. 첫 의견을 편하게 남겨 보세요.