본문 바로가기
빌드노트 검색

빌드노트

Maven -pl 빌드가 형제 모듈을 못 찾을 때 -am을 붙이는 이유

전체 package는 되는데 -pl app만 실패한다면 reactor의 빌드 대상을 확인한다. 빈 전용 저장소에서 -am의 효과를 비교하고, target의 JAR과 로컬 저장소에 설치된 아티팩트를 구별한다.

Maven 멀티 모듈 프로젝트에서 전체 package는 성공하는데 -pl app package만 형제 라이브러리를 찾지 못한다면, 이번 빌드에서 그 라이브러리 모듈을 제외했는지 먼저 확인한다. -pl app은 빌드 대상을 app으로 좁힌다. 같은 소스 트리의 lib까지 함께 빌드하려면, 두 모듈을 모으는 루트 POM에서 -pl app -am package를 실행한다.

Maven이 여러 모듈을 모아 순서를 정하고 빌드하는 장치를 reactor라고 한다. -am은 선택한 프로젝트가 필요로 하는 reactor 안의 프로젝트도 포함한다. 디렉터리가 옆에 있다는 이유만으로 lib가 자동 빌드되지는 않는다. Maven 3 멀티 모듈 안내 (새 탭에서 열림), Maven 3.9.16 CLI 옵션 (새 탭에서 열림)

빈 저장소에서는 app만 선택한 빌드가 실패했다

이를 확인하려고 실제 서비스와 별개인 parent·lib·app 세 POM을 만들었다. 루트 parent는 pom 패키징이고 app과 lib를 모듈로 등록한다. 두 자식은 parent를 상속하며, app은 lab.reactor:lib:1.0-SNAPSHOT을 일반 compile 의존성으로 선언했다. lib에는 문자열을 반환하는 클래스 하나, app에는 그 메서드를 호출하는 클래스 하나만 두었다.

환경은 Windows 11, Maven 3.9.16, JDK 17.0.2였다. 실험용 settings와 로컬 저장소를 따로 사용했고, 이 합성 lib는 원격 저장소에 배포하지 않았다. 처음에는 로컬 저장소도 비어 있었다.

-pl app package의 종료 코드는 1이었고, 오류의 핵심은 다음과 같았다. 로그에서 관련 세 줄만 발췌했다.

[WARNING] The POM for lab.reactor:lib:jar:1.0-SNAPSHOT is missing, no dependency information available
[ERROR] dependency: lab.reactor:lib:jar:1.0-SNAPSHOT (compile)
[ERROR] 	Could not find artifact lab.reactor:lib:jar:1.0-SNAPSHOT

다른 빈 전용 저장소에서 -pl app -am package를 실행하자 종료 코드 0으로 성공했다. 이번 로그에는 다음 빌드 순서가 나타났다.

[INFO] Reactor Build Order:
[INFO]
[INFO] parent                                                             [pom]
[INFO] lib                                                                [jar]
[INFO] app                                                                [jar]

루트의 모듈 목록에는 일부러 app을 lib보다 먼저 적었다. 그래도 lib가 먼저 빌드됐다. app이 lib에 의존하므로 reactor가 필요한 순서를 정한 것이다. 공식 안내도 모듈 간 실제 의존 관계를 정렬 기준으로 설명한다. 목록 순서를 바꾸기 전에 lib가 이번 빌드 대상에 들어왔는지 봐야 한다. reactor 정렬 규칙 (새 탭에서 열림)

target의 JAR과 설치된 JAR은 다르다

같은 실험에서 전체 package도 성공했다. 하지만 그 직후 -pl app package를 다시 실행하면 여전히 lib를 찾지 못했다. lib/target/lib-1.0-SNAPSHOT.jar는 존재했지만, 전용 로컬 저장소에는 lib의 POM과 JAR이 없었다.

package는 JAR을 만드는 단계이고, install은 산출물을 로컬 저장소에 넣는 단계다. 같은 reactor 실행에 포함된 lib의 결과를 사용하는 것과, 다음 실행에서 로컬 저장소의 lib를 해결하는 것은 구별해야 한다. Maven 빌드 생명주기 (새 탭에서 열림)

이어서 같은 전용 저장소에 -pl app -am install을 실행했다. parent POM과 lib·app 산출물이 설치됐고, 이후에는 -pl app package도 성공했다. 이때 빌드 로그에는 app만 나타났다. 단독 빌드의 성공이 lib 소스까지 이번에 빌드했다는 증거는 아니었다. 설치된 아티팩트가 의존성을 충족한 것이다.

따라서 내 PC에서만 단독 빌드가 된다면 이전 install이나 원격 저장소에서 받은 아티팩트가 있는지 확인한다. 현재 체크아웃의 lib 변경까지 함께 반영하려는 목적이라면, 과거 설치 상태에 기대기보다 -am으로 필요한 모듈을 포함하는 편이 맞다.

자기 프로젝트에서는 빌드 목록과 실패 좌표를 함께 읽는다

다음은 JDK와 Maven을 사용할 수 있는 Windows PowerShell용 확인 명령이다. app과 lib를 모두 등록하는 루트 pom.xml이 있는 폴더에서 실행한다. app은 자기 소비 모듈의 루트 기준 상대 경로로 바꾼다. 이 예제에서는 실제 디렉터리 이름이 app이다. 원래 빌드에 필요한 profile·settings 인자는 두 실행에 동일하게 유지한다.

평소 저장소의 설치 상태를 배제하려고 새 이름의 전용 로컬 저장소를 지정한다. 플러그인과 외부 의존성을 새로 받을 수 있어야 한다. 사내 저장소가 필요한 프로젝트라면 그 접속 설정도 필요하다. 기본 로컬 저장소 위치는 Maven settings 문서에서 확인할 수 있다. 로컬 저장소 설정 (새 탭에서 열림)

$repo = Join-Path $PWD ('.reactor-check-' + [guid]::NewGuid().ToString('N'))
mvn.cmd -B -ntp "-Dmaven.repo.local=$repo" -pl app package

합성 예제에서는 위 명령이 앞서 본 lib 누락 오류로 실패했다. 첫 실행에서 그 오류가 확인됐다면 같은 PowerShell 세션에서 같은 저장소를 유지하고 다음을 실행한다.

mvn.cmd -B -ntp "-Dmaven.repo.local=$repo" -pl app -am package

예제에서는 parent → lib → app 순서와 BUILD SUCCESS를 확인했다. 자기 프로젝트에서는 실패하던 아티팩트의 모듈이 빌드 목록에 포함됐는지 먼저 본다. 결과가 다르면 다음처럼 구별한다.

  • 첫 명령도 성공한다면: 원격 저장소가 해당 형제 아티팩트를 공급했을 수 있다. 성공만으로 현재 형제 소스를 빌드했다고 판단하지 말고 실제 빌드 목록을 확인한다.
  • -am을 붙여도 lib가 없다면: 올바른 집계 POM에서 시작했는지, 활성 모듈 목록에 lib가 있는지, app이 요구하는 groupId·artifactId·version이 lib와 같은지 확인한다. -am은 reactor 밖의 임의 디렉터리를 찾아 추가하지 않는다.
  • lib가 포함됐는데 다른 오류가 나면: 컴파일 오류나 외부 의존성·플러그인 다운로드 오류를 읽는다. 이 실험의 형제 모듈 제외 문제와 같은 원인으로 묶지 않는다.

이번 확인은 Maven 3.9.16에서 일반 JAR의 compile 의존성을 package·install까지 실행한 결과다. 별도 classifier나 test-JAR, 사용자 정의 빌드 확장, Maven 4의 동작은 검증하지 않았다. 공식 멀티 모듈 안내도 Maven 4용 문서를 따로 연결한다.

← 전체 글 보기