작년 네이버 커넥트재단에서 열린 부스트캠프에 Final Project 때 Atomic Design과 Storybook을 사용했던 경험에 대해서 소개한 글입니다.
Atomic Design이란 원자가 결합해서 분자가 되는 것처럼 단계를 나눠 작은 컴포넌트를 만들고, 그것들을 결합해 조금 더 큰 단위의 뷰를 만드는 방법론이며, 5개의 구분된 단계가 있습니다.
출처: https://brunch.co.kr/@ultra0034/63
Atoms(원자)
원자는 물질의 기본 구성 요소로, label, input, button과 같은 HTML tag입니다.
Molecules(분자)
분자는 원자들이 함께 결합한 것이며, 화합물의 가장 작은 기본단위입니다. 분자들은 그 자체의 특성이 있습니다.
Organisms(유기체)
유기체는 비교적 복잡하며 인터페이스에서 구분된 영역을 형성하는 서로 결합되어 있는 분자 그룹입니다.
Templates
템플릿은 주로 페이지를 구성하기 위해 서로 묶인 유기체 그룹으로 구성되며, 디자인 확인 및 레이아웃 동작을 볼 수 있습니다.
Pages
페이지는 템플릿의 특정 인스턴스로, 사용자가 보는 디자인을 정확하고 구체적으로 구현합니다.
슬랙을 구현하면서 프로필 이미지나 메시지, 입력창 등 일관된 디자인의 컴포넌트들이 많다고 생각했습니다. 또한 재사용 가능한 컴포넌트를 만들면 불필요한 코드 중복을 줄이고, 개발 생산성을 높일 수 있다고 판단했기 때문에 Atomic Design을 도입하기로 했습니다.
처음에는 Atomic Design을 도입했을 때 어떤 기준을 가지고 각 단계를 나눠서 구현해야 할지 고민했었습니다.
페이지별로 필요한 컴포넌트들을 나누는 게 좋겠다고 판단을 해서 채팅방 페이지, 채널 리스트 페이지 등 화면 단위로 요구사항을 먼저 나누고, 그 안에서 채널 검색창과 채널 리스트 등 보이는 큰 단위로 Organisms를 정했습니다.
정해진 Organism를 구성하고 있는 컴포넌트를 잘게 나눠 Atom을 먼저 구현하고, Atom들이 모여서 Molecule이 되고, Molecule이 모여서 Organism가 되도록 기준을 잡았습니다.
하지만 시간이 지날수록 각 단계를 나누는 기준이 굉장히 어려워졌던 것 같습니다.
Atomic Design Pattern을 쓰면서 많은 컴포넌트들이 생성되고 협업을 위해서는 다른 컴포넌트들이 무엇이 있는지 확인할 수 있는 장치가 필요했습니다. 그래서 Storybook을 도입하게 되었습니다.
이를 통해 많은 컴포넌트를 시각적인 Testing이 가능했고 다른 팀원과 원활한 소통이 가능해졌습니다.
Atomic Design을 적용해 계층 구조를 기반으로 컴포넌트를 작성하기 위해서 atoms - molecules - organisms - templates - pages 로 FE 디렉터리 구조를 잡았습니다.
client
├── env
├── public
│ └── imgs
└── src
├── common
│ ├── constants
│ ├── socket
│ │ ├── emits
│ │ └── types
│ ├── store
│ │ ├── actions
│ │ ├── reducers
│ │ ├── sagas
│ │ └── types
│ ├── theme
│ └── utils
├── components
│ ├── atoms
│ ├── molecules
│ ├── organisms
│ └── templates
├── pages
└── shared
ActiveProfileImg 컴포넌트는 Side Bar의 DM 목록과 화면 우측 상단 로그인된 사용자의 상태를 나타내는 부분에 사용되는 컴포넌트입니다.
atoms
에 가장 간단한 ActiveLight
컴포넌트와 ProfileImg
컴포넌트 구현했습니다./* client\src\components\atoms\ProfileImg\ProfileImg.tsx */
import React from 'react';
import styled from 'styled-components';
import Logo from '@imgs/logo.png';
import { color } from '@theme/index';
interface ProfileImgProps {
size?: 'small' | 'medium' | 'large';
isHover?: boolean;
src?: string;
}
// 생략
const ProfileImg: React.FC<ProfileImgProps> = ({ size = 'medium', isHover = false, src = Logo, ...props }) => {
return (
<ProfileImgContainter size={size} isHover={isHover} {...props}>
<Img size={size} isHover={isHover} src={src}></Img>
</ProfileImgContainter>
);
};
export { ProfileImg, ProfileImgProps };
ActiveLight.stories
와 ProfileImg.stories
작성했으며, 이를 통해 각 컴포넌트의 디자인을 수정하면서 효율적으로 개발을 했습니다./* client\src\components\atoms\ProfileImg\ProfileImg.stories.tsx */
import React from 'react';
import { Story, Meta } from '@storybook/react/types-6-0';
import { ProfileImg, ProfileImgProps } from './ProfileImg';
export default {
title: 'atom/ProfileImg',
component: ProfileImg
} as Meta;
const Template: Story<ProfileImgProps> = (args) => <ProfileImg {...args} />;
export const LargeProfileImg = Template.bind({});
LargeProfileImg.args = {
size: 'large'
};
export const MediumProfileImg = Template.bind({});
MediumProfileImg.args = {
size: 'medium'
};
export const SmallProfileImg = Template.bind({});
SmallProfileImg.args = {
size: 'small'
};
처음에는 Atomic Design의 각 단계를 어떤 기준으로 나눠야 할지 고민이 많았는데, 직접 가장 작은 컴포넌트부터 제작하고, 제작된 컴포넌트들을 합쳐서 더 큰 뷰를 구성하는 과정에서 블록을 조립해나가듯 합쳐가는 과정이 재미있게 느껴졌습니다.
또한, 모달창이나 버튼과 같이 공통으로 사용되는 컴포넌트들을 만들어 재사용함으로써 코드 중복을 줄이고 생산성을 높일 수 있었으며, Storybook 또한 Atoms나 Molecules 같은 작은 단위의 컴포넌트들을 제작할 때 많은 도움이 되었습니다.
하지만 구조적인 부분에 있어서 이렇게까지 나눌 필요가 있었나? 하는 생각은 떠나지 않았던 것 같습니다.
Atomic Design을 도입하면서 화면 전체에서부터 아래로 내려가며 디자인을 설계하던 기존 방식과는 다르게 작은 컴포넌트부터 제작함으로써 훨씬 체계적으로 컴포넌트 설계를 진행할 수 있었습니다. 또한 Atomic Design과 Storybook은 협업 관점에서 굉장히 잘 어울린다고 생각했습니다.
한편으로 Atomic Design의 각 단계를 어떤 기준으로 나눠야 할지 고민하면서 꼭 5단계가 아니더라도 프로젝트의 규모나 필요에 따라서 단계를 줄여서 사용해볼 수 있을 것 같다는 생각이 들었습니다. 실제로 기업에서도 Atomic Design을 부분적으로 도입해 프로젝트에 맞게 전략적으로 단계를 나누는 경우가 있다고 들었습니다.
Atomic Design의 장점을 최대한으로 가져가기 위해 어떻게 하면 재사용 가능한 컴포넌트를 만들 수 있을지, 단계는 어떻게 나눠야 할지 고민할 수 있었던 좋은 시간이었으며, 앞으로 Atomic Design을 적용해야 할 일이 있다면 이번 프로젝트를 하면서 느꼈던 고민이 많은 도움이 되리라 생각됩니다.