Android HTTP Client는 다양하게 나와있는 편이다. 많이 사용하는것은 정해져있지만, 아직은 순위가 바뀔 가능성은 충분히 많다. 그 중 하나의 방법으로 사용될 수 있는 Ktor에 대해 알아보았다.
Ktor이란
Ktor은 마이크로서비스, 웹 애플리케이션을 만들기 위한 비동기 프레임워크이다.
스스로 소개하기를 재미있고, 무료로 제공되는 오픈소스라고 한다.
현재 글을 쓰는 시점에는 버전이 1.6.1이 최신버전이다.
Ktor에는 서버와 클라이언트 두 가지 모듈이 모두 제공되고 있다. 이 중 안드로이드에서 사용할 것은 서버가 아니라 클라이언트이기 때문에 클라이언트로 알아본다.(물론 서버도 쓸데가 있을수도 있지만 일반적인것은 아니기에 잠시 접어두자)
또한 Ktor에는 request를 만들고, response를 처리하고, 인증, JSON직렬화 등과 같은 플러그인(feature로 알려진)으로 기능을 확장할 수 있는 멀티플랫폼 비동기 HTTP클라이언트가 포함되어 있다. 참고로 아래의 내용 중 API 호출과 관련된 부분은 Android 전용이 아니기 때문에, Kotlin 멀티플랫폼 코드에서도 활용이 가능하고, Kotlin Application에서도 활용이 가능하다.
Ktor을 안드로이드에 적용하기 위한 방법
0. 일단 Android Studio에서 기본 프로젝트가 생성되어 있다고 가정하자.
1. ktor 라이브러리를 사용하기 위해 모듈 내에 build.gradle에 아래와 같이 dependencies를 추가하자.
dependencies {
...
implementation "io.ktor:ktor-client-core:1.6.1"
implementation "io.ktor:ktor-client-cio:1.6.1"
}
2. HTTP통신을 하기 위한 별도의 클래스를 추가해준다.
3. 여느 HttpClient와 비슷하게 HttpClient를 객체로 추가해준다. 이때, CIO라는 HttpClientEngineFactory를 인자로 넣어준다. CIO라는 것은 Coroutine based I/O라는 뜻이다.
class HttpRequestHelper {
private val client: HttpClient = HttpClient(CIO)
}
4. 당연하겠지만, AndroidManifest.xml에 반드시 INTERNET 퍼미션을 추가해야 한다.
<uses-permission android:name="android.permission.INTERNET" />
5. 이제 Http request를 하면 끝이다. CIO를 사용했기 때문에 suspend를 기본으로 하고 있다는 점만 기억하면 된다.
기본적인 HTTP 통신 방법
1. 구현에는 여러 방법이 있지만, 앞에서 만든 client에 get()을 호출하면 바로 통신의 결과를 가져올 수 있다. 다만, Coroutine 베이스이기 때문에 해당 부분을 신경써서 작성해야 한다. 나의 예시에서는 withContext를 활용하였다.
withContext(Dispatchers.IO) {
val url = "https://cat-fact.herokuapp.com/facts/random"
val response: HttpResponse = client.get(url)
}
2. 이렇게 가져온 결과를 response에 저장하고 있으므로 해당 정보를 꺼내올 수 있다. 아주 간단하게 status만 체크하고 OK만 나왔다면 해당 컨텐츠의 내용을 text화 하여 리턴해보자. 만약 OK가 아니라면 에러의 내용을 리턴하도록 한다.
...
if (responseStatus == HttpStatusCode.OK) {
response.readText()
} else {
"error: $responseStatus"
}
3. 이제 이 두가지를 조합하고 함수화 하면 된다.
suspend fun requestKtorIo(): String =
withContext(Dispatchers.IO) {
val url = "https://cat-fact.herokuapp.com/facts/random"
val response: HttpResponse = client.get(url)
val responseStatus = response.status
Log.d(TAG, "requestKtorIo: $responseStatus")
if (responseStatus == HttpStatusCode.OK) {
response.readText()
} else {
"error: $responseStatus"
}
}
4. 이것을 Activity에서 Coroutine Scope를 사용하여 호출하면, 정상적으로 수행이 가능하다.
class MainActivity : AppCompatActivity(), CoroutineScope {
// CoroutineScope를 위해 컨텍스트를 정의한다.
private lateinit var job: Job
override val coroutineContext: CoroutineContext
get() = Dispatchers.Main + job
lateinit var tvResult: TextView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
job = Job() // CoroutineScope를 위해 job을 할당
setContentView(R.layout.activity_main)
tvResult = findViewById(R.id.tv_result_view)
}
override fun onDestroy() {
super.onDestroy()
job.cancel() // Activity종료시 job이 종료되도록 한다.
}
fun onSimpleRequest(view: View) {
launch(Dispatchers.Main) {
// 앞에서 만들었던 코드를 호출하고 결과를 받아와서 화면에 출력해 준다.
val result = HttpRequestHelper().requestKtorIo()
tvResult.text = result
}
}
}
헤더와 쿠키를 세팅해서 사용하는 방법
1. request를 위해 헤더를 세팅해서 사용해야 할 경우가 있다. 인증키를 세팅해야 하는 API에 대해서는 거의 헤더를 사용하게 될 것이다. 이 경우 아래와 같이 세팅한다.
companion object {
val TAG: String = HttpRequestHelper::class.java.name
const val APP_ID = "발급받은 APP ID"
}
val response: HttpResponse = client.request("https://dummyapi.io/data/api/user") {
method = HttpMethod.Get
headers {
append(HttpHeaders.Accept, "text/html")
append("app-id", APP_ID)
append(HttpHeaders.UserAgent, "ktor client")
}
}
참고로 앞의 예제에서 사용한 API는 dummyapi.io에 가면 쉽게 개인 APP ID를 발급받을 수 있다. 연습용으로 좋으니, 별다른 API가 생각나지 않을때에는 활용하는것을 권장한다.
2. 추가로 쿠키를 설정할 수도 있다. 쿠키 저장방법이 매우 간단하며, 아래와 같이 호출하면 된다. expire 날짜를 설정하는 과정까지 포함이다.
val response: HttpResponse = client.request("https://dummyapi.io/data/api/user") {
....
cookie(name = "user_name", value = "soobin park", expires = GMTDate(
seconds = 0,
minutes = 0,
hours = 10,
dayOfMonth = 1,
month = Month.JULY,
year = 2022
))
}
3. 이것도 완성된 코드는 다음과 같다.
suspend fun requestKtorIoInDetail(): String =
withContext(Dispatchers.IO) {
// set HttpRequestBuilder
val response: HttpResponse = client.request("https://dummyapi.io/data/api/user") {
method = HttpMethod.Get
headers {
append(HttpHeaders.Accept, "text/html")
append("app-id", APP_ID)
append(HttpHeaders.UserAgent, "ktor client")
}
cookie(name = "user_name", value = "soobin park", expires = GMTDate(
seconds = 0,
minutes = 0,
hours = 10,
dayOfMonth = 1,
month = Month.JULY,
year = 2022
))
}
val responseStatus = response.status
if (responseStatus == HttpStatusCode.OK) {
response.readText()
} else {
"error: $responseStatus"
}
}
4. 호출방법은 아까와 동일한 방식이다. 코루틴을 사용한다.
fun onDetailRequest(view: View) {
launch(Dispatchers.Main) {
val result = HttpRequestHelper().requestKtorIoInDetail()
tvResult.text = result
}
}
5. 완성된 앱은 다음과 같다.
위 코드의 전체코드는 github.com에 올려놓았으므로 받아서 확인하고 싶으면, 다음 저장소를 확인하면 된다.
https://github.com/binsoopark/KtorPractice
참고링크
- Ktor 공식사이트: https://ktor.io/
- CIO엔진 API Reference: https://api.ktor.io/ktor-client/ktor-client-cio/ktor-client-cio/io.ktor.client.engine.cio/-c-i-o/index.html
- Ktor Client Guide: https://ktor.io/docs/getting-started-ktor-client.html#make-request
'[Developer] > Kotlin' 카테고리의 다른 글
[코틀린 멀티플랫폼] 첫 번째 멀티플랫폼 앱 만들기 (0) | 2021.04.16 |
---|---|
[코틀린 멀티플랫폼] 시작/초기설정하기 (0) | 2021.04.15 |
코틀린 기본 문법 :: 반복문 표현 (0) | 2018.05.06 |
코틀린 기본 문법 :: 조건문 표현 (0) | 2018.04.25 |
댓글