2024. 10. 29. 19:12ㆍ🍏/Xcode
이 글을 쓰게 된 이유.. 웅장해진다...
[Architecture] Modular Architecture VS Clean Architecture
어느날 문득 Modular Architecture와 Clean Architecture에 대한 생각을 하게 되었습니다. 모듈화나 클린이나 결국엔 개념적으로 레이어를 나누고 개발자들의 상황에 맞춰 선택적으로 사용할텐데, 둘은 어
chanhhh.tistory.com
Modular Architecture와 Clean Architecture는 의존성 관리 방식에서 차이를 보입니다.
- Modular Architecture에서는 모듈 간 의존성이 명시적으로 설정됩니다. 즉, 하나의 모듈이 필요한 경우 다른 모듈에 의존성을 가지며, 이를 통해 기능별로 독립된 모듈들이 서로 필요한 부분에 연결됩니다.
- Clean Architecture에서는 각 레이어가 상위 레이어에만 의존하게 하여, 낮은 레벨의 레이어가 높은 레벨의 레이어에 종속되지 않도록 설계됩니다.
요약하면, Modular Architecture는 기능 단위의 독립적 모듈 간 의존성 관리, Clean Architecture는 레이어 간 명확한 의존성 규칙을 통해 코드의 결합도를 낮추는 방식이라고 볼 수 있습니다.
이때 두 아키텍처에서 어떤 기능을 수정했을때 증분 빌드는 어떻게 작동할 것인가 ? 에 대한 궁금증이 들어서 이 글을 쓰게 되었습니다.
들어가기 앞서..
두 아키텍처의 증분 빌드에 대한 이해를 들어가기 앞서 우선 빌드가 뭔지 알아보자.
아래는 Xcode 빌드의 병렬 처리에 관한 WWDC22의 컨텐츠입니다. 빌드에 대한 내용을 자세하게 담고있습니다.
Xcode 빌드에서의 병렬 처리에 대한 오해 해소 - WWDC22 - 비디오 - Apple Developer
Xcode 빌드 시스템이 빌드에서 최대 병렬 처리를 추출하는 방법을 알아보세요. 프로젝트를 구조화하여 빌드 효율을 개선하는 방법을 살펴보고, Xcode에서 타겟의 빌드 단계 간의 관계를 해결하기
developer.apple.com

Xcode 빌드 시스템의 이해
컴파일 과정
Xcode의 컴파일러는 Clang과 Swift를 사용하여 소스 코드를 객체 파일로 변환/생성합니다. 이 객체 파일들은 프로그램의 다양한 구성 요소들을 나타내며, 실행 파일을 생성하는 데 필수적인 요소입니다.
링크 과정
생성된 객체 파일들은 링커에 의해 결합됩니다. 링커는 이 객체 파일들을 하나의 실행 파일로 통합하고, 필요한 외부 라이브러리 참조를 추가하여 프로그램이 외부 코드와 상호작용할 수 있도록 합니다.
종속성 관리
빌드 시스템의 핵심은 종속성 관리입니다. 파일 콘텐츠 자체보다는 작업 간의 종속성이 중요합니다. 특정 작업이 다른 작업의 입력을 생성할 때, 그 작업이 완료되기 전까지는 다음 작업을 시작할 수 없습니다. 이를 통해 모든 작업이 올바른 순서로 실행될 수 있도록 합니다.
작업의 실행
차단이 해제된 작업(Unblock) > 다운스트림
차단하는 작업은(Block) > 업스트림.
종속성이 없는 작업은 먼저 실행되며, 이러한 작업들이 완료된 후 다운스트림 작업이 진행됩니다. 이 과정은 모든 계획된 작업이 완료될 때까지 이 프로세스를 따릅니다. 초기 빌드가 아닌 이후 빌드에서는, 출력이 최신 상태인 경우 입력이 변경되지 않은 작업은 빌드 시스템이 건너뛸 수 있습니다. 입력이 변경된 경우 해당 작업과 관련된 다운스트림 작업도 재실행됩니다.
위 사진을 보면 프레임워크와 앱 익스텐션은 종속적이지 않기 때문에, 병렬로 작업이 실행되는 것을 확인 할 수 있습니다.
증분 빌드(Incremental build)?
프로젝트 전체를 처음부터 빌드하는 것이 아니라 변경된 부분만을 대상으로 빌드를 수행하는 방법입니다. 위와 같은 빌드의 다른 모든 작업을 모두 건너 뛸 수 있다면 프로젝트의 처리 시간이 매우 줄어듭니다. 반복 작업시 배우 빨라지며 이를 증분 빌드라고 합니다. 즉, 최근 빌드 이후 수정된 소스 코드나 리소스 파일들만 새로 컴파일하거나 링크하여 전체 빌드 시간을 단축시키는 효율적인 프로세스입니다.

빌드 요약
- 컴파일러가 O 파일 생성, 링커가 개체 파일 소비
- 작업 간의 종속성을 관리하여 파일들을 올바른 순서로 처리
- 작업 실행. 적절한 업스트림과 다운스트림으로 종속성에 맞춰 작업 실행
- 재빌드(증분빌드) 변경된 부분만 대상으로 빌드 수행.
위의 내용을 토대로 빌드에 대해 이해하게 되었습니다.
근데 이제 제가 궁금해 했던 점을 알아봐야겠죠 ? 우선 Clean Architecture의 증분 빌드 부터 생각해보 겠습니다.
Clean Architecture의 증분 빌드
클린아키텍쳐는 대부분 하나의 타겟 내부에서 레이어로 나누는 구조일뿐이니, Xcode 입장에서는 하나의 Swift Module로 취급되어 이미지 01번과 같이 진행이 됨을 알 수가 있습니다. 따라서 증분 빌드는 일반적인 Swift 증분 컴파일 방식과 동일하게 동작합니다.
이때 병렬화를 증가시키는 방법에 대해서 영상에서 이야기하고 있습니다. (10:34)

FUSE_BUILD_SCRIPT_PHASES를 사용하면 된다고 합니다.

ENABLE_USER_SCRIPT_SANDBOXING으로 커스텀하게 병렬화를 세세하게 조정 할 수 있음을 알려줍니다.
Modular Architecture의 증분 빌드
Modular Architecture는 일반적으로 기능을 여러 Target 또는 Swift Package 단위의 모듈로 분리합니다. 이처럼 전체 앱이 여러 모듈의 의존성으로 구성되어 있기 때문에, 처음에는 모듈 간 의존성이 많아질수록 전체 앱의 빌드 시간도 함께 길어질 것이라고 생각했습니다.
가장 궁금했던게 이 영상에서 해소가 됐습니다.

위 사진은 타겟을 의존성으로 갖는 시스템에 대해서 빌드 플로우를 보여줍니다.
보이는 그대로 빌드 플로우를 갖게되면 사실 거대한 Modular Architecture에서 빌드를 하게 되었을 때, 엄청나게 빌드 시간이 들겁니다. 여기서 프레임워크 내부에서 증분빌드와 xcode의 빌드의 병렬성 덕분에 생각보다 적은 시간으로 빌드를 할 수 있게 해줍니다. 여기에서 Swift Driver가 분리된 emit-module 작업을 활용하여, 이를 기반으로 다른 타겟의 컴파일을 시작할 수 있도록 작업을 스케줄링합니다. 타겟간의 병렬성과 증분빌드의 효율을 향상 시킵니다.
![]() |
![]() |
![]() |
![]() |
Swift 타겟 종속성은 종속 항목이 public interface를 캡쳐하는 바이너리 모듈 파일을 제공하도록 함으로써 해결됩니다.
Xcode 14부터는 Swift로 작성된 완전히 새로운 Swift-Driver 덕분에 빌드 시스템과 컴파일러가 완전히 통합되었습니다.
Xcode 빌드시스템은 코드 컴파일을 위해 수행해야 하는 모든 작업의 central scheduler 관리자 역할을 합니다.
이 중앙 계획 매커니즘을 통해 Xcode는 세밀한 스케줄링 결정을 내릴 수 있고 프로젝트를 빌드할 때 사용 가능한 CPU 초과 구독과 시스템 성능 저하 없이 리소스만큼만 사용하도록 보장해 줍니다.
위의 영상을 보면 한번에 이해하기 쉬우실 겁니다.
Xcode 14 및 Swift 5.7 부터는 새롭게 타겟 모듈의 구성이 모든 소스 파일에서 직접 별도의 emit-module 작업에서 수행됩니다. 이는 타겟 종속성이 모듈 방출 작업이 완료되는 즉시 다른 컴파일러 작업을 기다리지 않고 다른 Target의 컴파일을 시작할 수 있음을 의미합니다. 프로젝트로 확장해보면 전반적으로 비슷한 양의 작업을 수행하고 있지만 빌드 시스템은 컴퓨터의 리소스를 더 효율적으로 사용할 수 있고 빌드를 훨씬 더 빠르게 완료할 수 있습니다.
Eager linking ?

일반적으로 두 타겟의 종속성 그래프는 위 사진과 같습니다.
종속 타겟을 링킹하려면 타겟 자체의 컴파일 출력과 함께 자체 종속성들이 링크된 product가 필요합니다.

Eager linking을 사용하면 이 링크 완료를 기다려야 하는 의존성이 제거되면서 종속 타겟이 더 일찍 링킹 될 수 있도록 합니다.
디펜던시가 링크된 product에 의존하는 대신, 이전 빌드 프로세스에서 emit-module 작업에 의해 생성된 텍스트 기반 dynamic library stub에 의존하게 됩니다. 이 stub에는 종속성이 사용할 수 있도록 링크된 product의 심볼 목록이 포함되어 있습니다.

Xcode build setting에서 이 최적화를 활성화 할 수 있습니다.
Eager linking은 디펜던시 항목에 의해 동적으로 링킹된 모든 Swift-only 타겟에 적용됩니다.
결론적으로
이번 글을 작성하기 전까지는 Modular Architecture는 타겟 의존성이 많아질수록 증분 빌드에서 불리할 것이라고 생각했습니다. 하나의 모듈이 변경되면 상위 모듈들이 연쇄적으로 영향을 받아 결국 전체 빌드 시간이 크게 늘어날 것이라는 단순한 가정을 가지고 있었기 때문입니다.
하지만 WWDC의 빌드 시스템 영상을 통해 확인한 내용은 제 예상과는 달랐습니다.
Xcode는 단순히 타겟을 순서대로 빌드하는 것이 아니라 프로젝트 전체의 의존성 그래프를 분석하여 가능한 많은 작업을 병렬로 수행합니다. 또한 Swift Driver는 emit-module 단계를 분리하여 다른 타겟이 더 이른 시점부터 컴파일을 시작할 수 있도록 만들었고, Eager Linking과 같은 최적화를 통해 링크 단계 역시 병목을 줄이고 있었습니다.
즉, 모듈화 자체가 빌드를 느리게 만드는 것이 아니라, 의존성 구조를 어떻게 설계하느냐가 더 중요한 요소였습니다.
모듈화는 빌드 비용을 증가시킬 수도 있지만, Xcode는 병렬 빌드와 증분 빌드를 통해 이러한 비용을 상당 부분 완화하도록 설계되어 있습니다.
결국 Modular Architecture와 Clean Architecture는 서로 경쟁하는 개념이라기보다 서로 다른 목적을 가진 설계 방식이며, 빌드 성능 또한 Xcode와 Swift 컴파일러가 제공하는 다양한 최적화 덕분에 생각보다 훨씬 효율적으로 관리되고 있다는 점을 알게 되었습니다.
느낀점
이번 글을 쓰면서 가장 크게 느낀 점은 막연한 추측과 실제 빌드 시스템의 동작은 생각보다 많이 다를 수 있다는 것이었습니다.
제가 맨 처음에 궁금했던 것은 ‘증분 빌드’였지만 실제로는 Xcode가 증분 빌드뿐 아니라 병렬 스케줄링, Swift Driver, Eager Linking과 같은 여러 최적화를 함께 활용하여 전체 빌드 시간을 줄이고 있다는 사실이었습니다. 결국 빌드 성능은 하나의 기술이 아니라 여러 최적화가 함께 만들어내는 결과라는 점을 이해할 수 있었습니다.
처음에는 “모듈이 많아지면 당연히 빌드도 느려질 것이다.“라는 단순한 가설에서 시작했습니다. 하지만 WWDC 세션과 관련 자료를 하나씩 찾아보며 빌드 시스템이 내부적으로 어떻게 동작하는지 이해하게 되었고, 그 과정에서 제가 가지고 있던 선입견이 자연스럽게 깨졌습니다.
Xcode 빌드에서의 병렬 처리에 대한 오해 해소가 이 영상의 주제였는데, 저의 기존 생각과는 다른 많은 부분들이 있었습니다. 병렬 처리와 증분 빌드의 원리를 이해하면서 이전에는 단순히 결과만 받아들이던 빌드 과정을 조금 더 내부적인 관점에서 바라볼 수 있게 되었습니다. 특히, Swift-Driver가 emit-module 단계를 분리하여 타겟 간 병렬성을 높이는 방식이나, Eager Linking과 같은 최적화 기법은 왜 Xcode가 예상보다 빠르게 빌드를 수행할 수 있는지를 이해하는 데 큰 도움이 되었습니다.
또 하나 느낀 점은 아키텍처를 선택할 때는 단순히 ‘좋다’, ‘유행한다’는 이유보다 그 구조가 실제로 어떤 비용과 장점을 가지는지 이해하는 과정이 훨씬 중요하다는 것입니다. 모듈화 역시 단순히 프로젝트를 잘게 나누는 것이 아니라 의존성을 어떻게 설계하고 관리하느냐가 핵심이라는 점을 다시 한번 느꼈습니다.
앞으로는 Modular Architecture를 실제 프로젝트에 적용하면서 빌드 로그와 Build Timeline, Build Assistant 등을 함께 분석해 이론이 실제 환경에서는 어떤 결과로 이어지는지 직접 검증해보고 싶습니다. 이번 글은 그 과정의 첫 번째 기록이라고 생각합니다.
이번 글을 작성하면서 단순히 WWDC 내용을 정리하는 것을 넘어, "빌드는 어떻게 동작하는가"라는 질문에 대해 한 단계 더 깊게 이해할 수 있었습니다. 앞으로도 개발하면서 막연하게 알고 있는 내용들을 그대로 받아들이기보다는, 공식 문서와 실제 동작 원리를 직접 확인하며 이해의 깊이를 넓혀가려고 합니다. 긴 글 읽어주셔서 감사합니다. ☺
'🍏 > Xcode' 카테고리의 다른 글
| [iOS] 코드베이스 프로젝트 세팅 / Codebase Project (0) | 2024.12.30 |
|---|---|
| [Xcode Cloud] 토이프로젝트에 CI / CD 적용하기 (1) | 2024.11.11 |
| [Tuist+Fastlane+GithubAction] App 배포 CI/CD 과정 (0) | 2024.04.28 |
| [Tuist] Version 4 / config / package / dependencies (0) | 2024.03.29 |
| [Tuist] Version 4 / UIKit / AppDelegate / SceneDelegate (1) | 2024.03.28 |



