[Android] scope

MariGold·3일 전

[Android]

목록 보기
12/12
post-thumbnail

Android 코루틴을 처음 접하면 헷갈리는 개념 중 하나가 Scope입니다. 왜 scope를 사용해야하고, 어떤 scope를 사용해야하는지 의문이 생기게 됩니다.

이번 글에서는 코루틴 Scope가 무엇인지, 왜 필요한지, 어떤 종류가 있고 어떻게 사용하는지 정리해보도록 하겠습니다.


Scope(스코프)란?

Scope는 코루틴의 생명주기를 관리하는 범위입니다. 쉽게 말해 "이 코루틴이 언제 시작되고 언제 끝나는지"를 결정하는 관리자라고 생각하면 됩니다.

왜 Scope가 필요한가?

// ❌ 이런 코드는 불가능
launch {  // 에러! Scope가 없음
    delay(1000)
}

// ✅ Scope와 함께 사용
lifecycleScope.launch {
    delay(1000)
}

Scope가 없을 경우

  • 코루틴이 언제 취소되어야 하는지 정의할 방법이 없음음
  • 메모리 누수 위험 (화면이 사라져도 작업이 계속 실행될 수 있음)

Scope가 있을 경우

  • 코루틴의 생명주기를 관리할 수 있음
  • 구조화된 동시성으로 안전한 비동기 처리
  • 예측 가능한 생명주기 (단, 어떤 Scope인지에 따라 동작이 다름)

📦 Scope의 종류

Android에서 사용하는 주요 Scope들을 살펴보겠습니다.

1. globalScope

GlobalScope.launch {
    // 앱이 종료될 때까지 살아있음
}

특징

  • 앱 전체 생명주기 동안 유지
  • 절대 자동으로 취소되지 않음

globalScope가 가진 특징 때문에 거의 사용되지 않고 있습니다. Activity가 종료되어도 코루틴이 계속 실행되어 메모리 누수가 발생할 수 있고, 코루틴이 언제 끝날지 알 수가 없습니다. 또한 불필요한 작업이 백그라운드에서 계쏙 돌아가기 때문에 리소스 낭비가 발생합니다. globalScope가를 사용해야될 대부분의 상황에 더 나은 대안이 있어서 globalScope는 거의 사용되지 않고 있습니다.

2. viewModelScope

class MyViewModel : ViewModel() {
    fun loadData() {
        viewModelScope.launch {
            val data = repository.fetchData()
            _uiState.value = data
        }
    }
}

특징

  • ViewModel이 clear될 때 자동으로 취소
  • Configuration Change (화면 회전 등)에도 유지
  • ViewModel의 생명주기와 완벽하게 동기화

viewModelScope는 ViewModel에서 사용되는 Scope입니다. ViewModel의 내부의 모든 비동기 작업에 사용되며, 네트워크 요청 또는 데이터베이스 조회를 할 때 사용됩니다. 추가적으로 비즈니스 로직 처리를 할 때도 viewModelScope를 사용합니다.

3. lifeCycleScope

class MyActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        
        lifecycleScope.launch {
            // Activity가 destroy되면 자동 취소
        }
    }
}

특징

  • Lifecycle이 DESTROYED 상태가 되면 자동 취소
  • Activity/Fragment의 생명주기와 완벽하게 동기화
  • Configuration Change 시 재생성됨

lifeCycleScope은 UI를 직접 업데이트하거나 일회성 작업에 사용됩니다. ViewModel없이 간단한 작업을 처리할 때도 사용됩니다. lifeCycleScope은 Activity가 백그라운드로 가도 계속 실행되는데, 이를 방지하기 위해 사용되는 것이 RepeatOnLifeCycle입니다.

// 일반 lifecycleScope
lifecycleScope.launch {
    // Activity가 백그라운드로 가도 계속 실행됨
    flow.collect { data ->
        updateUI(data)
    }
}

// repeatOnLifecycle (추천)
lifecycleScope.launch {
    repeatOnLifecycle(Lifecycle.State.STARTED) {
        // STARTED 상태일 때만 실행
        // 백그라운드로 가면 자동 중지, 돌아오면 재시작
        flow.collect { data ->
            updateUI(data)
        }
    }
}

RepeatOnLifeCycle을 사용하면 위의 코드의 설명과 같이 Activity의 생명주기가 STARTED일 때만 코루틴이 작동되고, 백그라운드로 가면 중지됩니다.

4. rememberCoroutineScope

@Composable
fun MyScreen() {
    val scope = rememberCoroutineScope()
    
    Button(onClick = {
        scope.launch {
            // 버튼 클릭 시 코루틴 실행
        }
    }) {
        Text("클릭")
    }
}

특징

  • Composable이 화면에서 사라지면 자동 취소
  • Recomposition에도 안전하게 유지
  • 이벤트 기반 작업에 최적

rememberCoroutineScope는 버튼 클릭 같은 일회성 이벤트에 주로 사용됩니다. SnackBar, Toast를 표시하거나, 애니메이션을 트리거하는데 사용되는 Scope입니다.

5. coroutineScope

class MyRepository {
    private val scope = CoroutineScope(Dispatchers.IO + SupervisorJob())
    
    fun fetchData() {
        scope.launch {
            // 작업
        }
    }
    
    fun cleanup() {
        scope.cancel() // 수동으로 취소 필요!
    }
}

특징

  • 완전한 수동 관리
  • 생명주기를 직접 제어
  • 취소도 직접 호출 필요

coroutineScope를 직접 생성해 사용하는 경우는 Repository나 Manager 같은 커스텀 클래스, 또는 Android 컴포넌트가 아닌 곳에서 코루틴을 사용할 때입니다.
특별한 생명주기 관리가 필요한 상황에서도 직접 생성해 사용할 수 있지만,
이 경우에는 반드시 cancel()을 호출해 메모리 누수를 방지해야 합니다.

6. supervisorScope

suspend fun loadUserProfile() = supervisorScope {
    val userDeferred = async { loadUser() }
    val postsDeferred = async { loadPosts() }

    // 하나가 실패해도 다른 작업은 계속됨
    val user = runCatching { userDeferred.await() }
    val posts = runCatching { postsDeferred.await() }

    combineResult(user, posts)
}

특징

  • 자식 코루틴 중 하나가 실패해도 전체 Scope가 취소되지 않음
  • 기본 CoroutineScope와 달리 부모가 자식의 예외를 전파받지 않음
  • 복수의 독립적인 작업을 병렬로 실행할 때 유용
    -> (예: 프로필 + 알림 + 피드 로딩을 동시에, 하나 실패해도 UI는 표시 가능)

supervisorScope은 "여러 자식 작업을 동시에 수행하지만, 서로의 실패로 인해 전체 작업이 중단되면 안 되는 상황"에서 사용됩니다. 예외가 발생해도 전체 코루틴이 죽지 않으므로, 구조화된 동시성을 유지하면서도 안정적인 처리가 가능합니다.


🎯 마무리

코루틴을 안전하고 예측 가능하게 사용하기 위해서는 작업의 성격과 생명주기에 맞는 Scope를 선택하는 것이 무엇보다 중요합니다. 각 Scope의 특성과 역할을 이해해두면 구조화된 동시성을 자연스럽게 적용할 수 있고, 더 안정적인 비동기 코드를 만들 수 있습니다.

결국 코루틴을 제대로 활용하는 첫 단계는 Scope를 올바르게 이해하고 상황에 맞게 사용하는 것입니다.

이 점을 기억해두면 Android 개발에서 코루틴을 훨씬 더 명확하고 효율적으로 다룰 수 있습니다.

profile
많은 것을 알아가고 싶은 Android 주니어 개발자

0개의 댓글