Next.js에서 getServerSideProps를 활용한 도서 데이터 관리

2025. 3. 2. 22:46프론트엔드/Next JS

728x90

Next.js에서 getServerSideProps를 활용한 도서 데이터 관리

1. 개요

Next.js에서는 서버 측에서 데이터를 가져와 페이지를 렌더링할 때 getServerSideProps를 사용할 수 있습니다. 이번 글에서는 도서 데이터를 가져오는 웹 애플리케이션을 예제로, getServerSideProps를 활용하여 서버에서 데이터를 가져와 렌더링하는 방법을 정리합니다.

이 글에서 다룰 주요 내용은 다음과 같습니다.

  • getServerSideProps를 사용하여 서버에서 데이터를 불러오기
  • fetchBooks, fetchRandomBooks 등을 활용한 API 호출
  • BookItem 컴포넌트를 이용한 데이터 렌더링
  • 개별 도서 페이지(/book/[id])에서 특정 도서의 데이터를 불러오는 방법

2. getServerSideProps를 사용한 데이터 패칭

Next.js에서 getServerSideProps는 서버에서 데이터를 가져온 후, 페이지를 렌더링할 때 사용됩니다. 이를 활용하면 클라이언트에서 API를 호출하지 않고, 페이지 요청 시 서버에서 데이터를 미리 가져와 렌더링할 수 있습니다.

📌 코드 예시 (pages/index.tsx)

import SearchableLayout from "@/components/searchable-layout";
import style from "./index.module.css";
import { ReactNode } from "react";
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 () => {
  const [allBooks, recoBooks] = await Promise.all([
    fetchBooks(),
    fetchRandomBooks(),
  ]); // 병렬 처리!

  return {
    props: {
      allBooks,
      recoBooks,
    },
  };
};

// 홈 페이지 컴포넌트
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>;
};

✨ 설명

  • getServerSideProps에서 fetchBooks()와 fetchRandomBooks()를 호출하여 도서 데이터를 가져옵니다.
  • 두 개의 API 요청을 병렬(Promise.all)로 처리하여 성능을 최적화합니다.
  • 가져온 데이터를 Home 컴포넌트에서 렌더링하여 화면에 표시합니다.
  • Home.getLayout을 사용하여 페이지 전체에 SearchableLayout을 적용합니다.

 


3. 도서 데이터 가져오기 (API 호출)

도서 데이터를 가져오는 함수는 lib/fetch-books.ts, lib/fetch-random-books.ts 등으로 분리하여 관리할 수 있습니다.

📌 모든 도서 목록 가져오기 (lib/fetch-books.ts)

import { BookData } from "@/types";

// 모든 책 데이터 불러오기
export default async function fetchBooks(
  q?: string // 검색어 옵션 (q)
): Promise<BookData[]> {
  let url = "http://localhost:12345/book";
  if (q) {
    url += `/search?q=${q}`;
  }

  try {
    const response = await fetch(url);
    return await response.json();
  } catch (err) {
    console.error(err);
    return [];
  }
}

📌 추천 도서 가져오기 (lib/fetch-random-books.ts)

import { BookData } from "@/types";

export default async function fetchRandomBooks(): Promise<BookData[]> {
  const url = "http://localhost:12345/book/random";

  try {
    const response = await fetch(url);
    return await response.json();
  } catch (err) {
    console.error(err);
    return [];
  }
}

✨ 설명

  • fetchBooks()는 기본적으로 모든 도서를 가져오지만, q 값을 전달하면 검색 결과를 반환합니다.
  • fetchRandomBooks()는 특정 API 엔드포인트(/book/random)를 호출하여 추천 도서를 가져옵니다.
  • try-catch 블록을 사용하여 API 호출이 실패할 경우 빈 배열을 반환하도록 처리합니다.

4. 개별 도서 상세 페이지 (/book/[id]) 구현

특정 도서의 정보를 가져와 상세 페이지를 구성하려면, getServerSideProps를 사용하여 ID에 해당하는 도서 데이터를 가져와야 합니다.

📌 코드 예시 (pages/book/[id].tsx)

import { GetServerSidePropsContext, InferGetServerSidePropsType } from "next";
import style from "./[id].module.css";
import fetchOneBook from "@/lib/fetch-one-book";

// 특정 도서 가져오기
export const getServerSideProps = async (context: GetServerSidePropsContext) => {
  const id = context.params!.id;
  const book = await fetchOneBook(Number(id));

  return {
    props: {
      book,
    },
  };
};

// 도서 상세 페이지 컴포넌트
export default function Page({
  book,
}: InferGetServerSidePropsType<typeof getServerSideProps>) {
  if (!book) return "문제가 발생했습니다. 다시 시도하세요.";

  const { title, subTitle, description, author, publisher, coverImgUrl } = book;

  return (
    <div className={style.container}>
      <div
        className={style.cover_img_container}
        style={{ backgroundImage: `url('${coverImgUrl}')` }}
      >
        <img src={coverImgUrl} alt={title} />
      </div>
      <div className={style.title}>{title}</div>
      <div className={style.subTitle}>{subTitle}</div>
      <div className={style.author}>
        {author} | {publisher}
      </div>
      <div className={style.description}>{description}</div>
    </div>
  );
}

📌 개별 도서 데이터 가져오기 (lib/fetch-one-book.ts)

import { BookData } from "@/types";

export default async function fetchOneBook(id: number): Promise<BookData | null> {
  const url = `http://localhost:12345/book/${id}`;

  try {
    const response = await fetch(url);
    return await response.json();
  } catch (err) {
    console.error(err);
    return null;
  }
}

✨ 설명

  • getServerSideProps에서 context.params!.id를 통해 URL의 id 값을 가져옵니다.
  • fetchOneBook(id)를 호출하여 해당 도서의 데이터를 가져옵니다.
  • book 데이터가 없을 경우, 사용자에게 "문제가 발생했습니다"라는 메시지를 출력합니다.
  • coverImgUrl을 style의 backgroundImage로 설정하여 커버 이미지를 표시합니다.

5. 결론

이번 글에서는 Next.js의 getServerSideProps를 활용하여 서버에서 데이터를 가져오는 방법을 살펴보았습니다. 이를 통해 데이터가 페이지 요청 시 서버에서 미리 렌더링되므로 SEO와 초기 로딩 속도를 최적화할 수 있습니다.

728x90