공부/디자인 패턴

[디자인 패턴] MVC 와 MVVM 패턴 이해하기

dev_jiwonpark 2026. 1. 29. 18:45

UI 아키텍처를 설계할 때 가장 흔히 마주치는 두 가지 패턴, MVC와 MVVM.

이 둘은 비슷해 보이지만 근본적으로 다른 철학을 가지고 있습니다.

핵심 비교

항목 MVC MVVM
중간 역할 Controller ViewModel
주요 기능 View와 Model을 직접 조율 View와 Model을 연결/변환
데이터 흐름 단방향 (Controller 중심) 양방향 (Data Binding)
View-Model 관계 의존성 높음 의존성 낮음
적합한 규모 소·중규모 프로젝트 대규모, 복잡한 UI
테스트 난이도 중간~높음 낮음

MVC 패턴

구조와 역할

MVC는 Model-View-Controller 세 가지 계층으로 구성됩니다.

  • Model: 데이터와 비즈니스 로직 담당
    • 프론트엔드에선 상태 저장, API 응답 데이터 관리를 담당함.
  • View: 사용자에게 보이는 UI
    • HTML/JSX, DOM 요소
  • Controller: 사용자 입력을 받아 Model과 View를 조율
    • 이벤트 핸들러, 함수

실제 예시 1 : MVC 패턴 ( JavaScript )

"좋아요 버튼을 누르면 좋아요 수가 증가하는" 기능을 구현한다고 해봅시다.

// ======== MODEL ========
// 데이터를 저장하고 관리하는 부분
const Model = {
  likes: 0,  // 좋아요 수를 저장
  
  // 좋아요를 증가시키는 메서드
  incrementLikes() {
    this.likes++;
    console.log(`좋아요 수: ${this.likes}`);
  },
  
  // 현재 좋아요 수를 가져오는 메서드
  getLikes() {
    return this.likes;
  }
};

// ======== VIEW ========
// 사용자가 보는 화면을 렌더링하는 부분
const View = {
  // HTML 요소들
  likeButton: document.getElementById('likeBtn'),
  likeCount: document.getElementById('likeCount'),
  
  // 화면을 업데이트하는 메서드
  render(likes) {
    this.likeCount.textContent = `❤️ ${likes}`;  // 좋아요 수를 화면에 표시
  },
  
  // 버튼을 클릭했을 때 할 일을 등록
  bindLikeClick(handler) {
    this.likeButton.addEventListener('click', handler);
  }
};

// ======== CONTROLLER ========
// 사용자 입력을 받아서 Model과 View를 조율하는 부분
const Controller = {
  init() {
    // 초기 화면 표시
    View.render(Model.getLikes());
    
    // 버튼 클릭 이벤트 등록
    View.bindLikeClick(() => {
      // 사용자가 버튼을 클릭했을 때:
      Model.incrementLikes();  // 1. Model의 데이터 변경
      View.render(Model.getLikes());  // 2. View 업데이트
    });
  }
};

// 앱 시작
Controller.init();

 

사용자가 "좋아요 버튼" 클릭
         ↓
    Controller가 감지
         ↓
Model.incrementLikes() ← Controller가 호출
(likes: 0 → 1)
         ↓
View.render(1) ← Controller가 호출
(화면에 "❤️ 1" 표시)

 

핵심 포인트:

  • Model: 그냥 데이터 저장소. 현재 좋아요가 몇 개인지만 알고 있음
  • View: 그냥 화면 표현. HTML 요소들을 렌더링함
  • Controller: 중개인 역할. "사용자가 클릭했어 → Model 데이터 바꿔 → View 업데이트해"
    • 따라서 MVC 패턴의 애플리케이션의 진입접은 컨트롤러 이다.

MVC의 장점

1. 단순하고 직관적 - 개념이 명확하고 개발 속도가 빠름

2. 코드 분리가 효과적 - 기능별로 나뉘어 가독성과 재사용성 증가

3. 결합도 낮음 - 각 컴포넌트가 독립적으로 동작 가능

MVC의 단점

1. View-Model 의존성 높음 - View와 Model이 직접 통신하여 변경에 취약

2. Massive View Controller 문제 - 프로젝트가 커질수록 Controller가 비대해짐

3. 유지보수 어려움 - 복잡한 UI일수록 Controller에서 처리할 게 많아짐


MVVM 패턴

구조와 역할

MVVM은 Model-View-ViewModel 세 가지 계층으로 구성되며, Controller 역할을 ViewModel이 담당합니다.

  • Model: 데이터와 비즈니스 로직 담당 (MVC와 동일)
  • View: 사용자에게 보이는 UI (MVC와 동일)
  • ViewModel: View를 위한 전용 Model - View와 Model 사이의 중간 매개자

MVVM의 주요 특징

Data Binding (양방향 자동 연결)

View의 입력 ↔ ViewModel의 상태 ↔ Model의 변경사항

사용자가 화면에서 입력을 하면 자동으로 ViewModel이 알게 되고, Model이 변경되면 자동으로 View가 업데이트됩니다.

사용자 액션(클릭, 입력 등)을 캡슐화하여 일관되게 처리합니다.

따라서 MVVM 패턴의 애플리케이션의 진입점은 뷰 이다. 

MVVM의 장점

1. View와 Model의 완전한 독립성 - 서로 영향을 주지 않음

2. 효율적인 테스트 - ViewModel을 Model과 분리하여 단위 테스트가 쉬움

3. 코드량 감소 - Data Binding으로 반복되는 업데이트 코드 제거

4. 확장성 우수 - 복잡한 UI도 깔끔하게 관리 가능

MVVM의 단점

1. 학습곡선 높음 - Data Binding 이해 필요

2. 설계가 어려움 - ViewModel을 제대로 설계하려면 경험 필요

3. 과도할 수 있음 - 간단한 프로젝트에는 과한 구조


MVC와 MVVM의 핵심 차이

1. 역할 분담

MVC에서 Controller의 역할:

  • 사용자 이벤트 → Model 변경
  • 사용자 이벤트 → View 변경 
       User Input
           ↓
    View (input)
           ↓ [View가 이벤트 발생]
    Controller [이벤트 리스너가 실행]
           ↓ [Controller가 Model 변경]
    Model [데이터 변경]
           ↓ [Controller가 명시적으로 View 업데이트]
    View (display) [수동으로 갱신]

 

MVVM에서 ViewModel의 역할:

  • 사용자 이벤트 → View 변경 (자동)
  • ViewModel의 데이터가 변경되면 View가 자동으로 감지하고 업데이트
       User Input
           ↓
    View ↔ ViewModel [양방향 자동 연결!]
           ↕️ (Data Binding)
           ↓
    Model [데이터]

View와 ViewModel이 자동으로 동기화된다.

2. 데이터 흐름

MVC: 단방향 흐름

사용자 입력 → Controller → Model 변경/View 변경

 

MVVM: 양방향 자동 연결

사용자 입력 → View ↔ ViewModel ↔ Model
(Data Binding으로 자동 동기화)

3. 의존성 관계

먼저 의존성이란?

" A 가 B에 의존한다 " => " A가 B에 대해 알아야 한다." 라는 말과 같다. 

 

MVC:

View ← → Controller ← → Model
   (높은 의존성)

 

실제 코드를 통해 MVC의 높은 의존성에 대해 이해해 볼수 있다.

예를 들어 사용자 정보 표시를 하는 기능이 있다고 가정해보자

// ======== MODEL ========
const UserModel = {
  user: {
    id: 1,
    name: "김철수",
    email: "kim@example.com",
    phone: "010-1234-5678"
  },
  
  getUser() {
    return this.user;
  }
};

// ======== VIEW ========
const UserView = {
  render(user) {
    // 주의: View가 Model의 구조를 직접 알고 있음!
    document.getElementById('name').textContent = user.name;
    document.getElementById('email').textContent = user.email;
    document.getElementById('phone').textContent = user.phone;
  }
};

// ======== CONTROLLER ========
const UserController = {
  init() {
    const userData = UserModel.getUser();
    UserView.render(userData);
  }
};

UserController.init();

 

여기서 문제점은 Model 이 변경될 때이다.

예를들어 백엔드에서 필드 이름을 변경했을때! 

// Model이 변경됨
const UserModel = {
  user: {
    id: 1,
    userName: "김철수",      // ← name → userName으로 변경됨!
    userEmail: "kim@example.com",  // ← email → userEmail로 변경됨!
    userPhone: "010-1234-5678"     // ← phone → userPhone으로 변경됨!
  },
  getUser() {
    return this.user;
  }
};

// View도 수정해야 함!
const UserView = {
  render(user) {
    document.getElementById('name').textContent = user.userName;    // ← 수정 필요
    document.getElementById('email').textContent = user.userEmail;  // ← 수정 필요
    document.getElementById('phone').textContent = user.userPhone;  // ← 수정 필요
  }
};

 

따라서 View가 Model을 직접 참조하고 있기 때문에 Model이 바뀌게 되면 View도 반드시 바뀌어야한다는 이유 때문에 MVC의 특징으로 " 높은 의존성 " 이 있다는 것이다. 

 

MVVM:

View ↔ ViewModel ← Model
(View와 Model은 분리됨)

 

MVC에 비해 MVVM은 낮은 의존성을 가지고 있는데 View는 ViewModel만 알고 있고 Model은 알지 못하며 ViewModel만이 Model을 알고 있기 때문에 VIew와 Model이 분리되는 것이다. 

같은 기능을 MVVM으로 구현한다고 가정해보자

// ======== MODEL ========
const UserModel = {
  user: {
    id: 1,
    userName: "김철수",
    userEmail: "kim@example.com",
    userPhone: "010-1234-5678"
  },
  
  getUser() {
    return this.user;
  }
};

// ======== VIEWMODEL ========
const UserViewModel = {
  // Model의 데이터를 "View가 원하는 형태"로 변환
  displayName: null,
  displayEmail: null,
  displayPhone: null,
  
  init() {
    const user = UserModel.getUser();
    // ViewModel이 Model의 필드명을 알고 있음
    this.displayName = user.userName;
    this.displayEmail = user.userEmail;
    this.displayPhone = user.userPhone;
  }
};

// ======== VIEW ========
const UserView = {
  render(viewModel) {
    // View는 ViewModel을 통해서만 데이터를 받음
    // View는 Model의 구조를 모름!
    document.getElementById('name').textContent = viewModel.displayName;
    document.getElementById('email').textContent = viewModel.displayEmail;
    document.getElementById('phone').textContent = viewModel.displayPhone;
  }
};

UserViewModel.init();
UserView.render(UserViewModel);

 

여기에서 만약 Model이 변경되어도 ViewModel만 수정하면 끝이 난다.

// Model이 다시 변경됨
const UserModel = {
  user: {
    id: 1,
    userName: "김철수",
    userEmail: "kim@example.com",
    userPhone: "010-1234-5678"
  }
};

// ViewModel만 수정하면 됨!
const UserViewModel = {
  displayName: null,
  displayEmail: null,
  displayPhone: null,
  
  init() {
    const user = UserModel.getUser();
    // ← 여기만 수정
    this.displayName = user.userName;
    this.displayEmail = user.userEmail;
    this.displayPhone = user.userPhone;
  }
};

// View는 수정할 필요 없음!
const UserView = {
  render(viewModel) {
    // View는 변경 없음
    document.getElementById('name').textContent = viewModel.displayName;
    document.getElementById('email').textContent = viewModel.displayEmail;
    document.getElementById('phone').textContent = viewModel.displayPhone;
  }
};

 


정리

MVC와 MVVM은 모두 UI와 데이터 로직을 분리하는 훌륭한 패턴이지만:

  • MVC는 Controller가 직접 Model과 View를 조율하는 중앙 집중식
  • MVVM은 ViewModel이 View를 위해 Model을 변환/중개하는 방식

프로젝트의 규모와 복잡도, 팀의 경험도에 따라 적절한 패턴을 선택하는 것이 중요한 것 같다.