Ktor: 그것이 뭐인가, 비동기 HTTP 클라이언트의 특징

저자: IT Sectr 게시일: 2026-03-07 읽는 시간: 8 분

Ktor는 JetBrains가 서버 및 클라이언트 개발을 위한 동명의 프레임워크의 일부로 개발한 Kotlin용 비동기 HTTP 클라이언트입니다. Ktor는 Kotlin 코루틴 위에 구축되었으며 멀티플랫폼을 지원합니다. JetBrains, 2025에 따르면, Ktor는 리플렉션과 추가 종속성 없이 Kotlin 생태계와의 네이티브 통합을 제공합니다.

핵심 요점

  • Ktor — 멀티플랫폼 지원이 있는 Kotlin 비동기 HTTP 클라이언트
  • 코루틴 — 콜백과 리액티브 스트림 없이 요청을 실행하는 기반
  • 플러그인 — 직렬화, 로깅 및 인증을 위한 모듈식 확장 시스템
  • 멀티플랫폼 — Android, iOS, Desktop 및 Server를 위한 단일 코드
  • Kotlinx Serialization — @Serializable을 통한 리플렉션 없는 네이티브 직렬화

Ktor란 무엇인가?

Ktor는 JetBrains가 만든 Kotlin으로 비동기 서버 및 클라이언트 애플리케이션을 구축하기 위한 프레임워크입니다. Ktor Client는 프레임워크의 클라이언트 측 부분으로, Kotlin 코루틴, 멀티플랫폼(JVM, Native, JS) 및 모듈식 플러그인 기반 아키텍처를 완전히 지원하는 HTTP 클라이언트를 제공합니다.

Ktor는 2018년에 Kotlin 우선 프로젝트를 위한 Retrofit 및 OkHttp의 대안으로 등장했습니다. 어노테이션이 있는 Java 방식을 포팅한 Retrofit과 달리, Ktor Client는 요청 구성에 Kotlin DSL을 사용합니다 — 어노테이션과 리플렉션이 없습니다. 이는 Kotlin 개발자에게 코드를 더 읽기 쉽고 타입 안전하게 만듭니다.

2024 Kotlin Multiplatform 설문조사에 따르면, Ktor Client는 Kotlin Multiplatform Mobile (KMM) 프로젝트의 35%에서 사용되어 Kotlin 커뮤니티에서 OkHttp 다음으로 두 번째로 인기 있는 HTTP 클라이언트입니다. Ktor는 멀티플랫폼 지원과 Kotlin 생태계와의 네이티브 통합이 중요한 프로젝트에서 선호됩니다.

Ktor Client 작동 방식

Ktor Client 아키텍처는 플러그인의 파이프라인을 기반으로 합니다. 각 요청은 요청, 응답을 수정하거나 로깅, 압축, 직렬화, 인증과 같은 부수 작업을 수행할 수 있는 설치된 플러그인 시퀀스를 통과합니다.

HttpClient { } DSL 블록을 통해 HTTP 클라이언트를 생성할 때 엔진(OkHttp, Android, CIO, Darwin)을 지정하고 플러그인을 설치합니다. 각 엔진은 특정 플랫폼에 대한 저수준 요청 전송을 구현합니다: Android에서는 OkHttp 엔진, iOS에서는 Darwin(URLSession), Desktop에서는 CIO(Coroutine I/O)가 사용됩니다. HttpClient는 현재 플랫폼에 최적의 엔진을 자동으로 선택합니다.

Ktor Client의 요청은 suspend 함수를 통해 실행되며, 이는 코루틴과의 완전한 통합을 의미합니다. 콜백, RxJava 또는 LiveData가 필요 없으며, 스레드를 차단하지 않고 비동기적으로 작동하는 suspend를 사용한 순차적 코드만 있습니다.

요청 처리 파이프라인

Ktor 파이프라인은 단계로 구성됩니다: 먼저 요청이 설치된 플러그인(예: JSON용 ContentNegotiation, 로그용 Logging)을 통과한 다음 엔진이 HTTP 요청을 실행하고, 응답은 역직렬화를 위해 다시 플러그인을 통과합니다. 각 플러그인은 파이프라인 코루틴에서 실행되는 suspend 함수입니다.

Ktor 파이프라인의 중요한 장점은 조건부 처리 기능입니다. 플러그인은 URL 또는 요청 헤더를 확인하고 조건이 충족되지 않으면 처리를 건너뛸 수 있습니다. 예를 들어, gzip을 사용한 ContentEncoding은 Content-Encoding: gzip 헤더가 포함된 응답에만 적용되고, Auth는 보호된 엔드포인트에 대해서만 트리거되어 공개 API에 영향을 주지 않습니다.

이 파이프라인 접근 방식을 통해 플러그인을 유연하게 결합할 수 있습니다: JSON과 함께 ContentNegotiation을 설치하고, Bearer 토큰으로 Auth를 추가하고, ContentEncoding 압축과 HttpTimeout을 활성화할 수 있으며, 모두 올바른 순서로 함께 작동합니다. 플러그인 설치 순서가 중요합니다: 먼저 설치된 플러그인이 다른 플러그인보다 먼저 요청을 처리합니다.

Ktor Client 플러그인

플러그인은 Ktor의 모듈식 확장 시스템으로, Retrofit 어노테이션과 OkHttp 인터셉터를 대체합니다. 각 플러그인은 특정 작업을 해결하며 HttpClient 블록의 install() 함수를 통해 설치됩니다. Ktor는 내장 플러그인을 제공하며 사용자 정의 플러그인 생성도 허용합니다.

플러그인목적
ContentNegotiationKotlinx Serialization을 통한 JSON, XML 직렬화 및 역직렬화
Logging구성 가능한 수준의 요청 및 응답 로깅
Auth인증: Basic, Bearer, Digest (자동 토큰 갱신 포함)
HttpTimeout연결, 읽기 및 요청 제한 시간 구성
ContentEncoding투명한 gzip 및 deflate 압축
DefaultRequest모든 요청에 대한 기본값 설정

사용자 정의 플러그인

특정 작업을 위해 createClientPlugin을 통해 사용자 정의 플러그인이 생성됩니다. 플러그인은 요청(onRequest), 응답(onResponse)을 가로채거나 오류(onError)를 처리할 수 있습니다. 이는 OkHttp의 Interceptor를 완전히 대체하지만, 타입이 지정된 Kotlin API와 suspend 함수 지원을 제공합니다.

사용자 정의 플러그인은 메트릭 추가, 자동 재시도 로직, 요청 추적 또는 엔드포인트의 A/B 테스트에 편리합니다. OkHttp 인터셉터와 달리, Ktor 플러그인은 Kotlin으로 작성되고 코루틴 컨텍스트에서 실행되어 오류 및 제한 시간 처리를 단순화합니다.

요청 디버깅을 위해 Logging 플러그인이 ALL, HEADERS 또는 BODY 수준으로 사용됩니다. Logging은 메서드, URL, 상태, 헤더 및 요청과 응답의 본문을 출력합니다. OkHttp의 HttpLoggingInterceptor와 달리, Ktor Logging은 비동기적으로 작동하며 구성을 변경하기 위해 애플리케이션을 중지하지 않고 로그 수준(ERROR, WARN, INFO, DEBUG)으로 필터링하도록 구성할 수 있습니다.

Kotlin의 Ktor Client 코드 예제

Ktor Client를 사용한 기본 GET 요청을 살펴보겠습니다. JSON용 ContentNegotiation 플러그인이 설치된 HttpClient가 생성됩니다. 요청은 suspend 함수 get()을 통해 실행되고 결과는 자동으로 데이터 클래스로 역직렬화됩니다.

kotlin
data class User(
    val login: String,
    val id: Int,
    val avatarUrl: String
)

val client = HttpClient {
    install(ContentNegotiation) {
        json(Json {
            ignoreUnknownKeys = true
        })
    }
}

suspend fun getUser(): User {
    return client.get("https://api.github.com/users/octocat").body()
}

본문이 있는 POST 요청의 경우 contentType() 및 body()와 함께 post() 함수가 사용됩니다. Ktor는 설치된 ContentNegotiation을 통해 객체를 자동으로 JSON으로 직렬화합니다. DSL 스타일은 코드를 순차적이고 읽기 쉽게 만듭니다.

kotlin
data class CreateRepo(
    val name: String,
    val description: String,
    val private: Boolean
)

suspend fun createRepo(): Unit {
    val repo = CreateRepo(
        name = "my-project",
        description = "Sample project",
        private = false
    )
    client.post("https://api.github.com/user/repos") {
        contentType(ContentType.Application.Json)
        setBody(repo)
    }
}

제한 시간 및 헤더 구성

HttpTimeoutDefaultRequest는 구성을 위한 두 가지 주요 플러그인입니다. HttpTimeout은 시간 제한을 설정하고 DefaultRequest는 모든 요청에 대한 헤더와 URL 매개변수를 지정하여 각 호출에서 코드 중복을 제거합니다.

kotlin
val client = HttpClient {
    install(HttpTimeout) {
        connectTimeoutMillis = 15000
        requestTimeoutMillis = 30000
    }
    install(DefaultRequest) {
        url("https://api.github.com/")
        header("Accept", "application/json")
    }
}

Ktor 멀티플랫폼 지원

멀티플랫폼은 OkHttp 및 Retrofit에 비한 Ktor의 주요 장점입니다. Ktor Client는 JVM(Android, Server), Native(iOS, macOS, Windows, Linux) 및 JS(Browser)에서 실행됩니다. 동일한 HTTP 클라이언트 코드가 변경 없이 모든 플랫폼에서 실행되며, 이는 Kotlin Multiplatform 프로젝트에 특히 가치 있습니다.

각 플랫폼에 대해 Ktor는 자체 엔진을 사용합니다. Android에서는 기본적으로 OkHttp 엔진이 사용되어 OkHttp 생태계와의 완전한 호환성을 제공합니다. iOS에서는 URLSession 기반의 DarwinEngine이 사용됩니다. 서버의 경우 CIOEngine(Coroutine I/O)이 사용됩니다. 엔진은 명시적으로 지정할 수 있습니다: HttpClient(OkHttp) { } 또는 HttpClient(Darwin) { }.

엔진을 선택할 때 기능을 고려하세요: OkHttp 엔진은 HTTP/2와 연결 풀링을 지원하고, DarwinEngine은 네이티브 iOS 네트워크 통합과 백그라운드 URLSession 세션을 제공하며, CIOEngine은 외부 종속성 없이 순수 코루틴 구현입니다. 웹 타겟의 경우 fetch API를 통해 작동하는 JsEngine 또는 BrowserEngine이 사용됩니다.

모든 플랫폼에서 통합된 API 덕분에 데이터 로드 코드는 Android, iOS 및 Desktop에서 동일하게 보입니다. 이는 Retrofit(Android)과 URLSession(iOS)에서 별도 구현과 비교하여 KMM 프로젝트의 코드 중복을 60~80% 줄입니다. 플러그인도 변경 없이 모든 플랫폼에서 작동합니다.

Ktor 사용 시 일반적인 실수

HttpClient 종료 무시는 Ktor의 일반적인 실수입니다. HttpClient는 Closeable을 구현하며 애플리케이션 종료 시 client.close()를 통해 닫아야 합니다. Android에서는 Activity의 onDestroy() 또는 ViewModel.onCleared()에서 수행됩니다. 닫히지 않은 클라이언트는 코루틴 및 엔진 스레드 누수로 이어집니다.

잘못된 플러그인 순서는 요청 처리를 망가뜨릴 수 있습니다. 예를 들어, ContentNegotiation은 콘텐츠 유형이 올바르게 적용되도록 DefaultRequest 전에 설치해야 합니다. Logging은 모든 수정 후 최종 요청 버전을 기록하기 위해 마지막에 설치하는 것이 좋습니다. 플러그인이 예기치 않게 동작하면 순서를 실험해 보세요.

suspend 함수의 예외 처리 부재. Ktor는 네트워크 오류에 IOException을, HTTP 4xx 상태에 ClientRequestException을 던집니다. get(), post() 및 기타 메서드에 대한 모든 호출에 try-catch 블록이 필수입니다. 각 메서드에서 try-catch를 중복하지 않고 전역 오류 처리를 위해 HttpClient 블록에서 HttpResponseValidator를 사용하세요.

자주 묻는 질문

Ktor와 Retrofit의 차이점은 무엇인가요?

Ktor는 어노테이션과 리플렉션 없이 Kotlin DSL과 플러그인을 사용합니다. Retrofit은 Java 어노테이션과 리플렉션으로 구축되었습니다. Ktor는 멀티플랫폼을 지원하고 Retrofit은 JVM/Android만 지원합니다. Ktor는 네이티브로 코루틴과 작동하며 Retrofit은 래퍼를 통해 suspend를 추가했습니다.

Android에 가장 적합한 Ktor 엔진은 무엇인가요?

Android에는 OkHttp 엔진이 최적입니다 — OkHttp 생태계와의 호환성, 연결 풀링, 캐싱 및 HTTP/2를 제공합니다. HttpClient(OkHttp) { }를 통해 선택하세요. 대안으로 Ktor에 내장된 CIOEngine이 있지만 Android에서는 덜 안정적입니다.

Ktor는 HTTP/2를 지원하나요?

네, Ktor는 해당 엔진을 통해 HTTP/2를 지원합니다. OkHttp 엔진은 OkHttp에서 HTTP/2 지원을 상속받습니다. iOS의 DarwinEngine은 URLSession을 통해 HTTP/2를 지원합니다. CIOEngine은 서버 측에서 HTTP/2를 지원합니다. 엔진 선택이 프로토콜 지원 수준을 결정합니다.

Ktor Client에서 인증을 구성하는 방법은?

bearer { } 설정과 함께 Auth 플러그인을 사용하세요. 플러그인은 각 요청에 Authorization 헤더를 자동으로 추가하고 refreshTokens를 통해 401 응답 시 토큰을 갱신할 수 있습니다. 예: install(Auth) { bearer { loadTokens { BearerTokens(token, refreshToken) } } }.

iOS에서 Ktor Client를 사용할 수 있나요?

네, Ktor Client는 URLSession을 사용하는 DarwinEngine을 통해 iOS에서 완전히 작동합니다. 모든 플러그인, 직렬화 및 코루틴은 Android와 동일하게 iOS에서 작동합니다. 이로 인해 Ktor는 Kotlin Multiplatform Mobile (KMM) 프로젝트의 기본 HTTP 클라이언트가 됩니다.

요약

  • Ktor — JetBrains의 멀티플랫폼 지원 비동기 HTTP 클라이언트
  • Kotlin DSL이 어노테이션 대체 — 리플렉션 없는 프로그래매틱 블록을 통한 구성
  • 플러그인 ContentNegotiation, Auth, Logging, HttpTimeout이 모듈식으로 기능 확장
  • 코루틴 — 실행 기반: 콜백과 리액티브 스트림 없는 모든 suspend 메서드
  • 멀티플랫폼 — Android, iOS, Desktop, Server 및 JS를 위한 단일 코드
  • 엔진 OkHttp, Darwin, CIO가 Ktor를 특정 플랫폼에 맞게 조정
  • HttpResponseValidator가 try-catch 중복 없이 HTTP 오류 처리를 중앙화

턴키 방식의 모바일 애플리케이션을 개발해 드립니다

IT Sectr는 2017년부터 스타트업과 기업을 위한 iOS 및 Android 애플리케이션을 만듭니다. 저희가 상담해 드리고 최적의 솔루션을 제안하겠습니다.

프로젝트 논의

더 읽어보기