달달한 스토리

728x90
반응형

 

네이티브로 돌아온 느낌은 꽤나 신선하다.

 

오늘은 현직에서도 자주 사용되고 있는 Retrofit을 다루려고 한다.

 

보통 서버 api 통신을 할 때 편리하고 간단하게 사용될 수 있는

 

라이브러리로써 많은 사람들에게 사랑받고 있는 라이브러리라고 할 수 있다.

 

그렇다면 한 번 예제를 봐보자.


사용법

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

우선 이 두 종속성을 build.gradle(:app)에 추가를 해주는 것으로 시작합니다.

 

최신 버전이 있다면 버전을 올려주세요!

 

여기서 잠깐 알고 갈 것!!

 

gson이란 retrofit으로 얻은 직렬화된 json 데이터를 

자바 객체로 다시 직렬화 혹은 역 직렬화 해주는 자바 라이브러리입니다.

 

서버에서 데이트를 받아오면서 또 그것을 자바에서 사용할 수 있게

 

직렬화해야 해주는 라이브러리 이므로, 필수라고 할 수 있습니다..

 

자 그러면 다음으로 넘어가 보죠.

 

 

 

우선 MVVM패턴을 바탕으로 만들어 보겠습니다.

 

MVVM 패턴 설명은 다음에 블로그에 기재해 두도록 하겠습니다.

 

저 같은 경우는 뷰 모델에 retrofit을 통해 데이터를 받아와 가공하는 패턴을 두어서,

 

폴더 구조는 다음과 같습니다.

 

(본의 아니게 패턴 공부가 되어 가는군요ㅠㅠ)

 

이 글은 레트로핏만 다루어 보겠습니다.

 

우선 dto 클래스를 만들어 줍니다.

 

여기서 dto란

 

Data Transfer Object(데이터 전송 객체)라는 뜻으로,

 

계층 간 데이터 교환하기 위해 사용되는 객체라고 할 수 있습니다.

 

레트로핏에서는 이런 데이터 전송 객체 말고도 엔티티 등등 사용되지만,

 

전 여기서 dto만 다루겠습니다!! 

 

우리는 이 dto 클래스를 통해서 데이터들을 받을 것이다 라는 내용으로 이해하시면 됩니다.

 

저 같은 경우는 

 

요소수 재고량에 대한 데이터를 가져오기 위해 아래 사이트에서 api를 얻게 되었습니다.

 

https://www.data.go.kr/data/15094782/fileData.do#tab-layer-openapi

 

공공데이터활용지원센터_요소수 중점 유통 주유소 재고현황_20211124

전국의 중점 유통 주유소의 요소수 재고 현황은 매일 2회 업데이트 됩니다. (12시 기준 재고현황 : 14시, 18시 기준 재고현황 : 20시)<br/> * 현재 버전 11월24일 18시 기준 업데이트 완료

www.data.go.kr

 

이 사이트에 들어가시게 되면

 

앗 먼저 이곳에서 회원가입과 api 승인요청에 대한 내용은 생략하겠습니다.

 

인터넷에 많은 자료들이 있어서 찾아보시면 좋을 듯 합니다.

 

여기 API 목록에 저 Get api항목을 클릭하시면,

 

아래와 같이 이 api를 통해 전달받을 수 있는 데이터가 Responses에 표시가 되어 있습니다.

위에 데이터를 받을 모델 클래스(dto)를 만들어 줄 작업을 해봅시다.

 

저 같은 경우는 아래와 같이 작성하였습니다.

 

728x90

 

DieselDataCless.java

public class DieselDataClass {
    @SerializedName("perPage")
    @Expose
    private String perPage;

    @SerializedName("data")
    @Expose
    private DieselData[] dieselData;

    @SerializedName("currentCount")
    @Expose
    private String currentCount;

    @SerializedName("page")
    @Expose
    private String page;

    @SerializedName("totalCount")
    @Expose
    private String totalCount;

    public String getPerPage ()
    {
        return perPage;
    }

    public void setPerPage (String perPage)
    {
        this.perPage = perPage;
    }

    public DieselData[] getDieselData()
    {
        return dieselData;
    }

    public void setDieselData(DieselData[] dieselData)
    {
        this.dieselData = dieselData;
    }

    public String getCurrentCount ()
    {
        return currentCount;
    }

    public void setCurrentCount (String currentCount)
    {
        this.currentCount = currentCount;
    }

    public String getPage ()
    {
        return page;
    }

    public void setPage (String page)
    {
        this.page = page;
    }

    public String getTotalCount ()
    {
        return totalCount;
    }

    public void setTotalCount (String totalCount)
    {
        this.totalCount = totalCount;
    }

    @Override
    public String toString()
    {
        return "ClassPojo [perPage = "+perPage+", data = "+ dieselData +", currentCount = "+currentCount+", page = "+page+", totalCount = "+totalCount+"]";
    }
}

 

 

그리고 json 중에 data라는 키값이 맵으로 되어 있기 때문에

 

추가로 DieselData dto 클래스를 만들어줍니다.(헷갈림 주의)

 

public class DieselData {

    @SerializedName("코드")
    @Expose
    private String code;

    @SerializedName("주소")
    @Expose
    private String address;

    @SerializedName("영업시간")
    @Expose
    private String operating_time;

    @SerializedName("가격")
    @Expose
    private String price;

    @SerializedName("명칭")
    @Expose
    private String detail_address;

    @SerializedName("위도")
    @Expose
    private String latitude;

    @SerializedName("재고량")
    @Expose
    private String diesel_stock;

    @SerializedName("전화번호")
    @Expose
    private String phone_number;

    @SerializedName("데이터기준일")
    @Expose
    private String update_date;

    @SerializedName("경도")
    @Expose
    private String longitude;

    public String getCode() {
        return code;
    }

    public void setCode(String code) {
        this.code = code;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    public String getOperatingTime() {
        return operating_time;
    }

    public void setOperatingTime(String operatingTime) {
        this.operating_time = operatingTime;
    }

    public String getPrice() {
        return price;
    }

    public void setPrice(String price) {
        this.price = price;
    }

    public String getDetailAddress() {
        return detail_address;
    }

    public void setDetailAddress(String detail_address) {
        this.detail_address = detail_address;
    }

    public String getLatitude() {
        return latitude;
    }

    public void setLatitude(String latitude) {
        this.latitude = latitude;
    }

    public String getDieselStock() {
        return diesel_stock;
    }

    public void setDieselStock(String dieselStock) {
        this.diesel_stock = dieselStock;
    }

    public String getPhoneNumber() {
        return phone_number;
    }

    public void setPhoneNumber(String phoneNumber) {
        this.phone_number = phoneNumber;
    }

    public String getUpdateDate() {
        return update_date;
    }

    public void setUpdateDate(String updateDate) {
        this.update_date = updateDate;
    }

    public String getLongitude() {
        return longitude;
    }

    public void setLongitude(String longitude) {
        this.longitude = longitude;
    }

    @NonNull
    @Override
    public String toString() {
        return "ClassPojo [code = " + code + ", address = " + address + ", operating_time = " + operating_time + ", price = " + price + ", detail_address = " + detail_address + ", latitude = " + latitude + ", diesel_stock = " + diesel_stock + ", phone_number = " + phone_number + ", update_date = " + update_date + ", longitude = " + longitude + "]";
    }
}

 

반응형

 

이렇게 DieselDataClass class와 DieselData class를 만들었습니다.

 

이름이 헷갈린 점 죄송합니다.

 

위에 코드를 간단히 설명드리면,

 

@SerialzedName
JSON으로 serialize 될 때 매칭 되는 이름을 명시하는 목적으로 사용되는 field 마킹 어노테이션이다.

한 마디로 서버에서 명명한 키 값을 이곳에다가 넣어주시면 됩니다.

 

@Expose
object 중 해당 값이 null일 경우, json으로 만들 필드를 자동 생략해 준다.

 

이런 식으로 각 변수마다 이 어노테이션을 넣어주었습니다.

(이 녀석들은 gson라이브러리에 포함된 녀석들입니다.)

 

그런데, 이렇게 복잡한 클래스를 자동으로 만들어 주는 사이트가 있습니다.

 

이러한 클래스의 게터 세터 자체의 클래스를 pojo라고 합니다.(pojo class)

 

이러한 POJO클래스를 자동으로 만들면 얼마나 편리할까요??

 

아래 사이트에서 내가 받을 데이터에 값을 입력하면 POJO클래스를 자동으로 만들어 줍니다.

 

http://pojo.sodhanalibrary.com

 

Convert XML or JSON to Java Pojo Classes - Online

 

pojo.sodhanalibrary.com

 

사이트로 들어가시면 클래스 네임을 입력해줍니다.

 

저는 아까 위에 처럼 DieselDataClass를 입력하겠습니다.

 

Select Input Type은 JSON으로 해주시고,

 

아래 에딧 창에는 내가 받을 데이터들을 json 형식으로 넣어주시면 됩니다.

 

아까 공공데이터 포털에서 보았던, json 데이터들을 넣어주겠습니다.

 

그리고 Submit버튼을 누르면,

 

이렇게 두 가지에 클래스가 만들어지게 됩니다.

 

여기서 각 전역 변수들에 위에

 

아까 위에 처럼 어노테이션만 달아주면, dto클래스는 여기까지 준비 완료입니다.

 

 

자 이제 Retrofit에 클라이언트를 만들어 줍시다.

 

이곳에서는 레트로핏에 인스턴스를 싱글톤으로 만들어 주어,

 

서버에서 한 번만 요청하게 만들었습니다.

 

// 싱글톤 패턴
// 사용법
// 먼저 getInstance로 retrofit빌드 해주고,
// getRetrofitAPI를 사용하면 된다.

public class RetrofitClient {
    private static RetrofitClient instance = null;
    private static RetrofitAPI retrofitAPI;
    //만약 레트로핏이
    private final static String BASE_URL = "https://api.odcloud.kr/api/";


    private RetrofitClient() {
        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl(BASE_URL)
                .addConverterFactory(GsonConverterFactory.create())
                .build();
        retrofitAPI = retrofit.create(RetrofitAPI.class);
    }

    public static RetrofitClient getInstance() {
        if (instance == null) {
            instance = new RetrofitClient();
        }
        return instance;
    }

    public static RetrofitAPI getRetrofitAPI() {
        return retrofitAPI;
    }
}

 

우선 위와 같이 싱글톤 패턴에 맞게 변수들을 static으로 만들어 줍니다.(메서드도)

 

instance만 null로 초기화를 해줍니다.

 

순서는 다음과 같습니다.

 

우선 getInstance메서드로 처음 인스턴스를 RetrofitClient 클래스의 instance를 생성합니다.

 

만약 처음 instance가 null이니 새로운 RetrofitClient 생성자가 실행이 될 거고,

 

Retrofit.Bilder() 메서드를 통해,

 

공공데이터 포털의 BaseUrl값도 넣어주고,

 

레트로핏의 이제 생성할 인터페이스인 RetrofitAPI도 넣어줍니다.

 

생성할 수 있게 각각 설정을 하고 레트로핏 사용 준비를 끝마칩니다.

 

그리고 retrofitApI 변수에 이 결과 값을 담아줍니다.

 

여기까지 완료되었으니,

 

이제 서버로 요청할 RetrofitAPI라는 인터페이스를 만들어줍니다.

 

 

public interface RetrofitAPI {

    @GET("15094782/v1/uddi:6b2017af-659d-437e-a549-c59788817675")
    Call<DieselDataClass> getDieselData(@Query("serviceKey") String serviceKey, @Query("perPage") int perPage);
}

저 같은 경우는 공공데이터 포털에 지침대로 다음과 같이 만들었습니다.

 

GET형식으로 공공데이터 포털에서 가져오길 원했고,

 

GET 어노테이션 안에 들어간 것은 데이터를 가져올 특정 api주소입니다.

 

그리고 반환받을 타입에 Call<DieselDataClass> 라고 쓰여 있습니다.

 

아까 저희가 만든 dto클래스를 넣어줌으로써, 호출하게 되면 클래스에 값이 담겨서 오게 되는 것입니다.

 

참 좋죠??

아까 봤던 이 녀석입니다.

 

아 맞다 혹시 몰라 말씀드리지만,

 

baseUrl은 이곳에 있습니다.

이 부분들은 공공데이터 포털 가이드라인에 잘 나왔으니,

 

저는 레트로핏만 설명하는 것만 하는 걸로 하겠습니다.

 

모르겠다 하시는 분들은 언제든지 댓글로 질문해주세요!

 

저렇게 인터페이스를 설정하고 나면,

 

사용할 준비가 완료가 됩니다.

 

이제 저 api를 요청하고, 데이터를 받을 수 있는 리스너를 만들어 보겠습니다.

 

저는 ViewModel안에서 작업했기 때문에

 

다른 분들은 Activity나 fragment에서 작업하셔도 상관없습니다.

 

   
public class HomeViewModel extends ViewModel {
   
   private MutableLiveData<ArrayList<GasStationModel>> dieseldata;
   private RetrofitAPI retrofitAPI;
   private final static String SECRET_KEY = "시크릿 키"

public MutableLiveData<ArrayList<GasStationModel>> getDieselData() {
        if (dieseldata == null) {
            dieseldata = new MutableLiveData<>();
            loadDieselData();
        }
        return dieseldata;
    }

    private void loadDieselData() {
        RetrofitClient retrofitClient = RetrofitClient.getInstance();

        if (retrofitClient != null) {
            retrofitAPI = RetrofitClient.getRetrofitAPI();
            retrofitAPI.getDieselData(SECRET_KEY, 1000).enqueue(new Callback<DieselDataClass>() {
                @Override
                public void onResponse(Call<DieselDataClass> call, Response<DieselDataClass> response) {
                    DieselDataClass dieseldataClass = response.body();
                    DieselData[] data = dieseldataClass != null
                            ? dieseldataClass.getDieselData()
                            : new DieselData[0];

                    ArrayList<GasStationModel> insertData = new ArrayList<>();

                    for (int i = 0; i < data.length; i++) {
                        String address = data[i].getAddress();
                        String operatingTime = data[i].getOperatingTime();
                        String price = data[i].getPrice();
                        String getDetailAddress = data[i].getDetailAddress();
                        String dieselStock = data[i].getDieselStock();
                        String phoneNumber = data[i].getPhoneNumber();
                        String updateDate = data[i].getUpdateDate();

                        insertData.add(new GasStationModel(
                                getDetailAddress,
                                operatingTime,
                                price,
                                address,
                                dieselStock,
                                phoneNumber,
                                updateDate
                        ));
                    }

                    dieseldata.setValue(insertData);
                }

                @Override
                public void onFailure(Call<DieselDataClass> call, Throwable t) {
                    //만약 서버통신이 안될때
                }
            });
        }
    }
    }

마지막이니 만큼 세세하게 설명드리겠습니다.

 

ViewModel가 LiveData에 개념에 이해를 못 하셔도, 

 

지금은 큰 필요가 되지 않으니..

 

그냥 변수로 이해하셔도 될 것 같습니다.

 

getDieselData() 메서드를 실행하게 되면,

 

아까 RetrofitClient 클래스와 마찬가지로 싱글톤 느낌으로,

 

처음에는 diesledata 변수가 null이므로, dieseldata 변수를 초기화하고,

 

loadDieselData() 메서드를 실행하게 된다.

 

먼저 RetrofitClient getInstance() 메서드로 인스턴스를 먼저 생성한 뒤에

 

if로 null 체크를 한 뒤 

 

RetrofitClient에서 getRetrofitAPI(인터페이스) 메서드를 실행하여, 인터페이스 내용을 가지고 와서,

 

우리가 아까 인터페이스에 만들어 둔 getDieselData를 비동기 요청을 보내고 콜백을 받고 오류도 알려주는

 

enqueue 메서드를 이용해 응답이 성공하면 onResponse와 실패하면 onFailure로 콜백을 받는다.

 

그리고 자신들이 원하는 대로 데이터를 response.body로 받고,

 

가공해서 사용하면 된다.

 

 

 

여기까지 마치면 레트로핏은 끝난다.

 

여기까지 읽어주셔서 감사합니다.

 

레트로핏은 설명도 어려워서 기본개념이 잡히시면, 쓰시는데 무리가 없는 라이브러리라고 생각합니다.

 

조금이라도 더 쉽게 설명하려 했으나, 여기서 부턴 직접 써보셔야지 실력이 느는 거라고 생각이 듭니다.

 

여기까지 레트로핏에 대한 글이었습니다.

 

감사합니다.

728x90
반응형

공유하기

facebook twitter kakaoTalk kakaostory naver band
loading