extension OpenMarketListCollectionViewCell {
// MARK: - configure cell
func configure(itemInformation: [OpenMarketItem], index: Int) {
let thumbnailDownloadLink = itemInformation[index].thumbnails[0]
self.itemThumbnail.applyDownloadedImage(link: thumbnailDownloadLink )
}
}
상품목록에 네트워크통신으로 불러온 이미지를 적용하기 위해 위와 같이 applyDownloadedImage()
메서드를 사용하였습니다.
extension UIImageView {
private var imageLoader: CachedImageLoader {
return CachedImageLoader(imageDownloader: ImageDownloader(network: Network()))
}
func applyDownloadedImage(link: String) {
imageLoader.loadImageWithCache(with: link) { image in
DispatchQueue.main.async {
self.image = image
}
}
}
}
해당 메서드는 UIImageView
를 확장한 뒤 네트워크 작업을 하기 위해 imageLoader
라는 CachedImageLoder
타입 모듈을 연산프로퍼티로 선언한 뒤 loadImageWithCache()
메서드를 통해 이미지를 불러오도록 구현이 되어있습니다.
final class CachedImageLoader {
private let cache = ImageCache<NSString, UIImage>()
private let imageDownloader: ImageDownloadable
init(imageDownloader: ImageDownloadable) {
self.imageDownloader = imageDownloader
}
func loadImageWithCache(with link: String, completion: @escaping (UIImage) ->()) {
let nsText = link as NSString
if let cachedImage = cache[nsText] {
return completion(cachedImage)
}
imageDownloader.downloadImage(url: link.createURL()) { result in
switch result {
case .failure(let error):
NSLog(error.localizedDescription)
case .success(let image):
self.cache.insert(image, forkey: nsText)
return completion(image)
}
}
}
}
CachedImageLoader
타입은 위와 같이 구현되어 있습니다.
내부에는 cache
와 imageDownloader
프로퍼티를 갖고 있고 imageDownloader
를 통해 받아온 초기 이미지 데이터를 클래스 내부 cache에 저장하며 UIImage를 반환하게 되고 추후에 동일한 이미지를 받아오게 되면 cache 내부에 해당 이미지가 있는지 확인작업을 한 뒤 만약 캐시에 해당 이미지가 존재한다면 받아온 이미지를 적용하는 로직을 적용하였습니다.
문제가 없어보이는 구조이지만 왠일인지 프로젝트를 실행해도 이미지가 캐시처리가 안되는 것을 확인 하였습니다. 뭐가 문제일까요...
이렇게 막히는 부분이 있으면 당연히 디버깅을 먼저 해봐야겠죠?
열심히 디버깅을 하다 이상한 부분을 발견하였습니다.
CachedImageLoader
객체 내에서 self.cache.insert(image, forKey: nsText)
해당 부분에 breakpoint를 찍은 뒤 imageCache를 살펴보니 다음과 같은 log를 확인할 수 있었습니다.
<ImageCache<NSString, UIImage>: 0x60000330d580>
<ImageCache<NSString, UIImage>: 0x60000330d940>
<ImageCache<NSString, UIImage>: 0x60000330da80>
두둥! 맙소사. 매번 insert를 할 때마다 ImageCache의 주소가 달라지네요.
왜 그런가 곰곰히 생각 해 보니...UIImageView의 확장에서 연산 프로퍼티를 통해서 imageLoader를 불러올 때
private var imageLoader: CachedImageLoader {
return CachedImageLoader(imageDownloader: ImageDownloader(network: Network()))
}
이런식으로 매번 새로운 imageLoader 인스턴스를 생성한 뒤 해당 imageLoader의 cache에다가 image를 저장하고 있던거였습니다. (저는 바보입니다..)
그렇기 때문에 각 cell에서 self.itemThumbnail.applyDownloadedImage(link: thumbnailDownloadLink )
를 할 때마다 각 각 다른 주소를 갖는 imageLoader 인스턴스의 imageCache에 저장이 되고 있던거죠 ㅡ,ㅡ
UIImageView
의 extension 에서 ImageLoader를 활용하여 Cache를 저장할 수 있고 ViewController의 imageLoader를 활용해서 이미지 캐시를 저장할 수 있는 공용 객체가 필요했고 저는 싱글톤 패턴이 이 때 활용하기에 가장 알맞은 것 같아서 Cache를 공용으로 저장할 수 있는 CacheManager
클래스를 만들었습니다.
import UIKit
final class CacheManager {
let cache = ImageCache<NSString, UIImage>(totalCostLimit: 500 * 1024 * 1024)
static let shared = CacheManager()
private init() { }
}
그리고 CachedImageLoader의 내부를 다음과 같이 수정하였어요
func loadImageWithCache(with link: String, completion: @escaping (UIImage) ->()) {
let nsText = link as NSString
if let cachedImage = CacheManager.shared.cache[nsText] {
return completion(cachedImage)
}
imageDownloader.downloadImage(url: link.createURL()) { result in
switch result {
case .failure(let error):
NSLog(error.localizedDescription)
case .success(let image):
CacheManager.shared.cache.insert(image, forkey: nsText)
return completion(image)
}
}
}
이렇게 구현하니 정상적으로 cache에 이미지가 저장되는 것을 확인할 수 있었습니다.
extension과 연산프로퍼티에 대해 오늘 하나 더 알아간 것 같아서 뿌듯합니다! 앞으로 오늘과 같은 실수는 더 이상 하지 않을거에요!!