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
810
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
210
Compose Web 개발하기
pangmoo
0
350
코틀린으로 멀티플랫폼 만들기
pangmoo
0
1.2k
Kotlin Multiplatform으로 Android/iOS/Desktop 번역기 만들기
pangmoo
0
550
MADC 2023 Kotlin Multiplatform (KMP)
pangmoo
0
110
안드로이드 UI 상태 저장 권장사항
pangmoo
1
770
Compose로 Android&Desktop 멀티플랫폼 만들기
pangmoo
0
460
Other Decks in Programming
See All in Programming
Kotlinの開発でも AIをいい感じに使いたい / Making the Most of AI in Kotlin Development
kohii00
5
1.9k
クリーンアーキテクチャから見る依存の向きの大切さ
shimabox
5
1.1k
PHPカンファレンス名古屋2025 タスク分解の試行錯誤〜レビュー負荷を下げるために〜
soichi
1
750
GoとPHPのインターフェイスの違い
shimabox
2
220
未経験でSRE、はじめました! 組織を支える役割と軌跡
curekoshimizu
1
200
Django NinjaによるAPI開発の効率化とリプレースの実践
kashewnuts
1
300
DRFを少しずつ オニオンアーキテクチャに寄せていく DjangoCongress JP 2025
nealle
2
290
Lambdaの監視、できてますか?Datadogを用いてLambdaを見守ろう
nealle
2
760
CSS Linter による Baseline サポートの仕組み
ryo_manba
1
160
Rails 1.0 のコードで学ぶ find_by* と method_missing の仕組み / Learn how find_by_* and method_missing work in Rails 1.0 code
maimux2x
1
260
Swift Testingのモチベを上げたい
stoticdev
2
200
「個人開発マネタイズ大全」が教えてくれたこと
bani24884
1
290
Featured
See All Featured
How to Think Like a Performance Engineer
csswizardry
22
1.4k
jQuery: Nuts, Bolts and Bling
dougneiner
63
7.7k
CoffeeScript is Beautiful & I Never Want to Write Plain JavaScript Again
sstephenson
160
15k
The Invisible Side of Design
smashingmag
299
50k
Thoughts on Productivity
jonyablonski
69
4.5k
Producing Creativity
orderedlist
PRO
344
40k
How to train your dragon (web standard)
notwaldorf
91
5.9k
Easily Structure & Communicate Ideas using Wireframe
afnizarnur
193
16k
GraphQLの誤解/rethinking-graphql
sonatard
69
10k
実際に使うSQLの書き方 徹底解説 / pgcon21j-tutorial
soudai
175
52k
Principles of Awesome APIs and How to Build Them.
keavy
126
17k
Why Our Code Smells
bkeepers
PRO
336
57k
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
vnycall74@naver.com @ @kisa002 @holykisa