본문 바로가기
공부/프로그래밍

[gradle] 멀티모듈에서 spring 프로젝트 -> spring 프로젝트 의존성 연결하기

by demonic_ 2021. 10. 27.
반응형

 

한쪽은 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

 

[gradle, springboot] multi project 설정하기

프로젝트가 늘다보니 공통으로 쓰이는 것이 생겼고, 그러다보니 모듈을 통합+재활용 용이하게 하는 방법을 찾던 중 gradle을 이용한 multi project 설정이 좋겠다 싶어 선택했다. 다만 이건 설정뿐만

lemontia.tistory.com

 

이렇게 하고나면 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 경로도 바로 보인다)

 

 

끝.

반응형

댓글