29일차 - Node.js 입문(5) 할일 메모 사이트

이상민·2024년 9월 6일

TIL

목록 보기
29/50

할일 메모 사이트를 만들어 보자

Express를 통해 MongoDB를 사용하고, REST API를 설계하고 구현하여 할일 메모를 만들어보자!

구현해볼 기능

  1. 할 일 추가하기
  2. 할 일 목록 보기
  3. 할 일 내용 변경하기
  4. 할 일 순서 변경하기
  5. 할 일 완료하기
  6. 할 일 완료 해제하기

API 서버 준비하기

먼저 vscode를 열고 프로젝트 폴더를 만든뒤, 터미널을 열어 express, mongoose 라이브러리를 설치한다.

# 프로젝트를 초기화
yarn init -y

# express와 mongoose를 yarn을 이용해 설치
yarn add express mongoose
  • yarn을 이용해 생성된 package.json 파일에서 type을 module로 꼭 추가해주기!
// package.json

{
  "name": "todo-list",
  "version": "1.0.0",
  "main": "index.js",
  "license": "MIT",
 🤡 "type": "module",🤡
  "dependencies": {
    "express": "^4.18.2",
    "mongoose": "^7.4.0"
  }
}
  • API경로는 항상 /api를 앞에 붙이는 방식으로 구성 하기위해 app.js 라는 파일을 만들어 준다.
import express from 'express';

const app = express();
const PORT = 3000;

// Express에서 req.body에 접근하여 body 데이터를 사용할 수 있도록 설정합니다.
// app.use 는 미들웨어를 사용하게 해주는 코드
app.use(express.json());
app.use(express.urlencoded({ extended: true }));

const router = express.Router();

router.get('/', (req, res) => {
  return res.json({ message: 'Hi!' });
});

app.use('/api', router);

app.listen(PORT, () => {
  console.log(PORT, '포트로 서버가 열렸어요!');
});
  • 그 다음 미리 만들어진 프론트엔트 html 파일을 assets 폴더에 전부 넣어준뒤, app.js 파일에 아래 코드를 추가 입력해준다.
// static Middleware, express.static()을 사용하여 정적 파일을 제공합니다.
// express.static() 함수는app.js 파일 기준으로, 
// 입력 값(지금은 "./assets") 경로에 있는 파일을 아무런 가공 없이 그대로 전달해주는 미들웨어
app.use(express.static('./assets'));

Mongoose Schema 설계하기

이제 MongoDB 연결준비를 위해 mongoose를 사용하여, Mongoose의 공식 문서를 따라 schemas 폴더를 생성하고, 하위 파일인 index.js를 아래처럼 준비해보자.

// schemas/index.js
import mongoose from 'mongoose';
const connect = () => {
  mongoose
    .connect(
      // 아래 부분은 대여한 ID, Password, 내 주소에 맞게끔 수정해주자!
      'mongodb+srv://sparta-user:aaaa4321@express-mongo.uy7ttg7.mongodb.net/?retryWrites=true&w=majority',
      {
        dbName: 'todo_memo', // todo_memo 데이터베이스명을 사용.
      },
    )
    .then(() => console.log('MongoDB 연결에 성공하였습니다.'))
    .catch((err) => console.log(`MongoDB 연결에 실패하였습니다. ${err}`));
};
mongoose.connection.on('error', (err) => {
  console.error('MongoDB 연결 에러', err);
});
export default connect;
  • 그 후 mongoose 에 연결하기 위해 app.js에 아래 코드를 추가해주자.
import connect from './schemas/index.js';
connect();

할 일 메모사이트 Schema 설계해보기

MongoDB를 도입하기 위해 필요한 데이터와 형식을 정의하는 것은 스키마(Schema) 설계의 가장 중요한 요소이다. 그러므로 할 일 메모 사이트의 기능을 바탕으로 필요한 데이터를 설계해보자.

  1. 먼저 해야할일을 (Value) 라는 문자열(String)형식의 필드(Field)를 정의하자.
  2. 그 다음 해야 할 일의 순서(order)라는 숫자(Number) 형식의 필드(Field)를 정의한다.
  3. 마지막으로, 할 일을 완료했다면 언제 완료했는지 시간을 확인하는 완료 날짜(doneAt)라는 날짜(Date) 형식의 필드(Field)를 정의해보자.
  • 그럼 이제 스키마(Schemas) 폴더안에 todo.schema.js 라는 파일을 만들어준뒤 아래 형식대로 코드를 작성해준다.

Create, Read 구현해보기😎

  • Create, Read 명세서

할 일 등록 => POST Method (/api/todos/)
요청(request)

{
"value":"제로 콜라 500ml 구매하기"
}

응답(response)

{
"todo": {
"value": "제로 콜라 500ml 구매하기",
"order": 1,
"_id": "64bd3e6a8f9c069e092ee5c4",
"__v": 0,
"todoId": "64bd3e6a8f9c069e092ee5c4",
"id": "64bd3e6a8f9c069e092ee5c4"
}
}

할 일 목록 조회 => GET Method (/api/todos/)
응답(response)

{
"todos": [
{
"_id": "64bd3e6a8f9c069e092ee5c4",
"value": "제로 콜라 500ml 구매하기",
"order": 1,
"__v": 0,
"todoId": "64bd3e6a8f9c069e092ee5c4",
"id": "64bd3e6a8f9c069e092ee5c4"
}
]
}
  • 가장 먼저, 할 일 메모 사이트의 API를 구현하기 위해서는, 이전처럼 해야할 일의 기능을 사용하는 라우터(Router)를 생성하고, app.js 파일에서 해당하는 Router를 등록해야한다.

  • 따라서 /routes/todos.router.js 파일을 생성한 후, 아래 코드를 입력해주자.

  • todos.router.js 파일을 추가하고 코드 작성이 완료되었다면, 이제는 app.js의 전역 미들웨어에 생성한 Router를 연결하기 위해 app.js파일에 아래 임포트 코드를 입력하자!

import TodosRouter from './routes/todos.router.js';

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

할 일 등록 API 만들기

이제 할 일(Todo) 모델을 구성했으니 이제 할 일 추가 API를 짜보자

우리는 위에서 할 일 스키마를 정의할 때 order라는 해야할 일 데이터가 생성될 때마다 순서를 가질 수 있게 만들어주는 필드를 정의해 주었다. 이에 따라 아래 처럼 할 일 추가 API를 구현해보자.

🤷‍♀️exec() 메서드는 왜 사용할까?

  • ongoose에서 exec()는 결과를 반환하기 위해 쿼리를 실행하고, 이 결과로 Promise를 반환하게 된다.
  • 만약, exec() 메서드를 사용하지 않는다면, 해당 쿼리는 결과값이 Promise로 반환되지 않기 때문에, null로 정의될 수 있다.

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

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

  • 예상치 못한 에러를 위해 미리 데이터 유효성 검사 기능을 아래와 같은 코드로 추가해놔야 한다.
  • 그 다음 테스트를 위해 insomnia 를 이용해 실험해보면 value 값을 잘 입력하면 추가가 되었고,
  • value 값이 아무 값도 입력이 되지 않으면 에러가 발생했다는것을 확인할 수 있다.

할 일 목록 가져오기 API

  • 위에서 POST Method를 통해 추가한 목록들을 조회 하기 위해 아래 코드를 routes/todos.router.js에 붙여주자
  • 그럼 아래와 같이 새로 입력했던 정보들이 오름차순으로 나열된 모습을 확인할 수 있다 !

Update, Delete 구현해보기💼

  • Update, Delete 명세서

할 일 순서변경, 내용 변경, 완료/해제 => PATCH Method (/api/todos/:todoId)
요청(request)

{
"order": 2,
"value": "수정된 해야할 일입니다.",
"done": false
}

응답(response)

{}

할 일 삭제 => DELETE Method (/api/todos/:todoId)
요청(request)

{}

응답(response)

{}
  • 현재 프론트엔드에서는 PATCH /api/todos:/todoId API를 호출한 후, 바로 GET /api/todos API를 요청하도록 구현되었기 때문에 response 칸은 비워져있다.

할 일 순서 변경 API 만들기

해야할 일 순서를 변경하는 것은 Todo 데이터에서 order 값만 변경해주면 된다. 여기서 주의해야할 점은 3번째 할 일을 2번으로 변경할 때, 만약 2번 할 일이 이미 존재한다면 3→2, 2→3 과 같이 2개의 Todo 데이터에서 order를 함께 변경해줘야 한다.

  • 아래와 같이 할 일 순서 변경 API를 만들어보자
  • 아래 11번째 내용을 14번째로 바꿔보자
  • 바꾸면 아래와 같이 order 번호가 14로 바뀐것을 확인 할 수 있다.

할 일 완료/해제 API 만들기

할 일 완료/해제 API는 done이라는 값을 전달받아 할 일 완료/해제 기능을 구현할 수 있다.

  • 할 일 완료/해제 API에서 가장 중요한 것은 API에서 Todo 항목의 doneAt이라는 필드에 완료된 시점의 시간을 기입해야 한다는 것이다.
  • 만약, done값이 true로 전달되면, 해당 할 일이 완료된 것으로 간주하고 doneAt필드에 현재 시간을 기록하게된다. 이를 통해 언제 작업이 완료되었는지 추적이 가능하며, done 값이 false로 전달되면, doneAt 필드또한 null로 수정이 필요하다.
  • 아래와 같은 코드를 추가 해주자.
 const { order, done } = req.body; // done 추가
// 아래쪽에
  if (done !== undefined) {
    // 변경하려는 '해야할 일'의 doneAt 값을 변경합니다.
    currentTodo.doneAt = done ? new Date() : null; // true 일시 현재시간을 넣어주고 false 면 null값을 넣어준다.
  }
  • 위의 코드를 추가 했으면 insomnia를 통해 아래와 같이 done 값을 true 로 입력해주면
  • 아래와 같이 doneAt 값이 생기면서 현재 시간이 추가되면서 나오게 된다.
  • 마찬가지로 done 의 값을 false로 입력해주면
  • doneAt의 값이 null로 나오는 것을 확인 할 수 있었다.

할 일 삭제 API 만들기

API는 삭제할 할 일의 ID를 클라이언트에게 전달받아, MongoDB에서 해당 데이터를 삭제하도록 구현하게 된다.

// 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({});
});
  • 위 코드가 입력이 되었으면 이번엔 할 일을 삭제할 시간이다.
  • 위의 13번째 내용을 삭제하기위해 아래와 같이 13번째의 id 값을 입력해주고 send를 누르면
  • 12번째 와 14번째 사이에 있어야 할 13번째 값이 삭제된 모습을 볼 수 있다.

할 일 내용 변경 API 만들기

const { order, done, value } = req.body;

  if (value) {
    // 변경하려는 '해야할 일'의 내용을 변경합니다.
    currentTodo.value = value;
  }
  • 내용을 변경 하는 연습을 해보자. PATCH를 통해 아래와 같이 value 값을 입력 해준다.
  • 그러면 14번째 value 값이 변경된 모습을 볼 수 있당.

0개의 댓글