Lock in $30 Savings on PRO—Offer Ends Soon! ⏳
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
API 통신, Retrofit 대신 Ktor 어떠신가요
Search
Pangmoo
April 03, 2023
Programming
2
930
API 통신, Retrofit 대신 Ktor 어떠신가요
GDG Korea Android super.init(version=4) 발표 자료 입니다.
Pangmoo
April 03, 2023
Tweet
Share
More Decks by Pangmoo
See All by Pangmoo
게임 개발하던 학생이이 세계에선 안드로이드 개발자?
pangmoo
0
350
Compose Web 개발하기
pangmoo
2
960
코틀린으로 멀티플랫폼 만들기
pangmoo
1
1.6k
Kotlin Multiplatform으로 Android/iOS/Desktop 번역기 만들기
pangmoo
0
650
MADC 2023 Kotlin Multiplatform (KMP)
pangmoo
0
150
안드로이드 UI 상태 저장 권장사항
pangmoo
1
870
Compose로 Android&Desktop 멀티플랫폼 만들기
pangmoo
0
550
Other Decks in Programming
See All in Programming
ELYZA_Findy AI Engineering Summit登壇資料_AIコーディング時代に「ちゃんと」やること_toB LLMプロダクト開発舞台裏_20251216
elyza
2
610
Cell-Based Architecture
larchanjo
0
140
「コードは上から下へ読むのが一番」と思った時に、思い出してほしい話
panda728
PRO
39
26k
The Art of Re-Architecture - Droidcon India 2025
siddroid
0
120
大規模Cloud Native環境におけるFalcoの運用
owlinux1000
0
190
脳の「省エネモード」をデバッグする ~System 1(直感)と System 2(論理)の切り替え~
panda728
PRO
0
120
実は歴史的なアップデートだと思う AWS Interconnect - multicloud
maroon1st
0
260
令和最新版Android Studioで化石デバイス向けアプリを作る
arkw
0
440
Vibe codingでおすすめの言語と開発手法
uyuki234
0
120
バックエンドエンジニアによる Amebaブログ K8s 基盤への CronJobの導入・運用経験
sunabig
0
170
re:Invent 2025 トレンドからみる製品開発への AI Agent 活用
yoskoh
0
210
SwiftUIで本格音ゲー実装してみた
hypebeans
0
500
Featured
See All Featured
CSS Pre-Processors: Stylus, Less & Sass
bermonpainter
359
30k
Designing for Timeless Needs
cassininazir
0
93
Discover your Explorer Soul
emna__ayadi
2
1k
Ten Tips & Tricks for a 🌱 transition
stuffmc
0
34
Abbi's Birthday
coloredviolet
0
3.7k
So, you think you're a good person
axbom
PRO
0
1.8k
Lightning talk: Run Django tests with GitHub Actions
sabderemane
0
92
Bioeconomy Workshop: Dr. Julius Ecuru, Opportunities for a Bioeconomy in West Africa
akademiya2063
PRO
0
31
A Soul's Torment
seathinner
1
2k
Imperfection Machines: The Place of Print at Facebook
scottboms
269
13k
Performance Is Good for Brains [We Love Speed 2024]
tammyeverts
12
1.4k
Typedesign – Prime Four
hannesfritz
42
2.9k
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