경험의 기록

❓ 레트로핏이란

▶ 서버와 HTTP 통신을 해서 받은 데이터를 앱에서 출력해서 보여주는 라이브러리

 

 

🔴 레트로핏 3가지 구성요소

  1. Model(POJO) : DTO(Data Transfer Object). 서버로부터 JSON 형식으로 통신하기위함.
  2. Interface : 가능한 HTTP 동작들을 정의해놓은 인터페이스
  3. Retrofit.Builder 클래스 : 인터페이스를 사용하는 인스턴스. Builder는 BASE_URL와  Converter를 설정

 


사용해보기

 

 

통신을 위한 예제로 아래 링크를 사용한다.

https://jsonplaceholder.typicode.com/posts/1

REST API 데이터가 JSON 형식으로 저장되어 있다.

 

종속성 추가

// Retrofit
    implementation 'com.squareup.retrofit2:retrofit:2.9.0'
    // Converter ( JSON 타입 결과를 객체로 매핑 )
    implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
    // okhttp3
    implementation 'com.squareup.okhttp3:logging-interceptor:4.5.0'

    // Coroutines
    implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.2'
    implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.4.1'

    // ViewModel
    implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.0'

레트로핏을 사용하기 위한 retrofit2

JSON 타입 결과를 객체로 매핑해주는 Converter

코루틴과 뷰모델을 추가해준다.

 

퍼미션 추가

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

기본적으로 인터넷 통신을 해야하므로 매니패스트에서 인터넷 권한을 추가해준다.

 

데이터클래스 생성

data class Post(
    @SerializedName("userId")
    val myUserId : Int,
    val id : Int,
    val title : String,
    val body : String
)

레트로핏의 3가지 구성요소 중 Model 역할을 할 data class를 생성해준다.

기본적으로 json 파일과 같은 이름의 변수이면 SerializedName 하지 않아도 되지만

만약 변수이름을 다르게하고 싶거나, json 파일의 이름이 한글이거나 할 경우엔 @SerializedName 어노테이션을 사용하여 역/직렬화를 해주면 된다.

 

인터페이스 생성

interface SimpleApi {

    @GET("posts/1")
    suspend fun getPost() : Post
}

레트로핏의 3가지 구성요소 중 Interface 역할을 할 인터페이스를 생성해준다.

여기서 posts/1 의 요소를 가져오는 동작을 하는 메소드를 정의해준다.

 

인스턴스 생성

object RetrofitInstance {

    private val retrofit by lazy {
        Retrofit.Builder()
            .baseUrl(BASE_URL)
            .addConverterFactory(GsonConverterFactory.create())
            .build()
    }

    val api : SimpleApi by lazy {
        retrofit.create(SimpleApi::class.java)
    }
}

object로 싱글톤으로 객체를 생성한다.

레트로핏의 3가지 구성요소 중 Retrofit.Builder 클래스 역할로 BASE_URL와  Converter를 설정해준다.

 

여기서 둘다 by lazy 로 늦은 초기화 해줌으로써,

api 변수가 사용될 때 초기화되고, 그 안에서 retrofit 변수를 사용하기 때문에 초기화 된다.

class Constants {

    companion object{
        const val BASE_URL = "https://jsonplaceholder.typicode.com"
    }
}

또한 baseUrl은 따로 상수 클래스를 생성하여 별도로 관리해준다.

 

Repository 생성

class Repository {

    suspend fun getPost() : Post {
        return RetrofitInstance.api.getPost()
    }
}

MVVM 패턴을 위해 데이터 통신을 하는 Repository 를 생성해준다.

여기서 통신한 값을 뷰모델에서 사용할 것이다.

 

ViewModel 생성

class MainViewModel(private val repository : Repository) : ViewModel() {

    val myResponse : MutableLiveData<Post> = MutableLiveData()

    fun getPost() {
        viewModelScope.launch {
            val response = repository.getPost()
            myResponse.value = response
        }
    }
}

통신에 대한 응답을 라이브데이터로 처리해준다.

 

ViewModelFactory 생성

class MainViewModelFactory(
    private val repository : Repository
    ) : ViewModelProvider.Factory {

    override fun <T : ViewModel?> create(modelClass: Class<T>): T {
        return MainViewModel(repository) as T
    }
}

또한 뷰모델에서 파라미터로 Repository를 받아야 하기 때문에 Factory를 생성해준다.

 

메인액티비티

class MainActivity : AppCompatActivity() {

    private lateinit var viewModel : MainViewModel

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val repository = Repository()
        val viewModelFactory = MainViewModelFactory(repository)
        viewModel = ViewModelProvider(this,viewModelFactory).get(MainViewModel::class.java)
        viewModel.getPost()
        viewModel.myResponse.observe(this, Observer {
            Log.d("Response",it.myUserId.toString())
            Log.d("Response",it.id.toString())
            Log.d("Response",it.title)
            Log.d("Response",it.body)
        })
    }
}

repository와 뷰모델을 연결해주고,

getPost()를 호출해주어 통신을 요청한다.

응답에 대한 결과를 옵저버가 감시하여 변경될 시 로그를 찍는다.

 

통신이 성공적으로 된 것을 알 수 있다.

 

 

❗ 하지만 존재하지 않는 페이지를 호출하는 등의 통신오류가 발생할 경우 충돌이 발생하게 된다.

예외처리를 해주기 위해서,

api, repository, viewmodel에서 전부 response으로 수정해준다.

 

그 후 메인 액티비티에서

viewModel.myResponse.observe(this, Observer {
            if(it.isSuccessful){
                Log.d("Response",it.body()?.myUserId.toString())
                Log.d("Response",it.body()?.id.toString())
                Log.d("Response",it.body()?.title!!)
                Log.d("Response",it.body()?.body!!)
                textView.text = it.body()?.title!!
            }
            else{
                Log.d("Response",it.errorBody().toString())
                textView.text = it.code().toString()
            }
        })

isSuccessful 로 성공했을 때만 실행하도록하고,

실패 시 에러코드를 반환하게 할 수 있다.

 

 

성공시

 

 

 

실패시

 

 

충돌하지 않고 잘 작동하는 것을 확인할 수 있다.

 

 

https://github.com/HanYeop/AndroidStudio-Practice/tree/master/Retrofit_Test

 

HanYeop/AndroidStudio-Practice

안드로이드 학습 내용 저장소. Contribute to HanYeop/AndroidStudio-Practice development by creating an account on GitHub.

github.com

 

 

 

참조

http://devflow.github.io/retrofit-kr/

https://www.youtube.com/watch?v=sBCE_hOFnQU&feature=youtu.be 

https://blog.naver.com/zxy826/220808302640

 

 

반응형

공유하기

facebook twitter kakaoTalk kakaostory naver band
loading