Apache Maven은 자바 프로젝트의 빌드, 라이브러리 의존성 관리, 프로젝트 구조 표준화를 자동화하는 도구입니다. "관습이 설정보다 우선한다"는 철학에 따라 최소한의 설정으로 효율적인 프로젝트 관리가 가능하며, 일관된 빌드 라이프사이클과 강력한 의존성 관리를 제공합니다.
오늘은 Apache Maven을 통한 자바 프로젝트 빌드 관리에 대해 알려드릴게요!
Apache Maven이 뭔가요? 🤔
여러분이 큰 요리 대회에 출전한다고 상상해보세요.
- 필요한 모든 식재료를 정확한 양으로 준비해야 합니다
- 요리 단계별로 정확한 순서를 따라야 합니다
- 여러 셰프가 함께 요리할 때는 동일한 레시피를 공유해야 합니다
Maven은 바로 이런 역할을 하는 자바 프로젝트의 '요리사'예요!
- 필요한 모든 라이브러리(식재료)를 자동으로 관리
- 빌드 과정(요리 단계)을 일관되게 자동화
- 표준화된 프로젝트 구조(레시피)를 제공
Apache Software Foundation에서 개발한 Maven은 2004년 처음 출시된 이후, 자바 생태계에서 가장 널리 사용되는 빌드 도구 중 하나가 되었습니다.
어떻게 동작하나요? 🎬
Maven은 "관습이 설정보다 우선한다(Convention over Configuration)"라는 철학을 따릅니다. 마치 유명 요리사가 수년간의 경험을 바탕으로 최적의 조리법을 정해놓은 것처럼, Maven도 프로젝트 구조와 빌드 프로세스의 모범 사례를 기본값으로 제공합니다.
1. 디렉토리 구조
Maven 프로젝트는 아래와 같은 표준 구조를 따릅니다:
my-app/
├── pom.xml # 프로젝트 설정 파일 (레시피)
├── src/
│ ├── main/ # 메인 코드
│ │ ├── java/ # 자바 소스 코드
│ │ ├── resources/ # 설정 파일, 리소스
│ │ └── webapp/ # 웹 애플리케이션 파일 (웹 프로젝트)
│ └── test/ # 테스트 코드
│ ├── java/ # 테스트 소스 코드
│ └── resources/ # 테스트 리소스
└── target/ # 빌드 결과물 (컴파일된 클래스, JAR, WAR 등)
이 구조를 따르면 Maven은 각 파일의 위치를 자동으로 인식하므로, 별도의 설정 없이도 소스 코드를 컴파일하고 테스트를 실행할 수 있습니다.
2. POM.xml - 프로젝트의 심장
POM(Project Object Model)은 Maven의 핵심 설정 파일로, 프로젝트의 정보, 의존성, 빌드 설정 등을 정의합니다. 마치 요리의 레시피와 같은 역할을 합니다.
<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">
<!-- POM 모델 버전 -->
<modelVersion>4.0.0</modelVersion>
<!-- 프로젝트 좌표 - 프로젝트를 유일하게 식별하는 정보 -->
<groupId>com.example</groupId> <!-- 조직 ID -->
<artifactId>my-app</artifactId> <!-- 프로젝트 이름 -->
<version>1.0-SNAPSHOT</version> <!-- 버전 -->
<!-- 프로젝트 정보 -->
<name>My First Maven App</name>
<description>A simple Maven project</description>
<!-- 의존성 정의 -->
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>5.3.13</version>
</dependency>
</dependencies>
<!-- 빌드 설정 -->
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>11</source>
<target>11</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
POM의 주요 요소:
- 프로젝트 좌표: groupId, artifactId, version으로 구성된 유일한 식별자
- 의존성: 프로젝트에서 사용하는 외부 라이브러리
- 플러그인: 빌드 과정에서 실행되는 작업을 정의
- 프로필: 환경별 설정을 관리 (개발, 테스트, 운영 등)
3. 빌드 라이프사이클 - 자동화된 요리 과정
Maven의 빌드 라이프사이클은 마치 요리의 단계처럼 체계적으로 구성되어 있습니다:
- validate: 프로젝트 구조가 올바른지 검증 (재료 확인)
- compile: 소스 코드를 컴파일 (재료 손질)
- test: 단위 테스트 실행 (맛보기)
- package: 컴파일된 코드를 JAR/WAR로 패키징 (요리 완성)
- verify: 패키지의 품질 검사 (품질 확인)
- install: 패키지를 로컬 저장소에 설치 (집 냉장고에 보관)
- deploy: 패키지를 원격 저장소에 배포 (레스토랑에 납품)
이 중 어느 단계를 실행하면, 그 이전의 모든 단계가 자동으로 실행됩니다. 예를 들어, mvn package
명령은 validate, compile, test 단계를 거쳐 package 단계까지 실행합니다.
# 프로젝트 컴파일
mvn compile
# 단위 테스트 실행
mvn test
# 패키지 생성 (JAR, WAR 등)
mvn package
# 로컬 저장소에 설치
mvn install
# 이전 빌드 결과물 삭제 후 새로 설치
mvn clean install
의존성 관리 - 재료 준비의 마법 ✨
Maven의 가장 강력한 기능 중 하나는 의존성 관리입니다. 필요한 외부 라이브러리를 POM 파일에 선언하기만 하면, Maven이 자동으로 다운로드하고 관리해 줍니다.
저장소 시스템
Maven은 세 가지 유형의 저장소를 사용합니다:
로컬 저장소: 개발자의 컴퓨터에 위치 (기본 위치:
~/.m2/repository
)- 의존성을 처음 다운로드하면 이곳에 저장됨
- 빌드 시 가장 먼저 여기서 의존성을 찾음
중앙 저장소: Maven 공식 저장소 (https://repo.maven.apache.org/maven2/)
- 대부분의 오픈소스 라이브러리가 여기에 있음
- 로컬에 없는 라이브러리를 자동으로 이곳에서 다운로드
원격 저장소: 회사나 조직이 자체적으로 운영하는 저장소
- 내부 라이브러리나 중앙 저장소에 없는 라이브러리를 호스팅
- pom.xml에 별도로 설정해야 함
<!-- 원격 저장소 설정 예시 -->
<repositories>
<repository>
<id>company-repository</id>
<url>http://maven.company.com/repository</url>
</repository>
</repositories>
의존성 범위(Scope)
의존성을 선언할 때 scope를 지정하여 의존성이 필요한 시점을 제어할 수 있습니다:
- compile: 기본값, 모든 상황에서 필요 (컴파일, 실행, 테스트)
- provided: 컴파일과 테스트 시에만 필요, 런타임에는 컨테이너가 제공 (예: 서블릿 API)
- runtime: 실행과 테스트 시에만 필요, 컴파일 시에는 불필요
- test: 테스트 시에만 필요 (예: JUnit)
- system: 로컬 시스템의 특정 JAR을 참조 (거의 사용하지 않음)
<!-- JUnit은 테스트 시에만 필요 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
<!-- 서블릿 API는 컴파일 시에만 필요, 런타임에는 WAS가 제공 -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>
플러그인 시스템 - 요리 도구의 확장 🧰
Maven은 플러그인 기반의 아키텍처를 가지고 있습니다. 모든 작업은 플러그인을 통해 수행되며, 필요에 따라 플러그인을 추가하여 기능을 확장할 수 있습니다.
주요 플러그인
- maven-compiler-plugin: 소스 코드 컴파일
- maven-surefire-plugin: 단위 테스트 실행
- maven-jar-plugin: JAR 파일 생성
- maven-war-plugin: WAR 파일 생성
- maven-dependency-plugin: 의존성 분석 및 관리
- spring-boot-maven-plugin: 스프링 부트 애플리케이션 패키징
<build>
<plugins>
<!-- 자바 버전 설정 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>11</source>
<target>11</target>
</configuration>
</plugin>
<!-- 스프링 부트 애플리케이션 패키징 -->
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>2.5.5</version>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
장점은? 🌟
의존성 관리가 자동화되어 있어요
- 필요한 라이브러리를 선언만 하면 자동으로 다운로드
- 라이브러리의 의존성(전이적 의존성)까지 자동으로 해결
- 버전 충돌을 쉽게 관리할 수 있음
표준화된 프로젝트 구조로 협업이 편해요
- 어떤 Maven 프로젝트든 동일한 구조를 가짐
- 새로운 개발자도 프로젝트 구조를 빠르게 이해 가능
- IDE와 상관없이 일관된 빌드 환경 제공
빌드 과정이 일관되고 재현 가능해요
- 모든 개발자가 동일한 방식으로 빌드
- CI/CD 파이프라인과 쉽게 통합
- 빌드 서버에서도 로컬과 동일한 결과 보장
플러그인 생태계가 풍부해요
- 다양한 작업을 위한 플러그인이 이미 존재
- 필요한 기능을 쉽게 추가 가능
- 커스텀 플러그인 개발도 가능
주의할 점 ⚠️
XML 설정이 장황할 수 있어요
- 복잡한 프로젝트의 경우 pom.xml이 매우 커짐
- XML 구문이 직관적이지 않을 수 있음
- Gradle의 DSL에 비해 가독성이 떨어질 수 있음
초기 학습 곡선이 있어요
- 개념과 용어를 이해하는 데 시간이 필요
- 의존성과 플러그인 설정이 처음엔 복잡하게 느껴짐
- 트러블슈팅이 어려울 수 있음
대규모 프로젝트에서는 빌드 속도가 느릴 수 있어요
- 모듈이 많은 대형 프로젝트에서 성능 이슈 발생 가능
- Gradle의 증분 빌드에 비해 속도가 느릴 수 있음
- 빌드 캐시 최적화 기능이 제한적
실제 사용 예시 📱
1. 간단한 웹 애플리케이션 설정
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>simple-webapp</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<dependencies>
<!-- 서블릿 API -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>
<!-- JSTL -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<!-- JUnit -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>3.3.1</version>
</plugin>
</plugins>
</build>
</project>
2. 스프링 부트 애플리케이션
<project>
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.5.5</version>
</parent>
<groupId>com.example</groupId>
<artifactId>spring-boot-app</artifactId>
<version>0.0.1-SNAPSHOT</version>
<dependencies>
<!-- 스프링 부트 웹 스타터 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- 스프링 부트 JPA 스타터 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!-- H2 데이터베이스 -->
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<!-- 스프링 부트 테스트 스타터 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
3. 멀티 모듈 프로젝트
대형 프로젝트는 여러 모듈로 분리하여 관리할 수 있습니다:
parent-project/
├── pom.xml (parent)
├── common/
│ └── pom.xml
├── service/
│ └── pom.xml
├── web/
│ └── pom.xml
└── api/
└── pom.xml
부모 POM 설정:
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>parent-project</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>pom</packaging>
<modules>
<module>common</module>
<module>service</module>
<module>web</module>
<module>api</module>
</modules>
<!-- 공통 의존성 및 플러그인 관리 -->
<dependencyManagement>
<!-- ... -->
</dependencyManagement>
</project>
Maven vs Gradle 비교 🔄
많은 개발자들이 Maven과 Gradle 중 어떤 것을 선택할지 고민합니다:
특성 | Maven | Gradle |
---|---|---|
설정 언어 | XML | Groovy/Kotlin DSL |
가독성 | 상세하지만 장황함 | 간결하고 표현력이 높음 |
성능 | 상대적으로 느림 | 증분 빌드, 캐싱으로 빠름 |
유연성 | 제한적 | 매우 유연함 |
생태계 | 성숙하고 안정적 | 빠르게 성장 중 |
학습 곡선 | 중간 | 가파름 |
주요 사용처 | 전통적인 자바 프로젝트 | 안드로이드, 최신 JVM 프로젝트 |
어떤 것을 선택해야 할까요?
- Maven: 안정성과 표준화가 중요한 기업 환경, 간단한 자바 프로젝트
- Gradle: 대규모/복잡한 프로젝트, 빌드 스크립트의 유연성이 필요한 경우, 안드로이드 개발
마치며 🎁
Apache Maven은 마치 요리사가 재료 준비부터 요리 과정, 플레이팅까지 체계적으로 관리하는 것처럼, 자바 프로젝트의 전체 라이프사이클을 일관되게 관리해주는 강력한 도구입니다.
복잡한 의존성 관리, 표준화된 프로젝트 구조, 자동화된 빌드 프로세스를 통해 개발자는 비즈니스 로직에 더 집중할 수 있게 됩니다. 특히 팀 프로젝트에서 Maven은 모든 개발자가 동일한 환경에서 작업할 수 있게 해주는 든든한 기반이 됩니다.
Maven이 제공하는 "관습이 설정보다 우선한다"는 철학을 통해, 복잡한 빌드 설정 없이도 효율적인 프로젝트 관리가 가능해집니다.
궁금하신 점 있으시다면 댓글로 남겨주세요! 😊
참고 자료
- Apache Maven 공식 문서: https://maven.apache.org/
- Maven 저장소와 의존성 관리: https://offbyone.tistory.com/163
- Maven vs Gradle 비교: https://backendcode.tistory.com/199
- Java 빌드 도구: https://gwonsungjun.github.io/articles/2018-03/javaBuildTool
- Maven 라이프사이클: https://www.slipp.net/wiki/pages/viewpage.action?pageId=4489306
'100===Dev Ops > Maven' 카테고리의 다른 글
Can you add custom remote repositories to Maven? How? (0) | 2024.05.29 |
---|---|
How does Maven manage dependencies, and where does it retrieve them from? (0) | 2024.05.29 |