집요정 도비의 일기
Retrofit 2로 Json파싱 없이 Http통신 구현 시간을 단축해보자. 본문
1. Intro
일하다보면 참 통신 구현할 곳이 많음.
요즘 만드는 앱들 보면 뭘 하든 통신이 안들어가는 경우가 없기 때문임.
기존 레거시 방식 같은 경우 직관적이긴 하나 코드가 너무 길어지고 단순 노가다를 하는 감이 없지 않아 있음.
이걸 좀 어떻게 줄이는 법이 없을까하고 석달즘 전에 열심히 찾아본 결과 Retrofit2님을 영접했음.
기존 레거시 방식의 httpClient와 비교했을 때의 장단점은 아래와 같음.
*장점
1. REST 뿐만 아니라 파일 업로드, 다운로드 등 모든 서버콜에 대해 짧고 간결한 코드로 대응이 가능하다.
2. okhttp 기반이라 심지어 더 빨라짐.
3. JSON파싱을 gson파서가 알아서 해줘서 코드가 더욱 더 줄어듬.
4. 콜백은 메인 쓰레드에서 돌고 통신부는 알아서 쓰레드에서 돌아서 별도의 asyncTask나 쓰레드를 만들 필요가 없음. 코드 어디서든 호출 가능.
*단점
1. 아무래도 처음 볼때는 조금 낯설다.
2. gson파싱을 위해 서버에서 받는 리스폰스 데이터를 미리 클래스화 해놔야한다.(이는 단점보다는 장점의 요소가 더 강하다.)
본인이 생각하기에 단점은 정말 처음에 조금 낯선 것 밖에 없다. 몇번 쓰다보면 금방 익숙해진다. 2번은 단점이라고 하기 어려운 면이 더 많다.
2. Setting
아래와 같은 라이브러리들을 build.gradle 디펜던시에 추가해주면 된다.
compile 'com.squareup.retrofit2:retrofit:2.1.0'
compile 'com.google.code.gson:gson:2.7'
compile 'com.squareup.retrofit2:converter-gson:2.1.0'
3. Usage
Retrofit을 사용하려면 미리 서버 api에 대한 정의를 해둬야한다.
또한, 서버에서 받을 리스폰스를 미리 클래스화해둬야한다.
예를들어, 서버에서 아래와 같은 post 리퀘스트를 보내야한다고 가정한다.
http://localhost:8080/login
param : {userId : sj , password : 12345}
response : {result : true, message : 로그인에 성공했습니다. }
3-1. 리스폰스값을 클래스로 만들어둔다.
서버에서 받을 리스폰스를 받을 클래스를 만들어주면 된다.
위의 경우, boolean result, String message를 갖는 클래스를 만들어주면 된다.
public class LoginResultModel {
private String message;
private boolean result; .... +getter, setter... }
3-2. 서버 api에 대한 정의
인터페이스를 만들어서 거기다 정의하면 된다. 위의 api를 예로 들어보면 다음과 같이 된다.
public interface WebEndPoints {
@FormUrlEncoded
@POST("/login")
Call<LoginResultModel> do_login(@Field("userId") String userId, @Field("password") String password,);
//Call객체가 뭔지 몰라도 된다. 중요한건 이 api의 리스폰스를 LoginResultModel을 받는다는 것이다.
}
자세히보면 Call<>안에 위에서 만든 LoginResultModel을 사용했음을 알 수 있다.
이는 즉, response로 받은 값을 LoginResultModel클래스로 받는다는 뜻이며, 자동적으로 이뤄진다.
3-3. 실제 통신 사용
위의 두 단계만 거치면 서버 통신이 하나 구현된거나 마찬가지다. 이제 어디든 가져다 쓰면 된다.
Retrofit retrofit = new Retrofit.Builder().baseUrl(BASE_URL).addConverterFactory(GsonConverterFactory.create())
.build();
WebEndPoints endPoints = retrofit.create(WebEndPoints.class);
endPoints.login_post(userId, password, key).enqueue(new Callback<LoginResultModel>() {
@Override
public void onResponse(Call<LoginResultModel> call, Response<LoginResultModel> response) {
LoginResultModel result = response.body();
//통신 성공
}
@Override
public void onFailure(Call<LoginResultModel> call, Throwable t) {
//통신 실패
}
});
위와 같이 사용하면 통신이 보내지며, callback으로 LoginResultModel에 알아서 데이터가 컨버팅되서 들어간다.
response.body()로 LoginResultModel과 같은 값이 차있는 리스폰스 객체를 받을 수 있다. 실질적으로 통신이 끝난것이다.
여기서 중요하게 짚고 넘어갈 것은, 통신 요청은 알아서 다른 쓰레드에서 돌며, onResponse와 onFailure는 메인쓰레드에서 동작한다.
따라서 달리 쓰레드나 AsyncTask를 정의해줄 필요가 없다.
3-4. 사용하기 편하게 래핑
래트로핏은 한번만 만들어도 여기저기서 쓸 수 있다. 즉, 매번 위와 같은 retrofit = new ...이런걸 해줄 필요가 없다는 것이다.
본인은 아래와 같이 래핑해서 사용한다.
public class RetrofitSender {
public static final String BASE_URL = "...서버의 아이피 주소...";
private static Retrofit retrofit = null;
private static WebEndPoints endPoints = null;
public static WebEndPoints getEndPoint(){
if(endPoints ==null) {
retrofit = new Retrofit.Builder().baseUrl(BASE_URL).addConverterFactory(GsonConverterFactory.create())
.build();
endPoints = retrofit.create(WebEndPoints.class);
}
return endPoints;
}
}
위와 같은 래퍼를 쓰면 보다 코드가 줄어든다. 3-3에서 사용한 로그인 리퀘스트가 이렇게 된다.
RetrofitSender.getEndPoint().login_post(userId, password).enqueue(new Callback<LoginResultModel>() {
@Override
public void onResponse(Call<LoginResultModel> call, Response<LoginResultModel> response) {
LoginResultModel result = response.body();
}
@Override
public void onFailure(Call<LoginResultModel> call, Throwable t) {
}
});
실제로 두줄이 줄어들었을 뿐이나, 귀찮음이 꽤 줄었음을 알 수 있다.
4. 여러가지 활용
위에서는 단순히 가장 많이 쓰는 POST의 예를 들었지만, GET, UPDATE, DELETE, 심지어 파일 업로드, 다운로드에 모두 대응된다.
통신 호출법은 파일 업, 다운로드 뺴고는 완전히 똑같다. 따라서 이를 제외하고는 통신 호출부에 대한 소개를 생략하고 인터페이스에 api정의할 때의 syntax위주로 소개하도록 한다.
이 포스트에서는 GET과 파일 업데이트만을 소개토록 한다.
(주로 본인이 다루는 것은 이미지 파일들인데, 이미지 파일 다운로드는 Glide나 Universal Image Loader가 훨씬 쉽고 강력하므로 다루지 않는다.)
4-1. GET (+response가 JSON object를 다수 포함한 json array 인 경우)
@GET("/users")
Call<List<UserModel>> getAllUsers();
위의 경우, UserModel객체의 리스트를 리스폰스로 받는 GET콜이다.
Call괄호 안에 리스트를 빼고 UserModel만을 쓴다면 UserModel하나만 받는 콜이 된다. 이는 당연히 post등에도 모두 적용된다.
4-2. 파일 업로드
@Multipart
@POST("/upload/")
Call<DataUpload> uploadImageFile(
@Part MultipartBody.Part file);
File file = new File(path);
MultipartBody.Part filePart = MultipartBody.Part.createFormData("file", file.getName(),
RequestBody.create(MediaType.parse("image/*"), file));
RetrofitSender.getEndPoint().uploadImageFile(filePart).enqueue(new Callback<DataUpload>() {
@Override
public void onResponse(Call<DataUpload> call, Response<DataUpload> response) {
DataUpload upload = response.body();
Log.e("model", "Success");
}
@Override
public void onFailure(Call<DataUpload> call, Throwable t) {
Log.e("model", "Failed");
}
});
파일 업로드도 위와 같이 매우 간단히 가능하다. 실질적으로 response를 받는것은 똑같으나, 파일을 MultiPart데이터로 만들어서 넣주는것만 차이가 있을 뿐이다.
5. Outro
이거 쓰다 예전 레거시 httpClient로 된 통신 코드보면 토가 쏠릴 것이다. 새롭게 프로젝트를 구성한다면 도입하는게 좋을 것 같다.
'개발 일기' 카테고리의 다른 글
FindViewById 노가다 그만합시다. Prettify로 1초만에 findViewById 자동으로 다 하기. (0) | 2017.03.12 |
---|---|
EventBus로 여기저기서 쉽게 콜백 받기 (0) | 2016.12.28 |
selector auto creator (0) | 2016.10.18 |
Step 4. 음악 스트리밍 서버(Spring boot), 앱(Android)을 만들어보자. (6) | 2016.03.05 |
Step 3. 음악 스트리밍 서버(Spring boot), 앱(Android)을 만들어보자. (0) | 2016.03.05 |