일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | ||||||
2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 | 29 |
30 | 31 |
- 대표
- 반응형
- ios
- collectionview
- Kingfisher
- CS
- 동시성
- 구름톤 유니브
- MVVM
- mvc
- 프로토콜
- 실습
- WeatherKit
- RxSwift
- 스트럭트
- 이론
- SwiftUI
- struct
- 토이프로젝트
- async
- 앱개발
- 네트워크
- GCD
- swift
- 세종대학교
- 학과별커뮤니티
- 기초문법
- Optional
- 옵셔널
- uikit
- Today
- Total
스윞한 개발자
Kingfisher 캐싱 톺아보기 본문
안녕하세요! 이번 포스팅은 iOS 앱 개발을 하며 안 쓸 수 없는 라이브러리 Kingfisher에 대해 톺아보려고 합니다.
Kingfisher를 사용하는 방법은 많은 자료들이 있으니! 저는 이번 포스팅에서 캐싱 기능에 대해 정리해 보겠습니다.

먼저,
Kingfisher란?
먼저 공식문서를 살펴보겠습니다. 개발을 하다 Assets, System에 존재하지 않는 Url 형태의 이미지(웹으로부터의)를 서버를 통해 다운로드 및 캐싱할 때! 앱에서 편하게 사용할 수 있도록 제공하는 라이브러리입니다.
https://github.com/onevcat/Kingfisher
GitHub - onevcat/Kingfisher: A lightweight, pure-Swift library for downloading and caching images from the web.
A lightweight, pure-Swift library for downloading and caching images from the web. - onevcat/Kingfisher
github.com
Kingfisher 라이브러리는 많은 기능을 제공하고 있습니다. 공식 문서에서 알려주는 기능들만 나열하더라도
1. 비동기 이미지 다운로드 및 캐싱.
2. URLSession 기반 네트워킹 또는 로컬에서 제공된 데이터에서 이미지 로드.
3. 유용한 이미지 프로세서 및 필터 제공.
4. 메모리와 디스크 모두에 대한 다중 계층 하이브리드 캐시.
5. 캐시 동작에 대한 정밀한 제어. 사용자 정의 가능한 만료 날짜 및 크기 제한.
6. 취소 가능한 다운로드 및 이전에 다운로드한 콘텐츠를 자동으로 재사용하여 성능을 개선.
7. 독립적인 구성 요소. 필요에 따라 다운로더, 캐싱 시스템 및 이미지 프로세서를 별도로 사용.
8. 이미지를 미리 페치하여 캐시에서 표시하여 앱을 강화.
9. URL에서 이미지를 직접 설정하기 위한 UIImageView, NSImageView, NSButton, UIButton, NSTextAttachment, WKInterfaceImage, TVMonogramView 및 CPListItem 확장.
10. 이미지 설정 시 내장된 전환 애니메이션.
11. 이미지를 로드하는 동안 사용자 정의 가능한 플레이스홀더 및 표시기.
12. 쉽게 확장 가능한 이미지 처리 및 이미지 형식.
13. 저데이터 모드 지원.
14. SwiftUI 지원.
15. Swift 6 및 Swift Concurrency(엄격 모드) 준비.
16. 라이브 사진 로드 및 캐시.
이렇게나 많이 제공된다고 합니다. 저는 지금까지 앱을 개발해보며.. 이미지를 로드하거나 placeholder를 넣는 정도? 만 사용해 보았는데 공식문서를 읽어보니 많은 기능을 제공해주고 있었다니.. 오늘은 이 많은 기능 중에! 캐싱에 대해 다뤄보겠습니다.
캐싱이란?
캐싱은 파일의 복사본을 임시 저장 위치에 저장해 빠르게 액세스할 수 있도록 하는 것입니다! 앱 개발을 할 때 서버를 통해 이미지를 로드할 상황이 많이 있습니다. 캐싱기능을 잘 활용하면 자동으로 url을 판별하여 이미 접근한 경우에는 또 서버를 불러오지 않고 비동기적으로 캐시에서 저장된 데이터로 이미지를 가져옵니다. 이렇게 되면, 통신 시간이 아무래도 많이 감소될 것입니다.
Kingfisher의 깃허브에 있는 Cheat Sheet의 Cache를 살펴보겠습니다.
https://github.com/onevcat/Kingfisher/wiki/Cheat-Sheet#cache
Cheat Sheet
A lightweight, pure-Swift library for downloading and caching images from the web. - onevcat/Kingfisher
github.com
Kingfisher는 캐시된 이미지들을 관리하는데 ImageCache(하이브리드 캐싱 시스템 = 메모리 + 디스크) 사용합니다. 메모리 저장소와 디스크 저장소에 유지됩니다.
1. 메모리 캐싱
최근에 사용한 이미지를 메모리에 저장하여 빠르게 접근이 가능합니다. 앱을 종료하면 메모리 캐시는 사라집니다.
2. 디스크 캐싱
이미지를 디스크에 저장하여 종료한 후에도 유지합니다. 사용자는 캐시 된 이미지의 경로를 지정하거나 기본 설정을 할 수 있습니다. Library/Caches에 저장됩니다!
Kingfisher는 ImageCache를 제공합니다. 이미지를 로드할 URL자체가 Cache Key가 됩니다. 다른 캐시 키를 사용하려면? 이미지 리소스를 사용해서 지정할 수 있습니다.
let resource = ImageResource(downloadURL: url, cacheKey: "my_cache_key")
imageView.kf.setImage(with: resource)
캐시에 이미지가 있는지 확인하기 위해서는 URL자체 혹은 설정한 캐시키를 이용해 확인할 수 있습니다.
let cache = ImageCache.default
let cached = cache.isCached(forKey: cacheKey)
// 어디에 캐시가 저장되어있는지 알수 있습니다.
let cacheType = cache.imageCachedType(forKey: cacheKey)
// `.memory`, `.disk` or `.none`.
캐시에 이미지가 어디에 저장되어 있는지도 확인 가능합니다.
만약 캐시에 이미지가 저장되어있다면, 서버와의 통신이 불가능할 때 등 이 기능을 다방면으로 활용 가능합니다.
cache.retrieveImage(forKey: "cacheKey") { result in
switch result {
case .success(let value):
print(value.cacheType)
print(value.image)
case .failure(let error):
print(error)
}
}
마찬가지로 설정된 캐시키를 이용해 캐시에 저장된 이미지를 불러올 수 있습니다.

캐시에 대한 설정
만약 메모리 저장소에 저장되어 있는 경우! 메모리 캐시의 사이즈와 메모리 캐시가 가질 수 있는 이미지의 개수 등도 설정이 가능합니다.
cache.memoryStorage.config.totalCostLimit = 300 * 1024 * 1024
cache.memoryStorage.config.countLimit = 150
기본적으로 메모리 캐시의 제한된 캐시의 용량은 메모리의 25%이며 이미지의 수에는 제한이 없습니다.
또 디스크 저장의 경우! sizeLimit도 설정이 가능합니다.
cache.diskStorage.config.sizeLimit = = 1000 * 1024 * 1024
메모리 저장소의 이미지는 마지막 액세스 후 5분 후에 만료되는 반면, 디스크 저장소의 이미지는 일주일이 걸립니다. 기본적인 설정은 저렇지만 만료 설정 또한 가능합니다.
cache.memoryStorage.config.expiration = .seconds(600)
cache.diskStorage.config.expiration = .never
하지만, 사용하다보면 모든 이미지에 대해 캐시 만료일을 동일하게 적용하는 상황은 잘 없을 것입니다. 그래서 특정이미지에 대해 캐시 만료일을 설정 가능하게 하는 기능 또한 제공해 주고 있습니다.
imageView.kf.setImage(with: url, options: [.memoryCacheExpiration(.never)])
이처럼, 이미지를 설정할때 해당 이미지에 대한 만료일을 설정할 수 있습니다.
기본적으로, Kingfisher 라이브러리를 사용한다면 이미지를 자동으로 캐시에 저장합니다. 하지만, 수동으로 이미지를 캐싱 및 삭제를 할 수 있습니다.
let image: UIImage = //...
cache.store(image, forKey: cacheKey)
cache.default.removeImage(forKey: cacheKey)
이렇듯, Kingfisher 라이브러리는 캐싱에 대해 여러 가지 기능을 제공하고 있습니다.
# 메모리 저장소
public enum MemoryStorage {
public class Backend<T: CacheCostCalculable> {
let storage = NSCache<NSString, StorageObject<T>>()
var keys = Set<String>()
private var cleanTimer: Timer? = nil
private let lock = NSLock()
public var config: Config {
didSet {
storage.totalCostLimit = config.totalCostLimit
storage.countLimit = config.countLimit
}
}
public init(config: Config) {
self.config = config
storage.totalCostLimit = config.totalCostLimit
storage.countLimit = config.countLimit
cleanTimer = .scheduledTimer(withTimeInterval: config.cleanInterval, repeats: true) { [weak self] _ in
guard let self = self else { return }
self.removeExpired()
}
}
메모리 저장소를 살펴보면 앞서 말했듯, Cache Key, 저장소의 사이즈, 개수등을 설정할 수 있는 부분으로 구성되어 있습니다. NSCache 타입으로 되어있으며, NSLock을 통해 처리해주고 있습니다.
실제 로딩 시간을 비교해보기 위해 프로젝트에 한번 적용시켜 보겠습니다.
우선 Kingfisher를 이용해 이미지를 가져오는 과정입니다. 우선 캐시에 저장된 캐시키가 따로 설정하지 않았기 때문에 imageURL이 캐시키가 될 것입니다. 이를 통해 캐시에 이미지가 저장되어 있는지 확인하고 불러올 수 있습니다.
이처럼, disk에 이미지들이 캐싱되어 있는 것을 확인 할 수 있습니다. 저는 Kingfisher 라이브러리에서 제공하는 캐싱 기능 말고 캐싱의 기능을 비슷하게 만들어 보았습니다.
NSLock을 이용해서 충돌을 방지했고, 싱글톤 패턴으로 구현해 보았습니다.
final class ImageCacheManager {
static let shared = ImageCacheManager()
private var cache = [String: String]()
private let lock = NSLock()
private init() {}
func getImage(for name: String) -> String? {
lock.lock()
defer { lock.unlock() }
return cache[name]
}
func setImage(_ image: String, for name: String) {
lock.lock()
defer { lock.unlock() }
cache[name] = image
}
}
서버로 부터 해당 이미지를 불러올 때! 저장된 이미지에 대해서는 로딩 시간을 줄이기 위해 이 캐싱 기능을 미리 사용했습니다.
final class ImageCacheManager {
static let shared = ImageCacheManager()
private let cache = NSCache<NSString, NSString>()
private init() {}
func getImage(for name: String) -> String? {
return cache.object(forKey: name as NSString) as String?
}
func setImage(_ image: String, for name: String) {
cache.setObject(image as NSString, forKey: name as NSString)
}
}
NSCache를 이용하면, cache 변수를 NSCache<NSString, NSString>()으로 바꿔주고 사용할 수 있습니다. NSCache(Key-Value 타입)를 사용하게 되면 자동으로 캐시의 메모리를 관리하기 때문에 공간이 부족하면 자동으로 캐시가 정리될 수 있습니다.
이번 포스팅에서는, Kingfisher 라이브러리의 캐싱 기능과 Kingfisher를 쓰지 않고 캐싱 기능을 구현하는 법에 대해 톺아보았습니다!
다음 포스팅에서는 더 유익한 내용을 정리해보겠습니다! 잘못된 점에 대한 조언은 언제든지 환영입니다!! 감사합니다!

'Swift 이론' 카테고리의 다른 글
옵셔널 내부구조 파해치기!👊🏻 (2) | 2025.01.05 |
---|---|
ViewController의 생명주기(+viewIsAppearing) 톺아보기 (2) | 2025.01.05 |
Saving Data - UserDefault에 대한 정리 (2) | 2025.01.01 |
앱 Thinning (0) | 2024.12.30 |
Mac Catalyst 살펴보기 (2) | 2024.12.29 |