공통 디자인 시스템 (npm 패키지)

작품 설명
여러 제품이 함께 쓰는 프론트엔드 공통 패키지를 프리랜서 프로젝트로 단독 설계·개발하고 npm에 공개 배포했습니다. 토큰(tokens), 프리미티브 컴포넌트(ui), 조합 패턴(patterns) 3개 패키지로 나눴고, 세 패키지는 항상 같은 버전으로 함께 배포합니다. 현재 v0.1.35입니다.
기획 의도
목표는 같은 브랜드 아래의 서로 다른 프로젝트가 하나의 제품처럼 보이도록, 통일된 컴포넌트를 함께 쓰는 것이었습니다. 그래서 버튼·입력·모달 같은 화면 요소를 한 번 만들어 npm 패키지로 배포하고, 브랜드 안의 어느 프로젝트든 설치만 하면 같은 모양과 동작의 컴포넌트를 쓰도록 했습니다. 여러 프로젝트에서 쓰이려면 쓰는 쪽 환경을 가정하지 않아야 했습니다. 브랜드색은 하나만 받아 나머지 단계를 파생하고, 라우터·폰트·아이콘에는 종속되지 않게 했으며, 서버 렌더링(Next.js)과 브라우저 렌더링(Vite) 양쪽에서 빌드가 통과해야만 배포되게 했습니다. 버튼 색 대비, 오류 상태 표시, 서버 렌더링 첫 화면처럼 프로젝트마다 놓치기 쉬운 예외 상황을 패키지 안에서 한 번 처리해, 쓰는 쪽은 신경 쓰지 않아도 되게 했습니다.
패키지 구성
tokens · 디자인 토큰
∙
테마, 브랜드색 파생, 타이포 스케일, Tailwind preset
∙
런타임 의존성 없음
ui · 프리미티브 컴포넌트
∙
버튼, 입력, 선택, 날짜·시간 피커, 모달, 알림 등 프리미티브 26종
∙
라우터, 폰트, 아이콘에 종속되지 않도록 설계
patterns · 조합 패턴
∙
DataTable, AppShell, Sidebar, Footer, TrendChart 등 화면 단위 조합 패턴
설계 결정
브랜드색 하나로 테마 만들기
∙
Provider의 필수 입력은 브랜드색 하나이고, hover·active·focus·연한 배경 단계를 명도·채도 규칙으로 파생
∙
브랜드색 위 글자색(on-brand)을 대비로 흰색/검정 중 자동 판정해, 브랜드색을 밝은 색에서 어두운 색으로 바꿔도 버튼 라벨이 읽히도록 함
∙
단계를 직접 지정하면 비워 둔 단계만 파생하고, 실행 중 테마 변경(setTheme)도 지원
서버 렌더링과 브라우저 렌더링 모두에서 같은 결과
∙
테마 변수를 렌더 중에 style로 출력해 서버와 브라우저 출력이 같도록 함 — useEffect로 넣으면 첫 화면에 기본색이 보였다가 바뀌는 문제를 피함
∙
Next.js(서버 렌더링)와 Vite(브라우저 렌더링) 확인용 앱 두 개를 두고, 양쪽 빌드가 모두 통과해야 배포
라우터 링크를 한 곳에서 연결
∙
라우터 링크 컴포넌트를 Provider에서 한 번만 받아 Sidebar, Footer, Pagination이 같은 링크를 공유
품질 검사와 배포
검사를 통과해야만 배포
∙
pnpm verify 한 번으로 린트, 타입 검사, 테스트·커버리지, 빌드, 보안 감사(high 이상)를 실행
∙
패키지별 커버리지 기준 80%(patterns 분기 75%), 테스트 파일 21개
∙
배포 스크립트는 검사를 먼저 돌리고, 실패하거나 커밋하지 않은 변경이 있으면 아무것도 올리지 않음 — 세 패키지 중 일부만 올라가 버전이 어긋나는 상황을 막기 위함
배포 경로 정리
∙
계정 2FA가 보안키·패스키일 때 pnpm publish가 브라우저 인증을 처리하지 못해 실패하는 문제를 확인하고, 묶기(pnpm pack)와 올리기(npm publish)를 분리
∙
--stage 옵션으로 바로 공개하지 않고 대기시킨 뒤 사람이 2FA로 승인하는 배포 방식 추가
∙
npmjs 공개 레지스트리로 옮겨 사용하는 쪽이 토큰 없이 설치할 수 있도록 변경
AI 에이전트용 디자인 시스템 조회 MCP
에이전트가 기존 컴포넌트를 찾아 쓰도록
∙
which_component, list_components, get_component, get_tokens, search_guidelines 5개 도구를 가진 MCP 서버를 직접 구현
∙
저장소 소스를 실행 시점에 직접 읽어 별도 데이터 갱신 없이 항상 최신 API를 반환
∙
이름만으로 구분하기 어려운 컴포넌트(모달 여러 종, 배지와 칩 등)를 나란히 비교해 알려줘, 에이전트가 props를 추측하거나 비슷한 컴포넌트를 새로 만들지 않도록 함