iOS
비동기적으로 NS 캐쉬를 사용하여, 네트워크 통신 줄이기
고감귤
2024. 3. 19. 16:20
앱 개발 기능 중, 게시글에서 사용되는 다수의 이미지를 네트워크에서 로드해야 하는 상황이 발생했습니다.
이미지 특성상 데이터의 사이즈가 크기 때문에, 이미지를 매번 불러올 때마다 이미지를 로딩하는 화면을 보아야 하는 상황이었습니다.
최적화 전 - 게시글에 해당하는 이미지를 매번 네트워크에서 요청하는 방식
static func loadImageFromURL(url: URL, completion: @escaping (UIImage?) -> Void) {
URLSession.shared.dataTask(with: url) { data, _, error in
if let error = error {
print("Error downloading image: \(error.localizedDescription)")
completion(nil)
return
}
if let data = data, let image = UIImage(data: data) {
completion(image)
} else {
completion(nil)
}
}.resume()
}
사용자가 보고 있는 게시글이 바뀔 때마다, 각각의 이미지가 loadImageFromURL 함수를 통해 불러오게 됩니다.
게시글을 보려고 메뉴를 선택 했을 때, 게시글에 해당하는 이미지들을 비동기적으로 로드하고 캐쉬에 저장하여 재사용한다면, 로딩 시간을 줄일 수 있다고 생각했습니다.
최적화 후 - 비동기적으로 이미지를 네트워크에서 요청한 후, NSCache에 저장하여 재사용하는 방식
static func loadImageFromURL(urlString: String, cnt: Int, completion: @escaping (UIImage) -> Void) {
DispatchQueue.global().async {
let cacheKey = NSString(string: urlString)
// NSCache는 Thread Safe함으로 여러 스레드에서 안전하게 사용할 수 있습니다.
if let cachedImage = ImageCacheManager.shared.object(forKey: cacheKey) {
completion(cachedImage)
return
}
if let url = URL(string: urlString) {
URLSession.shared.dataTask(with: url) { data, _, error in
if let error = error {
print("Error downloading image: \(error.localizedDescription)")
if cnt <= 2 {
UIImage.loadImageFromURL(urlString: urlString, cnt: cnt+1) { out in
completion(out)
}
}
else {
completion(UIImage())
}
return
}
if let data = data, let image = UIImage(data: data) {
ImageCacheManager.shared.setObject(image, forKey: cacheKey)
completion(image)
} else {
completion(UIImage())
}
}.resume()
}
}
}
이로써,
1. DispatchQueue.global().async로 비동기적으로 task 처리
2. NSCache를 사용하여, 이미지가 저장되어 있다면 재사용
3. 이미지가 없다면, URLSession.shared.dataTask를 통해서 url에 해당하는 요청을 백그라운드 스레드에서 처리
4. 네트워크 통신 실패 시, 재귀적으로 이미지 재요청 [ 최대 3번 - 로딩이 너무 길어도 사용자에게 불편함을 줄 것이라 판단 ]
5. escaping closure로 비동기 처리문이 끝난 후 Image 데이터를 반환하여 Image 뷰를 생성
하는 방식으로 이미지 로딩시간을 줄일 수 있게 되었습니다.