MVC 패턴이란
MVC 패턴은 소프트웨어 개발에서 사용되는 디자인 패턴 중 하나를 의미한다.
Model - View - Controller의 약자이며 기능 별로 코드를 분리하여 유지보수를 쉽게 하고 확장성과 재사용성을 강화할 수 있다는 장점을 가지고 있다. 각 모듈들의 상관관계를 그림으로 나타내면 아래와 같다.
Model
모델은 `데이터를 저장, 검색, 업데이트`하고 변경 사항을 관리하는 역할을 한다.
일반적으로 `View나 Controller에 대한 직접적인 참조를 가지지 않아야` 한다.
View
`사용자의 인터페이스`를 담당하며, 데이터를 시작적으로 표현하는 기능을 한다.
사용자가 정보를 보고 상호작용을 하는 부분으로, 데이터를 `모델에서 가져와 표시`하는 역할을 한다.
View는 `사용자가 입력한 데이터를 컨트롤러로 전달`하고, `모델로부터 데이터를 받아 표시`한다.
Controller
컨트롤러는 `사용자 입력을 처리`하고, `모델과 뷰 간의 통신을 관리`한다.
사용자가 View와 상호작용할 때 Controller는 해당 입력을 해석하고 모델을 업데이트 하거나, 새로운 데이터를 View에 전달한다.
`Model과 View 중간에서 동작`하면서, 각각이 독립적으로 작동할 수 있도록 돕는다.
MVC 패턴의 예시
MVC 패턴의 간단한 예시는 아래와 같이 구현할 수 있다.
Model
// models/UserModel.js
class UserModel {
constructor() {
this.users = [];
}
addUser(user) {
this.users.push(user);
}
getUsers() {
return this.users;
}
}
module.exports = UserModel;
User의 데이터를 저장하는 class이다.
addUser, getUsers 함수를 통해 user배열에 user데이터를 추가하고 빼는 작업만이 가능하다.
Controller
const UserModel = require('../models/UserModel');
class UserController {
constructor() {
this.userModel = new UserModel();
}
addUser(user) {
this.userModel.addUser(user);
}
getUsers() {
return this.userModel.getUsers();
}
}
module.exports = UserController;
기능 자체는 Model과 유사하다.
유저를 추가하고 리턴하는 함수를 가지고 있다.
// app.js
const express = require('express');
const app = express();
const UserController = require('./controllers/UserController');
const path = require('path');
const userController = new UserController();
app.set('view engine', 'ejs');
app.set('views', path.join(__dirname, 'views'));
app.use(express.urlencoded({ extended: true }));
app.get('/', (req, res) => {
const users = userController.getUsers();
res.render('index', { users });
});
app.post('/add-user', (req, res) => {
const { username } = req.body;
userController.addUser(username);
res.redirect('/');
});
app.listen(3000, () => {
console.log('Server is running on port 3000');
});
app.js에서는 클라이언트 측면에서 발생하는 get과 post명령을 처리하고 있으며,
해당 명령을 처리하는 과정에서 model의 add나 user 함수를 직접 수행하는 것이 아니라,
Controller를 호출하여 Controller가 가진 함수를 바탕으로 데이터를 추가하고 리턴받고 있다.
이러한 방식으로 model은 데이터를 저장하는 기능만, Controller는 사용자 요청을 바탕으로 model의 데이터를 변경하는 역할을 할 수 있다.
View
<!-- views/index.ejs -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>MVC Example</title>
</head>
<body>
<h1>User List</h1>
<ul>
<% for (const user of users) { %>
<li><%= user %></li>
<% } %>
</ul>
<form action="/add-user" method="post">
<input type="text" name="username" placeholder="Enter username" />
<button type="submit">Add User</button>
</form>
</body>
</html>
app.js에서 확인할 수 있듯이,
`app.get('/', (req, res) => {
const users = userController.getUsers();
res.render('index', { users });
});`
위 작업을 통해 컨트롤러가 데이터를 리턴하고, view는 해당 데이터를 이용하여 렌더링하는 것을 확인할 수 있다.
따라서 view는 컨트롤러가 제공한 데이터만을 활용하여 사용자에게 인터페이스를 제공하며, model과 직접적인 연관관계를 가지지 않는다.
MVC 패턴과 Service 구조의 결합
MVC 패턴을 사용하다 보면, Controller가 각종 로직을 다루는 과정에서 Model과의 결합도가 지나치게 높아지는 경우가 발생할 수 있다.
결합도가 지나치게 높아질 경우, 코드의 재사용성이나 확장성과 같이 MVC 패턴을 통해 얻고자 했던 이점이 줄어들 수 있다.
따라서 이를 해결하기 위해 Service 구조를 도입하여 각 컴포넌트 간의 결합도를 낮추는 시도를 할 수 있다.
Service는 비즈니스 로직을 수행하는 컴포넌트로, 정보를 가공하는 과정을 담당한다.
이 Service 컴포넌트는 Controller에 의해 호출되며, 어떤 Controller가 호출하는지에 관계 없이 입력 받은 매개변수에 따라 결과물을 리턴한다.
따라서 View에서 변화가 생기더라도, Controller에서는 변화가 생길 수 있지만 Service는 이에 완전히 독립적이기 때문에 리팩토링을 진행할 필요가 없다.
즉, 컴포넌트 간의 의존성을 낮춰 재사용성을 높이는 역할을 하는 것이다.
'개인학습' 카테고리의 다른 글
OAuth (0) | 2023.10.23 |
---|---|
Linear-time Temporal Logic guided Greybox Fuzzing 정리 (0) | 2023.09.05 |
(TIL) Model Checking (0) | 2023.08.22 |