iOS

비동기적으로 NS 캐쉬를 사용하여, 네트워크 통신 줄이기

고감귤 2024. 3. 19. 16:20

앱 개발 기능 중, 게시글에서 사용되는 다수의 이미지를 네트워크에서 로드해야 하는 상황이 발생했습니다.

이미지 특성상 데이터의 사이즈가 크기 때문에, 이미지를 매번 불러올 때마다 이미지를 로딩하는 화면을 보아야 하는 상황이었습니다.

 

최적화 전 - 게시글에 해당하는 이미지를 매번 네트워크에서 요청하는 방식

static func loadImageFromURL(url: URL, completion: @escaping (UIImage?) -> Void) {
    URLSession.shared.dataTask(with: url) { data, _, error in
      if let error = error {
        print("Error downloading image: \(error.localizedDescription)")
        completion(nil)
        return
      }

      if let data = data, let image = UIImage(data: data) {
        completion(image)
      } else {
        completion(nil)
      }
    }.resume()
}

사용자가 보고 있는 게시글이 바뀔 때마다, 각각의 이미지 loadImageFromURL 함수를 통해 불러오게 됩니다. 

 

 

 

게시글을 보려고 메뉴를 선택 했을 때, 게시글에 해당하는 이미지들을 비동기적으로 로드하고 캐쉬에 저장하여 재사용한다면, 로딩 시간을 줄일 수 있다고 생각했습니다.

최적화 후 -  비동기적으로 이미지를 네트워크에서 요청한 후, NSCache에 저장하여 재사용하는 방식

static func loadImageFromURL(urlString: String, cnt: Int, completion: @escaping (UIImage) -> Void) {
    DispatchQueue.global().async {
	    let cacheKey = NSString(string: urlString)
	    // NSCache는 Thread Safe함으로 여러 스레드에서 안전하게 사용할 수 있습니다.
	    if let cachedImage = ImageCacheManager.shared.object(forKey: cacheKey) {
	      completion(cachedImage)
	      return
	    }
    
	    if let url = URL(string: urlString) {
	      URLSession.shared.dataTask(with: url) { data, _, error in
	        if let error = error {
	          print("Error downloading image: \(error.localizedDescription)")
	          if cnt <= 2 {
	            UIImage.loadImageFromURL(urlString: urlString, cnt: cnt+1) { out in
	              completion(out)
	            }
	          }
	          else {
	            completion(UIImage())
	          }
	          return
	        }
	        
	        if let data = data, let image = UIImage(data: data) {
	          ImageCacheManager.shared.setObject(image, forKey: cacheKey)
	          completion(image)
	        } else {
	          completion(UIImage())
	        }
	      }.resume()
	    }
    }
  }

이로써, 

1. DispatchQueue.global().async로 비동기적으로 task 처리

2. NSCache를 사용하여, 이미지가 저장되어 있다면 재사용

3. 이미지가 없다면, URLSession.shared.dataTask를 통해서 url에 해당하는 요청을 백그라운드 스레드에서 처리

4. 네트워크 통신 실패 시, 재귀적으로 이미지 재요청 [ 최대 3번 - 로딩이 너무 길어도 사용자에게 불편함을 줄 것이라 판단 ]

5. escaping closure로 비동기 처리문이 끝난 후 Image 데이터를 반환하여 Image 뷰를 생성

하는 방식으로  이미지 로딩시간을 줄일 수 있게 되었습니다.