2-6. 데이터 삭제와 수정, 그리고 요청 검증
데이터 삭제
이번엔 데이터 삭제를 해보겠습니다. 데이터를 삭제 할 때에는 여러가지 방법이 있습니다:
.remove
: 특정 조건을 만족하는 데이터들을 모두 지웁니다..findByIdAndRemove
: id 를 찾아서 지웁니다..findOneAndRemove
: 특정 조건을 만족하는 데이터 하나를 찾아서 지웁니다.
우리는, .findByIdAndRemove
를 사용하도록 하겠습니다.
src/api/books/books.controller.js
exports.delete = async (ctx) => {
const { id } = ctx.params; // URL 파라미터에서 id 값을 읽어옵니다.
try {
await Book.findByIdAndRemove(id).exec();
} catch (e) {
if(e.name === 'CastError') {
ctx.status = 400;
return;
}
}
ctx.status = 204; // No Content
};
그 다음에는 books 라우트 인덱스에서 delete API 에 id 파라미터를 받도록 설정하세요. 그리고 앞으로 구현 할 put 과 patch 도 id 를 필요로 하니, 해당 API 들에도 :id
를 넣으세요.
PUT 과 PATCH
PUT 과 PATCH 는 서로 비슷하지만, 역할이 다릅니다. 둘 다 데이터를 변경하는데요, PUT 의 경우엔 데이터를 통째로 바꿔버리는 메소드이며, PATCH 의 경우엔 주어진 필드만 수정하는 메소드입니다.
따라서, 구현 방식은 비슷 할 수도 있겠지만, PUT 의 경우엔 모든 필드를 받도록 데이터를 검증해야합니다.
추가적으로 PUT 의 경우에 데이터가 존재하지 않는다면 데이터를 새로 만들어주어야 하는게 원칙입니다.
Request Body 를 검증하려면, if 문, 혹은 배열과 반복문을 통하여 각 필드를 일일히 체크하는 방식도 있겠지만, 이를 더 편리하게 해주는 라이브러리가 존재합니다. 심지어 값의 형식도 검사를 할 수 있게 해줍니다.
Joi
바로, Joi 라는 라이브러리 입니다. 이 라이브러리를 먼저 설치해주세요.
$ yarn add joi
그리고, comments.controller.js 의 상단에 Joi 를 불러오세요. 이번엔 ObjectId 도 사전 검증을 해보겠습니다. ObjectId 도 mongoose 에서 불러오도록 하겠습니다.
src/api/comments/comments.controller.js
- 상단
const Joi = require('joi');
const { Types: { ObjectId } } = require('mongoose');
코드가 좀 생소하게 느껴질수도있는데, 위 코드의 두번째 줄은 다음 코드와 동일합니다.
const ObjectId = require('mongoose').Types.ObjectId
먼저 PUT 메소드에서 사용 할 replace 를 구현하도록 하겠습니다. 이제 replace 함수에서 id 파라미터와, request body 를 검증하는 코드를 다음과 같이 입력하세요.
src/api/comments/comments.controller.js
- replace
exports.replace = async (ctx) => {
const { id } = ctx.params; // URL 파라미터에서 id 값을 읽어옵니다.
if(!ObjectId.isValid(id)) {
ctx.status = 400; // Bad Request
return;
}
// 먼저, 검증 할 스키마를 준비해야합니다.
const schema = Joi.object().keys({ // 객체의 field 를 검증합니다.
// 뒤에 required() 를 붙여주면 필수 항목이라는 의미입니다
title: Joi.string().required(),
authors: Joi.array().items(Joi.object().keys({
name: Joi.string().required(),
email: Joi.string().email().required() // 이런식으로 이메일도 손쉽게 검증가능합니다
})),
publishedDate: Joi.date().required(),
price: Joi.number().required(),
tags: Joi.array().items((Joi.string()).required())
});
// 그 다음엔, validate 를 통하여 검증을 합니다.
const result = Joi.validate(ctx.request.body, schema); // 첫번째 파라미터는 검증할 객체이고, 두번째는 스키마입니다.
// 스키마가 잘못됐다면
if(result.error) {
ctx.status = 400; // Bad Request
ctx.body = result.error;
return;
}
}
지금은, 에러처리만 해주었습니다. 이제 PUT 의 로직을 완성하겠습니다. 데이터를 교체할때는, .findByIdAndUpdate
를 사용하며 upsert
설정을 함으로서, 데이터가 존재하지 않으면 새로 데이터를 만들도록 설정하였습니다. 방금 작성한 코드 하단을 다음과 같이 완성하세요.
src/api/books/books.controller.js
- replace 하단
(...)
let book;
try {
// 아이디로 찾아서 업데이트를 합니다.
// 파라미터는 (아이디, 변경 할 값, 설정) 순 입니다.
book = await Book.findByIdAndUpdate(id, ctx.request.body, {
upsert: true, // 이 값을 넣어주면 데이터가 존재하지 않으면 새로 만들어줍니다
new: true // 이 값을 넣어줘야 반환하는 값이 업데이트된 데이터입니다.
// 이 값이 없으면 ctx.body = book 했을때 업데이트 전의 데이터를 보여줍니다.
});
} catch (e) {
return ctx.throw(500, e);
}
ctx.body = book;
}
이제, 기존의 POST 요청때 사용했던 request body 를 복사하고, PUT http://localhost:4000/api/books/:id
요청을 넣어보세요.
만약에 id 가 존재하지 않는다면 새 데이터를 생성하고, 스키마가 잘못되었다면 오류가 뜨게 됩니다.
데이터베이스에 접근하는 REST API 를 만들때에는, 파라미터와 스키마를 검사하는것이 중요합니다. 그렇게 함으로서, 값이 잘못되어서 발생 할 수 있는 버그를 잡아낼 수 있습니다.
PATCH 구현하기
PATCH 메소드는, 주어진 필드만 수정을 해줍니다. 구현 방식은 PUT 과 비슷하지만, 주어진 값만 수정하도록 로직을 작성해야합니다.
src/api/books/books.controller.js
- update
exports.update = async (ctx) => {
const { id } = ctx.params; // URL 파라미터에서 id 값을 읽어옵니다.
if(!ObjectId.isValid(id)) {
ctx.status = 400; // Bad Request
return;
}
let book;
try {
// 아이디로 찾아서 업데이트를 합니다.
// 파라미터는 (아이디, 변경 할 값, 설정) 순 입니다.
book = await Book.findByIdAndUpdate(id, ctx.request.body, {
// upsert 의 기본값은 false 입니다.
new: true // 이 값을 넣어줘야 반환하는 값이 업데이트된 데이터입니다. 이 값이 없으면 ctx.body = book 했을때 업데이트 전의 데이터를 보여줍니다.
});
} catch (e) {
return ctx.throw(500, e);
}
ctx.body = book;
};
이제, PATCH http://localhost:4000/api/books/:id
요청을 request body 에 일부 필드를 생략하고 요청을 해보세요. 잘되나요?
축하합니다, 이제 여러분은 REST API 를 만들 수 있게 되었습니다! 앞으로는 결국 이렇게 요청에서 받은 값을 토대로 데이터를 조회하고, 생성하고, 수정하고, 삭제를 하면서 프로젝트의 기능을 구현해나갈것입니다.
이제 우리는 더 이상 books API 와 모델은 필요하지 않습니다. books API 와 모델 코드를 삭제하셔도 좋습니다. 하지만, 작업하면서 참고를 하시고싶다면, 남겨두고 나중에 삭제를 하셔도 됩니다.