Cache Policy - Team-HGD/SniffMEET GitHub Wiki

์ž‘์„ฑ์ž: ๋ฐฐํ˜„์ง„

์ •์ฑ…

๊ฐ„๋‹จํ•˜๊ฒŒ ์ƒ๊ฐํ•ด๋ณธ ์ •์ฑ…

์บ์‹œ ์ƒ์„ฑ ์‹œ๊ฐ„ ์ถ”๊ฐ€ํ•˜๊ณ , ๋งŒ๋ฃŒ ์‹œ๊ฐ„์„ ์ •ํ•ด ์บ์‹œ ์ƒ์„ฑ๋œ์ง€ ๋งŒ๋ฃŒ ์‹œ๊ฐ„๋งŒํผ์ด ์ง€๋‚œ๋‹ค๋ฉด ์‚ญ์ œ

  • CacheableImage์— createdDate ์ถ”๊ฐ€
  • expiredTime ์ถ”๊ฐ€
  • ์บ์‹œ์˜ createdDate๋กœ๋ถ€ํ„ฐ expiredTime ๋งŒํผ์˜ ์‹œ๊ฐ„์ด ์ง€๋‚œ ๊ฒฝ์šฐ ์‚ญ์ œ

์บ์‹œ์ •์ฑ…์— ๊ด€ํ•˜์—ฌ

LRU - ์‚ฌ์šฉ

๊ฐ€์žฅ ์ตœ๊ทผ์— ์‚ฌ์šฉ๋œ ๋ฐ์ดํ„ฐ๋ฅผ ์บ์‹œ์— ์œ ์ง€ํ•˜๊ณ , ์˜ค๋ž˜๋œ ๋ฐ์ดํ„ฐ๋ฅผ ์ œ๊ฑฐํ•˜๋Š” ๋ฐฉ์‹

์ฆ‰, ์ž์ฃผ ์‚ฌ์šฉ๋˜๋Š” ๋ฐ์ดํ„ฐ๋Š” ์บ์‹œ์— ๋‚จ๊ธฐ๊ณ  ์‚ฌ์šฉ๋˜์ง€ ์•Š๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ์ œ๊ฑฐํ•œ๋‹ค.

  • ๋‹จ์  : ์ฐธ์กฐ๋œ ์‹œ๊ฐ„ ๋˜๋Š” ์ตœ๊ทผ ์‚ฌ์šฉ์—ฌ๋ถ€๋ฅผ ๊ธฐ๋กํ•ด์•ผ ํ•œ๋‹ค.
  • ์žฅ์  : ๋งŽ์ด ์‚ฌ์šฉ๋˜๋Š” ์บ์‹œ ์ •์ฑ… ์ค‘ ํ•˜๋‚˜์ด๋‹ค.

๋ฉ”๋ชจ๋ฆฌ ์บ์‹œ์™€ ๋””์Šคํฌ ์บ์‹œ ์ •์ฑ…

ํ˜„์žฌ ๋ฉ”๋ชจ๋ฆฌ ์บ์‹œ๋Š” NSCache๋ฅผ ์‚ฌ์šฉํ•จ์œผ๋กœ์จ ์ž๋™ ์บ์‹œ ์‚ญ์ œ ๊ธฐ๋Šฅ์ด ์ถ”๊ฐ€๋˜์–ด ์žˆ๋‹ค. ๋”ฐ๋ผ์„œ ์บ์‹œ ์‚ญ์ œ ๊ธฐ๋Šฅ์ด ์—†๋Š” ๋””์Šคํฌ ์บ์‹œ์— ์šฐ์„ ์ ์œผ๋กœ ์ •์ฑ… ์ ์šฉ ์‹œ๋„ํ–ˆ๋‹ค.

๋””์Šคํฌ ์บ์‹œ ์ •์ฑ… - LRU

  • cacheLimit : ๋””์Šคํฌ ์บ์‹œ์˜ ์ตœ๋Œ€ ์šฉ๋Ÿ‰ (์บ์‹œ์— ์ €์žฅํ•  ์ตœ๋Œ€ ์ด๋ฏธ์ง€ ์ˆ˜)
    • ์šฉ๋Ÿ‰์ด ์•„๋‹Œ ๊ฐœ์ˆ˜๋กœ ์ œํ•œ์„ ์„ค์ •ํ•˜๊ฒŒ๋˜๋ฉด ํฐ ์šฉ๋Ÿ‰ ๋ฐ์ดํ„ฐ๊ฐ€ ์ €์žฅ๋˜๋Š” ๊ฒฝ์šฐ ์บ์‹œ ์šฉ๋Ÿ‰์„ ๋น ๋ฅด๊ฒŒ ์ฐจ์ง€ํ•ด๋‚˜๊ฐˆ ์ˆ˜ ์žˆ๋‹ค. โ†’ ๊ทธ๋ƒฅ ์šฉ๋Ÿ‰์œผ๋กœ ์ œํ•œํ•˜๋Š” ๊ฒƒ์ด ๋” ๋‚˜์„๊นŒ? ๊ณ ๋ฏผํ•˜๊ฒŒ ๋จ
    • ํ•˜์ง€๋งŒ ๋ฉ”์ดํŠธ ๋ชฉ๋ก์— ์‚ฌ์šฉ๋˜๋Š” ์ด๋ฏธ์ง€๋“ค์€ ๋‹ค์šด์ƒ˜ํ”Œ๋ง๋œ ์ธ๋„ค์ผ ์ด๋ฏธ์ง€์ด๊ธฐ ๋•Œ๋ฌธ์— ํŠน๋ณ„ํžˆ ํŠ€๋Š” ์šฉ๋Ÿ‰์—†์ด ์ €์žฅ๋  ์ˆ˜ ์žˆ์„ ๊ฒƒ์ด๋‹ค. โ†’ ์ด๋ฏธ์ง€ ์ˆ˜๋กœ ์ œํ•œํ•ด๋„ ๋ฌธ์ œ ์—†์„ ๊ฒƒ.
    • ๊ฐœ์ˆ˜๋กœ ์ •์ฑ…์„ ์„ค์ •ํ•˜๋Š” ๊ฒƒ์— ๋Œ€ํ•ด์„œ๋„ ์ข‹๋‹ค๊ณ ๋Š” ์ƒ๊ฐ์ด ๋“ค์ง€ ์•Š๋„ค์š”ใ…  ์ €ํฌ๋Š” ์ด๋ฏธ์ง€๋ฅผ ๋”ฐ๋กœ ๋‹ค์šด์ƒ˜ํ”Œ๋ง์„ ํ•˜์ง€ ์•Š์œผ๋‹ˆ๊นŒ ์นœ๊ตฌ๊ฐ€ ์—…๋กœ๋“œํ•œ ์ด๋ฏธ์ง€์— ๋”ฐ๋ผ์„œ ๋””์Šคํฌ์บ์‹œ์— ์ฐจ์ง€ํ•˜๋Š” ์šฉ๋Ÿ‰์ด ์ฒœ์ฐจ๋งŒ๋ณ„์ด ๋  ๊ฒƒ ๊ฐ™์•„์š”.
  • usageOrder : ๋””์Šคํฌ ์บ์‹œ์˜ ์‚ฌ์šฉ๋œ ์ˆœ์„œ๋ฅผ ๊ด€๋ฆฌํ•˜๋Š” ๋ฐฐ์—ด
    • ์บ์‹œ๊ฐ€ ์žฌ์ฐธ์กฐ๋˜์–ด ์‚ฌ์šฉ๋œ๋‹ค๋ฉด ํ•ด๋‹น ๋ฐฐ์—ด์—์„œ ์ˆœ์„œ๋ฅผ ๋ณ€๊ฒฝํ•ด์ฃผ์–ด์•ผํ•จ
      • updateDiskUsageOrder()
    • ์ง€์ •๋œ ๋””์Šคํฌ ์บ์‹œ ์ด๋ฏธ์ง€ ์ˆ˜(cacheLimit)๋ฅผ ๋„˜์–ด์„ ๋‹ค๋ฉด ๋ฐฐ์—ด ์† ๊ฐ€์žฅ ์˜ค๋ž˜๋œ ๋ฐ์ดํ„ฐ๋ฅผ ์‚ญ์ œํ•ด์•ผํ•จ
      • removeOldestDiskImage()

๋ฉ”๋ชจ๋ฆฌ ์บ์‹œ ์ •์ฑ… - NSCache ๋‚ด๋ถ€ ์ •์ฑ…

LRU์™€ LFU๋ฅผ ํ˜ผํ•ฉํ•œ ์ •์ฑ…์ด๋ผ๊ณ  ๋ณผ ์ˆ˜ ์žˆ๋‹ค.

๊ฐ€์žฅ ์˜ค๋ž˜์ „ ์ฐธ์กฐ๋œ ๊ฐ์ฒด๋ฅผ ์šฐ์„ ์ ์œผ๋กœ ํ™•์ธํ•˜๊ณ  ๊ทธ ์ฑ…์ฒด๊ฐ€ ์ฐธ์กฐ๋œ ํšŸ์ˆ˜๊ฐ€ ์บ์‹œ๊ฐ€ ๊ฐ€์ง„ ํ‰๊ท  ์ฐธ์กฐ ํšŸ์ˆ˜๋ณด๋‹ค ๋†’๋‹ค๋ฉด ์ž์ฃผ ์ฐธ์กฐ๋œ ๊ฐ์ฒด์ด๋ฏ€๋กœ, ๋‹ค์‹œ ์ฐธ์กฐ ๊ฐ€๋Šฅ์„ฑ์ด ์ƒ๊ฒจ ์‚ญ์ œ ๋Œ€์ƒ์œผ๋กœ ์ •ํ•˜์ง€๋Š” ์•Š๋Š”๋‹ค.

์ฆ‰, ์ฐธ์กฐํ•œ์ง€ ๊ฐ€์žฅ ์˜ค๋ž˜๋œ ๊ฐ์ฒด ์ค‘ ์ฐธ์กฐ ํšŸ์ˆ˜๊ฐ€ ํ‰๊ท  ์ฐธ์กฐ ํšŸ์ˆ˜๋ณด๋‹ค ์ ์€ ๊ฐ์ฒด๋ฅผ ์‚ญ์ œํ•œ๋‹ค.

[iOS] NSCache์˜ ์บ์‹œ ์ •์ฑ… ๋œฏ์–ด๋ณด๊ธฐ

[Swift] NSCache ์ดํ•ดํ•˜๊ธฐ

์บ์‹ฑ์œผ๋กœ ์ธํ•œ ๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์šฉ๋Ÿ‰ ์ฆ๊ฐ€๋Š” ์‹œ์Šคํ…œ ๋ฆฌ์†Œ์Šค์— ๋ถ€๋‹ด์„ ์ค„ ์ˆ˜ ์žˆ์–ด NSCache๋‚˜ cachesDirectory์˜ ์ž๋™ ์‚ญ์ œ ๊ธฐ๋Šฅ์— ์˜์กดํ•˜๊ธฐ๋ณด๋‹ค๋Š”, ์•ฑ์˜ ์ƒํ™ฉ์— ๋งž๋Š” ์ ์ ˆํ•œ ์บ์‹œ ์ •์ฑ…์„ ์ˆ˜๋ฆฝํ•˜์—ฌ ์ ์šฉํ•˜๋Š” ๊ฒƒ์ด ์ค‘์š”ํ•˜๋‹ค๊ณ  ํ•œ๋‹ค.

ํ•˜์ง€๋งŒ, NSCache๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด Thread-Safe ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๋”ฐ๋กœ DispatchQueue๋ฅผ ์‚ฌ์šฉํ•˜๊ฑฐ๋‚˜ lock๋ฅผ ์ด์šฉํ•  ํ•„์š”๊ฐ€ ์—†๊ณ  ์ข€ ๋” ๊ฐ„๋‹จํ•˜๊ฒŒ ๊ตฌํ˜„์ด ๊ฐ€๋Šฅํ•˜๋‹ค.

๋งŒ์•ฝ LRU๋กœ ๋ณ€๊ฒฝํ•œ๋‹ค๋ฉด?

๋ฉ”๋ชจ๋ฆฌ ์บ์‹œ ์ •์ฑ…์„ NSCache๋ฅผ ์ด์šฉํ•ด ์ž๋™์œผ๋กœ ์‚ฌ์šฉํ•˜์ง€ ์•Š๊ณ , LRU๋ฅผ ์ด์šฉํ•œ ๋ฐฉ์‹์œผ๋กœ ๋ณ€๊ฒฝํ•œ๋‹ค๋ฉด ์บ์‹œ ์ œ๊ฑฐ ๊ธฐ์ค€์„ ์ง์ ‘ ์ง€์ •(๋””์Šคํฌ ์บ์‹œ ์ œํ•œ์ฒ˜๋Ÿผ)ํ•  ์ˆ˜ ์žˆ๋‹ค.

๋””์Šคํฌ ์บ์‹œ์— LRU ์ •์ฑ… ์ ์šฉํ•œ ๊ฒƒ๊ณผ ๋น„์Šทํ•œ ๋ฐฉ์‹์œผ๋กœ ๊ตฌํ˜„ํ•  . ์ˆ˜์žˆ๋‹ค. ๋‹ค๋ฅธ ์ ์€ ์ด๋ฏธ์ง€ ๋ฐ์ดํ„ฐ๋ฅผ ์ €์žฅํ•˜๋Š” ๋”•์…”๋„ˆ๋ฆฌ๊ฐ€ ํ•„์š”๋กœ ํ•ด์ง„๋‹ค๋Š” ์ ์ด๋‹ค. ์ด ๋”•์…”๋„ˆ๋ฆฌ๊ฐ€ ๋ฉ”๋ชจ๋ฆฌ ์บ์‹œ์˜ ์—ญํ• ์„ ํ•˜๊ฒŒ๋œ๋‹ค.

private var cachedImages: [String: CacheableImage] = [:]
private var usageOrder: [String] = []
private let cacheLimit: Int = 100

func saveMemoryCache(urlString: String, cacheableImage: CacheableImage) {
		// NSCache ๋ฐฉ์‹
    // cache.setObject(cacheableImage, forKey: urlString as NSString)

    if cachedImages[urlString] == nil && cachedImages.count >= cacheLimit {
        removeOldestImage()
    }

    cachedImages[urlString] = cacheableImage
    updateUsageOrder(for: urlString)
}

func imageFromMemoryCache(urlString: String) -> CacheableImage? {
		// NSCache ๋ฐฉ์‹
    // return cache.object(forKey: urlString as NSString)
    
		guard let image = cachedImages[urlString] else { return nil }
    updateUsageOrder(for: urlString)
    return image
}

private func updateUsageOrder(urlString: String) {
    if let index = usageOrder.firstIndex(of: urlString) {
        usageOrder.remove(at: index)
        usageOrder.append(urlString)
    }
}

private func removeOldestImage() {
    guard let oldestImage = usageOrder.first else { return }
    cachedImages.removeValue(forKey: oldestImage)
    usageOrder.removeFirst()
}

์บ์‹œ ์ •์ฑ… ์ ์šฉ ํ™•์ธ

ํ…Œ์ŠคํŠธ๋ฅผ ์œ„ํ•ด ๋””์Šคํฌ ์บ์‹œ ์ œํ•œ์„ ์ด๋ฏธ์ง€ 5๊ฐœ๋กœ ๋‘์—ˆ๋‹ค.

๋ฉ”์ดํŠธ ๋ชฉ๋ก์— 4๊ฐœ์˜ ๋ฐ์ดํ„ฐ๊ฐ€ ์กด์žฌํ• ๊ฒฝ์šฐ, (์•ฑ ์ฒซ ๋นŒ๋“œ ์‹œ์ ์ด๋ผ ์ „๋ถ€ ์ƒˆ๋กœ ์บ์‹œ์— ์ €์žฅ๋˜๋Š” ์ค‘) ์ •์ƒ์ ์œผ๋กœ ์บ์‹œ์— ์ถ”๊ฐ€๋˜๊ณ , usageOrderCount๊ฐ€ ์ฆ๊ฐ€ํ•จ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

Screenshot_2025-01-15_at_2 50 05_AM Screenshot_2025-01-15_at_2 51 24_AM

์ดํ›„ ๋ฐ์ดํ„ฐ๋ฅผ 1๊ฐœ์”ฉ ์ถ”๊ฐ€ํ•˜๋ฉฐ ํ™•์ธํ•ด๋ณด์•˜๋‹ค.

๋ฐ์ดํ„ฐ 5๊ฐœ

โ†’ ๋ฌธ์ œ๋‚˜ ๋ณ€ํ™” ์—†์ด usageOrderCount๊ฐ€ 5๋กœ ๋ณ€๊ฒฝ๋˜๋ฉฐ ์บ์‹œ์— ์ถ”๊ฐ€๋˜์—ˆ๋‹ค.

๋ฐ์ดํ„ฐ 6๊ฐœ

โ†’ usageOrderCount๊ฐ€ 6์œผ๋กœ ๋ณ€๊ฒฝ๋˜์—ˆ๋‹ค. ์ด๋•Œ, ์ œํ•œ๊ฐ’์„ ๋„˜์–ด์„œ๊ฒŒ ๋˜์–ด ์บ์‹œ ์‚ญ์ œ๊ฐ€ ์ง„ํ–‰๋œ๋‹ค. Disk cache contents ๋กœ๊ทธ ๋ถ€๋ถ„์„ ํ™•์ธํ•˜๋ฉด ๊ฐ€์žฅ ์ดˆ๊ธฐ ๊ฐ’์ด ์‚ญ์ œ๋˜์–ด 5๊ฐœ๋กœ ์œ ์ง€๋จ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

Screenshot_2025-01-15_at_2 52 50_AM Screenshot_2025-01-15_at_3 04 18_AM

์—ฌ๊ธฐ์„œ ์˜์•„ํ•œ ์ ์ด ์ƒ๊ฒผ๋‹ค.

์™œ ์ˆœ์„œ ์œ ์ง€๊ฐ€ ์•ˆ๋˜๋Š” ๊ฒƒ์ผ๊นŒ??

์ง€๊ธˆ ๋กœ๊ทธ์— ์ฐํžŒ ๋‚ด์šฉ์„ ํ™•์ธํ•ด๋ณด๋ฉด, ๊ฐ€์žฅ ๋จผ์ € ์œ„์น˜ํ•œ ๋ฐ์ดํ„ฐ๊ฐ€ ์ž˜ ์‚ญ์ œ๋˜์ง€๋งŒ, ์ƒˆ๋กœ ์ถ”๊ฐ€๋œ ๋ฐ์ดํ„ฐ๊ฐ€ ๊ฐ€์žฅ ๋งˆ์ง€๋ง‰์— ์ €์žฅ๋˜์ง€ ์•Š๊ณ  ์ค‘๊ฐ„์— ์œ„์น˜ํ•˜๊ฒŒ ๋˜๊ณ  ์žˆ๋‹ค.

์›์ธ์€ content๊ฐ€ ๋””์Šคํฌ ์บ์‹œ ํŒŒ์ผ์—์„œ ๊ฐ€์ ธ์˜จ ๋ฐ์ดํ„ฐ๋ผ๋Š” ์ ์ด์—ˆ๋‹ค.

usageOrder๋ฅผ ๊ธฐ์ค€์œผ๋กœ ๋กœ๊ทธ๋ฅผ ์ฐ์–ด๋ณด๋ฉด, ์ˆœ์œ„๊ฐ€ ์ •์ƒ์ ์œผ๋กœ ์ ์šฉ๋œ๋‹ค๋Š” ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์—ˆ๋‹ค.

Screenshot 2025-01-16 at 10 08 43โ€ฏPM

๋น„๋™๊ธฐ ์ฒ˜๋ฆฌ

์•ž์„  ๊ตฌํ˜„ ์ƒํƒœ์—์„œ๋Š” ๋น„๋™๊ธฐ ์ฒ˜๋ฆฌ๋ฅผ ๋”ฐ๋กœ ํ•˜์ง€ ์•Š์•„ ๋ชจ๋‘ ๋™๊ธฐ์ ์œผ๋กœ ๋™์ž‘ํ–ˆ๋‹ค.

๋””์Šคํฌ ์บ์‹œ์— ์ด๋ฏธ์ง€๋ฅผ ๊ฐ€์ ธ์˜ค๊ณ  ์ €์žฅํ•˜๋Š” ๊ณผ์ •์— ๋น„๋™๊ธฐ ์ฒ˜๋ฆฌ๋ฅผ ์ถ”๊ฐ€ํ•˜์—ฌ ํ•œ๋ฒˆ์— ๋งŽ์€ ์ด๋ฏธ์ง€๊ฐ€ ์ฒ˜๋ฆฌ๋˜์–ด๋„ ๋ฌธ์ œ์—†๋„๋ก ๊ฐœ์„  ์‹œ๋„ํ–ˆ๋‹ค.

Actor ์‚ฌ์šฉ

  • ๋””์Šคํฌ ์บ์‹œ์—์„œ๋Š” usageOrder ์ƒํƒœ๋ฅผ ๊ด€๋ฆฌํ•ด์ค„ ํ•„์š”๊ฐ€ ์žˆ๋‹ค. ์—ฌ๊ธฐ์„œ ๋™์‹œ์„ฑ ๋ฌธ์ œ๋ฅผ Actor๋ฅผ ์ด์šฉํ•˜๋ฉด ๋ฐฉ์ง€ํ•  ์ˆ˜ ์žˆ๋‹ค.
  • ๋””์Šคํฌ ์บ์‹œ์—์„œ ์ด๋ฏธ์ง€ ์ €์žฅ๊ณผ ๋กœ๋“œ๊ฐ€ ๋™์‹œ์— ๋ฐœ์ƒํ•˜๋”๋ผ๋„ Actor๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ํ•œ๋ฒˆ์— ํ•œ ์Šค๋ ˆ๋“œ์— ์ ‘๊ทผํ•˜๋„๋ก ๋ณด์žฅ๋œ๋‹ค.
  • ์ฆ‰, ๋™์‹œ์„ฑ ๋ฌธ์ œ์™€ ์ถฉ๋Œ ์—†์ด ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ๋‹ค.
  • Actor๋ฅผ ์‚ฌ์šฉํ•˜๋ฉฐ async await์„ ์ด์šฉํ•ด ๋น„๋™๊ธฐ ์ฒ˜๋ฆฌํ•จ์œผ๋กœ์จ ํ™•์ธ์ด ๋” ๊ฐ„๋‹จํ•˜๋‹ค.

์ ์šฉ

๊ธฐ์กด ์ฝ”๋“œ๋Š” ImageNSCacheManager ์†์—์„œ ์ „์ฒด์ ์œผ๋กœ ๋ฉ”๋ชจ๋ฆฌ ์บ์‹œ์™€ ๋””์Šคํฌ ์บ์‹œ๊ฐ€ ์ ์šฉ๋˜๊ณ  ์žˆ์—ˆ๋‹ค.

๋””์Šคํฌ ์บ์‹œ ๋ถ€๋ถ„์—๋งŒ Actor๋ฅผ ์ด์šฉํ•ด ๋น„๋™๊ธฐ ์ฒ˜๋ฆฌํ•˜๊ธฐ ์œ„ํ•ด์„œ DiskCacheManager๋กœ ๋”ฐ๋กœ ๋ถ„๋ฆฌํ•ด ๊ตฌํ˜„ํ–ˆ๋‹ค.

์ด๋•Œ, ๋””์Šคํฌ ์บ์‹œ ์‹คํ–‰ ํ•จ์ˆ˜๋“ค์ด ๋ถ„๋ฆฌ๋˜์—ˆ์–ด๋„, ๊ทธ ํ˜ธ์ถœ์€ ImageNSCacheManager์—์„œ ๊ทธ๋Œ€๋กœ ์ด๋ค„์ง€๊ณ  ์žˆ์—ˆ๊ธฐ ๋•Œ๋ฌธ์— ํด๋ž˜์Šค๋ช…์„ CacheManager๋กœ ๋ณ€๊ฒฝํ•˜์˜€๋‹ค.

  • ๋””์Šคํฌ ์บ์‹œ Actor๋กœ ๋ถ„๋ฆฌ
actor DiskCacheManager {
    private let encoder = JSONEncoder()
    private let decoder = JSONDecoder()
    private var usageOrder: [String] = []
    private let cacheLimit: Int = 50
    private let cacheDirectoryPath: URL

    init(cacheDirectoryPath: URL) {
    ...
    }

    func saveToDist(urlString: String, cacheableImage: CacheableImage) async throws {
        ...
    }

    func loadFromDist(urlString: String) async throws -> CacheableImage? {
        ...
    }

    private func updateDiskUsageOrder(urlString: String) async {
        ...
    }

    private func removeOldestDiskImage() async {
        ...
    }

    private func printDiskCacheDirectory() {
        ...
    }
}

์‹œ๊ฐ„ ๋ณต์žก๋„ ๊ฐœ์„ 

ํ˜„์žฌ ์‚ฌ์šฉ ์—ฌ๋ถ€๋ฅผ ๊ฐฑ์‹ ํ•ด ๊ฐ€์žฅ ์˜ค๋ž˜์ „์— ์‚ฌ์šฉ๋œ ๋ฐ์ดํ„ฐ๋ฅผ ์‚ญ์ œํ•˜๋Š”๋ฐ ๋ฐฐ์—ด์„ ์ด์šฉํ•˜๊ณ  ์žˆ์—ˆ๋‹ค.

ํ•˜์ง€๋งŒ ๋ฐฐ์—ด์„ ์ด์šฉํ•˜๊ฒŒ ๋˜๋ฉด ๊ฐ’์„ ๊ฒ€์ƒ‰ํ•˜๊ฑฐ๋‚˜ ์ œ๊ฑฐํ•  ๊ฒฝ์šฐ์˜ ์‹œ๊ฐ„ ๋ณต์žก๋„๊ฐ€ O(n) ์ด์—ˆ๋‹ค.

์†Œ๋Ÿ‰์˜ ๋ฉ”์ดํŠธ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ–๋Š” ์ƒํƒœ์—์„œ๋Š” ๋ฌธ์ œ๊ฐ€ ์—†์ง€๋งŒ, ๋ฉ”์ดํŠธ ๋ฐ์ดํ„ฐ๊ฐ€ ๋Œ€๋Ÿ‰์ด ๋  ๊ฒฝ์šฐ๋ฅผ ๋Œ€๋น„ํ•ด ์‹œ๊ฐ„๋ณต์žก๋„๋ฅผ ๊ฐœ์„ ํ•˜๊ณ ์ž ํ–ˆ๋‹ค.

์ฒ˜์Œ์—๋Š” ์บ์‹œ ์‹œ๊ฐ„๋ณต์žก๋„ ๊ฐœ์„ ์— ๋งŽ์ด ์‚ฌ์šฉ๋˜๋Š” ๋ฐฉ์‹์ธ Double Linked List ์‚ฌ์šฉ์„ ๊ณ ๋ คํ–ˆ๋‹ค.

์ด๋ฅผ ์ด์šฉํ•˜๋ฉด, ์‚ฌ์šฉ ์ˆœ์„œ๋ฅผ ์œ ์ง€ํ•˜๊ณ  ๋…ธ๋“œ์— ๋Œ€ํ•œ ์ฐธ์กฐ๋ฅผ ๋”•์…”๋„ˆ๋ฆฌ ํ˜•ํƒœ๋กœ ์ €์žฅํ•ด ํ‚ค๋ฅผ ๊ฒ€์ƒ‰ํ•˜๊ณ  ๋…ธ๋“œ ์ •๋„๋ฅผ ์—…๋ฐ์ดํŠธํ•ด์ค„ ์ˆ˜ ์žˆ๋‹ค. ๋ชจ๋‘ O(1)์˜ ์‹œ๊ฐ„๋ณต์žก๋„๋ฅผ ๊ฐ€์ง€๊ฒŒ ๋œ๋‹ค.

ํ•˜์ง€๋งŒ, ํŒ€์›๋“ค๊ณผ ์˜๊ฒฌ์„ ๋‚˜๋ˆ„๋Š” ๊ณผ์ •์—์„œ Swift Collections ํŒจํ‚ค์ง€๋ฅผ ์ถ”๊ฐ€ํ•ด ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” OrderedSet์„ ์ด์šฉํ•ด๋ณด๊ธฐ๋กœ ๊ฒฐ์ •ํ•˜์˜€๋‹ค. ์ด ๊ณผ์ •์—์„œ, OrderedSet์ด ๋ฐ์ดํ„ฐ ์ถ”๊ฐ€์™€ ์‚ญ์ œ์—๋Š” ์‹œ๊ฐ„๋ณต์žก๋„๊ฐ€ O(1)์ด์ง€๋งŒ ์กฐํšŒ์˜ ๊ฒฝ์šฐ์— O(logn)์˜ ์‹œ๊ฐ„๋ณต์žก๋„๋ฅผ ๊ฐ–๊ธฐ ๋•Œ๋ฌธ์— Double Linked List ์‚ฌ์šฉ์ด ๋” ๊ฐœ์„ ๋œ๋Š” ๋ถ€๋ถ„์ด ํฌ์ง€ ์•Š์„๊นŒ ํ•˜๋Š” ์˜๊ฒฌ๋„ ์กด์žฌํ–ˆ๋‹ค. ํ•˜์ง€๋งŒ, ์บ์‹œ ์ •์ฑ…์—์„œ OrderedSet์˜ ์กฐํšŒ ์—†์ด ์ถ”๊ฐ€์™€ ์‚ญ์ œ๋งŒ ์ด์šฉ๋œ๋‹ค๋Š” ์ ์—์„œ ์ข€ ๋” ์‚ฌ์šฉ์ด ๊ฐ„ํŽธํ•œ OrderedSet์„ ์ ์šฉํ•ด๋ณด๊ธฐ๋กœ ์ตœ์ข… ๊ฒฐ์ • ํ•˜์˜€๋‹ค.

Swift Collections ํŒจํ‚ค์ง€๋ฅผ ์ถ”๊ฐ€ํ•˜๊ณ  OrderedSet์„ ์ด์šฉํ•ด LRU ์ •์ฑ…์„ ์ ์šฉํ•˜์˜€๋‹ค.

์ •์ƒ์ ์œผ๋กœ ๋™์ž‘๋จ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์—ˆ๋‹ค.

Screenshot 2025-01-16 at 10 08 43โ€ฏPM

ํ•˜์ง€๋งŒ, ๊ทธ ์ดํ›„์— ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ–ˆ๋Š”๋ฐ, CI ํ…Œ์ŠคํŠธ ์‹คํ–‰ ๊ณผ์ •์—์„œ OrderedSet ๊ด€๋ จํ•œ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉฐ ์›Œํฌํ”Œ๋กœ์šฐ๊ฐ€ ์‹คํŒจ๋กœ ๋๋‚œ๋‹ค๋Š” ์ ์ด์—ˆ๋‹ค. ์ด ๋ถ€๋ถ„์€ ๋ฌธ์ œ ํ•ด๊ฒฐ ๊ณผ์ •์— ์žˆ์œผ๋ฉฐ, ํ•ด๊ฒฐ๋˜๋ฉด ํŠธ๋Ÿฌ๋ธ”์ŠˆํŒ…์œผ๋กœ ๋”ฐ๋กœ ๊ธฐ๋กํ•˜๋ ค๊ณ  ํ•œ๋‹ค.

๊ฐœ์„  ์ธก์ •

  • 14 Pro 18.1.1 ๊ธฐ์ค€
  • ์ƒˆ๋กœ ์ถ”๊ฐ€๋˜์–ด ์บ์‹œ ์กด์žฌํ•˜์ง€ ์•Š๋Š” ์ƒํƒœ ๊ธฐ์ค€

์œ„ ์กฐ๊ฑด์„ ๋™์ผํ•˜๊ฒŒ ํ•˜์—ฌ ์•„๋ž˜ 6๊ฐ€์ง€ ํ•ญ๋ชฉ์„ ์ธก์ •ํ•ด ๋น„๊ตํ•˜๊ณ ์ž ํ•œ๋‹ค.

  • ์†Œ๋Ÿ‰์˜ ๋ฉ”์ดํŠธ ๋ฐ์ดํ„ฐ (4๊ฐœ) โ†’ profiling ๋ธŒ๋žœ์น˜ ๊ธฐ์ค€์œผ๋กœ ์žฌ์ธก์ • ํ•„์š”
  • ๋‹ค๋Ÿ‰์˜ ๋ฉ”์ดํŠธ ๋ฐ์ดํ„ฐ (40๊ฐœ) โ†’ profiling ๋ธŒ๋žœ์น˜ ๊ธฐ์ค€์œผ๋กœ ์žฌ์ธก์ • ํ•„์š”
  • ์†Œ๋Ÿ‰์˜ ๋ฉ”์ดํŠธ ๋ฐ์ดํ„ฐ (4๊ฐœ) + ๋น„๋™๊ธฐ
  • ๋‹ค๋Ÿ‰์˜ ๋ฉ”์ดํŠธ ๋ฐ์ดํ„ฐ (40๊ฐœ) + ๋น„๋™๊ธฐ
  • ์†Œ๋Ÿ‰์˜ ๋ฉ”์ดํŠธ ๋ฐ์ดํ„ฐ (4๊ฐœ) + ๋น„๋™๊ธฐ + ์‹œ๊ฐ„๋ณต์žก๋„ ๊ฐœ์„  (OrderedSet)
  • ๋‹ค๋Ÿ‰์˜ ๋ฉ”์ดํ„ฐ ๋ฐ์ดํ„ฐ (40๊ฐœ) + ๋น„๋™๊ธฐ + ์‹œ๊ฐ„๋ณต์žก๋„ ๊ฐœ์„ 

ํ˜„์žฌ Instruments ๋™์ž‘ ๋ฉˆ์ถค ๋ฌธ์ œ๋กœ ์ธํ•ด ์ธก์ •์ด ๋ฐ€๋ฆฌ๊ณ  ์žˆ๋Š” ์ค‘์ด๋‹ค. ๊ณ„์†ํ•ด์„œ ๋™์ผ ํ˜„์ƒ ๋ฐœ์ƒํ•œ๋‹ค๋ฉด, ๋‹ค๋ฅธ ํŒ€์› ๊ธฐ๊ธฐ๋กœ ์ธก์ • ์‹œ๋„ํ•ด๋ด์•ผ ํ•  ๊ฒƒ ๊ฐ™๋‹ค.