MVC 패턴

WinG·2024년 3월 4일
0

Java

목록 보기
2/2
post-thumbnail

MVC 패턴이란?

MVC 패턴은 디자인 패턴 중 하나이다. 디자인 패턴은 간단하게 말하면, SW 개발방법을 공식화한 것으로 MVC 패턴은 애플리케이션을 Model, View, Controller 이렇게 3가지 역할로 구분한 개발 방법론을 말한다..그렇다면 왜 MVC 패턴을 사용하는걸까? MVC 패턴은 유지보수가 편한 패턴 중 하나이다. MVC 패턴의 사용을 통해 각 컴포넌트들의 결합도를 낮출 수 있고 커뮤니케이션의 효율을 높일 수 있으며 코드의 재사용성을 높일 수 있다.

유지보수가 편한 코드가 무엇일까?
특정 기능을 수정하거나 추가해야할 때, 가능한 최소한의 코드만을 수정해서 기능을 변경할 수 있는 코드를 유지보수가 편한 코드라고 한다.

그래서 저 3가지 역할이 뭐야? 🤔

Model

첫번째로, Model이다. 모델은 데이터와 비지니스 로직을 관리하는 역할을 한다.

View

두번째, View는 모델에 포함된 데이터의 시각화이다. 즉, 모델의 데이터를 사용하여 시각적인 화면을 구성하고 사용자의 입력을 컨트롤러에게 전달하는 역할을 한다.

Controller

마지막으로 Controller는 사용자의 요청을 처리한다. 즉, 컨트롤러는 사용자 입력을 받아서 Model에게 데이터 업데이트 여부를 확인하고 이에 따라 View의 UI를 업데이트를 할지 말지 정하게 된다.

MVC 흐름


위의 사진은 MVC 패턴 흐름을 간략하게 표현한 사진이다.

  1. 사용자는 원하는 기능을 처리하기 위한 모든 요청을 컨트롤러에게 보낸다.
  2. 컨트롤러는 모델을 사용하고, 모델은 알맞은 비즈니스 로직을 구현한다.
  3. 컨트롤러는 사용자에게 보여줄 뷰를 선택한다.
  4. 선택된 뷰는 사용자에게 알맞은 결과 화면을 보여준다. 이때, 사용자에게 보여줄 데이터는 컨트롤러를 통해서 전달받는다.

MVC를 지키며 코딩하기

MVC 패턴을 지키기 위해서는 5가지 규칙이 있다. 5가지 규칙에 대한 세부 설명에 첨부된 코드는 우테코 프리코스 2주차 미션 리팩토링 코드이다.

1. Model 내부에는 Controller와 View에 관련된 코드가 존재하면 안된다.

위의 말이 무슨말이까? 아래 코드를 보자.

package racingcar.domain;

import camp.nextstep.edu.missionutils.Randoms;

public class Car {
    private static final String ONE_STEP = "-";
    private static final int START_NUM = 0;
    private static final int END_NUM = 9;
    private static final int GO_FORWARD = 4;

    private final String name;
    private int location = 0;
    private StringBuilder status;

    public Car(String name) {
        this.name = name;
        location = 0;
        status = new StringBuilder();
    }

    public String getName() {
        return name;
    }

    public int getLocation() {
        return location;
    }

    public String getStatus() {
        return status.toString();
    }
}

모델은 데이터와 관련된 코드부분이다 보니 언제나 깔끔하고 정제된 데이터를 꺼내 쓸 수 있게 뷰나 컨트롤러의 코드를 섞어놓지 않고, 데이터와 관련된 코드들만 깔끔하게 모아두어야한다. 위의 코드도 컨트롤러와 뷰의 코드가 존재하지 않기 때문에 의존하면 안된다는 규칙을 잘 지키고 있다.

2. View는 Model에만 의존해야 하고, Controller에는 의존하면 안된다.

이 말은 View 내부에는 Model과 관련된 코드만 있을 수 있고, Controller의 코드가 있으면 안된다는 말과 같다.

package racingcar.view;

import java.util.List;

import racingcar.domain.Car;

public class OutputView {
    private static final String WIN_MESSAGE = "최종 우승자 : ";

    public static void printRoundResult(List<Car> cars) {
        for (Car car : cars) {
            System.out.printf("%s : %s\n", car.getName(), car.getStatus());
        }
        System.out.println();
    }

    public static void printWinners(List<Car> winners) {
        StringBuilder winnerName = new StringBuilder();
        for (int i = 0; i < winners.size(); i++) {
            winnerName.append(winners.get(i).getName());
            if (i < winners.size() - 1) {
                winnerName.append(", ");
            }
        }
        System.out.printf(WIN_MESSAGE + "%s", winnerName);
    }
}

위의 코드를 보면 OutputView 클래스의 메소드들은 모델에 관련된 코드인 Car를 파라미터로 받아오는 것을 확인할 수 있다.(실제로도 모델과 관련된 코드를 사용하기 위해 import 해온 것을 확인할 수 있다.) 이처럼 뷰 내부에는 모델과 관련된 코드는 있어도 상관이 없다. 다만 컨트롤러와 관련된 코드는 존재해서는 안된다.

3. View가 Model로부터 데이터를 받을 때에는 사용자마다 다르게 보여주어야하는 데이터에 대해서만 받아야한다.


View는 위의 사진에서 맨 왼쪽과 같이 모든 사람들에게 똑같이 보여져야하는 부분들과 빨간색 네모 박스쳐져있는 부분처럼 각 사용자들마다 다르게 보여져야하는 정보, 즉 모델로부터 받은 데이터가 합쳐져서 만들어진 화면이다. 위의 규칙은 View가 Model로부터 데이터를 받을 때에는 빨간 박스의 정보처럼 사용자마다 다르게 보여주어야하는 데이터에 대해서만 받아야한다는 점이다. 맨 왼쪽처럼 항상 일관된 정보들은 View가 자체적으로 가지고 있어야한다.
위로 돌아가 다시 OutputView 코드를 확인해보자. 모든 사용자에게 동일하게 보여주어야하는 문구의 경우 WIN_MESSAGE라는 상수처리를 통해 뷰가 자체적으로 가지고 있도록 하였다. 반면, 실행할 때마다 바뀌는 정보인 우승자 목록 데이터는 Model로부터 받아와 처리하였다.

4. Controller는 Model과 View에 의존해도 된다.

위의 규칙은 Controller 내부에는 모델과 뷰와 관련된 코드가 존재해도 된다는 말과 동일하다. 컨트롤러는 모델과 뷰의 중재자 역할을 하면서 전체 로직을 구성하기 때문이다.

package racingcar.controller;

import java.util.ArrayList;
import java.util.List;

import camp.nextstep.edu.missionutils.Console;
import racingcar.domain.*;
import racingcar.view.*;

public class RacingGame {
    private List<Car> cars;
    private int round;

    public RacingGame() {
        round = 0;
        cars = new ArrayList<>();
    }
}

...

코드가 길어 일부만 잘라왔지만, import문을 통해서 Controller 내부에 모델과 뷰의 코드가 존재함을 확인할 수 있다.

5. View가 Model로부터 데이터를 받을 때, 반드시 Controller에서 받아야한다.

이는 View가 모델로부터 데이터를 받을 때에는, 컨트롤러의 코드 내에서만 받아야함을 의미한다.

package racingcar.controller;

import java.util.ArrayList;
import java.util.List;

import camp.nextstep.edu.missionutils.Console;
import racingcar.domain.*;
import racingcar.view.*;

public class RacingGame {
    private List<Car> cars;
    private int round;

    public RacingGame() {
        round = 0;
        cars = new ArrayList<>();
    }

    public void play() {
        initCars();
        initTryCount();
        for (int i = 0; i < round; i++) {
            for (Car car : cars) {
                car.decideMove();
            }
            OutputView.printRoundResult(cars);
        }
        Referee referee = new Referee(cars);
        List<Car> winners = referee.judgeWinners();
        OutputView.printWinners(winners);
    }

위의 컨트롤러의 코드를 살펴보자. play라는 메소드에서 모델인 referee 클래스로부터 우승자의 데이터를 받아서 뷰의 printWinners 메소드에게 파라미터로 전달하는 것을 확인할 수 있다.

profile
공부하는 감자😎

0개의 댓글