brunch

You can make anything
by writing

C.S.Lewis

by Mobiinside Dec 26. 2017

안드로이드 어플리케이션에 MVP패턴을 적용하는 방법

BUZZVIL 블로그에 소개된 글을 편집한 뒤 모비인사이드에서 한 번 더 소개합니다.

1. 글을 시작하기 전에


부끄럽지만 저는 버즈빌의 초보 개발자입니다. 버즈빌에서의 다양한 경험들이 저를 많이 변화시키고 있구요. 버즈빌의 뛰어난 개발자들 사이에서 일을 배워나가면서, 앞으로 향후에 저도 뛰어난 개발자가 되기 위해서는 반드시 공부해야겠다고 생각한 몇 개의 영역이 있습니다.


그 중 하나가 ‘다양한 소프트웨어 디자인 패턴을 적절한 곳에 적용시키는 방법’이었습니다. 다행히 최근에는 많은 사람들의 경험을 통해 검증된 디자인 패턴이 많이 개발되고 있어, 다양한 패턴을 꾸준히 탐색해서 활용할 수 있는 안목만 키울 수 있다면 학습에 큰 어려움을 없을 것이라 생각하였습니다.


그동안 주로 기능적으로 시스템을 정상 동작하도록 만드는 것에만 집중했었지만, 여기서 더 나아가 가독성과 확장성이 좋고 협업하기 쉽게 만드는 구조를 짤 줄 알아야 장기적으로 더 좋은 프로덕트를 만들어 낼 수 있을 것이라는 어찌보면 막연한 생각도 배움에 대한 호기심을 자극하는데 한 몫 하였습니다.


그러던 중 실제 업무 진행 중에, 중 몇 가지 주요 패턴을 적용해 리팩토링을 할 기회를 갖게 되었습니다. 실무에서 직접 경험해보니 패턴의 유무가 만들어내는 결과의 차이는 생각보다 큰 역할을 하고 있었습니다.


미숙하지만, 이러한 맥락과 배경에서 저의 첫 포스팅을 시작해보려고 합니다. 이번 포스팅의 주요 내용은, 허니스크린 안드로이드 코드에 MVP패턴을 도입해 리팩토링 했던 경험을 바탕으로 안드로이드 어플리케이션에 MVP패턴을 적용하는 방법에 관한 것입니다. 


2. MVP 패턴에 대해서 


Model : 내부적으로 쓰이는 데이터를 저장하고, 처리하는 역할을 합니다. 흔히 ‘비즈니스 로직’ 이라고 부르는 부분입니다. View, Presenter등 다른 어떤 요소에도 의존적이지 않은 독립적인 영역이죠.


View
 : 사용자 인터페이스(User Interface-UI)라 불리는 영역입니다. 안드로이드에서는 Activity, Fragment가 대표적인 예입니다. Model에서 처리된 데이터를 Presenter를 통해 받아서 사용자에게 보여주며, 사용자 액션 및 Activity lifecycle 변화를 감지해서 Presenter에 보내는 역할을 합니다. Presenter를 이용해 데이터를 주고받기 때문에 Presenter에 의존적입니다.


Presenter
 : Model과 View사이의 매개체입니다. View에서 캐치한 유저 액션을 전달 받아서 Model의 로직을 호출하거나, Model의 로직으로부터 나온 결과를 전달 받아서 View에 보내 UI변경을 야기하는 등 둘 사이의 소통의 역할을 합니다. Model, View 모두에 의존적입니다.


‘MVP패턴’을 이용해서 이와 같이 Model과 View간의 결합도를 낮추면, 새로운 기능을 추가하거나 변경할 필요가 있을 때 관련된 부분만 수정하면 되기 때문에 확장성이 좋아지며, 테스트 코드를 작성하기 편리해지기 때문에 더 안전한 코드 작업이 가능해집니다.

MVP pattern (https://code.tutsplus.com/tutorials/how-to-adopt-model-view-presenter-on-android--cms-2

3. 안드로이드에 MVP가 적합한 이유


안드로이드에서 UI를 표현하는 컴포넌트들의 특징은 화면을 시각적으로 직접 그리는 역할 및 화면에 있는 UI 요소들에 대한 액션 처리를 항상 함께 담당한다는 것입니다. 이러한 프레임워크의 특징 때문에 기존에 웹 어플리케이션 등에서 많이 쓰이던 MVC(Model, View, Controller)패턴을 적용하기에는 화면을 그리는 View와 액션을 처리하는 Controller를 완전히 분리하기 어렵다는 한계가 있습니다. 이러한 이유때문에, MVP가 안드로이드에 더 적합하다는 논의가 이어져오고 있는거구요.


안드로이드를 개발한 구글 측에서도 Android architecture blueprints 라는 이름으로 MVP 패턴을 적용한 샘플 프로젝트를 공식적으로 운영하는 것으로 보아, 이 패턴은 안드로이드에 더 적절한 것으로 어느정도 검증되었다고 볼 수 있을 것 같습니다. 


4. MVP패턴 적용 전


제가 실무를 통해 MVP패턴을 적용하여 리팩토링 한 부분은 허니스크린 유저 회원가입의 마지막 단계였습니다. 다시 말하자면, 유저에게 닉네임, 나이, 성별, 추천인 등을 입력받아 서버에 회원 가입 요청을 보내는, ‘프로필 입력’ 단계라고 볼 수 있을 것 같습니다. 실제 코드를 바탕으로 다루기에는 지면이 충분하지 않기에 간략화된 버전을 이용해 MVP패턴을 적용하는 과정을 중심으로 기술하겠습니다.


기존에 ‘프로필 입력’ 단계를 구현한 Profile Activity의 코드를 간추리면 다음과 같습니다.


클릭하시면 확대된 이미지를 보실 수 있습니다.


보시다시피 하나의 Profile Activity 클래스에서 모든 역할을 다 수행하고 있는데요. 이건 간략화한 예시이기 때문에 내용 파악이 쉬운 편이지만 여기에 다양한 뷰 요소가 추가되거나, DB접근, AsyncTask등 복잡한 백그라운드 로직이 추가된다면, 쉽게 내용을 파악하기 힘들 뿐만 아니라 모든 로직이 뒤섞여 있어서 기능을 추가하거나 변경하기 어렵고, Android framework에 종속성을 가지기 때문에 Unit test를 작성하기가 어려울 것입니다. 


5. 요구 사항 분석


위의 코드에 구현된 ‘프로필 입력’ 단계의 요구사항은 다음과 같이 정리할 수 있습니다.

위 요구사항을 세분화하여 특성에 따라 Data, UI 파트로 나누도록 하겠습니다.


6. 뼈대 구조 완성


Data파트의 요구사항을 구현하는 것이 바로 Model이기에 위의 Data파트의 요구사항 리스트를 통해 Model이 구현해야 할 메소드들의 프로토타입을 정의하도록 하겠습니다. 앞의 코드와 메소드 이름 등 겹치는 부분이 많으나 주목할만한 차이점은 Intent, EditText와 같은 View요소를 접근하는 부분을 제거하고 파라미터로 내용만 전달받도록 바꾼 것, 그리고, API는 비동기적인 응답을 받기 때문에 Listener를 파라미터로 전달해 Model을 이용하는 쪽에(즉, Presenter에) 응답을 전달할 수 있는 구조로 변경한 것입니다.

UI파트의 요구사항을 ‘UI 변경’이라는 액티브 타입과 ‘이벤트 인식’이라는 패시브 타입으로 나누면 전자는 Presenter가 View를 호출하는 경우, 후자는 View가 Presenter를 호출하는 경우로 나눌 수 있습니다.


즉 전자에 해당되는 ‘추천 닉네임 보여주기’, ‘에러 메세지 보여주기’, ‘화면 전환하기’는 View에 구현되어야 하며, 후자에 해당되는 ‘액티비티 생성 이벤트 발생 시 인텐트 파싱’, ‘버튼 클릭 이벤트 감지’는 Presenter에 구현되어야 한다는 걸로 정리할 수 있습니다.


이렇게 정리한 것을 기반으로 서로 의존성을 갖는 View, Presenter간의 규약을 ‘Profile’이라는 Java interface에 정의하고, 최종 View, Presenter에서는 각각 Profile.View, Profile.Presenter interface를 구현해 보도록 하겠습니다.


클릭하시면 확대된 이미지를 보실 수 있습니다


7. 구현


이제 위에 정의된 프로토타입 및 인터페이스에 맞춰서 실제 클래스를 구현하도록 하겠습니다. 먼저 Model의 경우 Presenter, View에 의존성이 없기 때문에 위의 프로토타입 그대로를 구현하면 다음과 같이 완성됩니다.


클릭하시면 확대된 이미지를 보실 수 있습니다.


View, Presenter의 경우는 약간의 추가 작업이 필요합니다. View는 Presenter에 의존하기 때문에 Presenter 객체를 멤버 변수로 가지고 있으며 정해진 ‘이벤트 발생’ 시점에 해야 할 역할을 Presenter에게 위임하도록 구현해야 합니다.


또한, Presenter는 View와 Model에 종속적이기 때문에 둘 다를 멤버 변수로 가지고 있으며, 요구사항에 알맞게 View, Model의 메소드들을 호출해 UI파트와 data파트의 상호작용을 만들 수 있도록 구현해야 합니다.

이어서, ProfileActivity에서 Profile.View를 구현하면 다음과 같습니다. onCreate() 시점에 Presenter의 initUserData()를 호출하고, 버튼의 OnClickListener 내에서 callSignup()을 호출함으로써 이벤트 발생을 Presenter에게 알리고 있습니다.


클릭하시면 확대된 이미지를 보실 수 있습니다.


마지막으로, Profile.Presenter를 구현해서 ProfilePresenter 클래스를 완성시킵니다. 생성자 호출 방법은 여러 가지가 있으나 여기서는 고유한 lifecycle을 갖는 View가 생성되는 시점(onCreate())에 View를 파라미터로 전달하여 Presenter의 생성자를 호출하고 그 내부에서 ProfileModel 생성자를 호출하도록 구현하겠습니다. 나머지 메소드들은 요구사항에 정의된 대로 Model에서 데이터를 처리해서 View에 전달하는 역할을 합니다.


클릭하시면 확대된 이미지를 보실 수 있습니다.



8. 글을 마치며


이로써 프로필 입력 단계에 MVP 패턴을 적용하여 리팩토링하는 전체 과정이 완료되었습니다. 실제 코드에 이를 적용한 이후 가장 크게 느꼈던 장점은 새로운 기능을 추가할 때 UI, data 파트를 나누어 적용하게 되어서 해야 할 일이 명확해졌고, 그 결과 쉽고 빠르게 적용이 가능했다는 것입니다.


이번에 다룬 MVP패턴은 기존에 정형화된 개발 패턴이 없었던 안드로이드에서 점차 입지를 넓혀가고 있으며, 이에 따라 다양한 적용 사례들을 쉽게 찾아볼 수 있긴 하지만, 이 블로그를 통하여 저는 제가 직접 사례들을 찾아가며 공부할때 느꼈던 막연함을 보완하고 조금 더 쉬운 방법으로 설명할 수 있게 코드에 적용을 마친 결과만이 아니라 요구사항 분석, 인터페이스 정의, 구현 등 ‘과정’에 대해 자세하게 기술하도록 노력하였습니다.


어떻게 보면 작은 성과이지만, 학습하고자 했던 영역 중 하나인 디자인 패턴 적용에 대해 처음부터 끝까지 스스로 학습하고 적용해, 허니스크린 개발에 조금이나마 효율성을 가져다 주었다는데 의의를 찾고 싶습니다. 


Reference  

Android Testing Codelab

Github- android architecture

Envatotuts


[버즈빌의 누구나 궁금해하는 개발 이야기] 시리즈  


How to use Django rest framework

안드로이드 파편화(Fragmentation)에 대하여

오픈소스를 쇼핑하는 엔지니어


원문 바로가기: Android MVP Pattern – What, Why and How? 




작가의 이전글 우수한 품질과 독특함 중 하나를 선택해야 한다면?
브런치는 최신 브라우저에 최적화 되어있습니다. IE chrome safari