Image Cache - 개념편

김재형_LittleTale·2026년 1월 1일

Cache

목록 보기
1/2

들어가기에 앞서

해당 편은 이미지 캐시에 대한 이야기를 다루고자 합니다.

Cache

자주 사용하는 데이터, 값을 복사해 놓는 임시 장소


iOS 에서 캐싱 방식

- Disk

  • 구현: FileManager로 파일 저장/읽기, (메타데이터 JSON/DB로 관리 가능)

  • 특징

    • ( Caches / Documents / App Group )에 저장 → 앱 종료/재실행 후에도 유지
    • 용량이 비교적 크고 대용량 데이터에 유리
    • I/O 비용이 꽤 큼 -> Memory보다 느림
  • 장점

    • 재실행 후에도 캐시가 남아서 네트워크/처리 비용 절감
    • 메모리 압박과 무관하게 비교적 안정적으로 유지
    • 이미지/비디오 등 큰 데이터 캐싱에 적합
    • App Group 쓰면 앱+위젯 / 익스텐션 공유 가능
  • 단점

    • 읽기/쓰기 시 디스크 I/O → 빈번하면 성능 저하(스크롤 끊김 등)
    • 정리 정책 필요(용량 제한, 만료 시간, LRU prune) 안 하면 계속 쌓임
    • 메타 갱신(접근 시간 기록 등)을 자주 하면 write 폭증 가능
    • 암호화/백업 제외 등 고려

- Memory

  • 구현: NSCache, URLCache(HTTP 캐시), 또는 직접 메모리 딕셔너리(+eviction)

  • 특징

    • RAM에 저장 → 앱 종료 시 휘발
    • 접근이 빠름(디스크 대비) → UI 유리
    • 시스템이 메모리 압박 상황이 발생하면 자동으로 비울 수 있음
  • 장점

    • 빠른 캐시
    • 스크롤/재사용 뷰에서 유리
    • NSCache는 시스템이 필요하면 알아서 지움 (크래시 위험 감소)

단점

  • 앱 꺼지면 사라짐
  • 메모리 용량이 작고 변동적이라 항상 남아있다는 보장 없음
  • 큰 데이터(원본 이미지 Data)를 많이 올리면 메모리 폭증 위험

NSCache

  • key-value 형태
  • 시스템 메모리 자동관리
  • 딕셔너리 처럼 Key 값을 복사해서 가져오는 형태가 아님
    • Key 객체 참조를 그대로 사용
    • Value 가 사라지면 알아서 사라짐

만들어볼 캐싱 그림

                    싱글톤 구조인 매니저
                           ↓
            사용자 요구 (메모리, 디스크, 둘다, 안함)
                           ↓
                     둘다에 경우 승격구조 
     ( 메모리 탐색 없으면 -> 디스크 탐색 -> 적중시 메모리 승격 )
( 승격후 디스크 메타 데이터 반영 ( 일정시간 지난 후 혹은 백그라운드 진입 )
						   ↓
					  리사이징 여부
                           ↓
                         이미지

승격에 대한 이야기

메모리에 캐싱을 하게 되면 일단, 시스템에서 공간이 부족하거나 하였을때
알아서 제거를 해줍니다. 이는 사용자가 다른 앱으로 활동을 할때에
메모리 할당 용량이 줄어들게 되는 구조인거죠. ( 앱을 껏을때도 )

만약, 메모리에 올린후 디스크에 반영하지 않았다면
찾지 못하여 새로 API 를 요청해야 합니다.

그렇다면, 디스크에서 찾았다면요?
사용자가 UI에서 유리할 수 있도록 메모리로 승격을 하는 구조가 되죠.

디스크 비움에 대한 기준

고려해봐야 할 사항중
디스크 이야기를 뺄 수가 없을것 같군요

메모리에서 내려온 이미지를 위해 디스크를 활용하게 될텐데
그렇다면 디스크는 언제 제거해야 할까요?

LRU - Least Recently Used

여러가지 방법이 존재하겠으나
이번 시간에는 LRU 방식을 사용해 보려고 합니다.

이름을 해석하면
가장 오래 안쓴것 이죠.
오래 참조하지 않았으니 그만큼 사용자에겐 관심이 없다는 뜻 즉
그 기준으로 제거해 보려고 합니다.

메모리로 승격후 디스크반영

위에서 언급했듯이 결국 디스크에 사용했음을 기록해야합니다.
고민해 봐야 할 부분이죠. 왜냐...

I/O 부담이 있으니 디스크 기록 타이밍을 생각해봐야 하기 때문이죠.

시간 방식

이 부분은 이번 파트에선
사용자가 사용한 시점 기준 특정초까지 담아두었다
한번에 반영하는 구조를 구성해보려 합니다.

ETag

모든 서버는 아니지만, 서버 입장에서도 리소스를 아끼기 위해
Etag 라는것을 응답헤더에 달아준다는 사실을 알고 계셨나요?

ETag: "33a64df551425fcc55e4d42a148795d9f25f89d4"
ETag: W/"0815"

위와 같은 형태로 값을 서버에서 내려주게 되는데
이 값을 저장해 두었다가 서버에게

If-None-Match: "<etag_value>"

같은 형태로 보내게 된다면
304 - Not Modified 값을 주게 됩니다.
즉 저희는 캐시값을 그대로 쓸 수 있으며, 서버도 리소스를 아낄수 있게 되죠.

참조문헌-1_ETag

last-modified

모든 서버가 ETag를 주지 않듯이 이친구도 마찬가지 입니다만
Etag 대신 last-modified를 사용하는 서버도 있답니다.

Last-Modified: Wed, 21 Oct 2015 07:28:00 GMT

위와 같은 형태로 서버가 값을 주는데
날짜 형식이 서버마다 다를까 조사해 보았으나

GMT
그리니치 표준시. HTTP 날짜는 현지 시각이 아닌, 언제나 GMT로 표현합니다

라고 적혀 있기에 충분히 활용해 보면 좋을 것 같습니다.

참조문헌-2_last-modified

파일명 고려사항

  • /, ?, :, &, %, 유니코드 등 일부 파일명으로써 문제가 발생할 수 있는 상황
  • 파일이름이 무지막지하게 긴 경우
  • 파일명 유출

SHA256

해시함수
길이가 고정된 256비트 -> 32바이트 구조

  • 특징
    • 고정 길이
    • 해시함수니 같은 인풋은 같은 아웃풋 형태
    • 역으로 해석 불가
    • 이름 충돌 방지 ( 아예 안나는건 아님 확률이 극악 )

위 SHA256 해싱을 통해 이름을 16진수 형태로 바꾸어 연결하여
사용해 보겠습니다.

GitHub

참고로 이미 프로젝트는 만들어 놓은 상태입니다.
계속 디벨롭 해봐야 할 것 투성이지만
미리 보고 싶으신분들은 아래 링크를 타시면 되겠습니다.

GitHub 먼저 볼래요 - SimpleImageCacheSwift

마무리하면서

이번시간은 개념 뼈대와 흐름을 적어 보었습니다.
고려 사항들을 꼭 같이 고민해 주셨으면 합니다.
생각보다 생각할 부분들이 많더라구요.

특히 메모리 승격 부분이 저에게 있어선 좀 난제 였던 것 같습니다.
디스크 I/O 부담을 줄여보겠다고 고민을 많이 했어서

아무튼 오늘도 고생하셨습니다.
새해복 많이 받으세요 다들!

profile
IOS 개발자 새싹이, 작은 이야기로부터

0개의 댓글