Tuist란?
Tuist는 Xcode 프로젝트를 코드로 선언하고 자동 생성·관리하는 CLI 도구입니다.
대규모 iOS/macOS 프로젝트에서 수작업으로 변경되는 Xcode 설정을 코드로 표준화하여 협업 충돌과 설정 드리프트를 줄이고, 모듈화·의존성 그래프 관리·빌드 파이프라인을 체계화합니다.
+) XcodeGen과 다른점: Tuist는 swift로 프로젝트 선언이 가능하지만, XcodeGen은 yaml과 json 기반으로 프로젝트를 선언합니다.
Tuist를 사용하는 이유
(러닝커브만 보면 소규모 프로젝트에서 사용할 이유가 없어보임)
Git을 사용하면서 Xcode 프로젝트 설정(.xcodeproj) 파일의 충돌은 불가피합니다. 이를 개선해줄 수 있는 도구가 바로 Tuist입니다.
Tuist를 적용시켜 모듈화를 했을 때 효과
- 코드 안정성
- 모듈 간 결합도가 낮고 응집도가 높아져 변경에 유연하고 버그 전파 가능성이 줄어듦.
- 명확한 의존성 그래프(Tuist 자동 생성)를 통해 순환 참조나 불필요한 의존을 사전에 방지.
- 문제 발생 시 영향 범위가 좁아져 디버깅과 원인 분석에 용이함.
- 개발 생산성
- 모듈별 의존성이 줄어들어 특정 기능 개발 시 신경 써야 할 코드 범위가 축소.
- 코드 리뷰 시 관련 모듈만 집중 검토 가능 -> 리뷰 속도와 품질 향상.
- 템플릿·헬퍼 기능을 활용해 새로운 모듈 추가 시 설정을 반복 없이 빠르게 구성.
- 빌드 속도
- 변경되지 않은 모듈은 재빌드 없이 Tuist 캐시를 통해 바이너리로 재사용 -> 전체 빌드 시간 단축.
- 자주 수정되는 UI 레이어와 안정적인 Core 모듈을 분리하면 "핫 리로드" 느낌의 개발 사이클 가능.
- CI/CD 환경에서 빌드 캐시를 활용하면 병렬작업·빌드 재활용이 가능.
- 구조적 가독성과 유지보수성
- 기능·도메인·레이어별로 명확히 구분된 디렉토리/타겟 구조로 프로젝트 가독성 향상.
- 신규 개발자가 Project.swift 파일만 봐도 모듈 역할과 관계를 빠르게 파악 가능.
- 용도와 목적에 맞게 코드가 배치되어 유지보수와 확장이 수월.
Tuist 사용해보기
Tuist 설치
Homebrew가 설치되어 있는 환경입니다. Homebrew가 설치되어 있지 않으시다면 해당 문서를 참고하시길 바랍니다.
brew tap tuist/tuist
brew install --formula tuist
brew install --formula tuist@x.y.z <- x.y.z는 버전 지정
# M1 이상
arch -arm64 brew install --formula tuist
arch -arm64 brew install --formula tuist@x.y.z
Tuist 초기화
tuist init
위의 명령어를 터미널에서 실행하면 프로젝트를 새로 생성할 것인지 기존 프로젝트에 마이그레이션 할 것인지를 묻습니다. 방향키나 vim 이동키를 사용하여 옵션을 선택하여 진행할 수 있습니다.
이제 Project.swift
의 내용을 수정하여 생성할 프로젝트의 내용을 변경해줄 수 있습니다.
tuist edit
일단은 깡통 프로젝트이기 때문에 그냥 생성부터 해보겠습니다.
Tuist 생성
tuist generate
성공적으로 프로젝트가 생성되고 실행되는 모습을 볼 수 있습니다....만, 이번 프로젝트는 SwiftUI가 아닌 UIKit으로 진행할 것이기에 약간의 수정을 거쳐야합니다.
(Tuist v4 이상부터는 기본 프로젝트가 UIKit에서 SwiftUI로 설정됩니다.)
SwiftUI to UIKit
- 먼저 SwiftUI 엔트리를 제거합니다.
- (현재 프로젝트 기준 JipCoonApp.swift)
- UIKit 엔트리 파일을 추가합니다.
- AppDelegate.swift
- ViewController.swift
그리고 다시 tuist generate
를 실행하여 UIKit으로 전환되었는지 확인합니다.
깜빡하고 추가를 하지 않은 것이 있었습니다.
바로 SceneDelegate.swift입니다.
UIKit에서 SceneDelegate는 iOS13에서 도입된 UI 생명주기를 관리하는 역할을 합니다.
Tuist Project.swift에 해당 내용을 추가하고 재생성합니다.
infoPlist: .extendingDefault(
with: [
"UILaunchStoryboardName": "LaunchScreen.storyboard",
"UIApplicationSceneManifest": [
"UIApplicationSupportsMultipleScenes": false,
"UISceneConfigurations": [
"UIWindowSceneSessionRoleApplication": [
[
"UISceneConfigurationName": "Default Configuration",
"UISceneDelegateClassName": "$(PRODUCT_MODULE_NAME).SceneDelegate"
],
]
]
],
]
),