06 — 거버넌스와 RFC: 새 컴포넌트 추가 절차와 결정자
이 문서가 답하는 질문: 누가 “이 컴포넌트를 디자인 시스템에 넣자”를 결정하고, 그 결정 과정을 어떻게 문서로 남기며, contributor가 길을 잃지 않게 안내하는가. 한 줄 답 (Pyramid Top): 거버넌스는
(1) RFC 템플릿 + (2) 명시된 결정자(owners) + (3) CODEOWNERS 자동 라우팅 + (4) Definition of Done 체크리스트의 4-축으로 구성된다 — RFC는 왜·무엇을·대안·채택 전략을 강제하고, owners는 최종 결정 권한을 명시하며, CODEOWNERS는 PR을 자동으로 owner에게 배정한다.
Why — 왜 거버넌스가 필요한가
성공한 디자인 시스템 팀의 공통점은 기술이 아니라 합의 메커니즘에 있다.
| 거버넌스 없음의 증상 | 결과 |
|---|---|
| ”이 Dropdown을 시스템에 넣어달라” PR이 7개월째 리뷰 대기 | 사람들이 자기 앱에서 따로 구현 → 시스템 우회 |
| 컴포넌트는 들어왔는데 왜 이 API인지 아무도 모름 | 다음 사람이 부숨 |
| ”이건 디자인 시스템 답지 않다” 한 줄로 거절 | contributor가 다시는 안 옴 |
| 결정이 Slack DM에서 일어남 | 다른 팀이 모름, 비슷한 PR이 또 옴 |
| 풀려는 문제 | 이전 해법 | 한계 |
|---|---|---|
| ”이 컴포넌트 넣을까” 의사결정 | 팀 회의 | 회의 안 한 사람은 모름 |
| ”왜 이 API인가” 기록 | 위키·Notion | 코드와 분리 |
| 결정자 명시 | 암묵적 | contributor가 누구에게 물을지 모름 |
| 새 contributor 진입 | README | 어떤 PR이 환영되는지 안 적힘 |
RFC(Request for Comments) 제도가 위 4개를 동시에 푼다.
How — 4-축 거버넌스
각 단계가 공개되어야 한다 — Slack DM이 아니라 GitHub Discussion·PR·Issue.
What — 실제 구성
RFC 템플릿
.github/RFC_TEMPLATE.md:
---
title: <RFC 제목>
author: @raw
created: 2026-05-19
status: draft # draft → reviewing → accepted | rejected | deferred
target-release: v1.6.0
---
## Summary
<3문장 이내. 무엇을 추가/변경하려는가.>
## Motivation
<왜 이게 필요한가. 어떤 사용자 문제를 푸는가.>
<현재 시스템으로 *불가능한* 또는 *지나치게 어려운* 케이스 1~2개.>
## Detailed design
### API
```tsx
<Tooltip content="...">
<Button>Hover me</Button>
</Tooltip>
```
### Anatomy / Slots
<dl, dt 등 슬롯 구조>
### Variants & Tokens
<어떤 토큰을 사용/추가하는지>
### Accessibility
- 키보드: ...
- ARIA: ...
- Focus management: ...
### States
| State | Description |
|-------|-------------|
| default | ... |
| hover | ... |
## Alternatives considered
1. **<대안 A>** — 거절 이유: ...
2. **<대안 B>** — 거절 이유: ...
## Drawbacks
<이 제안이 *나쁜* 점. 솔직하게.>
## Adoption strategy
- 기존 사용자에게 어떤 영향?
- 마이그레이션 필요한가? codemod 동봉?
- 기존 컴포넌트(예: Popover) deprecate 하는가?
## Unresolved questions
- [ ] ...
- [ ] ...이 템플릿은 “왜”가 빠지면 진행 안 된다를 구조적으로 강제한다. Motivation과 Alternatives가 비어 있는 RFC는 reviewer가 즉시 needs-motivation 라벨을 붙인다.
RFC를 어디에 두는가
옵션 A — GitHub Discussions:
- 가벼움, 댓글 스레드 자연스러움
- 결정 시 별도 PR로
docs/rfcs/0042-tooltip.md추가 - 사용자 추천: 소~중규모 팀 (~30명 contributor)
옵션 B — 별도 RFC 레포 (org/design-system-rfcs):
- PR 자체가 RFC (
rfcs/0042-tooltip.md) - 댓글이 코드 review 도구에 — 라인별 코멘트 가능
- 사용자 추천: 대규모 팀 (100명+, Open source)
옵션 C — Issue 템플릿:
- 가장 가벼움. 작은 변경에 적합
- 큰 API 제안은 부적합
대형 디자인 시스템(Spectrum, Polaris, Carbon)은 옵션 B를 쓴다 — PR 댓글의 line-by-line 검토가 API 디테일을 잡는 데 결정적이기 때문.
결정자(Owners) 명시
OWNERS.md 또는 MAINTAINERS.md:
# Design System Maintainers
## Tier 1 — Steering Committee (최종 결정)
- @raw (Lead) — 토큰·테마·breaking change
- @alice — Accessibility·polymorphism
- @bob — 빌드·배포·CI
## Tier 2 — Domain Owners (영역 결정)
### Forms
- @charlie (Owner)
- @dave (Reviewer)
### Overlays (Tooltip, Popover, Dialog)
- @eve (Owner)
- @frank (Reviewer)
### Data Display (Table, List)
- @gina (Owner)
## RFC Decision Rules
- **추가 컴포넌트**: 해당 Domain Owner 1명 + Steering 1명 승인 → 채택
- **Breaking change (토큰·API)**: Steering 2/3 이상 승인 → 채택
- **사소한 patch**: Reviewer 1명 승인 → 머지 가능
## Veto
Steering Committee 어느 멤버든 *근거를 명시한 veto* 가능.
Veto는 *다음 Steering 회의*에서만 뒤집을 수 있다.핵심: “누가 거절할 수 있는가”가 명시되어야 한다. 거절자가 모호하면 RFC가 영원히 떠다닌다.
CODEOWNERS로 자동 라우팅
.github/CODEOWNERS:
# 전체 fallback
* @raw
# Forms
/packages/react/src/Input/ @charlie @dave
/packages/react/src/Select/ @charlie @dave
/packages/react/src/Checkbox/ @charlie
# Overlays
/packages/react/src/Tooltip/ @eve @frank
/packages/react/src/Popover/ @eve @frank
/packages/react/src/Dialog/ @eve
# Tokens — 변경에 신중
/packages/tokens/ @raw @alice
# 빌드·배포
/turbo.json @bob
/.changeset/ @bob
/.github/workflows/ @bob
# RFCs
/docs/rfcs/ @raw @alice @bobPR이 열리면 GitHub가 자동으로 해당 owner를 reviewer로 배정. owner의 승인 없이는 머지 불가(브랜치 보호 룰).
Definition of Done — 새 컴포넌트 체크리스트
.github/PULL_REQUEST_TEMPLATE/new_component.md:
# New Component: <Name>
## Linked RFC
- closes #<rfc-number>
## DoD Checklist
### Code
- [ ] `packages/react/src/<Name>/<Name>.tsx` 구현
- [ ] `<Name>.recipe.ts` (Panda) 또는 cva variants
- [ ] `<Name>.types.ts` props 타입
- [ ] forwardRef + asChild (해당하면)
### Tokens
- [ ] 모든 색·간격·라운드가 *기존 토큰*만 사용 (raw hex 0)
- [ ] 새 semantic 토큰이 필요하면 RFC에서 합의됨
### Accessibility
- [ ] 키보드 조작 (Tab, Enter, Esc, Arrow keys)
- [ ] ARIA roles·states 검증 (`axe-core` 또는 Storybook a11y addon)
- [ ] Screen reader 시나리오 1개 이상 stories에 기록
### Stories
- [ ] `<Name>.stories.tsx` — 모든 variant·state
- [ ] `<Name>.mdx` — 가이드 (When to use / When not to use / Anatomy)
- [ ] Chromatic snapshot OK (PR comment에서)
### Docs
- [ ] Figma 노드 URL `parameters.design`에 박힘
- [ ] `packages/react/CHANGELOG.md` 또는 changeset 작성
### Tests
- [ ] Unit test (`<Name>.test.tsx`) — happy path
- [ ] Interaction test (Storybook play function) — 키보드 조작체크박스가 모두 채워질 때까지 PR이 draft에 머문다. owner가 체크 안 된 항목을 보고 어디부터 봐야 할지 안다.
Contribution Guide
CONTRIBUTING.md:
# Contributing
## 어떤 PR이 환영되나
| 종류 | RFC 필요? | 절차 |
|------|----------|------|
| 버그 수정 | ❌ | 바로 PR |
| Story·문서 추가 | ❌ | 바로 PR |
| 토큰 *값* 조정 (예: blue.500의 hex) | ❌ | PR + Steering 1명 승인 |
| 토큰 *이름* 추가 | ✅ light | Discussion 코멘트 |
| 컴포넌트 *prop 추가* | ✅ light | Discussion 코멘트 |
| **새 컴포넌트** | ✅ full | RFC 템플릿 + Owner 검토 |
| **Breaking change** | ✅ full | RFC + Steering 2/3 승인 |
## 로컬 셋업
\`\`\`bash
pnpm install
pnpm dev # Storybook
pnpm test --filter @org/react
\`\`\`
## RFC 작성법
1. `.github/RFC_TEMPLATE.md` 복사
2. GitHub Discussion 카테고리 "RFC"에 새 글
3. 7일 코멘트 기간
4. Owner가 `accepted` / `rejected` / `deferred` 라벨
5. accepted 시 `docs/rfcs/####-name.md`로 PR작은 RFC 예시
docs/rfcs/0042-tooltip-delay-prop.md (단순 변경):
---
title: Tooltip — delay prop 추가
status: accepted
target-release: v1.6.0
deciders: @eve
---
## Summary
Tooltip에 `delay` (ms) prop을 추가해 hover 후 노출 지연 시간을 조절할 수 있게 한다.
## Motivation
- Form input 옆 Tooltip이 *너무 빠르게* 떠 사용자 시야 가림
- 현재 hard-coded 200ms — 일부 case에서 0 또는 500ms가 필요
## API
```tsx
<Tooltip content="..." delay={500}> // default 200Alternatives
instantboolean — 너무 거친 제어. 거절.- Global config — 컴포넌트 단위 override 불가. 거절.
Drawbacks
없음 — 추가만, 기존 동작 변함 없음.
Adoption
default 유지, codemod 불필요.
---
## What-if — 잘못 쓰면 어떻게 깨지는가
- **함정 1 — RFC가 *영원히 떠다님***: Owner가 7일 안에 답 안 함. *대응*: SLA 명시 — 7일 답 없으면 *자동으로 다음 owner*에게 escalation. Bot으로 reminder.
- **함정 2 — 모든 것에 RFC를 요구**: 버그 수정·문서 오타까지 RFC. 사람들이 *떠남*. *대응*: Contribution Guide 표로 *RFC 필요 기준* 명시.
- **함정 3 — Owner의 부재**: 결정자가 *휴직·퇴사*. RFC 정지. *대응*: OWNERS.md에 *백업 owner* 명시. Quarterly로 갱신.
- **함정 4 — Veto의 남용**: Steering 멤버 1명이 *근거 없이* veto. *대응*: Veto에는 *근거 문서화 의무*. 다음 회의에서 *다수결*로 뒤집기 가능.
- **함정 5 — RFC와 구현 불일치**: RFC는 accepted됐는데 *구현 시 API가 바뀜*. *대응*: PR description에 *"RFC와의 변경 사항"* 섹션. 큰 변경은 *RFC 수정 PR* 별도.
- **함정 6 — Contributor 진입 장벽**: 첫 PR이 "이건 RFC 먼저 쓰세요" 한 줄로 닫힘. *대응*: `good-first-issue` 라벨 + `help-wanted` 라벨 *유지*. RFC 안 필요한 *작은 변경*을 의도적으로 발굴해 둠.
- **함정 7 — 결정의 *재논의***: 2년 전 거절된 제안이 다시 올라옴. *대응*: `docs/rfcs/`에 거절된 RFC도 *보존*. 새 제안 시 *과거 RFC 링크 의무*. 단, *상황이 바뀌었으면* 재논의 허용 (예: "그때는 React 17이라 X였지만 18에서는 Y").
---
## Insight — Rust의 RFC가 디자인 시스템에 준 교훈
RFC 제도는 *Rust 언어*가 2014년 정립한 모델이다. 그 핵심 통찰:
1. **결정은 *공개 문서*에 남아야 한다** — Slack DM에서 죽지 않게
2. **거절도 *문서*다** — 거절 이유가 *미래의 같은 제안*을 막는다
3. **Comment period가 *마감*이 있어야 한다** — 7일·14일 등. 무한 토론 방지
4. **"Final Comment Period (FCP)"** — 결정 직전 1주일을 따로 둬 *마지막 반대*를 받음
Spectrum(Adobe), Polaris(Shopify), Carbon(IBM)이 *Rust RFC와 거의 동일한 절차*를 차용한 것은 우연이 아니다 — *언어 설계*와 *디자인 시스템 설계*는 모두 **"공개 API의 한 번 결정이 수십 년 살아남는"** 도메인이다.
> **반전**: RFC 제도의 *원조*는 1969년 ARPANET의 RFC 1 ("Host Software", Steve Crocker). *50년 넘게* 같은 형식이 유효하다 — *공개 토론 + 명시적 결정자 + 보존 가능 문서*. 디자인 시스템도 같은 원칙으로 산다.
---
## 요약
- 거버넌스는 **`RFC 템플릿 + Owners + CODEOWNERS + DoD 체크리스트`의 4-축**.
- RFC는 *왜·무엇을·대안·채택 전략*을 강제하고, 거절 RFC도 보존한다.
- Owners는 *결정 권한*과 *Veto 권리*를 명시한다.
- 모든 변경에 RFC를 요구하지 말 것 — *작은 PR은 환영*해야 contributor가 들어온다.
- Rust·ARPANET이 50년간 유지한 *공개 토론 + 명시적 결정자 + 보존 가능 문서*의 변주.
```mermaid
flowchart TB
classDef layer fill:#fff8dc,stroke:#aa8800
classDef domain fill:#e8f4ff,stroke:#3370b8
classDef result fill:#e8ffe8,stroke:#33b833
classDef danger fill:#ffe8e8,stroke:#b83333
Q["디자인 시스템을 어떻게 통치할까"]:::layer
Q --> A1["RFC: 왜·무엇을·대안·채택"]:::domain
Q --> A2["Owners: 결정자 명시"]:::domain
Q --> A3["CODEOWNERS: 자동 라우팅"]:::domain
Q --> A4["DoD: 머지 전 체크"]:::domain
A1 & A2 & A3 & A4 --> R["공개·명시·보존된 결정"]:::result
Q -.실패.-> X["RFC 떠다님 / Owner 부재 / Veto 남용"]:::danger