반응형
Retrofit
- 안드로이드에서 서버와 클라이언트간 http 통신을 위한 라이브러리
- Okhttp 라이브러리를 더 편히 사용하고자 만들어진 라이브러리
- 파이썬의 리스트를 편히 사용하고자 넘파이, 판다스가 나온 것과 비슷한 맥락
모듈 추가
- build.gradle (Module)
dependencies {
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
implementation("com.squareup.okhttp3:logging-interceptor:4.9.0")
}
서버와의 통신을 위한 인터넷 허용 권한 설정
- AndroidManifests.xml
<uses-permission android:name="android.permission.INTERNET" />
Retrofit 사용을 위한 기본 설정
이건 이해하는 것보다는 복사해서 쓰는 것이 편하다.
주의해야 할 부분은 Config.BASE_URL 부분만 자신의 API URL로 변경하면 된다.
보통은 보안상의 이유로(키 값, 시크릿 키 값 노출) 다른 클래스에 저장하여 비공개로 두는 경우가 많다.
API 서버를 설정하는 단계라고 보면 된다.
public class NetworkClient {
public static Retrofit retrofit;
public static Retrofit getRetrofitClient(Context context) {
if(retrofit == null) {
// TODO : 데이터 통신의 로그를 Logcat에서 확인할 수 있다.
HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor();
loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BASIC);
OkHttpClient httpClient = new OkHttpClient.Builder().connectTimeout(1, TimeUnit.MINUTES)
.readTimeout(1, TimeUnit.MINUTES)
.writeTimeout(1, TimeUnit.MINUTES)
.addInterceptor(loggingInterceptor)
.build();
retrofit = new Retrofit.Builder().baseUrl(Config.BASE_URL)
.client(httpClient)
.addConverterFactory(GsonConverterFactory.create()) // 데이터를 주고 받을 때 모델에 만든 클래스로 사용
.build();
}
return retrofit;
}
}
API 요청 방법
서버와 통신을 하려면 서버에서 요구하는 파라미터와 키 값을 맞춰주어야 한다.
아래의 예시는 내가 만든 테스트용 SNS 서버, GET 요청으로 돌아오는 키 값을 확인해보자.
- result_list
- id, imageUrl, userId, content, createAt, updatedAt, likes
예시에서 보여진 API서버에서는 GET요청을 정상적으로 응답받으려면,
인증키(Authorization)와 파라미터로 페이지의 번호를 필요로 한다.
이 두 개의 조건이 충족되어야만 GET요청에 대한 응답을 받을 수 있다.
이는 서버마다 다르며 개발시 유의해야 한다.
Retrofit을 이용한 API 요청
(예시로 보여준 API서버를 기준으로 작성하였습니다.)
API 요청 명령어
@메소드("경로")
Call<클래스> 사용 할 메소드명 (파라미터)
- 메소드 : GET / POST / PUT / DELETE
- 경로 : API 서버 URL의 하위 경로, 예시에서는 /posting
- Call<클래스> : API 요청 응답에 성공 할 경우, 전달 받을 데이터를 저장 할 클래스
- 사용 할 메소드명 : API 요청시 사용 할 메소드의 이름 정의
- 파라미터 : API 서버에서 요구하는 파라미터 정의, 예시에서는 Header(Authorization), Query(page)
- 반드시 응답받은 키 값으로 기입하여야 한다. "Authrozation", "page" 이런 식.
POSTMAN 프로그램을 통한 API 서버와의 GET 통신
GET
- 파라미터에 쿼리(@Query)를 사용, GET/DELETE는 쿼리스트링 사용
- 하위 경로 뒤에 '?키=밸류' 로 구성, 예시) /posting?page=1
- 보통은 게시판의 목록을 보여주는 기능으로 사용
- 예시 API서버에서의(내 포스팅 보기 기능) GET 메소드 필수 파라미터
- Header : 인증키(Authorization)
- Query : 페이지 번호
// 포스팅 리스트 호출 API
// GET은 Query String 사용
@GET("/posting")
Call<SnsListRes> getPostingList(@Header("Authorization") String token, @Query("page") int page);
POSTMAN 프로그램을 통한 API 서버와의 POST 통신
POST
- 파라미터에 바디(@Body)를 사용, PUT/POST는 바디를 사용
- 주의! 요청과 응답도 모두 바디로 사용하므로 혼동하면 안된다!!!
(내가 그랬음)
- 주의! 요청과 응답도 모두 바디로 사용하므로 혼동하면 안된다!!!
- 사용자가 서버에 데이터를 추가하거나 정보를 입력 할 때 사용 (회원가입, 글쓰기 등)
- 예시 API서버에서의(로그인 기능) POST 메소드 필수 파라미터
- Body : email, password
- 소스 코드에서는 User클래스의 user객체를 이용하여 email과 password의 값을 대체하였다.
- Body : email, password
// 로그인 API
// POST는 Body 사용
@POST("/users/login")
Call<UserRes> login(@Body User user);
만약 하위 경로 뒤에 추가 경로가 필요하다면?
- 파라미터에 패스(@Path) 사용
- 경로의 하위 경로 뒤에 '/{키}' 로 정의, 예시) /posting/{postingId}
- DELETE, PUT 또한 바디를 사용하기에 사용 방법은 POST 메소드와 같다.
- 예시 API서버에서의(포스팅 삭제 기능) DELETE 메소드 필수 파라미터
- Header : Authorization
- Path : postingId (특정 포스팅을 삭제해야하므로 포스팅의 id가 필요하기 때문, 포스팅 수정도 같은 설계 방식)
// 포스팅 삭제 API
// 추가 경로가 필요하면 @Path 사용
@DELETE("/posting/{postingId}")
Call<PostRes> deletePosting(@Header("Authorization") String token, @Path ("postingId") int postingId);
설정한 Retrofit을 이용하여 실제 서버와 통신 요청
- Retrofit 객체 생성
- Retrofit 생성할 객체명 = Retrofit 설정 클래스.getRetrofitClient(요청 할 액티비티 이름)
- 예) Retrofit retrofit = NetworkClient.getRetrofitClient(LoginActivity.this);
- Retrofit 생성할 객체명 = Retrofit 설정 클래스.getRetrofitClient(요청 할 액티비티 이름)
- API 객체 생성
- API 생성할 객체명 = retrofit.create(API.class)
- 예) UserApi api = retrofit.create(UserApi.class)
- API 생성할 객체명 = retrofit.create(API.class)
- 클래스의 멤버 변수를 저장할 객체 생성
- 클래스 생성할 객체명 = new 클래스(데이터1, 데이터2, 데이터3......)
- 예) User user = new User (email, password)
- 클래스 생성할 객체명 = new 클래스(데이터1, 데이터2, 데이터3......)
- API 요청 메소드 객체 생성
- Call<멤버 변수를 저장할 클래스> 생성할 객체명 = API객체명.API설정메소드(클래스 객체명)
- 예) Call<UserRes> call = api.login(user)
- Call<멤버 변수를 저장할 클래스> 생성할 객체명 = API객체명.API설정메소드(클래스 객체명)
- API 요청
- call.enqueue (new Callback<멤버 변수를 저장할 클래스>() { onResponse메소드, onFailure 메소드})
- onResponse : 요청에 성공 할 경우 / onFailure : 요청에 실패 할 경우
- call.enqueue (new Callback<멤버 변수를 저장할 클래스>() { onResponse메소드, onFailure 메소드})
// 네트워크로 데이터 전송, Retrofit 객체 생성
// NetworkClient : 위에서 Retrofit 기본 설정한 클래스 파일
// LoginActivity.this : API서버와 통신 할 액티비티 이름
Retrofit retrofit = NetworkClient.getRetrofitClient(LoginActivity.this);
// API 요청 메소드 객체 생성
// UserApi : 설정한 GET, POST 메소드를 정의해둔 클래스 파일 (회원가입, 로그인, 로그아웃)
UserApi api = retrofit.create(UserApi.class);
// POST 요청의 반환 값 저장 변수 선언
User user = new User(email, password);
// API POST 요청 설정
// UserRes : 요청 받을 데이터를 저장 할 변수들을 가지는 클래스
// api.login(user) : UserApi에서 설정한 login 메소드 사용
// user은 API 서버에서 요구하는 파라미터를 담은 변수
Call<UserRes> call = api.login(user);
// API 요청
call.enqueue(new Callback<UserRes>() {
@Override
public void onResponse(Call<UserRes> call, Response<UserRes> response) {
if(response.isSuccessful()) {
// 200 OK, 네트워크 정상 응답 할 경우, 코드 작성
}
}
@Override
public void onFailure(Call<UserRes> call, Throwable t) {
// API 응답 요청이 실패 할 경우, 코드 작성
}
});
안드로이드 스튜디오로 실제 서버와 통신을 하는 앱 개발
Retrofit 기능 설명을 하기 위한 것이므로 UI 설계는 생략합니다.
전체 소스 코드를 공유하기에는 파이썬으로 작성된 API 서버와,
자바로 작성된 안드로이드 스튜디오 프로젝트를 공유하기에는
설명이 너무 길어질 것 같아서 위에서 예시로 보여준 기능만 설명합니다.
내 포스팅 목록 보기 (GET)
// 포스팅 목록 호출
void getNetworkData() {
// 데이터 초기화
snsList.clear();
count = 0;
page = 1;
// 네트워크로 데이터 전송, Retrofit 객체 생성
Retrofit retrofit = NetworkClient.getRetrofitClient(MainActivity.this);
SnsApi api = retrofit.create(SnsApi.class);
// API 요청
// 헤더에 설정 할 데이터 확인, 변수로 저장되어있는 토큰 호출
Call<SnsListRes> call = api.getPostingList("Bearer "+accessToken, page);
call.enqueue(new Callback<SnsListRes>() {
@Override
public void onResponse(Call<SnsListRes> call, Response<SnsListRes> response) {
// 200 OK, 네트워크 정상 응답
// 응답 결과를 data에 저장
if(response.isSuccessful()) {
SnsListRes data = response.body();
// 응답은 되었으나 데이터가 존재하지 않을 경우
if (data.getResult_list() == null) {
Toast.makeText(getApplicationContext(), "포스팅이 존재하지 않습니다.", Toast.LENGTH_SHORT).show();
return;
}
// 기존의 데이터에서 추가
snsList.addAll(data.getResult_list());
// 응답받은 데이터 리사이클러뷰 어댑터에 리스트 목록 출력
// Retrofit 설명 포스팅이기 때문에 어댑터 설정은 생략
adapter = new SnsAdapter(MainActivity.this, snsList);
recyclerView.setAdapter(adapter);
}
}
@Override
public void onFailure(Call<SnsListRes> call, Throwable t) {
// API 응답에 실패 할 경우 코드 작성
}
});
}
로그인하기 (POST)
// 네트워크로 데이터 전송, Retrofit 객체 생성
Retrofit retrofit = NetworkClient.getRetrofitClient(LoginActivity.this);
UserApi api = retrofit.create(UserApi.class);
// POST URL의 반환 값 저장 변수 선언
User user = new User(email, password);
// API POST 요청
Call<UserRes> call = api.login(user);
call.enqueue(new Callback<UserRes>() {
@Override
public void onResponse(Call<UserRes> call, Response<UserRes> response) {
// 200 OK, 네트워크 정상 응답
if(response.isSuccessful()) {
// 응답 결과를 userRes에 저장
UserRes usersRes = response.body();
// API 호출시 AccessToken 헤더에 추가
// 반환받은 AccessToken은 SharedPreferences에 저장
SharedPreferences sp = getApplication()
.getSharedPreferences(Config.PREFERENCES_NAME, MODE_PRIVATE);
SharedPreferences.Editor editor = sp.edit();
editor.putString("accessToken", usersRes.getAccess_token());
editor.apply();
Toast.makeText(getApplicationContext(), "로그인되었습니다.", Toast.LENGTH_SHORT).show();
// MainActivity 이동, 해당 액티비티는 종료
Intent intent = new Intent(LoginActivity.this, MainActivity.class);
startActivity(intent);
finish();
}
}
@Override
public void onFailure(Call<UserRes> call, Throwable t) {
// API 응답에 실패 할 경우 코드 작성
}
});
UserApi.java / SnsApi.java
public interface UserApi {
// 로그인 API
@POST("/users/login")
Call<UserRes> login(@Body User user);
}
public interface SnsApi {
// 포스팅 리스트 호출 API
// GET은 Query String 사용
@GET("/posting")
Call<SnsListRes> getPostingList(@Header("Authorization") String token, @Query("page") int page);
}
User.java / UserRes.java / SnsListRes.java
// User.java
import java.io.Serializable;
public class User implements Serializable {
private String email;
private String password;
public User(String email, String password) {
this.email = email;
this.password = password;
}
public User() {
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
// UserRes.java
import java.io.Serializable;
public class UserRes implements Serializable {
private String result;
private String accessToken;
public String getResult() {
return result;
}
public void setResult(String result) {
this.result = result;
}
public String getAccess_token() {
return accessToken;
}
public void accessToken(String accessToken) {
this.accessToken = accessToken;
}
}
// SnsListRes.java
import java.io.Serializable;
public class SnsListRes implements Serializable{
private List<Sns> result_list;
public SnsListRes(List<Sns> result_list) {
this.result_list = result_list;
}
public List<Sns> getResult_list() {
return result_list;
}
public void setResult_list(List<Sns> result_list) {
this.result_list = result_list;
}
}
실제 모바일 화면
- 로그인하기
- 로그인 성공 모습 + 나의 포스팅 목록 보기
- 로그인 후에 나의 포스팅 목록을 바로 보여지게 설계하였음
반응형