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
2
890
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
290
Compose Web 개발하기
pangmoo
0
540
코틀린으로 멀티플랫폼 만들기
pangmoo
0
1.4k
Kotlin Multiplatform으로 Android/iOS/Desktop 번역기 만들기
pangmoo
0
610
MADC 2023 Kotlin Multiplatform (KMP)
pangmoo
0
140
안드로이드 UI 상태 저장 권장사항
pangmoo
1
830
Compose로 Android&Desktop 멀티플랫폼 만들기
pangmoo
0
510
Other Decks in Programming
See All in Programming
明示と暗黙 ー PHPとGoの インターフェイスの違いを知る
shimabox
2
480
なんとなくわかった気になるブロックテーマ入門/contents.nagoya 2025 6.28
chiilog
1
270
すべてのコンテキストを、 ユーザー価値に変える
applism118
3
1.2k
Discover Metal 4
rei315
2
130
PHP 8.4の新機能「プロパティフック」から学ぶオブジェクト指向設計とリスコフの置換原則
kentaroutakeda
2
760
イベントストーミング図からコードへの変換手順 / Procedure for Converting Event Storming Diagrams to Code
nrslib
2
650
データの民主化を支える、透明性のあるデータ利活用への挑戦 2025-06-25 Database Engineering Meetup#7
y_ken
0
350
High-Level Programming Languages in AI Era -Human Thought and Mind-
hayat01sh1da
PRO
0
760
関数型まつりレポート for JuliaTokai #22
antimon2
0
160
Systèmes distribués, pour le meilleur et pour le pire - BreizhCamp 2025 - Conférence
slecache
0
120
What Spring Developers Should Know About Jakarta EE
ivargrimstad
0
440
初学者でも今すぐできる、Claude Codeの生産性を10倍上げるTips
s4yuba
16
11k
Featured
See All Featured
10 Git Anti Patterns You Should be Aware of
lemiorhan
PRO
657
60k
Making Projects Easy
brettharned
116
6.3k
Build The Right Thing And Hit Your Dates
maggiecrowley
36
2.8k
Docker and Python
trallard
44
3.5k
RailsConf & Balkan Ruby 2019: The Past, Present, and Future of Rails at GitHub
eileencodes
138
34k
[RailsConf 2023 Opening Keynote] The Magic of Rails
eileencodes
29
9.6k
How To Stay Up To Date on Web Technology
chriscoyier
790
250k
The Cult of Friendly URLs
andyhume
79
6.5k
BBQ
matthewcrist
89
9.7k
Java REST API Framework Comparison - PWX 2021
mraible
31
8.7k
GraphQLとの向き合い方2022年版
quramy
49
14k
Practical Orchestrator
shlominoach
188
11k
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