| name | ui-component |
| description | bbl 레포의 UI 컴포넌트를 추가하거나 변경하는 스킬. 배치 판정(패키지·레이어) → 3파일 생성 → 레이어 배럴 등록 확인 → 클라이언트 경계 판정 → 사용처 연결 → 타입·린트·실제 렌더 검증까지의 워크플로를 다룬다. 사용자가 "컴포넌트 만들어줘", "버튼/카드 추가해줘", "이 컴포넌트 스타일 바꿔줘", "다크모드 대응 안 됐어", "스토리 추가해줘", "atoms로 옮겨줘", "about 페이지에 섹션 하나 넣어줘", "컴포넌트가 화면에 안 나와" 등 컴포넌트를 만들거나 고치거나 옮기는 요청을 하면 반드시 이 스킬을 사용할 것. 화면에 뿌릴 개인 기록 데이터 자체를 바꾸는 작업은 career-record, 글 본문을 쓰는 작업은 blog-post가 담당하며, 컴포넌트 코드를 건드리지 않는 대화라면 이 스킬을 적용하지 않는다. |
UI Component (컴포넌트 추가·변경 워크플로)
이 레포에서 컴포넌트 작업이 깨지는 지점은 구현이 아니라 등록·경계·검증 세 곳이다.
핵심 원칙은 "파일 3개를 만든 것은 완료가 아니다".
0. 대원칙
libs에서 'use client'를 쓰지 않는다. 클라이언트 경계는 앱이 얇은 래퍼로 소유하고 있고
libs에는 이 지시어가 단 한 줄도 없다(→ AGENTS.md §5). 훅을 쓰는 컴포넌트를 libs에 만들 때
할 일은 지시어를 붙이는 것이 아니라, 이미 클라이언트 경계 안에서 렌더되는지 확인하고 아니면
앱 쪽 래퍼를 하나 늘리는 것이다.
(나쁜 예: 새 컴포넌트 첫 줄에 'use client' / 좋은 예: 앱에 래퍼를 만들고 libs 컴포넌트는 순수하게 둔다)
- 레이어 배럴의 확장자를 먼저 확인하고 등록한다. 배럴이
.ts인 레이어와 .tsx인 레이어가
섞여 있다(→ §4의 표). 없는 확장자에 export를 추가하면 같은 레이어에 배럴이 두 개 생기고 둘 중
하나만 읽힌다 — 타입 에러 없이 컴포넌트가 그냥 안 나온다.
- 등록할 배럴은 레이어 배럴이고, 루트 배럴은 임포트 경로가 아니다. bare 별칭이
paths에
없어서 루트 배럴은 아무도 임포트하지 않는다(→ §3). 딥 경로가 유일한 방법이다.
"배럴에 export 했다"는 완료가 아니고, 쓸 파일에서 딥 경로로 임포트해 화면에 붙는 것까지가 완료다.
- 개인 데이터를 직접 읽는 컴포넌트는
features, props만 받는 컴포넌트는 ui-components다.
이 선을 잘못 그으면 재사용 가능한 컴포넌트가 개인 데이터에 묶인다(→ §2).
- exit 0 을 렌더 확인으로 쓰지 않는다. Storybook 빌드는 프리뷰가 죽어도 성공으로 끝날 수 있고,
워크트리에서는 turbo가 예전 실패 산출물을 캐시에서 복원한다(→ §10). 무엇이 생겼는지 직접 본다.
1. 배치 판정 (패키지 · 레이어)
래더:
- 같은 역할의 기존 컴포넌트가 있으면 그 위치를 그대로 따른다.
- 없으면 규칙으로 판정한다 — 개인 데이터 의존 여부로 패키지를, 원자성으로 레이어를.
- 규칙으로도 갈리면 사용자에게 한 줄로 묻는다.
게이트: 대상 디렉터리가 확정됐고, features라면 §4가 명시한 대로 그 패키지가 실제로 쓰는
레이어(organisms / templates)인지 확인했다.
막히면: 임의로 정하고 나중에 옮기는 경로는 금지한다. 옮기면 배럴 두 곳과 모든 임포트 경로가
함께 바뀐다.
2. 3파일 생성과 배럴 등록
래더:
- §8
COMPONENT-NEW를 쓴다. 실행 직후 산출물 3개의 위치와 배럴 줄이 들어간 파일을 즉시
확인한다.
- 배럴 줄이 새로 만들어진 파일이나 엉뚱한 경로에 들어갔으면 그것을 되돌리고 §4의 표가 지목하는
올바른 배럴에 직접 한 줄 추가한다.
- 제너레이터를 쓰지 않으면 같은 레이어의 기존 컴포넌트 3파일을 형태 기준으로 삼아 직접 작성한다.
게이트: kebab 디렉터리 안에 같은 kebab 이름의 3파일이 있고, 레이어 배럴에 export 줄이 정확히
1줄 늘었으며, 그 배럴 파일이 새로 생성된 것이 아니라 원래 있던 파일이다. 같은 레이어에
배럴이 두 개 있지 않다.
3. 구현
게이트: §4의 in-file 계약을 전부 만족한다 — Props 인터페이스 export, 네임드 + default export,
cn 임포트와 인자 순서, 모든 색상 클래스에 dark: 짝, 사용하지 않는 prop·임포트 0개.
스토리 title의 레이어 세그먼트가 실제 폴더 레이어와 일치하고 §4의 "복사하지 말 것" 3건에
걸리지 않았다.
스타일 래더:
- 기존 컴포넌트의 Tailwind 조합을 재사용한다.
- 없으면 인라인 Tailwind로 작성한다.
- Tailwind로 표현 불가한 것만 CSS 모듈 — 전례가 둘뿐이니 새로 만들기 전에 그 둘을 먼저 읽는다(§4).
4. 클라이언트 경계 판정
게이트: 훅·이벤트 핸들러·브라우저 API를 쓰는가? 안 쓰면 통과. 쓰면 이 컴포넌트가 렌더되는 트리에
이미 앱 쪽 클라이언트 경계가 있는지 §5로 확인한다.
래더:
- 기존 경계 안이면 아무것도 하지 않는다.
- 아니면 §5의 래퍼 목록에 하나를 추가하고 페이지가 실제로 렌더되는지 확인한다.
- 래퍼를 넣을 수 없는 위치(서버 컴포넌트가 직접 호출)라면 훅을 쓰지 않는 형태로 다시 설계한다.
libs에 지시어를 붙이는 선택지는 없다.
5. 연결
게이트: 실제 사용처 파일에서 §3의 규칙에 맞는 경로로 임포트했다 — 패키지 경계를 넘으면 별칭,
같은 패키지 안이면 상대 경로, 와일드카드 별칭은 딥 경로.
(나쁜 예: 루트 배럴에서 임포트 / 좋은 예: 딥 경로로 임포트)
6. 검증
래더:
- §8
TYPECHECK과 §8 LINT.
- §8
DEV로 그 컴포넌트가 실제로 보이는 페이지를 열고 라이트·다크 둘 다 확인한다.
- 아직 어느 페이지에도 붙지 않은 순수 컴포넌트라면 §8
STORYBOOK으로 스토리를 열어 확인한다.
- 둘 다 불가하면 타입체크·린트만으로 끝내고 렌더는
⚠️ 미검증으로 명시해 보고한다.
게이트: 타입체크 0 에러(§9의 미사용 변수·인덱스 접근 플래그 포함), 린트 0 에러, 스펙 통과,
라이트·다크 육안 확인 또는 명시적 ⚠️.
주의: §8 TEST는 스펙이 임포트하지 않는 파일의 타입을 보지 않는다. TYPECHECK을 생략하지 않는다.
7. 세션 종료 시
만들거나 바꾼 파일 목록, 배럴 등록 여부, 클라이언트 경계 처리 결과, 검증 상태(✅/⚠️),
남은 할 일을 요약한다.