응답 받기
HTTP 요청을 수행하는 데 사용되는 모든 함수(request
, get
, post
등)는 HttpResponse
객체로 응답을 받을 수 있도록 합니다.
HttpResponse
는 응답 본문을 다양한 방식(원시 바이트, JSON 객체 등)으로 얻고, 상태 코드, 콘텐츠 타입, 헤더와 같은 응답 파라미터를 획득하는 데 필요한 API를 노출합니다. 예를 들어, 파라미터 없는 GET
요청에 대한 HttpResponse
는 다음과 같이 받을 수 있습니다:
val response: HttpResponse = client.get("https://ktor.io/docs/welcome.html")
응답 파라미터 받기
HttpResponse
클래스를 사용하면 상태 코드, 헤더, HTTP 버전 등과 같은 다양한 응답 파라미터를 얻을 수 있습니다.
상태 코드
응답의 상태 코드를 얻으려면 HttpResponse.status
속성을 사용하세요:
import io.ktor.client.*
import io.ktor.client.call.*
import io.ktor.client.request.*
import io.ktor.http.*
val httpResponse: HttpResponse = client.get("https://ktor.io/")
if (httpResponse.status.value in 200..299) {
println("Successful response!")
}
헤더
HttpResponse.headers
속성을 사용하면 모든 응답 헤더를 포함하는 Headers 맵을 얻을 수 있습니다. 또한, HttpResponse
는 특정 헤더 값을 받기 위한 다음 함수들을 노출합니다:
contentType
for theContent-Type
header valuecharset
for a charset from theContent-Type
header value.etag
for theE-Tag
header value.setCookie
for theSet-Cookie
header value.
Ktor는 또한 호출 간에 쿠키를 유지할 수 있는 HttpCookies 플러그인을 제공합니다.
응답 본문 받기
원시 본문
응답의 원시 본문을 받으려면 body
함수를 호출하고 필요한 타입을 파라미터로 전달하세요. 아래 코드 스니펫은 String
으로 원시 본문을 받는 방법을 보여줍니다:
val httpResponse: HttpResponse = client.get("https://ktor.io/")
val stringBody: String = httpResponse.body()
마찬가지로, ByteArray
로 본문을 얻을 수 있습니다:
val httpResponse: HttpResponse = client.get("https://ktor.io/")
val byteArrayBody: ByteArray = httpResponse.body()
아래 실행 가능한 예제는 ByteArray
로 응답을 받아 파일에 저장하는 방법을 보여줍니다:
val client = HttpClient()
val file = File.createTempFile("files", "index")
runBlocking {
val httpResponse: HttpResponse = client.get("https://ktor.io/") {
onDownload { bytesSentTotal, contentLength ->
println("Received $bytesSentTotal bytes from $contentLength")
}
}
val responseBody: ByteArray = httpResponse.body()
file.writeBytes(responseBody)
println("A file saved to ${file.path}")
}
위 예제의 onDownload()
확장 함수는 다운로드 진행 상황을 표시하는 데 사용됩니다.
비 스트리밍 요청의 경우, 응답 본문은 자동으로 메모리에 로드되고 캐시되어 반복적인 접근을 허용합니다. 이는 작은 페이로드에는 효율적이지만, 큰 응답에서는 높은 메모리 사용량을 초래할 수 있습니다.
큰 응답을 효율적으로 처리하려면, 응답을 메모리에 저장하지 않고 증분적으로 처리하는 스트리밍 방식을 사용하세요.
JSON 객체
ContentNegotiation 플러그인이 설치된 경우, 응답을 받을 때 JSON 데이터를 데이터 클래스로 역직렬화할 수 있습니다:
val customer: Customer = client.get("http://localhost:8080/customer/3").body()
더 자세히 알아보려면 데이터 주고받기를 참조하세요.
ContentNegotiation 플러그인은 클라이언트와 서버 모두에서 사용할 수 있습니다. 사용 사례에 맞는 것을 사용해야 합니다.
데이터 스트리밍
HttpResponse.body
함수를 호출하여 본문을 얻을 때, Ktor는 응답을 메모리에서 처리하고 전체 응답 본문을 반환합니다. 전체 응답을 기다리는 대신 응답의 청크를 순차적으로 받아야 하는 경우, 스코프가 지정된 execute 블록과 함께 HttpStatement
를 사용하세요. 아래 실행 가능한 예제는 응답 콘텐츠를 청크(바이트 패킷) 단위로 받아 파일에 저장하는 방법을 보여줍니다:
val client = HttpClient(CIO)
val file = File.createTempFile("files", "index")
val stream = file.outputStream().asSink()
val fileSize = 100 * 1024 * 1024
runBlocking {
client.prepareGet("https://httpbin.org/bytes/$fileSize").execute { httpResponse ->
val channel: ByteReadChannel = httpResponse.body()
var count = 0L
stream.use {
while (!channel.exhausted()) {
val chunk = channel.readRemaining()
count += chunk.remaining
chunk.transferTo(stream)
println("Received $count bytes from ${httpResponse.contentLength()}")
}
}
}
println("A file saved to ${file.path}")
}
이 예제에서는 ByteReadChannel
이 데이터를 비동기적으로 읽는 데 사용됩니다. ByteReadChannel.readRemaining()
을 사용하면 채널의 모든 사용 가능한 바이트를 가져오고, Source.transferTo()
는 데이터를 파일에 직접 써서 불필요한 할당을 줄입니다.
추가 처리 없이 응답 본문을 파일에 저장하려면, 대신 ByteReadChannel.copyAndClose()
함수를 사용할 수 있습니다:
client.prepareGet("https://httpbin.org/bytes/$fileSize").execute { httpResponse ->
val channel: ByteReadChannel = httpResponse.body()
channel.copyAndClose(file.writeChannel())
println("A file saved to ${file.path}")
}