독서/코틀린 코루틴
[ 코틀린 코루틴 - 18장 ] 핫 데이터 소스와 콜드 데이터 소스
dongx._.2
2025. 6. 3. 23:24
우리가 사용하는 대부분의 데이터 소스는 두 가지 종류로 구분이 가능하기 때문에 핫 스트림 데이터와 콜드 스트림 데이터의 차이를 이해하는 것이 소프트웨어적인 측면에서 상당히 유용함
핫 | 콜드 |
컬렉션 (List, Set) | Sequence, Stream |
Channel | Flow, RxJava 스트림 |
핫 vs 콜드
Hot과 Cold 데이터 스트림의 차이는 아래와 같다.
핫 데이터 스트림
- 데이터를 소비하는 것과 무관하게 원소를 생성
- 항상 사용 가능한 상태이며, 여러 번 사용 되었을 때 결과를 다시 계산할 필요가 없음
fun main() {
val l = listOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
.map { m(it) } // m1 m2 m3 m4 m5 m6 m7 m8 m9 m10
// 처음 계산해둔 map으로 재사용 가능
println(l) // [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
println(l.find { it > 10 }) // 16
println(l.find { it > 10 }) // 16
println(l.find { it > 10 }) // 16
}
콜드 데이터 스트림
- 요청이 있을 때만 작업을 수행하며, 아무것도 저장하지 않음
fun main() {
val s = sequenceOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
.map { m(it) }
println(s.toList()) // [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
// sequence는 호출할 때마다 재계산 됨
println(s.find { it > 10 }) // m1 m2 m3 m4 16
println(s.find { it > 10 }) // m1 m2 m3 m4 16
println(s.find { it > 10 }) // m1 m2 m3 m4 16
}
리스트와 시퀀스의 차이
이 차이는 아래와 같이 리스트와 시퀀스의 동작을 보면 알 수 있음
fun main() {
val l = buildList {
repeat(3) {
add("User$it")
println("L: Added User")
}
}
val l2 = l.map {
println("L: Processing")
"Processed $it"
}
val s = sequence {
repeat(3) {
yield("User$it")
println("S: Added User")
}
}
val s2 = s.map {
println("S: Processing")
"Processed $it"
}
}
// L: Added User
// L: Added User
// L: Added User
// L: Processing
// L: Processing
// L: Processing
리스트의 경우 핫 데이터 스트림이기 때문에 연산이 바로 실행 되었으나, 시퀀스의 경우 최종 연산자가 없기 때문에 연산이 실행되지 않은 것을 볼 수 있음
위와 같은 특징으로 인해 콜드 데이터 스트림은
- 무한할 수 있음
- 최소한의 연산만 수행함
- 메모리를 적게 사용함 (중간에 생성되는 값들을 보관할 필요가 없기 때문)
아래 예제에서 더 자세한 동작 차이를 볼 수 있음
fun m(i: Int): Int {
print("m$i ")
return i * i
}
fun f(i: Int): Boolean {
print("f$i ")
return i >= 10
}
fun main() {
listOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
.map { m(it) }
.find { f(it) }
.let { print(it) }
// m1 m2 m3 m4 m5 m6 m7 m8 m9 m10 f1 f4 f9 f16 16
println()
sequenceOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
.map { m(it) }
.find { f(it) }
.let { print(it) }
// m1 f1 m2 f4 m3 f9 m4 f16 16
}
리스트의 동작
- 모든 원소에 map이 적용된 후 find가 실행 됨
시퀀스의 동작
- 시퀀스의 경우 원소를 하나씩 가져와서 연산을 적용
시퀀스의 처리 방식은 모든 중간 과정을 계산하고 모든 데이터 처리가 완료된 컬렉션을 반환하는 리스트의 처리 방식과 매우 다름
따라서 리스트의 경우 원소의 처리 순서가 달라지며, 컬렉션 처리 과정에서 좀 더 많은 메모리를 필요로 하고, 더 많은 연산을 수행하게 됨