목차
지난 번 글에선 메이븐이 무엇인지 설명했었다.
이미 만들어져있는 프로젝트나 폼파일을 가지고 설명했었다.
메이븐이란? (JAVA Maven the build tool)
회사에서 일을 하면서 내가 정말 해멨던 메이븐에 대해서 정리해보고자 한다. 나는 회사에서 메이븐 때문에 고생을 많이 했다. 정말정말정말정말!!!! 코드가 아무리 맞게 짜여져 있어도 메이븐
mylittlenotepad.tistory.com
이번 글에선 실습이다.
직접 텅빈 폴더에서, 폼파일도 작성하고 라이브러리도 다운받으며 메이븐 프로젝트의 생성 과정을 지켜보자.
학습/실습 목표
- IDE(예: 인텔리제이)를 활용하여 메이븐 프로젝트를 생성할 수 있다.
- pom.xml에 적힌 태그들을 이해할 수 있다. plugin, dependency, version 등
- pom.xml 내용물과 메이븐 레파지토리 폴더 내용물의 생성 관계를 이해할 수 있다.
- pom.xml 또한 소스코드 작성에 따라 수시 변경이 필요하다는 점을 이해할 수 있다.
[1] 인텔리제이를 활용하여 메이븐 프로젝트 생성하기
좌측 상단에서 File → New → Project를 누르면, New Project 만들기 팝업이 뜬다.
대부분의 빈칸은 직관적으로 채울 수 있을 것이다.
- 프로젝트명을 입력하고 (클래스가 아니니 굳이 대문자로 할 필요X)
- 프로젝트를 저장할 공간도 설정한다. (지정한 공간을 루트로 하여서, 아래 방금 정한 프로젝트명의 새로운 폴더가 생성될 것이다)
- JDK도 설정한다. (나는 1.8이 깔려져 있기 때문에, 그것으로 택)
- Archtype을 설정하라고 뜨는데. 나는 기본 탬플릿을 선택했다 : org.apache.maven의 maven-archetype-quickstart
이 방법을 통해서 아래 것들이 자동으로 수행된다.
- 폼파일 생성 (그룹아이디 아티팩트아이디 디팬던시 등등이 이미 다 잘 작성 되어있다)
- 그 폼파일을 읽어 아파치 원격 저장소에서 이것 저것 많은 것을 다운로드.
- 프로젝트 소스 폴더 구조 잡기
*org.apache.maven의 maven-archetype-quickstart* ?
메이븐 서버에 공통적으로 사용할 수 있는 간단한 프로젝트를 말하고 이를 입력시 메이븐 서버에서 해당 구조에 대한 파일이나 설정을 다운받게 된다.
메이븐 공식 홈페이지에 maven-archetype-quickstart의 구조가 정의되어있다.
https://maven.apache.org/archetypes/maven-archetype-quickstart/
src파일 아래 main폴더 test폴더가 두 갈래로 나뉜다.
main폴더에는 프로그램 기능 및 작동을 위한 클래스를 넣으면 되고.
test폴더에는 해당 클래스를 검증하기 위한 클래스를 넣으면 된다.
AppTest.java에서 junit 라이브러리 기능을 가져다 쓰도록 초기 설정 되어있어서, 아마 이미 폼파일에 junit 의존성이 있을 것이다.
[2] 폼파일 이해하기
탬플릿(org.apache.maven의 maven-archetype-quickstart)따라 자동 생성 된 초기 폼파일 상태는 아래와 같다.
<?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>
<groupId>org.example</groupId>
<artifactId>mavenbuildproject</artifactId>
<version>1.0-SNAPSHOT</version>
<name>mavenbuildproject</name>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.7</maven.compiler.source>
<maven.compiler.target>1.7</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
<plugins>
<!-- clean lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#clean_Lifecycle -->
<plugin>
<artifactId>maven-clean-plugin</artifactId>
<version>3.1.0</version>
</plugin>
<!-- default lifecycle, jar packaging: see https://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_jar_packaging -->
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<version>3.0.2</version>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.1</version>
</plugin>
<plugin>
<artifactId>maven-jar-plugin</artifactId>
<version>3.0.2</version>
</plugin>
<plugin>
<artifactId>maven-install-plugin</artifactId>
<version>2.5.2</version>
</plugin>
<plugin>
<artifactId>maven-deploy-plugin</artifactId>
<version>2.8.2</version>
</plugin>
<!-- site lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#site_Lifecycle -->
<plugin>
<artifactId>maven-site-plugin</artifactId>
<version>3.7.1</version>
</plugin>
<plugin>
<artifactId>maven-project-info-reports-plugin</artifactId>
<version>3.0.0</version>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>
태그 설명
pom.xml에 대해 더 살펴보자.
- 그룹아이디는 org.example로 만들어졌다. 자동 생성된 트리 구조도 src\main\java 아래 org.example 패키지가 있고 그 밑에 App.java라는 소스가 있는 식이다.
- 아티팩트아이디는 프로그램이자 메인프로젝트명인 mymavenproject이다.
- version은 1.0.SNAPSHOT인데 스냅샷은 아직 개발이 진행중이라는 suffix이다.
- dependencies 안에는 아직 junit(유닛테스트) 만 있다.
(탬플릿으로 프로젝트가 생성될 때 junit 유닛테스트 소스 파일이 있어서, 메이브 레파지토리에서 해당 junit 라이브러리가 다운받아진 것이다) - build 태그 안에 pluginManagement 태그 안에 plugins 태그 안에는 여러 plugin 태그들이 이있다.
maven-compiler-plugin, maven-resources-plugin, maven-clean-plugin 등을 어떤 버전으로 사용하는지 보인다. - url은 기본 www.example.com으로 지정되었는데, 난 사용하지 않을 것이므로 지워버렸다.
[3] 기본 생성된 플러그인 및 레파지토리 상태 이해하기
IntelliJ IDEA 우측에는 Maven Pane이 보이는데, 그럼 내 메이븐 프로젝트에 딸린 plugin과 dependency를 쉽게 볼 수 있다.
lifecycle은 (clean부터 deploy까지) 모든 메이븐 프로젝트가 동일하게 가지고 있는 목록일 테다.
하지만, plugins과 dependencies는 프로젝트마다 다르다.
먼저 현재 상태를 살펴보자:
plugins에는 clean, compiler, deploy, install, jar, resources, site, surefire가 되어있다.
dependencies에는 일단 junit밖에 없으며 버전은 4.11이다.
그래서 로컬 레파지토리 폴더 안의 junit 폴더를 파고 들어가면 4.11이라는 폴더가 보인다.
또한, 로컬 레파지토리 폴더 안에는 dom4j, javax, log4j 등 꼭 junit 말고도 여러개의 라이브러리들이 있는데, 현 프로젝트에서 사용하는 플러그인과 디팬던시들 관련 라이브러리일 것이다.
pom파일에 적혀진 정보를 가지고 자동 다운로드 받아진 것이다.
그래서 설사 레파지토리 안에있는 폴더들이 강제로 다 지워지더라도 문제는 없다. pom.xml에 이미 필요 라이브러리들이 적혀져 있으니, 손쉽게 다 다시 다운받을 수 있다. (굳이 내가 필요한 것들을 기억해 두고 하나 하나 찾아가며 다운받을 필요는 없다는 것이다.)
*Generate Sources and Update Folders
*Downlaod Sources and/or Documentation
이 두 버튼을 이용하면 된다.
하지만, pom.xml이 반드시 제대로 작성되어있어야한다.
만약 레파지토리에서 junit 폴더를 지우고, junit을 다시 다운받아야 하는 상황이라고 생각해보자. 그런데, pom.xml에 junit 버전이 현재 존재하지도 않는 4.99라고 적혀져 있다면? 이 상태에서 소스를 다운받으라고 하면, 에러가 발생한다.
라이브러리가 있다면 쫄지 말기
다음은 assertEquals를 처음 사용하려 했을 때의 상황이다. (빨간 줄..)
(assertTrue는 AppTest.java가 처음 생길 때 부터 적혀져 있던 코드이고, assertTrue는 내가 처음 추가하려고 하는 상황)
아무리 라이브러리가 있다고 하더라도, 코드를 작성하다보면, 처음 사용하는 메소드의 경우 빨간줄이 뜰 수 있다. 이때는 당황하지 말고 아래 처럼 해주자.
- 빨간 전구를 누르기. (또는 컨트롤 + 1)
- 해당 메소드를 불러와주어야 하니 --> import static method라는 옵션을 선택 (라이브러리가 있기 때문에 이게 가능)
- method to import라고 무엇을 다운 받을 지 물어주는데 내가 정말 원하는 메소드를 선택하면 된다.
(내가 현재 쓰고자 하는 assertEquals가 org.junit 라이브러리 것인지, 혹시 동명의 다른 소속의 라이브러리에서 가져와야 하는 것은 아닌지 확인하는 과정)
폼파일 1차 업데이트 상황
아래는 초기 AppTest를 좀 더 발전시켜 본 상태이다.
junit의 AssertEquals 외에도 hamcrest의 assertThat등이 새로 import 되었다.
package org.example;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import org.junit.Before;
import org.junit.Test;
public class AppTest {
String apple;
String banana;
@Before
public void setup() {
apple = "APPLE";
banana = "BANANA";
}
@Test
public void shouldAnswerWithTrue() {
assertTrue(true);
assertEquals(apple, "APPLE");
assertThat(banana, equalTo("BANANA"));
}
}
[4] 폼파일은 계속 변경된다. 레파지토리도 따라서 계속 업데이트 된다.
소스코드를 작성하고 변경하다보면 필요 라이브러리를 계속 import하고 업데이트 하게 될 것이다.
junit도 초기 다운로드 버전은 4.11이지만 (자동생성된 AppTest.java를 보니, asserTrue만 썻더라) 만약 더 복잡하게 유닛테스트 코드를 작성하다보면 분명 상위버전 라이브러리 함수들이 필요할 것이다.
일단 내가 아무리 hamcrest의 asserThat 및 equalTo를 새로 가져다 썼지만 아직까지는 새로운 라이브러리가 필요 없었다.
어짜피 junit 버전 4.11안에서 해결 되기 때문에 추가 pom파일 수정이나 라이브러리 다운은 필요가 없었다.
하지만, mockito는 다르지!
새 라이브러리를 새로 다운받아야한다.
아래를 수행했다.
- 모키토를 사용해야 하므로, 임의로 Complicate라는 클래스를 새로 생성하였다.
이 클래스에 "abcde"를 리턴하는 func()이란 메소드를 만들어주었다. - 폼파일에 모키토 의존성을 적었다. (버전 1.9.5)
- 이후 우측 상단 Maven Pane을 열고, Generate Sources and Update Folder 및 Download Sources and/or Documentation을 수행 하여서 org.mockito 라이브러리가 다운받아지도록 했다.
그럼 아래 코드를 문제 없이 작동 시킬 수 있다.
package org.example;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import org.junit.Before;
import org.junit.Test;
public class AppTest {
String apple;
String banana;
@Before
public void setup() {
apple = "APPLE";
banana = "BANANA";
}
@Test
public void shouldAnswerWithTrue() {
assertTrue(true);
assertEquals(apple, "APPLE");
assertThat(banana, equalTo("BANANA"));
}
@Test
public void testMockito() {
Complicate cp = mock(Complicate.class);
when(cp.func()).thenReturn("qwerty");
assertThat(cp.func(), equalTo("qwerty"));
}
@Test
public void testNonMockito() {
Complicate cp = new Complicate();
assertThat(cp.func(), equalTo("abcde"));
}
}
다음 글에서는 서브모듈(프로젝트)를 생성하여 모듈-모듈간 의존성과 라이브러리 관리가 어떻게 되는지 확인해보자.