빌드노트
BOM을 import했는데 클래스가 컴파일되지 않는 이유
BOM에 관리된 버전이 보여도 라이브러리가 컴파일 의존성에 포함된 것은 아니다. effective POM과 dependency tree를 대조하고, 버전 없는 실제 의존성 선언으로 컴파일 실패를 해결하는 과정을 확인한다.
Maven의 pom.xml에 BOM을 import했는데도 Java 컴파일에서 package ... does not exist가 날 수 있다. BOM의 dependencyManagement는 의존성이 사용될 때 적용할 버전 등을 관리한다. 그 목록에 있다는 이유만으로 라이브러리 JAR이 컴파일 클래스패스에 들어오지는 않는다. 실제 의존성 경로가 없다면 소비 모듈의 일반 dependencies에 필요한 라이브러리를 선언한다.
Maven에서 BOM import는 dependencyManagement 안에 type=pom, scope=import로 선언한다. 가져오는 것은 그 POM의 관리 항목이다. 라이브러리의 실제 사용 선언과 구분해야 한다. Maven 공식 가이드의 의존성 관리와 BOM 설명 (새 탭에서 열림)
관리 버전과 실제 의존성을 따로 확인한다
먼저 오류가 나는 소비 모듈의 pom.xml이 있는 폴더로 이동한다. JDK와 Maven을 사용할 수 있어야 하며, 평소 컴파일할 때 쓰는 프로필·설정·속성이 있다면 조회에도 똑같이 적용한다. 다음은 PowerShell 7용이다. 플러그인이 준비되지 않았다면 Maven 저장소에 접근할 수 있어야 한다.
첫 명령은 최종 POM을 파일에 저장하고, 둘째 명령은 실제 의존성 관계를 출력한다. 각 명령의 성공을 확인한 뒤 결과를 읽는다.
mvn.cmd -B -ntp org.apache.maven.plugins:maven-help-plugin:3.5.2:effective-pom "-Doutput=effective-pom.xml"
mvn.cmd -B -ntp org.apache.maven.plugins:maven-dependency-plugin:3.8.1:tree
effective-pom에는 부모에게 상속받은 설정과 활성 프로필 등이 반영된다. 생성된 effective-pom.xml에서 라이브러리 이름을 찾되, 어느 XML 경로에 있는지까지 읽는다. project/dependencyManagement/dependencies의 항목은 관리 정보다. project/dependencies는 실제 프로젝트 의존성 선언이다. build/plugins 내부의 의존성과도 혼동하지 않는다. Help Plugin의 effective-pom 설명 (새 탭에서 열림)
직접 만든 예제의 BOM은 lab.bom:marker-lib 버전 1.2.3을 관리했다. BOM만 import한 소비 모듈의 effective POM에도 다음 관리 항목은 정상적으로 나타났다. 자기 출력에서도 해당 라이브러리의 좌표와 버전이 이 위치에 있는지 확인한다.
<dependencyManagement>
<dependencies>
<dependency>
<groupId>lab.bom</groupId>
<artifactId>marker-lib</artifactId>
<version>1.2.3</version>
</dependency>
</dependencies>
</dependencyManagement>
하지만 그 소비 모듈에는 project/dependencies 항목이 없었고, dependency:tree에는 소비 모듈 자신인 lab.bom:consumer:jar:1.0 한 줄만 나왔다. 관리된 버전은 있지만 실제 의존성 경로는 없는 상태였다. 트리는 관리 목록을 나열하는 명령이 아니다. Dependency Plugin 3.8.1의 tree 설명 (새 탭에서 열림)
조회가 다운로드·인증·BOM 해석 오류로 실패했다면 이 상태를 확인한 것이 아니다. 그 오류부터 해결한다. 반대로 관리 항목조차 없다면 BOM 좌표, import 위치, 활성 프로필과 조회한 모듈이 맞는지 먼저 확인한다.
필요한 라이브러리를 일반 dependencies에 추가한다
관리 항목에는 있고 실제 트리에는 없는 라이브러리를 소스에서 직접 사용한다면, 소비 모듈 POM의 project 바로 아래에 다음 선언을 추가한다. 이미 일반 dependencies가 있으면 그 안에 dependency만 합친다. dependencyManagement 안의 목록에 다시 넣는 수정으로는 해결되지 않는다.
<dependencies>
<dependency>
<groupId>lab.bom</groupId>
<artifactId>marker-lib</artifactId>
</dependency>
</dependencies>
위 좌표는 실험용이다. 자기 프로젝트에서는 필요한 클래스가 들어 있는 실제 라이브러리의 groupId와 artifactId를 쓴다. BOM import는 그대로 유지한다. version을 생략한 이유는 앞서 확인한 관리 항목에서 1.2.3을 가져오기 때문이다. 이 예제는 classifier가 없는 일반 JAR이며, BOM에도 별도 scope가 없어 기본 compile 범위를 사용한다.
수정 후 같은 트리 명령을 다시 실행한다. 실험에서는 출력이 다음처럼 바뀌었다. 아래는 로그 안내를 제외한 의존성 트리 원문이다.
lab.bom:consumer:jar:1.0
\- lab.bom:marker-lib:jar:1.2.3:compile
일반 의존성 선언에는 버전을 쓰지 않았지만, 실제 트리에는 관리된 버전 1.2.3이 들어왔다. 이어 같은 모듈에서 컴파일을 확인한다.
mvn.cmd -B -ntp compile
버전 누락 오류가 난다면 실제 선언과 관리 항목이 매칭되지 않은 것이다. Maven의 매칭에는 groupId, artifactId뿐 아니라 type과 classifier도 관여한다. 일반 JAR과 다른 타입·classifier를 쓰는 프로젝트라면 그 차이까지 확인한다. 의존성 관리 항목의 매칭 기준 (새 탭에서 열림)
JAR이 저장소에 있어도 import만으로는 컴파일되지 않았다
차이를 확인하려고 새 라이브러리, BOM, 소비 모듈을 만들었다. 라이브러리의 lab.marker.Marker.value()는 marker=1.2.3을 반환한다. 소비 모듈은 이 클래스를 import하고 해당 메서드를 호출한다. 같은 Java 소스를 두 폴더에 두고, 소비 POM은 앞의 일반 의존성 선언 유무만 달리했다.
환경은 Windows 11, Maven 3.9.16, OpenJDK 17.0.2였다. Compiler Plugin은 3.14.0, Help Plugin은 3.5.2, Dependency Plugin은 3.8.1로 고정했다. 별도 프로젝트에서 플러그인을 먼저 준비한 뒤, 라이브러리 JAR과 BOM POM을 전용 로컬 저장소에 설치했다. 이후 두 소비 모듈의 조회와 컴파일은 모두 오프라인으로 실행했다. 사용자 기본 저장소는 사용하지 않았고 원격 배포도 하지 않았다.
BOM만 import한 조건에서는 effective POM과 트리 조회가 모두 종료 코드 0으로 끝났다. 그런데 compile은 종료 코드 1이었고, 컴파일러는 package lab.marker does not exist와 cannot find symbol을 출력했다. JAR이 전용 저장소에 이미 있어도 소비 모듈의 의존성 경로에 없으면 소스에서 사용할 수 없다는 관찰이다. BOM 다운로드나 플러그인 준비 실패를 클래스 누락으로 해석한 결과가 아니다.
버전 없는 실제 의존성 선언을 추가한 조건에서는 트리에 marker-lib:jar:1.2.3:compile이 나타났고 컴파일도 종료 코드 0으로 끝났다. Maven이 계산한 의존성 클래스패스와 컴파일 결과를 함께 사용해 실행하자 marker=1.2.3이 출력됐다. 두 조건의 관리 항목은 같았고, 바뀐 것은 실제 의존성 선언이었다.
자기 트리에 라이브러리가 이미 있다면 무조건 같은 선언을 더하지 않는다. 전이 경로로 들어올 수도 있다. 선택된 버전의 JAR에 필요한 클래스가 있는지, scope가 메인 소스 컴파일에 맞는지, 오류가 나는 모듈을 조회했는지 확인한다. 소스가 직접 쓰는 의존성을 명시하는 것은 Maven 공식 가이드의 권고 (새 탭에서 열림)이기도 하다.
이번 비교가 확인한 것은 일반 JAR 하나의 BOM 관리 버전 적용, 실제 의존성 포함, 컴파일과 작은 메서드 호출이다. 여러 BOM의 우선순위, 실제 외부 라이브러리 사이의 API 호환성, IDE의 별도 클래스패스, Maven 4와 실행 JAR 재패키징은 검증하지 않았다. 관리 목록에 이름이 보인다는 사실과 컴파일에 필요한 의존성이 들어왔다는 사실을 나눠 확인하면, 버전을 바꿔 보며 반복 빌드하기 전에 빠진 선언부터 찾을 수 있다.