본문 바로가기
내일배움 강의/강의- Node.js 입문, 숙련

입문 2주차 6 Create, Read 구현하기

by GREEN나무 2024. 11. 22.
728x90

목표

1. 할 일 메모 사이트의 Create, Read 기능을 구현한다.
2. 데이터 유효성 검사 기능에 대해 알아보고, 해당 기능을 추가한다.

01. [할 일 메모 사이트] - Create, Read 구현하기


1) [할 일 메모 사이트] - Create, Read API 정리하기

  Method URL Request Response
할일 POST /api/todos/ { "value":"제로 콜라 500ml 구매하기" } { "todo": { "value": "제로 콜라 500ml 구매하기", "order": 1, "_id": "64bd3e6a8f9c069e092ee5c4", "__v": 0, "todoId": "64bd3e6a8f9c069e092ee5c4", "id": "64bd3e6a8f9c069e092ee5c4" } }
할일 목록 조회 GET /api/todos/ { }
//추가옵션설정X
//전달데이터X
{ "todos": [ { "_id": "64bd3e6a8f9c069e092ee5c4", "value": "제로 콜라 500ml 구매하기", "order": 1, "__v": 0, "todoId": "64bd3e6a8f9c069e092ee5c4", "id": "64bd3e6a8f9c069e092ee5c4" } ] }

2) 할 일 Router(라우터) 추가하기

할 일 메모 사이트의 API를 구현하기 위해서는, 해야할 일의 기능을 사용하는 라우터(Router)를 생성하고, app.js 파일에서 해당하는 Router를 등록해야합니다.

여기서, 라우터(Router)란, 여러 가지 경로(route)를 관리하는 모듈이라고 생각하세요


/routes/todos.router.js 파일을 생성한 후, 아래의 코드스니펫을 복사해주세요!

// /routes/todos.router.js

import express from 'express';

const router = express.Router();

export default router;


app.js의 전역 미들웨어에 생성한 Router를 연결
app.js에 라우터를 가져오고 사용하는 코드 추가하기

import todosRouter from "./routes/todos.router.js";

// api 주소로 접근하였을 때, router와 TodosRouter로 클라이언트의 요청이 전달됩니다.
app.use("/api", [router, todosRouter]);

 

3) 할 일 등록 API 만들기

힐일 등록 API 만들기 -> 할일 조회 API 만들기 -> API 구현

 

order는 ‘해야할 일’ 데이터가 생성될 때마다 순서를 가질 수 있게 만들어주는 필드에요. 즉, 가장 최근에 추가된 ‘해야할 일’은 order값이 높을 것이고, order값이 높은 데이터는 할 일 목록의 상단에 위치하게 될 것입니다.
이번에는 클라이언트에게 전달받은 데이터를 MongoDB에 저장하는 API를 구현해보도록 하겠습니다


할 일 추가 API, routes/todos.router.js

// /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;

  // 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 });
});

export default router;

exec() 메서드

mongoose에서 exec()는 결과를 반환하기 위해 쿼리를 실행하고, 이 결과로 Promise를 반환하게 됩니다. 
만약, exec() 메서드를 사용하지 않는다면, 해당 쿼리는 결과값이 Promise로 반환되지 않기 때문에, 아직 데이터를 전달받지 않은 상태에서 다음 코드를 실행하게될 수 있습니다.
결국, 사용하려는 데이터가 null로 정의되어 있어, TypeError: Cannot read properties of null (reading 'id') 에러가 발생하게 될 것입니다. 


4) 데이터 유효성 검사 기능 추가하기

데이터 유효성 검사(Validation)란, 전달받은 데이터가 예상한 형식과 일치하는지 확인하기 위한 작업을 뜻합니다.


MongoDB에 비어 있는 데이터를 저장하려고 한다면, 아래와 같은 에러가 발생할 수 있습니다. 

value 데이터가 비었을 때, 발생하는 에러

그렇기 때문에, value 데이터의 유무를 미리 판단하고, 데이터가 존재하지 않을 때, 클라이언트에게 현재 상황을 전달함과 동시에 더이상 다음 코드로 실행되지 않도록 코드를 구현해야합니다.


할 일 추가 API에 데이터 유효성 검사 기능 추가하기

// /routes/todos.router.js
...
router.post('/todos', async (req, res) => {
  // 클라이언트에게 전달받은 value 데이터를 변수에 저장합니다.
  const { value } = req.body;

  // value가 존재하지 않을 때, 클라이언트에게 에러 메시지를 전달합니다.
  if (!value) {
    return res
      .status(400)
      .json({ errorMessage: '해야할 일 데이터가 존재하지 않습니다.' });
  }
...
더보기
// /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 });
});

export default router;

insomnia 로 테스트 하기

app.js 파일을 실행해 포트를 열어놓으세요

insomnia 컬렉션 만들기

 

Url : post localhost:3000/api/todos

// body는 json
{
	 "value" : "해야할일을 추가합니다. insomnia"
}

 

결과 : 할일이 추가 되었습니다.

         'order'이 1로 할당 되었습니다.('order'데이터가 없어서)
         고유 id인 _id도 정상적으로 등록되었습니다.

   'send' 버튼을 누를 때 마다 'order' 숫자가 정상적으로 증가합니다.

   'value'가 비었을 때의 오류처리도 정상적으로 됩니다.

           이제 서버는 데이터가 존재하지 않다면, 더이상 MongoDB에 데이터를 저장하는 작업을 수행하지 않습니다.

5) 할 일 목록 조회 API 만들기

order 라는 값을 기준으로 내림차순(.sort("-order"))해서 Todo 데이터를 가져오도록 하겠습니다. 

3개 이상의 할 일을 등록하고, order 값을 기준으로 내림차순해서 가장 높은 order값을 가지고 있는 ‘해야할 일’을 제일 상단에 위치하도록 코드를 작성하기

/** 해야할 일 목록 조회 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 });
});

 

더보기
// /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 });
});

export default router;

테스트 : 서버 다시 켜기 -> insumnia에서 get  localhost:3000/api/todos

 

[Response] GET /routes/todos 할 일 목록 조회 API 
3개의 할 일을 등록해놓은 상태. order값을 기준으로 내림차순 정렬이 되는지 확인하기
이 응답(Response)에서 todos 키 아래 배열이 위치해 있으며, 배열의 각 요소는 할 일을 나타내는 객체입니다. 각 객체는 할 일의 고유 아이디(id), 내용(value), 순서(order)를 가지고 있습니다.

{
	"todos": [
		{
			"_id": "64bd3eea8f9c069e092ee5cc",
			"value": "맥도날드 초코콘 사오기",
			"order": 3,
			"__v": 0,
			"id": "64bd3eea8f9c069e092ee5cc"
		},
		{
			"_id": "64bd3ee48f9c069e092ee5c9",
			"value": "클라이밍 10문제 풀기",
			"order": 2,
			"__v": 0,
			"id": "64bd3ee48f9c069e092ee5c9"
		},
		{
			"_id": "64bd3e6a8f9c069e092ee5c4",
			"value": "제로 콜라 500ml 구매하기",
			"order": 1,
			"__v": 0,
			"id": "64bd3e6a8f9c069e092ee5c4"
		}
	]
}