Chapter 11

디자인 시스템

"맡아 두기" 시범 화면을 네 책방 주인에게 보여 준 날, 헌책방 나무 주인이 물었다. "우리 가게 화면인데 왜 책방 E 초록이에요?" 작은 숲 주인은 노란색을, 한 줄 주인은 빨간색을 원했다. 그 자리에서 서윤이 화면을 네 벌 그렸다면 버튼은 네 가지 색, 네 가지 크기로 갈라졌을 것이고, 지후는 같은 버튼을 네 번 만들었을 것이다. 한 달 뒤 오류 메시지 색 하나를 고치려면 네 군데를 찾아다녀야 한다. 디자인 시스템은 이런 일을 막으려고 결정을 한 번 내리고 여러 곳에서 나눠 쓰는 방법이다. 이 장은 그 결정이 어떤 층으로 쌓이는지, 어디까지 같게 하고 어디서 다르게 둘지, 그리고 그 규칙을 누가 어떻게 지키는지를 본다.

같은 버튼을 네 번 그리지 않으려면

디자인 시스템(Design system)은 한 제품(또는 제품군)의 화면을 만들 때 함께 쓰는 결정의 묶음이다. 색과 간격 같은 가장 작은 값, 그 값으로 만든 버튼과 입력 칸 같은 부품, 부품을 엮어 자주 쓰는 화면 조각, 그리고 그것들을 언제 어떻게 쓰는지 적은 문서와, 바꾸고 더하는 절차까지 포함한다. 흔히 "컴포넌트 모음"이나 "디자인 파일의 스티커 시트"로 오해하지만, 부품만 있고 쓰는 규칙과 고치는 절차가 없으면 몇 달 안에 부품의 복사본이 부품보다 많아진다.

시스템의 결정은 층으로 쌓인다. 아래 그림은 이 장에서 쓰는 네 층이다. 아래층이 위층의 재료가 되고, 문서와 운영 규칙이 네 층 모두를 감싼다.

4 패턴 검색 결과 · 맡기기 확인 · 빈 결과 안내 3 컴포넌트 버튼 · 검색 칸 · 책 카드 · 상태 배지 · 토스트 2 의미 토큰 action.primary · text.default · feedback.danger 1 원시 토큰 ink-900 · green-600 · red-500 · space-16 문서 언제 쓰나 운영 누가 고치나 아래층의 값이 위층의 재료가 된다. 화살표는 "~로 만든다"
그림 1. 디자인 시스템의 층. 원시 토큰은 값 그 자체(짙은 잉크색 #22342a, 간격 16 px), 의미 토큰은 그 값이 맡은 역할(주 행동의 바탕색), 컴포넌트는 역할을 조합한 부품(주 버튼), 패턴은 부품을 엮은 화면 조각(검색 결과 한 줄)이다. 문서는 각 층을 언제 쓰고 언제 쓰지 않는지를, 운영 규칙은 누가 어떤 절차로 바꾸는지를 적는다.

층을 나누는 방식은 팀마다 다르다. 웹 디자이너 브래드 프로스트(Brad Frost)가 『아토믹 디자인』(Atomic Design, 2016)에서 화학의 비유를 빌려 원자·분자·유기체·템플릿·페이지의 다섯 단계로 나눈 틀이 널리 알려졌지만, 이것은 생각을 정리하는 비유이지 표준이 아니다. 어떤 팀은 셋으로, 어떤 팀은 여섯으로 나눈다. 중요한 것은 층의 이름이 아니라 한 결정이 어느 한 곳에만 적혀 있는가다. 버튼의 바탕색이 토큰 파일에도, 버튼 코드에도, 디자인 파일의 버튼에도 따로 적혀 있다면, 그 셋은 언젠가 서로 달라진다.

이 장은 WebBook 6장의 토큰과 WebBook 7장의 컴포넌트를 출발점으로 삼는다. 거기서는 웹사이트 하나를 만드는 사람의 눈으로 토큰과 상태를 봤다. 여기서는 여러 사람과 여러 가게가 오래 함께 쓰는 체계를 운영하는 눈으로 본다. 만드는 일보다 지키고 바꾸는 일이 더 길기 때문이다.

시스템의 사용자는 동료다 디자인 시스템도 하나의 제품이다. 다만 그 사용자는 손님이 아니라 화면을 만드는 디자이너와 개발자다. 그래서 이 책의 앞 장들이 손님에게 하던 일을 동료에게도 그대로 한다. 동료가 버튼을 찾다가 포기하고 복사해 만드는 순간을 관찰하고(2장), 문서의 이름 붙이기를 카드 소팅으로 점검하고(4장), 새 부품을 쓰는 데 걸리는 시간을 잰다. 쓰기 어려운 시스템은 아무리 정교해도 우회당한다.

토큰: 이름 붙인 결정

디자인 토큰(Design token)은 색, 글자 크기, 간격, 모서리 둥글기, 그림자 같은 디자인 값에 이름을 붙여 한곳에 모아 둔 것이다. 이 말은 2014년 무렵 한 대형 소프트웨어 회사의 디자인 시스템 팀이 쓰기 시작해 퍼졌다고 흔히 설명된다. 토큰은 디자인 도구와 코드가 같은 값을 읽게 해 주는 공용어 구실을 한다. W3C의 디자인 토큰 커뮤니티 그룹(Design Tokens Community Group)이 도구끼리 토큰을 주고받는 파일 형식을 정리해 왔다. 커뮤니티 그룹의 문서이며 W3C 권고(표준)는 아니다.

토큰은 보통 두 단계 이상으로 나눈다. 원시 토큰(Primitive token)은 "이런 색이 있다"는 목록이다. green-600 = #2f7d5b처럼 값에 붙인 이름이라, 이름에 쓰임이 들어 있지 않다. 의미 토큰(Semantic token)은 "이 역할에는 이 색을 쓴다"는 결정이다. action.primary = {ink-900}처럼 다른 토큰을 가리킨다. 컴포넌트는 의미 토큰만 읽는다. 아래는 그 모양을 줄여 적은 것이다(형식은 커뮤니티 그룹 초안의 $value와 중괄호 참조를 따랐다).

{
  "color": {
    "ink-900":   { "$value": "#22342a" },
    "green-600": { "$value": "#2f7d5b" },
    "red-500":   { "$value": "#d24f39" }
  },
  "action":   { "primary": { "$value": "{color.ink-900}" } },
  "text":     { "default": { "$value": "{color.ink-900}" } },
  "feedback": { "danger":  { "$value": "{color.red-500}" } }
}

같은 ink-900을 action.primary와 text.default가 함께 가리킨다는 데 주목한다. 지금은 우연히 같은 값이지만, 뜻은 다르다. 언젠가 "주 버튼만 조금 더 밝게"라는 결정이 나오면 action.primary가 가리키는 곳 하나만 바꾸면 된다. 컴포넌트가 원시 토큰을 곧바로 읽고 있었다면, ink-900을 바꾸는 순간 본문 글자까지 함께 밝아진다. 그걸 피하려면 ink-900을 쓰는 곳을 하나하나 찾아 "이건 버튼 뜻, 이건 글자 뜻"을 가려야 한다.

아래 시뮬레이터는 "맡아 두기" 서비스의 토큰과 컴포넌트, 패턴을 이은 연결망이다. 노드를 누르면 그 값을 바꿀 때 함께 바뀌는 것이 표시된다. 위쪽 단추로 의미 단계를 거치는 연결과 원시 값을 직접 읽는 연결을 바꿔 본다.

SIMULATOR

값을 바꾸면 어디까지 번지나

노드를 누른다
고른 노드—
함께 바뀌는 컴포넌트 · 패턴—
그 가운데 뜻이 다른 쓰임—
해볼 것: ① 처음 상태(세 층)에서 원시 토큰 ink-900을 누른다. 주 행동과 기본 글자, 두 의미 토큰을 거쳐 거의 모든 컴포넌트가 물든다. 원시 값은 함부로 바꾸는 것이 아니다. ② 의미 토큰 action.primary를 누른다. 버튼과 상태 배지 두 컴포넌트만 바뀌고, 뜻이 섞인 쓰임은 0이다. "주 버튼 색 바꾸기"라는 결정은 여기서 한 번에 끝난다. ③ "원시 값을 직접 읽음"으로 바꾸고 다시 ink-900을 누른다. 어떤 컴포넌트는 이 값을 버튼 바탕으로, 어떤 것은 글자로 쓰고 있어 "뜻이 다른 쓰임"이 여럿 나온다. 주 버튼만 바꾸려면 그 쓰임들을 하나씩 찾아 가려야 한다. (연결망은 이 장이 줄여 그린 "맡아 두기"의 일부다. 실제 시스템의 토큰은 수십에서 수백 개다.)

의미 단계는 값과 뜻 사이에 놓인 완충재다. 이 완충재 덕분에 테마도 가능해진다. 다크 모드는 원시 값을 바꾸는 것이 아니라 의미 토큰이 가리키는 곳을 바꾸는 일이고(WebBook 6장에서 "다크 모드는 반전이 아니다"라고 한 이유다), 다음 절에서 볼 책방별 강조색도 의미 토큰 하나가 책방마다 다른 원시 값을 가리키게 하는 일이다. 반대로 의미 단계가 너무 잘게 쪼개지면(버튼마다, 화면마다 의미 토큰을 하나씩 만들면) 원시 값을 직접 쓰는 것과 다를 바가 없어진다. 의미 토큰의 이름은 역할로 짓고, 그 역할을 쓰는 곳이 둘 이상일 때 만든다는 규칙이 흔히 쓰인다.

세 번째 단계: 컴포넌트 토큰 어떤 팀은 의미 토큰 위에 컴포넌트 토큰을 하나 더 둔다. button.primary.bg = {action.primary}처럼 특정 컴포넌트의 특정 부분에 이름을 붙이는 것이다. 버튼 하나만 따로 바꿔야 할 일이 잦은 큰 시스템에서는 쓸모가 있지만, 토큰 수가 컴포넌트 수만큼 늘어난다. 작은 서비스라면 두 단계로 시작하고, 같은 의미 토큰을 쓰는 컴포넌트들 가운데 하나만 자꾸 달라져야 할 때 그 하나에만 컴포넌트 토큰을 두는 편이 가볍다. "맡아 두기"는 두 단계로 시작했다.

한 칸만 바꾸는 테마

네 책방이 저마다 자기 색을 원하는 것은 당연하다. 손님은 "책방 E에서 맡겼다"는 것을 알아야 하고, 주인에게 가게의 색은 간판만큼 중요하다. 그렇다고 네 책방이 버튼 색, 글자 색, 배경 색을 모두 고를 수 있게 하면 서비스는 네 개로 쪼개진다. 손님은 책방을 옮겨 다닐 때마다 "맡아 두기" 버튼이 무슨 색인지 새로 배워야 하고, 오류 메시지의 빨강과 어느 책방의 빨강이 섞인다.

그래서 여러 브랜드를 한 시스템에 담을 때는 바꿀 수 있는 자리를 좁게 정한다. 이런 체계를 멀티 브랜드 테마(Multi-brand theming)라고 부른다. 서윤은 의미 토큰 가운데 딱 하나, shop.accent만 책방마다 다르게 두는 안을 냈다. 이 색은 책방 이름 배지와 책방 화면 머리의 얇은 띠에만 쓴다. 버튼, 링크, 상태 표시, 오류는 네 책방이 같은 색을 쓴다. 그리고 한 칸에 들어올 색에는 세 가지 조건을 건다.

  1. 배지 글자 대비 4.5:1 이상. 배지 위 글자는 흰색과 짙은 잉크색(#22342a) 가운데 대비가 큰 쪽을 자동으로 쓰고, 그 대비가 WCAG 2.2의 1.4.3(최소 대비, 보통 글자 4.5:1)을 넘어야 한다.
  2. 위험 색과 구별. feedback.danger(#d24f39)와의 색차 ΔE2000이 15 이상이어야 한다. 책방 배지가 오류 표시처럼 보이면 안 된다.
  3. 다른 책방과 구별. 한 검색 결과에 네 책방의 배지가 함께 나오므로, 가장 가까운 다른 책방 색과도 ΔE2000이 15 이상이어야 한다.

ΔE2000 15라는 문턱은 이 책이 정한 값이다. 표준이 정한 구별 기준은 없고, 작은 배지끼리 나란히 놓였을 때 한눈에 다른 색으로 보이는 정도를 어림한 것이다(색차 ΔE2000은 ColorBook 3장과 14장에서 다뤘다). 아래 종합 시뮬레이터에서 책방을 고르고 그 책방의 강조색을 명도와 색상으로 바꿔 본다. 가상 화면은 그 책방의 소개 화면이다.

SIMULATOR

책방별 강조색 한 칸

강조색—
배지 글자 대비—
위험 색과의 ΔE2000—
가장 가까운 다른 책방—
해볼 것: ① 네 책방을 차례로 눌러 본다. 각 책방이 처음 원한 색이다. 한 줄의 빨강은 배지 글자 대비는 넉넉하지만 위험 색과 너무 가깝다. ② 작은 숲을 고른다. 밝은 노랑은 흰 글자와는 대비가 1.7 정도에 그치지만, 짙은 잉크색 글자로 바꾸면 7.9가 되어 조건을 넘는다. 글자색을 자동으로 고르는 규칙 덕분에 노랑을 지킬 수 있다. ③ 한 줄에서 색상각을 300~340(자주) 근처로 옮기고 명도를 0.4 안팎으로 낮춰 세 조건을 모두 넘는 색을 찾는다. ④ "주 버튼에도 책방 강조색 쓰기"를 켠다. 책방마다 버튼이 달라져 손님이 같은 동작을 네 가지 색으로 배워야 하고, 책방 E에서는 배지와 버튼이 같은 색이라 배지도 눌리는 것처럼 보인다. 작은 숲의 노란 버튼은 흰 바탕 위에서 테두리가 거의 보이지 않는다. (채도는 각 책방이 처음 고른 색의 OKLCH 채도를 유지하고, 화면에 낼 수 없는 색은 채도를 줄여 맞춘다. 대비는 WCAG 2 식, 색차는 ΔE2000. 화면 색은 모니터마다 다르다.)

한 칸 규칙의 핵심은 정체성과 조작을 떼어 놓는 것이다. 책방의 색은 "여기가 어디인가"를 말하고, 서비스의 색은 "무엇을 누를 수 있는가"를 말한다. 두 말을 같은 색에 맡기면 둘 다 흐려진다. 주 버튼까지 책방 색으로 칠하는 순간 버튼 컴포넌트의 변형은 책방 수만큼 곱해지고(다음 절), 대비 검사도 네 번 해야 하며, 다섯 번째 책방이 들어오면 또 늘어난다.

책방처음 원한 색배지 글자배지 대비위험 색과 ΔE판정
책방 E#2f7d5b 책방 초록흰색4.9955.0통과
헌책방 나무#8b5a2b 고동색흰색5.8419.6통과
작은 숲#f2c230 노랑잉크색7.8742.2통과(글자색 자동)
한 줄#c0392b 빨강흰색5.446.7위험 색과 겹침

표의 값은 이 책의 계산 엔진(ART.contrast, ART.diff)으로 낸 것이다. 한 줄의 빨강은 배지만 보면 문제가 없지만, 맡기기가 실패했을 때 나오는 오류 표시와 색이 거의 같다(ΔE 6.7). 손님이 한 줄의 책을 맡기다 오류가 나면, 오류 표시와 책방 배지가 같은 색으로 나란히 놓인다.

색만으로 말하지 않는다 책방 배지는 색과 함께 반드시 책방 이름을 글자로 적는다. 색만으로 정보를 구별하게 하면 색각 이상이 있는 손님, 흑백으로 인쇄한 주인, 햇빛 아래의 휴대폰 화면에서 구별이 사라진다. WCAG 2.2의 1.4.1(색 사용)은 색이 정보를 전하는 유일한 수단이 되지 않게 하라고 한다. ΔE 15라는 구별 조건은 글자 위에 얹은 보조 장치이지 글자를 대신하지 않는다. 시뮬레이터 머리의 흑백 단추를 눌러 보면, 책방 E와 헌책방 나무의 배지는 명도가 비슷해 이름 없이는 구별하기 어렵다.

변형의 폭발

컴포넌트 하나에는 여러 변형(Variant)이 있다. 버튼이라면 크기(크게·보통·작게), 스타일(주·보조·텍스트·위험), 아이콘 자리(없음·앞·뒤), 그리고 WebBook 7장에서 정한 상태 다섯 가지(기본·호버·포커스·눌림·비활성)가 있다. 이 속성들은 서로 곱해진다.

$$N = \prod_{i} n_i = n_{\text{크기}} \times n_{\text{스타일}} \times n_{\text{아이콘}} \times n_{\text{상태}}$$
\(n_i\): 속성 \(i\)가 가질 수 있는 값의 수. 크기 3, 스타일 4, 아이콘 3, 상태 5이면 \(N = 3 \times 4 \times 3 \times 5 = 180\). 속성 하나에 값 하나를 더하면 전체가 그 속성의 비율만큼 늘어난다. 스타일 4 → 5이면 180 → 225.

180은 디자인 파일에 그려야 할 버튼 그림의 수이고, 개발자가 확인해야 할 조합의 수이며, 접근성 검사(대비, 포커스 표시)를 해야 할 경우의 수다. 여기에 책방 색 네 가지를 버튼 스타일로 넣으면 스타일이 4에서 8로 늘어 360이 된다. 속성 하나를 더하는 일은 늘 "하나만 더"처럼 보이지만, 곱셈이라 실제로는 전체를 몇 배로 만든다. 아래에서 속성의 값 수를 바꾸며 조합이 어떻게 불어나는지, 그리고 규칙 몇 줄이 그것을 어떻게 줄이는지 본다.

SIMULATOR

버튼 변형 계산기

변형 × 상태 5—
규칙 뒤 남는 조합—
한 번 검수하는 시간(가상)—
해볼 것: ① 처음 상태(크기 3, 스타일 8, 아이콘 3)의 조합 수를 본다. 스타일 8은 주·보조·텍스트·위험에 책방 색 넷을 더한 것이다. ② 스타일을 4로 줄인다. 책방 색을 버튼에서 뺀 것만으로 조합이 절반이 된다. ③ "규칙으로 줄이기"를 켠다. 크기는 두 단계, 스타일은 뜻으로 넷(주·보조·텍스트·위험), 아이콘은 "없음·앞" 둘, 텍스트 버튼과 위험 버튼은 기본 크기 하나만이라는 규칙이 적용되어 칸 대부분에 줄이 그어진다. ④ 규칙을 켠 채로 슬라이더를 끝까지 올려 본다. 값을 아무리 늘려도 규칙 밖의 조합은 쓰이지 않는다. 남는 조합의 수가 시스템이 실제로 책임지는 양이다. (검수 시간은 조합 하나를 디자인 파일·코드·대비·키보드로 확인하는 데 3분이 든다고 가정한 가상 값이다.)

규칙으로 줄인다는 것은 속성의 값을 뜻으로 이름 짓는다는 뜻이다. 버튼에 "색"이라는 속성을 열어 두면 누구든 아무 색이나 넣을 수 있고, 그 순간 조합은 무한해진다. 대신 "어조"라는 속성에 주·보조·텍스트·위험 네 값만 허락하면, 새 화면을 만드는 사람은 넷 중 하나를 고르기만 하면 되고, 시스템은 넷만 책임지면 된다. 개발자들은 이것을 컴포넌트의 속성(props)을 좁힌다고 말한다.

<!-- 열어 둔 속성: 조합이 무한하다 -->
<Button color="#c0392b" size="38px" iconRight="arrow">맡아 두기</Button>

<!-- 뜻으로 좁힌 속성: 네 가지 어조 × 두 가지 크기 × 아이콘 있음·없음 -->
<Button tone="primary" size="default" icon="bookmark">맡아 두기</Button>

속성을 좁히면 처음에는 불편하다는 말이 나온다. "딱 이 화면에서만 버튼을 조금 작게 하고 싶은데요." 그 불편이 바로 시스템이 하는 일이다. 작게 해야 할 이유가 정말 있다면 그 이유를 문서에 남기고 변형을 더하거나(그러면 모든 화면이 그 변형을 쓸 수 있다), 이유가 약하면 원래 크기를 쓴다. 어느 쪽이든 결정이 한곳에 남는다. 이 판단은 뒤의 "일관성과 예외" 절에서 다시 본다.

상태는 줄이지 않는다 조합을 줄이려고 할 때 가장 먼저 잘려 나가는 것이 상태다. "포커스 표시는 디자인 파일에 안 그려도 되겠지", "비활성 버튼은 그냥 흐리게." 그러나 상태는 장식이 아니라 손님에게 보내는 피드백이다(6장). 키보드로 쓰는 손님에게 포커스 표시가 없으면 화면 위 어디에 있는지 알 수 없다. 서윤은 크기와 스타일은 과감하게 줄이되, 상태 다섯 가지는 남은 모든 변형에 반드시 그리기로 했다. 줄일 것은 선택지이지 피드백이 아니다.

얼마나 달라야 다른 색인가: 색 감사

시스템을 처음 만들 때는 빈 종이에서 시작하지 않는다. 이미 화면들이 있고, 그 화면들에는 이미 쓰인 색과 간격과 버튼이 있다. 이미 쓰인 것을 모두 모아 늘어놓고 세어 보는 일을 인터페이스 목록 만들기(Interface inventory)라고 한다. 브래드 프로스트가 이름 붙여 널리 퍼진 방법이다. 모아 보면 대개 놀란다. 같은 초록이라고 생각한 색이 일곱 가지, 같은 크기라고 생각한 버튼이 다섯 가지쯤 나온다.

서윤은 시범 운영 화면과 네 책방이 각자 만들어 둔 안내문, 기존 웹사이트에서 쓰인 색 28개를 뽑았다(가상 자료). 이 가운데 무엇을 같은 색으로 묶고 무엇을 남길지는 "얼마나 달라야 다른 색인가"라는 문턱에 달려 있다. 아래에서 그 문턱을 ΔE2000으로 바꿔 본다. 묶음마다 가장 많이 쓰인 색이 대표(큰 칸)가 되고, 나머지(작은 칸)가 그 대표로 바뀐다.

SIMULATOR

색 감사: 28개의 색을 몇 개로

색의 수—
대표로 바뀌는 쓰임—
가장 크게 바뀌는 색—
해볼 것: ① 문턱 0에서 28개가 모두 따로 있다. 초록, 잉크, 크림, 빨강이 저마다 여러 벌이다. ② 문턱을 3 근처로 올린다. 눈으로 거의 구별되지 않던 쌍둥이 색들이 먼저 합쳐진다. 이 정도는 "실수로 생긴 차이"다. ③ 문턱을 8~10으로 올린다. 색이 열 개 안팎으로 줄어 토큰 후보 목록처럼 보인다. ④ 문턱을 20 이상으로 올린다. 책방 초록과 차분한 초록이, 크림과 종이색이 합쳐진다. 일부러 다르게 둔 색까지 지워지는 지점이다. (색과 쓰인 횟수는 가상 자료다. 묶기는 쓰인 횟수가 많은 색부터 대표로 세우고, 대표와의 ΔE2000이 문턱보다 작은 색을 그 대표에 붙이는 단순한 방법이다.)

문턱을 하나로 정해 기계적으로 묶으면 안 된다는 것이 이 시뮬레이터의 결론이다. 문턱 3 아래의 차이는 거의 늘 실수다. 누군가 색을 눈대중으로 다시 뽑았거나, 이미지 압축이 색을 조금 바꿨다. 문턱 20 근처의 차이는 대개 일부러 둔 것이다. 책방 초록 #2f7d5b과 차분한 초록 #567666은 ColorBook에서 서로 다른 일을 맡도록 고른 두 색이다. 그 사이의 회색 지대에서는 사람이 판단한다. 두 색이 같은 역할을 하는가, 다른 역할을 하는가. 같은 역할이면 하나로 합치고, 다르면 각자 의미 토큰을 준다.

탁자 위의 인터페이스 목록 자주 쓰는 앱이나 동네 가게의 웹사이트 화면을 열 장쯤 캡처해 인쇄한다(흑백이 아니라 색으로). 가위로 버튼만 모두 오려 탁자 위에 늘어놓고, 같은 버튼이라고 생각되는 것끼리 겹쳐 쌓는다. 쌓인 더미의 수를 센다. 다음에는 같은 방법으로 제목 글자를 오려 크기 순으로 줄 세운다. 크기가 몇 단계인지, 단계 사이가 일정한지 본다. 마지막으로 더미마다 "이 버튼은 무엇을 하라는 버튼인가"를 포스트잇에 적는다. 모양은 다른데 하는 일이 같은 더미, 모양은 같은데 하는 일이 다른 더미가 시스템이 정리해야 할 곳이다. 남의 서비스를 평가하려는 것이 아니라 목록 만들기의 감각을 익히려는 연습이다.

문서: 쓰는 법과 쓰지 않는 법

컴포넌트를 만들어 두기만 하면 사람들이 알아서 쓸 것 같지만, 그렇지 않다. 버튼이 있다는 것을 몰라서 새로 만들고, 있는 줄 알아도 언제 주 버튼을 쓰고 언제 보조 버튼을 쓰는지 몰라서 화면마다 다르게 쓴다. 컴포넌트 문서는 그 판단을 대신 내려 주는 곳이다. 아래 그림은 "맡아 두기"의 버튼 문서 한 쪽을 줄여 그린 것이다.

버튼 · Button 화면에서 손님이 할 수 있는 행동 하나를 시작한다 쓰는 때 화면의 가장 중요한 행동 하나 → 주 버튼은 화면에 하나 쓰지 않는 때 다른 화면으로 가기만 할 때 → 링크를 쓴다 변형과 상태 어조: 주 · 보조 · 텍스트 · 위험 크기: 기본 48 · 조밀 44 아이콘: 없음 · 앞 상태: 기본 · 호버 · 포커스 · 눌림 · 비활성 문구 규칙 하는 일을 쓴다(10장): "맡아 두기", "맡김 취소" 접근성 글자 대비 4.5 이상 · 포커스 테두리 · 누르는 자리 44 px 이상 바뀐 기록 v1.2 조밀 크기 v1.1 위험 어조 v1.0 처음 누가, 왜 바꿨는지
그림 2. 컴포넌트 문서 한 쪽. 모양보다 판단을 먼저 적는다. "쓰지 않는 때"가 "쓰는 때"만큼 중요하다. 변형 표에는 규칙 안에 남은 조합만 올리고, 문구 규칙은 10장의 원칙을, 접근성은 대비·포커스·누르는 자리를 적는다. 바뀐 기록은 나중에 "이건 왜 이렇게 됐지?"에 답한다. 버전 번호는 예시다.

좋은 컴포넌트 문서에 흔히 들어가는 것을 정리하면 다음과 같다.

칸무엇을 적나빠지면 생기는 일
이름과 한 줄 설명무슨 일을 하는 부품인가비슷한 부품이 다른 이름으로 또 생긴다
쓰는 때 · 쓰지 않는 때판단 기준과 대안(버튼 대신 링크 등)한 화면에 주 버튼이 셋이 된다
변형과 상태규칙 안에 남은 조합만규칙 밖의 조합이 "있는 것"처럼 쓰인다
문구 규칙어조, 길이, 동사(10장)같은 버튼이 "확인", "OK", "좋아요"로 갈린다
접근성대비, 포커스, 누르는 자리, 화면 낭독기 이름모양만 같고 키보드로는 못 쓰는 복사본
바뀐 기록버전, 날짜, 이유, 영향 받는 화면왜 그렇게 정했는지 아무도 모른다
하나의 원본 문서와 디자인 파일과 코드가 각자 버튼을 갖고 있으면 셋은 반드시 어긋난다. 디자이너가 디자인 파일의 버튼 모서리를 8에서 6으로 바꾸고 문서를 잊으면, 문서는 거짓말을 한다. 그래서 많은 팀이 하나의 원본(Single source of truth)을 정한다. 토큰은 한 파일에서 만들어 디자인 도구와 코드로 내보내고, 문서의 예시는 그림이 아니라 실제 컴포넌트 코드를 띄워 보여 준다. 작은 팀이라면 이렇게까지 자동화하지 않아도 된다. 대신 "토큰 값의 원본은 이 파일 하나"라고 정해 두고, 디자인 파일과 코드가 그 파일과 다를 때는 그 파일이 이긴다는 규칙만 지켜도 어긋남이 크게 준다.

일관성과 예외

시스템의 가치는 일관성에서 나온다. 같은 일은 같은 모양으로, 같은 자리에서 일어난다. 손님은 한 화면에서 배운 것을 다른 화면에서 다시 쓰고(6장의 멘탈 모델), 만드는 사람은 같은 판단을 매번 새로 내리지 않는다. 그러나 일관성이 목적이 되면 시스템은 감옥이 된다. 시집 전문 책방 한 줄의 소개 화면에서 시 한 구절을 세로로 쓰고 싶다는 요청을 "시스템에 세로쓰기 컴포넌트가 없어서 안 됩니다"로 막는다면, 시스템이 제품을 섬기는 것이 아니라 제품이 시스템을 섬기는 셈이다.

그래서 요청이 올 때마다 같은 질문을 같은 순서로 던지는 판단 나무를 둔다. 아래에서 네 가지 요청을 골라 나무를 따라가 본다. 질문 상자를 누르면 그 답을 뒤집을 수 있다. 답이 하나 바뀌면 결론이 어떻게 달라지는지 본다.

SIMULATOR

일관성과 예외의 판단 나무

질문 상자를 눌러 답을 바꾼다
결론—
시스템에 남는 것—
해볼 것: ① "작은 숲: 표지 크게"를 따라간다. 있는 책 카드로는 그림책 표지가 너무 작고(아니요), 책방 E도 그림책을 다루니 두 곳 이상에서 필요하며(예), 카드에 "표지 크게" 속성 하나를 더하면 된다(예). 결론은 변형 추가다. ② "한 줄: 세로쓰기"를 따라간다. 한 곳에서만 필요하지만 해가 없고 행사 기간으로 기한을 정할 수 있어 기록한 예외가 된다. ③ "어느 주인: 깜빡이는 빨간 버튼"은 위험 색과 겹치고 깜빡임이 일부 손님에게 해가 될 수 있어 다시 설계로 간다. ④ 아무 요청에서나 "두 곳 이상에서 필요한가?"를 눌러 답을 뒤집는다. 같은 요청도 쓰는 곳의 수에 따라 예외가 되기도, 시스템의 변형이 되기도 한다. (질문의 순서와 결론의 이름은 이 장이 정한 것이다. 팀마다 다른 나무를 쓴다.)

판단 나무에서 가장 중요한 갈림은 "두 곳 이상에서 필요한가"다. 한 곳에서만 필요한 것을 시스템에 넣으면 시스템이 불어나고, 여러 곳에서 필요한 것을 예외로 두면 같은 예외가 여러 벌 생긴다. 두 번째로 중요한 것은 예외에도 기록과 기한이 있다는 점이다. 한 줄의 세로쓰기는 "6월 시 낭독 주간 동안, 한 줄 소개 화면 머리에만"이라는 범위와 함께 예외 목록에 오른다. 기한이 지나면 지우거나, 그사이 다른 곳에서도 필요해졌다면 시스템으로 들인다. 기록 없는 예외는 몇 달 뒤 아무도 이유를 모르는 "원래 그랬던 것"이 된다.

마지막 갈림, "해가 없는가"는 다음 장(12장)으로 이어진다. 버튼을 깜빡이게 하자는 요청은 일관성의 문제이기 전에 손님을 압박하고 주의를 빼앗는 문제다. 깜빡이는 요소는 주의가 쉽게 흩어지는 손님에게 화면을 읽기 어렵게 만들고, WCAG 2.2의 2.2.2(일시 정지, 정지, 숨기기)는 5초 넘게 움직이거나 깜빡이는 내용을 멈출 수 있게 하라고 한다. 시스템은 이런 요청에 "규칙에 없어서"가 아니라 "이런 이유로"라고 답할 수 있어야 한다.

"아니요" 대신 "왜요?" 시스템을 맡은 사람은 요청을 거절하는 문지기가 되기 쉽다. 서윤은 요청이 오면 먼저 "그걸로 손님이 무엇을 할 수 있게 되나요?"를 묻기로 했다. 깜빡이는 빨간 버튼을 원한 주인의 진짜 걱정은 "손님이 맡기기 버튼을 못 찾는다"였다. 그렇다면 해답은 깜빡임이 아니라 버튼의 위치와 문구를 다시 보는 것이다(8장의 사용성 테스트로 확인할 수 있다). 요청의 겉모양이 아니라 그 밑의 문제를 시스템으로 풀면, 거절당한 사람도 결과에 납득한다.

기여와 거버넌스

시스템은 만든 날이 가장 깔끔하다. 그 뒤로 새 화면이 생길 때마다 시스템이 아직 다루지 못한 필요가 나타난다. 그때 만드는 사람에게는 두 길이 있다. 시스템에 새 부품이나 변형을 기여하거나, 자기 화면에만 쓸 것을 따로 만드는 것이다. 기여하는 길이 멀고 복잡할수록 사람들은 따로 만든다. 따로 만든 것은 다른 사람이 복사해 쓰고, 복사본이 또 복사된다. 시스템을 누가 어떤 절차로 바꾸고 지키는지에 대한 약속을 거버넌스(Governance)라고 한다.

아래 모형은 2년 동안 "맡아 두기"에 매달 새 화면 여덟 개가 생긴다고 가정한다. 시스템이 다루지 못하는 필요가 생기면, 기여 문턱이 높을수록 따로 만들 확률이 커진다. 따로 만든 변형은 다른 화면에 조금씩 복사되고, 정기 정리를 하면 그때마다 떠도는 변형의 일부가 시스템으로 들어오거나 지워진다.

SIMULATOR

떠도는 변형: 기여 문턱과 정리 주기

24개월 뒤 떠도는 변형—
시스템이 다루는 필요—
정리에 든 시간(가상)—
해볼 것: ① 처음 상태(문턱 높음, 정리 안 함)에서 떠도는 변형(주황 선)이 복사를 타고 가파르게 불어난다. ② 정리 주기를 3개월로 둔다. 톱니 모양으로 줄었다 늘기를 되풀이하며 수가 묶인다. ③ 정리를 끄고 문턱만 0.1로 낮춘다. 따로 만드는 일 자체가 줄어 떠도는 변형이 적고, 기여가 쌓여 시스템이 다루는 필요(초록 선)가 빨리 올라간다. ④ 문턱 0.1에 정리 6개월을 함께 둔다. 정리에 드는 시간도 가장 적다. 고치는 것보다 처음부터 기여하기 쉽게 하는 쪽이 싸다. (모든 수치는 가상 모형이다. 시스템이 처음에 필요의 60%를 다룬다고 두고, 기여 하나는 다루는 범위를 1%p씩 넓히며, 떠도는 변형은 매달 6%씩 복사되고, 정리 한 번에 떠도는 변형의 60%를 처리하며 하나에 30분이 든다고 가정했다.)

모형은 단순하지만 방향은 분명하다. 떠도는 변형을 줄이는 방법은 둘이다. 생긴 것을 주기적으로 치우거나, 애초에 덜 생기게 하는 것. 둘 다 필요하지만, 뒤쪽이 더 싸다. 기여 문턱을 낮춘다는 것은 기준을 낮춘다는 뜻이 아니다. 요청 양식을 한 장으로 줄이고, 누구에게 보내면 되는지 분명히 하고, 답이 언제 오는지 약속하는 것이다. 요청을 보내고 3주 동안 답이 없으면 사람들은 다음부터 묻지 않는다.

시스템을 누가 맡는지에도 몇 가지 모양이 있다. 디자인 시스템 컨설턴트 네이선 커티스(Nathan Curtis)가 2015년 글에서 정리한 구분이 자주 인용된다. 한 사람이나 한 팀이 자기 제품을 위해 만든 것을 남들이 가져다 쓰는 홀로 형, 시스템만 전담하는 팀이 있는 중앙 형, 여러 제품 팀의 사람들이 시간을 나눠 함께 맡는 연합 형이다. 어느 것이 옳다기보다 규모에 따라 다르다. 디자이너 한 명과 개발자 한 명, 주인 네 명인 "맡아 두기"는 홀로 형에서 시작할 수밖에 없다. 대신 네 주인이 "요청할 수 있는 사람"으로 절차 안에 들어온다.

요청서 한 장 분류 판단 나무 시험 시안·코드 문서 바뀐 기록 공개 알림 기록한 예외 범위·기한 기한이 오면 지우거나 두 곳 이상이면 위로 올린다 답은 2주 안에(약속) "맡아 두기"의 기여 절차. 각 단계의 이름과 기간은 이 케이스의 선택이다
그림 3. 요청에서 공개까지. 요청은 누구나 한 장으로 보낸다. 판단 나무로 분류해 변형 추가나 새 컴포넌트가 되면 시안과 코드로 시험하고, 문서와 바뀐 기록을 쓴 뒤 공개하고 쓰는 사람들에게 알린다. 예외가 되면 범위와 기한을 적어 따로 둔다.
바꾸는 것도 비용이다 시스템을 고치면 그것을 쓰던 모든 화면이 함께 바뀐다. 이것이 시스템의 힘이지만 위험이기도 하다. 버튼의 높이를 48에서 44로 줄이면 버튼이 들어 있던 모든 화면의 배치가 조금씩 움직이고, 어딘가에서 글자가 잘린다. 그래서 바뀐 것을 알리고, 큰 변경은 미리 예고하고, 어느 화면이 영향을 받는지 목록으로 확인한다. 많은 팀이 소프트웨어의 버전 번호 관례(고쳐도 쓰는 법이 그대로면 작은 번호, 쓰는 법이 바뀌면 큰 번호)를 시스템에도 쓴다.

서윤의 서비스 노트

CASE 서윤서비스 노트 · 11장

네 책방, 한 서비스, 강조색 한 칸

"맡아 두기"의 첫 화면들은 WebBook 때 만든 책방 E의 토큰을 그대로 가져다 썼다. 책방 초록 버튼, 크림 바탕, 짙은 잉크 글자. 시범 화면을 본 헌책방 나무 주인의 질문("우리 가게 화면인데 왜 책방 E 초록이에요?")이 이 장의 출발점이었다. 작은 숲 주인은 노란색을, 한 줄 주인은 도장의 인주 같은 빨간색을 원했다. 지후는 처음에 "책방마다 테마 파일을 따로 만들면 되죠"라고 했다가, 버튼 변형이 몇 개가 되는지 이 장의 계산기로 세어 보고 말을 바꿨다. "스타일 여덟 개면 버튼만 360가지예요. 저 혼자 다 못 봐요."

서윤은 먼저 주 행동의 색을 책방 E의 초록에서 떼어 냈다. 네 책방이 함께 쓰는 서비스의 버튼이 한 가게의 색을 달고 있을 이유가 없었다. 주 버튼은 손님 화면의 "맡아 두기"부터 6장에서 책방 초록으로 칠했던 주인 화면의 "수락"까지 모두 잉크색 #22342a(흰 글자 대비 13.19)로 바꾸고, 의미 토큰 action.primary가 그것을 가리키게 했다. 그리고 책방마다 다르게 둘 자리를 shop.accent 한 칸으로 정했다. 이 색은 책방 이름 배지와 책방 소개 화면 머리의 얇은 띠에만 쓴다. 세 조건(배지 글자 대비 4.5 이상, 위험 색과 ΔE2000 15 이상, 다른 책방 색과 15 이상)을 걸고 네 주인이 원한 색을 넣어 보니 셋은 그대로 통과했다. 작은 숲의 노랑은 흰 글자로는 1.68이었지만 배지 글자를 잉크색으로 자동으로 바꾸자 7.87이 되었다. 한 줄의 빨강은 오류 표시와 ΔE 6.7로 겹쳤다. 서윤이 시뮬레이터에서 색상각을 돌려 보이자, 한 줄 주인은 시집 표지에 자주 쓰던 먹자주 #6b2f4a를 골랐다(흰 글자 9.86, 위험 색과 ΔE 30.1).

하린은 색보다 이름을 먼저 챙겼다. "배지에 색만 있고 이름이 없으면, 흑백으로 인쇄한 주문 목록에서는 구별이 안 돼요." 그래서 배지에는 늘 책방 이름을 쓴다. 색 감사에서는 기존 화면과 안내문의 색 28개(가상 자료)를 ΔE 3 아래에서 기계적으로 합치고, 그 위는 역할을 따져 사람이 정했다. 마지막으로 컴포넌트 목록과 변형 규칙, 요청 절차를 한 장에 적어 네 주인에게 나눠 주었다.

항목서윤의 선택확인한 값
주 행동 색action.primary → 잉크 #22342a(책방 E 초록에서 분리). 손님 "맡아 두기", 주인 "수락" 모두흰 글자 대비 13.19
책방별 한 칸shop.accent: 배지와 머리 띠에만. 버튼·링크·상태·오류에는 쓰지 않음배지에는 책방 이름 글자를 함께
책방 E#2f7d5b, 배지 글자 흰색4.99 · 위험 색과 ΔE 55.0
헌책방 나무#8b5a2b, 흰색5.84 · ΔE 19.6
작은 숲#f2c230, 잉크색 글자(자동)7.87 · ΔE 42.2
한 줄#6b2f4a 먹자주(처음 원한 #c0392b에서 바꿈)9.86 · ΔE 30.1
컴포넌트(9)버튼, 검색 칸, 책 카드, 재고 줄, 상태 배지, 책방 배지, 토스트, 확인 대화상자, 빈 상태모두 문서 한 쪽씩
버튼 변형 규칙어조 4(주·보조·텍스트·위험) × 크기 2(기본 48 · 조밀 44) × 아이콘 2(없음·앞), 텍스트·위험은 기본 크기만, 상태 5는 모두버튼 조합 60(규칙 없을 때 360)
이 장에서 정한 것공통 토큰과 책방별 강조색 한 칸. 주 행동 색은 잉크 #22342a로 네 책방이 같이 쓰고, 책방마다 다른 것은 shop.accent 하나(책방 E #2f7d5b, 헌책방 나무 #8b5a2b, 작은 숲 #f2c230, 한 줄 #6b2f4a)다. 강조색의 조건은 배지 글자 대비 4.5 이상(글자색은 흰색과 잉크색 중 자동), 위험 색 #d24f39과 ΔE2000 15 이상, 다른 책방 색과 15 이상. 컴포넌트는 아홉 개, 버튼 변형은 어조 4 × 크기 2 × 아이콘 2에 텍스트·위험 버튼은 기본 크기만(상태 다섯은 빠짐없이). 새 변형은 두 곳 이상에서 필요할 때만 더하고, 한 곳만의 요청은 범위와 기한을 적은 예외로 둔다. 요청은 누구나 한 장으로 보내고 2주 안에 답한다. 한 줄 소개 화면의 세로쓰기 시구가 첫 번째 기록한 예외다.
아직 남은 문제시범 운영에서 한 주인이 "알림 받기"를 처음부터 체크해 두고, 기한이 지나면 다음 손님에게 넘어간다는 규칙은 작게 숨기자고 했다. 시스템의 확인 대화상자와 체크 상자는 그것을 막지 못한다. 부품은 어떻게 쓰이느냐에 따라 손님을 돕기도, 속이기도 한다. 손님의 전화번호를 얼마나 오래 갖고 있을지도 아직 정하지 않았다.
다음 단계12장: 다크 패턴과 윤리. 동의와 개인정보, 취소의 쉬움을 정한다.

핵심 정리

  1. 디자인 시스템은 부품 모음이 아니라 결정을 한 번 내리고 나눠 쓰는 방법이다. 원시 토큰 → 의미 토큰 → 컴포넌트 → 패턴의 층과, 문서와 운영 규칙으로 이루어진다.
  2. 의미 토큰은 값과 뜻을 떼어 놓는다. 컴포넌트가 의미 토큰만 읽으면 결정 하나를 한 곳에서 바꿀 수 있고, 다크 모드나 브랜드별 테마도 의미 토큰이 가리키는 곳을 바꾸는 일이 된다.
  3. 여러 브랜드를 담을 때는 바꿀 수 있는 자리를 좁게 정한다. 정체성(어디인가)과 조작(무엇을 누르는가)을 같은 색에 맡기지 않고, 바꾼 값에는 대비와 구별의 조건을 건다. 색만으로 구별하게 하지 않는다.
  4. 변형의 수는 속성의 곱 \(N = \prod n_i\)으로 늘어난다. 속성을 뜻으로 이름 짓고 값을 좁히면 조합이 크게 준다. 줄일 것은 선택지이고, 상태(피드백)는 줄이지 않는다.
  5. 색 감사는 이미 쓰인 것을 모아 묶는 일이다. 아주 작은 차이는 실수로 보고 합치되, 일부러 둔 차이는 역할을 따져 사람이 판단한다.
  6. 요청은 같은 질문 순서로 판단한다. 있는 것으로 되는가, 두 곳 이상에서 필요한가, 속성 하나로 되는가, 해가 없는가. 예외에도 범위와 기한을 적는다. 떠도는 변형은 정기 정리보다 기여 문턱을 낮추는 쪽이 더 싸게 막는다.

확인 퀴즈

1. 버튼 컴포넌트가 원시 토큰 ink-900을 바탕색으로 직접 읽고, 본문 글자도 ink-900을 직접 읽는다. "주 버튼만 조금 밝게" 하라는 결정이 나왔을 때 생기는 문제로 가장 알맞은 것은?

원시 값을 직접 읽으면 같은 값이 서로 다른 뜻(버튼 바탕, 본문 글자)으로 쓰여도 구별할 수 없다. 값 하나를 바꾸면 뜻이 다른 쓰임까지 함께 바뀐다. 의미 토큰 action.primary를 두고 버튼이 그것을 읽게 하면, 그 토큰이 가리키는 곳 하나만 바꿔 결정이 끝난다.

2. 버튼에 크기 3, 스타일 4, 아이콘 자리 3, 상태 5가 있다. 여기에 책방 색 넷을 스타일로 더하면 조합은 몇 개가 되는가?

처음에는 \(3 \times 4 \times 3 \times 5 = 180\)이다. 스타일이 4에서 8이 되면 \(3 \times 8 \times 3 \times 5 = 360\)으로 두 배가 된다. 속성의 값은 더해지는 것처럼 보이지만 전체에는 곱해진다. 184는 180에 4를 더한 것이고, 720은 한 번 더 두 배로 센 것이다.

3. 한 책방이 자기 브랜드 색인 빨강 #c0392b를 강조색으로 쓰고 싶어 한다. 흰 글자 대비는 5.44로 4.5를 넘는다. 이 장의 규칙으로 볼 때 가장 알맞은 판단은?

대비는 읽힘의 조건일 뿐이다. 이 서비스의 위험 색 #d24f39과 ΔE2000이 6.7로, 이 장이 정한 구별 문턱 15에 크게 못 미친다. 색 심리에 대한 일반론으로 금지하는 것이 아니라 이 서비스 안에서 색이 맡은 역할이 겹치기 때문이다. 이름을 지우는 것은 색만으로 정보를 전하게 되어 거꾸로 간다.

4. 시집 책방 한 곳이 6월 행사 동안 자기 소개 화면 머리에만 시구를 세로로 쓰고 싶어 한다. 다른 책방에서는 필요하지 않다. 판단 나무에 따른 처리로 가장 알맞은 것은?

한 곳에서만 필요한 것을 시스템에 넣으면 시스템이 불어나고, 규칙에 없다고 막으면 시스템이 제품을 섬기지 않게 된다. 해가 없고 범위가 좁으면 예외로 두되, 범위와 기한을 기록해 나중에 지우거나(기한이 지나면) 다른 곳에서도 필요해지면 시스템으로 들인다. 기록 없이 따로 만들면 떠도는 변형이 된다.