Skip to content

1.6.x에서 2.0.x로 마이그레이션하기

이 가이드는 Ktor 애플리케이션을 1.6.x 버전에서 2.0.x 버전으로 마이그레이션하는 방법을 안내합니다.

Ktor 서버

서버 코드가 'io.ktor.server.*' 패키지로 이동되었습니다

서버 및 클라이언트 API를 통합하고 더 잘 구분하기 위해 서버 코드가 io.ktor.server.* 패키지로 이동되었습니다 (KTOR-2865). 이는 애플리케이션의 의존성임포트를 아래와 같이 업데이트해야 함을 의미합니다.

의존성

하위 시스템1.6.x2.0.0
Locationsio.ktor:ktor-locationsio.ktor:ktor-server-locations
Webjarsio.ktor:ktor-webjarsio.ktor:ktor-server-webjars
AutoHeadResponseio.ktor:ktor-server-coreio.ktor:ktor-server-auto-head-response
StatusPagesio.ktor:ktor-server-coreio.ktor:ktor-server-status-pages
CallIdio.ktor:ktor-server-coreio.ktor:ktor-server-call-id
DoubleReceiveio.ktor:ktor-server-coreio.ktor:ktor-server-double-receive
HTML DSLio.ktor:ktor-html-builderio.ktor:ktor-server-html-builder
FreeMarkerio.ktor:ktor-freemarkerio.ktor:ktor-server-freemarker
Velocityio.ktor:ktor-velocityio.ktor:ktor-server-velocity
Mustacheio.ktor:ktor-mustacheio.ktor:ktor-server-mustache
Thymeleafio.ktor:ktor-thymeleafio.ktor:ktor-server-thymeleaf
Pebbleio.ktor:ktor-pebbleio.ktor:ktor-server-pebble
kotlinx.serializationio.ktor:ktor-serializationio.ktor:ktor-server-content-negotiation, io.ktor:ktor-serialization-kotlinx-json
Gsonio.ktor:ktor-gsonio.ktor:ktor-server-content-negotiation, io.ktor:ktor-serialization-gson
Jacksonio.ktor:ktor-jacksonio.ktor:ktor-server-content-negotiation, io.ktor:ktor-serialization-jackson
Authenticationio.ktor:ktor-authio.ktor:ktor-server-auth
JWT authenticationio.ktor:ktor-auth-jwtio.ktor:ktor-server-auth-jwt
LDAP authenticationio.ktor:ktor-auth-ldapio.ktor:ktor-server-auth-ldap
DataConversionio.ktor:ktor-server-coreio.ktor:ktor-server-data-conversion
DefaultHeadersio.ktor:ktor-server-coreio.ktor:ktor-server-default-headers
Compressionio.ktor:ktor-server-coreio.ktor:ktor-server-compression
CachingHeadersio.ktor:ktor-server-coreio.ktor:ktor-server-caching-headers
ConditionalHeadersio.ktor:ktor-server-coreio.ktor:ktor-server-conditional-headers
CORSio.ktor:ktor-server-coreio.ktor:ktor-server-cors
Forwarded headersio.ktor:ktor-server-coreio.ktor:ktor-server-forwarded-header
HSTSio.ktor:ktor-server-coreio.ktor:ktor-server-hsts
HttpsRedirectio.ktor:ktor-server-coreio.ktor:ktor-server-http-redirect
PartialContentio.ktor:ktor-server-coreio.ktor:ktor-server-partial-content
WebSocketsio.ktor:ktor-websocketsio.ktor:ktor-server-websockets
CallLoggingio.ktor:ktor-server-coreio.ktor:ktor-server-call-logging
Micrometer metricio.ktor:ktor-metrics-micrometerio.ktor:ktor-server-metrics-micrometer
Dropwizard metricsio.ktor:ktor-metricsio.ktor:ktor-server-metrics
Sessionsio.ktor:ktor-server-coreio.ktor:ktor-server-sessions

모든 플러그인을 한 번에 추가하려면 io.ktor:ktor-server 아티팩트를 사용할 수 있습니다.

임포트

하위 시스템1.6.x2.0.0
Applicationimport io.ktor.application.*import io.ktor.server.application.*
Configurationimport io.ktor.config.*import io.ktor.server.config.*
Routingimport io.ktor.routing.*import io.ktor.server.routing.*
AutoHeadResponseimport io.ktor.features.*import io.ktor.server.plugins.autohead.*
StatusPagesimport io.ktor.features.*import io.ktor.server.plugins.statuspages.*
CallIdimport io.ktor.features.*import io.ktor.server.plugins.callid.*
DoubleReceiveimport io.ktor.features.*import io.ktor.server.plugins.doublereceive.*
Requestsimport io.ktor.request.*import io.ktor.server.request.*
Responsesimport io.ktor.response.*import io.ktor.server.response.*
Pluginsimport io.ktor.features.*import io.ktor.server.plugins.*
Locationsimport io.ktor.locations.*import io.ktor.server.locations.*
Static contentimport io.ktor.http.content.*import io.ktor.server.http.content.*
HTML DSLimport io.ktor.html.*import io.ktor.server.html.*
FreeMarkerimport io.ktor.freemarker.*import io.ktor.server.freemarker.*
Velocityimport io.ktor.velocity.*import io.ktor.server.velocity.*
Mustacheimport io.ktor.mustache.*import io.ktor.server.mustache.*
Thymeleafimport io.ktor.thymeleaf.*import io.ktor.server.thymeleaf.*
Pebbleimport io.ktor.pebble.*import io.ktor.server.pebble.*
ContentNegotiationimport io.ktor.features.*import io.ktor.server.plugins.contentnegotiation.*
kotlinx.serializationimport io.ktor.serialization.*import io.ktor.serialization.kotlinx.json.*
Gsonimport io.ktor.gson.*import io.ktor.serialization.gson.*
Jacksonimport io.ktor.jackson.*import io.ktor.serialization.jackson.*
Authenticationimport io.ktor.auth.*import io.ktor.server.auth.*
JWT authenticationimport io.ktor.auth.jwt.*import io.ktor.server.auth.jwt.*
LDAP authenticationimport io.ktor.auth.ldap.*import io.ktor.server.auth.ldap.*
Sessionsimport io.ktor.sessions.*import io.ktor.server.sessions.*
DefaultHeadersimport io.ktor.features.*import io.ktor.server.plugins.defaultheaders.*
Compressionimport io.ktor.features.*import io.ktor.server.plugins.compression.*
CachingHeadersimport io.ktor.features.*import io.ktor.server.plugins.cachingheaders.*
ConditionalHeadersimport io.ktor.features.*import io.ktor.server.plugins.conditionalheaders.*
CORSimport io.ktor.features.*import io.ktor.server.plugins.cors.*
Forwarded headersimport io.ktor.features.*import io.ktor.server.plugins.forwardedheaders.*
HSTSimport io.ktor.features.*import io.ktor.server.plugins.hsts.*
HttpsRedirectimport io.ktor.features.*import io.ktor.server.plugins.httpsredirect.*
PartialContentimport io.ktor.features.*import io.ktor.server.plugins.partialcontent.*
WebSocketsimport io.ktor.websocket.*import io.ktor.server.websocket.*
CallLoggingimport io.ktor.features.*import io.ktor.server.plugins.callloging.*
Micrometer metricimport io.ktor.metrics.micrometer.*import io.ktor.server.metrics.micrometer.*
Dropwizard metricsimport io.ktor.metrics.dropwizard.*import io.ktor.server.metrics.dropwizard.*

WebSockets 코드가 'websockets' 패키지로 이동되었습니다

WebSockets 코드가 http-cio에서 websockets 패키지로 이동되었습니다. 이에 따라 임포트를 다음과 같이 업데이트해야 합니다:

1.6.x2.0.0
import io.ktor.http.cio.websocket.*import io.ktor.websocket.*

참고로 이 변경사항은 클라이언트에도 영향을 미칩니다.

Feature가 Plugin으로 이름이 변경되었습니다

Ktor 2.0.0에서 _Feature_가 _Plugin_으로 이름이 변경되었습니다. 이는 요청/응답 파이프라인을 가로채는 기능을 더 잘 설명하기 위함입니다 (KTOR-2326). 이는 전체 Ktor API에 영향을 미치며, 아래 설명된 대로 애플리케이션을 업데이트해야 합니다.

임포트

플러그인 설치에는 임포트 업데이트가 필요하며, 또한 서버 코드io.ktor.server.* 패키지로 이동하는 것과 관련이 있습니다:

1.6.x2.0.0
import io.ktor.features.*import io.ktor.server.plugins.*

커스텀 플러그인

Feature를 Plugin으로 이름 변경은 커스텀 플러그인 관련 API에 다음과 같은 변경 사항을 도입합니다:

  • ApplicationFeature 인터페이스가 BaseApplicationPlugin으로 이름이 변경되었습니다.
  • Features 파이프라인 단계Plugins로 이름이 변경되었습니다.

참고로 v2.0.0부터 Ktor는 커스텀 플러그인 생성을 위한 새로운 API를 제공합니다. 일반적으로 이 API는 파이프라인, 단계 등 내부 Ktor 개념에 대한 이해를 요구하지 않습니다. 대신, onCall, onCallReceive, onCallRespond 등 다양한 핸들러를 사용하여 요청 및 응답 처리의 여러 단계에 액세스할 수 있습니다. 파이프라인 단계가 새로운 API 핸들러에 어떻게 매핑되는지는 다음 섹션에서 확인할 수 있습니다: 파이프라인 단계와 새로운 API 핸들러 매핑.

콘텐츠 협상 및 직렬화

콘텐츠 협상 및 직렬화 서버 API는 서버와 클라이언트 간의 직렬화 라이브러리를 재사용하도록 리팩토링되었습니다. 주요 변경사항은 다음과 같습니다:

  • ContentNegotiationktor-server-core에서 별도의 ktor-server-content-negotiation 아티팩트로 이동되었습니다.
  • 직렬화 라이브러리가 ktor-*에서 클라이언트에서도 사용되는 ktor-serialization-* 아티팩트로 이동되었습니다.

애플리케이션의 의존성임포트를 아래와 같이 업데이트해야 합니다.

의존성

하위 시스템1.6.x2.0.0
ContentNegotiationio.ktor:ktor-server-coreio.ktor:ktor-server-content-negotiation
kotlinx.serializationio.ktor:ktor-serializationio.ktor:ktor-serialization-kotlinx-json
Gsonio.ktor:ktor-gsonio.ktor:ktor-serialization-gson
Jacksonio.ktor:ktor-jacksonio.ktor:ktor-serialization-jackson

임포트

하위 시스템1.6.x2.0.0
kotlinx.serializationimport io.ktor.serialization.*import io.ktor.serialization.kotlinx.json.*
Gsonimport io.ktor.gson.*import io.ktor.serialization.gson.*
Jacksonimport io.ktor.jackson.*import io.ktor.serialization.jackson.*

커스텀 컨버터

ContentConverter 인터페이스에 의해 노출되는 함수의 시그니처가 다음과 같이 변경되었습니다:

kotlin
interface ContentConverter {
    suspend fun convertForSend(context: PipelineContext<Any, ApplicationCall>, contentType: ContentType, value: Any): Any?
    suspend fun convertForReceive(context: PipelineContext<ApplicationReceiveRequest, ApplicationCall>): Any?
}
kotlin
interface ContentConverter {
    suspend fun serialize(contentType: ContentType, charset: Charset, typeInfo: TypeInfo, value: Any): OutgoingContent?
    suspend fun deserialize(charset: Charset, typeInfo: TypeInfo, content: ByteReadChannel): Any?
}

테스팅 API

v2.0.0부터 Ktor 서버는 테스팅을 위한 새로운 API를 사용하며, 이는 KTOR-971에 설명된 다양한 문제를 해결합니다. 주요 변경사항은 다음과 같습니다:

  • withTestApplication/withApplication 함수가 새로운 testApplication 함수로 대체되었습니다.
  • testApplication 함수 내에서 기존 Ktor 클라이언트 인스턴스를 사용하여 서버에 요청하고 결과를 확인할 수 있습니다.
  • 특정 기능(예: 쿠키 또는 WebSockets)을 테스트하려면 새로운 클라이언트 인스턴스를 생성하고 해당 플러그인을 설치해야 합니다.

1.6.x 테스트를 2.0.0으로 마이그레이션하는 몇 가지 예시를 살펴보겠습니다:

기본 서버 테스트

아래 테스트에서 handleRequest 함수는 client.get 요청으로 대체되었습니다:

kotlin
@Test
fun testRootLegacyApi() {
    withTestApplication(Application::module) {
        handleRequest(HttpMethod.Get, "/").apply {
            assertEquals(HttpStatusCode.OK, response.status())
            assertEquals("Hello, world!", response.content)
        }
    }
}
kotlin
@Test
fun testRoot() = testApplication {
    val response = client.get("/")
    assertEquals(HttpStatusCode.OK, response.status)
    assertEquals("Hello, world!", response.bodyAsText())
}

x-www-form-urlencoded

아래 테스트에서 handleRequest 함수는 client.post 요청으로 대체되었습니다:

kotlin
@Test
fun testPostLegacyApi() = withTestApplication(Application::main) {
    with(handleRequest(HttpMethod.Post, "/signup"){
        addHeader(HttpHeaders.ContentType, ContentType.Application.FormUrlEncoded.toString())
        setBody(listOf("username" to "JetBrains", "email" to "[email protected]", "password" to "foobar", "confirmation" to "foobar").formUrlEncode())
    }) {
        assertEquals("The 'JetBrains' account is created", response.content)
    }
}
kotlin
@Test
fun testPost() = testApplication {
    val response = client.post("/signup") {
        header(HttpHeaders.ContentType, ContentType.Application.FormUrlEncoded.toString())
        setBody(listOf("username" to "JetBrains", "email" to "[email protected]", "password" to "foobar", "confirmation" to "foobar").formUrlEncode())
    }
    assertEquals("The 'JetBrains' account is created", response.bodyAsText())
}

multipart/form-data

v2.0.0에서 multipart/form-data를 구성하려면 클라이언트의 setBody 함수에 MultiPartFormDataContent를 전달해야 합니다:

kotlin
    @Test
    fun testUploadLegacyApi() = withTestApplication(Application::main) {
        with(handleRequest(HttpMethod.Post, "/upload"){
            val boundary = "WebAppBoundary"
            val fileBytes = File("ktor_logo.png").readBytes()

            addHeader(HttpHeaders.ContentType, ContentType.MultiPart.FormData.withParameter("boundary", boundary).toString())
            setBody(boundary, listOf(
                PartData.FormItem("Ktor logo", { }, headersOf(
                    HttpHeaders.ContentDisposition,
                    ContentDisposition.Inline
                        .withParameter(ContentDisposition.Parameters.Name, "description")
                        .toString()
                )),
                PartData.FileItem({ fileBytes.inputStream().asInput() }, {}, headersOf(
                    HttpHeaders.ContentDisposition,
                    ContentDisposition.File
                        .withParameter(ContentDisposition.Parameters.Name, "image")
                        .withParameter(ContentDisposition.Parameters.FileName, "ktor_logo.png")
                        .toString()
                ))
            ))
        }) {
            assertEquals("Ktor logo is uploaded to 'uploads/ktor_logo.png'", response.content)
        }
    }
kotlin
@Test
fun testUpload() = testApplication {
    val boundary = "WebAppBoundary"
    val response = client.post("/upload") {
        setBody(
            MultiPartFormDataContent(
                formData {
                    append("description", "Ktor logo")
                    append("image", File("ktor_logo.png").readBytes(), Headers.build {
                        append(HttpHeaders.ContentType, "image/png")
                        append(HttpHeaders.ContentDisposition, "filename=\"ktor_logo.png\"")
                    })
                },
                boundary,
                ContentType.MultiPart.FormData.withParameter("boundary", boundary)
            )
        )
    }
    assertEquals("Ktor logo is uploaded to 'uploads/ktor_logo.png'", response.bodyAsText())
}

JSON 데이터

v1.6.x에서는 kotlinx.serialization 라이브러리에서 제공하는 Json.encodeToString 함수를 사용하여 JSON 데이터를 직렬화할 수 있습니다. v2.0.0부터는 새로운 클라이언트 인스턴스를 생성하고 특정 형식으로 콘텐츠를 직렬화/역직렬화할 수 있는 ContentNegotiation 플러그인을 설치해야 합니다:

kotlin
@Test
fun testPostCustomerLegacyApi() = withTestApplication(Application::main) {
    with(handleRequest(HttpMethod.Post, "/customer"){
        addHeader(HttpHeaders.ContentType, ContentType.Application.Json.toString())
        setBody(Json.encodeToString(Customer(3, "Jet", "Brains")))
    }) {
        assertEquals("Customer stored correctly", response.content)
        assertEquals(HttpStatusCode.Created, response.status())
    }
}
kotlin
@Test
fun testPostCustomer() = testApplication {
    val client = createClient {
        install(ContentNegotiation) {
            json()
        }
    }
    val response = client.post("/customer") {
        contentType(ContentType.Application.Json)
        setBody(Customer(3, "Jet", "Brains"))
    }
    assertEquals("Customer stored correctly", response.bodyAsText())
    assertEquals(HttpStatusCode.Created, response.status)
}

테스트 중 쿠키 보존

v1.6.x에서는 테스트 시 요청 간에 쿠키를 보존하기 위해 cookiesSession이 사용됩니다. v2.0.0부터는 새로운 클라이언트 인스턴스를 생성하고 HttpCookies 플러그인을 설치해야 합니다:

kotlin
    @Test
    fun testRequestsLegacyApi() = withTestApplication(Application::main) {
        fun doRequestAndCheckResponse(path: String, expected: String) {
            handleRequest(HttpMethod.Get, path).apply {
                assertEquals(expected, response.content)
            }
        }

        cookiesSession {
            handleRequest(HttpMethod.Get, "/login") {}.apply {}
            doRequestAndCheckResponse("/user", "Session ID is 123abc. Reload count is 0.")
            doRequestAndCheckResponse("/user", "Session ID is 123abc. Reload count is 1.")
            doRequestAndCheckResponse("/user", "Session ID is 123abc. Reload count is 2.")

            handleRequest(HttpMethod.Get, "/logout").apply {}
            doRequestAndCheckResponse("/user", "Session doesn't exist or is expired.")
        }
    }
kotlin
    @Test
    fun testRequests() = testApplication {
        val client = createClient {
            install(HttpCookies)
        }

        val loginResponse = client.get("/login")
        val response1 = client.get("/user")
        assertEquals("Session ID is 123abc. Reload count is 1.", response1.bodyAsText())
        val response2 = client.get("/user")
        assertEquals("Session ID is 123abc. Reload count is 2.", response2.bodyAsText())
        val response3 = client.get("/user")
        assertEquals("Session ID is 123abc. Reload count is 3.", response3.bodyAsText())
        val logoutResponse = client.get("/logout")
        assertEquals("Session doesn't exist or is expired.", logoutResponse.bodyAsText())
    }

WebSockets

이전 API에서는 WebSocket 대화를 테스트하기 위해 handleWebSocketConversation이 사용됩니다. v2.0.0부터는 클라이언트가 제공하는 WebSockets 플러그인을 사용하여 WebSocket 대화를 테스트할 수 있습니다:

kotlin
    @Test
    fun testConversationLegacyApi() {
        withTestApplication(Application::module) {
            handleWebSocketConversation("/echo") { incoming, outgoing ->
                val greetingText = (incoming.receive() as Frame.Text).readText()
                assertEquals("Please enter your name", greetingText)

                outgoing.send(Frame.Text("JetBrains"))
                val responseText = (incoming.receive() as Frame.Text).readText()
                assertEquals("Hi, JetBrains!", responseText)
            }
        }
    }
kotlin
    @Test
    fun testConversation() {
        testApplication {
            val client = createClient {
                install(WebSockets)
            }

            client.webSocket("/echo") {
                val greetingText = (incoming.receive() as? Frame.Text)?.readText() ?: ""
                assertEquals("Please enter your name", greetingText)

                send(Frame.Text("JetBrains"))
                val responseText = (incoming.receive() as Frame.Text).readText()
                assertEquals("Hi, JetBrains!", responseText)
            }
        }
    }

DoubleReceive

v2.0.0부터 DoubleReceive 플러그인 설정은 receiveEntireContent와 반대되는 cacheRawRequest 속성을 도입합니다:

  • v1.6.x에서는 receiveEntireContent 속성이 기본적으로 false로 설정됩니다.
  • v2.0.0에서는 cacheRawRequest가 기본적으로 true로 설정됩니다. receiveEntireContent 속성은 제거되었습니다.

전달된 헤더

v2.0.0에서 ForwardedHeaderSupportXForwardedHeaderSupport 플러그인은 각각 ForwardedHeadersXForwardedHeaders로 이름이 변경되었습니다.

캐싱 헤더

캐싱 옵션을 정의하는 데 사용되는 options 함수는 이제 OutgoingContent 외에도 람다 인수로 ApplicationCall을 받습니다:

kotlin
install(CachingHeaders) {
    options { outgoingContent ->
        // ...
    }
}
kotlin
install(CachingHeaders) {
    options { call, outgoingContent ->
        // ...
    }
}

조건부 헤더

리소스 버전 목록을 정의하는 데 사용되는 version 함수는 이제 OutgoingContent 외에도 람다 인수로 ApplicationCall을 받습니다:

kotlin
install(ConditionalHeaders) {
    version { outgoingContent ->
        // ... 
    }
}
kotlin
install(ConditionalHeaders) {
    version { call, outgoingContent ->
        // ... 
    }
}

CORS

CORS 설정에 사용되는 몇 가지 함수 이름이 변경되었습니다:

  • host -> allowHost
  • header -> allowHeader
  • method -> allowMethod
kotlin
install(CORS) {
    host("0.0.0.0:5000")
    header(HttpHeaders.ContentType)
    method(HttpMethod.Options)
}
kotlin
install(CORS) {
    allowHost("0.0.0.0:5000")
    allowHeader(HttpHeaders.ContentType)
    allowMethod(HttpMethod.Options)
}

MicrometerMetrics

v1.6.x에서는 baseName 속성이 HTTP 요청 모니터링에 사용되는 Ktor 메트릭의 기본 이름(접두사)을 지정하는 데 사용됩니다. 기본적으로 ktor.http.server와 같습니다. v2.0.0부터 baseName은 기본값이 ktor.http.server.requestsmetricName으로 대체되었습니다.

Ktor 클라이언트

요청 및 응답

v2.0.0에서는 요청을 하고 응답을 받는 데 사용되는 API가 일관성과 검색 용이성(discoverability)을 높이기 위해 업데이트되었습니다 (KTOR-29).

요청 함수

여러 매개변수를 가진 요청 함수는 더 이상 사용되지 않습니다. 예를 들어, portpath 매개변수는 HttpRequestBuilder에 노출된 url 매개변수로 대체되어야 합니다:

kotlin
client.get(port = 8080, path = "/customer/3")
kotlin
client.get { url(port = 8080, path = "/customer/3") }

HttpRequestBuilder는 또한 요청 함수 람다 내에서 추가 요청 매개변수를 지정할 수 있도록 합니다.

요청 본문

요청 본문을 설정하는 데 사용되는 HttpRequestBuilder.body 속성이 HttpRequestBuilder.setBody 함수로 대체되었습니다:

kotlin
client.post("http://localhost:8080/post") {
    body = "Body content"
}
kotlin
client.post("http://localhost:8080/post") {
    setBody("Body content")
}

응답

v2.0.0부터 get, post, put, submitForm 등 요청 함수는 특정 타입의 객체를 받는 제네릭 인수를 허용하지 않습니다. 이제 모든 요청 함수는 HttpResponse 객체를 반환하며, 이는 특정 타입 인스턴스를 받기 위한 제네릭 인수가 있는 body 함수를 노출합니다. bodyAsText 또는 bodyAsChannel을 사용하여 콘텐츠를 문자열 또는 채널로 받을 수도 있습니다.

kotlin
val httpResponse: HttpResponse = client.get("https://ktor.io/")
val stringBody: String = httpResponse.receive()
val byteArrayBody: ByteArray = httpResponse.receive()
kotlin
val httpResponse: HttpResponse = client.get("https://ktor.io/")
val stringBody: String = httpResponse.body()
val byteArrayBody: ByteArray = httpResponse.body()

ContentNegotiation 플러그인이 설치된 경우, 다음과 같이 임의의 객체를 받을 수 있습니다:

kotlin
val customer: Customer = client.get("http://localhost:8080/customer/3")
kotlin
val customer: Customer = client.get("http://localhost:8080/customer/3").body()

스트리밍 응답

요청 함수에서 제네릭 인수 제거로 인해 스트리밍 응답을 받으려면 별도의 함수가 필요합니다. 이를 위해 prepareGet 또는 preparePost와 같이 prepare 접두사가 붙은 함수가 추가되었습니다:

kotlin
public suspend fun HttpClient.prepareGet(builder: HttpRequestBuilder): HttpStatement
public suspend fun HttpClient.preparePost(builder: HttpRequestBuilder): HttpStatement

아래 예시는 이 경우 코드를 변경하는 방법을 보여줍니다:

kotlin
client.get<HttpStatement>("https://ktor.io/").execute { httpResponse ->
    val channel: ByteReadChannel = httpResponse.receive()
    while (!channel.isClosedForRead) {
        // Read data
    }
}
kotlin
client.prepareGet("https://ktor.io/").execute { httpResponse ->
    val channel: ByteReadChannel = httpResponse.body()
    while (!channel.isClosedForRead) {
        // Read data
    }
}

전체 예시는 여기에서 찾을 수 있습니다: 데이터 스트리밍.

응답 유효성 검사

v2.0.0부터 응답 유효성 검사에 사용되는 expectSuccess 속성은 기본적으로 false로 설정됩니다. 이는 코드에 다음과 같은 변경 사항을 요구합니다:

  • 기본 유효성 검사를 활성화하고 2xx가 아닌 응답에 대해 예외를 발생시키려면 expectSuccess 속성을 true로 설정하세요.
  • handleResponseExceptionWithRequest를 사용하여 2xx가 아닌 예외를 처리하는 경우에도 expectSuccess를 명시적으로 활성화해야 합니다.

HttpResponseValidator

handleResponseException 함수는 예외에 추가 정보를 제공하기 위해 HttpRequest에 대한 접근을 추가하는 handleResponseExceptionWithRequest로 대체되었습니다:

kotlin
HttpResponseValidator {
    handleResponseException { exception ->
        // ...
    }
}
kotlin
HttpResponseValidator {
    handleResponseExceptionWithRequest { exception, request ->
        // ...
    }
}

콘텐츠 협상 및 직렬화

Ktor 클라이언트는 이제 콘텐츠 협상을 지원하며 Ktor 서버와 직렬화 라이브러리를 공유합니다. 주요 변경사항은 다음과 같습니다:

  • JsonFeaturektor-client-content-negotiation 아티팩트에서 찾을 수 있는 ContentNegotiation으로 인해 더 이상 사용되지 않습니다.
  • 직렬화 라이브러리가 ktor-client-*에서 ktor-serialization-* 아티팩트로 이동되었습니다.

클라이언트 코드의 의존성임포트를 아래와 같이 업데이트해야 합니다.

의존성

하위 시스템1.6.x2.0.0
ContentNegotiationn/aio.ktor:ktor-client-content-negotiation
kotlinx.serializationio.ktor:ktor-client-serializationio.ktor:ktor-serialization-kotlinx-json
Gsonio.ktor:ktor-client-gsonio.ktor:ktor-serialization-gson
Jacksonio.ktor:ktor-client-jacksonio.ktor:ktor-serialization-jackson

임포트

하위 시스템1.6.x2.0.0
ContentNegotiationn/aimport io.ktor.client.plugins.contentnegotiation.*
kotlinx.serializationimport io.ktor.client.features.json.*import io.ktor.serialization.kotlinx.json.*
Gsonimport io.ktor.client.features.json.*import io.ktor.serialization.gson.*
Jacksonimport io.ktor.client.features.json.*import io.ktor.serialization.jackson.*

Bearer 인증

refreshTokens 함수는 이제 HttpResponse 람다 인수(it) 대신 RefreshTokenParams 인스턴스를 람다 수신자(this)로 사용합니다:

kotlin
bearer {
    refreshTokens {  // it: HttpResponse
        // ...
    }
}
kotlin
bearer {
    refreshTokens { // this: RefreshTokenParams
        // ...
    }
}

RefreshTokenParams는 다음 속성을 노출합니다:

  • response: 응답 매개변수에 접근하기 위함;
  • client: 토큰을 새로 고치기 위한 요청을 하기 위함;
  • oldTokens: loadTokens를 사용하여 얻은 토큰에 접근하기 위함.

HttpSend

HttpSend 플러그인의 API는 다음과 같이 변경되었습니다:

kotlin
client[HttpSend].intercept { originalCall, request ->
    if (originalCall.something()) {
        val newCall = execute(request)
        // ...
    }
}
kotlin
client.plugin(HttpSend).intercept { request ->
    val originalCall = execute(request)
    if (originalCall.something()) {
        val newCall = execute(request)
        // ...
    }
}

참고로 v2.0.0에서는 플러그인에 접근하기 위한 인덱스 접근이 지원되지 않습니다. 대신 HttpClient.plugin 함수를 사용하세요.

HttpClient.get(plugin: HttpClientPlugin) 함수가 제거되었습니다

2.0.0 버전부터 클라이언트 플러그인을 받는 HttpClient.get 함수는 제거되었습니다. 대신 HttpClient.plugin 함수를 사용하세요.

kotlin
client.get(HttpSend).intercept { ... }
// or
client[HttpSend].intercept { ... }
kotlin
client.plugin(HttpSend).intercept { ... }

Feature가 Plugin으로 이름이 변경되었습니다

Ktor 서버와 마찬가지로 클라이언트 API에서도 _Feature_가 _Plugin_으로 이름이 변경되었습니다. 이는 아래 설명된 대로 애플리케이션에 영향을 미칠 수 있습니다.

임포트

플러그인 설치를 위한 임포트를 업데이트하세요:

하위 시스템1.6.x2.0.0
`import io.ktor.client.features.*``import io.ktor.client.plugins.*`
인증
The Auth plugin handles authentication and authorization in your client application.
` import io.ktor.client.features.auth.* ` ` import io.ktor.client.features.auth.providers.* ` ` import io.ktor.client.plugins.auth.* ` ` import io.ktor.client.plugins.auth.providers.* `
쿠키
The HttpCookies plugin handles cookies automatically and keep them between calls in a storage.
`import io.ktor.client.features.cookies.*``import io.ktor.client.plugins.cookies.*`
로깅
Required dependencies: io.ktor:ktor-client-logging Code example: %example_name%
`import io.ktor.client.features.logging.*``import io.ktor.client.plugins.logging.*`
WebSockets
The Websockets plugin allows you to create a multi-way communication session between a server and a client.
`import io.ktor.client.features.websocket.*``import io.ktor.client.plugins.websocket.*`
콘텐츠 인코딩
The ContentEncoding plugin allows you to enable specified compression algorithms (such as 'gzip' and 'deflate') and configure their settings.
`import io.ktor.client.features.compression.*``import io.ktor.client.plugins.compression.*`

커스텀 플러그인

HttpClientFeature 인터페이스가 HttpClientPlugin으로 이름이 변경되었습니다.

네이티브 타겟을 위한 새로운 메모리 모델

v2.0.0부터 네이티브 타겟에서 Ktor 클라이언트를 사용하려면 새로운 Kotlin/Native 메모리 모델을 활성화해야 합니다: 새로운 MM 활성화.

v2.2.0부터 새로운 Kotlin/Native 메모리 모델은 기본적으로 활성화됩니다.

'Ios' 엔진이 'Darwin'으로 이름이 변경되었습니다

v2.0.0에서 Ios 엔진이 iOS뿐만 아니라 macOS 또는 tvOS를 포함한 다른 운영 체제를 타겟팅하기 때문에 Darwin으로 이름이 변경되었습니다. 이로 인해 다음과 같은 변경 사항이 발생합니다:

  • io.ktor:ktor-client-ios 아티팩트가 io.ktor:ktor-client-darwin으로 이름이 변경되었습니다.
  • HttpClient 인스턴스를 생성하려면 Darwin 클래스를 인수로 전달해야 합니다.
  • IosClientEngineConfig 구성 클래스가 DarwinClientEngineConfig로 이름이 변경되었습니다.

Darwin 엔진을 구성하는 방법을 알아보려면 Darwin 섹션을 참조하세요.

WebSockets 코드가 'websockets' 패키지로 이동되었습니다

WebSockets 코드가 http-cio에서 websockets 패키지로 이동되었습니다. 이에 따라 임포트를 다음과 같이 업데이트해야 합니다:

1.6.x2.0.0
import io.ktor.http.cio.websocket.*import io.ktor.websocket.*

기본 요청

DefaultRequest 플러그인은 HttpRequestBuilder 대신 DefaultRequestBuilder 구성 클래스를 사용합니다:

kotlin
val client = HttpClient(CIO) {
    defaultRequest {
        // this: HttpRequestBuilder
    }
}
kotlin
val client = HttpClient(CIO) {
    defaultRequest {
        // this: DefaultRequestBuilder
    }
}