Volley는 효율적인 HTTP 요청 실행과 이미지 로딩을 위해 Google이 개발한 Android용 네트워킹 라이브러리입니다. 라이브러리는 자동으로 스레드 풀을 관리하고, 응답을 캐싱하며, 요청에 우선순위를 지정합니다. Google, 2025에 따르면, Volley는 복잡한 종속성을 구성하지 않고 빠른 시작이 필요한 프로젝트에서 인기 있는 선택으로 남아 있습니다.
주요 내용
Volley는 Google이 I/O 2013 컨퍼런스에서 발표한 Android 애플리케이션용 네트워크 통신 라이브러리입니다. Volley라는 이름은 “일제 사격”을 의미하며 — 인터페이스 응답 속도가 중요한 UI 중심 애플리케이션의 특징인 여러 병렬 빠른 요청을 실행하도록 설계되었습니다.
Volley는 HttpURLConnection과 AsyncTask의 문제(수동 스레드 관리, 캐싱 부족, 요청 우선순위 지정의 복잡성, 번거로운 코드)에 대한 해결책으로 만들어졌습니다. Google은 Volley를 “fire-and-forget” 유형의 작업 — 결과가 인터페이스에 즉시 표시되는 작은 요청 — 을 위한 라이브러리로 포지셔닝했습니다.
Volley 아키텍처는 세 가지 주요 구성 요소로 구성됩니다: RequestQueue(큐 관리자), CacheDispatcher(캐시된 응답용 스레드), NetworkDispatcher(네트워크 스레드). 이 아키텍처는 요청을 자동으로 분산합니다: 먼저 캐시를 확인하고, 캐시가 없는 경우에만 네트워크 요청이 실행됩니다. 이렇게 하면 반복 데이터의 지연 시간이 50~80% 감소합니다.
RequestQueue는 Volley의 중앙 클래스입니다. Request<T> 객체가 추가되면 큐는 자동으로 이를 두 가지 유형의 스레드로 분산합니다: CacheDispatcher(하나의 스레드, 캐시 가능한 요청 처리)와 NetworkDispatcher(여러 스레드, 실제 HTTP 요청 수행). 기본적으로 Volley는 4개의 네트워크 스레드를 생성합니다.
요청이 추가되면 RequestQueue는 캐시에서 제공할 수 있는지 확인합니다. 캐시에 최신 응답이 있으면 CacheDispatcher는 네트워크 요청 없이 즉시 반환합니다. 캐시가 오래되었거나 없으면 요청이 NetworkDispatcher로 전달됩니다. 요청 우선순위(low, normal, high, immediate)는 큐 내 처리 순서를 결정합니다 — high 우선순위 요청이 normal보다 먼저 처리됩니다.
요청 실행 후 결과는 Handler를 통해 메인 스레드(UI 스레드)로 전달됩니다. Volley는 onResponse() 및 onErrorResponse() 콜백을 자동으로 메인 스레드로 전환하므로 추가 스레드 전환 없이 콜백에서 직접 인터페이스를 업데이트할 수 있습니다. 이렇게 하면 코드가 간소화되고 스레딩 오류의 전체 클래스가 제거됩니다.
Volley의 또 다른 기능은 자동 요청 중복 제거입니다. 동일한 URL과 동일한 매개변수를 가진 두 개의 동일한 GET 요청이 큐에 추가되면 Volley는 그중 하나만 실행하고 두 콜백 모두에 동일한 응답을 반환합니다. 이는 여러 구성 요소가 독립적으로 동일한 데이터를 요청하는 화면(예: 헤더와 설정 프래그먼트에서 동시에 필요한 사용자 프로필)에서 특히 유용합니다.
각 요청은 일련의 단계를 거칩니다: Request 생성, RequestQueue에 추가, 캐시 확인(CacheDispatcher), HTTP 요청 실행(NetworkDispatcher), Response.Listener를 통한 응답 파싱, UI 스레드로 결과 전달. 요청이 취소되면(cancel) RequestQueue는 큐에서 제거하고 콜백 호출을 방지합니다.
Volley는 또한 RetryPolicy를 지원하며, 이는 실패 시 재시도 횟수를 결정합니다. DefaultRetryPolicy는 기본적으로 2.5초의 타임아웃으로 한 번의 재시도를 수행합니다. 불안정한 연결의 경우 재시도 횟수를 3회, 타임아웃을 10초로 늘릴 수 있습니다. 사용자 정의 RetryPolicy는 getCurrentTimeout, getCurrentRetryCount 및 retry 메서드가 있는 RetryPolicy 인터페이스를 통해 구현됩니다.
Volley는 일반적인 데이터 형식을 위한 미리 준비된 요청 유형을 제공합니다. 각 유형은 추상 클래스 Request<T>를 구현하고 응답을 파싱하는 방법을 정의합니다. 사용자 정의 형식의 경우 parseNetworkResponse 메서드를 재정의하여 고유한 유형을 만들 수 있습니다.
| 요청 유형 | 반환 유형 | 목적 |
|---|---|---|
| StringRequest | String | 원시 텍스트 응답 가져오기 |
| JsonObjectRequest | JSONObject | JSON 객체 파싱 |
| JsonArrayRequest | JSONArray | JSON 배열 파싱 |
| ImageRequest | Bitmap | 이미지 로드 및 디코딩 |
| ClearCacheRequest | — | Volley 캐시 지우기 |
Gson 또는 Kotlinx Serialization으로 작업하려면 parseNetworkResponse에서 선택한 파서를 사용하는 사용자 정의 Request<T>를 만들 수 있습니다. 이렇게 하면 수동 JSONObject 파싱을 피하고 직접 입력된 객체를 받을 수 있습니다. 이 접근 방식은 이미 Gson 또는 Moshi를 통해 직렬화를 사용하는 프로젝트에서 특히 유용합니다.
데이터 전송을 위해 Volley는 세 가지 본문 유형을 지원합니다: JSONObject(POST 메서드의 JsonObjectRequest 통해), Form-encoded(생성자의 HashMap<String, String> 통해), Multipart(사용자 정의 MultipartRequest 통해). Multipart 요청은 이미지 및 파일 업로드에 유용하지만 OkHttp나 Dio와 달리 Volley에는 multipart/form-data에 대한 내장 지원이 없으므로 수동 구현이 필요합니다.
Volley의 제한 사항은 큰 응답을 처리할 때 두드러집니다. Volley는 콜백에 전달하기 전에 전체 응답을 메모리에 로드하므로 10~20MB보다 큰 JSON 파일의 경우 OutOfMemoryError가 발생할 수 있습니다. 큰 파일 다운로드에는 Volley가 적합하지 않습니다 — DownloadManager 또는 스트리밍 ResponseBody가 있는 OkHttp를 사용하세요. Volley는 중단된 다운로드(Range 헤더) 재개를 지원하지 않으며 실시간 Server-Sent Events 또는 WebSocket과 같은 스트리밍 프로토콜과 작동하지 않습니다.
기본 예제를 살펴보겠습니다 — 서버에서 데이터를 가져오는 StringRequest입니다. 먼저 Volley.newRequestQueue(context)를 통해 RequestQueue를 만듭니다. 그런 다음 URL과 성공 및 오류 콜백으로 요청을 구성합니다.
val queue = Volley.newRequestQueue(context)
val request = StringRequest(
Request.Method.GET,
"https://api.github.com/users/octocat",
{ response ->
println("응답: $response")
},
{ error ->
println("오류: ${error.message}")
}
)
queue.add(request)
JSON 요청의 경우 응답을 자동으로 JSONObject로 파싱하는 JsonObjectRequest가 사용됩니다. Volley는 GET 및 POST 요청을 지원합니다. POST의 경우 요청 본문에 JSONObject가 전달됩니다.
val jsonBody = JSONObject()
jsonBody.put("name", "New Repo")
jsonBody.put("description", "Created via Volley")
val request = JsonObjectRequest(
Request.Method.POST,
"https://api.github.com/user/repos",
jsonBody,
{ response ->
println("생성됨: ${response.getString("id")}")
},
{ println("오류: $it") }
)
queue.add(request)
요청을 취소하려면 cancel() 메서드 또는 태그별 그룹 취소가 사용됩니다. 취소 시 Volley는 onResponse와 onErrorResponse를 모두 호출하지 않으므로 화면을 떠난 후 인터페이스 업데이트를 방지합니다. 이는 Activity 및 Fragment의 메모리 누수를 방지하는 데 중요합니다.
request.tag = "profile_request"
queue.add(request)
// 화면 이탈 시 취소
queue.cancelAll("profile_request")
ImageLoader는 RequestQueue 위의 래퍼 클래스로, 이미지 로딩에 최적화되어 있습니다. 메모리 캐시(LruCache)를 지원하고 RecyclerView 목록에서 ImageView가 재사용될 때 자동으로 요청을 취소합니다. ImageLoader는 또한 View 크기에 맞게 이미지를 조정하여 메모리를 절약합니다.
NetworkImageView는 ImageLoader와 통합되는 사용자 정의 View로, 로딩을 자동으로 관리합니다: 로딩 중에는 플레이스홀더를 표시하고, 실패 시 오류로 대체하며, View가 화면을 벗어나면 요청을 취소합니다. DefaultImageUrlLoader는 URL로 이미지를 로드하고 빠른 재표시를 위해 LruCache에 저장합니다.
ImageLoader를 사용하려면 ImageLoader(queue, ImageCache)를 통해 인스턴스를 생성하기만 하면 됩니다. 여기서 ImageCache는 내부에 LruCache가 있는 ImageCache 인터페이스의 구현입니다. XML 레이아웃의 NetworkImageView는 setImageUrl() 메서드를 통해 ImageLoader에 연결되며, 플레이스홀더 및 오류 처리를 위한 추가 코드 없이 모든 로딩이 완전히 자동으로 이루어집니다.
각 Activity에서 RequestQueue 생성은 스레드 중복과 캐시 혼란을 초래하는 일반적인 실수입니다. RequestQueue는 Application에서 한 번 또는 싱글톤 클래스를 통해 생성하는 것이 좋습니다. 그렇지 않으면 각 화면이 자체 스레드 풀을 가지게 되고 캐시가 큐별로 별도로 저장됩니다.
화면 회전 시 요청 취소 무시. 구성 변경 시 Activity가 다시 생성되고 이전 Activity의 콜백이 메모리에 계속 남아 있습니다. 이로 인해 누수가 발생하고 파괴된 View를 업데이트하려는 시도가 이루어집니다. 항상 Activity별 태그로 onStop()에서 cancelAll()을 통해 요청을 취소하세요.
Volley는 HTTP/2 및 코루틴을 지원하지 않습니다 — 이는 사용 오류가 아니라 아키텍처상의 제한입니다. Volley는 2013년에 만들어졌으며 최신 프로토콜과 Kotlin 코루틴을 지원하지 않습니다. 새 프로젝트의 경우 Google은 Retrofit + OkHttp 사용을 권장합니다. Volley는 레거시 프로젝트를 지원하거나 최소한의 네트워킹 요구 사항이 있는 간단한 애플리케이션에만 적합합니다.
자주 묻는 질문
Volley는 새 프로젝트에는 구식입니다 — Google은 2017년 이후로 라이브러리를 업데이트하지 않았습니다. 최신 애플리케이션에는 Retrofit + OkHttp 또는 Ktor Client를 사용하세요. Volley는 기존 레거시 코드를 지원하거나 최소한의 네트워킹 작업이 있는 간단한 교육 프로젝트에서만 사용할 수 있습니다.
최신 기술 지원 부족: HTTP/2, Kotlin 코루틴, 멀티플랫폼 개발, 타입화된 직렬화. Volley는 유형 없이 JSONObject와 JSONArray를 사용하므로 JSON 구조가 예상과 일치하지 않을 때 런타임 오류가 발생합니다.
ImageLoader와 NetworkImageView를 통해 처리합니다. ImageLoader는 이미지 메모리 캐싱에 LruCache를 사용하고 View가 재사용될 때 자동으로 요청을 취소합니다. NetworkImageView는 로딩 중에 플레이스홀더를 표시하고 로드된 이미지 또는 오류 표시기로 대체합니다.
기술적으로 가능합니다 — Volley 콜백 위에 suspendCoroutine { } 래퍼를 통해. 그러나 Volley는 코루틴 취소에 기반한 취소를 지원하지 않고 Dispatchers.IO와 직접 작동하지 않으므로 이점이 없습니다. 기본 코루틴 지원이 있는 Ktor Client를 사용하는 것이 좋습니다.
타임아웃은 RetryPolicy를 통해 설정합니다. 기본적으로 DefaultRetryPolicy는 2.5초의 타임아웃과 한 번의 재시도를 사용합니다. 매개변수 변경: request.retryPolicy = DefaultRetryPolicy(10000, 1, 1.0f) — 10초 타임아웃, 한 번 시도.
요약
턴키 방식의 모바일 애플리케이션을 개발해 드립니다
IT Sectr는 2017년부터 스타트업과 기업을 위한 iOS 및 Android 애플리케이션을 만듭니다. 저희가 상담해 드리고 최적의 솔루션을 제안하겠습니다.