옛날에는 많은 개발자들이 고질적으로 겪는 문제가 있었다. 코드가 많아질 수록 복잡해져 수정이 어려운 문제가 있어 갈아엎는 경우가 많았다. (유지 보수의 어려움)
그러다 머리 좋은 개발자들이 이런 유지보수의 문제점을 해결할 수 있는 패턴을 발견했고 그것이 MVC 패턴이다.
어플리케이션을 3가지 컴포넌트(Model, View, Controller)로 나누고 역할을 부여하는 패턴(개발 방법론)이다.
MVC를 적용했을 때 이점은 도메인(비즈니스) 영역과 UI 영역이 분리된다는 점이다. 분리가 된다면 한쪽이 변경되었을 때 다른 한쪽 영역은 영향을 받지 않으므로 적용하지 않았을 때보다 쉽게 유지보수가 가능하다는 장점이 있다.
사용자가 View를 이용해 요청을 보내면 Controller가 받아서 원하는 결과를 Model로부터 가져온 뒤 View를 통해 사용자에게 요청에 대한 결과를 전달하는 식으로 요청/응답 을 한다.
(출처: geeksforgeeks - MVC Design Pattern)
무엇을 할 지 정의한다. 즉 도메인 로직을 처리하는 역할이다.
어플리케이션이 해결해야 하는 문제에 대한 중요한 로직이 이 Model에서 처리한다. 도메인 로직을 처리한 뒤에는 컨트롤러에 결과를 전달한다.
무언가 보여주는 역할을 한다. (단지 표현하는 역할만 한다.) 모델이 처리한 결과를 가지고 사용자에게 출력할 화면을 만드는 역할이다. (화면은 콘솔이 될 수 있고 웹 브라우저에서 HTML로 보여질 수 있다.)
Model과 View 사이에 존재하고 Model이 데이터를 어떻게 처리할지 알려주는 역할이다. 뷰 또는 다른 외부소스로부터 이벤트를 수신하고 이벤트에 대한 적절한 반응을 한다. (보통 Model의 메서드를 실행한다.)
MVC가 적용되기 전 코드이다.
public class Car {
...
public void printPresentMovingDistance() {
OutputView.printMovingDistance(carName, movingDistance);
}
}
public class OutputView {
...
public static void printMovingDistance(Object carName, int movingDistance) {
System.out.print(carName + " : ");
for (int moved = 0; moved < movingDistance; moved++) {
System.out.print("-");
}
System.out.println();
}
}
현재 이동거리를 Car에서 직접 OutputView의 메서드를 호출해서 나타내고 있는 코드이다. Car는 OuputView에 대한 정보를 알고 있으므로 규칙에 어긋난다.
만약 여기서 OuputView 클래스에서 자동차의 번호도 출력되는 것으로 변경된다면 Car 클래스도 변경이 되어야한다. 즉 Model이 View의 영향을 받는다.
이를 해결하기 위한 MVC가 적용된 코드다.
public class Car { // Model
...
public void getMovingDistance() {
return movingDistance;
}
public void getName() {
return name;
}
}
public class OutputView { // View
...
public static void printMovingDistance(Car car) {
System.out.print(car.getName() + " : ");
for (int moved = 0; moved < car.getMovingDistance(); moved++) {
System.out.print("-");
}
System.out.println();
}
}
public class Controller { // Controller
public void printPresentStatus(List<Car> cars) {
for (Car car : cars) {
OutputView.printMovingDistance(car);
}
}
}
Model은 View와 Controller에 대한 정보를 알지 못하고 View는 Model의 사용자마다 변경될 수 있는 데이터를 받아서 표현한다. 그리고 Controller는 View와 Model에 대한 존재를 알고 있으며 Model을 받아서 직접 View에 전달하는 모습을 볼 수 있다.
public class OutputView { // View
...
public static void printMovingDistance(Car car) {
System.out.print(car.getId() + car.getName() + " : ");
for (int moved = 0; moved < car.getMovingDistance(); moved++) {
System.out.print("-");
}
System.out.println();
}
}
만약 여기서 자동차의 번호도 출력하는 것으로 변경된다면 위 코드처럼 View에서 Car의 getNumber를 호출하면 된다. 즉 Model이 다른 컴포넌트의 변경으로부터 자유로워졌다.
Controller는 Model과 View에 대한 정보를 알고 있다는 점에서 프로젝트가 커져갈수록 필연적으로 Controller의 스케일이 커지는 문제가 발생한다. (Massive-View-Controller) 때문에 이를 보완하기 위한 다양한 패턴이 생겼다.