본문 바로가기

[Next.js] CSR에서 SSR로 마이그레이션 하기

곰곰킴 2024. 7. 11.

개요

Next.js 에서 애플리케이션의 성능과 SEO를 개선하기 위해 클라이언트 사이드 렌더링(CSR)에서 서버 사이드 렌더링(SSR)으로 전환한 경험을 공유합니다.

왜 SSR인가?

  • 성능: SSR은 서버에서 콘텐츠를 미리 렌더링하여 첫 번째 바이트 시간(TTFB)을 크게 줄일 수 있습니다.
  • SEO: 검색 엔진이 미리 렌더링된 페이지를 쉽게 인덱싱할 수 있어 애플리케이션의 검색 엔진 순위가 향상됩니다.
  • 사용자 경험: SSR은 초기 로드 시간을 단축하여 전반적인 사용자 경험을 향상시킵니다.

도전 과제

  1. 데이터 페칭: 서버에서 데이터를 페칭하고 클라이언트에 전달하는 것이 주요 과제 중 하나였습니다.
  2. 상태 관리: 서버 렌더링된 상태와 클라이언트 렌더링된 상태 간의 일관성을 유지하는 것이 중요했습니다.
  3. 조건부 렌더링: 특정 컴포넌트가 클라이언트 사이드 상태에 의존하는 시나리오를 처리해야 했습니다.

구현 단계

1. getStaticProps 및 getServerSideProps를 사용한 데이터 페칭

Next.js는 정적 생성(getStaticProps) 및 서버 사이드 렌더링(getServerSideProps)을 제공합니다.
getServerSideProps를 사용하는 예제입니다

export async function getServerSideProps() {
  const res = await fetch('https://api.example.com/data');
  const data = await res.json();

  return {
    props: {
      initialData: data,
    },
  };
}

const HomePage = ({ initialData }) => {
  return (
    <div>
      <h1>서버 사이드 렌더링된 페이지</h1>
      <pre>{JSON.stringify(initialData, null, 2)}</pre>
    </div>
  );
};

export default HomePage;

2. React Query를 사용한 상태 관리

서버 상태를 관리하고 캐싱, 페칭, 동기화를 처리하기 위해 React Query를 사용했습니다.
React Query를 SSR과 통합하는 방법입니다

import { Hydrate, QueryClient, QueryClientProvider } from 'react-query';
import { useState } from 'react';

function MyApp({ Component, pageProps }) {
  const [queryClient] = useState(() => new QueryClient());

  return (
    <QueryClientProvider client={queryClient}>
      <Hydrate state={pageProps.dehydratedState}>
        <Component {...pageProps} />
      </Hydrate>
    </QueryClientProvider>
  );
}

export default MyApp;
// pages/index.js
import { dehydrate, QueryClient, useQuery } from 'react-query';

export async function getServerSideProps() {
  const queryClient = new QueryClient();

  await queryClient.prefetchQuery('data', async () => {
    const res = await fetch('https://api.example.com/data');
    return res.json();
  });

  return {
    props: {
      dehydratedState: dehydrate(queryClient),
    },
  };
}

const HomePage = () => {
  const { data } = useQuery('data', async () => {
    const res = await fetch('https://api.example.com/data');
    return res.json();
  });

  return (
    <div>
      <h1>React Query와 함께하는 서버 사이드 렌더링된 페이지</h1>
      <pre>{JSON.stringify(data, null, 2)}</pre>
    </div>
  );
};

export default HomePage;
반응형

3.로딩 상태를 위한 Skeleton View 구현

데이터를 페칭하는 동안 사용자 경험을 개선하기 위해 Skeleton View를 구현했습니다.
이 placeholder 요소는 콘텐츠가 로드 중임을 시각적으로 나타냅니다.

import styled, { keyframes } from 'styled-components';

const gradient = keyframes`
  0% {
    background-position: 0% 100%;
  }
  100% {
    background-position: 100% 0%;
  }
`;

const SkeletonContainer = styled.div`
  display: flex;
  flex-direction: column;
  align-items: center;
  width: 100vw;
  height: 100vh;
  padding: 20px;
`;

const SkeletonElement = styled.div`
  width: ${(props) => props.width || '80%'};
  height: ${(props) => props.height || '20px'};
  margin-top: 20px;
  border-radius: 4px;
  background: linear-gradient(
    -45deg,
    #f0f0f0,
    #e0e0e0,
    #f0f0f0,
    #e0e0e0
  );
  background-size: 400% 400%;
  animation: ${gradient} 1.5s ease-in-out infinite;
`;

const SkeletonView = () => (
  <SkeletonContainer>
    <SkeletonElement height="50px" />
    <SkeletonElement height="200px" />
    <SkeletonElement height="200px" />
    <SkeletonElement height="50px" />
  </SkeletonContainer>
);

export default SkeletonView;

4. Fallback 상태 처리

마지막으로, 메인 페이지를 업데이트하여 데이터 페칭 중에 Skeleton View를 표시합니다.

// pages/index.js
import { useRouter } from 'next/router';
import SkeletonView from '../components/SkeletonView';

const HomePage = ({ initialData }) => {
  const router = useRouter();

  if (router.isFallback) {
    return <SkeletonView />;
  }

  return (
    <div>
      <h1>서버 사이드 렌더링된 페이지</h1>
      <pre>{JSON.stringify(initialData, null, 2)}</pre>
    </div>
  );
};

export default HomePage;

결론

우리의 React 애플리케이션에서 CSR에서 SSR로 전환한 것은 성능과 SEO를 개선하기 위한 중요한 단계였습니다. Next.js와 React Query를 활용하여 데이터 페칭과 상태 관리의 도전 과제를 해결할 수 있었습니다. Skeleton View를 구현하여 로딩 상태 동안 사용자가 더 나은 경험을 할 수 있도록 했습니다. 이 전환은 애플리케이션의 효율성을 향상시킬 뿐만 아니라 사용자 경험도 개선했습니다.

참고자료

Next.js Documentation
react-router-dom
데이터 prefetch

댓글