디자인 패턴은 소프트웨어의 개발 방법을 공식화 한 것으로, 협업시 커뮤니케이션의 효율성을 높일 수 있는 기법이다.
Model, View, Controller의 약자로 어플리케이션을 세가지의 역할로 구분한 개발 방법론이다. 아래 개념을 웹에 적용해보자. 사용자가 웹사이트에 접속하면, Controller는 Model을 호출하고, Model은 데이터 소스를 제어한 후에 결과를 리턴하고, Controller는 그 결과를 View에 반영하고, View는 사용자에게 보여진다.
이러한 디자인 패턴을 적용하게 된다면 역할(관심사, 염려)에 따라 코드가 분리된다. 데이터에 변화가 있다면 Model만 수정하고, UI가 수정되면 View만 수정하면 되기 때문에 유지 보수가 용이해진다.
UI는 프론트엔드가 담당하기 때문에 백엔드는 Controller와 Model을 담당하게 된다. 코드의 복잡성에 따라 두 개 이상으로 확장해 레이어링해야할 수도 있다. 이번 프로젝트(webucks)에서는 Route, Controller, Service, Model로 나눠보는 연습을 했다. 각각의 레이어는 바로 아래 있는 레이어에만 의존한다. (Route => Controller => Service => Model)
1. server.js : 서버 리스닝을 시작하는 엔트리 파일로, 서버 시작을 하는 역할만 한다.
import dotenv from 'dotenv';
dotenv.config();
import app from './app';
const { PORT } = process.env; //PORT번호는 .env에 있음
app.listen(PORT, () => console.log(`server on ${PORT}`));
import dotenv from 'dotenv';
import express from 'express';
import router from './routes';
const app = express();
dotenv.config();
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.use('/', router); //app이 라우터를 사용할 수 있도록 지정
app.use((err, req, res, next) => {
const { status, message } = err;
console.error(err);
res.status(status || 500).json({ message });
});
export default app;
자바스크립트에서 폴더 내에 index.js파일이 생성되면 그 폴더는 패키지로서 동작할 수 있다.
//index.js
import express from 'express';
const router = express.Router();
import categoryRouter from './categoryRouter';
import productRouter from './productRouter';
import userRouter from './userRouter';
router.use('/category', categoryRouter);
router.use('/product', productRouter);
router.use('/user', userRouter);
export default router;
//userRouter.js
import express from 'express';
const router = express.Router();
import { userController } from '../controllers';
router.get('/', userController.getUser);
// 이 경로로 들어오면 userController에 있는 getUser함수를 실행해주세요!
router.post('/new-user', userController.createUser);
export default router;
//only 경로분기(라우팅)만 한다!
//라우트는 컨트롤러에 명령을 내려야한다.
//index.js
import categoryController from './categoryController';
import productController from './productController';
import userController from './userController';
export { categoryController, productController, userController };
//userController.js
mport { userService } from '../services';
const getUser = async (req, res) => {
try {
const users = await userService.getUser();
res.status(201).json({
message: 'SUCCESS',
data: users,
});
} catch (err) {
console.log(err);
}
};
const createUser = async (req, res) => {
try {
const { email, password, username, address, phone_number } = req.body;
await userService.createUser(
email,
password,
username,
address,
phone_number
);
res.status(201).json({
message: 'CREATED',
});
} catch (err) {
console.log(err);
}
};
export default { getUser, createUser };
//controller 안에 있는 함수는 request을 받고 response을 보내주는 것을 control한다!
//request를 받고, 이런 저런 처리를 해서 response를 보내줌.
//index.js
import categoryServices from './categoryServices';
import productService from './productService';
import userService from './userService';
export { categoryServices, productService, userService };
//userService.js
import { userDao } from '../models';
const getUser = async () => {
return await userDao.getUser();
};
const createUser = async (email, password, username, address, phone_number) => {
return await userDao.createUser(
email,
password,
username,
address,
phone_number
);
};
export default { getUser, createUser };
//service는 어떠한 비즈니스 로직이 없으면 그냥 DAO만 호출하기도 한다.
//예를 들어, role에 따라 permission을 부여하는 로직을 작성할 수 있다.(인증은 미들웨어에서 한다)
//또는, 아이디가 이미 있는지 검증해서 dao 호출할 수도 있다.
//index.js
import categoryDao from './categoryDao';
import productDao from './productDao';
import userDao from './userDao';
export { categoryDao, productDao, userDao };
//userDao.js
import prisma from '../../prisma';
const getUser = async () => {
const users = await prisma.$queryRaw`
SELECT
email,
password,
username,
address,
phone_number
FROM
users;
`;
return users;
};
const createUser = async (email, password, username, address, phone_number) => {
return await prisma.$queryRaw`
INSERT INTO
users(
email,
password,
username,
address,
phone_number
)
VALUES (
${email},
${password},
${username},
${address},
${phone_number}
);
`;
};
export default { getUser, createUser };
prisma 폴더에 index.js 파일을 만들어 prisma를 인스턴스화했으며, Service 레이어에서 데이터베이스에 접근하는 로직에 사용될 수 있도록 했다. 이를 통해 코드를 중복으로 사용하는걸 피할 수 있었다.
import { PrismaClient } from '@prisma/client';
const prisma = new PrismaClient();
export default prisma;
middlewares는 컨트롤러에 닿기 전 반복되는 로직을 모듈화 해 놓는 폴더다. (아직 담긴 내용이 없다)