본문 바로가기
React TIL

[React] Day_75 데일리 정리

by 림졍 2025. 1. 6.

 

 

얼추 기획이 끝나서

드디어 기능 구현을 뚱땅뚱땅 거리게 될 림졍..!

근데 아직 뭔가 렌더링 방식 관련해서 많이 부족한 거 같아서

오늘은 렌더링 방식 정리로 TIL를 적어보고자 한다!

그럼 가시죠.

 

강의정리 - Next.js 렌더링 최적화와 하이브리드 패턴

렌더링 패턴 제대로 이해하기 (도전.)

 

빌드 실패 case - SSG 에서 라우트 핸들러 호출

 

SSG(Static Site Generation) 환경에서 route handler(API 라우트) 호출 하면 빌드에 실패함. (= 빌드 실패의 원인요소)

(∵ SSG는 빌드 시점에 실행되지만, route handler는 빌드 단계에서 활성화되지 않기 때문)

 

- 빌드 실패

export default async function Home() {
  const data = await fetch("http://localhost:3000/api/json").then(
    (res) => res.json(),
  );
  console.log("data:", data);
  return <>{data.title}</>;
}
  • localhost:3000/api/json은 route handler 호출이므로, 빌드 시 존재하지 않는다.

 

- 빌드 성공

 

export default async function Home() {
  const data = await fetch("https://jsonplaceholder.typicode.com/todos/1").then(
    (res) => res.json(),
  );
  console.log("data:", data);
  return <>{data.title}</>;
}
  • 직접 외부 API 호출로 대체하여 빌드 시에도 정상 동작

 

결론 : router handler 대신 외부 API를 호출하는 방식 채택하기 (in SSG)

 

 

In SSR - 서버 컴포넌트의 캐싱 (By Router Cache)

 

최신 데이터와 SEO를 모두 만족하는 방식이지만… SSR도 클라이언트 캐시(Router Cache)에 저장된다!

 

뒤로가기 후 같은 SSR 페이지를 재방문하면 서버에서 다시 실행X.

If 최신 데이터를 보장하고 싶다 → Router Cache 무효화(revalidate)필요

 

- 중요!

fetch에서 no-store를 설정해도 서버 캐시만 막을 뿐, Router Cache(클라이언트 캐시)에는 영향없다!

 

- Router Cache 무효화(revalidate) 코드 예시

 

"use server";
import { revalidatePath, revalidateTag } from "next/cache";

type RevalidateOptions =
  | { type: "path"; path: string; kind: "page" | "layout" }
  | { type: "tag"; tag: string };

/**
 * revalidate 함수: 매개변수에 따라 revalidatePath 또는 revalidateTag 실행
 * @param options - revalidation 옵션 (path 또는 tag)
 */
export const revalidate = (options: RevalidateOptions): void => {
  if (options.type === "path") {
    // 특정 경로를 revalidate
    revalidatePath(options.path, options.kind);
  } else if (options.type === "tag") {
    // 특정 태그를 revalidate
    revalidateTag(options.tag);
  }
};
  • revalidatePath: 특정 경로를 재검증(갱신)
  • revalidateTag: 특정 태그로 그룹화된 데이터를 재검증
  • 최신 데이터를 보여줘야 한다면 SSR 페이지에 Router Cache 무효화 로직 추가

 

SSG + SSR 하이브리드 패턴 - generateStaticParams

 

generateStaticParams = SSG와 SSR을 혼합하는 하이브리드 패턴

path parameter가 존재하는 동적 페이지를 빌드 타임에 미리 캐싱이 가능

자주 바뀌지 않는 데이터에 사용하면 효과적!

 

- 사용 예시 (동적 페이지)

export function generateStaticParams() {
  return [{ id: "1" }, { id: "2" }, { id: "3" }];
}
  • id 1, 2, 3에 해당하는 동적 페이지를 빌드 타임에 미리 생성
  • 이후 해당 id를 가진 페이지에 접근할 때는 SSR이 아닌 정적 페이지 반환됨

 

- 주의할 점

fetch 옵션을 no-store에서 force-cache로 변경해야 빌드 타임 캐싱 제대로 이루어짐

 

 

SSG + CSR 하이브리드 패턴 - Tanstack Query with Next.js

 

Tanstack Query + Next.js를 활용한 SSG + CSR 패턴 → SEO와 UX를 동시에 향상시킵니다.

 

장점

  • SEO: 일반 SSR 페이지처럼 100% 프리렌더링 효과를 가짐
  • UX: 캐시된 데이터를 활용해 뒤로가기 시에도 로딩 없이 즉시 화면 보여줌
    +) 백그라운드에서 최신 데이터 자동으로 가져옴

 

- 적용 예시

// app/posts/page.jsx
import {
  dehydrate,
  HydrationBoundary,
  QueryClient,
} from '@tanstack/react-query'
import Posts from './posts'

export default async function PostsPage() {
  const queryClient = new QueryClient()

  // prefetchQuery: 미리 데이터 불러오기
  await queryClient.prefetchQuery({
    queryKey: ['posts'],
    queryFn: getPosts,
  })

  return (
    // 클라이언트에서 캐시를 hydration(수화)하여 즉시 사용
    <HydrationBoundary state={dehydrate(queryClient)}>
      <Posts />
    </HydrationBoundary>
  )
}
  • dehydrate: 서버에서 미리 fetch한 데이터를 직렬화
  • HydrationBoundary: 클라이언트에서 해당 데이터를 수화(hydrate) 하여 즉시 사용 가능

 

좋은 EU는?

  • SEO 효과: 페이지는 서버에서 미리 렌더링
  • UX 향상: 클라이언트는 캐시된 데이터를 활용해 로딩 없이 즉시 화면을 보여줌

결론 = 빠른 첫 로딩 + 최신 데이터 모두 잡을 수 있는 최고의 조합

 

마무리

  • SSG에서는 route handler 대신 직접 API 호출을 사용하기
  • SSR 페이지의 최신성을 보장을 위해선 Router Cache 무효화(revalidate) 코드 추가하기
  • generateStaticParams 사용하면 동적 페이지도 SSG처럼 정적으로 빌드 가능!
  • Tanstack Query와 Hydration을 사용해 SSG + CSR 패턴으로 SEO와 UX를 동시에 챙기기
728x90
반응형