티스토리 뷰

요즘은 하나의 애플리케이션이라고 해도 다양한 형태로 서비스 됩니다. 간단한 쇼핑몰이라고 해도 웹 이외 모바일도 서비스해야 하고 외부 API를 제공해야 하고 관리자 사이트가 필요하며 배치 시스템 등도 만들어야 합니다. 서비스를 효율적으로 관리하고 재사용하기 위해서는 프로젝트를 멀티 모듈로 구성하는 것이 좋습니다. 

서비스를 모듈화 할 때 gradle 또는 maven을 많이 이용하는데 그동안은 gradle을 많이 이용했는데 maven으로 해야 할 필요가 생겨 이번 기회에 정리하였습니다. 

 

1. 목표 

메이븐을 이용해서 멀티 모듈 프로젝트 구축하기 

 

2. 환경  

  • spring boot 2.5.5
  • java 1.8
  • eclipse

3. 프로젝트 구성

전체 구성은 3개의 레이어로 나뉘어 집니다. 

공통으로 사용하는 라이브러리, 고객, 주문, 상품, 공지사항 등의 서비스 라이브러리 또 각 서비스 라이브러리를 이용해서 외부에 제공하는 서비스 라이브러리로 나누었습니다. 

명칭을 외부에서 서비스로 이용되는 것은 컴포넌트라 칭하고 내부에서 이용하는 라이브러리는 서비스 모듈, 공통은 공통 모듈로 명명했습니다. 

컴포넌트는 웹, 모바일, 배치 시스템, 외부 API 등으로 인스턴스로 서비스됩니다. 

서비스 모듈은 기능에 따라, 도메인에 따라 또는 독립적인 특성에 따라 추가 및 관리할 수 있습니다. 

공통 모듈은 도메인이나 특정 서비스가 아닌 전체에서 사용할 수 있는 공통적인 도메인 객체나, 유틸리티 등을 포함합니다.  

기본 형태를 3단으로 했지만 필요해 따라 세분화할 수 있습니다. 컴포넌트들을 개발하다 보면 중복적인 기능들이 많습니다. 이때는 공통 컴포넌트를 만들고 디펜던시를 잡아주면 됩니다. 마찬가지로 서비스 모듈에서도 공통 서비스를 만들어서 관리하면 됩니다. 

대략적인 구성 

4. 개발 flow 

  • Base 프로젝트 생성 : 전체 모듈을 포함하는 메이븐 프로젝트로 base 프로젝트를 생성합니다. 이때 base 프로젝트의 pom.xml에 packaging을 "pom"으로 해야 합니다. 
  • 모듈 프로젝트 생성 : base 프로젝트에 모듈 프로젝트들을 생성합니다. 모듈은 모두 packaging을 "jar"로 설정합니다. 
  • 디펜던시 설정 : 서비스 모듈은 공통 모듈을 디펜던시로 잡아 주고 컴포넌트는 사용하는 서비스를 모듈을 디펜던시로 관계를 만들어 줍니다. 
  • 빌드설정 : 컴포넌트들에는 pom.xml에 빌드를 설정해 줍니다. 
  • 테스트 : 각 컴포넌트를 실행해보고 정상이면 jar 파일로 패키지가 정상적으로 생성 되는지 테스트합니다. 만들어진 jar 파일을 임시 폴더 압축을 풀어서 정상적으로 패키지 되었는지 확인해 보는게 좋습니다. 여기까지 잘 되었다면 터미널에서 jar 파일을 실행해 보면 됩니다. 

5. Base 프로젝트 생성

maven project 생성 

Parent Project 설정 등은 프로젝트 생성 후에 수정하셔 됩니다.  위에 캡처에는 설정이 있지만 Base project의 role은 모듈 정의로 보시면 됩니다. 

생성 후 pom.xml에서 packagine을 pom으로 설정해 줍니다. 

Gradle에서 settings.gradle 떠올리면 됩니다. 

<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>
    <name>eblo parent</name>
    <description>eblo paretn</description>
    <packaging>pom</packaging>
    <groupId>eblo</groupId>
    <artifactId>eblo-parent</artifactId>
    <version>0.0.1-SNAPSHOT</version>
</project>

6. 공통 모듈 생성 

Base 프로젝트 선택 하고 메이븐 모듈을 생성합니다. 모듈은 수작업으로 생성하셔도 됩니다. 

예제 : eblo-common 모듈 생성 

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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.5.5</version>
        <relativePath/>
    </parent>
    <name>eblo-common</name>
    <packaging>jar</packaging>
    <groupId>eblo</groupId>
    <artifactId>eblo-common</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <properties>
        <java.version>1.8</java.version>
        <downloadSources>true</downloadSources>
        <downloadJavadocs>true</downloadJavadocs>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter-logging</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.16</version>
            <scope>provided</scope>
        </dependency>  
    </dependencies>
</project>

기본으로 생성한 pom.xml에 packaging이랑 해당 프로젝트의 디펜던시들을 추가해 줍니다. 

 

7.  서비스 모듈 생성 

공통 모듈 생성하는 거와 특별히 다른 내용은 없습니다. 그냥 서비스 모듈명만 잘 입력하고 생성하면 됩니다. 

예제 : eblo-module-notice 생성 

<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>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.5.5</version>
        <relativePath/>
    </parent>
    <packaging>jar</packaging>
    <groupId>eblo</groupId>
    <artifactId>eblo-module-notice</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <properties>
        <java.version>1.8</java.version>
        <downloadSources>true</downloadSources>
        <downloadJavadocs>true</downloadJavadocs>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
    </properties>
    <dependencies>
        <dependency>
            <groupId>eblo</groupId>
            <artifactId>eblo-common</artifactId>
            <version>${project.version}</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.16</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
    </dependencies>
</project>

모듈 간의 관계는 디펜던시 설정으로 잡아 주면 됩니다. 공통모듈에 디펜던시를 잡았다고 하더라도 scope이 provided, test는 전이성이 없기 때문에 각 프로젝트마다 설정을 해줘야 합니다. 중요!!!

 

eblo-module-customer 생성 및 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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.5.5</version>
        <relativePath/>
    </parent>
    <packaging>jar</packaging>
    <groupId>eblo</groupId>
    <artifactId>eblo-module-customer</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <properties>
        <java.version>1.8</java.version>
        <downloadSources>true</downloadSources>
        <downloadJavadocs>true</downloadJavadocs>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
    </properties>
    <dependencies>
        <dependency>
            <groupId>eblo</groupId>
            <artifactId>eblo-common</artifactId>
            <version>${project.version}</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.16</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
    </dependencies>
</project>

8. 컴포넌트 모듈 생성 

이클립스에서 메이븐 모듈 프로젝트 생성은 동일하기 때문에 공통 모듈 생성하는 거 참고하셔서 생성하면 됩니다.

eblo-component-web 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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.5.5</version>
        <relativePath/>
    </parent>
    <name>eblo-component-web</name>
    <description>eblo-component-web</description>
    <packaging>jar</packaging>
    <groupId>eblo</groupId>
    <artifactId>eblo-component-web</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <properties>
        <java.version>1.8</java.version>
        <downloadSources>true</downloadSources>
        <downloadJavadocs>true</downloadJavadocs>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
    </properties>
    <dependencies>
        <dependency>
            <groupId>eblo</groupId>
            <artifactId>eblo-module-notice</artifactId>
            <version>${project.version}</version>
        </dependency>
        <dependency>
            <groupId>eblo</groupId>
            <artifactId>eblo-module-customer</artifactId>
            <version>${project.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-tomcat</artifactId>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.16</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
    </dependencies>
    <build>
        <finalName>eblo-component-web-${project.version}</finalName>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

컴포넌트 모듈에서는 사용하는 서비스 모듈을 선별적으로 디펜던시를 잡아 주시고 <build> 추가해 주셔야합니다.

해당 되는 부분은 executableJar를 만들기 위한 플러그인입니다. 프로젝트가 단순 라이브러리라면 해당 부분은 제거되어야 합니다.  

빌드에 적절한 이름을 넣어 주시고 빌드(install) 하면 해당 이름의 jar 파일이 생성됩니다. 이때 아무것도 없는 상태에서 maven install 하면 실행 파일이 없다는 메시지가 나옵니다. 당연한 거겠죠. 스프링 프로젝트의 메인 클래스를 추가해 주시고 테스트하세요. 

package eblo.web;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class WebApplication {

	public static void main(String[] args) {
		SpringApplication.run(WebApplication.class, args);
	}
}
package eblo.web;


import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;

public class ServletInitializer extends SpringBootServletInitializer {

	@Override
	protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
		return application.sources(WebApplication.class);
	}

}

9. Base 프로젝트의 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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <name>eblo parent</name>
    <description>eblo paretn</description>
    <packaging>pom</packaging>
    <groupId>eblo</groupId>
    <artifactId>eblo-parent</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <modules>
        <module>eblo-common</module>
        <module>eblo-module-notice</module>
        <module>eblo-module-customer</module>
        <module>eblo-component-web</module>
        <module>eblo-component-mobile</module>
    </modules>
</project>

Module이 추가된 것 확인하면 됩니다. 

 

10. 전체 빌드하기 

테스트를 제외하고 싶다면 "mvn clean install -Dmaven.test.skip=true" 로 테스트하세요.  

[INFO] Reactor Summary for eblo parent 0.0.1-SNAPSHOT:
[INFO] 
[INFO] eblo parent ........................................ SUCCESS [  0.245 s]
[INFO] eblo-common ........................................ SUCCESS [  0.855 s]
[INFO] eblo-module-notice ................................. SUCCESS [  0.069 s]
[INFO] eblo-module-customer ............................... SUCCESS [  0.090 s]
[INFO] eblo-component-web ................................. SUCCESS [  1.175 s]
[INFO] eblo-component-mobile .............................. SUCCESS [  0.397 s]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  3.029 s
[INFO] Finished at: 2021-10-13T11:42:24+09:00
[INFO] ------------------------------------------------------------------------

10. 컴포넌트 하나만 빌드하기

서비스 배포 등을 할때는 해당 컴포넌트만 빌드합니다. 젠킨스에서 배포 관리를 할때 프로젝트 별 빌드할 때 필요합니다. 

[INFO] ------------------------------------------------------------------------
[INFO] Reactor Summary for eblo parent 0.0.1-SNAPSHOT:
[INFO] 
[INFO] eblo parent ........................................ SUCCESS [  0.239 s]
[INFO] eblo-common ........................................ SUCCESS [  0.738 s]
[INFO] eblo-module-notice ................................. SUCCESS [  0.054 s]
[INFO] eblo-module-customer ............................... SUCCESS [  0.047 s]
[INFO] eblo-component-web ................................. SUCCESS [  1.096 s]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  2.361 s
[INFO] Finished at: 2021-10-13T11:44:20+09:00
[INFO] ------------------------------------------------------------------------

11. 메이븐 빌드 옵션 

-rf, --resume-from 
특정 프로젝트만 제외하고 싶은 경우, "-rf 프로젝트명"

-pl, --projects
특정 프로젝트만 실행하고 싶은 경우, "-pl 프로젝트명" 

-am, --also-make
대상 프로젝트와 그 프로젝트에 필요한 프로젝트도 함께 빌드.
위의 예제에서 eblo-component-web을 빌드하면 
eblo-component-web, eblo-module-customer, eblo-module-notice, eblo-common
세개가 함께 빌드가 됩니다. 

-amd, --also-make-dependents
대상 프로젝트와 그 프로젝트를 사용하는 프로젝트도 함께 빌드합니다. 
위의 예제에서 eblo-component-web을 빌드하면 eblo-modudule-customer를 
사용하는 프로젝트들이 함께 빌드됩니다.
eblo-component-web, eblo-component-mobile, eblo-module-customer

따라서 특정 프로젝트만 빌드하고 싶다면 아래처럼 실행하면 됩니다. 
"mvn clean install -pl 프로젝트명 -am"
더불어 테스트를 제외 하고 싶다면 "-Dmaven.test.skip=true" 이 옵션을 추가하면 됩니다.

12. 메이븐 디펜던시 설정 시 고려사항. 

디펜던시 설정 시 가장 신경 써야 하는 부분은 범위와 전이성(Scope and Transitivity) 입니다. 

범위를 어떻게 지정하느냐에 따라 전이성이 다릅니다. 

d1에  d2, d3 이 디펜던시로 설정이 되어 있는 경우 d1을 디펜던시에 추가해 주면 d2, d3가 자동으로 추가가 됩니다. 

그런데 scope이 provided, test는 전이성없기 때문에 프로젝트 마다 설정을 해줘야 합니다. 

그외 자동으로 추가가 되기 때문 버전이  안맞는 경우에는 제외 시켜줘야 하는 경우도 있습니다. 

1. Compile
스코프를 설정하지 않으면 기본적으로 compile로 설정 됩니다. 

2. Provided
provided dependencies는 컴파일과 테스트 시점에 유효합니다. 전이성 없음. 

3. Runtime
컴파일할 때는 필요없고 runtime에서만 필요한 디펜던시입니다. 

4. Test
테스트 시점에 필요, 전이성 없음. 

5. System
deprecated, 시스템이 제공하는 특정 jar를 직접 가리켜서 사용하는 것으로 대표적인게 ojdbc입니다. 
public repository에 없기 때문에 시스템 안쪽에 라이브러리를 가져다 놓고 참조하게 합니다. 
문제는 systemPath가 절대 경로로 설정이 되어야 해서 시스템이 다른 경우 개발서버, 운영서버 등 문제가 발생할 수 있습니다. 

6. Import
Maven 2.0.9에서 추가된 속성이고 디펜던시 타입이 pom인 경우에만 사용 가능합니다.

13. 테스트 

빌드까지 무사히 완료 되었다면 빌드 된 파일 압축 풀고 확인한번 해보시고 해당 jar 파일을 터미널에서 실행해 보는게 좋습니다. 

 

14. 마무리 

지금까지 기본적인 프로젝트 구성만 마무리한 것입니다. profile 설정, .gitignore 등 설정, git 연결 등을 하고 프로젝트 진행하면 됩니다. 

메이븐은 2013년에 써보고 그 후에는 그레들만 썼는데 오래간만에 메이븐 써보니 기억이 가물 가물하네요^^ 

다음번에는 그레들로 구성하는 예제를 만들어 보도록 하겠습니다.

추가적으로 어설프게 build, plugin 등을 설정하면 에러가 엄청납니다. 추가적인 설정을 하고 싶다면 에러 안나는 상태까지 만들어 넣고 조금씩 추가해 보는게 좋습니다.  

 

Github 링크 

 

GitHub - kkaok/multi-module-maven-example

Contribute to kkaok/multi-module-maven-example development by creating an account on GitHub.

github.com

 

댓글
댓글쓰기 폼