이 글은 누구를 위한 것인가
- 클린 빌드에 5분 이상 걸리는 iOS 팀
- 코드 변경 후 증분 빌드가 느린 개발자
- CI/CD 빌드 시간을 단축하고 싶은 팀
들어가며
Xcode 빌드가 느리면 개발자 생산성이 크게 떨어진다. 10분 빌드 × 하루 20번 = 200분 낭비다. 빌드 설정 최적화와 코드 패턴 개선으로 빌드 시간을 절반 이하로 줄일 수 있다.
이 글은 bluefoxdev.kr의 iOS 개발 생산성 가이드 를 참고하여 작성했습니다.
1. 빌드 시간 병목 분석
[빌드 시간 측정 방법]
1. Xcode Build Timing Summary:
Product > Perform Action > Build with Timing Summary
각 파일 컴파일 시간 확인
2. 명령행 측정:
xcodebuild -workspace App.xcworkspace \
-scheme App -configuration Debug \
SWIFT_COMPILATION_MODE=wholemodule 2>&1 | \
grep -E "^CompileSwift|^Libt"
3. BuildTimeAnalyzer 도구:
오래 걸리는 함수 타입 추론 시각화
[일반적 빌드 시간 병목]
1. 느린 타입 추론 (50% 이상)
복잡한 클로저, 긴 배열 리터럴
해결: 타입 명시
2. 매크로 처리
@Observable, @Model 등
해결: 별도 타겟으로 분리
3. 전체 재컴파일 (헤더 변경)
Objective-C/Swift 브릿지
해결: 최소화
4. 패키지 빌드
SPM, CocoaPods
해결: 이진 의존성 (XCFramework)
[빌드 최적화 체크리스트]
□ Debug 빌드에서 최적화 끄기
□ Whole Module Optimization 이해
□ 불필요한 타겟 제거
□ 증분 빌드 활성화
□ 빌드 캐시 설정
2. 빌드 최적화 설정
# Xcode Build Settings 최적화
# 1. Compilation Mode
# Debug: Incremental (빠른 증분 빌드)
SWIFT_COMPILATION_MODE = incremental
# Release: Whole Module (최적화)
# SWIFT_COMPILATION_MODE = wholemodule
# 2. 타입 체커 제한 (느린 타입 추론 감지)
# Swift Other Flags:
OTHER_SWIFT_FLAGS = -warn-long-function-bodies=100 -warn-long-expression-type-checking=100
# 3. 병렬 빌드
SWIFT_ENABLE_BATCH_MODE = YES
# 4. Module Verifier 비활성화 (Debug)
ENABLE_MODULE_VERIFIER = NO
// 느린 타입 추론 패턴과 해결
// ❌ 느림: 복잡한 타입 추론
let result = items
.filter { $0.isActive && $0.count > 0 }
.map { item in
(id: item.id, value: item.value * 2.5, label: item.name + " (\(item.count))")
}
.sorted { $0.value > $1.value }
// ✅ 빠름: 타입 명시
let filtered: [Item] = items.filter { $0.isActive && $0.count > 0 }
let mapped: [(id: String, value: Double, label: String)] = filtered.map { item in
(id: item.id, value: item.value * 2.5, label: "\(item.name) (\(item.count))")
}
let result: [(id: String, value: Double, label: String)] = mapped.sorted { $0.value > $1.value }
// ❌ 느림: 큰 딕셔너리 리터럴
let config = [
"key1": "value1", "key2": "value2", "key3": "value3",
"key4": "value4", "key5": "value5", "key6": "value6",
// ... 수십 개
]
// ✅ 빠름: 분리
var config: [String: String] = [:]
config["key1"] = "value1"
config["key2"] = "value2"
// 또는 struct 사용
struct Item {
let id: String
let name: String
let value: Double
let count: Int
let isActive: Bool
}
#!/bin/bash
# CI 빌드 캐싱 스크립트
# 1. Derived Data 캐싱
DERIVED_DATA_PATH="$HOME/Library/Developer/Xcode/DerivedData"
CACHE_KEY=$(shasum Package.resolved Podfile.lock 2>/dev/null | md5)
# 캐시 복원
if [ -d "build-cache/$CACHE_KEY" ]; then
echo "캐시 복원 중..."
cp -R "build-cache/$CACHE_KEY" "$DERIVED_DATA_PATH"
fi
# 빌드 실행
xcodebuild \
-workspace App.xcworkspace \
-scheme App \
-configuration Debug \
-derivedDataPath "$DERIVED_DATA_PATH" \
-destination "platform=iOS Simulator,name=iPhone 15" \
build | xcbeautify
# 캐시 저장
echo "캐시 저장 중..."
mkdir -p "build-cache"
cp -R "$DERIVED_DATA_PATH" "build-cache/$CACHE_KEY"
# 2. SPM 패키지 캐싱
# Package.resolved를 캐시 키로 사용
SPM_CACHE_PATH="$HOME/Library/Caches/org.swift.swiftpm"
SPM_CACHE_KEY=$(shasum Package.resolved | cut -d' ' -f1)
# GitHub Actions 예시
# - name: Cache SPM
# uses: actions/cache@v3
# with:
# path: ~/Library/Caches/org.swift.swiftpm
# key: ${{ runner.os }}-spm-${{ hashFiles('Package.resolved') }}
# 3. XCFramework 이진 의존성 (패키지 빌드 완전 스킵)
# 자주 변경되지 않는 의존성을 미리 컴파일
# 예: firebase-ios-sdk XCFramework 사용
echo "빌드 완료"
// 모듈 분리로 재컴파일 최소화
// Package.swift에서 모듈 분리
// let package = Package(
// name: "MyApp",
// targets: [
// .target(name: "Core", dependencies: []),
// .target(name: "Features", dependencies: ["Core"]),
// .target(name: "App", dependencies: ["Core", "Features"]),
// ]
// )
// Preview 매크로 비용 최소화
// SwiftUI Preview는 별도 컴파일 필요
// → Preview 없이도 동작하게 하거나 별도 타겟
struct ContentView: View {
var body: some View {
Text("Hello")
}
}
// Preview를 조건부로
#if DEBUG
#Preview {
ContentView()
}
#endif
마무리
Xcode 빌드 최적화의 80%는 코드에 있다. -warn-long-expression-type-checking=100으로 100ms 이상 걸리는 표현식을 찾아 타입을 명시하면 증분 빌드가 30-50% 빨라진다. CI 빌드는 SPM 캐시와 DerivedData 캐싱으로 클린 빌드 시간을 2-3배 단축할 수 있다.