onStart: Android 화면에서 Activity 가시성의 본질

저자: IT Sectr 게시일: 2026-03-04 읽는 시간: 9 분

onStart는 Activity 또는 Fragment가 사용자에게 표시될 때 호출되는 Android 라이프사이클 메서드입니다. 이 순간 화면이 기기 디스플레이에 나타나지만 onResume이 호출될 때까지 입력 포커스가 없어 아직 사용자와 상호작용할 수 없습니다. onStart 메서드는 시스템 리스너 등록, 위치 정보 서비스 연결, 구성 요소가 화면에 표시되는 동안 실행되어야 하는 애니메이션 시작에 이상적입니다. 전체 Activity 라이프사이클에 대한 자세한 내용은 Activity Lifecycle 문서를 참조하세요.

핵심 포인트

  • onStart — Activity 또는 Fragment가 화면에 표시될 때 호출됨; onResume보다 먼저 실행
  • 리스너 등록 — BroadcastReceiver, LocationListener, SensorListener를 onStart에서 등록하고 onStop에서 해제
  • 애니메이션 — 화면이 표시되는 동안 실행되어야 하는 애니메이션 시작; onStop에서 일시 중지
  • 바운드 서비스 — onStart에서 bindService를 통해 클라이언트-서버 서비스에 연결, onStop에서 연결 해제
  • onStart vs onResume — onStart = 가시성, onResume = 포커스 + 상호작용; 서로 다른 화면 활동 수준
  • Fragment.onStart — Activity.onStart 후에 호출되며, Fragment가 컨테이너에 표시될 때 실행
  • onStart/onStop 쌍 — onStart에서 연결된 리소스는 누수 방지를 위해 onStop에서 반드시 해제

Android에서 onStart의 본질

onStart는 Activity 라이프사이클의 두 번째 메서드로, 시스템이 onCreate 후(또는 중지된 상태에서 돌아올 때 onRestart 후) 호출합니다. onStart가 호출되는 순간 Activity 또는 Fragment가 화면에 표시됩니다. 사용자는 인터페이스를 볼 수 있지만 화면은 아직 상호작용할 준비가 되지 않았습니다 — 입력 포커스는 onResume 후에만 나타납니다.

onStart 메서드는 Activity의 “가시 수명”(visible lifetime) — onStart와 onStop 사이의 간격에 속합니다. 이 기간 동안 Activity는 다른 창(예: 투명 Activity 또는 대화 상자 창)으로 부분적으로 가려질 수 있지만 UI는 계속 표시됩니다. 이것은 Activity가 완전한 입력 포커스를 가지는 “포그라운드 수명”(onResume — onPause)과 구별됩니다.

이 3단계 계층 구조를 이해하는 것은 코드를 올바르게 분배하는 데 중요합니다. onCreate — 일회성 초기화, onStart — 가시 리소스 연결, onResume — 독점 리소스에 대한 독점 액세스. 이러한 수준을 혼동하는 개발자는 화면 전환 시 메모리 누수나 애플리케이션 오작동을 초래할 위험이 있습니다.

Activity의 onStart

Activity에서 onStart 메서드는 화면이 디스플레이에 나타날 때마다 호출됩니다 — 첫 실행(onCreate 후)과 백그라운드에서 돌아올 때(onRestart 후) 모두 포함됩니다. onCreate와 달리 onStart는 Activity 인스턴스의 수명 동안 여러 번 호출될 수 있으므로 화면이 나타날 때마다 실행해야 하는 코드가 여기에 배치됩니다.

kotlin
class DashboardActivity : AppCompatActivity() {
    private val connectivityReceiver = object : BroadcastReceiver() {
        override fun onReceive(context: Context?, intent: Intent?) {
            val isConnected = ... // ConnectivityManager 확인
            binding?.statusIndicator?.setColor(
                if (isConnected) Color.GREEN else Color.RED
            )
        }
    }

    override fun onStart() {
        super.onStart()
        registerReceiver(
            connectivityReceiver,
            IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION)
        )
        SensorManager.getInstance().registerStepCounter()
    }

    override fun onStop() {
        unregisterReceiver(connectivityReceiver)
        SensorManager.getInstance().unregisterStepCounter()
        super.onStop()
    }
}

핵심 규칙: onStart에서 연결된 모든 리소스는 onStop에서 해제되어야 합니다. 이렇게 하면 Activity가 화면에서 숨겨질 때 배터리를 소모하지 않고, 시스템 이벤트를 수신하지 않으며, 메모리를 점유하지 않습니다. Android Studio에는 해당 등록 해제 없이 BroadcastReceiver를 등록하는 것을 경고하는 lint 규칙이 포함되어 있습니다.

Fragment의 onStart

Fragment의 onStart는 호스트 Activity의 라이프사이클과 밀접하게 연결되어 있습니다. Fragment는 그것을 포함하는 Activity가 onStart를 받은 후 onStart를 받습니다. 그러나 Fragment가 지연 모드(addToBackStack 없이 FragmentTransaction.commit())로 추가된 경우 onStart가 지연되어 호출될 수 있습니다.

kotlin
class MapFragment : Fragment() {
    private var mapView: MapView? = null

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View {
        mapView = MapView(requireContext())
        return mapView!!
    }

    override fun onStart() {
        super.onStart()
        mapView?.onStart()
        LocationService.connect(requireContext())
    }

    override fun onStop() {
        mapView?.onStop()
        LocationService.disconnect()
        super.onStop()
    }
}

Fragment.onStart의 특이사항: Fragment가 offscreenPageLimit = 1인 ViewPager에 있는 경우 인접한 프래그먼트도 표시되기 전에 onStart를 받습니다. 이로 인해 리스너가 조기에 등록될 수 있습니다. 이러한 경우 setUserVisibleHint() 메서드를 사용하거나 onStart 내에서 isVisible을 확인하여 실제로 표시되는 프래그먼트에 대해서만 리스너를 등록합니다.

onStart와 onResume의 차이

onStart와 onResume의 주요 차이점은 화면 활동 수준입니다. onStart는 Activity가 화면에 표시되지만 반드시 포그라운드에 있는 것은 아님을 나타냅니다. onResume은 Activity가 포그라운드에 있고 입력 포커스를 가지고 있음을 나타냅니다. 이 차이는 대화 상자 창 예제로 설명됩니다: Activity 위에 Dialog가 나타나면 Activity는 onResume을 잃지만(onPause 호출) 계속 표시됩니다 — onStart/onStop은 호출되지 않습니다.

비교 표는 각 메서드가 어떤 시나리오에서 호출되는지 명확히 보여줍니다:

시나리오onStartonResume
앱 실행호출됨호출됨
Activity 위에 Dialog 열림호출되지 않음onPause (포커스 손실)
홈 버튼 누름onStop (숨김)onPause → onStop
최근 앱에서 돌아옴onStart (표시)onResume (포커스)
화면 회전onCreate → onStart→ onResume
수신 전화onStop (숨김)onPause → onStop

이 표는 개발자가 특정 코드를 어떤 메서드에 배치할지 결정하는 데 도움을 줍니다. 예를 들어, 앱이 화면 겹침(대화 상자 포함) 시 비디오 재생을 일시 중지해야 하는 경우 코드는 onPause에 배치됩니다. 화면이 완전히 숨겨졌을 때만 비디오를 중지해야 하는 경우 — 코드는 onStop에 배치됩니다.

리스너 및 서비스 등록

onStart는 Activity가 화면에 표시되는 동안만 작동해야 하는 리스너를 등록하기에 최적의 위치입니다. 이는 시스템 이벤트용 BroadcastReceiver, 위치 정보용 LocationListener, 기기 센서용 SensorListener의 세 가지 주요 시스템 구성 요소에 관한 것입니다.

onStart에서의 BroadcastReceiver

BroadcastReceiver는 onStart에서 Context.registerReceiver()를 통해 동적으로 등록되고 onStop에서 unregisterReceiver()를 통해 등록 해제됩니다. 동적 등록은 매니페스트에서의 정적 등록보다 선호됩니다. 수신기의 수명을 Activity의 가시 기간으로 제한하기 때문입니다 — Activity가 숨겨져 있을 때 앱이 시스템 브로드캐스트 메시지로 깨어나지 않습니다.

kotlin
private val batteryReceiver = object : BroadcastReceiver() {
    override fun onReceive(context: Context?, intent: Intent) {
        val level = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1)
        binding?.batteryText?.text = "$level%"
    }
}

override fun onStart() {
    super.onStart()
    registerReceiver(batteryReceiver, IntentFilter(Intent.ACTION_BATTERY_CHANGED))
}

override fun onStop() {
    unregisterReceiver(batteryReceiver)
    super.onStop()
}

LocationListener 및 SensorListener

위치 정보와 센서는 리소스를 많이 소모하는 작업입니다. onStart에서 GPS 업데이트를 요청하고 onStop에서 취소하면 화면이 숨겨져 있을 때 앱이 배터리를 소모하지 않습니다. 세부 조정을 위해 최소 간격과 거리로 requestLocationUpdates를 사용합니다 — 예: 10초 및 10미터, 이는 정확성과 전력 소비 간의 최적의 균형을 제공합니다.

애니메이션과 onStart

onCreate 대신 onStart에서 애니메이션을 시작하면 화면이 나타날 때마다 애니메이션이 시작됩니다. onCreate에서 애니메이션을 시작하면 처음 Activity 생성 시에만 작동하고 백그라운드에서 돌아올 때는 작동하지 않습니다. onStart는 Activity가 표시될 때마다 호출되므로 순환 애니메이션과 전환을 시작하기에 이상적인 위치입니다.

kotlin
private lateinit var pulseAnimator: ValueAnimator

override fun onStart() {
    super.onStart()
    pulseAnimator.start()
    binding?.loadingIndicator?.animate()?.alpha(1f)?.start()
}

override fun onStop() {
    pulseAnimator.cancel()
    binding?.loadingIndicator?.animate()?.cancel()
    super.onStop()
}

ObjectAnimator 또는 ValueAnimator를 사용하는 애니메이션의 경우 onStop에서 cancel()을 호출하는 것이 중요합니다. Activity가 숨겨진 후에도 애니메이션이 계속 실행되면 GPU와 CPU 리소스를 불필요하게 소비하여 기기 성능이 저하되고 배터리 소모가 가속화됩니다. Android Studio Profiler(GPU 그래프)를 사용하면 활성 애니메이션을 추적하고 누수를 감지할 수 있습니다.

onStart/onStop 쌍 규칙은 미리보기용 카메라(CameraX) 작업에도 적용됩니다. onStart에서 카메라를 열고 onStop에서 닫으면 앱이 화면에 표시되지 않을 때 다른 앱에서 카메라가 차단되지 않습니다. 이 규칙을 위반하면 Google Play에서 부정적인 리뷰의 흔한 원인이 됩니다.

자주 묻는 질문

리스너 등록 시 onStart와 onResume의 차이점은 무엇인가요?

onStart — 화면이 표시되는 동안 작동해야 하는 리스너용 (BroadcastReceiver, LocationListener, SensorListener). onResume — 독점 액세스가 필요한 리소스용 (카메라, 비디오 캡처, 음성 인식). 시스템 이벤트 리스너는 독점 액세스가 필요하지 않으며 부분적 겹침에도 작동할 수 있으므로 onStart에서 등록됩니다. 카메라는 전체 포커스가 있을 때만 활성화되어야 하므로 onResume에서 열립니다.

onStart가 호출되지 않을 수 있나요?

Activity가 표시 상태로 전환되면 onStart는 항상 호출됩니다. onStart 없이 실행되는 유일한 시나리오는 Activity가 생성된 후 즉시 종료되는 경우입니다(예: onCreate 오류로 인해). 이 경우 onCreate 직후 onDestroy가 호출됩니다. 그러나 이것은 비상 시나리오이며 올바르게 작성된 코드에서는 발생하지 않아야 합니다.

onStart가 onResume 없이 호출될 수 있나요?

네, 다른 Activity나 투명 창이 Activity 위에 즉시 열리면 onStart가 onResume을 받지 못할 수 있습니다. 예를 들어, onCreate 후 인증 화면이 시작되면(Activity A → Activity B), Activity A에서는 onStart가 호출되지만 onResume은 호출되지 않습니다 — 화면 B에 가려지면 즉시 onPause → onStop을 받습니다.

onStart는 몇 번 호출될 수 있나요?

onStart는 Activity 인스턴스의 수명 동안 여러 번 호출될 수 있습니다. Activity가 숨겨진 상태(onStop)에서 표시 상태로 전환될 때마다 onStart가 호출됩니다. 실제로 앱을 활발히 사용하면 onStart가 세션당 수십 또는 수백 번 호출될 수 있습니다.

onStart에서 데이터를 로드해야 하나요?

onStart에서 데이터 로드는 화면이 나타날 때마다 데이터를 업데이트해야 하는 경우 정당화됩니다. 예를 들어, 뉴스 피드 또는 알림 목록입니다. 그러나 로드는 비동기식이어야 합니다 — lifecycleScope를 사용한 코루틴을 통해 UI 스레드를 차단하지 않도록 합니다. 화면 표시 간에 변경되지 않는 데이터의 경우 onCreate에서 한 번 로드하는 것으로 충분합니다.

요약

  • onStart — 가시 수명 메서드; Activity 또는 Fragment가 화면에 표시될 때 호출됨
  • onStart에서 등록 — BroadcastReceiver, LocationListener, SensorListener를 onStart에서 등록, onStop에서 해제
  • onStart vs onResume — onStart = 가시성, onResume = 입력 포커스; 리소스 유형에 따라 다른 수준
  • 애니메이션 — 순환 애니메이션을 onStart에서 시작, onStop에서 중지; GPU 리소스 누수 방지
  • Fragment.onStart — Activity.onStart에 연동; ViewPager에서 인접 프래그먼트에도 미리 호출됨
  • 쌍 규칙 — 모든 onStart 리소스는 onStop에서 해제되어야 함, 그렇지 않으면 메모리 및 배터리 누수
  • 데이터 로드 — onStart에서는 화면이 나타날 때마다 업데이트해야 하는 데이터를 로드

턴키 방식의 모바일 애플리케이션을 개발해 드립니다

IT Sectr는 2017년부터 스타트업과 기업을 위한 iOS 및 Android 애플리케이션을 만듭니다. 저희가 상담해 드리고 최적의 솔루션을 제안하겠습니다.

프로젝트 논의

더 읽어보기