얼추 기획이 끝나서
드디어 기능 구현을 뚱땅뚱땅 거리게 될 림졍..!
근데 아직 뭔가 렌더링 방식 관련해서 많이 부족한 거 같아서
오늘은 렌더링 방식 정리로 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를 동시에 챙기기
'React TIL' 카테고리의 다른 글
[React] Day_77 최종 프로젝트 관련 내용정리 (0) | 2025.01.08 |
---|---|
[React] Day_76 최종 프로젝트 관련 내용정리 (0) | 2025.01.07 |
[React] Day_73 심화 프로젝트 후기 (1) | 2025.01.02 |
[React] Day_72 데일리 정리 (0) | 2024.12.31 |
[React] Day_71 데일리 정리 (0) | 2024.12.30 |