디자인 시스템 혼자서도 만들수 있다 !

Sungho Kim·2023년 1월 8일
222

사이드프로젝트

목록 보기
5/5
post-thumbnail

개발자에게 있어서 가장 중요한게 무엇인가요? 라고 묻는다면 아마 많은 분들이 생산성과 효율성에 대해 생각하실텐데요. 디자인 시스템은 특히 프론트엔드 개발자의 생산성을 올리는데 많은 도움을 줍니다.

잘 만들어진 디자인 시스템을 이용해서 컴포넌트를 만들면, 컴포넌트가 마치 레고의 작은 조각처럼 작동하게 됩니다. 레고는 서로 연결되는 작은 부품들을 잘 만들어서 결국엔 필요에 따라 같은 부품으로 성을 만들수도, 비행기를 만들수도, 심지어 왕국을 만들 수도 있습니다.

레고라는 비유를 프로그래밍에 대입해볼까요?

호환이 되는 최소단위의 컴포넌트를 잘 만들어서, 그 컴포넌트를 연결, 연결하는 것만으로도 하나의 서비스를 만들 수 있는거죠. 하지만 직접 코드를 짜보면 레고처럼 컴포넌트를 만드는게 말처럼 쉽지는 않습니다. 왜이렇게 어려울까요?

  1. 개발을 하다보면 꽤 많은 경우, 같은 기능을 하는 새로운 컴포넌트를 만들어서 씁니다.
  2. 협업을 하게되면 동료 개발자가 그 기능을 만들었는지 알기 힘듭니다.
  3. 재사용 가능한 컴포넌트를 만드는 시간보다, 하나의 기능을 하는 컴포넌트를 만드는게 더 쉽습니다.

이런 상황이 몇번 반복되는 것만으로도 어느정도의 서비스 구현이 끝났을때, 이 서비스를 확장하거나 유지보수를 하는데 엄청난 어려움이 생깁니다.

그렇다면, 이러한 문제를 해결하는 가장 좋은 방법은 무엇일까요?

아마도 눈치 채셨겠지만 이런 문제점들을 저는 디자인 시스템을 통해 해결할 수 있다고 확신합니다. 이번 포스팅에서 저는 어떻게하면 디자인 시스템을 구현할 수 있는지 storybook이라는 컴포넌트 도구를 활용해서 직접 구현하며 어떻게 현재 진행하고 있는 프로젝트에 적용할 수 있을지 소개하려 합니다.

TL;DR

  • 디자인 시스템이 뭔지 알아봅시다
  • 디자인 시스템의 장점
  • 스토리북이란?
  • 디자인 시스템(MVP)
  • 스토리북 적용해보기
  • 몇가지 의문
  • 결론

디자인 시스템

오랜기간 디자인을 해온 디자이너에게 갑자기 "디자인이 뭔가요?" 라고 묻는다면, 대답하기 곤란한 경우가 있습니다. 일단 디자인과 시스템의 정의부터 알아보겠습니다.

디자인이란 무엇일까요?

디자인이 ‘의미’로 사용되는 경우에는 넓은 의미로는 ‘개선한다’, ‘창의적으로 한다’, ‘새롭게 한다’, ‘아름답게 한다’는 등의 의미로 사용됩니다.

맞습니다. 디자인이란 무언가를 새롭게 개선하거나, 아름답게 만드는 행위를 말하는건데요. 이를 개발자에게 대입하보면 컴포넌트를 아름답게 만드는것, 컴포넌트를 새롭게 개선하는것이라 할 수 있을겁니다.

그렇다면 시스템이란 무엇일까요?

시스템(system)이란 그리스어 ‘systema'에서 유래된 것으로 “특정한 목적을 달성하기 위하여 여러 가지 관련된 구성요소들이 상호작용하는 유기적 집합체다.

그렇습니다. 시스템이란 조직화된 요소들의 집합체인데요. 이렇게 두 단어가 합쳐져서 디자인 시스템이라는 말이 나왔습니다.

그럼 프론트엔드 개발자로서 디자인 시스템을 정의해볼까요?

디자인 시스템은 아름다운 컴포넌트를 새롭게 만들거나, 개선을 하기 위한 조직화된 요소들의 집합체 라고 정의할 수 있을꺼 같습니다.

여기서 주목해야할 점은 새롭게 만들거나, 개선인데요! 이는 프론트엔드 개발자에게 가장 중요한 생산성과 유지보수와 직접적인 연관이 있습니다. 즉, 디자인 시스템을 잘 만들어 놓는다면 우리는 생산성과 효율성이 높은 개발자가 될 수 있다는거죠.

디자인 시스템의 장점

디자인 시스템은 UX/UI를 작업하는 디자이너와 개발자가 여러명일지라도, 일관성 있는 인터페이스를 제공할수 있습니다. 이를 통해, 디자이너나 개발자는 기존에 있는 소스를 다시 만들거나, 검색하는데 지치지 않기 때문에 기회비용이 크게 상승합니다.

디자인 시스템은 단순히 디자이너 팀 내에서의 통합, 혹은 프론트엔드 개발자 팀의 통합이 아니라, 더 높은 수준에서의 통합을 이루기 때문에, 조직의 비용이 줄어들 수 있습니다. 기존에는 디자인팀 내에 단일화된 에셋이 존재하더라고, 그걸 사용해서 개발팀에 넘기면 다시 개발팀에서 찾거나 만들어야 하는 불필요성이 발생하기 때문이죠.

스토리북

디자인 시스템을 구현하는데 있어서 왜 스토리북을 사용할까요?

스토리북은 UI components를 라이브러리화 할때 매우 유용한 라이브러리입니다. 스토리북을 통해 UI 컴포넌트를 분리하고 관리할 수 있습니다. 스토리북을 통해 버튼, 인풋, 세퍼레이터의 스타일들을 라이브러리화 시킬 수 있습니다. 한가지 컴포넌트라고 할지라도 여러개의 스토리를 지정해서 다른 props를 전달할 경우 해당 컴포넌트의 design이 어떻게 달라질지 보는게 가능합니다.

스토리북은 쉽게 배포가 가능한데, 몇가지 명령어만 설정해놓으면 디자이너와 개발자가 참고하는 하나의 동기화된 library를 갖게되고, 더이상 이미 만들어 놓은 UI를 찾는데 시간을 쓰거나, 다시 만드는 비효율성을 발생시키지 않는거죠.

또한, 스토리북은 자동화된 accessibility, interaction, visual 테스팅 기능을 제공합니다. 유닛 테스트를 자동화시킴으로써, 해당 컴포넌트를 재사용하는데 있어서 오류를 미리 방지할 수 있습니다.

디자인 시스템 MVP(최소기능제품) 구현 해보기:

디자인 시스템을 구현하기 위해선 몇가지 정의하고 가야할 사항들이 있습니다. 레이아웃 크기, 반응형 디자인을 위한 break point, 그리드, 컬러, 스페이싱 등 여러가지 디자인적인걸 고려하면서 구현을 해야되죠. 협업하는 디자이너가 있으면 다행이지만, 디자이너가 아닌 개발자가 이 모든걸 하나하나 알아서 공부하기엔 생각보다 범위도 많고 너무 처음부터 많은것을 고려해서 제품을 개발하다보면 시작 하기도 전에 지치기 때문에 가장 필요한것 몇가지만 규정해봅시다.

브레이크 포인트, 레이아웃 크기

제가 찾아본 바로는 레이아웃의 크기를 mobile(390px~ 810px), tablet(811px~1200px), pc(1201px~)로 규정하고 있습니다. 따라서 810px에서 한번, 1200px한번의 컴포넌트 변화를 주는게 가장 이상적이지만, 저는 MVP로 작동하는 디자인 시스템을 구현하고 싶어서 600px을 기준으로 그거보다 작은 경우 모바일페이지, 그 이상을 PC로 간주해서 break point를 600px로 잡고 구현을 진행했습니다.

컬러시스템

컬러 시스템도 처음부터 공부하려면 끝이 없지만, 컬러를 뽑아내는 사이트가 많습니다. 대표적으로는 https://imagecolorpicker.com/ 사이트가 있는데 여기 들어가셔서 서비스가 주고 싶은 느낌의 색을 선택해서 주조색과 부조색을 선택하시면 됩니다. 다만, 주조색을 선택할땐 흰 배경과는 대조되는 채도가 높은 색을 선택하는걸 추천드리고, 빨간색은 기본적으로 취소, 되돌아가기 등에 쓰는 색상이기 때문에 디자인에 자신이 없을 경우 피하는걸 권장드립니다.

아래 리스트는 제가 실제로 규정해 놓고 쓰고 있는 색상이니 참고하실 분들은 참고하시면 좋을꺼 같습니다. 제 프로젝트 같은 경우, 주조색, 부조색, 글자색, 파란색, 빨간색을 분류했고, 각각의 컬러의 밝은 색과 어두운 색을 미리 지정해서 hover action을 넣어서 좀 더 interactive한 UI를 제공하기 위해 노력했습니다.

const lightTheme: DefaultTheme = {
  white: "#FFFFFF",

  bgColorLight: "#FFF8ED",
  bgColor: "#FFEED6",
  bgColorDark: "#FFE2B9",

  primary: "#FB8500",
  primaryDark: "#C47501",
  primaryLight: "#dc7400",

  secondaryLight: "#8FFD66",
  secondary: "#44FB00",
  secondaryDark: "#36C900",

  borderColor: "#DBDBDB",
  gray: "#bfbfbf",

  bgGrayLight: "#FDF8F1",
  bgGray: "#F0F0F0",
  bgGrayDark: "#eaeaea",

  fontColorLight: "#606060",
  fontColor: "#404040",
  fontColorDark: "#202020",

  fontLightGray: "#E9E9E9",
  fontGray: "#8E8E8E",
  fontDarkGray: "#65665B",

  blueLight: "#E0EDFD",
  blue: "#2688D4",
  blueDark: "#0663B0",

  danger: "#FF6347",
};

타이포그래피 시스템

다음으로는 폰트 크기입니다. 기본적으로 4종류의 title과 2개의 body, 1개의 caption을 추천합니다. 처음 레이아웃을 모바일과 pc를 규정했으니, 타이틀과 바디에 들어갈 폰트를
@media를 이용해서 글로벌 스타일에 미리 지정해놓으면, 컴포넌트별 일일이 폰트크기나 굵기를 지정할 필요가 없고, 일체감을 줄 수 있어서 global style에 설정을 해놓는걸 추천합니다.


const typography = {
	@media  screen and  (max-width: 600px) {
        h1{
          font-size: 30px;
          font-weight: 600;
          line-height: 1.4;
        }
        h2{
          font-size:26px;
          font-weight: 600;
          line-height: 1.4;

        }
        h3{
          font-size:20px;
          font-weight: 600;
          line-height: 1.4;

        }
        h4{
          font-size:18px;
          font-weight: 600;
          line-height: 1.4;

        }
        span{
          font-size:18px;
          font-weight: 400;
          line-height: 1.4;

        }
        p{
          font-size:16px;
          font-weight: 400;
          line-height: 1.4;

        }
        small{
          font-size:16px;
          font-weight: 400;
          line-height: 1.4;
        }
        
      }
      @media  screen and  (min-width: 601px) {
        h1{
          font-size: 48px;
          font-weight: 600;
          line-height: 1.4;
        }
        h2{
          font-size:36px;
          font-weight: 600;
          line-height: 1.4;
        }
        h3{
          font-size:24px;
          font-weight: 600;
          line-height: 1.4;
        }
        h4{
          font-size:18px;
          font-weight: 600;
          line-height: 1.4;
        }
        span{
          font-size:16px;
          font-weight: 400;
          line-height: 1.4;
        }
        p{
          font-size:14px;
          font-weight: 400;
          line-height: 1.4;
        }
        small{
          font-size:12px;
          font-weight: 400;
          line-height: 1.4;
        }
      }
}

스토리북 본격 적용해보기:

1단계:스토리북 install

# Add Storybook:
npx storybook init

storybook의 CLI를 통해 인스톨이 끝난다면,

npm run storybook

콘솔에 해당 명령어를 치면 local에서 사용할 수 있는 스토리북 페이지가 로딩됩니다. 자세한 사용법은 https://storybook.js.org/docs/react/get-started/install 링크를 참고하세요!

2단계: 스토리북 폴더구조 설계하기

위에 있는 사진처럼, 스토리북을 처음 만든다면 default로 이렇게 버튼, 해더, 페이지가 들어가 있는데, 제가 추천하는 폴더 트리는 atomic design pattern을 적용하는 것입니다. (Atomic pattern까지 설명을 하면 포스팅이 너무 길어질꺼 같아서 따로 포스팅을 진행하겠습니다)

앞서 언급한 "레고"처럼의 핵심은 컴포넌트를 가장 작은 단위로 쪼개는 것이라고 생각하는데, Atomic pattern을 이용해서 button, input, skeleton circle, box, text, seperator 등을 작은 단위로 쪼개서 관리를 하는것입니다.

폴더구조
src
ㄴhooks
ㄴassets
ㄴpages
  ㄴlogin
  ㄴhome
  ㄴadmin
ㄴstories
  ㄴFoundations
  	ㄴcolors
    ㄴtypography
    ㄴlayers
  ㄴAtoms
  	ㄴbuttons
    ㄴinputs
    ㄴskeleton
  ㄴMolecules
  	ㄴPhotoCard
  ㄴorganisms
  	ㄴPhotoListList
  ㄴtemplates
  	ㄴHomeTemplate
ㄴindex.tsx
ㄴstyles.ts

폴더구조를 보시면 react 프로젝트에서 흔하게 볼 수 있는 components 폴더가 없습니다. 저같은 경우, 스토리북에 있는 폴더트리를 atoms, molecules, organisms, templates로 구분해서 페이지 단위에서는 스토리북에 있는 컴포넌트만 빼서 사용하도록 설계를 했습니다.

3단계, 컴포넌트 감사하기

이 프로세스에서는 모든 엔드포인트에 걸쳐, 제품의 모든 화면 또는 인터페이스를 살펴보고 각각 고유의 UI 요소를 분리합니다. 이 과정에서 가장 중요한 포인트는, 동일한 작업을 수행하려고 하는 다른 UI를 찾아서 하나의 UI로 통합하는 것입니다.

예제를 봐보시면, 위에 있는 4가지 버튼은 모두 같은 기능을 하지만 모두 다시 만든 버튼들입니다. 모두 컴포넌트를 만들때 필요에 의해 다시 만들어진 컴포넌트들이죠. 처음 컴포넌트를 만들땐 모두 다른 기능을 한다고 생각하고 만들었지만, 결국엔 어떤 액션이 존재하고, 그 액션을 해주는 버튼들이였던 것이죠.

3단계에선 웹사이트에 있는 모든 요소를 추출해서 동일한 작업을 수행하지만 다른 객체로 존재하는 모든 컴포넌트를 뽑아내는 작업을 진행합니다.

여기서 말하는 "같은 기능을 한다"는 "한가지 액션을 받아서 그 액션을 처리한다"의 의미로 작성되었습니다!
예를들어 예제에 보이는 버튼은 각각
영양소 --> 영양소 페이지로 전환
전체 --> search param 을 "전체"로 변경
로그인 --> submit form
구매하기 --> window.location.assign("링크")
로 모두 다른 기능을 하는 것처럼 보이지만, 버튼 입장에서는 onclick event를 받는다 라는 공통점이 있습니다.
따라서 3단계에서 버튼을 추출할 때, onClick event를 받는 버튼을 추출하는 과정을 진행했습니다.

4단계, 컴포넌트 통합하기

이 프로세스에선 3단계에서 발견한 같은 기능을 하는 다른 컴포넌트를 하나로 통합합니다. 가장 흔한 컴포넌트이면서, 모든 웹사이트에 존재하는 버튼 컴포넌트로 예를들어서 진행하겠습니다.

재사용 가능한 버튼 만들기

재사용 가능한 버튼을 만들기 전에 한번 생각을 해봅시다. 만약 우리가 운영하고 있는 웹사이트에 단 한개의 버튼만 들어갈 수 있다면, 어떤 props를 전달받아서 얼마만큼의 자유도를 줄것인가요?

아마 너무 많은 자유도를 주게될 경우, 웹사이트의 전체적인 일체감이 깨질것이고, 너무 제한을 할 경우, 심미적으로 별로이거나 원하는 디자인을 충분히 수용할 수 없는 버튼이 되겠죠?
서비스의 규모에 따라 정하시면 되겠지만, 저같은 경우, 버튼의 디자인을 담당하는 variant와 사이즈로 나누고, 6개의 모양과 4개의 사이즈로 버튼을 디자인 했습니다.

interface ButtonProps {
  block?: boolean;
  size?: "sm" | "md" | "lg" | "xl";
  variant?:
    | "default"
    | "primary"
    | "secondary"
    | "tertiary"
    | "danger"
    | "ghost";
  fontWeight?: string;
  label: string;
  margin?: string;
  active?: boolean;
  disabled?: boolean;
  startIcon?: ReactNode;
  endIcon?: ReactNode;
  onClick?: (event: React.MouseEvent<HTMLButtonElement, MouseEvent>) => void;
}

버튼의 props는 필수요소 라벨을 제외하고 그냥 넣는다면 default style로 만들어지고, 스타일이나 사이즈, 마진 등 커스텀 할수 있는 요소들을 넣어서 동적으로 작동되게 제작했습니다.

import React, { ReactNode } from "react";
import styled from "styled-components";
import { lightTheme } from "../../../styles";

interface IStyledButtonProps {
  padding: string;
  width: string | undefined;
  fontSize: string;
  margin: string;
  backgroundColor: string;
  fontColor: string;
  hoverBackgroundColor: string;
  borderWidth: string;
  borderColor: string;
  borderStyle: string;
  hoverBorderColor: string;
  hoverFontColor: string;
}

const StyledButton = styled.button<IStyledButtonProps>`
  background-color: ${(props) => props.backgroundColor};
  width: ${(props) => props.width};
  margin: ${(props) => props.margin};
  padding: ${(props) => props.padding};
  font-size: ${(props) => props.fontSize};
  border-radius: 5px;
  color: ${(props) => props.fontColor};
  border-width: ${(props) => props.borderWidth};
  border-color: ${(props) => props.borderColor};
  border-style: solid;
  :hover {
    background-color: ${(props) => props.hoverBackgroundColor};
    border-color: ${(props) => props.hoverBorderColor};
    color: ${(props) => props.hoverFontColor};
    transition: 0.3s ease-in-out;
    cursor: ${(props) => (props.disabled ? "default" : "pointer")};
  }

  &.active {
    background-color: ${(props) => props.hoverBackgroundColor};
    border-color: ${(props) => props.hoverBorderColor};
    transition: 0.3s ease-in-out;
    color: ${(props) => props.hoverFontColor};
  }
  svg {
    margin: 0px 12px;
  }
`;

interface ButtonProps {
  block?: boolean;
  size?: "sm" | "md" | "lg" | "xl";
  variant?:
    | "default"
    | "primary"
    | "secondary"
    | "tertiary"
    | "danger"
    | "ghost";
  fontWeight?: string;
  label: string;
  margin?: string;
  onClick?: (event: React.MouseEvent<HTMLButtonElement, MouseEvent>) => void;
  active?: boolean;
  disabled?: boolean;
  startIcon?: ReactNode;
  endIcon?: ReactNode;
}

export const ButtonText = ({
  size = "md",
  variant = "default",
  block = false,
  fontWeight,
  label,
  margin = "0px 0px",
  active = false,
  disabled = false,
  onClick,
  ...props
}: ButtonProps) => {
  let padding = "0px";
  let fontSize = "14px";
  let width = undefined;
  let backgroundColor = "white";
  let fontColor = "";
  let borderWidth = "1px";
  let borderColor = "";
  let borderStyle = "soild";
  let hoverBackgroundColor = "inherit";
  let hoverBorderColor = "inherit";
  let hoverFontColor = "inherit";

  // about size
  switch (size) {
    case "sm":
      padding = "10px 16px";
      fontSize = "14px";
      break;
    case "md":
      padding = "11px 20px";
      fontSize = "14px";
      break;
    case "lg":
      padding = "12px 24px";
      fontSize = "16px";
      break;
    case "xl":
      padding = "14px 30px";
      fontSize = "20px";
      break;
  }
  // about full width
  if (block) {
    width = "100%";
  }
  // about style
  switch (variant) {
    case "default":
      backgroundColor = lightTheme.white;
      fontColor = lightTheme.fontColor;
      borderStyle = "soild";
      borderWidth = "1px";
      borderColor = lightTheme.borderColor;
      hoverBackgroundColor = lightTheme.white;
      hoverBorderColor = lightTheme.gray;
      hoverFontColor = lightTheme.fontColorDark;
      break;
    case "primary":
      backgroundColor = lightTheme.primary;
      fontColor = lightTheme.white;
      borderStyle = "soild";
      borderWidth = "1px";
      borderColor = lightTheme.primary;
      hoverBackgroundColor = lightTheme.primaryDark;
      hoverBorderColor = lightTheme.primaryDark;
      hoverFontColor = lightTheme.white;
      break;
    case "secondary":
      backgroundColor = lightTheme.white;
      fontColor = lightTheme.primary;
      borderStyle = "soild";
      borderWidth = "1px";
      borderColor = lightTheme.primary;
      hoverBackgroundColor = lightTheme.primary;
      hoverBorderColor = lightTheme.primary;
      hoverFontColor = lightTheme.white;
      break;
    case "tertiary":
      backgroundColor = lightTheme.white;
      fontColor = lightTheme.fontColor;
      borderStyle = "soild";
      borderWidth = "1px";
      borderColor = lightTheme.white;
      hoverBackgroundColor = lightTheme.bgColor;
      hoverBorderColor = lightTheme.bgColor;
      hoverFontColor = lightTheme.fontColor;
      if (active) {
        backgroundColor = lightTheme.bgColor;
        fontColor = lightTheme.primary;
        borderStyle = "soild";
        borderWidth = "1px";
        borderColor = lightTheme.white;
        hoverBackgroundColor = lightTheme.bgColor;
        hoverBorderColor = lightTheme.bgColor;
        hoverFontColor = lightTheme.primary;
      }
      break;
    case "danger":
      backgroundColor = lightTheme.white;
      fontColor = lightTheme.danger;
      borderStyle = "soild";
      borderWidth = "1px";
      borderColor = lightTheme.danger;
      hoverBackgroundColor = lightTheme.danger;
      hoverBorderColor = lightTheme.danger;
      hoverFontColor = lightTheme.white;
      break;
    case "ghost":
      backgroundColor = lightTheme.white;
      fontColor = lightTheme.primary;
      borderStyle = "soild";
      borderWidth = "1px";
      borderColor = lightTheme.white;
      hoverBackgroundColor = lightTheme.bgColor;
      hoverBorderColor = lightTheme.bgColor;
      hoverFontColor = lightTheme.primaryDark;
      break;
  }

  return (
    <StyledButton
      backgroundColor={backgroundColor}
      fontSize={fontSize}
      width={width}
      padding={padding}
      margin={margin}
      fontColor={fontColor}
      hoverBackgroundColor={hoverBackgroundColor}
      borderWidth={borderWidth}
      borderColor={borderColor}
      borderStyle={borderStyle}
      hoverBorderColor={hoverBorderColor}
      hoverFontColor={hoverFontColor}
      disabled={disabled}
      {...props}
    >
      {label}
    </StyledButton>
  );
};

위에 있는 코드가 디자인 패턴을 이용해 정의한 제 button Atom입니다. 기존에 필요에 따라 어쩔땐 윈도우에 맞춰서 width를 정하고, 어쩔땐 fixed한 사이즈를 줄때와는 달리, 모든 컴포넌트에서 쓴다면 어떻게 써야할까를 충분히 고려해서 만들었고 아마도 새로운곳에 버튼을 만들거나 할때 추가로 받는 props가 존재할수도 있습니다.

하지만, 필요에 따라 만들때 기존에 있는 코드가 깨지지 않게 새로운 프롭을 추가하되 default value를 넣는등의 작업을 하게 될 경우 확장을 하는데 어려움이 없었고, 가장 좋았던 부분은 기존 버튼들에 hover action을 넣고 싶었는데 시스템을 도입하기 전에는 하나하나 들어가서 했던 반면, 지금은 버튼 하나를 바꾸면 홈페이지에 있는 모든 버튼의 모양이나 기능이 바뀐다는 점이였습니다.

몇가지 의문점:

Part1. 오히려 느리지않나?

이렇게 기존에 있던 서비스를 하나하나 뜯어서 하나의 컴포넌트 UI로 통합 하는게 쉽다고는 말을 못하겠습니다. 제 작은 프로젝트에서도 버튼 컴포넌트 하나를 만드는데 6시간 정도가 들었던걸 보면 비용이 꾀나 드는 작업이라는 것은 명백한 사실입니다.

다만, 한번 버튼을 정의해놓고 보니, 새로운 기능을 개발하거나, atom단위의 개발이 끝나고 molecule단위의 컴포넌트를 다시 정의할때 생산성이 매우 증가 하는것이 느껴졌습니다.

Part2. 새로 디자인 시스템을 만드는것 보단, 이미 만들어진 library를 사용하는게 낫지않나?

실제로 UI컴포넌트 library, tool은 시중에 굉장히 많습니다. Material UI, Ant Design등의 이미 정형화되고 잘 만들어진 컴포넌트들을 쓰면 원하는 기능이나 컴포넌트를 쉽고 빠르게, 통일성 있게 만들 수 있죠.

하지만 이러한 라이브러리를 사용하는것도 문제가 있었습니다. 라이브러리를 이용할때 이미 만들어진 컴포넌트의 제약사항을 반드시 지켜야 한다는것 이였습니다. 만약 슬라이더 컴포넌트가 양쪽으로 넘기는것과 dots를 넣고 빼는것만 정의되어 있다면, 그안에 버튼을 넣어서 컴포넌트를 만드는게 불가능 했고, 결국 100개의 컴포넌트중 70개는 라이브러리에 있는 컴포넌트를, 나머지 30개를 직접 만든 컴포넌트를 쓰게 됩니다.

30개의 컴포넌트에는 문제가 생기기 시작했는데
1. 이미 만들어진 컴포넌트인지 아닌지 몰라서 같은 컴포넌트를 다시 개발하는 경우가 생김
2. 이미 만들어진 컴포넌트에 대한 문서화가 진행되지 않았기 때문에 그 컴포넌트를 다시 꺼내쓰거나 재사용하는데 오랜시간이 걸림
이 두가지 문제가 가장 큰 문제였습니다.

만약에 문서화를 잘 해뒀다고 하더라도 여전히 어떤 페이지나 기능을 추가할때 양쪽을 체크해보는건 꾀나 큰 리소스 낭비라고 생각합니다.

결론

이미 어느정도 규모의 서비스를 만들고 계신 분들은 이미 디자인패턴을 적용해서 효율적으로 소통하며 일하고 계실꺼라 생각합니다. 이 글은 저같이 1인 프로젝트를 진행하거나, 개발자나 디자이너가 사내에 없지만 생산성과 효율성에 대해 고민하고 계신 분들이 알면 좋을꺼 같아서 이렇게 공유하게 되었습니다.

스토리북을 통해 디자인 패턴을 도입하면서 가장 좋았던 점은, 더이상 닥치는데로 컴포넌트를 만드는게 아니라, 해당 컴포넌트가 어디에 다시 사용될지, 특정한 상태에 너무 의존하고 있진 않은지, 특정 기능이 필요하다면 그 기능을 어떻게 custom hook으로 만들지, props는 뭘 받아서 어떻게 처리를 할지 등을 미리 설계해서 컴포넌트를 만들기 시작했다는 점입니다.

저는 오늘 디자인 시스템, 스토리북이 무엇인지 알아보았습니다. 또한, 스토리북을 제 프로젝트에 어떤식으로 적용했고, 나아가 어떤 폴더 구조로 빌딩해나가면 좋을지에 대해 공유해보았습니다. 제가 정리한 내용이 정답은 아니겠지만 최근 한달간 스토리북과 디자인 시스템에 대해 공부하며 적용한걸 공유해봤습니다.

현재 디자인 시스템 도입을 시도하고 있따면, 부디 성공적인 디자인 시스템 도입을 바라며 글을 맺겠습니다. 긴글 읽어주셔서 감사합니다 :)

참고자료:

profile
공유하고 나누는걸 좋아하는 개발자

8개의 댓글

comment-user-thumbnail
2023년 1월 13일

3단계 컴포넌트 감시하기 본문에 4가지의 버튼이 같은 기능을 한다고 적혀있는데 어떠한 측면에서 같은 기능을 하는 버튼 인 것 인가요?

1개의 답글
comment-user-thumbnail
2023년 1월 16일

스토리북을 프로젝트에 도입하기 전에 여러 고민들이 있었는데 위 글 덕분에 많은 도움 받았어요!
좋은글 감사합니다!

1개의 답글
comment-user-thumbnail
2023년 1월 17일

오.. 거의 해외 medium 인기 아티클급의 퀄리티입니다. 한글로 이런 자료를 볼 수 있다는 것에 감사합니다.

1개의 답글