정적 분석 도구 활용
코드를 실행하지 않고 소스·바이트코드를 분석해 버그, 코드 냄새, 보안 취약점을 미리 찾아내는 정적 분석을 다룹니다. SpotBugs·PMD·Error Prone·SonarQube의 역할 구분, shift-left로 수정 비용을 낮추는 원리, 그리고 오탐 관리와 CI 통합 전략까지 정리합니다.
지난 글에서 컨벤션을 도구로 강제하는 자동화 계층을 다뤘다. 그 계층의 마지막이자 가장 강력한 조각이 정적 분석이다. 스타일 검사가 “이 코드가 규칙에 맞게 생겼는가”를 본다면, 정적 분석은 한 걸음 더 나아가 “이 코드가 실제로 버그를 품고 있는가”를 코드를 실행하지 않고 찾아낸다. 이번 글에서는 정적 분석이 무엇을 어떻게 잡아내는지, 왜 일찍 잡을수록 이득인지, 그리고 실전에서 오탐과 어떻게 공존하는지를 정리한다.
실행하지 않고 찾는다
정적 분석(static analysis)은 프로그램을 돌리지 않고 소스 코드나 바이트코드를 분석해 문제를 찾는 기법이다. 컴파일러가 타입 오류를 잡듯, 정적 분석기는 그보다 넓은 범위의 결함 패턴을 데이터 흐름 분석과 규칙 매칭으로 검출한다. 실행하지 않으므로 테스트가 도달하지 못한 예외 경로까지 훑을 수 있다는 것이 강점이다.
도구마다 잡는 영역이 조금씩 다르다. 어떤 도구를 왜 쓰는지 이해하려면 이 역할 구분을 먼저 잡아야 한다.
- 버그 패턴 —
SpotBugs(옛 FindBugs)는 바이트코드를 분석해 null 역참조, 자원 누수,==로 문자열 비교 같은 고전적 실수를 찾는다. 구글의Error Prone은 컴파일 단계에 끼어들어 오류 가능성이 높은 코드를 잡는다. - 코드 냄새·복잡도 —
PMD와SonarQube는 긴 메서드, 중복 코드, 지나치게 높은 순환 복잡도처럼 당장은 안 터지지만 유지보수를 갉아먹는 냄새를 지적한다. - 보안 취약점 —
FindSecBugs,Semgrep등은 SQL 인젝션, 하드코딩된 비밀, 안전하지 않은 역직렬화 같은 보안 결함에 특화돼 있다. - 스타일 위반 —
Checkstyle은 지난 글에서 본 명명·포맷 규칙 위반을 담당한다.
왜 일찍 잡아야 하는가
정적 분석의 진짜 가치는 검출 시점을 앞당기는 데 있다. 소프트웨어 공학에서 오래 관찰된 사실 하나는, 같은 결함이라도 늦게 발견될수록 수정 비용이 기하급수적으로 커진다는 것이다. IDE에서 타이핑하다 잡으면 몇 초면 고치지만, 프로덕션에서 터지면 장애 대응·핫픽스·회고까지 수십 배의 비용이 든다.
이 곡선을 왼쪽으로 미는 것을 shift-left 라고 부른다. 정적 분석은 바로 이 이동을 가능하게 한다. IDE 플러그인으로 작성 중에, 프리커밋 훅으로 커밋 전에, CI 파이프라인으로 머지 전에 결함을 잡아, 비싼 오른쪽 구간까지 넘어가지 못하게 막는다.
// build.gradle — SpotBugs를 빌드에 통합
plugins {
id 'com.github.spotbugs' version '6.0.0'
}
spotbugs {
effort = 'max' // 분석 강도 최대
reportLevel = 'high' // 신뢰도 높은 것만 우선
}
오탐과 공존하기
정적 분석을 처음 도입하면 대개 수백, 수천 건의 경고가 쏟아진다. 이 중 상당수는 오탐(false positive) 이거나 팀 맥락에서 무시해도 좋은 것들이다. 여기서 흔한 실패가 두 가지다. 하나는 경고에 압도돼 도구를 꺼버리는 것, 다른 하나는 모든 경고를 무리하게 없애려다 정작 기능 개발이 마비되는 것이다.
현실적인 전략은 기준선(baseline) 을 잡는 것이다. 기존 경고는 일단 기준선으로 동결해 두고, 새로 추가되는 코드가 새 경고를 만들지 않도록 CI에서 차단한다. 이렇게 하면 부채는 더 늘지 않고, 기존 경고는 해당 파일을 만질 때 점진적으로 갚아 나갈 수 있다. 앞선 글에서 다룬 레거시 현대화의 “새 경고 금지” 원칙과 정확히 같은 접근이다.
// 검증 후 의도적으로 무시하는 경고는 근거와 함께 억제한다
@SuppressWarnings("PMD.AvoidCatchingGenericException")
void handle() {
try {
risky();
} catch (Exception e) { // 최상위 경계에서 의도적으로 모두 포착
log.error("작업 실패", e);
}
}
억제할 때는 반드시 왜 안전한지 검토한 뒤 좁은 범위로 억제하고, 근거를 남긴다. 무분별한 억제는 도구를 무력화한다.
SonarQube로 추세를 관리한다
개별 검사를 넘어 품질을 지표로 관리 하고 싶다면 SonarQube 같은 플랫폼을 CI에 붙인다. 커밋마다 버그·취약점·중복률·커버리지·복잡도를 측정해 시간에 따른 추세를 보여주고, “새 코드의 커버리지 80% 미만이면 실패” 같은 품질 게이트(quality gate) 로 기준 미달 변경을 자동 차단한다. 품질이 개인의 성실성이 아니라 팀의 합의된 임계값으로 관리되는 것이다.
정리
- 정적 분석은 코드를 실행하지 않고 소스·바이트코드를 분석해 버그·냄새·보안 결함을 찾는다. 테스트가 닿지 못한 경로까지 훑는 것이 강점이다.
- 도구는 역할이 나뉜다. SpotBugs·Error Prone(버그), PMD·SonarQube(냄새·복잡도), FindSecBugs·Semgrep(보안), Checkstyle(스타일).
- 핵심 가치는 shift-left — 검출을 앞당겨 늦게 잡을수록 커지는 수정 비용을 회피한다.
- 오탐과는 기준선 + 새 경고 금지 로 공존하고, 억제는 근거와 함께 좁게 한다. 품질 게이트 로 추세를 팀 차원에서 관리한다.
언어의 기초 문법에서 시작해 JVM 내부, 동시성, 성능 튜닝, 그리고 코드 품질과 현대화까지 — 자바를 깊이 다루는 개발자에게 필요한 지도를 함께 그렸다. 각 주제의 세부는 언제든 개별 글에서 다시 펼쳐볼 수 있다. 긴 여정을 함께해 주셔서 감사합니다.
지난 글: 자바 코딩 컨벤션
읽어주셔서 감사합니다. 😊