비법 1: 비동기 맵 (mapAsync)

개념

여러 요소를 병렬로 변환하여 성능 향상

구현

suspend fun <T, R> List<T>.mapAsync(
    transformation: suspend (T) -> R
): List<R> = coroutineScope {
    [email protected] { async { transformation(it) } }  // 각 요소를 병렬 작업으로 시작
        .awaitAll()  // 모든 작업 완료 대기
}

사용 예시

// 학생 정보 병렬 조회
suspend fun getBestStudent(
    semester: String,
    repo: StudentsRepository
): Student =
    repo.getStudentIds(semester)
        .mapAsync { repo.getStudent(it) }
        .maxBy { it.result }

// 사용자 코스 병렬 처리
suspend fun getCourses(user: User): List<UserCourse> =
    courseRepository.getAllCourses()
        .mapAsync { composeUserCourse(user, it) }
        .filterNot { courseShouldBeHidden(user, it) }
        .sortedBy { it.state.ordinal }

핵심

순차 처리 → 병렬 처리로 성능 대폭 향상


비법 2: 지연 초기화 중단 (suspendLazy)

개념

suspend 함수도 한 번만 실행하고 결과 재사용

문제

// ❌ 컴파일 에러
val connection by lazy { makeConnection() }

해결책

fun <T> suspendLazy(
    initializer: suspend () -> T
): suspend () -> T {
    var mutex = Mutex()  // 동시성 제어
    var holder: Any? = NOT_SET  // 초기화 여부 확인

    return {
        if (holder !== NOT_SET) holder as T  // 이미 초기화됨
        else mutex.withLock {  // 락으로 안전하게
            if (holder === NOT_SET) holder = initializer()
            holder as T
        }
    }
}

사용 예시

suspend fun makeConnection(): String {
    println("Creating connection")
    delay(1000)
    return "Connection"
}

val getConnection = suspendLazy { makeConnection() }

suspend fun main() {
    println(getConnection()) // "Creating connection" → "Connection" (1초 후)
    println(getConnection()) // "Connection" (즉시)
    println(getConnection()) // "Connection" (즉시)
}