아이고 아이고. 일단 울고 시작한다.
WPF 개발하던 코드가 이식된 것과 같은 프로젝트이기도 하고, 또 나부터가 깊이 있는 배움 없이 바로 프로젝트에 투입되어 '무엇이 안드로이드다운 코드인가?' 에 대한 베이스가 부족했기에, 이번에 일단 배포는 시켰으니 브랜치를 꺾어 안드로이드스러운 개발을 하고자 했다.
우선 네트워크 통신 쪽을 손 보려 했다. 기존의 방식은
open class ActivityBase : AppCompatActivity() {
fun setNetworkData(data: String, responseCode: Int) {
}
}
와 같이 기초 액티비티를 생성, 다른 액태비티가 해당 액티비티를 extends 하여 오버라이딩, 내가 apiCall 별로 임의로 설정해준 responseCode
에 맞춰 JsonObject(data)
식으로 가져왔다.
물론 기능상의 문제가 있는 것은 아니지만 연속된 apiCall이나 그 응답인 JsonObject를 모델로 치환할 때 NullPointerException
방지를 위해서는 결국 SetNetWorkData
에서 응답을 받은 후 다른 메소드를 실행해야 했다. 비동기 통신이 가능한데 시스템은 동기식으로 움직일 수밖에 없던 것.
Coroutine과 Retrofit를 함께 활용하면 두 가지 apiCall을 함께 쏘더라도 하나가 응답이 올 때까지 suspend 시킬 수 있다고 이해하여 적용을 해보려고 했다. 일단 가져와보고 그 다음 돌아가는 것을 보며 이해하는 것이 지금까지의 배워온 방식이었기 때문.
하지만 난관에 부딪혔다.
JsonObject
를 인자로 사용할 때 Http Response는 정상적이지만 내가 Json으로 넣은 정보가 누락되어 들어간다.
방식은 이러했다.
https://yang-droid.tistory.com/21 를 참고하여 일단 apiBuilder와 실제 api 인터페이스를 만들었다.
ApiBuilder.kt
object LoginApiBuilder {
init {
val retrofit = Retrofit.Builder()
.baseUrl(myUrl)
.addConverterFactory(GsonConverterFactory.create())
.build()
api = retrofit.create(LoginApi::class.java)
}
}
LogInApi.kt
interface LoginApi {
// @FormUrlEncoded : 이걸 쓰면 IllegalArgumentException에 걸린다. @Field가 없어서.
@POST("/Login")
fun logIn(
@Body logInData: JSONObject
): Call<LoginApiResult>
}
ActivityLogIn
의 로그인 버튼을 클릭했을 때 쏘도록 리스너 안에 달아준 코드
ActivityLogIn.kt
var result = ApiResultForJsonObj(-99, null, null, null)
LoginApiBuilder.api.logIn(logInData).enqueue(object: CallbacK<LoginApiResult> {
override fun onResponse(call: Call<LoginApiResult>) {
result = response.body()!!
// TODO
}
override fun onFailure(call: Call<LoginApiResult>, t: Throwable) {
Log.d("Login Falied", t.toString())
// 실제로는 알러트를 띄운다.
}
})
response의 데이터가 JSONObject인 경우와 JSONArray인 경우로 나뉘는데, 일단 나는 처음 해보는 것이라 잘 모르므로,, 각각에 대한 데이터 클래스를 생성해주기로 했다. 향후 보완하면 되니까.
logIn()에 대한 response는 JSONObject로 들어오므로 그것부터 생성.
ApiResultForJsonObj.kt
data class ApiResultForJsonObj(
@SerializedName("ResponseCode") var resultCode: Int,
@SerializedName("ResultMessage") var resultMessage: String?,
@SerializedName("Data") var data: JSONObject?
)
Retrofit
을 액티비티 내에서 생성해주는 방법도 있긴 했는데 이게 매번 새로운 객체를 생성하는 것보다 빌더(Repository와 비슷한 개념인 건가 그러면?)로 관리해주는 게 좋다고. 버튼 클릭시마다 생성할 수도 있기는 있더라.
ActivityLogIn.kt
val retrofit = Retrofit.Builder()
.baseUrl(StaticValues.getServerUrl())
.addConverterFactory(GsonConverterFactory.create())
.build()
val logInRetroFitApi = retrofit.create(LoginApi::class.java)
var result = ApiResultForJsonObj(-99, null, null, null)
val call = logInRetroFitApi .logIn(jsonObj)
call.enqueue(object: Callback<ApiResultForJsonObj> {
override fun onResponse(call: Call<ApiResultForJsonObj>, response: Response<ApiResultForJsonObj>) {
// 실패할 경우 resonse가 null로 들어와서 try catch 구문을 사용했다.
// coroutine을 사용하면 exception handling을 이렇게 내부에 구현하지 않아도 된다고 하는데,
// 일단 retrofit과 MVVM에 성공하면 그때 시도하려고 한다.
try {
result = response.body()!!
} catch (e: NullPointerException) {
setDialog("로그인 실패", "로그인 프로세스 실패")
} catch (e: IOException) {
setDialog("로그인 실패", "로그인 프로세스 실패")
}
}
override fun onFailure(call: Call<ApiResultForJsonObj>, t: Throwable) {
setDialog("로그인 실패", "로그인 프로세스 실패")
}
})
처음에는 code=500
으로 bad response가 들어왔었는데, 이는 JsonObject를 문자열로 치환한 후 전송하면서 생긴 문제로, JsonObject로 전달하니 해당 문제는 발생하지 않았다. 원하는 결과를 얻지 못한 게 문제지...
DB에 다이렉트로 쿼리를 때려박을 수도 없는 노릇이고, 난감하다.
다음주에 좀 더 찾아보고 다시 시도해보기로 했다. 이번에는 이해하고 시도해야 하려나 싶네.
'개발일기' 카테고리의 다른 글
8월 한달 토이 프로젝트를 해보자! -02 : Navigation (0) | 2022.08.08 |
---|---|
8월 한달 토이 프로젝트를 해보자! -01 (0) | 2022.08.06 |
Retrofit - MVVM - Coroutine ㄲㄲㄲ (0) | 2021.09.15 |
Retrofit! Eureka! (0) | 2021.09.13 |
MVVM 모델로 나아가기 1단계 : Fragment (0) | 2021.09.13 |