Android App architecture: Architecture recommendations
안드로이드 App architecture: Architecture recommendations를 알아보겠습니다.
출처:
https://developer.android.com/topic/architecture/recommendations
이 문서에서는 몇 가지 Architecture에 좋은 추천들을 제시합니다. 이것을 받아들여 당신의 앱의 품질을 높이고 강력하고 확장 가능하게 만드세요. 이것은 또한 당신의 앱을 쉽게 유지 보수할 수 있고 테스트할 수 있게 해줍니다.
• 메모:
당신은 이 문서의 추천을 단지 추천으로 받아들이세요. 강제 사항이 아닙니다. 앱에 필요한 것만 받아들이세요.
아래의 사항들은 주제별로 묶여있습니다. 각각 항목에는 우선순위가 있고, 얼마나 강력하게 추천하는지 표시되어 있습니다. 우선순위의 항목은 아래와 같습니다.
• Strongly recommended: 강력하게 추천; 당신이 하고자 하는 것과 정면으로 충돌하지 않는다면 이것을 따르기를 추천합니다.
• Recommended: 추천; 이것은 당신의 앱을 향상 사키는 데 추천합니다.
• Optional: 선택 사항; 특정한 환경에서 당신의 앱을 향상시켜 줄 수 있습니다.
메모:
이 추천을 이해하기 위해서는 당신은 Architecture 지침에 익숙해져야 합니다.
⊙ Layered architecture: 계층 구조;
우리가 추천하는 layered(계층) architecture(구조)는 관심사를 나누는 것을 선호합니다. 이것은 UI(유아이)에서 data(데이터) models(모델)까지, single source of truth(하나의 출처 진실) 원칙을 따르고, unidirectional(단방향) data(데이터) flow(흐름) 원칙 적용하는 것입니다. 여기에 layered architecture의 좋은 사례가 있습니다.
• Use a clearly defined data layer [Strongly recommended]: 명확히 정의된 data layer 사용 [강력 추천];
data layer는 앱 data를 앱의 나머지 부분으로 노출합니다. 그리고 광범위하고 대부분의 business(비즈니스) logic(논리)를 가지고 있습니다.
- 당신은 repositories(레포지토리)를 만들어야 합니다. 비록 하나의 data source(소스)밖에 없더라도 말이죠.
- 작은 앱에선, data layer types(형식)은 data package(패키지) 또는 module에 있어야 합니다.
• Use a clearly defined UI layer [Strongly recommended]: 명확히 정의된 UI layer 사용 [강력 추천];
UI layer는 앱 data를 화면에 나타내고 user(사용자)와 상호작용하는 첫 번째 지점입니다.
- 작은 앱에선, data layer types를 ui package 또는 module에 넣을 수 있습니다.
더 많은 UI layer의 좋은 사례는 여기에 있습니다.
• The data layer should expose application data using a repository [Strongly recommended]: data layer는 앱 data를 repository를 사용해서 노출해야 합니다. [강력 추천];
UI layer에 있는 composables, activities 또는 ViewModels 같은 요소들은 data source와 직접적으로 상호작용해서는 안 됩니다. data sources의 예시는 아래와 같습니다.
- Databases, DataStore, SharedPreferences, Firebase APIs.
- GPS 위치 제공자.
- Bluetooth data 제공자.
- Network connectivity status 제공자.
• Use coroutines and flows [Strongly recommended]: Coroutines와 flows 사용 [강력 추천];
Coroutines와 flows를 사용하여 각 layers 사이를 통신.
더 많은 coroutines 사용 사례를 보려면 여기로.
• Use a domain layer [Recommended in big apps]: domain layer 사용 [큰 앱에 추천];
use cases인 domain layer를 사용. 만약 당신의 data layer가 많은 ViewModel에 걸쳐 접근되어 business logic의 재사용이 필요하다거나 당신이 특정한 ViewModel의 business logic의 복잡성을 간단하게 만들고 싶다면 사용하세요.
⊙ UI layer
UI layer의 역할은 앱 data를 화면에 보여주고 user와 상호작용하는 첫 번째 지점입니다. 아래의 좋은 사례를 제시합니다.
• Follow Unidirectional Data Flow(UDF) [Strongly recommended]: 단방향 data 흐름을 따름 [강력 추천];
Unidirectional Data Flow(UDF) 원칙에 따라, ViewModels는 UI state를 노출하고 observer(추적 가능한) 패턴을 사용합니다. UI로부터 ViewModel 함수 호출로 actions(액션)을 받습니다.
• Use AAC ViewModels if their benefits apply to your app [Strongly recommended]: 당신의 앱에 사용해서 얻을 이점이 있다면 AAC ViewModel을 사용합니다. [강력 추천];
Business logic을 다루기 위해 AAC ViewModel을 사용합니다. 그리고 앱 data를 최신화하여 UI state를 UI로 노출합니다. (Compose 또는 Android Views).
더 많은 ViewModel 사례는 여기를 보세요.
ViewModels의 이점은 여기를 보세요.
• Use lifecycle-aware UI state collection [Strongly recommended]: 생명 주기를 이해하는 UI state 수신부를 사용하세요 [강력 추천];
적절한 lifecycle-aware(생명 주기 이해) 한 coroutine builder를 사용하여 UI state를 UI로부터 수신합니다. View 시스템에서는 repeatOnLifecycle를 사용하고, Compose에선 collectAsStateWithLifecycle을 사용합니다.
더 알고 싶다면, repeatOnLifecycle을 읽어보세요.
더 알고 싶다면 collectAsStateWithLifecycle을 읽어보세요.
• Do not send events from the ViewModel to the UI [Strongly recommended]: ViewModel에서 event를 UI로 보내지 마세요 [강력 추천];
Event를 ViewModel에서 즉시 처리합니다. 그리고 state를 최신화하여 event 처리 결과를 관리합니다. 더 알고 싶다면 UI events를 보세요.
• Use a single-activity application [Recommended]: 앱에 하나의 activity를 사용하세요 [추천];
Navigation Fragment 또는 Navigation Compose를 사용하여 다른 화면 간에 화면 전환을 관리하고 하나 이상의 화면을 가지고 있다면 deep link(딥 링크)를 적용합니다.
• Use Jetpack Compose [Recommended]: Jetpack Compose 사용 [추천];
휴대폰, 태블릿 폴더블, 워치를 위해 Jetpact Compose를 사용하세요.
아래의 코드는 UI state를 lifecycle-aware 하게 수집하는 방법입니다.
Views
Compose
⊙ ViewModel:;
ViewModels는 UI state를 제공해 주는 역할과 data layer에 접근하는 역할이 있습니다. 아래에는 ViewModel의 좋은 예시를 보여줍니다.
• ViewModels should be agnostic of the Android lifecycle [Strong recommended]: ViewModels는 Android lifecycle을 몰라야 합니다. [강력 추천];
ViewModels는 Lifecycle-related type을 가지고 있어선 안됩니다. Activity, Fragment, Context, Resources를 dependency(의존성)으로 전달해서는 안 됩니다. 만약 Context가 ViewModel에 필요하다면, 올바르게 layer를 구성했는지 평가하세요.
• Use coroutines and flows [Strongly recommended]: Coroutines와 flows를 사용 [강력 추천];
Data와 Domain(도메인) layers와 ViewModel 상호작용.
- Kotlin flows를 사용하여 앱 data 수신.
- suspend 함수로 viewModelScope에서 actions 실행.
• Use ViewModels at screen level [Strongly recommended]: 화면 단계에서 ViewModel 사용 [강력 추천];
재사용 가능한 UI에서 ViewModels를 사용하지 마세요. ViewModels는 이러한 상황에 사용합니다.
- Screen-level composables.
- Activities/Fragments in Views.
- Jetpack Navigation를 사용한 destinations(도착지) 또는 graphs(그래프).
• Use plain state holder classes in reusable UI components [Strongly recommended]: 재사용 가능한 UI 요소에 plain(일반) state holder classes를 사용 [강력 추천];
plain state holder classes로 UI 요소의 복잡한 것을 관리. 이러한 것을 함으로써 state는 hoisted(호이스트) 되어 외부에서 관리됨.
• Do not use AndroidViewModel [Recommended]: AndroidViewModel은 사용하지 말 것 [추천];
AndroidViewModel보다는 ViewModel을 사용. Application class(클래스)는 ViewModel에서 사용되어서는 안됩니다. 대신 dependency를 UI나 data layer로 옮깁니다.
• Expose a UI state [Recommended]: UI state 노출 [추천];
ViewModels는 data를 UI에 넘겨야 합니다. 보통 uiState라는 이름으로 넘깁니다. 만약 UI가 다양하고 관계없는 data의 조각을 보여준다면 ViewModel은 여러 UI state properties(속성)을 노출할 수 있습니다.
- uiState를 StateFlow로 만들기.
- 만약 data가 다른 layer에서 stream(연속)으로 온다면 uiState를 stateIn과 WhileSubscribed(5000)을 사용하여 만들기
- Data layer에서 stream이 없이 전달되는 간단한 사례의 경우, MutableStateFlow를 변경 불가능한 StateFlow처럼 노출하는 게 가능합니다.
- {Screen}UiState를 data, loading(로딩), errors(오류)를 가진 data class로 다룰 수 있습니다. 이 class는 상태가 독점적이라면 sealed class가 될 수도 있습니다.
아래의 코드는 ViewModel에서 UI state를 노출하는 코드입니다.
⊙ Lifecycle: 생명주기;
아래는 Android lifecycle에 적합한 사례들입니다.
• Do not override lifecycle methods in Activities or Fragments [Strongly recommended]: Activities와 Fragments에 있는 lifecycle을 override(오버라이드) 하지 마세요. [강력 추천];
Activities 또는 Fragments의 onResume 같은 lifecycle 함수를 override 하지 마세요. 대신 LifecycleObserver를 사용하세요. 앱이 특정 Lifecycle.State의 상태에서 작동해야 한다면, repeatOnLifecycle API를 사용하세요.
아래의 코드는 Lifecycle state에 실행하는 코드입니다.
Views
Compose
⊙ Handle dependencies: 의존성 관리;
요소 간 dependencies를 관리할 때, 봐야 할 사례들이 있습니다.
• Use dependency injection [Strongly recommended]: 의존성 주입 사용 [강력 추천];
dependency injection은 좋은 사례입니다. 가능하다면 constructor(생성자) injection을 사용하세요.
• Scope to a component when necessary [Strongly recommended]: 필요할 때, 요소에 맞추기 [강력 추천];
type이 변경되는 data가 공유되어야 하거나, 초기화 비용이 높을 때 그리고 앱에 전반적으로 넓게 사용될 때, dependency container(컨테이너)에 맞춥니다.
• Use Hilt [Recommended]: Hilt를 사용하세요 [추천];
Hilt(힐트) 또는 manual(수동) dependency injection을 간단한 앱에 사용하세요. Hilt는 복잡한 앱에도 사용 가능합니다. 예를 들면,
- ViewModels와 함께 많은 화면들 - 통합.
- WorkManager 사용 - 통합.
- ViewModels에 맞춰진 nav graph 같은 진보된 Navigation 사용 - 통합.
⊙ Testing: 테스트;
테스트의 좋은 사례들입니다.
• Know what to test [Strongly recommended]: 무엇을 테스트하는지 알 것 [강력 추천];
당신의 앱이 hello world 앱처럼 대충 만든 앱이 아니라면 당신은 테스트를 해야 합니다. 최소한 이것들은 해주세요.
- ViewModels와 Flows Unit(유닛) test
- data layer entities(엔티티) Unit test. repositories와 data sources.
- CI에서 regression(회귀)에서 유용한 UI navigation tests
• Prefer fakes to mocks [Strongly recommended]: 가짜를 만들어서 테스트 [강력 추천];
더 알아보려면, Use test doubles in Android documentation을 읽어보세요.
• Test StateFlows [Strongly recommended]: StateFlows 테스트 [강력 추천];
StateFlow를 테스트할 때 다음을 준수하세요.
- 가능하다면, value property로 assert 하세요.
- 만약 WhileSubsribed를 사용한다면, collectJob을 사용해 주세요.
더 많은 정보는 What to test in Android DAC guide를 참고해 주세요.
⊙ Models: 모델;
당신의 앱에 models를 사용한다면, 아래의 좋은 사례를 참고하세요.
• Create a model per layer in complex apps [Recommended]: 복잡한 앱에선 각 layer마다 Model을 생성 [추천];
복잡한 앱에서 각 layer마다 새로운 model을 만들거나 요소를 만듭니다. 이러한 경우가 필요할 때를 고려해 보세요.
- 원격 data source에서 들어오는 네트워크 model을 앱에서 필요한 data를 가진 간단한 model로 변환.
- Repositories는 DAO models를 UI layer에서 필요한 간단한 model로 변경할 수 있습니다.
- ViewModel은 UiState classes에 data layer models를 가질 수 있습니다.
⊙ Naming conventions: 작명 규칙;
당신의 코드에 작명을 할 때, 아래의 좋은 사례를 참고하세요.
• Naming methods [Optional]: 함수 작명 [선택 사항];
함수는 verb(동사) 형식이어야 합니다. 예를 들면, makePayment().
• Naming properties [Optional]: 속성 작명 [선택 사항];
Properties는 noun[명사] 형식이어야 합니다. 예를 들면, inProgressTopicSelection.
• Naming streams of data [Optional]: 흐름 data 작명 [선택 사항];
Class가 Flow stream, LiveData 또는 어떠한 stream을 노출할 때, get{model}Stream()으로 짓습니다. 예를 들면, getAuthorStream(): Flow<Author>. 만약 함수가 models 목록을 반환한다면 model 이름은 복수형으로 합니다. getAuthorsStream(): Flow<List<Author>>
• Naming interfaces implementations [Optional]: 인터페이스 작명 [선택 사항];
Interface의 이름은 의미가 있어야 합니다. 좋은 이름이 없다면 Default를 접두사로 사용합니다. 예를 들면, NewsRepository interface는 OfflineFirstNewsRepository 또는 InMemoryNewsRepository가 될 수 있습니다. 좋은 이름이 없다면, DefaultNewsRepository도 괜찮습니다. Fake로 만든 것은 Fake를 접두사로 사용합니다. FakeAuthorsRepository.
끝.
카테고리: Android
댓글
댓글 쓰기
궁금한 점은 댓글 달아주세요.
Comment if you have any questions.