일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | ||
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 |
- 학과별커뮤니티
- 기초문법
- 네트워크
- Kingfisher
- 대표
- 구름톤 유니브
- 스트럭트
- 토이프로젝트
- uikit
- 이론
- CS
- WeatherKit
- MVVM
- SwiftUI
- 앱개발
- 동시성
- 반응형
- ios
- 프로토콜
- Optional
- async
- swift
- struct
- GCD
- collectionview
- RxSwift
- 세종대학교
- 옵셔널
- mvc
- 실습
- Today
- Total
스윞한 개발자
이미지 렌더링 + 캐싱/UIGraphicsImageRenderer 본문
안녕하세요!
요즘, 캐싱에 대해 많은 글을 쓰고 있는 것 같습니다..!
캐싱의 한 부분을 공부하다 보니 관련된 여러 주제로 뻗어나가는 것 같아요. 그래서 이번 포스팅은..! 이미지 캐시와 이미지 로더를 통합해 개선하는 과정과 개념에 대해 정리해 보려고 합니다.

어플이 이미지를 다운로드하는 경우, 일반적으로 이미지를 로딩하는데 시간이 걸립니다. 앱이 동일한 이미지를 여러 번 다시 로드할 때 불필요한 작업이 될 수 있고 사용자는 불편함을 느낄 수 있습니다. 테이블뷰나 컬렉션뷰를 가지고 있는 어플이 대부분일 것입니다. 앱을 실행하고 스크롤할 때 거의 대부분의 사용자가 버벅거리는 환경을 경험해 보았을 것입니다.
-> 이러한 이슈는 이미지 렌더링이 한 번에 이루어지지 않아서 생긴다고 합니다.
LazyStack이나, 테이블, 컬렉션 뷰는 화면에 이미지가 보일 때 이미지 렌더링을 실행합니다. 이 과정에서 버벅거리는 현상을 줄이기 위해 많은 양의 데이터를 한 번에 렌더링 한다면, 메인 스레드에서는 방대한 양의 일을 하게 될 것입니다.
# 이미지 렌더링 파이프라인
이미지 렌더링은 화면에 이미지를 보여주기 위해 데이터를 처리하고 화면에 표시하는 과정입니다.
* 로딩 : 압축된 이미지를 메모리에 로드하는 과정(로컬 디스크에 이미지를 읽는 과정)
* 디코딩 : 인코딩 된 이미지 데이터를 픽셀당 이미 정보로 변환하는 과정
먼저 앞 포스팅에서 정리한 캐시 기능을 이용해 네트워크 요청을 감소시킬 수 있습니다. 그리고 이미지 유효성을 검증하기 위해 백그라운드에서 처리할 수 있습니다.
렌더링 하는 과정에 대해 부담을 줄이기 위해 생각해 보았는데, 로딩과 디코딩하는 과정에 대해 접근해 볼 수 있겠다 생각했습니다.
if let decodedImage = decodedImageCache.object(forKey: url as AnyObject) as? UIImage {
return decodedImage
}
처음 이미지 캐시에 접근할 때, 디코딩이 이미 완료된 이미지가 캐시에 있다면 바로 반환을 시키고
if let image = imageCache.object(forKey: url as AnyObject) as? UIImage {
let decodedImage = image.decodedImage()
decodedImageCache.setObject(image as AnyObject, forKey: url as AnyObject, cost: decodedImage.diskSize)
return decodedImage
}
그렇지 않다면 즉, 원본 이미지가 캐시에 있다면! 디코딩된 이미지를 새로운 캐시에 저장해 주며, 디코딩된 이미지를 반환해 줄 수 있습니다.
캐시에 저장된 이미지를 로딩할 때도 마찬가지로 이미지 로딩 메서드를 커스텀해서 과정을 최적화할 수 있습니다. 마찬가지로 캐시에 저장되어 있는 유무를 판단하고, 백그라운드에서 네트워크와의 통신을 통해 유효성을 검증할 수 있습니다.
if let image = cache[url] {
return Just(image).eraseToAnyPublisher() ➊
}
무거운 작업을 백그라운드에서 처리하고 메인스레드에서는 UI 업데이트 과정만을 수행하기 위해 커스텀할 수 있습니다.
URLSession.shared.dataTaskPublisher(for: url)
.map { (data, response) -> UIImage? in return UIImage(data: data) }
.catch { error in return Just(nil) }
.handleEvents(receiveOutput: {[unowned self] image in ➋
guard let image = image else { return }
self.cache[url] = image
})
.subscribe(on: backgroundQueue)
.receive(on: RunLoop.main)
WWDC18의 iOS Memory Deep Dive를 보다 UIGraphicsImageRenderer부분을 살펴보게 되었는데, 기존에 사용했던 과정보다 더 적절한 해상도로 이미지를 생성해 주기 때문에 더 효율적으로 사용될 수 있습니다.
https://developer.apple.com/documentation/uikit/uigraphicsimagerenderer
UIGraphicsImageRenderer | Apple Developer Documentation
A graphics renderer for creating Core Graphics-backed images.
developer.apple.com
extension UIImage {
func decodedImageUsingRenderer() -> UIImage? {
let size = self.size
let renderer = UIGraphicsImageRenderer(size: size)
return renderer.image { _ in
self.draw(in: CGRect(origin: .zero, size: size))
}
}
}
UIGraphicsImageRenderer를 사용하면 위 과정에서 디코딩되는 과정과 다르게 원하는 설정한 크기로 줄이고 디코딩 된 후, 렌더링 할 수 있게 도와줍니다.
# 결론!
이미지 렌더링하는 과정을 Lazy 하게 처리하는 것이 제일 효율적인 과정이기에, 이 부분을 커스텀하는 것보다! 이미지 렌더링을 하는 자체 과정 중에서의 조금이나마 효율적인 방법이 없을지를 생각해 볼 수 있습니다.
- Lazy 한 이미지 렌더링
화면에 표시될 때 이미지를 로드하고 디코딩하는 방식입니다. 필요하지 않은 이미지는 로드하지 않기 때문에 메모리 사용량이 감소합니다. 총 100개의 이미지에 대해 초기 화면에 보이는 10개의 이미지만 보이기 때문에 렌더링 속도가 빨라져 버벅거림이나, 시간이 단축하게 됨으로 사용자가 더 편해질 수 있습니다.
- 로딩/디코딩 과정의 정제
이미지 렌더링을 lazy 하게 처리하더라도, 이미지 로딩과 디코딩 과정을 최적화하게 된다면 렌더링의 단점을 조금이나마 완화할 수 있습니다. 무거운 작업을 백그라운드에서 처리하고, 디코딩된 이미지를 캐시에 저장시키면 이후 렌더링된 속도가 빨라지게 됩니다.
lazy + 로딩/디코딩 과정의 정제를 생각해 보다.. 프리로딩이라는 방법을 알게 되었습니다! 또 두 가지 방법을 사용하되, 사용자가 스크롤하여 볼 가능성이 높은 이미지를 미리 로드해 두면 이 과정에서 렌더링 속도를 좀 더 개선할 수 있지 않을까 생각합니다.
이번 포스팅은 해외 자료들을 살펴보며 정리했는데, 어려운 내용이 많이 있었던 터라..! 혹시 글을 읽으시며, 잘못된 점이 있거나 보완사항이 필요하다 생각되신다면..! 피드백은 언제나 환영입니다! 긴 글 읽어주셔서 감사합니다.

'Swift 이론' 카테고리의 다른 글
뷰 드로잉 사이클 - View Drawing Cycle (0) | 2025.01.20 |
---|---|
Content Hugging Priority, Content Compression Resistance Priority🥇🥈🥉 (0) | 2025.01.12 |
Image Cache에 대한 고찰 💭 (0) | 2025.01.09 |
코드 사이닝? 프로비저닝 프로파일? 🤔 (1) | 2025.01.05 |
옵셔널 내부구조 파해치기!👊🏻 (2) | 2025.01.05 |