발표자료를 첨부합니다. (실제 구조에 비해 자료가 미약하긴 합니다만..)
이 구조가 final은 아니고 여러가지 시도중 하나라고 이해하시면 좋을듯합니다
더 좋은 구조가 얼마든지 있을 수 있으니 개선사항이나 궁금하신 점은 댓글로 달아주세요~!
프로젝트 구조에 대한 자기만의 노하우도 언급해주시면 서로에게 도움이 될 듯 해요
좋습니다.
mvc 패턴을 제대로 활용하려고 고민한 흔적이 보이네요.
또한 interface를 통해 view와 다른 역할자와 통신하는 것은 굉장히 멋집니다.
맨 아래 기술하겠지만 mvc의 근본적인 문제점을 개선하고자 하는 새 패턴에서 나온 방법입니다.
하지만 view와 model을 연결해서 update된 model의 내용을 view가 처리하는 것은
mvc model 초기 버전의 폐단이었습니다.
이런 구조에서는 view와 model에 커플링이 생기게 됩니다.
그리고 커플링이 생긴 상황에서는 오직 하나의 model에 하나의 view만이 생기므로 oop의 장점 중에 하나인
재사용성이 떨어지게 됩니다.
재사용성을 위해서 나온게 mvc model #2이고 android에서 사용하는 adapter가 이를 구현하고 있습니다.
adapter를 보시면 getview 와 같은 메소드를 통해서 view와 model을 분리하고 있죠.
여기서도 문제가 생기기 때문에 새로운 패턴으로 구조적 문제를 해결하고자 했습니다.
예를들어 올려주신 문서에서
model이 자신의 업데이트 상태를 바로 view에 보내기 때문에 해당 구조가 재사용성이 떨어진다면,
이를 activity+adapter 에서 받아 view를 반환해주는 형태를 취하고
model은 단순 업데이트 통보 형식을 취하면 재사용성과 직교성 모두를 취할 수 있습니다.
이런 구조는 최근에 MVP 라는 패턴으로 소개되고 있습니다.
먼저 간단히 질문을 한번 해보죠. 이것은 한번 생각해보시라는 뜻입니다.
"왜 현재 Android 구조에서 adapter계열을 둬서 model의 데이터를 view형태로 만들까?"
model의 업데이트 내역을 view 에서 직접 갱신하는 방법은 mvc 초창기 패턴에서 사용하던 방법입니다.
하나의 model에 하나의 view. 이것을 커플링이 생긴다고 말하는 것입니다.
왜냐면 model - view이 각자 독립적으로 나뉠 수 없기 때문입니다.
model 을 떼어서 다른 view에 붙히지도 못하고,
view를 떼어서 다른 model을 붙히니지도 못합니다.
정리하신 내용은 MVVM이라고 해서 Model-View-ViewModel 방법론으로 말하는 사람들도 있고,
저도 이것이 mvc라는 구시대의 방법론을 개선하고 직교성을 높히는 데의 의의가 있다고 생각합니다만,
재사용성이 떨어지는건 탐탁치 않은 모습입니다.
그렇다면 장점인 직교성을 취하면서 단점인 재사용성이 떨어지는 것을 해결하기 위해서는 어떻게 해야 할까?
같은 방법으로 interface를 통해 view가 다른 역할자와 통신하도록 하면 되고, 이것이 MVP라는 패턴의 골자입니다.
fragment를 언급하셨는데,
나중에 제가 정리해서 올리려고 했는데 지금 맛보기로 간략히 말씀드리면,
FragmentActivity + Fragment계열 adapter를 묶어서 presenter로 만들고,
View를 담당하는 Fragment는 Attach 될 때 callback interface를 생성해둬서
presenter와 통신할 수 있도록 하는 식입니다.
// View 와 presenter 간의 interface 통신 채널 구축
OnViewRequestListener mCallback;
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
Utils.logging(this, "onAttach : " + activity.toString() );
// This makes sure that the container activity has implemented
// the callback interface. If not, it throws an exception.
try {
mCallback = (OnViewRequestListener) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString()
+ " must implement OnConvertListener");
}
}
// 사용자 인터랙션
@Override
public boolean onDoubleTap( MotionEvent e ){
....중략...
mCallback.onViewModeChange();
return false;
}
// Presenter의 data update
@Override
public void onViewModeChange() {
mFragmentAdapter.ViewModeChange();
mFragmentAdapter.notifyDataSetChanged();
... 생략...
}
마플님의 상세한 설명 감사드립니다. 댓글의 댓글을 달면 depth가 깊어지니 일반 댓글로 이어갈게요
1. 우선 제가 발표한 방식을 'model의 업데이트 내역을 view 에서 직접 갱신하는 방법' 이라고 하셨는데, 제가 설명한 방식은 model이 하는 일은 자신이 업데이트 됐을 때 이를 notify하는 것이며 해당 notify를 받은 view가 자신을 갱신하는 방식입니다. view가 자신을 갱신하기 위해서는 그에 맞는 data가 필요할텐데 (text, image등등), 그 data들을 model에서 get 해오는 방식입니다. view가 그 model를 참조하고 있는 것이 아니며 update 됐다는 것을 알려주는 method의 parameter로 오는 형식입니다. 또한 해당 model이 update될 때 갱신될 수 있는 view는 하나가 아니라 그 model에 listener를 등록한 n개의 view가 됩니다. (게다가 view만 그 model의 listener가 될 수 있는 것이 아니라 view가 아닌 객체들도 해당 model에 listener로 등록되어서 model이 update 될 때마다 자신도 update되게 하는 방식입니다).
간단히 말해 마플님이 말씀하시는 '하나의 model에 하나 view'라는 말은 제가 발표한 것과 전혀 다른 내용입니다.
(하나의 view에 하나의 model이라고 이해하셨다면 그게 맞습니다. 즉, 여기서 말하는 model은 View Model입니다)
2. 제가 정리한 내용을 MVVM이라고 하셨는데 이는 거의 맞다고 할 수 있습니다. (딱 들어맞지는 않지만요)
그런데 MVVM의 단점을 해결하기 위해 나온 것이 MVP라고 설명하시는데, 그 말씀을 이해하기 어렵습니다.
MVC는 1970년대에 Smalltalk이 만들어지며 생긴 개념이고, MVP는 20년 뒤인 1990년대에 정립된 개념입니다.
그리고 MVVM은 MVP가 나온지 10년 뒤인 2000년대에 MVP를 보완하기 위해 생긴 개념입니다.
MVVM의 단점을 해결하기 위해 나온게 MVP라고 말씀하시면 어떻게 이해해야 할지 난감하네요.
3. 사소한 문제이긴하지만.. 예제로 남겨주신 코드에서 onAttach()에서 mCallback 을 셋팅하게되면 OnViewRequestListener가 무조건 Activity여야하는 한계가 있습니다. Activity가 아닌 다른 class가 OnViewRequestListener 를 구현하는 것이 불가능한 구조니 따로 setOnViewRequestListener 메소드가 필요할 것 같습니다.
4. 말씀하신 것처럼 안드로이드에서는 adapter를 사용하는 것이 필수적이기 때문에 이에 대한 구조도 생각되었는데요, 제가 발표한 구조에서 adapter는 어떤식으로 구현되었을까요? 한번 생각해보시라는 뜻으로 마지막은 질문으로 남겨둡니다.
네~ 로쓰님의 성실한 응대에 저도 감사드립니다.
1. 이 부분이 앞서 로쓰님과 했던 이야기의 키포인트입니다.
앞서 제가 했던 얘기와 MVVM, MVP 패턴 모두가 Model 과 View를 어떻게 추상화 할 것인가에 주안을 두고 있기 때문입니다. 그런 바탕 하에서 제가 봤을 때는, View의 상태 갱신을 Model의 update와 연결하는 의도 자체는 좋으나, 추상화가 되어 있지 않아 data가 view에 바로 바인딩 되는 형태기 때문에 커플링이 있는 것이라고 생각하는 것 입니다.
제가 안드로이드에서 adapter가 view를 반환하는 형식을 취하는 이유에 대해서 질의 했었는데,
그 이유가 여기 있습니다. update를 데이터 형식으로 취하는 것은 model의 데이터와 view 간에 공통된 data를 취급한다는 것은 커플링이 생긴 것이라고 말할 수 밖에 없습니다. 답변 중에 말씀하신 model이 viewmodel같은 역할이라는 표현하셨는데 전 전혀 공감할 수 없습니다. viewmodel은 제가 탐탁치 않아 하는 구조이긴 하지만 model 의 data를 view로 추상화 해야 하는 것을 잘 이해하고 있는데 올려주신 문서에서는 전혀 보이지 않는걸요. 미뤄 짐작해보건데 4번 답변을 보니 이에 대한 이해가 부족하신 것 같습니다. 그렇지 않다면 문서에서 다루지 않았을리 없겠다는 생각이 드네요.
2. MVVM 은 MVP의 단점을 해결하기 위해서 나온 것이 아닙니다. 같은 목표인 view의 추상화를 통한 model과 view간의 커플링 해소를 위해서 나온 것입니다. 그렇기 때문에 10년 뒤에 나왔다고 해서 더 우성(?)의 것이라고 단정할 수 없습니다. MVP와 MVVM을 비유해서 표현하자면, MVP는 아이폰이고 MVVM은 안드로이드 입니다. MVP는 MVC를 혁신하고 나온 것이과, MVVM은 MVP의 연장선 상에 있는 구현 방법론이라고 생각하면 됩니다. 그리고 제가 MVVM를 마음에 들어하지 않는 이유는 viewmodel이라는 것이 재사용성이 떨어지기 때문이라고 말씀드렸습니다. 이는 호불호가 있을 수 있으므로 더 이야기 하지 않는게 좋을 것 같습니다.
3. OnAttach 를 통해 Fragment에서 기본적으로 Activity를 인자로 넘겨주기 때문에 이런 식을 취한 것입니다.
또한 Activity여야하는 한계가 있다고 말씀하셨는데 이는 틀렸습니다. OnViewRequestListener는 통신을 위한 interface입니다. setOnViewRequestListener 메소드가 필요하다는 말씀은 넘기겠습니다.
4. adapter를 제가 비유한 이유를 오해하시는 것 같습니다.
저는 계속 view가 추상화되지 않아 model 과의 커플링이 생기는게 문제라고 말씀드렸습니다.
이를 인지시키지 못한 것은 제가 잘못한 것이므로 넘어가도록 하겠습니다.
MVVM이 MVP의 단점을 보완하기 위해 나왔다는 말은 다시 생각해보니 잘못됐네요. 말씀하신대로 단점 보완이 아니라 비슷한 문제를 해결하기 위한 새로운 형태라고 이해하는 편이 맞는 듯 합니다. 적어도 MVP가 MVVM의 단점을 보완하는건 아니라는 얘기를 하고 싶었습니다 (말씀하시는 MVVM의 단점이 재사용성 저하라는 점도 전혀 이해가 안됩니다만..) 그리고 제가 이해하는 view-model은 'view의 data를 추상화한 model'인데 'model의 data를 view로 추상화한다'는 말씀은 완전히 제가 생각한 것과는 반대네요. 일단 data를 view로 추상화한다는 개념도 잘 이해가 안되구요.
'view가 추상화되지 않아 model과의 커플링이 생겨 재사용성이 떨어진다'는 것이 핵심인 것 같은데 구체적인 예를 들어주실 수 있을까요? 제가 발표한 구조는 재사용 역시 고려하여 만들어진건데 그렇지 않다고 말씀하시니 적절한 예를 들어주세요. [지금 구조는 이러이러한 상황을 구현하기 위해서는 기존의 코드를 재사용하지 못하는데, MVP 구조에서는 재사용이 가능해진다] 이렇게요. 아무래도 원론적인 이야기보다는 구체적인 예나 코드가 쓰여있는게 이해가 빨라서요..
(그리고 3번은 통신을 위한 interface을 onAttach에서 받아서 문제가 된다는 뜻입니다. setOnViewRequestListener가 없이 activity가 아닌 다른 OnViewRequestListener 구현체를 mCallback에 setting 할 수 있나요? 또한 OnViewRequestListener를 구현하지 않은 activity는 사용이 불가능해지고. )
아, 그리고 자료에 adapter쪽 내용이 없는건 시간이 부족해서.. -_-;;
MVVM 의 재사용성 저하는 어디까지나 제 생각이므로 무시하셔도 되는 부분입니다.
굳이 설명을 하자면 MVVM에서는 View의 표현을 android로 말하자면 layout을 inflate하는 형식과 유사하게 취하는데, 저는 이와 같은 방법이 재사용성이 있다고 생각하지 않는 입장이기 때문입니다. 객체지향 까는 사람들이 재사용성에 대해서 의문을 제기하는 것과 유사한 입장이라고 생각하시면 됩니다.
저는 계속 view가 추상화되지 않아서 생기는 문제를 얘기했는데 이를 주지시켜드리지 못했으니 죄송하게 생각합니다.
예를 들어 adapter에서는 getView()에서 model의 데이터를 바인딩한 View를 리턴해주는데, 바로 이 과정을 통해 ListView와 같은 AdapterView 하위 컴포넌트들은 Model의 스키마나 데이터 변동에 대해 어떤 의존성도 없이 자기 역할을 잘 수행할 수 있게 됩니다.
Request 요청으로 변동사항이 전달될때 이를 추상화한 View로서 parentView에 추가되는 형식이 아니라 View의 구성요소에 바로 data가 set되는 형태라면 해당 view는 model의 변동사항에서 자유로울 수 없기 때문에 커플링이 생긴다고 말씀드렸습니다. 이제와 생각해보면 시간이 적어서 생략되었을 수도 있다는 생각은 들지만서도, 올려주신 문서에서 이 부분이 빠져서는 안된다는 생각이 들었기에 지적을 할 수 밖에 없었습니다.
마플님이 말하신
"model의 업데이트 내역을 view 에서 직접 갱신하는 방법은 mvc 초창기 패턴에서 사용하던 방법입니다.
하나의 model에 하나의 view. 이것을 커플링이 생긴다고 말하는 것입니다.
왜냐면 model - view이 각자 독립적으로 나뉠 수 없기 때문입니다.
1) model 을 떼어서 다른 view에 붙히지도 못하고,
2) view를 떼어서 다른 model을 붙히니지도 못합니다. "
의 내용이 이해가 안되네요.
로쓰님의 코드에서 update를 여러개의 view에 날려주면
1)이 안될이유가 없는데요?;;;
2) 보통 view를 재사용하는 경우는 잘게 쪼개서 각 디자인 요소를 여러개의 view에서 사용하는 형태 아닌가요?
view를 통채로 재사용할일은 많지 않고, 재사용되는 view부분만 떼면 되니 2)가 필요한 경우가 있을까 모르겠네요.
감사합니다. 어느정도 이해는 했는데 자세히 보고 싶었습니다.
원래 otto 를 적용해 보려고 하고 있었는데 로스님의 mvc도 같이 적용해 보려구요.
otto 에 대한 확신은 없어서 쓰다 보면 리스너로 돌아올지 otto를 계속 쓸지는 모르겠네요 ㅋㅋ
수고하세요~
궁금해서 여쭙니다.
Context를 인자로 받는데 findViewById() 를 사용하는 것은..
BaseLayout 에서 context를 강제로 activity로 캐스팅 해서 findViewById()를 구현한 것인가요?

로쓰님 좋은 자료감사합니다. 저도 MVC 패턴에 대해 관심이 많아 비슷한 구조로 사용하고 있습니다.
저 같은 경우에도 Activity 를 Controller 로 사용하고, View를 class로 만들어 inflate 하여 사용 하고 있습니다.
view에 컴포넌트들에 event 가 동작용 발생하면 interface 로 activity에 알려주도록 했습니다.
또한 activity인Controller 에서 웹,DB정보를 받아와 model 를 업데이트해주고 완료되면 interface가 구현된 view class 를 Controller 호출하게 되도록 했습니다. 저는 이러한 방법을 사용하고 있습니다.
PPT 자료에 Model class로 layout을 전달하는 구조를 말씀하셨는데요. layout을 전달받은 Model class가 어떻게? view 업데이트한다는지 이해가 잘 되지 않아서요. 소스가 많이 생략되었네요.
저같은 경우 interface를 2개사용해서 좋은 방법인지는 모르겠으나 만드신 것을 한번 적용하고 사용해보고 싶은 마음에 글을 남겼습니다.
넵, 큰 그림만 설명하려다보니 BaseLayout, BaseModel쪽이 생략되어있어서 발표 자료만으로는 자세하게 파악하기 어려운 점이 있습니다 ㅠㅠ; 우선 ModelListener<T extends BaseModel> 라는 인터페이스가 있고 model의 update를 감지하고자 하는 클래스는 이를 구현[onUpdated(T model);]합니다. model 에서 update() 를 호출하면 해당 model에 등록된 model listener의 list를 돌면서 listener.onUpdated(this); 를 호출해주는 방식입니다.
아, 그리고 퐁퐁님께서 이러한 방식의 단점을 한가지 지적해주셨는데, model 자체를 넘기면 view쪽에서도 model에 상태를 변경할 수 있는 메소드를 호출할 수 있는 위험이 있다는 점이었습니다. (예: fetch()) 이를 해결하기 위해서는 onUpdated(..) 메소드의 parameter를 해당 model이 아닌 해당 model에서 data만 가져오는 interface로 하면 됩니다. interface를 하나 더 선언해야한다는 번거로움이 trade-off 입니다.
참고가 되길 바랍니다~
만일 열명의 소프트웨어 개발자를 한 방에 두고 Model – View – Controller 패턴에 대해 토론하라고 한다면, 아마 토론이 끝났을 때 서로 다른 열두개의 의견이 나올 것이다.
Using MVC to Unit Test WPF Applications – CodeProject - 조쉬 스미스 (Josh Smith)
발표 잘들었습니다. 저희팀 프로젝트에도 적용해야 겠네요.