HJS

렌더링 방식: SSR, SSG, CSR, ISR

Next.js는 다양한 렌더링 방식을 통해 웹 애플리케이션을 효율적으로 최적화하고 성능을 개선할 수 있습니다.
각 렌더링 방식은 다르게 작동하며, 이를 잘 활용하면 웹 애플리케이션의 성능을 크게 향상시킬 수 있습니다.


1️⃣ SSR (Server-Side Rendering)

SSR은 페이지를 서버에서 렌더링하는 방식입니다. 사용자가 페이지를 요청할 때마다 서버가 해당 페이지를 렌더링하여 HTML을 응답으로 보내며, 이를 통해 SEO 최적화와 실시간 데이터 반영이 가능합니다.

🔹예시

export default async function Page() {
  const res = await fetch('https://api.example.com/data', {
    cache: 'no-store', // 매번 서버에서 최신 데이터를 요청하도록 설정
  });
  const data = await res.json();

  return (
    <div>
      <h1>SSR 페이지</h1>
      <pre>{JSON.stringify(data, null, 2)}</pre>
    </div>
  );
}

✔️ cache: 'no-store'는 매 요청마다 서버에서 데이터를 새로 가져오도록 설정하는 방법입니다. 이 설정은 캐시를 사용하지 않고 매번 새 데이터를 요청합니다.
✔️ 실시간 데이터를 즉시 반영할 수 있어 최신 정보를 제공합니다.

❗ 매번 요청할 때마다 서버에서 렌더링되므로, 동적인 데이터에 유리하지만 서버에 부담을 줄 수 있습니다.




2️⃣ SSG (Static Site Generation)

SSG는 페이지를 빌드 시점에 미리 생성하여 정적 파일로 제공하는 방식입니다. 빌드 시점에 페이지를 미리 렌더링하고, 사용자가 페이지를 요청할 때 빠르게 정적 파일을 제공합니다.

🔹 예시

export default async function Page() {
  const res = await fetch('https://api.example.com/data', {
    cache: 'force-cache', // 빌드 시에 데이터를 캐시하고, 이후 요청에서 캐시된 데이터를 사용
  });
  const data = await res.json();

  return (
    <div>
      <h1>SSG 페이지</h1>
      <pre>{JSON.stringify(data, null, 2)}</pre>
    </div>
  );
}

✔️ cache: 'force-cache' 설정은 정적 사이트에서 한 번 생성된 데이터를 캐시하고, 이후 요청에서 캐시된 데이터를 재사용합니다. 이는 SSG에서 성능을 최적화하는 데 유용합니다.
✔️ 정적인 콘텐츠에 유리하며, 페이지가 변경되지 않는 경우에 적합합니다.

❗동적 데이터(데이터가 변경된 경우)를 다루려면 재빌드가 필요할 수 있습니다.




3️⃣ CSR (Client-Side Rendering)

CSR은 페이지가 브라우저에서 렌더링되는 방식입니다. 클라이언트는 빈 HTML 파일을 받아오고, JavaScript가 실행되면서 클라이언트 측에서 데이터를 가져와 렌더링합니다.

🔹 예시

'use client';

import { useState, useEffect } from 'react';

const Page = () => {
  const [data, setData] = useState<any>(null);

  useEffect(() => {
    fetch('https://api.example.com/data')
      .then((res) => res.json())
      .then((data) => setData(data));
  }, []);

  if (!data) return <div>Loading...</div>;

  return (
    <div>
      <h1>CSR 페이지</h1>
      <pre>{JSON.stringify(data, null, 2)}</pre>
    </div>
  );
};

export default Page;

✔️ 클라이언트에서만 렌더링되므로, 서버 부하가 줄어듭니다.
✔️ 동적인 데이터는 클라이언트 측에서 처리되며, 페이지가 로드된 후에 데이터를 불러와 표시합니다. 이는 실시간 데이터나 자주 변경되는 데이터를 다룰 때 유리합니다.

❗ 페이지가 로드되기 전 빈 HTML이 클라이언트에 전달되고, 자바스크립트가 실행되며 데이터를 가져오기 때문에 초기 로딩 시간이 길어질 수 있습니다.
SEO 최적화에는 어려움이 있을 수 있습니다. 서버에서 렌더링된 HTML이 제공되지 않기 때문에 검색엔진 크롤러가 콘텐츠를 제대로 인식하지 못할 수 있습니다.




4️⃣ ISR (Incremental Static Regeneration)

ISR은 SSG의 확장된 형태로, 페이지를 정적으로 생성하면서도 주기적으로 또는 특정 시간 후에 페이지를 재생성하는 방식입니다.

🔹 예시

export default async function Page() {
  const res = await fetch('https://api.example.com/data', {
    next: { revalidate: 60 }, // 60초마다 페이지 재생성
  });
  const data = await res.json();

  return (
    <div>
      <h1>ISR 페이지</h1>
      <pre>{JSON.stringify(data, null, 2)}</pre>
    </div>
  );
}

✔️ next: { revalidate: 60 }은 해당 페이지가 60초마다 재생성되도록 설정하는 옵션입니다. 즉, 60초마다 새 데이터를 가져와 페이지를 업데이트하게 됩니다.
✔️ ISR을 사용하면 정적 파일을 빠르게 제공하면서도, 페이지가 주기적으로 새로운 데이터로 갱신됩니다. 이는 변경이 잦은 데이터에 적합합니다.

❗ 페이지가 재생성되는 주기에 따라 데이터가 갱신되므로, 실시간으로 변하는 데이터를 처리할 때는 별로 효율적이지 않을 수 있습니다.




5️⃣ 프로덕션 환경에서 렌더링 방식 테스트

Next.js에서 SSR, SSG, ISR, CSR 렌더링 방식을 사용할 때, 이들이 실제로 어떻게 동작하는지 확인하려면 개발 모드가 아닌 프로덕션 빌드 모드에서 테스트해야 합니다.

🔹개발 모드 (npm run dev)와 프로덕션 빌드 모드 (npm run buildnpm run start)의 차이점

✅ 개발 모드 (npm run dev)

코드 변경 사항을 실시간으로 반영해주어 빠르게 개발할 수 있지만, 일부 렌더링 방식은 제대로 동작하지 않을 수 있습니다. 예를 들어, ISR(Incremental Static Regeneration) 같은 경우, 페이지가 주기적으로 갱신되는 기능을 테스트하는 데는 개발 모드에서 제한이 있을 수 있습니다.

✅ 프로덕션 모드 (npm run build 및 npm run start)

실제 서비스 환경에서 렌더링 방식이 어떻게 동작하는지 정확하게 확인할 수 있습니다. SSR이나 SSG, ISR 등은 프로덕션 환경에서 최적화된 상태로 작동하기 때문에, 실제 사용자 환경에서 어떻게 성능이 발휘되는지 파악하는 데 중요합니다.

✔️ 렌더링 방식의 정확한 동작을 확인하고 싶다면, npm run build로 빌드한 후 npm run start로 프로덕션 환경에서 실행해보는 것이 필요합니다.

개발 모드에서는 렌더링 방식이 실제 서비스 환경과 다르게 작동할 수 있기 때문에, 프로덕션 환경에서 동작을 테스트하는 것이 중요합니다.