iOS 개발자라면 누구나 ⌘ + B를 누르고 Xcode가 프로젝트를 빌드하기를 가만히 기다려본 경험이 있을 겁니다.
그렇다면 Xcode에서 ⌘ + ⇧ + K 눌러본 적 있죠?
맞아요. Clean Build Folder입니다.
이걸 누르면 빌드 결과물이 싹 지워지고, 다음 빌드는 사실상 처음부터 다시 빌드하게 돼요.

그럼 그 이후에 빌드하는 것은 뭐가 다를까요? 왜 처음 클린 빌드 이후에 하는것보다 훨씬 빠르게 진행이 될까요??
그게 오늘 이야기할 증분 빌드입니다 😚
증분 빌드란?
증분 빌드는 아주 간단하게 말하면 이거예요.
변경된 부분과, 그 변경으로 인해 영향을 받는 부분만 다시 빌드한다
조금 더 풀어 쓰면:
- 🔁 바뀐 코드만
- ➕ 그 코드에 의존하는 코드만
- 다시 컴파일
- ❌ 나머지는 이전 빌드 결과 재사용
그래서 대부분의 초기 빌드 이후의 빌드는 훨 빠르게 끝나죠
🤔 Xcode는 무엇이 바뀌었는지 어떻게 알까?
그렇다면 Xcode는 어떻게 “어디까지 다시 빌드해야 할지”를 판단하는 걸까요?
1단계: 변경 감지
Xcode 빌드 시스템은 각 파일의 상태를 기록해 둡니다.
대표적으로 이런 것들이에요.
- 파일 내용의 hash
- timestamp
- 컴파일 옵션
- 이전 빌드 산출물
(.o, .swiftmodule 등)
그래서 빌드를 시작할 때, 이전 상태와 현재 상태를 비교해서
- 변경되지 않은 파일은 재사용
- 변경된 파일만 다시 컴파일
여기까지만 보면 “아, 그냥 파일 비교구나” 싶죠.
하지만!
🧠 Swift 컴파일러가 한 번 더 하는 일
Swift는 C / Objective-C 처럼 파일 하나 바뀌었다고 그 파일만 딱 컴파일하고 끝이 아닙니다
Swift 컴파일러는 먼저 다음 작업을 수행합니다.
- 같은 타겟에 속한 모든 .swift 파일을 스캔
- 타입, 프로토콜, 함수 시그니처를 분석
- 파일 간의 의존성 관계를 계산
이걸 보통 의존성 그래프라고 불러요.
예를 들어 이런 구조를 생각해봅시다.
ProductResponse.swift
↑
APIClient.swift
↑
ProductListView.swift
이 상태에서 ProductResponse.swift를 수정하면:
- 이를 사용하는 APIClient.swift → 영향 있음
- ProductListView.swift → 연쇄적으로 영향
하지만 전혀 관련 없는 파일은 다시 빌드하지 않습니다.
👉 핵심은 “모든 파일”이 아니라 “영향받는 파일만” 입니다.
근데 누가 이 의존성을 판단할까요?
Swift 컴파일러의 Driver가 이 역할을 담당합니다.
Swift Driver는 컴파일 과정에서 .swiftdeps 파일을 생성하고 이를 기반으로 파일 간 의존성 그래프를 관리합니다.
즉,
- Xcode는 빌드 전체를 orchestration 하고
- Swift Driver가 실제로
“증분 컴파일이 가능한지”를 판단합니다.
“영향을 받았는지 아닌지는 대체 뭘 기준으로 판단할까?”
정답은 인터페이스
인터페이스에 포함되는 것
- stored property
- 함수 시그니처
- protocol 요구사항
- public / open API
중요한 점은 이겁니다.
구현(Body)은 다르더라도 인터페이스가 같으면, 의존성 전파는 일어나지 않는다
Swift 5.x 이후 컴파일러는 함수의 서명(Signature) 과 구현(Body) 를 분리해서 관리합니다. 그래서 가능한 일입니다.
그래서 함수 body만 바꾸면 빠른 이유!!!
func foo() {
print("A") → print("B")
}
이건 내부 구현만 바뀐 거죠.
- 함수 이름 그대로
- 파라미터 그대로
- 리턴 타입 그대로
즉,
- 인터페이스 변화 ❌
- 의존성 그래프 변화 ❌
그래서 결과는?
- 이 파일만 재컴파일
- 나머지는 캐시 재사용
- ✅ 증분 빌드 성공
문제는 인터페이스가 바뀔 때입니다.
struct User {
let name: String
let address: String // 추가
}
이 한 줄이 의미하는 건 꽤 큽니다.
- 타입 크기 변경
- 메모리 레이아웃 변경
- 접근 방식 변경
그래서 User를 사용하는 모든 파일이 전부 영향 대상이 됩니다.
실제 XCode를 보면서 이해하기
이제 실제 예제를 통해 증분 빌드를 확인해봅시다.
struct Product: Identifiable, Decodable {
let id: Int
let name: String
let price: Double
}
@MainActor
final class ProductListViewModel: ObservableObject {
@Published private(set) var products: [Product] = []
@Published private(set) var isLoading = false
@Published private(set) var errorMessage: String?
private let apiClient: APIClient
init(apiClient: APIClient = APIClient()) {
self.apiClient = apiClient
}
func load() async {
isLoading = true
defer { isLoading = false }
do {
products = try await apiClient.fetchProducts()
errorMessage = nil
} catch {
errorMessage = error.localizedDescription
}
}
}
struct APIClient {
func fetchProducts() async throws -> [Product] {
let json = """
[
{ "id": 1, "name": "Keyboard", "price": 99.0 },
{ "id": 2, "name": "Mouse", "price": 49.0 }
]
"""
let data = Data(json.utf8)
return try JSONDecoder().decode([Product].self, from: data)
}
}
🧱 초기 빌드

초기 빌드 후 Build에 관해 로그를 찍은거에요
Compute target dependency graph
fetchedFromCache = false
이는 캐시된 결과가 없고, 의존성 그래프를 처음 계산하고 있다는 의미입니다.
를 발견하실 수 있어요




➡️ 모든 Swift 파일이 컴파일된 것을 확인할 수 있죠
✏️ 함수 body만 수정해보기
struct APIClient {
func fetchProducts() async throws -> [Product] {
let json = """
[
{ "id": 1, "name": "Keyboard", "price": 99.0 },
{ "id": 2, "name": "Mouse", "price": 49.0 }
]
"""
print(json)
let data = Data(json.utf8)
return try JSONDecoder().decode([Product].self, from: data)
}
}
여기에서 그냥 print문 하나만 추가해볼께요


- 인터페이스 변화 없음
- Swift Driver는 증분 컴파일 가능하다고 판단
- 해당 파일만 재컴파일(APIClient만!!)
인터페이스 변경해보기
struct Product {
let id: Int
let name: String
let price: Int
let owner: String
}
이번에는 인터페이스를 변경했습니다.

당연히 product의 fetchedFromCache = false겟죠? 나머지는어떨까요?


이를 사용하는 파일들도 → 연쇄적으로 재컴파일하는 것을 확인했습니다.
하지만 중요한 점은, 의존성 그래프 자체를 다시 계산한것이 아니라 재빌드 범위가 넓어진 것입니다
정리
- 증분 빌드는 “안 바뀐 건 다시 빌드하지 않는” 최적화 전략
- 기준은 파일 변경 여부가 아니라 인터페이스 변경 여부
- Swift Driver가 의존성 그래프를 기반으로 판단
- 그래서 body 수정은 빠르고, 인터페이스 수정은 느리다
이걸 알고 나면, 빌드 시간이 왜 갑자기 늘어났는지도 조금은 예측할 수 있게 되는거죠!
'SWIFT개발일지' 카테고리의 다른 글
| TCA + Clean Architecture에서 의존성 관리, Needle 도입한 이유 (0) | 2026.01.11 |
|---|---|
| ProtoBuf - 이게 뭔데 사람들은 환호성을 지를까? (1) | 2025.11.29 |
| 아 지겹다 복붙! Xcode 커스텀 템플릿 만들기 (1) | 2025.11.25 |
| BLE 완전 기초: CoreBluetooth를 이해하기 위한 필수 개념 (0) | 2025.11.16 |
| 이미지 URL 저장 시 마주하는 함정 문제들 (0) | 2025.09.11 |