춤추는 개발자

아키텍처 디자인 패턴 - MVC란? 본문

Android/study_til

아키텍처 디자인 패턴 - MVC란?

Heon_9u 2021. 5. 21. 17:54
728x90
반응형

위키피디아에서는 디자인 패턴을 아래와 같이 정의하고 있습니다.

 소프트웨어 개발 방법에서 사용되는 디자인 패턴은, 프로그램 개발에서 자주 나타나는 과제를 해결하기 위한 방법 중 하나로, 과거의 소프트웨어 개발 과정에서 발견된 설계의 노하우를 축적하여 이름을 붙여, 이후에 재이용하기 좋은 형태로 특정의 규약을 묶어서 정리한 것이다. 알고리즘과 같이 프로그램 코드로 바로 변환될 수 있는 형태는 아니지만, 특정한 상황에서 구조적인 문제를 해결하는 방식을 설명해 준다.

 

 이러한 디자인 패턴은 코드의 가독성과 테스트 및 유지보수를 간편하게 만들어 줍니다. 만약 반대로 디자인 패턴을 적용하지 않는다면? 무작정 하나의 Class에 온갖 코드를 작성하면서 코드 중복이나 for문, if문들을 남발하게 되고, 제품의 유지보수성을 떨어뜨리게 됩니다.

 

 실제 서비스 사용량이 많은 복잡한 로직의 앱을 개발하다 보면 시간이 흐르면서 사용자의 요구사항이 생기고, 처음보다 복잡한 기획이 요구됩니다. 이러한 상황에서 디자인 패턴이 없다면, 변경사항이 발생할 때마다 화면을 구성하는 코드와 비즈니스 로직이 들어간 코드를 동시에 매번 수정을 해야 합니다.

 즉, 서로 간의 의존성이 강해짐에 따라 유지보수가 어려워지게 됩니다. 이런 문제를 해결하기 위해 등장한 것이 바로 MVC패턴입니다.

 

MVC는 안드로이드와 관계없이 프로그래밍에 가장 널리 사용되는 구조로 Model, View, Controller로 구성됩니다. MVC구조에서의 입력은 모두 Controller에서 발생하고, 관리되게 하는 구조입니다. 이벤트가 발생한 Controller에 의해 각 모듈의 정의와 View의 사용 용도가 달라지게 됩니다.

 

MVC 구조

 

Model

 - 데이터를 가지며, 어플리케이션에서 사용되는 데이터와 그 데이터를 처리.

 - View 또는 Controller에 결합되지 않아 재사용이 가능.

 

View

 - 사용자에게 실제로 보여지는 화면을 표현.

 - 앱 및 UI와의 상호작용에서 Controller와 통신.

 - 유저와 어떤 입력을 하든 View 자체는 무엇을 수행할지 모름.

 

Controller

 - 사용자로부터 입력을 받고, 이 입력을 Model에 의해 View를 정의.

 - Model의 데이터 변화에 따라 View를 선택.

 

 

MVC의 흐름도

각각의 객체들은 위와 같은 흐름에 따라 작동합니다. 하지만, 안드로이드에서는 Activity에 View와 Controller 역할을 동시에 수행하게 됩니다. 그래서 아래와 같은 흐름도에 따라 작동하게 됩니다.

 

AOS에서 MVC 흐름도

 

이러한 MVC패턴은 Model과 View를 분리해줍니다. Model이 어디에도 종속되지 않고, 쉽게 테스트할 수 있습니다. 하지만, 이중에서도 단점이 존재합니다.

 

MVC패턴의 장단점

장점 단점
Model과 View의 분리.
Model의 비종속성으로 재사용이 가능.
구현이 쉽고 가장 단순함.
단위 테스트에서 View는 테스트할 부분이 없기 때문에 Model만 테스트 가능.

개발 기간이 짧아짐.
Model과 View 사이에 의존성이 발생
즉, View의 UI갱신을 위해 Model을 직/간접적으로 참조하므로 앱이 커지고 로직이 복잡해질수록 유지보수가 힘듬.
시간이 지날수록 Controller에 코드가 쌓여 이슈 발생 가능성이 높아짐.

Controller가 안드로이드 API에 깊게 종속되므로 단위 테스트가 어려움.

 

 

 위 내용들을 바탕으로 MVC의 각 클래스들이 담당하는 역할과 Controller의 실제 코드는 아래와 같습니다.

public class TicTacToeActivity extends AppCompatActivity {

    private Board model;

    /* View Components referenced by the controller */
    private ViewGroup buttonGrid;
    private View winnerPlayerViewGroup;
    private TextView winnerPlayerLabel;

    /**
     * In onCreate of the Activity we lookup & retain references to view components
     * and instantiate the model.
     */
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.tictactoe);
        winnerPlayerLabel = (TextView) findViewById(R.id.winnerPlayerLabel);
        winnerPlayerViewGroup = findViewById(R.id.winnerPlayerViewGroup);
        buttonGrid = (ViewGroup) findViewById(R.id.buttonGrid);

        model = new Board();
    }

    /**
     * Here we inflate and attach our reset button in the menu.
     */
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        MenuInflater inflater = getMenuInflater();
        inflater.inflate(R.menu.menu_tictactoe, menu);
        return true;
    }
    /**
     *  We tie the reset() action to the reset tap event.
     */
    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
            case R.id.action_reset:
                reset();
                return true;
            default:
                return super.onOptionsItemSelected(item);
        }
    }

    /**
     *  When the view tells us a cell is clicked in the tic tac toe board,
     *  this method will fire. We update the model and then interrogate it's state
     *  to decide how to proceed.  If X or O won with this move, update the view
     *  to display this and otherwise mark the cell that was clicked.
     */
    public void onCellClicked(View v) {

        Button button = (Button) v;

        int row = Integer.valueOf(tag.substring(0,1));
        int col = Integer.valueOf(tag.substring(1,2));

        Player playerThatMoved = model.mark(row, col);

        if(playerThatMoved != null) {
            button.setText(playerThatMoved.toString());
            if (model.getWinner() != null) {
                winnerPlayerLabel.setText(playerThatMoved.toString());
                winnerPlayerViewGroup.setVisibility(View.VISIBLE);
            }
        }

    }

    /**
     * On reset, we clear the winner label and hide it, then clear out each button.
     * We also tell the model to reset (restart) it's state.
     */
    private void reset() {
        winnerPlayerViewGroup.setVisibility(View.GONE);
        winnerPlayerLabel.setText("");

        model.restart();

        for( int i = 0; i < buttonGrid.getChildCount(); i++ ) {
            ((Button) buttonGrid.getChildAt(i)).setText("");
        }
    }
}

 

 코드를 살펴보면, MVC 패턴은 Model과 View를 분리해줍니다. 하지만 Controller가 안드로이드 API에 깊게 종속되므로 단위 테스트가 어렵습니다. Controller가 View에 단단히 결합되어 View를 변경하면 Controller로 돌아가서 함께 수정해야 합니다. 

 

(코드 출처: https://academy.realm.io/kr/posts/eric-maxwell-mvc-mvp-and-mvvm-on-android/)

 

 

 

 

 

728x90
반응형