본문으로 바로가기

  

 

공식문서에 따르면 useLayoutEffect를 다음과 같이 정의하고 있습니다.

'이 함수의 시그니처는 useEffect와 동일하나, 모든 DOM의 변경 후에 동기적으로 발생한다.'

 

먼저 '함수의 시그니처가 useEffect와 동일'하다는 것은 두 훅의 형태나 사용 예제가 동일하다는 것을 의미합니다.

export default function App() {
  const [count, setCount] = useState(0);

  useEffect(() => {
    console.log('useEffect', count);
  }, [count]);

  useLayoutEffect(() => {
    console.log('useLayoutEffect', count);
  }, [count]);

  function handleClick() {
    setCount((prev => prev + 1))
  }

  return (
    <>
      <h1>{count}</h1>
      <button onClick={handleClick}>+</button>
    </>
  )

 

위 코드의 실행결과는 아래와 같습니다.

 

useEffect와 useLayoutEffect를 사용한 예제 코드 모두 동일한 모습으로 작동하는 것처럼 보입니다. 여기서 useLayoutEffect를 이해하기 위한 중요한 사실은 '모든 DOM의 변경 후에 useLayoutEffect의 콜백 함수 실행이 동기적으로 발생한다'는 점입니다. 여기서 말하는 DOM 변경이란 렌더링이지, 브라우저에 실제로 해당 사항이 반영되는 시점을 의미하는 것은 아닙니다. 즉, 실행 순서는 다음과 같습니다.

  1. 리액트가 DOM을 업데이트
  2. useLayoutEffect을 실행
  3. 브라우저에 변경사항을 반영
  4. useEffect를 실행

위 예제 코드를 보면 알 수 있듯이 순서상으로는 useEffect가 먼저 선언돼 있지만 항상 useLayoutEffect가 useEffect보다 먼저 실행됩니다. 이는 useLayoutEffect가 브라우저에 변경 사항이 반영되기 전에 실행되는 반면 useEffect는 브라우저에 변경 사항이 반영된 이후에 실행되기 때문입니다.

 

그리고 동기적으로 발생한다는 것은 리액트는 useLayoutEffect의 실행이 종료될 때까지 기다린 다음에 화면을 그린다는 것을 의미합니다. 즉, 리액트 컴포넌트는 useLayoutEffect가 완료될 때까지 기다리기 때문에 컴포넌트가 잠시 동안 일시 중지되는 것과 같은 일이 발생하게 됩니다. 따라서 이러한 작동 방식으로 인해 웹 애플리케이션 성능에 문제가 발생할 수 있습니다.

 

그럼 언제 useLayoutEffect를 사용하는 것이 좋을까? useLayoutEffect의 특징상 DOM은 계산됐지만 이것이 화면에 반영되기 전에 하고 싶은 작업이 있을 때와 같이 반드시 필요할 때만 사용하는 것이 좋습니다. 특정 요소에 따라 DOM요소를 기반으로 한 애니메이션, 스크롤 위치를 제어하는 등 화면에 반영되기 전에 하고 싶은 작업에 useLayoutEffect를 사용한다면 useEffect를 사용했을 때보다 훨씬 더 자연스러운 사용자 경험을 제공할 수 있을 것입니다.

 

 

 

References

https://react.dev/reference/react/useLayoutEffect

[React] useEffect란?

React Deep DIve