[FDBS] Next.js static 페이지에 suspense 적용하기(with SWR)

Jay ·2022년 11월 4일
0

1.Goal

SSG(static-site-generation)으로 Pre-rendered 된 페이지에서
CSR로 불러오는 아이템 목록 컴포넌트에 Suspense 적용하기

1) 왜 CSR인가?

전체를 SSR로 만드는 것보다
SSG를 기본으로 CSR로 request가 있을때마다 일부 데이터를 보내주는게
더 효율적이고 서버부담이 적음.

2) Suspense?

비동기 처리를 우아하게.
Cumulative Layout shift를 손쉽게 방지할 수 있다.
https://velog.io/@sun1301/FDBS-isValidating-and-loading

2. 적용 이유

1) 에러, 상태 추적 용이

컴포넌트의 Depth가 늘어남에 따라 어느 컴포넌트에서 Error가 발생하는지. 로딩이 어느 단계에서 발생되는지 추적하는게 힘들어졌다.

2) 관심사의 분리

isLoading 등으로 로딩상태를 일일이 처리하다보니 코드가 직관적이지 않고 지저분해지게 되었음.
관심사의 분리가 필요.

3. 적용 과정

//적용 이전
const FictionsWithParams: NextPage<FictionsResponse> = ({
  keywords,
  categories,
  nationalities,
}) => {
  return (
    ...
	{!isLoading &&
        <FictionList
          checkedItems={checkedItems}
          checkedNationalities={checkedNationalities}
          checkedGenres={checkedGenres}
          checkedSortings={checkedSortings}
          isChecked={isChecked}
        />
	}
    ...
    )
}
///적용 이후

//Suspense는 SSR 미지원. Dynamic하게 import.
const DynamicFictionListWrapper = dynamic(
  () => import(`@components/fictionListWrapper`),
  {
    suspense: true,
  }
);

const FictionsWithParams: NextPage<FictionsResponse> = ({
  keywords,
  categories,
  nationalities,
}) => {
  return (
    ...
      <ErrorBoundary>
        <Suspense
          fallback={
            <FadeLoader
              className=" mx-auto"
              height={15}
              width={5}
              radius={2}
              margin={2}
              color={"#000000"}
            />
          }
        >
          //FictionList를 DynamicFictionList 안으로
          <DynamicFictionListWrapper
            checkedItems={checkedItems}
            checkedNationalities={checkedNationalities}
            checkedGenres={checkedGenres}
            checkedSortings={checkedSortings}
            isChecked={isChecked}
          />
        </Suspense>
      </ ErrorBoundary>
	}
    ...
    )
}

problems

1. Suspense는 기본적으로 SSR을 미지원. Next.js의 hydration과 충돌.

-> 컴포넌트에 Dynamic import 적용.
-> SWR suspense opion 사용.

2. Error: This Suspense boundary received an update before it finished hydrating. This caused the boundary to switch to client rendering. The usual way to fix this is to wrap the original update in startTransition.

-> key값이 null일때 empty data를 return해야함.
-> SWR이 Suspense와 동기적으로 데이터를 읽는 rule을 위반?

//해결책
const [ready, setReady] = useState(false)
const { data } = useSWR(ready ? '/api' : null, { suspense: true })  // <- we can't throw promise here!
useEffect(() => { setReady(true) }, [])
console.log(data)

https://github.com/vercel/swr/pull/357

3. state 변경마다 router.replace(shallow:true) 작동시 무한루프 발생.

-> shallow option을 넣더라도 리렌더링 발생.
-> useEffect를 컴포넌트 구조 최상층으로 올리고 종속성 배열을 isChecked로 변경.
-> useEffect를 올바른 위치에서, 종속성 배열을 정확하게.

profile
Jay입니다.

0개의 댓글