스타터와 의존성 관리
스타터가 전이 의존성으로 제공하는 기능과 Boot 의존성 관리의 호환 버전 집합을 읽고, 직접 버전 재정의·중복 로깅·서버 교체·미사용 의존성·잠금 파일 차이를 빌드와 런타임 증거로 검증합니다.
스타터는 소스 코드를 대신 생성하지 않고 특정 기능을 시작하는 의존성 묶음을 제공합니다.
의존성 관리는 그 그래프의 버전을 검증된 조합으로 맞춥니다.
애플리케이션 build.gradle에서 줄 수가 줄어드는 것보다, 팀이 어떤 기능을 선택했고 누가 버전 호환성을 책임지는지가 중요합니다.
스타터 의존성 그래프
Web MVC, Spring Data JPA, Actuator 스타터는 각각 MVC, JPA 영속성, 운영 엔드포인트 기능을 묶습니다. 하나를 추가하면 로깅, 검증, JSON처럼 이미 다른 스타터가 가져온 의존성이 중복 경로로 나타날 수 있지만 Gradle의 의존성 해결 과정은 하나의 버전을 선택합니다.
package board.deps;
import java.util.Set;
public record Capability(String name, Set<String> requiredModules) {
public Capability {
if (name.isBlank() || requiredModules.isEmpty()) {
throw new IllegalArgumentException("capability needs modules");
}
requiredModules = Set.copyOf(requiredModules);
}
public static void main(String[] args) {
var web = new Capability("webmvc", Set.of("mvc", "json", "server"));
System.out.printf("capability=%s modules=%d%n",
web.name(), web.requiredModules().size());
}
}스타터를 넣었는데 사용하지 않는 모듈은 컴파일 클래스 경로와 공격 표면을 늘립니다. 의존성 분석과 런타임 자동 구성 보고서를 함께 보고 제거합니다.
Boot BOM 버전 관리
플러그인 또는 플랫폼을 사용하면 Spring 프레임워크, Jackson, Tomcat, Hibernate 같은 연관 라이브러리 버전이 정렬됩니다. 한 라이브러리만 무리하게 올리면 바이너리 메서드 시그니처가 달라져 시작에 연결 오류가 날 수 있습니다.
관리 버전을 재정의할 때는 공식 지원 범위와 이유, 만료 날짜, 의존성 분석 결과, 통합 테스트를 기록합니다. 보안 패치가 필요하면 가능한 Boot 패치 릴리스로 전체 버전 집합을 올리는 방법을 먼저 검토합니다.
직접 의존성 버전이 없다는 것은 “최신 아무거나”가 아니라 가져온 플랫폼이 결정한다는 뜻입니다. 해결된 의존성 그래프를 릴리스 산출물과 함께 보존합니다.
서버·로깅 구현 교체
Tomcat 대신 다른 내장 서버를 쓰려면 새 스타터만 추가하지 않고 기존 기본값 서버 의존성을 제외합니다. 로깅 백엔드도 브리지 순환과 두 구현을 피합니다. 컴파일 성공 뒤 실제 서버 팩토리와 로거 바인딩이 하나인지 확인합니다.
package board.deps;
public final class RuntimeCapabilityProbe {
static boolean present(String className) {
try {
Class.forName(className, false,
RuntimeCapabilityProbe.class.getClassLoader());
return true;
} catch (ClassNotFoundException missing) {
return false;
}
}
public static void main(String[] args) {
boolean tomcat = present("org.apache.catalina.startup.Tomcat");
boolean springMvc = present("org.springframework.web.servlet.DispatcherServlet");
System.out.printf("mvc=%s tomcat=%s%n", springMvc, tomcat);
}
}mvc=true tomcat=true
serverFactories=1
loggingProviders=1
linkageErrors=0클래스 존재만으로 빈 활성화를 증명하지 않습니다. 조건 보고서와 웹 서버 시작을 추가로 확인합니다.
Spring Framework resolved version = 7.0.x
manually overridden integration version = incompatible line
compile = success
context refresh = failure
root cause = NoSuchMethodError
result = MANAGED_VERSION_SET_BROKEN이 실패는 누락 의존성과 다릅니다.
클래스는 존재하지만 컴파일 때 기대한 메서드와 런타임 바이트코드가 어긋난 것입니다.
dependencyInsight로 누가 버전을 선택했는지 확인하고 임의 제외를 더하기 전에 Boot 플랫폼으로 되돌립니다.
개발·운영 의존성
테스트 컨테이너, 개발용 재시작 도구, 애노테이션 처리기가 런타임 이미지에 들어가지 않게 구성 스코프를 확인합니다.
데이터베이스 드라이버는 실제 런타임에서 필요하지만 컴파일 API를 직접 쓰지 않으면 runtimeOnly가 적합할 수 있습니다.
선택적 기능을 스타터 하나에 모두 넣으면 사용자가 사용하지 않는 SDK를 받습니다. 자체 스타터는 핵심 API, 자동 구성, 선택적 통합을 모듈로 나누고 조건부 클래스 경로를 사용합니다.
라이선스와 플랫폼 아키텍처, 네이티브 라이브러리 여부도 그래프 검토 대상입니다. “전이 의존성이라 몰랐다”는 운영 환경 책임을 없애지 않습니다.
공급망 잠금·검증
버전 잠금은 동적 해결 차이를 막고 체크섬 검증은 산출물 바뀜을 감지합니다. 잠금 파일 갱신 PR은 이전·신규 그래프, CVE, 크기, 기본 구동 검증 결과를 함께 봅니다.
리포지토리 미러와 인증 정보를 빌드 스크립트에 하드 코딩하지 않습니다. 의존성 혼동 공격을 막도록 승인된 리포지토리와 그룹 정책을 사용합니다.
업그레이드 검증 계층
Java 컴파일, 컨텍스트 로딩, 실제 HTTP·JPA 동작, 실행 가능한 JAR 파일의 구동 검증을 단계별로 실행합니다. 사용 중단 예정 API 제거와 속성 이름 변경은 마이그레이션 안내서를 따라 수정하고 이전 호환 계층의 제거 시점을 정합니다.
의존성 변경 PR에는 해결된 의존성 그래프의 이전·이후, 추가·제거 JAR 크기, 자동 구성 긍정 일치 차이, 시작과 첫 요청 지연 시간을 첨부합니다. 버전 숫자만 최신이라는 사실은 호환성이나 효율을 증명하지 않습니다. 긴급 재정의에는 담당자와 만료 날짜를 지정하고 다음 Boot 패치 릴리스에서 제거되는지 확인합니다.
연습 문제
Web MVC와 Actuator만 필요한 회원 게시판 서비스의 의존성 그래프를 설계하세요. 서버 교체, H2 테스트 스코프, 운영 환경 드라이버, 애노테이션 처리기, 미사용 JPA 제거, 잠금 파일 갱신 근거를 표로 작성하세요.
해설 보기
기능별 담당 영역을 정하면 스타터를 추가한 이유와 각 의존성의 쓰임이 명확해집니다.
package board.deps;
public record DependencyDecision(
String coordinate,
String capability,
Scope scope,
String owner
) {
public enum Scope { COMPILE, RUNTIME, TEST, PROCESSOR }
public DependencyDecision {
if (coordinate.isBlank() || capability.isBlank() || owner.isBlank()) {
throw new IllegalArgumentException("dependency decision incomplete");
}
}
}정기 감사에서는 소스 코드에서 사용되지 않는 직접 의존성과 런타임 클래스 경로에만 남은 구식 모듈을 제거 후보로 제시합니다.