maven framework summary
maven framework 개념 정리

 
 
maven_logo
 

maven 이란


maven 은 보통 java 진영에서 웹 애플리케이션 개발을 할 때 사용하는 프로젝트 관리 프레임워크 이다.
동일한 재단 (Apache) 의 ant 빌드 도구의 대안으로 만들어 졌으며 오픈 소스 소프트웨어 이다.
 
이 둘의 가장 큰 차이점은 ant 는 tool 이고 maven 은 framework 라는 점이다.
maven 이 framework 라는 것만 잊지 않으면 그리 어렵지 않게 적응할 수 있을거라 생각 한다.
웹 애플리케이션을 개발할 때 표준처럼 사용하고 있는 Spring framework 를 생각해보면 maven 이 동작하는 방식이라던가 사용하기 위해서 무엇을 해야 하는지 이해할 수 있다.
 
전통적인 방식의 Servlet 을 만든다고 했을 때 web.xml 에 Servlet 으로 사용 할 클래스를 등록하고, 이 클래스가 HttpServlet 클래스를 상속 받아 doGet, doPost 같은 메소드를 재정의 하고는 했다.
그래서 오래된 웹 애플리케이션은 web.xml 에 수많은 Servlet 들이 등록 되어 있는게 당연했다.
Spring framework 는 (다양한 편의를 제공 하지만) 반복되는 Servlet 등록에 대해 @GetMapping, @PostMapping 등을 사용해서 이런 불편함을 해결해 주었다.
요즘은 너무나 당연해진 이 방법은 Spring framework 가 그렇게 쓰면 알아서 해준다고 약속했기 때문이다.
 
이런 맥락에서 maven 도 마찬가지이다.
java 기반의 maven 웹 애플리케이션을 개발할 때, maven 이 정의한 위치에 있어야 할 파일들을 위치시키기만 하면 프로젝트 관리를 알아서 해준다.
maven 을 사용해서 웹 애플리케이션을 개발하면, 파일 종류(또는 성격)에 따라 위치가 정해져 있다. (이런 관점에서 ant 가 maven 에 비해 자유도가 높다고 한다.)
 
ant 를 사용해봤다면 알겠지만, ant 는 내가 작성한 java 파일이 어디에 있는지, 라이브러리는 어느 경로에 있는지, 배포할 때 어느 위치에 어떻게 배포할지 등을 포함한 모든 것을 설정해 주어야 한다.
하지만 앞서 얘기했지만, maven 을 사용하면 maven 이 어느 곳에 어떤 파일들을 위치해야하는지 미리 정의하고 있기 때문에 그 규약(컨벤션, convention)만 지켜주면 다른 모든 것들에 대해 신경쓰지 않아도 된다.
 
maven documentation 을 보면 maven 프로젝트 표준 디렉토리 레이아웃에 대해 다음과 같이 정의하고 있다.
 

directory description
src/main/java  → Application/Library sources
src/main/resources  → Application/Library resources
src/main/filters  → Resource filter files
src/main/webapp  → Web application sources
src/test/java  → Test sources
src/test/resources  → Test resources
src/test/filters  → Test resource filter files
src/it  → Integration Tests (primarily for plugins)
src/assembly  → Assembly descriptors
src/site  → Site
LICENSE.txt  → Project’s license
NOTICE.txt  → Notices and attributions required by libraries that the project depends on
README.txt  → Project’s readme

 
성격에 맞는 파일을 위에 정의한 위치에 작성하기만 하면 이후 관리는 maven 이 알아서 해준다고 하니 자유도는 낮아졌지만 사용할 가치는 충분하다고 생각한다.
 
 

maven 주요 용어


 

maven pom (Project Object Model)


maven 은 프로젝트의 구조와 빌드 과정을 정의하는 pom.xml 이라는 파일이 있다.
ant 빌드 스크립트와 비교하면, 절차를 기술하기보다 프로젝트의 구조에 대해 정의하는 선언적 방법으로 작성 된다.
어떻게 보면 당연한 것이, 라이프사이클이 정해져 있기 때문에 각 단계에서 무엇을 사용할지 선언하는 것만으로 동작할 수 있기 때문이다.
pom.xml 에 작성할 수 있는 내용들은 다음과 같다.
 
(M) : mandatory
(O) : optional
 

 
pom.xml 기본 구조

<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <!-- 기본 정보 -->
    <groupId>...</groupId>
    <artifactId>...</artifactId>
    <version>...</version>
    <packaging>...</packaging>
    <dependencyManagement>...</dependencyManagement>
    <modules>...</modules>
    <properties>...</properties>
    <dependencies>...</dependencies>

    <!-- 빌드 설정 -->
    <build>...</build>
    <reporting>...</reporting>

    <!-- 프로젝트 추가 정보 -->
    <name>...</name>
    <description>...</description>
    <url>...</url>
    <inceptionYear>...</inceptionYear>
    <licenses>...</licenses>
    <organization>...</organization>
    <developers>...</developers>
    <contributors>...</contributors>

    <!-- 환결 설정 정보 -->
    <issueManagement>...</issueManagement>
    <ciManagement>...</ciManagement>
    <mailingLists>...</mailingLists>
    <scm>...</scm>
    <prerequisites>...</prerequisites>
    <repositories>...</repositories>
    <pluginRepositories>...</pluginRepositories>
    <distributionManagement>...</distributionManagement>
    <profiles>...</profiles>
</project>

 
다음은 일부 자주 사용하는 태그들에 대한 설명이다.

tag description
modelVersion  → maven 2.0 과 3.0 에서는 반드시 4.0.0 사용
groupId  → maven 에서 관리 할 애플리케이션의 분류 (주로 도메인을 역순으로 작성)
artifactId  → maven 에서 관리 할 애플리케이션 아이디, 보통 프로젝트 이름을 사용
version  → 애플리케이션 버전으로 maven 에서는 ‘SNAPSHOT’ 과 ‘RELEASE’ 를 접미어로 사용해서 구분
packaging  → 애플리케이션 packaging 방식 정의 / 주로 war, jar, ear 을 사용하고 그 외의 방법을 지정하려면 별도의 플러그인을 사용
dependencies  → 애플리케이션에서 사용 할 의존성을 정의 / 애플리케이션 개발에는 수많은 의존 라이브러리를 사용하는데, 그 라이브러리가 다른 라이브러리를 참조하는 추의적인 의존성에 대해서도 자동 관리
parent  → pom 은 상속 관계를 명시할 수 있으며, 이 태그를 사용해서 부모 pom 을 지정
modules  → pom 파일을 조합하여 multi module project 작성시 사용
properties  → pom 파일 내부에서 사용 할 속성 정의 / 주로 버전 정보 정의
profiles  → profile 을 정의하여 개발, 운영 등 구분하여 서로 다른 속성을 정의하고 싶을 때 사용
repositories  → maven 저장소 정보

 
groupId:artifactId:version 형태로 작성 한 것을 maven 주소 라고 하며, 이 값으로 각 프로젝트를 구분할 수 있다.

 

maven dependencies


maven 이 제공해주는 핵심 기능 중 의존성 관리 기능이 있다.
java 에서 의존한다는 것은 서로 다른 두 클래스 간의 사용 관계에 대한 정의 이다.
예를 들어 아래와 같이 코드를 작성 한 것을 ‘MyClass 클래스가 AnotherClass 클래스에 의존하고 있다.’ 라고 한다.

class MyClass {
    public static void main(String[] ar) {
        AnotherClass ac = new AnotherClass();
    }
}

class AnotherClass {}

이런 의존 관계에 대해 maven 은 pom 파일 내부에 dependency 태그를 사용해 정의하는 기능을 가지고 있다.
maven 에서 사용 가능한 의존성을 찾아볼 수 있는 maven repository 사이트가 있다.
예시로 lombok 라이브러리를 검색하면 다음과 같은 결과를 볼 수 있다.

maven_001
 
 
‘Project Lombok’ 링크를 클릭하면 다음과 같이 버전별로 자세한 정보를 확인할 수 있다.
 
 
maven_002
 
 
사용하려는 라이브러리의 버전을 선택 하면, maven pom.xml 의 태그 안에 정의할 수 있는 태그를 복사할 수 있다.
 
 
maven_003
 

<dependencies>
    <!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.18.16</version>
        <scope>provided</scope>
    </dependency>
</dependencies>

 
 
위와 같이 작성하고 maven update 를 하면 저장소에서 해당 라이브러리를 자동으로 로컬 저장소에 다운받은 것을 확인할 수 있다.
maven 을 사용하지 않는다면 다운 받은 라이브러리 jar 파일을 classpath 에 추가해 줘야 한다.
그리고 버전이 바뀌면 새로 다운 받아 파일을 대체 해줘야 하고, 만약 다운 받은 라이브러리가 다른 라이브러리를 사용한다면 열심히 검색해서 다운받아 classpath 에 추가해 주는 작업을 해줘야 한다.
 
maven 의 의존성 관리가 특별히 강력한 이유는, 추가한 라이브러리가 의존하는 다른 라이브러리에 대한 (추의적 또는 연쇄 의존성, transitive) 관리를 자동으로 해주기 때문이다.
즉, 라이브러리를 추가 하면 그 라이브러리가 의존하는 다른 라이브러리를 자동으로 다운받아 사용할 수 있도록 추가해 주기 때문에,
class not found 등의 문제로부터 자유로워지고 개발자는 프로젝트에서 직접 사용 할 라이브러리에 대한 의존성만 정의해주면 된다.
 
 
자동으로 관리해 주는 것과 관련하여 한가지 더 알아두어야 할 내용이 있다.
이렇게 추의적인 dependency 를 자동으로 추가하다보면 분명히 같은 라이브러리를 두 번 이상 추가하게 되는 경우가 발생할 수 있다.
maven 은 위와 같은 경우를 포함하여 다음과 같은 정책으로 의존성을 관리 한다.
 

Dependency Scope

마지막 단락 maven dependency 관리 관련 참고 : 'Dead & Street' tistory 블로그

 


provided scope

Spring boot maven project 기준으로 dependency 를 추가할 때 provided scope 을 사용하고,
build plugin 으로 spring boot maven plugin 을 사용해서 war 배포를 하면 다음과 같이 packaging 되는 것을 볼 수 있다.
 
maven_004  
독립 실행 가능한 jar 파일을 만들었다면 Main-Class 값으로 JarLauncher 클래스가 등록 되었겠지만, 지금은 war packaging 을 사용했기 때문에 WarLauncher 클래스가 등록되어 있다.
그리고 WEB-INF 디렉토리 하위에 lib 이외에 lib-provided 디렉토리가 생긴 것을 볼 수 있다.
이 디렉토리는 war packaging 을 하면서 dependency 에 provided 가 있는 경우 생기는데, WarLauncher 클래스에 대한 api 를 찾아보면 다음과 같은 부분을 볼 수 있다.
 
maven_005  
packaging 된 war 파일을 java -jar 명령으로 독립 실행 했을 때, ‘WEB-INF/lib’ 뿐만 아니라 ‘WEB-INF/lib-provided’ 로 함께 참조하여 실행 한다.
provided scope 를 사용해서 classpath 에 등록되지 않는 다는 것의 의미가 lib 디렉토리를 물리적으로 분리하고,
독립 실행 할 경우 provided 항목에 대해 제공되지 않을 것이기 때문에 독립 실행 할 경우에만 추가로 참조 할 라이브러리 경로를 만들어 둔 것 같다.
 
 

maven 설치


maven 을 설치하는 방법은 어렵지 않다.
maven download 사이트에 접속 한다.
 
maven_006
 
윈도우 환경이라고 가정하고 zip 파일을 다운받는다.
만약 최근 버전이 아닌 특정 버전을 사용해야 한다면, download 페이지 맨아래 legacy archives' 링크를 클릭한 다음 원하는 버전을 다운받으면 된다.
 
 
zip 파일을 내려받았으면, 원하는 위치에 압축을 풀어주는 것으로 maven 을 사용 할 준비는 끝이 난다.
 
압축을 푼 위치에 대해 환경변수를 설정할 수도 있는데, 지금은 IDE 를 사용할 것이기 때문에 이 내용은 생략한다.
 
 
 

eclipse IDE 에 maven 설정하기

 
eclipse 에 maven 을 설정하기 위해 굳이 프로젝트를 만들 필요는 없다.
eclipse 를 실행하고 ‘Window > Preference’ 메뉴를 클릭 한다.
 
maven_007
 
‘Maven’ -> ‘Installations’ 메뉴로 들어가면 사용 할 maven 을 선택할 수 있다.
다운 받은 maven 이 있으니까 ‘EMBEDDED’ 를 사용하지 말고, 오른쪽의 ‘ADD…’ 를 클릭한다.
‘Installation type’ 을 ‘External’ 로 선택 하고 ‘Installation home’ 우측의 ‘Directory…’ 버튼을 클릭 한다.
 
maven_008
 
maven 을 다운받은 경로를 설정 했으면, Finish 를 누르고 다음 이미지와 같이 추가한 maven 을 선택하고 ‘Apply’ 버튼을 눌러 eclipse 에서 사용 할 maven 등록을 완료 한다.
 
maven_009
 
 
마지막으로 maven 에 라이브러리 다운로드 경로를 설정하는 방법이다.
기본 경로를 사용해도 되지만, 개인적으로 직접 관리하고 싶어서 기본 경로를 사용하지 않는 편이다.
기본 경로는 C 드라이브의 ‘사용자’ (또는 Users) 아래 ‘사용자 계정’ (컴퓨터 이름) 아래 .m2 라는 이름의 디렉토리에 있는 ‘repository’ 디렉토리 이다.
maven pom.xml 파일에 dependency 를 추가 하면 기본적으로 이 경로에 라이브러리들을 다운 받는다.
 
 
이 경로는 eclipse 에서 설정 가능하다.
 
maven_010
 
위 이미지와 같은 경로로 들어가서 ‘Browse…’ 버튼을 클릭한 다음, 압축을 푼 maven 파일에서 ‘conf’ 폴더 아래 ‘settings.xml’ 파일을 선택하고 ‘열기(O)’ 버튼을 클릭해서 적용한다.
그리고나서 ‘User Settings’ 문자열 오른쪽의 ‘open file’ 링크를 클릭하면, eclipse editor 에서 setting.xml 파일이 열린다.
 
열린 파일에서 ‘localRepository’ 태그를 찾아서 주석 밖에 작성해 준다.
이 때 경로는 라이브러리들을 다운 받고 싶은 임의의 경로를 설정해주면 된다.
 
maven_011
 
이미지에 작성한 예제 기준으로 D 드라이브 아래 local 폴더 아래 repo 폴더 안에 다운받도록 설정 되어 있다.
저장 버튼을 누른 다음, ‘Window > Preference’ 메뉴에 다시 들어가보면 아까와 달리 ‘Local Repository (From merged user and global settings’ 경로가 바뀐 것을 확인할 수 있다.
 
maven_012
 
 
마지막 과정은 굳이 하지 않아도 괜찮지만 개인적으로 경로를 지정하고 사용하는걸 선호하기 때문에 추가한 내용이다.

intelliJ IDEA (community edition) 에 maven 설정하기

 
intelliJ IDEA 를 실행시킨 다음 ‘Customize’ 메뉴에 들어가서 ‘All settings…’ 버튼을 클릭 한다.
 
maven_013
 
왼쪽에서 ‘Build, Execution, Deployment’ > ‘Build tools’ > ‘Maven’ 메뉴에 들어가면 어떤 maven 을 사용할지 ‘Maven home path’ 를 수정할 수 있다.
‘…’ 버튼을 클릭해서 설치한 maven 을 선택해 준다.
 
maven_014
 
maven_015
 
다음으로 ‘User settings file’ 과 ‘Local repository’ 경로를 설정하기 위해 ‘Override’ 체크박스를 체크하고 경로를 맞춰 준다.
 
maven_016
 
‘User settings file’ 은 설치한 maven 의 conf 폴더 하위의 ‘settings.xml’ 파일을 선택해 준다.
 
maven_017
 
‘Local repository’ 는 굳이 설정하지 않아도 괜찮지만, 기본값을 사용하지 않고 관리하는 것을 개인적으로 선호해서 설정해 주었다.
 
maven_018
 
intelliJ IDEA 에 maven 설정을 마치면 위와 같이 입력된 것을 볼 수 있다.
 
 
 
 

war packaging custom

 
요즘엔 Spring Boot 를 많이 사용하다보니 Spring Boot 프로젝트를 war packaging 할 때 다음과 같은 build 스크립트를 default 로 사용 한다.
 

<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
        </plugin>
    </plugins>
</build>

 
하지만 이렇게 설정한 상태에서 maven package 를 해보면 *.war 와 *.war.original 두 개가 생긴다.
두 파일의 차이는 .original 파일의 확장자를 war 나 zip 등 압축 파일 형식으로 바꾸고 내용을 열어보면 두 가지 차이점이 존재한다.
 
maven_019
 
왼쪽이 *.war 의 결과이고 오른쪽이 .original 의 결과이다.
org 디렉토리 하위에는 JarLauncher 와 WarLauncher 가 포함되어 있다.
이 말은 독립 실행 가능한 압축 파일이라는 것을 뜻한다.
그리고 lib-provided 에 대한 설명은 위에 설명해 두었다.
 
그런 의미에서 오른쪽의 .original 은 jar 나 war Launcher 없이 외부 WAS 를 사용해서 웹 애플리케이션을 배포할 때 필요한 것들로만 구성되어 있다.
그런데 만약 외부 WAS 를 사용할 것이 확실하다면 *.war 는 불필요한 것들을 많이 가지고 있는 골칫거리가 될 수 있다.
그리고 .original 을 사용하기 위해 별도의 후속 작업이 필요할지도 모른다.
(내가 아직 깔끔하게 설정하는 방법을 모르는 것일 수 있고 아마 그럴것 같다.)  
 
마지막으로 이 Spring boot 프로젝트를 외부 WAS 를 사용하는, 위에서 본 *.original 과 같은 war 만 생성 되기를 바란다면 다른 plugin 을 사용해야 한다.
 

<build>
    <finalName>myProjectName</finalName>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-war-plugin</artifactId>
            <configuration>
                <packagingExcludes>WEB-INF/classes/static/**</packagingExcludes>
                <webResources>
                    <resource>
                        <directory>src/main/resources/static</directory>
                        <targetPath>static</targetPath>
                    </resource>
                </webResources>
            </configuration>
        </plugin>
    </plugins>
</build>

 
수정한 build 스크립트는 maven-war-plugin 을 사용 하였다.
굳이 이 plugin 을 사용한 이유는 spring-boot-maven-plugin 이 만드는 *.war 를 만들지 않기 위함이었다.
그리고 Spring boot 가 기본적으로 정적인 자원에 대해 src/main/resources/static 디렉토리를 사용하고 있으며,
이 디렉토리가 war 파일의 /WEB-INF/classes/static 하위에 풀려 외부 WAS 를 사용했을 때 웹 서버와 웹 애플리케이션 서버가 깔끔하게 구분할 수 없다는 단점이 있다.
 
왜냐하면 scr/main/resources 에는 static 뿐만 아니라 웹 애플리케이션 서버가 처리해야 할 리소스들도 포함되어 있기 때문이다.
그래서 위에 작성한 build 스크립트에는 두 가지 설정이 추가되어 있다.
 

  1. WEB-INF/classes/static 하위의 모든 내용을 war package 할 때 포함하지 않는다.
  2. src/main/resources/static 위치가 내가 사용하는 Web (정적 리소스) 리소스 인데, root 의 static 폴더를 만들어서 복사 한다.

 

이렇게 설정하면 다음과 같이 웹 서버가 바라볼 리소스를 root 레벨에서 분리되어 비교적 관리가 편해 진다.

 

maven_020
 
 
 
 

*****
I solemnly swear that I am a programmer.