Tuist를 쓰면서 Asset 코드생성은 직접 한 이유

by DDD

들어가며

안녕하세요, 사이드 프로젝트 DDD 동아리의 iOS 운영진 은표입니다.


iOS 프로젝트를 하다 보면, Tuist를 통해서든 다른 방식으로든 모듈화를 한 번쯤 고민하게 됩니다.


저는 Tuist를 사용하여 프로젝트 모듈화를 진행해봤는데요, 처음에는 정말 만족스러웠습니다.

git rebase 할 때 .pbxproj 파일 충돌도 나지 않고, 프로젝트 전체 구조를 내가 작성한 코드에 맞춰 알아서 잘 생성해주니까요.


그런데 프로젝트를 진행하다 보니 문득 이런 생각이 들었습니다.


img.jpg?credential=yqXZFxpELC7KVnFOS48ylbz2pIh7yKj8&expires=1774969199&allow_ip=&allow_referer=&signature=vsOzGSxAExCT8xOUXl0k%2FK09Dfs%3D
Asset 접근할 때 내가 쓰고 있는 방식이 진짜 좋은 방법이 맞나?

처음에는 별생각이 없었습니다.

Tuist의 generate 과정에서 자동으로 만들어지는 코드를 통해 Asset에 접근하면 오타도 줄고, 자동완성도 되고 꽤 편했으니까요.


하지만 프로젝트를 진행할수록 조금씩 거슬리기 시작했습니다.

Asset을 하나 추가하고 나서 바로 화면에서 확인하고 싶은데 Asset 접근 코드를 생성하는 과정이 tuist generate 에 묶여있어 생각보다 자주 작업 흐름이 끊기게 됩니다.

특히 최근에는 buildable folders를 적극적으로 활용하면 관리가 훨씬 편해지는 부분이 있는데, 정작 Tuist를 사용하면 이 장점을 살리지 못하게 됩니다.


그래서 이 문제를 저는 어떤 고민을 했고 최종적으로, 어떻게 결정했는지에 대한 과정을 공유드리려고 합니다.



해결책 찾기


가장 먼저 떠오른건 Xcode나 Asset Catalog가 기본으로 제공하는 자동 생성 기능을 적극적으로 활용하는 방법이었습니다.


이 방식은 단일 앱 타겟에서는 꽤 만족스럽게 쓸 수 있습니다.

기본적인 사용성도 괜찮고, 별도 도구 없이 시작할 수 있다는 장점도 분명합니다.


다만 프로젝트 구조가 조금만 복잡해지면 또 다시 아쉬움이 생깁니다.


예를 들어 Asset을 담아놓은 모듈이 따로 있고, 그걸 여러 모듈이 공통으로 참조하는 구조라면 이야기가 달라집니다.

특히 dynamic framework 같은 형태에서는 접근 제어나 번들 처리, 사용 방식에서 미묘하게 불편한 부분이 생기기 시작합니다.

자동으로 만들어주긴 하는데, 결국 제가 원하는 방식으로는 쓰지 못하게 되더라구요.

img.jpg?credential=yqXZFxpELC7KVnFOS48ylbz2pIh7yKj8&expires=1774969199&allow_ip=&allow_referer=&signature=3lVtxrz7O2X%2Fx1xgyKREiuDdSQ0%3D



다시 옛날으로..?

그러다보니 옛날에 쓰던 라이브러리들이 다시 떠오릅니다.

R.swiftSwiftGen


사실 이쯤 되면 이 둘은 꽤 현실적인 선택지입니다.

이미 많은 팀에서 사용해왔고

문자열 기반 접근보다 훨씬 안전하고

빌드 전에 코드를 생성한다는 흐름도 직관적입니다

그래서 그냥 다시 쓸까? 했는데, 막상 다시 보니 완전히 마음에 들지는 않았습니다.

이유는 기능 부족이 아니라, 생성되는 코드의 형태였습니다.


어떤 팀은 메서드 기반 접근이 더 명확하다고 느낄 수 있습니다.
그런데 어떤 팀은 static property 중심의 API를 훨씬 더 선호합니다.


이 차이는 사소해 보여도, 코드베이스 전체에서는 꽤 크게 남습니다.


예를 들어 저는 아래 같은 형태가 더 읽기 좋다고 느꼈습니다.

let image: UIImage = Asset.Image.Home.banner


이런 스타일은 사용성 자체도 깔끔하고, 생성 규칙도 단순한 편입니다.
반대로 어떤 생성 도구는 이와 조금 다른 형태를 기본으로 내보냅니다.


물론 그게 틀렸다는 건 아니지만, 팀 스타일과 안 맞으면 결국 또 래핑하거나 적응해야 합니다.


그러다 보니 생각이 조금씩 바뀌었습니다.

기성 도구를 어떻게든 맞춰 쓰는 것보다, 옛날과 달리 성능이 기가 막힌 LLM도 있겠다 필요한 만큼만 직접 만드는 게 더 낫지 않을까?



최종 결정

img.jpg?credential=yqXZFxpELC7KVnFOS48ylbz2pIh7yKj8&expires=1774969199&allow_ip=&allow_referer=&signature=EDY5%2FQqwRC8FyKTFTCAgaWi70iE%3D
결국은 커스텀 스크립트를 만들기로 했습니다.



비슷한 고민을 먼저 해본 팀들의 사례를 찾아보면, 생각보다 많은 팀이 결국 커스텀 스크립트 쪽으로 가 있었습니다.

방식은 단순합니다.

1. Asset 폴더를 스캔하고

2. 이름 규칙에 맞춰 코드를 생성하고

3. 실제 Asset이 존재하는지 확인하는 테스트도 같이 만든다


그리고 작업자는 아래와 같은 명령 하나만 실행합니다.

make generate_asset


이 방식이 좋았던 이유는 아주 명확했습니다.

첫 번째는 tuist generate에 덜 의존할 수 있다는 점입니다.
Asset 하나 추가할 때마다 프로젝트 생성 타이밍을 다시 의식하지 않아도 됩니다.

두 번째는 생성되는 API를 팀 스타일에 맞출 수 있다는 점입니다.
이름 규칙, 폴더 구조 반영 방식, 접근 형태, 공개 범위까지 전부 우리가 정할 수 있습니다.

세 번째는 예외 처리도 우리가 결정할 수 있다는 점입니다.
예를 들어 app_Icon와 appIcon처럼 코드 생성 시 이름이 충돌하는 경우를 어떻게 처리할지, 특정 폴더는 생성 대상에서 제외할지 같은 규칙도 넣을 수 있습니다.

즉, 이 시점부터는 기성 도구가 얼마나 완벽하냐보다 우리 팀이 어떤 개발 경험을 원하느냐가 훨씬 중요해집니다.


마무리

이번 경험을 통해 느낀 건, Asset 접근 방식도 결국 팀의 개발 경험을 설계하는 문제라는 점이었습니다.


특히 저에게는 tuist generate에 계속 의존하는 흐름이 생각보다 큰 마찰로 다가왔고, 그 과정에서 “무엇을 쓸까?”보다 “우리는 어떤 흐름으로 작업하고 싶은가?”가 더 중요한 질문이라는 걸 체감하게 됐습니다.

또 하나 흥미로웠던 점은, 예전 같았으면 직접 스크립트를 만든다는 선택이 꽤 무겁게 느껴졌을 텐데 지금은 그렇지 않다는 점입니다.

반복적인 코드 생성이나 초안 작성은 LLM의 도움을 받으면 훨씬 빠르게 시작할 수 있고, 그 덕분에 팀은 정말 필요한 규칙과 API 모양을 정하는 데 더 집중할 수 있게 됐습니다.


아쉽게도 이번 글에서는 예시 코드를 크게 다루지 않았습니다.
중요한 건 구현 디테일보다는, 각 팀이 원하는 사용 방식과 생성 흐름을 먼저 정하는 일이라고 생각했기 때문입니다.


정답이 하나로 정해져 있다고 생각하지는 않습니다.
어떤 팀에는 Tuist 기본 기능이 더 잘 맞을 수도 있고, 어떤 팀에는 R.swiftSwiftGen이 더 나은 선택일 수도 있습니다.


다만 한 가지는 분명했습니다.

이번 글에서는 tuist generate를 예로 들었지만, 특정 도구의 기본 기능이 팀의 워크플로우와 어긋나기 시작하는 순간, 그 의존성을 줄이기 위한 작은 도구를 직접 만드는 선택도 충분히 현실적이라는 점입니다.


그리고 그때부터 중요한 건 무엇을 쓸까? 보다 우리는 어떤 흐름으로 일하고 싶은가? 라는 질문이었습니다.


비슷한 고민을 하고 계신다면, 파이썬으로 작은 생성 스크립트를 한 번 직접 만들어서 사용해보시길 권장합니다.
생각보다 금방 만들 수 있고, 한 번 팀 스타일에 맞게 돌아가기 시작하면 왜 직접 만들고 싶어졌는지 금방 체감하게 됩니다.

긴 글 읽어주셔서 감사합니다.


✍️ 작성자: 은표

직군: iOS Developer

GitHub: https://github.com/honghoker

작가의 이전글Tuist 모듈 자동화 CLI 자동화 만들기