아티클 목록으로 가기

연구 분석: Java로 작성된 Retrofit은 어떻게 Kotlin의 코루틴을 보간하여 `suspend` 함수를 지원하는가

skydovesJaewoong Eum (skydoves)||4분 소요

연구 분석: Java로 작성된 Retrofit은 어떻게 Kotlin의 코루틴을 보간하여 suspend 함수를 지원하는가

현대 안드로이드 개발 생태계에서 Kotlin과 Java 사이의 시너지는 여전히 매우 중요합니다. 특히 수많은 전통적인 프로젝트가 Java로 작성되어 있기 때문에, 두 언어 간의 원활한 협업은 필수적입니다. 이러한 뛰어난 상호운용성(interoperability)을 보여주는 대표적인 사례가 바로 Square의 Retrofit 라이브러리입니다. Retrofit은 전적으로 Java로 작성되었음에도 불구하고, Kotlin의 suspend 함수를 매끄럽게 지원하여 네트워크 요청을 위한 깔끔하고 관용적인 비동기 코드를 작성할 수 있게 해 줍니다. 이 기능은 마법이 아니라, Kotlin 컴파일러와 Retrofit의 동적 리플렉션(reflection) 기반 아키텍처 사이에 구축된 정교한 협력의 결과물입니다.

이 글에서는 이러한 "보간(interpolation)"을 가능하게 만드는 내부 메커니즘을 심층적으로 분석합니다. Java 라이브러리가 본래 자체적으로는 아무런 개념도 갖고 있지 않은 언어 기능과 어떻게 상호작용할 수 있는지 그 원리를 밝혀 보겠습니다. 안드로이드 개발에서 Retrofit을 사용하시는 분이라면, 내부 동작 원리를 이해함으로써 문제 해결 역량을 한 단계 끌어올리실 수 있을 것입니다.

기반 원리: CPS(Continuation-Passing Style) 변환

Kotlin의 일시 중단 메커니즘의 핵심은 CPS(Continuation-Passing Style) 변환이라는 컴파일러 변환 과정에 있습니다. Kotlin 컴파일러는 suspend 함수를 처리할 때, 해당 함수의 시그니처와 본문을 근본적으로 재작성하여 JVM(Java Virtual Machine)과 호환되도록 만듭니다. JVM 자체에는 코루틴이라는 개념이 내장되어 있지 않기 때문에 이러한 변환이 반드시 필요합니다.

Retrofit 인터페이스에 정의된 간단한 suspend 함수를 살펴보겠습니다.

interface ApiService {
    @GET("user/{id}")
    suspend fun getUser(@Path("id") userId: String): User
}

Kotlin 개발자의 관점에서 보면, 이 함수는 일시 중단되었다가 다시 재개될 수 있는 함수입니다. 하지만 JVM 수준에서는 컴파일러가 이를 수정된 시그니처를 가진 일반적인 Java 메서드로 변환합니다. 컴파일된 바이트코드를 개념적으로 표현하면 다음과 같습니다.

이 아티클은 구독자 전용입니다

Dove Letter를 구독하시면 안드로이드, 코틀린 개발 관련 독점 아티클의 전체 내용을 볼 수 있습니다.

구독하기
아티클 목록으로 가기