Layered Pattern
Layered Pattern
모듈화가 필요한 이유
- 협업 가능한 코드를 작성하는 것이 좋은 개발자의 역할이다.
- 좋은 코드, 협업 가능한 코드는 무었인가?
- 확장성, 재사용성, 유지-보수 가능성, 가독성, 테스트 가능성이 좋은 코드이다.
MVC Pattern [ Model, View, Controller ]
접근 순서로는 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,
};
댓글남기기