목차
- Tuist란? 설치해보자
- Static/Dynamic Framework란?
- ✅ Tuist를 정복해보자!!
1. Dependencies
프로젝트가 성장함에 따라 여러 타겟으로 분리되고 복잡한 의존성 그래프가 형성됩니다.
이러한 상황에서 명시적이고 정적인 의존성 그래프를 지향하여 쉽게 검증하고 최적화할 수 있도록 돕습니다.
- Local dependencies
- 다양한 유형의 로컬 의존성을 지원합니다.
Target
,Project
,Framework
,Library
,XCFramework
,SDK
,XCTest
등을 의존성으로 설정할 수 있습니다.- 또한 조건부 의존성 설정을 통해 특정 상황에서만 의존성이 적용되도록 할 수 있습니다.
- ONEstore에서는
Tuist/ProjectDescriptionHelpers
디렉토리에Dependencies+Extension.swift
파일을 추가하여 관리하고 있습니다.import ProjectDescription extension TargetDependency { public enum OnestoreProject { public enum AppCore { fileprivate static let GROUP_NAME = "AppCore" } public enum Network { fileprivate static let GROUP_NAME = "Network" } } private static func moduleProject(groupName:String, _ name:String) -> TargetDependency { return TargetDependency.project(target: name, path: .relativeToRoot("\(Project.rootDirectory)/\(groupName)/\(name)"), condition: .none) } } public extension TargetDependency.OnestoreProject.AppCore { /** 원스토어 앱 코어 - dependency: .external(name: "KeychainAccess", condition: .none), .OnestoreProject.Network.NetworkApi */ static let ONEstoreCore = TargetDependency.moduleProject(groupName: Self.GROUP_NAME, "ONEstoreCore") /** 원스토어 앱 네비게이션 Interface - dependency: .OnestoreProject.Network.NetworkModel, .external(name: "Factory", condition: .none) */ static let ONEstoreNavigation = TargetDependency.moduleProject(groupName: Self.GROUP_NAME, "ONEstoreNavigation") } public extension TargetDependency.OnestoreProject.Network { /** 네트워크 모듈의 코어 - dependency: .external(name: "CryptoSwift", condition: .none), .external(name: "Factory", condition: .none), */ static let NetworkCore = TargetDependency.moduleProject(groupName: Self.GROUP_NAME, "NetworkCore") /** Network Api의 Response 모델 구성 - dependency: 없음 */ static let NetworkModel = TargetDependency.moduleProject(groupName: Self.GROUP_NAME, "NetworkModel") /** Network Api - dependency: .OnestoreProject.Network.NetworkModel, */ static let NetworkApi = TargetDependency.moduleProject(groupName: Self.GROUP_NAME, "NetworkApi") }
- 아래는 로컬 의존성을 가진 NetworkApi Framework의
Project.swift
파일의 내용입니다.import ProjectDescription import ProjectDescriptionHelpers /// Project 생성 /// - Parameters: /// - name: Framework의 이름을 지정합니다. /// - sources: 소스 코드 파일의 위치를 지정합니다. /// Sources/**는 Sources 디렉토리 내의 모든 파일을 포함합니다. /// - dependencies: 타겟의 의존성을 설정합니다. /// - environmentVariables: 환경변수를 설정합니다. let project = Project( name: "NetworkApi", targets: [ .target( name: "NetworkApi", destinations: .iOS, product: .framework, bundleId: "com.onestore.NetworkApi", deploymentTargets: .iOS("17.4"), infoPlist: .file(path: "NetworkApi.plist"), sources: ["Sources/**"], dependencies: [ .OnestoreProject.Network.NetworkCore, .OnestoreProject.Network.CCSModel, ], environmentVariables: [ "IDEPreferLogStreaming" : "YES", "IDELogRedirectionPolicy" : "oslogToStdio", "OS_ACTIVITY_MODE" : "disable", ] ), ] )
- 다양한 유형의 로컬 의존성을 지원합니다.
- External dependencies
- Swift Package Manager(SPM)를 통해 의존성 관리를 합니다.
- 다음 코드를 추가하여
Tuist
디렉토리의Package.swift
파일 내에서 외부 의존성을 선언합니다.import PackageDescription /// 패키지의 product type을 변경하여 static/dynamic framework으로 설정하여 패키지 간의 빌드 설정 충돌을 해결할 수 있습니다. /// 외부 의존성 관리를 더욱 유연하게 만들어 프로젝트 통합 과정에서 발생할 수 있는 여러 문제들을 해결할 수 있습니다. let packageSettings = PackageSettings( productTypes: [ "Factory" : .framework, "FBLPromises" : .framework, "GoogleUtilities-Environment" : .framework, "third-party-IsAppEncrypted" : .framework, "GoogleUtilities-Logger" : .framework, "GoogleUtilities-UserDefaults" : .framework, ] ) let package = Package( name: "ONEstore", dependencies: [ .package(url: "https://github.com/google/GoogleSignIn-iOS.git", from: "8.0.0"), .package(url: "https://github.com/firebase/firebase-ios-sdk.git", from: "11.1.0"), .package(url: "https://github.com/hmlongco/Factory.git", from: "2.3.2"), .package(url: "https://github.com/Kitura/Swift-JWT.git", from: "4.0.0"), .package(url: "https://github.com/google/GoogleUtilities.git", from: "8.0.2"), .package(url: "https://github.com/krzyzanowskim/CryptoSwift.git", from: "1.8.3"), .package(url: "https://github.com/kishikawakatsumi/KeychainAccess.git", from: "4.2.2"), .package(url: "https://github.com/airbnb/lottie-ios.git", from: "4.5.0"), ] )
Package.swift
파일은 외부 의존성을 선언하는 인터페이스입니다.
- 설정을 마쳤으면 다음 명령어로 의존성을 해결하고 프로젝트에 가져옵니다.
$ tuist install
- 이 방식을 통해 프로젝트의 의존성 관리에 대한 더 많은 제어력과 유연성을 얻을 수 있습니다.
2. Workspace
Tuist에서 Workspace는 여러 프로젝트를 관리하고 구성하는 데 사용됩니다.
Workspace 파일을 통해 워크스페이스를 정의할 수 있습니다.
- ONEstoreGlobal Project의
Workspace.swift
파일 내용입니다.import ProjectDescription import ProjectDescriptionHelpers /// Workspace 생성 /// - Parameters: /// - name: 워크스페이스의 이름을 지정합니다. /// 이 이름으로 .xcworkspace 파일이 생성됩니다. /// - projects: 워크스페이스에 포함될 프로젝트들의 경로를 배열 혹은 최상위 루트로 지정합니다. /// 배열의 경로나 하위 디렉토리에 있는 Project.swift파일을 인식합니다. /// - fileHeaderTemplate: 새로 생성되는 파일의 헤더에 들어갈 내용을 지정합니다. /// 이 옵션을 사용하여 저작권 정보나 라이선스 문구를 자동으로 추가할 수 있습니다 let workspace = Workspace( name: "ONEstore Workspace", projects: [ "Projects/**" ], fileHeaderTemplate: Workspace.fileHeaderTemplate )
- 위 설정을 토대로 워크스페이스의 기본 구조는 다음과 같습니다.
ONEstore/ // 루트 디렉토리 ├── Workspace.swift ├── Projects/ │ ├── A/ │ │ └── Project.swift │ ├── B/ │ │ └── Project.swift │ └── ... └── Tuist/ ├── ProjectDescriptionHelpers/ │ ├── Dependencies+Extension.swift │ └── Workspace+Extension.swift └── Package.swift
- fileHeaderTemplate 내용이 추가된
Workspace+Extension.swift
파일 내용입니다.import ProjectDescription public extension Workspace { /// 새로운 파일을 생성할 때 자동으로 추가되는 주석이나 코드를 커스터마이즈하는데 사용됩니다. /// https://help.apple.com/xcode/mac/11.4/#/dev7fe737ce0 참고 static let fileHeaderTemplate:FileHeaderTemplate = .string( """ Created ___DATE___ /*------------------------------------------------------------------------------ * PROJECT : ___PROJECTNAME___ * NAME : ___FILEBASENAME___ * DESC : * AUTHOR : ___FULLUSERNAME___ * Copyright ___YEAR___ ONEstore All rights reserved *------------------------------------------------------------------------------*/ """ ) }
Workspace.swift
파일은 보통 프로젝트의 루트 디렉토리에 위치합니다.- 이 파일을 통해 여러 프로젝트를 하나의 워크스페이스로 묶어 관리할 수 있습니다.
- Tuist는 이 설정을 바탕으로 .xcworkspace 파일을 생성합니다.
- 이렇게 구성된 워크스페이스를 통해 여러 모듈이나 프로젝트를 효율적으로 관리하고, 의존성을 쉽게 설정할 수 있습니다.
3. Project
- 프로젝트의 기본 구조는 다음과 같습니다.
ONEstore/Projects/ONEstoreApp/ // 프로젝트 디렉토리 ├── Project.swift ├── Sources/ │ ├── .swift │ └── AppDelegate.swift └── Resources/ ├── .assets └── .png
Project.swift
파일은 프로젝트의 설정을 정의합니다.
ONEstoreGlobal App의Project.swift
파일 내용입니다.import ProjectDescription let baseDependencies:[TargetDependency] = [ // Pakcage.swift 에 추가된 외부 의존성 .external(name: "FirebaseAnalytics", condition: .none), .external(name: "FirebaseMessaging", condition: .none), .external(name: "FirebaseCrashlytics", condition: .none), .external(name: "SwiftJWT", condition: .none), .external(name: "GoogleSignIn", condition: .none) // Dependencies+Extension 에서 관리되고 있는 로컬 의존성 .OnestoreProject.AppCore.ONEstoreCore, .OnestoreProject.AppCore.NetworkApi ] /// Project 생성 /// - Parameters: /// - name: 프로젝트의 이름을 지정합니다. /// - options: 프로젝트 옵션을 설정합니다. /// 여기서는 자동 스키마 생성을 비활성화했습니다. /// - targets: 프로젝트의 타겟들을 정의합니다. /// 각 타겟은 이름, 플랫폼, 제품 유형, 번들 ID, Info.plist 설정, 소스 파일 경로, 리소스 파일 경로, 의존성 등을 지정합니다. /// - sources: 소스 코드 파일의 위치를 지정합니다. /// Sources/**는 Sources 디렉토리 내의 모든 파일을 포함합니다. /// - resources: 리소스 파일의 위치를 지정합니다. /// Resources/**는 Resources 디렉토리 내의 모든 파일을 포함합니다. /// - dependencies: 타겟의 의존성을 설정합니다. let project = Project( name: "ONEstore", options: .options( automaticSchemesOptions: .disabled ), targets: [ Target( name: "ONEstore_QA", platform: .iOS, product: .app, bundleId: "com.onestore.ios.qa", infoPlist: .default, sources: ["Sources/"], resources: ["Resources/"], dependencies: baseDependencies ), Target( name: "ONEstore_Release", platform: .iOS, product: .app, bundleId: "com.onestore.ios", infoPlist: .default, sources: ["Sources/"], resources: ["Resources/"], dependencies: baseDependencies ) ] )
- 소스 파일과 리소스 파일은 지정된 디렉토리(Sources, Resources)에 올바르게 위치해야 합니다.
- 필요에 따라 추가적인 타겟(예: 프레임워크, 익스텐션 등)을 정의할 수 있습니다.
- infoPlist 설정을 통해 커스텀 Info.plist 파일을 사용하거나 동적으로 생성할 수 있습니다.
- Tuist는 이 설정을 바탕으로 Project는 .xcodeproj을 생성합니다.
프로젝트 설정이 완료되면, 루트 디렉토리에서 다음 명령어를 실행하여 Xcode 프로젝트를 생성합니다.
$ tuist generate
Workspace.swift
파일과Project.swift
파일들을 기반으로 Xcode 워크스페이스를 생성하여 실행합니다.
마치며
- iOS 개발 환경에서 프로젝트 관리와 의존성 처리는 종종 복잡하고 까다로운 과제가 됩니다.
이러한 상황에서Tuist는 개발자들에게 강력하고 유연한 해결책을 제시
합니다. - Tuist의 XcodeProj 기반 통합 방식은 Swift 패키지 관리에 새로운 차원의 제어력을 제공하며, 특히
중대형 프로젝트에서 그 진가를 발휘
합니다. - 로컬 의존성, 외부 의존성 세부 설정을 정밀하게 조정할 수 있어, 프로젝트의 특정 요구사항에 맞춤 대응이 가능해집니다.
이러한 기능들은 단순히 의존성 문제를 해결하는 것을 넘어,프로젝트의 전반적인 구조와 워크플로우를 개선
하는 데 기여합니다.
결과적으로 Tuist는 iOS 개발자들에게 더 효율적이고 안정적인 프로젝트 관리 경험을 제공하며, 복잡한 개발 과정을 보다 순조롭게 만들어줍니다.