2. Who is this guy?
• 경력 19년차 SW 엔지니어
• 폐업 전문가: 미국에서 한 번, 한국에서 두 번 스타트업 창업 경험
• IBM iX Japan에서 Android/iOS 담당 Senior Specialist로 근무 중
• Droid Knights 2018 - 내가 안드로이드 엔지니어가 되었을 때 아무도 알려주
지 않은 것들
3. IBM iX Japan - We’re building business by design
• Design Thinking, Mobile Interaction, Digital
Strategy, …
• 좌우지간 새롭고 재밌고, 유저 지향적인 신 기술/사업을
구현해주는 컨설팅 조직
• 주요 고객: 일본 10대 그룹, 은행사, 양대 항공사
4. 목차
• Part I. 서론
1. 주의사항
2. 선수 지식: 안드로이드 아키텍처 4분만에 벼락치기 - MVC, Clean Architecture, Testability, Reusability
• Part II. Non-MVC 아키텍처 총정리
1. Non-MVC 아키텍처의 기본 설계 전략
2. MVP
3. MVVM
4. Redux
• Part III. New Trend in 2019
1. Rx vs non-Rx
2. Multi-module
• Part IV. Best Practices
6. 1. 시작하기 전 주의사항
• 지나친 기대 금지 - 45분 강의에 너무 많은 걸 기대하시면 매우 곤란..
• 암기 금지 - 원리를 이해하는 것이 더 중요
• 무턱대고 납득하기 금지 - 왜 저런 얘기를 하는가를 이해하고, 판단은 여러분 스
스로
• 사진 촬영 무쓸모: 슬라이드는 아래 링크에서 다운 받으실 수 있습니다
• https://www.slideshare.net/saryongkang/droid-knights-2019-
largescale-app-android-architecture
8. 왜, 특별히 안드로이드 아키텍처 설계/구현에 있어서, 비판적 사고
가 중요한가?
• General Practice는 많은데 Best Practice는 찾아보기 어렵다
9. 왜, 특별히 안드로이드 아키텍처 설계/구현에 있어서, 비판적 사고
가 중요한가?
• General Practice는 많은데 Best Practice는 찾아보기 어렵다
• 심지어 G사가 만든 샘플조차 갑론을박의 여지가..
10. 왜, 특별히 안드로이드 아키텍처 설계/구현에 있어서, 비판적 사고
가 중요한가?
• General Practice는 많은데 Best Practice는 찾아보기 어렵다
• 심지어 G사가 만든 샘플조차 갑론을박의 여지가..
• 아키텍처 패턴의 겉을 맛보게 해주는 글은 많지만 핵심 철학에 대해 얘기하
는 글은 매우 희박하다
11. 왜, 특별히 안드로이드 아키텍처 설계/구현에 있어서, 비판적 사고
가 중요한가?
• General Practice는 많은데 Best Practice는 찾아보기 어렵다
• 심지어 G사가 만든 샘플조차 갑론을박의 여지가..
• 아키텍처 패턴의 겉을 맛보게 해주는 글은 많지만 핵심 철학에 대해 얘기하
는 글은 매우 희박하다
• 같은 이름을 가진 아키텍처 패턴도 각자 상황에 따라 매우 이질적으로 구현될
수 있다
14. 2. 선수지식
• Architecture의 기본 중의 기본: MVC란 무엇인가?
• Model = State + 비즈니스 로직 + 데이터 저장소
• View = UI (보통 declarative하게 구현됨)
15. 2. 선수지식
• Architecture의 기본 중의 기본: MVC란 무엇인가?
• Model = State + 비즈니스 로직 + 데이터 저장소
• View = UI (보통 declarative하게 구현됨)
• Controller = Flow 로직 + [비즈니스 로직] +
[State]
16. (30초만에 이해하는) Clean Architecture의 기본
출처: https://qiita.com/koutalou/items/07a4f9cf51a2d13e4cdc
17. (30초만에 이해하는) Clean Architecture의 기본
• MVC에서 Model을 분해 → Domain Layer + Data Layer
출처: https://qiita.com/koutalou/items/07a4f9cf51a2d13e4cdc
18. (30초만에 이해하는) Clean Architecture의 기본
• MVC에서 Model을 분해 → Domain Layer + Data Layer
• MVC에서 비즈니스 로직을 분해 → Presentational Logic (in Presenter) / Domain Logic (in UseCase)
출처: https://qiita.com/koutalou/items/07a4f9cf51a2d13e4cdc
19. 더 자세한 건…
Clean Architecture 적용에 대한 철학과 원칙에 내한 내용
을 들으실 수 있습니다.
20. • 그런데, 아주 작은 부분처럼 보이는 Presentation Logic에 주로 주목하는 이유는?
21. • 그런데, 아주 작은 부분처럼 보이는 Presentation Logic에 주로 주목하는 이유는?
• 도메인 로직이 차지하는 부분이 (상대적으로) 적음 - 대부분의 앱의 경우, 가장 복
잡한 비즈니스 로직은 백엔드 서버에 존재함
22. • 그런데, 아주 작은 부분처럼 보이는 Presentation Logic에 주로 주목하는 이유는?
• 도메인 로직이 차지하는 부분이 (상대적으로) 적음 - 대부분의 앱의 경우, 가장 복
잡한 비즈니스 로직은 백엔드 서버에 존재함
• 복잡하고 (대부분) 비동기적인 상태의 관리, 그리고 라이프 사이클 연동이 훨씬 복
잡하고 중요한 문제
23. • 그런데, 아주 작은 부분처럼 보이는 Presentation Logic에 주로 주목하는 이유는?
• 도메인 로직이 차지하는 부분이 (상대적으로) 적음 - 대부분의 앱의 경우, 가장 복
잡한 비즈니스 로직은 백엔드 서버에 존재함
• 복잡하고 (대부분) 비동기적인 상태의 관리, 그리고 라이프 사이클 연동이 훨씬 복
잡하고 중요한 문제
• 바꿔 얘기하면 모바일 아키텍처에서 가장 큰 난제는 tight coupling between
ui and state changes 를 어떻게 해소할 것인가의 문제
25. 아키텍처 관점에서 아래 두 개념은 왜 그리 중요한가?
• 테스트 가능성
• 재사용성
• “난 유닛테스트 따윈 필요 없어!”라고 차마 말하기 전에..
• Testablility가 떨어지는 코드란 무엇인가 생각해 봅시다 → Tightly-coupled
• 테스트 가능성이 떨어지는 코드는 중요 라이브러리 변경, 기반 구조 변경에 대응하기가 어렵다
26. 아키텍처 관점에서 아래 두 개념은 왜 그리 중요한가?
• 테스트 가능성
• 재사용성
• “난 유닛테스트 따윈 필요 없어!”라고 차마 말하기 전에..
• Testablility가 떨어지는 코드란 무엇인가 생각해 봅시다 → Tightly-coupled
• 테스트 가능성이 떨어지는 코드는 중요 라이브러리 변경, 기반 구조 변경에 대응하기가 어렵다
• “현실적으로 한 번 짠 코드를 클래스/Fragment 단위로 재사용 가능한 경우가 얼마나 돼?”라고 차마
말하기 전에..
• Re-usability란 게 애초에 무엇이었나 생각해봅시다 → 적절한 모듈화, 캡슐화
• 재사용성이 떨어지는 구조는 기능 변경 / 확장성이 떨어진다 ≒ 코드 가독성이 떨어진다
34. Fat Activity/Fragment - 만악의 근원
여기서 작년의 복습: 왜 Android MVC는 나쁜가?
• Activity가 할 수 있는 일
• 라이프사이클 관리, 모든 UI 로직 - 레이아웃 생성, 렌더링, 애니메이션, Shared
Preferences, 파일 IO, 화면 전환, 권한 설정, Loader, 그 외에 수 백 가지
35. Fat Activity/Fragment - 만악의 근원
여기서 작년의 복습: 왜 Android MVC는 나쁜가?
• Activity가 할 수 있는 일
• 라이프사이클 관리, 모든 UI 로직 - 레이아웃 생성, 렌더링, 애니메이션, Shared
Preferences, 파일 IO, 화면 전환, 권한 설정, Loader, 그 외에 수 백 가지
• 기본 Android Architecture에서 Activity란?
• Controller + View + alpha + omega = god
• Fragment도 그냥 작은 신일뿐..
😱
37. Non-MVC의 설계 전략
• Non-MVC에서 Activity는 무엇인가? → Activity는 컨트롤러! (그것도 아주 제한적인..)
38. Non-MVC의 설계 전략
• Non-MVC에서 Activity는 무엇인가? → Activity는 컨트롤러! (그것도 아주 제한적인..)
• MVP의 (궁극적) 접근법
39. Non-MVC의 설계 전략
• Non-MVC에서 Activity는 무엇인가? → Activity는 컨트롤러! (그것도 아주 제한적인..)
• MVP의 (궁극적) 접근법
• Activity에서 View와 Controller의 역할을 최대한 빼앗아 View와 Presenter로 넘김.
Activity는 순수 flow 관리 역할 위주
40. Non-MVC의 설계 전략
• Non-MVC에서 Activity는 무엇인가? → Activity는 컨트롤러! (그것도 아주 제한적인..)
• MVP의 (궁극적) 접근법
• Activity에서 View와 Controller의 역할을 최대한 빼앗아 View와 Presenter로 넘김.
Activity는 순수 flow 관리 역할 위주
• MVVM/Redux 등의 접근법
41. Non-MVC의 설계 전략
• Non-MVC에서 Activity는 무엇인가? → Activity는 컨트롤러! (그것도 아주 제한적인..)
• MVP의 (궁극적) 접근법
• Activity에서 View와 Controller의 역할을 최대한 빼앗아 View와 Presenter로 넘김.
Activity는 순수 flow 관리 역할 위주
• MVVM/Redux 등의 접근법
• 마찬가지로 Activity는 최대한 일부 context 의존 기능만 하도록..
View logic은 최대한 data binding으로 구현
42. Non-MVC의 설계 전략
• Non-MVC에서 Activity는 무엇인가? → Activity는 컨트롤러! (그것도 아주 제한적인..)
• MVP의 (궁극적) 접근법
• Activity에서 View와 Controller의 역할을 최대한 빼앗아 View와 Presenter로 넘김.
Activity는 순수 flow 관리 역할 위주
• MVVM/Redux 등의 접근법
• 마찬가지로 Activity는 최대한 일부 context 의존 기능만 하도록..
View logic은 최대한 data binding으로 구현
• 여기서 의문: 왜 구글은 (배우기도 어렵고 Rx가 없으면 제대로 구현도 안 되는) ViewModel만을 AAC(Android
Architecture Component)에서 지원하는가?
43. Non-MVC의 설계 전략
• Non-MVC에서 Activity는 무엇인가? → Activity는 컨트롤러! (그것도 아주 제한적인..)
• MVP의 (궁극적) 접근법
• Activity에서 View와 Controller의 역할을 최대한 빼앗아 View와 Presenter로 넘김.
Activity는 순수 flow 관리 역할 위주
• MVVM/Redux 등의 접근법
• 마찬가지로 Activity는 최대한 일부 context 의존 기능만 하도록..
View logic은 최대한 data binding으로 구현
• 여기서 의문: 왜 구글은 (배우기도 어렵고 Rx가 없으면 제대로 구현도 안 되는) ViewModel만을 AAC(Android
Architecture Component)에서 지원하는가?
• 기존 Activity/Fragment의 형태를 깨뜨리지 않으면서 재사용성/생산성 높은 아키텍처를 구현 가능하기 때문*
44. Non-MVC의 설계 전략
• Non-MVC에서 Activity는 무엇인가? → Activity는 컨트롤러! (그것도 아주 제한적인..)
• MVP의 (궁극적) 접근법
• Activity에서 View와 Controller의 역할을 최대한 빼앗아 View와 Presenter로 넘김.
Activity는 순수 flow 관리 역할 위주
• MVVM/Redux 등의 접근법
• 마찬가지로 Activity는 최대한 일부 context 의존 기능만 하도록..
View logic은 최대한 data binding으로 구현
• 여기서 의문: 왜 구글은 (배우기도 어렵고 Rx가 없으면 제대로 구현도 안 되는) ViewModel만을 AAC(Android
Architecture Component)에서 지원하는가?
• 기존 Activity/Fragment의 형태를 깨뜨리지 않으면서 재사용성/생산성 높은 아키텍처를 구현 가능하기 때문*
※주의: 어디까지나 추측입니다.
47. Model-View-Presenter?
• 1990년대 초 IBM에서 최초로 구현
• 2006년 Martin Fowler의 소개로 널리 알려짐
참조: GUI Architectures
https://martinfowler.com/eaaDev/uiArchs.html
48. Model-View-Presenter?
• 1990년대 초 IBM에서 최초로 구현
• 2006년 Martin Fowler의 소개로 널리 알려짐
참조: GUI Architectures
https://martinfowler.com/eaaDev/uiArchs.html
• 사실 안드로이드 초기부터 구전으로 전해져 왔던 아키텍처
49. Model-View-Presenter?
• 1990년대 초 IBM에서 최초로 구현
• 2006년 Martin Fowler의 소개로 널리 알려짐
참조: GUI Architectures
https://martinfowler.com/eaaDev/uiArchs.html
• 사실 안드로이드 초기부터 구전으로 전해져 왔던 아키텍처
• 근데 이미 한물 간 구조 아닌가? 아직도 쓰나요?
50. Model-View-Presenter?
• 1990년대 초 IBM에서 최초로 구현
• 2006년 Martin Fowler의 소개로 널리 알려짐
참조: GUI Architectures
https://martinfowler.com/eaaDev/uiArchs.html
• 사실 안드로이드 초기부터 구전으로 전해져 왔던 아키텍처
• 근데 이미 한물 간 구조 아닌가? 아직도 쓰나요?
• 예, 아직도 많이 씁니다. 예를 들면, 같은 회사가요.
51. Model-View-Presenter?
• 1990년대 초 IBM에서 최초로 구현
• 2006년 Martin Fowler의 소개로 널리 알려짐
참조: GUI Architectures
https://martinfowler.com/eaaDev/uiArchs.html
• 사실 안드로이드 초기부터 구전으로 전해져 왔던 아키텍처
• 근데 이미 한물 간 구조 아닌가? 아직도 쓰나요?
• 예, 아직도 많이 씁니다. 예를 들면, 같은 회사가요.
54. Presenter
View
Model
사용자 입력을 통보
데이터 갱신 요청
데이터 변경 통보
뷰 변경 요청
• 모든 Presentational Business Logic을 Presenter에서 처리
• View는 Presenter의 요청에 따라 수동적으로 UI를 처리함
56. Google Architecture Blueprint로 분석해보는 MVP의 핵심
• https://github.com/googlesamples/android-architecture/tree/todo-
mvp/
• Contractor Pattern
57. Google Architecture Blueprint로 분석해보는 MVP의 핵심
• https://github.com/googlesamples/android-architecture/tree/todo-
mvp/
• Contractor Pattern
• View, Presenter는 interface 형태로 서로를 참조
58. Google Architecture Blueprint로 분석해보는 MVP의 핵심
• https://github.com/googlesamples/android-architecture/tree/todo-
mvp/
• Contractor Pattern
• View, Presenter는 interface 형태로 서로를 참조
• V와 P의 구현이 섞이거나, P가 플랫폼 의존적인 API를 직접 접근할 여지를
원천 봉쇄
59. /**
* This specifies the contract between the view and the presenter.
*/
public interface TasksContract {
interface View extends BaseView<Presenter> {
// ...
void setLoadingIndicator(boolean active);
void showTasks(List<Task> tasks);
void showAddTask();
void showTaskDetailsUi(String taskId);
}
interface Presenter extends BasePresenter {
// ...
void loadTasks(boolean forceUpdate);
void addNewTask();
void openTaskDetails(@NonNull Task requestedTask);
void completeTask(@NonNull Task completedTask);
void activateTask(@NonNull Task activeTask);
void clearCompletedTasks();
void setFiltering(TasksFilterType requestType);
TasksFilterType getFiltering();
}
}
60. /**
* This specifies the contract between the view and the presenter.
*/
public interface TasksContract {
interface View extends BaseView<Presenter> {
// ...
void setLoadingIndicator(boolean active);
void showTasks(List<Task> tasks);
void showAddTask();
void showTaskDetailsUi(String taskId);
}
interface Presenter extends BasePresenter {
// ...
void loadTasks(boolean forceUpdate);
void addNewTask();
void openTaskDetails(@NonNull Task requestedTask);
void completeTask(@NonNull Task completedTask);
void activateTask(@NonNull Task activeTask);
void clearCompletedTasks();
void setFiltering(TasksFilterType requestType);
TasksFilterType getFiltering();
}
}
• TODO 앱의 각 작업(task)를 보여주고 추가
하는 화면을 위한 TasksContractor
interface 정의
61. /**
* This specifies the contract between the view and the presenter.
*/
public interface TasksContract {
interface View extends BaseView<Presenter> {
// ...
void setLoadingIndicator(boolean active);
void showTasks(List<Task> tasks);
void showAddTask();
void showTaskDetailsUi(String taskId);
}
interface Presenter extends BasePresenter {
// ...
void loadTasks(boolean forceUpdate);
void addNewTask();
void openTaskDetails(@NonNull Task requestedTask);
void completeTask(@NonNull Task completedTask);
void activateTask(@NonNull Task activeTask);
void clearCompletedTasks();
void setFiltering(TasksFilterType requestType);
TasksFilterType getFiltering();
}
}
• TODO 앱의 각 작업(task)를 보여주고 추가
하는 화면을 위한 TasksContractor
interface 정의
• View-Presenter 간의 직접 연결을 끊음
62. /**
* This specifies the contract between the view and the presenter.
*/
public interface TasksContract {
interface View extends BaseView<Presenter> {
// ...
void setLoadingIndicator(boolean active);
void showTasks(List<Task> tasks);
void showAddTask();
void showTaskDetailsUi(String taskId);
}
interface Presenter extends BasePresenter {
// ...
void loadTasks(boolean forceUpdate);
void addNewTask();
void openTaskDetails(@NonNull Task requestedTask);
void completeTask(@NonNull Task completedTask);
void activateTask(@NonNull Task activeTask);
void clearCompletedTasks();
void setFiltering(TasksFilterType requestType);
TasksFilterType getFiltering();
}
}
• TODO 앱의 각 작업(task)를 보여주고 추가
하는 화면을 위한 TasksContractor
interface 정의
• View-Presenter 간의 직접 연결을 끊음
• Good?!
63. • 다시 한 번 자세히 봅시다.
🤔/**
* This specifies the contract between the view and the presenter.
*/
public interface TasksContract {
interface View extends BaseView<Presenter> {
// ...
void setLoadingIndicator(boolean active);
void showTasks(List<Task> tasks);
void showAddTask();
void showTaskDetailsUi(String taskId);
}
interface Presenter extends BasePresenter {
// ...
void loadTasks(boolean forceUpdate);
void addNewTask();
void openTaskDetails(@NonNull Task requestedTask);
void completeTask(@NonNull Task completedTask);
void activateTask(@NonNull Task activeTask);
void clearCompletedTasks();
void setFiltering(TasksFilterType requestType);
TasksFilterType getFiltering();
}
}
64. • 다시 한 번 자세히 봅시다.
• 어라? Presenter의 메소드들을 보니 데
이터를 넘겨주는 것들이 있네?
🤔/**
* This specifies the contract between the view and the presenter.
*/
public interface TasksContract {
interface View extends BaseView<Presenter> {
// ...
void setLoadingIndicator(boolean active);
void showTasks(List<Task> tasks);
void showAddTask();
void showTaskDetailsUi(String taskId);
}
interface Presenter extends BasePresenter {
// ...
void loadTasks(boolean forceUpdate);
void addNewTask();
void openTaskDetails(@NonNull Task requestedTask);
void completeTask(@NonNull Task completedTask);
void activateTask(@NonNull Task activeTask);
void clearCompletedTasks();
void setFiltering(TasksFilterType requestType);
TasksFilterType getFiltering();
}
}
65. • 다시 한 번 자세히 봅시다.
• 어라? Presenter의 메소드들을 보니 데
이터를 넘겨주는 것들이 있네?
• 그럼 상태 정보를 View가 갖는다는 얘
기?!
🤔/**
* This specifies the contract between the view and the presenter.
*/
public interface TasksContract {
interface View extends BaseView<Presenter> {
// ...
void setLoadingIndicator(boolean active);
void showTasks(List<Task> tasks);
void showAddTask();
void showTaskDetailsUi(String taskId);
}
interface Presenter extends BasePresenter {
// ...
void loadTasks(boolean forceUpdate);
void addNewTask();
void openTaskDetails(@NonNull Task requestedTask);
void completeTask(@NonNull Task completedTask);
void activateTask(@NonNull Task activeTask);
void clearCompletedTasks();
void setFiltering(TasksFilterType requestType);
TasksFilterType getFiltering();
}
}
66. /**
* Display a grid of {@link Task}s. User can choose to view all, active or completed tasks.
*/
public class TasksFragment extends Fragment implements TasksContract.View {
private TasksContract.Presenter mPresenter;
//...
TaskItemListener mItemListener = new TaskItemListener() {
@Override
public void onTaskClick(Task clickedTask) {
mPresenter.openTaskDetails(clickedTask);
}
@Override
public void onCompleteTaskClick(Task completedTask) {
mPresenter.completeTask(completedTask);
}
@Override
public void onActivateTaskClick(Task activatedTask) {
mPresenter.activateTask(activatedTask);
}
};
//...
private static class TasksAdapter extends BaseAdapter {
private List<Task> mTasks;
private TaskItemListener mItemListener;
public TasksAdapter(List<Task> tasks, TaskItemListener itemListener) {
setList(tasks);
mItemListener = itemListener;
}
}
}
• Bingo! - 각 상태에 따른 태스크를 view가
판단해서 보내주고 있음
67. /**
* Display a grid of {@link Task}s. User can choose to view all, active or completed tasks.
*/
public class TasksFragment extends Fragment implements TasksContract.View {
private TasksContract.Presenter mPresenter;
//...
TaskItemListener mItemListener = new TaskItemListener() {
@Override
public void onTaskClick(Task clickedTask) {
mPresenter.openTaskDetails(clickedTask);
}
@Override
public void onCompleteTaskClick(Task completedTask) {
mPresenter.completeTask(completedTask);
}
@Override
public void onActivateTaskClick(Task activatedTask) {
mPresenter.activateTask(activatedTask);
}
};
//...
private static class TasksAdapter extends BaseAdapter {
private List<Task> mTasks;
private TaskItemListener mItemListener;
public TasksAdapter(List<Task> tasks, TaskItemListener itemListener) {
setList(tasks);
mItemListener = itemListener;
}
}
}
• Bingo! - 각 상태에 따른 태스크를 view가
판단해서 보내주고 있음
• 그 item listener를 호출하는 부분을 거
슬러 올라갔더니..
68. /**
* Display a grid of {@link Task}s. User can choose to view all, active or completed tasks.
*/
public class TasksFragment extends Fragment implements TasksContract.View {
private TasksContract.Presenter mPresenter;
//...
TaskItemListener mItemListener = new TaskItemListener() {
@Override
public void onTaskClick(Task clickedTask) {
mPresenter.openTaskDetails(clickedTask);
}
@Override
public void onCompleteTaskClick(Task completedTask) {
mPresenter.completeTask(completedTask);
}
@Override
public void onActivateTaskClick(Task activatedTask) {
mPresenter.activateTask(activatedTask);
}
};
//...
private static class TasksAdapter extends BaseAdapter {
private List<Task> mTasks;
private TaskItemListener mItemListener;
public TasksAdapter(List<Task> tasks, TaskItemListener itemListener) {
setList(tasks);
mItemListener = itemListener;
}
}
}
• Bingo! - 각 상태에 따른 태스크를 view가
판단해서 보내주고 있음
• 그 item listener를 호출하는 부분을 거
슬러 올라갔더니..
• Task 객체의 상태를 View가 갖고 있고,
어떤 presenter 함수가 호출되어야 할 지
를 View 결정한다?!
69. • 헐?! 이건 MVP가 아니잖아!
• View가 passive하지 않다!
• 애초에 Activity가 Controller, Fragment
가 View의 역할을 하도록 만든 것 자체가, 일
정 규모 이상의 앱에서는 문제가 될 수 있는
설계
😒
70. • 이런 오해 때문에 Martin Fowler는 Presenter라는 이름을 폐기하고
Supervising Controller라고 부를 것을 제안. (그리고 View는 Passive View
로 부름)
• Ref: Retirement note for Model View Presenter Pattern
https://martinfowler.com/eaaDev/ModelViewPresenter.html .
71. 더 자세한 건…
왜 View는 Passive 해야하는가, 그리고 바람직한 View와
Presenter의 역할은 무엇인가?
속시원하게 알려드립니다… (아마도)
72. • 타 아키텍처에도 적용되는 얘기만 하자면,
P
V
M
Home MVP*
P
V
M
Banner MVP
P
V
M
Original List MVP
P
V
M
Preview List MVP
※주의: 어디까지나 추측입니다.
73. • 타 아키텍처에도 적용되는 얘기만 하자면,
• Android의 경우, nested 구조로 갈 수밖에
없음.
P
V
M
Home MVP*
P
V
M
Banner MVP
P
V
M
Original List MVP
P
V
M
Preview List MVP
※주의: 어디까지나 추측입니다.
74. • 타 아키텍처에도 적용되는 얘기만 하자면,
• Android의 경우, nested 구조로 갈 수밖에
없음.
• 그럼 구글은 왜 아키텍처 청사진을 이렇게 짰을
까?
P
V
M
Home MVP*
P
V
M
Banner MVP
P
V
M
Original List MVP
P
V
M
Preview List MVP
※주의: 어디까지나 추측입니다.
75. • 타 아키텍처에도 적용되는 얘기만 하자면,
• Android의 경우, nested 구조로 갈 수밖에
없음.
• 그럼 구글은 왜 아키텍처 청사진을 이렇게 짰을
까?
• 주의점: 특정 회사의 공식 샘플은 어떤 의도를
가지고 만들어 질 수도 있다는 점을 미리 염두
에 둘 필요가 있음
P
V
M
Home MVP*
P
V
M
Banner MVP
P
V
M
Original List MVP
P
V
M
Preview List MVP
※주의: 어디까지나 추측입니다.
82. • ViewModel
• 그 자체로 독립적인 시스템
• View에 접근할 수도 없고, 존재 자체도 모름
• AAC의 ViewModel
• LifeCycle 지원 기능 이외에 특별한 기능 없음 (차기 버전에서도 SavedState가 추가되는 정
도)
• 직접 Life Cycle 처리를 구현하고 AAC-ViewModel을 사용하지 않아도 무방
• Pro tip: MVVM 구조 설명에서 AAC 그림부터 나오는 글이라면 믿고 거르시면 됩니다
84. Redux - 출발점
• Part I.에서 얘기했듯, 모바일 앱에서 가장 큰 난제 중의 하나는 복잡한 상태
들을 어떻게 깔끔하고 유지보수성 높게 구현할 것인가의 문제
85. Redux - 출발점
• Part I.에서 얘기했듯, 모바일 앱에서 가장 큰 난제 중의 하나는 복잡한 상태
들을 어떻게 깔끔하고 유지보수성 높게 구현할 것인가의 문제
• 발상을 바꿔서 앱 전체의 이벤트를 State Machine으로 처리한다면 어떨까?
86. Redux - 출발점
• Part I.에서 얘기했듯, 모바일 앱에서 가장 큰 난제 중의 하나는 복잡한 상태
들을 어떻게 깔끔하고 유지보수성 높게 구현할 것인가의 문제
• 발상을 바꿔서 앱 전체의 이벤트를 State Machine으로 처리한다면 어떨까?
• Life Cycle 처리로 골머리 앓을 일이 급격히 줄어듦
87. Redux - 출발점
• Part I.에서 얘기했듯, 모바일 앱에서 가장 큰 난제 중의 하나는 복잡한 상태
들을 어떻게 깔끔하고 유지보수성 높게 구현할 것인가의 문제
• 발상을 바꿔서 앱 전체의 이벤트를 State Machine으로 처리한다면 어떨까?
• Life Cycle 처리로 골머리 앓을 일이 급격히 줄어듦
• 상태에 따라 다르게 동작하는 UI를 보다 직관적으로 관리 가능
88. Redux - 출발점
• Part I.에서 얘기했듯, 모바일 앱에서 가장 큰 난제 중의 하나는 복잡한 상태
들을 어떻게 깔끔하고 유지보수성 높게 구현할 것인가의 문제
• 발상을 바꿔서 앱 전체의 이벤트를 State Machine으로 처리한다면 어떨까?
• Life Cycle 처리로 골머리 앓을 일이 급격히 줄어듦
• 상태에 따라 다르게 동작하는 UI를 보다 직관적으로 관리 가능
• 특정화면에서 수정 된 내용 (예: “좋아요” 상태 변경)이 다른 곳에서 저절로
적용
90. • 그런데..
• State Machine을 직접 구현하다 보면.. 각 로직의 추상화를 어떻게 할 지 많은 고민
을 하게 됨
91. • 그런데..
• State Machine을 직접 구현하다 보면.. 각 로직의 추상화를 어떻게 할 지 많은 고민
을 하게 됨
• 상태: 먼저 상태를 저장하는 부분은 보통 enum class 아니면, sealed class로 구현
92. • 그런데..
• State Machine을 직접 구현하다 보면.. 각 로직의 추상화를 어떻게 할 지 많은 고민
을 하게 됨
• 상태: 먼저 상태를 저장하는 부분은 보통 enum class 아니면, sealed class로 구현
• 상태 천이(transition/flow): 어떤 조건에서는 B로, 또 다른 조건에서는 C로 변경
93. • 그런데..
• State Machine을 직접 구현하다 보면.. 각 로직의 추상화를 어떻게 할 지 많은 고민
을 하게 됨
• 상태: 먼저 상태를 저장하는 부분은 보통 enum class 아니면, sealed class로 구현
• 상태 천이(transition/flow): 어떤 조건에서는 B로, 또 다른 조건에서는 C로 변경
• 로직 수행: 각 상태가 변경 되었을 때 수행해야할 실제 로직
94. • 그런데..
• State Machine을 직접 구현하다 보면.. 각 로직의 추상화를 어떻게 할 지 많은 고민
을 하게 됨
• 상태: 먼저 상태를 저장하는 부분은 보통 enum class 아니면, sealed class로 구현
• 상태 천이(transition/flow): 어떤 조건에서는 B로, 또 다른 조건에서는 C로 변경
• 로직 수행: 각 상태가 변경 되었을 때 수행해야할 실제 로직
• 위 세 요소의 복잡도에 따라 상황에 맞게 구현해야하지만 가장 범용적인 방법 중 하나
는 uni-directional한 pipeline 형태 -> 바로 Flux 구조!
96. MVI? Flux? Redux?
• Flux: Facebook에서, 웹 프론트엔드의 상태 처리를 직관적이고 깔끔하게 하기 위
해 고안한 Architectural Pattern. 단방향의 pipelined loop를 통해 이벤트를 처리
• MVI: Flux에 View 처리 등 모바일 관련 내용을 추가해서 부르는 이름. 안드로이드
개발에선 Flux와 같은 용어라고 생각해도 무방
• Redux: Flux 패턴의 변형 구현체 중 가장 효율적이라고 알려진 구현체. (물론 Java
Script로..) - immutable state, single source of truth를 실현한 단순한 구현
• Dan Abramov라는 젊은 개발자가 고안. 100여 줄에 불과한 라이브러리로 모든
Flux 구현체를 평정 -> flux 원 고안자에게 인정 받은 후, 바로 Facebook에서 스
카웃
97. • Action: 상태를 변경하라는 명령만을 가짐
그림 출처: https://speakerdeck.com/yuyakaido/droidkaigi-2019
98. • Action: 상태를 변경하라는 명령만을 가짐
• 보통은 Action Creator를 통해 만들어짐
그림 출처: https://speakerdeck.com/yuyakaido/droidkaigi-2019
99. • Action: 상태를 변경하라는 명령만을 가짐
• 보통은 Action Creator를 통해 만들어짐
• State: 상태의 데이터만을 가짐. no logic
그림 출처: https://speakerdeck.com/yuyakaido/droidkaigi-2019
100. • Action: 상태를 변경하라는 명령만을 가짐
• 보통은 Action Creator를 통해 만들어짐
• State: 상태의 데이터만을 가짐. no logic
• immutable (flux는 mutable)
그림 출처: https://speakerdeck.com/yuyakaido/droidkaigi-2019
101. • Action: 상태를 변경하라는 명령만을 가짐
• 보통은 Action Creator를 통해 만들어짐
• State: 상태의 데이터만을 가짐. no logic
• immutable (flux는 mutable)
• Store
그림 출처: https://speakerdeck.com/yuyakaido/droidkaigi-2019
102. • Action: 상태를 변경하라는 명령만을 가짐
• 보통은 Action Creator를 통해 만들어짐
• State: 상태의 데이터만을 가짐. no logic
• immutable (flux는 mutable)
• Store
• 상태의 저장소
그림 출처: https://speakerdeck.com/yuyakaido/droidkaigi-2019
103. • Action: 상태를 변경하라는 명령만을 가짐
• 보통은 Action Creator를 통해 만들어짐
• State: 상태의 데이터만을 가짐. no logic
• immutable (flux는 mutable)
• Store
• 상태의 저장소
• 상태를 변경시키고, 통지 하는 역할
그림 출처: https://speakerdeck.com/yuyakaido/droidkaigi-2019
104. • Action: 상태를 변경하라는 명령만을 가짐
• 보통은 Action Creator를 통해 만들어짐
• State: 상태의 데이터만을 가짐. no logic
• immutable (flux는 mutable)
• Store
• 상태의 저장소
• 상태를 변경시키고, 통지 하는 역할
• RxJava 혹은 Coroutine으로 손쉽게 구현 가능
그림 출처: https://speakerdeck.com/yuyakaido/droidkaigi-2019
105. • Action: 상태를 변경하라는 명령만을 가짐
• 보통은 Action Creator를 통해 만들어짐
• State: 상태의 데이터만을 가짐. no logic
• immutable (flux는 mutable)
• Store
• 상태의 저장소
• 상태를 변경시키고, 통지 하는 역할
• RxJava 혹은 Coroutine으로 손쉽게 구현 가능
• Reducer
그림 출처: https://speakerdeck.com/yuyakaido/droidkaigi-2019
106. • Action: 상태를 변경하라는 명령만을 가짐
• 보통은 Action Creator를 통해 만들어짐
• State: 상태의 데이터만을 가짐. no logic
• immutable (flux는 mutable)
• Store
• 상태의 저장소
• 상태를 변경시키고, 통지 하는 역할
• RxJava 혹은 Coroutine으로 손쉽게 구현 가능
• Reducer
• 순수 함수(pure function) 형태로 상태 변경을 구
현: (State, Action) -> new State그림 출처: https://speakerdeck.com/yuyakaido/droidkaigi-2019
107. 그럼 side effect 처리는 대체 누가? → Middleware!
그림 출처: https://speakerdeck.com/yuyakaido/droidkaigi-2019
110. Redux의 장단점
• 장점
• 상태 처리에서의 라이프 사이클의 영향을 어느 정도 배제할 수 있음
• 화면에서 back 버튼 누른 후에 뒤늦게 발생한 API 에러.. 같은 애매하고 난해한 상황도 효과적으로 대처 가능
111. Redux의 장단점
• 장점
• 상태 처리에서의 라이프 사이클의 영향을 어느 정도 배제할 수 있음
• 화면에서 back 버튼 누른 후에 뒤늦게 발생한 API 에러.. 같은 애매하고 난해한 상황도 효과적으로 대처 가능
• 상태의 정의 부분, 저장 부분, 처리 부분이 완벽히 나눠져 있기 때문에 수정/확장이 극도로 편함
112. Redux의 장단점
• 장점
• 상태 처리에서의 라이프 사이클의 영향을 어느 정도 배제할 수 있음
• 화면에서 back 버튼 누른 후에 뒤늦게 발생한 API 에러.. 같은 애매하고 난해한 상황도 효과적으로 대처 가능
• 상태의 정의 부분, 저장 부분, 처리 부분이 완벽히 나눠져 있기 때문에 수정/확장이 극도로 편함
• Functional한 구조의 특성상 유닛 테스트를 작성하는 것이 극도로 편리함
113. Redux의 장단점
• 장점
• 상태 처리에서의 라이프 사이클의 영향을 어느 정도 배제할 수 있음
• 화면에서 back 버튼 누른 후에 뒤늦게 발생한 API 에러.. 같은 애매하고 난해한 상황도 효과적으로 대처 가능
• 상태의 정의 부분, 저장 부분, 처리 부분이 완벽히 나눠져 있기 때문에 수정/확장이 극도로 편함
• Functional한 구조의 특성상 유닛 테스트를 작성하는 것이 극도로 편리함
• Debugging의 신세계! 현재의 모든 전역적 상태를 확인할 수 있고, 상태 변수 값을 직접 수정해서 전체 상태를 바꿔볼
수도 있음
114. Redux의 장단점
• 장점
• 상태 처리에서의 라이프 사이클의 영향을 어느 정도 배제할 수 있음
• 화면에서 back 버튼 누른 후에 뒤늦게 발생한 API 에러.. 같은 애매하고 난해한 상황도 효과적으로 대처 가능
• 상태의 정의 부분, 저장 부분, 처리 부분이 완벽히 나눠져 있기 때문에 수정/확장이 극도로 편함
• Functional한 구조의 특성상 유닛 테스트를 작성하는 것이 극도로 편리함
• Debugging의 신세계! 현재의 모든 전역적 상태를 확인할 수 있고, 상태 변수 값을 직접 수정해서 전체 상태를 바꿔볼
수도 있음
• 단점
115. Redux의 장단점
• 장점
• 상태 처리에서의 라이프 사이클의 영향을 어느 정도 배제할 수 있음
• 화면에서 back 버튼 누른 후에 뒤늦게 발생한 API 에러.. 같은 애매하고 난해한 상황도 효과적으로 대처 가능
• 상태의 정의 부분, 저장 부분, 처리 부분이 완벽히 나눠져 있기 때문에 수정/확장이 극도로 편함
• Functional한 구조의 특성상 유닛 테스트를 작성하는 것이 극도로 편리함
• Debugging의 신세계! 현재의 모든 전역적 상태를 확인할 수 있고, 상태 변수 값을 직접 수정해서 전체 상태를 바꿔볼
수도 있음
• 단점
• 상당한 수준의 학습 비용이 소요
116. Redux의 장단점
• 장점
• 상태 처리에서의 라이프 사이클의 영향을 어느 정도 배제할 수 있음
• 화면에서 back 버튼 누른 후에 뒤늦게 발생한 API 에러.. 같은 애매하고 난해한 상황도 효과적으로 대처 가능
• 상태의 정의 부분, 저장 부분, 처리 부분이 완벽히 나눠져 있기 때문에 수정/확장이 극도로 편함
• Functional한 구조의 특성상 유닛 테스트를 작성하는 것이 극도로 편리함
• Debugging의 신세계! 현재의 모든 전역적 상태를 확인할 수 있고, 상태 변수 값을 직접 수정해서 전체 상태를 바꿔볼
수도 있음
• 단점
• 상당한 수준의 학습 비용이 소요
• 코드량 증가, 특히 boiler plate 코드의 반복적 생산은 피하기 어려움
118. Flux / Redux의 현재와 미래
• 현재
• ReKotlin, RxRedux이 있으나 현재로선 직접 구현을 추천
119. Flux / Redux의 현재와 미래
• 현재
• ReKotlin, RxRedux이 있으나 현재로선 직접 구현을 추천
• State / Action의 접점을 View로 할 수도 있지만 ViewModel로도 가능
120. Flux / Redux의 현재와 미래
• 현재
• ReKotlin, RxRedux이 있으나 현재로선 직접 구현을 추천
• State / Action의 접점을 View로 할 수도 있지만 ViewModel로도 가능
• 그리고 Flux의 경우는 Store를 ViewModel로 구현!
121. Flux / Redux의 현재와 미래
• 현재
• ReKotlin, RxRedux이 있으나 현재로선 직접 구현을 추천
• State / Action의 접점을 View로 할 수도 있지만 ViewModel로도 가능
• 그리고 Flux의 경우는 Store를 ViewModel로 구현!
• RxJava로 Store, 그리고 side effect를 처리하는 middleware를 구현 vs Coroutine + LiveData 조합
122. Flux / Redux의 현재와 미래
• 현재
• ReKotlin, RxRedux이 있으나 현재로선 직접 구현을 추천
• State / Action의 접점을 View로 할 수도 있지만 ViewModel로도 가능
• 그리고 Flux의 경우는 Store를 ViewModel로 구현!
• RxJava로 Store, 그리고 side effect를 처리하는 middleware를 구현 vs Coroutine + LiveData 조합
• 미래?
123. Flux / Redux의 현재와 미래
• 현재
• ReKotlin, RxRedux이 있으나 현재로선 직접 구현을 추천
• State / Action의 접점을 View로 할 수도 있지만 ViewModel로도 가능
• 그리고 Flux의 경우는 Store를 ViewModel로 구현!
• RxJava로 Store, 그리고 side effect를 처리하는 middleware를 구현 vs Coroutine + LiveData 조합
• 미래?
• Rx를 굳이 쓰지 않아도 되는 use case가 대부분이고, coroutine 환경이 성숙/안정화 됨에 따라 급격히 coroutine으로
중심이 넘어갈 듯
124. Flux / Redux의 현재와 미래
• 현재
• ReKotlin, RxRedux이 있으나 현재로선 직접 구현을 추천
• State / Action의 접점을 View로 할 수도 있지만 ViewModel로도 가능
• 그리고 Flux의 경우는 Store를 ViewModel로 구현!
• RxJava로 Store, 그리고 side effect를 처리하는 middleware를 구현 vs Coroutine + LiveData 조합
• 미래?
• Rx를 굳이 쓰지 않아도 되는 use case가 대부분이고, coroutine 환경이 성숙/안정화 됨에 따라 급격히 coroutine으로
중심이 넘어갈 듯
• 현재 주류인 Redux-Thunk middleware를 Redux-Saga가 대체할 것으로 예상
125. Flux / Redux의 현재와 미래
• 현재
• ReKotlin, RxRedux이 있으나 현재로선 직접 구현을 추천
• State / Action의 접점을 View로 할 수도 있지만 ViewModel로도 가능
• 그리고 Flux의 경우는 Store를 ViewModel로 구현!
• RxJava로 Store, 그리고 side effect를 처리하는 middleware를 구현 vs Coroutine + LiveData 조합
• 미래?
• Rx를 굳이 쓰지 않아도 되는 use case가 대부분이고, coroutine 환경이 성숙/안정화 됨에 따라 급격히 coroutine으로
중심이 넘어갈 듯
• 현재 주류인 Redux-Thunk middleware를 Redux-Saga가 대체할 것으로 예상
• Coroutine과 완벽하게 매치 되는 async loop 구조
126. Flux / Redux의 현재와 미래
• 현재
• ReKotlin, RxRedux이 있으나 현재로선 직접 구현을 추천
• State / Action의 접점을 View로 할 수도 있지만 ViewModel로도 가능
• 그리고 Flux의 경우는 Store를 ViewModel로 구현!
• RxJava로 Store, 그리고 side effect를 처리하는 middleware를 구현 vs Coroutine + LiveData 조합
• 미래?
• Rx를 굳이 쓰지 않아도 되는 use case가 대부분이고, coroutine 환경이 성숙/안정화 됨에 따라 급격히 coroutine으로
중심이 넘어갈 듯
• 현재 주류인 Redux-Thunk middleware를 Redux-Saga가 대체할 것으로 예상
• Coroutine과 완벽하게 매치 되는 async loop 구조
• 극도로 아름답고 깔끔한 테스트 코드 작성 가능
131. 1. Multi-module!
• 여기서 잠시 질문
• internal class (inner class 아님)가 무엇인지 아시나요?
• private / protected / internal / public 중 internal과 public의 차이는?
132. 1. Multi-module!
• 여기서 잠시 질문
• internal class (inner class 아님)가 무엇인지 아시나요?
• private / protected / internal / public 중 internal과 public의 차이는?
• internal 식별자 -> 다른 module (package가 아님) 에서 보이지 않게 함
133. 1. Multi-module!
• 여기서 잠시 질문
• internal class (inner class 아님)가 무엇인지 아시나요?
• private / protected / internal / public 중 internal과 public의 차이는?
• internal 식별자 -> 다른 module (package가 아님) 에서 보이지 않게 함
• android gradle plugin 3.0.0부터 compile -> implementation으로 바뀐 이유
가 무엇인지 아시나요?
134. 1. Multi-module!
• 여기서 잠시 질문
• internal class (inner class 아님)가 무엇인지 아시나요?
• private / protected / internal / public 중 internal과 public의 차이는?
• internal 식별자 -> 다른 module (package가 아님) 에서 보이지 않게 함
• android gradle plugin 3.0.0부터 compile -> implementation으로 바뀐 이유
가 무엇인지 아시나요?
• 멀티 모듈 지원 기능이 강화 되면서 추가된 개념
135. 1. Multi-module!
• 여기서 잠시 질문
• internal class (inner class 아님)가 무엇인지 아시나요?
• private / protected / internal / public 중 internal과 public의 차이는?
• internal 식별자 -> 다른 module (package가 아님) 에서 보이지 않게 함
• android gradle plugin 3.0.0부터 compile -> implementation으로 바뀐 이유
가 무엇인지 아시나요?
• 멀티 모듈 지원 기능이 강화 되면서 추가된 개념
• 정확히는, compile == api 로 바뀜
136. 참조 가능
• Compile (== api) 식별자
그림 출처: https://speakerdeck.com/sansanbuildersbox/multi-module-android-application
137. 참조 가능
• Compile (== api) 식별자
• 특별히 지정하지 않아도
손자 모듈까지 참조 가능
그림 출처: https://speakerdeck.com/sansanbuildersbox/multi-module-android-application
138. 참조 가능
• Compile (== api) 식별자
• 특별히 지정하지 않아도
손자 모듈까지 참조 가능
• 바꿔 얘기하면,
module2가 변경이 되
면 app 모듈도 재컴파
일 해야함
그림 출처: https://speakerdeck.com/sansanbuildersbox/multi-module-android-application
139. 참조 불가
• implementation 식별자
그림 출처: https://speakerdeck.com/sansanbuildersbox/multi-module-android-application
140. 참조 불가
• implementation 식별자
• 명시적으로 지정하지 않
은 손자 모듈 참조 불가
그림 출처: https://speakerdeck.com/sansanbuildersbox/multi-module-android-application
141. 참조 불가
• implementation 식별자
• 명시적으로 지정하지 않
은 손자 모듈 참조 불가
• 바꿔 얘기하면,
module2가 변경이 되
어도 app 모듈은 컴파
일할 필요가 없음
그림 출처: https://speakerdeck.com/sansanbuildersbox/multi-module-android-application
145. • 멀티 모듈은 왜 쓰는가?
• 현실적인 이유: 빌드 속도
• 풀 빌드: 36.4s → 30s, incremental build: 14.2s → 7s
146. • 멀티 모듈은 왜 쓰는가?
• 현실적인 이유: 빌드 속도
• 풀 빌드: 36.4s → 30s, incremental build: 14.2s → 7s
• Ref: https://speakerdeck.com/sansanbuildersbox/multi-module-android-
application
147. • 멀티 모듈은 왜 쓰는가?
• 현실적인 이유: 빌드 속도
• 풀 빌드: 36.4s → 30s, incremental build: 14.2s → 7s
• Ref: https://speakerdeck.com/sansanbuildersbox/multi-module-android-
application
• incremental이야 그렇다쳐도 왜 풀빌드도 빠르지? 주된 이유는 annotation
processing. kapt의 경우는 아직도 incremental processing을 지원하지 않음.
148. • 멀티 모듈은 왜 쓰는가?
• 현실적인 이유: 빌드 속도
• 풀 빌드: 36.4s → 30s, incremental build: 14.2s → 7s
• Ref: https://speakerdeck.com/sansanbuildersbox/multi-module-android-
application
• incremental이야 그렇다쳐도 왜 풀빌드도 빠르지? 주된 이유는 annotation
processing. kapt의 경우는 아직도 incremental processing을 지원하지 않음.
• 그외 realm과 같이 byte code weaving을 하는 라이브러리를 별도 모듈로 빼는 것
만으로 30% 빌드 성능 향상
149. • 멀티 모듈은 왜 쓰는가?
• 현실적인 이유: 빌드 속도
• 풀 빌드: 36.4s → 30s, incremental build: 14.2s → 7s
• Ref: https://speakerdeck.com/sansanbuildersbox/multi-module-android-
application
• incremental이야 그렇다쳐도 왜 풀빌드도 빠르지? 주된 이유는 annotation
processing. kapt의 경우는 아직도 incremental processing을 지원하지 않음.
• 그외 realm과 같이 byte code weaving을 하는 라이브러리를 별도 모듈로 빼는 것
만으로 30% 빌드 성능 향상
• 효율적인 분업 프로세스 가능: 모듈 별로 구현의 격리 / 테스트가 가능
150. • 멀티 모듈은 왜 쓰는가?
• 현실적인 이유: 빌드 속도
• 풀 빌드: 36.4s → 30s, incremental build: 14.2s → 7s
• Ref: https://speakerdeck.com/sansanbuildersbox/multi-module-android-
application
• incremental이야 그렇다쳐도 왜 풀빌드도 빠르지? 주된 이유는 annotation
processing. kapt의 경우는 아직도 incremental processing을 지원하지 않음.
• 그외 realm과 같이 byte code weaving을 하는 라이브러리를 별도 모듈로 빼는 것
만으로 30% 빌드 성능 향상
• 효율적인 분업 프로세스 가능: 모듈 별로 구현의 격리 / 테스트가 가능
• 독립적인 설계를 촉진
152. • 모듈은 어떻게 나누는가? - (1) Layer 별 수직 분리
• Presentation / Domain / Data Layer
153. • 모듈은 어떻게 나누는가? - (1) Layer 별 수직 분리
• Presentation / Domain / Data Layer
• 단, Application에서 DB를 생성해야 하기 때문에, Presentation -> Domain ->
Data 형태의 배치는 불가능
154. • 모듈은 어떻게 나누는가? - (1) Layer 별 수직 분리
• Presentation / Domain / Data Layer
• 단, Application에서 DB를 생성해야 하기 때문에, Presentation -> Domain ->
Data 형태의 배치는 불가능
• app은 DI를 정의, ui / data 모듈에 domain 모듈이 주입될 수 있도록 함
155. • 모듈은 어떻게 나누는가? - (1) Layer 별 수직 분리
• Presentation / Domain / Data Layer
• 단, Application에서 DB를 생성해야 하기 때문에, Presentation -> Domain ->
Data 형태의 배치는 불가능
• app은 DI를 정의, ui / data 모듈에 domain 모듈이 주입될 수 있도록 함
그림 출처: https://speakerdeck.com/sansanbuildersbox/multi-module-android-application
157. • 모듈은 어떻게 나누는가? - (2) 수평 분리
• 화면별 / 기능별로 분리 → feature별 독립성 확보
158. • 모듈은 어떻게 나누는가? - (2) 수평 분리
• 화면별 / 기능별로 분리 → feature별 독립성 확보
• 라이브러리 기준 분리: Realm 등 black magic 계의 기술이 사용된 부분은 극력 별도 모듈로 분리
159. • 모듈은 어떻게 나누는가? - (2) 수평 분리
• 화면별 / 기능별로 분리 → feature별 독립성 확보
• 라이브러리 기준 분리: Realm 등 black magic 계의 기술이 사용된 부분은 극력 별도 모듈로 분리
• → 결과적으로 10~30 개 정도의 모듈로 앱이 구성됨 (주의: 중규모 이상의 팀의 경우)
160. • 모듈은 어떻게 나누는가? - (2) 수평 분리
• 화면별 / 기능별로 분리 → feature별 독립성 확보
• 라이브러리 기준 분리: Realm 등 black magic 계의 기술이 사용된 부분은 극력 별도 모듈로 분리
• → 결과적으로 10~30 개 정도의 모듈로 앱이 구성됨 (주의: 중규모 이상의 팀의 경우)
• 주의점: 상호 참조
161. • 모듈은 어떻게 나누는가? - (2) 수평 분리
• 화면별 / 기능별로 분리 → feature별 독립성 확보
• 라이브러리 기준 분리: Realm 등 black magic 계의 기술이 사용된 부분은 극력 별도 모듈로 분리
• → 결과적으로 10~30 개 정도의 모듈로 앱이 구성됨 (주의: 중규모 이상의 팀의 경우)
• 주의점: 상호 참조
• 안드로이드 모듈의 참조는 단방향만 허용하므로 양방향의 참조가 불가
162. • 모듈은 어떻게 나누는가? - (2) 수평 분리
• 화면별 / 기능별로 분리 → feature별 독립성 확보
• 라이브러리 기준 분리: Realm 등 black magic 계의 기술이 사용된 부분은 극력 별도 모듈로 분리
• → 결과적으로 10~30 개 정도의 모듈로 앱이 구성됨 (주의: 중규모 이상의 팀의 경우)
• 주의점: 상호 참조
• 안드로이드 모듈의 참조는 단방향만 허용하므로 양방향의 참조가 불가
• 두 개의 화면이 각자 서로를 시작할 수 있는 경우는, 각자의 intent를 가져야 하므로 분리가 불가능해짐
163. • 모듈은 어떻게 나누는가? - (2) 수평 분리
• 화면별 / 기능별로 분리 → feature별 독립성 확보
• 라이브러리 기준 분리: Realm 등 black magic 계의 기술이 사용된 부분은 극력 별도 모듈로 분리
• → 결과적으로 10~30 개 정도의 모듈로 앱이 구성됨 (주의: 중규모 이상의 팀의 경우)
• 주의점: 상호 참조
• 안드로이드 모듈의 참조는 단방향만 허용하므로 양방향의 참조가 불가
• 두 개의 화면이 각자 서로를 시작할 수 있는 경우는, 각자의 intent를 가져야 하므로 분리가 불가능해짐
• Intent 생성 인터페이스만을 갖고 구현은 최상위 모듈(:app)이 소유 → DI로 얻어오기
164. • 모듈은 어떻게 나누는가? - (2) 수평 분리
• 화면별 / 기능별로 분리 → feature별 독립성 확보
• 라이브러리 기준 분리: Realm 등 black magic 계의 기술이 사용된 부분은 극력 별도 모듈로 분리
• → 결과적으로 10~30 개 정도의 모듈로 앱이 구성됨 (주의: 중규모 이상의 팀의 경우)
• 주의점: 상호 참조
• 안드로이드 모듈의 참조는 단방향만 허용하므로 양방향의 참조가 불가
• 두 개의 화면이 각자 서로를 시작할 수 있는 경우는, 각자의 intent를 가져야 하므로 분리가 불가능해짐
• Intent 생성 인터페이스만을 갖고 구현은 최상위 모듈(:app)이 소유 → DI로 얻어오기
• 중앙집중식 navigation 클래스를 만들고 각 모듈은 인터페이스만 갖도록 구현 (DroidKaigi 2018 앱
참조)
165. • 모듈은 어떻게 나누는가? - (2) 수평 분리
• 화면별 / 기능별로 분리 → feature별 독립성 확보
• 라이브러리 기준 분리: Realm 등 black magic 계의 기술이 사용된 부분은 극력 별도 모듈로 분리
• → 결과적으로 10~30 개 정도의 모듈로 앱이 구성됨 (주의: 중규모 이상의 팀의 경우)
• 주의점: 상호 참조
• 안드로이드 모듈의 참조는 단방향만 허용하므로 양방향의 참조가 불가
• 두 개의 화면이 각자 서로를 시작할 수 있는 경우는, 각자의 intent를 가져야 하므로 분리가 불가능해짐
• Intent 생성 인터페이스만을 갖고 구현은 최상위 모듈(:app)이 소유 → DI로 얻어오기
• 중앙집중식 navigation 클래스를 만들고 각 모듈은 인터페이스만 갖도록 구현 (DroidKaigi 2018 앱
참조)
• Tip: Dagger를 이용한 주입 + 사용자 파라미터가 동시에 필요한 경우라면, AssistedInjection 을 사용
167. • Dependency Inversion Principle: 이제까지 Android에서 중요시 된 적 없던 설계 원칙
• 상위 모듈은 하위 모듈에 의존성이 있어서는 안 된다 & 상위 모듈 하위 모듈 모두 인터페이스에만 의존한다
168. • Dependency Inversion Principle: 이제까지 Android에서 중요시 된 적 없던 설계 원칙
• 상위 모듈은 하위 모듈에 의존성이 있어서는 안 된다 & 상위 모듈 하위 모듈 모두 인터페이스에만 의존한다
• 의존성이 생기는 클래스가 volatile 하다면 인터페이스로만 참조가 일어나야 한다
169. • Dependency Inversion Principle: 이제까지 Android에서 중요시 된 적 없던 설계 원칙
• 상위 모듈은 하위 모듈에 의존성이 있어서는 안 된다 & 상위 모듈 하위 모듈 모두 인터페이스에만 의존한다
• 의존성이 생기는 클래스가 volatile 하다면 인터페이스로만 참조가 일어나야 한다
• A 객체가 B 객체의 레퍼런스를 직접 참조하는 대신, interface를 참조하여 구현 의존성을 끊음
170. • Dependency Inversion Principle: 이제까지 Android에서 중요시 된 적 없던 설계 원칙
• 상위 모듈은 하위 모듈에 의존성이 있어서는 안 된다 & 상위 모듈 하위 모듈 모두 인터페이스에만 의존한다
• 의존성이 생기는 클래스가 volatile 하다면 인터페이스로만 참조가 일어나야 한다
• A 객체가 B 객체의 레퍼런스를 직접 참조하는 대신, interface를 참조하여 구현 의존성을 끊음
• B가 변경되어도 A는 컴파일할 필요가 없음
171. • Dependency Inversion Principle: 이제까지 Android에서 중요시 된 적 없던 설계 원칙
• 상위 모듈은 하위 모듈에 의존성이 있어서는 안 된다 & 상위 모듈 하위 모듈 모두 인터페이스에만 의존한다
• 의존성이 생기는 클래스가 volatile 하다면 인터페이스로만 참조가 일어나야 한다
• A 객체가 B 객체의 레퍼런스를 직접 참조하는 대신, interface를 참조하여 구현 의존성을 끊음
• B가 변경되어도 A는 컴파일할 필요가 없음
• 동일한 인터페이스를 갖지만 전혀 다른 로직으로 구현된 제3의 모듈로 쉽게 변경 가능
→ Dependency Injection의 도움을 받으면 각 모듈 별 설정을 일관된 방식으로 손쉽게 수정 가능
180. 2. RxJava vs Coroutine vs LiveData
• 왜 필요한가?
• Reactive: 비동기적인 처리를 imperative하게 처리할 수 있게 해줌
181. 2. RxJava vs Coroutine vs LiveData
• 왜 필요한가?
• Reactive: 비동기적인 처리를 imperative하게 처리할 수 있게 해줌
• RxJava
182. 2. RxJava vs Coroutine vs LiveData
• 왜 필요한가?
• Reactive: 비동기적인 처리를 imperative하게 처리할 수 있게 해줌
• RxJava
• 여전히, Android에서의 비동기 처리 위한 현존하는 가장 강력하고 편리한 수단
183. 2. RxJava vs Coroutine vs LiveData
• 왜 필요한가?
• Reactive: 비동기적인 처리를 imperative하게 처리할 수 있게 해줌
• RxJava
• 여전히, Android에서의 비동기 처리 위한 현존하는 가장 강력하고 편리한 수단
• Android에서의 복잡한 상태처리를 직관적으로 구현 가능
184. 2. RxJava vs Coroutine vs LiveData
• 왜 필요한가?
• Reactive: 비동기적인 처리를 imperative하게 처리할 수 있게 해줌
• RxJava
• 여전히, Android에서의 비동기 처리 위한 현존하는 가장 강력하고 편리한 수단
• Android에서의 복잡한 상태처리를 직관적으로 구현 가능
• View - ViewModel과의 가교를 위한 black magic 스러운 다양한 구현도 가능
185. 2. RxJava vs Coroutine vs LiveData
• 왜 필요한가?
• Reactive: 비동기적인 처리를 imperative하게 처리할 수 있게 해줌
• RxJava
• 여전히, Android에서의 비동기 처리 위한 현존하는 가장 강력하고 편리한 수단
• Android에서의 복잡한 상태처리를 직관적으로 구현 가능
• View - ViewModel과의 가교를 위한 black magic 스러운 다양한 구현도 가능
• eg. 상황에 따라 순차적으로 실행해야 하는 애니메이션들을 각각
Completable로 정의한 후, Combine해서 실행
186. • 이런 것도 가능!
• ViewGroup의 중점이 바뀔 때마다 ViewModel이 통지를 받을 수 있도록 View 측에서 호
출할 수 있는 헬퍼 함수
fun createVerticalCenterPublisher(layout: View): Observable<Int> =
Observable.create<Int> { emitter ->
emitter.onNext(layout.bottom - layout.top)
val callback = View.OnLayoutChangeListener { _, _, top, _, bottom, _, _, _, _ ->
emitter.onNext(bottom - top)
}
layout.addOnLayoutChangeListener(callback)
emitter.setCancellable {
layout.removeOnLayoutChangeListener(callback)
}
}.subscribeOn(AndroidSchedulers.mainThread())
.throttleLatest(100, TimeUnit.MILLISECONDS, true)
.map { it / 2 }
187. 더 자세한 건…
헐!? 그 명강의를 안 들었다고요?! 😱
YouTube 다시보기를 강추드립니다.
191. Coroutine
• 뭐가 좋은가?
• 성능 이점: coroutine is light-weight threads
• Rx보다 덜 어렵다
• 보다 functional한 구현 - Saga 패턴 같은 형태의 구현에 탁월
192. Coroutine
• 뭐가 좋은가?
• 성능 이점: coroutine is light-weight threads
• Rx보다 덜 어렵다
• 보다 functional한 구현 - Saga 패턴 같은 형태의 구현에 탁월
• 이제는 안정기에 접어들었음 - Kotlin 1.3과 함께 정식 버전이 되었고, 최신 사양에 맞춘 다양한 지
원이 예정되어 있음.
193. Coroutine
• 뭐가 좋은가?
• 성능 이점: coroutine is light-weight threads
• Rx보다 덜 어렵다
• 보다 functional한 구현 - Saga 패턴 같은 형태의 구현에 탁월
• 이제는 안정기에 접어들었음 - Kotlin 1.3과 함께 정식 버전이 되었고, 최신 사양에 맞춘 다양한 지
원이 예정되어 있음.
• lifecycle-viewModel-ktx 2.1.0 (현재 alpha03)부터 lifecycle-aware한 Coroutine Scope
인 viewmodelScope를 공식 지원
194. Coroutine
• 뭐가 좋은가?
• 성능 이점: coroutine is light-weight threads
• Rx보다 덜 어렵다
• 보다 functional한 구현 - Saga 패턴 같은 형태의 구현에 탁월
• 이제는 안정기에 접어들었음 - Kotlin 1.3과 함께 정식 버전이 되었고, 최신 사양에 맞춘 다양한 지
원이 예정되어 있음.
• lifecycle-viewModel-ktx 2.1.0 (현재 alpha03)부터 lifecycle-aware한 Coroutine Scope
인 viewmodelScope를 공식 지원
• retrofit 차기 버전에서 “suspend fun”을 형태로 api interface 정의 (현재 “Deferred”를 리턴
하는 형태)
198. LiveData
• LiveData에 대해 논하기 전에..
• View - ViewModel (or Presenter) 간의 이벤트 통지에 있어서 가장 이상적인 이
벤트 통지 전달자는 어떤 특징을 가져야할까?
199. LiveData
• LiveData에 대해 논하기 전에..
• View - ViewModel (or Presenter) 간의 이벤트 통지에 있어서 가장 이상적인 이
벤트 통지 전달자는 어떤 특징을 가져야할까?
• 이벤트 전달이 UI 스레드에서 이뤄져야 한다
200. LiveData
• LiveData에 대해 논하기 전에..
• View - ViewModel (or Presenter) 간의 이벤트 통지에 있어서 가장 이상적인 이
벤트 통지 전달자는 어떤 특징을 가져야할까?
• 이벤트 전달이 UI 스레드에서 이뤄져야 한다
• Side Effect의 공유가 가능해야한다. -> 쉬운 말로 하면 하나의 이벤트를 여러 이
벤트 핸들러가 공유할 수 있어야 한다
201. LiveData
• LiveData에 대해 논하기 전에..
• View - ViewModel (or Presenter) 간의 이벤트 통지에 있어서 가장 이상적인 이
벤트 통지 전달자는 어떤 특징을 가져야할까?
• 이벤트 전달이 UI 스레드에서 이뤄져야 한다
• Side Effect의 공유가 가능해야한다. -> 쉬운 말로 하면 하나의 이벤트를 여러 이
벤트 핸들러가 공유할 수 있어야 한다
• 에러를 skip 할 수 있어야 한다
202. LiveData
• LiveData에 대해 논하기 전에..
• View - ViewModel (or Presenter) 간의 이벤트 통지에 있어서 가장 이상적인 이
벤트 통지 전달자는 어떤 특징을 가져야할까?
• 이벤트 전달이 UI 스레드에서 이뤄져야 한다
• Side Effect의 공유가 가능해야한다. -> 쉬운 말로 하면 하나의 이벤트를 여러 이
벤트 핸들러가 공유할 수 있어야 한다
• 에러를 skip 할 수 있어야 한다
• → RxSwift의 경우, Signal/Driver가 이에 해당
205. • LiveData의 특징
• 기본적으로 UI thread에서 동작
• Side Effect(값의 변화, 이벤트)의 공유가 가능
206. • LiveData의 특징
• 기본적으로 UI thread에서 동작
• Side Effect(값의 변화, 이벤트)의 공유가 가능
• Configuration 변경 시 마지막 발생한 이벤트를 replay 해줌 (단, 의도치 않은 버그가 생길 여지
있음)
207. • LiveData의 특징
• 기본적으로 UI thread에서 동작
• Side Effect(값의 변화, 이벤트)의 공유가 가능
• Configuration 변경 시 마지막 발생한 이벤트를 replay 해줌 (단, 의도치 않은 버그가 생길 여지
있음)
• transformation을 통해 RxJava의 combine과 비슷하게 구현 가능
208. • LiveData의 특징
• 기본적으로 UI thread에서 동작
• Side Effect(값의 변화, 이벤트)의 공유가 가능
• Configuration 변경 시 마지막 발생한 이벤트를 replay 해줌 (단, 의도치 않은 버그가 생길 여지
있음)
• transformation을 통해 RxJava의 combine과 비슷하게 구현 가능
• → RxSwift의 Driver와 유사한 개념 + LifeCycle-awareness
209. • LiveData의 특징
• 기본적으로 UI thread에서 동작
• Side Effect(값의 변화, 이벤트)의 공유가 가능
• Configuration 변경 시 마지막 발생한 이벤트를 replay 해줌 (단, 의도치 않은 버그가 생길 여지
있음)
• transformation을 통해 RxJava의 combine과 비슷하게 구현 가능
• → RxSwift의 Driver와 유사한 개념 + LifeCycle-awareness
• LiveData는 언제 쓰는가?
210. • LiveData의 특징
• 기본적으로 UI thread에서 동작
• Side Effect(값의 변화, 이벤트)의 공유가 가능
• Configuration 변경 시 마지막 발생한 이벤트를 replay 해줌 (단, 의도치 않은 버그가 생길 여지
있음)
• transformation을 통해 RxJava의 combine과 비슷하게 구현 가능
• → RxSwift의 Driver와 유사한 개념 + LifeCycle-awareness
• LiveData는 언제 쓰는가?
• Rx의 대체재가 아닌 보완재
211. • LiveData의 특징
• 기본적으로 UI thread에서 동작
• Side Effect(값의 변화, 이벤트)의 공유가 가능
• Configuration 변경 시 마지막 발생한 이벤트를 replay 해줌 (단, 의도치 않은 버그가 생길 여지
있음)
• transformation을 통해 RxJava의 combine과 비슷하게 구현 가능
• → RxSwift의 Driver와 유사한 개념 + LifeCycle-awareness
• LiveData는 언제 쓰는가?
• Rx의 대체재가 아닌 보완재
• DataBinding 용도로 ObservableField보다 적합할 수도 있으나.. 있으나.. ;;;
212. 더 자세한 건…
개인적으로는 large-scale app이라면 LiveData를 일부 소
극적으로 쓰고, RxBinding을 적극적으로 사용합니다.
214. 선정 조건
• 앱 규모: Toy project가 아니고 실제 Play Store에 올라온 앱인가
• 설계: Clean Architecture 등이 잘 적용되어 있는가
• 테스트: Unit Test가 요소요소 잘 구현되어 있는가
• 코드 품질: 내 프로젝트에도 갖다 쓸만한 표현이 많은가
215. Google IO 2018
• https://github.com/google/iosched
• Clean Architecture - Domain layer의 UseCase 구현 및 테스트
• LiveData 사용의 진수: RxJava 아니면 안 될 것 같은 기능들까지도 몽땅
MediatorLiveData로 구현!
• 주의할 점: LiveData를 실제로 저렇게 쓰는 것은 극력 비추천
• 기타 아키텍처 외적으로 세부 코드 구현에 참고할 점이 매우 많음
218. I’ll be back!
• DroidKnights 2020 안드로이드 아키텍처 세번째 시리즈로 돌아옵니다.
내용은 아마도..
• Coroutine + Arrow로 구현한 functional한 Redux 구조 심층 분석
• 대규모 Multi-module 프로젝트 구축
• 기타 등등
• (개점휴업 중이던) 블로그 재개합니다! - 4/20부터
• 실제 프로젝트에서 의미있는 Test-Driven Development
• Dagger 2 Dependency Injection 구현 전략 총정리
강사룡
Email: justfaceit@gmail.com
Blog: https://medium.com/@justfaceit
Slide: https://slideshare.net/saryongkang