-
AllTrails: 코드 재작성 없이 모멘텀 유지하기
AllTrails CTO인 James Graham의 비하인드 스토리를 통해 해당 팀이 UIKit 앱에 SwiftUI를 어떻게 적용했는지 알아보세요. 기존 코드베이스를 유지하면서 새로운 기능을 더 빠르게 빌드할 수 있도록 해 주는 점진적 도입을 위한 전략을 살펴보세요. 또한 UIKit과의 완벽한 상호 운용성 덕분에 SwiftUI가 어떻게 앱을 위한 실용적인 선택이 되는지 알아보세요.
이 세션은 원래 Apple과의 만남 활동 ‘SwiftUI 기초: SwiftUI로 멋진 앱 빌드하기'의 일부로 진행되었습니다. 전체 비디오를 시청하여 더 많은 인사이트와 관련 세션을 확인하세요.
리소스
-
비디오 검색…
여러분, 안녕하세요. 여러분께 드릴 질문이 하나 있습니다. 어쩌면 공감이 가실지도 모르겠네요. 기존 UKit 코드베이스를 살펴본 적이 있으신가요? 아마도 4년 전에 작성된 거대한 뷰 컨트롤러를 보고 ‘이런, 이건 대대적인 리팩토링이 필요하겠구나’라고 생각한 적이 있으실 겁니다. 이건 잠시 제쳐두고, 전체를 SwiftUI로 다시 만들어 봅시다. 잠깐 손을 들어 보세요. 이런 경험이 있으신가요? 와, 정말 많네요. 손을 든 분들이 정말 많이 보이네요. 온라인으로 시청하고 계신 분들도 분명 더 많으실 겁니다. 매력적인 생각이지만, 저희가 운영하는 규모에서 코드를 전면 재작성하는 것은—엔지니어의 노력으로 하든 AI 기반 워크플로우를 활용하든—엄청난 위험을 초래합니다. 안녕하세요, 제 이름은 제임스 그레이엄입니다. 저는 Alltrails의 CTO이며, 오늘은 리라이팅 없이도 현대적인 개발 속도를 어떻게 달성했는지 여러분께 이야기해 드리고자 합니다. 상부에서 하부로 강제로 도입하는 대신, SwiftUI가 우리 조직에 자연스럽게 스며들게 한 방법을 보여드리겠습니다. 본론에 들어가기 전에, 이번 이야기의 흐름을 먼저 설명해 드리겠습니다. 먼저, Alltrails가 어떤 회사인지, 우리의 규모와 제약 조건에 대해 공유하겠습니다. 그 다음에는 코드 재작성이나 강제 지시 없이 SwiftUI가 어떻게 우리 코드베이스에 도입되었는지 설명하겠습니다. 그 후에는 전환점이 된 순간을 보여드리겠습니다. SwiftUI가 더 이상 실험 단계가 아니라 기본 선택지가 되기 시작한 시점입니다.
마지막으로, 현재 상황과 우리가 하이브리드 아키텍처를 어떻게 바라보고 있는지에 대해 말씀드리며 마무리하겠습니다.
저희의 기술적 결정을 이해하시려면, 저희의 규모를 먼저 파악하셔야 합니다. 오늘날 Alltrails는 야외 탐험을 위한 세계에서 가장 인기 있고 신뢰받는 플랫폼입니다. 저희의 사명은 간단합니다. 전 세계 사람들이 야외에서 자신의 길을 찾을 수 있도록 돕는 것입니다. 저희는 사람들이 트레일을 발견하고, 자신 있게 이동하며, 트레일에서의 경험을 한층 더 풍요롭게 할 수 있도록 돕습니다. 지역 공원 산책이든 며칠에 걸친 하이킹이든 상관없이, 최신 트레일 정보와 경로상의 사진을 강조해 보여주는 ‘사진 투어’와 같은 기능을 제공합니다. 저희가 여러분을 든든히 지원해 드립니다. Alltrails에는 9,000만 명 이상의 커뮤니티 회원이 있습니다. 전 세계에 50만 개 이상의 트레일이 등록되어 있으며, 회원들은 총 19억 마일 이상을 기록했습니다.
14개 언어로 서비스를 제공하고 있으며, 이는 우리가 내리는 모든 기술적 결정이 다양한 기기, 다양한 지역, 그리고 무엇보다도 다양한 연결 환경 조건에 있는 수백만 명의 회원에게 영향을 미친다는 것을 의미합니다.
우리는 각기 다른 관심사와 선호도를 가진 폭넓은 회원층을 대상으로 서비스를 제공합니다. 한편으로는 지역 호수의 멋진 경치를 감상하며 쉽고 비교적 평탄한 오후 산책을 즐기는 캐주얼한 회원이 있는 반면, 다른 한편으로는 휴대폰 신호가 닿지 않는 곳에서 오프라인 내비게이션을 이용해 하루 종일 하프 돔(Half Dome) 등반에 도전하는 열정적인 등산객도 있습니다.
이러한 다양성은 안정성, 배터리 수명, UI 성능에 대해 엄격한 제약을 부과합니다. 결함이 있는 코드를 출시할 수는 없지만, 저희 앱은 정적인 것이 아닙니다. 새로운 인터페이스 요소와 심화된 기능으로 끊임없이 진화하고 있습니다.
SwiftUI가 등장했을 무렵, Alltrails는 이미 매우 규모가 크고 성숙하며 성공적인 UIKit 앱이었습니다. 이러한 진화의 한 예로, 지난 몇 년간의 저희 홈페이지를 보여드리겠습니다.
우리는 주간 릴리스 주기로 서비스를 운영하며, 무료 및 유료 서비스 모두를 지원하고 있었습니다.
그리고 기존 코드에 대해 제가 말씀드리고 싶은 가장 중요한 점은 바로 이것입니다. UIKit은 해결해야 할 문제가 아니었습니다. 그것은 우리의 규모 확장을 가능하게 한 토대였습니다. 그 때문에 우리는 트레일을 중단하고 변경 작업을 진행할 수 없었습니다. 우리는 트레일을 유지 관리해야 했고, 등산 도중에도 업그레이드를 진행해야만 했습니다. SwiftUI가 등장했을 때, 그것은 우리가 간절히 원하던 것들을 약속했습니다. 데이터가 변경되면 자동으로 업데이트되는 더 깔끔한 상태 관리 뷰로, UI가 모델과 동기화되지 않아 발생하는 모든 종류의 버그를 제거해 주며, 코드량도 줄여주었습니다. UIKit에 상응하는 코드에 비해 약 40%가 줄어든 수준입니다. 이는 유지 관리하고, 업데이트하고, 라이브 미리보기를 확인해야 할 코드가 40% 줄어든다는 뜻입니다. 디자인 변경 사항을 즉시 반영할 수 있는 능력도 생겼습니다. 하지만 이미 완성된 앱을 다시 작성하는 것은 선택지가 아니었습니다. 기술적으로나 조직적으로나 말이죠. 우리는 다른 접근 방식이 필요했습니다. 그래서 SwiftUI는 조용히 우리 코드베이스에 들어왔습니다. 이는 강제 사항도, 로드맵 항목도 아니었습니다. 우리는 샌드박스를 만들고, 프로토타입과 독립된 서비스를 위한 저위험 실험에 이를 활용했습니다. 이를 통해 릴리스 후보 버전을 위험에 빠뜨리지 않고도 프레임워크를 배울 수 있는 공간을 확보했습니다. 첫 번째 실질적인 결정은 UIKit과 SwiftUI 중 어느 것을 선택할 것인가가 아니었습니다. 바로 ‘이 두 가지가 어떻게 함께 잘 작동할 수 있을까?’였습니다. 우리는 초기 단계부터 상호 운용성에 투자했습니다. 여기 있는 이 코드 스니펫을 한 번 살펴보세요.
아, 맞다, 이것이 바로 우리의 브리지입니다. 우리는 트레일 코디네이터와 같은 SwiftUI 기능을 가져옵니다. 이를 호스팅 뷰로 감싸서 표준 UKit 스택 뷰에 바로 배치합니다.
그리고 이를 스크롤 뷰에 추가합니다.
여기 페이지를 아래로 스크롤해 보면, SwiftUI 서브뷰를 포함하는 호스팅 뷰의 일부 섹션을 볼 수 있습니다. 저희는 이 패턴을 초기 단계부터 확립했습니다. 이는 경계를 명확히 하고, 시간이 지나도 두 세계가 같은 언어로 소통할 수 있도록 보장하기 위한 프로세스상의 결정이었습니다. 두 가지 병행 트랙이 자연스럽게 형성됩니다. UIKit은 여전히 앱 라이프사이클 내비게이션과, 트레일 페이지나 커뮤니티 활동 뷰와 같이 복잡하거나 깊이 통합된 인터페이스의 무거운 작업을 대신 처리해 줍니다.
성숙한 앱의 경우, 프레임워크를 변경하기 위해 안정적이고 실전에서 검증된 화면을 다시 작성하는 것은 합리적이지 않았기 때문에, 등산 도중 잘 표시된 트레일의 경로를 변경하는 것을 피하고, SwiftUI 측면에서 명확한 사용자 경험과 성능 향상을 가져다주는 부분에 새로운 투자를 집중했습니다. 우리는 고립되어 있고 경계가 명확한 화면, 즉 무거운 뷰를 렌더링하거나 새로운 실험을 진행하는 데 SwiftUI가 가장 적합한 곳에서 의도적으로 이를 사용하고 있습니다.
여기서 두 가지 사례를 확인할 수 있습니다. 트레일 리뷰 흐름은 동적인 상태와 UI 업데이트가 발생하는 독립적인 화면으로, SwiftUI의 선언적 모델과 잘 부합합니다.
Apple Intelligence를 기반으로 구축된 ‘트레일에 무엇이든 물어보세요(Ask the Trail Anything)’ 경험은 더 새롭고 실험적인 서비스입니다. SwiftUI를 사용하면 앱의 핵심 아키텍처와 밀접하게 결합하지 않고도 이곳에서 빠르게 반복 작업을 수행하고 사용자 경험을 발전시킬 수 있습니다.
SwiftUI가 우리 디자인 시스템으로서 특히 빛을 발하는 또 다른 영역입니다. 'Denali'라는 이름의 디자인 시스템을 시각화한 디버그 모드의 앱을 살펴보겠습니다. Denali는 날마다 규모가 커지고 있으며, 모든 신규 기능이 이 시스템을 활용할 수 있도록 디자인, 시스템, 엔지니어링 팀이 협력하고 있습니다.
여기 보이는 모든 핵심 컴포넌트, 버튼, 세그먼트, 컨트롤, 배지는 우리 디자인 시스템의 일부이며, 디버그 모드에서 앱 내에서 확인할 수 있고 이제 SwiftUI로 구축되었습니다.
예전에는 수백 줄의 상용구 코드가 필요했던 부분입니다. 이제 그 일부만으로도 충분합니다. 그리고 새로운 변형을 추가하거나 간격을 조정해야 할 때, 간단한 변경만으로도 모든 곳에 반영됩니다. SwiftUI 덕분에 코드 베이스를 부풀리지 않고도 디자인 시스템을 확장할 수 있게 되었습니다.
또한 SwiftUI를 도입하면서 예상치 못한 변화가 일어났음을 발견했습니다. 아키텍처에 영향을 미치기 시작한 것입니다.
UI와 상태를 동기화하기 위해 작성하던 글루 코드를 더 이상 작성하지 않게 되면서, 뷰 모델의 크기가 종종 3분의 1 정도로 줄어들었습니다.
또한 SwiftUI가 상태 전파를 자동으로 처리해 주었기 때문에, 명시적인 연결 코드, 퍼블리셔, 연산자, 그리고 라이프사이클 관리 코드도 훨씬 줄어들었습니다.
게다가 UI, 상태, 동작이 하나로 통합되면서 변경으로 인해 영향을 받는 코드 줄 수가 줄었습니다. UI 관련 풀 리퀘스트의 크기가 30~40% 줄어들었고, 코드 리뷰 속도도 눈에 띄게 빨라졌습니다.
그때부터 SwiftUI는 단순한 UI 실험이 아니라 시스템을 구축하는 올바른 방식처럼 느껴지기 시작했습니다. 우리는 엔지니어들에게 SwiftUI 사용을 강요한 적이 없습니다. 그들은 새로운 작업을 위해 마찰이 덜한 SwiftUI를 선택했습니다. 이는 인지적 부담을 줄여주었기 때문입니다.
선택이 옳다면, 코드 양이 40% 줄어드는 것조차 자연스럽게 지속됩니다.
이것이 우리가 얻은 교훈입니다.
SwiftUI가 확산된 이유는 우리가 사람들에게 사용하라고 지시했기 때문이 아닙니다. 그것이 앞으로 나아가는 가장 빠른 길이었기 때문에 확산된 것입니다. 프레임워크가 인지적 부담을 줄이고 복잡성을 해소해 주면, 엔지니어들은 설득할 필요가 없습니다. 우리는 그저 자연스럽게 그것을 선택합니다. 하지만 코드베이스는 하룻밤 사이에 완전히 바뀌지는 않았습니다. 점차 기울어졌습니다. UKit은 여전히 깊이 뿌리내리고 있습니다. SwiftUI는 그 주위를 감싸며 성장하고 있습니다. 우리는 기능당 코드 줄 수 감소, 더 빠른 반복 주기,
그리고 분리된 SwiftUI 기능에서의 회귀 현상 감소와 같은 성공 지표를 측정합니다.
우리는 상호 운용성이 바로 인프라라는 사실을 깨달았습니다. 우리는 호스팅 래퍼, 공유 애니메이션, 브릿지, 통합 테마에 투자했습니다. 브릿지가 견고해지면, SwiftUI는 더 이상 새로운 기술이 아니라 기반이 되는 기술로 느껴집니다.
이 점을 보여주는 완벽한 예가 바로 우리의 Apple Watch 앱입니다. 나침반과 지도 화면 모두 SwiftUI로 구축되었으며, 지도 기능은 MapKit을 사용합니다. 이는 현대적인 아키텍처를 활용해 성능에 민감한 핵심 기능을 어떻게 출시할 수 있는지 보여줍니다.
우리가 여기서 SwiftUI를 선택한 이유는 단순히 멋있어서가 아니라, 기존 네비게이션 로직을 건드리지 않고도 복잡한 화면을 더 빠르게 반복 개선할 수 있었기 때문입니다.
그렇다면 현재 상황은 어떨까요? All Trails가 아직 완전히 SwiftUI로 마이그레이션된 것은 아닙니다. 이점을 누리기 위해 앱 전체를 다시 작성할 필요는 없습니다. 우리는 명확한 방향성을 가진 하이브리드 시스템을 운영하고 있습니다. UIKit은 안정성을 제공합니다. 복잡한 컬렉션 뷰나 정교한 네비게이션 바와 같은 심층적인 UI 커스터마이징에는 여전히 탁월합니다.
SwiftUI는 우리의 성장을 정의합니다. SwiftUI는 빠르게 발전하고 있으며, 여기 보이는 식물 식별 기능은 100% SwiftUI로 구현되었습니다.
지금까지 설명한 모든 내용은 All Trails의 규모, 회원, 제약 조건에 특화된 것입니다. 그리고 이는 의도된 것입니다. 팀들이 흔히 저지르는 실수는 도입을 이분법적인 결정으로 취급하는 것입니다. SwiftUI를 도입해야 할까요?
더 나은 접근 방식은 다음과 같습니다. 어떤 조건에서 도입이 위험이 아닌 추진력을 만들어낼까요?
저희에게 실제로 효과가 있었던 점—버그 감소, 더 빠른 배포, 팀 간 확장성 향상—을 살펴보았을 때, 이는 단일한 기술적 선택이 아니었습니다. 의도적으로 내린 일련의 결정들이었습니다.
제가 자주 듣는 질문 중 하나는 ‘UI Kit와 SwiftUI가 안전하게 공존할 수 있을까?’입니다. 오늘 제가 보여드린 내용을 바탕으로 보면, 그 대답은 단연코 ‘예’입니다. 따라서 무엇을 도입해야 한다고 말씀드리기보다는, 여러분이 각자의 상황에 맞춰 도입 여부를 평가하는 데 활용할 수 있는 세 가지 질문을 남겨드리겠습니다. 첫 번째는 상호 운용성을 인프라로 간주하는 것입니다. 상호 운용성이 보장된다 해도, 그 상호 운용성이 취약하거나 임시방편적인 수준이라면 실제 제품 개발의 압박이 가해지는 순간 도입은 주춤하게 될 것입니다. 두 번째 질문입니다. 새로운 도구가 인지적 부담을 줄여주나요? 도구가 진정으로 정신적 부담을 단순화한다면, 도입을 강제로 추진할 필요가 없습니다. 엔지니어들은 자발적으로 이를 선택할 것입니다. 그리고 세 번째 질문입니다. 전환율 대신 추진력을 측정하고 계신가요? 추진력은 더 작은 페어 단위의 협업, 더 빠른 리뷰, 그리고 더 적은 회귀 오류로 나타납니다. 코드베이스의 어느 정도를 전환했는지가 아니라 말입니다.
따라서 여기서 얻을 수 있는 교훈이 있다면, 진전을 이루기 위해 코드를 전면 재작성할 필요는 없다는 점입니다. 필요한 것은 방향성입니다. 오늘 시간을 내주셔서 감사드리며, All Trails의 이야기를 나눌 기회를 주신 점에 깊이 감사드립니다. 트레일에서 뵙겠습니다.
-