
Retrofit과 Ktor Client 비교는 안드로이드 네트워크 계층을 만들 때 자주 나오는 고민입니다. 둘 다 HTTP 요청을 보낼 수 있지만, 코드 모양과 확장 방향이 꽤 다릅니다.
핵심은 무엇이 더 최신인가가 아니라 우리 앱의 네트워크 계층을 어떤 방식으로 유지할 것인가입니다. 팀의 경험, 멀티플랫폼 계획, 테스트 전략까지 함께 봐야 합니다.
Retrofit과 Ktor Client 선택 기준

Retrofit은 인터페이스로 API를 선언한다
Retrofit 공식 문서는 HTTP API를 Java interface로 바꾸는 type-safe HTTP client라고 설명합니다. Android 개발자에게 오래 익숙한 방식은 API endpoint를 인터페이스 메서드로 선언하는 구조입니다.
interface PostApi {
@GET("posts/{id}")
suspend fun getPost(@Path("id") id: String): PostDto
}이 방식은 API 목록이 문서처럼 보이고, 팀원이 endpoint를 찾기 쉽습니다. 서버 API가 명확하고 Android 전용 앱이라면 Retrofit은 여전히 안정적인 선택입니다.
Ktor Client는 Kotlin DSL과 플러그인 구성이 중심이다
Ktor의 client 문서는 Kotlin Multiplatform에서 사용할 수 있는 비동기 HTTP client를 설명합니다. Ktor Client는 요청을 DSL처럼 작성하고, ContentNegotiation, Logging 같은 기능을 plugin으로 구성하는 흐름이 자연스럽습니다.
val client = HttpClient {
install(ContentNegotiation) {
json()
}
}
val post: PostDto = client.get("https://api.example.com/posts/1").body()Kotlin 코드 안에서 요청 구성을 세밀하게 다루고 싶거나, Android 밖의 Kotlin target까지 염두에 둔다면 Ktor Client가 더 자연스러울 수 있습니다.
coroutine 지원만으로는 차이가 충분하지 않다
요즘 Retrofit도 suspend 함수를 자연스럽게 사용할 수 있고, Ktor Client도 coroutine 기반으로 동작합니다. 따라서 단순히 coroutine을 쓸 수 있는지만으로 고르면 차이가 잘 보이지 않습니다.
더 중요한 차이는 API를 인터페이스로 고정해 관리할지, 요청 빌더와 plugin 조합으로 유연하게 다룰지입니다.
멀티플랫폼 계획이 있으면 Ktor 쪽을 더 검토한다
Kotlin Multiplatform으로 공통 네트워크 코드를 공유할 계획이 있다면 Ktor Client의 장점이 커질 수 있습니다. 반대로 Android 앱 하나만 안정적으로 운영하고 Retrofit 기반 코드와 테스트가 이미 많다면 굳이 바꿀 이유가 약할 수 있습니다.
- Android 전용 앱이고 팀이 Retrofit에 익숙하면 Retrofit이 실용적이다.
- KMP 공통 모듈을 고려하면 Ktor Client를 검토할 가치가 있다.
- 인터페이스 기반 API 문서성이 중요하면 Retrofit이 읽기 쉽다.
- 요청 구성을 코드로 유연하게 바꾸고 싶으면 Ktor가 편할 수 있다.
테스트와 에러 모델을 먼저 정해야 한다
라이브러리 선택보다 중요한 것은 네트워크 계층의 경계입니다. DTO를 어디서 domain model로 바꿀지, HTTP error를 어떤 sealed class나 Result로 감쌀지, mock 서버나 fake client를 어떻게 붙일지 정해야 합니다.
네트워크 계층이 화면 코드에 바로 스며들면 Retrofit을 쓰든 Ktor를 쓰든 유지보수가 어려워집니다. Offline-first 구조와 repository 경계는 Offline-first Android 앱 구조 글과도 이어집니다.
실무 구조: 라이브러리는 repository 뒤에 숨긴다
Retrofit을 쓰든 Ktor Client를 쓰든 화면이나 use case가 HTTP client를 직접 알게 만들면 교체 비용이 커집니다. 네트워크 라이브러리는 data layer 안에 두고, domain이나 UI는 repository interface만 바라보게 하는 편이 좋습니다.
interface PostRepository {
suspend fun getPost(id: String): Result<Post>
}
class RetrofitPostRepository(
private val api: PostApi
) : PostRepository {
override suspend fun getPost(id: String): Result<Post> =
runCatching { api.getPost(id).toDomain() }
}이렇게 하면 Retrofit에서 Ktor로 바꾸더라도 화면 코드는 크게 흔들리지 않습니다. 바뀌는 것은 구현체와 테스트 fixture 쪽입니다.
에러 모델을 먼저 정해야 한다
네트워크 계층에서 가장 많이 망가지는 지점은 성공 응답 예제가 아니라 실패 처리입니다. timeout, 401, 500, 파싱 실패, 네트워크 미연결을 모두 예외 하나로 던지면 화면이 어떤 메시지를 보여줘야 하는지 애매해집니다.
sealed interface NetworkError {
data object Unauthorized : NetworkError
data object Timeout : NetworkError
data object Offline : NetworkError
data class Server(val code: Int) : NetworkError
data class Unknown(val cause: Throwable) : NetworkError
}선택 기준을 팀 상황으로 바꾸기
- API가 많고 문서처럼 관리하고 싶다면 Retrofit interface가 강점이다.
- KMP 공통 모듈에서 같은 client를 쓰고 싶다면 Ktor Client가 유리할 수 있다.
- 기존 코드가 Retrofit으로 안정화되어 있다면 교체보다 에러 모델 정리가 먼저일 수 있다.
- 새 프로젝트에서 Kotlin DSL과 plugin 구성을 선호하면 Ktor를 실험할 가치가 있다.
- 둘 중 무엇을 쓰든 repository 경계와 테스트 전략이 더 중요하다.
정리
Retrofit과 Ktor Client 중 하나가 절대적으로 더 좋다고 말하기는 어렵습니다. Retrofit은 인터페이스 기반 API 선언과 Android 생태계의 익숙함이 강점이고, Ktor Client는 Kotlin DSL, plugin 구성, 멀티플랫폼 확장성에서 장점이 있습니다. 선택 전에 네트워크 계층의 책임, 테스트 방식, 앱의 확장 방향을 먼저 정하는 것이 좋습니다.