Next.js에서 Prisma를 이용해 REST API를 만들어 보자 - 2

이준혁·2022년 5월 7일
1

next-tutorials

목록 보기
3/7
post-thumbnail

이 글은 이전 포스트에 이어 Next.js의 프론트엔드에서 REST API를 사용하는 방법을 설명합니다.
다음 지식들을 안다면 문제없이 읽을 수 있습니다.

  • TypeScript
  • Node.js
  • React 기초 문법
  • Next.js 기초 문법
  • Promise
  • ES6
  • REST API

Axios

프론트엔드에서 api에 접근하기 위해서는 fetch 혹은 axios를 사용합니다. 두 메소드에 대한 자세한 내용은 좋은 포스트가 있으니 확인해 보시기 바랍니다. 여기서는 axios를 사용하겠습니다.

설치

npm install axios 혹은 yarn add axios 를 이용해 설치합니다.

사용법

axios는 여러 가지 방법으로 사용할 수 있습니다. axios.(요청을 보낼 메소드 명) 방식으로 사용하겠습니다. 다시 설명하자면, api/users url에 GET 요청을 보내고 싶다면 다음과 같이 작성하면 됩니다.

axios.get("/api/users");

POST, PUT, DELETE 요청도 동일하게 작성하면 됩니다. 만약 추가 정보가 필요한 경우는 다음과 같이 두 번째 인자에 객체를 주면 됩니다.

axios.post("/api/users", {
  headers: { ... },
  data: { ... },
});

백엔드 코드

프론트엔드 코드를 작성하기 전에, 저번 포스트에서 작성한 내용을 바탕으로 /api 폴더의 백엔드 코드를 작성해보려 합니다. 그 전에, 각 요청을 받을때 API가 어떤 방식으로 동작해야할 지를 정의해 봅시다.

  1. GET 요청을 받는 경우: 모든 user 정보를 가게 이름들과 함께 주면 됨
  2. POST 요청을 이름과 함께 받는 경우: 새 user를 만듦
  3. PUT 요청을 id, 이름과 함께 받는 경우: 해당 id의 유저 이름을 변경
  4. DELETE 요청을 id와 함께 받는 경우 : 해당 id의 유저를 삭제

정보 가져오기

위 네 가지 동작을 수행하는 코드를 작성하는 것은 저번에 배웠지만, 요청에서 id 혹은 이름을 어떻게 가져올까요? 보통 axios에서는 요청의 bodydata에 정보를 담아 보내기 때문에, id를 가져오려면 다음 방법들 중 하나를 사용하면 됩니다.

const id = req.body.data.id;
// With ES6
const {
  body: {
    data: { id },
  },
} = req;

응답 보내기

GET 요청같은 경우에는 응답을 프론트엔드에 보내야 합니다. 이 경우에는 prisma client가 반환한 값을 json에 실어 보내면 됩니다.

const users = await client.users.findMany({});
res.json({ ok: true, users });

위와 같이 작성하면 프론트엔드에서는 수신한 데이터의 users 속성을 확인해 데이터를 확인할 수 있습니다.

API 작성하기

이제 정보를 가져오고 응답을 보내는 방법을 알았으니 API를 작성하면 됩니다. 저번 포스트에서 설명한 내용이라 별도로 설명하지는 않겠습니다. 이 함수는 handler 함수의 내부를 작성한 것입니다.

if (req.method === "GET") {
  const users = await client.users.findMany({
    include: {
      shops: {
        select: {
          name: true,
        },
      },
    },
  });

  res.json({ ok: true, users });
}

if (req.method === "POST") {
  const {
    body: {
      data: { name },
    },
  } = req;
  const createUser = await client.users.create({
    data: {
      username: name,
    },
  });

  res.json({ ok: true });
}

if (req.method === "PUT") {
  const {
    body: {
      data: { id, name },
    },
  } = req;
  const updateUser = await client.users.update({
    where: {
      id: +id,
    },
    data: {
      username: name,
    },
  });

  res.json({ ok: true });
}

if (req.method === "DELETE") {
  const {
    body: { id },
  } = req;

  const deleteUser = await client.users.delete({
    where: {
      id: +id,
    },
  });
}
  1. id를 사용할때 +id 형태로 사용하는 것은, 받은 id가 숫자인지 확실하지 않기 때문에 숫자로 변환시키기 위해 +를 붙여주는 것입니다.
  2. axios.delete 함수가 body 내의 data 부분이 없다는 점 대문에 DELETE 메소드일땐 id를 바로 body에서 가져옵니다. (이 부분은 저도 조금 헷갈리네요...)

프론트엔드 코드

백엔드에서 어떻게 API를 다루는지는 끝났습니다. 이제는 프론트엔드에서 API에 요청을 보내는 코드를 작성해보겠습니다. 그 전에 데이터베이스에서 CRUD 작업이 잘 이루어지는지 확인하기 위해 prisma studio를 활용하겠습니다.

우선 /pages 폴더에 users.tsx 파일을 다음과 같이 생성해 줍니다.

export default function Users() {
  const onPostClick = async () => {};
  const onGetClick = async () => {};
  const onPutClick = async () => {};
  const onDeleteClick = async () => {};
  return (
    <div className="flex flex-col space-y-8">
      <button onClick={onPostClick}>POST</button>
      <button onClick={onGetClick}>GET</button>
      <button onClick={onPutClick}>PUT</button>
      <button onClick={onDeleteClick}>DELETE</button>
    </div>
  );
}

맨 상위 div 에 작성된 "flex flex-col space-y-8"은 CSS를 적용한 것입니다. 자세한 내용은 tailwindcss 를 참고 바랍니다.

위 코드는 각각 POST, GET, PUT, DELETE 요청을 보내는 버튼들을 모아두었습니다. 각 버튼에 연결된 함수들을 구현해 보겠습니다.

Create (POST)

const onPostClick = async () => {
  await axios.post("/api/users", {
    headers: { "Content-Type": "application/json" },
    data: {
      name: "Max Demian",
    },
  });
};

위 코드가 실행되면 /api/users url로 {name: Max Demain} 데이터와 함께 POST 요청이 갑니다. 따라서 usernameMax Demian 속성을 가지고 있는 user가 만들어질 것입니다.

실제로 다음과 같이 데이터가 만들어진 것을 볼 수 있습니다.

Read (GET)

GET 요청을 수행하기 전에 방금 만든 user와 연결되는 shop 두개를 만들어 보겠습니다. prisma studio로 만들거나 위 create 코드를 응용해 만드시면 됩니다.

const onGetClick = async () => {
  const result = await axios.get("/api/users");
  console.log(result);
};

위 코드와 같이 현재 GET 요청은 추가적인 정보를 필요로 하지 않습니다. 그리고 axios.get 함수의 반환값을 살펴보면 다음과 같습니다.

여기서 저희가 원하는 부분은 data 부분이므로, ES6의 도움을 받아 다음과 같이 작성해 원하는 결과를 얻을 수 있습니다.

const { data } = await axios.get("/api/users");
console.log(data);

위와 같이 users 배열을 반환하고, 각 우리가 만든 shops 정보도 user 데이터 안에 있는 것을 확인할 수 있습니다.

Update (PUT)

const onPutClick = async () => {
  await axios.put("/api/users", {
    data: {
      id: 2,
      name: "Max Emil",
    },
  });
};

PUT 요청은 간단합니다. 프론트엔드에서 id:2, name: "Max Emil"을 제공하면 위 API에서 설계한 바와 같이 해당 id를 가진 userusername이 바뀌게 됩니다.

Delete (DELETE)

const onDeleteClick = async () => {
  await axios.delete("/api/users", {
    data: {
      id: 2,
    },
  });
};

DELETE 요청 역시 PUT 요청과 크게 다르지 않습니다. 해당하는 iduser를 삭제합니다.

데이터가 잘 삭제된 것을 확인할 수 있습니다. 또한 저번 포스트에서 shopsonDelete 옵션을 cascade로 주었기 때문에 user가 삭제됨에 따라 shop도 같이 삭제된 것을 확인 가능합니다.


마치며

이 포스트에서는 저번에 만든 REST api를 프론트엔드에서 어떻게 사용하는지에 대해 알아 봤습니다. 원래는 form을 이용해 직접 데이터를 DB에 넣고 수정하는 것 까지 하려 그랬는데 너무 복잡해져서 그만뒀습니다. react-hook-form까지 이용한다면 쉽게 여러 페이지를 만드실 수 있을겁니다.

코드 원본은 여기를 참고해 주시면 됩니다.

참고자료

  1. velog - Axios vs Fetch
  2. Axios 공식 문서
  3. Prisma client CRUD
  4. Next JS 강의
profile
만들고 개선하는 것을 좋아하는 개발자

0개의 댓글