나미래 Android 개발자

Retrofit 이전에는 Android에서 네트워크 통신을 어떻게 했을까? 본문

안드로이드/Why

Retrofit 이전에는 Android에서 네트워크 통신을 어떻게 했을까?

Moimeme Futur 2026. 1. 5. 17:54

Android 개발에서 Retrofit은 사실상 표준처럼 사용되지만, "왜 Retrofit이 필요한가?"에 대해서는 깊이 고민해보지 않았다.
High-level의 Retrofit을 사용하는 장점을 글로써 이해하는 것이 아니라 직접 경험하기 위해서는 Low-level에서의 네트워크 통신이 어떻게 동작하는지 알 필요가 있다고 느껴졌다. 그래서 Java에서 제공하는 HttpURLConnection을 이용한 네트워크 통신 방법을 학습하기로 했다.

 

HttpURLConnection GET 사용법

먼저 Low-level의 HttpURLConnection에서 기본적인 REST API - GET 요청을 하는 소스 코드는 다음과 같다.

소스 코드

fun fetchUserWithHttpUrlConnection(userId: Int): User? {
    var connection: HttpURLConnection? = null
    try {
        val url = URL("https://jsonplaceholder.typicode.com/users/$userId")
        connection = url.openConnection() as HttpURLConnection
        connection.requestMethod = "GET"
        connection.connectTimeout = 5000
        connection.readTimeout = 5000

        val responseCode = connection.responseCode
        if (responseCode == HttpURLConnection.HTTP_OK) {
            val inputStream = connection.getInputStream()
            val reader = BufferedReader(InputStreamReader(inputStream))
            val response = reader.readText()
            reader.close()

            val gson = Gson()
            return gson.fromJson(response, User::class.java)
        }
        return null
    } catch (e: Exception) {
        e.printStackTrace()
        return null
    } finally {
        connection?.disconnect()
    }
}

 

HttpURLConnection을 통한 네트워크 통신 이해 및 소스 코드 분석

1. 네트워크 통신 요청을 위해서는 HttpURLConnection 객체 생성이 필요하고, 요청에 대한 정보를 담아야한다.

  • HttpURLConnection은 URL.openConnection()를 통해서 객체를 생성한다.
    • openConnection()연결 객체를 준비하는 단계이며, 실제 네트워크 연결은 lazy 하게 이후 시점에서 시작된다.
  • HttpURLConnection의 속성값(method, connectionTimeout, readTimeout)들을 설정한다.

2. 네트워크 연결/요청 전송은 responseCode 또는 inputStream/outputStream 접근 시점에 트리거된다.

  • val responseCode = connection.responseCode를 통해서 트리거된다.

3. 네트워크 통신 요청 결과값을 읽을 수 있도록, 즉 처리할 수 있도록 셋팅한다.

  • val inputStream = connection.getInputStream(): 서버로부터 수신된 데이터를 OS의 소켓 버퍼를 통해 읽어올 수 있는 스트림이다.
  • val reader = BufferedReader(InputStreamReader(inputStream)): 바잍 단위로 전달되는 데이터를 문자로 처리하기 위해 InputStreamReader를 사용한다. 그리고 효율적으로 데이터를 읽어오기 위해서 BufferedReader로 변환한다.
  • val response = reader.readText(): 내부적으로 read()를 반복 호출해서 스트림의 끝까지 읽어 하나의 문자열을 만든다. 이 과정에서 데이터 전달이 완료되지 않으면 read()에서 Block 될 수 있다.

서버 응답 값은 한 번에 완성되어 전달되는 것이 아니라 네트워크 특성상 조각 단위로 전달된다.
InputStream/BufferedReader는 데이터를 미리 다 받아두는 것이 아니라, 스트림에서 read()가 호출될 때마다 전달된 만큼 읽고(없으면 block) 문자로 디코딩, 버퍼링하도록 준비하는 Wrapper다.
따라서 서버/네트워크가 늦으면 reader.readText() 내부의 read 과정에서 스레드가 block 될 수 있고, 이를 메인 스레드에서 수행하면 UI 정지(ANR) 위험이 생길 수 있다.

4. 네트워크 스트림 및 소켓과 같은 OS 리소스 해제를 위해 BufferedReader를 종료한다.

  • reader.close()

5. 전달받은 Json 타입의 문자열 데이터를 User 클래스 타입으로 변환한다.

 

생각 정리

Square에서 OkHttp와 Retrofit을 제공하기 전에는 Java에서 제공하는 HttpURLConnection을 사용한 것 같다.

 

HttpURLConnection을 사용해서 네트워크 통신을 할 수는 있지만, 개발하고 유지보수하는 입장에서 네트워크 통신을 위한 객체 connection을 생성하고 Builder 패턴이나 DSL 패턴 없이 요청에 대한 정보를 설정하는데 있어서 자유도가 높은 대신, 관리 포인트가 많아 유지보수 비용이 컸을 것으로 보인다.
OkHttp를 이용하면 Request를 이용해서 Builder 패턴으로 네트워크 통신을 위한 규칙을 강제해서 관리하기 쉽고,
Retrofit을 이용하면 주석 기반으로 관리하기 용이한데 말이다.

 

몇 개 안되는 네트워크 통신에서는 사용할 수 있겠지만 실제 프로덕트에서는 수 많은 통신이 이루어지고 각 통신에서 변경사항이 발생한다면 유지보수하기가 상당히 어려웠을 것으로 예상된다.

'안드로이드 > Why' 카테고리의 다른 글

OkHttp는 왜 사용했을까?  (1) 2026.01.06
Comments