Comfit이 Storybook을 적용하기로 한 이유

Yujin Jung·2026년 1월 1일

앱잼을 준비하면서 가장 걱정했던 건 “개발을 얼마나 빨리 하느냐”가 아니라, 팀으로 만들 때 생기는 소모였다.
기획/디자인/프론트/백엔드가 동시에 달리다 보면, 기능 하나 붙이는 데도 생각보다 시간이 많이 새는 구간이 있다.

  • “이 버튼 hover는 어떻게 되는 거였지?”
  • “모달 padding이 왜 페이지마다 달라…?”
  • “이 컴포넌트, 여기선 되는데 저 페이지에서만 깨져요”
  • “디자이너가 말한 ‘카드’가 A카드인지 B카드인지부터 다시 맞추자…”

나도 팀 프로젝트를 해보면서 느낀 건, 이런 문제들이 실력 부족 때문이 아니라 협업 구조가 불명확해서 터진다는 점이었다.
그래서 앱잼 전에 체계를 정리해놓고 싶었고, 그게 Storybook으로 이어졌다.


✔️ 팀 프로젝트에서 제일 자주 터지는 문제: “UI가 어디에 정의되어 있지?”

개인 프로젝트와 다르게 팀 프로젝트는 화면이 늘어나면서 “비슷한 UI”가 여기저기 복제된다.
기존에는 보통 이런 흐름으로 개발을 진행했다.

  1. 페이지 하나 생성
  2. 그 안에서 버튼/카드/모달을 바로 구현
  3. “다른 데서도 쓰겠네?” 싶으면 그때 컴포넌트 분리

결국 UI를 ‘코드’로 정의하고 관리하는 기준점이 필요했다.
그게 Comfit이 Storybook을 찾게 된 첫 번째 이유다.

Storybook을 쓰기 시작하면 앞으로 흐름이 아래와 같이 변경될 것이다.

  1. 컴포넌트를 먼저 생성
  2. Story에서 상태별로 확인
  3. 컴포넌트가 어느 정도 책임을 가져도 되는지 판단
  4. 그 다음 페이지에 조립

특히 앱잼처럼 UI 요구사항이 자주 바뀌는 환경에서는 페이지 안에서 수정하는 것보다 컴포넌트 단위에서 검증하는 게 훨씬 빠르다고 생각했다.


✔️ Storybook이 해결해준 핵심은 “컴포넌트의 단일 출처(Single Source of Truth)”

Storybook은 한 줄로 정리하면 이거였다.

UI 컴포넌트를 페이지랑 분리해서, 컴포넌트 자체를 전시하고 테스트하는 공간

나는 Storybook을 도입하기 전에, 컴포넌트가 항상 페이지에 붙어 있는 형태로만 작업했다.
그러다 보니 컴포넌트를 고칠 때마다 이런 식의 비효율이 발생했다.

  • 페이지 실행 → 해당 컴포넌트 있는 화면 이동 → 상태 만들어서 확인 → 다시 수정 → 다시 확인
  • props 조합이 바뀌면 또 다른 페이지 가서 확인
  • 빈 상태(empty), 로딩, 에러 상태 같은 건 페이지에서 만들기 번거로워서 “나중에…”가 됨

Storybook은 이걸 반대로 만든다.

  • 컴포넌트만 단독으로 띄워서
  • props를 바꿔가며 케이스를 정리하고
  • “정상/로딩/빈상태/에러/긴텍스트/모바일” 같은 모든 상태를 의도적으로 만들어 확인한다

이 흐름이 팀 프로젝트에서 특히 유용했던 이유는 명확했다.

☑️ “디자인 확인”이 페이지가 아니라 컴포넌트 기준이 됨

페이지는 여러 요소가 섞여 있어서 디테일 체크가 어렵다.
근데 Storybook에서는 버튼 하나, 카드 하나를 정확히 그 컴포넌트만 보고 검증할 수 있다.

☑️ “QA”가 기능 QA뿐 아니라 UI 케이스 QA까지 가능해짐

팀 프로젝트에서 자주 놓치는 케이스가 이거였다.

  • 텍스트가 길면 줄바꿈이 터지는지
  • 이미지 없으면 레이아웃이 무너지는지
  • 버튼 disabled 상태가 명확한지
  • 에러 메시지가 두 줄 이상이면 UI가 깨지는지

이런 걸 페이지에서 다 확인하려면 시간이 정말 많이 든다.
Storybook에서는 한 화면에서 “케이스”를 정리해두면 계속 재활용할 수 있다.


✔️ “그래서 우리는 왜 ‘지금’ Storybook을 붙이기로 결정했나”

결론적으로 Storybook을 도입하기로 한 결정은 “좋아 보인다”가 아니라, 앱잼의 현실적인 제약 때문에 더 확실해졌다.

☑️ 앱잼은 시간이 짧고, UI 수정이 잦다

짧은 기간에 MVP를 만들면, 기획과 디자인이 중간중간 계속 바뀐다.
이때 페이지에서만 UI를 관리하면, 변경이 생길 때마다 전체 화면을 다시 열어서 “어디가 영향 받는지” 추적해야 한다.

Storybook이 있으면 바뀐 컴포넌트의 영향 범위를 Stories 목록만 봐도 바로 알 수 있다.

☑️ 협업에서 가장 많이 깨지는 건 “컴포넌트 재사용”이다

예를 들어 팀원 A는 PrimaryButton을 만들어 쓰는데
팀원 B는 같은 버튼을 페이지에 그냥 새로 만들 수도 있다. “어디에 뭐가 있는지” 안 보여서 생기는 문제라고 생각한다.

Storybook은 “컴포넌트 카탈로그” 역할을 해서 어떤 컴포넌트를 써야 하는지 한 번에 알 수 있게 한다.

☑️ 우리는 특히 ‘상태 관리’가 정리된 UI를 만들고 싶었다

로딩, 에러, 빈 상태 처리가 흔히 “나중에”로 미뤄진다.
근데 앱잼은 데모까지 가야 해서, 이런 디테일이 결국 완성도를 가를 수 있다고 생각한다.

Storybook에서 상태를 강제로 정리해두면
개발 과정에서 “기본 상태만 있는 UI”가 되는 걸 막아줄 수 있다고 한다.


✔️ Storybook 설치 & 세팅 (React 기준 / 실습 기록)

나는 “일단 빨리 붙이고, 팀에서 쓰기 좋은 구조”를 목표로 했다.

☑️ 설치

npx storybook@latest init

실행하면 알아서 프로젝트 환경에 맞게 설정해준다.
(React/Vite/Next 등 자동 감지)

☑️ 실행

npm run storybook

기본 포트는 6006


✔️내가 실제로 만든 예시: Button 컴포넌트 스토리

☑️ Button 컴포넌트

// components/Button/Button.tsx
type ButtonProps = {
  label: string;
  variant?: 'primary' | 'secondary';
  disabled?: boolean;
  onClick?: () => void;
};

export default function Button({
  label,
  variant = 'primary',
  disabled = false,
  onClick,
}: ButtonProps) {
  return (
    <button
      disabled={disabled}
      onClick={onClick}
      className={`btn ${variant} ${disabled ? 'disabled' : ''}`}
    >
      {label}
    </button>
  );
}

☑️ Story 파일

// components/Button/Button.stories.tsx
import type { Meta, StoryObj } from '@storybook/react';
import Button from './Button';

const meta: Meta<typeof Button> = {
  title: 'components/Button',
  component: Button,
  args: {
    label: '버튼',
    variant: 'primary',
    disabled: false,
  },
};

export default meta;
type Story = StoryObj<typeof Button>;

export const Primary: Story = {};
export const Secondary: Story = { args: { variant: 'secondary' } };
export const Disabled: Story = { args: { disabled: true } };
export const LongText: Story = { args: { label: '텍스트가 엄청 길어지면 어떻게 될까 확인하기' } };

이렇게 해두면 팀이 버튼을 쓸 때 Primary/Secondary/Disabled/긴텍스트 케이스를 “페이지 실행” 없이 바로 확인 가능하다.


✔️ args를 쓰기 시작하면서 “상태 정의”가 명확해짐

Storybook에서 가장 많이 쓰게 될 기능이 바로 args이다.

export const Disabled: Story = {
  args: {
    disabled: true,
  },
};

이 한 줄이 의미하는 바는 아래와 같다.

  • 이 컴포넌트에는 disabled 상태가 존재한다
  • 이 상태는 디자인/기획적으로도 고려된 상태
  • 페이지에서 “나중에 처리”할 수 없는 상태가 된다

특히 아래 상태들을 Story로 만들어두는 게 좋을 것 같다.

  • disabled
  • loading
  • empty (데이터 없음)
  • 텍스트가 비정상적으로 긴 경우
  • 모바일 뷰포트


✔️ Viewport로 모바일 UI를 미리 검증하기

앱잼 프로젝트 특성상 모바일 UI 대응은 거의 필수다.
Storybook에서는 애드온 하나로 이걸 바로 확인할 수 있다고 한다.

// .storybook/preview.ts
import type { Preview } from '@storybook/react';

const preview: Preview = {
  parameters: {
    viewport: {
      defaultViewport: 'mobile1',
    },
  },
};

export default preview;

이렇게 설정해두면,

  • 모바일에서 버튼이 너무 커지지는 않는지
  • 텍스트가 두 줄로 깨질 때 레이아웃이 무너지지 않는지
  • 터치 영역이 너무 작지 않은지

페이지 들어가지 않고도 확인할 수 있다.


✔️ Storybook을 붙이고 나서, 실제로 체감될 변화

☑️ 컴포넌트를 수정이 더욱 용이할 것이다

예전에는 “이 컴포넌트 고치면 어디 깨질지”가 무서웠다.
Storybook에서는 관련 story를 쭉 돌려보면서 영향 범위를 빠르게 확인할 수 있을 것이다.

☑️ 디자이너 커뮤니케이션이 쉬워질 것이다

“이 카드에서 padding이 조금만 더…” 같은 피드백이 들어오면,
페이지 링크보다 Story 링크를 공유하는 게 훨씬 빨랐다.

  • 페이지: 다른 UI 요소 때문에 뭐가 문제인지 설명이 길어짐
  • Story: “이 컴포넌트 여기 부분만” 명확하게 잡힘

☑️ ‘상태’가 정리된 UI가 만들어질 것이다

로딩/빈상태/에러상태를 story로 만들어두니까
나중에 페이지 개발할 때 “이 상태 처리부터 해야지”라는 습관이 생길 것 같다.


✔️ 우리는 Storybook을 “이렇게” 운영하려고 한다 (Comfit 팀 규칙)

앱잼에서 Storybook을 그냥 설치만 해두면, 한정된 시간 안에 정신 없이 결국 안 쓰게 될 수도 있다 생각한다.
그래서 나는 운영 규칙을 최소한으로 잡는 게 중요하다고 생각한다.

  • 공용 컴포넌트는 PR 올릴 때 story도 같이 작성
  • “상태 케이스”는 최소 3개(기본/disabled/긴텍스트 or 로딩)
  • 컴포넌트 이름과 story title은 폴더 구조랑 일치
  • 디자인 변경이 생기면 먼저 story에서 수정 후 페이지 적용

Storybook 도입은 “선택”이 아니라 “비용 절감”일 것이다

Storybook을 도입하기 전엔
“이거까지 하면 시간이 더 드는 거 아닌가?”라는 생각이 컸다.

하지만 직전 기수 앱잼 팀 프로젝트를 살펴보니 그 반대였다.

  • 페이지에서 확인하느라 새는 시간
  • 수정 영향 범위 추적하느라 새는 시간
  • QA에서 UI 케이스 다시 찾느라 새는 시간
  • 디자인 커뮤니케이션 비용

이런 것들을 합치면 Storybook은 초반 투자로 전체 비용을 줄이는 도구일 것이다.
앱잼처럼 짧고 빠르게 완성해야 하는 프로젝트에서는 특히 더 효과가 크다고 느껴질 것이라 생각한다.

profile
매일매일 조금씩 성장하려 노력하는 프론트엔드 개발자입니다!

0개의 댓글