본문 바로가기
프로그래밍/DB/JAVA/안드로이드/GAE

[안드로이드] HttpClient 와 HttpURLConnection [펌]

by 아유카와 2012. 12. 18.

Android’s HTTP Clients


[이 포스트는 Dalvik 팀의 Jesse Wilson 에 의해 작성되었습니다. —Tim Bray]




  안드로이드의 많은 어플리케이션이 데이터를 주고 받을 때 HTTP 프로토콜을 사용합니다. 안드로이드 플랫폼에서는 두 종류의 HTTP 클라이언트를 제공하는데, 하나는 Java 의 기본적인 HttpURLConnection 이고, 다른 하나는 아파치 HTTP 클라이언트 입니다. 양쪽 모두 HTTPS 프로토콜, 스트리밍 방식의 업로드 및 다운로드, 타임아웃 설정, IPv6 지원, 커넥션 풀등의 기능을 제공합니다. 그렇다면 둘 중 어느 것을 사용하는 편이 좋을까요?


아파치 HTTP 클라이언트

  HTTPClient 인터페이스를 구현 한 DefaultHttpClient 와 AndroidHttpClient 클래스가 제공됩니다. 웹브라우저를 만들기에 충분할 만큼, 방대하고 유연한 API 가 지원되며, 안정적으로 동작하고 별다른 버그도 없습니다.

 

 하지만 너무 방대한 API 가 지원되기 때문에, 안드로이드 개발팀이 해당 클래스를 유지 보수 하고 성능을 개선하는데 어려움이 있습니다. 기존 어플리케이션과 호환성을 유지하기 위해서 너무 많은 사항을 고려해야하기 때문에 그렇습니다. 그래서, 사실 안드로이드 개발팀은 이 클래스를 개선하는 작업을 더이상 진행하고 있지 않습니다.


HttpURLConnection

 HttpURLConnection 은 일반적인 목적으로 사용가능한, 가벼운 HTTP 클라이언트 입니다. 대부분의 어플리케이션에 적합하지요. 이 클래스는 사실 좀 별볼일 없었는데, 하지만 핵심적인 API 만을 갖고 있는 단순한 형태이기 때문에, 성능 개선 작업을 진행하기가 그 만큼 더 용이하였습니다.


 프로요 이전 버전의 경우 사실 HttpURLConnection 클래스에는 몇 가지 이상한 버그들이 있었습니다. 한 예로, InputStream 을 열어둔 상태에서 close() 메서드를 호출하면, 커넥션 풀이 오작동하는 일들도 있었지요. 따라서, 많은 개발자 들이 아래와 같이 커넥션 폴링 기능을 아예 비활성화 시키기도 했습니다. (주> 버그의 내용을 간단히 소개합니다. HttpURLConnection 은 커넥션 풀을 지원함으로, 연결중인 HTTP 커넥션을 종료하더라도, 바로 소켓 연결이 끊어지는 것이 아니며, 커넥션 풀로 반환 되었다고, 이 후, 동일 주소에 대한 또 다른 요청이 생기면 새로운 커넥션을 생성하는 대신, 기존에 연결되어 있던 커넥션을 재활용 하게 됩니다. 이 떄, 이전 커넥션에서 데이타 InputStream 을 끝까지 읽기 전에 close() 를 하고, 해당 커넥션이 재활용될 때, 기존에 미처 읽지 못했던 데이터가 남아 있는 버그가 있다고 하는구요.) 


private void disableConnectionReuseIfNecessary() {
   
// HTTP connection reuse which was buggy pre-froyo
   
if (Integer.parseInt(Build.VERSION.SDK) < Build.VERSION_CODES.FROYO) {
       
System.setProperty("http.keepAlive", "false");
   
}
}


 진저 브레드에서, HttpURLConnection 을 통해, 압축된 형식의 HTTP 응답을 수신할 수 있는 기능이 추가되었습니다. HttpURLConnection 은 Http 요청 시 자동으로 아래의 헤더를 추가하며, 만일 서버가 이에 따라 압축된 형식으로 응답을 전송해도 이를 적절히 처리하여 개발자에게 전달해 줍니다. (개발자는 데이타 인코딩/디코딩에 대하여 신경쓸 필요가 없습니다.)

Accept-Encoding: gzip

 단, 한 가지 주의점이 있는데, HTTP 의Content-Length 헤더는 실재 전송된 데이터의 크기를 나타냄으로, 이 경우, 압축된 데이타의 크기를 전달합니다. 따라서, 압축이 풀린 데이터를 저장하기 위한 버퍼를 생성할 떄, getContentLength() 값을 이용하여 버퍼 크기를 결정해서는 않됩니다. 대신 InputStream 의 read() 메서드가 -1 을 리턴할 때 까지 차곡 차곡 데이터를 읽어야 합니다.


 따라서, 여러분이 안드로이드 어플리케이션과 상호작용하는 웹 서버를 만들 때도, 이런 기능을 활용할 수 있도록 서버 측에 요청에 대한 응답을 압축해서 전달하닌 기능을 추가해 두면 좋을 것 있습니다. 만일 이 기능을 사용하고 싶지 않다면, HTTP 요청 시 "Accept-Encoding" 헤더를 다음과 같이 선언하시면 됩니다.

  urlConnection.setRequestProperty("Accept-Encoding", "identity");


 또한 진저브레드 버전에서는 HTTPS 에 관한 여러가지 기능 개선을 이루어 졌습니다. HttpsURLConnection 은 이제, Server Name Indication (SNI) 기능을 지원합니다. 따라서, 하나의 아이피 주소를 갖지만, 여러 HTTPS 호스트를 유지하는 웹서버와 별다른 문제없이 안전한 연결을 맺을 수 있습니다. 또한, 압축된 데이타 전송과 최근에 연결된 TLS 세션을 재활용 할 수 있는 Session Ticket 기능도 지원합니다. 만일, 이런 새로운 기능을 활용하여 연결이 실패할 경우, 안드로이드 시스템은 이런 기능을 제거 하고 자동으로 재연결을 시도합니다. 따라서, 개발자 분들은 HttpsURLConnection 을 이용하여 효율적으로 최신 서버와 데이터를 주고 받을 수 있으며, 동시에 오랜된 서버들과도 호환성 문제없이 네트워크를 연결을 구성할 수 있습니다.


 더 나악, 아이스크림 샌드위치 버전에서는 에서는 Response Cache(응답 캐쉬) 기능이 추가됩니다. 캐쉬가 활용됨으로 HTTP 요청에 대한 응답은 다음의 세 가지 방식으로 얻어질 수 있습니다.

  • 만일 이미 온전하게 캐슁 되어 있는 HTTP 응답이 있는 경우, 해당 값을 활용합니다. 별도의 네트워크 커넥션이 일어나지 않으며, 요청 결과가 즉시 반환됩니다.
  • 조건부 캐쉬가 존재하는 경우, 우선 캐슁된 결과가 유효한지 확인할 필요가 있습니다. 클라이언트는 우선, "만일 어제 이후로 foo.png 파일이 변경되었다면 나한테 해당 컨텐츠를 다오" 같은 식으로 서버에 요청을 전달하고, 서버는 업데이트된 컨텐츠를 전송하거나 304 Not Modified 상태 코드를 반환 할 것 입니다. 컨텐츠가 변경되지 않았다면 캐슁된 컨텐츠를 사용할 수 있습니다.
  • 캐슁된 응답이 없는 경우에는, 일반적인 방법으로 서버로 요청을 전달하고 응답을 수신합니다. 수신된 응답은 이 후 재 사용될 수 있도록, 로컬 캐쉬 공간에 저장됩니다.

 

 자바 리플렉션 기법을 이용하여, 호환성 문제를 걱정하지 않고, 캐쉬 기능을 지원하는 디바이스에서 한하여 해당 기능을 사용할 수 있습니다. 아래의 샘플 코드는 한 예인데, 아이스 크림 샌드위치 버전 에서는 캐쉬 기능을 사용하고, 이전 버전에서는 관련 기능을 사용하지 않도록 만들어 줍니다.

private void enableHttpResponseCache() {
   
try {
       
long httpCacheSize = 10 * 1024 * 1024; // 10 MiB
       
File httpCacheDir = new File(getCacheDir(), "http");
       
Class.forName("android.net.http.HttpResponseCache")
           
.getMethod("install", File.class, long.class)
           
.invoke(null, httpCacheDir, httpCacheSize);
   
} catch (Exception httpResponseCacheNotAvailable) {
   
}
}


어떤 클라이언트를 사용해야 할 까요?

아파치 HTTP 클라이언트는 이클레어 및 프로요 버전에서 HttpUrlConnection 에 비하여 안정적으로 동작합니다. 이 플랫폼들을 위해서는 아파치 Http 클라이언트가 최선의 선택입니다.


 진저 브레드 이 후 부터는, HttpURLConnection 이 최선의 선택입니다. 간단한 API 와 작은 크기는 안드로이드에 꼭 알맞습니다. 투명한 압축 지원과 응답 캐슁 기능은 네트워크 사용을 최소화 하며, 속도를 향상 시키고 배터리를 절약하게 해 줍니다. 따라서, 여러분이 새롭게 제작하는 어플리케이션은 HttpURLConnection 을 사용하시는 편이 좋습니다. 이 클래스가 바로 안드로이드 개발팀이 열심히 작업을 수행하고 있는 클래스이기 때문입니다.