Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

MVP 패턴 소개

17,197 views

Published on

MVP 패턴에 대해 소개한다. MVP 패턴의 기본 구성과 동작 방식, 장점, 안드로이드에서의 구현 안 등을 설명한다.

Published in: Technology
  • Be the first to comment

MVP 패턴 소개

  1. 1. MVP 패턴 소개 (안드로이드) 신림프로그래머, 최범균, 2016-06-18 madvirus@madvirus.net
  2. 2. 다룰 내용 • 막 만들면 • MVP 패턴 – 적용 결과 • MVP 패턴 적용 예 – 안드로이드 – 자바스크립트 • 정리 2
  3. 3. 간단한 앱, 막 만들면… 3 드래그 탭 미림분식 별: 5
  4. 4. 4 public class StoreActivity extends Activity …. { private Handler handler = new Handler(); protected void onStart() { loadingProgress.show(); LoadListThread lThread = new LoadListThread(); lThread.start(); } private class LoadListThread extends Thread { public void run() { URL url = new URL(urlstr); HttpURLConnection conn = (HttpURLConnection)url.openConnection(); ... handler.post(new Runnable() { public void run() { loadingProgress.hide(); if (loadingResult) { localDB.save(loadedData); drawMarkerForStores(loadedData); } else { toasts..short("에러!"); } } }); } } 기능 흐름 제어 데이터 보관 처리 UI 화면 제어 네트 워크 UI 처리 위한 쓰레드 화면 제어 쓰레드 제어
  5. 5. 5 public class StoreActivity extends Activity …. { private MapPoint center; public void onMapViewDragEnded( MapView mapView, MapPoint mapPoint) { center = mapPoint; loadingProgress.show(); LoadListThread lThread = new LoadListThread(); lThread.start(); } private class LoadListThread extends Thread { public void run() { URL url = new URL(urlstr); HttpURLConnection conn = (HttpURLConnection)url.openConnection(); ... if (center != null) { params.put("center", toCoord(center)); } ... handler.post(new Runnable() { public void run() { loadingProgress.hide(); … } }); } } 사용자 이벤트 처리 UI 의존 데이터 화면 제어 + 쓰레드 제어
  6. 6. 6 public class StoreActivity extends Activity …. { private LocalDB localDB; public void onMarkerTab(MapView mapView, Marker marker) { StoreData storeData = localDB.select(marker.getMeta().getId()); if (storeData == null) { alerts.showAlert("데이터가 없습니다."); } else { showBalloon(mapView, storeData); } } private void showBallon(MapView mapView, StoreData storeData) { BalloonInfoWindow infoWin = new BallonInfoWindow(this,storeData); mapView.addFigure(infoWin); … } 데이터 조회 화면 흐름 제어 위젯 제어
  7. 7. 막 만들다 보면 7 UI 조작 흐름 제어 로직 쓰레드 네트워크 DB 도메인 로직
  8. 8. 단점 • 실행 흐름 파악이 어렵다. – 흐름 제어 로직이 산재 • 부분적인 테스트가 어렵다. – 위젯 조작 + UI 흐름 제어 + 데이터 관리 짬뽕 • 변경이 점점 어려워진다. – 산재한 로직, 뒤섞인 코드  코드 변경 어려움 8
  9. 9. 해결책 9 역할에 따라 알맞게 나누기
  10. 10. 10 MVP
  11. 11. Model-View-Presenter 11 모델프리젠터뷰 • 비즈니스 로직과 어플리케이션 데이터 • 어플리케이션의 인터페이스 • 사용자에게는 뷰가 곧 어플리케이션 • 빈번하게 바뀌는 영역 • 모델과 뷰를 연결 • MVP에서 프리젠터는 기능/흐름을 제공 • 요구사항이 프리젠터의 기능에 대응
  12. 12. Model-View-Presenter 12 모델프리젠터뷰 • 비즈니스 로직과 어플리케이션 데이터 • 예, 인증 • 예, 특정 위치 기준 가계 정보 • 이벤트를 프리젠터에 전달 • 예, 로그인 버튼 클릭을 클릭하면 프리젠터에 인증 실행 요청 • 사용자에게 알맞은 화면 제공 • 예, 프리젠터를 통해 전달받은 데이터를 리스트와 같은 위젯을 이용해서 표현 • 로딩 중, 경고 대화창 등 안내 • 사용자 요청에 반응 • 예, 뷰를 통해 인증 요청을 받으면 모델을 사용해서 인증을 수행하고, 결과를 뷰에 전달 • 뷰의 흐름 제어 • 예, 인증 시작 전에 뷰를 통해 진행 중 대화창을 보여주고, 인증에 성공하면 뷰를 통해 메인 화면으로 이동
  13. 13. 뷰 • 인터페이스 – 사용자에게 보여질 기능을 정의 – 프리젠터가 호출할 기능 • 예 – 데이터 출력 기능 – 진행 중 알림 기능 • 뷰 구현체 – 인터페이스에 정의한 기능의 구현 제공 – 사용자 이벤트를 프리젠터에 전달 • 예 – 데이터를 리스트로 표시 – 사용자가 항목을 클릭하면 프리젠터에 선택 요청 전달 13
  14. 14. 뷰 구현 14 public interface StoreListView { void showLoadingMessage(); void hideLoadingMessage(); void displayStores(stores); void showFailMessage(); } public class MapStoreListView implements StoreListView, … { private StoreListPresenter presenter; public void MapStoreListView(StoreListPresenter presenter) { presenter.setView(this); } public void onMapViewDragEnded( MapView mapView, MapPoint mapPoint) { Coord center = toCoord(mapView.getCenter())) presenter.onLocationChange(center); } public void showLoadingMessage() { progress = ProgressDialog.show(this, "", "로딩중"); } public void hideLoadingMessage() { progress.hide(); } public void displayStores(List<Store> stores) { …지도위에 마커 표시 }
  15. 15. 프리젠터 정의 • 사용자의 UI 행위를 추상화한 메서드로 기능을 정의 – 뷰 구현에 의존하지 않음 15 onLocationChange: 상점을 조회할 위치 기준을 변경한다는 의미, O 뷰 구현 기술에 종속되지 않음 onMapDragged: 뷰의 지도를 드래그했다는 것을 의미, X 뷰 구현 기술에 종속됨
  16. 16. 프리젠터 구현 • 모델과 뷰를 사용해서 사용자의 요청 처리 16 public class StoreListPresenter { private StoreListView view; private StoreListModel model; public void onLocationChange(Coord location) { view.showLoadingMessage(); try { List<Store> stores = model.getStoresIn(location, 100); view.hideLoadingMessage(); view.displayStores(stores); } catch(Exception e) { view.hideLoading(); view.showFailMessage(); } } UI 로직을 제어 모델로 비즈니스 로직 실행
  17. 17. 전형적인 실행 흐름 17
  18. 18. MVP 적용 결과 • 뷰 구현 기술과 UI 로직(프리젠터)의 분리 – UI 로직 응집도 향상 18
  19. 19. MVP 적용 결과 • UI 구현 기술(뷰)과 UI 로직(프리젠터)의 분리 – UI 로직에 영향을 주지 않고 뷰 구현 변경 가능 19 public class MapStoreListView implements StoreListView, … { private StoreListPresenter presenter; … public void displayStores(List<Store> stores) { listAdapter.clear(); listAdapter.addAll(stores); } public class MapStoreListView implements StoreListView, … { private StoreListPresenter presenter; … public void displayStores(List<Store> stores) { map.clearAllMarks(); for(Store store : stores) { map.addMarker(createMarker(store)); } }
  20. 20. MVP 적용 결과 • UI 구현 기술(뷰), UI 로직(프리젠터), 도메인 로직(모델) 구현의 분리 – 뷰/모델 구현없이 UI 로직 테스트 가능 • 뷰를 구현하지 않아도 모의(mock) 객체로 UI 로직에 대한 테스트 가능 20
  21. 21. 21 안드로이드와 MVP
  22. 22. 기본 구조 22
  23. 23. Activity나 Fragment 역할 23 public class MapStoreListActivity implements StoreListView, … { private StoreListPresenter presenter; public void onCreate(Bundle savedInstanceState) { StoreListModel model = new RestStoreListModel(); this.presenter = new StoreListPresenter(this, model); this.presenter.init(); } public void onMapViewDragEnded(MapView mapView, MapPoint mapPoint) { Coord center = toCoord(mapView.getCenter())) presenter.onLocationChange(center); } public void showLoadingMessage() { progress = ProgressDialog.show(this, "", "로딩중"); } public void hideLoadingMessage() { progress.hide(); } public void displayStores(List<Store> stores) { …지도위에 마커 표시 } 뷰 구현 프리젠터/모델 객체를 생성하고 연결
  24. 24. 안드로이드 앱에서 모델의 특징 • 외부 서버와 통신을 비동기로 처리 – 허니콤(3.0): Main(UI) 쓰레드에서 네트워크 금지 – AsyncTask나 Thread 사용 • 프리젠터에 비동기 결과 전달 방법 필요 – 주요 방법: 콜백(Callback), 옵저버(Observer) 등 24
  25. 25. 모델과 콜백 25 public class RestStoreListModel implements StoreListModel { public void getStoresIn(Coord center, int distance, StoreListCallback callback) { new Thread(new Runnable() { public void run() { try { … network 처리, 데이터 변환 callback.onSuccess(results); } catch(Exception ex) { callback.onFail(ex); } } }).start(); } public interface StoreListCallback { void onSuccess(List<Store> stores); void onFail(Exception ex); }
  26. 26. 프리젠터는 콜백을 통해 결과 수신 26 public class StoreListPresenter { public void onLocationChange(Coord coord) { view.showProgressMessage(); model.getStoresIn(coord, 100, new StoreListCallback() { public void onSuccess(List<Store> stores) { view.hideProgressMessage(); view.displayStores(stores); } public void onFail(Exception ex) { view.hideProgressMessage(); view.showFailMessage(ex); } }); }
  27. 27. 프리젠터와 라이프사이클 • Activity/Fragment의 라이프사이클에 맞춰 기능을 구현해야 할 경우, 프리젠터에 라이프사이클 관련 메서드 정의 27 public class StoreListPresenter { public void onResume() { … } public void onStop() { … } … } public class StoreListActivity … { @Override protected void onResume() { super.onResume(); presenter.onResume(); } @Override protected void onStop() { super.onStop(); presenter.onStop(); }
  28. 28. 유용한 도구 • Dagger – 의존 주입(DI) – Activity에서 직접 프리젠터나 뷰를 생성하지 않도록 해 줌 • RxJava – 옵저버 – 콜백 인터페이스 대체 28
  29. 29. 29 자바스크립트
  30. 30. 기본 구현 • 구성 요소는 동일 – 언어 특성상 인터페이스없이 구현 • 초기화 – HTML 로딩 시점에 뷰/모델/프리젠터 초기화 • 예, $.ready()에서 초기화 30
  31. 31. 뷰 예시 31 functionMeasureListView(){ varpresenter; functiononSelect(idx){ presenter.selectMeasure(idx); } functioninit(){ resizeContent(); ... }; ... init(); return{ setPresenter:function(_presenter){ presenter=_presenter; }, showMeasures:function(measures){ if(measures.length==0){ $("#noData").show(); }else{ $("#hasData").show(); measureBox.setState({measures:measures,loading:false}); } }, showLoadingError:function(){console.log("error"); } }; }
  32. 32. 프리젠터 예시 32 function MeasureListPresenter(view, model) { this.reloadList = function() { model.getMeasureList({ success: function(measures) { view.showMeasures(measures); }, error: function() { view.showLoadingError(); } }); }; this.selectMeasure = function(idx) { var measure = model.getMeasure(idx); view.showMeasure(measure); }; }
  33. 33. 모델 예시 33 functionMeasureListModel(){ varmeasures=[]; functiontoCoord(coordStr){...} functionconvertToMeasure(feeds){...}; return{ getMeasureList:function(callback){ $.ajax({url:"https://api.mycompany.com/....", type:'GET', success:function(data){ varmeasures=convertToMeasure(data.feeds); callback.success(measures); }, error:function(xhr,status,error){callback.error();} }); }, getMeasure:function(idx){returnmeasures[idx];} }; }
  34. 34. 초기화 예 34 <script> $(document).ready(function() { var model = new MeasureListModel(); var view = new MeasureListView(); var presenter = new MeasureListPresenter(view, model); view.setPresenter(presenter); presenter.reloadList(); }); </script>
  35. 35. 35 생각할 거리
  36. 36. 뷰와 외부 입력 • 뷰의 역할: 외부 데이터 입력 – 기능 예 • 블루투스로 데이터를 받는 기능 • GPS로 현재 좌표 받는 기능 – 이 두 기능은 뷰를 통해 전달받는 사용자 이벤트 36
  37. 37. 모델 영역의 구조 • 모델에 얼마나 많은 도메인 로직이 있나? – 앱/웹에서 모델은 주로 다음의 두 가지 수행 • 서버와 통신 – 중요 로직은 서버에 존재 • 임시로 데이터 보관 – 로컬DB, 메모리 등에 보관 • 모델에 다양한 로직이 있다면 테스트 가능한 구조로 만드는 것이 중요 37
  38. 38. 프리젠터 • 인터페이스와 구현체로 구분 필요 여부 – 코드 수준 테스트 대상은 주로 프리젠터와 모델 – 프리젠터가 유연해야 하는 경우 드뭄 – 모델 테스트시 프리젠터에 대한 모의 객체가 필요한 경우 없음 • 프리젠터 메서드 이름, 파라미터, 리턴 타입 – 뷰에 의존하면 안 됨 • 프리젠터 먼저 구현하기 38
  39. 39. 39 정리
  40. 40. MVP • 뷰 구현, UI 로직, 모델 구현을 분리 • 복잡도 감소 – 분리에 따른 복잡도 <<< 뒤섞일 때의 복잡도 • 생산성 향상시키기 – 코드 변경이 상대적으로 쉬워짐 – UI 구현없이 사용자와의 상호작용에 대한 테스트 가능 40
  41. 41. 41 끝

×