Retrofit이란?
- Rest API 통신을 위해 구현된 라이브러리
- Squareup사의 OkHttp 라이브러리의 상위 구현체
- Retrofit은 OkHttp를 네트워크 계층으로 활용하고 그 위에 구축됨
초창기 안드로이드 네트워크 통신은 HttpURLConnection / Apache HTTP Client 사용
하지만 단점이 많아 Deprecated 되고 OkHttp, Volley, Retrofit 등 라이브러리 사용
근데 왜 하필 Retrofit이냐!
서버 통신은 비동기 처리를 위해 스레드를 사용해야 하는데 스레드 성능이 제일 빠르기 때문이다
Retrofit 장점
- 매우 빠른 성능!!
- 위에서 보았듯이 매우매우매우 빠르기 때문에 안 쓸 이유가 없다
- 간단한 구현HttpURLConnection의 Connection / Input & OutputStream / URL Encoding등등 귀찮던 작업들을 라이브러리 내에서 모두 처리해준다
- OkHttp의 QueryString / Request & Response
- 반복된 작업을 라이브러리 내에서 처리
- 가독성
- Annotation 사용으로 가독성이 좋고 직관적이다
- 동기/비동기 쉬운 구현
Retrofit의 구성 요소 3가지
- DTO(POJO) - ‘Data Transfer Object’, ‘Plain Old Java Object’형태의 모델 / JSON 타입 변환에 사용
- Interface - 사용할 HTTP CRUD Method들을 정의해놓은 인터페이스
- Retrofit.Builder Class - Interface를 사용할 인스턴스, baseUrl과 converter 설정
Retrofit 사용 방법
Gradle 의존성 추가
Retrofit2를 사용하기 위해서 Gradle에 의존성을 추가해야 한다.
implementation 'com.squareup.retrofit2:retrofit:(insert latest version)'
implementation 'com.squareup.retrofit2:converter-gson:(insert latest version)'
인터넷 사용 권한 추가
Menifest에서 인터넷 권한을 추가해준다.
<uses-permission android:name="android.permission.INTERNET"/>
Retrofit 객체 생성
서버와 통신하기 위해서 Retrofit 객체가 필요하다
Retrofit.Build() 메서드를 통해 Retrofit 객체를 생성할 수 있다
싱글톤 패턴을 이용해서 Retrofit 객체를 생성해준다
//Kotlin
object RetrofitClient {
private val BASE_URL = "http://ec2-54-241-190-224.us-west-1.compute.amazonaws.com/"
private val retrofit = Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build()
val retrofitInterface: RetrofitInterface = retrofit.create(RetrofitInterface::class.java)
}
//Java
private RetrofitClient retrofitClient;
private RetrofitInterface retrofitInterface;
private static String BASE_URL = "http://ec2-54-241-190-224.us-west-1.compute.amazonaws.com/";
private RetrofitClient() {
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build();
retrofitInterface = retrofit.create(RetrofitInterface.class);
}
이렇게 Retrofit 객체를 만들어두고 통신이 필요한 곳마다 해당 객체를 가져와 쓰면 된다
BASE_URL에는 서버 주소를 넣으면 된다
요청 & 응답 객체 정의
서버와 통신할 때 JSON 객체를 통해 데이터를 주고 받는데,
JSON 타입의 객체를 매핑할 수 있는 DTO(POJO)를 만들어야 한다
로그인 통신을 하는 상황을 예시로 들었다
▶ 로그인 요청 JSON & DTO
로그인 요청 JSON이 아래와 같다고 가정한다
//JSON
{
"email": "test",
"password": "test"
}
//Kotlin
data class LoginRequest(
@SerializedName("email")
val inputId: String
@SerializedName("password")
val inputPw: String
)
//Java
public class LoginRequest {
@SerializedName("email")
public String inputId;
@SerializedName("password")
public String inputPw;
public String getInputId() {
return inputId;
}
public String getInputPw() {
return inputPw;
}
public void setInputId(String inputId) {
this.inputId = inputId;
}
public void setInputPw(String inputPw) {
this.inputPw = inputPw;
}
public LoginRequest(String inputId, String inputPw) {
this.inputId = inputId;
this.inputPw = inputPw;
}
}
▶ 로그인 응답 JSON & DTO
로그인 응답 JSON이 아래와 같다고 가정한다
//JSON
{
"id": "7b1ab647-8056-4d55-a267-3efe2841ad68",
"dateTime": "2023-01-01T01:01:01.000+00:00",
"message": "유저 로그인 성공",
"list": {
"accessToken": "eyJhbGciOiJIUzI1NiJ9...",
"refreshToken": "eyJhbGciOiJIUzI1NiJ9..."
}
}
//Kotlin
data class LoginResponse(
@SerializedName("id")
val id: String
@SerializedName("dateTime")
val dateTime: String
@SerializedName("status")
val status: String
@SerializedName("message")
val message: String
@SerializedName("list")
val list: TokenList
)
data class TokenList(
@SerializedName("accessToken")
val accessToken: String
@SerializedName("refreshToken")
val refreshToken: String
)
//Java
public class LoginResponse {
@SerializedName("id")
public String id;
@SerializedName("dateTime")
public String dateTime;
@SerializedName("status")
public String status;
@SerializedName("message")
public String message;
@SerializedName("list")
private TokenList list;
public class TokenList{
@SerializedName("accessToken")
public String accessToken;
@SerializedName("refreshToken")
public String refreshToken;
public String getAccessToken() {
return accessToken;
}
public void setAccessToken(String accessToken) {
this.accessToken = accessToken;
}
public String getRefreshToken() {
return refreshToken;
}
public void setRefreshToken(String refreshToken) {
this.refreshToken = refreshToken;
}
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getDateTime() {
return dateTime;
}
public void setDateTime(String dateTime) {
this.dateTime = dateTime;
}
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public TokenList getList() {
return list;
}
public void setList(TokenList list) {
this.list = list;
}
}
API Interface 정의
서버와 통신하기 위해 HTTP CRUD Method를 정의한다
DTO를 위와 같이 정의를 했다는 가정하에 인터페이스를 만들어 보겠다
//Kotlin
interface RetrofitInterface {
@POST("/api/v1/login")
fun getLogin(@Body loginRequest: LoginRequest): Call<LoginResponse>
}
//Java
public interface RetrofitInterface {
@POST("/login")
Call<LoginResponse> getLoginResponse(@Body LoginRequest loginRequest);
}
통신 처리
위 항목들을 통해 통신할 준비를 모두 끝냈으니 통신이 필요한 곳에서 통신을 하면 된다
Callback 객체를 getLoginResponse에 넣어서 응답이 날라오면 처리할 수 있게 해준다
응답 코드가 200이 아닌 400이여도 onResponse를 호출하기 때문에 if(response.isSucceddful())을 이용해서 성공 처리를 해줘야 한다
//Kotlin
RetrofitClient.retrofitInterface.getLoginResponse(loginRequest).enqueue(object : Callback<LoginResponse> {
override fun onResponse(call: Call<LoginResponse>, response: Response<LoginResponse>) {
//통신 성공
if(response.isSuccessful() && response.body() != null) {
//response.body()를 result에 저장
var result: LoginResponse = response.body()!!
//받은 코드 저장
var resultCode: Int = response.code()
var success = 200 //로그인 성공
var errorTk = 403 //토큰 유효x
var errorId = 500 //아이디, 비밀번호 일치x
if (resultCode == success) {
//성공 처리 code == 200 ex)결과 저장
var tokenList: LoginResponse.TokenList = result.getList()
var accToken: String = tokenList.getAccessToken()
var refToken: String = tokenList.getRefreshToken()
} else if (resultCode == errorId) {
//실패 처리 code == 403
} else if (resultCode == errorTk) {
//실패 처리 code == 500
} else {
//실패 처리
}
} else {
//실패 처리
}
}
override fun onFailure() {
// 실패 처리
}
}
//Java
retrofitClient = RetrofitClient.getInstance(null);
retrofitInterface = RetrofitClient.getRetrofitInterface();
retrofitInterface.getLoginResponse(loginRequest).enqueue(new Callback<LoginResponse>() {
@Override
public void onResponse(Call<LoginResponse> call, Response<LoginResponse> response) {
//통신 성공
if (response.isSuccessful() && response.body() != null) {
//response.body()를 result에 저장
LoginResponse result = response.body();
//받은 코드 저장
int resultCode = response.code();
int success = 200; //로그인 성공
int errorTk = 403; //토큰 유효x
int errorId = 500; //아이디, 비밀번호 일치x
if (resultCode == success) {
//성공 처리 code == 200 ex)결과 저장
LoginResponse.TokenList tokenList = result.getList();
String accToken = tokenList.getAccessToken();
String refToken = tokenList.getRefreshToken();
} else if (resultCode == errorId) {
//실패 처리 code == 403
} else if (resultCode == errorTk) {
//실패 처리 code == 500
} else {
//실패 처리
}
} else {
//실패 처리
}
}
//통신 실패
@Override
public void onFailure(Call<LoginResponse> call, Throwable t) {
//실패 처리
}
});
Retrofit + Kotlin Coroutines
Java를 사용할 때에는 Callback 메서드 자체가 비동기로 처리 되기 때문에 따로 비동기 처리 없이 바로 Callback 메서드를 호출 했다
물론 Kotlin에서도 똑같이 Callback 메서드 자체가 비동기로 처리 되기 때문에 Java와 똑같이 사용해도 되지만,
이번 프로젝트에서는 Coroutines를 꼭 사용해보고 싶었기 때문에 굳이굳이 비동기 프로그래밍을 사용할 것이다
Coroutines를 사용해서 네트워크 통신을 사용하면 오는 이점은 안드로이드 액티비티의 생명주기에 따라 비동기 작업을 더욱 수월하게 해 줄 수 있기 때문에 사용한다
또한 Callback 메서드와 같이 비동기 작업을 하겠다는 코드 없이도 비동기 작업을 실행할 수 있게된다
예를 들어 Coroutines를 사용하면 통신 코드는 이렇게 된다 아마도..?(공부중이라 코드가 정확하진 않다)
//Kotlin + Coroutines
lifecycleScope.launch {
val loginRequest = LoginRequest(id, password)
val response: LoginResponse = RetrofitClient.retrofitInterface.getLogin(loginRequest)
//성공 처리 code == 200 ex)결과 저장
var tokenList: LoginResponse.TokenList = response.list
var accToken: String = tokenList.accessToken
var refToken: String = tokenList.refreshToken
}
마치며
Retrofit에 대해 알아봤는데 Java로 Retrofit을 한 경험이 있다보니 나름 수월하게 작성했지만 Kotlin과 Java 문법 차이가 나를 힘들게 했다
여기에 Coroutines까지 하려고 하니 죽을 맛이다
Coroutines를 공부를 확실하게 해서 다음에 블로그에 올리든 뭐든 해보겠다 아자아자🥲
참고
https://velog.io/@sasy0113/Android-Kotlin-Retrofit2
https://codechacha.com/ko/android-coroutine-retrofit/
'Android' 카테고리의 다른 글
[Android] Retrofit Multipart로 이미지 전송하기 (0) | 2023.02.05 |
---|---|
[Android] Retrofit + Kotlin Coroutine 서버 통신하기 (0) | 2023.01.29 |
[Android] BottomNavigationView를 사용하여 탭 화면 구성을 통해 Fragment 전환하기 - 로아랑(캡스톤 디자인) 프로젝트 (0) | 2022.12.13 |
[Android] Jsoup 라이브러리를 이용하여 웹 크롤링 (Web Crawling) 해보기 (AsyncTask, RxJava) (0) | 2022.08.19 |
[Android] 안드로이드 스튜디오 (Android Studio) 다운로드 & 설치 (Mac) (0) | 2022.08.07 |