shadcn/ui와 디자인 시스템: 컴포넌트 소유권 관리
복사해서 소유하는 컴포넌트를 오래 관리하는 규칙

핵심 요약
- shadcn/ui는 복사 방식이라 자유도가 높은 대신 업스트림 변경 추적 부담이 따른다.
- 복사한 원본(components/ui)과 화면 전용 조합 컴포넌트를 분리해 원본 직접 수정을 최소화한다.
- diff를 주기적으로 확인하고 --overwrite는 로컬 변경 유실 위험을 검토한 뒤 선택적으로 쓴다.
- 색·반경 등 하드코딩을 줄이고 토큰·variant·화면 전용 스타일을 층으로 나눠 테마 일관성을 지킨다.
shadcn/ui는 컴포넌트를 패키지로 설치하지 않고 CLI로 프로젝트에 복사해 온다. 그래서 스타일을 자유롭게 고칠 수 있지만, 반대로 "원본이 개선되면 어떻게 따라갈 것인가"라는 문제가 생긴다. 처음에는 컴포넌트 몇 개를 복사하는 것으로 끝나지만, 사내 디자인 시스템으로 키우면 포크와 원본의 차이를 추적해야 하는 부담이 커진다. 이 글은 복사 기반 컴포넌트를 오래 관리하기 위한 소유권 규칙을 정리한다.
복사 기반 컴포넌트의 장점과 부담
복사 방식의 장점은 명확하다. 의존성 버전에 묶이지 않고, 접근성 프리미티브 위에 스타일을 직접 제어할 수 있으며, 필요한 컴포넌트만 가져온다. 부담도 명확하다. 업스트림이 버그를 고치거나 API를 개선해도 자동으로 반영되지 않고, 프로젝트마다 조금씩 다른 변형이 쌓이면 "사내 표준"이 사라진다.
컴포넌트 소유권과 수정 규칙 정하기
가장 먼저 정할 것은 "누가 무엇을 고칠 수 있는가"다. 공통 컴포넌트와 특정 화면 전용 변형을 구분하지 않으면, 한 화면을 위한 수정이 다른 화면을 깨뜨린다.
공통 컴포넌트와 로컬 변형 구분
실무에서는 다음 규칙이 잘 동작한다.
- 복사한 원본 컴포넌트는
components/ui에 두고, 스타일 변형은variantprop이나 클래스 유틸로 표현한다. - 특정 화면에서만 필요한 조합은 별도 조합 컴포넌트로 감싸고 원본을 직접 고치지 않는다.
- 원본에 손을 대야 한다면 이유를 주석과 PR 설명에 남기고, 이후 업스트림 비교 시 확인할 수 있게 한다.
이 규칙이 없으면 컴포넌트 하나가 화면마다 다른 모양으로 갈라진다.
업스트림 변경을 반영하는 방법
복사 방식에서 원본 변경을 따라가는 방법은 "차이를 계산하는 것"에서 출발한다. shadcn/ui 공식 CLI 문서에 따르면 shadcn diff 명령은 로컬 components/ui 파일과 업스트림 레지스트리의 차이를 비교해 주므로, 이를 주기적으로 돌려 바뀐 파일 목록을 만든다.
npx shadcn@latest diff button
npx shadcn@latest add button --overwrite
# 업데이트가 있으면 diff를 보고 선택 반영
npx shadcn@latest add dialog --overwrite
중요한 것은 --overwrite를 습관적으로 쓰지 않는 것이다. 로컬 수정분이 사라질 수 있으므로, diff를 읽고 "업스트림 변경을 받을지, 로컬 변형을 유지할지"를 파일 단위로 결정한다. 컴포넌트를 주기적으로 재검토하는 주기를 정해 두면 이 작업이 몰아서 터지지 않는다.
디자인 토큰과 테마 일관성
복사 기반 컴포넌트는 스타일이 CSS 변수와 Tailwind 유틸리티로 표현되므로, 색·간격·반경 같은 값을 토큰으로 모아 두는 것이 일관성의 핵심이다. 컴포넌트 안에 하드코딩된 색상이 늘어나면 다크 모드나 테마 전환이 깨진다.
| 구분 | 두는 위치 | 예시 |
|---|---|---|
| 색·반경 토큰 | 전역 CSS 변수/테마 | --primary, --radius |
| 컴포넌트 변형 | 컴포넌트 내 variant 정의 | default, outline, ghost |
| 화면 전용 스타일 | 조합 컴포넌트/유틸 클래스 | 레이아웃 조합 |
Tailwind v4로 올린 프로젝트라면 테마 토큰이 CSS에 모이므로 이 구조가 더 자연스럽다. 관련 내용은 Tailwind CSS v4 마이그레이션 글에서 다룬 @theme 구성과 연결된다.
문서화와 사용 가이드
컴포넌트가 늘면 "이 상황에는 무엇을 쓰는가"가 문제가 된다. 스토리북으로 각 컴포넌트의 상태를 문서화하고, 어떤 variant를 언제 쓰는지 예시를 함께 둔다. 문서가 코드와 어긋나지 않도록 스토리를 컴포넌트 옆에 두고, 변경 시 함께 수정한다. 컴포넌트 수정 후 키보드 포커스와 명도 대비, ARIA 레이블이 깨지지 않았는지 검증하는 절차는 WCAG 2.2 기준 접근성 점검 가이드와 함께 적용하면 체계적으로 관리할 수 있다.
컴포넌트 카탈로그와 릴리스 흐름
디자인 시스템이 커지면 컴포넌트도 소프트웨어처럼 릴리스 단위로 관리하는 편이 좋다. 공통 컴포넌트에 호환성을 깨는 변경이 필요하면 영향을 받는 화면을 먼저 파악하고 변경을 예고한다. 컴포넌트 목록과 사용처를 자동으로 뽑아 둘 수 있으면 누락을 줄인다. 또 deprecate 절차를 두어 옛 variant를 바로 지우지 않고 일정 기간 유지하면서 사용처를 옮긴다. 복사 기반이므로 버전 태그가 강제되지 않는 대신, 변경 로그를 남기고 소비 화면이 언제 따라왔는지 확인하는 습관이 필요하다. 카탈로그가 커질수록 "무엇이 안정 버전인가"를 표시하는 일이 중요해진다. 실험적 컴포넌트와 안정 컴포넌트를 구분해 표시하지 않으면 신규 화면이 불안정한 조합을 그대로 가져다 쓰게 된다.
문서에는 컴포넌트의 사용 예뿐 아니라 하지 말아야 할 사용법도 함께 적는다. 레이아웃 목적의 여백을 컴포넌트 prop으로 넘기지 않는다든지, 색상 토큰을 하드코딩하지 않는다는 규칙을 예시와 함께 둔다. 금지 예시가 없으면 신규 화면이 컴포넌트를 우회하는 스타일을 다시 만들어 낸다.
접근성·품질 기준 유지
shadcn/ui는 접근성 프리미티브를 기반으로 하지만, 로컬에서 스타일을 고치다 보면 포커스 표시나 키보드 동작이 깨질 수 있다. 공통 컴포넌트를 수정할 때는 키보드 탐색, 포커스 링, 스크린리더 레이블을 확인 항목으로 둔다. 이런 점검이 PR 체크리스트에 없으면 시간이 지날수록 접근성이 조용히 퇴행한다.
정리하면 복사 기반 디자인 시스템의 성패는 "원본을 얼마나 잘 따라가는가"보다 "로컬 수정 범위를 얼마나 잘 통제하는가"에 달려 있다.
초보자가 자주 실수하는 포인트
- 화면마다 원본 컴포넌트를 직접 고쳐 variant 계층이 무너진다.
- diff를 확인하지 않고 --overwrite로 업데이트해 로컬 수정분을 잃는다.
- 색상과 간격을 컴포넌트에 하드코딩해 다크 모드·테마 전환이 깨진다.
체크리스트
- components/ui 원본과 화면 전용 조합 컴포넌트의 경계를 정했다.
- 업스트림 diff를 확인하는 주기와 담당을 정했다.
- 업데이트 시 로컬 수정 유지/반영을 파일 단위로 결정하는 절차를 만들었다.
- 색·반경 등 디자인 토큰을 전역으로 모으고 하드코딩을 줄였다.
- 포커스·키보드·레이블 등 접근성 확인을 PR 체크리스트에 넣었다.
자주 묻는 질문
shadcn/ui를 npm 패키지처럼 버전으로 관리할 수 없나요?
복사 기반 방식은 버전 의존성을 갖지 않는 대신 업스트림 변경을 자동으로 받지 못한다. CLI의 diff·업데이트 기능으로 차이를 확인하는 절차를 두는 것이 대안이다.
원본 컴포넌트를 고치면 안 되나요?
금지는 아니다. 다만 공통 사용처에 영향을 주므로, 고친 이유를 남기고 업스트림 비교 시 확인할 수 있게 관리한다.
사내 디자인 시스템 문서는 무엇으로 만드나요?
컴포넌트 옆에 스토리를 두는 방식이 유지보수에 유리하다. 스토리북으로 상태와 variant를 문서화하면 코드와 문서가 함께 갱신된다.
참고 자료 · 검증 기준
위 자료와 내용을 대조한 날짜: . 도구·서비스 정책은 이후 바뀔 수 있으므로 적용 전 공식 문서를 다시 확인하세요.
이 글은 실제 실무 환경 테스트 및 검증된 기술 자료를 바탕으로 작성되었으며, 환경 변화에 따라 지속적으로 갱신됩니다.