오늘은 Node 강의 입문주차 일부를 듣고 실습해보는 시간을 가졌다. 전체적인 흐름은 이해가 되나 보지 않고 코딩할 정도는 아직 먼 것 같다.
[ 오늘 푼 알고리즘 문제 ]
HTTP 요청을 받기 위하여 express 라이브러리를 활용해 서버를 만들어줍니다.
import express from 'express'
const app = express();
// express의 인스턴스를 app으로 지정합니다.
const port = 3000;
// 포트는 3000번입니다.
app.use(express.json());
app.use(express.urlencoded({extended : true}));
// express에서 들어오는 요청의 데이터를 파싱하는
// 미들웨어로 JSON과 URL-encoded 형식의 데이터를 처리합니다.
const router = express.Router();
// router 객체를 생성합니다.
// router는 자체적으로 미들웨어를 가질 수 있으며,
// 특정 경로와 관련된 요청을 독립적으로 처리합니다.
router.get('/', (req, res) => {
return res.json({ message : 'Hi' });
});
// root로 접속하였을 때 json을 리스폰 합니다.
app.use('/api', router);
// /api 에 대한 요청을 router 미들웨어로 처리합니다.
app.listen(PORT, ()=>{
console.log(PORT, '포트 연결 성공'
});
// 항시 포트를 추적하다가 맞는 PORT의 요청이 들어왔을 경우
//로그를 출력하고 명령을 수행합니다.
프론트엔드 파일을 담고 있는 asset 폴더를 Express 서버에서 접근할 수 있게 정적 파일 미들웨어를 사용합니다.
app.use(express.static('./assets'));
// static Middleware를 사용하여 정적 파일을 제공합니다.
- 할 일 추가하기
- 할 일 조회하기
- 할 일 수정하기
- 할 일 순서 변경하기
- 할 일 완료하기
- 할 일 완료 해제하기
기능을 바탕으로 필요한 데이터 도출
// schemas/todo.schema.js
import mongoose from 'mongoose';
const TodoSchema = new mongoose.Schema({
value: {
type: String,
required: true, // value 필드는 필수 요소입니다.
},
order: {
type: Number,
required: true, // order 필드 또한 필수 요소입니다.
},
doneAt: {
type: Date, // doneAt 필드는 Date 타입을 가집니다.
required: false, // doneAt 필드는 필수 요소가 아닙니다.
},
});
// TodoSchema를 바탕으로 Todo모델을 생성하여, 외부로 내보냅니다.
export default mongoose.model('Todo', TodoSchema);
// routes/todos.router.js
import Todo from '../schemas/todo.schema.js';
// 스키마 모델을 불러옵니다.
router.post('/todos', async (req, res) => {
// 클라이언트에게 전달받은 value 데이터를 변수에 저장합니다.
const { value } = req.body;
// 구조분해할당으로 JSON에 들어있는 value에 대한 값을 value 변수에 할당합니다.
// value가 존재하지 않을 때, 클라이언트에게 에러 메시지를 전달합니다.
if (!value) {
return res
.status(400)
.json({ errorMessage: '해야할 일 데이터가 존재하지 않습니다.' });
}
// Todo모델을 사용해, MongoDB에서 'order' 값이 가장 높은 '해야할 일'을 찾습니다.
const todoMaxOrder = await Todo.findOne().sort('-order').exec();
// exec를 사용하는 이유는 promise를 반환받기 위함이며, 사용하지 않으면 await가 무용지물 됩니다.
// findOne은 단 하나의 검색 결과만 가져옵니다.
// -order를 했을 경우 내림차순으로 조회합니다.
// 'order' 값이 가장 높은 도큐멘트의 1을 추가하거나 없다면, 1을 할당합니다.
const order = todoMaxOrder ? todoMaxOrder.order + 1 : 1;
// Todo모델을 이용해, 새로운 '해야할 일'을 생성합니다.
const todo = new Todo({ value, order });
// 생성한 '해야할 일'을 MongoDB에 저장합니다.
await todo.save();
// 성공했을 경우 스테이터스와 JSON을 반환합니다.
return res.status(201).json({ todo });
});
export default router;
router.get('/todos', async (req, res) => {
// Todo모델을 이용해, MongoDB에서 'order' 값이 가장 높은 '해야할 일'을 찾습니다.
const todos = await Todo.find().sort('-order').exec();
// 찾은 '해야할 일'을 클라이언트에게 전달합니다.
return res.status(200).json({ todos });
});
// routes/todos.router.js
router.patch('/todos/:todoId', async (req, res) => {
// 변경할 '해야할 일'의 ID 값을 가져옵니다.
const { todoId } = req.params;
// '해야할 일'을 몇번째 순서로 설정할 지 order 값을 가져옵니다.
const { order } = 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;
}
// 변경된 '해야할 일'을 저장합니다.
await currentTodo.save();
return res.status(200).json({});
});
// routes/todos.router.js
/** 할 일 삭제 **/
router.delete('/todos/:todoId', async (req, res) => {
// 삭제할 '해야할 일'의 ID 값을 가져옵니다.
const { todoId } = req.params;
// 삭제하려는 '해야할 일'을 가져옵니다. 만약, 해당 ID값을 가진 '해야할 일'이 없다면 에러를 발생시킵니다.
const todo = await Todo.findById(todoId).exec();
if (!todo) {
return res
.status(404)
.json({ errorMessage: '존재하지 않는 todo 데이터입니다.' });
}
// 조회된 '해야할 일'을 삭제합니다.
await Todo.deleteOne({ _id: todoId }).exec();
return res.status(200).json({});
});
router handler callback 에 async function을 사용하면, 서버가 종료될 위험이 있습니다. async에서 발생되는 에러를 처리하는 코드가 없기 때문에 try-catch 구문을 사용하여 처리해야 합니다.
router.get('/todos', async(req, res) => {
try{
const todos = await Todo.find().sort('-order').exec();
return res.status(200).json({ todos });
}catch(error){
console.error(error);
return res.status(500).json({errorMessage : "서버 오류"});
}
});
다른 방법으로는 express-async-handler 를 사용하는 방법이 있습니다.
import asyncHandler from 'asyncHandler';
router.get('/todos', asyncHandler(async (req, res) => {
const todos = await Todo.find().sort('-order').exec();
return res.status(200).json({ todos });
}));