[Kotlin/Android] Ktor 를 이용한 Api 통신

Programming/Android 2024. 3. 24. 13:55 Posted by 생각하는로뎅
반응형

Ktor 은 Server 와 Client가 있고, 글쓴이는 Client 를 사용 한다.

 

사용할 Ktor 버전은 2.3.9 이고, 로그인 기능을 구현해 볼 것이다.

 

https://ktor.io/docs/getting-started-ktor-client.html

 

Creating a client application | Ktor

 

ktor.io

 

 

라이브러리를 사용하기 위해서, 아래와 같이 사전 작업이 필요하다.

 

0. AndroidManifest.xml 

  인터넷을 사용하기 위해서는 퍼미션을 꼭 설정해 준다.

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">

    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

    <application
        .....
    </application>

</manifest>

 

 

1. settings.gradle.kts

pluginManagement {
    repositories {
        ....
        maven {
            setUrl("https://kotlin.bintray.com/kotlinx")
        }
        resolutionStrategy {
            eachPlugin {
                if (requested.id.id == "kotlinx-serialization") {
                    useModule("org.jetbrains.kotlin:kotlin-serialization:${requested.version}")
                }
            }
        }
    }
}

 

 

2. build.gradle.kts : Project Level

plugins {
    ...
    id("org.jetbrains.kotlin.plugin.serialization") version "1.6.21" apply true
}

 

 

3. build.gradle.kts : app Level

plugins {
    .....
    id("kotlinx-serialization")
}

....

dependencies {

    val ktor_version = "2.3.9"

    implementation("io.ktor:ktor-client-core:$ktor_version")
    implementation("io.ktor:ktor-client-android:$ktor_version")
    implementation("io.ktor:ktor-client-json:$ktor_version")
    implementation("io.ktor:ktor-client-content-negotiation:$ktor_version")
    implementation("io.ktor:ktor-client-serialization:$ktor_version")
    implementation("io.ktor:ktor-serialization-kotlinx-json:$ktor_version")

}

 

 

Gradle Sync 후 코드로 구현한다.

 

4. LoginRequestBean.kt

    @Serializable 을 이용하여, JSON 송신(Request)할 data class 를 생성한다.

import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

/**
 * 로그인
 */
@Serializable
data class LoginRequestBean (
    @SerialName("email")
    val id : String,
    @SerialName("encryptedPassword")
    val pwd : String
)

 

 

5. LoginResultBean.kt

    @Serializable 을 이용하여, 서버로부터 JSON 수신(Receive) 결과를 받을 data class 를 생성한다.

package com.leestech.smarthealth.network.beans

import kotlinx.serialization.Serializable

/**
 * 결과
 */
@Serializable
data class LoginResultBean (val result : Boolean)

 

 

6. HttpRequest.kt

 

  1) _client 라는 HttpClient 를 객체를 생성하되, Json 통신을 하기 위한 각종 설정을 해주고,

        // http client
        private val _client : HttpClient = HttpClient(Android) {
            install(ContentNegotiation) {
                json(Json {
                    prettyPrint = true
                    isLenient = true
                    ignoreUnknownKeys = true
                })

                install(HttpTimeout) {
                    requestTimeoutMillis = _NETWORK_TIME_OUT
                    connectTimeoutMillis = _NETWORK_TIME_OUT
                    socketTimeoutMillis = _NETWORK_TIME_OUT
                }

                install(ResponseObserver) {
                    onResponse { response ->
                        Log.d("HTTP status:", "${response.status.value}")
                    }
                }

                install(DefaultRequest) {
                    header(HttpHeaders.ContentType, ContentType.Application.Json)
                }

                defaultRequest {
                    contentType(ContentType.Application.Json)
                    accept(ContentType.Application.Json)
                }
            }
        }

 

 

  2) loginRequstBean.kt 객체 생성 후 setBody 에 주입하면 JSON 으로 변환되어 전송되어 진다.

      결과는 body() 를 통해서 LoginResultBean에 JSON이 파싱되어 생성되어 진다.

// 결과 반환 객체
var response = LoginResultBean(false)
            
response = _client.post("$_serverUrl$_login") {
                    contentType(ContentType.Application.Json)
                    setBody(loginRequestBean)
                }.body()

 

 

Full 코드는 아래와 같다.

import android.util.Log
import com.leestech.smarthealth.network.beans.LoginRequestBean
import com.leestech.smarthealth.network.beans.LoginResultBean
import io.ktor.client.HttpClient
import io.ktor.client.call.body
import io.ktor.client.engine.android.Android
import io.ktor.client.plugins.DefaultRequest
import io.ktor.client.plugins.HttpTimeout
import io.ktor.serialization.kotlinx.json.*
import io.ktor.client.plugins.contentnegotiation.*
import io.ktor.client.plugins.defaultRequest
import io.ktor.client.plugins.observer.ResponseObserver
import io.ktor.client.request.accept
import io.ktor.client.request.header
import io.ktor.client.request.post
import io.ktor.client.request.setBody
import io.ktor.http.ContentType
import io.ktor.http.HttpHeaders
import io.ktor.http.contentType
import kotlinx.serialization.json.Json

class HttpRequest {

    companion object {

        const val _NETWORK_TIME_OUT = 6_000L

        // 서버 주소
        const val _serverUrl : String = "http://192.168.0.6/api/v2/"

        // 로그인
        const val _login : String = "login"

        // http client
        private val _client : HttpClient = HttpClient(Android) {
            install(ContentNegotiation) {
                json(Json {
                    prettyPrint = true
                    isLenient = true
                })

                install(HttpTimeout) {
                    requestTimeoutMillis = _NETWORK_TIME_OUT
                    connectTimeoutMillis = _NETWORK_TIME_OUT
                    socketTimeoutMillis = _NETWORK_TIME_OUT
                }

                install(ResponseObserver) {
                    onResponse { response ->
                        Log.d("HTTP status:", "${response.status.value}")
                    }
                }

                install(DefaultRequest) {
                    header(HttpHeaders.ContentType, ContentType.Application.Json)
                }

                defaultRequest {
                    contentType(ContentType.Application.Json)
                    accept(ContentType.Application.Json)
                }
            }
        }

        /**
         * 로그인 요청
         */
        suspend fun requstLogin(id : String, pwd : String) : LoginResultBean {

            // 결과 반환 객체
            var response = LoginResultBean(false)

            try {

                var loginRequestBean = LoginRequestBean(id, pwd)

                response = _client.post("$_serverUrl$_login") {
                    contentType(ContentType.Application.Json)
                    setBody(loginRequestBean)
                }.body()

                Log.i("Network", "ok")

            } catch (ex : Exception) {
                ex.message.let {
                    it.let {
                        Log.e("NetworkError", "$it")
                    }
                }

            } finally {

            }

            return response
        }


    }




}

 

 

7. 특정 액션에 따라서 아래와 같이 HttpRequest.requstLogin 를 호출하여 사용한다.

    /**
     * 로그인 요청
     */
    fun requestLogin() {

        // 로딩바 보이기
        this.binding.spLoading.visibility = View.VISIBLE

        CoroutineScope(Dispatchers.IO).launch {

            val response = HttpRequest.requstLogin(binding.edId.text.toString(), binding.edPwd.text.toString())

            CoroutineScope(Dispatchers.Main).launch {

                // 로딩바 숨기기
                binding.spLoading.visibility = View.GONE

                // 로그인 성공시
                if (response.result) {

                    // 화면 이동

                } else {

                    // 로그인 실패시
                    context?.let {
                        AlertDialog.Builder(it).apply {
                            setMessage(getString(R.string.msg_fail_login))
                            setPositiveButton(getString(R.string.ok), DialogInterface.OnClickListener { dialog, which ->
                            })
                            show()
                        }
                    }


                }
            }

        }

    }

 

반응형