Android 앱 라이프사이클 설명: AI 생성 앱이 회전에서도 살아남는 이유

발행: (2026년 3월 2일 오전 09:15 GMT+9)
12 분 소요
원문: Dev.to

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 재구성이 작동하는 방식

회전이 발생하면:

  1. Activity가 파괴됨rememberSaveable이 상태를 Bundle에 저장합니다.
  2. Activity가 재생성됨rememberSaveableBundle에서 상태를 복원합니다.
  3. 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 또는 SerializableBundle에 넣을 수 있게 해줍니다. 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개의 템플릿이 라이프사이클을 올바르게 처리합니다.

https://myougatheax.gumroad.com

0 조회
Back to Blog

관련 글

더 보기 »