코딩 에이전트를 위해 Gradle 빌드보다 빠른 Java 검사기를 Rust로 만들다
요약
기존의 느린 빌드 시스템 대신, Rust로 작성된 정적 분석기 'grounds'를 소개합니다. 이 도구는 Java 프로젝트에서 편집 후 즉시 실행되어 컴파일 오류, 누락된 빈(bean), 널 역참조 등 다양한 문제를 빠르게 잡아냅니다. Claude Code와 같은 에이전트 환경의 효율성을 극대화하여 개발 워크플로우를 개선하는 데 초점을 맞추었습니다.
핵심 포인트
- Rust 기반 정적 분석기 'grounds'로 Java 검사 속도를 혁신했습니다.
- 빌드 시스템(Gradle/Maven) 없이도 즉각적인 피드백을 제공합니다.
- 컴파일 오류, Spring 와이어링 문제 등 광범위한 문제를 포착합니다.
- 에이전트가 작업하는 동안 실시간으로 변경 사항을 감지하여 개발 효율성을 높입니다.
저는 자바 프로젝트에서 Claude Code를 많이 사용합니다. 거의 모든 변경 후에, 에이전트는 무언가 문제가 생겼는지 확인하기 위해 ./gradlew build를 실행하려고 합니다. 제가 작업하는 프로젝트 중 일부는 2~5분이 걸리며, 대부분의 경우 아무것도 발견하지 못합니다. 만약 에이전트에게 빌드를 건너뛰라고 지시하면, 실수가 쌓이다가 마지막 테스트 실행 때 한꺼번에 다섯 가지를 풀어내야만 합니다.
저는 그 중간 정도의 것을 원했습니다: 모든 편집 후에 실행되고, 1초도 걸리지 않으며, 지루한 문제들을 잡아내는 검사기. 더 이상 존재하지 않는 메서드, Spring이 찾지 못할 빈(bean), 명백히 터질 null 값 같은 것들 말입니다.
그래서 저는 grounds를 만들었습니다. 이것은 Rust로 작성된 Java용 정적 분석기(static analyzer)입니다. 실행하는 데 Gradle, Maven 또는 JVM이 필요하지 않습니다.
주의: 실험적인 버전입니다. 현재 0.0.6 버전에 있습니다. 21개의 오픈 소스 프로젝트에서 테스트되었으며 그게 전부입니다. 만약 귀하의 코드가 이들과 다르다면, 오탐(false positives)과 아직 이해하지 못하는 설정이 있을 수 있음을 예상해야 합니다.
기능
이는 Claude Code 후크(hook)로 실행됩니다. 에이전트가 파일을 편집한 후에, grounds는 변경된 줄을 검사하며 보통 0.1초에서 0.3초 만에 처리하고, 새로 발견된 모든 것을 에이전트에게 다시 전송합니다. 에이전트가 턴(turn)을 완료하려고 할 때, 변경된 모든 것에 대해 전체 규칙 세트를 실행하고 무언가를 발견하면 턴을 차단합니다.
무엇을 찾는지:
- 컴파일 오류 (Compile breaks). 더 이상 존재하지 않는 메서드나 생성자에 대한 호출, 잘못된 인자 유형, 아무것도 오버라이딩하지 않는
@Override등이 포함됩니다. 만약 메서드를 이름 변경하면 아직 업데이트하지 않은 다른 파일의 모든 호출 지점을 나열해 줍니다. 에이전트에게는 이 부분이 가장 유용했습니다. - Spring / Micronaut 와이어링 (wiring). 누락된 빈(beans), 모호한 빈, 오타가 있는
@Qualifier, 생성자 순환 참조(constructor cycles), Lombok이 조용히 드롭하는@Qualifier등이 있습니다. - 데이터 흐름 (Dataflow). 널 역참조(Null dereferences), 절대 닫히지 않는 리소스, 검사 없이
Optional.get()사용, 데드 스토어(dead stores) 등이 포함됩니다. - Sonar, SpotBugs, PMD, Error Prone 및 IntelliJ에서 재구현한 약 300개의 규칙이 있습니다. 각 규칙은 해당 도구의 어떤 규칙에 해당하는지 명시합니다.
메서드를 이름 변경했을 때 에이전트가 보는 모습은 다음과 같습니다:
grounds: API changes in src/main/java/.../Owner.java broke 15 call site(s) in 6 other file(s) not yet updated:
src/main/java/.../PetController.java:103:9: `Owner` has no method `addPet`
...
에이전트 없이도 사용할 수 있습니다. grounds check는 전체 프로젝트에서 실행되며, grounds changed는 HEAD 이후 변경된 부분에서만 실행됩니다.
빌드 없이 작동하는 방식
모든 것을 tree-sitter로 구문 분석(parse)하고 각 파일을 작은 스텁(stub)으로 변환합니다. 이 스텁에는 시그니처, 어노테이션, 상수 등이 포함됩니다. 스텁은 파일 해시(file hash)에 의해 캐싱됩니다. 메서드 본체는 실제로 검사하는 파일에 대해서만 분석됩니다. 작은 데몬이 편집 사이의 인덱스를 유지합니다.
의존성(dependencies)의 경우 ~/.gradle 및 ~/.m2 캐시에서 JAR 파일을 직접 읽어오기 때문에, 해당 머신에서 프로젝트가 최소한 한 번 빌드된 상태일 때 가장 잘 작동합니다. JDK의 경우 ct.sym을 읽습니다. 정확한 클래스패스가 필요하다면, grounds classpath를 실행하여 Gradle 또는 Maven이 한번 돌게 하여 덤프(dump)합니다.
제가 계속 돌아가며 강조하는 규칙은 다음과 같습니다: 타입을 모르면 아무것도 말하지 않는다. JAR 파일 누락은 잘못된 결과가 아니라 적은 발견 건수를 의미합니다. 에이전트가 거짓 경보에 시간을 낭비하게 하는 것보다 무언가를 놓치는 편이 낫습니다.
수치 (Numbers)
M4 Pro에서 실행한 전체 프로젝트 테스트:
| project | files | time |
|---|---|---|
| spring-petclinic | 50 | 0.24 s |
| ... | ||
| A single-file check through the daemon is about 0.1 s. The biggest file I tried, a 6,800-line one, took 0.32 s. |
테스트 방법
대부분의 시간이 소요된 부분입니다. 규칙을 작성하는 것은 빠릅니다. 올바른 코드에서 이 규칙들이 침묵하게 만드는 것이 훨씬 오래 걸립니다.
- 21개 프로젝트 모두 컴파일되고 앱이 시작되므로, 여기에 대한 컴파일 오류나 DI(Dependency Injection) 관련 발견 사항은 거짓 양성입니다. 현재는 없습니다. 새로운 Spring 프로젝트를 처음 실행했을 때 1,400개가 넘는 보고서가 나왔는데, 대부분 제가 아직 모델링하지 못한 Lombok 기능과 캐시된 jar와 프로젝트 간의 버전 불일치 때문이었습니다.
- 같은 코드를 가지고 PMD와 비교했습니다. 그 결과 필드 조회 버그가 발견되었고, 이는 1,524개의 허위
• Apple Silicon Mac 전용으로 미리 빌드된 바이너리만 제공됩니다. 다른 플랫폼은 소스에서 직접 빌드해야 합니다.
• Windows는 테스트되지 않았습니다. 데몬은 Unix 소켓을 사용합니다.
• Kotlin 소스는 무시되므로, 혼합된 Kotlin/Java 프로젝트에는 사각지대가 생길 수 있습니다.
• DataSource나 ObjectMapper와 같은 Spring 자동 구성 빈(auto-configured beans)은 모델링되지 않으므로, 해당 부분에 대해서는 침묵합니다.
만약 사용해 보시고 잘못 보고하는 부분이 있다면, 이를 재현할 수 있는 짧은 코드 스니펫을 보내주시는 것이 가장 유용합니다. 이슈는 GitHub에서 열려 있습니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기