- 코틀린으로 비동기 클라이언트 및 서버 앱을 작성할 수 있게 하는 Jetbrains에서 만든 코틀린 프레임워크
- 코틀린 코루틴 기반으로 설계되어 비동기 처리 유리
- iOS, Web 등 코틀린 지원 플랫폼에서 동일한 네트워킹 코드 사용 가능 -> 주로 KMP 프로젝트에서 사용
- 리플렉션을 사용하는 Retrofit보다 속도가 빠르고 메모리 사용량이 적음
- DSL 방식을 사용하여 보다 직관적
- TimeOut 등 HttpClient에서 원하는 기능만 사용 가능
import io.ktor.client.HttpClient
import io.ktor.client.engine.android.Android
import io.ktor.client.plugins.DefaultRequest
import io.ktor.client.plugins.HttpTimeout
import io.ktor.client.plugins.contentnegotiation.ContentNegotiation
import io.ktor.client.plugins.defaultRequest
import io.ktor.client.plugins.logging.LogLevel
import io.ktor.client.plugins.logging.Logger
import io.ktor.client.plugins.logging.Logging
import io.ktor.client.plugins.observer.ResponseObserver
import io.ktor.client.request.accept
import io.ktor.client.request.header
import io.ktor.http.ContentType
import io.ktor.http.HttpHeaders
import io.ktor.http.contentType
import io.ktor.serialization.kotlinx.json.json
HttpClient(Android) {
install(ContentNegotiation) {
json(
Json {
prettyPrint = true
isLenient = true
useAlternativeNames = true
ignoreUnknownKeys = true
encodeDefaults = false
}
)
}
install(HttpTimeout) {
requestTimeoutMillis = NETWORK_TIME_OUT
connectTimeoutMillis = NETWORK_TIME_OUT
socketTimeoutMillis = NETWORK_TIME_OUT
}
install(Logging) {
logger = object: Logger {
override fun log(message: String) {
Log.v("Logger Ktor =>", message)
}
}
level = LogLevel.ALL
}
install(ResponseObserver) {
onResponse {
response - >
Log.d("HTTP status:", "${response.status.value}")
}
}
install(DefaultRequest) {
header(HttpHeaders.ContentType, ContentType.Application.Json)
}
defaultRequest {
contentType(ContentType.Application.Json)
accept(ContentType.Application.Json)
}
}
- 서비스에 API 요청 정의 - HttpClient와 Service를 분리하여 구현으며, interface로 정의 후, Impl로 구현하여도 문제 없다. get, post 등 HTTP 통신 전부 가능하며, query parameter의 경우 paramter()를 통해 넣어줄 수 있다.
suspend fun getMealsByCategory(category: String) =
client.get {
url(getString(R.string.meals_url))
parameter("c", category)
}
- 헤더를 추가할 때는 람다 안에 headers를 통해 추가할 수 있다
client.get(URL) {
headers {
append(HttpHeaders.Authorization, "Bearer YOUR_TOKEN_HERE")
append("Custom-Header", "MyValue")
}
}
- body는 람다 안에 setBody()를 통해 @Seriablizable 객체를 넣어줌으로서 전달할 수 있다
suspend fun createUser(user: User): HttpResponse {
return client.post("https://api.example.com/users") {
setBody(user) // @Serializable이 붙은 객체를 그대로 전달
}
}