Ver2.0 안드로이드 Retrofit으로 REST API 통신 마스터하기: 초보자도 쉽게 따라하는 완벽 가이드 🚀

안드로이드 Retrofit으로 REST API 통신 마스터하기: 초보자도 쉽게 따라하는 완벽 가이드 🚀
안녕하세요, 개발자 여러분! 😊 오늘은 안드로이드 앱 개발에서 빠질 수 없는 REST API 통신의 강자, Retrofit에 대해 함께 알아볼게요. 서버와 통신하는 코드 때문에 머리 아프셨죠? 이제 그런 고민은 bye-bye~ 쉽고 재밌게 Retrofit 마스터가 되어봐요! ㅋㅋㅋ
📚 목차
- Retrofit이 뭐길래? 기본 개념 이해하기
- 개발 환경 세팅하기 (의존성 추가)
- Retrofit 인터페이스 설계하기
- Retrofit 인스턴스 생성 및 설정하기
- API 호출 및 응답 처리하기
- 실전 예제: 영화 정보 앱 만들기
- 에러 핸들링 및 디버깅 팁
- 고급 기능 활용하기
- 성능 최적화 팁
- 마무리 및 추가 자료
1. Retrofit이 뭐길래? 기본 개념 이해하기 🤔
여러분~ 요즘 앱 개발하면 거의 필수로 서버랑 통신하잖아요? 인스타그램이나 유튜브 같은 앱들 생각해보세요. 모든 데이터가 서버에서 오는 거죠! 그럼 이 통신을 어떻게 할까요? 여기서 등장하는 게 바로 Retrofit이에요! 😎
Retrofit이란?
Square사에서 개발한 안드로이드(+자바)용 REST API 클라이언트 라이브러리로, HTTP API를 자바 인터페이스로 변환해주는 초강력 라이브러리예요. 쉽게 말하면 서버와 통신을 쉽게 할 수 있도록 도와주는 도구라고 생각하면 됩니다!
진짜 대박인 점은 뭐냐면요, 복잡한 HTTP 요청 코드를 직접 짤 필요 없이 인터페이스만 정의하면 Retrofit이 알아서 다 해준다는 거예요! 어때요? 개이득 아닌가요? ㅋㅋㅋ
Retrofit의 주요 장점들을 살펴볼까요? 🧐
- 간결한 코드 - 복잡한 네트워크 코드를 인터페이스로 간단하게 표현 가능
- 유연성 - 다양한 HTTP 메소드(GET, POST, PUT, DELETE 등) 지원
- 확장성 - 커스텀 타입 컨버터 추가 가능
- 성능 - 내부적으로 OkHttp를 사용해 빠른 통신 속도 제공
- 유지보수 - API 변경 시 인터페이스만 수정하면 됨
재능넷에서 개발자 재능을 공유하는 분들도 Retrofit을 많이 활용하시더라구요! 특히 프리랜서 개발자분들이 클라이언트 프로젝트에서 빠르게 API 연동할 때 필수 도구로 꼽히는 것 같아요. 😉
2. 개발 환경 세팅하기 (의존성 추가) 🛠️
자, 이제 본격적으로 Retrofit을 사용해볼까요? 먼저 개발 환경부터 세팅해봐요!
1. Gradle 의존성 추가하기
app 수준의 build.gradle 파일에 다음 의존성을 추가해주세요:
dependencies {
// Retrofit
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
// Gson 컨버터 (JSON 파싱용)
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
// OkHttp 로깅 인터셉터 (디버깅용, 선택사항)
implementation 'com.squareup.okhttp3:logging-interceptor:4.9.1'
}
Gson 컨버터는 JSON 응답을 자바 객체로 자동 변환해주는 역할을 해요. 진짜 편해요! 일일이 JSON 파싱하는 코드 안 써도 되니까 개발자 인생이 달라져요 ㅋㅋㅋ
2. 인터넷 권한 추가하기
AndroidManifest.xml 파일에 인터넷 권한을 추가해주세요:
<manifest ...>
<uses-permission android:name="android.permission.INTERNET" />
...
</manifest>
인터넷 권한 빼먹으면 앱이 서버랑 통신을 못 해서 멘붕 올 수 있으니 꼭 추가해주세요! 😱 처음 하시는 분들이 자주 빼먹는 부분이에요~
💡 프로 팁!
만약 HTTP(암호화되지 않은) URL에 접근해야 한다면, Android 9(Pie) 이상에서는 추가 설정이 필요해요:
<application
...
android:usesCleartextTraffic="true"
... >
하지만 보안을 위해 가능하면 HTTPS를 사용하는 것이 좋아요!
여기까지 완료했다면 Retrofit을 사용할 준비가 끝났어요! 다음으로 넘어가볼까요? 🚀
3. Retrofit 인터페이스 설계하기 📝
Retrofit의 진짜 매력은 인터페이스로 API를 정의한다는 점이에요! 복잡한 HTTP 요청 코드를 직접 작성할 필요 없이, 인터페이스만 만들면 Retrofit이 알아서 구현체를 만들어준다니... 개발자 천국 아닌가요? ㅋㅋㅋ
API 인터페이스 만들기
예를 들어, 영화 정보를 가져오는 API를 사용한다고 가정해볼게요:
public interface MovieApiService {
@GET("movies")
Call<List<Movie>> getMovies();
@GET("movies/{id}")
Call<Movie> getMovie(@Path("id") int movieId);
@POST("movies")
Call<Movie> createMovie(@Body Movie movie);
@PUT("movies/{id}")
Call<Movie> updateMovie(@Path("id") int movieId, @Body Movie movie);
@DELETE("movies/{id}")
Call<Void> deleteMovie(@Path("id") int movieId);
}
위 코드에서 @GET, @POST, @PUT, @DELETE 같은 애노테이션은 HTTP 메소드를 나타내요. 그리고 괄호 안의 문자열은 API의 엔드포인트(상대 URL)를 의미해요.
Retrofit에서 자주 사용하는 애노테이션들을 알아볼까요? 🧐
@Path
URL의 동적 부분을 대체해요. 예: movies/{id}에서 {id} 부분
@Query
URL 쿼리 파라미터를 추가해요. 예: ?page=1&sort=name
@Body
요청 본문에 객체를 포함시켜요. 주로 POST, PUT에서 사용
@FormUrlEncoded
폼 데이터 전송 시 사용해요. @Field와 함께 씀
@Multipart
파일 업로드 등 멀티파트 요청 시 사용해요. @Part와 함께 씀
@Headers
HTTP 헤더를 추가할 때 사용해요
이제 API 응답을 받을 데이터 모델(POJO)도 만들어볼게요:
public class Movie {
private int id;
private String title;
private String director;
private int year;
private float rating;
// 생성자, getter, setter 생략...
}
Gson이 JSON 응답을 이 클래스의 객체로 자동 변환해줄 거예요. 필드 이름이 JSON의 키와 일치해야 한다는 점 기억하세요! 만약 다르다면 @SerializedName 애노테이션을 사용하면 돼요.
예시: 필드 이름이 JSON 키와 다른 경우
public class Movie {
private int id;
@SerializedName("movie_title")
private String title;
@SerializedName("movie_director")
private String director;
// 나머지 생략...
}
이렇게 하면 JSON의 "movie_title" 키가 Java 객체의 "title" 필드로 매핑돼요! 진짜 편하죠? 😎
4. Retrofit 인스턴스 생성 및 설정하기 ⚙️
인터페이스를 만들었으니, 이제 실제로 Retrofit 인스턴스를 생성해볼게요! 이 인스턴스가 우리가 정의한 인터페이스를 구현해서 실제 네트워크 요청을 처리할 거예요.
기본 Retrofit 인스턴스 생성
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.example.com/")
.addConverterFactory(GsonConverterFactory.create())
.build();
MovieApiService apiService = retrofit.create(MovieApiService.class);
위 코드에서 baseUrl()은 모든 API 요청의 기본 URL을 설정해요. 그리고 addConverterFactory()는 JSON 응답을 Java 객체로 변환하는 컨버터를 추가하는 부분이에요.
마지막으로 retrofit.create() 메소드가 우리가 정의한 인터페이스의 구현체를 동적으로 생성해줘요. 이게 바로 Retrofit의 마법이죠! ✨
🔍 고급 설정: OkHttp 클라이언트 커스터마이징
더 세밀한 제어가 필요하다면 OkHttp 클라이언트를 커스터마이징할 수 있어요:
// 로깅 인터셉터 생성
HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor();
loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
// OkHttp 클라이언트 생성
OkHttpClient client = new OkHttpClient.Builder()
.addInterceptor(loggingInterceptor) // 로깅 추가
.connectTimeout(30, TimeUnit.SECONDS) // 연결 타임아웃
.readTimeout(30, TimeUnit.SECONDS) // 읽기 타임아웃
.writeTimeout(30, TimeUnit.SECONDS) // 쓰기 타임아웃
.build();
// Retrofit 인스턴스에 OkHttp 클라이언트 설정
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.example.com/")
.client(client) // 커스텀 클라이언트 설정
.addConverterFactory(GsonConverterFactory.create())
.build();
로깅 인터셉터는 개발 중에 네트워크 요청과 응답을 로그로 확인할 수 있게 해줘서 디버깅에 정말 유용해요! 근데 릴리즈 버전에선 빼는 게 좋아요. 성능이랑 보안 때문이죠! 😉
위 다이어그램을 보면 Retrofit이 어떻게 동작하는지 한눈에 이해할 수 있죠? 앱에서는 API 인터페이스만 호출하면, Retrofit이 내부적으로 OkHttp를 통해 실제 네트워크 통신을 처리해요. 정말 깔끔한 구조죠? 👍
💡 싱글톤 패턴 활용하기
Retrofit 인스턴스는 앱 전체에서 하나만 있으면 충분해요. 싱글톤 패턴을 사용하면 메모리도 절약하고 코드도 깔끔해져요:
public class RetrofitClient {
private static Retrofit retrofit = null;
public static Retrofit getClient(String baseUrl) {
if (retrofit == null) {
retrofit = new Retrofit.Builder()
.baseUrl(baseUrl)
.addConverterFactory(GsonConverterFactory.create())
.build();
}
return retrofit;
}
}
이렇게 하면 어디서든
RetrofitClient.getClient("https://api.example.com/")로 동일한 인스턴스에 접근할 수 있어요!
5. API 호출 및 응답 처리하기 📞
자, 이제 Retrofit으로 실제 API를 호출해볼 차례예요! Retrofit에서는 API 호출을 동기식과 비동기식 두 가지 방법으로 할 수 있어요.
비동기 API 호출 (권장)
대부분의 경우 UI 스레드를 블로킹하지 않기 위해 비동기 호출을 사용해요:
apiService.getMovies().enqueue(new Callback<List<Movie>>() {
@Override
public void onResponse(Call<List<Movie>> call, Response<List<Movie>> response) {
if (response.isSuccessful()) {
List<Movie> movies = response.body();
// 성공적으로 데이터를 받았을 때의 처리
updateUI(movies);
} else {
// 서버 오류 처리 (4xx, 5xx 응답)
handleErrorResponse(response.code());
}
}
@Override
public void onFailure(Call<List<Movie>> call, Throwable t) {
// 네트워크 오류 처리
handleNetworkError(t);
}
});
enqueue() 메소드는 백그라운드 스레드에서 API 호출을 실행하고, 응답이 오면 콜백을 통해 결과를 전달해줘요. UI 스레드를 블로킹하지 않아서 앱이 멈추지 않죠! 👍
동기 API 호출 (주의 필요)
백그라운드 스레드에서만 사용해야 해요! UI 스레드에서 호출하면 앱이 멈춰요!
// 반드시 백그라운드 스레드에서 실행해야 함!
new Thread(new Runnable() {
@Override
public void run() {
try {
Response<List<Movie>> response = apiService.getMovies().execute();
if (response.isSuccessful()) {
final List<Movie> movies = response.body();
// UI 업데이트는 메인 스레드에서 해야 함
runOnUiThread(new Runnable() {
@Override
public void run() {
updateUI(movies);
}
});
}
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
동기 호출은 UI 스레드에서 절대 사용하면 안 돼요! ANR(Application Not Responding) 오류가 발생할 수 있어요. 꼭 백그라운드 스레드에서만 사용하세요! ⚠️
🌟 모던 안드로이드 접근법: Coroutines + Retrofit
코틀린 코루틴을 사용하면 비동기 코드를 더 간결하게 작성할 수 있어요:
// API 인터페이스 (코틀린)
interface MovieApiService {
@GET("movies")
suspend fun getMovies(): List<Movie>
}
// API 호출 (코틀린 + 코루틴)
lifecycleScope.launch {
try {
val movies = apiService.getMovies()
// 성공 처리
updateUI(movies)
} catch (e: Exception) {
// 오류 처리
handleError(e)
}
}
코루틴을 사용하면 콜백 지옥에서 벗어나 동기 코드처럼 깔끔하게 작성할 수 있어요! 🚀
이제 응답을 처리하는 방법에 대해 좀 더 자세히 알아볼까요?
응답 처리하기
Response 객체에서 유용한 메소드들:
isSuccessful()- HTTP 상태 코드가 200-299 범위인지 확인body()- 응답 본문을 변환된 객체로 반환errorBody()- 오류 응답 본문 (4xx, 5xx 응답)code()- HTTP 상태 코드headers()- 응답 헤더
오류 응답 처리 예시:
if (!response.isSuccessful()) {
// 오류 응답 처리
int statusCode = response.code();
if (statusCode == 404) {
// 리소스를 찾을 수 없음
showNotFoundError();
} else if (statusCode == 401) {
// 인증 오류
showAuthError();
} else if (statusCode >= 500) {
// 서버 오류
showServerError();
}
// 오류 응답 본문 읽기
try {
String errorBody = response.errorBody().string();
Log.e("API_ERROR", errorBody);
// 오류 메시지 파싱 및 표시
} catch (IOException e) {
e.printStackTrace();
}
}
응답 처리를 잘하면 사용자에게 더 나은 경험을 제공할 수 있어요. 단순히 "오류가 발생했습니다"보다는 "영화를 찾을 수 없습니다" 같은 구체적인 메시지가 훨씬 좋겠죠? 😉
6. 실전 예제: 영화 정보 앱 만들기 🎬
자, 이제 배운 내용을 종합해서 간단한 영화 정보 앱을 만들어볼게요! 영화 목록을 가져와서 RecyclerView에 표시하는 예제예요.
Step 1: 데이터 모델 클래스 만들기
public class Movie {
private int id;
private String title;
private String posterPath;
private String overview;
private float rating;
// 생성자, getter, setter 생략...
// 포스터 전체 URL 반환 메소드
public String getPosterUrl() {
return "https://image.tmdb.org/t/p/w500" + posterPath;
}
}
Step 2: API 인터페이스 정의하기
public interface MovieApiService {
@GET("movie/popular")
Call<MovieResponse> getPopularMovies(@Query("api_key") String apiKey);
}
// API 응답을 위한 래퍼 클래스
public class MovieResponse {
@SerializedName("results")
private List<Movie> movies;
public List<Movie> getMovies() {
return movies;
}
}
Step 3: Retrofit 인스턴스 생성하기
public class RetrofitClient {
private static final String BASE_URL = "https://api.themoviedb.org/3/";
private static Retrofit retrofit = null;
public static Retrofit getClient() {
if (retrofit == null) {
retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build();
}
return retrofit;
}
}
Step 4: RecyclerView 어댑터 만들기
public class MovieAdapter extends RecyclerView.Adapter<MovieAdapter.MovieViewHolder> {
private List<Movie> movies;
private Context context;
// 생성자, ViewHolder 클래스 등 생략...
@Override
public void onBindViewHolder(@NonNull MovieViewHolder holder, int position) {
Movie movie = movies.get(position);
holder.titleTextView.setText(movie.getTitle());
holder.ratingTextView.setText(String.valueOf(movie.getRating()));
// 이미지 로딩 라이브러리(Glide, Picasso 등) 사용
Glide.with(context)
.load(movie.getPosterUrl())
.into(holder.posterImageView);
}
// 나머지 메소드 생략...
}
Step 5: MainActivity에서 API 호출하기
public class MainActivity extends AppCompatActivity {
private RecyclerView recyclerView;
private MovieAdapter adapter;
private List<Movie> movieList;
// API 키 (실제로는 보안을 위해 gradle.properties 등에 저장하는 것이 좋음)
private static final String API_KEY = "your_api_key_here";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
recyclerView = findViewById(R.id.recyclerView);
movieList = new ArrayList<>();
adapter = new MovieAdapter(this, movieList);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
recyclerView.setAdapter(adapter);
// API 호출
loadMovies();
}
private void loadMovies() {
MovieApiService apiService = RetrofitClient.getClient().create(MovieApiService.class);
apiService.getPopularMovies(API_KEY).enqueue(new Callback<MovieResponse>() {
@Override
public void onResponse(Call<MovieResponse> call, Response<MovieResponse> response) {
if (response.isSuccessful() && response.body() != null) {
movieList.clear();
movieList.addAll(response.body().getMovies());
adapter.notifyDataSetChanged();
} else {
showError("데이터를 불러오는데 실패했습니다.");
}
}
@Override
public void onFailure(Call<MovieResponse> call, Throwable t) {
showError("네트워크 오류가 발생했습니다: " + t.getMessage());
}
});
}
private void showError(String message) {
Toast.makeText(this, message, Toast.LENGTH_SHORT).show();
}
}
이렇게 하면 인기 영화 목록을 가져와서 RecyclerView에 표시하는 간단한 앱이 완성돼요! 🎉
재능넷에서도 이런 식으로 API를 활용한 앱 개발 재능이 인기가 많더라구요. 특히 영화, 음악, 맛집 정보 등 다양한 API를 활용한 앱 개발 재능이 많이 거래되고 있어요! 😊
💡 프로 팁!
실제 앱에서는 MVVM 아키텍처와 Repository 패턴을 사용하는 것이 좋아요. ViewModel에서 API 호출을 관리하고, LiveData나 Flow를 사용해 데이터 변경을 관찰하면 코드가 더 깔끔해져요!
7. 에러 핸들링 및 디버깅 팁 🐞
네트워크 통신에서 에러 처리는 정말 중요해요! 인터넷 연결이 끊기거나, 서버가 다운되거나, API 응답이 예상과 다를 수 있으니까요. 잘 처리하지 않으면 앱이 뻗어버릴 수도 있어요! 😱
주요 에러 유형
- 네트워크 에러 - 인터넷 연결 문제, 타임아웃 등
- HTTP 에러 - 4xx, 5xx 상태 코드
- 파싱 에러 - JSON 형식이 예상과 다른 경우
- 인증 에러 - API 키 만료, 권한 부족 등
각 에러 유형별로 처리 방법을 알아볼게요!
1. 네트워크 에러 처리
@Override
public void onFailure(Call<MovieResponse> call, Throwable t) {
// 구체적인 에러 타입 확인
if (t instanceof UnknownHostException) {
// 인터넷 연결 문제
showError("인터넷 연결을 확인해주세요.");
} else if (t instanceof SocketTimeoutException) {
// 타임아웃
showError("서버 응답이 너무 늦어요. 나중에 다시 시도해주세요.");
} else if (t instanceof JsonSyntaxException) {
// JSON 파싱 오류
showError("데이터 형식에 문제가 있어요.");
} else {
// 기타 오류
showError("오류가 발생했어요: " + t.getMessage());
}
// 로그 기록
Log.e("API_ERROR", "API 호출 실패", t);
}
2. HTTP 에러 처리
@Override
public void onResponse(Call<MovieResponse> call, Response<MovieResponse> response) {
if (response.isSuccessful()) {
// 성공 처리
handleSuccessResponse(response.body());
} else {
// HTTP 에러 처리
switch (response.code()) {
case 400:
showError("잘못된 요청이에요.");
break;
case 401:
showError("인증에 실패했어요. 다시 로그인해주세요.");
refreshToken(); // 토큰 갱신 시도
break;
case 403:
showError("접근 권한이 없어요.");
break;
case 404:
showError("요청한 정보를 찾을 수 없어요.");
break;
case 500:
case 502:
case 503:
case 504:
showError("서버에 문제가 있어요. 잠시 후 다시 시도해주세요.");
break;
default:
showError("오류가 발생했어요: " + response.code());
break;
}
// 오류 응답 본문 로깅
try {
if (response.errorBody() != null) {
Log.e("API_ERROR", "Error body: " + response.errorBody().string());
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
3. 재시도 메커니즘 구현
// OkHttp 인터셉터를 사용한 재시도 메커니즘
OkHttpClient client = new OkHttpClient.Builder()
.addInterceptor(new Interceptor() {
@Override
public okhttp3.Response intercept(Chain chain) throws IOException {
Request request = chain.request();
okhttp3.Response response = chain.proceed(request);
int retryCount = 0;
while (!response.isSuccessful() && retryCount < 3) {
retryCount++;
Log.d("RETRY", "Retry #" + retryCount);
// 이전 응답 닫기
response.close();
// 잠시 대기 (지수 백오프)
try {
Thread.sleep(1000 * retryCount);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 재시도
response = chain.proceed(request);
}
return response;
}
})
.build();
디버깅 팁 🔍
- 로깅 인터셉터 사용하기 - 요청과 응답을 로그로 확인
- Postman으로 API 테스트 - API가 제대로 동작하는지 먼저 확인
- Charles/Fiddler로 트래픽 모니터링 - 앱과 서버 간 통신 분석
- 네트워크 프로파일러 사용 - Android Studio의 네트워크 프로파일러로 통신 모니터링
로깅 인터셉터 설정 예시:
HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
// 개발 중에는 BODY 레벨로 설정 (모든 요청/응답 내용 로깅)
logging.setLevel(HttpLoggingInterceptor.Level.BODY);
// 배포 버전에서는 NONE으로 설정하거나 아예 제거
// logging.setLevel(HttpLoggingInterceptor.Level.NONE);
OkHttpClient client = new OkHttpClient.Builder()
.addInterceptor(logging)
.build();
에러 처리를 잘하면 사용자 경험이 크게 향상돼요! "알 수 없는 오류가 발생했습니다"보다는 "인터넷 연결을 확인해주세요"라고 하는 게 사용자에게 훨씬 도움이 되겠죠? 😉
8. 고급 기능 활용하기 🚀
기본적인 API 통신은 이제 마스터했으니, 더 고급 기능들을 알아볼까요? 이 기능들을 활용하면 더 강력하고 유연한 네트워크 통신을 구현할 수 있어요!
1. 동적 URL 및 헤더 설정
public interface ApiService {
@GET
Call<ResponseData> getDynamicUrl(@Url String fullUrl);
@GET("users/{user}/repos")
Call<List<Repo>> listRepos(
@Path("user") String user,
@Query("sort") String sort,
@Query("page") int page
);
@GET("protected-resource")
Call<SecretData> getProtectedResource(
@Header("Authorization") String authToken
);
// 여러 헤더를 한번에 설정
@Headers({
"Accept: application/vnd.github.v3.full+json",
"User-Agent: My-App"
})
@GET("users/{username}")
Call<User> getUser(@Path("username") String username);
// 동적 헤더 맵 사용
@GET("some-resource")
Call<Data> getData(@HeaderMap Map<String, String> headers);
}
2. 폼 데이터 및 멀티파트 요청
// 폼 데이터 전송
@FormUrlEncoded
@POST("user/login")
Call<LoginResponse> login(
@Field("email") String email,
@Field("password") String password
);
// 멀티파트 요청 (파일 업로드)
@Multipart
@POST("user/profile")
Call<ProfileResponse> updateProfile(
@Part("name") RequestBody name,
@Part MultipartBody.Part photo
);
// 파일 업로드 구현 예시
File file = new File(imagePath);
RequestBody requestFile = RequestBody.create(MediaType.parse("image/*"), file);
MultipartBody.Part photoPart = MultipartBody.Part.createFormData("photo", file.getName(), requestFile);
RequestBody nameBody = RequestBody.create(MediaType.parse("text/plain"), userName);
apiService.updateProfile(nameBody, photoPart).enqueue(new Callback<ProfileResponse>() {
// 콜백 구현...
});
3. 인증 인터셉터 구현
// 모든 요청에 인증 토큰을 자동으로 추가하는 인터셉터
public class AuthInterceptor implements Interceptor {
private SessionManager sessionManager;
public AuthInterceptor(SessionManager sessionManager) {
this.sessionManager = sessionManager;
}
@Override
public okhttp3.Response intercept(Chain chain) throws IOException {
Request originalRequest = chain.request();
// 인증이 필요한 요청인지 확인 (예: 특정 URL 패턴)
if (needsAuth(originalRequest)) {
String token = sessionManager.getAuthToken();
if (token == null) {
// 토큰이 없으면 로그인 화면으로 리다이렉트 등의 처리
throw new IOException("Authentication required");
}
// 토큰을 헤더에 추가한 새 요청 생성
Request newRequest = originalRequest.newBuilder()
.header("Authorization", "Bearer " + token)
.build();
return chain.proceed(newRequest);
}
return chain.proceed(originalRequest);
}
private boolean needsAuth(Request request) {
// 인증이 필요한 URL 패턴 확인 로직
String url = request.url().toString();
return !url.contains("/login") && !url.contains("/register");
}
}
// OkHttpClient에 인터셉터 추가
OkHttpClient client = new OkHttpClient.Builder()
.addInterceptor(new AuthInterceptor(sessionManager))
.build();
4. 커스텀 타입 컨버터
// 예: Date 객체를 특정 형식으로 변환하는 컨버터
public class DateConverter implements Converter<Date, String> {
private final SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.US);
@Override
public String convert(Date date) throws IOException {
return format.format(date);
}
}
// 커스텀 컨버터 팩토리 구현
public class DateConverterFactory extends Converter.Factory {
@Override
public Converter<?, RequestBody> requestBodyConverter(Type type,
Annotation[] annotations,
Retrofit retrofit) {
if (type == Date.class) {
return new Converter<Date, RequestBody>() {
@Override
public RequestBody convert(Date date) throws IOException {
DateConverter dateConverter = new DateConverter();
String dateString = dateConverter.convert(date);
return RequestBody.create(MediaType.parse("text/plain"), dateString);
}
};
}
return null;
}
}
// Retrofit에 커스텀 컨버터 추가
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(new DateConverterFactory())
.addConverterFactory(GsonConverterFactory.create())
.build();
5. 요청 취소하기
// Call 객체 저장
private Call<MovieResponse> call;
// API 호출
private void loadMovies() {
// 이전 요청이 있으면 취소
if (call != null) {
call.cancel();
}
// 새 요청 생성 및 실행
call = apiService.getPopularMovies(API_KEY);
call.enqueue(new Callback<MovieResponse>() {
// 콜백 구현...
});
}
// 액티비티 종료 시 요청 취소
@Override
protected void onDestroy() {
super.onDestroy();
if (call != null) {
call.cancel();
}
}
이런 고급 기능들을 활용하면 더 복잡한 API 통신도 쉽게 구현할 수 있어요! 특히 인증 인터셉터는 토큰 기반 인증을 사용하는 앱에서 정말 유용해요. 매번 토큰을 헤더에 추가하는 코드를 작성할 필요가 없으니까요! 👍
재능넷에서도 이런 고급 기능을 활용한 앱 개발 재능이 인기가 많아요. 특히 파일 업로드나 인증 기능이 필요한 앱 개발 의뢰가 많이 올라오더라구요! 😊
9. 성능 최적화 팁 ⚡
API 통신은 앱 성능에 큰 영향을 미칠 수 있어요. 특히 모바일 환경에서는 네트워크 상태가 불안정하거나 배터리 소모가 중요한 이슈가 될 수 있죠. 여기 몇 가지 성능 최적화 팁을 알려드릴게요!
1. 캐싱 구현하기
// OkHttp 캐시 설정
File cacheDir = new File(context.getCacheDir(), "http-cache");
int cacheSize = 10 * 1024 * 1024; // 10 MB
Cache cache = new Cache(cacheDir, cacheSize);
OkHttpClient client = new OkHttpClient.Builder()
.cache(cache)
.addNetworkInterceptor(new Interceptor() {
@Override
public okhttp3.Response intercept(Chain chain) throws IOException {
okhttp3.Response originalResponse = chain.proceed(chain.request());
// 캐시 제어 헤더 추가
return originalResponse.newBuilder()
.header("Cache-Control", "public, max-age=60") // 60초 동안 캐시 유효
.build();
}
})
.build();
오프라인 캐싱을 위한 인터셉터 추가:
// 오프라인 캐싱을 위한 인터셉터
client = new OkHttpClient.Builder()
.cache(cache)
.addInterceptor(new Interceptor() {
@Override
public okhttp3.Response intercept(Chain chain) throws IOException {
Request request = chain.request();
// 네트워크 연결 확인
if (!isNetworkAvailable()) {
// 오프라인 모드: 캐시된 데이터만 사용
request = request.newBuilder()
.header("Cache-Control", "public, only-if-cached, max-stale=" + 60 * 60 * 24 * 7) // 1주일
.build();
}
return chain.proceed(request);
}
})
.build();
2. 요청 최소화 및 배치 처리
여러 개의 작은 요청보다 하나의 큰 요청이 효율적일 수 있어요:
// 여러 ID의 데이터를 한 번에 요청
@GET("movies")
Call<List<Movie>> getMoviesByIds(@Query("ids") String commaSeparatedIds);
// 사용 예시
String ids = TextUtils.join(",", movieIds);
apiService.getMoviesByIds(ids).enqueue(...);
또는 배치 API 엔드포인트 사용:
@POST("batch")
Call<BatchResponse> executeBatch(@Body BatchRequest batchRequest);
3. 응답 압축 활용
// Gzip 압축 요청
OkHttpClient client = new OkHttpClient.Builder()
.addInterceptor(new Interceptor() {
@Override
public okhttp3.Response intercept(Chain chain) throws IOException {
Request request = chain.request().newBuilder()
.header("Accept-Encoding", "gzip")
.build();
return chain.proceed(request);
}
})
.build();
대부분의 서버는 자동으로 Gzip 압축을 지원하지만, 명시적으로 요청하면 확실히 압축된 응답을 받을 수 있어요.
4. 필요한 데이터만 요청하기
API가 지원한다면, 필요한 필드만 요청하는 것이 좋아요:
@GET("movies/{id}")
Call<Movie> getMovie(
@Path("id") int movieId,
@Query("fields") String fields
);
// 사용 예시: 제목과 평점만 요청
apiService.getMovie(123, "title,rating").enqueue(...);
5. 페이지네이션 구현
@GET("movies")
Call<PagedResponse<Movie>> getMovies(
@Query("page") int page,
@Query("per_page") int itemsPerPage
);
// 페이지네이션 응답 모델
public class PagedResponse<T> {
private List<T> items;
private int totalItems;
private int totalPages;
private int currentPage;
// getter, setter 생략...
}
RecyclerView와 함께 사용하면 스크롤할 때 다음 페이지를 자동으로 로드할 수 있어요:
recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
LinearLayoutManager layoutManager = (LinearLayoutManager) recyclerView.getLayoutManager();
int visibleItemCount = layoutManager.getChildCount();
int totalItemCount = layoutManager.getItemCount();
int firstVisibleItemPosition = layoutManager.findFirstVisibleItemPosition();
// 마지막 아이템에 가까워지면 다음 페이지 로드
if (!isLoading && !isLastPage) {
if ((visibleItemCount + firstVisibleItemPosition) >= totalItemCount
&& firstVisibleItemPosition >= 0) {
loadNextPage();
}
}
}
});
6. 연결 풀링 및 타임아웃 설정
OkHttpClient client = new OkHttpClient.Builder()
.connectTimeout(15, TimeUnit.SECONDS) // 연결 타임아웃
.readTimeout(30, TimeUnit.SECONDS) // 읽기 타임아웃
.writeTimeout(30, TimeUnit.SECONDS) // 쓰기 타임아웃
.connectionPool(new ConnectionPool(5, 30, TimeUnit.SECONDS)) // 연결 풀 설정
.build();
연결 풀링을 사용하면 동일한 서버에 대한 여러 요청이 기존 연결을 재사용할 수 있어 성능이 향상돼요.
이런 최적화 기법들을 적용하면 앱의 네트워크 성능이 크게 향상될 수 있어요! 특히 캐싱은 데이터 사용량을 줄이고 오프라인 사용성을 높이는 데 큰 도움이 돼요. 사용자들이 "이 앱 진짜 빠르다!"라고 느끼게 만들 수 있죠! 😎
10. 마무리 및 추가 자료 📚
자, 여기까지 안드로이드에서 Retrofit을 사용한 REST API 통신에 대해 알아봤어요! 이제 여러분은 Retrofit의 기본부터 고급 기능까지 마스터했네요! 🎉
배운 내용 정리
- Retrofit의 기본 개념과 장점
- 개발 환경 설정 및 의존성 추가
- API 인터페이스 설계 방법
- Retrofit 인스턴스 생성 및 설정
- API 호출 및 응답 처리
- 실전 예제: 영화 정보 앱
- 에러 핸들링 및 디버깅 팁
- 고급 기능 활용 (인증, 파일 업로드 등)
- 성능 최적화 기법
Retrofit은 정말 강력한 라이브러리예요! 복잡한 네트워크 코드를 간결하게 작성할 수 있게 해주고, 유지보수도 쉽게 만들어주죠. 이제 여러분도 Retrofit을 활용해서 멋진 앱을 만들어보세요! 😊
추가 학습 자료 📚
- Retrofit 공식 문서 - 가장 정확하고 최신 정보
- Retrofit GitHub 샘플 코드 - 실제 사용 예제
- Future Studio Retrofit 튜토리얼 - 자세한 가이드
- Volley - 구글의 대안 네트워킹 라이브러리
- Gson - JSON 파싱 라이브러리
마지막으로, 개발 중에 문제가 생기면 Stack Overflow나 Android 개발자 커뮤니티에 질문해보세요! 많은 개발자들이 비슷한 문제를 겪었을 거예요. 그리고 재능넷에서도 안드로이드 개발 관련 재능을 공유하거나 구매할 수 있으니 참고하세요! 😉
여러분의 앱 개발 여정에 행운을 빕니다! 화이팅! 💪
댓글 작성
이 글에 대한 여러분의 생각을 들려주세요
로그인이 필요합니다
댓글을 작성하려면 먼저 로그인해주세요.
댓글 0
지식인의 숲 - 지적 재산권 보호 고지
지적 재산권 보호 고지
- 저작권 및 소유권: 본 컨텐츠는 재능넷의 독점 AI 기술로 생성되었으며, 대한민국 저작권법 및 국제 저작권 협약에 의해 보호됩니다.
- AI 생성 컨텐츠의 법적 지위: 본 AI 생성 컨텐츠는 재능넷의 지적 창작물로 인정되며, 관련 법규에 따라 저작권 보호를 받습니다.
- 사용 제한: 재능넷의 명시적 서면 동의 없이 본 컨텐츠를 복제, 수정, 배포, 또는 상업적으로 활용하는 행위는 엄격히 금지됩니다.
- 데이터 수집 금지: 본 컨텐츠에 대한 무단 스크래핑, 크롤링, 및 자동화된 데이터 수집은 법적 제재의 대상이 됩니다.
- AI 학습 제한: 재능넷의 AI 생성 컨텐츠를 타 AI 모델 학습에 무단 사용하는 행위는 금지되며, 이는 지적 재산권 침해로 간주됩니다.
아직 댓글이 없습니다.