Activity 하나를 두고 화면들을 그 안의 목적지로 관리하는 구조.
@AndroidEntryPoint
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent { AppTheme { AppNavHost() } }
}
}
화면마다 Activity를 두면 무엇이 아픈가
- 전환 애니메이션을 세밀하게 제어하기 어렵다
- 화면 간 데이터 전달이 Intent extra(직렬화) 로만 가능하다
- 공유 요소 전환이 복잡하다
- 각 Activity 가 자기 생명주기를 따로 관리한다
- 매니페스트에 전부 선언해야 한다
- 상태 공유가 어렵다 (Activity 범위 ViewModel 을 못 나눈다)
단일 Activity가 주는 것
- 백스택을 NavController 한 곳에서 본다
- 화면 간 공유 상태를 Activity 범위 ViewModel 로 자연스럽게 둔다
- 전환 애니메이션이 컴포지션 안의 문제가 된다
- 딥링크가 그래프 하나에 모인다
- 인자 전달이 타입 안전해진다
그래도 Activity를 나누는 경우
-
외부에서 진입하는 독립 화면 (공유 인텐트 수신 · 위젯 설정)
-
완전히 다른 태스크로 띄워야 하는 화면
-
서로 다른 launchMode 나 테마가 필요한 경우
-
다른 프로세스로 격리해야 하는 화면 (WebView 격리 등)
-
"단일" 이 "반드시 하나" 를 뜻하지는 않는다 — 기본이 하나라는 뜻이다
주의 — Activity 범위 상태의 수명
// 이 ViewModel 은 앱이 살아 있는 내내 산다
val sharedVm: SharedViewModel = hiltViewModel(LocalActivity.current as ComponentActivity)
편리하지만 남용하면
- 로그아웃해도 이전 사용자 데이터가 남는다
- 어떤 화면이 무엇을 바꾸는지 추적이 안 된다
- 사실상 전역 변수가 된다
대부분의 공유는 '그래프 범위' 로 충분하다
시스템 뒤로가기
// Android 13+ 예측형 뒤로가기
BackHandler(enabled = hasUnsavedChanges) { showConfirmDialog = true }
AndroidManifest 에 android:enableOnBackInvokedCallback="true" 를 켜야 예측형 뒤로가기(뒤 화면 미리보기) 애니메이션이 동작한다
면접 함정
- ❌ "단일 Activity면 생명주기를 신경 안 써도 된다" → Activity 하나의 생명주기는 여전히 있고, 프로세스 죽음도 그대로다.
- ❌ "Fragment를 안 쓰면 단일 Activity" → 목적지가 Fragment든 Composable이든 구조는 같다.
예측형 뒤로가기
// Android 13+ · 34 부터 기본 활성화 경로
<application android:enableOnBackInvokedCallback="true">
// Compose 에서 뒤로가기를 가로챈다
BackHandler(enabled = hasUnsavedChanges) { showConfirm = true }
// 진행률까지 받는 예측형 API
PredictiveBackHandler(enabled = true) { progress: Flow<BackEventCompat> ->
progress.collect { drawerOffset = it.progress }
// 정상 완료되면 여기 도달 → 실제로 닫는다
}
onBackPressed() 오버라이드는 폐기됐다
- OnBackPressedDispatcher · BackHandler 를 쓴다
- 여러 컴포넌트가 각자 뒤로가기를 다룰 수 있고 우선순위가 정해진다
화면 하나가 곧 컴포저블 하나일 필요는 없다
// 태블릿/폴더블에서 목록+상세를 함께 보여 준다
val windowSize = calculateWindowSizeClass(activity)
when (windowSize.widthSizeClass) {
WindowWidthSizeClass.Compact -> NavHost(...) // 화면 전환
else -> Row { ListPane(); DetailPane() } // 나란히
}
Activity 단위였다면 이런 적응형 레이아웃이 훨씬 어렵다 단일 Activity + Compose 의 실질적 이득이 여기서 나온다