직면하는 에러의 원인 및 해결 방법에 대해 작성합니다.
간혹 어드민에서 변경한 이미지가 클라이언트 단에서 반영되지 않는 경우가 있습니다.
이는 이미지는 변경되었지만, 이미지 URL 주소가 동일하여
클라이언트 단에서 이미지가 변경되었다는 것을 감지하지 못해 발생합니다.
Kingfisher를 활용하여 해당 이슈를 해결한 방법을 작성해보겠습니다.
fromMemoryCacheOrRefresh는 Kinfisher로 이미지를 로딩할 때
설정할 수 있는 옵션 중 하나입니다.
Kingfisher 라이브러리 내에 이 옵션에 대한 설명이 주석으로 작성되어 있는데,
내용은 아래와 같습니다.
이 옵션을 설정하면, Kingfisher는 먼저 메모리 캐시에서 이미지를 가져오려고 시도합니다.
이미지가 메모리 캐시에 없다면 디스크 캐시는 무시하고 네트워크에서 이미지를 다시 다운로드합니다.
이는 동일한 URL 뒤에 있는 변경 가능한 이미지를 같은 앱 세션 내에서 표시하고자 할 때,
이미지를 여러 번 다운로드하지 않도록 방지하는 데 유용합니다.
메모리 캐시를 확인하고, 메모리 캐시에 해당 이미지가 없다면
디스크 캐시에 이미지가 있더라도 이미지를 다시 다운로드 하도록 하는 옵션입니다.
정확히는 디스크 캐시 자체를 고려하지 않습니다.
따라서 메모리 캐시에 해당 이미지가 없다면
이미지 url의 헤더에 붙어있는 ETag 와 Last-Modified 값을 확인하고
이 값이 수정되었다면 이미지를 새로 다운로드 합니다.
동작 흐름을 정리하면 아래와 같습니다.
1. 이미지 요청 시작
|
2. 메모리 캐시 확인 (이미지 존재 여부)
|
├── 이미지 있음 ──▶ 메모리 캐시 이미지 바로 사용 → 끝
|
└── 이미지 없음 ──▶ 네트워크 요청 진행
|
3. 네트워크 요청 시
- 이전에 저장된 ETag/Last-Modified 메타데이터가 있으면
HTTP 헤더에 포함 (If-None-Match, If-Modified-Since)
- 요청 전송
|
4. 서버 응답 수신
├── 304 Not Modified
│ └─ 디스크 캐시에 저장된 기존 이미지 사용
└── 200 OK (새 이미지)
├─ 새 이미지 다운로드
├─ 메모리 캐시에 저장
└─ 디스크 캐시에 저장 (메타데이터 포함)
|
5. 이미지 화면에 표시
그러나, 메모리 이미지가 있는 경우에도
값이 수정되었다면 새로 다운받아야하는 경우가 있습니다.
이미지가 매우 중요한 서비스인 경우 유저가 해당 앱을 사용하고 있는 중에도
실시간으로 이미지 변경이 반영되어야하기 때문입니다.
이 때, forceRefresh 옵션을 사용합니다.
forceRefresh에 대한 설명은 아래와 같습니다
설정하면 Kingfisher는 캐시를 무시하고 이미지 소스에 대한 다운로드 작업을 시작하려고 시도합니다.
메모리 캐시 유무에 상관 없이 이미지를 무조건 다운받도록하는 옵션입니다.
다만, ETag 와 Last-Modified 값을 확인하고
해당 값이 수정되지 않았다면 다운받지 않습니다.
forceRefresh 옵션의 동작 흐름은 아래와 같습니다.
1. 이미지 요청 시작
|
2. 메모리 캐시 확인 (이미지 존재 여부)
|
├── 이미지 있음 ──▶ 네트워크 요청 진행 (캐시 무시)
|
└── 이미지 없음 ──▶ 네트워크 요청 진행
|
3. 네트워크 요청 시
- 이전에 저장된 ETag/Last-Modified 메타데이터가 있으면
HTTP 헤더에 포함 (If-None-Match, If-Modified-Since)
- 요청 전송
|
4. 서버 응답 수신
├── 304 Not Modified
│ └─ 디스크 캐시에 저장된 기존 이미지 사용
└── 200 OK (새 이미지)
├─ 새 이미지 다운로드
├─ 메모리 캐시에 저장
└─ 디스크 캐시에 저장 (메타데이터 포함)
|
5. 이미지 화면에 표시
그렇다면 두 옵션 중 어떤 것을 사용하는 것이 좋을까요?
저는 fromMemoryCacheOrRefresh를 선택했습니다.
이유는 다음과 같습니다.
먼저, 서비스 특성상 이미지가 중요한 요소이긴 하지만 화면에 노출되는 이미지 수가 많기 때문에
매번 서버에서 응답을 받아야 하는 forceRefresh는 이미지 로딩 속도 저하로 이어질 수 있고
이는 사용자에게 이미지 갱신 누락보다 더 큰 불편으로 체감될 수 있기 때문입니다.
또한, 이미지 변경이 아주 잦지는 않고 변경되더라도 fromMemoryCacheOrRefresh는 앱이 재시작되었을 때 디스크 캐시를 기반으로 서버 변경 여부를 확인하기 때문에 일정 수준의 갱신도 확보할 수 있습니다.
물론 이미지 변경 이슈가 자주 발생하거나, 변경 여부를 더 정확하게 감지해야 할 필요가 생긴다면 그땐 forceRefresh로 변경을 고려할 수도 있을 것 같아요
그땐 기획팀과 사용자 경험과 이미지 정확도 사이에서 우선순위를 결정해서 판단할 것 같아요.
ETag는 서버가 파일에 붙여주는 "변경 감지용 고유 태그"로,
파일이 바뀌면 이 태그도 바뀌어 서버에서 수정 여부를 빠르게 확인할 수 있게 한다.
1️⃣ 최초 요청
2️⃣ 이후 요청
If-None-Match헤더에 포함하여 요청Last-Modified는 서버가 파일을 마지막으로 수정한 날짜와 시간을 알려주는 정보로,
이 값을 통해 클라이언트는 파일이 변경됐는지 판단할 수 있다.
curl -I 이미지주소
터미널에서 위의 커맨드라인을 실행하면

다음과 같이 ETag와 Last-Modified 값을 확인할 수 있습니다.
이미지가 변경될 때 실제로 Last-Modified 값이 변경되는지 확인해본 후
fromMemoryCacheOrRefresh 또는 forceRefresh 옵션을 사용하면 좋을 것 같습니다
메모리 캐시 : 앱의 RAM(휘발되는 메모리)에 저장되어 앱 종료 시 사라짐
디스크 캐시 : 앱의 디스크 공간에 저장되어 앱 종료 이후에도 유지됨
잘못된 정보가 있다면 피드백 환영입니다... 😇
추가)
ETag/Last-Modified 값을 비교하려면 서버에 요청을 해야하기 때문에 이에 따른 시간 지연이 우려되어
fromMemorhCacheOrRefresh 나 forceRefresh 둘 다 사용하지 않고
특정 시간에 메모리 캐시와 디스크 캐시 자체를 갱신하는 것으로 변경되었어요.