한쪽은 Spring 을 이용한 모듈 서비스를, 한쪽은 Spring 웹 프로젝트로 구성된 Gradle 멀티 모듈을 사용하려 한다. 그러기 위해선 다음의 과정을 해결해야 한다.
1) Spring Core를 쓰는 곳에 web 모듈을 끈다.
2) Spring Core 에서 Dependency 할 때 bootJar 가 실행되지 않도록 한다(jar 실행)
상세하게 알아보자
1) Spring Core를 쓰는 곳에 web 모듈을 끈다.
스프링은 별도 설정을 하지않으면 웹서버를 가정한다. 그래서 웹서비스를 꺼두지 않으면 실행할 때 다음의 에러가 발생한다.
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'webConfig': Invocation of init method failed; nested exception is org.springframework.context.ApplicationContextException: Unable to start web server; nested exception is org.springframework.context.ApplicationContextException: Unable to start ServletWebServerApplicationContext due to missing ServletWebServerFactory bean. ... Caused by: org.springframework.context.ApplicationContextException: Unable to start web server; nested exception is org.springframework.context.ApplicationContextException: Unable to start ServletWebServerApplicationContext due to missing ServletWebServerFactory bean. |
web server 가 이미 한쪽에서 실행되고 있는데, 같은게 실행되다 보니 ServletWebServerFactory Bean 이 생성에 실패한 것이다.
때문에 이럴때 SpringBoot 설정에 web을 꺼두면 된다. Spring core를 사용하는 쪽에 아래처럼 설정한다.
public static void main(String[] args) {
SpringApplicationBuilder application = new SpringApplicationBuilder(AppConfig.class);
application.web(WebApplicationType.NONE);
application.run();
}
2) Spring Core 에서 Dependency 할 때 bootJar 가 실행되지 않도록 한다(jar 실행)
이번엔 Gradle 를 설정한다.
멀티모듈을 사용할때 A라는 모듈이 B라는 모듈을 연결하려면 다음과 같이 설정되어 있어야 한다.
아래는 project-web 이라는 웹을 담당하는 모듈 안에 있는 build.gradle 의 한 부분이다.
project(':project-web') {
dependencies {
implementation project(':project-core')
}
}
위 설정은 project-web 이 project-core 를 의존하게 하는 설정이다. 혹시 멀티 모듈을 설정하는 법을 모른다면 아래 포스팅을 참고하자
https://lemontia.tistory.com/1013
이렇게 하고나면 project-web 을 build 또는 bootJar로 생성할 때 project-core 를 빌드한 다음 lib를 추가하여 jar 파일로 생성된다. 문제는 코딩할떄는 문제가 없던것이 bootJar 로 빌드할때 다음과 같은 문제가 발생한다
(project-core의 메인클래스가 com.test.gradle.MainSpring.java 이다. SpringBoot는 메인 클래스에서 실행이 가능)
error: package com.test.gradle does not exist
import com.test.gradle.MainSpring;
개발하는 동안 에러가 발생하지 않은 이유는 jar로 생성하지 않고 프로젝트 파일에 직접 접근되었기 때문이다. 문제는 project-core 가 build 되면서 다음과 같은 구조로 빌드된다는 것이다.
BOOT-INF 라는 폴더 안에 classes 폴더가 생성되고 컴파일되다보니 코드상에 있던 com.test.gradle 이라는 경로가 참조가 되지 않는 것이다. 위와 같은 build는 사실상 bootJar 로 생성된, 즉 스프링부트를 실행하기 위해 생성된 jar다. 문제는 project-web 은 이렇게 되는게 맞지만 project-core는 일반 jar처럼 되어있어야 한다는 것이다.
이 문제를 해결하기 위해 2가지를 해야한다
1) project-core 에서 jar를 생성할때 bootJar 가 아닌 jar 명령어를 이용해 생성되어야 한다.
2) jar 파일이 생성될 때 관련된 lib 파일이 모두 같이 묶여야 한다.(의존성 문제)
그러기 위해선 jar 명령어를 수정할 필요가 있다.
다음을 살펴보자.
project-core 안에 있는 build.gradle 파일이다
...
jar {
// bootJar 대신에 jar 를 실행하도록 한다(일반 jar처럼 묶임)
enabled = true
archiveFileName = '[생성될 jar파일명].jar'
// manifest를 지정한다
manifest {
attributes 'Main-Class': 'com.test.gradle.AppStart'
}
// project-core 에 필요한 라이브러리들을 모두 포함해서 패키징한다
from {
configurations.runtimeClasspath.collect {
it.isDirectory() ? it : zipTree(it)
}
}
// build 중에 중복되는 파일이 생성될경우 에러가 발생한다. 그것을 방지하기 위한 설정이다.
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
}
...
참고로 라이브러리를 포함시켜 패키징하는 부분을 다른 포스팅을 보면 다음과 같이 설정되 있을 수 있다.
...
jar {
from {
configurations.compile.collect {
it.isDirectory() ? it : zipTree(it)
}
}
...
위 방법은 gradle 버전에 따라 다른거 같다. 내 경우 사용하는 gradle 버전은 7.1 이다. 환경에 맞는 걸 사용하면 되겠다.
그리고 중복파일 무시하는 설정을 넣었는데, 만약 그걸 넣지않으면 다음과 같은 에러를 볼 수도 있다.
Execution failed for task ':jar'.
> Entry META-INF/LICENSE.txt is a duplicate but no duplicate handling strategy has been set. Please refer to https://docs.gradle.org/7.1/dsl/org.gradle.api.tasks.Copy.html#org.gradle.api.tasks.Copy:duplicatesStrategy for details.
* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. Run with --scan to get full insights.
내용을 보면 META-INF/LICENSE.txt 파일이 중복되서 발생한 에러다. 중요하지 않은 파일의 중복 에러라 제외하도록 설정했다.
이제 project-web 에서 bootJar 를 하면 does not exist 에러가 나지 않을 것이다. 생성된 project-core 의 jar파일을 확인해봐도 이전과 구조가 달라진 점을 확인할 수 있다.
참고로 project-core 에서 task 실행 시, bootJar 로 실행하면 안된다. jar 로 실행해야 한다. 조치를 해두지 않으면 다음 사람은 bootJar를 실행할 수도 있으니 반드시 설정 & 주석을 추가해주자
bootJar {
// jar를 수행해야 원하는 jar 파일이 나옵니다
enabled = false
}
생성된 jar 파일의 압축을 풀어보자.
위 구조와 비교하면 달라진 것을 확인할 수 있다.(com.test.gradle 경로도 바로 보인다)
끝.
'공부 > 프로그래밍' 카테고리의 다른 글
[log4j] 로그레벨 package 별로 설정하기 (0) | 2021.12.03 |
---|---|
[JPA] 쿼리 로깅에 p6spy multi line 적용하기 (0) | 2021.11.05 |
[gradle] intellij 에서 gradle을 이용해 runnable jar 만들기 (1) | 2021.10.22 |
[java] JNA를 이용해 공유 라이브러리 파일(.so 파일) 읽기 (0) | 2021.10.15 |
[jenkins] webhook 사용 자동빌딩(배포) 시, 특정 브런치명만 실행하기(CI/CD 옵션) (0) | 2021.10.13 |
댓글