Do.

iOS - URLCache 본문

iOS

iOS - URLCache

Hey_Hen 2022. 3. 21. 16:45

소개

Cache(캐시)는 컴퓨팅에서 데이터를 일시적으로 저장하는 것을 뜻한다.
어플리케이션에서 캐시는 요긴하게 쓰이는데, 예를 들어 네트워크 요청을 할 때, 한 번 요청한 결과에 대해서 캐시를 해두고, 다음에 해당 요청을 한번 더 할 때 네트워크 요청을 하지 않고 캐시에서 결과를 가져 올 수 있다. 정리하자면 아래와 같은 장점이 있다.

  1. 클라이언트에서는 요청 결과를 훨씬 더 빨리 화면에 보여줄 수 있다.
  2. 서버에서는 동일한 요청에 대해 동작을 중복 수행하지 않아도 된다.

URLCache

iOS에서 네트워크 요청을 하기 위해서는 URLSession이라는 API를 사용하는데, URLSession의 Task에는 대표적으로 몇가지 종류가 있다.

  • DataTask
  • DownloadTask

Steam이나, Socket, Upload는 예외하겠다.
두 Task는 모두 요청 결과를 받아오는 동작을 수행할 수 있는데, 조금 다른 결과를 가져온다.

  • DataTask: Cache정책에 따라 memory와 disk에 동시에 cache한다. 시간이 지나면 memory에서는 해제된다.
  • DownloadTask: 다운로드 한 파일을 메모리에 Cache하고, disk에는 cache하지 않는다. (Tep Directory에 저장하고 필요가 없어지면 지운다.)

URLSession은 고맙게도 default 구성이 cache를 자동으로 실시한다.
Cache된 데이터는 Library/Caches~ 에 저장되며, 클라이언트에서 같은 네트워크 요청이 있을 때, 별도의 코드없이 자동으로 Cache를 확인하고, 디스크에서 불러온다.
하지만 이때, 모든 데이터가 Cache되는 것은 아닌데, URLSession는 URLCache에 의존하고 있는데, URLCache가 default로 가지고 있는 메모리, 디스크 용량 제한이 있다.
이때 용량 제한은 아래와 같다.

  • Memory Capacity: 4MB
  • Disk Capacity: 20MB

별도로 지정해 주지 않으면, 큰 파일은 자동으로 Cache되지 않는다.
이를 해결하는 방법은 URLCache 인스턴스를 새로 생성해 URLSession 프로퍼티에 할당하거나, URLCache에 store메서드를 통해 저장할 수 있다.

let ownCache = URLCache(memoryCapacity: 20*1024*1024*1024, diskCapacity: 80*1024*1024*1024, directory: FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask)[0].appendingPathComponent("DownloadImage"))

private lazy var session: URLSession = {
    let configuration = URLSessionConfiguration.default
    configuration.urlCache = ownCache
    return URLSession(configuration: configuration, delegate: self, delegateQueue: nil)
  }()

URLSessionConfiguration을 통해 URLCache 인스턴스를 전달할 수 있다.
이 방법은 정해진 용량 아래서, session의 데이터 받아오기가 끝나면 자동으로 Cache가 된다.
혹은 데이터 다운로드가 완료된 시점에서

ownCache.storeCachedResponse(.init(response: task.response!, data: self.data!), for: task.currentRequest!)

storeCachedResponse를 통해서 데이터를 저장할 수 있다.
만약 별도의 URLCache 인스턴스를 URLSession으로 전달하지 않았다면 URLCache는 싱글톤 인스턴스를 제공하기 때문에 이를 이용할 수 있다.

URLCache.shared.storeCachedResponse(.init(response: task.response!, data: self.data!), for: task.currentRequest!)

DownloadTask는 앞서 Cache 하지 않는다고 얘기했는데, 위와 같이 동일한 방법으로 Cache 할 수 있다. 다른 방법이라면 DownloadTask는 데이터를 디스크에 쓴 다음 Location URL을 제공하는 것만 다를 뿐이다.

DataTask vs DownloadTask

DataTask와 DownloadTask는 각각 언제 사용하는 것이 좋을까?
Cache는 언제 Cache하고, 삭제하고, 업데이트 하는지에 대한 내용은 Cache Policy를 따른다.
이게 왜 중요하냐면
URLSessionDataTask는 자동으로 Data를 Cache한다고 앞서 얘기했는데, 예를들어 Daum 검색 API를 URLSessionDataTask로 요청을 보냈다고 한다면, API는 검색 결과를 Data로 보낼 것이다.
이때 Cache 덕분에 동일한 요청 결과에 대해서는 API에 추가적으로 요청하지 않고, 클라이언트에서 빠르게 보여줄 수 있지만, 만약 잠깐 사이에 검색 결과가 변경이 되었을 가능성이 있는데, 클라이언트에서는 Cache 때문에 해당 요청에 대해 업데이트 Data를 받아오지 않고, Cache한 Data를 보여줄 수 있다.

이러한 결과가 나타내는 바는, Cache는 특정 시점을 선택하고, 시점에 따라 삭제하고 업데이트 해야 한다는 것이다. 그 시점은 특정 기간일 수도 있고, 애플리케이션이 종료되는 시점일 수도 있다. 클라이언트 입장에서 Cache는 언제 삭제되어도 이상하지 않은 기록 방법인 것이다.
반면 DownloadTask는 그 기능을 보면 알지만 더 큰 Data를 받는데 특화되어 있다. 예를들어 Background 다운로드, Data 이어서 받기와 같은...
이러한 큰 Data는 다운받은데 큰 네트워크 리소스를 사용하므로 클라이언트가 종료되었다고 삭제할 수는 없다.
또 다운로드 한 Data의 목적에 따라 저장되어야 하는 Directory가 다르기 때문에, 마찬가지로 구분 되어야 한다.

참고

HTTP Cache로 불필요한 네트워크 요청 방지

불필요한 네트워크 요청을 어떻게 피할 수 있습니까? 브라우저의 HTTP 캐시는 첫 번째 방어선입니다. 이 방법이 반드시 가장 강력하고 유연한 방법은 아니며 캐시된 응답의 수명을 제한적으로 제

web.dev

https://stackoverflow.com/questions/48516806/cache-handling-with-moya

Cache Handling with Moya

We have implemented Moya, RxSwift And Alamofire as pods in our project. Does anyone know how you gain control of the cache policies per url request using this tech? I have read through quite a ...

stackoverflow.com

https://nshipster.com/nsurlcache/

Comments