UI를 구현하다 보면 네트워크 요청이나 데이터베이스 작업처럼 시간이 오래 걸리는 작업을 처리해야 하는 경우가 많습니다. 이런 작업을 메인 스레드에서 그대로 실행하면 화면이 멈추거나 앱이 응답하지 않는 문제가 발생할 수 있습니다.
그래서 안드로이드에서는 오래 걸리는 작업을 별도의 Thread에서 실행하는 방식이 기본적으로 사용됩니다. 이번 글에서는 Thread가 무엇인지, 그리고 UI 구현에서 왜 중요한지 정리해보려고 합니다.
Thread는 프로그램 내에서 작업을 실행하는 가장 기본적인 단위입니다. 안드로이드 앱은 기본적으로 하나의 메인 스레드(UI 스레드)에서 시작되며, 이 스레드는 화면을 그리거나 사용자 입력을 처리하는 역할을 담당합니다.
하지만 메인 스레드에서 시간이 오래 걸리는 작업을 수행하면 화면이 멈추게 됩니다. 이를 방지하기 위해 별도의 Thread를 생성하여 작업을 처리할 수 있습니다.
Thread {
// 오래 걸리는 작업 (예: 네트워크 요청)
}.start()
Thread는 강력하지만 직접 사용할 경우 몇 가지 단점이 있습니다.
특히 화면이 사라졌는데도 Thread가 계속 실행되는 경우, 불필요한 작업이 계속 수행될 수 있습니다.
안드로이드에서 중요한 점은 UI는 반드시 메인 스레드에서만 업데이트할 수 있다는 것입니다. 즉, 백그라운드 Thread에서 작업을 수행한 후에는 다시 메인 스레드로 돌아와 UI를 갱신해야 합니다.
Thread {
val result = "데이터 로드 완료"
Handler(Looper.getMainLooper()).post {
textView.text = result
}
}.start()
이처럼 Thread를 사용할 때는 작업 실행 (백그라운드), UI 반영 (메인 스레드)을 명확하게 나눠야 합니다.
Thread를 이해했다면, 자연스럽게 Kotlin의 Coroutine과의 차이도 함께 알아두는 것이 좋습니다. Coroutine은 Thread 위에서 동작하는 경량 작업 단위로, 비동기 처리를 더 간단하게 만들어주는 도구입니다.
두 개념의 핵심 차이는 다음과 같습니다.
Thread를 사용할 경우 작업을 직접 생성하고, 실행 흐름과 스레드 전환까지 모두 관리해야 합니다.
Thread {
val result = doWork()
Handler(Looper.getMainLooper()).post {
updateUi(result)
}
}.start()
반면 Coroutine을 사용하면 이러한 과정을 더 간단하게 표현할 수 있습니다.
lifecycleScope.launch {
val result = withContext(Dispatchers.IO) {
doWork()
}
updateUi(result)
}
이처럼 Coroutine은 아래와 같은 특징을 가지고 있습니다.
특히 안드로이드에서는 Android Jetpack의 lifecycleScope, viewModelScope 등을 통해
화면이 사라지면 자동으로 작업이 취소되도록 만들 수 있습니다.
Thread는 안드로이드에서 비동기 작업을 처리하기 위한 가장 기본적인 방법입니다. 하지만 직접 관리해야 할 요소가 많기 때문에 실무에서는 점점 더 추상화된 방식이 사용되고 있습니다.
안드로이드에서 안정적인 UI를 만들기 위해서는 메인 스레드를 블로킹하지 않는 것이 핵심입니다. Thread는 그 출발점이 되는 개념이며, 이후 Coroutine과 같은 기술을 이해하기 위한 중요한 기반이 됩니다.