Computer >> 컴퓨터 >  >> 스마트폰 >> iPhone

SwiftUI로 디자인 시스템 구축하는 방법: 토큰부터 버튼 컴포넌트까지

하나의 제품을 지원하는 디자인 시스템을 만드는 일은 결코 쉽지 않습니다. 확장성을 고려하면 견고함(robust)과 유연성(flexibility)을 동시에 갖춰야 하기 때문입니다. 하지만 도전적인 작업인 만큼, 시각적 측면과 코드 측면 모두에서 좋은 시스템을 구축하는 데 도움이 되는 원칙과 접근법을 다룬 훌륭한 자료들이 이미 많이 공유되어 왔습니다. 이 글에서는 그런 선배들의 지혜를 바탕으로, 아직 많이 알려지지 않은 영역인 SwiftUI로 디자인 시스템을 구축하는 방법에 초점을 맞춰 이야기해 보려 합니다.

왜 이 글을 쓰게 되었나

뉴욕 ITP에서 첫 여름을 보내던 시절, 저는 운 좋게 Line Break Studio에서 iOS 개발 인턴으로 일할 기회를 얻었습니다. 그중 맡은 과제가 바로 디자인 시스템을 두 단계로 나누어 구축하는 것이었습니다. 먼저 Sketch로 시각적으로 설계하고, 이어서 SwiftUI로 프로그래밍 방식으로 구현하는 작업이었습니다.

새로운 프레임워크를 실험하며 디자인 시스템을 만들어가는 경험은 놀라웠지만, 그 과정에서 여러 번 버그와 마주치기도 했습니다. 그래서 우리는 이 경험을 커뮤니티와 공유하고자 하며, 이 글이 여러분의 개발 과정을 조금이라도 수월하게 만들어 주기를 바랍니다.

SwiftUI란 무엇인가

Apple은 WWDC 2019에서 이 혁신적인 새 프레임워크를 발표했는데, 최근 몇 년간 발표된 것 중 손꼽히는 발표였습니다. 웹 개발자의 관점에서 보면, SwiftUI에서의 프로젝트 개발 경험은 기존 프론트엔드 스택 및 프레임워크와 상당히 유사합니다.

이는 분명 훌륭한 변화입니다. UI를 프로그래밍하고 상태(state)를 관리하는 일이 예전보다 훨씬 쉬워졌기 때문입니다. 게다가 가장 좋은 점은 UIKit과 SwiftUI를 매끄럽게 통합할 수 있다는 것입니다. SwiftUI의 기초를 배우고 싶다면 Apple이 제공하는 공식 튜토리얼이 큰 도움이 됩니다.

WWDC에서 소개된 SwiftUI 입문 세션

데모 프로젝트 소개

설명을 돕기 위해, Line Break Studio에서 우리가 만든 디자인 시스템의 축소판을 준비했습니다. 이 프로젝트는 다양한 형태의 버튼(button) 컴포넌트 집합으로, 그 하위에는 타이포그래피(typography)컬러 팔레트(colorPalette)라는 두 개의 저수준 파트가 깔려 있습니다.

SwiftUI로 디자인 시스템 구축하는 방법: 토큰부터 버튼 컴포넌트까지
데모 프로젝트의 동적 렌더링 화면

프로젝트는 GitHub에 공개되어 있으며, 저는 Xcode 11 Beta 5를 사용해 개발했습니다. 디자인 시스템 관리 허브로 활용한 Airtable 베이스(워크플로우 관리에 대한 자세한 내용은 별도 문서 참고) 역시 참고용으로 공개되어 있습니다.

디자인 시스템 구축의 원칙

코드로 된 디자인 시스템은 디자이너와 개발자 사이의 미들웨어(middleware)라고 할 수 있습니다. 시스템 개발자는 시각적 형태의 디자인 시스템으로부터 입력을 받아, 이후 개발에 사용될 API를 산출합니다. 이 시스템을 코드로 완성하기 위해 다음 두 가지 원칙을 반드시 인지해야 합니다.

1. 토큰(token)으로 소통하기

근본적으로 프로그램 안에 디자인 시스템을 두는 목적은 더 나은 코드 관리나 개발 효율이 아니라, 뷰(view)가 디자인 파일과 일치하도록 보장하는 것입니다. 그 목표를 달성하려면 특정 색상, 폰트, 크기 등 시각 요소를 토큰으로 표기하는 것이 필수적입니다. 이를 통해 팀 내 개발자, 디자이너, 기획자 간의 소통 품질을 일정하게 유지할 수 있습니다.

SwiftUI로 디자인 시스템 구축하는 방법: 토큰부터 버튼 컴포넌트까지
Salesforce가 구축한 Lightning Design System의 토큰

2. 계층 구조(hierarchy) 만들기

EightShapes의 글에서는 "옵션을 먼저 보여주고, 그다음 결정을 보여줘라(show options first, then decisions next)"를 강조합니다. "옵션 없이는 결정을 내릴 수 없다"는 이유에서입니다.

SwiftUI로 디자인 시스템 구축하는 방법: 토큰부터 버튼 컴포넌트까지
EightShapes의 디자인 토큰 관련 아티클

이러한 순차적 아키텍처는 서로 다른 계층 간의 결합도(coupling)를 낮춰 주기 때문에, 이후 수정 사항이 생겼을 때 더 큰 유연성과 대응력을 발휘합니다. 저는 계층을 아래에서 위로 material → base → token 순서로 구성했지만, 팀이 편안하게 느끼는 어떤 방식이든 괜찮습니다.

코드로 들어가기

다음 섹션에서는 우리의 경험을 바탕으로 짚고 넘어가고 싶은 핵심 포인트들을 정리했습니다. 전체 코드는 GitHub 저장소를 참고해 주세요. 개선을 위한 피드백이나 비판은 언제든 환영합니다.

1. 계층 구조 아키텍처링

저수준의 재료(material)들을 쌓아 최상위 토큰을 만드는 방식은 두 가지가 있습니다.

  • 타입 안정성과 코드 가독성을 위한 enum 활용

코드에서 그룹핑 래퍼나 함수의 파라미터로 enum을 사용하는 장점은 이미 널리 알려져 있습니다. 여기서 언급할 만한 부분은 계층 구조의 구현 방식입니다.

우리는 폰트 크기(CGFloat)와 폰트 이름(String) 같은 로우 값(raw value)을 항상 최하위 계층에 저장합니다. 이 값들은 함부로 건드리고 싶지 않기 때문입니다. 하지만 enum의 raw value는 반드시 리터럴(literal)이어야 하므로, 다른 enum의 값을 case에 그대로 할당할 수는 없습니다.

이 문제를 우회하기 위해, 필요할 때 switch 문으로 raw value를 반환해 주는 getValue 함수를 구현했습니다.

  • 더 간단한 구조를 위한 struct 활용

enum이 훌륭하지만, 모든 경우에 그 고유 기능이 필요한 것은 아닙니다. 예를 들어 Xcode가 동적 색상(dynamic color) 처리라는 무거운 작업을 대신 해 주고, API 엔드포인트에서 파라미터 옵션이 필요 없다면, 간단한 두 단계의 struct만으로도 컬러 팔레트를 구성할 수 있습니다.

2. 명확하고 직관적인 API 엔드포인트 네이밍

네이밍 규칙은 논쟁의 여지가 많은 주제입니다. 우리는 기본적인 Swift 컨벤션 외에 딱 두 가지 규칙만 따릅니다. 첫째, 약어를 쓰지 않는다. 둘째, 단순하게 만든다. 예를 들어 타이포그래피와 컬러 시스템을 사용할 때, 새로운 엔드포인트를 만드는 대신 Font와 Color 구조체를 extension하여 활용합니다. 이 접근법 덕분에 개발자가 익숙하지 않은 API 이름을 외우는 부담이 줄어듭니다.

3. 라이트·다크 두 모드의 컬러 셋 동적으로 관리하기

이제 다크 모드는 업계 표준이 되었고, iOS와 Android 진영 모두 이 기능을 구현했습니다. 사용자에게는 좋은 흐름이지만, 디자이너와 개발자에게는 특히 그레이스케일(grayscale) 컬러 셋의 관리와 네이밍 같은 과제를 안겨 줍니다.

SwiftUI로 디자인 시스템 구축하는 방법: 토큰부터 버튼 컴포넌트까지
Material Design의 다크 테마 가이드

그레이스케일 색상을 동적으로 사고하고 소통하려면 white, light, black, dark 같은 용어로는 한계가 있습니다. 예를 들어 HEX 값 #000000(블랙)인 동적 색상을 라이트 컬러 스킴에서 'black' 또는 'dark'라고 불렀다면, 다크 컬러 스킴에서 #FFFFFF(화이트)로 바뀌어야 하는 이 색상을 어떻게 지칭할까요? 'defaultDark'? 아니면 'lightDark'?

SwiftUI로 디자인 시스템 구축하는 방법: 토큰부터 버튼 컴포넌트까지
헷갈리기 쉬운 컬러 셋의 전환

기존 방식으로 그레이스케일 동적 컬러 셋의 이름을 짓는 것은 매우 혼란스럽습니다. 이 혼란을 피하기 위해 우리는 themecontrast라는 개념으로 라이트·다크 스킴의 하나의 컬러 셋을 관리합니다.

SwiftUI로 디자인 시스템 구축하는 방법: 토큰부터 버튼 컴포넌트까지
데모 Airtable 베이스의 컬러 네이밍 예시

단, 그레이스케일 색상이 항상 반대 모드에서 색이 뒤집히는 것은 아닙니다. 밝은 색은 밝게, 어두운 색은 어둡게 유지되는 경우라면 단순히 light 또는 dark로 이름 짓으면 됩니다.

이 네이밍 방식에 익숙해지고 나면, Xcode에서 컬러 팔레트 아키텍처를 관리하는 일은 쉽습니다. 새 컬러 셋을 만들려면 새 Asset Catalog 파일 생성 → 새 Color Set 추가 → AppearancesAny, Light, Dark로 변경하면 끝입니다.

SwiftUI로 디자인 시스템 구축하는 방법: 토큰부터 버튼 컴포넌트까지
Xcode에서 컬러 에셋 추가하는 방법

4. environment 설정

SwiftUI 프레임워크의 멋진 기능 중 하나는 environment 수정자(modifier)입니다. 대상 뷰의 환경값(environment values)을 제어할 수 있게 해 줍니다. 디자인 시스템 관점에서 이 기능은 앱의 폰트를 루트(root) 레벨에서 한 번에 변경할 수 있는 편리한 방법을 제공합니다. 또한 environmentValue를 사용하면 개발 중 라이트·다크 컬러 스킴을 손쉽게 전환하고 테스트할 수 있다는 장점도 있습니다.

5. buttonStyle과 버튼 라벨

UIKit 시절과 비교하면, SwiftUI에서 재사용 가능한 버튼을 만드는 일은 훨씬 쉬워졌습니다. Button 뷰는 두 부분으로 구성됩니다. 버튼이 눌렸을 때 실행될 이벤트인 action 클로저와, 버튼의 본체인 label입니다. 그리고 이 뷰에 buttonStyle 수정자를 체이닝할 수 있습니다. 재사용 가능한 버튼 만들기에 대한 자세한 내용은 Alejandro의 튜토리얼을 읽어보길 추천합니다. 포괄적이면서도 유용한 자료입니다.

우리의 커스텀 버튼 컴포넌트에서는 먼저 TokenButtonLabelTokenButtonStyle이라는 두 개의 구조체를 만듭니다. 이 두 구조체는 디자인 파일에 정의된 버튼 타입에 맞춰 프로그래밍됩니다. 예를 들어 라벨은 아이콘(icon)과 텍스트(text), 두 종류뿐이며, 각 타입마다 새 인스턴스를 위한 서로 다른 파라미터를 가진 init 함수가 설계되어 있습니다.

반면 버튼 스타일은 크게 네 가지 타입이 있습니다. 원형 아이콘(circle icon), 아이콘(icon), 캡슐(capsule), 텍스트(text)입니다. ButtonStyle 프로토콜을 따르려면 makeBody 함수를 구현해야 합니다. 이 함수는 configuration 프로퍼티를 제공하며, 여기에는 버튼이 눌렸는지 감시할 수 있는 네이티브 isPressed 값이 포함되어 있습니다.

마지막으로 TokenButtonLabelTokenButtonStyle 위에 쌓인 버튼 컴포넌트 API의 최종 엔드포인트는 TokenButton입니다. 버튼의 콘텐츠와 스타일을 함께 감싸는 그룹핑으로, 시각적 디자인 시스템의 버튼 타입들을 그대로 반영합니다.

6. 래퍼(wrapper)로서의 AnyView

ButtonStyle 프로토콜이 제공하는 makeBody 함수를 다루던 중, View를 활용하는 데 유용한 팁 하나를 발견했습니다. 뷰를 변수에 저장할 때 타입 어노테이션을 AnyView로 지정하면, SwiftUI에서 뷰의 범용 컨테이너처럼 동작합니다.

우리의 경우, 모든 타입의 버튼에 opacity 수정자를 configuration.label에 적용하고 싶었는데, 각 switch case마다 반복해서 붙이는 대신 마지막에 한꺼번에 체이닝하는 편이 합리적이었습니다. AnyView의 장점을 활용하면 다음과 같은 패턴을 구현할 수 있습니다.

7. mutating 함수로 뷰 수정자 만들기

버튼의 스타일을 동적으로 업데이트하려면 자체 수정자를 만들 수 있습니다. 먼저 뷰 안에 커스텀 mutable state 프로퍼티를 인스턴스화한 다음, 대상 상태 프로퍼티를 업데이트한 후 Self 타입을 반환하는 mutating 함수를 생성하면 됩니다.

8. 까다로운 테두리(border) 스타일

SwiftUI의 한 가지 단점은 원형 모양에 원형 테두리를 입히는 작업이 전혀 직관적이지 않다는 점입니다. 한동안 고민하다가 StackOverflow에서 해결책을 찾았습니다. clipShapeoverlay 수정자를 함께 사용해야 의도대로 동작합니다.

마치며

SwiftUI는 Apple이 이룬 놀라운 발전입니다. 아직 부족한 점이 남아 있지만, 이를 활용하면 견고하고 유연한 디자인 시스템, 나아가 복잡한 iOS UI를 그 어느 때보다 효율적으로 구축할 수 있습니다. 이 글이 UI를 구축하려는 iOS 팀에게 도움이 되기를 바라며, 어떤 피드백이든 언제든 환영합니다!

? vinceshao.com에서 저의 다른 작품을 확인하실 수 있으며, Twitter나 LinkedIn에서 저를 팔로우할 수 있습니다.