이 페이지에서는 여러 아키텍처 권장사항을 설명합니다. 앱의 품질과 견고성, 확장성을 개선하는 데 참고하세요. 이를 통해 앱을 더 쉽게 유지관리하고 테스트할 수도 있습니다.
아래 권장사항은 주제별로 그룹화되어 있습니다. 각각 권장사항의 강도를 반영하여 우선순위가 지정되어 있습니다. 우선순위 목록은 다음과 같습니다.
- 적극 권장됨: 자체 접근 방식과 기본적으로 충돌하지 않는 한 권장사항을 구현해야 합니다.
- 권장됨: 이 권장사항으로 앱이 개선될 가능성이 높습니다.
- 선택사항: 특정 상황에서 이 권장사항으로 앱이 개선될 수 있습니다.
계층화된 아키텍처
권장되는 계층화된 아키텍처는 관심사 분리를 선호합니다. 데이터 모델에서 UI를 구동하고 단일 정보 소스 원칙을 준수하며 단방향 데이터 흐름 원칙을 따릅니다. 다음은 계층화된 아키텍처에 관한 권장사항입니다.
| 권장사항 | 설명 |
|---|---|
| 명확하게 정의된 데이터 레이어를 사용합니다.
적극 권장됨 |
데이터 레이어는 앱의 나머지 부분에 애플리케이션 데이터를 노출하고 앱의 대다수 비즈니스 로직을 포함합니다.
|
| 명확하게 정의된 UI 레이어를 사용합니다.
적극 권장됨 |
UI 레이어는 화면에 애플리케이션 데이터를 표시하고 사용자 상호작용의 기본 지점으로 기능합니다. Jetpack Compose는 앱의 UI를 빌드하기 위한 권장되는 최신 도구 키트입니다.
|
| 저장소를 사용하여 데이터 레이어에서 애플리케이션 데이터를 노출합니다.
적극 권장됨 |
컴포저블이나 ViewModel과 같은 UI 레이어의 구성요소가 데이터 소스와 직접 상호작용하지 않도록 합니다. 데이터 소스의 예는 다음과 같습니다.
|
| 코루틴 및 흐름을 사용합니다.
적극 권장됨 |
코루틴 및 흐름을 사용하여 레이어 간에 통신합니다.
코루틴 권장사항에 관한 자세한 내용은 Android의 코루틴 권장사항을 참고하세요. |
| 도메인 레이어를 사용합니다.
큰 앱에 권장됨 |
여러 ViewModel에서 데이터 레이어와 상호작용하는 비즈니스 로직을 재사용해야 하는 경우 또는 특정 ViewModel의 비즈니스 로직 복잡성을 단순화하려는 경우 사용 사례가 있는 도메인 레이어를 사용합니다. |
UI 레이어
UI 레이어의 역할은 화면에 애플리케이션 데이터를 표시하고 사용자 상호작용의 기본 지점으로 기능하는 것입니다. 다음은 UI 레이어에 관한 권장사항입니다.
| 권장사항 | 설명 |
|---|---|
| 단방향 데이터 흐름(UDF)을 따릅니다.
적극 권장됨 |
단방향 데이터 흐름(UDF) 원칙을 따릅니다. 여기서 ViewModel은 관찰자 패턴을 사용하여 UI 상태를 노출하고 메서드 호출을 통해 UI에서 작업을 수신합니다. |
| 앱에 이점이 적용되는 경우 AAC ViewModel을 사용합니다.
적극 권장됨 |
AAC ViewModel을 사용하여 비즈니스 로직을 처리하고, 애플리케이션 데이터를 가져와 UI 상태를 UI에 노출합니다.
ViewModel 권장사항에 관한 자세한 내용은 아키텍처 권장사항을 참고하세요. ViewModel의 이점에 관한 자세한 내용은 비즈니스 로직 상태 홀더로서의 ViewModel을 참고하세요. |
| 수명 주기 인식 UI 상태 컬렉션을 사용합니다.
적극 권장됨 |
적절한 수명 주기 인식 코루틴 빌더 collectAsStateWithLifecycle를 사용하여 UI에서 UI 상태를 수집합니다.
|
| ViewModel에서 UI로 이벤트를 전송하면 안 됩니다.
적극 권장됨 |
ViewModel에서 즉시 이벤트를 처리하고 이벤트 처리 결과로 상태 업데이트를 발생시킵니다. UI 이벤트에 관한 자세한 내용은 ViewModel 이벤트 처리를 참고하세요. |
| 단일 활동 애플리케이션을 사용합니다.
적극 권장됨 |
앱에 화면이 두 개 이상 있으면 Navigation 3을 사용하여 화면 간에 이동하고 딥 링크로 앱에 연결합니다. |
| Jetpack Compose를 사용합니다.
적극 권장됨 |
Jetpack Compose를 사용하여 스마트폰, 태블릿, 폴더블, Wear OS용 새 앱을 빌드합니다. |
다음 스니펫은 수명 주기 인식 방식으로 UI 상태를 수집하는 방법을 설명합니다.
@Composable
fun MyScreen(
viewModel: MyViewModel = viewModel()
) {
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
}
ViewModel
ViewModel은 UI 상태를 제공하고 데이터 레이어에 액세스하는 역할을 합니다. 다음은 ViewModel 권장사항입니다.
| 권장사항 | 설명 |
|---|---|
| ViewModel을 Android 수명 주기와 독립적으로 유지하세요.
적극 권장됨 |
ViewModel에서 수명 주기 관련 유형에 대한 참조를 보유하지 마세요. Activity, Context 또는 Resources를 종속 항목으로 전달하면 안 됩니다.
ViewModel에 Context가 필요한 경우 올바른 레이어에 있는지 신중하게 평가해야 합니다. |
| 코루틴 및 흐름을 사용합니다.
적극 권장됨 |
ViewModel은 다음을 사용하여 데이터 또는 도메인 레이어와 상호작용합니다.
|
| 화면 수준에서 ViewModel을 사용합니다.
적극 권장됨 |
UI의 재사용 가능한 부분에서 ViewModel을 사용하면 안 됩니다. 다음에서 ViewModel을 사용해야 합니다.
|
| 재사용 가능한 UI 구성요소에서 일반 상태 홀더 클래스를 사용합니다.
적극 권장됨 |
일반 상태 홀더 클래스를 사용하여 재사용 가능한 UI 구성요소의 복잡성을 처리합니다. 이렇게 하면 상태를 외부에서 끌어올려 제어할 수 있습니다. |
AndroidViewModel을 사용하면 안 됩니다.권장됨 |
AndroidViewModel이 아닌 ViewModel 클래스를 사용합니다. ViewModel에서 Application 클래스를 사용하지 마세요. 대신 종속 항목을 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
)
// ...
}
수명 주기
활동 수명 주기 작업 권장사항을 따르세요.
| 권장사항 | 설명 |
|---|---|
Activity 수명 주기 콜백을 재정의하는 대신 컴포저블에서 수명 주기 인식 효과를 사용하세요.
적극 권장됨 |
UI 관련 작업을 실행하기 위해
|
다음 스니펫은 특정 수명 주기 상태에서 작업을 실행하는 방법을 설명합니다.
@Composable
fun LocationChangedEffect(
locationManager: LocationManager,
onLocationChanged: (Location) -> Unit
) {
val currentOnLocationChanged by rememberUpdatedState(onLocationChanged)
LifecycleStartEffect(locationManager) {
val listener = LocationListener { newLocation ->
currentOnLocationChanged(newLocation)
}
try {
locationManager.requestLocationUpdates(
LocationManager.GPS_PROVIDER,
1000L,
1f,
listener,
)
} catch (e: SecurityException) {
// TODO: Handle missing permissions
}
onStopOrDispose {
locationManager.removeUpdates(listener)
}
}
}
종속 항목 처리
구성요소 간 종속 항목을 관리할 때는 권장사항을 따르세요.
| 권장사항 | 설명 |
|---|---|
| 종속 항목 삽입을 사용합니다.
적극 권장됨 |
종속 항목 삽입 권장사항, 가능하면 주로 생성자 삽입을 사용합니다. |
| 필요한 경우 구성요소로 범위를 지정합니다.
적극 권장됨 |
공유해야 하는 변경 가능한 데이터가 유형에 포함되어 있거나, 유형이 초기화 비용이 많이 들고 앱에서 널리 사용되는 경우 종속 항목 컨테이너로 범위를 지정합니다. |
| Hilt를 사용합니다.
권장됨 |
간단한 앱에서는 Hilt 또는 수동 종속 항목 삽입을 사용합니다. 프로젝트가 충분히 복잡하다면(예: 다음 중 하나가 포함된 경우) Hilt를 사용합니다.
|
테스트
다음은 테스트 권장사항입니다.
| 권장사항 | 설명 |
|---|---|
| 테스트 항목을 파악합니다.
적극 권장됨 |
프로젝트가 'hello world' 앱만큼 간단하지 않은 이상 테스트해야 합니다. 최소한 다음을 포함하세요.
|
| 모의 테스트보다 가짜 테스트를 선호합니다.
적극 권장됨 |
페이크 사용에 관한 자세한 내용은 Android에서 테스트 더블 사용을 참고하세요. |
| StateFlow를 테스트합니다.
적극 권장됨 |
StateFlow를 테스트할 때는 다음을 실행하세요.
|
자세한 내용은 Android에서 테스트할 항목 및 Compose 레이아웃 테스트를 참고하세요.
모델
앱에서 모델을 개발할 때 다음 권장사항을 준수하세요.
| 권장사항 | 설명 |
|---|---|
| 복잡한 앱에서는 레이어별 모델을 만듭니다.
권장됨 |
복잡한 앱에서는 적절하다면 다양한 레이어나 구성요소에서 새 모델을 만듭니다. 다음 예를 고려하세요.
|
이름 지정 규칙
코드베이스의 이름을 지정할 때는 다음 권장사항에 유의하세요.
| 권장사항 | 설명 |
|---|---|
| 메서드의 이름을 지정합니다.
선택사항 |
동사 구문을 사용하여 메서드 이름을 지정합니다(예: makePayment()). |
| 속성 이름을 지정합니다.
선택사항 |
명사구를 사용하여 속성 이름을 지정합니다(예: inProgressTopicSelection). |
| 데이터 스트림 이름을 지정합니다.
선택사항 |
클래스가 Flow 스트림 또는 기타 스트림을 노출하는 경우 이름 지정 규칙은 get{model}Stream입니다. 예: getAuthorStream(): Flow<Author>
함수가 모델 목록을 반환하면 복수형 모델 이름(getAuthorsStream(): Flow<List<Author>>)을 사용합니다. |
| 인터페이스 구현 이름을 지정합니다.
선택사항 |
인터페이스 구현에 의미 있는 이름을 사용합니다. 더 나은 이름을 찾을 수 없는 경우 접두사로 Default를 사용합니다. 예를 들어 NewsRepository 인터페이스의 경우 OfflineFirstNewsRepository 또는 InMemoryNewsRepository가 있을 수 있습니다. 적절한 이름을 찾을 수 없는 경우 DefaultNewsRepository를 사용합니다.
가짜 구현에는 FakeAuthorsRepository과 같이 Fake 접두사를 붙입니다. |
추가 리소스
Android 아키텍처에 관한 자세한 내용은 다음 추가 리소스를 참고하세요.