안드로이드에서 대표적인 아키텍처 패턴으로 MVC, MVP, MVVM이 거론된다. 사실상 MVC 패턴은 거의 사용되지 않는다. 더 좋은 아키텍처 패턴이 등장해서 사장된 것일 수도 있지만 여전히 다른 분야에서는 많이 사용되는 패턴으로 알고 있다.
그렇다면 왜 안드로이드에서는 MVC 패턴을 사용하지 않을까? 더 좋은 패턴이 있기 때문이라면 그 말에는 상대적으로 더 좋다는 의미가 내포되어 있다. 그렇다는 것은 MVC 패턴에 존재하는 단점을 개선한 패턴이 있기 때문이라고 할 수 있다. 결국 개발자들이 MVC 패턴의 단점이 치명적이라고 판단했다고 유추해 볼 수 있다.
그런데 안드로이드의 MVC가 진짜 MVC 패턴일까?
위 구조는 MVC 패턴을 나타낸 것이다. Controller(이하 컨트롤러)에 의해서 View(이하 뷰)와 Model(이하 모델)가 소통하는 구조로 되어 있어서 뷰와 모델의 의존성을 제거할 수 있다. 이렇게 분리하면 각각의 책임이 명확해진다. 코드 설계를 이해하기 쉬워지고 그만큼 유지보수에도 용이하다. 또한 분리가 잘되어 있을수록 테스트하기 쉬워진다.
안드로이드에서 MVC를 이야기할 때 Activity 또는 Fragment를 컨트롤러로 본다. 좀 더 디테일하게 이야기할 때는 컨트롤러와 뷰의 역할을 동시에 한다고 표현한다. 바로 이 부분이 참 애매한 부분이다. 이렇게 애매하게 말하게 되는 이유가 뭘까? 안드로이드 시스템의 구조적인 특징 때문이다.
먼저 안드로이드가 아닌 콘솔 프로그램을 기준으로 생각해 보자. 위 MVC 구조를 콘솔 프로그램 기준으로 좀 더 세분화해 보면 다음과 같이 나타낼 수 있지 않을까?
뷰라고 하는 부분은 (코틀린 기준) readln()이나 println()과 같은 메서드를 호출하는 객체로 이뤄진다. 그런데 그 뷰 객체는 실제로 해당 메서드를 가지고 있는 Console 객체(Console 클래스)에 실제 뷰를 나타내는 작업을 맡긴다. (물론 이렇게 계속 타고 들어가면 커널의 어떤 부분까지 내려가게 되겠지만 말이다.)
안드로이드 MVC 구조는 어떨까?
안드로이드에서 컨트롤러는 Activity 또는 Fragment가 되고 뷰는 레이아웃이 정의된 XML(View 클래스)이 된다. 여기서 XML을 하나의 객체로 생각하면 어쩐지 앞서 살펴본 콘솔 프로그램의 Console 객체와 비슷한 역할을 하는 것 같다는 생각이 들지 않는가?XML이라고 표현했지만 실제로 View 클래스가 존재하기 때문에 객체다. 이런 생각을 반영하여 콘솔 프로그램의 MVC 구조를 다음과 같이 다시 그려보았다.
안드로이드 MVC 구조와 차이점이 보인다. 콘솔 프로그램에서 뷰라고 했던 부분을 안드로이드에서는 컨트롤러라고 지칭하고 있다. 콘솔 프로그램의 뷰 객체는 실제 뷰를 그리기 위해서 Console 객체의 메서드를 이용한다. 안드로이드도 마찬가지가 아닌가? Activity도 뷰를 그리기 위한 작업은 View 객체의 메서드를 호출한다. 물론 Activity는 콘솔 프로그램의 뷰 객체에 비해서 (안드로이드 종속적인) 더 다양한 작업을 할 수 있다는 차이는 있다. 그래서 아마 컨트롤러이면서 뷰라는 말이 나왔을 것 같다.
하지만 이러한 관점은 MVP 패턴에서 바뀐다. 안드로이드 MVP 패턴에서 Activity는 뷰로 취급된다. 안드로이드에서 뷰를 그리기 위해선 안드로이드 시스템에 해당 작업을 요청해야 한다. 따라서 안드로이드 시스템에 의존성이 큰 Activity는 컨트롤러 보단 뷰에 더 가깝다고 생각할 수 있다. 뷰를 그리는 작업은 안드로이드 의존성을 끊고 할 수 없지만 컨트롤러의 역할은 가능하기 때문이다.
이러한 관점에서 안드로이드 MVC에서 컨트롤러라고 하는 Activity와 뷰라고 하는 XML(View 객체)은 둘을 합쳐서 뷰라고 볼 수 있다. 둘의 관계는 콘솔 프로그램에서 뷰 객체와 Console 객체의 관계와 크게 다르지 않기 때문이다. (코틀린 콘솔 프로그램의 뷰 객체는 클래스로만 만들면 JVM 상에서 콘솔 출력이 가능한 메서드를 호출할 수 있지만 안드로이드는 Activity나 Fragment와 같은 특정 클래스로 제약되어 있다는 차이점만 존재한다.)
그러면 결국 안드로이드 MVC 패턴은 MVC가 아니라 MV 패턴이라고 불러야 하지 않을까?
그러면 안드로이드에서 MVC 패턴은 없는 것일까? 또는 불가능한 것인가? 그렇지는 않다. 안드로이드 아키텍처 패턴에서 (일반적인) MVC 패턴과 실제로 가장 유사한 것은 MVP 패턴이다.
위 구조는 MVP 패턴을 나타낸 것이다. 구조만 보면 컨트롤러라고 부르는 것을 Presenter라고 부르는 것일 뿐이 아닌가 싶을 만큼 똑같다. 실제 역할에는 차이가 존재하지만 안드로이드에서 MVC 패턴이라고 하지만 사실상 MV 패턴인 구조에서 컨트롤러 역할을 할 수 있는 부분을 Presenter라는 것으로 만들면 MVC스러운 구조가 되지 않을까? 그러면 안드로이드 MV 패턴보다는 MVP 패턴이 흔히 말하는 MVC 패턴과 더 유사하다고 볼 수도 있다.
단, 여기서 중요한 점은 Presenter를 단순히 이름만 Presenter로 갖춰서는 별 의미가 없다는 것이다. 만약 Presenter가 안드로이드의 뷰인 Activity에 의존하는 형태라면 그것은 처음의 안드로이드 MVC 패턴과 다를 바가 없다. 이름만 Presenter인 것이지 Activity와 강하게 결합되면 결국 안드로이드 시스템에 종속적인 객체가 되고 그것은 (역할에 따를 수 있겠지만) 또 하나의 뷰 객체가 되고 말 것이다.
그렇기 때문에 안드로이드 MVP 패턴에서는 일반적으로 Contract라는 인터페이스로 뷰를 추상화한다. 이를 통해서 Presenter와 뷰(Acitivty)가 가지는 안드로이드 시스템에 대한 의존성을 끊는다. 기껏 이렇게 끊어냈는데 Presenter에서 안드로이드 시스템에 관련된 코드를 작성한다면 Presenter를 안드로이드 MVC 패턴에서 컨트롤러라고 여기던 Activity와 다를 바가 없어지게 된다.
안드로이드 시스템 의존성이 없어진 Presenter는 JVM 환경에서 단위 테스트가 가능해진다. 이러한 단위 테스트 과정에서 테스트 더블을 사용할 수도 있다. 그래서 MVP 패턴을 적용하면 테스트에 이점이 있다고 하는 것이다.
정리하자면 안드로이드 MVC 패턴은 MV 패턴이라고 보는 편이 좀 더 명확하고 그렇기 때문에 안드로이드 MVC와 MVP는 분명히 다르다. 그 둘을 명확히 다르다고 할 수 있는 요소는 바로 뷰를 추상화하여 Presenter가 안드로이드 시스템 의존성을 가지지 않게 하여 기존의 컨트롤러라고 오해한 Activity처럼 되지 않도록 하는 것이다.