[Android] Retrofit이란?

fanthasium·2022년 11월 7일
0

안드로이드에서 기본적으로 데이터 처리를 위해선

  • remote DataSource
  • local DataSource (RoomDB)

이 두가지를 사용한다
또한 여기서 remote DataSource에 해당되는 model은 주로 Retorifit2
를 사용하는데 이는 Http Api를 java interface로 가져와 안드로이드에서 데이터를 요청할 수 있는 구조를 지원한다

  • remote DataSource 에는 Retorifit2 말고도 여러 통신 라이브러리가 있는데 암튼 가독성,유지보수,비용에서도 좋은 Retorifit2를 쓰는 것 자세한 내용은 쓰레드 동기 비동기 실행에서 전하겠습니다..

Retrofit2 사용방법

1. 통신을 하기 위해선 manifest에

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

인터넷 사용 허가를 추가해주고

만약 불러오는 api의 주소가 https가 아닌 http라면

<application
             ...
 android:usesCleartextTraffic="true"
             ..
             </application> 

http는 보안 문제로 인해 모든 통신을 허가해준다는 permission을 줘야 한다

2. Gradle module파일에 implementation을 해줘야한다

dependencies {
   //retrofit2
   implementation 'com.squareup.retrofit2:retrofit:2.9.0'
   implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
   implementation 'com.google.code.gson:gson:2.8.2'
}

여기서 Gson retrofit2에서 사용하는데 왜 implementation해주냐면 데이터 표준표현인 Json을 java에서 파싱하고 생성하기 위함이다.
좀더 파고들자면 직렬화 역질렬화 개념이 나오는데

  • 직렬화(Serialization): 직렬화하여 전송 가능한 형태로 객체 데이터를 보내줄 때
  • 역직렬화(Deserialization): 직렬화된 파일 등을 역으로 직렬화하여 다시 객체의 형태로 만듦, 저장된 파일을 읽거나 전송된 스트림 데이터를 읽어 원래 객체의 형태로 복원시킬 때

3. API의 구조 짜기 (postMan을 사용하여 테스트함)

Http 통신에선 Request, Response로 원하는 데이터의 요청과 응답을 받게 해준다. 그러나 이 두 과정을 하기 위해선 중간 통신 과정이 필요하다.

http 통신이란?
이 글을 참고하며 Android에서 어떻게 해야할지 알아보자.
-DTO-
Dto란?: Data Transfer Object를 의미하는데 이는 의미 그대로 데이터를 객체화 해주는 것이다!
객체화의 이유는: 데이터를 객체화 시키지 않으면 통신 횟수의 증가, 로직의 비효율성때문 https://kafcamus.tistory.com/13 참고해보면 좋을 것이다
1.Requset구조 짜기 ** Http Header에서 요구하는 값을 넣을 data Class**

data class LoginRequest(
 @SerializedName("id") val id : String,
 @SerializedName("password") val password : String,
)

1-1.Response 구조짜기 ** Http Body에서 보내준 값을 넣을 받을 dataClass**

data class GameResultData(
 @SerializedName("result") val result: List<GameResult>? = null
)

data class GameResult(

 @SerializedName("name") val title: String,
 @SerializedName("age") val kind: String,
 @SerializedName("gender") val category: String
)

-> 내가 받고자하는 Json은 {result : {name:" " , age: " " , gender:" " }} 로 돼 있어서 반환값을 준 것

2.통신 service 구조짜기

interface RequestDto {
 companion object {
        const val CONTENT_TYPE = "content-type: application/json"
          const val LOGIN_API = "공통url뒤의 path부분"
        }

    @Headers(CONTENT_TYPE)
    @POST(LOGIN_API)
    suspend fun getLogin(@Body data: LoginRequest): Response<LoginResultData>
}

@GET @POST 말고도 정보를 수정하거나 삭제하는 요청 방식이 있는데, http method(POST, GET, PUT, PATCH, DELETE)를 사용하여 서버에 원하는 작업을 요청할 수 있다.
여기서 주목할 GET방식과 POST 방식은 데이터받는 과정이 다르다는 점

  • @Get: url 파라미터에 정보를 담아 전송
  • @Post: Body에 담아서 정보를 전송

이후 데이터를 Response or Call방식으로 activity에서 .enqueue,execute() 방식으로 쓸지 body()로 쓸지, 구조의 차이로 나아가기에 잘 구분하여 사용해야한다
3. http통신 클라이언트짜기

class Http {
    companion object {
        private val _LOGIN = "http://testapi.testtest.co.kr"

        val clientBuilder = OkHttpClient.Builder()
        val loggingInterceptor = HttpLoggingInterceptor()


        val retrofit = Retrofit.Builder()
            .baseUrl(_LOGIN)
            .addConverterFactory(GsonConverterFactory.create())
            .client(clientBuilder.build())
            .build()

        val service = retrofit.create(RequestDto::class.java)

   }
}
  • .baseUrl(_LOGIN) : 고정되는 api의 주소

  • val service = retrofit.create(RequestDto::class.java): retrofit builder에 넣어 사용할 수 있지만 한 곳에서만 사용 할 것이 아니기에 따로 뺴놨다

  • clientBuilder: okHttp에서 통신 logger를 보다 쉽게 해줘 넣은 것이라 안 써도된다 -> 사용시엔 종속성 추가해줘야 함

4. activity에서 사용


       val intent = Intent(this@LoginActivity, InfoActivity::class.java)
      
   Http.clientBuilder.addInterceptor(Http.loggingInterceptor)
        Http.loggingInterceptor.level = HttpLoggingInterceptor.Level.BODY

        binding.loginTxtView.setOnClickListener {
            CoroutineScope(Dispatchers.Main).launch {
              
              //데이터 요청
                val resultData = Http.service.getLogin(
                    data = LoginRequest(
                        id = binding.idEditTxtView.text.toString(),
                        password = binding.passwordEditTxtView.text.toString(),
                        role = "RCT",
                        token = ""
                    )
                )

                   try {
                   // code : 200,300 요청 왔을 때 (성공_)
                       if (resultData.isSuccessful) {
                           Timber.e("${resultData.body()}")
                           Log.e("LoginActivity(Body)", "${resultData.body()}")

                           val accessToken = resultData.body()?.result?.access
                           val user = resultData.body()?.result!!.user

                    //viewModel 사용하지 않고 데이터를 2번째 activity에 넘겨줌
                           intent.apply {
                               putExtra(  "name", user.name)
                               putExtra(  "age", user.age)
                               putExtra(  "gender", user.birth)
                           }

                           startActivity(intent)
                           finish()
                       }
                       throw Exception("ERROR")
                 // 그외 에러 code나올 때
                   } catch (e: Exception) {
                       e.printStackTrace()
                       Log.e("FAILED : ", "${resultData.code()}")
                   }
               }
        }

이렇게 하면 로그는
E/LoginActivity(Body): LoginResultData(result=LoginResult(name = 홍길동,age = 18, gender = man)이런식으로 온다

model에서 추출한 데이터를 안드로이드에서 효율적으로 사용하는 방법엔 MVVM패턴 사용과 SharedPreferences내부 저장소가 있다 이 부분에 대해서도 공부해보자

        
profile
디그다 디그다 (끙챠끙챠)

0개의 댓글