왜 성능을 최적화해야 하나?
- 사용자 기대: 앱은 빠르게 시작하고 즉시 반응해야 합니다. 느린 앱은 삭제로 이어집니다.
- 비용과 배터리: 과다한 네트워크 요청은 데이터 비용을 증가시키고 배터리를 빨리 소모합니다.
- 경험의 일관성: 더 짧은 지연은 더 높은 전환율과 사용자 만족으로 이어집니다.
간단 정의: iOS — iPhone과 iPad에서 구동되는 Apple의 모바일 운영체제.
핵심 원칙
- 메인 스레드(메인 큐)에서 무거운 작업을 피합니다. UI는 메인 스레드에서만 안전하게 갱신할 수 있으므로, 디코딩·계산·네트워크 같은 무거운 작업은 백그라운드로 이동시킵니다.
- 반복적으로 호출되는 콜백(스크롤, 셀 구성 등)은 극도로 경량화합니다.
- 측정(프로파일링) 없이는 최적화하지 마세요. 가짜 가정은 비용만 발생시킵니다.
뷰 수와 투명도 줄이기
설명: 화면에 많은 뷰가 겹치거나 투명 효과가 많으면 합성 작업이 늘어납니다. GPU/CPU 부하가 커져 프레임 드롭이 발생합니다.
실행 방법:
- Xcode에서 View Debugging -> Rendering 도구를 사용해 중첩된 투명 레이어를 시각화하세요. 메뉴 항목 예: Debug → View Debugging → Rendering → Color Blended Layers. 이 도구는 겹침이 많은 뷰를 색으로 표시해 줍니다.
- 가능한 경우 투명도를 제거하거나 배경을 단일 색으로 변경합니다.
- 복잡한 커스텀 뷰는 단순화하거나 캡처한 이미지를 사용해 렌더링 오버헤드를 줄이세요.
언제 효과가 적은가(예외):
- 애니메이션이나 디자인 요구로 투명도가 필수인 경우, 대체로 성능을 트레이드오프로 허용해야 합니다.
함수 내부 작업량 줄이기
설명: scrollViewDidScroll, cellForItemAt: indexPath: 같은 콜백은 매우 자주 호출됩니다. 여기서 무거운 작업이 있으면 전체 UX가 떨어집니다.
권장 방법:
- 셀 재사용(reuse)을 철저히 적용하세요.
- 셀 구성은 최소한의 연산으로 유지하고, 네트워크/이미지 로딩은 비동기로 처리합니다.
- 레이아웃 계산(특히 동적 텍스트 측정)은 사전 계산하거나 캐싱하세요.
- “가장 단순한” 뷰와 셀을 사용하세요. 즉, 불필요한 객체 할당이나 제약 조건을 피합니다.
예시: 텍스트 크기 측정(boundingRectWithSize)은 비용이 크므로 필수시만 사용하고 결과를 캐시하세요.
JPEG(이미지) 디코딩 최적화
문제: 이미지 디코딩은 흔한 프레임 드롭 원인입니다. UIImageView가 메인 스레드에서 디코딩을 수행하면 큰 이미지에서 UI 정체가 발생합니다.
해결책:
- 디코딩(데코딩로드)을 백그라운드 스레드에서 수행하고, 메인 스레드에서는 이미 디코딩된 비트맵을 설정합니다.
- 정적 썸네일이나 리사이즈된 이미지는 서버에서 미리 만들어 전달하거나 앱 내에서 한 번만 생성해 재사용합니다.
- 이미지 캐시 전략을 수립하세요(메모리 캐시 vs 디스크 캐시).
비고: UIImageView의 기본 디코딩이 항상 최적은 아닙니다. 직접 디코딩을 제어하면 메인 스레드 블로킹을 회피할 수 있습니다.
오프스크린 렌더링(Off-Screen Rendering)
설명: 특정 레이어 속성(예: 미세한 마스크, 그림자, 복잡한 코너 반경 등)을 사용하면 시스템이 화면 밖에서 뷰를 렌더링한 뒤 합성합니다. 이 과정은 CPU/GPU 비용이 큽니다.
진단 방법:
- Xcode Debug → View Debugging → Rendering → Color Offscreen-Rendered Yellow를 이용하면 오프스크린 렌더링 컴포넌트를 노란색으로 표시합니다.
- 노란색/빨간색 표시된 뷰를 찾아 속성(shouldRasterize, shadowPath, cornerRadius, masksToBounds 등)을 검토하세요.
완화 방안:
- 그림자는 shadowPath를 사용해 GPU 연산을 줄이세요.
- 복잡한 마스크는 미리 렌더링된 이미지로 대체하세요.
- shouldRasterize는 상황에 따라 도움이 되지만, 스크롤 중에는 비활성화하는 것이 좋습니다.
기타 일반 권장사항
- 자동 레이아웃을 사용할 때 뷰 계층 구조를 단순하게 유지하고 오래된 디바이스에서의 성능을 고려하세요.
- 가능한 작업은 백그라운드 큐로 이동하되 메모리 경고를 감시하세요.
- 캐시 정리: KineMaster 같은 리소스 집약 앱에서는 주기적 캐시 정리가 성능 회복에 도움이 됩니다.
- 권한 제한: 필요 이상의 위치·카메라 권한 요청은 피하고, 권한 요청을 최소화하면 동작·전력 관점에서 이득이 있습니다.
원본에서 나온 명령 예시(메뉴): Debug → View Debugging → Rendering → Color Blended Layers
실제 진단·개선 미니 방법론
- 측정: Instruments(시스템 프로파일러), Xcode의 Time Profiler, Core Animation FPS, Allocations를 구동합니다.
- 재현: 느려지는 화면을 재현 가능한 스크립트나 시나리오로 만듭니다(예: 긴 스크롤, 대량 이미지 로드).
- 병목 찾기: 프레임 드롭, 긴 메인 스레드 작업, 과도한 레이아웃/컴포지션을 찾아냅니다.
- 작은 변경 적용: 하나의 원인에 대해 하나의 수정만 적용하고 재측정합니다.
- 반복: 변경의 효과를 측정하고, 필요하면 롤백 또는 대체 전략을 적용합니다.
의사결정 흐름(간단한 진단 트리)
flowchart TD
A[앱이 느리다] --> B{주로 어느 상황에서?}
B -->|앱 시작| C[런치 / 초기화 측정]
B -->|스크롤| D[셀 구성·레이아웃 검사]
B -->|네트워크| E[요청 수·응답 지연 분석]
B -->|이미지 렌더| F[이미지 디코딩·오프스크린 검사]
C --> G[메인 스레드 블로킹 확인]
D --> G
E --> H[네트워크 최적화: 배칭·압축·캐시]
F --> G
G --> I[백그라운드로 이동하거나 캐시 사용]
H --> I
I --> J[재측정]
J -->|개선됨| K[배포]
J -->|개선 불가| L[설계 변경 검토]
역할별 체크리스트
개발자:
- Instruments로 메인 스레드와 메모리 프로파일링 수행
- 이미지 디코딩을 백그라운드로 이전
- 셀 재사용 및 레이아웃 캐시 구현
- 오프스크린 렌더링 유발 속성 점검
QA 엔지니어:
- 성능 테스트 시나리오(부하·연속 스크롤·네트워크 변동) 작성
- 프레임 드랍, 메모리 증가, 앱 재시작 지표 기록
- 레거시 디바이스(예: 2–3세대 이전)에서 회귀 확인
프로덕트 매니저:
- 가장 민감한 사용자 플로우 우선순위 지정
- 성능 목표 설정(예: 95% 뷰 처리가 16ms 이내)
- 변경의 UX 영향 평가 및 배포 승인
운영(DevOps):
- 이미지 서버에서 리사이징/포맷 전환(예: WebP 또는 HEIF) 지원
- CDN과 캐시 정책 최적화
반례와 대안
- 투명 효과가 브랜드 아이덴티티의 핵심인 경우 성능을 일부 포기할 수 있습니다. 이때는 특정 디바이스에 한해 디자인을 단순화하는 전략이 현실적입니다.
- 서버 측에서 썸네일을 제공할 수 있다면 앱 내 리사이즈 비용을 줄이는 것이 보통 더 효율적입니다.
요약
- 측정 후 개선: Instruments와 Xcode 디버깅 도구로 병목을 찾으세요.
- 메인 스레드를 가볍게 유지하고, 반복 콜백을 최적화하세요.
- 이미지 디코딩과 무거운 계산은 백그라운드로 이동하세요.
- 오프스크린 렌더링을 시각화해 제거하거나 완화하세요.
Important: 변경 한 건당 하나의 측정을 수행하고, 효과를 확인한 뒤 다음 변경으로 넘어가세요.
Notes:
- 자동 레이아웃과 복잡한 뷰 계층은 오래된 디바이스에서 특히 문제가 됩니다.
- 메모리 경고를 항상 처리하고 캐시 정책을 마련하세요.
요약 정리:
- 뷰 수·투명도·오프스크린 렌더링을 줄이세요.
- 반복 호출 함수는 가능한 한 빠르게 유지하세요.
- 이미지 디코딩을 백그라운드로 이동하고 캐시 전략을 마련하세요.
- 역할별 체크리스트로 책임을 분명히 하고, 측정->수정->재측정을 반복하세요.