Retrofit이란 무엇이고, Retrofit을 이용한 공공데이터 API ( 날씨 ) + Hilt로 DI + MVVM 구조로 만드는 기본적인 과정의 이해

2022. 9. 28. 17:57android/날씨공공데이터앱

Retrofit2란 무엇일까?

Android App 개발 시 REST API를 구현하기 위해 사용하는 네트워크 통신 라이브러리

 

기존의 네트워크 통신 라이브러리들은 뭐가 있었을까?

1. HttpURLConnection

    - 초기 안드로이드 표준 라이브러리

2. HttpClient

    - Apache HTTP 라이브러리, HttpURLConnection 내부 문제로 함께 사용됨

3. Volley

    - 2013년 Google I/O 발표 http 통신 라이브러리

4. okHttp

    - square사에서 만든 네트워크 통신 라이브러리

5. Retrofit

    - 2013년 1.0, 2016년 2.0 발표 현재 안드로이드 앱 개발에 가장 많이 사용하는 네트워크 통신 라이브러리

    - OkHttp 상위 구현체 

    - Google Android에서 권장함

 

왜 가장 많이 쓰고 뭐가 다를까?

 

1. 성능이 좋다

2. 구현하기 좋다.

3. 다양한 컨버터와 인터셉터를 통한 유연성

4. 유지보수성

 

왜 성능이 좋을까?

다른 api 통신 라이브러리에 비해 retrofit2에서 자체적인 동기/비동기 처리 속도가 매우 향상됨.

 

OkHttp 상위 구현체이면 Retrofit2만 사용하면 되지 않나?

아니다 Retorift2는 OkHttp를 Base로 Rest Api를 손쉽게 사용하기 위한 구현체의 기능을 초점으로 작동한다. 둘의 역할이 다르기 때문에 함께 사용한다. Okhttp는 네트워크 요청을 처리하는데 초점을 맞춘 라이브러리이다. 연결/ 재사용, 인터셉터, 데이터 변환 등의 작동은 Okhttp가 담당해서 맡고 Rest API의 역할을 Retrofit2가 한다. 말 그대로 분업화이다. 따라서 둘을 함께 사용하여 네트워크 통신 성능을 개선할 수 있다.

 

사용법

https://www.data.go.kr/tcs/dss/selectApiDataDetailView.do?publicDataPk=15084084

 

기상청_단기예보 ((구)_동네예보) 조회서비스

초단기실황, 초단기예보, 단기((구)동네)예보, 예보버전 정보를 조회하는 서비스입니다. 초단기실황정보는 예보 구역에 대한 대표 AWS 관측값을, 초단기예보는 예보시점부터 6시간까지의 예보를,

www.data.go.kr

동네예보 조회서비스를 가지고 한번 알아보도록 하겠다. 아키텍처는 Android 권장 아키텍처를 따르며 DI는 Hilt를 통해 진행했다. 일단 Manifest.xml 파일에 permission을 추가해 준다.

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

 

Base Url와 Key 

Companion object로 관리하기 쉽게 만들었다. 나중에 사용한다.

BASE_URL은 초단기실황, 단기실황 등등으로 나누어져 있는 날씨 API의 기본이 되는 URL이다.

 

Network Module

Retrofit2를 이용한 구현에 앞서 Retrofit2를 Hilt를 통해 주입하기 위한 Module을 생성한다. 여기서 Retrofit2의 Builder를 만든다. 또한 API도 Repository에 주입해야 하므로 만들어준다.

 

 

이제 Retrofit2의 Model, DTO, API를 만들 차례이다.

Model(DTO)

@SerializedName Annotaion을 전부 사용하였다. Json에서 받아오는 데이터의 키값과 지정한 변수명이 만약 같다면,

@SerializedName을 사용할 필요가 없다고 한다. @SerializedName을 사용하면 받아올 객체에 키 값을 확인하고 그 객체를 다른 변수명으로 새로 선언할 수 있게 됩니다. 하지만 필수적으로 사용하는 것이 좋다고 한다. 이유는 Proguard로 인해 앱을 release 할 시에 Code shirinking 과정에서 선언해 둔 필드가 변환될 수 있어서 서버로부터 받는 Json 값이 매핑되지 않을 수 있다고 한다.

 

Weather Api

@Get에 이제 자신이 어떤 종류의 날씨 api를 받아올지 정할 수 있다.

만약 나는 초단기 실황을 받아오고 싶다 그러면 /getUltraSrtNcst?serviceKey=$API_KEY 이렇게 작성하면 된다.

이러한 정보들은 명세에 나와있다. 날씨 데이터를 활용하는 사람은 ZIP 파일을 받을 수 있는데 그 안에 워드 파일을 보면 된다. 아래를 보자

 

명세1

여기 서비스 URL 부분이 BASE_URL을 담당한다.


명세2

명세 2의 상세기능 목록은 내가 어떻게 URL을 만들어야 하는지 알려준다.


명세3

명세 3 Call Back URL이 초단기를 사용할 사람들이 써야 하는 URL이 된다. 이러한 정보를 가지고 구성한다.

다시 워드 파일을 내리다 보면 아래와 같이 쓰여있는 부분이 있다.


요청 명세

이 부분이 내가 보내야 하는 총 데이터들이다. 지키지 않으면 받을 수 없다.


이곳을 보고 요청메시지 예를 알게 된다. 내가 어떻게 요청을 보내야 하는지 형식이다.

 


다시 내 api를 보자

요청 메시지 명세에 있는 정보들을 모두 포함해야 한다. @Query annotion에 각 데이터 이름을 적고 타입을 정하고 보낼 때 내가 앱에서 사용할 변수들 말고는 모두 초기화를 진행해 고정된 값을 할당해 주었다. 

 

@Get 안에 Contstants.API_KEY는 아까 위에 만들어두었던 Constants 클래스의 companion object이다. 위로 가서 확인해 보자.

 

WeatherRepository interface

 

Api RepositoryImpl

복잡해 보이지만 별거 없다.하나씩 보자.

 

1. 변수

cal, hour, min은 이름처럼 캘린더, 시, 분

todayWeatherTime은 단기 실황으로 오늘 하루의 날씨를 가져올 때 api가 업데이트되는 최신 시간에 맞게 변경한 변수이다.currentWeatherTime은 지금 바로 가장 가까운 시간의 초단기 실황 정보를 가져오기 위한 명세 시간을 계산해 준다.아래에 함수가 있다. 워드 파일에 보면 api가 언제 업데이트되는지 나와있으므로 알맞은 변환 작업을 거친다.

fun calCurrentWeatherTime(h: Int, m: Int): String =
    if (m < 45) {
        if (h == 0) "2330"
        else {
            val resultH = h - 1
            if (resultH < 10) "0" + resultH.toString() + "30"
            else resultH.toString() + "30"
        }
    } else h.toString() + "30"

fun calDayWeatherTime(h: Int, m: Int): String = when (h) {
    in 2 until 5 -> if (h == 2 && m < 10) "2310" else "0210"
    in 5 until 8 -> if (h == 5 && m < 10) "0210" else "0510"
    in 8 until 11 -> if (h == 8 && m < 10) "0510" else "0810"
    in 11 until 14 -> if (h == 11 && m < 10) "0810" else "1110"
    in 14 until 17 -> if (h == 14 && m < 10) "1110" else "1410"
    in 17 until 20 -> if (h == 17 && m < 10) "1410" else "1710"
    in 20 until 23 -> if (h == 20 && m < 10) "1710" else "2010"
    else -> if (h == 23 && m < 10) "2010" else "2310"
}

todayWeatherDate도 오늘 날짜를 알맞게 넣기 위한 작업을 한 변수다 

currentWeatherDate도 마찬가지.

 

2. WeatherRepository를 상속받는다.

두 개의 메서드를 구현해야 한다. 

1 ) 오늘 날씨를 가져오는 메서드

2 ) 현재 시간의 날씨를 가져오는 메서드

 

WeatherApi에서 @Get을 통해 요청을 보낸다. 그리고 성공하면 data를 반환한다. 실패하면 Message를 반환한다.

그리고 try문 data를 변환해 주는데 itemsToWeatherData()로  확장함수를 만들어주었다. 

여기서 이제 데이터를 처리하는데 이건 개인이 맞게 하면 된다. 

 

그냥 간단하게 데이터만 받을거다 그러면 아래처럼 써도 된다.

class WeatherRepository @Inject constructor(
    private val weatherApi: WeatherApi
) {
    suspend fun getWeather() : Response<Weather> {
        return weatherApi.getWeather()
    }
}

 

usecase

class GetWeather @Inject constructor(
    private val repository: WeatherRepository
) {
    suspend operator fun invoke(): Response<WeatherModel> {
        return repository.getWeather()
    }
}

굳이 안 만들어도 된다. 하지만 미리미리 만들어보는 습관을 들이기 위해 만들어봤다.

 

 

간단하게 만들면 아래와 같이 log로 찍히는 걸 확인할 수 있습니다.

 

두서없는 글이 된 것 같다. 중요한 것은 Retrofit2의 기본적이 사용법을 알고 과정을 이해하는 것이라고 생각한다. 

 

REST API란 무엇인지도 알아야 한다.

2022.09.26 - [android] - Restful, Rest, Restful API

 

Restful, Rest, Restful API

Rest API에서 API 무엇인가요?? 먼저 API란 쉽게 말하면 "원칙, 규칙" 입니다. 규칙은 합의한 사항이고 지켜야 하는 법칙입니다. 그럼 무엇을 어디에서 합의하고 지켜야하는걸까?? 바로 " 서로 다른 애

dev-musa.tistory.com

 

 

혹시 궁금하시거나 지적, 조언해 주실 부분이 있으시면 댓글로 남겨주세요