[Kotlin Coroutines] 7장. 코루틴 컨텍스트
|개요
- CoroutineScope 는 코루틴 빌더의 정의에서 리시버뿐만 아니라 마지막 인자의 리시버
- 중요한 개념처럼 보이니 정의를 확인
|
|
- CoroutineContext 를 감싸는 래퍼처럼 보임
- Continuation 또한 CoroutineContext 를 포함
- 코틀린 코루틴에서 가장 중요한 요소들이 CoroutineContext 를 사용하고 있는 걸 알 수 있음
- 도대체 뭘까?
CoroutineContext 인터페이스
- CoroutineContext 는 원소나 원소들의 집합을 나타내는 인터페이스
- Collection 이랑 개념이 비슷
- 특이한 점은 각 Element 또한 CoroutineContext
- 따라서 컬랙션 내 모든 원소는 그 자체 만으로 컬렉션이라 할 수 있음
- 원소가 원소의 컬렉션이 되는 건 확실히 직관적인 개념
- 머그잔을 생각해보자.
- 머그잔은 하나의 원소지만, 단 하나의 원소를 포함하는 컬렉션이기도 함
- 머그잔 하나를 더하면 두 개의 원소를 가진 컬렉션이 됨
- 컨텍스트의 지정과 변경을 편리하게 하기 위해 CoroutineContext 의 모든 원소가 CoroutineContext 로 되어 있음
- 컨텍스트를 지정하고 더하는 건 명시적인 집합을 만드는 것보다 훨씬 쉽다.
- 컨텍스트에서 모든 원소는 식별할 수 있는 유일한 Key 를 가지고 있음
- 각 키는 주소로 비교
- CoroutineName 이나 CoroutineContext 인터페이스를 구현한 CoroutineContext.Element 를 구현
CoroutineContext 에서 원소 찾기
- CoroutineContext 는 컬렉션과 비슷하기 때문에 get 을 이용해 유일한 키를 가진 원소를 찾을 수 있음
- 대괄호도 가능
- 원소가 컨텍스트에 있으면 반환된다는 점에서 Map 과 비슷
- 원소가 없으면 null 로 반환
|
|
|
|
- 키는 (CoroutineName 과 같은) 클래스나 Job과 SupervisorJob 처럼 같은 키를 사용하는 클래스가 구현한 (Job과 같은) 인터페이스를 가리킴
컨텍스트 더하기
- CoroutineContext 의 정말 유용한 기능
- 두 개의 CoroutineContext 를 합쳐 하나의 CoroutineContext 로 만들 수 있는 것!
- 다른 키를 가진 두 원소를 더하면 만들어진 컨텍스트는 두 가지 키를 모두 가진다.
|
|
- 만약 같은 키를 가진 또 다른 원소가 더해지면?
- 맵 처럼 새로운 원소가 기존 원소를 대체하게 됨
|
|
비어 있는 코루틴 컨텍스트
- CoroutineContext 는 컬렉션이므로 빈 컨텍스트 또한 생성 가능
- 당연히 원소가 없어서 더해도 변화가 없다.
|
|
|
|
원소 제거
- minusKey 를 통해 해당 원소를 컨텍스트에서 제거 가능
|
|
컨텍스트 폴딩
- 컬렉션의 fold 를 컨텍스트에서도 유사하게 사용할 수 있다.
- fold 에는 다음 값이 필요
- 누산기의 첫 번째 값
- 누산기의 현재 상태와 현재 실행되고 있는 원소로 누산기의 다음 상태를 계산할 연산
|
|
코루틴 컨텍스트와 빌더
- CoroutineContext 는 코루틴의 데이터를 저장하고 전달하는 방법
- 부모 - 자식 관계의 영향 중 하나
- 부모는 기본적으로 컨텍스트를 자식에게 전달
- 자식은 부모로부터 컨텍스트를 상속
|
|
- 모든 자식은 빌더의 인자에서 정의된 특정 컨텍스트를 가질 수 있다.
- 인자로 전달된 컨텍스트는 부모로부터 상속받은 컨텍스트를 대체
|
|
- 코루틴 컨텍스트를 계산하는 간단한 공식
- defaultContext + parentContext + childContext
- 새로운 원소가 같은 키를 가졌으면 해당 원소를 대체하므로
- 자식 컨텍스트는 부모 컨텍스트로부터 상속받은 컨텍스트 중 같은 키를 가진 원소를 대체
- 현재 디폴트로 설정되는 원소는 Dispatchers.Default
- 애플리케이션이 디버그 모드일 때는 CoroutineId 도 디폴트로 설정됨
- Job 은 변경이 가능하며, 코루틴이 자식과 부모가 소통하기 위해 사용되는 특별한 컨텍스트
중단 함수에서 컨텍스트에 접근하기
- CoroutineScope 는 컨텍스트에 접근할 때 사용하는 coroutineContext 프로퍼티를 가지고 있음
- 일반적인 중단 함수에서는 어떻게 컨텍스트에 접근할 수 있을까?
- 컨텍스트는 중단 함수 사이에 전달되는 컨티뉴에이션 객체가 참조하고 있음
- 따라서 중단 함수에서 부모 컨텍스트에 접근하는 것이 가능
- coroutineContext 프로퍼티는 모든 중단 스코프에서 사용 가능하며, 이를 통해 컨텍스트에 접근 가능
컨텍스트를 개별적으로 생성하기
- 커스텀하게 만드는 경우는 흔치 않지만, 방법은 간단함
CoroutineContext.Element
인터페이스를 구현하는 클래스를 만드는 것- 이러한 클래스는
CoroutineContext.Key<*>
타입의Key
프로퍼티를 필요로 함- 이 키는 컨텍스트를 식별하는 그 키
- 가장 전형적인 사용 방법은 클래스의 컴패니언 객체를 키로 사용하는 것
|
|
- 테스트 환경과 프로덕션 환경에서 서로 다른 값을 쉽게 주입하기 위해 커스텀 컨텍스트를 사용하는 경우도 있지만, 흔한 방법은 아닐 것 같다.
|
|
요약
- CoroutineContext 는 맵이나 집합과 같은 컬렉션과 개념적으로 비슷
- CoroutineContext 는 Element 인터페이스의 인덱싱된 집합
- Element 또한 CoroutineContext
- CoroutineContext 안의 모든 원소는 식별할 수 있는 유일한 Key 를 가짐
- 같은 Key 를 가진 원소가 추가되면, Map 처럼 대체
- CoroutineContext 는 코루틴에 관련된 정보를 객체로 그룹화하고 전달하는 보편적인 방법
- CoroutineContext 는 코루틴에 저장
- CoroutineContext 를 사용해
- 코루틴의 상태가 어떤지 확인 가능
- 어떤 스레드를 선택할 지 가능
- CoroutineContext 을 사용해서 코루틴의 작동 방식을 정할 수 있다.