이미지 렌더링 최적화 — Downsampling
앞서 살펴본 것처럼 iOS에서 이미지는 다음 과정을 거쳐 화면에 표시됩니다.
Image Load
↓
UIImage 생성
↓
UIImageView에 설정
↓
Layer Tree Commit
↓
Display Preparation (Decode)
↓
GPU Texture Upload
↓
Core Animation Compositing
↓
Framebuffer
↓
Display
이 과정에서 가장 비용이 큰 단계는 Decode 단계입니다.
압축된 JPEG / PNG 이미지는 이 단계에서 RGBA Bitmap으로 변환됩니다.
예를 들어 다음과 같은 이미지가 있다고 가정해보겠습니다.
파일 크기 : 590KB
해상도 : 2048 × 1536
디코딩이 발생하면 메모리 사용량은 다음과 같습니다.
2048 × 1536 × 4 bytes (RGBA)
≈ 12MB
즉 590KB의 파일이 디코딩 후에는 약 12MB의 메모리를 사용하게 됩니다.
문제는 대부분의 경우 이렇게 큰 해상도의 이미지가 그대로 필요하지 않다는 것입니다.
예를 들어 다음과 같은 UI가 있다고 가정해보겠습니다.
imageView.frame = CGRect(x: 0, y: 0, width: 100, height: 100)
디바이스의 screen.scale이 3이라면 실제 렌더링 해상도는
100pt × 3 = 300px
입니다.
하지만 일반적인 이미지 로딩 방식에서는 다음 일이 발생합니다.
JPEG (2048px)
↓
Decode
↓
2048px Bitmap 생성
↓
GPU에서 300px로 축소 렌더링
즉 실제로는 300px만 필요하지만 2048px 전체 이미지를 디코딩하고 있는 것입니다.
이 문제를 해결하기 위한 방법이 바로 Downsampling입니다.
Downsampling이란
Downsampling은 이미지를 디코딩할 때 필요한 해상도로 축소하여 bitmap을 생성하는 기법입니다.
즉 기존 방식과 Downsampling 방식은 다음과 같은 차이가 있습니다.
일반 이미지 디코딩
JPEG (2048px)
↓
Decode
↓
2048px Bitmap
↓
GPU 축소 렌더링
Downsampling 적용
JPEG (2048px)
↓
Downsampling Decode
↓
300px Bitmap
↓
GPU 렌더링
즉 처음부터 필요한 크기의 bitmap만 생성합니다.
이렇게 하면 다음과 같은 장점이 있습니다.
메모리 사용량 감소
CPU Decode 비용 감소
GPU Texture Upload 비용 감소
스크롤 성능 개선
특히 UICollectionView / UITableView 이미지 스크롤 성능에 매우 큰 영향을 줍니다.
WWDC에서 소개된 Downsampling 구현
Apple은 WWDC 세션에서 ImageIO 프레임워크를 활용한 Downsampling 방식을 소개했습니다.
핵심 API는 다음입니다.
CGImageSourceCreateThumbnailAtIndex
이 API를 사용하면 이미지를 디코딩하면서 동시에 크기를 축소할 수 있습니다.
다음은 WWDC에서 소개된 패턴을 기반으로 한 예제 코드입니다.
import ImageIO
import UIKit
func downsample(
imageData:Data,
topointSize:CGSize,
scale:CGFloat
)->UIImage? {
let imageSourceOptions = [
kCGImageSourceShouldCache:false
]asCFDictionary
guard let imageSource = CGImageSourceCreateWithData(
imageData as CFData,
imageSourceOptions
)else {
return nil
}
let maxDimension = max(pointSize.width,pointSize.height)*scale
let downsampleOptions= [
kCGImageSourceCreateThumbnailFromImageAlways:true,
kCGImageSourceShouldCacheImmediately:true,
kCGImageSourceCreateThumbnailWithTransform:true,
kCGImageSourceThumbnailMaxPixelSize:maxDimension
]as CFDictionary
guard let cgImage = CGImageSourceCreateThumbnailAtIndex(
imageSource,
0,
downsampleOptions
)else {
return nil
}
return UIImage(cgImage:cgImage)
}
이 함수의 핵심 동작은 다음과 같습니다.
JPEG / PNG 데이터
↓
CGImageSource 생성
↓
CreateThumbnailAtIndex
↓
지정된 크기로 디코딩
↓
UIImage 생성
즉 디코딩과 리사이징이 동시에 수행됩니다.
Downsampling 사용 예시
다운로드한 이미지를 Downsampling하여 사용하는 예시는 다음과 같습니다.
let url = URL(string:"<https://example.com/photo.jpg>")!
URLSession.shared.dataTask(with:url) { data, _, _in
guard let data else {return }
let image = downsample(
imageData: data,
to: imageView.bounds.size,
scale: UIScreen.main.scale
)
DispatchQueue.main.async {
imageView.image = image
}
}.resume()
이 경우 이미지 처리 흐름은 다음과 같습니다.
Image Download
↓
Downsampling Decode
↓
UIImage 생성
↓
imageView.image
↓
Render
즉 불필요하게 큰 bitmap이 생성되지 않습니다.
Downsampling의 효과
예를 들어 다음과 같은 이미지가 있다고 가정해보겠습니다.
원본 이미지 : 4000 × 3000
표시 크기 : 300 × 300
일반 디코딩 방식
4000 × 3000 × 4 bytes
≈ 48MB
Downsampling 적용
300 × 300 × 4 bytes
≈ 360KB
즉 메모리 사용량이
48MB → 360KB
로 크게 감소합니다.
또한 다음 비용도 줄어듭니다.
Decode CPU Cost
GPU Texture Upload
Memory Bandwidth
그래서 Apple에서도 이미지 리스트 UI에서는 Downsampling 사용을 강하게 권장합니다.
핵심 정리
iOS에서 이미지를 그대로 디코딩하면 다음 문제가 발생합니다.
불필요하게 큰 bitmap 생성
메모리 사용량 증가
Decode CPU 비용 증가
스크롤 성능 저하
이를 해결하기 위한 방법이 Downsampling입니다.
Downsampling은 다음 특징을 가집니다.
필요한 크기로만 이미지 디코딩
메모리 사용량 감소
Decode 비용 감소
GPU 업로드 비용 감소
따라서 대용량 이미지를 리스트 형태로 표시하는 UI에서는 Downsampling이 매우 중요한 최적화 기법입니다.
'iOS' 카테고리의 다른 글
| [iOS] Image: iOS 이미지 렌더링 과정 (0) | 2025.09.20 |
|---|---|
| [iOS] Image: Scale Factor와 Point, Pixel (0) | 2025.09.19 |
| [iOS] Optimistic UI의 정의 및 적용 사례 (SwiftUI) (2) | 2025.06.08 |
| [iOS] Decimal을 사용해야 하는 이유 (0) | 2025.04.02 |
| [iOS] Module, Library, Framework, Package (0) | 2025.03.16 |