iOS & Swift

[모듈화 #5] Xcode 빌드 느린 이유와 해결법, 모듈화가 답인 이유 (측정 도구 포함)

코드 한 줄 고치고 빌드 버튼을 눌렀는데 몇 분씩 기다려 본 경험, iOS 개발자라면 누구나 있을 겁니다.

이석우iOS Developer5분 읽기
[모듈화 #5] Xcode 빌드 느린 이유와 해결법, 모듈화가 답인 이유 (측정 도구 포함) 대표 이미지

코드 한 줄 고치고 빌드 버튼을 눌렀는데 몇 분씩 기다려 본 경험, iOS 개발자라면 누구나 있을 겁니다.

하루에 빌드를 수십 번 한다고 치면, 빌드 1분 단축은 과장 없이 하루 30분 이상을 돌려받는 일이에요.

핵심 답부터 드리면, Xcode 빌드가 느린 가장 큰 구조적 원인은 “조금만 바꿔도 다시 컴파일되는 범위가 너무 넓다”는 것이고, 모듈화는 바로 이 재컴파일 범위를 줄이는 처방입니다.

이 글에서는 빌드가 느려지는 원인을 계층별로 나누고, 모듈화가 어디에 효과가 있는지, 그리고 개선 전후를 숫자로 측정하는 방법까지 정리합니다.

Xcode 빌드 속도, 측정해 보면 병목이 어디인지 바로 나옵니다
Xcode 빌드 속도, 측정해 보면 병목이 어디인지 바로 나옵니다

핵심 요약입니다.

  1. 증분 빌드의 재컴파일 범위는 모듈 단위로 갇힙니다. 모듈이 없으면 앱 전체가 한 단위입니다
  2. 측정 없이 튜닝하지 마세요. Xcode의 Build With Timing Summary가 기본 도구입니다
  3. 모듈화 외에 타입 추론 병목, dSYM 설정 같은 국지적 원인도 함께 봐야 합니다

Xcode 빌드는 왜 느려질까?

원인은 세 층으로 나눠 볼 수 있습니다.

첫째, 재컴파일 범위.

Swift는 파일 하나가 바뀌면 그 파일이 속한 모듈 안에서 영향받는 파일들을 다시 컴파일합니다. 문제는 모듈화가 안 된 앱은 타깃 전체가 모듈 하나라는 점이에요.

수십만 줄짜리 단일 타깃에서는 사소한 수정도 넓은 재컴파일을 유발합니다. 특히 여러 곳에서 쓰이는 타입을 건드리면 사실상 클린 빌드에 가까워지죠.

둘째, 타입 체킹 병목.

Swift 컴파일러는 타입 추론에 시간을 많이 씁니다. 복잡한 표현식 하나가 몇 초씩 잡아먹는 경우도 있어요. 긴 체이닝, 복잡한 삼항 연산, 거대한 SwiftUI body가 단골입니다.

셋째, 빌드 설정.

디버그 빌드인데 dSYM을 생성하고 있거나(DWARF with dSYM File), Whole Module Optimization이 잘못 걸려 있으면 매 빌드에 불필요한 비용이 추가됩니다.


모듈화는 빌드 속도를 어떻게 개선하나요?

모듈화가 개선하는 건 첫 번째 층, 재컴파일 범위입니다.

모듈 경계는 재컴파일의 방화벽입니다. 모듈 내부 구현만 바뀌면, 그 모듈 밖은 다시 컴파일할 필요가 없습니다.

지난 글의 레이어 구조가 여기서 힘을 발휘합니다.

  • 검색 기능(FeatureSearch) 내부를 수정하면, 재컴파일은 FeatureSearch와 앱 타깃 정도로 끝납니다
  • 반대로 모두가 의존하는 하위 모듈(CoreNetwork)의 공개 인터페이스를 바꾸면 상위가 줄줄이 재컴파일됩니다

그래서 빌드 속도 관점의 모듈 설계 원칙이 그대로 도출됩니다.

  1. 자주 바뀌는 코드는 그래프 상류(Feature)에, 안정된 코드는 하류(Core·Domain)에 둔다
  2. 하류 모듈의 public 인터페이스는 최소로 유지한다 (public이 아니면 밖에 영향이 없다)
  3. 모두가 의존하는 비대한 Common 모듈을 만들지 않는다

병렬화 효과도 있습니다. 의존이 없는 모듈끼리는 Xcode가 동시에 컴파일하므로, 그래프가 넓고 얕을수록 클린 빌드도 빨라집니다.

모듈 경계가 있으면 재컴파일은 이 범위에서 끝나요
모듈 경계가 있으면 재컴파일은 이 범위에서 끝나요

빌드 시간, 어떻게 측정하나요?

감으로 하는 최적화는 대부분 빗나갑니다. 도구 세 가지면 충분합니다.

Build With Timing Summary. Xcode 메뉴 Product → Perform Action → Build With Timing Summary를 실행하면, 빌드 로그 끝에 단계별 소요 시간이 정리됩니다. 어떤 타깃·어떤 단계가 병목인지 여기서 먼저 확인하세요.

타입 체킹 경고 플래그. 오래 걸리는 함수·표현식을 컴파일러가 직접 알려 줍니다. Other Swift Flags에 추가합니다.

-Xfrontend -warn-long-function-bodies=100
-Xfrontend -warn-long-expression-type-checking=100
// 100ms 넘는 함수·표현식에 경고 발생

빌드 타임라인. 빌드 로그에서 Assistant 영역의 타임라인을 열면 타깃별 병렬 실행 현황이 보입니다. 직렬로 길게 늘어선 구간이 있다면 의존 그래프가 병렬화를 막고 있다는 신호예요.


모듈화 말고도 확인할 것들

측정해 보면 모듈화보다 설정 한 줄이 문제인 경우도 많습니다. 디버그 빌드 기준 체크리스트입니다.

항목 권장 설정 (Debug)
Debug Information Format DWARF (dSYM 생성 끄기)
Compilation Mode Incremental
Optimization Level -Onone
Build Active Architecture Only Yes

여기에 더해 Xcode 16부터는 명시적 모듈 빌드(Explicitly Built Modules)가 도입되어 모듈 준비 단계의 병렬화가 개선됐습니다. 최신 Xcode를 쓰는 것 자체가 빌드 속도 개선이기도 합니다.

면접에서는 이렇게 물어봅니다

Q. 대규모 iOS 앱에서 빌드 시간을 줄이기 위한 접근을 설명해 보세요.

먼저 Build With Timing Summary로 병목을 측정하고, 설정(디버그의 dSYM 생성, 컴파일 모드)을 점검합니다. 구조적으로는 모듈화로 재컴파일 범위를 모듈 안에 가두는 것이 핵심이며 자주 바뀌는 코드를 의존 그래프 상류에 두고 하류 모듈의 public 표면적을 최소화합니다.

Q. 모듈을 나눴는데도 빌드가 계속 느리다면 무엇을 의심하겠습니까?

모두가 의존하는 하위 모듈이 자주 바뀌는 구조인지, public 인터페이스 변경이 잦은지 확인합니다. 또 타입 체킹 병목을 경고 플래그로 찾아 복잡한 표현식을 분해하고 의존 그래프가 직렬화되어 병렬 컴파일을 막고 있지 않은지 빌드 타임라인으로 확인합니다.

빌드 기다리는 시간, 하루로 모으면 생각보다 큽니다
빌드 기다리는 시간, 하루로 모으면 생각보다 큽니다

빌드 속도는 모듈화의 가장 체감되는 보상이지만, 모듈이 수십 개로 늘어나면 이번엔 프로젝트 관리 자체가 일이 됩니다.

시리즈 마지막 글에서는 이 관리 비용을 낮춰 주는 도구, Tuist를 다룰게요. pbxproj 충돌 해방과 바이너리 캐싱까지 같이 정리합니다.