지난 시간에 이어, OpenApi Generator 적용기 2탄입니다.
(1탄: https://velog.io/@_im_ssu/OpenApi-Generator-in-Android)
1탄에서는, OpenApi Generator 그게 뭔데, 어떻게 하는 건데! 를 알아보기 위한 테스트를 다루었는데요.
이번 2탄에서는, 실제 적용을 어떻게 할지, 적용이 가능할지를 알기 위한 세팅 및 테스트 작업을 다뤄보도록 할게요.
저희가 개발할 서비스는 우테코 크루들을 위한 공지&출결 앱 서비스 인데요.
다만,
(1) 우테코 내에는 아직 ios 과정은 없어, 아이폰을 쓰는 크루들을 위한 앱 서비스는 부재하게 되어요. (물론 웹 서비스 사용이 가능할 예정이어요!)
(2) 또한, kotlin multiplatform을 경험&시도 해보고자 하는 욕구가 있어요.
(3) 현업 개발자가 아닌 학습을 하고 있는 크루로서, 짱짱 레아와 함께 개발할 수 있는 지금, 이것저것 해보기에 대한 두려움을 갖지 않기로 했습니다. (a.k.a. 낭만주도개발)
물론 러닝 커브에 대한 걱정이 많았지만, 앞으로 개발을 하며 경험해보기 힘들 수 있는 것들을 이번 기회에 도전해보면 좋겠다는 제이슨의 말씀이 많이 와닿았습니다. 뭉클,,감동,,
이러한 이유로, 저희는 kotlin multiplatform 활용에 대한 확장성을 열어두고, ktor를 적용하기로 결정하였습니다. (잘부탁해 산군ㅎㅎ)
공식문서를 보면,
다음과 같이 ktor를 지원함을 볼 수 있는데요.
결론부터 말씀드리면, 위의 문서는 업데이트가 더딘 것 같아요.
이유에 대해서는 후술하도록 할게요!
ktor 세팅은 다음과 같이 해줍니다.
// build.gradle.kts (project)
id("org.jetbrains.kotlin.plugin.serialization") version "1.6.21"
// build.gradle.kts (app)
plugins {
...
id("kotlinx-serialization")
}
// ktor
implementation("io.ktor:ktor-client-core:2.1.3")
implementation("io.ktor:ktor-client-cio:2.1.3")
implementation("io.ktor:ktor-client-content-negotiation:2.1.3")
testImplementation("io.ktor:ktor-client-mock:2.1.3")
// serialization
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.1")
implementation("io.ktor:ktor-serialization-kotlinx-json:2.1.3")
ktor-client-core
is a core dependency that provides the main client functionality, whilektor-client-cio
is a dependency for an engine processing network requests.
ktor-client-core
는 ktor-client 기능들을 사용하기 위한 핵심 라이브러리고, ktor-client-cio
는 네트워크 요청을 처리하는 엔진에 대한 종속성입니다.
Ktor HTTP 클라이언트는 JVM, Android, JS, Native(iOS/desktop)과 같이 다른 플랫폼에서 사용될 수 있고, 플랫폼마다 네트워크 처리 요청을 위한 특정 engine이 필요할 수 있어요.
Content Negotiation
은 client와 server 간의 media type을 중계해주고 (Accept, Content-Type Header 사용),
요청/응답 콘텐츠를 직렬화/역직렬화합니다.
JSON 직렬화/역직렬화를 위해 kotlinx.serialization
의존성도 추가해주었습니다.
지난 블로그에서 설명한 방식대로, 환경에 맞게 코드를 generate 합니다.
configOptions.set(
mapOf(
"library" to "jvm-ktor",
"dateLibrary" to "java8",
"omitGradleWrapper" to "true",
"sourceFolder" to "src/main/java",
"useCoroutines" to "true",
"serializationLibrary" to "kotlinx_serialization"
),
)
"useCoroutines" to "true"
를 하면 알아서 suspend가 붙은 함수들이 생성되고,
"serializationLibrary" to "kotlinx_serialization"
를 추가하면 kotlinx_serialization으로 직렬화/역직렬화 할 수 있도록 모델이 생성됩니다.
추가하지 않으면 기본으로 moshi를 사용하던데,
To serialize/deserialize JSON data, you can choose one of the following libraries: kotlinx.serialization, Gson, or Jackson.
공식문서를 보면 moshi를 지원하지 않으니 꼭 serializationLibrary를 설정해주세요! (저는 3시간 삽질햇슴니다..엉엉)
위 과정을 거쳐 코드를 생성하고,
더불어 함께 새롭게 생겨난 build.gradle
을 확인해보시면,
ext.ktor_version = '2.1.3'
ktor_version을 2.1.3을 사용하고 있음이 보입니다.
이러한 이유로 app gradle에서도 ktor 버전을 1.6.7
-> 2.1.3
으로 올려주었습니다.
놀랍게도 이 버전을 맞춰주니 generate된 코드들 중 빨간 줄이 뜨던 것들이 싹 사라집니다!! (뿌듯)
다만, generate된 코드들 중 딱 한 가지 수정사항이 있었는데요.
// ApiClient.kt
private val clientConfig: (HttpClientConfig<*>) -> Unit by lazy
{
it.install(ContentNegotiation) {
json() // 요 한 줄 추가 !!
}
httpClientConfig?.invoke(it)
}
}
HttpClient 인스턴스를 관리하는 ApiClient 클래스가 생성되었는데요.
(마치 Retrofit 인스턴스 - Retrofit.Builder 클래스가 떠오릅니다)
공식문서에 따르면,
이는 Json serializer를 등록하기 위한 코드입니다.
즉, 나 앱에서 Json 형태로 통신할거야 ~~ 라고 등록하는 겁니다!
이 코드가 없다면,
No transformation found: class io.ktor.utils.io.ByteBufferChannel -> class model.{ClassName}
위의 오류가 발생합니다.
(2023/10/24 update)
다만, open api generator의 특성상,
api에 수정사항이 생길 때마다 코드를 새롭게 generate 해야 합니다.
그러므로 최대한 코드에 수정을 하지 않는 것이 좋은데요.
kotlinx-serialization
이 아닌 gson
을 사용하면
해당 코드 위치에 gson()
이 굳이 추가하지 않아도 작성되어 있습니다.
번거로움이 싫다면 gson
을 사용하시길,,,
(이렇게 백로그는 계속 추가되는데...)
generate된 코드들이 잘 작동하는지, 이거이거 쓸만한건지~ 를 알기 위해 테스트 코드를 kotest로 하려고 했지만 익숙하지 않아 힘들어서 우선 JUnit으로 작성해보았는데요 !
@Test
fun codegenTest() = runTest {
val api = DefaultApi(
MOCK_SERVER_URL, null, null
)
val response = api.resourceIdGet(1)
val actual = response.body()
val expected = ResourceIdGet200Response(1, "Example Resource")
assertEquals(expected, actual)
}
sample api를 만들고, postman으로 mock server를 만들어 테스트를 진행했습니다.
(mock server 만드는 과정은 이 링크에!)
이미 코드들이 만들어져 있다보니, 사실상 사용할 것은
Api/DefaultApi
, model/ResourceIdGet200Response
이 두 클래스 뿐이었어요.
DefaultApi에는 URL만 mock server url을 넣어주고,
나머지는 null로 두어 기본 default로 생성되도록 했습니다.
이후 api를 호출하고, response의 body를 가져와
예상값(mock server example)과 비교해주었어요.
결과는? 짜잔! 테스트가 통과했습니다. 👏👏
그래서 후기는.. 생각보다 쓸만할지도? 입니다.
우여곡절은 좀 있었지만, 점점 이거 괜찮은데 쓸만하겠는데? 생각이 들어요.
물론, api가 복잡해지고, 로그인 기능이 붙는다면 어떨지는 앞으로 차차 공부고민공부고민을 해보아야겠습니다 🤔
중간중간 링크한 공식문서 외,,
Ktor Client&Engine
https://ktor.io/docs/eap/http-client-engines.html
https://essie-cho.com/kotlin-4/
https://velog.io/@shins/Kotlin-HTTP-Client-ktor-client
Content negotiation and serialization
https://ktor.io/docs/serialization-client.html
Configure a serializer
https://jyami.tistory.com/150
Ktor request
https://essie-cho.tistory.com/36
Ktor test
https://ktor.io/docs/http-client-testing.html
Ktor 간단 설명
https://ktor.io/docs/http-client-testing.html#share-config