일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- 반응형
- uikit
- 토이프로젝트
- 라이브러리
- 캐시
- mvc
- SwiftUI
- 동시성
- 네트워크
- 이론
- WeatherKit
- async
- 세종대학교
- snkit
- collectionview
- 기초문법
- 스트럭트
- Kingfisher
- 프로토콜
- 옵셔널
- RxSwift
- 구름톤 유니브
- observable
- 학과별커뮤니티
- struct
- 실습
- ios
- CS
- 대표
- swift
- Today
- Total
스윞한 개발자
SNKit #2: 메모리 캐시의 구현 본문
안녕하세요! 이번 포스팅에서는 최근 개발을 시작한 SNKit의 개발 첫 번째 이야기를 다뤄보려고 합니다.
첫 번째 글에서는 SNKit의 개발 배경과 전체적인 기획에 대해 살펴보았습니다. 이번 글에서는 SNKit의 핵심 기능인 메모리 캐시와 디스크 캐시의 구현에 대해 자세히 알아보겠습니다.

캐시 시스템 설계 요구사항
효율적인 이미지 캐싱 시스템을 구축하기 위해 다음과 같은 설계 요구사항을 구성했습니다!!
- 메모리 효율성: 과도한 메모리 사용 방지하기
- 지속성: 앱 재시작 후에도 필요한 데이터 유지하기
- 만료 관리: 오래된 캐시 항목을 자동으로 정리하기
- 용량 제한: 지정된 용량을 초과하지 않도록 자동으로 관리하기
- 스레드 안전성: 여러 스레드에서 동시에 접근해도 안전하게 동작하도록 관리
- 메타데이터 관리: 이미지 외에도 관련 메타데이터(ETag 등)도 관리해줄 것입니다!
이러한 요구사항을 충족시키기 위해 메모리 캐시와 디스크 캐시를 각각 독립적으로 구현한 후, 이를 통합한 하이브리드 캐시 시스템을 구축하겠습니다.
Cacheable 프로토콜 구현
SNKit에서는 Cacheable 프로토콜을 통해 이를 정의했습니다.
public protocol Cacheable: Sendable {
var image: UIImage? { get set }
var imageURL: URL { get }
var identifier: String { get }
var eTag: String? { get }
}
- image: 캐시할 실제 이미지 객체입니다.
- imageURL: 이미지의 원본 URL입니다.(Key로 사용합니다)
- identifier: 캐시 시스템 내에서 사용할 고유 식별자입니다.
- eTag: HTTP ETag 값으로, 이미지 변경 여부를 확인하는 데 사용됩니다.
struct CacheableImage: Cacheable {
var image: UIImage?
let imageURL: URL
let identifier: String
let eTag: String?
public init(imageURL: URL, identifier: String? = nil, eTag: String? = nil) {
self.imageURL = imageURL
self.identifier = identifier ?? imageURL.absoluteString
self.image = nil
self.eTag = eTag
}
public init(image: UIImage, imageURL: URL, identifier: String? = nil, eTag: String? = nil) {
self.image = image
self.imageURL = imageURL
self.identifier = identifier ?? imageURL.absoluteString
self.eTag = eTag
}
}
CacheableImage 구조체는 두 가지 초기화 방법을 제공합니다!
- URL만으로 초기화 - 아직 이미지를 로드하지 않은 상태
- 이미지와 URL로 초기화 - 이미 이미지를 다운로드한 상태
식별자가 명시적으로 제공되지 않으면 URL 문자열을 자동으로 식별자로 사용하는 편의 기능도 포함되어 있습니다.
메모리 캐시 구현 세부사항
메모리 캐시는 빠른 접근 속도가 강점이지만, 앱이 종료되면 모든 데이터가 소멸된다는 단점이 있습니다.
SNKit에서는 Apple의 NSCache 클래스를 기반으로 메모리 캐시를 구현했습니다.
먼저, 캐시 항목을 나타내는 CacheItem 클래스를 정의했습니다.
final class CacheItem: NSObject {
let image: UIImage
let identifier: String
let eTag: String?
let createdAt: Date
var lastAccessedAt: Date
init(image: UIImage, identifier: String, eTag: String? = nil, createdAt: Date = Date()) {
self.image = image
self.identifier = identifier
self.eTag = eTag
self.createdAt = createdAt
self.lastAccessedAt = Date()
super.init()
}
}
CacheItem은 단순한 이미지뿐만 아니라 관련 메타데이터도 함께 저장합니다.
- createdAt: 캐시 항목이 생성된 시간
- lastAccessedAt: 마지막으로 접근한 시간
- eTag: HTTP ETag 값
메모리 관리를 효율적으로 하기 위해 생성된 시간과 마지막 접근시간을 함께 저장해 LRU 알고리즘을 이용할 예정입니다.
다음으로, 메모리 캐시의 본체인 MemoryCache 클래스를 구현했습니다!!
final class MemoryCache {
private let cache = NSCache<NSString, CacheItem>()
init(capacity: Int) {
cache.totalCostLimit = capacity
}
func store(_ cacheable: Cacheable) {
guard let image = cacheable.image else { return }
let key = cacheable.identifier as NSString
let cacheItem = CacheItem(
image: image,
identifier: cacheable.identifier,
eTag: cacheable.eTag,
createdAt: Date()
)
let cost = Int(image.size.width * image.size.height * 4)
cache.setObject(cacheItem, forKey: key, cost: cost)
}
func retrieve(with identifier: String) -> UIImage? {
return cache.object(forKey: identifier as NSString)?.image
}
func remove(with identifier: String) {
cache.removeObject(forKey: identifier as NSString)
}
}
더 많은 코드가 있지만! 간단하게 설명하기 위해 기본적인 코드만 가져왔습니다! 자세한 코드는 배포 후에 ㅎㅎ
MemoryCache 클래스의 주요 특징은 다음과 같습니다.
# NSCache 활용
애플의 NSCache를 내부 저장소로 사용하여 시스템의 메모리 관리 기능을 최대한 활용합니다. 애플에서 말하길 NSCache는 내부적으로 스레드 세이프하기 때문에! lock과 같은 작업을 걸어주지 않았습니다! 아마 애플에서 잘(?) 구성했을 듯합니다..ㅎㅎ
# 메모리 관리
NSCache는 시스템 메모리 부족 시 자동으로 일부 항목을 제거하는 기능을 제공합니다. 이는 메모리 사용량을 효율적으로 관리하는 데 도움이 됩니다.
이번 포스팅에서는 메모리 캐시에 대해 구현해 보았습니다!! 다음 포스팅에서는 더 많이 개발해서.... 더 나은 포스팅으로 찾아오겠습니다!!
제가 처음 캐시 라이브러리를 배포해 보는 과정이라, 잘못된 내용에 대한 피드백은 언제든 환영입니다!! 감사합니다!

'프로젝트' 카테고리의 다른 글
SNKit #4: ETag 검증과 이미지 다운로드 시스템 (2) | 2025.04.08 |
---|---|
SNKit #3: 디스크/하이브리드 캐시의 구현 (0) | 2025.03.31 |
SNKit #1: iOS 이미지 캐싱 라이브러리 기획 (0) | 2025.03.16 |
그래빗 : 성공적인 투자 GraBit과 함께! (0) | 2024.04.08 |
수달 : 집수리 달인 (0) | 2024.03.10 |