1. 들어가며
프로젝트를 진행하다 보면 하나의 화면을 완성하는 과정이 생각보다 여러 작업으로 나뉘어 있는 경우가 많습니다.
저는 프로젝트에서 화면의 마크업, 스타일, 인터랙션 등 UI 구현을 담당하고 있습니다. 이전까지는 퍼블리싱 리포지터리와 개발 리포지터리가 물리적으로 분리되어 있어서, 저는 퍼블리싱 리포지터리 안에서만 작업을 진행했습니다. 화면 마크업과 스타일을 완성해 두면 개발자가 이를 개발 리포지터리로 옮겨와 로직과 API를 붙이는 방식이었습니다.
그런데 이번 프로젝트에서는 구조가 달라졌습니다. 퍼블리셔가 개발 리포지터리에 직접 들어가서, 브랜치를 나누어 개발자와 병렬로 작업을 진행하는 방식으로 바뀐 것입니다. 같은 리포지터리, 같은 코드베이스를 공유하게 되면서 얻는 이점도 있었지만, 동시에 새로운 어려움도 생겼습니다. 같은 화면을 같은 저장소 안에서 작업하고 있음에도 불구하고, 서로의 브랜치 작업이 아직 합쳐지기 전까지는 실제로 완성된 화면을 함께 확인하기 어려웠던 것입니다.
퍼블리싱 브랜치에서 작업하는 시점에는 아직 API가 준비되지 않았거나, 다른 브랜치에서 진행 중인 상태 관리 로직이 완성되지 않은 경우도 있었습니다.
이 글에서는 이렇게 하나의 리포지터리 안에서 퍼블리싱과 개발이 브랜치 단위로 나뉘어 진행되는 환경에서, UI를 보다 독립적으로 개발하고 확인하기 위해 Storybook을 적용했던 경험과 실제 적용 과정에서 겪었던 문제를 정리하고자 합니다.
2. 문제 정의: 화면 하나를 확인하기 위해 필요한 것들
같은 리포지터리 안에서 브랜치로 작업이 나뉘어 있다 보니, 오히려 "합치면 바로 볼 수 있을 것 같은데 실제로는 그렇지 않다"는 종류의 답답함이 있었습니다. 제 브랜치의 마크업과 개발 브랜치의 로직이 아직 병합되지 않은 시점에는, 완성된 화면을 실제로 함께 확인할 방법이 마땅치 않았습니다.
이 상태에서 특정 화면을 실제 환경에서 확인하려면 여러 조건이 필요했습니다.
먼저 해당 화면이 포함된 개발 환경을 실행해야 했고, 경우에 따라 여러 애플리케이션 또는 개발 서버가 함께 실행되어야 했습니다. 또한 로그인 상태나 API 응답이 필요한 화면의 경우, 원하는 상태를 만들기 위해 실제 데이터를 준비해야 했습니다.
하지만 아직 병합되지 않은 브랜치에서 작업하는 시점에는 이러한 조건이 항상 준비되어 있는 것은 아니었습니다.
예를 들어 API 스펙은 정의되어 있지만 실제 서버가 아직 구현되지 않았거나, 특정 상태의 데이터를 의도적으로 만들기 어려운 경우가 있었습니다. 빈 목록, 특정 버튼의 노출 조건, 신청 완료 상태, 에러 상태 등은 실제 서비스를 실행한다고 해서 항상 쉽게 확인할 수 있는 것이 아니었습니다.
결국 단순히 특정 UI 상태를 확인하기 위해 전체 개발 환경을 실행하고, 원하는 화면까지 이동한 뒤 특정 조건을 만들어야 하는 상황이 발생했습니다.
이 과정에서 다음과 같은 문제가 있었습니다.
- 퍼블리싱 작업이 개발 환경의 준비 상태에 영향을 받습니다.
- 특정 UI 상태를 반복적으로 재현하기 어렵습니다.
- API 데이터가 변경될 때마다 화면 확인 과정도 영향을 받습니다.
- 화면 상태를 확인하려면 매번 개발 쪽에 데이터나 환경이 준비되기를 기다리거나 요청해야 합니다.
특히 UI 작업에서는 정상 상태뿐만 아니라 빈 상태, 예외 상태, 로딩 상태 등 다양한 상황을 함께 확인해야 합니다. 하지만 실제 개발 환경에서는 이러한 상태를 매번 의도적으로 만드는 데 시간이 필요했습니다.
따라서 필요한 것은 단순히 화면을 별도로 띄우는 것이 아니라, 특정 컴포넌트와 화면의 상태를 언제든 동일하게 재현할 수 있는 환경이었습니다.
3. 해결 방법: Storybook을 통한 독립적인 UI 개발 환경 구성
이 문제를 해결하기 위해 Storybook을 적용했습니다.
특히 API가 아직 준비되지 않았거나, 심하게는 로그인 자체가 되지 않아 개발 환경으로는 화면 진입조차 안 되는 시점에도, 개발 쪽에 데이터나 환경을 요청하지 않고 제 로컬에서 바로 화면 상태를 확인하고 싶었던 것이 가장 큰 동기였습니다.
Storybook은 컴포넌트를 애플리케이션 전체 환경과 분리하여 독립적으로 렌더링하고 확인할 수 있는 도구입니다. 하지만 단순히 컴포넌트를 목록으로 확인하는 용도로 사용하는 것보다, 프로젝트의 실제 UI 개발 환경과 최대한 비슷한 상태를 만드는 것을 목표로 했습니다.
이를 위해 크게 다음과 같은 방향으로 구성했습니다.
- 실제 프로젝트와 동일한 Vite 설정을 Storybook에서도 사용할 것
- 실제 애플리케이션에서 사용하는 Provider 환경을 재현할 것
- 개발 쪽 API나 로그인 환경이 준비되지 않았을 때도, 요청 없이 제 로컬에서 화면 상태를 바로 확인할 수 있을 것
3.1 실제 프로젝트 환경과 Storybook 환경을 맞추기
Storybook을 별도의 환경으로 구성하면 편리하지만, 실제 애플리케이션과 설정이 달라질 경우 또 다른 문제가 발생할 수 있습니다.
예를 들어 Storybook에서는 정상적으로 보이지만 실제 애플리케이션에서는 alias 설정이나 스타일 설정이 다르게 적용되는 경우입니다.
이를 줄이기 위해 프로젝트에서 사용하고 있던 Vite 설정을 Storybook에서도 함께 사용할 수 있도록 구성했습니다.
framework: {
name: '@storybook/react-vite',
options: {},
},
viteFinal: async (config) => {
const { config: userConfig } = await loadConfigFromFile(
path.resolve(__dirname, '../vite.config.mts'),
);
return mergeConfig(config, {
...userConfig,
resolve: {
alias: {
'~': path.resolve(__dirname, '../src'),
},
},
});
},
이렇게 구성함으로써 실제 프로젝트에서 사용하는 alias, 스타일 처리 방식 등의 설정을 Storybook에서도 최대한 동일하게 유지할 수 있었습니다.
또한 Storybook의 preview 설정에서는 실제 애플리케이션에서 사용하던 Provider 환경을 함께 구성했습니다.
예를 들어 테마를 제공하는 Provider, 데이터 요청을 관리하는 Provider, 알림 메시지를 처리하는 Provider 등을 Storybook 환경에서도 적용했습니다.
이 과정은 단순히 Storybook을 실행하기 위한 설정이라기보다, Storybook과 실제 애플리케이션의 차이를 줄이기 위한 작업이었습니다.
UI를 독립적으로 확인하는 환경을 만들었지만, 실제 애플리케이션과 너무 다른 환경이라면 결국 두 번의 검증이 필요하기 때문입니다.
3.2 API 의존성을 줄이기 위한 Mock 데이터 구성
Storybook을 적용하면서 가장 중요하게 생각했던 부분은 API 의존성을 줄이는 것이었습니다.
실제 화면의 상당수는 서버에서 내려오는 데이터를 기반으로 구성됩니다. 따라서 단순히 컴포넌트만 Storybook에서 렌더링하는 방식으로는 다양한 상태를 충분히 재현하기 어려웠습니다.
처음에는 Story 파일마다 필요한 데이터를 직접 작성하는 방식도 고려할 수 있었습니다.
export const Default = {
args: {
data: {
// Story마다 필요한 Mock 데이터 작성
},
},
};
하지만 화면이 늘어나고 상태가 다양해질수록 동일하거나 비슷한 Mock 데이터가 여러 Story에 반복될 가능성이 높았습니다.
그래서 Mock 데이터를 역할별로 분리하는 방식을 적용했습니다.
src
└── mocks
├── fixtures
└── handlers
fixtures에는 화면에서 사용하는 대표적인 데이터를 정리하고, handlers에서는 실제 API 요청을 가로채 원하는 데이터를 반환하도록 구성했습니다.
이렇게 하면 하나의 화면에 대해서도 여러 상태를 비교적 명확하게 분리할 수 있었습니다.
예를 들어 다음과 같이 구성할 수 있습니다.
화면
├── 기본 상태
├── 빈 상태
├── 특정 조건 상태
└── 에러 상태
Storybook에서는 각각의 상태를 독립적인 Story로 만들어 바로 확인할 수 있도록 했습니다.
결과적으로 실제 백엔드가 준비되지 않은 상태에서도 UI 작업을 진행할 수 있었고, 특정 상태를 확인하기 위해 매번 실제 환경에서 데이터를 만들어야 하는 과정도 줄일 수 있었습니다.
4. 실제 적용 과정에서 겪은 문제
Storybook을 적용한다고 해서 퍼블리싱과 개발 사이의 모든 문제가 해결되는 것은 아니었습니다.
오히려 Storybook을 실제 프로젝트에 적용하면서, 기존에는 크게 의식하지 않았던 문제들이 더 명확하게 드러나기도 했습니다.
4.1 컴포넌트 리네이밍으로 인해 발생한 Story 오류
프로젝트를 진행하면서 컴포넌트의 역할을 명확하게 구분하기 위해 네이밍을 정리하는 작업이 있었습니다.
기존 컴포넌트 이름에 역할을 구분할 수 있는 접미사를 추가하면서 여러 파일의 이름과 import 경로가 변경되었습니다.
예를 들어 다음과 같은 변경이 발생할 수 있습니다.
JobDetail
↓
JobDetailWkr
이 과정에서 이미 작성되어 있던 Story 파일의 import 경로도 함께 영향을 받았습니다.
// 변경 전
import { JobDetail } from './JobDetail';
// 변경 후
import { JobDetail } from './JobDetailWkr';
컴포넌트 하나의 이름을 변경하는 작업 자체는 크지 않을 수 있습니다. 하지만 해당 컴포넌트를 참조하는 Story 파일이 존재하는 경우, 변경 범위는 예상보다 넓어질 수 있습니다.
이전처럼 퍼블리싱과 개발 리포지터리가 분리되어 있었다면 이런 변경은 제가 작업 결과물을 다시 가져오는 시점에야 알 수 있었을 것입니다. 하지만 같은 리포지터리 안에서 브랜치로 작업하다 보니, 개발 브랜치의 변경 사항이 병합 시점에 곧바로 충돌로 드러났고, 그 자리에서 바로 맞춰야 했습니다.
이 경험을 통해 Storybook 역시 실제 프로젝트 코드와 분리된 별도의 산출물이 아니라, 프로젝트 구조 변화의 영향을 함께 받는 코드라는 점을 체감했습니다.
또한 파일 구조나 네이밍과 같이 여러 작업자에게 영향을 주는 변경은 가능한 한 사전에 공유하는 것이 중요하다고 느꼈습니다.
Story 파일을 유지보수하는 입장에서는 작은 사전 공유만으로도 불필요한 수정 작업을 줄일 수 있기 때문입니다.
4.2 모든 것을 Storybook에서 확인할 수 있는 것은 아니었습니다
Storybook을 적용하면서 한 가지 분명하게 느낀 점은, Storybook이 실제 애플리케이션을 완전히 대체하는 도구는 아니라는 것이었습니다.
컴포넌트 단위의 UI 상태를 확인하는 데는 매우 효율적이지만, 여러 화면이 연결되는 흐름이나 실제 환경에 의존하는 기능은 별도의 확인이 필요했습니다.
예를 들어 다음과 같은 영역입니다.
- 화면 간 이동
- 여러 애플리케이션 간 전환
- 실제 API 응답과 UI의 연결
- 실제 실행 환경에서만 발생하는 문제
- 외부 환경과의 메시지 처리
이러한 부분은 Storybook의 독립적인 환경에서는 확인하기 어렵거나, 별도의 Mock 구현이 필요했습니다.
처음에는 가능한 많은 화면을 Storybook에서 확인하는 것이 좋을 것이라고 생각했습니다. 하지만 실제로 적용해 보니 모든 것을 Storybook으로 옮기는 것 자체가 목적이 되어서는 안 된다고 판단했습니다.
결국 역할을 구분하는 방식이 더 효율적이었습니다.
Storybook
→ 컴포넌트 및 화면의 상태 확인
→ UI 리뷰
→ 다양한 데이터 상태 재현
→ 퍼블리싱 결과 공유
실제 개발 환경
→ 화면 간 이동 확인
→ 실제 API 연동 확인
→ 전체 사용자 흐름 확인
→ 실제 실행 환경 검증
Storybook과 실제 애플리케이션은 경쟁 관계가 아니라 서로 다른 목적을 가진 검증 환경으로 사용하는 것이 적절했습니다.
5. 적용 전후 비교
|
구분 |
기존 방식 |
Storybook 적용 후 |
|---|---|---|
|
실행 단위 |
전체 개발 환경 실행 필요 |
필요한 컴포넌트 또는 화면만 실행 |
|
데이터 준비 |
실제 API 및 로그인 상태에 영향 |
Mock 데이터로 원하는 상태 구성 |
|
상태 재현 |
특정 상태를 직접 만들어야 함 |
Story별로 상태를 즉시 확인 |
|
UI 확인 |
원하는 화면까지 이동 필요 |
해당 Story로 바로 접근 |
|
재확인 방식 |
매번 시나리오를 다시 밟아야 함 |
저장해 둔 Story로 즉시 재현 가능 |
|
적합한 영역 |
전체 흐름 및 실제 연동 확인 |
UI 상태 및 컴포넌트 단위 검증 |
Storybook을 도입하면서 가장 크게 달라진 점은 단순히 화면을 더 쉽게 볼 수 있게 되었다는 것이 아니었습니다.
기존에는 특정 화면 상태를 확인하려면 환경을 준비하고 해당 상태를 만드는 과정이 먼저 필요했습니다.
하지만 Storybook에서는 필요한 상태 자체를 하나의 Story로 만들어 둘 수 있었습니다.
즉, 화면을 확인하기 위한 준비 과정이 줄어들었고, 한 번 정의한 상태는 이후에도 동일하게 재현할 수 있게 되었습니다.
6. 이 경험을 통해 배운 점
6.1 Storybook의 가장 큰 장점은 UI 자체보다 상태 재현에 있었습니다
처음에는 Storybook을 컴포넌트를 독립적으로 확인하는 도구라고 생각했습니다.
하지만 실제 프로젝트에 적용하면서 가장 큰 장점은 특정 상태를 반복적으로 재현할 수 있다는 점이라고 느꼈습니다.
정상 상태뿐만 아니라 빈 화면이나 예외 상태처럼 실제 환경에서는 만들기 어려운 상황도 미리 정의할 수 있었습니다.
한 번 만든 Story는 이후에도 동일한 상태를 유지할 수 있기 때문에 UI 수정이나 리뷰 과정에서도 반복적으로 활용할 수 있었습니다.
6.2 개발 환경이 준비되지 않았을 때도 작업을 멈추지 않을 수 있었습니다
브랜치가 아직 병합되지 않았거나, 심하게는 로그인 자체가 되지 않아 개발 환경으로는 화면 진입조차 안 되는 시점이 생각보다 자주 있었습니다.
이전 같았으면 이런 상황에서는 개발 쪽에 데이터나 계정 상태를 요청하거나, 환경이 정상화될 때까지 작업을 미뤄야 했을 것입니다.
하지만 Storybook과 Mock 데이터를 함께 쓰면서부터는, 이런 상황에서도 제 로컬에서 원하는 화면 상태를 직접 만들어 확인할 수 있었습니다. 필요한 데이터를 요청하거나 환경이 복구되기를 기다리지 않고도 UI 작업을 계속 이어갈 수 있었던 것입니다.
이 경험을 통해 느낀 것은, Storybook의 가치가 화면을 예쁘게 보여주는 데 있다기보다 제 작업이 다른 쪽의 준비 상태에 최대한 덜 의존하도록 만들어 준다는 데 있다는 점이었습니다.
6.3 모든 화면을 Storybook으로 만드는 것이 목표는 아니었습니다
Storybook을 적용하면서 가장 중요하게 생각하게 된 점은 도구를 얼마나 많이 적용했는지가 아니라, 어디에 적용했을 때 가장 효과적인가였습니다.
UI 상태가 많고 실제 데이터 없이도 충분히 확인할 수 있는 화면은 Storybook의 효과가 컸습니다.
반면 여러 화면의 흐름이나 실제 환경과의 연동이 중요한 기능은 기존 개발 환경에서 확인하는 것이 더 적절했습니다.
따라서 이후에는 모든 화면을 동일한 방식으로 관리하기보다는, 병렬 작업 과정에서 확인 비용이 큰 화면부터 Storybook으로 분리하는 방향이 효율적이라고 생각하게 되었습니다.
7. 마치며
이번 경험을 통해 Storybook은 단순히 UI 컴포넌트를 보여주는 도구가 아니라, 개발 환경이 아직 준비되지 않은 시점에도 제가 맡은 UI 작업을 독립적으로 검증할 수 있게 해주는 확인 환경으로 활용할 수 있다는 점을 알게 되었습니다.
특히 API나 로그인 같은 개발 쪽 환경이 준비되지 않은 상황에서 Mock 데이터와 함께 사용하면, 개발 쪽에 요청하거나 기다리지 않고도 특정 상태를 반복적으로 재현하며 작업을 이어갈 수 있어 효과적이었습니다.
물론 Storybook만으로 모든 문제를 해결할 수는 없습니다. 실제 API 연동이나 화면 간 흐름, 실행 환경에 의존하는 기능은 여전히 실제 개발 환경에서 확인해야 합니다.
하지만 모든 것을 하나의 환경에서 해결하려 하기보다는, UI 상태 확인은 Storybook에서, 실제 흐름과 연동은 실제 애플리케이션에서 확인하는 방식으로 역할을 나누는 것이 더 효율적이었습니다.
결국 이번 경험에서 가장 크게 느낀 점은 Storybook이라는 특정 도구 자체보다도, 제 작업이 다른 쪽의 준비 상태에 얼마나 덜 의존하며 진행될 수 있는가가 더 중요한 문제라는 점입니다.
앞으로도 새로운 프로젝트를 진행할 때 모든 화면을 처음부터 Storybook으로 구성하기보다는, 개발 환경이나 데이터에 대한 의존성이 높아 UI 확인 비용이 큰 영역부터 우선 적용해 볼 생각입니다. 그렇게 쌓인 Story가 단순한 테스트용 화면이 아니라, UI를 확인하고 공유하기 위한 하나의 공통 기준으로 활용될 수 있다고 생각합니다.
nature