Server-Side Rendering (SSR) - Page Router

SSR은 페이지 요청이 들어올 때마다 서버에서 HTML을 생성하는 렌더링 방식이다.
Next.js Page Router에서는 getServerSideProps를 사용해 SSR 페이지를 구현할 수 있다.
tsx
import SearchableLayout from '@/components/searchable-layout';
import style from './index.module.css';
import { ReactNode, useEffect } from 'react';
import books from '@/mock/books.json';
import BookItem from '@/components/book-items';
import { InferGetServerSidePropsType } from 'next';
// 이 함수가 존재하면 해당 페이지는 SSR로 동작한다.
// 페이지 요청 시 서버에서만 실행된다.
export const getServerSideProps = () => {
// 페이지 컴포넌트보다 먼저 실행되어, 렌더링에 필요한 데이터를 서버에서 준비한다.
const data = 'Hello';
// 반드시 { props: { ... } } 형태의 객체를 반환해야 한다.
// 반환된 props는 페이지 컴포넌트의 props로 전달된다.
return {
props: {
data,
},
};
};
// 서버에서 한 번 실행되어 HTML을 생성한다.
// 이후 브라우저에서 JS 번들이 로드되며, Hydration 과정에서 다시 실행된다.
export default function Home({
data,
}: InferGetServerSidePropsType<typeof getServerSideProps>) {
console.log(data);
// 브라우저에서만 실행된다 (Hydration 이후)
// 브라우저 전용 API(window, document 등)는 반드시 이 안에서 사용해야 안전하다
useEffect(() => {
console.log(window);
}, []);
return (
<div className={style.container}>
<section>
<h3>지금 추천하는 도서</h3>
{books.map((book) => (
<BookItem key={book.id} {...book} />
))}
</section>
<section>
<h3>등록된 모든 도서</h3>
{books.map((book) => (
<BookItem key={book.id} {...book} />
))}
</section>
</div>
);
}
// - Page Router에서 페이지별 레이아웃을 적용하기 위한 패턴
// - 페이지 컴포넌트를 인자로 받아 레이아웃으로 감싼다
Home.getLayout = (page: ReactNode) => {
return <SearchableLayout>{page}</SearchableLayout>;
};getServerSideProps
- 페이지 요청 시 서버에서 실행되며, 해당 페이지를 SSR로 동작하게 만든다.
- 반환한 props는 페이지 컴포넌트에 전달된다.
- 요청마다 실행되므로 캐싱 없이 항상 최신 데이터를 사용한다.
SSR 페이지 컴포넌트의 실행 흐름
- 서버에서 컴포넌트 실행 → HTML 생성
- 브라우저에서 HTML 수신
- JS 로드 후 Hydration → 컴포넌트 재실행
이 때문에 컴포넌트 내부에서는 window, document 같은 브라우저 전용 객체를 바로 사용할 수 없다.
브라우저 전용 코드 처리
브라우저에서만 실행되어야 하는 코드는 useEffect 내부에서 처리한다.
getServerSideProps Context 객체
getServerSideProps는 Next.js가 자동으로 context 객체를 인자로 전달한다.
tsx
export const getServerSideProps = (context) => {
const { params, query, req, res } = context;
return { props: {} };
};| 속성 | 설명 |
|---|---|
params | 동적 라우트 파라미터 ([id].tsx → params.id) |
query | URL 쿼리 스트링 (?sort=latest → query.sort) |
req | Node.js HTTP 요청 객체 |
res | Node.js HTTP 응답 객체 |
예시: 동적 라우트에서 데이터 불러오기
pages/books/[id].tsx
tsx
export const getServerSideProps = (context) => {
const { id } = context.params;
// id를 이용해 해당 책 데이터를 가져온다
const book = fetchBookById(id);
return {
props: { book },
};
};
export default function BookDetail({ book }) {
return <h1>{book.title}</h1>;
}/books/123요청 시params.id는 "123"이 된다.- getServerSideProps의
context를 활용하면 요청 정보에 따라 동적인 SSR 페이지를 구현할 수 있다.
SSR에서 실제 API 데이터 페칭
SSR에서는 페이지 요청 시점에 서버에서 API를 호출해 렌더링에 필요한 데이터를 미리 준비할 수 있다.
tsx
export const getServerSideProps = async () => {
// 서로 의존하지 않는 API는 병렬로 호출해 응답 시간을 줄일 수 있다
const [allBooks, recoBooks] = await Promise.all([
fetchBooks(),
fetchRandomBooks(),
]);
return {
props: {
allBooks,
recoBooks,
},
};
};getServerSideProps내부에서 호출된 API는 요청마다 서버에서 실행된다.- 여러 API를 호출해야 하는 경우
Promise.all을 사용해 병렬 처리할 수 있다.
쿼리 스트링 기반 검색 SSR
context.query를 사용하면 URL 쿼리 스트링을 서버에서 직접 읽을 수 있다.
이를 활용해 검색 결과 페이지를 SSR로 구현할 수 있다.
tsx
export const getServerSideProps = async (context) => {
const q = context.query.q;
const books = await fetchBooks(q as string);
return {
props: { books },
};
};/search?q=react요청 시context.query.q는 "react"가 된다.- 서버에서 검색 결과를 만들어 HTML에 포함해 응답할 수 있다.
동적 라우트 기반 상세 페이지 SSR
SSR에서는 context.params를 사용해 동적 라우트 값을 처리할 수 있다.
이를 통해 상세 페이지를 요청마다 서버에서 렌더링할 수 있다.
tsx
export const getServerSideProps = async (context) => {
const id = Number(context.params?.id);
const book = await fetchOneBooks(id);
if (!book) {
return { notFound: true };
}
return {
props: { book },
};
};/books/123요청 시context.params.id는 "123"이 된다.- 서버에서 해당 ID에 맞는 데이터를 조회한 뒤 페이지를 렌더링한다.
- 데이터가 존재하지 않는 경우 notFound: true를 반환해 404 페이지를 표시할 수 있다.
정리
- SSR은 요청 시점에 서버에서 데이터를 준비해 HTML을 생성한다.
- 검색, 상세 페이지처럼 요청마다 결과가 달라지는 화면에 적합하다.
context.query,context.params를 활용하면 동적인 SSR 페이지를 구현할 수 있다.- 요청 정보(쿠키, 헤더, 파라미터)를 기반으로 한 개인화 UI 구현에 적합하다.
SSR은 “요청마다 서버에서 데이터를 준비해 HTML을 생성한다”는 특성 때문에
검색, 상세, 개인화 페이지처럼 요청별로 결과가 달라지는 화면에 특히 잘 어울린다.
개인화 페이지란,
같은 URL이라도 요청한 사용자에 따라 서로 다른 데이터를 보여주는 페이지를 의미하며, 요청 정보를 바로 활용할 수 있는 SSR 방식이 특히 적합하다.