Model
- 앱의 데이터와 비즈니스 로직을 관리.
- 데이터의 상태를 정의하고 이를 변경하는 기능 제공.
// 유저 모델
class UserModel {
constructor() {
this.users = [];
}
// 데이터 추가
addUser(user) {
if (user.name && user.email) {
this.users.push(user);
} else {
throw new Error('유효하지 않은 사용자 데이터');
}
}
// 데이터 수정
updateUser(id, updatedData) {
const userIndex = this.users.findIndex((user) => user.id === id);
if (userIndex !== -1) {
this.users[userIndex] = { ...this.users[userIndex], ...updatedData };
} else {
throw new Error('사용자를 찾을 수 없음');
}
}
// 데이터 삭제
deleteUser(id) {
this.users = this.users.filter((user) => user.id !== id);
}
// 데이터 조회
getUser(id) {
return this.users.find((user) => user.id === id);
}
// 데이터 조회
getUsers() {
return this.users;
}
}
View
- 사용자에게 데이터를 시각적으로 표시하는 역할.
- Model의 데이터를 기반으로 UI를 렌더링.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>User Management</title>
</head>
<body>
<h1>User Management</h1>
<!-- 사용자 추가 폼 -->
<form id="addUserForm">
<label for="name">Name:</label>
<input type="text" id="name" name="name" required>
<label for="email">Email:</label>
<input type="email" id="email" name="email" required>
<button type="submit">Add User</button>
</form>
<hr>
<!-- 사용자 목록 -->
<h2>User List</h2>
<ul id="userList"></ul>
<script>
// Controller와 통신을 담당
class UserView {
constructor(apiUrl) {
this.apiUrl = apiUrl;
this.userListElement = document.getElementById('userList');
this.addUserForm = document.getElementById('addUserForm');
this.init();
}
// 초기화
init() {
this.loadUsers();
this.setupEventListeners();
}
// 사용자 목록 로드
async loadUsers() {
try {
const response = await fetch(`${this.apiUrl}/users`);
if (!response.ok) throw new Error('Failed to fetch users');
const users = await response.json();
this.renderUserList(users);
} catch (error) {
console.error(error.message);
}
}
// 사용자 목록 렌더링
renderUserList(users) {
this.userListElement.innerHTML = ''; // 기존 목록 초기화
users.forEach((user) => {
const li = document.createElement('li');
li.textContent = `${user.name} (${user.email})`;
li.id = `user-${user.id}`;
// 삭제 버튼 추가
const deleteButton = document.createElement('button');
deleteButton.textContent = 'Delete';
deleteButton.onclick = () => this.deleteUser(user.id);
li.appendChild(deleteButton);
this.userListElement.appendChild(li);
});
}
// 사용자 추가
async addUser(user) {
try {
const response = await fetch(`${this.apiUrl}/users`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(user),
});
if (!response.ok) throw new Error('Failed to add user');
const newUser = await response.json();
this.loadUsers(); // 목록 새로고침
} catch (error) {
console.error(error.message);
}
}
// 사용자 삭제
async deleteUser(userId) {
try {
const response = await fetch(`${this.apiUrl}/users/${userId}`, {
method: 'DELETE',
});
if (!response.ok) throw new Error('Failed to delete user');
this.loadUsers(); // 목록 새로고침
} catch (error) {
console.error(error.message);
}
}
// 이벤트 리스너 설정
setupEventListeners() {
this.addUserForm.addEventListener('submit', (event) => {
event.preventDefault();
const name = this.addUserForm.name.value;
const email = this.addUserForm.email.value;
if (name && email) {
this.addUser({ name, email });
this.addUserForm.reset(); // 폼 초기화
}
});
}
}
// UserView 인스턴스 생성 및 초기화
const userView = new UserView('http://localhost:3000'); // API URL 설정
</script>
</body>
</html>
Controller
- 사용자 입력(이벤트)을 받아 Model과 View를 조율.
- Model에서 데이터를 가져오거나 수정하고, View에 전달하여 화면을 업데이트.
// 유저 컨트롤러
class UserController {
constructor(userModel) {
this.userModel = userModel;
}
// 사용자 추가
createUser(req, res) {
try {
const { id, name, email } = req.body; // 요청에서 사용자 데이터 가져오기
this.userModel.addUser({ id, name, email });
res.status(201).json({ message: '사용자 생성 성공', user: { id, name, email } });
} catch (error) {
res.status(400).json({ message: error.message });
}
}
// 사용자 조회
getUser(req, res) {
try {
const { id } = req.params; // 요청에서 사용자 ID 가져오기
const user = this.userModel.getUser(id);
if (user) {
res.status(200).json({ user });
} else {
res.status(404).json({ message: '사용자를 찾을 수 없음' });
}
} catch (error) {
res.status(500).json({ message: error.message });
}
}
// 사용자 리스트 조회
getUsers(req, res) {
try {
const users = this.userModel.getUsers();
if (users) {
res.status(200).json({ users });
} else {
res.status(404).json({ message: '사용자 리스트를 찾을 수 없음' });
}
} catch (error) {
res.status(500).json({ message: error.message });
}
}
// 사용자 수정
updateUser(req, res) {
try {
const { id } = req.params; // 요청에서 사용자 ID 가져오기
const updatedData = req.body; // 요청에서 업데이트 데이터 가져오기
this.userModel.updateUser(id, updatedData);
res.status(200).json({ message: '사용자 수정 성공' });
} catch (error) {
res.status(400).json({ message: error.message });
}
}
// 사용자 삭제
deleteUser(req, res) {
try {
const { id } = req.params; // 요청에서 사용자 ID 가져오기
this.userModel.deleteUser(id);
res.status(200).json({ message: '사용자 삭제 성공' });
} catch (error) {
res.status(400).json({ message: error.message });
}
}
}
동작 원리
- 사용자가 View를 통해 애플리케이션에 입력을 제공. (위의 User View에서 <form id='addUserForm'> 부분)
- Controller가 사용자의 입력을 처리하여 Model에 요청(데이터 변경 또는 조회). (UserView의 addUser -> UserController의 createUser -> UserModel의 addUser -> ...)
- Model은 데이터를 업데이트하거나 상태를 반환. (UserView의 getUser -> UserController의 getUser -> UserModel의 getUsers)
- View는 Model로부터 데이터를 받아 UI를 갱신하여 사용자에게 표시. (UserView의 renderUserList)
장점
- 각 구성 요소가 독립적이므로 유지보수와 확장이 용이.
- Model과 View가 분리되어 서로 독립적으로 변경 가능.
- Model -> Controller -> View를 통해 상호작용하기 때문에 Model의 변경이 View에 직접적인 영향을 주지 않는다.
- 그렇기에 Model의 변화가 View에 주는 영향을 최소화할수 있고, 또한 View의 변화는 Model에 영향을 주지 않는다.
- 동일한 Model을 여러 View에서 사용할 수 있음.
- Model이 독립적으로 구성되어 있기에 재사용이 가능하다.
단점
- 작은 애플리케이션에서는 MVC를 구현하는 것이 불필요하게 복잡할 수 있음.
- 작은 규모의 작업에서는 아키텍처를 구현하는 것이 불필요할수도 있다.
- View ↔ Controller ↔ Model 간의 데이터 전달로 인해 성능에 영향을 줄 수 있음.
- View ↔ Controller ↔ Model 단계를 거치며 각 계층에서 추가적인 작업을 각각 수행하므로, 복잡성이 늘어날수록 성능에 악영향을 끼칠수 있음.
- MVC에서 각 계층은 독립적이지만 Controller가 모든 요청을 조율하기 때문에 Controller가 비대해질수 있다. 그로 인해 Controller의 수정이나 확장이 어려워질수 있다.
'Dev > 아키텍처' 카테고리의 다른 글
[아키텍처] MVP 아키텍처 (0) | 2025.01.27 |
---|