우리가 iOS 앱 시작 시간을 60% 줄인 방법
Source: Dev.to
죄송합니다만, 번역하려는 전체 텍스트를 제공해 주시면 해당 내용을 한국어로 번역해 드릴 수 있습니다. 현재는 링크만 제공되어 있어 실제 기사 본문을 확인할 수 없습니다. 번역이 필요한 텍스트를 복사해서 알려주시면 바로 도와드리겠습니다.
소개
앱 실행 시간은 첫인상입니다. 앱이 열리는 데 2–3 초 이상 걸리면 사용자가 눈치채고, 5 초가 되면 떠납니다. 우리는 중급 기기에서 콜드 런치 시간이 4.8–5.2 초에 머물렀던 실제 iOS 앱에서 이 문제를 겪었습니다. 집중적인 최적화 스프린트 후 실행 시간을 ≈ 60 % 줄여 ~2 초 수준으로 감소시켰습니다.
1단계 — 최적화하기 전에 측정하기
추측하지 말고 측정하세요. 우리는 다음을 사용했습니다:
- Xcode Instruments → Time Profiler
- App Launch Metric (Xcode Organizer)
- DYLD_PRINT_STATISTICS
- Custom logging for
application(_:didFinishLaunchingWithOptions:)
기준 수치
| 지표 | 전 |
|---|---|
| Cold launch | 5.1 s |
| Warm launch | 2.7 s |
| Main thread blocked | 3.4 s |
통찰 – 대부분의 시간이 첫 프레임이 렌더링되기 전에 소요되었으며, 이는 시작 작업이 메인 스레드를 차단하고 있음을 의미합니다.
Step 2 — Find What Blocks the Main Thread
Problems we discovered:
- 런치 시 무거운 의존성 주입
- 시작 중 데이터베이스 마이그레이션
- 동기식 네트워크 호출
- 큰 스토리보드 초기화
- 너무 많은 동적 프레임워크
첫 화면이 나타나기 전에 모두 발생합니다.
60 % 향상을 가져온 최적화
1. 비핵심 작업 지연 처리 (가장 큰 효과)
Before
func application(_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
setupAnalytics()
migrateDatabase()
preloadImages()
fetchRemoteConfig()
return true
}
After
DispatchQueue.global(qos: .background).async {
self.setupAnalytics()
self.migrateDatabase()
self.preloadImages()
self.fetchRemoteConfig()
}
// Or, even later:
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
// non‑critical work
}
Result – 즉시 약 1.8 초 절감.
2. 의존성 지연 로드
Before
let networkManager = NetworkManager()
let cacheManager = CacheManager()
let analytics = Analytics()
After
lazy var networkManager = NetworkManager()
Result – 약 400 ms 절감 (해당 기능이 사용될 때만 비용 발생).
3. 스토리보드 복잡도 감소
초기 스토리보드에는 20개 이상의 뷰 컨트롤러, 무거운 Auto Layout, 커스텀 폰트, 내장 네비게이션이 포함돼 있었습니다.
Fixes
- 스토리보드를 더 작은 조각으로 나눔.
- 가벼운 런치 스크린 사용.
- 무거운 뷰를 코드 기반 UI로 이동.
Result – 약 300–500 ms 절감.
4. 동적 프레임워크 최적화
각 동적 프레임워크는 런치 오버헤드(dyld 링크, 심볼 해석)를 추가합니다. 우리는 18개의 프레임워크를 사용하고 있었습니다.
Actions
- 작은 프레임워크를 병합.
- 일부를 정적 라이브러리로 전환.
- 사용되지 않는 pod 제거.
Result – 약 700 ms 절감.
5. 데이터베이스 마이그레이션을 시작 시점에서 분리
우리는 매 실행마다 SQLite 마이그레이션을 수행하고 있었습니다.
Fix
- 스키마 버전이 변경된 경우에만 마이그레이션 실행.
- 첫 화면이 표시된 뒤 백그라운드 큐에서 마이그레이션 수행.
Result – 약 600 ms 절감.
6. 이미지 및 에셋 최적화
Problems
- 큰 PNG와 불필요한 @3x 에셋.
- 런치 시점에 이미지 미리 로드.
Fixes
- 에셋을 WebP/HEIF 형식으로 변환.
- 필요할 때 이미지 로드.
- 미리 로드 제거.
Result – 약 200–300 ms 절감.
최종 지표
| 지표 | 전 | 후 |
|---|---|---|
| 콜드 런치 | 5.1 s | 2.0 s |
| 웜 런치 | 2.7 s | 1.1 s |
| 메인 스레드 차단 | 3.4 s | 0.9 s |
총 개선: ≈ 60 % 더 빠른 실행.
주요 교훈
해야 할 일
- 비핵심적인 모든 것을 연기하세요.
- 의존성을 지연 로드하세요.
- Instruments로 측정하세요.
- 동적 프레임워크를 최소화하세요.
- 런치 스크린을 가볍게 유지하세요.
하지 말아야 할 일
- 시작 시 API를 호출하지 마세요.
- 메인 스레드에서 데이터베이스를 마이그레이션하지 마세요.
- 모든 서비스를 즉시 초기화하지 마세요.
- 무거운 스토리보드를 로드하지 마세요.
- 메인 스레드를 차단하지 마세요.
빠른 시작 최적화 체크리스트
- 가벼운 런치 스크린을 사용하세요.
- 서비스를 지연 로드하세요.
- 불필요한 프레임워크를 제거하세요.
- 분석 초기화를 연기하세요.
- 데이터베이스 작업을 백그라운드에서 수행하세요.
- 시작 시 무거운 DI 컨테이너 사용을 피하세요.
- Instruments를 사용해 정기적으로 프로파일링하세요.
최종 생각
런치 시간은 직접적으로 영향을 미칩니다:
- 유지율
- 평점
- 인지된 품질
- 전환율
사용자는 몇 초 만에—문자 그대로—앱을 판단합니다. 시작 성능을 사후 생각이 아니라 기능으로 다루세요. 작업을 현명하게 연기하고, 지연 로딩을 적용하며, 불필요한 요소를 제거함으로써 핵심 기능을 변경하지 않고도 60 % 개선을 달성했습니다.