본문 바로가기

FrontEnd

컴포넌트 합성 : 서브 컴포넌트의 모든 것

반응형
 

원문 : https://medium.com/eightshapes-llc/subcomponents-753ce9f6600a

 

Subcomponents

Relinquish control, offer parts, and let implementers compose

medium.com

TL;DR

재사용성을 고려한 컴포넌트를 만들기 위해서...

  • 모든 것의 제어를 포기하고 (inversion of control)
  • 부픔(서브컴포넌트)를 제공하고
  • 구현하는 개발자가 컴포넌트를 합성하도록 합니다

sub components

디자인 시스템의 모든 사용 사례를 컴포넌트화 하는 것은 어리석은 일입니다만,

실무에서는 기존 피그마 컴포넌트를 작게 분리하는 것을 싫어하는 경향이 있습니다.

그리고 개발자는 바닥부터 컴포넌트를 다시 만드는 것을 싫어하죠.

 

컴포넌트 라이브러리가 점점 원자와 분자로 채워짐에 따라, 거대한 설정 덩어리가 생겨나며,
이러한 복잡성은 다음과 같은 디자인 결정을 강요합니다.

  • 특정 컴포넌트는 설정의 일부 항목을 지원하지 않기로 합니다.
  • 그럼에도 불구하고 모든 설정을 최대한 지원하도록 노력합니다.

이 게시물에서는

설정 기반 컴포넌트의 한계를 알아보고,

설정 기반 프로그래밍의 대안으로서,

재사용 가능한 청크와 유연한 컨테이너를 분리하여 합성하는 방법을 알아봅니다.

 


설정 기반 컴포넌트

아래의 카드 컴포넌트는 다음과 같은 설정(props)과 슬롯(slot)이 존재합니다.

  • 컴포넌트 합성을 위한 슬롯(slot)
  • 시각적 표현을 위한 속성 (prop)
  • 콘텐츠를 위한 속성 (prop)

Card component with many configurable properties

다양한 속성들을 통해 아래와 같이 여러 카드의 변형을 만들 수 있습니다.

Cards of varying states, content, sizes, and color modes

하지만 prop 기반 설정 방식으론 아래의 어떤 것도 구현할 수 없습니다.

  • Card 수직 방향 항목 순서 바꾸기
  • Card 수평 방향 항목 순서 바꾸기
  • Card 구석에 삭제 버튼 추가하기
  • 타이틀 앞에 뱃지 추가하기
  • 이미지 대신 배경 아이콘 보여주기
  • 액션 버튼, 링크 버튼 대신 아이콘 버튼 사용하기

Card variations that the component does not support

많은 팀에 서비스를 제공하는 시스템의 경우 이러한 케이스들이 앞으로 필요할 수 있습니다.
이런 경우가 등장할 때마다 새로운 디자인, 컴포넌트가 필요할까요?


설정에서 합성으로

우리는 다음과 같은 방법으로 컴포넌트의 사용 사례를 추가할 수 있습니다.

옵션 1: 설정 추가하기

엘리먼트가 필요하면 엘리먼트를 추가합니다.
prop이 필요하면 prop을 추가합니다.
디자이너와 개발자는 컴포넌트의 API(craft a component’s API)를 만들기 위해 유사한 접근 방식을 사용합니다.
이 습관은 자연스럽고 예측 가능합니다. 필요한 기능을 하나씩 추가하기만 하면 됩니다.

Adding more and more properties to support rare cases

이 방법의 단점은 다음과 같습니다.

  • 기능이 너무 많은 컴포넌트는 변경에 취약해집니다.
  • prop이 너무 많으면, 컴포넌트를 이해하기 어려워집니다.
  • 모든 prop 집합이 앞으로의 모든 사용 사례를 커버하는 건 아니며, 때로는 불필요한 속성이 존재할 수도 있습니다.
  • 테스트도 점점 어려워집니다.

죄송하지만, 이 방법은 사양하겠습니다.

옵션 2: 개발자가 처음부터 만들도록 하기

디자인 시스템에 없으면 개발자가 직접 만들도록 합니다.

이 방법은 문제가 뭘까요?

  • 시스템이 없어 일관적이지 않습니다.
  • 디자인에 시간과 비용을 많이 씁니다.
  • 스타일, 구조, 상태 등이 기존 시스템과 동기화되지 않습니다.
    • 색상, 타이포그래피, 공간, 크기 등...

모든 측면에서 시스템 차원에서 지원해야하는 변형이 부족합니다.

또한 아무도 처음부터 계속 컴포넌트를 만들고 싶어하지 않습니다.

옵션 3: 서브 컴포넌트를 제공하기

아래의 조건을 충족하는 작지만 유용한 부품을 제공할 수 있다면 어떨까요?

  • 필요한 모든 요구 사항을 충족합니다.
  • 디자인 시스템과 일관성을 유지합니다.
  • 시스템 개발을 가속화합니다.
서브컴포넌트는
버튼, 아이콘 또는 이미지와 같은 단순 중첩된 컴포넌트를 넘어,
특정 클래스 또는 컨텍스트의 솔루션을 구축하기 위해 더 구체적이고 잘 짜여진 컴포넌트입니다.
서브컴포넌트:
특정 상위 컴포넌트 또는 컨텍스트 내에서만 사용하며,
이를 의도한 잘 정의된 API를 지닌,
독립적으로 합성 가능한 UI 컴포넌트 입니다.

Card는 다음과 같이 쪼갤 수 있습니다.

  • CardContainer
  • CardText
  • CardImage
  • CardActions

List는 다음과 같이 쪼갤 수 있습니다.

  • ListItemText
  • ListItemIcon
  • ListItemAction

서브컴포넌트는 범위와 의도를 표현하기 위해
상위 이름(List 또는 Card) 앞에 추가되는 이름(ListItem 또는 CardText)을 사용합니다.

 

서브컴포넌트는 다음과 같은 많은 이점을 제공합니다.

  • 더 작은 prop 집합으로 더 많은 케이스를 유연하게 지원합니다.
  • 개발자가 시스템에 얽매이지 않고 더 많은 것을 제어할 수 있도록 권한을 부여합니다.
  • 개발자가 고유한 요구사항을 해결하기 위해 더 많은 자유도를 얻을 수 있으므로 디자인 시스템 팀에 대한 의존도가 감소합니다.
  • 많은 팀에서 공유할 필요가 없는, 시스템을 복잡하게 만드는 기능 추가를 줄입니다.
  • 개발자가 도전하고, 혁신하고, 확장할 수 있느 여러 방법에 대한 탐색과 다양한 사고를 장려합니다.
하지만 더 큰 것을 어떻게 더 작고 재사용 가능한 것으로 분해할 수 있을까요?

서브 컴포넌트 구현하기

  1. 재사용 가능한 청크와 이를 배치할 컨테이너를 식별합니다.
  2. prop들을 컨테이너 단위로 분산합니다.

1. 재사용 가능한 청크 식별하기

독립적으로 배치할 수 있지만 더 이상 나눌 필요가 없는 요소를 식별하기 위해, 작은 요소부터 시작합니다.

서브컴포넌트는 목적에 따라 다음과 같이 분류할 수 있습니다.

  • 잠금(lockups)
  • 확장(extensions)
  • 조합(combinations)
  • 반복되는 요소 집합(repeating sets of elements)

잠금(lockups)

서브컴포넌트 잠금은 요소를 결합하고 내부 공간을 캡슐화합니다.

예를 들어 CardText는 title과 metadata, description을 결합합니다.

prop에는 colorMode(밝음/어두움용), size 및 inset 토글(인접한 항목과 쉽게 합성할 수 있도록)이 포함될 수 있습니다.

A CardText lockup with properties to configure visibility, content and style

확장(extension)

서브컴포넌트 확장(extension)은 더 좁은 컨텍스트에서 특정 방식으로 기능하도록
기존 컴포넌트를 확장합니다.
 
해당 컴포넌트는 기존 컴포넌트들 다음과 같은 방식으로 확장합니다.
예를 들어 CardImage는 Image를 확장하여 다음과 같은 기능을 사용할 수 있습니다.
  • alt-text prop을 전달
  • 호버 애니메이션을 추가
  • asprct-ratio 변형을 Card 컴포넌트와 관련된 변형으로만 제한하는 파사드
  • 특정 프롭을 금지
A subcomponent extension can apply, add, limit or inhibit access to features, such as aspect ratio options

조합(combinations)

서브컴포넌트는 허용된 조합을 노출할 수 있습니다.
예를 들어 CardAction은 조합을 통해 다음 요소들을 한정할 수 있습니다.
  • 버튼, 링크 및 아이콘 버튼의 수량
  • 버튼, 링크 및 아이콘 버튼의 조합
  • 버튼, 링크 및 아이콘 버튼의 순서
  • 버튼, 링크 및 아이콘 버튼의 간격
  • 버튼, 링크 및 아이콘 버튼의 변형
이것은 다음과 같은 효과가 있습니다.
  • API의 이해를 돕습니다.
    • (권장하는 옵션을 가시화)
  • 일관성을 개선합니다.
    • (옵션을 제한적인 집합으로 제한)
  • 효율성을 증가시킵니다.
    • (설정하기 더 쉬운 API)
Subcomponents can enumerate approved combinations and encapsulate space within

조합은 설계 도구와 코드 도구에서 서로 다른 접근 방식을 보장할 수 있습니다.

  • 디자이너는 컴포넌트들의 권장되는 조합, 크기 및 좋은 디폴트 값을 중요하게 생각할 수 있습니다.
  • 개발자는 보다 일반적인 컨테이너를 이용한 유연한 합성을 선호할 수 있습니다.

반복(repeater)

repeater 서브컴포넌트는 다음과 같은 역할을 합니다.
  • CheckboxGroup 내의 CheckboxItems, List 내의 ListItems 또는 Stepper 내의 Steps와 같은 리스트(그룹)를 형성합니다.
  • 각 항목은 그 안의 요소를 앞뒤 항목과 관련하여 정렬합니다.
Material UI React의 List 컴포넌트(Material UI React’s List component)는 이에 대한 훌륭한 예입니다.
 

2. 컨테이너 식별하기

다음으로 컴포넌트에서 벗어나 컨테이너를 식별합니다.
서브컴포넌트, 중첩된 컴포넌트 및 기타 요소를 정렬하는 데 필요한 영역(수준)입니다.
컨테이너는 다음과 같은 역할을 합니다.
(컨테이너는 서브컴포넌트의 최상위 래퍼 역할을 할 수도 있지만 일단 서브컴포넌트 입니다.)
  • 레이아웃 규칙을 캡슐화합니다.
  • 간격을 제어합니다.
  • 배경색, 둥근 모양 및 대화형 상태와 같은 prop을 적용할 수 있습니다.

CardContainer와 같은 일반적인 컨테이너(general container)는 좋은 출발점입니다.

원래 카드를 CardConstainer 서브 컴포넌트와 비교하기
일반적인 컨테이너와 달리 타입 지정 컨테이너(typed containers)는 특정 자식을 기대하는 영역 역할을 합니다.
카드의 경우 CardMedia, CardContent 및 CardActions 타입 지정 컨테이너를 제공할 수 있습니다.

각 서브컴포넌트는 유연한 합성의 대상이지만,
각 타입 지정 컨테이너 내에는 레이아웃과 간격이 존재합니다.

CardContainer with three children zones for media, content and actions

CardMedia 타입 컨테이너는 SpotIllustration, Image, ProductImage 또는 Icon 중 하나를 서브 컴포넌트로 예상합니다.
반면 CardImage 컨테이너는 Image만 서브컴포넌트로 예상합니다.

개발자는 대화형 상태 컨테이너 서브컴포넌트를
대화형 상태가 있는 자식 컴포넌트와 같이 사용할 수 있습니다.
대화형 서브컴포넌트에는 ...Button, ...Action 또는 ...ActionArea와 같은 이름이 있습니다.
 
아래는 대화형 상태가 있는 컨테이너의 예시입니다.
CardContainer with features for color mode, color hierarchy, and interactive state
컨테이너 선택은 까다롭습니다.
저는 Card의 서브컨테이너를 설계할 때 다음과 같은 질문에 직면했습니다
  • 상태(focus, hover 등)를 최상위 CardContainer에 결합할까요?
  • 별도의 CardButton 하위 컴포넌트를 제공할까요?
  • colorMode를 제한된 집합으로 제공할까요?, 아니면 16진수나 모든 색상 토큰을 받을 수 있도록 할까요?
  • 패딩을 강제할까요? 패딩을 많이 혹은 적게 제공해야할까요? 디폴트로 패딩을 제공하지 않고, inset prop을 제공할까요?
  • Card 콘텐츠를 어떻게 처리할까요?
    • CardContent 서브컴포넌트를 포함하고 해당 컴포넌트에 패딩을 추가하면, spacing 원칙을 따르지만 복잡성을 추가합니다.
    • CardText 컴포넌트에 마진을 추가하는 것은 효율적이지만, 마진을 이용해 공간을 만드는 것은 spacing 원칭을 위반하는 것입니다.
실험, 피드백 및 반복을 통해 우리는 유연성, 정밀도, 명확성 및 복잡성의 적절한 균형을 찾는 것이 좋습니다.
이는 어렵고 완벽한 답은 없습니다.
주 : 개발 시 컴포넌트의 명시적 spacing 관심사를 없애려면 최대한 중첩 합성하는 방법밖엔 없더라구요 ㅎ

서브컴포넌트 합성

위에서 설명한 설정으로 불가능했던 사용 사례들을 구현해 봅니다.

순서 변경

컨테이너 내에서 CardText와 CardImage의 순서를 변경합니다.

컨테이너 내에서 CardText와 CardImage의 순서를 변경합니다.

레이아웃 변경

카드컨테이너의 클래스명 혹은 속성을 통해 레이아웃을 변경합니다.

요소 추가하기

카드를 제거하기 위해 오른쪽 상단 모서리에 아이콘을 추가하는 것도 쉽습니다.

스타일 커스터마이징

아이콘과 배경색이 있는 미디어 영역을 원한다면

CardImage 대신 CardMedia를 사용합니다.

중첩 컴포넌트 포함하기

CardText 위에 배지를 표시합니다.

대화형 요소 포함하기

위와 같이 각 예시에서 합성은 매우 간단함을 알 수 있습니다.


서브 컴포넌트에 익숙해지기

  1. 매우 일반적인 컴포넌트를 설정 기반으로 만듭니다.
  2. 일반적이지 않은 컴포넌트는 합성 기반으로 만듭니다.
  3. 덜 일반적인 컴포넌트는 시간이 허락하는 한 설정 기반으로 만듭니다.

서브 컴포넌트를 분리 및 설계하는 것은 어렵지만,

개발, 테스트, 유지보수의 효율성을 높여줍니다.

 

내용의 핵심은 좋은데 원 저자가 글을 못알아먹게 써서 요점을 파악하는게 어렵군요

 

같이 보면 좋은 글 

https://itchallenger.tistory.com/905

 

컴포넌트 기반 디자인 시스템 with Figma

재사용과 유지보수를 고려한 컴포넌트 설계를 위해, 디자인 문서와 코드를 align 하는 방법을 설명합니다. 원문 : https://www.smashingmagazine.com/2022/01/composition-based-design-system-figma/ Composition-Based Design Sy

itchallenger.tistory.com

 
반응형