Design to Code #5: AI를 사용하여 디자인 시스템 구축하기
요약
AI가 코드베이스의 특수한 규칙과 컨텍스트를 이해하지 못해 발생하는 문제를 해결하기 위한 워크플로우 구축 사례를 다룹니다. llms.txt와 CLAUDE.md를 활용하여 디자인 시스템과 코딩 규칙을 AI에게 효과적으로 전달하는 방법을 설명합니다.
핵심 포인트
- llms.txt를 활용한 단계별 컨텍스트 제공 방식
- CLAUDE.md를 통한 코딩 규칙 및 제약 사항 자동 로드
- AI의 잘못된 경로 제공 및 작업 미완료 문제 해결 전략
- 디자인 토큰 및 컴포넌트 패턴의 AI 학습 최적화
나는 Claude에게 Switch 컴포넌트 사양을 제공하고 variables.css를 참조하도록 지정했습니다. 그 결과로 나온 것들 — CVA variants, Radix Primitives, forwardRef, 그리고 제어형(controlled) 및 비제어형(uncontrolled) 방식이 모두 연결된 코드 — 은 진심으로 훌륭했습니다. 제가 처음 작성했을 초안보다 더 나았습니다. 저는 그날 오후에 큰 수정 없이 바로 배포했습니다.
그것은 3월의 어느 때였습니다. 4월이 되었을 때, 저는 Claude가 완료되지 않은 일을 완료했다고 보고하는 것을 잡아내는 것을 유일한 업무로 하는 세 개의 쉘 스크립트(shell scripts)를 작성했습니다.
코드에 대해 거짓말을 하는 것은 아니었습니다. 코드는 여전히 훌륭했습니다.
Claude를 위해 내가 구축한 것
어떠한 문제들이 발생하기 전에, 먼저 설정(setup)이 필요합니다. 저는 AI 워크플로우를 위한 인프라를 구축하는 데 예상보다 더 많은 시간 — 솔직히 말하면 원했던 것보다 더 많은 시간 — 을 소비했습니다.
핵심적인 문제는 이렇습니다: 제가 말해주지 않으면 Claude는 제 코드베이스(codebase)에 대해 아무것도 모른다는 점입니다. 그리고 이 코드베이스는 중요한 측면에서 매우 구체적입니다. 커스텀 토큰 변수(Custom token variables), 특정한 컴포넌트 패턴 (Radix + CVA + forwardRef + named exports 전용), 명명 규칙(naming convention), 그리고 단 하나의 JSON 소스로부터 sync-tokens가 생성하는 정확히 11개의 배포 파일 목록 등이 있습니다. 이 중 그 어떤 것도 누군가의 학습 데이터(training data)에 포함되어 있지 않습니다.
그래서 저는 llms.txt를 구축했습니다. 실제로는 여섯 가지 변형이 있습니다:
llms.txt(루트) — 라이브러리 개요, 설치, 빠른 참조tokens/llms.txt— 토큰 패키지 세부 사항, 변수 이름 및 카테고리cli/llms.txt— CLI 명령 참조, 플래그(flags), 의존성 해결(dependency resolution)public/llms.txt— URL을 통해 컨텍스트를 첨부하는 AI 도구들을 위한 경량 버전public/llms-full.txt— Prop types를 포함한 전체 컴포넌트 API 참조public/llms-cli.txt— CLI 워크플로우 형식, 이미@/components/ui/로 변환된 임포트 경로(import paths)
마지막 파일이 존재하는 이유는 CLI를 통해 소스 파일을 로컬로 복사했을 때 임포트 경로가 달라지기 때문이며, 저는 Claude가 코드 예제에서 사용자에게 잘못된 경로를 제공하는 것에 지쳤기 때문입니다. 작은 문제였지만, 알아차리는 데 시간이 좀 걸렸습니다.
llms.txt 파일 외에도, 레포지토리 루트에는 CLAUDE.md가 있습니다. Claude Code 세션을 시작할 때 자동으로 로드되는 운영 매뉴얼입니다. 코딩 규칙, 토큰 사용 규칙, 절대적인 제약 사항들이 담겨 있습니다. "항상 items-center를 사용하고, mt-0.5를 이용한 수동 오프셋과 함께 items-start를 절대 사용하지 말 것"과 같이 명확하지 않은 패턴들도 포함됩니다. 반복되는 워크플로우를 위한 스킬 파일(Skill files)을 만들어, 매번 동일한 발행 전 체크리스트를 다시 설명하지 않도록 했습니다. 또한 세션 전반에 걸쳐 유지되는 사실들을 담은 메모리 디렉토리(Memory directory)도 있습니다. 예를 들어 "공개 레포지토리 커밋에 Co-Authored-By를 추가하지 말 것"과 같은 내용입니다 (레포지토리는 제 이름으로 되어 있으며, MIT 라이선스에는 소유권 기록이 중요하기 때문입니다).
이 모든 것을 한꺼번에 구축한 것은 아닙니다. 대부분 무언가 잘못된 후에 만들어졌습니다.
42개의 컴포넌트
컴포넌트 코드의 경우, 이 워크플로우는 진정으로 유용합니다. 명세(Spec), 관련 토큰 파일, 그리고 확립된 패턴이 주어지면, Claude는 제가 읽고 이해할 수 있으며 약 10~20%의 수정만으로 배포할 수 있는 결과물을 만들어냅니다. CVA 변형(variant) 구조, Radix 프리미티브(primitive) 배선, TypeScript 타입 등—컨텍스트가 제대로 설정되어 있다면 이 모든 것을 정확하게 처리합니다.
7onic에는 42개의 컴포넌트가 있습니다. 저는 대부분의 컴포넌트를 이런 방식으로 구축했습니다.
덜 신뢰할 수 있는 부분은 다음과 같습니다: 코드베이스의 전체 상태를 한꺼번에 유지해야 하는 모든 작업입니다. "이 변경 사항이 다른 부분을 망가뜨리지는 않을까?"라는 질문은 사용자가 명시적으로 보여준 것만 볼 수 있는 도구에게는 어려운 문제입니다. 저는 범위(scope)를 구체적으로 지정하는 법을 배웠고, 도구가 파일 간의 영향을 스스로 잡아내기를 기대하는 것을 그만두었습니다. 그 부분은 제가 적응했습니다. 컴포넌트 코드는 여전히 훌륭했습니다.
문제는 완전히 다른 곳에 있었습니다.
v0.3.0의 날
v0.3.0 릴리스 사이클은 이제 아키텍처 결정 기록(Architecture Decision Record, ADR)에 문서화되어 있습니다. 제가 이를 기록해 두는 이유는 읽는 것이 즐거워서가 아니라, 그 기록이 설명하는 패턴이 더 미묘한 형태로 계속 반복되며, 어딘가에 글로 적어두는 것이 도움이 되기 때문입니다.
단 하루 만에 — 22개의 복합 컴포넌트 (compound components)를 이름이 지정된 내보내기 (named exports)로 전환하는 파괴적 변경 (breaking change) 마이그레이션을 진행하던 중 — 무언가가 검증되었거나 완료되었다는 확신에 찬 보고를 세 번이나 따로 받았습니다. 그중 단 하나도 정확하지 않았습니다.
구체적인 내용은 매번 달랐습니다. 한 번은 Claude가 스스로 체크를 다시 실행하지 않고 서브 에이전트 (subagent)의 검증 결과를 자신의 결과인 것처럼 인용했습니다. 또 한 번은 보고된 치환 횟수가 실제 파일에 있는 내용과 일치하지 않았는데, 구조적 정확성을 확인하는 대신 텍스트 출현 횟수를 세어버린 것이었습니다. 또 한 번은 "기술적으로 검증됨 (technically verified)"이라는 말이 코드가 컴파일되었다는 뜻이었는데, 정작 제게 필요했던 것은 렌더링된 결과물이 모든 모드 (modes)에서 예상된 출력값과 일치한다는 확인이었습니다.
매번 언어는 동일했습니다: 완료됨 (completed), 검증됨 (verified), 모두 통과 (all passing). 매번 저는 그것을 믿었고, 나중에 문제를 발견했습니다.
ADR(Architecture Decision Record)은 이를 "완료 선언 시 실측 증거 결여" — 실제 측정 없이 완료를 주장하는 것 — 라고 부릅니다. 문제는 Claude가 틀렸다는 것이 아니었습니다. 문제는 Claude가 확신에 차 있었고, 그 틀린 방식이 마치 확신에 차서 맞게 말하는 방식과 똑같이 보였다는 점입니다. 응답의 그 어떤 것도 이 둘을 구분해주지 않았습니다.
세 번째 일이 발생한 후, 저는 노트북을 덮고 산책을 나갔습니다. 돌아온 후 저는 아키텍처 결정 기록 (architecture decision record)을 작성했습니다. 실수를 반복하지 않기 위해 제가 해야 할 일은 분명 이것인 것 같습니다.
이후 구축된 사항
첫 번째는 어휘 금지 (vocabulary ban)였습니다. 이제 CLAUDE.md §4는 직접적인 도구 출력 (tool output)이 첨부되지 않은 채 사용되는 "완료", "검증됨", "all clean", "PASS"와 같은 단어들을 허위 보고로 분류합니다. 완료 주장에 필요한 형식은 다음과 같습니다:
✓ [item] — 도구: `[command]` → `[output line verbatim]`
명령어와 출력을 인용할 수 없는 경우, 요구되는 응답은 이유를 명시한 "⚠ 검증 안 함"입니다. 이는 숨겨야 할 실패 모드가 아니라, 정직한 "확인하지 않았음"입니다.
두 번째는 커밋을 위한 게이트(gate)였습니다. scripts/hooks/verify-artifact-gate.sh는 /tmp/ 디렉토리에 특정 증거 파일이 존재하지 않는 한 UI 소스 파일을 건드리는 커밋을 차단합니다. 이 증거 파일은 라이브 감사(live audit) 스크립트에 의해 작성되며, 타임스탬프가 찍히고 15분 후에 만료됩니다. 증거 파일이 없으면 커밋도 없습니다. 이 훅(hook)은 파일을 읽지만, 메시지를 읽지는 않습니다.
세 번째는 scripts/hooks/hundred-percent-detector.sh로, 들어오는 프롬프트(prompt)에서 "100%", "전수", "완벽하게"와 같은 문구를 스캔합니다. 이러한 문구가 나타나면 세션을 /100-percent-verify 프로토콜로 강제 전환합니다. 이 프로토콜은 5단계의 게이트 단계(gated phases)로 구성되며, 각 단계는 다음으로 넘어가기 전에 실제 도구 출력(tool output)을 요구합니다.
제가 마지막으로 추가한 것 — 즉, 제가 그런 상황을 허용할 뻔했다는 것을 깨달은 후에 추가한 것 — 은 scripts/hooks/manual-only-gate.sh입니다. 이는 .claude/gates/에 일회용 승인 토큰 파일이 존재하지 않는 한 npm publish, gh workflow run publish, npm dist-tag를 차단합니다. 검증 워크플로(verification workflow)를 실행하던 중 배포(publish) 단계가 체크 단계 바로 옆에 위치해 있으며, 구조적으로 Claude가 이를 실행하는 것을 막을 수 있는 것이 아무것도 없다는 사실을 발견했습니다. 이제 해당 명령어들은 수동(manual-only)으로만 실행 가능합니다. 이것은 Claude가 우회할 수 있는 제약 사항이 아닙니다. 제가 생성하기 전까지는 존재하지 않는 파일에 의한 제약입니다.
또한 비슷한 시기에 문서 전파(document propagation) 모델을 뒤집었습니다. 이전에는 제가 코드를 변경할 때마다 Claude가 자동으로 문서를 업데이트했습니다. 활발한 개발 단계 동안 이는 10분 뒤에 포기할 반복 작업(iterations)을 위해 문서가 계속 다시 작성된다는 것을 의미했습니다. 새로운 모델은 승인 기반(approval-based)입니다. Claude는 커밋 시점에 git diff를 스캔하고, 전파 계획을 제안한 뒤, 승인(yes)을 기다립니다. 더 느리지만, 문서는 제가 이미 번복한 결정들을 반영하는 일을 멈추게 됩니다.
여전히 무너지는 지점들
훅(hooks)들은 v0.3.0을 망가뜨렸던 특정 패턴을 방어합니다. 하지만 다른 문제들은 여전히 발생합니다.
검증(Verification)은 가장 위험한 모드입니다. 실제 도구 출력(tool output)을 생성하기보다는 그에 대한 해석을 내놓지 않도록 명시적으로 강제할 필요가 있습니다. "모든 곳에서 이것이 올바른지 확인해"라는 명령은 "이 파일을 읽고, 이제 저 파일을 읽고, 이 특정 값들을 비교해"라는 명령과는 매우 다르게 작용합니다. 파일 간 일관성(Cross-file consistency)은 수동으로 세분화하지 않으면 일반적으로 신뢰하기 어렵습니다.
긴 세션(Long sessions)이 최악입니다. 세션 시작 시점에 분명히 활성화되어 있던 제약 사항(Constraints)들이 세 번째나 네 번째 작업에 이르면 아무런 신호도 없이 조용히 누락됩니다. 저는 세션 초반에는 CLAUDE.md 규칙을 주의 깊게 따르다가, 세션 끝부분에는 규칙이 적용되지 않는 상태로 종료된 경험이 있습니다. 이는 반항적인 것이 아니라, 그저 규칙이 부재한 상태였습니다. 이 지점에서 스킬(skills)이 도움이 됩니다. 스킬은 몇 시간 전에 설정된 것이 계속 유지되기를 기다리는 대신, 규칙이 필요한 순간에 규칙을 재설정합니다.
저는 여전히 거의 모든 작업에 Claude를 사용합니다. 컴포넌트 코드는 진정으로 훌륭하며, llms.txt 인프라와 CLAUDE.md, 그리고 메모리 시스템 덕분에 새로운 세션이 코드베이스에 빠르게 적응합니다. 유지 관리 오버헤드(maintenance overhead)는 실제로 존재하지만, 매일 처음부터 다시 신뢰를 쌓아야 했던 대안보다는 적습니다.
셸 훅(shell hooks)은 예외적인 상황(edge cases)을 위해 존재합니다. 그것들이 있어서 다행입니다. 하지만 그것들이 필요하지 않다면 더 다행일 것입니다.
다음: 7onic이 다크 모드를 처리하는 방식 — JavaScript도, documentElement.classList 토글도 사용하지 않습니다. 오직 CSS만 사용하며, 왜 하나의 전략만으로는 충분하지 않았는지에 대해 다룹니다.
7onic 소개 — 디자인과 코드가 결코 어긋나지 않는 오픈 소스 React 디자인 시스템입니다. 무료이며 MIT 라이선스를 따릅니다. 문서와 인터랙티브 플레이그라운드는 7onic.design에서 확인할 수 있습니다. 소스 코드는 GitHub에 있으며, 스타(stars)를 환영합니다. 이 시리즈의 더 많은 포스트는 blog.7onic.design에서 볼 수 있습니다. X에서의 업데이트는 @7onicHQ를 팔로우하세요.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기