OkHttp — 정의, 기능 그리고 HTTP 클라이언트 아키텍처

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

OkHttp는 Square가 Retrofit 그리고 다른 네트워킹 라이브러리의 기반으로 개발한 Android와 Kotlin용 고성능 HTTP 클라이언트입니다. 단복적인 커넥션 관리, 내장 캐시잉 그리고 HTTP/2 지원을 제공합니다. Square, 2025에 따르면, OkHttp는 전 세계 애플리케이션에서 매일 수십억 개의 요청을 처리합니다.

핵심 요점

  • OkHttp — HTTP/2와 SPDY 지원이 있는 Square의 Android와 Kotlin용 HTTP 클라이언트
  • 커넥션 풀 — 지연 시간을 줄이기 위해 TCP 커넥션을 재사용하는 메커니즘
  • 인터셉터 — Interceptor와 NetworkInterceptor가 요청과 응답을 수정함
  • 캐시잉 — 내장 Cache가 반복 요청의 트래픽을 줄임
  • WebSocket — WebSocket 프로토콜을 통한 양방향 콤니케이션 지원

OkHttp란 무엇인가?

OkHttp는 Square가 개발한 Java, Android 그리고 Kotlin용 효율적인 HTTP 클라이언트입니다. 이 라이브러리는 HTTP/2, SPDY, WebSocket 그리고 네트워크 장애 시 자동 커넥션 복구를 지원하는 HTTP 요청 실행을 위한 저수준 API를 제공합니다.

OkHttp는 HttpURLConnection의 문제점을 해결할 신뢰할 수 있는 HTTP 클라이언트의 필요성에 답하여 2013년에 등장했습니다 — 커넥션 풀 부재, 약한 HTTP/2 지원, 불편한 API. 2025년까지 OkHttp는 Android API 시스템 레벨에서 사용됩니다: OkHttp는 Android 4.4 (API 19) 이후 HttpURLConnection 구현에 통합되어 있습니다.

Google I/O 2024에 따르면, OkHttp는 Android 생태계통에서 모든 HTTP 요청의 70%를 초과하여 처리합니다. 이것은 OkHttp가 Retrofit, Apollo GraphQL, Firebase 그리고 다른 많은 라이브러리의 트랜스포트 계층으로 작동하기 때문이다. 개발자는 명시적으로 추가하지 않고도 OkHttp 기능을 자동으로 얻습니다.

OkHttp 작동 방식

OkHttp 아키텍처는 인터셉터 체인에 기반합니다. 각 요청은 요청, 응답을 수정하거나 실행을 중단할 수 있는 인터셉터의 시퀀스를 거쳐갑니다. 이 아키텍처는 Chain of Responsibility 패턴과 유사하며 유연한 확장성을 제공합니다.

애플리케이션이 요청을 보내면, OkHttp는 다음 단계를 수행합니다: DNS를 해결하고, 풀에서 커넥션을 선택하고, TLS 핸드셰이크를 열고, HTTP 요청을 보내고, 응답을 수신하고, 애플리케이션에 반환합니다. RealCall은 생성부터 완료까지 요청의 전체 생명 주기를 관리하는 내부 클래스입니다.

OkHttp는 자동으로 리다이렉트(302, 301)를 처리하고, 네트워크 장애 시 요청을 재시도하며, keep-alive 프로토콜을 따르고, 투명한 gzip 압축을 지원합니다. 개발자가 이러한 작업을 위해 코드를 작성할 필요가 없습니다 — OkHttp가 서버 헤더를 기반으로 자동으로 수행합니다.

HTTP/2 지원 그리고 멀티플렉싱

HTTP/2는 헤드-오브-라인 블록킹(HTTP/1.1의 특징)없이 단일 TCP 커넥션을 통해 동시에 여러 요청을 보낼 수 있습니다. OkHttp는 서버가 지원하면 자동으로 HTTP/2를 사용하고, 필요할 때 HTTP/1.1로 투명하게 횏어옫니다.

HTTP/2 멀티플렉싱은 커넥션 설정 지연 시간(TCP + TLS)이 100–300 c00리초가 될 수 있는 모바일 애플리케이션에서 특히 중요합니다. 10개의 순차 접속 대신 OkHttp는 하나를 사용하여, 몰안정적인 커넥션이 있는 일반적인 Android 기기에서 전체 지연 시간을 40–60% 감소시킵니다.

OkHttp 인터셉터: Interceptor와 NetworkInterceptor

Interceptor는 단일 메소드 intercept(Chain)를 가진 인터페이스로, 요청을 수신하고, 작업을 수행하고, 응답을 반환합니다. 인터셉터에는 애플리케이션 인터셉터(addInterceptor로 추가) 그리고 네트워크 인터셉터(addNetworkInterceptor)라는 두 유형이 있습니다.

애플리케이션 인터셉터는 HTTP 요청이 형성되기 전에 실행됩니다 — 그들을 원본 요청과 모든 변환 후의 최종 응답을 봅니다. 네트워크 인터셉터는 네트워크 레벨에서 실행됩니다: gzip 압축, Content-Length 헤더 추가, 리다이렉트 그리고 재시도 후의 요청을 봅니다. 응답이 캐시에서 제공되면 네트워크 인터셉터가 호출되지 않습니다.

인터셉터 유형추가 방법호출 시점캐시 보기
Application InterceptoraddInterceptor()요청 전후
Network InterceptoraddNetworkInterceptor()네트워크 레벨아니오

인터셉터의 실용적 사용

실제로 OkHttp 인터셉터는 세 가지 주요 작업을 해결합니다: 인증(Authorization 헤더 추가), 로깅(디버그용 HttpLoggingInterceptor), 재시도(네트워크 장애 시 자동 요청 재시도). 여러 인터셉터를 결합하여 애플리케이션의 각 HTTP 호출에서 코드를 중복하지 않고 완전한 요청 처리 파이프라인을 구축할 수 있습니다.

인터셉터를 추가하는 순서가 중요합니다: 처음 추가된 Interceptor는 입력 시 처음에 그리고 출력 시 마지막에 실행됩니다. NetworkInterceptor의 경우 순서는 네트워크 스택에 의해 결정됩니다. 권장되는 순서: AuthInterceptor(토큰 추가), LoggingInterceptor(요청 로그), RetryInterceptor(실패 시 재시도).

HttpLoggingInterceptor를 통한 로깅

네트워크 요청을 디버그하기 위해 HttpLoggingInterceptor가 사용됩니다 — Square의 기본 인터셉터입니다. 메소드, URL, 헤더 그리고 요청/응답 본문을 로깅합니다. 로깅 레벨: BASIC(메소드 + URL + 코드), HEADERS(헤더 포함), BODY(전체 요청 및 응답). BODY는 개발 중에는 유용하지만, 보안과 성능 이유로 운영 환경에서는 비활성화됩니다.

Kotlin에서의 OkHttp 코드 예제

OkHttp를 사용한 기본 GET 요청을 살펴보겠습니다. 먼저 OkHttpClient가 생성됩니다 — 한 번 생성되고 재사용되는 무거운 객체입니다. 그 다음 URL이 있는 Request가 작성되고, execute를 통해 동기적으로 문제는 enqueue를 통해 비동기적으로 실행됩니다.

kotlin
val client = OkHttpClient.Builder()
    .connectTimeout(15, TimeUnit.SECONDS)
    .readTimeout(15, TimeUnit.SECONDS)
    .build()

val request = Request.Builder()
    .url("https://api.github.com/users/octocat")
    .header("Accept", "application/vnd.github.v3+json")
    .build()

val response = client.newCall(request).execute()
println(response.body()?.string())

비동기 실행을 위해 enqueue 메소드가 사용되며, Callback을 수낝합니다. OkHttp는 백그라운드 스레드에서 요청을 실행하고 동일한 스레드의 콜백에 결과를 반환합니다. Android 메인 스레드로 전환하려면 Handler나 코루틴을 사용하세요.

kotlin
client.newCall(request).enqueue(object : Callback {
    override fun onFailure(
        call: Call, e: IOException
    ) {
        println("요청 실패: ${e.message}")
    }

    override fun onResponse(
        call: Call, response: Response
    ) {
        println(response.body()?.string())
    }
})

인증을 위한 인터셉터 추가

커스텀 Interceptor가 각 요청에 Bearer 토큰을 추가합니다. 인터셉터는 Authorization 헤더의 존재 여부를 확인하고, 토큰이 아직 설정되지 않았으면 저장소에서 추가합니다. 401 응답에서 인터셉터는 Authenticator를 통해 토큰을 새로고칠 수 있습니다.

kotlin
class AuthInterceptor(
    private val tokenProvider: () -> String?
) : Interceptor {
    override fun intercept(chain: Interceptor.Chain): Response {
        val originalRequest = chain.request()
        val token = tokenProvider.invoke()
        val request = originalRequest.newBuilder()
            .header("Authorization", "Bearer $token")
            .build()
        return chain.proceed(request)
    }
}

OkHttp의 커넥션 풀 그리고 캐시잉

커넥션 풀(ConnectionPool)은 여러 요청을 위해 TCP 커넥션을 재사용할 수 있는 OkHttp의 기본 최적화 기능입니다. 각 요청마다 새 소켓을 생성하는 대신, OkHttp는 기본적으로 5분 동안 최대 5개의 비활성 커넥션을 저장하여 동일한 호스트에 반복되는 요청의 지연 시간을 30–70% 감소시킵니다.

응답 캐시잉은 Cache 클래스를 통해 구현됩니다. 캐시를 사용하려면 OkHttpClient.Builder에서 디렉토리와 최대 크기를 지정하면 됩니다. OkHttp는 Cache-Control, Expires 그리고 ETag 헤더에 따라 GET 응답을 자동으로 캐시하고, 넘구지 않았으면 네트워크 요청 없이 캐시된 데이터를 반환합니다.

kotlin
val cacheDir = File(context.cacheDir, "http-cache")
val cache = Cache(cacheDir, 10L * 1024 * 1024)

val client = OkHttpClient.Builder()
    .cache(cache)
    .connectionPool(ConnectionPool(5, 5, TimeUnit.MINUTES))
    .build()

풀과 캐시의 적절한 구성은 빈번한 요청이 있는 애플리케이션 — 뉴스 피드, 채팅, 데이터 업데이트 —에서 특히 중요합니다. 풀이 없으면 각 TCP 커넥션은 3방향 핸드셰이크(SYN, SYN-ACK, ACK) 그리고 가능한 TLS 핸드셰이크(2–3 라운드 트립)가 필요하여 각 요청에 100–500 밀리초가 추가됩니다.

OkHttp는 RealWebSocket 클래스를 통해 WebSocket도 지원합니다. WebSocket 커넥션은 HTTP 핸드셰이크(101 Switching Protocols)를 통해 설정되고 양방향 프로토콜로 전환됩니다. OkHttp는 커넥션을 유지하기 위해 자동으로 ping 프레임을 보내고 연결이 끝나면 재접속합니다. OkHttp의 WebSocket은 wss://echo.websocket.org같은 표준 엔드포인트와 호환됩니다.

OkHttp 사용시 일반적인 실수

요청마다 OkHttpClient 생성하기는 가장 흔한 실수입니다. OkHttpClient에는 커넥션 풀, 캐시 그리고 스레드 풀이 포함되어 있습니다. 각 요청마다 새 인스턴스를 생성하면 메모리가 낭비될 뿐만 아니라 커넥션 재사용의 이점도 잃게됩니다. OkHttpClient는 DI 컨테이너를 통해 싱글톤이어야 합니다.

Response.body() 닫기를 무시하는 것은 자원 누수를 유발합니다. ResponseBody에는 읽은 후 닫혀야 하는 InputStream이 포함되어 있습니다. body().string()이나 body().bytes()를 사용하면 OkHttp가 자동으로 스트림을 닫지만, body().byteStream()이나 body().charStream()을 읽을 때는 finally 블록에서 명시적으로 close()를 호출해야 합니다.

타임아웃 처리 부재는 또 다른 문제입니다. 기본적으로 OkHttp의 connectTimeout은 10초, readTimeout은 10초, writeTimeout은 10초입니다. 불안정한 커넥션의 모바일 애플리케이션의 경우 connectTimeout을 15–30초, readTimeout을 15–30초로 설정할 것을 권장합니다. 그렇지 않으면 신호가 약할 때 사용자가 너무 오래 기다리게 됩니다.

자주 묻는 질문

OkHttp는 Retrofit과 어떻게 다릅니까?

OkHttp는 요청과 응답을 수동으로 관리하는 저수준 HTTP 클라이언트입니다. Retrofit은 주석을 사용한 고수준 추상화입니다. OkHttp는 Retrofit의 트랜스포트 계층으로 사용되지만 추가 라이브러리 없이 독립적으로도 작동할 수 있습니다.

OkHttp는 HTTPS를 어떻게 처리합니까?

OkHttp는 TLS 핸드셰이크를 위해 SSLSocketFactory를 사용합니다. 이 라이브러리는 인증서 핀닝을 위한 CertificatePinner, 커스텀 검증을 위한 TrustManager, 그리고 인증서에 대한 호스트명 검증을 위한 HostnameVerifier를 지원합니다.

OkHttp에서 네트워크 오류를 잡는 법은?

동기 요청은 네트워크 문제 시 IOException을 발생시킵니다. 비동기 요청은 IOException으로 onFailure 호출을 수신합니다. HTTP 오류(4xx, 5xx)의 경우 응답은 성공으로 간주됩니다 — 오류 코드는 response.isSuccessful()을 통해 확인합니다.

OkHttp는 WebSocket을 지원합니까?

네, OkHttp에는 WebSocket 클래스와 WebSocketListener를 통한 내장 WebSocket 지원이 있습니다. 커넥션이 설정된 후 WebSocket은 반복되는 HTTP 요청 없이 실시간으로 메시지를 보내고 받을 수 있습니다.

OkHttp에서 리다이렉트를 비활성화하는 법은?

OkHttpClient.Builder에서 followRedirects(false) 그리고 followSslRedirects(false)를 통해 자동 리다이렉트를 비활성화합니다. 이것은 예를 들어 리다이렉트 URL에서 토큰을 추출하는 등 리다이렉트를 수동으로 처리하려는 경우 유용합니다.

요약

  • OkHttp — HTTP/2와 SPDY 지원이 있는 Square의 고성능 HTTP 클라이언트
  • 인터셉터 아키텍처가 요청 수정을 위해 Chain of Responsibility 구현
  • 커넥션 풀이 TCP 커넥션을 재사용하여 지연 30–70% 감소
  • 캐시잉 Cache-Control과 ETag가 반복 요청의 트래픽 감소
  • WebSocket이 실시간 양방향 콤니케이션 가능
  • OkHttpClient는 싱글톤이어야 함 — 요청마다 생성하면 누수 발생
  • ResponseBody는 byteStream으로 읽을 때 명시적 닫기 필요

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

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

프로젝트 논의

더 읽어보기