Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
API 통신, Retrofit 대신 Ktor 어떠신가요
Search
Pangmoo
April 03, 2023
Programming
960
2
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
API 통신, Retrofit 대신 Ktor 어떠신가요
GDG Korea Android super.init(version=4) 발표 자료 입니다.
Pangmoo
April 03, 2023
More Decks by Pangmoo
See All by Pangmoo
게임 개발하던 학생이이 세계에선 안드로이드 개발자?
pangmoo
0
390
Compose Web 개발하기
pangmoo
12
1.4k
코틀린으로 멀티플랫폼 만들기
pangmoo
3
1.6k
Kotlin Multiplatform으로 Android/iOS/Desktop 번역기 만들기
pangmoo
0
690
MADC 2023 Kotlin Multiplatform (KMP)
pangmoo
0
190
안드로이드 UI 상태 저장 권장사항
pangmoo
1
920
Compose로 Android&Desktop 멀티플랫폼 만들기
pangmoo
0
590
Other Decks in Programming
See All in Programming
Dataformのリポジトリを立ち上げるときにまずやること / dataform-day0-2026
snhryt
0
180
キャリア迷子上等 ─ "ない道"は自分で作ればいい
16bitidol
3
2.2k
AI 時代のソフトウェア設計の学び方
masuda220
PRO
29
13k
The NotImplementedError Problem in Ruby
koic
1
920
そのテスト、説明できますか?~LWテスト戦略FW~のご紹介
nakahara
0
160
Claspは野良GASの夢をみるか
takter00
0
210
LaravelLive Japan の裏方のすべて — 第188回 PHP勉強会@東京 (2026-06-24)
suguruooki
2
110
「AIで開発し、AIを届ける」をEvalでつなぐ 〜AIネイティブに始めるプロダクト開発の実践〜 / Connecting "Develop with AI, deliver AI" with Eval
rkaga
4
5.4k
技術記事、 専門家としてのプログラマ、 言語化
mizchi
13
6.5k
不変条件と整合性境界—ビジネスが決める設計判断と実現パターン / Invariants and Consistency Boundaries
nrslib
14
5.8k
AI時代のUIはどこへ行く?その2!
yusukebe
22
7.5k
スマートグラスで並列バイブコーディング
hyshu
0
260
Featured
See All Featured
Paper Plane
katiecoart
PRO
1
52k
Reflections from 52 weeks, 52 projects
jeffersonlam
356
21k
The Cost Of JavaScript in 2023
addyosmani
55
10k
How People are Using Generative and Agentic AI to Supercharge Their Products, Projects, Services and Value Streams Today
helenjbeal
1
220
The Director’s Chair: Orchestrating AI for Truly Effective Learning
tmiket
1
200
Ten Tips & Tricks for a 🌱 transition
stuffmc
0
140
Exploring anti-patterns in Rails
aemeredith
3
420
The SEO identity crisis: Don't let AI make you average
varn
0
500
I Don’t Have Time: Getting Over the Fear to Launch Your Podcast
jcasabona
34
2.8k
A Guide to Academic Writing Using Generative AI - A Workshop
ks91
PRO
1
330
Designing for humans not robots
tammielis
254
26k
It's Worth the Effort
3n
188
29k
Transcript
@ @kisa002 @holykisa
None
None
None
None
None
None
None
None
None
None
None
None
None
None
None
None
None
• • •
None
None
None
None
None
implementation("com.google.code.gson:gson:2.10.1") implementation("com.squareup.retrofit2:retrofit:2.9.0") implementation("com.squareup.retrofit2:converter-gson:2.6.0")
None
None
None
object KtorClient { val client = HttpClient(CIO) }
None
KtorClient.client .get("https://haeyum.dev/articles") .body<String>() // or bodyAsText()
None
None
None
None
implementation("io.ktor:ktor-serialization- kotlinx-json:2.2.4") implementation("io.ktor:ktor-client-content- negotiation:2.2.4") plugins { // skip... id("org.jetbrains.kotlin.plugin.serialization") version
"1.8.10" }
@Serializable data class Article( val id: String, val title: String,
val content: String )
object KtorClient { val client = HttpClient(CIO) { install(ContentNegotiation) {
json() } } } object KtorClient { val client = HttpClient(CIO) { install(ContentNegotiation) { json() // for json xml() // for xml cbor() // for cbor protobuf() // for protobuf } } }
None
RetrofitClient.service.getArticles().enqueue(object : Callback<List<Article>> { override fun onResponse(call: Call<List<Article>>, response: Response<List<Article>>)
{ println("onResponse: ${response.body()}") } override fun onFailure(call: Call<List<Article>>, t: Throwable) { println("onFailure: $t") } })
None
None
None
None
suspend fun fetchArticlesKtor(): List<Article> = KtorClient .client .get("https://haeyum.dev/articles") .body() suspend
fun fetchArticlesKtor(): List<Article> = runCatching { KtorClient .client .get("https://haeyum.dev/articles") .body<List<Article>>() }.getOrDefault(emptyList())
None
None
None
None
None
None
None
Caused by: kotlinx.serialization.MissingFieldException: Field 'id' is required for type with
serial name 'com.haeyum.ktorretrofit.Article', but it was missing at path: $[0] at path: $[0] at kotlinx.serialization.json.internal.StreamingJsonDeco der.decodeSerializableValue(StreamingJsonDecoder.kt:9 0)
@Serializable data class Article( val title: String, val content: String
)
None
None
• • •
object KtorClient { val client = HttpClient(CIO) { install(ContentNegotiation) {
json(Json { ignoreUnknownKeys = true coerceInputValues = true prettyPrint = true isLenient = true // ... }) } } }
• • • • • • • •
None
None
None
class VersionInterceptor(private val versionName: String, private val versionCode: String) :
Interceptor { override fun intercept(chain: Interceptor.Chain): Response = chain.proceed( chain .request() .newBuilder() .addHeader("versionName", versionName) .addHeader("versionCode", versionCode) .build() ) }
val retrofit = Retrofit.Builder() .baseUrl(BASE_URL) .client(provideOkHttpClient(BuildConfig.VERSION_NAME, BuildConfig.VERSION_CODE)) .addConverterFactory(GsonConverterFactory.create()) .build() val
service = retrofit.create(RetrofitService::class.java) private fun provideOkHttpClient(versionName: String, versionCode: String): OkHttpClient { return OkHttpClient.Builder() .addInterceptor(VersionInterceptor(versionName, versionCode)) .build() }
None
object KtorClient { val client = HttpClient(CIO) { install(ContentNegotiation) {
json() } } } object KtorClient { val client = HttpClient(CIO) { install(ContentNegotiation) { json() } defaultRequest { header("versionName", BuildConfig.VERSION_NAME) header("versionCode", BuildConfig.VERSION_CODE) } } }
None
• • •
implementation("io.ktor:ktor-client-mock:2.2.4") testImplementation("io.ktor:ktor-client-mock:2.2.4")
val mockEngine = MockEngine { request -> val articles =
listOf( Article("First", "First article"), Article("Second", "This is Mock!"), Article("GDG Korea Android!", "Ktor is awesome!"), ) val headers = headersOf("Content-Type" to listOf(ContentType.Application.Json.toString())) when (request.url.encodedPath) { "/articles" -> respond(Json.encodeToString(articles), headers = headers) else -> respond("Not Found", HttpStatusCode.NotFound) } }
val client = HttpClient(CIO) { install(ContentNegotiation) { json() } defaultRequest
{ header("versionName", BuildConfig.VERSION_NAME) header("versionCode", BuildConfig.VERSION_CODE) } } 실제 서버 사용 시
val client = HttpClient(mockEngine) { install(ContentNegotiation) { json() } defaultRequest
{ header("versionName", BuildConfig.VERSION_NAME) header("versionCode", BuildConfig.VERSION_CODE) } } Mock 사용 시
suspend fun fetchArticlesKtor(): List<Article> = runCatching { KtorClient .client .get("https://haeyum.dev/articles")
.body<List<Article>>() }.getOrDefault(emptyList())
None
when (request.url.encodedPath) { "/articles" -> respond(Json.encodeToString(articles), headers = headers) "/article"
-> { request.url.parameters["id"]?.toIntOrNull()?.let { id -> articles.getOrNull(id)?.let { respond(Json.encodeToString(it), headers = headers) } ?: respond("Not Found", HttpStatusCode.NotFound) } ?: respond("Bad Request", HttpStatusCode.BadRequest) } else -> respond("Not Found", HttpStatusCode.NotFound) } val article = kotlin.runCatching { KtorClient .client .get("/article") { parameter("id", 2) } .body<Article>() }.getOrNull()
None
None
None
None
None
None
None
[email protected]
@ @kisa002 @holykisa