티스토리 뷰



들어가며

Compose로 개발하면서 "Activity 라이프사이클 아직도 알아야 해?" 라는 의문이 들었다. 결론부터 말하면 알아야 하는데, 생각보다 적게 알아도 된다.

React 개발자 관점에서 정리해봤다.


Activity 라이프사이클 기본

onCreate → onStart → onResume → [실행 중] → onPause → onStop → onDestroy

콜백 상태 메모리

onCreate 생성됨 할당
onStart 화면에 보임 유지
onResume 상호작용 가능 유지
onPause 일부 가려짐 유지
onStop 완전히 안 보임 유지
onDestroy 죽음 해제

상황별 호출 정리

상황 호출

다이얼로그 뜸 onPause
홈 버튼 / 다른 앱 전환 / 백버튼 onPause → onStop
화면 회전 / 다크모드 전환 onDestroy → onCreate (재생성)
최근 앱에서 스와이프 onDestroy (진짜 종료)

참고: Android 12부터 백버튼 동작 변경

예전: 백버튼 → 앱 종료
지금: 백버튼 → 백그라운드 전환 (onStop)

진짜 종료는 최근 앱에서 스와이프해야 한다.


React와 비교

Android React 의미

onCreate 마운트 최초 생성
onDestroy 언마운트 제거
onPause/onResume visibilitychange 화면 전환

핵심 차이

브라우저: 탭 닫기 전까지 안 죽음 → 라이프사이클 단순

Android: 시스템이 언제든 앱 죽일 수 있음 → 라이프사이클 복잡

// React - 탭 전환 감지 (선택적)
useEffect(() => {
  const handleVisibility = () => {
    if (document.hidden) pause()
    else play()
  }
  document.addEventListener('visibilitychange', handleVisibility)
  return () => document.removeEventListener('visibilitychange', handleVisibility)
}, [])
// Android - 백그라운드 전환 감지 (필수에 가까움)
DisposableEffect(lifecycleOwner) {
    val observer = LifecycleEventObserver { _, event ->
        when (event) {
            Lifecycle.Event.ON_STOP -> pause()
            Lifecycle.Event.ON_START -> play()
            else -> {}
        }
    }
    lifecycleOwner.lifecycle.addObserver(observer)
    onDispose { lifecycleOwner.lifecycle.removeObserver(observer) }
}

Compose에서 진짜 신경 써야 할 것

결론: 2가지만

  1. 백그라운드 리소스 관리 (onStop)
  2. 상태 복원 (Configuration Change)

신경 안 써도 되는 것

  • onPause/onResume 구분: Compose Dialog는 같은 Activity 안이라 라이프사이클 변화 없음. 시스템 다이얼로그 떠도 Activity 살아있으니 특별히 할 게 없음.

1. 백그라운드 리소스 관리

사실 대부분 라이브러리가 해줌

// CameraX - lifecycleOwner 넘기면 끝
cameraProvider.bindToLifecycle(
    lifecycleOwner,
    cameraSelector,
    preview
)

// ExoPlayer - 자동 처리 옵션 있음
player.setPlayWhenReady(true)

직접 해야 하는 경우

Analytics 로깅 정도?

DisposableEffect(lifecycleOwner) {
    val observer = LifecycleEventObserver { _, event ->
        when (event) {
            Lifecycle.Event.ON_RESUME -> analytics.logScreenView("Home")
            Lifecycle.Event.ON_PAUSE -> analytics.logScreenExit("Home")
            else -> {}
        }
    }
    lifecycleOwner.lifecycle.addObserver(observer)
    onDispose { lifecycleOwner.lifecycle.removeObserver(observer) }
}

2. 상태 복원 (Configuration Change)

화면 회전, 다크모드 전환 시 Activity가 죽었다 다시 생성된다.

상황 remember rememberSaveable ViewModel

Recomposition
화면 회전
프로세스 킬
// ❌ 화면 회전하면 날아감
var text by remember { mutableStateOf("") }

// ✅ 화면 회전해도 유지
var text by rememberSaveable { mutableStateOf("") }

판단 기준

  • 임시 UI 상태 (애니메이션 등) → remember
  • 사용자 입력값 (폼 데이터) → rememberSaveable
  • 비즈니스 로직 상태 → ViewModel

LocalLifecycleOwner 사용법

기본 패턴

@Composable
fun MyScreen() {
    val lifecycleOwner = LocalLifecycleOwner.current
    
    // 라이브러리에 넘김
    cameraProvider.bindToLifecycle(lifecycleOwner, ...)
}

어디서 오는 거?

setContent 내부에서 CompositionLocal로 주입됨. React의 Context와 같은 개념.

// setContent 내부 (간략화)
CompositionLocalProvider(
    LocalLifecycleOwner provides this,  // Activity
    LocalContext provides this,
) {
    content()
}

Navigation 쓰면 달라짐

NavHost(...) {
    composable("home") { 
        // LocalLifecycleOwner = NavBackStackEntry (화면별로 분리)
    }
}

상황 LocalLifecycleOwner

Activity + setContent Activity
Fragment + ComposeView Fragment
Navigation Compose NavBackStackEntry

Navigation 2 vs 3 (LifecycleOwner 관점)

Nav2: 자동

NavHost(...) {
    composable("home") { 
        // 그냥 됨. NavBackStackEntry가 LifecycleOwner.
    }
}

Nav3: 명시적

NavDisplay(
    backStack = backStack,
    entryDecorators = listOf(
        rememberSceneSetupNavEntryDecorator(),      // Lifecycle
        rememberSavedStateNavEntryDecorator(),      // 상태 저장
        rememberViewModelStoreNavEntryDecorator()   // ViewModel scoping
    ),
)

decorator 안 넣으면 LifecycleOwner도 없고, ViewModel scoping도 안 됨.

왜 바꿨나?

Nav2: 배터리 포함 (자동) → 커스텀 어려움
Nav3: 배터리 별매 (명시적) → 필요한 것만 조합

Compose 철학이 "암묵적 마법 X, 명시적 조합 O"라서.


NavEntry의 Lifecycle 상태

Navigation 사용 시 각 화면(NavEntry)은 Activity처럼 상태를 가짐.

상태 의미

CREATED 백스택에 있음
STARTED 화면 전환 애니메이션 중
RESUMED 현재 화면
DESTROYED 백스택에서 제거됨
Home → Detail 이동 (애니메이션 중)
├── Home: STARTED
└── Detail: STARTED

애니메이션 끝
├── Home: CREATED (백스택)
└── Detail: RESUMED (현재)

이게 왜 중요하냐면:

LaunchedEffect(Unit) {
    // RESUMED일 때만 실행됨
    // 백스택에 있으면 (CREATED) 중단됨
}

백스택 화면에서 불필요한 작업 안 돌아감.


정리

콜백 Compose에서 할 일

onPause 거의 없음
onStop 거의 없음 (라이브러리가 처리)
onDestroy (Configuration Change) remember → rememberSaveable 판단

진짜 결론: Compose에서 라이프사이클 관련해서 신경 쓸 건 rememberSaveable 쓸지 말지 정도. 나머지는 라이브러리한테 lifecycleOwner 넘기면 끝.

댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
TAG
more
«   2026/04   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30
글 보관함