MVC 패턴 웹 애플리케이션 구축하기

MVC (Model-View-Controller) 패턴은 소프트웨어 디자인 패턴으로, 애플리케이션을 모델, 뷰 및 컨트롤러 세 가지 주요 구성 요소로 분리하여 개발하는 방법입니다. Node.js와 Express.js를 활용하여 MVC 패턴을 구현하려면 다음과 같은 구조로 폴더를 구성할 수 있습니다.

Models란?

데이터와 데이터 처리를 담당합니다. 데이터의 유효성 검사, 저장 및 가져오기와 같은 작업을 처리합니다.

Views란?

사용자에게 보여지는 내용을 담당합니다. 클라이언트에게 데이터를 보여주는 역할을 합니다.

Controllers란?

모델과 뷰 사이의 중간 역할을 수행합니다. 클라이언트의 요청을 받아 해당 요청에 맞는 모델 작업을 처리하고, 그 결과를 뷰에 전달합니다.


MVC 패턴 폴더 구조 예시

  • Name
    폴더 구조 예시
    Type
    Description

    app.js는 Express 애플리케이션의 진입점이며, 라우팅, 미들웨어 설정 등을 관리합니다. routes 폴더 내에서는 각각의 엔티티(사용자, 포스트 등)에 대한 라우팅을 분리하여 관리합니다.

    이러한 구조를 기반으로, Express.js의 미들웨어 및 라우팅을 활용하여 클라이언트의 요청을 처리하고 데이터를 모델과 뷰 사이에서 조율하는 방식으로 MVC 패턴을 구현할 수 있습니다.

폴더 구조

root/
├─ models/
│  ├─ user.js
│  └─ post.js

├─ views/
│  ├─ home.ejs
│  └─ profile.ejs

├─ controllers/
│  ├─ userController.js
│  └─ postController.js

├─ public/ (정적 파일)

├─ routes/
│  ├─ index.js
│  ├─ userRoutes.js
│  └─ postRoutes.js

├─ app.js
└─ package.json

app.js (Express 설정)

  • Name
    Express 설정
    Type
    Description

    Express 프레임워크를 사용하여 웹 애플리케이션을 구축하고, 라우팅을 중심으로 웹 요청을 처리하는 방법입니다. 라우팅은 클라이언트의 요청에 따라 적절한 핸들러 함수를 호출하여 해당 요청을 처리하는 매커니즘입니다.

    기존 설정과 비교했을 때, app.use('/users', userRoutes)가 추가 된걸 확인할 수 있습니다.

    기본 설정 코드 보기

app.js

const express = require('express');
const app = express();
const userRoutes = require('./routes/userRoutes');
const postRoutes = require('./routes/postRoutes');

app.use(express.json());
app.use('/users', userRoutes);
app.use('/posts', postRoutes);

app.listen(3000, () => {
    console.log('Server is running on port 3000');
});

Routes

  • Name
    API 엔드포인트 정의
    Type
    Description

    사용자 관련 API 엔드포인트를 라우팅 파일에 정의하여 사용자 컨트롤러를 통해 두 가지 종류의 HTTP 요청을 처리하는 방법을 보여줍니다.

userRoutes.js

const express = require('express');
const router = express.Router();
const userController = require('../controllers/userController');

router.get('/', userController.getAllUsers);
router.post('/', userController.createUser);

module.exports = router;

Controllers

  • Name
    사용자 요청 처리
    Type
    Description

    사용자 관련 로직을 처리하는 컨트롤러입니다. 코드는 두 가지 예를 통해 API 요청에 대한 처리 방법을 보여줍니다.

userController.js

const { User } = require('../models');

exports.getAllUsers = async (req, res) => {
    try {
        const users = await User.findAll();
        res.json(users);
    } catch (error) {
        res.status(500).json({ message: 'Error retrieving users' });
    }
};

exports.createUser = async (req, res) => {
const { name, email } = req.body;
    try {
        const newUser = await User.create({ name, email });
        res.json(newUser);
    } catch (error) {
        res.status(500).json({ message: 'Error creating user' });
    }
};

Models

  • Name
    ORM(Sequelize를 활용한 모델 정의)
    Type
    Description

    Sequelize를 사용하여 데이터베이스 모델을 정의하는 파일의 예제입니다. Sequelize는 Node.js에서 SQL 데이터베이스를 다루기 위한 프로미스 기반 ORM(Object-Relational Mapping) 라이브러리입니다.

user.js

const { DataTypes } = require('sequelize');
const sequelize = require('../database');

const User = sequelize.define('User', {
    name: {
        type: DataTypes.STRING,
        allowNull: false
    },
    email: {
        type: DataTypes.STRING,
        allowNull: false
    }
});

module.exports = User;

MVC 패턴 정리

간단한 예시 코드입니다. Sequelize를 사용하여 데이터베이스 모델을 정의하기 위해 별도의 설정이 필요하고, Express.js를 활용하여 라우팅과 컨트롤러 구성의 경우 각 파일의 경로 및 설정에 따라 코드 오류 처리, 보안 등 추가 사항을 고려해야합니다.

현재 글은 MVC 패턴에 대해서 정리했지만, MVCS(Service) 패턴을 권장드립니다. 서비스 레이어를 추가하여 컨트롤러 코드를 더욱 깔끔하게 유지하고, 재사용 가능한 비즈니스 로직을 모듈화할 수 있습니다. 로직을 분리하는 것은 MVC 패턴을 확장하고 보다 모듈화 할 수 있습니다.