| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | |||
| 5 | 6 | 7 | 8 | 9 | 10 | 11 |
| 12 | 13 | 14 | 15 | 16 | 17 | 18 |
| 19 | 20 | 21 | 22 | 23 | 24 | 25 |
| 26 | 27 | 28 | 29 | 30 | 31 |
- 아키텍처
- 보안
- php
- REST API
- 프론트엔드
- 메일자동화
- Kotlin
- 배포
- 백엔드
- JWT
- 마이그레이션
- electron
- javascript
- 소셜로그인
- 상태관리
- next.js
- 온디바이스AI
- Android
- python
- 크롤링
- typescript
- FastAPI
- React
- github actions
- 인증
- Vite
- playwright
- contextBridge
- 도메인설계
- sqlite
- Today
- Total
HM소프트
AI 코딩 에이전트용 디자인 시스템 플러그인 개발기 (1) — 'AI 티 나는 디자인'을 강제로 깨뜨리기 본문

AI 코딩 에이전트용 디자인 시스템 플러그인을 마크다운 문서만으로 설계한 기록. 스킬·서브에이전트·리뷰 명령어 세 진입점, 코드 생성 전 톤 선언과 안티패턴 회피 강제, bg-blue-500 같은 안티패턴 카탈로그화, 출력 형식 강제와 점진적 공개, 마켓플레이스 스키마 함정까지 다룬다.
'AI가 만든 티'는 어디서 새어 나오는가
AI 코딩 에이전트에게 "랜딩 페이지 hero 섹션 하나 만들어 줘"라고 부탁해 본 사람은 안다. 결과물이 묘하게 다 비슷하다. 가운데 정렬된 hero, 파랑에서 보라로 흐르는 그라데이션 배경(from-blue-500 to-purple-600), 모든 모서리에 rounded-lg, 모든 카드에 shadow-md, "Build amazing products" 혹은 "Streamline your workflow" 같은 카피, 그리고 똑같이 생긴 카드 3개가 나란히 놓인 피처 그리드. 프로젝트의 성격이 무엇이든, 브랜드가 무엇이든, 매번 같은 화면이 나온다.
처음엔 이게 모델의 한계라고 생각했다. 그런데 직접 디자이너처럼 손으로 같은 화면을 짜 보면, 모델이 못 그리는 게 아니라는 걸 알게 된다. 모델은 충분히 정교한 CSS를 쓴다. 문제는 선택을 안 한다는 데 있다. 톤도, 폰트도, 색도, 레이아웃의 비대칭도 결정하지 않은 채 곧장 코드부터 뽑으니, 학습 데이터에서 가장 흔한 패턴 — 즉 가장 무난하고 가장 안전한 디폴트 — 로 수렴한다. bg-blue-500이 나쁜 색이라서 문제가 아니라, 아무 고민 없이 반사적으로 고를 수 있는 색이라서 문제다.
그래서 만든 것이 이 글의 주제인 디자인 원칙 플러그인이다. AI 코딩 에이전트에 끼우면, 에이전트가 UI 작업을 할 때마다 코드를 뽑기 전에 의도적인 디자인 결정을 강제로 내리게 만드는 도구다. 코드 한 줄 실행하지 않고 오직 마크다운 문서만으로 에이전트의 행동을 바꾸는, 일종의 "행동 패치"에 가깝다. 이번 글에서는 그 설계를 처음부터 끝까지 — 왜 스킬·서브에이전트·명령어 세 갈래로 나눴는지, 안티패턴을 어떻게 데이터로 박제했는지, 출력 형식을 어떻게 강제했는지, 그리고 첫 배포에서 마켓플레이스 스키마에 어떻게 걸렸는지 — 차례로 풀어 본다.
전체 구조 — 같은 원칙, 세 개의 진입점
먼저 전체 그림부터. 이 플러그인은 단일 기능이 아니라, 같은 디자인 철학을 세 가지 사용 방식으로 노출한 묶음이다. 디렉터리 구조가 곧 설계 의도다.

세 진입점이 각자 다른 상황을 맡는다.
- 스킬(자동 활성화): 사용자가 "대시보드 디자인 개선해 줘" 같은 UI 요청을 하면, 에이전트가 *스스로* 이 스킬을 로드한다. 사용자가 명시적으로 부르지 않아도 켜진다는 게 핵심이다. 평소 작업 흐름에 자연스럽게 스며든다.
- 서브에이전트(위임): 랜딩 페이지 한 장을 통째로 맡기고 싶을 때, 디자인 전담 서브에이전트에게 위임한다. 시스템 프롬프트에 "15년차 시니어 프로덕트 디자이너"의 관점을 박아 둬서, 메인 대화의 컨텍스트를 오염시키지 않고 디자인에만 집중한다.
- 리뷰 명령어: 이미 만들어진 화면을 디자이너의 눈으로 감리하고, 파일·라인 단위로 "여기
bg-blue-500은 이렇게 바꿔라"는 구체적 수정안을 내놓는다.
세 갈래로 나눈 이유는 분명하다. 자동 스킬은 *놓치지 않게* 하고, 서브에이전트는 *깊게 파게* 하고, 명령어는 *사후 검증*을 한다. 하나의 거대한 프롬프트로 다 욱여넣었다면, 에이전트가 컨텍스트 한도 안에서 그 무게를 매번 짊어져야 했을 것이다. 진입점을 쪼갠 덕분에 필요한 순간에 필요한 무게만 로드된다.
스킬의 심장 — 'The Three Rules'
스킬의 진입점 문서(SKILL.md)는 단 하나의 강박을 담고 있다. 코드를 쓰기 전에, 반드시 순서대로 세 가지를 한다. 이걸 건너뛰는 순간이 바로 AI 디폴트 출력이 새어 나오는 지점이라고 못 박아 둔다.

여기서 가장 신경 쓴 한 문장은 "clean and modern으로 도망가지 마라"다. 처음 프롬프트를 짤 때는 "깔끔하고 모던하게"라는 지시를 긍정적인 방향처럼 썼는데, 실험해 보니 이 표현이야말로 AI 디폴트를 부르는 주문이었다. "깔끔하고 모던"은 어떤 결정도 강제하지 않는다. 그래서 톤을 명시하지 않으면 디자인을 시작하지 말고 먼저 물어보라고 강제로 막아 뒀다. 결정을 미루는 순간 디폴트로 빨려 들어가기 때문이다.
톤은 막연하게 두지 않고 6개로 못 박았다. editorial(매거진풍), brutalist(거칠고 빽빽한), soft & humane(따뜻한), technical(데이터 밀집), minimalist Swiss(엄격한 그리드), playful(과감한). 각 톤마다 hero·타이포·색·레이아웃이 어떻게 달라지는지 표로 정리해 두어, 에이전트가 "editorial을 고른다"고 선언하는 순간 나머지 결정들이 줄줄이 따라오게 만들었다. 톤 선언은 단순한 라벨이 아니라 이후 모든 결정의 기준점 역할을 한다.
안티패턴을 '데이터'로 박제하기
Rule 2가 가리키는 ai-antipatterns.md가 이 플러그인의 진짜 심장이다. "AI 티 나게 하지 마라"는 막연한 훈계로는 모델이 움직이지 않는다. 그래서 AI가 실제로 저지르는 패턴 하나하나를 카탈로그로 박제했다. 각 항목은 똑같은 3단 구조를 갖는다 — *AI가 뭘 하는가 / 왜 그게 AI 티가 나는가 / 대신 뭘 하라.*

이 카탈로그는 6개 범주로 나뉜다 — 레이아웃(A1~A5), 타이포그래피(T1~T5), 색(C1~C5), 컴포넌트(Co1~Co5), 콘텐츠(Cn1~Cn3), 이미지(I1~I3). 예를 들면 이런 것들이다.
- A2 — 3-column 피처 그리드: 똑같이 생긴 카드 3개. "디자인 결정이 아니라 템플릿"이라고 못 박고, 1/2/4 칼럼이나 비대칭 배치로 깨라고 지시.
- A3 — 전부 같은 패딩: 모든 섹션이
py-20, 모든 카드가p-6. "여백은 강조의 도구"라며 섹션 간 패딩을 극적으로 다르게 두라고 요구. - T5 — 데이터에 tabular-nums 미적용: 숫자가 흔들리는 현상. 프로 데이터 UI는 항상 고정폭 숫자를 쓴다는 점을 강제.
- Co4 — 이모지를 아이콘으로: 📊 🚀 ✨ 같은 것. 플랫폼마다 렌더링이 다르고 게으른 선택이라며 실제 아이콘으로 대체하게 함.
- Cn1 — "Build amazing products"류 카피: 의미 없는 SaaS 상투어. "Streamline your workflow" 대신 "Stop reading three Slack threads to find one decision"처럼 구체적 동사·구체적 고통을 쓰라는 예시까지 박아 둠.
왜 이렇게까지 잘게 쪼갰을까. 모델은 추상적 원칙("좋은 디자인을 해라")에는 약하지만, 구체적 회피 목록("bg-blue-500을 쓰지 마라")에는 강하다. 그래서 원칙을 검사 가능한 체크리스트로 번역했다. 문서 맨 끝에는 출하 직전 스캔용 체크박스 목록까지 붙였다.

출력 형식을 강제하면 도망칠 곳이 사라진다
여기서 가장 효과가 컸던 발견을 짚고 넘어가야겠다. 출력 형식 자체를 강제하면, 에이전트가 디폴트로 도망갈 수 없다. 서브에이전트의 시스템 프롬프트에는 "반드시 이 형식으로만 답하라(non-negotiable)"는 출력 템플릿이 박혀 있다.

이 형식의 묘미는 "비-디폴트 선택 3개를 정당화하라"는 칸에 있다. 에이전트가 코드를 뽑기 전에 "내가 왜 이 색, 이 폰트, 이 레이아웃을 골랐는가"를 세 줄로 적어야 한다. 그런데 bg-blue-500을 "정당화"하려고 하면 쓸 말이 없다. 정당화할 근거가 없는 선택은 곧 반사적 디폴트라는 뜻이고, 그 칸을 채우려는 시도 자체가 모델을 디폴트 밖으로 밀어낸다. 강제된 자기 설명이 강제된 의사결정으로 이어지는 구조다.
마지막 Self-review 칸은 일종의 자가 린트다. 출력을 내보내기 직전에 "blue-500/rounded-lg/bg-white/text-black 반사를 안 썼는가: yes/no"를 스스로 체크하게 만든다. 사람이 검수할 때도 이 한 칸만 보면 품질을 한눈에 가늠할 수 있다. 형식이 곧 품질 게이트인 셈이다.
서브에이전트 프롬프트에는 이 위에 하드 블록 목록도 따로 뒀다. "이건 절대 출하하지 마라"는 절대 금지선이다.

컨텍스트를 아끼는 점진적 공개
스킬을 만들 때 가장 고민한 비기능 요건은 컨텍스트 예산이었다. 디자인 지식을 전부 한 파일에 몰아넣으면, UI 작업을 할 때마다 타이포·색·레이아웃·웹패턴·모바일패턴 지식 전체가 컨텍스트를 잡아먹는다. 정작 색 팔레트만 추천하면 되는 상황에서도 모바일 네비게이션 가이드까지 읽어야 한다면 낭비다.
그래서 지식을 의도적으로 쪼갰다. 진입점 SKILL.md는 얇게 유지하고, 깊은 지식은 별도 파일로 빼서 필요할 때만 꺼내 읽는 점진적 공개(progressive disclosure) 구조로 짰다.

SKILL.md에는 "전부 미리 읽지 마라(Do not read all of them upfront)"는 지시를 명시적으로 박아 뒀다. 결정의 종류와 참고 파일을 표로 매핑해 두어, 색을 정할 땐 color.md만, 그리드를 짤 땐 layout.md만 펼치게 했다. 안티패턴 파일만은 예외로 "항상" 읽게 했는데, 그게 이 스킬의 존재 이유이기 때문이다.
쪼개 둔 참고 파일도 그냥 산문이 아니라 바로 복붙 가능한 표와 값으로 채웠다. 예를 들어 색 파일은 순백을 죽이는 오프화이트 값을 톤별로 테이블화해 둔다.

모델 입장에서 "따뜻한 오프화이트를 써라"는 추상 지시보다 "#FAFAF7을 써라"는 구체값이 훨씬 잘 먹힌다. 색·타이포·레이아웃 파일 전부 이 원칙으로 채웠다 — 추상 원칙은 진입점에, 복붙 가능한 구체값은 참고 파일에.
Before/After를 문서에 박아 두는 이유
문서로 행동을 바꾸려 할 때 가장 강력한 무기는 나쁜 예와 좋은 예를 나란히 보여주는 것이었다. SKILL.md 맨 끝에는 같은 hero를 AI 디폴트로 짠 버전과 editorial 톤으로 의도적으로 짠 버전을 대조해 박아 뒀다.
<!-- Before: 전형적인 AI 디폴트 -->
<section class="bg-gradient-to-br from-blue-500 to-purple-600 text-white py-20">
<div class="max-w-6xl mx-auto text-center">
<h1 class="text-5xl font-bold mb-4">Build Amazing Products</h1>
<p class="text-xl mb-8">Streamline your workflow with our platform.</p>
<button class="bg-white text-blue-600 px-8 py-3 rounded-lg">Get Started</button>
</div>
</section>
<!-- After: editorial 톤, 의도적 결정 -->
<section class="bg-[#FAFAF7] text-stone-900 pt-32 pb-40">
<div class="max-w-[1280px] mx-auto px-8 grid grid-cols-12 gap-6">
<div class="col-span-7">
<p class="font-mono text-xs tracking-widest uppercase text-stone-500">Issue 04 — November</p>
<h1 class="font-serif text-[7rem] leading-[0.95] tracking-tight">
The quiet case for <em class="italic font-light">slower</em> software.
</h1>
</div>
<figure class="col-span-5 -mr-16 mt-24">
<img src="/cover.jpg" class="w-full grayscale" alt="" />
</figure>
</div>
</section>
After 코드 밑에는 "주목할 차이"를 글머리표로 풀어 둔다 — 순백이 아닌 오프화이트, 가치 제안 슬로건이 아닌 개성 있는 헤드라인, 세리프 디스플레이 + 모노 메타 + 산세리프 본문의 세 가지 역할 분담, 7/5 비대칭 그리드와 오른쪽으로 흘러나오는 이미지(-mr-16), hero에 CTA를 일부러 안 넣은 editorial식 자신감, 그리고 굵게가 아닌 이탤릭으로 주는 강조. 모델은 이 대조를 보고 "아, 이 방향으로 밀라는 거구나"를 추상 설명보다 훨씬 빠르게 잡는다. 백 마디 원칙보다 한 쌍의 before/after가 낫다는 게 이 플러그인을 만들며 가장 크게 체감한 부분이다.
리뷰 명령어 — 만든 뒤를 감리하다
세 번째 진입점인 디자인 리뷰 명령어는 사후 검증을 맡는다. 인자로 파일·디렉터리를 받으면 그걸 보고, 비워 두면 현재 브랜치의 미커밋 UI 변경(.tsx, .jsx, .html, .css, .svelte, .vue, .swift, .kt)을 git status/git diff로 찾아 검사한다. 명령어 문서 안에는 색→레이아웃→타이포→컴포넌트→콘텐츠→이미지 순으로 훑는 인라인 체크리스트가 통째로 들어 있고, 리뷰 결과도 정해진 형식으로만 내놓게 했다.
## Design Review
**Tone read:** [코드가 전하는 톤, 혹은 "일관된 톤 없음"]
**Overall verdict:** [Ship-ready / Needs work / Heavy rework]
### Antipatterns found
- **Location:** `LandingHero.tsx:8`
- **Pattern:** Centered hero with gradient
- **Why it reads as AI:** blue→purple 그라데이션은 가장 흔한 AI 그라데이션.
- **Specific fix:** `bg-[#FAFAF7] text-stone-900`로 교체, 그라데이션 제거,
헤드라인을 7rem 세리프로 키우고 letter-spacing -0.02em.
### Working well
[잘한 점 2~3개 — 무조건 부정만 하지 말 것]
### Top 3 changes for biggest impact
1. [파일:라인 포함 가장 임팩트 큰 변경]
리뷰 형식에서 일부러 신경 쓴 두 가지가 있다. 첫째, "잘한 점(Working well)" 칸을 강제했다. 리뷰가 끝없이 부정만 하면 사람이 방어적으로 변하고 결국 무시한다. 둘째, "가장 임팩트 큰 변경 3개"를 따로 뽑게 했다. 안티패턴 20개를 나열하면 압도당하지만, "이 셋만 고쳐라"로 좁히면 실제로 손을 댄다. 그리고 모든 지적은 "개선의 여지가 있다" 같은 두루뭉술한 말이 아니라 파일명·라인번호·실제 교체 코드까지 박도록 했다. 리뷰가 막연하면 안 고친다 — 구체적이면 고친다.
첫 배포에서 만난 스키마 함정
v0.1.0을 릴리스하고 곧장 설치 테스트를 했는데, 마켓플레이스 등록이 깨졌다. claude plugin validate가 검증에서 실패했고, 원인은 마켓플레이스 JSON의 플러그인 source 경로였다. 공식 스키마가 경로를 ^\./.* 패턴으로 검증하는데, 나는 ./ 접두사 없이 .(점 하나)로 적었던 것이다.
{
"plugins": [
{
"name": "...",
"source": "." // ❌ 스키마 ^\./.* 불일치 → validate 실패
// "source": "./" // ✅ 점-슬래시로 고치면 로컬·GitHub-URL 설치 모두 동작
}
]
}
수정 자체는 점 하나에 슬래시를 붙이는 한 글자다. 하지만 교훈은 컸다. 플러그인 생태계 초기에는 스키마가 곧 문서다. 친절한 에러 메시지나 튜토리얼이 아직 무르익지 않은 단계라, 에러 문구를 노려보는 것보다 스키마 정의(^\./.*)를 직접 펼쳐 읽는 게 압도적으로 빨랐다. 그리고 이걸 계기로 "릴리스 직후, 실제 설치 경로로 설치까지 해 본다"를 배포 루틴에 못 박았다. 빌드가 통과하는 것과 사용자가 설치할 수 있는 것은 다른 문제다 — 패키징·배포 경계에서 깨지는 버그는 단위 테스트로는 절대 안 잡힌다.
정리하며
이번 편은 "AI 코딩 에이전트가 만드는 화면이 왜 다 비슷한가"라는 질문에서 출발해, 그걸 코드가 아니라 문서로 교정하는 플러그인을 설계한 과정이었다.
- AI가 만든 화면이 다 비슷한 건 모델의 한계가 아니라 선택을 안 해서다. 톤도 색도 레이아웃도 결정하지 않으면 가장 흔한 디폴트로 수렴한다. 그래서 코드 생성 전에 톤 선언 → 안티패턴 회피 → 비-디폴트 선택 3개 정당화를 순서대로 강제했다.
- 막연한 훈계는 모델에 안 먹힌다. 그래서 AI가 실제로 저지르는 패턴을 6범주(레이아웃·타이포·색·컴포넌트·콘텐츠·이미지)로 카탈로그화하고, 각 항목을 "AI가 뭘 하나 / 왜 티 나나 / 대신 뭘 하라"의 3단 고정 포맷으로 박았다.
- 출력 형식을 강제하면 디폴트로 도망칠 곳이 사라진다. "비-디폴트 선택 3개를 정당화하라"는 칸이 강제된 자기 설명을 강제된 의사결정으로 바꾼다.
Self-review칸은 출하 직전 자가 린트가 된다. - 컨텍스트를 아끼려고 지식을 진입점(얇게)과 참고 파일(깊게)로 쪼개 점진적 공개 구조로 짰다. 추상 원칙은 진입점에, 복붙 가능한 구체값(
#FAFAF7)은 참고 파일에 뒀다 — 모델은 추상보다 구체값에 강하다. - 백 마디 원칙보다 한 쌍의 before/after가 낫다. 같은 hero의 AI 디폴트 버전과 의도적 버전을 나란히 보여 주는 것만으로 방향이 전달된다.
- 첫 배포에서 마켓플레이스 스키마(
^\./.*)에 한 글자로 걸렸다. 릴리스 후 실제 설치 검증은 빌드 성공과는 별개의 필수 절차다.
다음 편이 생긴다면, 안티패턴 목록을 실제 사용 로그를 보며 어떻게 늘려 가고 있는지, 그리고 리뷰 명령어의 평가 기준을 모바일·대시보드 같은 화면 종류별로 어떻게 분화시키는지를 다뤄 볼 생각이다.
'기술 개발일지' 카테고리의 다른 글
| AI 에이전트 기반 웹 QA 도구 개발기 (1) — 시니어 QA처럼 움직이는 자율 탐색 테스터 (0) | 2026.06.18 |
|---|---|
| 프로젝트 산출물 자동화 개발기 (1) — 규칙 엔진과 검증 훅을 갖춘 문서 생성 플러그인 (0) | 2026.06.18 |
| 실시간 미리보기 + html2canvas 이미지 내보내기 — 텍스트 교체 도구 (3) (0) | 2026.06.12 |
| 스텝 위저드 UX와 전역 상태 설계 — 이미지 텍스트 교체 도구 (2) (0) | 2026.06.12 |
| OCR + 오버레이 렌더링 엔진 만들기 — 이미지 텍스트 교체 도구 (1) (0) | 2026.06.12 |