brunch

매거진 개발노트

You can make anything
by writing

C.S.Lewis

by April Feb 04. 2022

SSR에서 useLayoutEffect 사용하기

[React] Warning: useLayoutEffect does nothing on the server 해결하기


  Antd 를 이용해 해당 예제처럼 사이드바 메뉴를 구성하였다. 사이드바 메뉴를 구성하기 위해서 리액트 라우트에서 사용하는 라우트 정보를 받아 map으로 서브 메뉴가 있는 경우에는 SubMenu, 없는 경우에는 Menu.Item 을 이용해 컴포넌트를 그리도록 만들었다.

  그런데 화면 렌더링이 될 때마다 혹은 메인 화면에서 새로고침을 할 때마다 아래와 같이 Warning 로그가 터미널에 출력이 되고 있었다. 처음에는 useLocation 의 값이 변할 때마다  useEffect 를 실행하도록 해서 그런가 싶었는데, 해당 코드를 제거하고 실행해도 똑같이 출력되었다.

  왜 이런 로그가 뜨는지 정확하게 알기 위해 로그에 있는 https://fb.me/react-uselayouteffect-ssr  링크를 들어가보았다.

  일단 SSR 에서는 useLayoutEffect 와 useEffect 를 자바스크립트가 로드되기 전까지 사용할 수 없다. 그렇기 때문에 서버에서 useLayoutEffect 를 사용하려고 시도하면 위와 같은 로그가 터미널에 찍히게 된다. 문서에서는 이를 해결하기 위한 일반적인 방법 2가지를 소개하고 있다.


[참고] useEffect 와 useLayoutEffect

useEffect

렌더링 이후에 컴포넌트가 어떤 일을 수행해야하는 지 알려준다.

DOM 의 레이아웃 배치(render)와 페인트(paint)가 끝난 후 이펙트 함수가 호출된다.

DOM 이 그려지고 이펙트 내부의 코드들을 실행해 재렌더링이 수행된다.       따라서 간단한 DOM 구조의 경우 금방 렌더링할 수 있지만 화면이 복잡해지면 렌더링 시간이 증가하게 된다.
→ 사용자는 화면에서 default state 값으로 셋팅된 DOM을 오랜동안 보게 된다.  


useLayoutEffect   

useEffect 의 복잡한 렌더링 시 오랜 시간이 걸리는 문제를 해결하기 위해 등장한 훅

브라우저가 화면을 DOM 에 그리기 전에 이펙트를 실행한다. (레이아웃 이펙트 내부의 코드를 실행한 후 페인트한다)

paint 가 되기 전에 실행되므로 DOM 을 조작하는 코드가 있어도 사용자는 깜빡임을 경험하지 않는다.
→ 따라서 렌더링할 상태가 이펙트 내에서 초기화되어야 하는 경우 사용자 경험을 위해 useLayoutEffect 를 활용할 것





아래의 방법은  https://fb.me/react-uselayouteffect-ssr 를 기반으로 작성한 내용입니다.


방법 1: useEffect 로 변환하기

useEffect 는 첫번째 렌더링할 때 크게 영향이 없는 경우 사용한다. 예를 들어 데이터를 가져오거나 state 를 리셋하거나 그런 것들을 처리할 때 사용한다.


하지만 나같은 경우에는 useEffect 에서 DOM 이 렌더링할 때 필요한 defaultKey 값을 계산해 저장하는 로직을 실행하고 있었고, 이 부분이 문제가 되어 터미널에 상단에 있는 로그가 출력된 것이었다.



방법 2: useLayoutEffect 를 사용해 컴포넌트를 지연시켜 나타내기

  useEffect 를 사용하면 UI 가 깨진 것처럼 보이는데, 이러한 현상을 useLayoutEffect 로 해결할 수 있다. 다만 useLayoutEffect 로 수정하게 되면 effect 가 실행되기 전까지는 그려낼 컴포넌트가 제대로 보이지 않게 된다. 즉 자바스크립트가 로드될 때까지 서버가 렌더링한 HTML 요소들이 제대로 보이지 않는 다는 것을 의미한다. 

  그래서 처음에 언급했던 것처럼 서버사이드 렌더링을 하는 이등은 없고, 사용자에게 멈춰있거나 제대로 표현되지 않은 UI 가 장시간 노출되게 된다. 이러한 문제를 해결하기 위해서는 클라이언트 측 JS 가 로드되고 hydrate할 때까지 렌더링할 컴포넌트를 표시하는 것을 지연시킬 수 있다. 서버에서 렌더링된 HTML에 레이아웃 이펙트가 필요한 하위 컴포넌트를 제외하려면 조건부로 렌더링할 수 있다.


나는 useEffect 에 작성한 로직을 지우고 방법 2처럼 하나의 컴포넌트 안에 부모와 자식 컴포넌트를 나눠 useLayoutEffect 에서 DOM 을 조작하도록 변경하였다.


위의 방법을 사용해 Warning 로그를 제거할 수 있었으나 아직은 리액트에 대해 알아가야할 게 많다는 생각이 들었다. 리액트에서는 SSR에 대해서 어떻게 처리하는 지도 알아야하고, useEffect 와 useLayoutEffect 에 대해서도 이슈를 해결하기 위해 간단히 알아보긴 했지만 좀 더 깊이 공부하는 게 좋을 것 같다는 생각이 들었다.

브런치는 최신 브라우저에 최적화 되어있습니다. IE chrome safari