
안드로이드 앱 구조 입문 시리즈 (3편) – 생명주기를 알아야 설계가 쉬워지는 이유는 생명주기를 콜백 목록이 아니라 설계 기준으로 이해하게 만드는 글입니다. 안드로이드에서 코드를 어디에 둬야 하는지가 왜 생명주기와 연결되는지 설명합니다.
이 글이 시리즈에서 맡는 역할
1편에서는 안드로이드 앱을 구조로 보는 큰 그림을 잡았고, 2편에서는 Activity와 Fragment를 왜 나눠야 하는지 설명했습니다. 이번 3편은 그다음 단계로 화면 수명과 코드 책임을 어떻게 연결해서 봐야 하는지 설명합니다.
생명주기를 왜 외우기만 하면 안 될까
생명주기를 외우는 것 자체가 나쁜 것은 아닙니다. 문제는 그걸로 끝나는 경우입니다. 초보자는 보통 생명주기를 호출 순서로만 이해하지만 안드로이드에서 더 중요한 것은 화면이 계속 같은 상태로 유지되지 않는다는 사실입니다.
즉 생명주기는 콜백 표가 아니라 화면의 수명이 계속 바뀐다는 신호입니다. 이 사실을 놓치면 화면이 잠깐 사라질 때 멈춰야 할 작업과 다시 돌아왔을 때 유지되어야 할 상태를 구분하지 못하게 됩니다.
안드로이드에서 설계가 어려운 이유는 화면 수명이 짧기 때문이다
안드로이드에서는 화면이 다른 앱에 가려질 수 있고, 회전으로 다시 만들어질 수 있고, 시스템이 메모리를 확보하려고 제거할 수도 있습니다. 즉 지금 보이는 화면을 영구한 대상으로 취급하면 설계가 바로 흔들리기 시작합니다.
이때 중요한 질문은 이것입니다. 화면이 사라져도 이 데이터는 남아 있어야 하는가, 이 작업은 화면이 안 보여도 계속 살아야 하는가, 이 코드는 지금 보이는 UI에만 속한 것인가. 이 질문이 바로 생명주기와 설계가 연결되는 지점입니다.

생명주기를 설계 기준으로 본다는 것은 무슨 뜻일까
안드로이드에서 코드를 어디에 둘지는 사실상 그 코드의 수명을 어디에 둘지 정하는 일과 같습니다. 잠깐 화면에 텍스트를 보여주는 코드는 화면 수명과 같이 가도 되지만, 서버에서 가져온 데이터를 보관하는 상태는 화면보다 더 길게 가야 할 수 있습니다.
즉 생명주기를 이해한다는 것은 콜백을 많이 안다는 뜻이 아니라 코드마다 적절한 수명을 배정할 수 있다는 뜻에 가깝습니다.
각 생명주기 단계는 설계에서 어떻게 읽어야 할까
onCreate: 화면의 뼈대를 처음 세우는 시점
onCreate()는 보통 화면의 기본 틀을 준비하는 위치입니다. setContentView(), view binding 초기화, RecyclerView나 Toolbar 같은 기본 UI 설정처럼 화면의 초기 뼈대를 세우는 코드가 잘 어울립니다. 하지만 초보자는 여기에 모든 것을 몰아넣기 쉽습니다.
onStart: 화면이 사용자에게 보이기 시작하는 구간
onStart()는 화면이 사용자에게 보이기 시작하는 단계입니다. 생성되었다와 보이기 시작했다는 다를 수 있다는 감각이 중요합니다. 즉 화면 가시성과 연결된 준비를 생각하기 좋은 위치입니다.
onResume: 사용자가 실제로 상호작용하는 구간
onResume()은 화면이 실제로 포커스를 가지고 사용자가 상호작용할 수 있는 상태에 들어온 시점으로 볼 수 있습니다. 입력과 직접 연결된 처리나 다시 화면이 활성화될 때 필요한 작업을 떠올리기 좋은 위치입니다. 다만 자주 다시 호출될 수 있다는 점을 같이 이해해야 합니다.
onPause, onStop, onDestroy를 어떻게 읽어야 할까
onPause()는 화면이 상호작용 상태를 잃기 시작하는 신호로, onStop()은 더 이상 보이지 않는 구간으로 읽으면 좋습니다. onDestroy()는 객체가 정리되는 단계이지만 무조건 앱이 완전히 끝난다는 뜻으로 이해하면 안 됩니다. 이 단계들은 화면 수명 변화의 단계로 읽는 편이 중요합니다.
같은 코드라도 어디에 두느냐에 따라 의미가 달라진다
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
loadData()
}
private fun loadData() {
// 네트워크 호출
}
}문법상 문제는 없지만 설계 관점에서는 질문이 생깁니다. 회전되면 다시 호출돼도 되는가, 이미 가져온 상태를 다시 덮어도 되는가, 이 작업은 정말 Activity 수명에 붙어 있어야 하는가 같은 질문입니다. 즉 중요한 것은 코드가 돌아가느냐가 아니라 이 코드가 이 위치에 있어야 하느냐입니다.
초보자가 생명주기에서 자주 하는 실수
- 화면과 상태를 같은 것으로 보는 경우
- 모든 초기화를 onCreate에 몰아넣는 경우
- 비동기 작업을 생명주기와 분리해서 보는 경우
- 콜백 이름은 외웠지만 책임 분리는 안 된 경우
이 실수들의 공통점은 생명주기를 알고 있다고 생각하지만 실제 설계 판단 기준으로는 쓰지 못한다는 점입니다.
왜 ViewModel 이야기가 자연스럽게 이어질까
생명주기를 제대로 이해하면 자연스럽게 다음 질문이 생깁니다. 그럼 화면보다 오래 살아야 하는 상태는 어디에 둬야 하지? 여기서 ViewModel이 등장합니다. 즉 ViewModel은 갑자기 튀어나온 기술이 아니라 생명주기를 이해한 다음에야 필요성이 보이는 구조적 답입니다.
class MainViewModel : ViewModel() {
private val _uiState = MutableStateFlow<MainUiState>(MainUiState.Loading)
val uiState: StateFlow<MainUiState> = _uiState
fun setSuccess(items: List<String>) {
_uiState.value = MainUiState.Success(items)
}
}이 예시는 단순하지만 화면은 잠깐 사라질 수 있어도 상태는 다른 수명을 가질 수 있다는 점을 보여줍니다. 즉 생명주기를 이해하면 왜 상태를 화면 밖으로 빼야 하는지도 자연스럽게 이해됩니다.
생명주기는 비동기 작업과도 바로 연결된다
class MainViewModel(
private val repository: ItemRepository
) : ViewModel() {
fun loadItems() {
viewModelScope.launch {
repository.loadItems()
}
}
}생명주기를 설계 기준으로 보면 비동기 작업도 다르게 보입니다. 왜 Activity 안이 아니라 ViewModel에서 시작하는가, 왜 화면 수명과 작업 수명을 같이 고려하는가 같은 질문이 중요해집니다. 즉 코루틴도 그냥 비동기 문법이 아니라 화면 수명과 연결된 구조 문제로 보이기 시작합니다.
생명주기를 설계 기준으로 보는 습관이 왜 중요한가
안드로이드 앱은 기능보다 구조가 먼저 흔들립니다. 중복 로딩, 이상한 상태 초기화, 어색한 복구, 너무 커진 Activity, 책임이 섞인 UI 코드 같은 문제의 상당수는 생명주기를 설계 기준으로 보지 않았을 때 생깁니다.
즉 생명주기를 잘 이해한다는 것은 콜백을 많이 외운다는 뜻이 아니라 화면 수명과 코드 수명이 다를 수 있다는 사실을 기준으로 설계한다는 뜻에 가깝습니다.
이번 편의 핵심 정리
- 생명주기는 콜백 이름 암기보다 설계 기준으로 보는 것이 중요하다
- 안드로이드 화면은 계속 살아 있는 대상이 아니다
- 코드를 어디에 둘지는 결국 그 코드의 수명을 어디에 둘 것인지의 문제다
- 상태와 비동기 작업은 생명주기와 함께 봐야 한다
- ViewModel은 생명주기 이해 다음에 자연스럽게 이어지는 답이다
이 기준이 잡히면 다음 편인 ViewModel은 왜 필요할까도 훨씬 쉽게 읽힙니다.