SSR 환경에서 Suspense와 useSuspenseQuery가 오작동하는 문제 해결하기

태현·2023년 12월 2일
4

개요

크레센도 프로젝트는 Next.js에서 React Query를 통해 비동기 작업을 관리하고 useSuspenseQuery 와 React의 Suspense를 사용하여 데이터 페칭 로딩 상태를 선언적으로 관리합니다.

문제의 발단

개발 초기 단계에서 useQueryCSR 방식으로 데이터를 가져오는 페이지가 하나 있었습니다. 로컬에서 빌드할 때는 아무런 문제가 발생하지 않았지만 CI 과정에서 해당 페이지에 접근하기 전에 서버에서 페칭을 시도해버려 에러가 발생하였습니다.

보일러 플레이트를 구성할 때부터 전역 queryClientsuspense: true로 설정하였고, 해당 페이지는 <Suspense /> 로 감싸지 않았습니다.

그래서 저는 근본적인 원인을 찾지 못하고 진입점 파일useIsMounted 라는 훅을 적용하여 임시적으로 해결을 하였습니다.


당시 PR 내용입니다.
결국 어떠한 사이드 이펙트를 가져왔을지 설명하려고 합니다.

임시 해결을 통해 발생한 사이드 이펙트


저는 어느 순간부터 TTL이 저하되고 서버에서 정적 생성된 HTML 페이지가 빈 화면을 렌더링 하는 문제를 인지하였습니다. 빌드 시 정적 생성된 페이지(SSG), 동적으로 생성하는 페이지(SSR) 모두 적용되지 않았습니다.

Next.js를 사용하는 의미가 퇴색된다고 생각해 해결 방법을 찾기 위해 여러 시도를 해보았습니다.

해결 과정

앞서 적용한 useIsMounted를 제거하고 에러 로그를 살펴보니 공통점이 있었습니다. useSuspenseQuery 또는 <Suspense /> 로 감싼 페이지에서만 앞에 언급했던 문제가 발생하는 것이였습니다.
Suspense와 SSR 관련 키워드로 검색을 하였고 다음과 같은 블로그 포스팅을 찾을 수 있었습니다.

공통적으로 언급한 문제점이 있습니다.

  1. Suspense는 서버 사이드에서 지원되지 않는다.
  2. 서버에서 렌더링 될 때 Suspense를 읽는다.

해당 링크를 참고하여 커스텀한 Suspense를 작성하였습니다.

/**
 * Suspense가 서버사이드 렌더링 과정에서 거치지 않도록 커스텀한 컴포넌트입니다.
 */

const SSRSafeSuspense = (props: React.ComponentProps<typeof Suspense>) => {
  const { fallback } = props;
  const isMounted = useIsMounted();

  if (isMounted) {
    return <Suspense {...props} />;
  }
  return <>{fallback}</>;
};

const Home = () => {
  return (
    <>
      <SSRSafeSuspense fallback={<Loader />}>
        <ToDoList />
      </SSRSafeSuspense>
    </>
  );
};

서버 사이드 환경에서는 fallback만을 렌더링하고 Suspense 를 거치지 않도록 하였습니다.

마무리하며

드디어 Suspense를 빈 HTML을 반환하지 않고 정상적으로 사용할 수 있게 되었습니다.

저와 같이 Suspense와 SSR 환경의 문제점을 겪으시는 분들께서 이 글을 통해서 해결하셨으면 좋겠습니다. 감사합니다.

4개의 댓글

comment-user-thumbnail
2024년 3월 21일

‘useSuspenseQuery’ 관련하여 읽을 내용이 많지 않은데 큰 도움이 되었습니다. 감사합니다 🙂

1개의 답글
comment-user-thumbnail
2024년 3월 28일

압도적 감사...

1개의 답글