티스토리 뷰
프로젝트를 진행하면서 깃헙에서 충돌이 자주 발생했었다. 만약 팀원 A가 파일 추가하고, 팀원 B가 설정을 변경했을 때 둘 다 .xcodeproj 파일에 영향을 주어 충돌이 발생했다.
또한 팀원이 프로젝트 설정을 변경했을 때 .pbxproj 파일은 XML 형식으로 나타나서 PR 리뷰를 하기 어려운 문제가 있었다.
추가적으로, 프로젝트에서 CocoaPods로 의존성 관리를 했지만, 팀원이 CocoaPods 사용 중인 것을 깜빡하고 SPM을 사용하면 두 의존성 관리 도구가 충돌해서 오류가 발생하는 상황도 있었다.
정리하자면 다음과 같은 문제가 있었다.
- xcodeproj 충돌 발생
- .pbxproj 파일의 코드 확인 어려움
- spm과 cocoapods 충돌 발생
이 문제들을 해결하기 위한 방법으로 Tuist라는 툴을 알게 되었는데 Tuist가 무엇이고 어떻게 적용하는지 알아보자.
📝 목차
1. Tuist란 무엇인가
2. Tuist 설치
3. Tuist 프로젝트에 적용
4. 프레임워크 추가 후 import 해보기
5. 라이브러리 설치 후 적용
1. Tuist란 무엇인가
What is Tuist? | Tuist
docs.tuist.dev
Tuist는 Xcode 프로젝트를 관리할 수 있는 툴이다.
기존에 Xcode에서 target, SPM, info, buid-setting 을 관리하던 것을 tuist라는 툴을 활용해서 할 수 있다.
🟢 장점
1️⃣ 코드로 설정하는 프로젝트 구조
Tuist는 Project.swift 파일을 통해 프로젝트의 구조와 모듈을 코드로 정의할 수 있다.
2️⃣ 모듈화 편리 및 모듈 간 의존성 관리
Tuist는 모듈 간 의존성을 명확하게 관리할 수 있어요. 각 모듈은 다른 모듈에 의존할 수 있으며, 이를 Project.swift에서 코드로 관리한다.
또한, 의존성 관리 도구인 CocoaPods, SPM, Carthage를 사용하되, Tuist의 설정에 맞춰 쉽게 통합할 수 있다.
그렇다고 "Tuist = 모듈화"인건 아니다. Tuist는 단지 모듈화를 할 수 있게 도와주는 도구일 뿐이다.
3️⃣ 빌드 속도 개선
필요하지 않은 모듈을 재빌드하지 않아서 전체 빌드 시간을 단축할 수 있다.
4️⃣ 충돌 회피 가능
기존 방식 | Tuist 방식 | |
파일 추가/삭제 | .xcodeproj 변경 → 충돌 | Project.swift만 변경 |
의존성 관리 | SPM/CocoaPods → 설정 꼬임 | Tuist/Dependencies.swift로 통합 |
프로젝트 변경 PR 리뷰 | .xcodeproj 비교 어려움 | 설정 코드만 리뷰 가능 |
Tuist는 프로젝트 설정을 코드로 선언하고, .xcodeproj 파일은 필요할 때 tuist generate로 새로 생성하게 된다.
또한 Git에는 .xcodeproj 파일을 올리지 않아서 충돌이 발생할 확률도 줄어든다.
🔴 단점
1️⃣ 러닝 커브
Xcode의 GUI에 익숙한 팀원이 tuist 코드 기반 프로젝트 설정 방식에 적응하기까지 시간이 걸릴 수 있다.
특히, Project.swift, Workspace.swift, Dependencies.swift 같은 파일 구조와 문법을 이해하고 관리해야 하므로, 초반 학습 부담이 있을 수 있다.
2️⃣ Tuist 의존성
Tuist를 사용하면 프로젝트 설정이 Tuist에 종속되기 때문에, 만약 Tuist가 더 이상 유지보수되지 않거나 호환성 문제가 생기면 프로젝트 마이그레이션이 까다로울 수 있다.
=> "Xcode만 있으면 돌아가는 프로젝트"에서 Tuist 설치가 필수가 되기 때문에, 새 팀원이 프로젝트 받을 때 "Tuist 설치부터 해야 한다"는 장벽이 생길 수 있다.
3️⃣ Xcode와 동기화 문제
- Tuist가 .xcodeproj 파일을 생성하긴 하지만, Xcode에서 설정을 직접 바꾸면 Tuist 코드와 동기화가 깨질 수 있다.
- Tuist에서는 프로젝트 설정을 코드로 관리하므로, Xcode에서 설정 변경을 해도 Tuist 파일엔 반영이 안 됨 → 결국 Project.swift 코드만 믿고 가야 한다.
=> Xcode 설정을 바꾸지 않고, Tuist 설정 파일만 관리해야 한다. (하지만 Xcode GUI에 익숙한 팀원들에게는 불편할 수 있음...)
=> 장기적으로 프로젝트를 유지보수해야 하거나, 코드 기반으로 프로젝트를 관리하는 게 중요하다면 Tuist 도입이 효율적일 수 있다. 다만, 단점들도 분명히 존재하므로 팀원들과 충분히 상의한 후 적용하는 것이 좋을 것 같다.
2. Tuist 설치
1️⃣ mise 설치 및 버전확인
# mise 설치
curl https://mise.run | sh
# mise 버전확인
$ ~/.local/bin/mise --version
2️⃣ shell에 연결
echo 'eval "$(~/.local/bin/mise activate bash)"' >> ~/.bashrc
echo 'eval "$(~/.local/bin/mise activate zsh)"' >> ~/.zshrc
echo '~/.local/bin/mise activate fish | source' >> ~/.config/fish/config.fish
echo $SHELL을 입력해서 출력 확인해 보면 bash, zsh 중 뭔지 나온다.
나는 zsh이므로 두번째를 사용했다.
3️⃣ node.js 설치 및 node.js 전역 설정
# 설치 및 설정
mise use --global node@20
# 버전 확인
$ node -v
4️⃣ mise로 Tuist 설치
mise install tuist
🧐 주의
만약 install로 Tuist를 설치하셨다면 프로젝트 폴더에서 Tuist 생성이 안될 수 있다고 한다.
그래서 use -g 를 사용해서 전역설정을 해줘야 된다.
mise use -g tuist
3. Tuist 프로젝트에 적용
1️⃣ 처음 Tuist 프로젝트 생성 - tuist init
tuist init --path ios --template swiftui
이렇게 하니까 계속 init에 --template라는 옵션이 없다고 나와서 찾아보니 tuist init으로도 설정할 수 있는 걸 알았다.
tuist init으로 프로젝트 생성해 보자.
나는 프로젝트 새로 만드는 거여서 첫 번째로 했다.
이렇게 설정해 주다가 Authentication이 나오는데 여기서는 https://~ 여기 링크에 접속해서 로그인해 주면 된다.
Tuist 프로젝트 만들기 성공~~!!!
파일 확인해 보면 이렇게 잘 만들어졌다
2️⃣ 프로젝트 수정 - tuist edit
tuist edit
이 명령어는 프로젝트 정의를 수정할 때 사용하는 명령어다.
Tuist/Project.swift 파일을 열어서 프로젝트를 정의하게 되는데, 이걸 바로 편집할 수 있는 임시 Xcode 프로젝트를 만들어준다.
터미널에 tuist edit을 입력하면 바로 xcode가 실행되면서 수정 화면이 나온다.
3️⃣ 설정된 버전 설치 - tuist install
원래는 tuist fetch를 사용했었는데 tuist install로 바뀐 것 같다.
만약 설정 파일을 수정했는데, Tuist 버전이 다르면 generate 명령어가 아예 안 돌아갈 수도 있다.
그래서 edit으로 파일 수정한 후 generate 하기 전에 버전 맞추는 용도로 install을 해줘야 한다고 한다.
4️⃣ 최종 프로젝트 생성 - tuist generate
generate는 edit에서 수정된 파일에 따라서 프로젝트를 생성한다.
Tuist/Project.swift 파일에 정의된 내용을 바탕으로 실제 프로젝트 파일을 생성하게 된다.
generate한 후 파일을 확인하면 .xcodeproj와 .xcworkspace가 생긴 것을 확인할 수 있다.
4. 프레임워크 추가 후 import 해보기
TuistStudy는 제공하고 싶은 app으로 하고, NetworkKit은 framework로 설정해서 TuistStudy에서 NetworkKit를 import해서 사용할 수 있도록 해보자.
tuist edit으로 다음과 같이 수정한다.
import ProjectDescription
let appProject = Project(
name: "TuistStudy",
targets: [
.target(
name: "TuistStudy",
destinations: .iOS,
product: .app,
bundleId: "io.tuist.TuistStudy",
infoPlist: .extendingDefault(
with: [
"UILaunchScreen": [
"UIColorName": "",
"UIImageName": "",
],
]
),
sources: ["Sources/**"],
resources: ["Resources/**"],
dependencies: []
),
.target(
name: "TuistStudyTests",
destinations: .iOS,
product: .unitTests,
bundleId: "io.tuist.TuistStudyTests",
infoPlist: .default,
sources: ["Tests/**"],
resources: [],
dependencies: [.target(name: "TuistStudy")]
),
]
)
let networkProject = Project(
name: "NetworkKit",
targets: [
.target(
name: "NetworkKit",
destinations: .iOS,
product: .framework,
bundleId: "io.tuist.NetworkKit",
infoPlist: .extendingDefault(
with: [
"UILaunchScreen": [
"UIColorName": "",
"UIImageName": "",
],
]
),
sources: ["Sources/**"],
dependencies: []
)
]
)
차이점을 조금 보자면 각각의 target의 Product가 .app과 .framework로 설정이 되어있고 NetwrokKit에는 Resoures 폴더가 필요 없어서 추가하지 않았다.
이제 각각의 Project에 필요한 Sources와 Resoures 디렉터리를 추가해 준다.
이렇게 다 추가해 준 후 tuist install -> tuist generate를 꼭 해줘야 수정한 파일이 적용되어서 프로젝트가 만들어진다.
이제 생성된 프로젝트 파일을 확인하면 TuistStudy와 NetworkKit이 생긴 걸 확인할 수 있다.
이제 프로젝트를 실행할 수 있도록 Project 폴더 내부에 Add Files to ""로 Sources 폴더와 Resources 폴더를 추가해 준다.
그럼 이렇게 추가된 걸 확인할 수 있다.
📌 NetworkKit import 하기
이제 TuistStudy에서 NetworkKit을 import로 불러와 사용할 수 있게 해 보자.
다시 tuist edit 한 후, TuistStudy의 Project 파일에 dependencies를 추가해 준다.
이제 ContenView에서 import를 하면 NetworkKit을 찾지 못한다.
그 이유는 NetworkKit의 Sources 폴더에 파일이 없어서 import가 안된 것이기 때문에 파일을 하나 추가해 주자.
그냥 간단한 문자열을 출력해 보는 코드를 넣었다.
- NetworkKit -> Sources에 파일 추가
import SwiftUI
public extension View {
func printLog() {
print("NetworkKit")
}
}
- NetworkKit import 성공 후, pringLog() 함수 호출해 보기
import SwiftUI
import NetworkKit
public struct ContentView: View {
public init() {}
public var body: some View {
Text("Hello, World!")
.padding()
.onAppear {
printLog()
}
}
}
그럼 이렇게 실행해 보면 "NetworkKit"가 잘 출력되는 걸 확인할 수 있다.
tuist graph 하면 이렇게 의존성을 사진으로 딱 보여주는데, NetworkKit을 의존하고 있는 것으로 잘 나오고 있다.
5. 라이브러리 설치 후 적용
📌 라이브러리 설치하기
마지막으로 라이브러리 설치 후 프로젝트에 적용하는 걸 해보자.
먼저 Tuist edit을 한 후 Tuist 폴더의 Package 파일에 dependencies를 추가해 주자.
나는 Alamofire을 추가하기 위해 .package()로 넣어줬다.
그리고 NetworkKit에 dependencies로 Alamofire을 넣어줘야 한다.
다시 tuist install -> tuist generate를 해준 후, NetworkKit에서 import Alamofire을 하면 오류가 발생하지 않고 성공적으로 잘 가져오는 걸 확인할 수 있다.
다시 tuist graph를 하면 다음과 같이 의존관계를 한눈에 확인할 수 있다.
💡느낀점💡
Tuist를 통해 Target 간 의존성을 명확하게 정의하고, 모듈화 된 구조를 유지할 수 있어서 프로젝트의 전체 흐름을 직관적으로 파악할 수 있다는 게 큰 장점으로 다가왔다. 특히 .pbxproj 같은 복잡한 설정 파일을 직접 다루지 않아도 돼서, 협업 시 발생할 수 있는 충돌 관리 부담이 줄어드는 부분이 강력한 이점이라고 생각된다. 하지만 러닝커브가 높기 때문에 어떤 프로젝트에 적용해야 할지는 좀 고민해봐야 할 것 같다.
그래도 익숙해지면 유지보수성과 확장성이 훨씬 개선된 프로젝트 관리가 가능할 것 같다!!
출처
'스위프트 > SwiftUI' 카테고리의 다른 글
[SwiftUI] - async & await 이해하기 (0) | 2025.04.16 |
---|---|
[SwiftUI] - NavigationLink에 LazyView 적용 (0) | 2025.04.05 |
[SwiftUI] - 앱 추적 권한 요청 (0) | 2025.02.06 |
[SwiftUI] - WebView 구현하기 (0) | 2024.12.02 |
[SwiftUI] - 텍스트에 따라 너비, 높이 동적 조절 (1) | 2024.10.12 |