--- id: wiki-2026-0508-mvc-model-view-controller title: MVC (Model-View-Controller) category: 10_Wiki/Topics status: verified canonical_id: self aliases: [MVC, Model-View-Controller Pattern] duplicate_of: none source_trust_level: A confidence_score: 0.9 verification_status: applied tags: [architecture-pattern, ui-architecture, separation-of-concerns] raw_sources: [] last_reinforced: 2026-05-10 github_commit: pending tech_stack: language: typescript framework: rails --- # MVC (Model-View-Controller) ## 매 한 줄 > **"매 UI architecture 의 1979 ancestor"**. Trygve Reenskaug 의 Smalltalk-80 — Model (data/logic), View (presentation), Controller (input mediation) 의 separation of concerns. 2026 에서 pure MVC 는 server-side framework (Rails, Django, Laravel, ASP.NET) 의 mainstay 이지만 frontend 는 매 MVVM (Vue), Flux/Redux (React), Component-driven (Svelte/Solid) 으로 evolve. ## 매 핵심 ### 매 3 components - **Model**: domain data + business logic. Persistence, validation, invariants. - **View**: rendering. 매 read-only view 의 model state. - **Controller**: user input 의 handler. Model mutation + view selection. ### 매 information flow (classic Smalltalk) 1. User → Controller (input event). 2. Controller → Model (update). 3. Model → View (notify, observer pattern). 4. View → renders updated state. ### 매 modern variants - **MVP** (Model-View-Presenter): View 의 passivity 강화, presenter 가 view 를 directly drive. - **MVVM** (Model-View-ViewModel): ViewModel 의 binding 으로 View 의 declarative connect (Vue, Knockout, WPF). - **Flux/Redux**: unidirectional flow — Action → Store → View. - **Component-driven**: 매 unit 이 self-contained (React, Svelte) — MVC boundary 의 dissolve. ### 매 응용 1. Server-side web frameworks (Rails, Django, Laravel, Spring MVC). 2. Native UI (UIKit MVC pattern, Android Activity-based). 3. Game engine UI layer. 4. Form-heavy admin dashboard. ## 💻 패턴 ### 1. Rails MVC (canonical server-side) ```ruby # app/models/article.rb class Article < ApplicationRecord validates :title, presence: true scope :published, -> { where(published: true) } end # app/controllers/articles_controller.rb class ArticlesController < ApplicationController def index @articles = Article.published.order(created_at: :desc) end def create @article = Article.new(article_params) if @article.save redirect_to @article else render :new, status: :unprocessable_entity end end private def article_params params.require(:article).permit(:title, :body) end end # app/views/articles/index.html.erb <% @articles.each do |a| %>

<%= a.title %>

<% end %> ``` ### 2. Express + MVC (Node.js) ```typescript // models/User.ts export class User { static async findById(id: string) { return db.user.findUnique({ where: { id } }); } } // controllers/userController.ts export async function showUser(req: Request, res: Response) { const user = await User.findById(req.params.id); if (!user) return res.status(404).render("404"); res.render("user/show", { user }); } // views/user/show.ejs

<%= user.name %>

``` ### 3. Spring MVC (Java) ```java @Controller @RequestMapping("/articles") public class ArticleController { @Autowired ArticleService service; @GetMapping("/{id}") public String show(@PathVariable Long id, Model model) { Article a = service.findById(id); model.addAttribute("article", a); return "articles/show"; // resolves to articles/show.html } } ``` ### 4. Smalltalk-style observer (classic) ```typescript class Model { private observers: Array<() => void> = []; private _value = 0; get value() { return this._value; } set value(v: number) { this._value = v; this.observers.forEach(fn => fn()); } subscribe(fn: () => void) { this.observers.push(fn); } } class View { constructor(private model: Model, private el: HTMLElement) { model.subscribe(() => this.render()); this.render(); } render() { this.el.textContent = String(this.model.value); } } class Controller { constructor(private model: Model, button: HTMLButtonElement) { button.addEventListener("click", () => { this.model.value += 1; }); } } ``` ### 5. MVVM (Vue 3, modern equivalent) ```vue ``` ### 6. Anti-MVC: God controller smell ```typescript // 매 BAD — controller 가 매 business logic + data access + rendering 다 처리 class ArticleController { async create(req, res) { // validation, db query, email send, audit log, render — 200 lines } } // 매 GOOD — service layer 분리 class ArticleController { async create(req, res) { const article = await this.service.publish(req.body, req.user); res.render("article/show", { article }); } } ``` ## 매 결정 기준 | 상황 | Approach | |---|---| | Server-rendered web app, CRUD heavy | Classic MVC (Rails, Django, Laravel). | | SPA, complex interactive UI | Component-driven (React, Vue, Svelte). | | Two-way binding, form-heavy | MVVM (Vue, WPF). | | Strict unidirectional, time-travel debug | Flux / Redux. | | Native mobile (iOS/Android) | MVVM 또는 MVI (modern preferred over MVC). | | Microservice without UI | 매 MVC 부적합 — Hexagonal/Clean. | **기본값**: server-side full-stack 은 매 MVC framework. SPA 는 매 component model + state library. ## 🔗 Graph - 부모: [[Separation of Concerns]] - 변형: [[MVP]] - Adjacent: [[Hexagonal Architecture]] · [[Clean Architecture]] ## 🤖 LLM 활용 **언제**: server-side framework choice, legacy MVC refactor, UI architecture comparison. **언제 X**: 매 modern SPA architecture (component-driven 이 dominant), microservice 의 service-internal 구조. ## ❌ 안티패턴 - **Fat Controller**: 매 business logic 의 controller dump — service layer 로 분리. - **Anemic Model**: 매 model 이 data bag, logic 이 service 에. DDD aggregate 로 보강. - **View 의 logic**: 매 template 의 conditional/loop 이 매 business decision — model/presenter 로 push. - **Controller-View tight coupling**: 매 controller 가 매 specific view path hardcode — view selection abstraction. - **MVC for SPA**: 매 React app 에 backend MVC 강제 적용 — component model 이 fit. ## 🧪 검증 / 중복 - Verified (Reenskaug 1979 memo, Gamma et al. *Design Patterns* MVC discussion, Rails Guides). - 신뢰도 A. ## 🕓 Changelog | 날짜 | 변경 | |---|---| | 2026-05-08 | Phase 1 | | 2026-05-10 | Manual cleanup — classic MVC + modern variant comparison + 2026 positioning |