땀이 삐질삐질 나는 개발 일기

Retrofit2 기본 사용법 본문

개발 Tip

Retrofit2 기본 사용법

삐질 2019. 7. 19. 23:41
안녕하세요. 삐질삐질 개발하는 개발자 삐질입니다.

지난 시간에 이어서 외부 서버와 API를 통해 통신을 할때 사용하는  Retrofit2(이하 “레트로핏”)에 대해 알아보려합니다.

레트로핏은 크게 4가지 부분으로 이루어져 있습니다.

  1. 네트워크 통신에 필요한 전반적인 설정을 관리하는 Retrofit 몸통 부분
  2. 통신 할 API의 Http Method를 정의하는 Service Interface
  3. Request / Response
  4. Dto (Data Transfer Object )


간단히 코드로 살펴보도록 하겠습니다.

  • Gradle Dependency
//retrofit2
implementation 'com.squareup.retrofit2:retrofit:2.5.0'
implementation 'com.squareup.retrofit2:converter-gson:2.5.0'
implementation 'com.google.code.gson:gson:2.8.5'
implementation 'com.squareup.okhttp3:logging-interceptor:3.8.0'
implementation 'com.squareup.okhttp3:okhttp:3.8.0'


  1. NetWorkHelper( Retrofit 몸통 부분)
    -추가 수정 -
        - SingleTone으로 선언할 때는, 생성자를 private으로 선언해야함 ( 외부에서 생성하지 않기 때문 )
public class NetworkHelper {
    private Retrofit retrofit;
    private ApiService apiService;
    public static NetworkHelper networkHelper = new NetworkHelper();

    private NetworkHelper() {
        retrofit = new Retrofit.Builder()
                .baseUrl(Const.BASEURL) //api baseURL
                .addConverterFactory(GsonConverterFactory.create()) // 나는 데이터를 자동으로 컨버팅할 수 있게 GsonFactory를 씀
                .build();

        apiService =retrofit.create(ApiService.class); //실제 api Method들이선언된 Interface객체 선언
    }

    public static NetworkHelper getInstance() { //싱글톤으로 선언된 레트로핏 객체 얻는 용
        return networkHelper;
    }

   
    public ApiService getApiService() { // API Interface 객체 얻는 용
        return apiService;
    }
}


  1. ApiService.interface( server api를 보고 해당 스펙에 맞게 메서드 선언)
public interface ApiService {
    // dummy url

    @POST("login") // api path
    Call<Authorization> loginAccount(@Header("Authorization") String authKey); //해더에 key Authorization String 형태의 토큰을 요구함

    @GET("accounts/{accountId}") //api path
    Call<Authorization> getAccountInfo(@Header("Authorization") String authKey,//해더에 key Authorization String 형태의 토큰을 요구함, {}
                                     @Path("accountId") String accountId);

    @GET("accounts/test")//api path
    Call<Authorization> getTestApi(@Header("Authorization") String authKey,//해더에 key Authorization String 형태의 토큰을 요구함
                                       @Query("params") String accountId); //  accounts/text라는 api Qurey인자로 Key: params String 값을 요구함
    @FormUrlEncoded
    @POST("user/login")//api path
    Call<Result> getInfo(@Field("params") String name); //user/login api에서 파라미터로 key : params String형태의 값을 요구함
}


  1. Request / Response 
public void requestTestApi(){

Call<Result> call = NetworkHelper.getInstance().apiService.getInfo("삐질"); //콜 객체선언
Callback<Result> callback = new Callback<Result>() { //리스폰 시, 대응할 구현체
@Override
public void onResponse(Call<Result> call, Response<Result> response) {
if(response.isSuccessful()) { //check for Response status
Result result = response.body(); //리스폰의 바디를 Result객체로 담아쥼.
result.getAge();
result.getName();
//Todo : 성공시 할일
}
else{
// Todo: 실패시 할일
}
}

@Override
public void onFailure(Call<Result> call, Throwable t) {

}
};

call.enqueue(callback); // call 객체를 통해 콜백 객체를 큐에넣고 실행
}
  1. DTO ( Data Transfer Object )
  • Authorization DTO
    -추가 수정-
    댓글을 통해 좋은 지적이 들어왔습니다.  아래 처럼 서버에서 내려주는 Response값에 대해 SerialIzeName로 어노테이션을 붙이는 이유는 , 기본적으로 Response <-> DTO간의 Variable 맵핑을 위한 이유입니다. 즉 Response를 DTO오브젝트로 변환할 때 ,   SerialIzeName에 같은 변수명으로 할당 된 곳에 옮겨담아 줍니다. 

    Response를 DTO의 각 변수로 맵핑하는 방법에는 두가지가 있습니다. ( 기본적으로 setter함수는 필수 )

    1. @SerialIzedName 어노테이션을 통해 변수명 맵핑의 경우 ( 보통 서버에서 내려주는 변수명과,  내가 직접 만든 변수명이 다른 경우에 사용합니다.)
    Ex) 서버에서 성별을 gender 라고 내려주는데, 내 스타일은 sex다 . 이때 내가 사용할 private String sex .  서버가 내려주는 gender 를 맵핑하기 위해 
    어노테이션으로 사용합니다.

    2.@SerialIzedName 어노테이션을 사용하지 않고, 변수명 그 자체를 서버의 Response와 일치시켜주는 경우 ( 이 경우 어노테이션을 쓰지 않아도 자동 맵핑 됩니다. ) 


    두가지의 방식중에 , 저는 상황에 따라 어노테이션을 쓰기도 하고 , 쓰지 않기도 합니다. 그러나 저는 상황에 따라 변수네임을 바꿔야하는 경우도 있기 떄문에 기본적으로 귀찮지만 어노테이션을 사용하는 편 입니다. 

    이 두방식에는 정답은 없고, 스타일은 있습니다. 원하시는 방식으로 작성하시면 좋습니다.
public class Authorization {

    @SerializedName("user_id")
    @Expose
    private String userId;

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

    public void setUserId(String userId) {
        this.userId = userId;
    }

    public String getId() {
        return userId;
    }

    public void setToken(String token) {
        this.token = token;
    }

    public String getToken() {
        return token;
    }
}


  • Result Dto
public class Result {

    @SerializedName("name")// api 리스폰 시 들어올 name이라는 json key
    private String name;
    @SerializedName("age") // api 리스폰 시 들어올 age라는 json key
    private int age;

    /*반드시 게터 세터 를 선언해줘야함*/

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}


DTO가 무엇인가요? : 데이터를 송 / 수신 할때 서버와 정의한 Object 데이터형식에 맞게 작성된 그릇 클래스 
기본적으로  레트로핏은 ResponseBody를 통해 (Generic 역할)  서버가 전해주는 Response를 받을 수 있습니다. 하지만, 리스폰스바디를 통해 받게되면, 파싱을 또 일일이 해야하는 일련의 작업들이 있기 떄문에
    Ex ) ResponseBody  response = response.body;
    JsonObject json = JsonParser.parse(response.string()) … 뭐이런식으로?? 제이슨으로 파싱을 하고, 각 키값을 일일이 가져와야함.
앞서 몸통부분에 소개한 .addConverterFactory(GsonConverterFactory.create())  코드를 통해, 내가 지정한 DTO클래스대로 자동으로 파싱해줘~~ 라고 사용하는 것 입니다. (DTO는 반드시 서버가 전달해주는 규격과 맞추어야하며, @SerializedName 을 통해, 서버가 주는 각 Key와 매칭시켜주는 것이 좋음. @SerializedName 를 사용하지 않아도 기본적으로 변수명이 같으면 상관 없지만, 서버에서는 “name”으로 주는데 내 코드작성 스타일은 보통 “Username” 사용한다 싶은 분들은 @SerializedName  를 사용해주는 것이 좋다.

제네릭 타입으로 DTO를 지정하는 방법은 아래 코드들이다.
Call<Result> call
Call<Authorization>
Call<Result>

주의할 점
  • 각 http method는 어노테이션(@)를 이용해 표기함
  • POST위의 FormUrlEncoded는:이 인코딩은 URL 매개 변수에 사용 된 인코딩과 동일합니다. 키 / 값 쌍을 입력하면 해당 contentType:application/application/x-www-form-urlencoded 의 파람을 인코딩할 때 쓰입니다.
  • Get 메서드 path 안에 {accountId}는 동적으로 path를 인자로 받아 지정하기 위한 방법이며, @Path로 받을 수있습니다.
  • @Header는 헤더에 key:value 값을 지정해줄 수 있습니다.
  • DTO를 사용하여 객체단위로 데이터를 관리하는 것이 매우 좋음.


이상으로  레트로핏2의 설명을 마치겠습니다.

안드로이드 초보 개발자를 위해 아래와 같은 카카오 오픈톡을 운영 중입니다



Comments