Maven을 이용하여 Java 외부 라이브러리를 사용하는 예로, H2 데이터베이스 엔진을 사용하여 JPA에서 데이터베이스 액세스하는 프로그램을 만드는 방법을 설명한다.


H2 + JPA 프로젝트 생성

Maven은 개발할 때, 필요한 라이브러리를 중앙 저장소에서 자동으로 다운로드하고 빌드한다. 따라서 개발하는데 뭔가 라이브러리를 사용하는 경우에도, Maven을 사용하고 있다면 해당 사이트에 가서 라이브러리를 다운로드하여 프로젝트에 추가할 필요가 전혀 없다. pom.xml에 라이브러리를 작성하고, 빌드하면 자동으로 모든 라이브러리가 참조되어 사용되기 때문이다.

그러면 실제로 어떤 라이브러리를 이용한 개발을 해보록 하자. Java 응용 프로그램에서 많이 사용되는 라이브러리라고 하면 "데이터베이스" 관련일 것이다.

여기에서는 "H2" 데이터베이스 엔진을 사용해 보기로 한다. H2는 pure Java의 SQL 데이터베이스 라이브러리이다. pure Java이어서 Java 프로그램 내에서 그대로 사용할 수 있다. 또한 H2는 파일에 데이터를 저장하고 메모리에 데이터를 저장할 수 있다. 개발 중에는 메모리에 저장해 두고, 릴리스 시에는 파일에 저장하는 방법도 있다.

중앙 저장소에서 검색

H2를 이용하기 위해 <dependency> 태그 작성법에 대해 알아 본다. 먼저 중앙 저장소 검색을 하는 사이트에 액세스를 한다.

https://search.maven.org/

여기에 액세스해서, 입력 필드에 "h2"라고 입력하여 검색해 본다. 검색 결과가 목록으로 표시될 것이다. 거기에서 그룹 ID는 "org.h2database", 아티팩트 ID는 "h2"라는 항목을 찾아, 해당 "Latest Version" 버전 번호 링크를 클릭한다. 버전 정보가 표시된다.

여기에 주요 빌드 도구에서의 라이브러리 추가하는 코드가 도구별로 정리되어 있다. "Dependency Information" 의 "Apache Maven"에 Maven의 <dependency> 태그가 게재되어 있다. 이것을 복사하여 pom.xml의 <dependencies> 안에 붙여 넣으면, H2 라이브러리를 프로젝트에 넣을 수 있다.

<dependency>
    <groupId>com.h2database</groupId>
    <artifactId>h2</artifactId>
    <version>1.4.196</version>
</dependency>

Maven에서 라이브러리를 이용하려면 이렇게 Maven의 검색 사이트에서 라이브러리를 검색하고 그 Dependency Information을 복사하여 pom.xml에 붙이는 식으로 라이브러리를 추가 할 수 있다. 익숙해지면 간단하다.

프로젝트와 pom.xml을 완성시킨다

이제 프로젝트를 만들어 보자. 이번에도 이전에 사용한 SampleMavenApp 프로젝트를 재사용할 수 있다. 만약 새로운 프로젝트를 생성하려면, mvn archetype:generate 명령으로 프로젝트를 작성한다. 이때 -DarchetypeArtifactId=maven-archetype-quickstart를 지정하여 실행하면 된다.

프로젝트가 준비되면 pom.xml을 완성시킨다. H2 라이브러리의 <depenency> 태그는 쉽게 준비할 수 있었지만, 이 외에도 필요한 라이브러리가 있다. 이번에는 JPA를 사용하여 데이터베이스에 액세스를 한다. 이를 위해 javax.persistence과 Persistence JPA를 준비해야 한다.

아래에 완성된 pom.xml을 올려 두었다.

<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>
 
    <groupId>com.devkuma</groupId>
    <artifactId>SampleMavenApp</artifactId>
    <version>1.0</version>
    <packaging>jar</packaging>
 
    <name>SampleMavenApp</name>
    <url>http://maven.apache.org</url>
 
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>
 
    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>3.8.1</version>
            <scope>test</scope>
        </dependency>
 
        <!--h2-->
        <dependency>
            <groupId>com.h2database</groupId>
            <artifactId>h2</artifactId>
            <version>1.4.196</version>
        </dependency>
 
        <!-- eclipse.persistence-->
        <dependency>
            <groupId>org.eclipse.persistence</groupId>
            <artifactId>javax.persistence</artifactId>
            <version>2.2.0</version>
        </dependency>
        <dependency>
            <groupId>org.eclipse.persistence</groupId>
            <artifactId>org.eclipse.persistence.jpa</artifactId>
            <version>2.7.0</version>
        </dependency>
 
    </dependencies>
 
    <build>
        <!--resource folder-->
        <resources>
            <resource>
                <directory>src/main/resources</directory>
                <filtering>true</filtering>
            </resource>
        </resources>
 
        <plugins>
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>exec-maven-plugin</artifactId>
                <version>1.6.0</version>
                <configuration>
                    <mainClass>com.devkuma.App</mainClass>
                </configuration>
            </plugin>
        </plugins>
    </build>
 
</project>

이번에는 플러그인 관계는 exec:java 위한 exec-maven-plugin 만 넣어 두었다. 일단 동작하면 되기에 이것으로 충분하다.

3개의 <dependency> 태그 외에 <build> 태그에도 낯선 태그가 추가되어 있다. 이것들은 모든 JPA 이용을 위해 필요한 것이다.

pom.xml에 추가된 내용

pom.xml에 이번 추가한 내용을 간략하게 정리하고 설명한다.

Eclipse의 Java Persistence API

JPA를 이용하면, Java Persistence API (javax.persistene)와 JPA 구현 라이브러리가 필요하다. 이번은 Eclipse Foundation이 개발하는 오픈 소스 라이브러리를 사용하고 있다.

Java Persistence API

<dependency>
    <groupId>org.eclipse.persistence</groupId>
    <artifactId>javax.persistence</artifactId>
    <version>2.2.0</version>
</dependency>

javax.persistence 패키지의 라이브러리이다. API가 없으면 아무것도 할 수 없기에 이는 필수이다.

JPA

<dependency>
    <groupId>org.eclipse.persistence</groupId>
    <artifactId>org.eclipse.persistence.jpa</artifactId>
    <version>2.7.0</version>
</dependency>

JPA의 Eclipse Foundation에 의한 구현이다. JPA를 이용하는데 이것도 필요하다. 이 두 라이브러리 세트로 준비해야 한다고 생각하면 된다.

이에 앞서의 H2의 라이브러리를 맞춰 총 3 개의 라이브러리를 추가하면 JPA에 따르면 H2 데이터베이스 액세스를 사용할 수있게됩니다.

리소스 폴더의 등록

이 밖에 <build> 태그에도 새롭게 추가한 부분이 있다. 이 태그 부분이다.

<resources>
    <resource>
        <directory>src/main/resources</directory>
        <filtering>true</filtering>
    </resource>
</resources>

이것은 리소스 폴더를 등록하기 위한 것이다. <resource>는 리소스 폴더의 정보를 작성하는 것으로, 여기에서는 src/main/resources 경로를 추가한다. 이것으로 main 폴더의 resoures 폴더에 있는 리소스 파일을 빌드할 때에 로드하는 패키지에 추가하게 된다.



JPA를 사용하는 프로그램

JPA를 사용하는 프로그램을 만드는 방법을 설명한다.

persistence.xm 준비

JPA를 사용하는 경우, persistence.xml이라는 파일을 준비해야 한다. 이것은 JPA에서 사용하는 퍼시스턴스 유닛을 정의하는 것이다.

main 폴더에 resources 폴더를 작성한다. 이것은 앞에서 언급한 바와 같이, 리소스 폴더로 등록한 것이다. 이 폴더에 "META-INF"라는 폴더를 만든다. 그리고 그 안에 "persistence.xml '라는 이름으로 파일을 준비한다.

아래에 persistence.xml 소스 코드를 올려 두었다.

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.1"
    xmlns="http://xmlns.jcp.org/xml/ns/persistence"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence
        http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd">
 
    <persistence-unit name="sample-persistance" transaction-type="RESOURCE_LOCAL">
        <provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
        <class>com.devkuma.MyEntity</class>
        <properties>
            <property name="javax.persistence.jdbc.driver" value="org.h2.Driver" />
            <property name="javax.persistence.jdbc.url" value="jdbc:h2:mem:db" />
            <property name="javax.persistence.jdbc.user" value="sa" />
            <property name="eclipselink.ddl-generation" value="drop-and-create-tables" />
            <property name="eclipselink.ddl-generation.output-mode" value="database" />
        </properties>
    </persistence-unit>
</persistence>

persistence.xml은 <persistence> 태그 안에 <persistence-unit>라는 태그를 준비하고, 거기에 퍼시스턴스 유닛의 정보를 작성한다. 여기에서는 다음과 같은 정보가 정리되어 있다.

  • 유닛 이름 : sample-persistance
  • 공급자 : org.eclipse.persistence.jpa.PersistenceProvider
  • 엔티티 클래스 : com.devkuma.MyEntity
  • 속성 :
    • javax.persistence.jdbc.driver (드라이버 클래스) : org.h2.Driver
    • javax.persistence.jdbc.url (데이터베이스 URL) : jdbc : h2 : mem : db
    • javax.persistence.jdbc.user (사용자 이름) : sa
    • eclipselink.ddl-generation (Eclipse Link 설정) : drop-and-create-tables
    • eclipselink.ddl-generation.output-mode (Eclipse Link 설정) : database

이들은 마지막 2개(Eclipse Link 설정) 이외는 JPA를 사용할 때 대부분은 필요한 정보이다. 이 중에는 특히 처음에 있는 유닛 이름, 프로바이더 클래스, 엔터티 클래스의 세 가지 필수 항목으로 반드시 준비한다.

<properties> 태그 안에 있는 속성의 값은 사용하는 데이터베이스에 따라 달라진다. 이 드라이버와 URL, 사용자 이름은 우선 대부분의 데이터베이스에 필요한 것이다. H2 이외의 데이터베이스의 설정은 여기에서 다루지 않는다. 별도 학습하길 바란다.

엔티티 클래스 생성

persistence.xml이 준비되면 다음에 필요한 것은 "엔티티 클래스"이다. 엔티티 클래스는 JPA에서 데이터베이스에 액세스할 때 기본이 되는 것이다. JPA에는 데이터베이스 테이블의 내용을 엔티티 클래스로서 정의해야 레코드를 주고 받을 때는 이 엔티티 클래스의 인스턴스로 처리하게 된다.

앞에서 persistence.xml에서 클래스로 com.devkuma.MyEntity 값을 설정했다. 이것이 sample-persistance 지속성 단위로 사용하는 엔티티 클래스이다. 이 클래스를 만들어 보자.

src/main/java/com/devkuma/ 폴더 안에 "MyEntity.java"라는 이름으로 파일을 작성한다. 그리고 아래와 같이 소스 코드를 작성하자.

package com.devkuma;
 
import java.util.Date;
import java.util.Calendar;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
 
@Entity
public class MyEntity {
    @Id
    @GeneratedValue
    private Integer id;
 
    private String name;
    private String message;
    private Date date = Calendar.getInstance().getTime();
 
    public MyEntity(){
        super();
    }
 
    public MyEntity(String name, String msg){
        super();
        this.name = name;
        this.message = msg;
    }
 
    public Integer getId() {
        return id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getMessage() {
        return message;
    }
    public void setMessage(String msg) {
        this.message = msg;
    }
    public Date getDate() {
        return date;
    }
 
    @Override
    public String toString() {
        return "{\n\tid: " + id + ",\n\tname: " + name + ",\n\tmessage: "
             + message+ ",\n\tdate: " + date + "\n}";
    }
 
}

엔티티 클래스는 @Entity 어노테이션을 붙인 POJO 클래스이다. Primary key가 되는 필드에는 @Id를 분여, 더불어 @GeneratedValue으로 값을 자동 할당하는 것을 나타낸다.

이 후에는 private 필드와 그 Setter/Getter 메소드의 형태로 저장하는 값을 준비하고 있다. 이것들은 테이블에 포함하는 항목에 해당하는 것이다. 이번에는 id, name, message, date 등 항목을 준비해 두었다.

MyEntity를 이용하기

이제 H2 + JPA에서 데이터베이스를 사용하는 환경이 되었다. 실제로 이용해 보자.

아래에 간단한 샘플을 올려 두었다. App.java을 편집한다.

package com.devkuma;
 
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
import javax.persistence.Query;
 
public class App {
    private static EntityManagerFactory factory
        = Persistence.createEntityManagerFactory("sample-persistance");
 
    public static void main( String[] args ){
        EntityManager manager = factory.createEntityManager();
        try {
            manager.getTransaction().begin();
            manager.persist(new MyEntity("sungjin", "This is sample message!"));
            manager.persist(new MyEntity("wonsuk", "안녕하세요!"));
            manager.persist(new MyEntity("byeongho", "이것을 테스트입니다."));
            manager.getTransaction().commit();
 
            Query query = manager.createQuery("select i from MyEntity i");
            List<MyEntity> list = query.getResultList();
            int count = 0;
            for (MyEntity p : list) {
                System.out.print("No," + ++count + " ");
                System.out.println(p);
            }
            System.out.println(" -- total: " + list.size() + " data.");
        } finally {
            manager.close();
        }
    }
}

소스가 변경되면,

mvn packge
mvn exec:java

이와 같이 실행하여 패키지와 프로그램의 실행을 한다. 실행하면 콘솔에 다음과 같은 텍스트가 출력된다.

No, 1 {
        id : 1
        name : sungjin,
        message : This is sample message !,
        date : Fri Oct 13 21:28:11 JST 2017
}
No, 2 {
        id : 2
        name : wonsuk,
        message : 안녕하세요!,
        date : Fri Oct 13 21:28:11 JST 2017
}
No, 3 {
        id : 3
        name : byeongho,
        message : 이것을 테스트입니다. ,
        date : Fri Oct 13 21:28:11 JST 2017
}
 - total : 3 data.

여기에서는 더미로 3개의 엔티티를 생성하여 데이터베이스에 저장하고 다시 데이터베이스에서 MyEntity을 얻어 내용을 출력한다. 우선 데이터의 읽고 쓰기 가능하다는 것은 확인할 수 있다.

여기서는 JPA의 사용법을 설명하기 위한 것이 아니기 때문에 자세한 설명은 생략하지만, 이것으로 "JPA를 사용하는 프로그램"의 기본적인 만드는 방법은 알게 되었다. 우선은 데이터베이스부터 시작하여 조금씩 사용할 수 있는 라이브러리의 폭을 넓혀 가면 된다.

Maven은 다양한 라이브러리를 관리한다. 물론 직접 라이브러리를 만들고 Maven으로 관리 할 수도 있다. 그 기본에 대해 설명한다.


자체 라이브러리 프로젝트 생성

Maven은 중앙 저장소에서 라이브러리를 자동으로 다운로드하여 사용할 수 있다. 그럼 이용할 수 있는 것은 중앙 저장소에 등록되어 있는 것 뿐인가? 라는 의문이 생긴다.

그러나 그렇지는 않다. 직접 만든 라이브러리를 프로젝트에서 참조하여 이용하는 것도 물론 가능하다.

라이브러리 프로젝트 생성

그러면 실제로 간단한 라이브러리를 만들고, 그것을 이용하여 보자. 여기에서는 예로서 "mylib"라는 라이브러리 프로젝트를 만들려고 한다. 다음과 같이 명령을 실행하고 Project의 설정 정보를 입력하여 생성한다.

Project 생성 명령

$ mvn archetype:generate -DarchetypeArtifactId=maven-archetype-quickstart

입력 정보

  • 그룹 ID : com.devkuma.mylib
  • 아티팩트 ID : MyLib
  • 버전 : 1.0-SNAPSHOT: :( 기본 상태)
  • 패키지 : com.devkuma.mylib: :( 기본 상태)

프로젝트가 생성되면, 여기에 간단한 처리를 위한 클래스를 추가하고 라이브러리로 사용하려고 한다.

템플릿으로 maven-archetype-quickstart를 지정하였기, 이 프로젝트는 매우 일반적인 Java 응용 프로그램 프로젝트이다. 라이브러리에 뭔가 특별한 것이 있는 것은 아니다.

MyLib.java 생성

기본적으로 App.java라는 클래스가 준비되어 있지만, 이것은 응용 프로그램으로 사용 메인 클래스로 이용하기로 한다. 이와는 별도로 라이브러리용 클래스를 만든다.

Project 안에 /src/main/java/com/devkuma/mylib/ 폴더 안에 "MyLib.java"라는 이름으로 Java 소스 코드 파일을 만든다. 그리고 아래와 같이 작성한다.

package com.devkuma.mylib;
 
import java.util.*;
 
public class MyLib {
    private List<Integer> data = new ArrayList<Integer>();
 
    public MyLib(Integer...n){
        super();
        data = Arrays.asList(n);
    }
 
    public void add(int n){
        data.add(n);
    }
 
    public int getTotal(){
        int num = 0;
        for(int n:data){
            num += n;
        }
        return num;
    }
 
    @Override
    public String toString(){
        String res = "{data:[";
        for(int n:data){
            res += n + ", ";
        }
        res += "]}";
        return res;
    }
}

여기에는 몇 가지 정수를 List에 모와서 그 합계를 표시하는 메소드을 제공하고 있다. 평범하지만 라이브러리의 동작을 확인하는 정도의 수준으로 충분하다.

pom.xml은 일단 기본 상태로 그대로 유지하고, 라이브러리이라고 해도 프로젝트 자체는 당연한 Java 어플리케이션이기 때문에 특별한 것은 필요없다.

App.java 수정

만든 MyLib 클래스를 실제로 사용해 보자. 기본적으로 제공되는 App.java을 열고 아래와 같이 소스 코드를 작성한다. 그리고 빌드하고 응용 프로그램을 실행 해 봅니다.

package com.devkuma.mylib;
 
public class App {
 
    public static void main( String[] args )    {
        MyLib lib = new MyLib(10, 20, 30, 40);
        System.out.println("MyLib " + lib);
        System.out.println("total " + lib.getTotal());
    }
}

패키지 생성

$ mvn package

패키지를 사용하여 App 클래스 실행

$ java -classpath target\MyLib-1.0-SNAPSHOT.jar com.devkuma.mylib.App

실행하면 다음과 같이 콘솔에 출력된다. App 클래스에서 MyLib 클래스를 생성하여 이용한 결과이다.

MyLib {data : 10, 20, 30, 40,]}
total 100

우선 MyLib의 기능이 잘 동작하고 있는 것을 확인하였다.

로컬 저장소 설치

이것으로 패키지가 완성은 되지만, 이대로 MyLib 다른 프로젝트에서 사용할 수 없다. 왜냐하면 이용하려고 해도 저장소에 MyLib가 준비되어 있지 않기 때문이다.

저장소라는 것은 라이브러리가 등록되어 있는 곳이다. 이것은 중앙 저장소가 가장 널리 사용되고 있지만,이 외에도 로컬 환경에도 저장소는 준비되어 있다. 이를 "로컬 저장소"라고 한다.

로컬 저장소는 사용자 폴더에 .m2 폴더명으로 위치한다.

/Users/{사용자 폴더}/.m2

직접 만든 라이브러리를 사용해야 경우는 이 로컬 저장소에 라이브러리를 추가하면, 그것을 다른 프로젝트에서 참조하고 사용할 수 있다. 여기에는 다음과 같이 실행한다.

$ mvn install

이 명령은 로컬 저장소에 패키지를 설치하는 것이다. 이것으로 MyLib가 로컬 저장소에 추가되고, 다른 프로젝트에서 참조할 수 있게 되었다.

SampleMavenApp에서 사용

생성한 MyLib을 다른 프로그램에서 사용해 보자. 지금까지 샘플로 사용한 SampleMavenApp 프로젝트 중에서 이용해 보기로 한다. 아래에 App.java 샘플 코드를 올려 두었다.

package com.devkuma;
 
import com.devkuma.MyLib;
 
public class App {
     
    public static void main( String[] args ){
        MyLib lib = new MyLib(123, 456, 78, 90);
        System.out.println("total " + lib.getTotal());
        System.out.println(lib);
    }
}

SampleMavenApp 내에서 MyLib를 이용하기 위해서는, MyLib를 의존 라이브러리로 프로젝트에 등록 한다. 이것은 pom.xml의 <dependencies> 태그 내에 MyLib 설정을 추가하면 된다.

<dependency>
    <groupId>com.devkuma.mylib</groupId>
    <artifactId>MyLib</artifactId>
    <version>1.0-SNAPSHOT</version>
</dependency>

이것이 MyLib의 <dependency> 태그이다. 이 태그를 pom.xml의 <depdendencies>에 추가한다.

동작 확인

그럼 프로그램이 제대로 작동하는지 확인한다. 먼저 응용 프로그램을 패키지한다.

$ mvn package

이것으로 Jar 파일이 생성되었고, 이어서 프로그램을 실행하여 동작을 확인해 본다.

mvn exec:java

SampleMavenApp에는 exec-maven-plugin 플러그인의 설정을 작성하였기에, exec:java으로 App 클래스를 실행할 수 있다. (자세한 내용은 Java 응용 프로그램 개발을 참조)

이것으로 일단은 외부 라이브러리를 사용할 수 있게 되었다. 이도 아직 완벽하지 않다. 일반적으로 java 명령을 사용하여 실행하자.

$ java -classpath target\SampleMavenApp-1.0.jar com.devkuma.App

이것으로 프로그램이 실행되지 않고 에러가 발생한다. 이는 도대체 왜 일까? java 커멘드로 수행할 수 없는 것인가. 그 이유는 라이브러리가 들어 있지 않기 때문이다.

mvn package으로 target 에 저장되는 Jar 파일에는 사실 MyLib의 클래스는 포함되지 않다. 이 SampleMavenApp 프로젝트의 클래스만 있을 뿐이다. exec:java으로 수행할 수 있었던 것은 로컬 저장소에 있는 라이브러리를 참조하도록 되어 있었기 때문이다.

그에 비해 일반적인 java 명령은 로컬 저장소를 참조하지 않는다. 그래서 MyLib의 Jar 파일을 Java와 JDK의 lib 폴더에 복사해야 사용할 수 있다.

더 확실한 것은 MyLib도 함께 Jar 파일에 포함시키는 것이다. 포함 시키는 방법은 maven-assembly-plugin를 참조하길 바란다.



maven-assembly-plugin 의존 라이브러리 포함

의존 라이브러리를 포함시키기

패키지를 할시에 의존 라이브러리로 Jar 파일에 포함하여 저장 시킬 수 있는데, 이는 maven-assembly-plugin이라는 플러그인을 설정하면 할 수 있다. 아래에 종속 라이브러리를 포함하여 Jar 파일에 저장하기 위한 설정을 올려 두었다.

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-assembly-plugin</artifactId>
    <version>3.1.0</version>
    <configuration>
        <descriptorRefs>
            <descriptorRef>jar-with-dependencies</descriptorRef>
        </descriptorRefs>
    </configuration>
    <executions>
        <execution>
            <phase>package</phase>
            <goals>
                <goal>single</goal>
            </goals>
        </execution>
    </executions>
</plugin>

<configuration> 태그에 대해

설정 정보를 기술하는 <configuration>는 <descriptorRefs>라는 태그를 제공하고, 나아가 그 안에 <descriptorRef>라는 태그를 준비하고 있다. 여기에 jar-with-dependencies로 지정하여 Jar의 일체화를 위한 설정임을 지정할 수 있다.

그 후에 <executions>에 있는 <execution>에 필요한 설정을 한다. <phase>으로 package 를 <goals>의 <goal>으로 single 을 각각 지정하고 있는데, 이는 패키징을 할때 1개의 Jar 파일로 정리하도록 하는 것이다. Jar 파일명은 SampleMavenApp-1.0-jar-with-dependencies.jar과 같이 -jar-with-dependencies가 붙여진다.

이 플러그인 설정을 pom.xml에 추가하여, 다시 mvn package하여 패키지를 해보자. 그리고 다시 java 커멘드로 Jar 파일을 실행해 본다.

$ java -classpath target\SampleMavenApp-1.0-jar-with-dependencies.ja com.devkuma.App

이번에는 제대로 프로그램이 실행될 것이다. Jar 파일에 MyLib이 포함되기 때문이다. 생성된 Jar 파일은 다른 컴퓨터에 복사하고 실행하여도 문제없이 동작한다.

SampleMavenApp의 완성된 pom.xml

SampleMavenApp의 pom.xml의 완성된 전체 빌드 스크립트를 올려 둔다.

<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>

  <groupId>com.devkuma</groupId>
  <artifactId>SampleMavenApp</artifactId>
  <version>1.0-SNAPSHOT</version>
  <packaging>jar</packaging>

  <name>SampleMavenApp</name>
  <url>http://maven.apache.org</url>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  </properties>

  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>3.8.1</version>
      <scope>test</scope>
    </dependency>
     <dependency>
       <groupId>com.devkuma.mylib</groupId>
       <artifactId>MyLib</artifactId>
       <version>1.0-SNAPSHOT</version>
     </dependency>
  </dependencies>

  <build>
    <plugins>
      <plugin>
        <groupId>org.codehaus.mojo</groupId>
        <artifactId>exec-maven-plugin</artifactId>
        <version>1.6.0</version>
        <configuration>
          <mainClass>com.devkuma.App</mainClass>
        </configuration>
      </plugin>

      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-jar-plugin</artifactId>
        <version>3.0.2</version>
        <configuration>
          <archive>
            <manifest>
                <mainClass>com.devkuma.App</mainClass>
                <addClasspath>true</addClasspath>
                <addExtensions>true</addExtensions>
                <packageName>com.devkuma</packageName>
            </manifest>
          </archive>
        </configuration>
      </plugin>
    
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-assembly-plugin</artifactId>
        <version>3.1.0</version>
        <configuration>
          <descriptorRefs>
            <descriptorRef>jar-with-dependencies</descriptorRef>
          </descriptorRefs>
        </configuration>
        <executions>
           <execution>
             <phase>package</phase>
             <goals>
               <goal>single</goal>
             </goals>
          </execution>
        </executions>
      </plugin>
            
    </plugins>
  </build>

</project>

이번 했던 작업에서 알 수 있듯이, 자체 라이브러리의 사용은 라이브러리 자체에 아무것도 특별한 작업은 필요없다. 유일하게 mvn install 명령으로 라이브러리를 로컬 저장소에 설치할 뿐이다.

최대의 포인트는 라이브러리를 이용하는 프로젝트에 있다. 먼저 <dependency> 태그 라이브러리를 추가하는 것이 필수 사항이다. 이것으로 exec:java라면 수행할 수 있다.

의존 라이브러리까지 모든 1개의 Jar에 정리하는 exec-assembly-plugin 플러그인 설정을 추가한다. 이는 프로젝트에 고유의 설정 등은 없기에, 여기에 게재한 태그를 그대로 복사해서 붙여 넣기하여 사용하면 될 것이다.

Web 응용 프로그램의 개발도 물론 Maven을 사용할 수 있다. 이에 대한 작업과 pom.xml에 대해 설명한다.



일반적인 Java 응용 프로그램과 달리 Web 응용 프로그램은 여러가지 고려해야 할 것이 있다. 먼저 프로그램의 구성이다. 일반적인 Web 응용 프로그램 개발에서는 Web에 공개하는 디렉토리를 준비하고 그 안에 JSP 등을 배치한다. 또한 "WEB-INF"폴더를 준비하고, 거기에 web.xml을 준비한다. 이러한 디렉토리의 구성에 따라 프로젝트를 만들어 나가야 한다.

또한, Web 응용 프로그램은 그대로 실행할 수 없다. 실행은 서블릿 컨테이너(이른바 Java 서버)가 필요하다. 서버를 시작하고, 거기 Web 응용 프로그램이 배포되어야 사용할 수 있게 된다.

이에 따라, Web 어플리케이션의 개발에는 일반 Java 어플리케이션과는 다른 만드는 방법이 필요하다.

maven-archetype-webapp를 사용하기

Web 응용 프로그램의 프로젝트 작성에는 "maven-archetype-webapp"라는 것을 사용한다. 명령 프롬프트 또는 터미널에서 다음과 같이 실행한다.

$ mvn archetype:generate -DarchetypeArtifactId=maven-archetype-webapp

이것으로 Web 응용 프로그램의 프로젝트를 생성하기 위한 템플릿이 사용된다. 예제로 다음과 같이 항목을 입력한다.

Define value for property 'groupId': : com.devkuma
Define value for property 'artifactId': : SampleWebApp
Define value for property 'version':  1.0-SNAPSHOT: : 
Define value for property 'package':  com.devkuma: : 
  • 그룹 ID : com.devkuma
  • 아티팩트 ID : SampleWebApp
  • 버전 : 1.0-SNAPSHOT : :( 기본 상태)
  • 패키지 : com.tuyano.libro : :( 기본 상태)

입력이 완료되면 SampleWebApp라는 프로젝트가 생성된다. 이것을 바탕으로, Web 어플리케이션 개발을 하면 된다.

생성된 프로젝트의 구성

그럼 생성된 프로젝트가 어떻게 되어 있는지 확인해 보자. 프로젝트 폴더를 열면 다음과 같은 구성으로 되어 있다.

SampleMavenApp 폴더

.
├── pom.xml
└── src
    └── main
        ├── resources
        └── webapp
            ├── WEB-INF
            │   └── web.xml
            └── index.jsp

main 폴더의 내용

src 폴더 안에는 main 폴더가 있고, 그 안에는 java 폴더가 아닌 대신에, resources와 webapp이 배치된다. 또한 test 폴더도 포함되어 있지 않다.

resources 폴더

resources 폴더는 그 이름 그대로, Web 어플리케이션의 프로젝트에 필요한 리소스 파일을 배치하는 곳이다.

webapp 폴더

이 폴더가 Web 어플리케이션으로서 공개되는 부분이다. 이 안에는 JSP 파일과 WEB-INF 폴더가 준비되어 있다. HTML 및 CSS, JavaScript 등의 파일이 그 안에 들어 가면 된다.

서블릿은?

Java 프로젝트이면서, 여기에는 java 폴더가 없다. 서블릿 등을 만들려고 하면 어떻게 되는 거야? 라고 생각한 사람도 있을 것이다.

java 폴더는 없지만, 프로그램 작성의 기본은 동일하다. 따라서 main 폴더에 java 폴더를 준비하고, 그 안에 패키지 폴더 및 소스 코드 파일을 배치해 나가면 된다.

생성된 pom.xml

생성된 프로젝트의 pom.xml이 어떻게 되어 있는지 확인하자. 아래에 디폴트 상태의 pom.xml을 올려 둔다.

<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/maven-v4_0_0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.devkuma</groupId>
  <artifactId>SampleWebApp</artifactId>
  <packaging>war</packaging>
  <version>1.0-SNAPSHOT</version>
  <name>SampleWebApp Maven Webapp</name>
  <url>http://maven.apache.org</url>
  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>3.8.1</version>
      <scope>test</scope>
    </dependency>
  </dependencies>
  <build>
    <finalName>SampleWebApp</finalName>
  </build>
</project>

보면 알 수 있듯이, pom.xml 자체는 일반적인 Java 응용 프로그램의 pom.xml와 거의 변함이 없다.

우선, 이제 제대로 개발할 수 있는지 확인해 본다. 다음 명령을 실행해 보자.

$ mvn package

실행하면 target 폴더가 생성되고, 그 안에 SampleWebApp.war 파일이 생성된다. 이 파일의 압축을 풀어 보면, webapp 폴더의 내용을 정리하고 있다. 또한, main 안에 java 폴더를 생성하고, 소스 코드를 준비 했다면, WEB-INF 폴더에 classes 폴더를 배치되고 거기에 클래스 파일이 포함될 것이다. 일단, Web 어플리케이션으로의 기본 부분은 잘 만들어져 있는 것을 알 수 있다.

이것으로 Maven 명령에서 war 파일을 만들고 이것을 Java 서버에 배포한다면, 이것으로 충분히 개발이 가능할 것이다.



jetty-maven-plugin Web 실행

Web 어플리케이션의 개발을 수행하면서 매번 "패키지를 작성하여 war 파일을 Java 서버에 배포하고 ......" 하는 것보다 바로 Java 서버를 시작하고, 거기에 빌드한 Web 어플리케이션를 기동하여 동작 체크한다면 매우 편리할 것이다.

그래서, 서블릿 컨테이너 "Jetty"을 이용하여 동작 확인을 실시할 수 있는 구조를 갖춰 보도록 해보자. 해야 하는 것은 물론 pom.xml 편집이다.

이번에는 여러가지 해야 할 것이 많기 때문에, 우선은 완성 버전을 올려두기로 한다. 아래에 이번 프로젝트의 완성된 pom.xml은 아래와 같다. 처음 본 것이 여러가지가 있지만 우선이 이대로 다시 작성한다.

<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/maven-v4_0_0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.devkuma</groupId>
  <artifactId>SampleWebApp</artifactId>
  <packaging>war</packaging>
  <version>1.0-SNAPSHOT</version>
  <name>SampleWebApp Maven Webapp</name>
  <url>http://maven.apache.org</url>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <jetty.version>9.4.8.v20171121</jetty.version>
  </properties>

  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>3.8.1</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>org.eclipse.jetty</groupId>
      <artifactId>jetty-server</artifactId>
      <version>${jetty.version}</version>
    </dependency>
    <dependency>
      <groupId>org.eclipse.jetty</groupId>
      <artifactId>jetty-webapp</artifactId>
      <version>${jetty.version}</version>
    </dependency>
  </dependencies>
  <build>
    <finalName>SampleWebApp</finalName>
    <plugins>
      <plugin>
        <groupId>org.eclipse.jetty</groupId>
        <artifactId>jetty-maven-plugin</artifactId>
        <version>${jetty.version}</version>
      </plugin>
    </plugins>
  </build>
</project>

설치 실행

수정한 후 프로그램을 빌드하고 로컬 환경에 설치한다. 이것은 다음 명령을 실행한다.

$ mvn install

이 명령어는 로컬 저장소(로컬 환경의 라이브러리 장치를 관리하는 곳)에 빌드한 프로그램을 설치하는 것이다. 다만, 여기에서는 필요한 라이브러리을 다운로드, 프로그램 빌드 및 war 파일 생성과 같은 일련의 작업을 수행할 목적으로 수행하고 있다.

처음 실행할 때는 필요한 라이브러리을 중앙 저장소에서 다운로드한다. 따라서 꽤 시간이 걸린다. 2번째 이후는 내 컴퓨터 이미 다운로드가 되어 있기에 그다지 시간이 오래 걸리지 않는다.

Jetty 실행

프로그램이 성공적으로 빌드되면, Jetty를 사용하여 Web 응용 프로그램을 실행한다. 이것은 다음의 명령으로 실행할 수 있다.

$ mvn jetty:run

이 명령도 처음 실행할 때 필요한 라이브러리를 중앙 저장소에서 다운로드한다. 그리고 Jetty를 시작하고, 생성된 Web 응용 프로그램을 배포한다. 부팅 처리가 완료되면 다음 주소에 접근해 본다.

http://localhost:8080/

Web 브라우저가 열리면 화면에 "Hello World!"가 표시된다. 이는 webapp 안에 기본적으로 포함되어 있는 index.jsp가 표시되는 것이다. 이걸로 서버가 기동되고 Web 응용 프로그램에 액세스할 수 있는지 확인할 수 있다.

대충 동작 확인을 한 후에 명령 프롬프트 또는 터미널에서 "Ctrl + C"키를 눌러 명령의 실행을 중단한다. 그러면 서버가 종료하고 원래의 입력 대기 상태로 돌아온다.

속성 및 변수

그럼, 만든 pom.xml에 대해 살펴 보겠다. 우선 먼저 눈에 띄는 것은 패키지에 대한 설정이다.

<packaging>war</packaging>

이것이다. 이렇게 하면 빌드된 프로그램은 Jar 파일이 아닌, war 파일이 만들어 진다. Web 어플리케이션의 경우, <packaging>는 war가 기본이다.

속성에 대해

또, 매우 중요한 역할을 하는 것이 <properties> 태그이다. 만든 pom.xml을 보면 이렇게 적혀 있다.

<properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <jetty.version>9.4.8.v20171121</jetty.version>
</properties>

<project.build.sourceEncoding> 태그는 이미 설명했었다. 소스 파일의 인코딩을 지정하는 것이다.

또 다른 하나는 <jetty.version>라는 태그이다. 이것은 Jetty 버전을 지정한다. 그러면 9.4.8.v20171121 버전(2017 년 12 월 현재 최신 버전)이 jetty.version라는 속성에 지정된다.

이것이 어떻게 이용되고 있는지, 다른 곳을 보면 알 수 있다. 예를 들어, <dependencies> 태그를 보면 아래와 같이 작성된 것을 볼 수 있다.

<version>${jetty.version}</version>

이 ${jetty.version}라는 것으로 <jetty.version>의 값을 설정하고 있다. 이 ${jetty.version}은 jetty.version라는 변수이다. 즉, <jetty.version>라는 것은 jetty.version라는 변수를 만드는 것이다.

Jetty 버전 변수인 jetty.version는 이런 이름이 정해져 있는 것이 아니다. 우선 알기 쉬운 이름으로 붙인 것이다. 변수 이름은 각각 원하는대로 붙여 상관 없다. 예를 들어 "myval"라는 변수를 사용하고 싶다면, <myval>라는 태그로 값을 준비하고 ${myval}라는 형태로 변수를 이용된다.

pom.xml에는 특정 버전에 맞게 라이브러리를 준비할 수 있다. 이럴 때는 미리 변수를 준비하고 버전 이름을 설정해 두고 그 변수를 사용하여 각각의 라이브러리를 구성할 수 있도록 하는 경우가 있다.

이렇게 하면, 예를 들어 버전을 변경하면 <jetty.version>의 값을 다시 작성하기만 하면 된다. 그러지 않고 각각의 라이브러리에 개별적으로 값을 작성하게 되면, 전체 값을 찾아서 다시 변경해야 한다. 그러다가 변경되지 않고 이전 버전의 남아 있는 라이브러리가 나올지도 모른다.

이렇게 <proierties> 태그에 속성을 준비하여 변수를 만드는 방식은 다수의 라이브러리와 플러그인 등에 이용할 수 있는 필수 기능이다.

Jetty의 이용에 대해

그런데, 이번은 Maven의 기능 중에서도 가장 중요한 "의존 라이브러리 관리"를 하고 있다. <dependencies> 태그를 살펴 보자. JUnit 라이브러리 외에 2개의 라이브러리 설정이 추가되는 것을 알 수 있다(아래 참조).

의존 라이브러리

<dependency>
   <groupId>org.eclipse.jetty</groupId>
   <artifactId>jetty-server</artifactId>
   <version>${jetty.version}</version>
</dependency>
<dependency>
   <groupId>org.eclipse.jetty</groupId>
   <artifactId>jetty-webapp</artifactId>
   <version>${jetty.version}</version>
</dependency>

이것들은 Jetty 이용에 관한 것이다. 각각의 역할을 간단하게 설명하면 아래와 같다.

jetty-server

첫번째는 Jetty 서버의 라이브러리이다. Jetty 서버의 기본 라이브러리가 포함된다.

jetty-webapp

이것은 Jetty Webapp 및 Security, Servlet, XML 등 Web 어플리케이션 관련 라이브러리 장치가 포함된다. 이 라이브러리가 없어도 Jetty 서버 자체는 시작할 수 있지만, Web 어플리케이션에서 사용되는 자원에 따라 동작에 문제가 생길 가능성도 있으므로, jetty-server와 세트로 준비한다.

Jetty 플러그인

라이브러리 외에 Jetty 서버를 시작하는 플러그인 설정도 필요하다. jetty-maven-plugin라는 것으로, 그룹 ID, 아티팩트 ID 및 버전의 최소한의 설정만 준비해두면 된다.

Jetty플러그인

<build>
    <finalName>SampleWebApp</finalName>
    <plugins>
        <plugin>
            <groupId>org.eclipse.jetty</groupId>
            <artifactId>jetty-maven-plugin</artifactId>
            <version>${jetty.version}</version>
        </plugin>
    </plugins>
</build>

앞에서 mvn jetty:run으로 Jetty를 시작했지만 이를 가능하게 했던 것이 이 Jetty 플러그인이다. 특별히 설정도 필요없이 간단하다. 이 플러그인으로 Jetty를 사용한 Web 어플리케이션의 실행이 가능하게 된다.

이상, 2개의 <dependency>과 하나의 <plugin>를 추가하면 Jetty에서 Web 응용 프로그램을 실행 동작시킬 수 있다. Web 애플리케이션 개발의 기본 기능으로서 사용법을 기억해 두자.

Maven에 의한 Java 응용 프로그램 개발의 기본에 대해 기술한다. 실행하는 커멘드, 그리고 pom.xml에 기술에 대해 설명한다.

DarchetypeArtifactId 지정

Maven은 다양한 프로그램을 만들 수 있다. 이것들은 각각에 프로젝트를 작성하는 단계에서 작성 순서가 다르거나, pom.xml에 대한 작성이 다를 것이다. Maven을 이용하여 개발을 하려면, "자신이 만들려는 프로그램을 어떻게 만들어야 할지"를 알아야 한다.

특정 프로젝트 만들기

먼저 기본 중의 기본으로 Java에 의한 어플리케이션(보통 컴퓨터에서 더블 클릭으로 실행할 수 있는 Java 응용 프로그램) 개발부터 생각해 보도록 하자.

Maven으로 프로젝트를 만들 때에 "mvn archetype:generate"라는 커멘드를 실행했었다. 이것은 프로젝트 생성의 가장 기본이 되는 것이다. 하지만 이것을 실행하면, 템플릿 목록이 길게 나열된다. "여기에서 사용하는 템플릿을 선택"하는 것이지만, 이미 1000개 이상의 항목이 있기 때문에 선택하기에 매우 힘들다.

실은 프로젝트를 만들 때, 사용하는 템플릿을 미리 지정해 둘 수 있다. 이것은 - DarchetypeArtifactId라는 옵션을 사용한다.

mvn archetype:generate -DarchetypeArtifactId=아티팩트ID

이렇게 호출한다. 이 -DarchetypeArtifactId라는 것은 사용하는 템플릿 아티펙트 ID를 지정한다. 아무튼, 대부분의 경우에는 아티팩트 ID를 지정하면 그것으로 어떤 템플릿인지 특정할 수 있다.

그러나 경우에 따라서는 특정할 없는 경우도 있다(우연히 같은 아티팩트 ID 템플릿이 여러 개가 있는 것 같은 경우). 이럴 때는 그룹 ID를 지정하는 -DarchetypeGroupId라는 옵션도 준비되어 있다. 이를 사용하여 다음과 같이 실행하면 확실해 진다.

mvn archetype:generate -DarchetypeGroupId=그룹ID -DarchetypeArtifactId=아티팩트ID

기본은 maven-archetype-quickstart

그럼 일반적인 Java 응용 프로그램을 개발할 때는 어떻게 지정하면 되는가. 기본은 아래와 같다.

$ mvn archetype:generate -DarchetypeArtifactId=maven-archetype-quickstart

이와 같이 실행하면 다음과 같이 출력이 된다.

[INFO] Scanning for projects...
[INFO]                                                                         
[INFO] ------------------------------------------------------------------------
[INFO] Building Maven Stub Project (No POM) 1
[INFO] ------------------------------------------------------------------------
[INFO] 
[INFO] >>> maven-archetype-plugin:2.2:generate (default-cli) > generate-sources @ standalone-pom >>>
[INFO] 
[INFO] <<< maven-archetype-plugin:2.2:generate (default-cli) < generate-sources @ standalone-pom <<<
[INFO] 
[INFO] --- maven-archetype-plugin:2.2:generate (default-cli) @ standalone-pom ---
[INFO] Generating project in Interactive mode
Define value for property 'groupId': : 
Define value for property 'artifactId': : 
Define value for property 'version':  1.0-SNAPSHOT: : 
Define value for property 'package':  : : 
[WARNING] Archetype is not fully configured
Define value for property 'groupId': : ^Ckimkcui-MacBook-Pro:mvn kimkc$ 
kimkcui-MacBook-Pro:mvn kimkc$ mvn archetype:generate -DarchetypeArtifactId=maven-archetype-quickstart
[INFO] Scanning for projects...
[INFO]                                                                         
[INFO] ------------------------------------------------------------------------
[INFO] Building Maven Stub Project (No POM) 1
[INFO] ------------------------------------------------------------------------
[INFO] 
[INFO] >>> maven-archetype-plugin:2.2:generate (default-cli) > generate-sources @ standalone-pom >>>
[INFO] 
[INFO] <<< maven-archetype-plugin:2.2:generate (default-cli) < generate-sources @ standalone-pom <<<
[INFO] 
[INFO] --- maven-archetype-plugin:2.2:generate (default-cli) @ standalone-pom ---
[INFO] Generating project in Interactive mode
Define value for property 'groupId': : 그룹 ID 입력
Define value for property 'artifactId': : 아티팩트 ID 입력
Define value for property 'version':  1.0-SNAPSHOT: : (default)
Define value for property 'package':  그룹 ID : :  (default)

그룹 ID, 아티팩트 ID, 버전, 패키지 등을 차례로 입력한다. 보면 알 수 있듯이, 이것은 앞전에 프로젝트 생성할 때, 입력한 것과 동일하다. 이러한 입력하면 그대로 Java 애플리케이션 프로젝트가 만들어진다.



플러그인(plugin)

작성한 프로젝트는 Maven 명령으로 빌드 Jar 파일에 통합할 수 있었다. 그러나, 이 Jar 파일, 좀 다루기가 번거롭다. -classpath에서 Jar 파일을 지정하여, 메인 클래스를 실행해 주지 않으면 안된다. "좀 더 실행을 간단히 할 수 없을까"라고 생각한 사람도 많을 것이다. 이는 가능하다. 다만 그러기 위해서는 '플러그인'의 사용법을 알아야 한다.

Maven은 다양한 기능이 "플러그인"으로 추가되어 있다. 프로그램의 실행은 "exec-maven-plugin"이라는 플러그인으로 구현되어 있다. 이 플러그인에 대한 설정 정보를 pom.xml에 추가하여 프로그램을 실행 할 수 있다.

플러그인이라는 것은 pom.xml의 <project> 태그 안에 다음과 같은 형태로 포함된다. (※빌드에 대한 플러그인의 경우)

<build>
    <plugins>
        <plugin> ... 생략 ... </plugin>
    </ plugins>
</ build>

<build>는 빌드에 관한 설정을 기술하기 위한 것으로, <plugins>는 플러그인 설정 정보를 정리하기위한 것이다. 이 안에 <plugin>라는 태그를 사용하여, 특정 플러그인에 대한 설정을 정리하고 있다.

플러그인은 이뿐만 아니라 많이 준비되어 있다. 사실은 이미 설명한 것 중에도 플러그인을 사용한 기능은 있다. 예를 들어, "mvn compile"라는 명령은 maven-conpiler-plugin이라는 플러그인으로 구현되는 것이다. 또한 "mvn package"는 maven-jar-plugin이라는 플러그인으로 구현되어 있다.

결국은 Maven에서 사용하는 골(compile이나 package 같은 것)라는 것은 모든 플러그인으로 내장되어있는 것이다. 이것들은 <dependency> 태그 등으로 라이브러리를 다운로드할 필요는 없다. 표준으로 제대로 사용할 수 있게 되어있는 것도 있고, 없다면 필요에 따라 다운로드된다. 따로 <dependency> 태그을 준비할 필요는 없다.

<plugin> 태그

이 플러그인의 설정 정보를 기술하는 <plugin> 태그는 그 플러그인에 대한 필요한 정보를 태그에 준비해야 한다.

최소한 준비해야 하는 태그으로는 대체로 다음과 같다

<plugin>
    <groupId>그룹 ID</groupId>
    <artifactId>아티팩트 ID</artifactId>
    <version>버전</version>
    <configuration>
        ...... 설정 정보 ......
    </configuration>
</plugin>

그룹 ID, 아티팩트 ID 및 버전 같은 것은 이제 익숙할 것이다. 이것들을 사용하여 사용하는 플러그인 프로그램을 지정한다. 이 후에 <configuration> 태그는 이 플러그인을 사용하는데 필요한 정보 등을 준비하기 위한 것이다. 이것은 플러그인에 의해 기술하는 내용은 다르다(필요없는 경우도 있다).



exec-maven-plugin Java 실행

작성한 Java 응용 프로그램을 간단히 실행할 수 있도록 하는 플러그인 설정에 대해 설명한다.

프로그램의 실행은 exec-maven-plugin이라는 플러그인을 이용한다. 이 플러그인에 실행하는 응용 프로그램 클래스의 정보를 추가 기입해 두는 것으로, 프로그램을 간단히 수행 할 수 있다.

이 플러그인의 설정 정보는 다음과 같이 작성한다. (버전은 2017 년 12 월 현재 최신 버전이다)

<plugin>
    <groupId>org.codehaus.mojo</groupId>
    <artifactId>exec-maven-plugin</artifactId>
    <version>1.6.0</version>
    <configuration>
        <mainClass>실행 클래스 지정</mainClass>
    </configuration>
</plugin>

설정으로 <configuration> 태그 안에 작성해야 하는 것은 <mainClass>라는 태그이다. 이것이 실행되는 응용 프로그램의 클래스를 설정한다.

앞전에 작성한 SampleMavenApp 프로젝트에서는 com.devkuma.App라는 클래스가 메인 클래스로 되어 있었는데, 이것을 <mainClass>에 지정하면 된다.

<plugin>
    <groupId>org.codehaus.mojo</groupId>
    <artifactId>exec-maven-plugin</artifactId>
    <version>1.6.0</version>
    <configuration>
        <mainClass>com.devkuma.App</mainClass>
    </configuration>
</plugin>

프로그램 실행

이제 프로그램을 실행해 보자. 이는 매우 간단한다. 프로젝트 폴더에 현재 디렉토리를 이동해서 다음과 같이 실행하면 된다.

$ mvn exec:java

그러면 <mainClass>에 지정된 클래스를 실행한다. 예제인 SampleMavenApp 프로젝트 실행을 하면 다음과 같이 출력될 것이다.

[INFO] Scanning for projects...
[INFO]                                                                         
[INFO] ------------------------------------------------------------------------
[INFO] Building SampleMavenApp 1.0-SNAPSHOT
[INFO] ------------------------------------------------------------------------
[INFO] 
[INFO] --- exec-maven-plugin:1.6.0:java (default-cli) @ SampleMavenApp ---
Hello World!
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 0.703 s
[INFO] Finished at: 2017-12-13T20:59:26+09:00
[INFO] Final Memory: 7M/155M
[INFO] ------------------------------------------------------------------------

출력의 중간 쯤에 "Hello World!"라는 텍스트가 보일 것이다. 이것이 App 클래스를 실행하여 출력된 결과이다. 마지막으로 "BUILD SUCCESS"이라고 빌드 결과가 출력되고, 제대로 프로그램이 실행되는 것을 확인할 수 있다.



maven-jar-plugin 실행 가능한 Jar 생성

앞장에서 프로그램의 실행은 간단히 했지만 아직 완전하지 않다.

target 폴더에 패키지된 Jar 파일이 생성되었지만, 이를 그대로 실행하려고 하면 동작하지 않는다. 예를 들어, 아래와 같이 명령을 실행해 보자.

java -jar target/SampleMavenApp-1.0-SNAPSHOT.jar

그러면 다음과 같은 에러 메시지가 출력된다.

target/SampleMavenApp-1.0-SNAPSHOT.jar에 기본 Manifest 속성이 없습니다.

mvn package으로 생성된 Jar 파일은 실행 가능한 Jar가 아니다. 매니페스트 파일이 제대로 생성되지 않기 때문에 그대로는 사용할 수 없다.

maven-jar-plugin 설정에 대해

Jar 파일의 생성은 maven-jar-plugin이라는 플러그인에 의해 이루어 진다. 이 플러그인에 매니페스트 파일 작성을 위한 설정 정보를 추가하면 다음과 같은 형태가 된다.

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-jar-plugin</artifactId>
    <version>3.0.2</version>
    <configuration>
        <archive>
            <manifest>
                <mainClass>메인 클래스 지정</mainClass>
                <addClasspath>true</addClasspath>
                <addExtensions>true</addExtensions>
                <packageName>패키지</packageName>
            </manifest>
        </archive>
    </configuration>
</plugin>

설정 정보를 작성하는 <configuration>안에, 아카이브에 관한 설정인 <archive> 태그가 있다. 그 안에 매니페스트 파일에 대한 <manifest> 태그가 있고, 여기에 매니페스트에 대한 정보를 작성한다. 작성하는 내용은 다음과 같다.

태그설명
<mainClass>메인 클래스를 지정한다.
<addClasspath>클래스 패스(Class-Path 값)을 추가할지 여부를 지정한다. 이것은 true로 하면 된다.
<addExtensions>확장 정보가 포함된다. 이것은 <dependencies>에 작성된 라이브러리 정보 를 내보내는 위한 것이다. 이것도 true 하면 된다.
<packageName>Jar 패키지(Package)을 출력한다.

특히 라이브러리도 사용하지 않고, SampleMavenApp와 같은 간단한 프로그램이라면 <mainClass> 만 지정하면 된다.

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-jar-plugin</artifactId>
    <version>3.0.2</version>
    <configuration>
        <archive>
            <manifest>
                <mainClass>com.devkuma.App</mainClass>
                <addClasspath>true</addClasspath>
                <addExtensions>true</addExtensions>
                <packageName>com.devkuma</packageName>
            </manifest>
        </archive>
    </configuration>
</plugin>

이렇게 pom.xml에 작성하고, mvn package으로 Jar 파일을 생성하여 java -jar으로 SampleMavenApp-1.0-SNAPSHOT.jar을 실행해 본다. 이번에는 문제없이 실행할 수 있을 것이다.

$ java -jar target/SampleMavenApp-1.0-SNAPSHOT.jar 
Hello World!

pom.xml의 완성판

지금까지 설명했던 플러그인 정보를 추가한 pom.xml의 완성판는 아래와 같다.

<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>

  <groupId>com.devkuma</groupId>
  <artifactId>SampleMavenApp</artifactId>
  <version>1.0-SNAPSHOT</version>
  <packaging>jar</packaging>

  <name>SampleMavenApp</name>
  <url>http://maven.apache.org</url>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  </properties>

  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>3.8.1</version>
      <scope>test</scope>
    </dependency>
  </dependencies>

  <build>
    <plugins>
      <plugin>
        <groupId>org.codehaus.mojo</groupId>
        <artifactId>exec-maven-plugin</artifactId>
        <version>1.6.0</version>
        <configuration>
          <mainClass>com.devkuma.App</mainClass>
        </configuration>
      </plugin>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-jar-plugin</artifactId>
        <version>3.0.2</version>
        <configuration>
          <archive>
            <manifest>
                <mainClass>com.devkuma.App</mainClass>
                <addClasspath>true</addClasspath>
                <addExtensions>true</addExtensions>
                <packageName>com.devkuma</packageName>
            </manifest>
          </archive>
        </configuration>
      </plugin>
    </plugins>
  </build>

</project>

이는 Java 응용 프로그램을 작성하여 바로 실행해 보거나, 실행 가능한 Jar 파일을 만드는 경우에 기본적인 코드이다.

플러그인이 불필요하게 되면 삭제해도 상관 없다. 예를 들어, 실행 가능한 Jar를 만들 경우, exec:java으로 실행을 못해도 상관 없다면, exec-maven-plugin의 <plugin> 부분은 삭제해도 된다. 필요에 따라 수정하면서 이용하면 된다.



Maven은 "pom.xml"라는 빌드 파일을 사용하여 빌드 정보를 기술한다. 이 파일이 어떤 내용으로되어 있는지, 그 기본형을 설명한다.


Maven은 커멘드를 사용하여 간단히 프로젝트를 만들거나 빌드가 가능하다. 예제로 만든 프로젝트는 단지 App.java라는 소스 코드 파일이 있는 만큼 간단한 것이었다.

Maven의 강점은 다양한 라이브러리와 프레임워크 등을 이용하는 경우도 그들을 모두 Maven이 관리해주는 점이다. 이러한 "프로젝트 관리"를 하기 위해서는, 단지 Maven 명령을 실행하는 것만으로는 끝나지 않는다. 프로젝트를 관리하고 있는 "빌드 파일"이라는 것에 대해 이해할 필요가 있다.

프로젝트 폴더를 열면, 거기에 "pom.xml"라는 파일이 포함되어 있는 것을 알 수 있다. POM은 "Project Object Model"의 약자로, 프로젝트의 다양한 정보를 처리하기 위한 객체 모델이다. pom.xml에는 프로젝트 설정을 XML 태그로 기술하고 있다.

그럼 기본적으로 어떤 소스 코드가 적혀 있는지 살펴 보자(아래 참조).

<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>

  <groupId>com.devkuma</groupId>
  <artifactId>SampleMavenApp</artifactId>
  <version>1.0-SNAPSHOT</version>
  <packaging>jar</packaging>

  <name>SampleMavenApp</name>
  <url>http://maven.apache.org</url>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  </properties>

  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>3.8.1</version>
      <scope>test</scope>
    </dependency>
  </dependencies>
</project>

이것이 pom.xml의 기본 코드라고 해도 좋을 것이다. 좀 더 불필요한 것을 모두 제거하고 나면 기본 코드는 다음과 같다.

<project ... 중략 ...>

    <modelVersion>4.0.0</modelVersion>
    <groupId>그룹 ID</groupId>
    <artifactId>아티팩트 ID</artifactId>
    <version>버전</version>
    <packaging>jar</packaging>
    <name>이름</name>

    <properties>
        ...... 속성 정보 ......
    </properties>

    <dependencies>
        ...... 의존 라이브러리 정보 ......
    </dependencies>

</project>

pom.xml은 <project>라는 루트 태그 내에 모든 정보를 기술한다. 이 <project>는 xmlns / xmlns:xsi / sxi : schemaLocation 세세한 것들이 속성으로 여러 붙어 있는데, 이들은 모두 정해진 값이므로, 복사해서 쓰면 된다고 생각하면 된다. 필요에 따라 수정할 부분은 없다.

이 후에는 필요한 정보를 태그로 기술해 나갈뿐이다. 그럼 각각의 내용을 정리해 보겠다.

프로젝트 기본 속성

<project> 태그 다음에는 프로젝트에 대한 기본 속성을 설정하는 태그들이 있다. 이것들은 어떤 프로젝트도 대부분은 필요한 것이므로, 역할 정도는 정확히 기억해 두도록 한다.

<modelVersion>4.0.0</modelVersion>

POM 모델 버전이다. 이것은 "4.0.0"라고 설정한다. 당분간이 버전이 바뀔 것은 없다.

<groupId> 그룹 ID </ groupId>

그룹 ID이다. 프로젝트를 만들 때 입력했다. 제작자와 회사, 단체 등을 식별하기 위한 것이다.

<artifactId>아티팩트 ID</artifactId>

아티팩트 ID는 프로젝트에 할당한 고유 ID 이다.

<version> 버전 </ version>

프로그램 버전이다. 기본적으로 1.0-SNAPSHOT이 설정되어 있다.

<packaging>jar</packaging>

패키지 종류로 jar 또는 zip을 지정한다. Web 어플리케이션의 개발은 war를 지정할 수도 있다.

<name>이름</name>

프로그램 이름이다. 아티팩트 ID를 그대로 지정하는 것이 많을 것이다.

<url>주소</url>

프로젝트와 관련된 Web 사이트의 주소이다. 기본적으로 Apache Maven의 Web 사이트의 주소가 지정되어 있다.

<properties>
    ...... 속성 정보 ......
</ properties>

이 pom.xml에서 사용하는 속성 값 등을 모아 둔다. 이것은 필요에 따라 기술한다. 기본적으로는 아래에 하나만 포함되어 있다.

<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>

이 project.build.sourceEncoding라는 속성은 소스 코드의 기본 인코딩을 나타낸다. 일반적으로 UTF-8이 설정되어 있다.

<dependencies>
    ...... 의존 라이브러리 정보 ......
</ dependencies>

"의존 라이브러리"라고 하는 것은 이 프로그램이 참조하는 라이브러리이다. Java 기본 시스템 라이브러리는 제외한다.

최후에 <properties>와 <dependencies>를 제외하고, 프로젝트를 만들 때 입력한 것이 많다. 각각의 항목은 프로젝트를 만들 때 자동으로 기록되어 있기 때문에, 자신이 편집하는 일은 거의 없다. 역할 정도 알고 있으면 충분하다.

<dependencies>와 <dependency>

기본적으로 생성된 pom.xml의 기술에서 하나 뿐인 매우 태그가 복잡한 부분이 있었다. <dependencies>라는 태그이다.

이것은 이미 언급한 바와 같이 의존 라이브러리 정보를 기술해 두기 위한 것이다. 하지만, 이 기술하는 태그가 많은 계층적으로 되어 있기 때문에, 상당히 이해하기 어려울지도 모른다. 이 태그는 기본적으로 다음과 같은 형태로 기술되어 있다.

<dependencies>
    <dependency>... 생략 ...</dependency>
    <dependency>... 생략 ...</dependency>
    ...... 중략 ......
</ dependencies>

<dependencies>은 의존 라이브러리를 한곳에 모아 기술하기 위한 것이다. 각각의 의존 라이브러리 정보는 <dependency> 태그를 사용하여 작성한다. 이 <dependency> 태그를 필요한만 큼 <dependencies> 태그 안에 기술한다.

<dependency> 태그

라이브러리 정보를 기술하는 <dependency> 태그는 태그 안에 몇 가지 정보를 기술하고 라이브러리 지정한다. 정리하면 다음과 같은 형태이다.

<dependency>
    <groupId>그룹 ID</groupId>
    <artifactId>아티팩트 ID</artifactId>
    <version>버전</version>
    <scope>범위</scope>
</ dependency>

그룹 ID, 아티팩트 ID 및 버전은 앞전에 프로젝트와 동일하다. 라이브러리들도 모든 Maven 프로그램으로 만드는 이상, 그룹 ID 및 아티팩트 ID가 설정되어 있다. 이렇게 지정함으로써 어떤 라이브러리를 사용하는지를 알 수 있다.

단, <scope>라는 것이 이해하기 어려울지도 모른다. 이것은 이 라이브러리가 이용되는 범위를 지정하는 것이다. 범위라고 하면 이해하기 어려울 수도 있지만, "어떤 때 사용하는지"를 나타내는 것이다. 이 응용 프로그램을 실행할 때 사용하는 경우는 특별히 지정하지 않아도 된다. 특별한 경우에만 준비하는 것이라고 생각해도 좋을 것이다.

JUnit 라이브러리

샘플로 만든 프로젝트에서는 "JUnit '라는 라이브러리 정보가 <dependencies>에 기술되어 있다. 이것은 단위 테스트를 위한 기능을 제공하는 Java 라이브러리이다. Java에서 테스트라고 하면 대부분이 JUnit을 사용한다.

<dependency>
  <groupId>junit</groupId>
  <artifactId>junit</artifactId>
  <version>3.8.1</version>
  <scope>test</scope>
</dependency>

이 라이브러리의 정보는 다음과 같이되어 있다.

그룹 ID : junit
아티팩트 ID : junit
버전 : 3.8.1 (경우에 따라 다르지도?)
범위 : test

그룹 ID와 아티팩트 ID는 고정으로 각각 "junit"로 지정되어 있다. 이것이 JUnit에 할당된 값이다. 버전은 프로젝트를 생성할 때 최소한의 버전이 설정되어 있을 것이다.

범위(scope)는 "test"로 되어 있다. 이것은 테스트 실행시에만 이용되는 것을 나타낸다. 실제로 프로그램을 실행할 때 사용할 수 없다. 따라서 프로그램을 빌드 패키징할 때도 이 JUnit이라는 라이브러리는 포함되지 않는다.

Maven는 프로그램 빌드 및 패키징 등의 처리할 때는, 반드시 단위 테스트도 동시에 실행된다. 이 때에 테스트 라이브러리가 포함되어 있지 않으면 빌드가 실패한다. 따라서 기본적으로 기술되는 JUnit 라이브러리의 <dependency> 태그는 "Maven 프로젝트에 필요한 라이브러리 설정"이라고 할 수 있다. 사용하지 않는다고 해서 삭제하지 않도록 한다.

'Maven' 카테고리의 다른 글

[Maven] Web 응용 프로그램 개발  (0) 2017.12.23
[Maven] Java 응용 프로그램 개발  (0) 2017.12.23
[Maven] pom.xml 파일 기본  (0) 2017.12.23
[Maven] Maven 프로젝트 작성  (0) 2017.12.23
[Maven] Maven 빠르게 시작(퀵 가이드)  (0) 2017.12.23
[Maven] Maven 준비  (0) 2017.12.23

+ Recent posts