AI에게 린트 & 테스트를 시키면 매번 1k 토큰이 증발합니다
AGENTS.md에 '린트 돌려' 적고 있었다면, 지금 당장 Hook으로 바꾸세요.
들어가며
이전에 클로드 코드 사용에 주저 하고 있는 당신이라면에서 AGENTS.md에 아래와 같이 작성해서 코드 품질을 관리하는 방법을 소개한 적이 있다.
작업 완료 후 TODO
- ./gradlew build를 통해 빌드에 문제가 없는지 확인 하세요
- ./gradlew lint를 통해 린트룰을 모두 준수 했는지 확인 하세요.
- ./gradlew test 를 통해 테스트가 잘 수행 되는지 확인 하세요.이 방법은 작동하긴 한다. 클로드가 작업을 끝내고 빌드, 린트, 테스트를 순서대로 돌려준다.
문제는 비용이다. 클로드가 매번 이 명령어들을 "읽고", "실행하고", "결과를 해석하는" 과정 자체가 토큰을 소모한다. 작업 한 건당 거의 1k 토큰 가까이 날아간다. 린트가 깨끗하게 통과하는 정상 케이스에서도 말이다.
최근에 더 나은 방법을 찾았다. Hook 기반으로 처리하면 정상 케이스는 사실상 노 비용이다. 이 글은 그 방법에 대한 AS(After-Service)다.
기존 방식의 문제점
기존 방식을 다시 정리하면 이렇다.
클로드가 코드 작성 완료
→ AGENTS.md의 "작업 완료 후 TODO"를 읽음 (토큰 소모)
→ ./gradlew build 실행 (토큰 소모)
→ 결과 해석 (토큰 소모)
→ ./gradlew lint 실행 (토큰 소모)
→ 결과 해석 (토큰 소모)
→ ./gradlew test 실행 (토큰 소모)
→ 결과 해석 (토큰 소모)
→ "모두 통과했습니다" 응답 (토큰 소모)
문제가 없는 정상 케이스에서도 이 전체 흐름을 매번 탄다. 린트 위반이 하나도 없고, 테스트가 전부 통과해도 토큰은 소모된다. 통과하는 걸 확인하기 위해 돈을 쓰는 셈이다.
Hook이란?
Claude Code는 특정 이벤트에 반응하여 셸 명령어를 자동 실행하는 Hook 시스템을 지원한다. 주요 이벤트는 다음과 같다.
| Hook | 실행 시점 |
|---|---|
PreToolUse | 도구 호출 전 (차단 가능) |
PostToolUse | 도구 호출 완료 후 |
UserPromptSubmit | 사용자 프롬프트 제출 시 |
Stop | Claude가 응답을 마칠 때 |
SubagentStop | 서브에이전트 작업 완료 시 |
Notification | 알림 발생 시 |
핵심은 Stop 훅이다. 클로드가 모든 작업을 완료하고 응답을 마치는 시점에 자동으로 실행된다.
Hook에 대한 자세한 내용은 공식 문서를 참고.
Hook 기반으로 전환하기
{프로젝트 디렉토리}/.claude/settings.json에 다음과 같이 설정한다.
{
"hooks": {
"Stop": [
{
"hooks": [
{
"type": "command",
"command": "./gradlew spotlessApply detekt"
}
]
}
]
}
}이게 전부다. 이제 클로드가 작업을 마칠 때마다 spotlessApply와 detekt가 자동으로 실행된다.
왜 이게 더 좋은가
핵심은 exit code 활용이다.
린트 도구들은 exit code를 잘 활용한다. 정상이면 0, 문제가 있으면 0이 아닌 코드를 반환한다. Claude Code는 이 exit code를 기반으로 동작한다.
정상 케이스 (exit code 0)
클로드가 코드 작성 완료
→ Stop 훅 발동
→ ./gradlew spotlessApply detekt 실행
→ exit code 0 (성공)
→ 끝. 클로드는 아무것도 하지 않음.
토큰 소모 = 0. 훅은 클로드 외부에서 실행되는 셸 명령어이기 때문에, 성공하면 클로드가 개입할 이유가 없다.
문제가 있는 케이스 (exit code != 0)
클로드가 코드 작성 완료
→ Stop 훅 발동
→ ./gradlew spotlessApply detekt 실행
→ exit code 1 (실패)
→ 클로드가 출력 로그를 읽고 문제 수정
문제가 있을 때만 클로드가 개입한다. spotlessApply가 자동 수정 가능한 포맷팅 문제는 알아서 고치고, detekt가 잡아낸 코드 품질 이슈 중 자동 수정이 안 되는 건만 클로드가 처리한다.
비교 정리
| 구분 | 기존 (AGENTS.md) | Hook 기반 |
|---|---|---|
| 정상 케이스 토큰 | ~1k 토큰 | 0 토큰 |
| 오류 케이스 토큰 | ~1k+ 토큰 | 오류 수정분만 소모 |
| 자동 수정 | 클로드가 직접 수정 | spotlessApply가 먼저 수정 |
| 실행 보장 | 클로드가 까먹을 수 있음 | 무조건 실행 |
마지막 행이 은근히 중요하다. AGENTS.md에 적어놨어도 클로드가 가끔 린트 실행을 빼먹는 경우가 있다. Hook은 시스템 레벨에서 강제하기 때문에 빼먹을 수가 없다.
"훅이 안 되는 거 아니야?" 당황하지 마세요
처음 Hook을 설정하고 나서 당황할 수 있다. 나도 그랬다.
클로드가 작업을 끝냈는데 아무 반응이 없다. "훅이 동작을 안 하는 건가?" 싶어서 설정을 다시 확인하고, JSON 문법이 틀렸나 점검하고... 그런데 사실 정상이다.
exit code 0으로 성공한 경우, 클로드는 아무런 출력도 하지 않는다. 조용히 훅이 실행되고, 조용히 끝난다. 이게 정상 동작이다.
실패하는 경우에만 클로드가 로그를 읽고 수정 작업에 들어가면서 터미널에 출력이 나온다. 즉, 아무 반응이 없으면 잘 되고 있는 것이다.
훅이 잘 동작하는지 확인하고 싶다면, 일부러 린트 위반 코드를 넣어서 실패 케이스를 한 번 만들어보면 된다.
한 걸음 더: PostToolUse 활용
Stop 훅 외에도 PostToolUse 훅을 활용하면 더 세밀한 제어가 가능하다. 예를 들어, 파일 편집 후 즉시 포매팅을 적용하고 싶다면:
{
"hooks": {
"PostToolUse": [
{
"matcher": "tool == 'Edit' || tool == 'Write'",
"hooks": [
{
"type": "command",
"command": "./gradlew spotlessApply"
}
]
}
],
"Stop": [
{
"hooks": [
{
"type": "command",
"command": "./gradlew detekt"
}
]
}
]
}
}이렇게 하면 파일을 수정할 때마다 포매팅이 즉시 적용되고, 모든 작업이 끝나면 정적 분석이 한 번 돌아간다. 물론 이 정도는 프로젝트 상황에 맞게 조절하면 된다.
마치며
정리하면 간단하다.
- AGENTS.md에 "린트 돌려", "테스트 돌려" 적는 방식은 매번 토큰을 소모한다.
- Hook으로 바꾸면 정상 케이스는 비용 0, 문제 있을 때만 클로드가 개입한다.
- 시스템 레벨 강제라 실행 누락도 없다.
특히 프로젝트가 커지고 작업 횟수가 많아질수록 차이가 벌어진다. 하루에 수십 번 작업한다고 치면, 매번 1k 토큰씩 아끼는 건 꽤 유의미한 절감이다.
이전 글에서 소개한 방법을 쓰고 계신 분이 있다면, Hook 기반으로 전환하는 걸 추천한다.