Storybook으로 UI 컴포넌트를 독립적으로 관리하기
출처: https://www.youtube.com/watch?v=XIMON_SEge0 · 개발같은소리하네 · 8:39 · 2022-07-28
요지
- Storybook은 UI 컴포넌트를 애플리케이션에서 분리해 살펴보고, 개별적으로 개발·점검하도록 돕는 도구다.
- 컴포넌트를 코드로 분리하는 데서 더 나아가, 여러 컴포넌트와 그 상태를 한곳에서 탐색할 수 있게 한다.
- 검증된 컴포넌트를 재사용하면 반복 구현과 검증에 드는 비용을 줄일 수 있다.
- 디자이너와 개발자가 같은 컴포넌트를 보며 논의하면 디자인의 일관성을 유지하고 변경 사항을 전달하기 쉬워진다.
- 영상은 가디언의 컴포넌트 모음과 경기 정보 UI의 속성 변경을 통해 이러한 활용 방식을 보여준다.
개요
React 같은 UI 개발 도구에서는 버튼, 아바타, 댓글 등 화면의 구성 요소를 컴포넌트로 나누어 구현한다. 하지만 컴포넌트를 파일로 나누었다고 해서 무엇이 있는지, 어떤 상태를 지원하는지, 어떻게 재사용하면 되는지가 저절로 드러나지는 않는다. 실제 페이지에서 특정 컴포넌트의 모습을 확인하려면 해당 화면으로 이동하고 필요한 데이터를 준비해야 할 수도 있다.
Storybook은 이런 컴포넌트를 독립적으로 확인하는 작업 공간을 제공한다. 영상은 설치나 코드 작성보다 도입 목적과 협업상의 이점을 설명하는 입문 자료다. 마지막에는 가디언의 공개 Storybook을 살펴보며, 실제 서비스의 반복되는 UI를 한곳에 모아 관리하는 모습을 보여준다.
배경 / 사전 지식
UI 컴포넌트는 화면을 구성하는 재사용 가능한 단위다. 버튼은 표시할 문구와 클릭 동작을 받고, 아바타는 이미지와 사용자 정보를 받아 화면에 나타낼 수 있다. 같은 구조를 유지하면서 입력값을 바꾸면 여러 상황에서 같은 컴포넌트를 사용할 수 있다.
**속성(props)**은 컴포넌트에 전달하는 입력이다. 경기 정보 컴포넌트라면 리그명, 경기장, 경기 상태가 입력이 될 수 있다. 컴포넌트가 같아도 입력에 따라 화면은 달라진다. 따라서 컴포넌트 하나를 확인할 때도 대표 입력과 예외 입력을 함께 살펴봐야 한다.
캡슐화는 관련된 구조와 동작을 일정한 경계 안에 묶는 것이다. 컴포넌트가 주변 페이지에 덜 의존할수록 별도로 렌더링하고 검증하기 쉽다. 다만 로그인 정보나 테마처럼 외부 환경이 필요한 컴포넌트는 독립된 확인 환경에서도 그 조건을 제공해야 한다.
핵심 개념
독립적인 컴포넌트 확인
Storybook에서는 전체 서비스의 탐색 흐름을 거치지 않고 개별 컴포넌트를 대상으로 작업할 수 있다. 예를 들어 댓글 UI를 수정할 때 기사 페이지 전체보다 댓글의 구조와 표시 상태에 집중할 수 있다.
여기서 독립적이라는 말은 실제 서비스와 아무 관계가 없다는 뜻이 아니다. 서비스에서 사용하는 컴포넌트를 별도의 환경에서 관찰하고 다루는 것이다.
컴포넌트 목록과 상태의 문서화
컴포넌트 목록은 팀이 이미 만든 UI를 찾아보는 창구가 된다. 이름과 실제 모습을 함께 확인하면 비슷한 요소를 새로 만드는 일을 줄일 수 있다.
보충 개념으로, Storybook의 **스토리(story)**는 특정 조건에서 렌더링한 컴포넌트의 예시다. 버튼이라면 기본 상태, 비활성 상태, 긴 문구 상태 등을 구분해 생각할 수 있다. 영상은 스토리 작성 문법까지 다루지는 않는다.
재사용과 검증 비용
발표자는 개발자의 이점으로 반복 작업 감소, 검증된 코드의 활용, 개별 테스트의 용이성을 든다. 같은 버튼을 화면마다 새로 구현하면 각 구현의 동작과 디자인을 따로 확인해야 한다. 공통 버튼을 재사용하면 공통 구현에 대한 검증을 여러 화면에서 활용할 수 있다.
다만 재사용하는 코드도 변경되거나 다른 환경에 놓이면 문제가 생길 수 있다. 검증 비용이 줄어드는 것이지 검증이 없어지는 것은 아니다.
디자인 일관성과 협업
공통 컴포넌트를 중심으로 디자인을 관리하면 여러 화면에 적용할 변경 사항을 한 단위에서 논의할 수 있다. 디자이너와 개발자가 동일한 UI와 상태를 보면서 이야기하므로 추상적인 설명만 주고받을 때보다 의도를 맞추기 쉽다.
영상은 이를 사용자 경험과 브랜드 정체성의 일관성으로 연결한다. 웹과 모바일에서도 같은 디자인 원칙을 공유할 수 있지만, 서로 다른 플랫폼이 같은 컴포넌트 코드를 그대로 실행한다는 뜻으로 받아들여서는 안 된다.
작동 원리
- 반복되는 UI를 식별한다. 영상은 가디언 기사 화면에서 아바타, 시간 표시, 광고 관련 박스 등 반복되는 요소를 관찰한다. 반복되는 구조와 변경 가능한 입력을 구분하는 것이 출발점이다.
- 컴포넌트를 한곳에 모아 탐색한다. 가디언의 Storybook에서는 여러 UI를 목록에서 선택해 확인한다. 실제 서비스 화면과 컴포넌트 목록을 대응시키면 각 요소의 역할을 이해하기 쉽다.
- 특정 컴포넌트를 독립적으로 확인한다. 아바타처럼 단순한 요소는 표시 형태를 살펴보고, 경기 정보처럼 여러 입력이 있는 요소는 입력별 변화를 살펴본다.
- 입력을 바꾸어 결과를 관찰한다. 영상에서는 경기 정보 UI의 리그명을 프리미어리그에서 라리가로 바꾸고, 지정된 경기 상태를 선택한다. 이를 통해 같은 컴포넌트가 다른 데이터를 어떻게 표현하는지 확인한다.
- 공통 구현을 수정하고 사용하는 화면에 반영한다. 댓글 디자인을 바꾸는 경우 공통 댓글 컴포넌트를 수정하고, 이를 사용하는 애플리케이션에서 변경 내용을 반영한다. 실제 반영 방식은 프로젝트 구조와 배포 방식에 달려 있다.
- 사용 맥락에서도 다시 확인한다. 보충 실무 단계로, 개별 확인 후 실제 페이지의 레이아웃·데이터·상호작용과 결합해 문제가 없는지 검증한다.
코드 예시
영상에는 설치 명령이나 구현 코드가 제시되지 않는다. 아래는 영상의 경기 정보 예시를 바탕으로 만든 독립 실행 HTML 실습이다. Storybook API 사용 예제가 아니라, ‘입력을 바꾸면서 동일한 UI를 독립적으로 확인한다’는 원리를 체험하는 보충 코드다.
다음 내용을 match-preview.html로 저장하고 브라우저에서 열면 실행할 수 있다.
<!doctype html>
<html lang="ko">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>경기 정보 컴포넌트 실습</title>
<style>
body { font-family: sans-serif; margin: 32px; }
label { display: block; margin-bottom: 12px; }
article { border: 1px solid #aaa; padding: 20px; max-width: 360px; }
.live { color: #b00020; font-weight: bold; }
</style>
</head>
<body>
<label>
리그명
<input id="league" value="프리미어리그">
</label>
<label>
경기 상태
<select id="status">
<option value="scheduled">예정</option>
<option value="live">진행 중</option>
<option value="finished">종료</option>
</select>
</label>
<div id="preview" aria-live="polite"></div>
<script>
const labels = {
scheduled: '예정',
live: '진행 중',
finished: '종료'
};
function MatchCard({ league, status }) {
const card = document.createElement('article');
const heading = document.createElement('h2');
const state = document.createElement('p');
heading.textContent = league.trim() || '리그 미정';
state.textContent = labels[status] || '상태 미정';
state.className = status === 'live' ? 'live' : '';
card.append(heading, state);
return card;
}
const league = document.querySelector('#league');
const status = document.querySelector('#status');
const preview = document.querySelector('#preview');
function render() {
preview.replaceChildren(MatchCard({
league: league.value,
status: status.value
}));
}
league.addEventListener('input', render);
status.addEventListener('change', render);
render();
</script>
</body>
</html>
MatchCard는 리그명과 경기 상태를 입력으로 받는 UI 단위다. 바깥의 입력창과 선택 상자는 이 컴포넌트를 관찰하기 위한 조작 환경이다. 리그명을 라리가로 바꾸거나 상태를 ‘진행 중’으로 선택해 표시가 바뀌는지 확인한다. 리그명을 비우면 대체 문구가 나타나는지도 확인할 수 있다.
실제 Storybook을 구성할 때는 이런 대표 입력 조합을 스토리로 남겨 팀이 반복해서 확인하도록 할 수 있다. 이 실습 자체에는 Storybook의 목록 탐색, 스토리 관리, 테스트 연동 기능이 포함되어 있지 않다.
함정·실수
- 컴포넌트를 분리하기만 하면 재사용하기 쉽다고 생각한다. 특정 페이지의 데이터나 전역 상태에 강하게 의존하면 다른 곳에서 사용하기 어렵다. 필요한 입력과 외부 의존성을 명확하게 드러낸다.
- Storybook에 표시되면 테스트가 끝났다고 여긴다. 화면을 볼 수 있다는 사실과 올바른 동작이 검증되었다는 사실은 다르다. 대표 상태를 확인하고 필요한 동작 검증을 추가한다.
- 정상적인 짧은 데이터만 확인한다. 긴 이름, 빈 값, 비활성 상태 등에서 레이아웃이나 의미 전달이 깨질 수 있다. 실제 사용에서 나타날 경계 조건을 함께 준비한다.
- 공통 컴포넌트 수정이 모든 제품에 즉시 반영된다고 생각한다. 소비하는 프로젝트의 버전 갱신이나 배포가 필요할 수 있다. 변경의 전달 경로를 팀에서 정해 둔다.
- 컴포넌트를 많이 모으는 것 자체를 목표로 삼는다. 이름과 분류가 불명확하면 목록이 커질수록 찾기 어렵다. 팀이 실제로 검색하고 선택할 수 있는 구조로 관리한다.
베스트 프랙티스
다음은 영상의 재사용·개별 확인·협업 관점을 실무에 적용하기 위한 보충 지침이다.
- 버튼, 입력창, 아바타처럼 여러 화면에서 반복되는 요소부터 정리한다. 실제 재사용 수요가 있는 요소를 먼저 다루면 관리 비용 대비 효과를 확인하기 쉽다.
- 컴포넌트마다 기본 상태와 중요한 변형을 함께 보여준다. 경기 정보라면 예정·진행 중·종료 상태를 구분하고, 긴 리그명처럼 표시가 까다로운 입력도 포함한다.
- 이름과 입력 속성을 팀의 공통 용어로 정한다. 디자이너와 개발자가 같은 대상을 같은 이름으로 부를 수 있어야 목록이 협업 도구로 기능한다.
- 공통 스타일이나 테마처럼 렌더링에 필요한 환경을 재현한다. 독립된 미리보기와 실제 화면의 차이를 줄이면 확인 결과를 신뢰하기 쉬워진다.
- 공통 컴포넌트를 변경할 때 대표 사용 화면도 확인한다. 개별 컴포넌트의 정확성과 화면 전체의 적합성을 함께 검증한다.
- 작은 실험에서는 독립된 미리보기 페이지로 출발할 수 있다. 관리할 컴포넌트와 상태가 늘고 팀의 공유 필요가 커지면 Storybook 같은 체계적인 환경의 가치를 평가한다.
참고
- 원본 영상: Storybook이 뭐죠?
- 영상에서 언급한 자료: Storybook 홈페이지의 Showcase와 전체 Storybook 목록. 공개된 기업 사례를 탐색하는 경로로 소개한다.
- 영상에서 살펴본 사례: 가디언의 웹사이트와 ‘가디언 웹’ Storybook. 아바타, 경기 정보, 댓글 등의 컴포넌트를 확인한다.
- 영상에서 추가로 언급한 기업·기관 사례: Shopify, Airbnb, GitHub, NASA. 각 사례의 구현 내용까지 분석하지는 않는다.
- 원본 영상 외 자료의 정확한 URL은 제공된 자막에 명시되어 있지 않다. 화면과 사례 설명은 영상 게시 당시를 기준으로 한다.