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
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
970
코틀린으로 멀티플랫폼 만들기
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
JETLS.jl ─ A New Language Server for Julia
abap34
2
460
マスタデータ問題、マイクロサービスでどう解くか
kts
0
140
Pythonではじめるオープンデータ分析〜書籍の紹介と書籍で紹介しきれなかった事例の紹介〜
welliving
3
620
Vibe codingでおすすめの言語と開発手法
uyuki234
0
130
生成AIを利用するだけでなく、投資できる組織へ
pospome
2
410
LLM Çağında Backend Olmak: 10 Milyon Prompt'u Milisaniyede Sorgulamak
selcukusta
0
140
AI前提で考えるiOSアプリのモダナイズ設計
yuukiw00w
0
190
Giselleで作るAI QAアシスタント 〜 Pull Requestレビューに継続的QAを
codenote
0
300
Findy AI+の開発、運用におけるMCP活用事例
starfish719
0
1.8k
Implementation Patterns
denyspoltorak
0
130
PostgreSQLで手軽にDuckDBを使う!DuckDB&pg_duckdb入門/osc25hi-duckdb
takahashiikki
0
180
AI時代を生き抜く 新卒エンジニアの生きる道
coconala_engineer
1
440
Featured
See All Featured
Typedesign – Prime Four
hannesfritz
42
2.9k
Art, The Web, and Tiny UX
lynnandtonic
304
21k
CoffeeScript is Beautiful & I Never Want to Write Plain JavaScript Again
sstephenson
162
16k
Become a Pro
speakerdeck
PRO
31
5.7k
Designing for Timeless Needs
cassininazir
0
96
Building a Modern Day E-commerce SEO Strategy
aleyda
45
8.4k
DevOps and Value Stream Thinking: Enabling flow, efficiency and business value
helenjbeal
1
71
Cheating the UX When There Is Nothing More to Optimize - PixelPioneers
stephaniewalter
286
14k
The World Runs on Bad Software
bkeepers
PRO
72
12k
I Don’t Have Time: Getting Over the Fear to Launch Your Podcast
jcasabona
34
2.6k
Sam Torres - BigQuery for SEOs
techseoconnect
PRO
0
150
Thoughts on Productivity
jonyablonski
73
5k
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