Retrofit: 개념, Android HTTP 클라이언트 특징

저자: IT Sectr 게시일: 2026-03-07 읽는 시간: 8 분

Retrofit은 Square 회사가 개발한 Android 및 Kotlin용 타입화된 HTTP 클라이언트입니다. 이 라이브러리는 어노테이션을 사용하여 REST API를 Java 또는 Kotlin 인터페이스로 변환할 수 있게 해줍니다. Square, 2025에 따르면, Retrofit은 HTTP 요청 작업을 위한 표준 도구로 수천 개의 앱에서 사용됩니다.

핵심 요점

  • Retrofit은 선언적 API를 갖춘 Android 및 Kotlin용 Square의 타입화된 HTTP 클라이언트입니다
  • 어노테이션 @GET, @POST, @Path, @Query가 boilerplate 코드 없이 HTTP 요청을 설명합니다
  • 컨버터 Gson, Moshi, Kotlinx Serialization이 JSON을 Kotlin 객체로 변환합니다
  • OkHttp는 Retrofit 내부에서 모든 HTTP 요청을 실행하는 필수 전송 계층입니다
  • Suspend 함수가 비동기 호출을 위해 Retrofit을 Kotlin 코루틴과 통합합니다

Retrofit이란?

Retrofit은 Square가 개발한 Android 플랫폼에서 REST API와 타입화된 상호작용을 위한 라이브러리입니다. 어노테이션이 있는 Java 또는 Kotlin 인터페이스를 통해 HTTP 요청을 선언적으로 설명하는 방법을 제공하며, 수동 JSON 파싱 및 HTTP 연결 관리의 필요성을 완전히 제거합니다.

이 라이브러리는 2013년 AsyncTask 및 HttpURLConnection과 같은 번거로운 솔루션의 대안으로 등장했습니다. 2025년 현재 Retrofit은 단순함과 타입 안전성 덕분에 Android 앱에서 네트워크 통신의 사실상 표준으로 남아 있습니다. JetBrains Developer Ecosystem 2024 설문조사에 따르면, 65% 이상의 Android 개발자가 상업 프로젝트에서 Retrofit을 사용합니다.

Retrofit의 대안과의 주요 차이점은 선언적 접근 방식입니다: 개발자는 수행할 작업(어떤 엔드포인트를 호출할지, 어떤 매개변수를 전달할지)을 설명하고, 수행 방법(연결을 여는 방법, InputStream을 읽는 방법, JSON을 파싱하는 방법)은 설명하지 않습니다. 이는 HttpURLConnection의 수동 사용과 비교하여 boilerplate 코드를 60~70% 줄입니다.

Retrofit 작동 방식

작동 원리 Retrofit은 Java 동적 프록시를 기반으로 합니다. 개발자가 어노테이션이 달린 인터페이스의 메서드를 호출하면, Retrofit이 Proxy.newProxyInstance 메커니즘을 통해 호출을 가로채서 HTTP 요청으로 변환합니다. 전체 프로세스는 컴파일 타임에 코드 생성 없이 런타임에 발생합니다.

Retrofit.Builder 인스턴스를 생성할 때 기본 URL과 컨버터 팩토리가 지정됩니다. Builder는 OkHttpClient를 구성하여 타임아웃, 인터셉터, 연결 풀 및 캐시를 설정합니다. create(Class) 메서드는 인터페이스 구현을 생성하여 일반 클래스처럼 호출할 수 있는 프록시 객체를 반환합니다.

요청 실행 체인은 다음과 같습니다: 어노테이션이 HTTP 메서드를 추출하고, 매개변수가 URL 또는 요청 본문에 대체되며, 컨버터가 본문을 직렬화하고, OkHttp가 요청을 실행하며, 컨버터가 응답을 역직렬화하고, 결과가 지정된 타입으로 반환됩니다. 각 단계는 분리되어 있으며 사용자 정의 구현으로 대체할 수 있습니다. 예를 들어 테스트를 위해 OkHttpClient를 MockWebServer로 대체하거나 API 변경 시 컨버터를 교체할 수 있습니다.

중요한 특징 — Retrofit은 스트리밍 데이터 전송을 직접 지원하지 않습니다. 스트리밍을 위해 인터페이스 메서드의 반환 타입으로 OkHttp ResponseBody가 사용됩니다. Retrofit은 요청 취소를 자동으로 관리하지 않습니다. 취소하려면 Call에 대한 참조를 유지하고 cancel()을 호출해야 합니다. Kotlin의 suspend 함수에서는 부모 코루틴이 취소될 때 요청 취소가 자동으로 발생합니다.

Call 객체의 수명 주기

Call<T>는 단일 HTTP 요청을 나타내는 객체입니다. 실행(execute 또는 enqueue) 후 Call을 재사용할 수 없습니다. 반복 요청을 위해서는 인터페이스 메서드를 호출하여 새 Call을 생성해야 합니다. 이는 서버에서 중복 작업을 초래할 수 있는 동일한 요청의 실수로 인한 두 번 전송을 방지합니다.

Kotlin에서는 Call 대신 suspend 함수가 사용되며, 요청 수명 주기를 자동으로 관리합니다. Retrofit이 실행을 Dispatchers.IO로 전환하고 결과를 코루틴에 반환합니다. 이는 Call 및 Callback 버전과 비교하여 코드를 30~40% 줄입니다.

HTTP 메서드를 위한 Retrofit 어노테이션

어노테이션은 Retrofit에서 HTTP 요청을 구성하는 주요 메커니즘입니다. 각 어노테이션은 표준 HTTP 메서드에 해당하며 엔드포인트에 대한 상대 경로를 받습니다. Retrofit은 GET, POST, PUT, DELETE, PATCH, HEAD 및 OPTIONS를 지원합니다.

어노테이션HTTP 메서드목적
@GETGET서버에서 데이터 가져오기
@POSTPOST새 리소스 생성
@PUTPUT리소스 전체 업데이트
@DELETEDELETE리소스 삭제
@PATCHPATCH리소스 부분 업데이트

요청 매개변수 어노테이션

@Path는 URL 세그먼트에 값을 대체합니다: @Path("id") Int id는 경로의 {id}를 대체합니다. @Query는 쿼리 매개변수를 추가합니다: @Query("page") Int page는 ?page=5가 됩니다. @Body는 선택한 컨버터를 통한 자동 직렬화로 요청 본문에 객체를 전달합니다. @Header@Headers는 HTTP 헤더를 관리합니다(정적 또는 동적).

이러한 어노테이션을 조합하여 모든 REST 엔드포인트를 설명할 수 있습니다. 예를 들어, 엔드포인트 POST /api/users/{id}/posts?limit=10의 경우 @POST, id용 @Path, limit용 @Query, 전달된 객체용 @Body가 필요합니다. Retrofit이 자동으로 올바른 HTTP 요청을 조립합니다. 추가로 @Url(동적 URL), @Field(폼 인코딩 본문), @Part 및 @PartMap(파일이 있는 멀티파트 요청용)이 지원됩니다.

Kotlin의 Retrofit 코드 예제

실용적인 예제를 살펴보겠습니다 — GitHub API용 인터페이스입니다. 리포지토리 목록을 가져오는 메서드가 있는 Kotlin 인터페이스가 생성됩니다. Repo 데이터 클래스가 JSON 응답 구조를 설명합니다.

kotlin
data class Repo(
    val name: String,
    val description: String?,
    val stargazersCount: Int,
    val forksCount: Int
)

interface GitHubApi {
    @GET("users/{user}/repos")
    suspend fun getRepos(
        @Path("user") user: String,
        @Query("sort") sort: String = "updated"
    ): List<Repo>
}

인터페이스를 설명한 후 Builder를 통해 Retrofit 인스턴스가 생성됩니다. 기본 URL, 컨버터 및 OkHttpClient는 한 번 구성되고 의존성 주입을 통해 재사용됩니다.

kotlin
val retrofit = Retrofit.Builder()
    .baseUrl("https://api.github.com/")
    .addConverterFactory(GsonConverterFactory.create())
    .client(OkHttpClient.Builder()
        .connectTimeout(30, TimeUnit.SECONDS)
        .build())
    .build()

val api = retrofit.create(GitHubApi::class.java)

Response 래퍼로 응답 처리

HTTP 상태 코드의 유연한 처리를 위해 Response<T> 래퍼를 사용하세요. 4xx 및 5xx 오류에서 예외를 던지지 않고 응답 코드, 헤더 및 본문에 접근할 수 있습니다. 이를 통해 try-catch 없이 404 및 500을 처리할 수 있습니다.

kotlin
interface GitHubApi {
    @GET("users/{user}/repos")
    suspend fun getRepos(
        @Path("user") user: String
    ): Response<List<Repo>>
}

val response = api.getRepos("octocat")
if (response.isSuccessful) {
    println(response.body()?.size)
} else {
    Log.e("API", "Error: ${response.code()}")
}

Retrofit의 컨버터 및 직렬화

컨버터는 객체를 HTTP 본문으로 변환하고 그 반대로 변환하는 Retrofit의 구성 요소입니다. Retrofit은 직렬화를 코어에 포함하지 않고 Converter.Factory를 통한 모듈식 접근 방식을 사용하여 모든 직렬화 라이브러리를 플러그인할 수 있습니다.

가장 인기 있는 컨버터는 Gson 라이브러리 기반 Google의 GsonConverterFactory입니다. 대부분의 프로젝트에서 작동하며 사용자 정의 TypeAdapter 및 JsonDeserializer를 지원합니다. 그러나 Gson은 리플렉션을 사용하고 Kotlin의 null 안전성을 존중하지 않아 예기치 않은 null 필드에서 NPE가 발생할 수 있습니다.

대안으로 Square의 MoshiConverterFactory가 있습니다: 타입에 더 엄격하고 Kotlin 지원(null 안전성, 기본값)이 더 우수하며 리플렉션이 없습니다. 순수 Kotlin 프로젝트에는 컴파일 타임에 @Serializable 어노테이션으로 작동하는 Kotlinx Serialization Converter가 최적입니다. 리플렉션을 사용하지 않으며 sealed class, 기본값 및 멀티플랫폼을 지원합니다.

컨버터 선택은 성능 및 타입 안전성에 영향을 줍니다. Gson은 사용자 정의 구성 없이 Kotlin의 non-null 필드에 null을 역직렬화하여 접근 시 NPE를 유발할 수 있습니다. Moshi는 @Json(name) 어노테이션과 failOnUnknown을 통해 이 문제를 해결합니다. Kotlinx Serialization이 가장 안전하며 컴파일 타임에 코드를 생성하여 런타임 타입 오류를 완전히 제거합니다.

Retrofit 사용 시 흔한 실수

suspend 함수의 HTTP 오류 처리 부재가 가장 흔한 문제입니다. 서버가 4xx 또는 5xx를 반환하면 Retrofit이 HttpException을 던집니다. try-catch가 없으면 앱이 충돌합니다. 반환 타입으로 Response<T>를 사용하면 본문에 접근하기 전에 isSuccessful을 확인할 수 있어 이 문제가 해결됩니다.

잘못된 캐싱 구성은 과도한 트래픽을 초래합니다. Retrofit이 자체적으로 응답을 캐시하지 않으며 OkHttpClient가 Cache를 통해 이 작업을 수행합니다. 캐시가 없으면 데이터가 변경되지 않은 경우에도 모든 요청이 완전히 실행됩니다. OkHttpClient에 10MB 캐시를 추가하면 동일한 정보에 대한 반복 요청에서 트래픽이 40~60% 감소합니다.

요청마다 Retrofit 생성은 초보자의 흔한 실수입니다. Retrofit.Builder는 런타임에 프록시 클래스 생성을 포함하는 리소스 집약적 작업입니다. 올바른 방법은 하나의 Retrofit 인스턴스를 생성하고 DI 프레임워크를 통해 재사용하는 것입니다. Hilt, Koin 또는 Dagger는 전체 앱에 싱글톤 Retrofit 인스턴스를 제공하여 메모리를 절약하고 요청을 빠르게 합니다.

인증을 위한 Interceptor 무시는 네 번째 문제입니다. 각 호출에 수동으로 Authorization 헤더를 추가하는 대신 OkHttpClient에서 전역 Interceptor를 구성하세요. Interceptor는 모든 요청을 가로채서 Bearer 토큰을 추가하고, Authenticator는 401 응답을 처리하여 토큰을 갱신하고 자동으로 요청을 재시도합니다. 이렇게 하면 인증 로직이 중앙화됩니다.

자주 묻는 질문

Retrofit과 OkHttp의 차이점은 무엇인가요?

Retrofit은 OkHttp 위의 래퍼로 어노테이션을 통해 선언적 API를 제공합니다. OkHttp는 Request와 Response를 직접 다루는 저수준 HTTP 클라이언트입니다. Retrofit은 OkHttp를 전송으로 사용하여 타입화, 직렬화 및 응답 처리를 간소화합니다.

Retrofit에 어떤 컨버터를 선택해야 하나요?

Java 프로젝트에는 GsonConverterFactory. Kotlin과 Moshi에는 MoshiConverterFactory(타입에 더 안전). 순수 Kotlin에는 Kotlinx Serialization Converter가 최적입니다. 리플렉션 없이 작동하며 sealed class와 기본값을 지원합니다.

Retrofit이 코루틴을 지원하나요?

네, 버전 2.6.0부터 Retrofit은 suspend 함수를 지원합니다. 메서드를 suspend로 선언하면 Retrofit이 Dispatchers.IO에서 요청을 실행하고 결과를 코루틴에 반환합니다. Call과 enqueue를 사용할 필요 없이 코드가 순차적이 됩니다.

Retrofit에서 인증을 설정하는 방법은?

인증은 OkHttp Interceptor를 통해 추가됩니다. intercept()에서 Authorization 헤더를 추가하세요. 동적 토큰의 경우 OkHttp의 Authenticator를 사용하세요. 401 응답을 가로채서 토큰을 자동으로 갱신하고 새 헤더로 요청을 재시도합니다.

Retrofit을 OkHttp 없이 사용할 수 있나요?

아니요 — Retrofit은 항상 OkHttp를 전송 계층으로 사용합니다. OkHttpClient는 Builder.client()를 통해 전달되며 타임아웃, 인터셉터, 캐싱 및 연결 풀을 관리합니다. OkHttp 없이 Retrofit은 단 하나의 요청도 실행할 수 없습니다.

요약

  • Retrofit은 선언적 어노테이션 기반 API를 갖춘 Android 및 Kotlin용 Square의 타입화된 HTTP 클라이언트
  • 어노테이션 @GET, @POST, @Path, @Query, @Body가 boilerplate 코드 없이 REST 요청을 설명
  • Java 동적 프록시가 런타임에 인터페이스 메서드 호출을 HTTP 요청으로 변환
  • 컨버터 Gson, Moshi, Kotlinx Serialization이 JSON 직렬화를 객체로 제공
  • OkHttp는 인터셉터, 캐싱 및 연결 풀을 갖춘 필수 전송 계층
  • Suspend 함수가 비동기 HTTP 호출을 Kotlin 코루틴과 통합
  • Response 래퍼가 처리되지 않은 예외 없이 4xx 및 5xx HTTP 오류 처리

턴키 방식의 모바일 애플리케이션을 개발해 드립니다

IT Sectr는 2017년부터 스타트업과 기업을 위한 iOS 및 Android 애플리케이션을 만듭니다. 저희가 상담해 드리고 최적의 솔루션을 제안하겠습니다.

프로젝트 논의

더 읽어보기