Express + Mysql 로 회원,투두리스트 API 설계 및 구현 - 1

GunYong·2024년 9월 15일

React

목록 보기
11/17

API 설계

강의를 들으며 배운 내용을 바탕으로 구현해보려고 한다.

Express와 mysql을 docker를 이용하여 구성한다.

postman을 이용해서 작동을 확인하고 DB를 붙인 후 배포하여 프론트와 연결해보는 과정을 해보려고 한다.

매번 배포된 Api만 받아서 사용했었는데 직접 설계하고 배포 후 연결까지 갈 길이 멀다.

회원

  1. 회원가입

    (post) /signup

    • req : body (id / pw / name)
    • res : 응답 메세지
    // 회원가입
    router.post("/signup", function (req, res) {
      let { userId } = req.body;
      if (req.body) {
        db.set(userId++, req.body);
    
        res.status(201).json({
          message: `${db.get(userId - 1).name}님 환영합니다.`,
        });
      } else {
        res.status(400).json({
          message: "올바른 입력값을 입력해주세요.",
        });
      }
    });
  2. 개별 회원 조회

    (get) /users/:id

    • req : id
    • res : id, name
    // 개별 회원 조회
    router.get("/users", function (req, res) {
      let { userId } = req.body;
    
      const user = db.get(userId);
      if (user) {
        res.status(200).json({
          message: `안녕하세요 ${user.name}님 반갑습니다.`,
        });
      } else {
        res.status(400).json({
          message: "해당 유저는 존재하지 않습니다.",
        });
      }
    });
  3. 로그인

    (post) /login

    • req : body (id / pw)
    • res : 응답 메세지
    // 로그인
    router.post("/login", function (req, res) {
      const { userId, password } = req.body;
      let loginUser = {};
    
      // 해당 db안에는 value / key / Map 순으로 담겨있다.
      db.forEach(function (user, id) {
        if (user.userId === userId) {
          loginUser = user;
        }
      });
    
      if (Object.keys(loginUser).length) {
        // pwd 비교
        if (loginUser.password === password) {
          res.status(200).json({
            message: "올바른 비밀번호 입니다.",
          });
        } else {
          res.status(400).json({
            message: "올바른 비밀번호를 입력해주세요.",
          });
        }
      } else {
        res.status(400).json({
            message: "존재하지 않는 회원입니다.",
          });
      }
    });
  4. 회원 탈퇴

    (delete) /users/:id

    • req : id
    • res : 응답 메세지
    // 개별 회원 탈퇴
    router.delete("/users/:id", function (req, res) {
      let { userId } = req.body;
    
      const user = db.get(userId);
      if (user) {
        db.delete(userId);
        res.status(200).json({
          message: `${user.name}님 지금까지 사용해주셔서 감사합니다.`,
        });
      } else {
        res.status(400).json({
          message: "해당 회원은 존재하지 않습니다.",
        });
      }
    });

Database 연결

여기서 docker 와 mysql을 이용하여 DB를 연결해줄 것이다.

  1. 데이터베이스 생성

    CREATE DATABASE todo_app;
    USE todo_app;
  2. users 테이블 생성

    CREATE TABLE users (
        id INT AUTO_INCREMENT PRIMARY KEY,
        userId VARCHAR(255) NOT NULL,
        password VARCHAR(255) NOT NULL,
        name VARCHAR(255) NOT NULL
    );

여기서 설정해줄 것은 기존에 id를 포스트맨으로 테스트 할 때

// Map => key, value
let db = new Map();
let id = 1;

db.set(userId++, req.body);

증감 연산자를 통해서 저장할때 증가하게 설정하였다.

하지만 테이블 설정해줄때 AUTO_INCREMENT를 통해서 자동으로 증가시켜주고 유저를 구분하는 userId를 따로 설정해준다.

  1. Express와 Mysql 연결

    yarn add mysql2 // 설치
    
    const express = require("express");
    const router = express.Router();
    const mysql = require("mysql2");
    
    // MySQL 연결 설정
    const db = mysql.createConnection({
      host: "localhost",
      user: "root",
      password: "root",
      database: "todo_app",
    });
    
    db.connect((err) => {
      if (err) throw err;
      console.log("MySQL connected...");
    });
  2. 회원가입 API db와 연결

    // 회원가입
    // 회원가입
    router.post("/signup", function (req, res) {
    const { userId, password, name } = req.body;
    
    // 먼저 userId가 이미 존재하는지 확인
    db.query("SELECT * FROM users WHERE userId = ?", [userId], (err, result) => {
     if (err) {
       return res.status(500).json({ message: "데이터베이스 오류", error: err });
     }
    
     if (result.length > 0) {
       // userId가 이미 존재할 경우
       return res.status(400).json({
         message: "이미 존재하는 사용자 ID입니다.",
       });
     }
    
     // userId가 중복되지 않으면 회원가입 진행
     db.query(
       "INSERT INTO users (userId, password, name) VALUES (?, ?, ?)",
       [userId, password, name],
       (err, result) => {
         if (err) {
           return res.status(400).json({
             message: "회원가입 중 오류가 발생했습니다.",
             error: err,
           });
         }
    
         res.status(201).json({
           message: `${name}님 환영합니다.`,
         });
         console.log(result);
       }
     );
    });
    });
    
  3. postman으로 올바른 body 값을 담아서 응답 확인 후 데이터베이스 확인

  4. 로그인 API db와 연동 후 테스트

// 로그인
router.post("/login", (req, res) => {
  const { userId, password } = req.body;

  // MySQL 쿼리 실행: userId로 사용자 조회
  db.query("SELECT * FROM users WHERE userId = ?", [userId], (err, results) => {
    if (err) {
      return res.status(500).json({ message: "데이터베이스 오류", error: err });
    }

    // 결과가 없는 경우 (사용자가 없는 경우)
    if (results.length === 0) {
      return res.status(400).json({ message: "존재하지 않는 회원입니다." });
    }

    // 사용자 정보 가져오기
    const loginUser = results[0];

    // 비밀번호 확인
    if (loginUser.password === password) {
      return res.status(200).json({
        message: "올바른 비밀번호입니다.",
      });
    } else {
      return res.status(400).json({
        message: "올바른 비밀번호를 입력해주세요.",
      });
    }
  });
});

회원가입을 통해서 저장된 userId로 사용자를 조회하고 예외처리까지 해주었다.

  1. 개별 회원 조회 API db와 연결 및 예외 처리
// 개별 회원 조회 (userId를 URL로 받음)
router.get("/users/:userId", function (req, res) {
  const { userId } = req.params; // URL에서 userId 가져오기

  db.query("SELECT * FROM users WHERE userId = ?", [userId], (err, result) => {
    if (err) {
      return res.status(500).json({
        message: "데이터베이스 오류",
      });
    }

    if (result.length === 0) {
      return res.status(400).json({
        message: "해당 유저는 존재하지 않습니다.",
      });
    }

    res.status(200).json({
      message: `안녕하세요 ${result[0].name}님 반갑습니다.`,
      response: result,
    });
  });
});
  1. 개별 회원 삭제 API db와 연결 및 예외 처리
// 개별 회원 탈퇴 (userId를 URL로 받음)
router.delete("/users/:userId", function (req, res) {
  const { userId } = req.params; // URL에서 userId 가져오기

  db.query("DELETE FROM users WHERE userId = ?", [userId], (err, result) => {
    if (err) {
      return res.status(500).json({
        message: "데이터베이스 오류",
      });
    }

    // 삭제된 회원이 없는 경우 처리
    if (result.affectedRows === 0) {
      return res.status(400).json({
        message: "해당 회원은 존재하지 않습니다.",
      });
    }

    res.status(200).json({
      message: "회원이 성공적으로 삭제되었습니다.",
    });
  });
});

기존에 데이터베이스를 조회하면 2명의 회원이 조회되고 userId가 gunyongbokId1 인 유저를 삭제 처리 한 후 다시 조회하면 정상적으로 삭제가 된 것을 볼 수 있다.

느낀점

간단한 API를 직접 설계하고 만들어보니까 신기하고 재밌었다. 벡엔드한테 API를 받아쓰고 명세서를 통해서만 응답 타입과 요청 타입을 수동적으로 맞춰주고 조율했어야 했던 것들을 직접 만드니까 수고가 줄어들고 시간이 절약되었다.

추가로 예외처리 생각하는 부분이 까다로웠다. 프론트에서 할 때와는 다르게 근본적으로 예외처리 해야 하는 경우가 무척 많았고 프로젝트를 진행하면서 필요한 예외처리를 추가해 나아갈 예정이다.

남은 할일 테이블도 구현한 후 배포까지 해서 프론트와 연결해보고 싶다.

0개의 댓글