| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 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 |
- Android Interceptor
- Android 12
- WebView
- Android ViewPager2
- Android ProgressBar
- 안드로이드 갤러리 접근
- 카카오 알고리즘
- 영어공부
- 66챌린지
- 알고리즘 자바
- 코틀린 코루틴
- Java
- 안드로이드 카카오 로그인
- Kotlin FCM
- Android Jetpack
- coroutine
- 영어독립365
- scope function
- 습관만들기
- Android
- 프로그래머스 알고리즘
- android recyclerview
- 안드로이드
- OkHttp Interceptor
- MVP Architecture
- DataBinding
- Kotlin
- Android Navigation
- Android WebView
- Android 12 대응
- Today
- Total
주맨의 개발노트
[AI] Claude Code 훅으로 디자인 시스템 위반 자동 감지하기 본문
들어가며
Claude Code를 프로젝트에 적극적으로 활용하다 보면, 빠르게 코드를 만들어주는 만큼 팀 컨벤션을 놓치는 장면도 자주 보게 됩니다. 저희 팀에서는 특히 디자인 시스템 컴포넌트를 사용해야 하는 화면에서 Claude가 Material3 기본 컴포넌트를 선택하는 문제가 반복됐습니다.
이 글은 CLAUDE.md로 규칙을 알려주는 것에서 한 단계 더 나아가, PostToolUse 훅으로 디자인 시스템 위반을 자동 감지한 경험을 정리한 기록입니다. 핵심은 Claude에게 더 길게 설명하는 것이 아니라, 반드시 지켜야 하는 규칙을 코드로 검사하는 구조를 함께 두는 것입니다.
Claude가 Material3 AppBar를 사용했다
새 화면을 만들 때 Claude에게 맡기면 Screen, Scaffold, ViewModel 연결, 상태 구독까지 빠르게 완성됩니다. 그런데 작성된 코드를 리뷰하던 중 다음과 같은 코드를 발견했습니다.
Scaffold(
topBar = {
CenterAlignedTopAppBar(title = { Text("설정") })
}
) { ... }프로젝트에는 이미 디자인 시스템으로 정의한 NeveraAppBar가 있었지만, Claude는 Material3의 기본 컴포넌트를 사용했습니다. 별도의 지시가 없다면 Claude가 가장 일반적이고 익숙한 Material3 API를 선택하는 것은 어느 정도 자연스러운 결과였습니다.
한두 번은 코드 리뷰에서 잡을 수 있었습니다. 하지만 기능 개발 속도가 빨라지고 Claude를 더 적극적으로 활용할수록 같은 문제가 반복됐습니다. 리뷰어가 매번 같은 코멘트를 남기는 것도 피로하고, 잘못된 컴포넌트가 여러 화면에 퍼진 뒤 수정하는 것은 더 큰 비용이었습니다.
왜 커스텀 AppBar를 만들었나
Material3의 TopAppBar는 유연한 컴포넌트입니다. 하지만 팀 단위 프로젝트에서는 그 유연함이 오히려 일관성을 해칠 수 있습니다. 각 화면에서 직접 사용하면 status bar padding 처리, shadow 유무, 아이콘 크기, 여백 같은 세부 스타일이 조금씩 달라질 수 있기 때문입니다.
그래서 저희는 AppBar를 디자인 시스템 컴포넌트로 만들었습니다. 용도별로 네 가지 AppBar를 정의했고, 모든 화면은 이 네 가지 중 하나를 선택하도록 했습니다.
| 컴포넌트 | 용도 |
|---|---|
NeveraAppBar |
일반 상세·설정 화면. 중앙 제목, 좌측 navigation, 우측 action을 제공 |
NeveraDisplayAppBar |
섹션 진입·타이틀 강조 화면. 좌측 큰 제목과 우측 action을 제공 |
NeveraLogoAppBar |
메인·브랜드 화면. 좌측 로고와 우측 action을 제공 |
NeveraSearchAppBar |
검색 화면. AppBar 내부에 검색 UI를 직접 배치 |
사용법은 단순합니다. 화면을 만드는 개발자는 AppBar의 스타일을 매번 결정하지 않고, 화면 성격에 맞는 디자인 시스템 컴포넌트만 선택하면 됩니다.
Scaffold(
topBar = {
NeveraAppBar(
title = "화면 제목",
navigation = NeveraAppBarNavigation.Back(onClick = onBackClick),
)
}
) { ... }CLAUDE.md에 적어두면 충분할까?
Claude Code를 사용한다면 자연스럽게 CLAUDE.md를 떠올리게 됩니다. Claude Code가 실행될 때 프로젝트 컨텍스트로 읽는 파일이니, 여기에 AppBar 규칙을 명시하면 되지 않을까 생각할 수 있습니다.
실제로 효과는 있었습니다. CLAUDE.md에 AppBar 규칙을 추가한 뒤 Claude가 디자인 시스템 컴포넌트를 선택하는 비율은 높아졌습니다. 하지만 그것만으로는 충분하지 않았습니다.
CLAUDE.md의 한계: 프롬프트는 해석된다
CLAUDE.md는 Claude에게 프로젝트 컨텍스트를 제공합니다. 문제는 실제 개발 흐름에서 대화가 길어지는 경우가 많다는 점입니다. 새 기능 하나를 만들면서 파일을 10개, 20개 수정하는 일도 흔합니다.
작업이 길어질수록 초반에 읽었던 CLAUDE.md의 내용은 컨텍스트 안에서 점점 멀어집니다. 사람도 온보딩 문서를 꼼꼼히 읽었더라도 시간이 지나면 세부 규칙을 잊을 수 있습니다. Claude도 마찬가지였습니다.
CLAUDE.md는 안내입니다. 안내는 Claude가 해석하고 적용하지만, 코드는 조건 없이 실행됩니다.
여기서 접근 방식이 바뀌었습니다. CLAUDE.md로 "이 프로젝트에서는 커스텀 AppBar를 사용하세요"라고 안내하는 것만으로 끝내지 않고, 커스텀 AppBar를 사용하지 않았을 때 즉시 경고를 발생시키는 검사를 추가하기로 했습니다.
Claude가 규칙을 알고 있더라도 긴 작업 중 맥락을 놓칠 수 있습니다. 리뷰에서 뒤늦게 발견되면 수정 범위가 커질 수 있습니다.
Claude에게 규칙을 알려주고, 파일 수정 직후 실제 코드가 규칙을 지키는지도 검사합니다. 예방과 교정이 함께 동작합니다.
두 레이어로 접근하기
Claude에게 팀 규칙을 안정적으로 적용하려면 두 가지가 동시에 필요했습니다. 하나는 Claude가 코드를 생성할 때 규칙을 인지하도록 만드는 것이고, 다른 하나는 그래도 실수했을 때 파일 작성 직후 알 수 있게 만드는 것입니다.
CLAUDE.md로 디자인 시스템 AppBar 규칙을 알려준다
PostToolUse 훅으로 파일 수정 직후 위반 여부를 검사한다
CLAUDE.md: Claude에게 규칙 알려주기
CLAUDE.md에는 AppBar 규칙을 명시했습니다. Claude는 새 화면을 만들 때 이 맥락을 참고해 Material3 기본 AppBar 대신 디자인 시스템 컴포넌트를 선택할 수 있습니다.
## AppBar 규칙 Scaffold의 `topBar` 파라미터에는 반드시 디자인 시스템에서 정의된 AppBar를 사용해야 합니다. Material3 기본 AppBar(`TopAppBar`, `CenterAlignedTopAppBar` 등)를 직접 사용하지 않습니다. ### 잘못된 사용 예시 (금지) Scaffold( topBar = { TopAppBar(title = { Text("제목") }) // X CenterAlignedTopAppBar(title = { ... }) // X } )
PostToolUse 훅: 어기면 즉시 감지하기
PostToolUse는 Claude가 파일을 수정한 직후 자동으로 실행되는 훅입니다. 여기서 규칙 위반을 감지하면 Claude는 현재 작업 흐름 안에서 바로 피드백을 받고 수정할 수 있습니다.
먼저 .claude/settings.json에 훅을 등록했습니다.
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "bash .claude/hooks/check-appbar.sh"
}
]
}
]
}
}실제 검사 로직은 쉘 스크립트로 작성했습니다. .kt 파일이 아니거나 Scaffold(가 없으면 조용히 통과하고, topBar가 있는데 허용된 AppBar 패턴이 없을 때만 경고를 출력합니다.
#!/bin/bash # PostToolUse hook: Scaffold의 topBar에 Nevera 디자인 시스템 AppBar 사용 여부 검사 INPUT=$(cat) FILE=$(echo "$INPUT" | python3 -c " import sys, json data = json.load(sys.stdin) print(data.get('tool_input', {}).get('file_path', '')) " 2>/dev/null || echo "") # .kt 파일만 검사 [[ "$FILE" != *.kt ]] && exit 0 [[ ! -f "$FILE" ]] && exit 0 # Scaffold가 없으면 통과 grep -q "Scaffold(" "$FILE" || exit 0 # topBar가 있는데 Nevera AppBar를 쓰지 않으면 경고 if grep -q "topBar" "$FILE"; then if ! grep -qE "Nevera(Display|Logo|Search)?AppBar" "$FILE"; then echo "[디자인 시스템 위반] $FILE" echo "Scaffold의 topBar에는 Material3 기본 AppBar 대신 Nevera 디자인 시스템 AppBar를 사용해야 합니다." exit 2 fi fi
Claude Code에서 훅이 exit 2를 반환하면 Claude의 작업 흐름이 중단되고 경고 메시지가 출력됩니다. 그러면 Claude는 이 피드백을 보고 즉시 코드를 수정할 수 있습니다.
[디자인 시스템 위반] feature/settings/SettingsScreen.kt Scaffold의 topBar에는 Material3 기본 AppBar 대신 Nevera 디자인 시스템 AppBar를 사용해야 합니다. 허용된 컴포넌트: - NeveraAppBar : 일반 상세·설정 화면 - NeveraDisplayAppBar : 섹션 진입·타이틀 강조 화면 - NeveraLogoAppBar : 메인·브랜드 화면 - NeveraSearchAppBar : 검색 화면
두 레이어가 하는 일은 다르다
팀에 새 개발자가 합류하면 온보딩을 합니다. 프로젝트 규칙과 컨벤션을 설명합니다. 그렇다고 린트를 없애지는 않습니다. 온보딩을 마친 개발자도 실수할 수 있기 때문입니다.
CLAUDE.md와 훅의 관계도 비슷합니다. CLAUDE.md는 Claude에게 규칙을 알려주고, 훅은 파일이 수정될 때마다 코드가 실제로 규칙을 따르는지 검사합니다.
| 구분 | 무엇을 하는가 | 무엇에 의존하는가 |
|---|---|---|
CLAUDE.md |
Claude에게 규칙을 알려준다 | Claude의 주의와 현재 컨텍스트 |
PostToolUse 훅 |
파일이 수정될 때마다 규칙을 검사한다 | 코드. 조건 없이 실행된다 |
둘 다 있어야 하는 이유
CLAUDE.md만 있으면 Claude가 규칙을 알고 있더라도 긴 작업 중에 실수할 수 있습니다. 훅만 있으면 Claude가 왜 틀렸는지 충분한 맥락 없이 경고만 받게 됩니다. 둘을 함께 사용하면 예방과 교정을 동시에 가져갈 수 있습니다.
CLAUDE.md는 Claude가 처음부터 올바른 선택을 하도록 유도하고, 훅은 그래도 실수가 생겼을 때 작성 직후 잡아냅니다. CLAUDE.md 덕분에 훅이 발동하는 횟수가 줄고, 훅 덕분에 CLAUDE.md의 빈틈을 보완할 수 있습니다.
도입 후 좋아진 점
이 설정을 추가한 뒤 AppBar 관련 리뷰 코멘트가 줄었고, 개발자가 같은 내용을 반복해서 확인하는 피로도도 낮아졌습니다. Claude가 새 화면을 만들 때 NeveraAppBar를 먼저 선택하고, 실수하더라도 훅이 바로 알려주는 흐름이 만들어졌습니다.
특히 좋았던 점은 이 설정이 Git에 포함된다는 것입니다. 팀원이 Claude Code로 개발할 때도 동일한 규칙이 적용됩니다. 별도로 설명을 반복하지 않아도, 프로젝트 안에 들어온 자동화가 같은 기준을 계속 적용합니다.
훅을 쓰면 토큰이 더 많이 소비될까?
팀원들에게 이 설정을 공유했을 때 가장 먼저 나온 질문은 토큰 사용량이었습니다. 파일을 수정할 때마다 쉘 스크립트가 실행된다면, 그 결과가 Claude의 컨텍스트로 들어오면서 토큰 소비가 늘어나지 않을까 하는 걱정이었습니다.
결론부터 말하면, 대부분의 경우 훅은 아무 출력도 내지 않고 종료됩니다. 스크립트는 여러 단계에서 조건을 확인하고, 조건에 맞지 않으면 즉시 exit 0으로 빠져나옵니다.
.kt 파일이 아니면 exit 0
Scaffold( 또는 topBar가 없으면 exit 0
NeveraAppBar 계열을 사용하면 exit 0
exit 2
Claude Code의 훅은 출력이 있을 때만 컨텍스트에 추가됩니다. exit 0에 아무 출력도 없다면 Claude의 컨텍스트에는 추가되는 내용이 없습니다. 즉, 규칙을 잘 지키고 있는 한 훅이 실행되더라도 토큰 소비는 사실상 발생하지 않습니다.
정리하며
Claude Code를 잘 활용하려면 "Claude에게 더 자세히 말하기"만으로는 부족할 때가 있습니다. 프로젝트 규칙 중 반드시 지켜져야 하는 부분은 프롬프트에만 두지 말고, 코드로 검사하는 구조를 함께 두는 것이 안정적입니다.
CLAUDE.md는 Claude가 올바른 선택을 하도록 돕고, PostToolUse 훅은 결과물이 규칙을 벗어났을 때 즉시 알려줍니다. 이 조합은 작은 자동화지만, 팀 컨벤션을 지키는 데 꽤 실용적인 안전망이 되었습니다.
- CLAUDE.md는 Claude에게 디자인 시스템 규칙을 알려주는 예방 레이어입니다.
- PostToolUse 훅은 파일 수정 직후 실제 코드가 규칙을 지키는지 검사하는 교정 레이어입니다.
- 프롬프트는 해석되지만 훅은 조건이 맞을 때 코드로 실행됩니다.
- exit 0에 출력이 없으면 대부분의 정상 작업은 컨텍스트와 토큰 사용량에 영향을 주지 않습니다.
- 둘을 함께 쓰면 AI 보조 개발에서도 팀 컨벤션을 더 안정적으로 유지할 수 있습니다.
'AI' 카테고리의 다른 글
| [AI] Claude Code Hook으로 작업 완료 알림 받기 (0) | 2026.03.31 |
|---|---|
| [AI] Claude Code Skill 사용 경험 - 왜 user-invocable: false로 설계했나 (1) | 2026.03.30 |
| [AI] Claude Code Skill로 커밋 메시지 추천 자동화 (1) | 2026.03.27 |
| [AI] Claude Code Skills 설정 가이드 — SKILL.md 구조부터 Frontmatter까지 (0) | 2026.03.20 |
| [AI] Claude Agent Skills - Skill 이해와 사용법 (0) | 2026.03.19 |