Android 앱 라이프사이클 설명: AI 생성 앱이 회전에서도 살아남는 이유
Source: Dev.to
액티비티 라이프사이클: 다섯 단계
onCreate() → onStart() → onResume() → [Activity visible and interactive]
↓
onPause() → onStop() → onDestroy()
1. onCreate()
- 액티비티가 처음 생성될 때 호출됩니다.
- UI를 초기화하고 데이터 바인딩을 설정합니다.
- 저장된 상태를 복원합니다(자세한 내용은 아래).
2. onStart()
- 액티비티가 사용자에게 보이게 됩니다.
- 카메라나 위치 서비스와 같은 리소스를 시작합니다.
- 아직 인터랙티브하지는 않습니다.
3. onResume()
- 액티비티가 이제 완전히 인터랙티브합니다.
- 사용자는 버튼을 누르고, 텍스트를 입력하는 등 상호작용할 수 있습니다.
- 애니메이션을 시작하거나 비디오 재생을 재개합니다.
4. onPause()
- 사용자가 액티비티를 떠나고(다른 앱이 전경에 나타남)
- 여기서 중요한 데이터를 저장합니다.
- 애니메이션을 중지하고, 비디오를 일시 정지합니다.
- Note: 시스템이 앱을 종료하기 전에 약 500 ms가 있습니다.
5. onStop()
- 액티비티가 더 이상 보이지 않습니다.
- 무거운 리소스(예: 데이터베이스 연결)를 정리합니다.
- 시스템이 메모리를 확보하기 위해 여기서 앱을 종료할 수 있습니다.
6. onDestroy()
- 액티비티가 파괴되고 있습니다(사용자가 뒤로 가기를 누르거나 시스템이 종료함).
- 최종 정리 작업을 수행합니다.
문제: 구성 변경(회전)
사용자가 휴대폰을 회전하면 Android 현재 Activity를 파괴하고 처음부터 다시 생성합니다.
User rotates phone
↓
onPause() → onStop() → onDestroy() [old Activity destroyed]
↓
onCreate() → onStart() → onResume() [new Activity created]
결과
- 모든 로컬 변수가 사라집니다.
- 메모리 내 데이터가 사라집니다.
- UI 상태가 초기화됩니다.
- UI가 재구성되면서 화면에 깜빡임이 발생합니다.
이것이 많은 AI‑생성 앱이 회전 시 충돌하는 이유이며, 개발자가 상태를 제대로 저장하지 않았기 때문입니다.
Solution 1: Using ViewModel to Survive Configuration Changes
ViewModel은 구성 변경을 견디는 Jetpack 컴포넌트입니다. ViewModel에 저장된 데이터는 액티비티가 회전해도 파괴되지 않습니다.
Kotlin Code Example
// 1. Define your ViewModel
class UserViewModel : ViewModel() {
private val _userName = MutableLiveData("")
val userName: LiveData<String> = _userName
fun setUserName(name: String) {
_userName.value = name
}
}
// 2. In your Activity, use the ViewModel
class MainActivity : AppCompatActivity() {
private val viewModel: UserViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val nameInput = findViewById<EditText>(R.id.nameInput)
// When user types, save to ViewModel
nameInput.addTextChangedListener { text ->
viewModel.setUserName(text.toString())
}
// Observe ViewModel data
viewModel.userName.observe(this) { name ->
nameInput.setText(name)
}
}
}
Why this works
- ViewModel은 Activity에 스코프가 지정됩니다.
- Android는 구성 변경 동안 ViewModel을 계속 살아 있게 유지합니다.
- 데이터가 회전(rotate) 중에도 지속됩니다.
- UI는 LiveData를 통해 자동으로 업데이트됩니다.
솔루션 2: rememberSaveable를 사용한 Jetpack Compose
Compose(현대적인 Android UI 프레임워크)를 사용한다면, 패턴이 더욱 간단합니다.
@Composable
fun UserScreen() {
// This state survives rotation because of rememberSaveable
var userName by rememberSaveable { mutableStateOf("") }
Column {
TextField(
value = userName,
onValueChange = { userName = it },
label = { Text("Enter your name") }
)
Text("Hello, $userName!")
}
}
주요 차이점
| Compose 함수 | 회전 시 생존 여부 |
|---|---|
remember { } | 아니오 – 파괴 시 상태가 사라짐 |
rememberSaveable { } | 예 – Bundle에 저장되어 복원됩니다 |
Compose는 Activity가 재생성될 때 저장된 상태를 자동으로 복원합니다.
Compose 재구성이 작동하는 방식
회전이 발생하면:
- Activity가 파괴됨 →
rememberSaveable이 상태를Bundle에 저장합니다. - Activity가 재생성됨 →
rememberSaveable이Bundle에서 상태를 복원합니다. - Compose가 재구성되어 UI 트리를 다시 그리며, 복원된 값을 가진
TextField를 렌더링합니다.
User rotates phone
↓
rememberSaveable saves state → Activity destroyed
↓
Activity recreated → rememberSaveable restores state
↓
Compose recomposes → TextField shows saved text
솔루션 3: savedInstanceState에 저장 (onSaveInstanceState)
보다 복잡한 데이터를 저장하려면 onSaveInstanceState() 콜백을 사용하세요.
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val nameInput = findViewById<EditText>(R.id.nameInput)
// Restore from saved state
if (savedInstanceState != null) {
val savedName = savedInstanceState.getString("userName", "")
nameInput.setText(savedName)
}
}
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
val nameInput = findViewById<EditText>(R.id.nameInput)
outState.putString("userName", nameInput.text.toString())
}
}
onSaveInstanceState는 Activity가 파괴되기 전에 호출되며, 원시 타입, Parcelable 또는 Serializable을 Bundle에 넣을 수 있게 해줍니다. Activity가 다시 생성될 때, 저장된 값은 onCreate(또는 onRestoreInstanceState)에서 사용할 수 있습니다.
TL;DR
- Activity lifecycle은 언제 안전하게 리소스를 할당하고 해제할 수 있는지를 결정합니다.
- Rotation은 전체 파괴/재생성 사이클을 트리거하여 메모리 내 상태를 모두 지웁니다.
- ViewModel,
rememberSaveable, 그리고 **onSaveInstanceState**는 구성 변경 시 데이터를 보존하는 주요 세 가지 방법입니다.
이러한 전략 중 하나(또는 조합)를 구현하면 AI가 생성한 Android 앱이 회전에도 정상적으로 살아남을 수 있습니다.
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
outState.putString("userName", nameInput.text.toString())
}
제한 사항
Parcelable/Serializable객체에만 작동합니다.Bundle은 약 1 MB 크기 제한이 있습니다.- 대용량 데이터 세트에는 적합하지 않습니다.
회전 방지: 구성 변경 비활성화 (권장되지 않음)
<!-- Add to your Activity declaration in AndroidManifest.xml -->
<activity
android:name=".MainActivity"
android:configChanges="orientation|screenSize|keyboardHidden"
android:screenOrientation="portrait" />
왜 하지 말아야 하는가
- 사용자는 회전하지 않는 앱을 싫어합니다.
- 회전에 의존하는 사람들의 접근성을 손상시킵니다.
- 회전을 무시하는 AI‑생성 앱은 나쁜 리뷰를 받습니다.
앱이 종료될 때: 프로세스 사망
Android는 메모리를 확보하기 위해 백그라운드에서 전체 프로세스를 종료할 수 있습니다. 이는 구성 변경과 다릅니다.
시퀀스
사용자가 앱을 백그라운드로 전환함
↓
시스템에 메모리가 필요함
↓
시스템이 프로세스를 종료함 (onDestroy() 호출)
↓
사용자가 앱으로 돌아옴
↓
시스템이 전체 앱 스택을 재생성함
onSaveInstanceState()는 여기서 여전히 작동하지만 UI 상태에만 적용됩니다.
중요한 데이터는 영구 저장소를 사용하세요:
- Room (로컬 데이터베이스)
- SharedPreferences
- DataStore (Jetpack)
Room 예시
@Database(entities = [User::class], version = 1)
abstract class AppDatabase : RoomDatabase() {
abstract fun userDao(): UserDao
}
@Dao
interface UserDao {
@Insert
suspend fun insertUser(user: User)
@Query("SELECT * FROM users LIMIT 1")
fun getUser(): Flow<User>
}
재시작 후 데이터 복원
class MainActivity : AppCompatActivity() {
private val db = AppDatabase.getInstance(this)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
lifecycleScope.launch {
db.userDao().getUser().collect { user ->
// Restore UI with user data
}
}
}
}
요약 표
| 시나리오 | 솔루션 | 회전 유지? | 데이터 제한 |
|---|---|---|---|
| 구성 변경 (회전) | ViewModel + LiveData | 예 | 제한 없음 |
| 구성 변경 (Compose) | rememberSaveable | 예 | ~1 MB (Bundle) |
잠시 일시정지 (onPause) | ViewModel | 예 | 제한 없음 |
| 프로세스 종료 | Room / SharedPreferences / DataStore | 예 | 기기 저장소 |
| 사용자가 뒤로 버튼을 누름 | 저장되지 않음 | 아니오 | 해당 없음 |
AI‑생성 앱 및 라이프사이클
Claude Code 또는 다른 AI를 사용해 Android 앱을 생성할 때는 항상 확인하세요:
- ✅ 회전 시 충돌 없이 정상 동작합니까?
- ✅
ViewModel또는rememberSaveable을 사용합니까? - ✅ 중요한 데이터를 데이터베이스에 영구 저장합니까?
- ✅ 정리 작업을 위해
onPause()를 올바르게 구현했습니까?
잘못 생성된 앱에서 흔히 발생하는 문제
- 라이프사이클을 전혀 고려하지 않음.
- 회전 시 데이터 손실.
- 프로세스 종료 시 충돌.
- 사용자 입력을 저장하지 않음.
우리 템플릿은 이 모든 사항을 올바르게 처리합니다.
다이어그램 (참고용)
┌─────────────────────────────────────────────────────┐
│ Activity Lifecycle Flow │
└─────────────────────────────────────────────────────┘
onCreate()
↓
onStart()
↓
onResume() ← [User interacting]
↙ ↖
onPause() [Rotation: destroys & recreates]
↓
onStop()
↓
onDestroy()
ViewModel survives rotation ✓
rememberSaveable survives rotation ✓
Local variables do NOT survive rotation ✗
결론
Android의 라이프사이클은 복잡하지만 마법은 아니다. 핵심 요점:
- UI 상태에는 ViewModel 또는
rememberSaveable를 사용하세요. - 영구 데이터에는 Room, SharedPreferences, 또는 DataStore 를 사용하세요.
- 예외 상황을 위해
onSaveInstanceState()에 상태를 저장하세요. - 배포 전에 회전 테스트를 수행하세요.
모든 8개의 템플릿이 라이프사이클을 올바르게 처리합니다.