Claude Code 개발 자동화 플러그인 만들기 — ttutak 개발기
왜 만들었나
PRD 쓰고, 설계하고, 구현하고, 리뷰 받고, 커밋하고, PR 올리고. 매번 같은 사이클이었습니다. Claude Code를 쓰면서 각 단계를 자동화할 수 있겠다는 생각이 들었고, 그걸 하나의 플러그인으로 묶어보고 싶었습니다.
Claude Code에는 스킬(Skill)이라는 시스템이 있습니다. .claude/skills/ 디렉토리에 마크다운 파일을 두면, 그게 하나의 명령이 됩니다. 사용자가 /dev 로그인 기능 개발해줘라고 입력하면 해당 스킬이 발동되는 구조입니다.
처음엔 “이걸로 커밋 자동화나 해볼까” 정도였는데, 만들다 보니 PRD 작성부터 PR 생성까지 전체 파이프라인을 자동화하는 플러그인이 되었습니다. 이름은 ttutak(뚝딱). “개발해줘” 한마디면 뚝딱 만들어준다는 의미였습니다.
9개 스킬, 어떻게 나눴나
처음부터 9개를 계획한 건 아니었습니다. commit 스킬 하나로 시작해서, 필요할 때마다 하나씩 추가하다 보니 지금의 구조가 되었습니다.
| 스킬 | 역할 | 왜 분리했나 |
|---|---|---|
| dev | 전체 개발 사이클 오케스트레이터 | 파이프라인 전체를 관리하는 중앙 컨트롤러가 필요했습니다 |
| context | 도메인 지식 등록/관리 | 에이전트가 도메인 용어를 이해해야 정확한 코드를 생성합니다 |
| commit | 브랜치 타입 파싱 + 한국어 커밋 | git 명령을 직접 치면 스킬의 안전장치가 무시되기 때문입니다 |
| pull-request | 커밋 히스토리 기반 PR 생성 | PR 본문을 매번 손으로 쓰는 게 귀찮았습니다 |
| test | 도메인별 단위/통합/E2E 테스트 작성 | 구현은 자동화하면서 테스트는 수동이면 반쪽짜리였습니다 |
| lens | 코드에서 비즈니스 정책 탐지 | PO/PD가 “현재 정책이 뭐야?”라고 물을 때 코드를 직접 안 봐도 되게 하고 싶었습니다 |
| humanizer | AI 글쓰기 패턴 교정 | dev가 생성한 PRD나 설계서에 AI 티가 나면 곤란했습니다 |
| research | 웹 검색 기반 도메인 리서치 | context 등록 전에 도메인 조사가 필요한 경우가 있었습니다 |
| setup | 초기 설정 자동화 | gh 인증, Slack 웹훅 같은 초기 설정을 매번 설명하기 싫었습니다 |
핵심 원칙은 각 스킬이 독립적으로 쓸 수 있어야 한다는 것이었습니다. /commit만 단독으로 써도 되고, /dev가 내부에서 /commit을 호출해도 됩니다. Facade 패턴과 비슷한 구조입니다.
7단계 파이프라인
dev 스킬의 내부 구조입니다. 사용자가 “개발해줘”라고 하면 이 파이프라인이 순차적으로 실행됩니다.
1
Setup → Requirements(PRD) → Design → Implement → Review → Test → Complete(Commit/PR)
각 단계마다 다른 에이전트가 담당합니다.
| 단계 | 에이전트 | 하는 일 |
|---|---|---|
| Requirements | 제품책임자(PO) | Q&A로 요구사항을 구체화하고 PRD를 작성합니다 |
| Design | 설계자 + 비판자 | 기술 설계서를 작성하고, 비판자가 과잉 설계를 걸러냅니다 |
| Implement | 개발자 | 설계서 기반으로 코드를 구현합니다 |
| Review | QA + 보안 감사자 | 코드 리뷰와 보안 점검을 병렬로 수행합니다 |
| Test | 개발자 | 도메인별 단위/통합/E2E 테스트를 작성합니다 |
| Complete | 오케스트레이터 | 커밋, PR 생성, 알림까지 처리합니다 |
사용자 승인 없이 다음 단계로 넘어가지 않습니다. PRD를 보여주고 “이대로 진행할까요?”를 물어보고, 설계서도 확인받고, 구현 계획도 승인받습니다. 자동화하되 사용자가 통제권을 잃지 않는 구조를 만들고 싶었습니다.
hotfix 모드
“긴급 수정해줘”라고 하면 설계와 리뷰를 건너뛰는 경량 경로로 진행됩니다. 테스트 단계도 건너뜁니다.
1
Setup → Requirements(경량 PRD) → Implement → Complete
모든 상황에 전체 파이프라인을 강제하면 간단한 버그 수정에도 30분이 걸립니다. 그래서 모드를 나눴습니다.
자연어 라우팅
스킬 이름을 외울 필요 없이, 자연어로 말하면 적절한 스킬이 발동되는 구조를 만들었습니다.
1
2
3
4
5
6
7
8
| 이렇게 말하면 | 발동 스킬 |
|----------------------|----------|
| "개발해줘" | dev |
| "테스트 작성해줘" | test |
| "커밋해줘" | commit |
| "PR 올려" | pull-request |
| "정책 확인해줘" | lens |
| "도메인 등록해줘" | context |
.claude/rules/skill-routing.md에 매핑 테이블을 정의해두면, 에이전트가 사용자의 의도를 파악해서 해당 스킬을 호출합니다. 체이닝도 됩니다. “커밋하고 PR 올려”라고 하면 commit 완료 후 pull-request를 순차 실행합니다.
삽질: 스킬 호출이 안 될 때
개발 중 가장 골치 아팠던 문제는 dev 파이프라인의 마지막 단계에서 commit/pull-request 스킬이 제대로 호출되지 않는 것이었습니다.
문제: Read() 인라인 실행
처음에는 phase-complete(완료 단계)에서 commit 스킬을 이렇게 호출했습니다.
1
commit 스킬을 Read하여 프로세스를 실행한다.
스킬 파일을 Read()로 읽어서 그 내용을 인라인으로 따르는 방식이었습니다. 문제가 두 가지 있었습니다.
allowed-tools 제한이 무시됩니다. 스킬 파일의 frontmatter에 사용 가능한 도구를 제한해뒀는데,
Read()로 읽으면 이 제한이 시스템 레벨에서 강제되지 않았습니다. LLM이 스킬 프로세스를 건너뛰고 직접git commit을 실행할 가능성이 있었습니다.다른 플러그인의 동명 스킬과 충돌할 수 있습니다. 만약 다른 플러그인에도
commit이라는 이름의 스킬이 있으면, 어느 쪽이 실행될지 보장이 없었습니다.
해결: Skill 도구 + ttutak: prefix
Read() 대신 Skill 도구를 사용하도록 변경했습니다.
1
Skill("ttutak:commit")을 호출하여 커밋한다.
Skill 도구로 호출하면 스킬의 allowed-tools가 시스템 레벨에서 강제됩니다. 그리고 ttutak: prefix를 붙여서 반드시 이 플러그인의 스킬이 호출되도록 했습니다.
모든 스킬 간 교차 참조에 prefix를 적용하는 데 11개 파일을 수정해야 했습니다. 사용자 안내 텍스트의 /commit도 전부 /ttutak:commit으로 바꿨습니다. 번거로웠지만, 플러그인이 다른 환경에서 설치되었을 때 충돌 없이 동작하려면 필요한 작업이었습니다.
재미있는 점은, 플러그인 레포 자체에서 개발할 때는 Skill("ttutak:commit")이 안 됩니다. 로컬 스킬은 prefix 없이 commit으로 등록되기 때문입니다. 다른 프로젝트에서 ttutak을 플러그인으로 설치하면 그때 ttutak:commit으로 네임스페이스가 붙습니다. oh-my-claudecode 플러그인이 oh-my-claudecode:cancel 같은 prefix로 등록되는 것과 같은 원리였습니다.
test 스킬 — 빠진 퍼즐 한 조각
dev 파이프라인이 구현과 리뷰까지 자동화하는데, 테스트는 수동이었습니다. 이건 반쪽짜리 자동화였습니다.
test 스킬을 만들 때 두 가지 선택지가 있었습니다.
- dev 내부에 phase-test 추가 — dev 안에서만 쓸 수 있습니다
- 독립 스킬로 분리 — dev 없이도 “테스트 작성해줘”로 바로 쓸 수 있습니다
2번을 선택했습니다. 독립 스킬로 만들되, dev의 phase-complete에서 Skill("ttutak:test")를 호출하면 파이프라인에도 자연스럽게 연동됩니다.
test 스킬의 핵심은 기존 테스트 구조를 먼저 분석하는 것이었습니다. 프로젝트마다 테스트 네이밍 패턴, 디렉토리 구조, assertion 라이브러리가 다릅니다. 기존 테스트 2-3개를 읽어서 스타일을 파악한 뒤, 같은 패턴으로 새 테스트를 생성합니다.
1
2
3
단위 테스트: domain/{도메인}/{Entity}Test
통합 테스트: domain/{도메인}/{Service}IntegrationTest
E2E 테스트: interfaces.api/{도메인}/{Controller}E2ETest
dev 파이프라인의 PRD에 수용 기준(AC)이 있으면 그걸 테스트 케이스로 변환합니다. “사용자는 하루 최대 5건까지 주문할 수 있다” → 주문_6건째에_예외가_발생한다() 같은 식입니다.
자가점검 — Copilot이 잡아준 것들
PR을 올리면 GitHub Copilot이 자동으로 코드 리뷰를 해줍니다. ttutak PR에서 Copilot이 잡아준 것들이 꽤 유용했습니다.
git diff main --name-only→ base가 main이 아닌 레포에서 깨집니다.git merge-base를 써야 했습니다.- phase-complete에서 “build/test 실행”이라고 썼는데, commit 스킬은 build만 합니다. test는 test 스킬이 담당합니다.
- “8개 스킬”이라고 쓴 곳이 여전히 있었는데, test를 추가해서 9개가 되었습니다.
- index.html에서 “6단계 파이프라인”이라는 제목 아래 7개 박스가 표시되고 있었습니다.
이런 문서 간 불일치는 사람이 놓치기 쉽습니다. 자가점검 에이전트(harsh-critic)를 돌려서 CRITICAL/HIGH/MEDIUM으로 분류하고, 하나씩 수정하는 프로세스를 만들어두니 편했습니다.
릴리스
ttutak은 현재 v1.1.1입니다. GitHub Release는 PR이 main에 머지되면 자동으로 생성됩니다. plugin.json에서 버전을 추출하고, 해당 태그가 없으면 CHANGELOG.md에서 내용을 파싱해서 Release를 만드는 워크플로우입니다.
버전을 올릴 때 세 군데를 맞춰야 합니다. plugin.json, marketplace.json, CHANGELOG.md. 하나라도 빠지면 플러그인 UI에 이전 버전이 표시됩니다. 이것도 릴리스 규칙 문서(.claude/rules/release.md)에 체크리스트로 남겨뒀습니다.
배운 것
플러그인을 만들면서 가장 크게 느낀 건, LLM에게 “뭘 하라”보다 “뭘 하지 마라”를 정의하는 게 더 중요하다는 것이었습니다.
git push --force차단- main 브랜치 직접 커밋 차단
- PR 머지 절대 금지 (PR 생성까지만)
- 민감 파일 커밋 경고
이런 안전장치가 없으면 자동화가 사고를 자동화하는 도구가 됩니다. 스킬의 allowed-tools로 사용 가능한 도구를 제한하고, hooks로 위험한 명령을 사전 차단하는 구조를 만들었습니다.
다음 글에서는 이런 제어 기법을 하네스 엔지니어링이라는 관점에서 좀 더 깊이 다뤄보겠습니다.
ttutak 소스코드: github.com/rnqhstmd/ttutak