반응형

📝MVC

MVC패턴은 Model + View + Controller를 합친 용어입니다

동작과정으로 Controller로 (url)요청이 들어오면 Model을 이용해 데이터를 가져오고 수정해서 Controller로 보내고 해당 데이터로 View를 업데이트합니다.

 

대표적으로 Spring MVC가 있습니다. 참고로 그림이 이해가 안 되는 건 Model하고 View 결합이 없을텐데 왜 결합이 있는 것처럼 표현된 건지입니다.

 

  • Model
    • 데이터와 비즈니스 로직을 담는 공간입니다
  • View
    • 사용자에게 제공하는 UI 부분입니다 (JSP, Thymeleaf)
  • Controller
    • 들어온 데이터나 보내줄 데이터에 대한 로직처리하는 부분입니다

 

💗장점

  • 역할이 잘 분리되어 있어서 협업에 유리하고 테스트 및 유지보수(분리가 잘 되어있어서)가 쉽습니다.

 

⚠️단점

  • Controller가 여러개의 Model을 가지게 되고 그에 따라 연결된 View도 많아지기 때문에 복잡하다
    • DDD나 도메인으로 폴더구조를 잘 나누면 된다고 생각한다.

 

추후 Spring의 경우는 MVC에서 Model의 로직을 Service로 따로 분리했습니다. Model은 DTO, Entity가 이에 해당합니다. MVVM이 최신임에도 바뀌지 않는 이유는 이게 Spring 웹 프레임웨크에 가장 적합한 형태이기 때문입니다.

 

  • 사용 프레임워크
    • Ruby On Rails
    • Django
    • Spring
    • Express.js

 

// Controller
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;

@Controller
public class GreetingController {
    
    @GetMapping("/greeting")
    public String greeting(@RequestParam(name="name", required=false, defaultValue="World") String name, Model model) {
        model.addAttribute("name", name);
        return "greeting";  // 뷰 이름 반환
    }
}

 

<!-- greeting.jsp -->
<html>
<body>
    <h1>Hello, ${name}!</h1>
</body>
</html>

 

 

📝MVP

사용자가 View에서 행동을 하면 Presenter에게 요청하고 Presenter가 Model을 이용해 데이터를 받아오고 수정해서 View에게 제공합니다 MVC랑 유사합니다만 View 하나당 1개의 Presenter를 가지고 있습니다.

 

  • Model
    • 데이터 관리 및 비즈니스로직 처리
  • View
    • 사용자에게 제공하는 UI 부분입니다. 
  • Presenter
    • Model과 View 사이의 상호작용을 조정합니다. View로부터 입력을 받아 Model을 업데이트하고, Model의 상태 변경에 따라 View를 업데이트합니다.

 

💗장점

  • MVC랑 동일하고 Presenter랑 View 1:1로 관리하기 때문에 더 유지보수에 용이해보입니다.

 

  • 사용 프레임워크
    • GWT
    • Backbone.js
    • WinForms
    • Vaadin

 

HTML

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>MVP Example</title>
</head>
<body>
    <h1>MVP Example</h1>
    <div id="data-display">Click the button to load data</div>
    <button id="load-data-btn">Load Data</button>

    <script src="app.js"></script>
</body>
</html>

 

Javascript

// Model
class DataModel {
    constructor() {
        this.data = null;
    }

    // 비동기로 데이터를 받아오는 메소드
    fetchData() {
        return new Promise((resolve, reject) => {
            setTimeout(() => {
                this.data = { message: "Hello, MVP Pattern!" };
                resolve(this.data);
            }, 1000);  // 1초 후에 데이터 반환 (모의 비동기 처리)
        });
    }

    getData() {
        return this.data;
    }
}

// Presenter
class DataPresenter {
    constructor(view, model) {
        this.view = view;
        this.model = model;

        this.view.bindLoadData(this.handleLoadData.bind(this));
    }

    async handleLoadData() {
        try {
            const data = await this.model.fetchData();
            this.view.displayData(data.message);
        } catch (error) {
            this.view.displayError('Failed to load data');
        }
    }
}

// View
class DataView {
    constructor() {
        this.dataDisplay = document.getElementById('data-display');
        this.loadDataBtn = document.getElementById('load-data-btn');
    }

    bindLoadData(handler) {
        this.loadDataBtn.addEventListener('click', handler);
    }

    displayData(data) {
        this.dataDisplay.textContent = data;
    }

    displayError(error) {
        this.dataDisplay.textContent = error;
    }
}

// 초기화 코드
document.addEventListener("DOMContentLoaded", () => {
    const model = new DataModel();
    const view = new DataView();
    const presenter = new DataPresenter(view, model);
});

MVP 패턴은 데스크톱 애플리케이션을 위해 개발되었기 때문에 서버 Presenter랑 클라이언트 Presenter랑 나뉘는 형태로 현재 HTML랑 JS로 설명하기 이상할 수 있지만 가장 많이 접해봤을 웹측면에서 설명했습니다. 해당 패턴을 써보진 못해서 말만 들어서는 뭐가 발전한 거에 대해서 정확히는 잘 모르겠습니다

 

 

📝MVVM

MVP를 발전시킨 패턴으로 데이터 바인딩을 이용해 뷰와 모델사이의 결합도를 더욱 낮췄습니다 현재는 Angular, Vue.js등 다양한 프론트엔드 프레임워크에서도 채용하고 있습니다

 

💗장점

  • ViewModel에 비즈니스 로직이 집중되어 유지보수에 용이합니다.
  • ViewModel이 뷰의 상태를 관리하기 때문에, 상태 유지가 필요한 복잡한 UI의 경우에 유리합니다.

 

  • Model
    • 비즈니스 로직과 데이터를 관리합니다. 여기에서 로직이란 DB접근하던가 API통신 또는 계산 데이터처리등과 같은 로직이 해당합니다.
  • View
    • 사용자에게 제공하는 UI 부분입니다.
  • ViewModel
    • View와 Model의 중개자역할을 하며 View로부터 요청이 들어오면 Model로 처리후 필터링해 View에게 제공합니다. UI 관련 로직과 데이터를 관리합니다. 여기에서 로직이란 Model에서 데이터를 가져와 UI에 맞는 형태로 변환하거나 필터링 또는 UI 상태(로딩, 오류메시지)를 관리하고 유지하는 로직이 들어갑니다. 사용자가 버튼을 클릭하거나 입력하는 등에 대한 UI 이벤트 처리가 이에 해당합니다.
    • View와 1:N으로 연결되어서 중복을 제거하고 다른 곳에서 사용할 수 있다 → 개인적으로 DDD처럼 각각 하나씩 대응하는게 나은 거 같다 이거는 공통을 제대로 잘 뽑지 않는 이상은 의미가 없어보임 물론 MVP에서 아예 안 되는 내용이면 MVVM의 장점이라고 할수도 있을 것 같다.

 

  • 사용 프레임워크
    • Android
    • iOS
    • Flutter

Flutter 내용 추가 필요

 

📝결론

프레임워크가 그 패턴을 사용하는 건 그걸 의도하고 만든 것이기 때문에 따라가면 좋고 그걸 보완할 좋은 방안이 있으면 적용시키면 됩니다. 딱딱한 사고는 안 하는게 좋은 거 같네요 MVC의 경우도 Service 레이어가 생겨나는 등 변화가 있었으니... 그리고 그 프레임워크가 추구하는 방향대로 해야한다. 왜냐하면 그에 맞게 라이브러리가 만들어진 게 많기 때문이다. 이념에 맞지 않아 개발에 고생할 수 있음

 

📝단방향 데이터 바인딩

모델에서 뷰로 보내는 데이터의 흐름이 자동처리 되어 Model이 변경되면 View가 변경되지만 그 반대는 적용되지 않습니다 단방향 데이터 바인딩 적용한 건 React가 있다고 합니다

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>React One-way Data Binding</title>
    <script src="https://unpkg.com/react@17/umd/react.development.js"></script>
    <script src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"></script>
    <script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
</head>
<body>
    <div id="root"></div>
    <script type="text/babel">
        class App extends React.Component {
            constructor(props) {
                super(props);
                this.state = { name: "World" };
            }

            handleChange = (event) => {
                this.setState({ name: event.target.value });
            };

            render() {
                return (
                    <div>
                        <input 
                            type="text" 
                            value={this.state.name} 
                            onChange={this.handleChange}
                        />
                        <p>Hello, {this.state.name}!</p>
                    </div>
                );
            }
        }

        ReactDOM.render(<App />, document.getElementById('root'));
    </script>
</body>
</html>

React의 공식 예제를 살펴보면 State로 상태를 관리하며 setState로 상태에 변화를 감지해 부분렌더링하게 되는데 onChange에 접합시켜서 input에 변화가 있을 때 input 상태값을 변화시키는 즉, HTML에서의 수정이 JS로 이어지고 JS의 수정 즉, setState로 인한 수정도 HTML에 반영되기 때문에 양방향 데이터 바인딩처럼 동작합니다

하지만 진짜 양방향의 경우 이러한 작업이 자동처럼 처리 되어 코드가 줄어들고 컴포넌트간의 데이터 전달시 props로 하위 컴포넌트로 보낼 수 있지만 상위로 보낼 수 없다는 점에서 단방향이라고 부르는 것 같네요

 

 

📝양방향 데이터 바인딩

Model을 수정시 View에 적용되며 View 수정시에도 Model에 적용되어 동기화된 상태를 유지시킵니다 이를 활용한 기술은 Angular, Vue.js가 존재합니다

 

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Angular Two-way Data Binding</title>
    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.8.2/angular.min.js"></script>
</head>
<body ng-app="myApp" ng-controller="myCtrl">
    <input type="text" ng-model="name">
    <p>Hello, {{name}}!</p>

    <script>
        var app = angular.module('myApp', []);
        app.controller('myCtrl', function($scope) {
            $scope.name = "World";
        });
    </script>
</body>
</html>

Angular의 예제를 보면 React와 다르게 간결한 코드로 양방향으로 데이터 바인딩이 가능합니다

 

📝Back-End, Front-End

요즘은 Front-End랑 Back-End로 나누어서 작업하기 때문에 역할이 잘 분리되어있습니다 물론 Front-End쪽에서는 MVVM이고 BackEnd에서는 View를 제거한 API만 제공하는 Model과 Controller만 사용하는 형태 등 이런식으로 사용하게 됩니다

 

 

🔗 참고 및 출처

https://beomy.tistory.com/43

반응형