RxKotlin을 공부하면서 이해하지 못한 개념 정리
728x90
반응형

이해하지 못하는 내 표정을 대변하는 듯 하다.🤨

 

요즘 Rx를 공부하고 있다. "요즘 핫하다", "트렌드"다라고 하기에는 실무에서는 어느 정도 당연하게 쓰고 있다. 늦은 감은 있지만 더 늦는 것보다 지금이라도 공부하자 싶어서 [코틀린 리액티브 프로그래밍]이라는 책을 보고 있다. 이 서적은 요 근래 서적중 가장 마음에 드는 IT 서적이기에 학습이 끝날 때 리뷰도 꼭 하고 싶다. (물론 아직 절반도 보지 못해서 뒷장 공부할 때 마음이 바뀔 수 있다.) 아무리 괜찮은 서적이라도 내 기반 지식이 모자라면 이해하지 못하는 포인트가 생긴다. 학습중 이해하지 못한 개념을, 일종의 중간 점검이라고 생각하면서 작성해본다.

 

1. 코루틴의 async와 await

Rx를 학습하고 있어서 코루틴도 추가적으로 학습을 했다. 하지만 Rx를 위한 보조 학습으로 코루틴에 접근을 해서 이해하지 못하는 포인트가 있었다. 그 중 하나가 async.

 

서브루틴의 확장개념인 코루틴. 쉽게 이해시키기 위해 경량 스레드라고 설명하기도 한다. 실제로 공식문서에서 그렇게 표현을 하고 있다. 이 코루틴에서 async와 await는 핵심적인 키워드다.

 

val time = GlobalScope.async {
    longRunningTask()
}

runBlocking {
    println("Printing time ${time.await()}")
}

suspend fun longRunningTask(): Long {
    return measureTimeMillis {
        delay(2000)
    }
}

async와 await의 설명은 안드로이드 공식 문서에서 참고했다. 먼저 async는 비동기 코드를 작성하기 위해 코루틴의 시작을 알린다. 물론 launch로도 시작할 수 있다. 이 둘의 차이는 다음과 같다.

  • launch : Job을 반환하고 결과 값을 제공하지 않는다.
  • async
    • 다른 코루틴 내부에서만 사용하거나 정지 함수 내에서 병렬 분해를 실행할 때 사용한다.
    • 결과값을 가지고 있는 Job인 Deferred를 반환한다.

async가 반환한 값은 await 함수로 얻을 수 있다. await는 값을 반환하기 전에 코루틴이 완료되도록 보장하면서 코루틴을 중지시킬 수 있다.

 

 

2. CommonPool

공부를 하다보니 다음과 같은 설명을 볼 수 있었다.

async 코드 블록은 전달된 코루틴 컨텍스트에서 비동기적으로 블록 내부의 코드를 실행한다. 기본적으로 코루틴 컨텍스트에는 세가지 유형이 있다. (중략) CommonPool은 공통 스레드 풀에서 실행되거나 새로운 코루틴 컨텍스트를 만들 수 있음을 의미한다.

읽다가 눈에 띄는게 'CommonPool'이라는 단어였다. 쉽게 감이 잡히지 않는 단어였고 쉽게 설명되어 있는 자료도 부족하여 공부하는데 애를 먹었던 사항이다.

 

CommonPool을 이해하기 전에 알아야 하는 것이 Fork/Join Framework이다. Java7에 등장한 기능으로 효율적인 병렬 처리를 목적으로 한다. (물론 자바에서만 사용하는 개념은 아니다.) 하나의 작업을 분할 가능한 상태까지 분할한 다음, 각각 별도의 work thread로 처리한 후 다시 작업을 합치는 것을 말한다. Fork/Join Framework은 크기가 조정되는 ThreadPool을 가지는데 그 이름이 ForkJoinPool이다.

 

Java8에서는 이 ForkJoinPool을 개선하기 위해 commonPool() 메서드를 추가하였다. 명시적으로 어떤 Pool을 사용하는지 정해져 있지 않은 대부분의 작업에 이 메서드를 사용할 수 있다. Common pool, 말 그대로 기본적이고 공통적인 ThreadPool을 사용하여 리소스 사용량을 감소시킨다. 코루틴은 병렬처리는 아니지만 병렬처리처럼 동작하게 만들려고 이 CommonPool을 사용한 것이다.

 

 

3. 원시타입 (Primitive type)

코틀린의 모든 것들은 클래스와 객체인데 자바와는 다르게 따로 원시타입이 존재하지 않는다라고 한다. 여기서 원시타입은 무엇일까?

 

데이터 타입에는 원시타입과 참조타입(Reference type), 두 가지로 분류된다.

  • 원시타입
    • 선언된 변수가 실제값을 가진다 (스택 메모리, 참조타입보다 접근이 빠르다. 차지하는 메모리양이 적다.)
    • null을 가질 수 없다
    • 제네릭 타입을 사용할 수 없다
  • 참조타입
    • 선언된 변수가 메모리 주소를 값으로 가진다 (힙 메모리에서 실제 값이 있는 스택 메모리)
    • null을 가질 수 있다
    • 제네릭 타입을 사용할 수 있다

이제 Koltin과 Java의 참조타입 Boolean을 살펴보자

package kotlin

public class Boolean private constructor() : Comparable<Boolean> {
  // 생략
}
package java.lang;

public final class Integer extends Number implements Comparable<Integer> {
  // 생략
}

참고로 코틀린은 원시타입과 wrapper type을 구분하지 않고, 컴파일시 Java의 primitive 또는 wrapper 타입으로 자동변환된다

 

 

 

 

 

4. 인터리브(Interleave) 방식

AsyncSubject에 대해 공부할 때, AsyncSubject는 인터리브 방식으로 작동하지 않는다는 설명이 있었다. 원문을 찾아보면 아래와 같이 설명되어 있다.

Note that as the outputs suggest, AsyncSubject doesn't in an interleave manner, that is, it will replay its action multiple times to emit the value to multiple Observers, although it is only one value.

사실 번역을 어떻게 하느냐의 문제가 아닐까 싶다. AsyncSubject를 Interleave로 표현한 곳이 거의 이 서적뿐이어서 나름대로의 추측으로 이해했다. 그러니 정답이라고 보지 말고 참고정도만 하시길 바란다.

 

인터리브 혹은 인터리빙에 대한 자료를 찾을 때 자주 등장하는 것이 메모리 인터리빙(Memory Interleaving)이었다. 여기서 힌트를 얻어보았다. 

메모리 접근 시간을 최소화 하기 위해 메모리를 복수개의 모듈로 나누고 각 모듈에 연속적인 주소를 부여하여 동시에 접근이 가능하게 하는 기법
(출처 : niceit.tistory.com/283)

책에서 설명한 것이 이 메모리 인터리빙일수도 아닐수도 있다. 난 아닐 것으로 예상하고 추측을 해보았을 때 다음과 같다.

인터리브 방식이라면, 하나의 값을 사용해 여러 옵저버에게 동시에 내보낼 것이다. 하지만 AsyncSubject는 하나의 값을 사용해 여러 옵저버에게 내보내는 작업을 '반복'한다. 그러니 인터리브 방식이라고 볼 수 없다.

아니... 공부를 이렇게 하면 안될 것 같기도 한데... 혹시나 이 접근법에 문제가 있다면(문제가 있지. 당연히 있지) 지적을 부탁드린다.

728x90
반응형