Network Caching
- 네트워크 작업은 고비용의 작업입니다. 따라서 매번 동일한 데이터를 받아올 때마다 요청과 응답을 수행하게된다면 시간 및 리소스를 상당히 낭비하게 됩니다.
- 그래서 필요한 것이 Caching입니다. Caching을 사용하면 불필요한 작업을 최대한 줄여서 시스템의 성능을 향상시킬 수 있습니다.
- URL Loading System을 통해 이러한 Caching에 설정을 수행할 수 있습니다.
- Cache Store 및 Cache Policy를 변경할 수 있습니다.
- URL Loading System에서는 4가지 Cache Policy를 제공합니다.
- useProtocolCachePolicy : protocol 특성에 따른 기본 캐시정책 (서버에서 전달한 Cache-Control Header를 그대로 따름)
- reloadIgnoringLocalCacheData : local 캐시를 무시하고 항상 네트워크에 접속하도록 설정하는 정책
- returnCacheDataDontLoad : 네트워크에 접속하지 않고 항상 local 캐시를 사용하도록 설정하는 정책
- returnCacheDataElseLoad : local 캐시를 확인하고 캐시가 없는 경우에만 네트워크에 접속하도록 설정하는 정책
- Cache를 저장하는 기간이 너무 길면 이미 관련 데이터가 업데이트되었음에도 사용자는 예전 데이터를 보게될 위험이 있습니다. 따라서 이를 적절히 관리하도록 하는 것이 중요합니다.
- Cache 관련 설정은 Request 혹은 Session 단계에서 설정이 가능합니다.
- 이렇게 저장되는 Cache에는 단순한 Data 뿐 아니라 서버 응답과 관련된 메타 데이터도 함께 저장됩니다. 저장 타입은 CachedURLResponse 입니다.
Request에서 설정하기
- Request에서 Cache를 설정하는 방법을 알아봅시다.
var request = URLRequest(url: url)
request.cachePolicy = .returnCacheDataElseLoad
- request에 cachePolicy를 지정하기만하면 됩니다.
- 다만 이와 같이 returnCacheDataElseLoad 정책을 사용할 경우 캐시의 저장기간이 가장 중요합니다. 앞서 언급했듯이 이미 관련 데이터가 업데이트되었음에도 사용자는 예전 데이터를 보게될 위험이 있기 때문입니다.
- Swift에서는 Cache를 삭제하는 다양한 메서드를 제공하지만, 제대로 동작하지 않는 경우가 더 많아 전체 캐시를 삭제하는 메서드를 제외하고는 사용하지 않는다고 합니다.
- 따라서 Cache를 삭제하지 않고 Cache Policy를 임시로 바꾸는 방법으로 문제를 해결합니다.
var lastDate: Data?
...
if lastDate.timeIntervalSinceNow < -5 {
request.cachePolicy = .reloadIgnoringLocalCacheData
lastDate = Date()
} else {
request.cachePolicy = .returnCacheDataElseLoad
}
- 이런 방식입니다.
- 마지막으로 접근한 시간과 현재시간을 비교해 캐시정책을 임시로 바꿔 접근하도록합니다.
session.configuration.urlCache?.removeAllCachedResponses()
- 참고로 전체 캐시를 삭제하는 방법은 위와 같습니다.
Session에서 설정하기
- 다음으로 Session에서 설정하는 방법입니다.
let config = URLSessionConfiguration.default
config.requestCachePolicy = .returnCacheDataElseLoad
let session = URLSession(configuration: config, delegate: self, delegateQueue: OperationQueue.main)
- config에서 requestCachePolicy를 설정할 수 있습니다.
- 다만 Request에서 Cache Policy를 설정했다면 그 설정이 더 우선적으로 적용됩니다.
Delegate를 사용하여 캐시에 추가적인 처리하기
- URLSessionDataDelegate에는 cache가 처리되기 전에 호출되는 Delegate method가 존재합니다.
func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, willCacheResponse proposedResponse: CachedURLResponse, completionHandler: @escaping (CachedURLResponse?) -> Void) {
completionHandler(proposedResponse)
}
- proposedResponse에는 현재 캐시에 저장되려는 데이터입니다.
- 이를 completionHandler에 전달하면 캐시에 저장이 수행됩니다.
func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, willCacheResponse proposedResponse: CachedURLResponse, completionHandler: @escaping (CachedURLResponse?) -> Void) {
guard let url = proposedResponse.response.url else {
completionHandler(nil)
return
}
if url.scheme == "https" {
let response = CachedURLResponse(response: proposedResponse.response, data: proposedResponse.data, userInfo: proposedResponse.userInfo, storagePolicy: .allowedInMemoryOnly)
completionHandler(response)
} else {
let response = CachedURLResponse(response: proposedResponse.response, data: proposedResponse.data, userInfo: proposedResponse.userInfo, storagePolicy: .notAllowed)
completionHandler(response)
}
}
- 이런 방식을 응용하면 위와 같은 처리가 가능해집니다.
- url의 scheme이 https인 경우에만 메모리 캐싱을 허용하고 있습니다.