Java/Maven

[폐쇄망 CI/CD] Maven 의존성을 Nexus에 자동 업로드하기

범데이 2026. 6. 3. 10:18

외부 인터넷이 차단된 폐쇄망 환경에서 Maven 프로젝트의 CI/CD를 구축하면서 겪은 의존성 관리 문제와 해결 과정을 정리한다.

 

1. 배경

회사 보안 규정상 외부 인터넷이 막힌 폐쇄망 서버에 Spring Boot 기반 사내 시스템을 배포해야 했다. TeamCity로 CI/CD 파이프라인을 구성했는데, 빌드 서버가 Maven Central에 접근할 수 없으니 의존성을 어디선가 가져와야 했다.

해결 방향은 세 단계로 정리됐다.

  1. 내부망에 Nexus Repository Manager 구축
  2. 개발자 PC의 로컬 .m2 캐시에서 필요한 의존성만 추려서 Nexus에 업로드
  3. 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만 믿으면 반드시 빌드가 깨진다.

 

728x90
반응형