iOS 앱의 규모가 커지면 단일 타겟 프로젝트는 한계에 부딪힌다.
수천 개의 파일이 하나의 프로젝트 파일(.xcodeproj)에 얽히게 되면, 작은 수정에도 프로젝트 전체를 다시 인덱싱해야 하며 Git 협업 시 프로젝트 파일의 'Merge Conflict'는 개발자의 정신을 피폐하게 만든다.
이를 해결하기 위한 근본적인 해결책이 바로 모듈화(Modularization)다.
프로젝트를 기능 단위로 쪼개어 독립적인 라이브러리로 관리하는 이 방식은 현대 iOS 개발의 표준이 되고 있다.
Tuist 는 Swift로 Xcode 프로젝트를 정의하고 생성하는 명령행 도구(CLI)다.
기존 방식은 Xcode UI를 통해 설정을 변경하고 그 결과가 바이너리 형태의 .xcodeproj에 저장되었으나, Tuist는 Project.swift라는 Swift 파일을 통해 프로젝트 구조를 기술한다.
.xcodeproj를 깃에 올리지 않아도 된다. tuist generate 명령 한 번이면 언제든 동일한 환경을 재구성할 수 있다.tuist graph를 통해 복잡한 모듈 간의 의존성 지도를 한눈에 확인할 수 있다.효율적인 모듈화를 위해서는 계층 구조(Layering)가 명확해야 한다.
대표적인 Micro-features 아키텍처를 응용한 레이어 설계는 아래와 같다.
Tuist에서 모듈은 Target으로 정의된다.
각 타겟 간의 의존성은 .target(name:) 또는 .project(target:path:)를 통해 연결한다.
// Project.swift 예시
let project = Project(
name: \"HomeFeature\",
targets: [
Target(
name: \"HomeFeature\",
platform: .iOS,
product: .staticFramework, // 또는 .framework
bundleId: \"com.kaguya.home\",
sources: [\"Sources/**\"],
dependencies: [
.project(target: \"Core\", path: \"../Core\"),
.project(target: \"DesignSystem\", path: \"../Shared/DesignSystem\")
]
)
]
)
이렇게 정의된 모듈은 tuist generate 시 자동으로 의존성이 연결된 워크스페이스로 생성된다.
변경이 발생한 모듈과 그에 의존하는 모듈만 다시 빌드하면 된다.
대규모 프로젝트에서 전체 빌드 시간이 10분에서 2분으로 단축되는 마법을 경험할 수 있다.
특정 기능(Feature)만 포함하는 독립적인 타겟을 만들어 실행할 수 있다.
전체 앱을 실행하지 않고 로그인 기능만 빠르게 테스트할 수 있어 개발 생산성이 극대화된다.
모듈 간의 인터페이스를 명확히 정의해야 하므로 스파게티 코드가 방지된다.
특정 기능을 수정해도 다른 기능에 미치는 영향을 최소화할 수 있다.
Tuist를 통한 모듈화는 단순한 유행이 아니라 지속 가능한 개발 환경을 위한 필수 선택이다.
물론 초기 설정 비용이 발생하고 Swift 언어에 대한 이해가 깊어야 하지만, 그 보상은 압도적이다.
프로젝트 초기부터 모듈화를 염두에 둔다면, 나중에 감당할 수 없을 정도로 커진 프로젝트를 수술하는 고통을 피할 수 있을 것이다.