
import { BookData } from "@/types";
export default async function fetchBooks(): Promise<BookData[]> {
let url = `http://localhost:12345/book`;
try {
const response = await fetch(url);
return await response.json();
} catch (err) {
console.error(err);
return [];
}
}
export interface BookData {
id: number;
title: string;
subTitle: string;
author: string;
publisher: string;
description: string;
coverImgUrl: string;
}
fetchBooks는 fetch 메소드를 이용해서 서버에서 모든 도서 데이터를 가져오는 역할을 수행한다.
따라서 우선 해당 함수의 반환값의 타입은, 비동기 호출의 반환값을 의미하는 Promise를 적용하고. 반환되는 도서 데이터들의 타입 파일인 BookData를 제네릭으로 적용해준다. 반환값은 배열 형식이므로 'BookData[]'이 된다.
export const getServerSideProps = async () => {
const allBooks = await fetchBooks();
return {
props: {
allBooks,
},
};
};
페이지 컴포넌트의 실행 전에 데이터를 페칭해오고 싶다면? getServerSideProps 함수 내부에 동작해야하는 코드들을 작성하면 된다.
그리고 받아온 데이터는 return 키워드를 통해 반환해주면 된다.
const allBooks = await fetchBooks();
const recoBooks = await fetchRandomBooks();
const [allBooks, recoBooks] = await Promise.all([
fetchBooks(),
fetchRandomBooks(),
]);
따라서 이렇게 Promise.all 메소드를 이용해서 데이터 페칭을 병렬 방식으로 변경하여 동시에 수행되도록 하는 것이 좋다.
Promise.all, 인수로 전달한 배열 내부의 모든 비동기 함수들을 동시에 실행시키는 메소드.
export default function Home({
allBooks,
}: InferGetServerSidePropsType<typeof getServerSideProps>) {
return (
<div className={style.container}>
<section>
<h3>등록된 모든 도서</h3>
{allBooks.map((book) => (
<BookItem key={book.id} {...book} />
))}
</section>
</div>
);
}
export const getServerSideProps = async (
context: GetServerSidePropsContext
) => {
const q = context.query.q;
const books = await fetchBooks(q as string);
return {
props: {
books,
},
};
};
일단 입력값을 페이지 컴포넌트에서 처리하는 기능부터 구현해보자. getServerSideProps 함수에서는 이런 상황에 맞춰 context라는 인자를 제공해주고 있다. 인자에 대응하는 GetServerSidePropsContext 타입도 제공해준다.
context는 현재 브라우저에서 받은 요청에 해당하는 모든 정보가 포함되어 있다. 쿼리 스크링 또한, context 객체 내부에 query라는 이름으로 포함된다.
getServerSideProps에서 context와 query를 이용해서 쿼리 스트링을 꺼내온 다음, 데이터 페칭 함수로 넘겨주면 된다.
export default async function fetchBooks(
q?: string
): Promise<BookData[]> {
let url = `http://localhost:12345/book`;
if (q) {
url += `/search?q=${q}`;
}
...
}
학습 프로젝트에서는 일전에 구현한 fetchBooks의 내용을 수정해주면 된다. 따로 함수를 구현해도 되지만, 하나의 함수에서 여러 상황을 대응하게 하는 것도 좋은 방법.
우선 검색값이 존재하지 않을 수도 있다. 따라서 ?를 붙여 선택적 프로퍼티로 설정해준다.
if문을 사용하여, 검색 키워드가 존재할 때만 서버로 요청을 보낼 때 url의 구성이 달라지도록 한다.
export const getServerSideProps = async (
context: GetServerSidePropsContext
) => {
const id = context.params!.id;
const book = await fetchOneBook(Number(id));
return {
props: {
book,
},
};
};
id 값을 이용해서 특정 데이터만을 호출하는 상황에서도 context를 이용한다.
URL 파라미터를 이용할 경우, context 내부에 params 객체의 값을 가져오면 되는데. params은 context에 존재하지 않을 수도 있기 때문에, 단언 연산자!를 사용하여 params의 값이 반드시 존재한다고 선언해주어야 한다.
다만 학습 프로젝트의 해당 페이지의 경우, id의 값이 존재하지 않는 상황이 100% 없기 때문에 !을 사용하는 것이다. 다른 프로젝트를 수행할 때에는 params의 상황이 어떤지 반드시 파악해두어야 한다.
