안드로이드와 서버간의 REST API 통신을 도와주는 라이브러리로, OkHTTP에 기반을 두고 있으며 현재 가장 널리 쓰이는 통신 라이브러리이다.
Volley
, AsyncTask
와 같은 여러 통신 라이브러리가 있는데 Retrofit 이 사랑받는 이유는 무엇일까?
아래의 표를 보자.
🎃 힘의 차이가 느껴지십니까?
한 눈에 보아도 Retrofit 이 위의 두 항목보다 빠른 성능을 자랑하고 있는 모습을 확인할 수 있다.
Annotation으로 HTTP 메소드를 정의함으로서 코드의 구현이 쉬워지며 개발자들은 행위를 손쉽게 알아볼 수 있게 되어 직관적으로 코드를 설계할 수 있게 된다.
Retrofit은 서버 연동 시 주로 주고받는 데이터인 JSON, XML을 자동을 파싱해주는 Converter 연동을 지원해주기 때문에, 개발자 입장에서는 유지보수가 매우 편리할 수 밖에 없다.
이렇게 보았을 때, 현재 시중에 있는 통신 라이브러리에서 Retrofit을 사용하지 않을 이유가 없다. 성능도 뛰어나고, 코드 작성도 편리하고, 유지보수도 쉬운데 마다할 이유가 어디 있을까?
implementation 'com.squareup.retrofit2:retrofit:2.5.0'-
implementation 'com.squareup.retrofit2:converter-gson:2.6.0'
converter-gson - JSON 타입의 결과를 객체로 자동 매핑(파싱)해주는 아주 기특한 녀석이다.
<uses-permission android:name="android.permission.INTERNET" /> <!-- 인터넷 권한 선언 -->
네트워크 통신에 인터넷 권한 선언은 기본적으로 필수이다.
Model 은 서버 연동을 위해 사용하는 데이터 추상화 클래스이다. 위에서 이야기했던 convertor가 JSON 데이터를 자동으로 파싱하고, 객체를 생성한 후 모델에서 정의한 변수에 데이터를 담아준다. 보통 DTO 클래스라고 부른다.
응답 데이터 구조에 맞게 모델 클래스를 선언하면 된다. 간단한 예시로, 사용자가 즐겨찾는 장소를 저장해놓은 API를 선정하였다.
/*
API 응답 데이터 구조
[{"success":true,"id":"hoyaho","latitude":37.5024853,"longitude":126.8675475,"title":"favorite 1","content":"content 1"},
{"success":true,"id":"hoyaho","latitude":37.446536,"longitude":126.640313,"title":"favorite 2","content":"content 2"},
{"success":true,"id":"hoyaho","latitude":37.462619,"longitude":126.659094,"title":"favorite 3","content":"content 3"},
{"success":true,"id":"hoyaho","latitude":37.457564,"longitude":126.607801,"title":"favorite 4","content":"content 4"}]
*/
public class Favorite {
@SerializedName("success") // 변수명이 일치할 때는 사용하지 않아도 상관 없음.
private boolean success;
private String id;
@SerializedName("latitude")
private double latitude;
@SerializedName("longitude")
private double longitude;
@SerializedName("title")
private String title;
@SerializedName("content")
private String content;
public Favorite(int fnum, String id, double latitude, double longitude, String title, String content) {
this.id = id;
this.latitude = latitude;
this.longitude = longitude;
this.title = title;
this.content = content;
}
@Override
public String toString() {
return "Favorite{" +
"success=" + success +
", id='" + id + '\'' +
", latitude=" + latitude +
", longitude=" + longitude +
", title='" + title + '\'' +
", content='" + content + '\'' +
'}';
}
}
jsonschema2pojo 의 도움을 받으면 매우 간편해진다. JSON 데이터를 자바 클래스로 변환해주는 사이트로, 응답받는 JSON 데이터를 복사해서 그대로 붙여넣기 하면 자동으로 모델을 완성시켜준다.
이렇게 응답 데이터의 결과값을 복사해서 그대로 붙여넣기만 해주면?
🤗 5초만에 모델이 완성된 모습을 확인할 수 있다.
Retrofit 의 핵심이라고 할 수 있는 부분이다. 해당 함수에서 annotation 으로 HTTP Method 를 지정하고, 서버에 전송할 데이터를 추가하면 그 정보에 맞게 서버를 연동할 수 있는 Call 객체를 자동으로 생성하는 구조이다.
즉, 어떤 형태로 어떻게 통신을 할건지 개발자가 지정만 해주면 Retrofit 이 알아서 구현해준다는 의미이다. 참고로 Service 는 Retrofit 에서 사용하는 용어로, API를 정의하는 인터페이스를 의미한다.
public interface FavoriteService {
String FAVORITE_URL = "YOUR_URL"; // URL
// 일반적인 GET방식 서버 호출
@GET("favorite.php") // HTTP Method, baseUrl 뒤의 경로 지정
Call<List<Favorite>> getFavorite(
@Query("id") String id
// @Query : url에 쿼리 파라미터 추가
);
// URL : https://YOUR_URL/favorite.php?id=hoyaho
// URL의 일부분이 동적 데이터에 의해 결정될 때
@GET("{group}/favorite.php")
Call<List<Favorite>> getFavorite(
@Path("group") String group,
// @Path : url의 경로를 동적 할당
@Query("id") String id
);
// URL : https://YOUR_URL/group/favorite.php?id=hoyaho
@FormUrlEncoded
// @POST annotation 에서만 사용 가능,
// @Field annotation 이 추가된 데이터를 인코딩하여 전송하는 역할 수행
@POST("favorite.php")
Call<CheckSuccess> insertFavorite(
@Field("id") String id, // key, value 형식으로 데이터를 전달
@Field("latitude") double latitude,
@Field("longitude") double longitude,
@Field("title") String title,
@Field("content") String content
// @Field annotation 은 개별 매개 변수를 전송하고자 할 때 이용한다.
);
// URL : https://YOUR_URL/favorite.php
// 서버 전송 데이터 : id="hoyaho"&latitude=37.5024853&longitdue= . . .
@POST("favorite.php")
Call<CheckSuccess> insertFavorite(
@Body Userinfo userinfo // 객체를 JSON 형식으로 전송할 때 사용
/*
@Body annotaiton 역시 POST에서만 사용이 가능하다.
객체 변수에 값이 지정되어 있지 않다면, 해당 데이터는 전송되지 않는다.
그러나 기본 데이터 타입은 default 값이 대입되므로 제외된다.
*/
);
// URL : https://YOUR_URL/favorite.php
// 서버 전송 데이터 : {"id":"hoyaho","latitude":37.5024853, "longitude": . . .}
@DELETE("favorite.php") // @Delete Annotation
Call<CheckSuccess> deleteFavorite(
@Query("id") String id
);
// URL : https://YOUR_URL/favorite.php?id=hoyaho
}
예시만 보아도 손쉽게 REST API 통신이 구현되는 것을 확인할 수 있을 것이다. @Field 와 @Body 의 차이점도 간략하게나마 알아두고 가면 후에 도움이 될 것이다. 각 annotation 의 역할에 대해서는 이 후 자세하게 포스팅하도록 하고, 우선 인터페이스 부분은 넘어가도록 하자.
이제 준비는 모두 끝났다. 실제 액티비티에서 Retrofit 객체를 사용하기만 하면 된다.
public void getFavorite() { //
Gson gson = new GsonBuilder().setLenient().create();
/*
통신 시 JSON 사용과 해당 객체의 파싱을 위해 생성
개인적으로 <List><객체>> 부분을 불러올 때 이 부분이 없으면
IllegalArgumentException 이 발생하는 것으로 판단됨.
*/
// Retrofit 생성
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(FavoriteService.FAVORITE_URL) // 기본으로 적용되는 서버 URL (반드시 / 로 마무리되게 설정)
.addConverterFactory(GsonConverterFactory.create(gson)) // JSON 데이터를 Gson 라이브러리로 파싱하고 데이터를 Model에 자동으로 담는 converter
.build();
FavoriteService retrofitAPI = retrofit.create(FavoriteService.class);
// Retrofit 클래스로 interface 객체를 구현한다.
retrofitAPI.getFavorite(userId).enqueue(new Callback<List<Favorite>>() {
// interface 에서 정의했던 메소드 중 하나를 선언하고, 비동기 통신을 실행한다.
// 통신이 완료되었을 때 이벤트를 처리하기 위해 Callback 리스너도 함께 등록한다.
@Override
public void onResponse(Call<List<Favorite>> call, Response<List<Favorite>> response) {
if (response.isSuccessful()) { // 원활하게 통신이 이뤄졌을 때
List<Favorite> data = response.body(); // 응답 내용을 변수에 입력
} else { // 원활한 통신이 이뤄지지 않았을 때
// 비정상 통신 대응
}
}
// 통신 중 생각하지 못한 예외(네트워크 오류 등)가 발생되었을 때 호출된다.
@Override
public void onFailure(Call<List<Favorite>> call, Throwable t) {
t.printStackTrace();
}
});
}
해당 부분에서는 Retrofit 객체를 생성하고, interface 객체를 구현한 후 통신이 원활하게 이뤄졌을 때, 원활하게 이뤄지지 못했을 때의 행동들을 구현해주면 된다.
코드에 담지는 않았지만, 통신이 성공했을 때 리사이클러뷰로 즐겨찾기 리스트를 띄우는 것을 구현하였다. 성공적으로 서버에서 받아와 데이터를 출력하는 모습을 확인할 수 있다.
기본적으로 Retrofit 은 거의 모든 안드로이드 개발자가 사용하는 통신 라이브러리라고 해도 무방하니, 안드로이드 개발을 공부하고 있다면 꼭 알아두는 것이 좋다.
참고 및 출처
Retrofit2 '레트로핏' - 기본 사용법
Retrofit2 한글판 문서
Get Started With Retrofit 2 HTTP Client