3 분 소요

Layered Pattern

모듈화가 필요한 이유

  • 협업 가능한 코드를 작성하는 것이 좋은 개발자의 역할이다.
  • 좋은 코드, 협업 가능한 코드는 무었인가?
  • 확장성, 재사용성, 유지-보수 가능성, 가독성, 테스트 가능성이 좋은 코드이다.

MVC Pattern [ Model, View, Controller ]

image-20220323093542164

접근 순서로는 Router -> Controller -> Service -> Model

Controller - 요청 / 응답 처리 Service - 비지니스 로직처리 Model - SQL 처리

view

  • 유저와 상호작용이 일어나는 것이며

  • 화면에 보여주기 위한 역할이다.

Controller

  • view와 model을 잇는 다리 역할을 하는 부분입니다.
  • 유저의 요청에 응답하는 부분으로 요청을 받아서 처리합니다.

Model

  • 서버에서 필요한 모든 데이터는 모델에서 정의된다.
  • prisma를 사용해서 데이터베이스 모델을 정의하고 데이터베이스와 소통합니다.

MVC pattern 장점

  • 염려의 분리 [ 각각의 레이어가 하는 역할이 명확하다. ]
  • 동시적인 개발 [ 세개의 레이어로 역할이 나뉘어져 있어 동시적인 개발 및 협업이 가능하다.]
  • 수정의 용이함 [ 다른 레이어에 영향을 주지 않고 문제가 있는 로직을 찾아 해결이 가능하다.]
  • 테스트-주도 개발 [ 각각의 레이어, 그리고 레이어에 속한 모듈을 테스트하기 좋습니다. ]

예시

하나의 파일에 담겨 있던 로그인 파일을 MVC 패턴을 이용해서 분리해 봤습니다.

const { PrismaClient } = require("@prisma/client");
const prisma = new PrismaClient();
const bcrypt = require("bcrypt");
const jwt = require("jsonwebtoken");
const salt = bcrypt.genSalt();

const userLogin = async (req, res) => {
  try {
    const { email, password } = req.body;

    if (email === undefined || password === undefined) {
      const error = new Error("KEY_ERROR");
      error.statusCode = 400;
      throw error;
    }
    const selectUser = await prisma.$queryRaw`
        SELECT * FROM users WHERE email = ${email}`;

    //유저정보에 이메일 없음
    if (selectUser[0] == undefined) {
      const error = new Error("NOT_A_USER");
      error.statusCode = 404;
      throw error;
    }
    //비밀번호 체크
    const checkpw = bcrypt.compareSync(password, selectUser[0].password);

    console.log(checkpw);
    //if(true) -> Token 생성
    if (checkpw) {
      const user = { user_id: selectUser[0].id };
      const token = jwt.sign(user, process.env.SECRET);
      const user_info = jwt.verify(token, process.env.SECRET, {
        expiresIn: "1h",
      });
      return res.status(200).json({ message: token });
    } else {
      const error = new Error("WRONG_PASSWORD");
      error.statusCode = 400;
      throw error;
    }
  } catch (error) {
    return res.status(error.statusCode || 500).json({ message: error.message });
  }
};

module.exports = { userLogin };

Index.js

const express = require("express");
const router = express.Router(); // express 라우팅 기능을 사용하기 위해서 router 객체가 필요합니다.

const userRoute = require("./userRoute"); -> userRoute를 불러오고

router.use("/user", userRoute); // '/users' 엔드포인트를 처리하기 위해 UserRouter 를 붙여줍니다.

module.exports = router; // 이렇게 내보낸 router 는 express app 의 미들웨어로 사용됩니다.

userRoute.js

const express = require("express");
const router = express.Router();

const userController = require("../controllers/userController"); ->Controller에서 불러온다.
// Route 는 오직 Controller 에만 의존 합니다.

router.post("/login", userController.login);

module.exports = router; // 이렇게 내보내면 부모 router 에 자동으로 연결됩니다. -> index.js로 연결

userController.js [ 유저의 요청에 응답하는 부분으로 req, res 를 이용한다. ]

const userService = require("../services/userService");

const login = async (req, res, next) => {
  try {
    const { email, password } = req.body;

    if (email === undefined || password === undefined) {
      const error = new Error("KEY_ERROR");
      error.statusCode = 400;
      throw error;
    }
    const token = await userService.login(email, password);  -> service  연결

    res.status(201).json({
      message: "LOGIN_SUCESS",
      token,   -> 토큰 출력
    });
  } catch (err) {
    return res.status(err.statusCode || 500).json({ message: err.message });
  }
};


module.exports = {
  login,
};

userService.js [ 비지니스 로직 처리, 커스텀 에러 처리 ]

const userDao = require("../models/userDao");
const bcrypt = require("bcrypt");
const jwt = require("jsonwebtoken");
const { UNSAFE_NavigationContext } = require("react-router-dom");


const login = async (email, password) => {
  const selectUser = await userDao.selectUser(email, password);  -> model  연결 

  if (selectUser[0] == undefined) {
    const error = new Error("NOT_A_USER");
    error.statusCode = 404;
    throw error;
  }
  //비밀번호 체크
  const checkpw = bcrypt.compareSync(password, selectUser[0].password);

  //if(true) -> Token 생성
  if (checkpw) {
    const user = { user_id: selectUser[0].id };
    const token = jwt.sign(user, process.env.SECRET);
    // const user_info = jwt.verify(token, process.env.SECRET, {
    //   expiresIn: "1h",
    // });
    return token;
  } else {
    const error = new Error("WRONG_PASSWORD");
    error.statusCode = 400;
    throw error;
  }
};


module.exports = {  login,  };

userDao.js [ Model을 Dao라는 이름을 쓴다. 유저 데이터에 접근하는 객체라는 의미이다. ]

const { PrismaClient } = require("@prisma/client");
const prisma = new PrismaClient();

const selectUser = async (email) => {
  const selectUser = await prisma.$queryRaw`
    SELECT * FROM users WHERE email = ${email}`;

  return selectUser;  -> 리턴으로 Service로 데이터를 보냄
};


module.exports = {
  selectUser,
};

카테고리:

업데이트:

댓글남기기