01. [할 일 메모 사이트] - Update, Delete
1) [할 일 메모 사이트] - Update, Delete API 정리하기
Method | URL | Request | Response | |
할일 순서, 내용 변경, 완료/해제 | PATCH | /api/todos/:todoId | { "order": 2, "value": "수정된 해야할 일입니다.", "done": false } |
{ } |
할일 삭제 | DELETE | /api/todos/:todoId | { } | { } |
데이터 수정 : PATCH, put
할일 삭제 : 삭제권한 인증, 인가는나중에
// 코드에서 요청 반환하도록만들어서 Response필요 X
2) 할 일 순서 변경 API 만들기
Todo 데이터에서 order 값만 변경하여 할 일 순서를 바꿉니다.
여기서 주의해야할 점이 있습니다. 3번째 할 일을 2번으로 변경할 때, 만약 2번 할 일이 이미 존재한다면 어떻게 해야할까요? 3→2, 2→3 과 같이 2개의 Todo 데이터에서 order를 함께 변경해줘야합니다.
이 과정을 거치면, 두 개의 Todo 데이터의 order 값이 서로 바뀌게 되므로, 할 일 목록의 순서도 바뀌게 되는 것이죠.
이번엔 할 일 순서를 변경하는 API를 짜볼 건데요. Todo 데이터에 order 값을 서로 바꿔서 저장하는 기능을 구현해볼거에요!
해야할 일 순서를 변경하는 것은 어떻게 구현할까요? 간단합니다. Todo 데이터에서 order 값만 변경해주면 되는 것이죠.
그런데, 여기서 주의해야할 점이 있습니다. 3번째 할 일을 2번으로 변경할 때, 만약 2번 할 일이 이미 존재한다면 어떻게 해야할까요? 3→2, 2→3 과 같이 2개의 Todo 데이터에서 order를 함께 변경해줘야합니다.
이 과정을 거치면, 두 개의 Todo 데이터의 order 값이 서로 바뀌게 되므로, 할 일 목록의 순서도 바뀌게 되는 것이죠.
이제 위에서 설명한 내용을 바탕으로 할 일 순서를 변경하는 API를 작성해볼까요? 😎
[코드스니펫] 할 일 순서 변경하는 API, routes/todos.router.js
/** 해야할 일 순서 변경 API**/
// /:todoId' : 수정해야 할 데이터를 알기 위해 지정.
router.patch("/todos/:todoId", async (req, res) => {
// 변경할 '해야할 일'의 ID 값을 가져옵니다.
const { todoId } = req.params;
// '해야할 일'을 몇번째 순서로 설정할 지 order 값을 가져옵니다.
const { order } = req.body;
// 현재 나의 order가 무엇인자 알아야 한다.
//.findById() : ()안의 ID로 데이터 찾기
// 변경하려는 '해야할 일'을 가져옵니다. 만약, 해당 ID값을 가진 '해야할 일'이 없다면 에러를 발생시킵니다.
const currentTodo = await Todo.findById(todoId).exec();
if (!currentTodo) {
return res
.status(404) // 400은 클라이언트 잘못
.json({ errorMessage: "존재하지 않는 해야할 일 입니다." });
}
// 해야할 일 순서 변경
if (order) {
// 변경하려는 order 값을 가지고 있는 '해야할 일'을 찾습니다.
// findOne : 하나만 찾기(DB 컬럼 조건에 유일성을 지정해서 'order' 값이 일치하는 데이터는 하나밖에 없긴 함.)
const targetTodo = await Todo.findOne({ order }).exec(); // {order}는 {order:order}를 의미함. 객체의 값이라 {}사용
if (targetTodo) {
// targetTod 값이 있다면
// 만약, 이미 해당 order 값을 가진 '해야할 일'이 있다면, 해당 '해야할 일'의 order 값을 변경하고 저장합니다.
targetTodo.order = currentTodo.order; // order값 바꾸기.. 4->1이면 2,3번은 그대로 있고 4, 1번만 바뀜
await targetTodo.save();
}
// 변경하려는 '해야할 일'의 order 값을 변경합니니다.
currentTodo.order = order;
}
// 변경된 '해야할 일'을 저장합니다.
await currentTodo.save();
return res.status(200).json({});
});
export default router;
// /routes/todos.router.js
import express from "express";
import Todo from "../schemas/todo.schema.js";
// 라우터 생성
const router = express.Router();
/** 할일등록 API **/
// 할일 등록은 .post메소드 사용
// URL : /todos
// DB사용하려면 async, 즉 비동기 함수를 사용해야 합니다. 데이터를 조회할 동안 프로그램이 기다리도록 만들어야 합니다.
router.post("/todos", async (req, res, next) => {
// next는 데스트용.
// 1. 클라이언트에게 전달받은 value 데이터를 변수에 저장합니다.
const { value } = req.body;
// 1-5. 데이터 유효성 검사 기능 추가
// value가 존재하지 않을 때, 클라이언트에게 에러 메시지를 전달합니다.
if (!value) {
// 값이 없을 때
return res
.status(400) // 400 : 클라이언트 잘못으로 오류 발생
.json({ errorMessage: "해야할 일 데이터가 존재하지 않습니다." }); // 리턴 데이터 형태: json
}
// 2. 해당하는 마지막 order 데이터를 조회 (MongoDB에서 'order' 값이 가장 높은 '해야할 일')
// findOne : 한개의 데이터 조회
// sort(컬럼) : 지정 컬럼을 기준으로 정렬
// sort('컬럼이름') : 오름차순 정렬
// sort('-컬럼이름') : 내림차순 정렬
// .exec(); : 몽구스 DB에서 조회시 무조건 마지막에 붙여야 오류 안남
// promis로 반환하는데, .exec()가 없으면 await가 작동하지 않습니다.
// Todo 컬렉션에서 'order'차트를 내림차순으로 맨 위의 것(제일 큰 값)을 하나 가져옴니다.
const todoMaxOrder = await Todo.findOne().sort("-order").exec();
// 3. 만약 존재한다면 현재 할 일에 +1, order 데이터가 존재하지 않는다면 1로 할당합니다.('order' 값이 가장 높은 도큐멘트의 1을 추가하거나 없다면, 1을 할당합니다.)
const order = todoMaxOrder ? todoMaxOrder.order + 1 : 1;
// 4. 해야할 일 등록
// todo를 실제 인스턴스형식으로 만듦
const todo = new Todo({ value, order });
// 실제로 DB에 저장
await todo.save();
// 5. 해야할 일을 클라이언트에게 반환
return res.status(201).json({ todo });
});
/** 해야할 일 목록 조회 API**/
// API등록은 router에서 합니다.
router.get("/todos", async (req, res, next) => {
// 1. 해야할 일 목록 조회를 진행한다.
const todos = await Todo.find().sort("-order").exec();
// 2. 해야할 일 목록 조회 결과를 클라이언트에게 반환 한다.
return res.status(200).json({ todos }); // 200 성공
});
/** 해야할 일 순서 변경 API**/
// /:todoId' : 수정해야 할 데이터를 알기 위해 지정.
router.patch("/todos/:todoId", async (req, res) => {
// 변경할 '해야할 일'의 ID 값을 가져옵니다.
const { todoId } = req.params;
// '해야할 일'을 몇번째 순서로 설정할 지 order 값을 가져옵니다.
const { order } = req.body;
// 현재 나의 order가 무엇인자 알아야 한다.
//.findById() : ()안의 ID로 데이터 찾기
// 변경하려는 '해야할 일'을 가져옵니다. 만약, 해당 ID값을 가진 '해야할 일'이 없다면 에러를 발생시킵니다.
const currentTodo = await Todo.findById(todoId).exec();
if (!currentTodo) {
return res
.status(404) // 400은 클라이언트 잘못
.json({ errorMessage: "존재하지 않는 해야할 일 입니다." });
}
// 해야할 일 순서 변경
if (order) {
// 변경하려는 order 값을 가지고 있는 '해야할 일'을 찾습니다.
// findOne : 하나만 찾기(DB 컬럼 조건에 유일성을 지정해서 'order' 값이 일치하는 데이터는 하나밖에 없긴 함.)
const targetTodo = await Todo.findOne({ order }).exec(); // {order}는 {order:order}를 의미함. 객체의 값이라 {}사용
if (targetTodo) {
// targetTod 값이 있다면
// 만약, 이미 해당 order 값을 가진 '해야할 일'이 있다면, 해당 '해야할 일'의 order 값을 변경하고 저장합니다.
targetTodo.order = currentTodo.order; // order값 바꾸기.. 4->1이면 2,3번은 그대로 있고 4, 1번만 바뀜
await targetTodo.save();
}
// 변경하려는 '해야할 일'의 order 값을 변경합니니다.
currentTodo.order = order;
}
// 변경된 '해야할 일'을 저장합니다.
await currentTodo.save();
return res.status(200).json({});
});
export default router;
test : 6번(장조림) 과 3번 order 번호 바꾸기
// localhost:3000/api/todos/_ID값
PATCH localhost:3000/api/todos/6740546947412f98efd6d5f3
// body json
// { "order" : 바꿀 order번호}
{
"order" : 3
}
Response값이 왜 아무것도 전달하지 않는가
현재 프론트엔드에서는 PATCH /api/todos:/todoId API를 호출한 후, 바로 GET /api/todos API를 요청하도록 구현했기에 변경된 Response를 반환하지 않더라도 괜찮습니다.
할 일 순서 변경 API에서는 클라이언트가 전달한 ID에 해당하는 ‘해야할 일’이 존재하는지 확인하고, order에 해당하는 Todo 데이터가 존재한다면, 2개의 순서를 변경하게 됩니다.
현재는 2개의 데이터를 변경하기 위해서 1개의 할 일을 수정한 이후 나머지 다른 할 일 순서를 변경하는 방식으로 비즈니스 로직이 구현되어있습니다.
3) 할 일 완료/해제 API 만들기
API는 done이라는 값을 전달받아 할 일 완료/해제 기능을 구현하게됩니다.
할 일 완료/해제 API에서 가장 중요한 것은 API에서 Todo 항목의 doneAt이라는 필드에 완료된 시점의 시간을 기입해야 한다는 것입니다.
만약, done값이 true로 전달되면, 해당 할 일이 완료된 것으로 간주하고 doneAt필드에 현재 시간을 기록하게됩니다. 이를 통해 언제 작업이 완료되었는지 추적할 수 있습니다.
done 값이 false로 전달되면, doneAt 필드또한 null로 수정되어야합니다.
할 일 완료/해제 API PATCH /api/todos/:todoId API를 수정
// /routes/todos.router.js
router.patch('/todos/:todoId', async (req, res) => {
const { todoId } = req.params;
const { order, done } = req.body; // done 추가(완료여부 조회)
...
if (done !== undefined) {
// 변경하려는 '해야할 일'의 doneAt 값을 변경합니다.
currentTodo.doneAt = done ? new Date() : null;
}
// /routes/todos.router.js
import express from "express";
import Todo from "../schemas/todo.schema.js";
// 라우터 생성
const router = express.Router();
/** 할일등록 API **/
router.post("/todos", async (req, res, next) => {
// 1. 클라이언트에게 전달받은 value 데이터를 변수에 저장합니다.
const { value } = req.body;
// 1-5. 데이터 유효성 검사 기능 추가
if (!value) {
return res
.status(400) // 400 : 클라이언트 잘못으로 오류 발생
.json({ errorMessage: "해야할 일 데이터가 존재하지 않습니다." });
}
// 2. 해당하는 마지막 order 데이터를 조회 (MongoDB에서 'order' 값이 가장 높은 '해야할 일')
const todoMaxOrder = await Todo.findOne().sort("-order").exec();
// 3. 만약 존재한다면 현재 할 일에 +1, order 데이터가 존재하지 않는다면 1로 할당합니다.('order' 값이 가장 높은 도큐멘트의 1을 추가하거나 없다면, 1을 할당합니다.)
const order = todoMaxOrder ? todoMaxOrder.order + 1 : 1;
// 4. 해야할 일 등록
// todo를 실제 인스턴스형식으로 만듦
const todo = new Todo({ value, order });
// 실제로 DB에 저장
await todo.save();
// 5. 해야할 일을 클라이언트에게 반환
return res.status(201).json({ todo });
});
/** 해야할 일 목록 조회 API**/
// API등록은 router에서 합니다.
router.get("/todos", async (req, res, next) => {
// 1. 해야할 일 목록 조회를 진행한다.
const todos = await Todo.find().sort("-order").exec();
// 2. 해야할 일 목록 조회 결과를 클라이언트에게 반환 한다.
return res.status(200).json({ todos }); // 200 성공
});
/** 해야할 일 순서 변경, 완료/해제 API **/
// /:todoId' : 수정해야 할 데이터를 알기 위해 지정.
router.patch("/todos/:todoId", async (req, res) => {
const { todoId } = req.params;
// 완료여부확인을 위해 done 추가
const { order, done } = req.body;
const currentTodo = await Todo.findById(todoId).exec();
if (!currentTodo) {
return res
.status(404) // 400은 클라이언트 잘못
.json({ errorMessage: "존재하지 않는 해야할 일 입니다." });
}
// 해야할 일 순서 변경
if (order) {
const targetTodo = await Todo.findOne({ order }).exec(); // {order}는 {order:order}를 의미함. 객체의 값이라 {}사용
if (targetTodo) {
targetTodo.order = currentTodo.order; // order값 바꾸기.. 4->1이면 2,3번은 그대로 있고 4, 1번만 바뀜
await targetTodo.save();
}
// 변경하려는 '해야할 일'의 order 값을 변경합니니다.
currentTodo.order = order;
}
// 할일을 완료한 경우
if (done !== undefined) {
// false는 done에 값이 들어가지 않습니다.
// 변경하려는 '해야할 일'의 doneAt 값을 변경합니다.
// 위에서 todoId 조회를 위해 사용한 currentTodo필드에 doneAt라는 컬럼에 할당한다
// 삼항문으로 done의 값이 있는 경우 그 값을 할당 합니다.
currentTodo.doneAt = done ? new Date() : null;
}
// 변경된 '해야할 일'을 저장합니다.
await currentTodo.save();
return res.status(200).json({});
});
export default router;
test
숫정한 코드를 저장하고 app.js를 실행해 서버를 열어주세요
_id로 할일을 특정하고 done값을 true로 바꾸세요
PATCH localhost:3000/api/todos/674038bc91f2313409188a1d
MongoDB에 할 일이 완료된 시간 즉, doneAt 필드를 조회한다면, 아래와 같은 doneAT를 확인할 수 있니다.
저장되어 있는 시간이 UTC(협정 세계시)를 기준으로 설정되어 있는데 이는 백엔드 서버가 전 세계의 다양한 시간대의 사용자와 통신할 때 시간에 대한 혼란을 방지하기 위함입니다.
일반적으로 컴퓨터의 시간은 유닉스 시간(Unix time)이라 불리는 1970년 1월 1일 00:00:00 UTC부터의 시간을 뜻하게됩니다. 보통 백엔드 서버에서 “유닉스 시간”을 사용하여 시간을 저장하며, 이를 통해 서로 다른 시간대의 사용자와의 통신에서 발생할 수 있는 문제를 방지하게 됩니다.
따라서, 우리가 구현하는 시스템에서도 UTC를 기준으로 시간을 관리하는것이 중요합니다,
4) 할 일 삭제 API 만들기
API는 삭제할 할 일의 ID를 클라이언트에게 전달받아, MongoDB에서 해당 데이터를 삭제하도록 구현하게됩니다.
클라이언트에게 전달받은 ID를 바탕으로 MongoDB에서 해당 Todo 데이터를 삭제합니다. 만약, 해당 ID의 할 일이 존재하지 않는다면 에러 메시지를 반환합니다.
할 일 삭제 API routes/todos.router.js
// routes/todos.router.js
/** 할 일 삭제 **/
router.delete("/todos/:todoId", async (req, res) => {
// 삭제할 '해야할 일'의 ID 값을 가져옵니다.
// todoId : 경로 매개변수인 req.params에서 가져오기
const { todoId } = req.params;
// 삭제하려는 '해야할 일'을 가져옵니다.(조회)
const todo = await Todo.findById(todoId).exec();
//만약, 해당 ID값을 가진 '해야할 일'이 없다면 에러를 발생시킵니다.
if (!todo) {
return res
.status(404) // 클라이언트 잘못. id 입력 오류
.json({ errorMessage: "존재하지 않는 todo 데이터입니다." });
}
// 조회된 '해야할 일'을 삭제합니다.
// MongDB의 데이터 id는 '_id'에 있습니다.
// 코드에서 중복없이 발급되는 데이터 id를 컬럼todoId에 할당하고 있습니다.
await Todo.deleteOne({ _id: todoId }).exec();
return res.status(200).json({});
});
order 2의 할일을 삭제하겠습니다.
order 2번의 데이터가 삭제됬을 확인할 수 있습니다.
5) 할 일 내용 변경 API 만들기
이번 API는 이전에 작업한 PATCH /api/todos/:todoId API의 코드를 수정하여 구현하게됩니다.
[Request] PATCH /api/todos/:todoId
{
"value": "수정된 할 일 입니다."
}
[Response] PATCH /api/todos/:todoId ( 빈 객체 전달)
{ }
/routes/todos.router.js
router.patch("/todos/:todoId", async (req, res) => {
const { todoId } = req.params;
const { order, done, value } = req.body; // value 추가
...
if (value) {
currentTodo.value = value; // 변경하려는 '해야할 일'의 내용을 변경합니다.
}
// /routes/todos.router.js
/** 순서 변경, 할 일 완료/해제, 할 일 내용 변경 **/
router.patch('/todos/:todoId', async (req, res) => {
// 변경할 '해야할 일'의 ID 값을 가져옵니다.
const { todoId } = req.params;
// 클라이언트가 전달한 순서, 완료 여부, 내용 데이터를 가져옵니다.
const { order, done, value } = req.body;
// 변경하려는 '해야할 일'을 가져옵니다. 만약, 해당 ID값을 가진 '해야할 일'이 없다면 에러를 발생시킵니다.
const currentTodo = await Todo.findById(todoId).exec();
if (!currentTodo) {
return res
.status(404)
.json({ errorMessage: '존재하지 않는 todo 데이터입니다.' });
}
if (order) {
// 변경하려는 order 값을 가지고 있는 '해야할 일'을 찾습니다.
const targetTodo = await Todo.findOne({ order }).exec();
if (targetTodo) {
// 만약, 이미 해당 order 값을 가진 '해야할 일'이 있다면, 해당 '해야할 일'의 order 값을 변경하고 저장합니다.
targetTodo.order = currentTodo.order;
await targetTodo.save();
}
// 변경하려는 '해야할 일'의 order 값을 변경합니니다.
currentTodo.order = order;
}
if (done !== undefined) {
// 변경하려는 '해야할 일'의 doneAt 값을 변경합니다.
currentTodo.doneAt = done ? new Date() : null;
}
if (value) {
// 변경하려는 '해야할 일'의 내용을 변경합니다.
currentTodo.value = value;
}
// 변경된 '해야할 일'을 저장합니다.
await currentTodo.save();
return res.status(200).json({});
});
test
'내일배움 강의 > 강의- Node.js 입문, 숙련' 카테고리의 다른 글
입문 마지막 코드 (0) | 2024.11.25 |
---|---|
입문 2주차 8 에러 핸들러와 미들웨어, Joi 설치 (0) | 2024.11.24 |
입문 2주차 9 코드 서식 정리하기 (0) | 2024.11.24 |
입문 2주차 10 배포를 위한 Git 학습 (0) | 2024.11.24 |
입문 2주차 11 AWS 배포하기 (0) | 2024.11.24 |