[NEXT.js] 넥스트 JS를 배워보자 4편 - API Routes

2022. 3. 20. 15:18Library,Framework

728x90

넥스트 JS의 또 하나의 장점으로는 API 라우트 기능을 통해 API 엔드포인트를 클라이언트 코드와 함께 작성할 수 있다는 점이다. 

 

물론 한 곳에서 작성만 할 수 있을 뿐이지 백엔드에서 실행되어야 할 코드가 클라이언트에서 실행되는 것은 아니다. 해당 라우트에 작성한 코드들은 클라이언트 번들 사이즈에 포함되지 않기 때문에 추후 별도로 배포를 할 수도 있고 serverless 함수 형식 구글의 cloud function이나 aws의 lambda)처럼 관리할 수도 있다. 

 


API Routes 사용해서 REST API 구현해보기

 

기존 예제 프로젝트에서는 JSON placeholder REST API포인트에 요청해서 포스트를 불러오는 방식으로 진행했는데 이번에는 API Routes를 사용해서 직접 엔드포인트를 구축하는 방법으로 진행을 해보려고한다.

 

pages/api에 있는 파일들은 페이지가 아닌 API 엔드포인트로 인식되기 때문에 해당 폴더에 photos.js 핸들러 파일을 만들어주자. 보통은 db에 사진 정보를 저장해서 GET 요청이 들어오면 사진 정보를 내보내는 방식으로 진행하겠지만 지금은 API Routes 기능을 살펴보는 게 목표이기 때문에 그냥 JSON Placeholder API에서 받아온 1~10까지의 데이터 정보를 그대로 내보내는 방식으로 진행했다. 

 

// pages/api/photos.js

const photos = [
  {
    albumId: 1,
    id: 1,
    title: "accusamus beatae ad facilis cum similique qui sunt",
    url: "https://via.placeholder.com/600/92c952",
    thumbnailUrl: "https://via.placeholder.com/150/92c952",
  },
  {
    albumId: 1,
    id: 2,
    title: "reprehenderit est deserunt velit ipsam",
    url: "https://via.placeholder.com/600/771796",
    thumbnailUrl: "https://via.placeholder.com/150/771796",
  },
  // 1 ~ 10 까지
];

export default function handler(request, response) {
  const { method } = request;
  // GET, POST, PUT, PATCH 등 요청에 맞는 작업 실행
  if (method === "GET") {
    return response.status(200).json({
      photos,
    });
  }
}

 

클라이언트에서 GET 요청 시 photos 데이터를 JSON형식으로 status code 200과 함께 response 객체에 담아서 전달하는 방식이다. (status code 종류).

 

만약에 인증이 필요한 api라면 미들웨어를 사용해서 추가적으로 인증을 확인한 뒤 결과를 전달해주면 된다.  또 기본적으로 위와 같이 설정된 엔드포인트는 동일 출처 정책으로 세팅되어 있기 때문에 외부에서 API를 사용하려면 CORS 미들웨어를 사용해야 한다.

 

지금은 같은 도메인이기 때문에 그냥 클라이언트 내에서 아래와 같은 방법으로 요청을 해주면 된다

 

const Photos = () => {
  const [photos, setPhotos] = useState([]);
  useEffect(() => {
    const fetchPhotos = async () => {
      const res = await fetch("/api/photos");
      const { photos } = await res.json();
      setPhotos(photos);
    };
    fetchPhotos();
  }, []);
  //...생략
  }

 

여기서 한 가지 주의할 점은 getStaticProps나 getStaticPaths처럼 서버 환경에서 작동되는 data fetching 함수에서 API Route를 fetch하지 않는 게 좋다는 것이다. Pre Rendering 편에서도 언급했다시피 해당 함수는 빌드할때만 실행되고 클라이언트 번들에 추가되지 않기 때문에 굳이 fetch를 하지 않고 데이터베이스에 직접 요청을 하는 방식으로 진행하길 권고하고 있다.

 

Do Not Fetch an API Route from getStaticProps or getStaticPaths - NEXT js 공식문서

 

그럼 어떻게 써야 할까?

 

CSR 방식을 사용한다면 위와 같은 방법 또는 커스텀 훅을 만들어서 클라이언트에서 fetch 또는 axios를 통해 데이터를 불러오는 방법이 있다.

 

SSR 방식을 사용한다면 fetch를 사용하지 않고 데이터베이스 요청 쿼리 같은 주요 로직만 임포트 해서 사용하는 방법이 있다.

예를 들어서 지금은 그냥 지정된 사진을 불러오지만 getPhotosFromDB라고 데이터 베이스에 요청을 하는 쿼리가 있다고 하면 아래와 같이 해당 부분만 임포트 해서 사용하면 된다.

 

// pages/api/photos.js

export const getPhotosFromDB = () => {
  // DB Query 로직
  const photos = [
    {
      albumId: 1,
      id: 1,
      title: "accusamus beatae ad facilis cum similique qui sunt",
      url: "https://via.placeholder.com/600/92c952",
      thumbnailUrl: "https://via.placeholder.com/150/92c952",
    }, //... 생략 ]
  return photos;
};


export default function photosHandler(request, response) {
  const { method } = request;

  if (method === "GET") {
    const photos = getPhotosFromDB();
    return response.status(200).json({
      photos,
    });
  }
}

 

 

// pages/photos.js

export const getStaticProps = async () => {
  const photos = getPhotosFromDB();
  return {
    props: {
      photos,
    },
  };
};

 

동적 API Route 

앞서 살펴본 내용은 /api/photos 에 요청을 했을 경우 전체 사진 정보를 불러오는 작업이었는데 만약 포스트 하나에 대한 정보를 불러오기 위해서는 어떻게 해야 할까?

 

답은 간단하다. 전에 pages 세팅할 때 dynamic routing 설정을 했던 것처럼 하면 된다.

 

api/photos/[id]에 index.js파일을 하나 생성해주고 아래와 같이 만들어주자

 

import { getPhotosFromDB } from "../../photos";

export default function photoHandler(request, response) {
  const {
    method,
    query: { id },
  } = request;

  if (method === "GET") {
    const photo = getPhotosFromDB(id); // id를 입력하면 해당 아이디의 사진을 받아오도록 로직 수정
    return response.status(200).json({
      photo,
    });
  }
}

 

이제 똑같이 클라이언트에서 사용해주면 된다

 

const Photo = () => {
  const router = useRouter();
  const { id } = router.query;
  const [photo, setPhoto] = useState([]);

  useEffect(() => {
    const fetchPhoto = async () => {
      const res = await fetch(`/api/photos/${id}`);
      const { photo } = await res.json();
      setPhoto(photo);
    };
    fetchPhoto();
  }, [id]);

  return (
  	// 정보 매핑 로직
   )
};

 

좀 더 디테일한 동적 라우팅 방법이 알고 싶다면 아래 공식문서 링크를 참고하길 바란다. 

 

 

API Routes: Dynamic API Routes | Next.js

You can add the dynamic routes used for pages to API Routes too. Learn how it works here.

nextjs.org

 


다음 포스트에서는 Varcel을 통해서 배포하는 방법을 마무리로 Next JS 배워보자 시리즈를 마무리 짓도록 하겠다.