(번역) 상태들의 참담한 상태

TapK·2025년 7월 22일
9
post-thumbnail

오늘날의 피그마 에셋은 디자인이 코드와 더 잘 어울릴 수 있음을 보여줍니다.

원문: https://medium.com/@nathanacurtis/the-sorry-state-of-states-89dd4668737e
저자: Nathan Curtis

몇 년 전, 저는 컴포넌트 API 함께 만들기라는 글을 썼습니다. 피그마는 헤게모니적1인 디자인 도구로 자리 잡았고, 유용하며 사용하기 쉬운 API로 컴포넌트를 구성하는 능력은 계속해서 강화되고 있습니다. 그로 인해 라이브러리는 더 깊숙이 자리 잡았습니다.

하지만 최고의 시스템 설계자조차도 코드가 실제로 작동하는 방식을 모방할 기회를 놓치고 있습니다. 텍스트 입력, 텍스트 영역, 드롭다운, 체크박스, 라디오 버튼과 같은 많은 상호작용 요소의 상태 state 속성의 안타까운 상태를 보면 이를 더 명확히 알 수 있습니다.

이 글에서는 디자인 시스템 설계자가 코드 작동 방식과 일치하지 않는 피그마 에셋의 states를 어떻게 설계하는지 살펴봅니다. 텍스트 입력 예시는 옵션 조합의 부분 집합과 상호 의존적인 속성부터 불리언과 열거형 옵션에 이르기까지 순수하게 피그마 에셋을 모델링하기 위해 어느 정도까지 가야 하는지(또는 가지 말아야 하는지)에 대한 교훈을 제공합니다.

오늘날 상태들의 상태

상태는 디자이너와 개발자가 다양한 상황에 적용하는 일반적인 용어입니다. Eric Bailey가 정리한 사용자 중심의 상태리스트는 이를 잘 보여줍니다. 리스트에는 rest, hover, active 상태부터 disabled, readonly, selected, deselected, loading, ghost origin, dirty와 같이 덜 일반적인 고려 사항까지 총 38가지가 포함됩니다.

[Eric Bailey의 모든 사용자 표시 상태 예시에서 발췌한 내용입니다.]

호기심 많은 크롬 사용자들은 크롬 인스펙터를 통해 텍스트 입력을 선택하고 마우스 오른쪽 버튼을 클릭하여 검사를 선택하면 프런트엔드 개발자가 CSS 조합으로 활용할 수 있는 강력한 HTML 양식 유효성 검사 후크(:disabled, :valid, :invalid, ...)를 포함한 다양한 요소 상태(:active, :hover, :focus, ...)를 확인할 수 있습니다.

[특정 요소 상태를 포함한 텍스트 입력에 대한 크롬 인스펙터의 상태]

문제는 상태라는 용어가 지나치게 일반적이라는 점입니다. 가능한 모든 상태가 대안인 것은 아닙니다. 대신, 사용할 수 있는 상태는 논리적 관계에 따라 상호 의존적인 관심사가 뒤섞여 조합되어 사용되는 경우가 많습니다.

그러나 디자인 비평 토론, 디자인 명세서 섹션 헤더, 특히 피그마 에셋 속성에서 사용될 때 state는 충분히 모델링되지 않은 아이디어를 아우르는 훌륭한 만능 해결책이 되어버립니다.

텍스트 입력의 경우 당연히 hoveractive를 표시합니다. 이를 감안할 때 첫 번째 base(안 함), default(괜찮음), enable 또는 initial(더 좋음) 또는 rest(최선?)을 호출할지 잠시 멈추고 고민해 보겠습니다. 일단, resting으로 생각해 봅시다. 그리고 얼른 다음 단계로 넘어가 보겠습니다.

이러면 복잡한 문제가 발생합니다. disabled은 어떻게 하나요? 물론 필요합니다. readonly는요? 아, 그건 생각지도 못했네요. error 상태는 당연히 필수입니다. states 집합은 점차 커지다가 ‘충분히 포착했다’고 느낄 때 안정화됩니다. 디자이너가 모든 “Eric Bailey 상태”를 다 해결하지 못해도 용서받을 수 있습니다. 저도 전부 다 다뤄본 적은 없어요. 그 목록은 정말 끝도 없이 길거든요!

오늘날 피그마 에셋의 상태

다음은 피그마 커뮤니티에 게시된 디자인 시스템 중 Text input / Input / 기타 피그마 컴포넌트를 살펴보겠습니다.

[비교가 가능하도록 상태와 관련된 속성만 포함하도록 정규화 및 단순화된 Atlassian, Github 및 Shopify의 텍스트 입력 피그마 컴포넌트]

state 속성 패턴은 제가 컨설팅하는 팀에서 관찰한 것과 일치했습니다.

  • Hover: 10개 중 6개가 state:hover(또는 state:hovered) 옵션을 지원합니다.
  • Active: 2개는 state:active 옵션을 지원합니다.
  • Focus: 10개 중 8개가 state:focus를 지원하며 별도의 불리언 속성으로 focus를 제공하는 것은 없습니다.
  • Disabled: 10개 중 9개가 state:disabled를 지원하며, 단 하나(Atlassian)만이 별도의 불리언 속성으로 isDisabled를 구분합니다.
  • Read only: 10개 중 6개가 state:readonly을 지원하며, readonly을 불리언 속성으로 제공하는 것은 없습니다.
  • ErrorSuccess: 10개 중 8개가 state:error(또는 유사한 이름의 유효성 검사) 옵션을 지원하며, 1개(Atlassian)는 오류를 별도의 불리언 속성으로 제공했습니다.

컴포넌트에는 때때로 warning, skeleton, typing과 같은 다른 state 옵션이 포함되었습니다. 심지어 여기서는 다루지 않는 valueplaceholder 속성의 인접 상태 문제를 다루는 filled도 포함되었습니다.

어떤 피그마 에셋도 readonly + focus 또는 hover + error와 같은 그럴듯한 상태 조합을 지원하지 않았습니다.

오늘날 코드 라이브러리의 상태

해당 라이브러리의 텍스트 입력 컴포넌트 코드를 유사하게 검토한 결과 다른 일반적인 모델이 도출되었습니다.

  • HoverActive: 대부분의 코드 라이브러리는 사용자 상호작용에 반응하는 상태로 암시적으로 구현합니다.
  • DisabledRead only: 거의 모든 코드 라이브러리가 이러한 속성을 구현하며, 두 속성을 모두 기존 속성으로 간주할 수 있습니다.
  • ErrorSuccess: 많은 코드 라이브러리에서 error(또는 inInvalid, hasError 또는 이와 유사한) 불리언 프로퍼티를 구현하여 오류를 표시합니다. GitHub Primer는 errorsuccess을 구분하기 위해 이진수에서 열거형 validationStatus 프로퍼티로 확장합니다. 일부 코드 라이브러리에서는 error 텍스트에 대한 오류 프로퍼티를 구현합니다.

Shopify Polaris의 리액트 컴포넌트는 disabledreadonly을 위한 타입스크립트 인터페이스를 구현하여 컴포넌트 간에 의도적인 규칙을 제안합니다. 이 라이브러리는 또한 강제 focused을 구현합니다.

[오늘날 에셋이 작동하는 방식과 코드에 맞춰 조정된 경우의 작동 방식을 비교하기]

디자인과 코드는 다릅니다. 그래서 뭐가 문제일까요?

증거는 분명합니다. hoveractive와 같은 상호 작용 상태를 넘어, 피그마 에셋과 코드 컴포넌트는 뚜렷한 API 시그니처를 제공합니다. 사실, 좀 더 정확하게 말하자면, 제품 디자이너가 재사용을 위해 공개한 디자인 에셋은 제품 개발자가 사용하는 코드 에셋과 다릅니다.

시스템 디자이너가 시스템 개발자에게 넘겨준 디자인 솔루션이 코드에서 일어난 일과 완벽하게 일치할 수도 있습니다. 하지만 제 경험에 따르면 그렇지 않습니다. 디자인 이터레이션과 사양(종종 EightShapes Specs 플러그인으로 자동화됨)은 거의 항상 같은 "상태로 여러 가지 복잡한 문제를 동시에 다루는" 접근 방식을 드러냅니다. 왜 신경 써야 할까요?

효율성, 사용성 및 만족도에 미치는 부정적 영향

최소한 대부분의 디자이너는 부정확하고/또는 불완전한 디자인 전달의 일반적인 함정을 알고 있습니다. 이는 다음과 같은 문제를 초래합니다.

  1. 제품 디자이너가 코드와 다른 모델을 사용하는 공개된 피그마 에셋을 사용하여 제품 개발자에게 전달할 때 발생하는 마찰.
  2. 피그마 에셋의 잘 정리되지 않은 속성은 사용하기 어렵고, 실제로는 독립적인 여러 가지 선택 사항이 하나 또는 몇 개의 속성가 무질서하게 흩어져 있습니다.
  3. 디자이너가 컴포넌트의 속성을 효과적으로 모델링하는 능력이 부족하거나 부적절하다고 판단하는 개발자들의 지속적인 불신과 무례함.

마지막 부분은 아프네요. 때로는 정당화될 수 있는 "내 물건에 손대지 마"라는 태도를 지속시키는 것이죠. 일부 개발자들은 여전히 그런 태도를 고수하지만, 대신 함께 컴포넌트 API를 설계하는 데 집중할 수 있었을 텐데요. 그건 다른 날에 논의할 주제입니다.

자동화에 미치는 영향을 최소화하기

일부 잘 보이지 않지만, 현재 제 협력자 대부분에게 훨씬 더 중요한 것은 피그마의 API를 활용하여 시스템 표면의 점점 더 많은 영역을 자동화하고 정리하면서 미치는 부정적인 영향입니다. 부적절하게 모델링된 속성은 다음과 같은 문제를 야기할 수 있습니다.

  1. 디자인과 코드 라이브러리 간의 정합성을 점검하고 리포팅하는 자동화 기회를 제한하게 됩니다.
  2. 에셋을 데이터로 활용하여 정보를 제공하거나, 단일 진실의 원천(source of truth)으로 삼거나, 심지어 코드 개발을 자동화하는 능력 또한 제한됩니다.
  3. 애초에 피할 수 있는 Figma Code Connect 같은 도구에서 복잡하고 깨지기 쉬운 변환을 매핑하는 데 드는 비용이 증가할 수 있습니다.

2021년에 이 주제에 대해 작성한 이후로, 디자인을 포함한 라이브러리 간 API 일치에 대한 제 믿음은 더욱 확고해졌습니다. 제가 작업하는 시스템과 마찬가지로 많은 존경받는 디자인 시스템이 코드와 불필요하게 이탈하는 것을 볼 때, 우리는 모두 더 나은 방법을 찾아야 합니다. 이 문제는 사실 어렵지 않습니다.

모델링 프로퍼티 조합 및 상호작용

이제 단계별로 자세히 살펴보며 상태 기반 프로퍼티와 그 작동 방식을 이해해 보겠습니다. 먼저 라디오 버튼과 체크박스에서 볼 수 있는 selectedstate의 자연스러운 조합 특성을 살펴보겠습니다. 그다음 텍스트 입력 필드의 disabled, readonly, 검증 상태를 통해 프로퍼티가 어떻게 조합되고 상호작용하는지 배워보겠습니다.

완전한 조합 집합

일부 유형의 상태는 결합되어 관련 조합의 완전한 집합을 형성합니다. 예를 들어, CheckboxRadio button은 상호작용 state(rest, hover, active, focus)와 selected 상태(not selected, selected)를 결합하여 8개의 조합을 형성합니다.

다음 표는 stateselected 항목에 대해 모든 조합이 존재하며(✅로 표시됨) 각각 고유한 시각적 디자인이 필요할 수 있음을 보여줍니다.

[상태 대 선택 항목 조합 표]

피그마 컴포넌트 내 Radio button 상태에 대해, hover, active, focusselected 상태와 같은 옵션을 포함하는 state 속성을 추가할 수 있습니다. 그러나 이는 selectedhover가 동시에 적용되는 경우와 같이, 가능한 조합을 제외하게 됩니다. 이러한 조합은 hover selected과 같은 추가 state 옵션으로 추가할 수 있지만, 복합 옵션 이름을 통해 모든 조합을 제공하는 위험한 경로로 빠질 수 있습니다. selected 상태(옵션이 truefalse일지라도 시각적 디자인이 다름)와 state를 구분하여 별도의 피그마 변형 속성을 사용하는 것이 더 좋습니다.

[라디오 버튼을 별도의 프로퍼티로 선택하는 것과 상태 프로퍼티의 옵션으로 선택하는 것]

체크박스의 세 번째 checked 상태인 불확정(indeterminate) 상태의 가능성은 12가지 가능한 조합을 생성하며, 이는 복합 용어의 열거 상태 목록을 덜 선호되는 것으로 만들어집니다. 또한, 체크 속성은 이진 변형에서 not checked(기본값), indeterminatechecked의 열거된 옵션을 표시하는 것으로 변경됩니다.

[체크박스에서 선택된 상태를 별도의 프로퍼티로 설정하는 것과 상태 프로퍼티의 옵션으로 설정하는 것의 차이]

Eric Bailey의 상태 목록을 자세히 검토할 만큼 용감한 분들은 Deselected와 같은 더 관련성 있는 고려 사항을 발견할 수 있을 것입니다. 대부분의 팀에게는 Deselected(Selected에서 변경된 상태)를 Rest(선택되지도 않았고 상호작용도 되지 않은 상태)와 구분하여 포함하는 것은 실용적이지 않습니다.

부분 조합 집합

disabled 속성은 true 또는 (기본값으로) false로 설정된 변형 집합입니다. 비활성화된 컨트롤은 기본적으로 rest 상태이며, 시각적으로 구분되며 hover, activefocus 상태는 관련이 없습니다.

[상태별 비활성화 조합 표]

결과적으로 디자이너는 disabled 상태를 상호작용 state에서 분리할지, 아니면 disabled 상태를 state의 옵션으로 포함할지 선택할 수 있습니다. 두 선택 모두 피그마 에셋이 rest 상태가 아닌 비활성화 상태를 구현하지 않는 한, 가능한 8가지 조합 중 5가지의 부분 집합이 됩니다.

[텍스트 입력 컴포넌트에서 disabled를 별도 속성으로 처리할지, state 속성의 일부로 통합할지에 대한 비교]

disabled 옵션을 상태 state 범주에 끼워 넣는 게 편할까요? 물론, 쉬워 보일 수는 있습니다. 하지만 코드란 그렇게 작동하지 않습니다. 그리고 disabledstate로 흡수하면, 모델의 명확성과 의미가 떨어질 뿐만 아니라, 곧 보게 되겠지만 다른 관계들을 제대로 모델링하는 데에도 제약이 생깁니다. (말장난이지만 정말로 disabled 됩니다.)

상호 의존적인 속성

readonly 속성은 코드에서 일반적으로 불리언 값으로 사용되는 속성입니다. disabled 속성과 마찬가지로 readonly는 기본적으로 false로 설정됩니다. 그러나 disabled와 달리 readonlyrestfocus를 지원하지만 일반적으로 hoveractive는 지원하지 않습니다.

[상태별 비활성화 및 읽기 전용 조합 표]

또한, disabledreadonly는 동시에 발생하지 않습니다. disabledreadonly가 모두 true로 설정되면 disabled가 우선순위를 가지며 readonly는 무시됩니다. 이로 인해 이 두 속성은 상호 의존적입니다.

디자이너가 7가지 유효한 조합을 모두 구현하면 피그마는 이 경우를 충분히 잘 처리합니다. 우선순위를 고려할 때 disabled를 먼저 설정하고, 그다음 readonly, 마지막으로 state를 설정해야 합니다. 컴포넌트 사용자가 disabledfalse로 설정하면 컴포넌트는 다른 staterest 상태로 복원합니다.

[읽기 전용 텍스트 입력 필드를 별도의 프로퍼티로 설정하는 것과 상태 프로퍼티의 옵션으로 설정하는 것의 차이]

disabledreadonly를 모두 state 속성의 옵션으로 통합할 수 있을까요? 물론 가능합니다. 하지만 여러 속성의 차원을 하나의 복합적인 이름을 가진 속성으로 결합하는 방식은 코드 구조와 잘 맞지 않고, 확장성에도 한계가 있습니다.

열거형 또는 불리언 속성 선택

에러와 성공 같은 검증 상태는 더 많은 의문을 불러일으킵니다. 반면 disabledreadonly는 기분 좋을 정도로 이진적(binary)이며, true 또는 false 값으로 간단히 표현할 수 있습니다 (피그마의 variant 속성으로 구성하더라도 마찬가지입니다).

일부 디자인 시스템은 에러 상태만을 제공하는데, 이 경우 익숙한 선택지가 다시 등장합니다. 즉, error variant 속성을 제공하고, 기본값인 false 또는 true로 설정 가능하게 하는 방식을 추천하는 것입니다.

아래 조합 표는 흥미로운 질문을 제기합니다. error: true인 입력 필드에 readonly: true 또는 disabled: true를 동시에 설정할 수 있을까요? 형식주의자(purist)의 관점에서 보자면, 아마도 가능할 것입니다.

[상태별 오류, 비활성화, 읽기 전용 조합표]

하지만 HTML에서 error(무효?)와 disabled 또는 readonly 속성이 결합된 경우 다음과 같은 더 심각한 고려 사항이 발생합니다.

  • 상충되는 시각적 신호: error 상태인지, disabled 상태인지 어떻게 구분할까요?
  • 상충되는 해결 방법: 입력값이 error 상태라면 사용자는 해당 입력값과 상호작용하여 문제를 해결할 수 있어야 합니다. disabled 상태라면 그렇지 않습니다.

이는 API 디자인 문제라기보다는 사용자 경험 디자인 문제입니다. 지침에서는 사용자에게 필요하고 직관적이지 않은 경우 이 조합을 피하고 다른 UI 패턴을 사용하도록 권장할 수 있습니다. 그러나 피그마 컴포넌트와 이를 구현하는 개발자에 대한 관련 커뮤니케이션의 경우, 두 가지가 모두 true로 설정된 경우 어떤 일이 발생하는지 명확히 하기 위해 명시적인 에셋이 아니더라도 최소한 대화가 필요합니다.

속성은 입력값이 error 상태(일반적으로 빨간색과 X 아이콘을 사용)와 반대되는 succuess 상태(녹색과 체크마크 아이콘을 결합하는 경우가 많음)를 구현할 경우 더욱 발전합니다.

이 경우, error(또는 무효)는 성공(또는 invalid)과 상호 배타적이며, 시각화된 성공 상태는 error의 빨간색이나 성공의 녹색을 표시하지 않는 것과 다릅니다. 따라서, none(기본값), error성공 옵션을 가진 열거형 검증 변수 속성을 고려하십시오.

독립적인 속성으로 둘 것인가, 아니면 다른 속성의 옵션으로 포함시킬 것인가

지금까지는 focusrest, hover, active와 같은 상태들과 상호 배타적인 선택지로 간주되어 왔습니다. 하지만 실제로는 하나의 요소가 hover+focus, 혹은 active+focus 상태를 동시에 가질 수 있습니다.

focus(true/false)를 상호작용 state(rest, hover, active)와는 별개의 속성으로 분리해야 할까요?

[상태별 비활성화, 읽기 전용, 포커스 조합표]

앞선 섹션들을 통해 순수주의적 사고로 전환된 디자이너는, 우려를 과도하게 분리하려는 시도 끝에 focus 변형 속성을 따로 만들고 기본값을 false로 설정하려 할 수 있습니다. 하지만 이는 지나친 접근일 수 있습니다.

실용적인 관점에서 볼 때, focus 속성을 상태(state) 속성과 분리하는 것은 실제로는 꼭 필요하지 않습니다. 그 이유는 다음과 같습니다.

  • Focus는 거의 항상 클라이언트 측 상호작용에 의해 발생하며, 개발자가 설정하는 속성이 아닙니다. focushover와 마찬가지로 사용자 상호작용에 따라 발생하는 인터랙티브 상태입니다. 일부 라이브러리에서는 focus를 속성으로 강제로 설정할 수 있도록 지원하지만, 대부분의 개발자는 disabled, readonly, error처럼 focus를 명시적으로 설정할 필요를 느끼지 않습니다.

  • Focus는 보통 고유한 시각적 속성에 영향을 줍니다. focus는 일반적으로 hoveractive와는 구분되는 시각적 속성(예: 컨테이너의 그림자 또는 별도의 focus ring)을 변화시킵니다. 반면, hoveractive는 보통 배경색이나 테두리 색상에 영향을 주는 경우가 많습니다.

  • 그렇지 않은 경우에도 focus는 동일한 시각적 속성에서 우선순위를 갖습니다. 만약 focushover와 마찬가지로 컨테이너의 테두리 두께나 색상을 변경한다고 하더라도, 일단 요소가 focus 상태라면 hoveractive의 약한 시각적 효과는 거의 또는 전혀 중요하지 않게 됩니다. 즉, focushover보다 우선시되며, 이는 disabledreadonly보다 우선시되는 것과 같은 맥락입니다. 또한 focus+hover가 동시에 적용될 때 이를 위한 별도의 고유한 스타일이 필요한 경우는 거의 없습니다.

[크롬의 인스펙터에서 호버, 활성화, 및 다중 포커스 상태가 집합 형태로 그룹화되어 있습니다]

  • 일반적으로 하나의 집합으로 여겨집니다. 이 세 가지는 매우 밀접하게 관련된 CSS 가상 클래스로, 그들의 존재를 통해 제시되고 일반적으로 추구됩니다. focus을 구분하려면, hoveractive를 구분하는 것도 좋을 것입니다. 그 과정에서, focus-within를 더욱 구분할 때가 되었을까요? 아직 그걸 코딩하는 개발자를 만난 적도 없고, 더구나 그 문제를 해결하는 디자이너도 본 적이 없습니다. 우리는 모두 언젠가 위대함을 추구할 수 있을까요?

검토한 코드 예제들 대부분에는 focused 속성이 존재하지 않았습니다. 오직 Shopify Polaris만이 focused 속성을 제공하여 강제로 포커스 상태를 설정할 수 있도록 했습니다. 그 목적을 추측해 보자면, 아마도 input 내부에 슬롯으로 삽입된 요소와의 상호작용이 부모 컨테이너에 포커스 상태를 강제로 부여해야 할 필요가 있어서일 수 있습니다. 또한 Shopify Polaris의 focused 속성은 사용자 상호작용으로 발생하는 일반적인 포커스 상태와는 별개로, 이를 추가적으로 활성화할 수 있는 방식으로 작동합니다. 즉, 이 속성을 설정하면 실제 포커스 여부와 관계없이 포커스 스타일이 적용되며, 상호작용에 의한 focus와 병행될 수 있습니다.

focus: true,false로 따로 분리해 상호작용 state: rest,hover,active와 같이 별도로 관리해야 한다는 주장에는 아직 설득되지 않습니다. 물론, 협업이 실패하지 않는 한 (그럴 일은 없겠지만), 또는 라이브러리 간의 자동화가 그것을 필요로 하기 전까지는 말이죠 (언젠가는 가능성 있습니다).

이 지점에서 멈추는 것은 좋은 시사점을 줍니다. 완벽한 정렬, 즉 디자인을 구성하기 위한 완벽한 모델을 추구하는 과정은 종종 그 완벽함에 도달하기도 전에 동력을 잃습니다. 3년 전의 그 블로그 글만 보더라도, 디자인 에셋과 코드 에셋 간의 state가 서로 다르게 유지되는 것에 대해 비교적 관대했습니다.

하지만 오늘날에는 훨씬 더 깊은 통합과 강한 정렬을 지향하는 방향으로 기준이 이동해 왔습니다. 이것은 수많은 팀들이 디자인 시스템을 구축하며 수년에 걸쳐 밟아가는 여정과 다르지 않습니다. 언젠가는 서로 수렴하게 될지도 모릅니다. 어쩌면 머지않아 말이죠.

각주

1: 헤게모니 - 어떤 집단이나 국가가 다른 집단이나 국가를 지배하거나 주도하는 위치에 있는 상태를 의미

profile
누구나 읽기 편한 글을 위해

2개의 댓글

comment-user-thumbnail
2025년 7월 31일

This translation sheds light on the political and economic instability affecting various states, exposing systemic issues and public frustration. Just like how the El Pollo Loco menu offers a range of bold choices reflecting different tastes https://elpollolocomenus.net/ , the piece reveals how each state’s condition stems from unique policy “ingredients.” A sobering, insightful read that highlights the cost of long-term mismanagement.

답글 달기
comment-user-thumbnail
2025년 7월 31일

This reflection on the grim state of various conditions highlights deeper societal or structural issues. In contrast, Shadow Fight 2 Mod APK https://fightshadow2apk.com/ offers a more controlled escape a fully unlocked game where players can overcome chaos with skill and strategy.

답글 달기