이 페이지에서는 여러 아키텍처 권장사항을 설명합니다. 앱의 품질과 견고성, 확장성을 개선하는 데 참고하세요. 이를 통해 앱을 더 쉽게 유지관리하고 테스트할 수도 있습니다.
아래 권장사항은 주제별로 그룹화되어 있습니다. 팀에서 권장하는 강도를 반영하여 각각 우선순위가 지정되어 있습니다. 우선순위 목록은 다음과 같습니다.
- 적극 권장됨: 자체 접근 방식과 기본적으로 충돌하지 않는 한 권장사항을 구현해야 합니다.
- 권장됨: 이 권장사항으로 앱이 개선될 가능성이 높습니다.
- 선택사항: 특정 상황에서 이 권장사항으로 앱이 개선될 수 있습니다.
계층화된 아키텍처
권장되는 계층화된 아키텍처는 관심사 분리를 선호합니다. 데이터 모델에서 UI를 구동하고 단일 정보 소스 원칙을 준수하며 단방향 데이터 흐름 원칙을 따릅니다. 다음은 계층화된 아키텍처에 관한 권장사항입니다.
권장사항 | 설명 |
---|---|
명확하게 정의된 데이터 레이어를 사용합니다.
적극 권장됨 |
데이터 레이어는 앱의 나머지 부분에 애플리케이션 데이터를 노출하고 앱의 대다수 비즈니스 로직을 포함합니다.
|
명확하게 정의된 UI 레이어를 사용합니다.
적극 권장됨 |
UI 레이어는 화면에 애플리케이션 데이터를 표시하고 사용자 상호작용의 기본 지점으로 기능합니다.
|
데이터 레이어는 저장소를 사용하여 애플리케이션 데이터를 노출해야 합니다.
적극 권장됨 |
컴포저블, 활동, ViewModel과 같은 UI 레이어의 구성요소는 데이터 소스와 직접 상호작용해서는 안 됩니다. 데이터 소스의 예는 다음과 같습니다.
|
코루틴 및 흐름을 사용합니다.
적극 권장됨 |
코루틴 및 흐름을 사용하여 레이어 간에 통신합니다. |
도메인 레이어를 사용합니다.
큰 앱에 권장됨 |
여러 ViewModel에서 데이터 레이어와 상호작용하는 비즈니스 로직을 재사용해야 하는 경우 또는 특정 ViewModel의 비즈니스 로직 복잡성을 단순화하려는 경우 도메인 레이어 사용 사례를 활용합니다. |
UI 레이어
UI 레이어의 역할은 화면에 애플리케이션 데이터를 표시하고 사용자 상호작용의 기본 지점으로 기능하는 것입니다. 다음은 UI 레이어에 관한 권장사항입니다.
권장사항 | 설명 |
---|---|
단방향 데이터 흐름(UDF)을 따릅니다.
적극 권장됨 |
단방향 데이터 흐름(UDF) 원칙을 따릅니다. 여기서 ViewModel은 관찰자 패턴을 사용하여 UI 상태를 노출하고 메서드 호출을 통해 UI에서 작업을 수신합니다. |
앱에 이점이 적용되는 경우 AAC ViewModel을 사용합니다.
적극 권장됨 |
AAC ViewModel을 사용하여 비즈니스 로직을 처리하고, 애플리케이션 데이터를 가져와 UI 상태를 UI(Compose 또는 Android 뷰)에 노출합니다. |
수명 주기 인식 UI 상태 컬렉션을 사용합니다.
적극 권장됨 |
적절한 수명 주기 인식 코루틴 빌더(뷰 시스템의 repeatOnLifecycle , Jetpack Compose의 collectAsStateWithLifecycle )를 사용하여 UI에서 UI 상태를 수집합니다.
|
ViewModel에서 UI로 이벤트를 전송하면 안 됩니다.
적극 권장됨 |
ViewModel에서 즉시 이벤트를 처리하고 이벤트 처리 결과로 상태 업데이트를 발생시킵니다. UI 이벤트에 관해 자세히 알아보세요. |
단일 활동 애플리케이션을 사용합니다.
권장됨 |
앱에 화면이 두 개 이상 있으면 Navigation Fragments 또는 Navigation Compose를 사용하여 화면 간에 이동하고 딥 링크로 앱에 연결합니다. |
Jetpack Compose를 사용합니다.
권장됨 |
Jetpack Compose를 사용하여 스마트폰, 태블릿, 폴더블, Wear OS용 새 앱을 빌드합니다. |
다음 스니펫은 수명 주기 인식 방식으로 UI 상태를 수집하는 방법을 설명합니다.
뷰
class MyFragment : Fragment() {
private val viewModel: MyViewModel by viewModel()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
viewLifecycleOwner.lifecycleScope.launch {
viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
viewModel.uiState.collect {
// Process item
}
}
}
}
}
Compose
@Composable
fun MyScreen(
viewModel: MyViewModel = viewModel()
) {
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
}
ViewModel
ViewModel은 UI 상태와 데이터 레이어 액세스 권한을 제공합니다. 다음은 ViewModel 권장사항입니다.
권장사항 | 설명 |
---|---|
ViewModel은 Android 수명 주기에 구애받지 않아야 합니다.
적극 권장됨 |
ViewModel은 Lifecycle 관련 유형에 대한 참조를 보유해서는 안 됩니다. Activity, Fragment, Context 또는 Resources 를 종속 항목으로 전달하면 안 됩니다.
ViewModel에 Context 가 필요한 경우 올바른 레이어에 있는지 적극적으로 평가해야 합니다. |
코루틴 및 흐름을 사용합니다.
적극 권장됨 |
ViewModel은 다음을 사용하여 데이터 또는 도메인 레이어와 상호작용합니다.
|
화면 수준에서 ViewModel을 사용합니다.
적극 권장됨 |
UI의 재사용 가능한 부분에서 ViewModel을 사용하면 안 됩니다. 다음에서 ViewModel을 사용해야 합니다.
|
재사용 가능한 UI 구성요소에서 일반 상태 홀더 클래스를 사용합니다.
적극 권장됨 |
일반 상태 홀더 클래스를 사용하여 재사용 가능한 UI 구성요소의 복잡성을 처리합니다. 이렇게 하면 상태를 외부에서 끌어올려 제어할 수 있습니다. |
AndroidViewModel 을 사용하면 안 됩니다.권장됨 |
AndroidViewModel 이 아닌 ViewModel 클래스를 사용합니다. Application 클래스는 ViewModel에서 사용하면 안 됩니다. 대신 종속 항목을 UI 또는 데이터 레이어로 이동합니다. |
UI 상태를 노출합니다.
권장됨 |
ViewModel은 단일 속성 uiState 를 통해 UI에 데이터를 노출해야 합니다. UI에 관련 없는 여러 데이터가 표시되면 VM은 여러 UI 상태 속성을 노출할 수 있습니다.
|
다음 스니펫은 ViewModel에서 UI 상태를 노출하는 방법을 설명합니다.
@HiltViewModel
class BookmarksViewModel @Inject constructor(
newsRepository: NewsRepository
) : ViewModel() {
val feedState: StateFlow<NewsFeedUiState> =
newsRepository
.getNewsResourcesStream()
.mapToFeedState(savedNewsResourcesState)
.stateIn(
scope = viewModelScope,
started = SharingStarted.WhileSubscribed(5_000),
initialValue = NewsFeedUiState.Loading
)
// ...
}
수명 주기
다음은 Android 수명 주기를 사용하기 위한 권장사항입니다.
권장사항 | 설명 |
---|---|
활동 또는 프래그먼트에서 수명 주기 메서드를 재정의하면 안 됩니다.
적극 권장됨 |
활동이나 프래그먼트에서 onResume 과 같은 수명 주기 메서드를 재정의하면 안 됩니다. 대신 LifecycleObserver 를 사용하세요. 수명 주기가 특정 Lifecycle.State 에 도달할 때 앱에서 작업을 실행해야 하는 경우 repeatOnLifecycle API를 사용합니다. |
다음 스니펫은 특정 수명 주기 상태에서 작업을 실행하는 방법을 설명합니다.
뷰
class MyFragment: Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
viewLifecycleOwner.lifecycle.addObserver(object : DefaultLifecycleObserver {
override fun onResume(owner: LifecycleOwner) {
// ...
}
override fun onPause(owner: LifecycleOwner) {
// ...
}
}
}
}
Compose
@Composable
fun MyApp() {
val lifecycleOwner = LocalLifecycleOwner.current
DisposableEffect(lifecycleOwner, ...) {
val lifecycleObserver = object : DefaultLifecycleObserver {
override fun onStop(owner: LifecycleOwner) {
// ...
}
}
lifecycleOwner.lifecycle.addObserver(lifecycleObserver)
onDispose {
lifecycleOwner.lifecycle.removeObserver(lifecycleObserver)
}
}
}
종속 항목 처리
구성요소 간 종속 항목을 관리할 때는 다음과 같은 권장사항을 따라야 합니다.
권장사항 | 설명 |
---|---|
종속 항목 삽입을 사용합니다.
적극 권장됨 |
종속 항목 삽입 권장사항, 가능하면 주로 생성자 삽입을 사용합니다. |
필요한 경우 구성요소로 범위를 지정합니다.
적극 권장됨 |
공유해야 하는 변경 가능한 데이터가 유형에 포함되어 있거나, 유형이 초기화 비용이 많이 들고 앱에서 널리 사용되는 경우 종속 항목 컨테이너로 범위를 지정합니다. |
Hilt를 사용합니다.
권장됨 |
간단한 앱에서는 Hilt 또는 수동 종속 항목 삽입을 사용합니다. 프로젝트가 충분히 복잡하다면 Hilt를 사용합니다. 다음과 같은 경우를 예로 들 수 있습니다.
|
테스트
다음은 테스트 권장사항입니다.
권장사항 | 설명 |
---|---|
테스트 항목을 파악합니다.
적극 권장됨 |
프로젝트가 Hello World 앱만큼이나 간단하지 않은 이상 최소한 다음을 사용하여 테스트해야 합니다.
|
모의 테스트보다 가짜 테스트를 선호합니다.
적극 권장됨 |
자세한 내용은 Android에서 테스트 더블 사용 문서를 참고하세요. |
StateFlow를 테스트합니다.
적극 권장됨 |
StateFlow 테스트 시:
|
자세한 내용은 Android DAC에서 테스트할 항목 가이드를 참고하세요.
모델
앱에서 모델을 개발할 때 다음 권장사항을 준수해야 합니다.
권장사항 | 설명 |
---|---|
복잡한 앱에서는 레이어별 모델을 만듭니다.
권장됨 |
복잡한 앱에서는 적절하다면 다양한 레이어나 구성요소에서 새 모델을 만듭니다. 다음 예를 고려하세요.
|
이름 지정 규칙
코드베이스의 이름을 지정할 때는 다음 권장사항에 유의하세요.
권장사항 | 설명 |
---|---|
메서드의 이름을 지정합니다.
선택사항 |
메서드는 동사구여야 합니다. 예를 들어 makePayment() 가 이에 해당합니다. |
속성 이름을 지정합니다.
선택사항 |
속성은 명사구여야 합니다. 예를 들어 inProgressTopicSelection 이 이에 해당합니다. |
데이터 스트림 이름을 지정합니다.
선택사항 |
클래스가 Flow 스트림, LiveData 또는 기타 스트림을 노출하는 경우 이름 지정 규칙은 get{model}Stream() 입니다. getAuthorStream(): Flow<Author> 를 예로 들 수 있습니다. 이 함수가 모델 목록을 반환하면 모델 이름은 다음과 같이 복수형이어야 합니다. getAuthorsStream(): Flow<List<Author>> |
인터페이스 구현 이름을 지정합니다.
선택사항 |
인터페이스 구현의 이름은 의미가 있어야 합니다. 더 나은 이름을 찾을 수 없는 경우 접두사로 Default 를 사용합니다. 예를 들어 NewsRepository 인터페이스의 경우 OfflineFirstNewsRepository 또는 InMemoryNewsRepository 가 있을 수 있습니다. 적절한 이름을 찾을 수 없는 경우 DefaultNewsRepository 를 사용합니다.
가짜 구현은 FakeAuthorsRepository 와 같이 Fake 접두사를 붙여야 합니다. |