[WWDC22] The Craft of SwiftUI APIs: Progressive Disclosure

kimseawater
daily-monster
Published in
8 min readApr 23, 2024

안녕하세요 화요일마다 돌아오는 kimseawater입니다.

오늘은 오랜만에 SwiftUI 관련 세션을 듣고 정리해봤습니다!

이 세션에서는 SwiftUI팀에서 SwiftUI를 설계할때의 원칙 중 하나인 Progressive Disclosure(단계적 공개)에 대해 소개합니다

Progressive Disclosure(단계적 공개)란?

먼저, 단계적 공개 라는 말이 나오는데, 이는 사실 API 설계에서만 볼 수 있는 내용은 아닙니다.

macOS의 저장하기 창을 보면, 처음에는 기본적으로 저장 위치가 설정되어 있고, 드롭다운 메뉴를 열면 주로 사용하는 위치가 나옵니다. 그래서 선택 가능성이 큰 위치를 쉽게 선택할 수 있게 합니다.

만약 또 다른곳을 지정하기를 원할경우에, 창을 확대하면 더 포괄적인 UI가 나오게 됩니다.

이러한 UI는 필요할 때마다 단계적으로 더 복잡한 UI가 나타나도록 설계되어 있습니다. 이를 단계적 공개 (Progressive Disclosure)라 하고, 이를 API에도 적용시킨 것입니다.

API는 결국, 사용할 때의 느낌이 좋아야 합니다.

개발자는 선언하는 곳(declaration site)에서의 코드를 보며 코딩하지만, 사실 코드를 보기 좋게 만들려면 만든 코드를 호출해 직접 사용되는 곳 (call site)에서 봐야합니다. 이상적인 API는 간단하고 접근성이 좋지만, 복잡한 경우에도 잘 동작해야 합니다.

Progressive Disclosure의 좋은점

그럼 이런식으로 API를 설계하면 좋은점이 뭘까요?

  1. 첫 빌드와 실행에 걸리는 시간을 최소화해서, API를 빠르게 사용할 수 있습니다.
  2. 코드에 쉽게 익숙해져서, 사용할 API가 usecase와 상관없는 개념으로 가득해질 걱정이 없습니다.
  3. 피드백 주기가 짧아지기 때문에, 각 단계에서 만든걸 보고 한 부분씩 점진적으로 추가해나갈 수 있습니다.

결국 앱을 빠른주기로 개선해서 개발할 수 있다는점, 처음부터 완벽히 만들려고 노력하지 않아도 되는점이 매우 좋은 점이라고 할 수 있습니다.

어떻게 Progressive Disclosure를 API 설계에 적용하나?

먼저, 가장 일반적인 사용 사례들을 고려해봐야 합니다. 그리고 API에 intelligent한 default값을 제공해서 사용할때 필요한 경우에만 일반적인 사례를 구체화합니다. 또 call site를 최적화하고, 조각조각 구성해서 compose하는 방식을 사용하면 됩니다.

하나씩 살펴봅시다.

1) 일반적인 usecase(사용사례) 고려하기

label을 예시로 들어봅시다. Button을 생성하면, label을 넣으라고 요구하고 이는 보통 어떤 버튼인지를 설명합니다.

label을 좀 더 커스텀하고 싶으면, 다른 오버로드를 이용해서 label을 임의의 뷰로 나타내면 됩니다.

이런식으로 더 복잡한 기능을 물론 넣을수는 있습니다. 하지만 버튼을 사용하는 99%의 경우에서는 간단히 text만 들어가는 기능이 필요할 것입니다.

위에서 본 label같은 패턴은 SwiftUI에서 엄청 많이 볼 수 있습니다.

결국, 텍스트만 필요한 99퍼센트의 사람들은 간단히 텍스트만 작성하도록, 혹시나 더 커스텀한 뷰를 넣고싶다면 제공되는 오버로드를 통해 넣을 수 있도록 설계한 것 입니다.

2. intelligent한 default값 적용

SwiftUI에서는 사용하는 곳에서 간결히 사용할 수 있도록 명시적으로 구체화하지 않는 모든 항목에 default를 제공합니다.

SwiftUI의 Text를 봅시다.

이렇게 간단한 코드만 해도, SwiftUI가 environment locale의 앱 번들 내의 localized string을 검색해서 텍스트를 현지화하고, 바로 다크모드를 지원합니다. 그리고 글자 크기 설정에 따라 자동으로 커지거나 작아집니다. 위 코드는 겉으로 보기에는 간단하지만, 그 뒤에서는 많은 일들이 일어납니다.

텍스트 두 개를 위아래로 배치하면, 현재 텍스트의 맥락에 맞게 간격을 자동으로 조정합니다. 물론, 수동으로 지정해 줄 수도 있지만, default값이 있기 때문에 관련없는 내용은 호출할 때 나타나지 않습니다.

toolbar의 예시를 봅시다.

toolbar를 생성해주면, 각 버튼의 위치를 명시적으로 지정하지 않아도 os에 알맞게 배치됩니다. 맥에서는 왼쪽, iOS에서는 네비바, 워치에서는 네비바 아래 고정형태로 나타납니다.

물론 항목을 제어하고 싶으면 맞춤 설정도 가능합니다. 그치만 intelligent default값이 많은 사례들을 처리합니다.

3. call site 최적화

intelligent default를 제공하면 멋진 경험을 만들 수 있지만, API가 무겁게 느껴질 수도 있습니다. 이를 줄이기 위해 call site 최적화가 필요합니다.

표에서 복잡한 기능(ex. 정렬기능, 칼럼, row 설정 등)도 필요할 때가 있지만, 그냥 완전 간단한 일반적인 표를 만들고 싶을 때도 있을 것입니다. 어떻게 코드에서 call site를 최적화 할 수 있을지 봅시다.

위 코드를 보면서 call site를 최적화해봅시다.

먼저, 위에서는 ForEach문을 돌려각 항목에서 TableRow()를 제공합니다. 사실 개발자가 이 과정을 할 필요는 없으니 이는 background가 하도록 해봅시다.

테이블로 바로 보내서, ForEach동작은 background에서 수행되도록 하면 call site를 단순화할 수 있다.

여기서 더 생각해보면, 표에 나타내야 하는 값이 string이라면 Text()를 통해 보여주고 있는데, 그냥 이런경우에는 string만 입력받아도 될 것입니다.

그럼 이런식으로 작성할 수 있습니다.

물론 더 단순화할 수 있습니다. 위에서는 정렬기능이 있는데, 사실 제일 간단한 사용사례를 생각했을때는 정렬이 필요 없을수도 있습니다. 그래서 정렬을 고려하지 않은 표를 제공합니다.

이렇게되면 call site에서 정말 간단하게 코드를 사용할 수 있습니다.

이렇게 설계하려면, 중요한 두 가지 질문을 던지면 됩니다.

  1. What are the common use cases? (일반적인 사용 사례가 뭘까?)
  2. What is the essential information? (핵심 정보가 뭘까?)

이는 call site를 최적화할 수 있지만, 사실 API에 주는 영향을 제대로 고려하지 않으면 의도하지 않게 동작할 수도 있습니다. 그래서 아래 전략이 필요합니다.

4. 열거하지 않고 compose 한다.

SwiftUI의 Stack을 봅시다. 그중 HStack에서 가장 핵심 정보는 어떤 컨텐츠가 들어가는지내부에서 어떻게 정렬할지 일 것입니다.

먼저 HStack에서 일반적인 사용사례가 뭘까요?

아마 이런 경우들이 있을 것입니다.

VStack에는 이미 정렬에 관한 api가 있습니다. 이와 비슷하게 enum을 이용해서 스택에 사용하고 싶어질 수 있습니다.

그치만 이런식으로 열거하다보면, 항목이 너무 많아질 뿐 아니라 모든 케이스에 대한 enum을 추가해야 하는데 설계할때 모든 경우를 생각해내지 못할수도 있습니다.

이때는 열거하지 말고, compose하면 됩니다. API를 조각으로 나눠서 해결책을 만들어 봅시다.

스택의 경우에는 Spacer()를 통해서 훨씬 많은 정렬들을 구현할 수 있습니다.

이렇듯 단계적 공개를 이용하면 call site를 최적화할 수 있고, call site에서 모든 케이스를 다룰 수 있게 됩니다. 설계할때 이런 방법을 적용해보세요~

--

--