PromleeBlog
sitemap
aboutMe

posting thumbnail
MVC 패턴 완벽 이해 - 화면과 데이터 로직 깔끔하게 분리하기
Mastering MVC Pattern Separating View Logic and Data

📅

🚀

들어가기 전에 🔗

우리가 소프트웨어를 만들다 보면 코드가 점점 복잡해지고, 한 부분이 다른 부분과 뒤섞여 어디를 수정해야 할지 막막해지는 경험을 하곤 합니다.
특히 사용자에게 보여지는 화면(UI) 코드와 핵심적인 데이터 처리 로직이 한데 엉켜 있다면, 작은 변경 하나가 큰 파장을 일으킬 수 있습니다.


오늘은 강력한 설계 패턴 중 하나인
MVC 패턴
에 대해 알아보겠습니다.
MVC 패턴은 Model, View, Controller의 약자로, 애플리케이션의 구성 요소를 세 가지 역할로 깔끔하게 나누어 서로의 영향은 최소화하면서 협력하도록 만드는 방법입니다.
마치 잘 정돈된 부엌에서 각자의 역할(요리사, 서빙, 설거지)이 분담되어 효율적으로 음식이 준비되듯, MVC 패턴은 소프트웨어 개발의 효율성과 유지보수성을 크게 향상시켜 줍니다.

🚀

MVC 패턴이란 무엇일까요? 🔗

MVC 패턴은 1970년대 후반 제록스 파크(Xerox PARC)에서 스몰토크(Smalltalk) 언어 개발과 함께 처음 소개된 오래되고 검증된 아키텍처 패턴입니다.
주된 목적은 *관심사의 분리(Separation of Concerns)*입니다.
즉, 애플리케이션을 구성하는 요소들을 다음과 같은 세 가지 주요 역할로 나누어 각각의 책임에만 집중하도록 하는 것입니다.

모델 (Model) 🔗

애플리케이션의
데이터
핵심 비즈니스 로직
을 담당합니다.
데이터를 저장, 검색, 수정하고, 데이터의 유효성을 검사하며, 데이터 변경 시 관련된 다른 부분(주로 뷰)에 알리는 역할을 합니다.
화면에 어떻게 보일지, 사용자의 입력은 어떻게 처리할지에 대해서는 전혀 관여하지 않습니다. 오직 데이터와 그 데이터를 다루는 규칙에만 집중합니다.

뷰 (View) 🔗

사용자에게
보여지는 부분(UI)
을 담당합니다.
모델로부터 데이터를 받아와 사용자에게 시각적으로 표현하고, 사용자와의 상호작용(예: 버튼 클릭, 텍스트 입력)을 받아들이는 역할을 합니다.
뷰는 데이터를 직접 저장하거나 처리 로직을 가지지 않고, 주로 모델이 제공하는 데이터를 어떻게 보여줄지에만 집중합니다.

컨트롤러 (Controller) 🔗

모델과 뷰 사이의
중재자
역할을 합니다.
사용자로부터 입력을 받아(주로 뷰를 통해), 이 입력을 해석하여 모델에게 특정 작업을 수행하도록 요청합니다.
모델의 작업 결과를 받아 다시 뷰에게 전달하여 사용자 화면을 업데이트하도록 지시합니다.
즉, 전체적인 애플리케이션의 흐름을 제어합니다.
👨‍💻
이 세 가지 구성 요소는 서로 완전히 독립적이지는 않지만, 각자의 역할이 명확히 구분되어 있어 한 부분의 변경이 다른 부분에 미치는 영향을 최소화할 수 있습니다.

🚀

MVC 패턴의 구성 요소와 상호작용 흐름 🔗

MVC 패턴에서 각 구성 요소는 어떻게 서로 메시지를 주고받으며 동작할까요?
일반적인 상호작용 흐름은 다음과 같습니다.
MVC 패턴의 상호작용 흐름 다이어그램
MVC 패턴의 상호작용 흐름 다이어그램
  1. 사용자 입력 발생
    사용자가 뷰(예: 웹 페이지의 버튼 클릭, 폼 입력)와 상호작용합니다.
  2. 뷰에서 컨트롤러로 이벤트 전달
    뷰는 사용자의 액션(이벤트)을 감지하고, 이를 컨트롤러에게 전달합니다.
    뷰는 이벤트를 어떻게 처리해야 할지 직접 알지 못합니다.
  3. 컨트롤러가 모델에 작업 요청
    컨트롤러는 사용자의 요청을 해석하여, 모델에게 필요한 데이터 변경이나 비즈니스 로직 수행을 요청합니다. (예: "새로운 게시글을 저장해줘", "상품 목록을 가져와줘")
  4. 모델이 데이터 처리 및 상태 변경
    모델은 컨트롤러의 요청에 따라 데이터를 처리하고, 필요한 경우 자신의 상태를 변경합니다.
    데이터베이스와 연동하거나 복잡한 계산을 수행할 수 있습니다.
  5. (선택적) 모델이 뷰에 변경 알림
    모델의 상태가 변경되면, 모델은 자신을 관찰하고 있는 뷰들에게 데이터가 변경되었음을 알립니다.
    (옵저버 패턴 등을 통해 구현 가능) 또는 컨트롤러가 이 역할을 대신할 수도 있습니다.
  6. 컨트롤러가 뷰에 업데이트 지시 (또는 뷰가 모델 조회)
    컨트롤러는 모델로부터 작업 결과를 받거나, 모델의 변경 알림을 인지한 후, 어떤 뷰를 업데이트해야 할지 결정하고 해당 뷰에게 화면을 갱신하도록 지시합니다.
    이때 필요한 데이터를 모델로부터 직접 가져오거나, 컨트롤러가 모델로부터 받은 데이터를 뷰에 전달할 수 있습니다.
  7. 뷰가 화면 업데이트
    뷰는 모델로부터 최신 데이터를 가져와(또는 컨트롤러로부터 받아) 사용자 화면을 업데이트하여 변경된 내용을 보여줍니다.
이러한 흐름을 통해 각 구성 요소는 자신의 책임에만 집중할 수 있게 됩니다.

모델 (Model) 🔗

모델은 MVC 패턴의 핵심으로, 애플리케이션의 모든 데이터와 그 데이터를 다루는 비즈니스 규칙을 가지고 있습니다.
➡️

역할 🔗

➡️

특징 🔗

➡️

예시(Java) 🔗

User.java (데이터를 담는 간단한 모델 객체 - DTO 또는 Entity 역할)
public class User {
    private String id;
    private String name;
    private String email;
 
    public User(String id, String name, String email) {
        this.id = id;
        this.name = name;
        this.email = email;
    }
 
    // Getters and Setters
    public String getId() { return id; }
    public void setId(String id) { this.id = id; }
    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
    public String getEmail() { return email; }
    public void setEmail(String email) { this.email = email; }
}
UserService.java (비즈니스 로직을 처리하는 모델 - Service 역할)
import java.util.HashMap;
import java.util.Map;
 
public class UserService {
    private Map<String, User> userDatabase = new HashMap<>(); // 간단한 인메모리 DB 역할
 
    public User getUserById(String id) {
        return userDatabase.get(id);
    }
 
    public void createUser(String id, String name, String email) {
        if (userDatabase.containsKey(id)) {
            System.out.println("오류: 사용자 ID '" + id + "'는 이미 존재합니다.");
            return;
        }
        User newUser = new User(id, name, email);
        userDatabase.put(id, newUser);
        System.out.println("사용자 '" + name + "'이(가) 성공적으로 생성되었습니다.");
        // 데이터 변경이 발생했으므로, 옵저버에게 알리는 로직이 추가될 수 있음
    }
}

뷰 (View) 🔗

뷰는 사용자에게 정보를 시각적으로 표현하고, 사용자의 입력을 받는 인터페이스 역할을 합니다.
➡️

역할 🔗

➡️

특징 🔗

➡️

예시 (Java) 🔗

UserView.java (사용자 정보를 출력하는 간단한 뷰)
public class UserView {
    public void printUserDetails(String userName, String userEmail) {
        System.out.println("--- 사용자 정보 ---");
        System.out.println("이름: " + userName);
        System.out.println("이메일: " + userEmail);
        System.out.println("------------------");
    }
 
    public void showMessage(String message) {
        System.out.println(message);
    }
}
실제 애플리케이션에서는 HTML, GUI 프레임워크 등을 사용하게 됩니다.

컨트롤러 (Controller) 🔗

컨트롤러는 사용자의 입력을 받아 모델과 뷰 사이의 상호작용을 조정하고 제어하는 역할을 합니다.
➡️

역할 🔗

➡️

특징 🔗

➡️

예시 (Java) 🔗

UserController.java (모델과 뷰를 연결하는 컨트롤러)
public class UserController {
    private UserService model; // 모델(UserService)에 대한 참조
    private UserView view;     // 뷰(UserView)에 대한 참조
 
    public UserController(UserService model, UserView view) {
        this.model = model;
        this.view = view;
    }
 
    public void handleCreateUserRequest(String id, String name, String email) {
        model.createUser(id, name, email);
        // 모델에서 생성 성공/실패 여부를 받아 뷰에 다른 메시지를 보여줄 수도 있음
        // 여기서는 UserService 내부에서 간단히 출력함
    }
 
    public void handleGetUserRequest(String id) {
        User user = model.getUserById(id);
        if (user != null) {
            view.printUserDetails(user.getName(), user.getEmail());
        } else {
            view.showMessage("사용자 ID '" + id + "'를 찾을 수 없습니다.");
        }
    }
 
    // main 메서드 (애플리케이션 실행 예시)
    public static void main(String[] args) {
        // 1. 모델과 뷰 객체 생성
        UserService userService = new UserService();
        UserView userView = new UserView();
 
        // 2. 컨트롤러 생성 및 모델/뷰 주입
        UserController userController = new UserController(userService, userView);
 
        // 3. 사용자 요청 시뮬레이션 (컨트롤러를 통해 상호작용)
        // 사용자 생성 요청
        userController.handleCreateUserRequest("user001", "홍길동", "gildong@example.com");
        userController.handleCreateUserRequest("user002", "이순신", "sunsin@example.com");
        System.out.println(); // 줄 바꿈
 
        // 사용자 정보 조회 요청
        userController.handleGetUserRequest("user001");
        System.out.println();
        userController.handleGetUserRequest("user003"); // 존재하지 않는 사용자
    }
}
main 메서드는 MVC 각 컴포넌트를 생성하고 사용자 요청을 시뮬레이션하여 컨트롤러를 통해 상호작용하는 간단한 예시입니다.

🚀

MVC 패턴의 장점과 단점 🔗

MVC 패턴은 많은 장점을 제공하지만, 모든 상황에 완벽한 만능 해결책은 아닙니다.

장점 🔗

단점 🔗


🚀

다양한 프레임워크에서의 MVC 구현 방식 🔗

MVC 패턴은 개념적인 모델이며, 실제 다양한 프로그래밍 언어와 프레임워크에서 조금씩 다른 형태로 구현됩니다.

웹 프레임워크 🔗

GUI 애플리케이션 🔗

👍
이처럼 많은 현대적인 프레임워크들이 MVC 또는 그 변형(MVP, MVVM 등)을 기본 아키텍처로 채택하고 있어, MVC 패턴을 이해하는 것은 다양한 기술을 배우는 데 훌륭한 밑거름이 됩니다.

🚀

결론 🔗

오늘은 애플리케이션의 구조를 체계적으로 설계하는 데 도움을 주는 강력한 도구인 MVC 패턴에 대해 알아보았습니다.
모델, 뷰, 컨트롤러라는 세 가지 역할로 애플리케이션을 분리함으로써, 우리는 각 부분의 책임을 명확히 하고, 코드의 유연성과 확장성, 그리고 유지보수성을 크게 향상시킬 수 있습니다.
물론 MVC 패턴이 모든 문제의 해결책은 아니며, 프로젝트의 규모나 성격에 따라 적용 방식이나 필요성이 달라질 수 있습니다.
하지만
관심사를 분리
하려는 MVC 패턴의 핵심 아이디어는 어떤 종류의 소프트웨어를 개발하든 항상 유념해야 할 중요한 원칙입니다.
다음 시간에는 소프트웨어의 요구사항을 명확하게 정의하고 문서화하는 방법, 즉
소프트웨어 요구사항 문서(SRS) 제대로 작성하기
에 대해 알아보겠습니다.

참고 🔗