SSR (Server-Side Rendering)
1. 기존 리액트 앱의 동작 방식
- 불러온 데이터를 보관할 State 생성
- 데이터 페칭 함수 생성
- 컴포넌트 마운트 시점에 fetchData 호출
- 데이터 로딩중일때의 예외처리
💡 문제점
클라이언트에서 데이터를 요청하고 로딩하는 데 오랜 시간이 걸림초기 접속 시, 화면이 렌더링되기 전에 필요한 데이터를 받아와야 하기 때문에 UX가 저하됨
2. SSR (Server-Side Rendering)이란?
- 서버에서 미리 HTML을 생성하여 클라이언트에 전달하는 방식
- 요청이 들어올 때마다 서버에서 렌더링을 수행하고, 완성된 HTML을 반환함
- SEO 최적화 및 초기 로딩 속도 개선에 유리함
3. SSR의 동작 방식
- 클라이언트가 페이지를 요청함
- 서버에서 데이터 요청 및 페이지 렌더링을 수행함
- 완성된 HTML을 클라이언트에 전달함
- 클라이언트에서 React가 Hydration(재활성화)하여 인터랙티브한 요소를 추가함
4. React App과 Next App 비교
5. Next.js에서 SSR 사용하기
Next.js에서는 getServerSideProps 함수를 사용하여 SSR을 구현할 수 있음.
예제: SSR을 이용한 도서 목록 페이지
📌 getServerSideProps 함수 활용
- 넥스트에서 약속된 이름의 함수로 만들어서 내보내면 해당 페이지는 SSR로 동작하도록 자동으로 설정된다.
- 컴포넌트보다 먼저 실행되어서, 컴포넌트에 필요한 데이터를 불러오는 함수
// index.tsx
import SearchableLayout from "@/components/searchable-layout";
import style from "./index.module.css";
import { ReactNode } from "react";
import books from "@/mock/books.json";
import BookItem from "@/components/book-item";
import { InferGetServerSidePropsType } from "next";
import fetchBooks from "@/lib/fetch-books";
import fetchRandomBooks from "@/lib/fetch-random-books";
export const getServerSideProps = async () => {
// 데이터 병렬 호출 (Promise.all을 사용하여 비동기 요청을 동시에 실행)
const [allBooks, recoBooks] = await Promise.all([
fetchBooks(),
fetchRandomBooks(),
]);
return {
props: {
allBooks,
recoBooks,
},
};
};
// InferGetServerSidePropsType : getServerSideProps함수의 반환값 타입을 자동으로 추론해주는 타입
export default function Home({
allBooks,
recoBooks,
}: InferGetServerSidePropsType<typeof getServerSideProps>) {
return (
<div className={style.container}>
<section>
<h3>지금 추천하는 도서</h3>
{recoBooks.map((book) => (
<BookItem key={book.id} {...book} />
))}
</section>
<section>
<h3>등록된 모든 도서</h3>
{allBooks.map((book) => (
<BookItem key={book.id} {...book} />
))}
</section>
</div>
);
}
Home.getLayout = (page: ReactNode) => {
return <SearchableLayout>{page}</SearchableLayout>;
};
❌ 데이터 직렬 호출
fetchBooks() 완료 후 → fetchRandomBooks() 실행
- 각 요청이 순차적으로 처리되어 전체 로딩 시간이 증가함
fetchBooks() -> fetchRandomBooks() -> 렌더링
✅ 데이터 병렬 호출
- Promise.all()을 사용하여 두 개의 요청을 동시에 실행함
- Promise.all : 인수로 전달한 배열 안에 있는 모든 비동기 함수를 동시에 실행시키는 메서드
- 전체 데이터 로딩 속도가 단축됨
fetchBooks() + fetchRandomBooks() (동시 실행) -> 렌더링
📌 fetchBooks 함수 (API 호출)
// fetch-books.ts
import { BookData } from "@/types";
// 비동기로 반환하기 때문에 비동기 결과를 의미하는 promise의 제네릭으로 bookdata타입 불러와서 정의
export default async function fetchBooks(q?: string): Promise<BookData[]> {
let url = `http://localhost:12345/book`;
if (q) {
url += `/search?q=${q}`; // 쿼리스트링을 사용하여 검색 기능 구현
}
try {
const response = await fetch(url);
if (!response.ok) {
throw new Error();
}
return await response.json();
} catch (err) {
console.error(err);
return [];
}
}
📌 검색 기능을 포함한 SSR 구현 (쿼리스트링 활용)
// search/index.tsx
import SearchableLayout from "@/components/searchable-layout";
import { ReactNode } from "react";
import BookItem from "@/components/book-item";
import { GetServerSidePropsContext, InferGetServerSidePropsType } from "next";
import fetchBooks from "@/lib/fetch-books";
// 쿼리스트링으로 전달된 검색어 읽어오기
// context: 현재 브라우저로부터 받은 모든 요청이 들어있음
export const getServerSideProps = async (
context: GetServerSidePropsContext
) => {
const q = context.query.q; // 쿼리스트링을 이용하여 검색어를 받아옴
const books = await fetchBooks(q as string);
return {
props: { books },
};
};
export default function Page({
books,
}: InferGetServerSidePropsType<typeof getServerSideProps>) {
return (
<div>
{books.map((book) => (
<BookItem key={book.id} {...book} />
))}
</div>
);
}
Page.getLayout = (page: ReactNode) => {
return <SearchableLayout>{page}</SearchableLayout>;
};
6. SSR의 장점과 단점
✅ 장점
- SEO 최적화: 검색 엔진이 HTML을 읽을 수 있어 검색 노출이 유리함
- 빠른 초기 렌더링: 서버에서 미리 렌더링된 HTML을 제공하여 사용자 경험 개선
- 데이터 최신성 유지: 요청 시마다 최신 데이터를 받아옴
❌ 단점
- 서버 부하 증가: 모든 요청에서 서버가 렌더링을 수행해야 하므로 성능 이슈 발생 가능
- 캐싱 어려움: 동적인 페이지 특성상 캐싱이 쉽지 않음
- 긴 응답 시간: 서버에서 렌더링하는 과정이 필요하여 응답 속도가 느려질 수 있음
7. 배운점
- SSR은 SEO 및 초기 로딩 속도 개선에 강점이 있는 렌더링 방식
- Next.js에서 getServerSideProps를 활용하여 손쉽게 SSR을 구현 가능
- 데이터 요청 시 병렬 호출 (Promise.all) 을 활용하여 성능 최적화 필요
- 서버 부하와 캐싱 문제를 고려하여 CSR, SSG와 혼합하여 사용하는 것이 중요함
'Frontend > Next.js' 카테고리의 다른 글
[Next.js/Page Router] 4. SEO 설정 및 Next.js 프로젝트 배포하기 (0) | 2025.02.05 |
---|---|
[Next.js/Page Router] 3. ISR (Incremental Static Regeneration) (0) | 2025.02.04 |
[Next.js/Page Router] 3. SSG (Static Site Generation) (0) | 2025.02.03 |
[Next.js] 1. Next.js 프로젝트와 Page Router 기본 (1) | 2025.02.01 |
[Next.js] 0. Next.js 개요 (1) | 2025.01.21 |