본문 바로가기

IT일반

Maven과 Nexus 완벽 가이드: 아키텍처부터 실전 사용법까지

개요

Java 프로젝트를 개발하다 보면 수십, 수백 개의 라이브러리를 관리해야 하는 상황을 마주하게 됩니다. Apache Maven은 이러한 의존성 관리와 빌드 자동화를 해결하는 대표적인 빌드 도구이며, Sonatype Nexus는 이 아티팩트들을 조직 내에서 안전하고 효율적으로 관리할 수 있게 해주는 리포지토리 매니저입니다. 이 글에서는 Maven의 핵심 아키텍처부터 Nexus의 역할, 그리고 둘을 연동하는 실전 사용법까지 깊이 있게 살펴보겠습니다.

 

Maven이란 무엇인가

Apache Maven은 아파치 소프트웨어 재단에서 개발한 Java 기반 프로젝트의 빌드, 의존성 관리, 프로젝트 라이프사이클 관리를 위한 도구입니다. Maven이라는 단어는 "지식의 축적자"라는 의미를 가지고 있으며, 그 이름처럼 프로젝트 구성에 필요한 모든 정보를 체계적으로 관리합니다.

Convention Over Configuration

Maven의 가장 핵심적인 철학은 CoC(Convention Over Configuration), 즉 "설정보다 관례"입니다. 개발자가 일일이 디렉토리 구조나 빌드 설정을 지정하지 않아도, Maven이 정한 관례를 따르면 최소한의 설정만으로 프로젝트를 구성할 수 있습니다.

Maven의 표준 디렉토리 구조는 다음과 같습니다:

my-project/
├── pom.xml
├── src/
│   ├── main/
│   │   ├── java/          # 소스 코드
│   │   └── resources/     # 리소스 파일
│   └── test/
│       ├── java/          # 테스트 코드
│       └── resources/     # 테스트 리소스
└── target/                # 빌드 산출물

이 구조를 따르기만 하면 별도의 빌드 설정 없이도 Maven이 소스 코드를 찾아 컴파일하고, 테스트를 실행하며, 패키징까지 자동으로 수행합니다.


POM: Maven의 심장

POM이란

POM(Project Object Model)은 Maven 프로젝트의 핵심 설정 파일로, pom.xml이라는 이름으로 프로젝트 루트에 위치합니다. 프로젝트의 모든 정보 - 의존성, 빌드 설정, 플러그인, 배포 정보 등 - 가 이 파일 하나에 정의됩니다.

기본 POM 구조

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
         http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <!-- 프로젝트 좌표 (GAV) -->
    <groupId>com.example</groupId>
    <artifactId>my-application</artifactId>
    <version>1.0.0</version>
    <packaging>jar</packaging>

    <!-- 프로젝트 속성 -->
    <properties>
        <java.version>17</java.version>
        <spring.version>6.1.0</spring.version>
    </properties>

    <!-- 의존성 정의 -->
    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>${spring.version}</version>
        </dependency>
    </dependencies>
</project>

GAV 좌표

모든 Maven 아티팩트는 GAV 좌표(GroupId, ArtifactId, Version)로 고유하게 식별됩니다.

  • groupId: 프로젝트 그룹을 식별하는 ID (보통 도메인 역순, 예: com.example)
  • artifactId: 프로젝트의 고유 이름 (예: my-application)
  • version: 프로젝트 버전 (예: 1.0.0, 2.0-SNAPSHOT)

이 세 가지 좌표의 조합은 전 세계 모든 Maven 리포지토리에서 해당 아티팩트를 유일하게 식별하는 역할을 합니다.


Maven 빌드 라이프사이클

Maven의 빌드 프로세스는 잘 정의된 라이프사이클(Lifecycle)을 따릅니다. 라이프사이클은 여러 개의 페이즈(Phase)로 구성되며, 각 페이즈는 순차적으로 실행됩니다.

3가지 기본 라이프사이클

1. default (빌드) 라이프사이클

실제 프로젝트 빌드와 배포를 담당하는 핵심 라이프사이클입니다. 주요 페이즈는 다음과 같습니다:

  • validate: 프로젝트 구조가 올바른지 검증
  • compile: 소스 코드를 바이트코드로 컴파일
  • test: 단위 테스트 실행
  • package: 컴파일된 코드를 JAR, WAR 등으로 패키징
  • verify: 통합 테스트 결과 검증
  • install: 로컬 리포지토리에 아티팩트 설치
  • deploy: 원격 리포지토리에 아티팩트 배포

2. clean 라이프사이클

이전 빌드에서 생성된 파일을 정리합니다. mvn clean 명령으로 target/ 디렉토리를 삭제합니다.

 

3. site 라이프사이클

프로젝트 문서 사이트를 생성합니다. JavaDoc, 테스트 리포트 등을 포함하는 HTML 사이트를 만들어 줍니다.

 

페이즈의 순차 실행

중요한 점은 특정 페이즈를 실행하면 그 이전의 모든 페이즈가 자동으로 실행된다는 것입니다:

# package 페이즈를 실행하면
# validate → compile → test → package 순서로 모두 실행됨
mvn package

# 일반적으로 가장 많이 사용하는 명령
mvn clean install

 


의존성 관리

의존성 선언과 Scope

Maven의 가장 강력한 기능 중 하나는 자동 의존성 관리입니다. pom.xml에 필요한 라이브러리를 선언하면, Maven이 자동으로 다운로드하고 클래스패스에 추가합니다.

<dependencies>
    <!-- 컴파일 시점에 필요 (기본값) -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>6.1.0</version>
        <scope>compile</scope>
    </dependency>

    <!-- 테스트 시에만 필요 -->
    <dependency>
        <groupId>org.junit.jupiter</groupId>
        <artifactId>junit-jupiter</artifactId>
        <version>5.10.0</version>
        <scope>test</scope>
    </dependency>

    <!-- 런타임에만 필요 -->
    <dependency>
        <groupId>com.mysql</groupId>
        <artifactId>mysql-connector-j</artifactId>
        <version>8.2.0</version>
        <scope>runtime</scope>
    </dependency>

    <!-- 컨테이너가 제공 (서블릿 API 등) -->
    <dependency>
        <groupId>jakarta.servlet</groupId>
        <artifactId>jakarta.servlet-api</artifactId>
        <version>6.0.0</version>
        <scope>provided</scope>
    </dependency>
</dependencies>

주요 scope 종류:

  • compile: 기본값. 모든 클래스패스에서 사용 가능
  • test: 테스트 컴파일과 실행 시에만 사용
  • runtime: 실행 시에만 필요 (컴파일 시 불필요)
  • provided: 컴파일 시 필요하지만 런타임에는 컨테이너가 제공
  • system: provided와 유사하지만 JAR 경로를 직접 지정

 

전이적 의존성 (Transitive Dependencies)

Maven은 전이적 의존성을 자동으로 처리합니다. 예를 들어 A가 B에 의존하고 B가 C에 의존한다면, A에서 B만 선언해도 C가 자동으로 포함됩니다.

하지만 때로는 원치 않는 전이적 의존성이 들어올 수 있습니다. 이때 <exclusions>를 사용합니다:

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-core</artifactId>
    <version>6.1.0</version>
    <exclusions>
        <exclusion>
            <groupId>commons-logging</groupId>
            <artifactId>commons-logging</artifactId>
        </exclusion>
    </exclusions>
</dependency>

dependencyManagement

멀티 모듈 프로젝트에서 의존성 버전을 중앙에서 관리하려면 <dependencyManagement>를 사용합니다:

<!-- 부모 POM -->
<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>6.1.0</version>
        </dependency>
    </dependencies>
</dependencyManagement>

<!-- 자식 모듈 POM - 버전 생략 가능 -->
<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-core</artifactId>
        <!-- 버전이 부모에서 관리됨 -->
    </dependency>
</dependencies>

플러그인 시스템

플러그인과 Goal

Maven의 모든 실제 작업은 플러그인(Plugin)이 수행합니다. 각 플러그인은 하나 이상의 Goal을 제공하며, Goal은 특정 빌드 페이즈에 바인딩되어 실행됩니다.

<build>
    <plugins>
        <!-- 컴파일러 플러그인 설정 -->
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.11.0</version>
            <configuration>
                <source>17</source>
                <target>17</target>
            </configuration>
        </plugin>

        <!-- JAR 패키징 시 메인 클래스 지정 -->
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-jar-plugin</artifactId>
            <version>3.3.0</version>
            <configuration>
                <archive>
                    <manifest>
                        <mainClass>com.example.Main</mainClass>
                    </manifest>
                </archive>
            </configuration>
        </plugin>
    </plugins>
</build>

자주 사용하는 플러그인

  • maven-compiler-plugin: Java 소스 코드 컴파일
  • maven-surefire-plugin: 단위 테스트 실행
  • maven-jar-plugin: JAR 파일 생성
  • maven-war-plugin: WAR 파일 생성
  • maven-deploy-plugin: 원격 리포지토리에 배포
  • maven-shade-plugin: 의존성을 포함한 실행 가능 JAR 생성 (uber-jar)
  • spring-boot-maven-plugin: Spring Boot 실행 가능 JAR 생성

Maven 리포지토리 구조

Maven 리포지토리는 아티팩트를 저장하고 관리하는 저장소입니다. 크게 세 가지 유형이 있습니다.

로컬 리포지토리

개발자의 로컬 머신에 위치하며, 기본 경로는 ~/.m2/repository입니다. Maven이 의존성을 다운로드하면 이곳에 캐시되어, 동일한 의존성을 반복 다운로드할 필요가 없습니다.

중앙 리포지토리 (Maven Central)

Maven Central은 오픈소스 라이브러리들이 호스팅되는 공식 리포지토리입니다. 별도의 설정 없이도 Maven은 기본적으로 Central 리포지토리에서 의존성을 검색합니다.

원격 리포지토리

조직 내부에서 운영하는 사설 리포지토리로, Nexus나 Artifactory 같은 도구로 구축합니다. 내부 라이브러리 배포, 외부 접근 제한, 프록시 캐싱 등의 목적으로 사용됩니다.

의존성 해결 순서

Maven이 의존성을 찾는 순서는 다음과 같습니다:

  1. 로컬 리포지토리 (~/.m2/repository) 확인
  2. 없으면 원격 리포지토리 (설정된 사설 리포지토리) 확인
  3. 없으면 Maven Central 리포지토리에서 다운로드
  4. 다운로드한 아티팩트를 로컬 리포지토리에 캐시

Nexus Repository Manager란

Nexus의 역할

Sonatype Nexus Repository Manager는 소프트웨어 컴포넌트를 저장, 관리, 배포할 수 있는 아티팩트 리포지토리 매니저입니다. 단순히 Maven 아티팩트만 관리하는 것이 아니라, npm, Docker, NuGet, PyPI, RubyGems 등 다양한 패키지 형식을 지원합니다.

Nexus를 도입하면 다음과 같은 이점을 얻을 수 있습니다:

  • 빌드 속도 향상: 외부 리포지토리에서 매번 다운로드하지 않고 내부 캐시에서 제공
  • 네트워크 대역폭 절약: 동일한 아티팩트를 팀원들이 공유
  • 보안 강화: 외부 의존성을 통제하고 취약점이 있는 라이브러리를 차단
  • 내부 아티팩트 관리: 사내 라이브러리를 체계적으로 버전 관리하고 배포
  • 빌드 안정성: 외부 리포지토리 장애 시에도 캐시된 아티팩트로 빌드 가능

Nexus OSS vs Nexus Pro

  • Nexus OSS (Open Source): 무료 버전으로 기본적인 리포지토리 관리 기능 제공
  • Nexus Pro: 고가용성(HA), 스테이징, 보안 취약점 스캐닝 등 엔터프라이즈 기능 포함

Nexus 리포지토리 유형

Nexus에서 관리하는 리포지토리는 세 가지 유형으로 나뉩니다. 이 구조를 이해하는 것이 Nexus 활용의 핵심입니다.

Hosted Repository (호스팅 리포지토리)

자체 아티팩트를 저장하는 리포지토리입니다. 조직 내부에서 개발한 라이브러리나 애플리케이션을 이곳에 배포합니다.

일반적으로 두 개의 Hosted 리포지토리를 운영합니다:

  • maven-releases: 정식 릴리스 버전 저장 (한번 배포하면 덮어쓰기 불가)
  • maven-snapshots: 개발 중인 스냅샷 버전 저장 (같은 버전으로 반복 배포 가능)

Proxy Repository (프록시 리포지토리)

외부 원격 리포지토리의 미러 역할을 하는 리포지토리입니다. Maven Central이나 다른 외부 리포지토리에 대한 프록시를 설정하면, 팀원들이 직접 외부에 접근하지 않고 Nexus를 통해 아티팩트를 가져옵니다.

 

동작 방식:

  1. 개발자가 Nexus의 프록시 리포지토리에 아티팩트를 요청
  2. Nexus가 로컬 캐시를 확인
  3. 캐시에 없으면 외부 리포지토리에서 다운로드
  4. 다운로드한 아티팩트를 캐시에 저장 후 개발자에게 전달
  5. 이후 동일 요청은 캐시에서 즉시 제공

Group Repository (그룹 리포지토리)

여러 리포지토리를 하나의 URL로 묶어주는 가상 리포지토리입니다. 개발자는 하나의 URL만 설정하면 되고, Nexus가 내부적으로 여러 리포지토리를 순회하며 아티팩트를 찾아줍니다.

일반적인 그룹 구성:

  • maven-public (그룹)
    • maven-releases (호스팅)
    • maven-snapshots (호스팅)
    • maven-central (프록시 → Maven Central)

개발자는 maven-public URL 하나만 알면 됩니다.


Nexus 설치와 초기 설정

Docker로 Nexus 설치

가장 간편한 설치 방법은 Docker를 사용하는 것입니다:

# Nexus 3 컨테이너 실행
docker run -d \
  --name nexus \
  -p 8081:8081 \
  -v nexus-data:/nexus-data \
  sonatype/nexus3:latest

# 초기 관리자 비밀번호 확인
docker exec nexus cat /nexus-data/admin.password

컨테이너가 시작되면 http://localhost:8081에서 Nexus 웹 UI에 접근할 수 있습니다. 초기 로그인 후 반드시 관리자 비밀번호를 변경하세요.

기본 리포지토리 생성

Nexus 설치 후 기본적으로 다음 리포지토리가 생성되어 있습니다:

  • maven-central: Maven Central에 대한 프록시 리포지토리
  • maven-releases: 릴리스 아티팩트용 호스팅 리포지토리
  • maven-snapshots: 스냅샷 아티팩트용 호스팅 리포지토리
  • maven-public: 위 세 리포지토리를 묶은 그룹 리포지토리

대부분의 경우 이 기본 설정으로 충분하지만, 필요에 따라 추가 리포지토리를 생성할 수 있습니다.


Maven과 Nexus 연동 설정

settings.xml 설정

Maven의 전역 설정 파일인 ~/.m2/settings.xml에 Nexus 연결 정보를 설정합니다:

<?xml version="1.0" encoding="UTF-8"?>
<settings xmlns="http://maven.apache.org/SETTINGS/1.2.0"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.2.0
          http://maven.apache.org/xsd/settings-1.2.0.xsd">

    <!-- Nexus 인증 정보 -->
    <servers>
        <server>
            <id>nexus-releases</id>
            <username>admin</username>
            <password>your-password</password>
        </server>
        <server>
            <id>nexus-snapshots</id>
            <username>admin</username>
            <password>your-password</password>
        </server>
    </servers>

    <!-- 모든 요청을 Nexus로 미러링 -->
    <mirrors>
        <mirror>
            <id>nexus</id>
            <mirrorOf>*</mirrorOf>
            <url>http://your-nexus-host:8081/repository/maven-public/</url>
        </mirror>
    </mirrors>

    <!-- 프로파일 설정 -->
    <profiles>
        <profile>
            <id>nexus</id>
            <repositories>
                <repository>
                    <id>central</id>
                    <url>http://central</url>
                    <releases><enabled>true</enabled></releases>
                    <snapshots><enabled>true</enabled></snapshots>
                </repository>
            </repositories>
        </profile>
    </profiles>

    <activeProfiles>
        <activeProfile>nexus</activeProfile>
    </activeProfiles>
</settings>

pom.xml 배포 설정

프로젝트의 아티팩트를 Nexus에 배포하려면 pom.xmldistributionManagement를 설정합니다:

<distributionManagement>
    <repository>
        <id>nexus-releases</id>
        <name>Nexus Release Repository</name>
        <url>http://your-nexus-host:8081/repository/maven-releases/</url>
    </repository>
    <snapshotRepository>
        <id>nexus-snapshots</id>
        <name>Nexus Snapshot Repository</name>
        <url>http://your-nexus-host:8081/repository/maven-snapshots/</url>
    </snapshotRepository>
</distributionManagement>

여기서 <id> 값은 반드시 settings.xml<server> id와 일치해야 합니다. 그래야 Maven이 올바른 인증 정보를 사용하여 Nexus에 접속할 수 있습니다.

배포 실행

# 프로젝트 빌드 후 Nexus에 배포
mvn clean deploy

# 버전이 1.0.0-SNAPSHOT이면 → maven-snapshots에 배포
# 버전이 1.0.0이면 → maven-releases에 배포

멀티 모듈 프로젝트

대규모 프로젝트에서는 여러 모듈을 하나의 프로젝트로 관리하는 멀티 모듈 구조를 사용합니다.

부모 POM 설정

<!-- parent/pom.xml -->
<project>
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.example</groupId>
    <artifactId>my-platform</artifactId>
    <version>1.0.0</version>
    <packaging>pom</packaging>  <!-- 반드시 pom으로 설정 -->

    <!-- 하위 모듈 정의 -->
    <modules>
        <module>core</module>
        <module>api</module>
        <module>web</module>
    </modules>

    <!-- 공통 의존성 버전 관리 -->
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>3.2.0</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
</project>

자식 모듈 설정

<!-- core/pom.xml -->
<project>
    <modelVersion>4.0.0</modelVersion>

    <parent>
        <groupId>com.example</groupId>
        <artifactId>my-platform</artifactId>
        <version>1.0.0</version>
    </parent>

    <artifactId>core</artifactId>

    <dependencies>
        <!-- 버전은 부모 POM에서 관리 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
    </dependencies>
</project>

부모 POM에서 <dependencyManagement>로 버전을 중앙 관리하면, 자식 모듈에서는 버전을 명시하지 않아도 됩니다. 이를 통해 전체 프로젝트의 의존성 버전을 일관되게 유지할 수 있습니다.


실전 팁과 Best Practice

Maven 명령어 모음

# 기본 빌드 명령
mvn clean install              # 클린 빌드 후 로컬 설치
mvn clean package              # 클린 빌드 후 패키징만
mvn clean deploy               # 클린 빌드 후 원격 배포

# 테스트 관련
mvn test                       # 단위 테스트만 실행
mvn verify                     # 통합 테스트 포함 검증
mvn install -DskipTests        # 테스트 건너뛰기

# 의존성 분석
mvn dependency:tree            # 의존성 트리 확인
mvn dependency:analyze         # 미사용/미선언 의존성 분석
mvn versions:display-dependency-updates  # 업데이트 가능한 의존성 확인

# 디버깅
mvn clean install -X           # 디버그 모드 (상세 로그)
mvn help:effective-pom         # 최종 적용된 POM 확인
mvn help:effective-settings    # 최종 적용된 settings 확인

SNAPSHOT vs Release 버전 전략

  • SNAPSHOT 버전 (1.0.0-SNAPSHOT): 개발 중인 불안정 버전. 동일 버전으로 반복 배포 가능. Maven은 원격 리포지토리에서 최신 스냅샷을 주기적으로 확인
  • Release 버전 (1.0.0): 정식 릴리스 버전. 한번 배포하면 변경 불가 (불변성 보장). 프로덕션 환경에서 사용

Nexus 보안 설정 권장사항

  • 기본 관리자 비밀번호를 반드시 변경할 것
  • 역할(Role) 기반 접근 제어를 설정할 것
  • 배포 권한과 조회 권한을 분리할 것
  • 프록시 리포지토리의 캐시 정책을 적절히 설정할 것

마무리

Maven과 Nexus는 Java 생태계에서 빌드와 아티팩트 관리의 근간을 이루는 도구입니다. Maven은 CoC 철학에 기반하여 프로젝트의 빌드, 테스트, 패키징, 배포를 체계적으로 관리하고, Nexus는 이 아티팩트들을 조직 차원에서 안전하고 효율적으로 관리합니다. 특히 CI/CD 파이프라인과 결합하면 빌드부터 배포까지 완전히 자동화된 개발 워크플로우를 구축할 수 있습니다. 처음에는 설정이 다소 복잡하게 느껴질 수 있지만, 한번 구축해 놓으면 팀 전체의 개발 생산성을 크게 향상시킬 수 있습니다.


참고 자료

이 글은 Claude Code를 활용하여 작성되었습니다.