외부 인터넷이 차단된 폐쇄망 환경에서 Maven 프로젝트의 CI/CD를 구축하면서 겪은 의존성 관리 문제와 해결 과정을 정리한다.
1. 배경
회사 보안 규정상 외부 인터넷이 막힌 폐쇄망 서버에 Spring Boot 기반 사내 시스템을 배포해야 했다. TeamCity로 CI/CD 파이프라인을 구성했는데, 빌드 서버가 Maven Central에 접근할 수 없으니 의존성을 어디선가 가져와야 했다.
해결 방향은 세 단계로 정리됐다.
- 내부망에 Nexus Repository Manager 구축
- 개발자 PC의 로컬 .m2 캐시에서 필요한 의존성만 추려서 Nexus에 업로드
- CI/CD 빌드 서버는 Nexus만 바라보도록 settings.xml 설정
2. 환경 정보
| 항목 | 값 |
| Nexus URL | 내부망 Nexus 서버 주소 |
| 업로드 레포 | hosted 타입 레포 (프로젝트 전용) |
| 그룹 레포 | maven-public (업로드 레포 포함) |
| 의존성 수집 경로 | 로컬 임시 수집 디렉터리 |
| 총 의존성 | 172개 JAR + 55개 parent POM |
CI/CD 서버의 settings.xml은 그룹 레포만 바라보면 된다. 개별 레포를 일일이 추가할 필요 없이 그룹 하나로 통합 관리한다.
3. 스크립트 구성
작업을 두 단계 PowerShell 스크립트로 분리했다.
cicd/nexus/
├── 01_collect-mes-deps.ps1 # 의존성 수집 (인터넷 연결된 PC에서 실행)
├── 02_upload-to-nexus.ps1 # Nexus 업로드
└── README.md
4. Step 1 — 의존성 수집
mvn dependency:list로 프로젝트 의존성 목록을 추출한 뒤, 각 항목을 로컬 .m2에서 찾아 임시 수집 경로로 복사하는 방식이다.
# mvn으로 의존성 목록 추출
mvn dependency:list "-DoutputFile=C:\temp\my-project-deps.txt" -DincludeScope=test -q
# .m2에서 수집 경로로 복사
foreach ($line in $depLines) {
$parts = $line.Trim() -split ":"
$groupId = $parts[0]
$artifactId = $parts[1]
$version = $parts[-2]
$srcPath = Find-M2Path -base "$M2_DIR\$groupPath\$artifactId" -version $version
if ($srcPath) {
Copy-Item -Path "$srcPath\*" -Destination $dstPath -Recurse -Force
Write-Host "COPY ${groupId}:${artifactId}:${version}"
} else {
$missed += ... # 나중에 처리
}
}
수집 후에는 .lastUpdated, _remote.repositories 같은 불필요한 메타파일을 제거했다. 이런 파일들이 Nexus에 올라가면 이후 빌드에서 문제가 생길 수 있다.
mvn dependency:list는 JAR 아티팩트만 출력하고 parent POM은 누락한다. 이 때문에 빌드가 실패했고 별도 로직으로 해결했다. (다음 포스팅에서 자세히 다룬다.)
4-1. system scope 의존성 처리
pom.xml에 <scope>system</scope>으로 선언된 로컬 JAR는 .m2에 없으므로 systemPath에서 직접 복사했다.
# pom.xml의 system scope 의존성을 systemPath에서 복사
foreach ($dep in $pom.project.dependencies.dependency) {
if ($dep.scope -eq "system") {
$path = $dep.systemPath -replace '\$\{project\.basedir\}', $PROJECT_ROOT
Copy-Item -Path $path -Destination $jarDst -Force
}
}
5. Step 2 — Nexus 업로드
수집 경로의 파일들을 Nexus REST API(PUT)로 업로드했다.
$url = "$NEXUS_URL/repository/maven-my-project-deps/$relativePath"
$result = Invoke-WebRequest -Uri $url -Method PUT -InFile $file.FullName `
-Headers @{ Authorization = "Basic $cred" } -UseBasicParsing
if ($result.StatusCode -eq 201) {
Add-Content $DONE_LOG $relativePath # 완료 기록
Write-Host "OK $relativePath" -ForegroundColor Green
}
핵심은 재시작 내성 기능이다. 완료된 파일 경로를 upload-done.log에 기록해두고, 재실행 시 이미 올라간 파일은 SKIP한다. 네트워크가 불안정해 중간에 끊겨도 이어서 실행할 수 있다.
6. CI/CD 빌드 서버 설정
TeamCity 빌드 에이전트의 Maven settings.xml에 Nexus 미러 설정만 추가하면 된다.
<settings>
<mirrors>
<mirror>
<id>nexus</id>
<mirrorOf>*</mirrorOf>
<url>http://nexus.internal:8081/repository/maven-public/</url>
</mirror>
</mirrors>
<servers>
<server>
<id>nexus</id>
<username>배포용_계정</username>
<password>패스워드</password>
</server>
</servers>
</settings>
7. 결과
172개 JAR 의존성과 55개 parent POM 수집을 완료했고, 전체 Nexus 업로드에 성공했다. 최종적으로 폐쇄망 TeamCity 빌드까지 통과했다.
이후 parent POM 누락 문제와 로그 충돌 문제 두 가지 추가 트러블슈팅이 있었다. 각각 별도 포스팅으로 정리했다.
(parent POM 누락문제 포스팅: https://bumday.tistory.com/294)
(로그 충돌 문제 포스팅: https://bumday.tistory.com/295)
폐쇄망 Maven 빌드에서 의존성 문제의 핵심은 JAR 외에 parent POM까지 챙기는 것이다. mvn dependency:list만 믿으면 반드시 빌드가 깨진다.
'Java > Maven' 카테고리의 다른 글
| [Troubleshooting] Maven "was cached in the local repository" — 폐쇄망 빌드 서버에서 캐시된 실패 처리 (0) | 2026.06.03 |
|---|---|
| [Troubleshooting] Maven parent POM이 Nexus에 없어서 빌드 실패 — mvn dependency:list의 함정 (0) | 2026.06.03 |
| [Troubleshooting] Spring Boot 2.3 + JEUS 로그 라이브러리 충돌 해결 (0) | 2026.05.05 |
| [Maven] 외부망에서 .m2 복사했는데 내부망 빌드 실패할 때 (0) | 2026.05.05 |
| [Maven] clean / package / --offline / -U 실무 정리 (0) | 2026.05.05 |