Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

메인페이지 SSR 수정 #325

Merged
merged 10 commits into from
Aug 14, 2024
Merged

메인페이지 SSR 수정 #325

merged 10 commits into from
Aug 14, 2024

Conversation

bomi8489
Copy link
Contributor

@bomi8489 bomi8489 commented Aug 10, 2024

📑 이슈 번호

#324

🚧 구현 내용

메인페이지 SSR이 정상적으로 동작하지 않아 개선을 시도중입니다.

발견한 원인 및 이슈

1. Mount 이후 렌더링되는 ThemeProvider

기존의 최상단 layout.tsx에서 Header를 비롯한 메인화면의 요소들이 다크모드 적용을 위한 next-themeThemeProvider로 감싸져 있었습니다.

'use client'

import { useEffect, useState } from 'react'
import { ThemeProvider } from 'next-themes'
import { ThemeProvider as NextThemesProvider } from 'next-themes'

const Providers = ({ children }: { children: React.ReactNode }) => {
  const [isMount, setMount] = useState(false)

  useEffect(() => {
    setMount(true)
  }, [])

  if (!isMount) {
    return null
  }

  return <NextThemesProvider attribute="class">{children}</NextThemesProvider>
}

export default Providers

개발하던 당시 다크모드 상태에서 리렌더링이 발생하거나 새로고침을 하게 될 시 서버에서 다크모드여부를 판별하지 못해 깜빡임 현상이 발생하였고 Providers가 마운트되었을때 렌더링을 하게 코드를 작성하여 깜빡임 현상을 없애긴 했지만 이로인해 Providers 하위의 요소들은 서버사이드에서 렌더링을 할 수가 없게 되었습니다.

image

변경사항

  • Providers에서 컴포넌트 마운트가 되었을시에만 ThemeProvider를 렌더링하던 코드를 다시 이전으로 되돌렸습니다.
'use client'

import { ThemeProvider as NextThemesProvider } from 'next-themes'

const Providers = ({ children }: { children: React.ReactNode }) => {
  return <NextThemesProvider attribute="class">{children}</NextThemesProvider>
}

export default Providers



2. page.tsx에서 사용하는 useSearchParams

useSearchParams훅을 사용하는 컴포넌트가 포함된 페이지는 클라이언트사이드에서만 렌더링되며 요소들이 서버사이드에서 미리생성되지 않았습니다.

function Home() {
  const { sort, sortIndex, handleSortChange } = useSortParam('space') // useSearchParams를 사용하는 훅
  const { category, categoryIndex, handleCategoryChange } =
    useCategoryParam('all') // useSearchParams를 사용하는 훅

  return ( /***/ )
}

변경사항

useSearchParams를 사용하는 컴포넌트는 따로 모듈로 뺀다음 부모 컴포넌트에서 Suspense바운더리에 넣어 주었습니다.



3. 메인화면 인기링크리스트 prefetching

기존의 클라이언트 사이드에서 데이터를 fetch하던 메인화면의 인기링크리스트를 react-query의 Hydration을 사용하여 서버사이드에서 prefetch 하여 SSR을 적용하였습니다.

React-query Hydration의 동작과정은 다음과 같습니다.

  1. getQueryClient를 사용해 QueryClient를 가져온다.
  2. QueryClientprefetchQuery 메소드를 사용하여 데이터를 프리페치하고 완료될 때까지 기다린다.
  3. QueryClientdehydrate 메소드를 활용하여 dehydrate 한다.
  4. 데이터를 사용할 컴포넌트를 <HydrationBoundary>로 랩핑하고 dehydrate된 상태값을 props로 넘겨준다.
  5. JS가 실행되면서 <HydrationBoundary> 컴포넌트가 실행되고 클라이언트의 QueryClientdehydrate된 상태값을 다시 넣어 hydrate 한다. 즉 클라이언트에서도 서버에서 prefetching 한 데이터가 QueryClient안에 캐싱되게 된다.

코드

// HydratePopularLinkList 컴포넌트
const HydratePopularLinkList = async () => {
  const queryClient = getQueryClient()
  await queryClient.prefetchQuery({
    queryKey: ['PopularLinks'],
    queryFn: fetchGetPopularLinks,
  })

  return (
    <HydrationBoundary state={dehydrate(queryClient)}>
      <PopularLinkList />
    </HydrationBoundary>
  )
}
// PopularLinkList 컴포넌트
const PopularLinkList = () => {
  const { data } = useQuery({
    queryKey: ['PopularLinks'],
    queryFn: fetchGetPopularLinks,
  })
  return ( /*...*/ )
}

결과

스크린샷 2024-08-14 오후 2 47 39

link-pre-fetching

🚨 특이 사항

@bomi8489 bomi8489 added the 🔨 Refactor 리팩토링 label Aug 10, 2024
@bomi8489 bomi8489 self-assigned this Aug 10, 2024
@bomi8489 bomi8489 merged commit a5b7ab0 into develop Aug 14, 2024
1 check passed
@bomi8489 bomi8489 deleted the feat/#324/refactor-main-ssr branch August 14, 2024 06:15
@bomi8489 bomi8489 changed the title 메인페이지 SSR 적용 (수정중) 메인페이지 SSR 적용 (1/2) Aug 14, 2024
@bomi8489 bomi8489 changed the title 메인페이지 SSR 적용 (1/2) 메인페이지 SSR 수정 Aug 29, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
🔨 Refactor 리팩토링
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant