토요일, 12월 8

MVC? MVP? 무엇에 쓰는 패턴인고?

MVC = Model View Controller
MVP = Model View Presenter
MVP & MVC


0. MVC, MVP 패턴의 등장 이유
1. MVC 패턴은 무엇인가? 
2. MVP 패턴은 무엇인가? 
3. 결론


0. MVC, MVP 패턴의 등장 이유

GUI 의 등장. 보다 효율적으로 사용자와 상호작용 할 수 있는 구현의 필요성.  



1. MVC 패턴은 무엇인가? 

MVC 패턴은 MVC = Model View Controller, 3가지 요소로 나누고자 하는 의도에서 나왔다.
과거 OS에서 GUI를 구성할 때 View가 Event를 handle하지 못했고 때문에 Controller가 사용자와의 상호작용을 전담하게 되면서 나온 구도라고 한다.

MVC 패턴이 뭐길래? 
MVC 패턴은 Model View Controller를 분리하고자 나온 개념이다. 그 이유는 각 요소를 분리하여 보다 '잘' 만들고자 하는데 있다. 객체지향은, 기본적으로 객체간의 상호 의존성을 줄여 재사용성과 유지보수를 최적화 하는데 목적이 있다.

 하지만 MVC 패턴은 View와 Model 을 분리할 수 없는 의존성의 한계가 있다. 
 
MVC Model 1

MVC Model 1 은 극단적으로 Model과 View가 상호 의존이 심해, 거의 구분이 불가능한 상태를 가지고 있다. 이를 말로 표현하자면 사용자가 요청에 따라 Controller가 하나의 DAO를 가져오며 ( Bean 같은거 ) 이 DAO 를 보여주기 위해 View를 만든다. 라고 할 수 있다.

즉, DAO가 바뀌면 View는 당연히 새로 만들어야 하는 구조였다. 

이와 같은 문제에 MVC Model 2이 등장하게 되었는데, 
골자는 View를 갱신시켜주는 Observer를 통해 의존성을 감소시키는 것이었다.

MVC Model 2

얘기를 진행하기 전에 MVC Model 2에서 Controller 에 Servlet이 온 점을 먼저 집고 넘어가고자 한다. 기본적으로 Controller는 Controller의 역할을 할 수 있으면 된다. 그러므로 Controller가 JSP로 구현되었어도 같은 역할을 수행한다면 Servlet과의 차이는 없다. 

MVC Model 2 Diagram
MVC Model 2 에 새로 등장한 Observer interface는 Model과 View의 의존성을 어느 정도 경감시켜준다고 한다. ( 본인은 잘 모르겠지만... ) MVC Model 2에는 Flow SynchronizationObserver Synchronization 패턴이 적용됐다고 한다.

Web을 예로 든다면, 
Model 1  : controller 가 요청을 받음 -> Model 갱신 -> Model 에 따라 View 만듬
Model 2  : controller 가 요청을 받음 -> Model 갱신 -> Observer에게 갱신 요청함 -> View에서 갱신이 됨. 

Android를 예로 든다면, 
Model 1  : onItemClickEvent 받음 -> Model 갱신 -> 새 ListView 만듬. 
Model 2  : onItemClickEvent 받음 -> Model 갱신 -> Adapter에게 Notify함 -> View 갱신


MVC 의 패턴 한계 
MVC 패턴에 대한 한계점은 엄청 많은거 같은데, 몇 가지만 얘기해보고자 한다. 
첫 째는, MVC 패턴 자체로서가 아닌 타 패턴( 특히 Observer의 등장 )과 함께 사용해야 본 의도인 MVC의 삼권 분리를 이룰 수 있다는 점으로 사실상 패턴으로서의 한계점을 드러내는 점이라고 할 수 있다. 

둘 째는, Observer의 등장으로 인해 신경써야 할 일이 더 많아졌다는 것이다. Model1에서는 하나의 Model에 하나의 View을 만들었기에 현재 어떤 View가 보여지고 있는지 신경쓸 이유가 없었다. Controller가 명령하면 보여질 Model에 보여질 View를 사용하면 됐으니까. 하지만 Observer를 사용하게 되면서 현재 보여지는 View 뿐 아니라 동시에 View의 교체 혹은 갱신 시에 Flow의 Synchronize 까지 신경써야 된다.

셋 째는, Android를 개발하는 입장에서 가끔 답답함을 느끼게 되는 Model의 갱신 문제인데, 어떤 블로그에서는 이를 이렇게 표현합니다.  - "예를 들어 사용자가 체크 버튼을 눌렀을 때, 해당 갱신 기록을 어디서 처리해야 할까요?" 

여러 분의 생각은 어떠한지 궁금하다. 
현재 Android 에서 adapterView를 상속하는 View들은 모두 Observer 패턴이 적용되며, 이는 Adapter로 표현된다. MVC Model 2 에서는 앞의 질문에 대한 답을 이렇게 한다.

[ 사용자의 요청 -> Controller -> Model 갱신 -> Observer에게 갱신 명령 -> Observer에 의한 View 갱신(체크) ] 이 된다. 딱 봐도 뭔가 느낌이 미려하지가 않다. 

-기타
그 외에 Observer 패턴 자체 문제로 디버깅이 어렵다 등이 있다고 합니다. 


2. MVP 패턴은 무엇인가? 


MVP 는 Model View Presenter 로 구성한 GUI 용 패턴이다.

앞서 MVC 패턴 등장할 당시의 OS 환경에서 GUI를 구성할 때 View가 Event를 handle하지 못했고 때문에 Controller가 사용자와의 상호작용을 전담하게 되면서 나온 구도라고 했다.

현재의 OS는 View에서 Event의 Handle 을 할 수 있으므로, View 가 Event를 Handling 한다. 그리고 View 는 Presenter와 약속된 interface로서 Event 를 전송하고 Presenter는 해당 Event를 통해 Model을 갱신하는 구조이다.

MVP 패턴 예 
 앞서 예를 든, Check box 의 선택을 비교해서 생각해보자.
 MVC : onItemClickEvent 받음 -> Model 갱신 -> Adapter에게 Notify함 -> View 갱신
 MVP : onItemClickEvent 받음 -> View 갱신 -> Presenter에게 apply 함 -> Model 갱신


 View와 Presenter는 interface로서 통신한다. 
MVP 패턴은 Model과 View의 의존성을 경감시킴으로 진정한 View와 Model의 재사용성을 보장할 수 있다.

"예를 들어 사용자가 체크 버튼을 눌렀을 때, 해당 갱신 기록을 어디서 처리해야 할까요?"

라는 의문에 대해서 MVP 패턴은 이렇게 대답할 수 있다. 
[ 사용자의 요청 -> 사용자와 상호작용하는 View에서 Handle -> View 갱신(체크) -> Presenter 에 apply -> Presenter 에서 Model 갱신 ]

너무나 직관직이다. 
이것은 개발하는 사람에게도, 코드를 보는 사람에게도 직관적으로 느껴질 것이다. 

다음에 기회가 된다면 두 코드가 얼마나 다르게 느껴지는지 비교해보도록 하겠습니다.


3. 결론


MVP 가 좋음. MVP 를 씁시다. ^0^/


References

http://www.martinfowler.com/eaaDev/PresentationModel.html
http://www.martinfowler.com/eaaDev/uiArchs.html
http://aspiringcraftsman.com/2007/08/25/interactive-application-architecture/
http://blog.daum.net/picus2/186
http://msdn.microsoft.com/en-us/magazine/cc188690.aspx








목요일, 12월 6

Android의 다해상도 지원 문제 해결 방법 정리


Android 의 다해상도 지원 문제.
















안드로이드 기기별 해상도의 차이 때문에 골치썩는 분들 좀 많으신거 같습니다.

오늘은 아주아주 간단하게 Android에서 발생하는 다해상도 관련 문제들을 해결할 방법들을 정리 해보겠습니다.


이 글에서 정리할 다해상도 지원 방법은 대충 이렇습니다.
1. 각 해상도 별 Layout 제작.
2. Weight 기반 Layout 제작.
3. 이도저도 싫으면 Runtime 시에 크기 맞추기.

1번. 각 해상도 별 Layout 제작.

이 방법은 별거 아닙니다.
Android 기본 프로젝트를 만들면 res floder에 기본으로
drawable-hdpi , drawable-ldpi, drawable-mdpi, drawable-xdpi
라고 폴더가 생성된걸 보셨을 겁니다.

저 위에 그림을 한번 다시 보시죠.
순서대로 ldpi 는 dpi 100부터~ ( 몇까지냐 구글 이놈들아 ) 인 것을 알 수 있습니다.
즉, 각 폴더의 -뒤에 오는 문자는 타겟 기기의 dpi를 뜻합니다.

같은 원칙으로 device의 inch 도 지정할 수 있습니다. 

그럼 이 원칙을 layout에 적용하면 어떻게 될까요?
layout
layout-land
layout-normal-land-480x320
layout-normal-land-854x480
layout-port-480x320
layout-port-800x480

짜잔~ 요런 식입니다. 대충 무슨 뜻인지 감이 오시죠?
layout-불라불라-해상도로 만들면 대상 해상도를 위한 layout폴더가 만들어진 것 입니다.

정리하자면,
layout-[land or port] - [ 화면 크기 ]- [ dpi ]-[ 해상도 ]

이렇게 되는것이죠.
한번 예를 들어볼까요? 샘숭 갤록시S3 는 어떨까요?

샘숭 갤록시S3를 위한 layout 폴더는 다음과 같습니다.
layout-large-xhdpi-1280x720 혹은 layout-xdpi-1280x720
입니다.

참고로 별도로 폴더를 생성하지 않은 경우에 리소스는 기본 폴더에서 가져옵니다.



이와 같은 방법은 resource의 수가 많을 수록, 용량이 클 수록 비효율적이 되지만 장점도 있습니다. 그것은 모든 기기에서 같은 품질을 유지할 수 있다는 것 입니다.



2. weight를 사용한 레이아웃 구성 방법

눈보라이야기 님 블로그를 참고하세요  - http://snowbora.com/422

weight라는 것은 가중치를 뜻합니다만, 쉽게 생각하면 %를 생각하면 될 것 같습니다.

귀찮으므로 다음으로 넘어가려다 한가지만 집고 가겠습니다.

weight를 최대한 세밀하게 잡으셔서 각 구성원들을 배치하는 곳에 LinearLayout  같은 별도의 컴퍼넌트를 배치해서 구성하는 것을 지양하셔야 됩니다. 개발자가 layout을 불러들이는 순간 허수로 넣은 Linearlayout도 객체를 생성하게 됩니다.

그러므로 버튼간 간격 등은 크게 문제가 안된다면 가급적 dp등의 수치를 사용하는게 낫다고 생각됩니다. 



3. Runtime에 크기 맞추기.

Runtime에 크기를 맞추는 것은 조금 복잡할 수 있습니다.


예를 들어 위와 같은 레이아웃이 있다고 생각해봅시다.

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="320px"
    android:layout_height="460px"
    android:background="#000"
    tools:context=".MainActivity" >

    <ImageView
        android:id="@+id/imageView1"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_centerHorizontal="true"
        android:layout_centerVertical="true"
        android:src="@drawable/uwang" />

</RelativeLayout>


이 Layout은 가로 320px 세로 460px 에 딱 맞게 제작된 레이아웃입니다.
( 320x460은 우리나라에 처음 들어온 아이퐁3gs의 해상도입니다. ^_^ )

그리고 대상 타깃은 가로 480px 세로 800px 의 Google Nexus One 입니다.
해상도를 또 수정하는 것은 여간 잔망스런 일이 아닙니다.
그래서 runtime시에 기기의 해상도에 따라 맞춰줄 수 있으면 좋겠다는 생각이 들죠.

 
 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  
  // 아래 문장은 setContentView(R.layout.activity_main) 와 같습니다. 
  LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
  View v = (View) inflater.inflate(R.layout.activity_main, null);
  
  DisplayMetrics displayMetrics = new DisplayMetrics();
  WindowManager wm = (WindowManager) getApplicationContext().getSystemService(Context.WINDOW_SERVICE); 
  // the results will be higher than using the activity context object or the getWindowManager() shortcut
  wm.getDefaultDisplay().getMetrics(displayMetrics);
  int screenWidth = displayMetrics.widthPixels;
  int screenHeight = displayMetrics.heightPixels;
  Log.v("DOIT", "screenWidth : " + screenWidth + "px , screenHeight : " + screenHeight + "px");
  ViewGroup rl = (ViewGroup) ((ViewGroup)v).getChildAt(0);
  ViewGroup.LayoutParams lp = rl.getLayoutParams();
  lp.height = screenHeight; lp.width = screenWidth;
  rl.setLayoutParams(lp);
  setContentView(v);

  System.out.println( "Don't use console output ");
  
 }

위의 소스를 적용하면 이렇게 나옵니다.





이 방법을 제대로 구현하고자 한다면,
각 컴포넌트들을 해상도 비에 따라 scaling 해주는 작업이 있어야 할 것 입니다.
나중에 제가 만들게 되면 올리기는 하겠습니다.


자~ 이번 글에서는 다해상도를 지원하는 방법을 알아봤습니다.

방법이 모두 장단점과 문제점을 가지고 있습니다.

첫 번째 방법은,
무식해보이지만 품질 관리차원에서.
그리고 해상도별 레이아웃을 최적화 하는데는 매우 좋습니다.

예를 들어, 4인치의 레이아웃과 5인치의 레이아웃을 다르게 하는 등
더 다양한 고객 대응이 가능해 질 것 입니다.

단, 그만큼의 비용이 추가되겠죠.


두 번재 방법은,
하나의 레이아웃을 모든 화면에서 사용할 수 있으며
추가 작업이 필요 없다는 점에서는 좋습니다.

하지만, 화면을 구성하기 어렵다는 점은 여전히 맹점이죠.
예를 들어 우측으로부터 몇 dp 아래로부터 몇 dp 등을 처리하는 것은
이 weight 로 하기는 좀 까다로운 점이 있습니다.


세 번째 방법은,


두 번째 방법과 마찬가지로 하나의 레이아웃을 모든 화면에서 사용할 수 있습니다.
또한 컴퍼넌트의 배치에 제약받지 않기도 하는데,

대신 만들때 꽤나 골치가 아플 것입니다. ^~^ FIN

일요일, 11월 18

apk 용량 50mb 초과시 google expansion 사용


Google play 는 한번에 업로드 가능한 APK 파일 용량을 50mb 로 제한하고 있습니다만,
만약 APK 파일 용량이 50mb 가 넘어간다면 Google에서 제공하는 expansion 을 사용하면 됩니다.

출처 - 안드로이드 개발자 가이드
http://developer.android.com/guide/google/play/expansion-files.html


1. expansion( 확장 ) 파일
apk 파일과 함께 첨부될 확장 파일들은 zip으로 압축해서 올리면 됩니다.
이 때 반드시 압축 옵션은 '압축안함'으로 지정하셔야 됩니다.

업로드 된 확장 파일은
[main|patch].<expansion-version>.<package-name>.obb

와 같은 이름으로 변경됩니다. 예를 들면 현 apk 버전이 314159 라면
main.314159.com.example.app.obb
라는 이름이 됩니다.