250x250
반응형
Notice
Recent Posts
Recent Comments
Link
관리 메뉴

스윞한 개발자

SNKit #4: ETag 검증과 이미지 다운로드 시스템 본문

프로젝트

SNKit #4: ETag 검증과 이미지 다운로드 시스템

스윞남 2025. 4. 8. 09:59
728x90
반응형
SMALL

안녕하세요! 이번 포스팅에서는 캐시 라이브러리 SNKit에 대한 4번째 개발일지를 적어보려 합니다!

 

벌써 4번째라니.. 처음에는 걱정이 많았지만 잘(?) 개발하고 있는 거 같습니다!!

 

오늘 소개해 드릴 내용은 제가 캐시 라이브러리를 만들어보고자 한 이유 중 하나인 ETag에 대해 다뤄보려고 합니다.

이번 글에서는! SNKit의 핵심 기능인 ETag와 이미지 다운로드 시스템에 대해 다뤄보도록 하겠습니다!

 

HTTP 캐싱과 ETag의 이해

이미지 캐싱 시스템을 효율적으로 구현하기 위해서는 HTTP 프로토콜의 캐싱 메커니즘을 잘 활용하는 것이 중요합니다. 그중에서도 ETag는 매우 유용한 기능입니다.

 

제가 처음 캐시에 대해 스터디를 할때 여러 질문 공격(?)을 받았던 적이 있습니다.

1. URL을 키로 이미지를 저장한다면, 만약 url은 그대로이지만 이미지가 바뀌게 된다면 어떻게 할 것인가?

2. 캐시를 사용하는 이유 중 하나가 통신 콜수를 줄이기 위함인데 ETag를 사용한다면 똑같이 콜수를 늘리는 게 아닌가?

.... 등 많은 질문을 받았었죠.. 저의 머릿속은 백지가 되었습니다! ㅎㅎ

 

그 이후로, 더 공부하고 이번 프로젝트에 ETag를 적용시켜 보게 되었습니다!!

 

우선 ETag에 대해 정리해 보겠습니다!

ETag란 무엇인가?

우선 정의는!

ETag는 웹 리소스의 특정 버전에 대한 식별자로, HTTP 응답 헤더에 포함됩니다. 일종의 지문이나 해시값으로, 리소스의 내용이 변경되면 ETag도 변경됩니다. 서버는 각 리소스에 대해 ETag를 생성하고, 클라이언트는 이 값을 저장해 둡니다.

 

HTTP/1.1 200 OK
Content-Type: image/jpeg
ETag: "33a64df551425fcc55e4d42a148795d9f25f89d4"

 

클라이언트가 다시 같은 리소스를 요청할 때, 저장해 둔 ETag 값을 If-None-Match 헤더에 포함시켜 요청합니다.

 

GET /image.jpg HTTP/1.1
If-None-Match: "33a64df551425fcc55e4d42a148795d9f25f89d4"

 

이 값을 수신받은 서버는 현재 리소스의 ETag와 요청에 포함된 ETag를 비교합니다!

 

 

# 일치한다면

리소스가 변경되지 않았으므로, 서버는 304 Not Modified 응답을 보냅니다. 이 응답에는 리소스 본문이 포함되지 않아 네트워크 트래픽을 크게 절약할 수 있습니다. -> 이때 어차피 통신 콜을 하지 않는가?라고 생각할 수 있지만 본문에 항상 이미지를 보낼 것을 생각하면 훨씬 효율적입니다.

 

# 일치하지 않는다면

리소스가 변경되었으므로, 서버는 200 OK와 함께 새로운 리소스와 새로운 ETag를 보냅니다.

 

 

 

 

 

결론적으로, 리소스의 일부만 변경된 경우에도 효율적으로 처리할 수 있습니다! 그래서 이런 ETag를 저의 SNKit에 적용시켜 보았는데요,

 

SNKit의 ETag 처리 시스템

SNKit은 이러한 ETag 메커니즘을 효과적으로 활용하기 위해 ETagHandler 클래스를 구현했습니다:

 

enum ETagValidationResult {
    case reused(UIImage) // 이미지 변경 X (304 응답)
    case fresh(UIImage)  // 이미지 변경 O (200 응답)
    case error(Error)    // 오류 발생
}

 

이 처럼,

1. 이미지가 변경되지 않았을 때

2. 이미지가 변경되었을 때

3. 오류(통신)

이 세 가지의 경우로 나눠주었습니다.

 

핸들러에는 이미지를 통신하는 과정을 작성하며,

 

if httpResponse.statusCode == 304 {
                // 이미지가 바뀌지 않았다면 기존의 이미지 리턴
                DispatchQueue.main.async {
                    completion(.reused(cachedImage))
                }
} else if httpResponse.statusCode == 200 {
                // 이미지가 바뀐 경우 새로 다운로드 후 리턴
}

 

이 처럼 상태코드를 확인해 304, 200인 경우 각각 이미지를 리턴, 새로 다운로드 후 캐시에 저장시킨 후 리턴되는 과정으로 구성했습니다.

 

저의 ETagHandler 클래스의 주요 특징은 다음과 같습니다.

  1. If-None-Match 헤더 활용: 캐시 된 ETag 값을 If-None-Match 헤더에 포함시켜 서버에 요청합니다.
  2. 응답 코드 처리:
    • 304 Not Modified: 이미지가 변경되지 않았으므로 캐시된 이미지를 재사용합니다.
    • 200 OK: 이미지가 변경되었으므로 새로운 이미지와 ETag를 캐시에 저장합니다.
  3. iOS 버전 호환성: iOS 13 이상과 이전 버전에서 모두 동작할 수 있도록 ETag 헤더 값을 가져오는 방법을 분기 처리했습니다.
  4. 비동기 콜백: 검증 결과를 비동기적으로 호출자에게 전달합니다.

그 후, 이미지를 다운할 ImageDownloader를 구현해 주었습니다.

이미지 다운로더 구현

ETag 처리 시스템과 함께 작동하는 이미지 다운로더는 SNKit의 핵심 컴포넌트 중 하나입니다. 다음은 ImageDownloader 클래스의 구현입니다.

 

public enum CacheOption {
    case cacheFirst       // 캐시 우선 (기본값)
    case eTagValidation   // ETag 검증 우선
    case forceDownload    // 강제 다운로드
}

 

사용자(개발자)에게 입력받을 수 있도록 세 가지 옵션을 두었습니다.

 

제가 구현한 ImageDownloader 클래스의 주요 특징은 다음과 같습니다.

  1. 다양한 캐싱 전략: 캐시 우선, ETag 검증, 강제 다운로드 등 다양한 옵션을 제공합니다.
  2. ETag 처리 통합: ETagHandler를 활용하여 효율적인 이미지 검증을 수행합니다.
  3. 비동기 처리: 모든 네트워크 작업은 전용 디스패치 큐에서 비동기적으로 처리되어 UI 스레드 블로킹을 방지합니다.
  4. 상세 결과: 이미지가 새로 다운로드되었는지, 캐시에서 로드되었는지, ETag 검증을 통과했는지 등의 상세한 결과 상태를 제공합니다.

 

동시 다운로드 관리

iOS 앱에서는 여러 이미지를 동시에 다운로드해야 하는 경우가 많습니다. 이때 중복 다운로드를 방지하고 리소스를 효율적으로 사용하는 것이 중요하기에, SNKit에서는 아래의 방법으로 동시 다운로드를 관리해 보았습니다.

 

private var activeTasks: [String: URLSessionDataTask] = [:]
private let taskLock = NSLock()

func downloadImage(with url: URL, ...) {
    let identifier = url.absoluteString
    
    taskLock.lock()
    if let existingTask = activeTasks[identifier], existingTask.state == .running {
        taskLock.unlock()
        return
    }
    taskLock.unlock()
    
    // 이미지 다운로드를 여기서!!
    
    let task = session.dataTask(with: url) { /* ... */ }
    
    taskLock.lock()
    activeTasks[identifier] = task
    taskLock.unlock()
    
    task.resume()
}

 

코드의 첫 줄에 현재 실행 중인 모든 다운로드 태스크를 URL별로 추적합니다. 동일한 URL에 대한 다운로드가 이미 진행 중이면 새 요청을 무시합니다. 그 후, NSLock을 사용하여 여러 스레드에서 동시에 접근해도 안전하게 동작하도록 합니다. 다운로드가 완료되면 활성 태스크 목록에서 해당 태스크를 제거합니다.

 

이러한 방식으로 동일한 이미지에 대한 중복 다운로드를 방지하고, 네트워크 리소스와 메모리를 효율적으로 사용하려고 했습니다. 이번 포스팅에서는 SNKit의 ETag 검증 시스템과 이미지 다운로드 시스템을 어떻게 구현했는지 개발일지를 작성해 보았습니다!

 

다음 포스팅에서는 제가 기획한 SNKit의 마지막 핵심 기능인 이미지 처리에 대해 기록해 보겠습니다. 

 

긴 글 읽어주셔서 감사합니다!! 글을 읽으시다 부족한 부분이나 잘못된 부분에 대한 피드백은 언제나 환영입니다 ㅎㅎ 

 

 

728x90
반응형
LIST