Atomic Design

younghyun·2023년 3월 7일
0

카카오 페이지 웹에서 컴포넌트 개발 시 겪은 문제

컴포넌트 파편화

디자인시스템 내에서 관리되지 않은 버튼이 있다고 하면 폰트 크기, 배경색, 테두리 둥글기 등이 페이지마다 제각각일 수 있음. 디자이너 혹은 유관부서와 특정 페이지의 버튼에 관해 얘기할 때 “프로필 설정 페이지의 최하단 저장 버튼”과 페이지 명과 위치를 같이 얘기해야 함. 이와 같은 대화는 불필요하게 대화 시간을 길어지게 하고, 디자인과 개발 부서에서도 여러 개의 버튼을 유지보수해야 함.

명확한 컴포넌트 설계 기준 미비

컴포넌트 구조 설계 시 명확한 기준이 없음. 팀원들 각자가 생각하는 의미있는 기준으로 컴포넌트를 나눔. 성격이 비슷한 컴포넌트 끼리 묶어서 아래 이미지와 같이 페이지를 여러가지 방법으로 표현할 수 있음.

하지만 제각각인 규칙으로 인해 관심사가 너무 많거나 재사용이나 확장할 수 없는 컴포넌트가 생기는 문제가 있음. 기준이 없으므로 구조에 대한 코드 리뷰를 하기 어려움. 이에 따라 좋은 구조를 가진 컴포넌트를 만들기 어려운 문제가 있음.

Atomic Design

위와 같은 문제는 디자인 시스템으로 어느정도 해결 할 수 있다고 생각. 디자인 시스템을 통해 서비스의 고유한 타이포그래피, 컬러 팔레트 등 이러한 디자인의 기초 요소들을 만들고 이것을 기반으로 공통 컴포넌트를 잘 정의하여 위 문제들을 해결할 수 있음. 카카오 페이지 웹에서는 디자인 시스템 방법론인 아토믹 디자인을 활용하여 디자인 시스템을 구축하고 있음.

디자인 시스템과 아토믹 디자인
brad frost에 따르면 디자인 시스템은 어떤 조직이 디지털 인터페이스를 디자인하고 구축하는 방식에 대한 이야기라고 말함. 디자인 시스템은 여러가지 하위 시스템을 포함하는데, UI 컴포넌트와 variants, 타이포그래피 시스템, 컬러 팔레트 시스템, 레이아웃 / 그리드 시스템 등이 있음. 아토믹 디자인은 디자인 시스템을 만드는 방법론. brad frost의 디자인 시스템 이야기에 관심이 있으시다면 design systems are for user interface 글을 추천.

brad frost의 아토믹 디자인 은 화학적 관점에서 영감을 얻은 디자인 시스템. 모든 것은 atom(원자)으로 구성되어있고 atom(원자)들이 서로 결합하여 molecule(분자)이 되고, molecule는 더 복잡한 organism(유기체)으로 결합하여 궁극적으로 모든 물질을 생성. 아토믹 디자인에서는 이 개념을 차용해서 컴포넌트를 atom, molecule, organism, template, page의 5가지 레벨로 나눔.

아토믹 디자인은 다음과 같이 단계별 기준을 가지고 있음. 단계별로 추상적인 것에서 구체화하는 단계를 가짐. 이 과정을 통해 일관성을 가지고 확장하면서 최종 콘텐츠를 보여줄 수 있음.

Atom

atom은 더 이상 분해할 수 없는 기본 컴포넌트. label, input, button과 같이 기본 HTML element 태그 혹은 글꼴, 애니메이션, 컬러 팔레트, 레이아웃과 같이 추상적인 요소도 포함될 수 있음. atom은 모든 기본 스타일을 한눈에 보여주므로 디자인 시스템을 개발할 때 유용하게 사용됨. atom은 추상적인 개념을 표현할 수 있는데 이것을 단일 컴포넌트로 사용하기엔 어려운 경우가 있음. 예를 들어 레이아웃과 같은 atom 그 자체로는 실제 페이지에서 바로 사용하기에 유용하지 않을 수 있음. 또한 atom을 다른 atom과 결합한 (뒤에 설명할) molecule 혹은 organism 단위에서 여러 단위와 결합하여 유용하게 사용될 수 있음.

Molecule

molecule은 여러 개의 atom을 결합하여 자신의 고유한 특성을 가짐. 아래 이미지와 같이 atom들을 결합할 경우, button atom을 클릭하여 form을 전송하는 molecule로 정의할 수 있음.
molecule 중요한 점은 한 가지 일을 하는 것. SRP(Single Responsibility Principle) 원칙으로 인해 키워드 전송 기능이 필요한 곳에서 재사용될 수 있음. molecule의 SRP는 재사용성과 UI에서의 일관성, 테스트하기 쉬운 조건이라는 이점을 가짐.

Organism

organism은 앞 단계보다 좀 더 복잡하고 서비스에서 표현될 수 있는 명확한 영역과 특정 컨텍스트를 가짐. 이것은 atom, molecule, organism으로 구성할 수 있음. 예를 들어 header 라는 컨텍스트에 logo(atom), navigation(molecule), search form(molecule)을 포함할 수 있습니다. atom, molecule에 비해 좀 더 구체적으로 표현되고 컨텍스트를 가지기 때문에 상대적으로 재사용성이 낮아지는 특성을 가짐.

Template

템플릿은 page를 만들 수 있도록 여러 개의 organism, molecule로 구성할 수 있음. 아래 이미지와 같이 실제 컴포넌트를 레이아웃에 배치하고 구조를 잡는 와이어 프레임. 즉, 실제 콘텐츠가 없는 page 수준의 스켈레톤이라고 정의할 수 있음.

Page

page는 유저가 볼 수 있는 실제 콘텐츠를 담고 있음. template의 인스턴스라고 할 수 있음. 예를 들어 장바구니 페이지에 유저가 담은 상품이 없거나 10개를 담는 다양한 template의 인스턴스를 볼 수 있음.

참고로 아토믹 디자인의 각 단계는 선형(linear) 프로세스가 아님. 오히려 UI를 응집력 있는 전체와 일부분에 대한 콜렉션으로 생각할 수 있도록 도와주는 멘탈 모델. button과 기타 요소를 따로따로 디자인한 다음 모든 것이 합쳐져 하나의 응집력 있는 전체를 만드는 것이 아님. 즉, 1. atom, 2. molecule, 3. organism .. 이렇게 스텝 별로 나아가는 방법론이 아님. brad frost의 아토믹 디자인 플로우 에서 팀원들과 페이지의 스크린샷을 찍고 고유한 UI 패턴을 카테고라이징하는 과정을 설명. 아토믹 디자인의 멘탈 모델 개념을 이해하는 데 도움이 될 수 있음.

알려준 대로 컴포넌트를 나누면 될 것 같지만..

아토믹 디자인을 처음 접했을 때에는 Brad Frost의 아토믹 디자인 기준대로만 컴포넌트를 나누면 되겠구나 라고 막연하게 생각. 하지만 아토믹 디자인 단위를 나누는 기준은 주관적이기 때문에 팀원 간 이 기준을 좁히는데 시간을 많이 사용. 그래서 이것에 대해 얘기해보려고 함.

Atomic Desgin 적용하면서 겪은 것들

Molecule과 Organism 을 나누는 기준의 주관성

molecule은 atom으로 구성되어 SRP에 따라 1가지 책임을 지고, organism은 atom, molecule, organism으로 구성되어 서비스에서 Layout을 기준으로 나눌 수 있는 영역을 가짐.

작성한 컴포넌트에 컨텍스트가 있는 경우에는 organism으로, 컨텍스트 없이 UI 적인 요소로 SRP를 지킬 수 있다면 재사용하기 쉬운 molecule로 작성함. 그래서 molecule의 컴포넌트 네이밍은 컨텍스트 없이 주로 UI 적인 네이밍이 포함됨. 반면에 organism 네이밍은 컨텍스트가 포함됨.

위 기준으로 판단하기 모호할 경우에는 일단 organism으로 작성한 후에 코드 리뷰를 통해 적절하게 변경할 수 있도록 논의를 나눔.

Organism을 나누는 기준

organism은 UI에서 명확한 영역을 가짐. 이 명확한 영역에 대한 정의는 주관적이라 여러 가지 방식으로 해석될 수 있음.

극단적으로는 A와 같이 큰 영역을 organism이라고 할 수 있고, B와 같이 비슷한 유형의 책임을 그루핑하여 구분할 수 있음. (네이밍보다 organism 영역을 어떻게 구분했는지에 집중해주세요)

A보다는 B와 같이 공통된 컨텍스트를 묶어 organism 컴포넌트로 표현하면 적당한 책임을 가진 컴포넌트를 작성할 수 있음. 예를 들어 CommentListToolbar 컴포넌트의 경우 댓글 데이터를 다루는 책임을 가지고 있음. 또한 Comment 컴포넌트는 레이아웃이나 Comment를 리스트로 표현할 때 여백 처리를 쉽게 할 수 있음.

약간 다른 Organism이 있을 때 중복 컴포넌트가 생기거나 불필요한 Props의 증가

Comment 댓글 아이콘, 댓글 개수 영역이 빠지거나 아래와 같이 프로필 사진, 닉네임, 댓글 작성일이 1줄로 보여야하는 요구 사항이 추가됨.

새로운 organism을 만들거나 혹은 댓글 아이콘과 댓글 개수 영역 노출 유무, 프로필 텍스트 1줄 보여주기에 대한 추가 props를 받아야 합니다. 새로운 컴포넌트를 만들자니 이런 상황마다 컴포넌트가 늘어나며 비슷한 컴포넌트의 중복이 생길 수 있습니다. props의 경우에도 마찬가지입니다.

이런 경우에 대해서 organism 내에서 compound 컴포넌트를 이용한 해결책을 찾을 수 있습니다. compound 컴포넌트를 통해 컴포넌트 구조에 유연하게 대처할 수 있습니다. 꼭 compound 컴포넌트가 아니더라도 organism 정의를 다시 하거나 다른 단위의 아토믹 컴포넌트로 재정의하여 해결할 수도 있습니다.

<Comment>
  <div>
    <Comment.ProfileImage />
    <Comment.Name />
    <Comment.Info />
  </div>
  <Comment.Content />
  <div>
    <Icon type={IconType.Heart} />
    ...
  <div />
</Comment>

아토믹 디자인 방법론 이외에 하고 있는 것들

컴포넌트를 제어하는 것들은 아토믹 컴포넌트 외부에서 주입

카카오페이지 웹에서 컴포넌트 작업시 스토리북을 적극적으로 활용하고 있습니다. 컴포넌트를 제어할 수 있는 UI상태나 동작에 대한 이벤트 핸들러를 외부에서 처리할 수 있도록 props로 다룹니다. 스토리북에서 컴포넌트의 상태와 동작을 한눈에 확인할 수 있고 유연성과 재사용성이 높아지는 장점이 있습니다.

아토믹 컴포넌트의 원천은 디자이너가 작성한 피그마 컴포넌트

리액트의 신뢰 가능한 단일 출처 (single source of truth) 를 차용하여 디자이너가 작성한 컴포넌트, 컬러팔레트, 타이포그래피 등의 네이밍을 리액트 컴포넌트의 네이밍과 동일하게 작성합니다. 가장 큰 장점은 디자이너와 개발자가 동일한 이름을 가지고 빠르고 쉽게 대화할 수 있습니다. 예를 들어 프로필 설정 페이지의 저장 버튼이 아닌 “solid button”, “Bottom Fixed Button” 이라는 공통된 네이밍으로 의사소통할 수 있습니다.

UI 모델링

디자인된 페이지를 담당 개발자들과 함께 아토믹 컴포넌트 단위와 네이밍을 작성합니다. 카카오 페이지 웹에서는 figjam을 이용하여 UI 모델링을 진행합니다. 이 작업을 같이 하게되면 논의를 통해 적절한 단위의 아토믹 컴포넌트를 작성할 수 있습니다. 또한 팀원 간 아토믹 단위에 대한 주관적인 기준을 점점 맞춰나갈 수 있습니다. 단 한번의 모델링으로 이상적인 아토믹 디자인 단위를 나눌 수 없습니다. 컴포넌트를 실제 코드로 작성하며 혹은 다른 페이지에서 비슷한 디자인의 컴포넌트가 추가된다면 얼마든지 변경될 수 있습니다. UI 모델링 외에도 지속적인 커뮤니케이션, 긴밀한 피드백을 통해 이상적인 아토믹 디자인 구조로 나아갈 수 있습니다.

레이아웃과 관련된 스타일은 외부에서 주입

아토믹 디자인 뿐만 아니라 디자인 시스템의 컴포넌트는 재사용 될 수 있습니다. 재사용성을 높이기 위해 마진, 패딩 등과 같은 스타일은 아토믹 컴포넌트에서 정의하지 않고 컴포넌트를 사용하면서 주입하도록 합니다. 아래와 같이 가장 바깥쪽 DOM의 attribute를 인터페이스로 확장합니다. 이것을 구조 분해 할당을 이용하여 사용하는 곳에서 레이아웃을 지정할 수 있도록 합니다. 이 간단한 규칙을 통해 재사용성을 높일 수 있었습니다.

// organism/Comment.tsx
interface CommentProps extends HTMLAttributes<HTMLDivElement> {
  name: string;
}

function Comment({ name, children, ...props }: CommentProps) {
  return (
    <div {...props}>
    	<Text>{name}</Text>
      <Text>{children}</Text>
    </div>
  )
}

// pages/Product.tsx
function ProductPage ({
  commentList,
  ...
}) {
  return (
    <div>
      ...
      {
        commentList.map(comment => <Comment style={{margin: '20px 40px', flex: 1}}/>)
      }
    </div>
  )
}

// pages/Comment.tsx
function CommentPage({
  ...
}) {
  return (
    <div>
      ...
      <Comment style={{ width: '80%', margin: '0 auto'}}>
    </div>
  )
}

참고
https://fe-developers.kakaoent.com/2022/220505-how-page-part-use-atomic-design-system/

profile
선명한 기억보다 흐릿한 메모

0개의 댓글