OkHttp 와 Retrofit

쓰리원·2022년 6월 17일
0

Android Network Library

목록 보기
2/2
post-thumbnail

1. 배경 지식

정리 링크 : HTTP란?

  • 1. HTTP

HTTP는 HyperText Transfer Protocol의 약자로, 인터넷 상에서 HTML 문서와 같은 데이터를 링크 기반으로 주고받기 위한 프로토콜(통신 규약)으로, 서버-클라이언트 모델(클라이언트 - 요청 / 서버 - 응답)을 따르는 프로토콜로 전송 제어 프로토콜(TCP)와 인터넷 프로토콜(IP) 위에서 동작합니다.

  • 2. HTTPS

HTTP + Secure SocKet(보안 통신)의 약자로 모든 통신 내용을 암호화하며, 공개키 암호화 방식을 사용합니다.

3. REST

REST(Representational State Transfer)는 웹 서비스를 설계하는 데 사용되는 아키텍처 스타일입니다. REST는 인터넷에서 웹 서비스 간의 통신을 용이하게 하는 몇 가지 기본 원칙을 제시합니다. 이러한 원칙을 따르는 API를 REST API 또는 RESTful API라고 합니다.

4. API

정보 제공자가 제공하는 기능에 대해 응용 프로그램에서 사용할 수 있도록 만든 인터페이스 입니다.

5. REST API

Representational State Transfer 이라는 용어의 약자이고 자원의 표현으로 상태를 전달하는 것으로 URI로 자원(이미지, 동영상, DB 데이터)을 표현하는 데에 집중하고, 자원의 상태(행위)에 대한 정의는 HTTP Method(POST, GET, PUT, DELETE)로 하는 것이 REST한 API를 설계하는 중심 규칙(아키텍처)이라고 볼 수 있습니다.

이를 통해 CRUD 작업를 처리할 수 있게됩니다. 클라이언트는 자원의 상태에 대한 조작을 요청하고 서버로부터 받은 응답인 JSON, XML 로 이루어지게 됩니다. 즉, REST API 는 REST 아키텍처의 제약조건을 준수하는 API를 의미 합니다.

2. OkHttp 와 Retrofit 란?

1. OkHttp 란?

OkHttp는 Square에 의해 개발된 오픈 소스 HTTP 클라이언트 라이브러리입니다. REST API 및 서버와 HTTP 기반의 클라이언트의 요청과 응답에 편의성 제공을 위한 라이브러리 입니다. HTTP와 HTTP/2의 최신 기능을 지원합니다. Java와 Kotlin, 그리고 Android와 같은 JVM 기반 시스템에서 동작합니다.

2. Retrofit란?

Square에서 개발한 라이브러리로, HTTP API를 Kotlin 혹은 Java 인터페이스로 매핑하는 역할을 합니다. 내부적으로 OkHttp를 사용하며, 간편한 API 선언 및 JSON/XML 파싱을 위한 플러그인을 지원합니다.

3. OkHttp 와 Retrofit 비교분석

OkHttp와 Retrofit은 모두 HTTP 클라이언트 라이브러리이지만, 사용 방식과 목적에서 차이점이 있습니다.

  • OkHttp
    OkHttp는 저수준 HTTP 클라이언트 라이브러리로, HTTP 요청과 응답을 생성하고 처리하는 기본 기능을 제공합니다. OkHttp를 사용하면 HTTP 헤더, 바디 등의 세부 사항을 수동으로 제어할 수 있으며, 요청 및 응답에 대한 세밀한 제어가 가능합니다.
  • Retrofit
    Retrofit은 상위 수준의 REST 클라이언트 라이브러리로, HTTP API를 Kotlin 또는 Java 인터페이스로 매핑하는데 초점을 두고 있습니다. 이로 인해 Retrofit는 복잡한 HTTP 요청을 단순화하고, 코드의 가독성을 높이며, 코드 중복을 줄일 수 있습니다. 기본적으로 Retrofit은 OkHttp를 내부적으로 사용하여 실제 HTTP 요청을 수행합니다.

OkHttp를 직접 사용하는 것이 아니라 Retrofit과 같은 라이브러리를 사용하는 주된 이유는, Retrofit이 HTTP 통신을 추상화하여 코드를 더 깔끔하게 작성할 수 있게 해주기 때문입니다. 하지만 복잡한 HTTP 기능이 필요한 경우에는 OkHttp를 직접 사용하는 것이 더 적합할 수 있습니다.

즉, OkHttp는 낮은 수준의 HTTP 작업에 적합하며 Retrofit은 고수준의 RESTful 서비스 구현에 적합합니다. 각자의 장점과 용도를 이해하고 알맞게 사용하는 것이 중요합니다.

3. OkHttp 의 예제

아래는 OkHttp를 사용하여 HTTP GET 요청을 보내는 간단한 예제입니다.

import okhttp3.OkHttpClient
import okhttp3.Request

// OkHttpClient 인스턴스를 생성합니다.
val client = OkHttpClient()

// 요청을 정의합니다.
val request = Request.Builder()
    .url("http://www.example.com")
    .build()

// 요청을 실행합니다.
client.newCall(request).execute().use { response ->
    if (!response.isSuccessful) throw IOException("Unexpected code $response")

    // 응답을 출력합니다.
    println(response.body!!.string())
}

위의 예제는 HTTP GET 요청을 보내는 가장 기본적인 형태입니다. 만약 POST 요청을 보내거나, 헤더를 추가하거나, 요청 본문을 첨부하려면 빌더 패턴을 사용해 요청을 더욱 세밀하게 구성할 수 있습니다.

예를 들어, JSON 데이터를 본문으로 가지는 POST 요청을 보내려면 다음과 같이 작성할 수 있습니다.

import okhttp3.MediaType
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.RequestBody

// OkHttpClient 인스턴스를 생성합니다.
val client = OkHttpClient()

// 요청 본문을 정의합니다.
val json = """
    {
      "key": "value"
    }
""".trimIndent()

val body = RequestBody.create(MediaType.get("application/json; charset=utf-8"), json)

// 요청을 정의합니다.
val request = Request.Builder()
    .url("http://www.example.com")
    .post(body)
    .build()

// 요청을 실행합니다.
client.newCall(request).execute().use { response ->
    if (!response.isSuccessful) throw IOException("Unexpected code $response")

    // 응답을 출력합니다.
    println(response.body!!.string())
}

4. Retrofit 의 예제

interface ExampleService {
    @GET
    fun getResponseFromExample(): Call<ResponseBody>
}

이 인터페이스에서 @GET 어노테이션은 HTTP GET 요청을 나타냅니다. getResponseFromExample 함수는 http://www.example.com 에서 응답을 가져오는 역할을 합니다. 그 다음, Retrofit 인스턴스를 생성하고, 이를 사용하여 서비스 인터페이스의 구현을 만듭니다.

// Retrofit 인스턴스를 생성합니다.
val retrofit = Retrofit.Builder()
    .baseUrl("http://www.example.com")
    .build()

// 서비스 인터페이스의 구현을 생성합니다.
val service = retrofit.create(ExampleService::class.java)

마지막으로, 생성된 구현을 사용하여 요청을 보내고 응답을 처리합니다.

// 요청을 실행합니다.
val call = service.getResponseFromExample()
val response = call.execute()

// 응답을 처리합니다.
if (!response.isSuccessful) throw IOException("Unexpected code $response")

// 응답을 출력합니다.
println(response.body!!.string())

이렇게 Retrofit을 사용하면 HTTP API를 Kotlin 인터페이스로 변환할 수 있습니다. 이 인터페이스의 각 메서드는 HTTP 요청을 나타내며, 각 요청에는 어노테이션을 사용하여 매개변수와 반환 타입을 지정할 수 있습니다.

5. OkHttp 와 Retrofit 비교

Retrofit과 OkHttp 둘 다 Square Inc.에서 제공하는 HTTP 통신을 위한 라이브러리입니다. 각각이 주는 편의성과 컨트롤 레벨이 다르기 때문에 사용하는 상황에 따라 선택할 수 있습니다.

Retrofit을 사용하는 경우를 예로 들면

interface ApiService {
    @GET("users/{user}/repos")
    fun listRepos(@Path("user") user: String): Call<List<Repo>>
}

val retrofit = Retrofit.Builder()
    .baseUrl("https://api.github.com/")
    .build()

val service = retrofit.create(ApiService::class.java)

service.listRepos("octocat").enqueue(object : Callback<List<Repo>> {
    override fun onResponse(call: Call<List<Repo>>, response: Response<List<Repo>>) {
        if (response.isSuccessful) {
            val repos = response.body()
            // handle the repos
        } else {
            // handle the error
        }
    }

    override fun onFailure(call: Call<List<Repo>>, t: Throwable) {
        // handle the failure
    }
})

위 코드는 Retrofit을 사용하여 GitHub의 특정 유저의 저장소 목록을 가져오는 HTTP GET 요청을 수행하는 예제입니다. Retrofit은 인터페이스 기반의 API 선언으로 인해 코드를 매우 간결하게 작성할 수 있게 해줍니다.

하지만 이 경우, HTTP 통신의 세부 사항에 대한 제어가 제한적입니다. 예를 들어, 특정 HTTP 요청 헤더를 수정하거나 복잡한 요청 바디를 구성하려면 추가적인 작업이 필요합니다.

OkHttp를 직접 사용하는 경우를 살펴보면,

val client = OkHttpClient()

val request = Request.Builder()
    .url("https://api.github.com/users/octocat/repos")
    .build()

client.newCall(request).execute().use { response ->
    if (!response.isSuccessful) throw IOException("Unexpected code $response")
    // process the response...
}

위의 코드는 OkHttp를 사용하여 동일한 작업을 수행하는 예제입니다. OkHttp는 낮은 수준의 HTTP 클라이언트이므로, HTTP 헤더, 요청 바디 등 HTTP 요청 및 응답의 모든 세부 사항에 대해 제어할 수 있습니다.

즉, OkHttp를 직접 사용하면 세세한 컨트롤이 가능합니다. 하지만 이는 코드가 복잡해지고 가독성이 떨어질 수 있다는 단점이 있습니다. 따라서 필요한 수준의 컨트롤과 개발의 편의성 사이에서 적절한 선택을 해야 합니다.

개발자의 코딩 스타일, 프로젝트의 요구 사항, 팀의 코드 통일성 등을 고려하여 선택하는 것이 좋습니다. 보통은 Retrofit을 사용하고, 필요에 따라 OkHttp의 기능을 활용하는 경우가 많습니다. 왜냐하면 Retrofit 내부적으로 OkHttp를 사용하고 있기 때문에, 두 라이브러리의 장점을 모두 활용할 수 있기 때문입니다.

6. OkHttp 와 Retrofit 상세 비교

1. URL을 문자열로 직접 작성

OkHttp를 사용할 때, HTTP 요청의 URL을 직접 문자열 형태로 작성해야 합니다. 예를 들어, https://api.github.com/users/octocat/repos 와 같이 말이죠. 이 방식의 문제는 오타나 잘못된 URL 구조 등으로 인해 문제가 발생할 수 있다는 점입니다. 반면에, Retrofit은 URL 구조를 쉽게 파악할 수 있게 @GET, @POST 등의 애노테이션과 함께 URL의 일부를 메소드 매개변수로 취급하는 등의 방식으로 이 문제를 완화합니다.

예를 들면,

//OkHttp를 사용한 예제

val client = OkHttpClient()
val request = Request.Builder()
    .url("https://api.github.com/users/octocat/repos")
    .build()

client.newCall(request).execute().use { response ->
    if (!response.isSuccessful) throw IOException("Unexpected code $response")
    // 응답 처리...
}

여기서는 URL 전체를 하나의 문자열로 직접 작성하였습니다.

interface GitHubService {
    @GET("users/{user}/repos")
    fun listRepos(@Path("user") user: String): Call<List<Repo>>
}

val retrofit = Retrofit.Builder()
    .baseUrl("https://api.github.com/")
    .build()

val service = retrofit.create(GitHubService::class.java)
val call = service.listRepos("octocat")

이 예제에서는 @GET 애노테이션과 함께 URL의 일부 {user}를 메소드 매개변수로 취급하였습니다. 이를 통해 URL의 구조를 명확하게 알 수 있으며, 사용자 이름 "octocat"을 쉽게 바꿔줄 수 있습니다. 이러한 점들이 OkHttp에 비해 Retrofit을 사용하는 이점입니다.

2. Retrofit이 제공하는 데이터 모델 변환 기능 존재 여부

OkHttp를 사용한 예제로는

val client = OkHttpClient()
val request = Request.Builder()
    .url("https://api.github.com/users/octocat/repos")
    .build()

client.newCall(request).enqueue(object : Callback {
    override fun onFailure(call: Call, e: IOException) {
        e.printStackTrace()
    }

    override fun onResponse(call: Call, response: Response) {
        if (!response.isSuccessful) throw IOException("Unexpected code $response")

        val gson = Gson()
        val type = object : TypeToken<List<Repo>>(){}.type
        val repos: List<Repo> = gson.fromJson(response.body?.charStream(), type)

        // 이제 repos 리스트를 사용할 수 있습니다.
    }
})

위의 코드에서, 우리는 HTTP 요청을 설정하고 (Request.Builder() 부분), 그 요청을 실행하고 (client.newCall(request).enqueue() 부분), 그리고 응답을 처리하고 있습니다 (onResponse() 메서드 내부). 응답이 성공적이라면, 우리는 Gson 라이브러리를 사용하여 JSON 응답을 List 객체로 변환하고 있습니다.

Retrofit을 사용한 예제로는

// API 서비스 인터페이스를 정의
interface GithubService {
    @GET("users/{user}/repos")
    fun listRepos(@Path("user") user: String): Call<List<Repo>>
}

// Retrofit 인스턴스 생성
val retrofit = Retrofit.Builder()
    .baseUrl("https://api.github.com/")
    .addConverterFactory(GsonConverterFactory.create())
    .build()

val service = retrofit.create(GithubService::class.java)

// 요청 실행 및 응답 처리
service.listRepos("octocat").enqueue(object : Callback<List<Repo>> {
    override fun onResponse(call: Call<List<Repo>>, response: Response<List<Repo>>) {
        val repos = response.body()

        // 이제 repos 리스트를 사용할 수 있습니다.
    }

    override fun onFailure(call: Call<List<Repo>>, t: Throwable) {
        t.printStackTrace()
    }
})

위의 코드에서, 우리는 먼저 GithubService 인터페이스를 정의하여 API를 선언합니다. 그리고 Retrofit 인스턴스를 만들고, 이를 사용하여 GithubService의 구현체를 만듭니다 (retrofit.create(GithubService::class.java) 부분). 그런 다음 listRepos() 메서드를 호출하여 HTTP 요청을 실행하고, enqueue를 통해 응답을 처리합니다.

여기서 주목해야 할 점은, Retrofit을 사용하면 Gson 라이브러리를 직접 사용하여 JSON을 파싱하는 과정이 필요 없다는 것입니다. 이 모든 일을 Retrofit이 내부적으로 처리해줍니다 (GsonConverterFactory.create() 부분). 따라서 코드가 더 짧고 간결하며, 데이터 변환에 대해 걱정할 필요가 없습니다.

요약하자면, OkHttp와 Retrofit은 모두 강력한 라이브러리지만, 사용 방법과 제공하는 기능에 따라 적합한 사용 사례가 다릅니다. OkHttp는 저수준의 HTTP 클라이언트 작업에 더 초점을 맞추고 있으며, Retrofit은 더 높은 수준의 추상화와 편의성을 제공하며, 특히 RESTful API와 함께 사용하기에 적합합니다.

profile
가장 아름다운 정답은 서로의 협업안에 있다.

0개의 댓글