-
Tuist Clean Architecture 적용기 - 1iOS 2025. 2. 24. 15:34
어느날 회사 프로젝트의 리빌드의 베이스 작업을 맡게 되어서 Tuist에 Clean Architecture을 적용해보기로 생각했다.
일단 Tuist가 뭔지 정확히 몰라 이것저것 둘러 보면서 공부하고 내용을 끄적여 본다.
Tuist란?
Xcode 프로젝트를 관리 할 수 있는 툴
장점
- 모듈화를 통해 의존성을 낮추고 재활용성을 높임
- 유지보수가 쉬워짐
- 빌드속도가 향상됨
단점
- 프로젝트 초기 설정에 시간이 오래걸림
- CocoaPod을 지원하지 않음
저는 Tuist 4.21.0을 사용했고 Tuist는 버전에 따라 바뀌는 내용이 많은것 같아서..버전 타겟팅이 중요해 보인다.
Tuist 설치과정은 생략..
먼저 설치하고 Tuist 프로젝트를 만들고 싶은 폴더를 만들어 터미널로 해당 폴더 위치로 이동한다.
tuist init --platform ios
tuist는 같이 협업하는 모든 분들이 같은버전을 사용해야 오류가 안나고 정상적으로 돌아가기 때문에 버전을 고정시킬수 있는데 해당 과정은
아래와 같습니다. 먼저 tuist root 경로로 이동하셔서 터미널에 두줄 작성하면 됩니다.
echo "4.21.0" > .tuist-version cat .tuist-version
4.21.0 문자열을 .tuist-version파일로 작성해서 넣으면 4.21.0 사용자들만 사용할 수 있고 아닐경우 4.21.0 버전을 설치하게 됩니다.
cat명령어로 해당 파일 내용을 확인해 볼 수 있습니다.
"tuist init --platform ios" 로 tuist 프로젝트를 생성하고 만든 프로젝트는 기본적으로 SwiftUI로 생성된다.
tuist generate
위 명령어로 실행해보면 <Tuist프로젝트이름>/Sources에 SwiftUI로 구성(ContentView, ~App)되어 있다. UIkit으로 사용하고 싶으면 해당 두개의 SwiftUI에 관한 .swift 파일을 지우고 AppDelegate, SceneDelegate를 넣으면 된다. 저는 기존에 사용하던 프로젝트가 UIkit이고 AppDelegate만 사용하고 있기 때문에 해당 SwiftUI에 대한 파일을 지우고 AppDelegate를 작성하였다.
@main class AppDelegate: UIResponder, UIApplicationDelegate { var window: UIWindow? func application( _ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil ) -> Bool { window = UIWindow(frame: UIScreen.main.bounds) let viewController = ViewController() window?.rootViewController = viewController window?.makeKeyAndVisible() return true } }
다시 터미널로 돌아가서
tuist edit
위 명령어를 작성하면 tuist 설정에 관한 xcode 플젝이 실행되고 거기 디렉토리를 펼쳐보면 Tuist 폴더 아래에
Project.swift / Config.swift / Package.swift 등의 파일이 있을것이다.
일단 있던것들 우선으로 설명하자면
Project.swift : .xcodeproj 프로젝트를 어떻게 만들지 정의하는 파일
Config.swift : Plugin과 같이 tuist 프로젝트 전역에서 사용할 것들을 지정해주는 파일 ex) TargetDependency, settings 등등
Package.swift: local 패키지나 spm과 같은 프로젝트에서 사용할 package를 지정해주는 파일
위와 같이 기본구성이 있지만 모듈화를 위해선 몇개의 파일을 더 만들어줘야 합니다.
dir 구조 위처럼 Workspace파일을 만들어 주고 해당 파일은 여러개의 프로젝트를 하나의 워크스페이스로 묶을때 사용합니다.
Projects폴더 안에는 모듈화된 여러개의 프로젝트를 만들어 줍니다. 기본 구조는 아래와 같습니다.
모듈화 하나하나 구조 모듈화하고자하는 폴더이름으로 상단에 두고 아래에 project.swift 파일을 생성하면 됩니다.
Projects 폴더아래에 여러개의 모듈화된 플젝을 만들고 싶다하면 App1 / App2 / App3 처럼 여러개의 폴더를 만들어주고 각각 Project.swift 파일을 만들어 주면 됩니다.
이렇게 만들었다면 일단 Workspace.swift에서 사용하고자 하는 모듈들을 묶어보겠습니다.
import ProjectDescription let workspace = Workspace( name: "APP", projects: [ "Projects/App1", "Projects/App2", "Projects/App3" ] )
위처럼 프로젝트를 묶을 수 있습니다.
그리고 이제 전역에서 사용하기 Dependency Target이나 Settings에 관한것들을 작성해 보겠습니다.
(다른 블로그를 보면 Plugin을 작성하여 거기에 작성하는데 그 과정은 추후에 보여드리겠습니다)
우선 보면 Tuist 폴더 아래에 ProjectDescriptionHelpers폴더를 만들고 아래에 Dependency+Project.swift, InfoPlists.swift, Settings.swift 파일을 만들어 줍니다. 그리고 Dependency+Project를 아래와 같이 작성합니다.
import ProjectDescription extension TargetDependency { public enum Project {} } public extension TargetDependency.Project { static let App1 = TargetDependency.project(target: "App1", path: .relativeRoot("Projects/App1") static let App2 = TargetDependency.project(target: "App2", path: .relativeRoot("Projects/App2") static let App3 = TargetDependency.project(target: "App2", path: .relativeRoot("Projects/App3") }
위 코드는 추후에 가독성과 편리함을 더해줄겁니다.
그리고 InfoPlists 를 작성해줍니다.
import ProjectDescription extension ProjectDescription.InfoPlist { public static let appInfoPlist: [String : Plist.Value] = [ "UILaunchScreen" : [:], "UILaunchStoryboardName" : "LaunchScreen", "CFBundleDisplayName" : "bundleName", "CFBundleShortVersionString" : "1.0", "CFBundleVersion" : "1" .... ] }
위와 같이 plist에 추가하고 싶은 내용 작성해 주시면 되고 필요하시면 더 써도 됩니다.
다음은 Settings 작성해보겠습니다.
import ProjectDescription extension ProjectDescription.Settings { public static var projectAllSettings: Settings { .settings( configurations: [ .release(name: "AppStore"), .debug(name: "Debug") ], defaultSettings: .recommended ) } } extension String { public static var bundleID: String = "com.example" public static var organizationName: String = "projectOrganizationName" }
위 내용은 build setting에 관한 내용인데 .release로 작성하면 모든 조건의 바이너리를 생성하고 .debug로 작성하면 해당 조건에만 맞는 바이너리를 생성합니다. ex) debug시 iPhone15에만 맞는 바이너리 생성 .release시에는 모든 바이너리 생성
위와 같이 모든 것들을 작성하면 이제 App1~3까지의 Project 파일을 작성하면 됩니다. 일단 App1이 메인이 될꺼고
App2,3은 framework로 가는 구조로 작성될 것 입니다.
import ProjectDescription import ProjectDescriptionHelpers let project = Project( name: "App1", // 프로젝트 파일의 이름을 결정함 organizationName: .organizationName, // 프로젝트 파일의 Organizaion 이름을 결정함 settings: .projectAllSettings, targets: [ .target( name: "App1", destinations: .iOS, product: .app, bundleId: .bundleID, //모든 bundleId를 다르게 설정 해주지 않으면 앱 디버깅시 오류 발생함. infoPlist: .extendingDefault( with: InfoPlist.appInfoPlist //코드로 info.plist 설정한 경우 ), sources: ["Sources/**"], // sources안에 아무것도 넣지 않으면 오류발생 resources: ["Resources/**"], // resources안에 아무것도 넣지 않으면 오류발생 dependencies: [ .Projects.App2 ], settings: .settings(configurations: [ .debug(name: "Debug") ]) ) ] )
App2, 3는 아래와 같이 작성
import ProjectDescription import ProjectDescriptionHelpers let project = Project( name: "App2", // 프로젝트 파일의 이름을 결정함 organizationName: .organizationName, // 프로젝트 파일의 Organizaion 이름을 결정함 settings: .none, targets: [ .target( name: "App2", destinations: .iOS, product: .framework, bundleId: .bundleID + "App2", deploymentTargets: .iOS("15.0"), infoPlist: .extendingDefault( with: InfoPlist.appInfoPlist ), sources: ["Sources/**"], dependencies: [ ] ) ] )
App3도 위와 같이 똑같이 작성해주면 됩니다. 물론 name만 변경하면 됩니다.
denpendencies는 추후에 CleanArchitecture를 할때 넣어 주겠습니다.
infoPlist는 plist파일 자체로도 설정 가능합니다 infoPlist: .file(path: "") 이런식으로 디렉토리 경로를 지정해주면 됩니다.
다음은 이제 플러그인을 지정하는 방법을 알려드리겠습니다.
Config.swift파일을 작성해 줍니다.
import ProjectDescription let config = Config( plugins: [ .local(path: .relativeToRoot("Plugins/MyPlugin")) ] )
위와 같이 작성해주시고 지정한 디렉토리에 맞게 파일을 생성합니다.
<TuistRoot경로>/Plugins/MyPlugin/Plugin.swift 를 만들어 줍니다.
Plugin.swift는 다음과 같이 작성합니다.
import ProjectDescription let myPlugin = Plugin(name: "MyPlugin")
그리고 아까 작성했던 ProjectDescriptionHelpers폴더의 모든것들을 MyPlugin 폴더에 넣으면 import MyPlugin 하시면 모든곳에서 사용 가능합니다. 이제 tuist generate를 사용하셔서 xcode를 실행하고 빌드하면 됩니다.
위에 모든 내용들은 tuist에 아주 기초적인 부분이고 다음 내용부터는 clean architecture를 tuist 프로젝트로 설정하는법, 구조에대한 이야기를 해보겠습니다 피드백은 환영합니다.
'iOS' 카테고리의 다른 글
[iOS] Coordinator with Clean Architecture DiContainer (0) 2025.03.06 Tuist Clean Architecture 적용기 - 2 (0) 2025.02.25