들어가며
1월 2일 첫 회사에 입사하고 저는 처음 접하는 스택들에 둘러쌓이게 되었습니다.
그렇게 처음 접하게 된 개념 중 하나가 바로 멀티 모듈이었습니다.
사실 MSA에 대해서 학습할 때 키워드로만 접해봤습니다만, 본격적으로 이를 학습하고 구축해보는 것은 처음이었는데요.
이 과정에서 알게된 것들과 겪었던 오류 사례들을 정리해보는 것이 이 글의 목적입니다.
멀티모듈이 무엇인가?
모놀리식 Monolithic, 마이크로 서비스 MicroService Archtecture 그리고 멀티모듈..?
처음 개념을 접하며 가장 먼저 들었던 질문은 "MSA와 멀티모듈이 무슨 차이점이 있고 왜 같이 많이 언급되는가?" 하는 것이었는데요. 그래서 먼저 이 질문에 대한 답변을 짚고 넘어가려고합니다.
결론적으로 모놀리식과 마이크로서비스 개념은 반대 개념이지만, 멀티모듈은 다른 계층의 개념으로 하나의 프로젝트 내부의 설계 방식입니다.
어플리케이션 아키텍처에 대한 개념이 없을 때 우리가 보통 구축하게 되는 어플리케이션의 모양은 모놀리식 Monolithic 아키텍처입니다.
... the communication in Monolithic applications are inter-process communication. So that means it is working on single process that invoke one to another by using method calls. You just create class and call the method inside of target module. All running the same process.
모놀리식 애플리케이션의 통신은 프로세스 내부의 통신입니다. 즉, 메서드 호출을 사용하여 다른 프로세스를 호출하는 단일 프로세스에서 작동한다는 의미입니다. 클래스를 생성하고 대상 모듈 내부에서 메서드를 호출하기만 하면 됩니다. 모두 동일한 프로세스를 실행합니다.
- Monolithic Architecture Is Still Worth at 2021?
소프트웨어의 모든 구성요소가 한 프로젝트에 통합되어 있어 빠르게 구축하기 좋고, 디버깅과 배포에도 편리합니다. 소규모 어플리케이션을 구축할 때 여전히 유효한 설계입니다. 특히 트래픽이 그지 크지 않고 앞으로도 크지 않다면 네트워크 리소스를 상대적으로 덜 사용하는 모놀리식을 선택하는 것이 최선일 수 있습니다.
다만 단점으로는 시간이 지나고 서비스 규모가 점차 커지면 코드를 관리하기 어려워집니다. 새로운 기술을 활용하고 기능을 구현하거나 개선할 때 고려해야하는 것이 많다는 단점이 있습니다.
[ 아래 : 모놀리식 아키텍처의 발전과정 ]
앞서 언급한 모놀리식 아키텍처의 단점으로 인해 좀 더 확장가능성 있고, 유연하게 리팩터링을 할 수 있는 개발 아키텍처로 많이 선택되는 것이 마이크로 서비스 아키텍처 MSA입니다.
각 서비스를 모듈화하여 커다란 소프트웨어 기능을 작게 쪼개어 제공하는 방식인데요. 이들이 각자 개별적인 작업을 담당하며 독립적으로 움직이고, API를 통해 다른 서비스와 통신합니다.
각 기능의 코드가 물리적으로 많지 않기 때문에 다른 개발자가 읽기에 어렵지 않고, 단일 모듈의 장애가 전체 어플리케이션에는 크게 영향을 받지 않는 장점이 있습니다. 특 각 모듈별로 새로운 기술을 적용하고 리팩터링하는 데에 유연합니다. 필요에 의해 일부 모듈에 리소스를 더 할당할 수 있기도 하고요.
다만 구축이 상대적으로 복잡하여 테스트,디버깅이나 트랜잭션 관리가 어렵습니다. 그리고 네트워크 비용이 발생하는 등의 단점이 있습니다.
왜 멀티모듈과 MSA는 같이 언급되는 것일까?
이부분을 학습할 때는 우아한테크세미나 [우아한 멀티모듈] 을 많이 참고했습니다.
(작성중....)
겪었던 에러 상황들과 고민했던 것들
처음 멀티모듈을 구성할 때 튜토리얼은 Gradle 공식문서와 멀티 모듈 적용하기 with Gradle, SpringBoot + Kotlin 멀티 모듈 구성 - 단일모듈에서 멀티모듈로 변경해보기 를 많이 참고했습니다.
그리고 구축과정, 테스트와 run해보며 겪었던 오류에 대해 시간순서대로 정리하였습니다.
개발 환경
Springboot, Gradle, Kotlin
파일 구조를 잘 못 설정했을 때 `Execution failed for task ':bootRun'.`
- 에러코드
처음 모듈을 세팅하고 하위 서브 모듈을 설정했을 때 발생한 예외코드입니다.
Execution failed for task ':bootRun'.
> Failed to calculate the value of task ':bootRun' property 'mainClass'.
> Main class name has not been configured and it could not be resolved
* 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.
> Get more help at https://help.gradle.org.
Deprecated Gradle features were used in this build, making it incompatible with Gradle 9.0.
You can use '--warning-mode all' to show the individual deprecation warnings and determine if they come from your own scripts or plugins.
For more on this, please refer to https://docs.gradle.org/8.4/userguide/command_line_interface.html#sec:command_line_warnings in the Gradle documentation.
BUILD FAILED in 1s
3 actionable tasks: 2 executed, 1 up-to-date
- 원인
프로젝트 파일 구성에서 root module에 src가 남아있기 때문이었습니다.
├── buildSrc
│ ...
├── api
│ ├── src
│ │ └──...
│ └── build.gradle
├── services
│ └── person-service
│ ├── src
│ │ └──...
│ └── build.gradle
├── shared
│ ├── src
│ │ └──...
│ └── build.gradle
├── src # 이부분이 있어서....
└── settings.gradle
- 해결
root module에 있는 src 파일을 삭제해 주었습니다.
`build.gradle`dependency configuration 시 `api`, `implementation`의 차이점은 무엇일까?
// build.gadle.kts
dependencies {
implementation("org.hibernate:hibernate-core:3.6.7.Final")
}
공식 문서에 따르면 이런 차이점이 있습니다.
- `implementation`
- 프로젝트에 의해 노출된 API의 일부가 아닌 프로젝트 프로덕션 소스를 컴파일하는데 필요한 종속성
- `api`
- 프로젝트에 의해 노출된 API의 일부인 프로젝트의 프로덕션 소스를 컴파일 하는 데 필요한 종속성
- `testImplementation`
- 프로젝트의 테스트 소스를 컴파일하고 실행하는 데 필요한 종속성
가장 큰 차이점은 `api`의 경우 의존성 받는 모듈의 하위 의존성까지 참조할 수 있게 됩니다.
단 이 경우 의존성의 전파로 상위 모듈 오염이 발생할 수 있는 가능성이 늘어나므로 가급적 지양하는 것이 좋습니다. (물론 정답이라기 보다 한 관점임) [참고 링크]
`org.junit.platform.commons.JUnitException` 발생시
Spock api test를 작성하고 실행시켰을 때 다음과 같은 에러가 발생하였습니다.
- 에러코드
/Users/berapt/Library/Java/JavaVirtualMachines/azul-17.0.9/Contents/Home/bin/java -ea -Didea.test.cyclic.buffer.size=1048576 -javaagent:/Applications/IntelliJ IDEA.app/Contents/lib/idea_rt.jar=65269:/Applications/IntelliJ IDEA.app/Contents/bin -Dfile.encoding=UTF-8 -classpath /Applications/IntelliJ IDEA.app/Contents/lib/idea_rt.jar:/Applications/IntelliJ IDEA.app/Contents/plugins/junit/lib/junit5-rt.jar:/Applications/IntelliJ IDEA.app/Contents/plugins/junit/lib/junit-rt.jar:/Users/berapt/Documents/GitHub/backend-ojt/app/out/test/classes:/Users/berapt/Documents/GitHub/backend-ojt/app/out/test/resources:/Users/berapt/Documents/GitHub/backend-ojt/app/out/production/classes:/Users/berapt/Documents/GitHub/backend-ojt/app/out/production/resources:/Users/berapt/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-reflect/1.9.20/34eeb4eed5f493cdbb760ef50a5653ec414006bf/kotlin-reflect-1.9.20.jar:/Users/berapt/Documents/GitHub/backend-ojt/domain/command/out/production/classes:/Users/berapt/Documents/GitHub/backend-ojt/domain/command/out/production/resources:/Users/berapt/Documents/GitHub/backend-ojt/domain/query/out/production/classes:/Users/berapt/Documents/GitHub/backend-ojt/domain/query/out/production/resources:/Users/berapt/Documents/GitHub/backend-ojt/domain/common/out/production/classes:/Users/berapt/Documents/GitHub/backend-ojt/infrastructure/out/production/classes:/Users/berapt/Documents/GitHub/backend-ojt/infrastructure/out/production/resources:/Users/berapt/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/1.9.20/e58b4816ac517e9cc5df1db051120c63d4cde669/kotlin-stdlib-1.9.20.jar:/Users/berapt/.gradle/caches/modules-2/files-2.1/jakarta.inject/jakarta.inject-api/2.0.1/4c28afe1991a941d7702fe1362c365f0a8641d1e/jakarta.inject-api-2.0.1.jar:/Users/berapt/.gradle/caches/modules-2/files-2.1/org.springframework.boot/spring-boot-starter-webflux/2.6.8/746f6f3e5df1aa38846b5283e721734b6132089b/spring-boot-starter-webflux-2.6.8.jar:/Users/berapt/.gradle/caches/modules-2/files-2.1/org.springframework.boot/spring-boot-starter-test/2.6.8/fc0744a41a4366a1507b4137ebc8f44db92d0847/spring-boot-starter-test-2.6.8.jar:/Users/berapt/.gradle/caches/modules-2/files-2.1/org.spockframework/spock-spring/2.2-M1-groovy-4.0/fd3b978ab984e81333afd7af939556779e3d29ec/spock-spring-2.2-M1-groovy-4.0.jar:/Users/berapt/.gradle/caches/modules-2/files-2.1/org.spockframework/spock-core/2.2-M1-groovy-4.0/3b001b8365c2703fd2dfb509da22b63144dbe3f0/spock-core-2.2-M1-groovy-4.0.jar:/Users/berapt/.gradle/caches/modules-2/files-2.1/io.kotest/kotest-runner-junit5-jvm/5.7.2/ac0bf80b9970b7c5b835454acb80f4bc07b378e5/kotest-runner-junit5-jvm-5.7.2.jar:/Users/berapt/.gradle/caches/modules-2/files-2.1/io.kotest/kotest-assertions-core-jvm/5.7.2/b684e98ebc3ead2ad779e78ebb00c52fccc3709e/kotest-assertions-core-jvm-5.7.2.jar:/Users/berapt/.gradle/caches/modules-2/files-2.1/io.kotest/kotest-property-jvm/5.7.2/f3daf8de510335662205f12df4243aa2335728f9/kotest-property-jvm-5.7.2.jar:/Users/berapt/.gradle/caches/modules-2/files-2.1/org.jetbrains/annotations/13.0/919f0dfe192fb4e063e7dacadee7f8bb9a2672a9/annotations-13.0.jar:/Users/berapt/.gradle/caches/modules-2/files-2.1/org.springframework.boot/spring-boot-starter-json/2.6.8/ea3311052a96a1bf0f975334cda285769d40a6b/spring-boot-starter-json-2.6.8.jar:/Users/berapt/.gradle/caches/modules-2/files-2.1/org.springframework.boot/spring-boot-starter/2.6.8/d6ad2d708e9bf7ebc71f85e0b001a68d591ba2d0/spring-boot-starter-2.6.8.jar:/Users/berapt/.gradle/caches/modules-2/files-2.1/org.springframework.boot/spring-boot-starter-reactor-netty/2.6.8/647038dc085a4006b624841b71682a9be472a2b/spring-boot-starter-reactor-netty-2.6.8.jar:/Users/berapt/.gradle/caches/modules-2/files-2.1/org.springframework/spring-webflux/5.3.20/4f93011f4c62be81463fe4d944c115989d465465/spring-webflux-5.3.20.jar:/Users/berapt/.gradle/caches/modules-2/files-2.1/org.springframework/spring-web/5.3.20/3c2fe9363760d62d5b7c9f087bb4255e3377a0b2/spring-web-5.3.20.jar:/Users/berapt/.gradle/caches/modules-2/files-2.1/org.springframework.boot/spring-boot-test-autoconfigure/2.6.8/da8747b50d70ad643527c845044266cf851205d9/spring-boot-test-autoconfigure-2.6.8.jar:/Users/berapt/.gradle/caches/modules-2/files-2.1/org.springframework.boot/spring-boot-test/2.6.8/7607c8f01a4b9e51697276304ab1bebec0956113/spring-boot-test-2.6.8.jar:/Users/berapt/.gradle/caches/modules-2/files-2.1/com.jayway.jsonpath/json-path/2.6.0/67f565b424f7903a12d4f5b9361b11462ecacdac/json-path-2.6.0.jar:/Users/berapt/.gradle/caches/modules-2/files-2.1/jakarta.xml.bind/jakarta.xml.bind-api/2.3.3/48e3b9cfc10752fba3521d6511f4165bea951801/jakarta.xml.bind-api-2.3.3.jar:/Users/berapt/.gradle/caches/modules-2/files-2.1/org.assertj/assertj-core/3.21.0/27a14d6d22c4e3d58f799fb2a5ca8eaf53e6942a/assertj-core-3.21.0.jar:/Users/berapt/.gradle/caches/modules-2/files-2.1/org.hamcrest/hamcrest/2.2/1820c0968dba3a11a1b30669bb1f01978a91dedc/hamcrest-2.2.jar:/Users/berapt/.gradle/caches/modules-2/files-2.1/org.junit.jupiter/junit-jupiter/5.8.2/5a817b1e63f1217e5c586090c45e681281f097ad/junit-jupiter-5.8.2.jar:/Users/berapt/.gradle/caches/modules-2/files-2.1/org.mockito/mockito-junit-jupiter/4.0.0/b76de25bd6e5d8f7924d0536729c0076e37e9396/mockito-junit-jupiter-4.0.0.jar:/Users/berapt/.gradle/caches/modules-2/files-2.1/org.mockito/mockito-core/4.0.0/f5195e0c4a45716bbd2d1d29173adbd148acce3a/mockito-core-4.0.0.jar:/Users/berapt/.gradle/caches/modules-2/files-2.1/org.skyscreamer/jsonassert/1.5.0/6c9d5fe2f59da598d9aefc1cfc6528ff3cf32df3/jsonassert-1.5.0.jar:/Users/berapt/.gradle/caches/modules-2/files-2.1/org.springframework/spring-test/5.3.20/33a92d5066fb810023969a0d70fac96387962769/spring-test-5.3.20.jar:/Users/berapt/.gradle/caches/modules-2/files-2.1/org.springframework/spring-core/5.3.20/4b88aa3c401ede3d6c8ac78ea0c646cf326ec24b/spring-core-5.3.20.jar:/Users/berapt/.gradle/caches/modules-2/files-2.1/org.xmlunit/xmlunit-core/2.8.4/35be57989ca80eefa03161b211630e319a8f36c6/xmlunit-core-2.8.4.jar:/Users/berapt/.gradle/caches/modules-2/files-2.1/org.junit.platform/junit-platform-engine/1.8.2/b737de09f19864bd136805c84df7999a142fec29/junit-platform-engine-1.8.2.jar:/Users/berapt/.gradle/caches/modules-2/files-2.1/org.apache.groovy/groovy/4.0.0/6856ea9e77c50c52429d996a9ce194db8497e682/groovy-4.0.0.jar:/Users/berapt/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk8/1.9.20/e2b4d1f475ae0606d063a84fce4dccdb45c7e12a/kotlin-stdlib-jdk8-1.9.20.jar:/Users/berapt/.gradle/caches/modules-2/files-2.1/org.junit.platform/junit-platform-launcher/1.8.2/c334fcee82b81311ab5c426ec2d52d467c8d0b28/junit-platform-launcher-1.8.2.jar:/Users/berapt/.gradle/caches/modules-2/files-2.1/org.junit.platform/junit-platform-suite-api/1.8.2/a148ffd359cac121fcba000ad7f5a75b5e2ac2b4/junit-platform-suite-api-1.8.2.jar:/Users/berapt/.gradle/caches/modules-2/files-2.1/org.junit.jupiter/junit-jupiter-api/5.8.2/4c21029217adf07e4c0d0c5e192b6bf610c94bdc/junit-jupiter-api-5.8.2.jar:/Users/berapt/.gradle/caches/modules-2/files-2.1/com.github.curious-odd-man/rgxgen/1.4/8d5947bd00bd8e12313c56b5e6f5f9f2f0e34433/rgxgen-1.4.jar:/Users/berapt/.gradle/caches/modules-2/files-2.1/com.fasterxml.jackson.datatype/jackson-datatype-jsr310/2.13.3/ad2f4c61aeb9e2a8bb5e4a3ed782cfddec52d972/jackson-datatype-jsr310-2.13.3.jar:/Users/berapt/.gradle/caches/modules-2/files-2.1/com.fasterxml.jackson.module/jackson-module-parameter-names/2.13.3/f71c4ecc1a403787c963f68bc619b78ce1d2687b/jackson-module-parameter-names-2.13.3.jar:/Users/berapt/.gradle/caches/modules-2/files-2.1/com.fasterxml.jackson.datatype/jackson-datatype-jdk8/2.13.3/d4884595d5aab5babdb00ddbd693b8fd36b5ec3c/jackson-datatype-jdk8-2.13.3.jar:/Users/berapt/.gradle/caches/modules-2/files-2.1/com.fasterxml.jackson.core/jackson-databind/2.13.3/56deb9ea2c93a7a556b3afbedd616d342963464e/jackson-databind-2.13.3.jar:/Users/berapt/.gradle/caches/modules-2/files-2.1/org.springframework.boot/spring-boot-autoconfigure/2.6.8/cd1a9b61a270219ba6964fc8f4212fc80990be6/spring-boot-autoconfigure-2.6.8.jar:/Users/berapt/.gradle/caches/modules-2/files-2.1/org.springframework.boot/spring-boot/2.6.8/2bfaf71bfba3d7dcbc0ad0301a0c0aa3af8513af/spring-boot-2.6.8.jar:/Users/berapt/.gradle/caches/modules-2/files-2.1/org.springframework.boot/spring-boot-starter-logging/2.6.8/8b473f1982ebd19f2fd16f7e3f65b6e8c070d0ea/spring-boot-starter-logging-2.6.8.jar:/Users/berapt/.gradle/caches/modules-2/files-2.1/jakarta.annotation/jakarta.annotation-api/1.3.5/59eb84ee0d616332ff44aba065f3888cf002cd2d/jakarta.annotation-api-1.3.5.jar:/Users/berapt/.gradle/caches/modules-2/files-2.1/org.yaml/snakeyaml/1.29/6d0cdafb2010f1297e574656551d7145240f6e25/snakeyaml-1.29.jar:/Users/berapt/.gradle/caches/modules-2/files-2.1/io.projectreactor.netty/reactor-netty-http/1.0.19/bcb2d93714306e8d1235e16cc953ac2bf88ac93c/reactor-netty-http-1.0.19.jar:/Users/berapt/.gradle/caches/modules-2/files-2.1/org.springframework/spring-beans/5.3.20/ab88bd9e3a8307f5c0516c15d295c88ec318659/spring-beans-5.3.20.jar:/Users/berapt/.gradle/caches/modules-2/files-2.1/io.projectreactor/reactor-core/3.4.18/29f4f3a4876a65861deffc0f7f189029bcaf7946/reactor-core-3.4.18.jar:/Users/berapt/.gradle/caches/modules-2/files-2.1/net.minidev/json-smart/2.4.8/7c62f5f72ab05eb54d40e2abf0360a2fe9ea477f/json-smart-2.4.8.jar:/Users/berapt/.gradle/caches/modules-2/files-2.1/org.slf4j/slf4j-api/1.7.36/6c62681a2f655b49963a5983b8b0950a6120ae14/slf4j-api-1.7.36.jar:/Users/berapt/.gradle/caches/modules-2/files-2.1/jakarta.activation/jakarta.activation-api/1.2.2/99f53adba383cb1bf7c3862844488574b559621f/jakarta.activation-api-1.2.2.jar:/Users/berapt/.gradle/caches/modules-2/files-2.1/org.junit.jupiter/junit-jupiter-params/5.8.2/ddeafe92fc263f895bfb73ffeca7fd56e23c2cce/junit-jupiter-params-5.8.2.jar:/Users/berapt/.gradle/caches/modules-2/files-2.1/net.bytebuddy/byte-buddy/1.11.22/8b4c7fa5562a09da1c2a9ab0873cb51f5034d83f/byte-buddy-1.11.22.jar:/Users/berapt/.gradle/caches/modules-2/files-2.1/net.bytebuddy/byte-buddy-agent/1.11.22/2fbcf3210dfc09b42242e3b66a5281cc5b9adb80/byte-buddy-agent-1.11.22.jar:/Users/berapt/.gradle/caches/modules-2/files-2.1/com.vaadin.external.google/android-json/0.0.20131108.vaadin1/fa26d351fe62a6a17f5cda1287c1c6110dec413f/android-json-0.0.20131108.vaadin1.jar:/Users/berapt/.gradle/caches/modules-2/files-2.1/org.springframework/spring-jcl/5.3.20/35119231d09863699567ce579c21512ddcbc5407/spring-jcl-5.3.20.jar:/Users/berapt/.gradle/caches/modules-2/files-2.1/org.junit.platform/junit-platform-commons/1.8.2/32c8b8617c1342376fd5af2053da6410d8866861/junit-platform-commons-1.8.2.jar:/Users/berapt/.gradle/caches/modules-2/files-2.1/org.opentest4j/opentest4j/1.2.0/28c11eb91f9b6d8e200631d46e20a7f407f2a046/opentest4j-1.2.0.jar:/Users/berapt/.gradle/caches/modules-2/files-2.1/org.apiguardian/apiguardian-api/1.1.2/a231e0d844d2721b0fa1b238006d15c6ded6842a/apiguardian-api-1.1.2.jar:/Users/berapt/.gradle/caches/modules-2/files-2.1/io.kotest/kotest-framework-engine-jvm/5.7.2/50d6dc19ba13259fe121cc44a00068634d0e39fd/kotest-framework-engine-jvm-5.7.2.jar:/Users/berapt/.gradle/caches/modules-2/files-2.1/io.kotest/kotest-framework-api-jvm/5.7.2/c7a563e04b5ddc008ffb237f066504e1d0ec5025/kotest-framework-api-jvm-5.7.2.jar:/Users/berapt/.gradle/caches/modules-2/files-2.1/io.kotest/kotest-framework-discovery-jvm/5.7.2/9a13b703f1cf45e5c7d63fd393cf446428e0802b/kotest-framework-discovery-jvm-5.7.2.jar:/Users/berapt/.gradle/caches/modules-2/files-2.1/io.kotest/kotest-common-jvm/5.7.2/508fd4ecd453d987171a1297f9580c211fb02462/kotest-common-jvm-5.7.2.jar:/Users/berapt/.gradle/caches/modules-2/files-2.1/io.kotest/kotest-extensions-jvm/5.7.2/3badce4a9576705e043fbe548d85ffe2ca30488d/kotest-extensions-jvm-5.7.2.jar:/Users/berapt/.gradle/caches/modules-2/files-2.1/io.kotest/kotest-framework-concurrency-jvm/5.7.2/dd63871080d2f739eb1937754f48abbf9a14b091/kotest-framework-concurrency-jvm-5.7.2.jar:/Users/berapt/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlinx/kotlinx-coroutines-core-jvm/1.5.2/f4cc07a50437659e0043e7da762809a46932b6a0/kotlinx-coroutines-core-jvm-1.5.2.jar:/Users/berapt/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk7/1.9.20/8b4b73f4e08efaae93fc01d8c6eaab58a72d2ab3/kotlin-stdlib-jdk7-1.9.20.jar:/Users/berapt/.gradle/caches/modules-2/files-2.1/io.kotest/kotest-assertions-shared-jvm/5.7.2/1329f64da3b88621e8cf1c7d29d6cc90761e3751/kotest-assertions-shared-jvm-5.7.2.jar:/Users/berapt/.gradle/caches/modules-2/files-2.1/com.fasterxml.jackson.core/jackson-annotations/2.13.3/7198b3aac15285a49e218e08441c5f70af00fc51/jackson-annotations-2.13.3.jar:/Users/berapt/.gradle/caches/modules-2/files-2.1/com.fasterxml.jackson.core/jackson-core/2.13.3/a27014716e4421684416e5fa83d896ddb87002da/jackson-core-2.13.3.jar:/Users/berapt/.gradle/caches/modules-2/files-2.1/org.springframework/spring-context/5.3.20/517a42165221ea944c8b794154c10b69c0128281/spring-context-5.3.20.jar:/Users/berapt/.gradle/caches/modules-2/files-2.1/ch.qos.logback/logback-classic/1.2.11/4741689214e9d1e8408b206506cbe76d1c6a7d60/logback-classic-1.2.11.jar:/Users/berapt/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-to-slf4j/2.17.2/17dd0fae2747d9a28c67bc9534108823d2376b46/log4j-to-slf4j-2.17.2.jar:/Users/berapt/.gradle/caches/modules-2/files-2.1/org.slf4j/jul-to-slf4j/1.7.36/ed46d81cef9c412a88caef405b58f93a678ff2ca/jul-to-slf4j-1.7.36.jar:/Users/berapt/.gradle/caches/modules-2/files-2.1/io.projectreactor.netty/reactor-netty-core/1.0.19/adb58ba62d297b56d6b7915a50f048eddcfc81a6/reactor-netty-core-1.0.19.jar:/Users/berapt/.gradle/caches/modules-2/files-2.1/io.netty/netty-codec-http2/4.1.77.Final/9e58eeeacc74f8ad2b2acb240b1f01d2c40159d7/netty-codec-http2-4.1.77.Final.jar:/Users/berapt/.gradle/caches/modules-2/files-2.1/io.netty/netty-codec-http/4.1.77.Final/c5ac5afa9af5b4dc0e8bdbfd686979af77ebdb3c/netty-codec-http-4.1.77.Final.jar:/Users/berapt/.gradle/caches/modules-2/files-2.1/io.netty/netty-resolver-dns-native-macos/4.1.77.Final/ba23bed7fd221158b5064096f9f8e286b190250c/netty-resolver-dns-native-macos-4.1.77.Final-osx-x86_64.jar:/Users/berapt/.gradle/caches/modules-2/files-2.1/io.netty/netty-resolver-dns/4.1.77.Final/aad506ab6804e2720771634e2de2a065fa678126/netty-resolver-dns-4.1.77.Final.jar:/Users/berapt/.gradle/caches/modules-2/files-2.1/io.netty/netty-transport-native-epoll/4.1.77.Final/8d10e9e138dac52172dd83229bdc89197100c723/netty-transport-native-epoll-4.1.77.Final-linux-x86_64.jar:/Users/berapt/.gradle/caches/modules-2/files-2.1/org.reactivestreams/reactive-streams/1.0.3/d9fb7a7926ffa635b3dcaa5049fb2bfa25b3e7d0/reactive-streams-1.0.3.jar:/Users/berapt/.gradle/caches/modules-2/files-2.1/net.minidev/accessors-smart/2.4.8/6e1bee5a530caba91893604d6ab41d0edcecca9a/accessors-smart-2.4.8.jar:/Users/berapt/.gradle/caches/modules-2/files-2.1/com.github.ajalt/mordant/1.2.1/6cbab1a74ab6dafbf81b7706733d4c2fbaff2e0b/mordant-1.2.1.jar:/Users/berapt/.gradle/caches/modules-2/files-2.1/io.github.classgraph/classgraph/4.8.162/85bc1625bc8aac51ad32971ebb26a3e35cb97356/classgraph-4.8.162.jar:/Users/berapt/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlinx/kotlinx-coroutines-test/1.5.2/afa586e67fc5213fbea0d4c075af56b36a73da94/kotlinx-coroutines-test-1.5.2.jar:/Users/berapt/.gradle/caches/modules-2/files-2.1/org.springframework/spring-aop/5.3.20/c82f17997ab18ecafa8d08ce34a7c7aa4a04ef9e/spring-aop-5.3.20.jar:/Users/berapt/.gradle/caches/modules-2/files-2.1/org.springframework/spring-expression/5.3.20/20e179f0dfabf0a46428f22c2150c9c4850fd15d/spring-expression-5.3.20.jar:/Users/berapt/.gradle/caches/modules-2/files-2.1/ch.qos.logback/logback-core/1.2.11/a01230df5ca5c34540cdaa3ad5efb012f1f1f792/logback-core-1.2.11.jar:/Users/berapt/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-api/2.17.2/f42d6afa111b4dec5d2aea0fe2197240749a4ea6/log4j-api-2.17.2.jar:/Users/berapt/.gradle/caches/modules-2/files-2.1/io.netty/netty-handler-proxy/4.1.77.Final/d1ac0d95b770098c46b6679fbfd417ae277012d4/netty-handler-proxy-4.1.77.Final.jar:/Users/berapt/.gradle/caches/modules-2/files-2.1/io.netty/netty-handler/4.1.77.Final/47a81089de03635a27f509f3e4e13386ae1db275/netty-handler-4.1.77.Final.jar:/Users/berapt/.gradle/caches/modules-2/files-2.1/io.netty/netty-codec/4.1.77.Final/4efc5f59335301d6ba0d7cd31dd10651119b03c8/netty-codec-4.1.77.Final.jar:/Users/berapt/.gradle/caches/modules-2/files-2.1/io.netty/netty-transport/4.1.77.Final/2a3373bbd20d520c821f210bd5ee886788512043/netty-transport-4.1.77.Final.jar:/Users/berapt/.gradle/caches/modules-2/files-2.1/io.netty/netty-buffer/4.1.77.Final/d97571f99e5e739d86824d0df99f35d295276b5f/netty-buffer-4.1.77.Final.jar:/Users/berapt/.gradle/caches/modules-2/files-2.1/io.netty/netty-common/4.1.77.Final/ea0fc20f4e6178966b9d62017b7fcb83dfe0e713/netty-common-4.1.77.Final.jar:/Users/berapt/.gradle/caches/modules-2/files-2.1/io.netty/netty-resolver-dns-classes-macos/4.1.77.Final/60a6b7a3d81982bcf98db89c20b04f870d2d5ea0/netty-resolver-dns-classes-macos-4.1.77.Final.jar:/Users/berapt/.gradle/caches/modules-2/files-2.1/io.netty/netty-codec-dns/4.1.77.Final/a0a9bc85703efbab626fb8642e08e221b59dc604/netty-codec-dns-4.1.77.Final.jar:/Users/berapt/.gradle/caches/modules-2/files-2.1/io.netty/netty-resolver/4.1.77.Final/4a239dbf8d8bb5f98aa51462c35011c0516395fd/netty-resolver-4.1.77.Final.jar:/Users/berapt/.gradle/caches/modules-2/files-2.1/io.netty/netty-transport-classes-epoll/4.1.77.Final/dd70dbccbcf98382223a59044f3c08d8e9920cad/netty-transport-classes-epoll-4.1.77.Final.jar:/Users/berapt/.gradle/caches/modules-2/files-2.1/io.netty/netty-transport-native-unix-common/4.1.77.Final/c95d53486414b3270d08057957c5da8e0c37e4eb/netty-transport-native-unix-common-4.1.77.Final.jar:/Users/berapt/.gradle/caches/modules-2/files-2.1/org.ow2.asm/asm/9.1/a99500cf6eea30535eeac6be73899d048f8d12a8/asm-9.1.jar:/Users/berapt/.gradle/caches/modules-2/files-2.1/com.github.ajalt/colormath/1.2.0/c62f49b31f34588dbbfb477c08fd56bc3026d202/colormath-1.2.0.jar:/Users/berapt/.gradle/caches/modules-2/files-2.1/io.kotest/kotest-assertions-api-jvm/5.7.2/38eb14f47f6add08ae2f9927adb1d3af6abb4aad/kotest-assertions-api-jvm-5.7.2.jar:/Users/berapt/.gradle/caches/modules-2/files-2.1/io.netty/netty-codec-socks/4.1.77.Final/17bb510aa545fc73a18ab804c594593e32de1a1d/netty-codec-socks-4.1.77.Final.jar:/Users/berapt/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlinx/kotlinx-coroutines-reactor/1.5.2/43700d2146eb36140428bae27755c0243650e75b/kotlinx-coroutines-reactor-1.5.2.jar:/Users/berapt/.gradle/caches/modules-2/files-2.1/org.springframework.boot/spring-boot-starter-data-r2dbc/2.6.8/2282f2d3a1215be80f3fc35b0937d78d6fa461f4/spring-boot-starter-data-r2dbc-2.6.8.jar:/Users/berapt/.gradle/caches/modules-2/files-2.1/org.springframework.boot/spring-boot-starter-data-redis-reactive/2.6.8/ae04d87ee2ca19c1012bbaad0aa41639e4307dfc/spring-boot-starter-data-redis-reactive-2.6.8.jar:/Users/berapt/.gradle/caches/modules-2/files-2.1/io.projectreactor.kotlin/reactor-kotlin-extensions/1.1.6/5ad285e06ba04edad1ec372a38010984425cc352/reactor-kotlin-extensions-1.1.6.jar:/Users/berapt/.gradle/caches/modules-2/files-2.1/io.r2dbc/r2dbc-h2/0.8.5.RELEASE/a694edf4436d91db2c3a982969642f0018c398bc/r2dbc-h2-0.8.5.RELEASE.jar:/Users/berapt/.gradle/caches/modules-2/files-2.1/org.jetbrains/annotations/20.1.0/2fcd1f3225bca0c4a7bc931142076f8c1e80993f/annotations-20.1.0.jar:/Users/berapt/.gradle/caches/modules-2/files-2.1/org.junit.platform/junit-platform-testkit/1.8.2/43c593ad99a975588d56b501fd4353065facebfc/junit-platform-testkit-1.8.2.jar:/Users/berapt/.gradle/caches/modules-2/files-2.1/org.ow2.asm/asm/9.2/81a03f76019c67362299c40e0ba13405f5467bff/asm-9.2.jar:/Users/berapt/.gradle/caches/modules-2/files-2.1/cglib/cglib-nodep/3.3.0/87271c95d5bc9e37e4981c9593ff14d470b6684b/cglib-nodep-3.3.0.jar:/Users/berapt/.gradle/caches/modules-2/files-2.1/org.objenesis/objenesis/3.2/7fadf57620c8b8abdf7519533e5527367cb51f09/objenesis-3.2.jar:/Users/berapt/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlinx/kotlinx-coroutines-reactive/1.5.2/6ab6c941fa2cd480a89490b438ba27e6033a30ec/kotlinx-coroutines-reactive-1.5.2.jar:/Users/berapt/.gradle/caches/modules-2/files-2.1/org.springframework.data/spring-data-r2dbc/1.4.4/14b7fc538c8a194a8b132a20844626ab61cbfad2/spring-data-r2dbc-1.4.4.jar:/Users/berapt/.gradle/caches/modules-2/files-2.1/io.r2dbc/r2dbc-pool/0.8.8.RELEASE/a979c923133ae0e7afdee36da951bb3ebea2670c/r2dbc-pool-0.8.8.RELEASE.jar:/Users/berapt/.gradle/caches/modules-2/files-2.1/io.r2dbc/r2dbc-spi/0.8.6.RELEASE/9940d38ab7ea27657a4477f50c496451e09b01f2/r2dbc-spi-0.8.6.RELEASE.jar:/Users/berapt/.gradle/caches/modules-2/files-2.1/org.springframework.boot/spring-boot-starter-data-redis/2.6.8/37e8659feb933c627d6876e3b5f83cefa79c0a08/spring-boot-starter-data-redis-2.6.8.jar:/Users/berapt/.gradle/caches/modules-2/files-2.1/com.h2database/h2/1.4.200/f7533fe7cb8e99c87a43d325a77b4b678ad9031a/h2-1.4.200.jar:/Users/berapt/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlinx/kotlinx-coroutines-jdk8/1.5.2/9c62137ef8ed62459f7a412bfc9884e2c6ca15d6/kotlinx-coroutines-jdk8-1.5.2.jar:/Users/berapt/.gradle/caches/modules-2/files-2.1/io.github.java-diff-utils/java-diff-utils/4.12/1a712a91324d566eef39817fc5c9980eb10c21db/java-diff-utils-4.12.jar:/Users/berapt/.gradle/caches/modules-2/files-2.1/org.junit.jupiter/junit-jupiter-engine/5.8.2/c598b4328d2f397194d11df3b1648d68d7d990e3/junit-jupiter-engine-5.8.2.jar:/Users/berapt/.gradle/caches/modules-2/files-2.1/org.opentest4j/opentest4j/1.3.0/152ea56b3a72f655d4fd677fc0ef2596c3dd5e6e/opentest4j-1.3.0.jar:/Users/berapt/.gradle/caches/modules-2/files-2.1/org.springframework.data/spring-data-relational/2.3.4/a10399ef878628c27b8b265e70debdae31b6a16b/spring-data-relational-2.3.4.jar:/Users/berapt/.gradle/caches/modules-2/files-2.1/org.springframework.data/spring-data-commons/2.6.4/8bf118f8f249e1d9b7cd5410b7dc6482531b173b/spring-data-commons-2.6.4.jar:/Users/berapt/.gradle/caches/modules-2/files-2.1/org.springframework/spring-r2dbc/5.3.20/be93e2006010af38cebb60cedb7ed9bd2358f32c/spring-r2dbc-5.3.20.jar:/Users/berapt/.gradle/caches/modules-2/files-2.1/org.springframework/spring-tx/5.3.20/9a4ec2249dc3523ac70e0710a64288c14fc3ff78/spring-tx-5.3.20.jar:/Users/berapt/.gradle/caches/modules-2/files-2.1/io.projectreactor.addons/reactor-pool/0.2.8/6ad5eca1908b59fc5d160a8f3a9cd17367901918/reactor-pool-0.2.8.jar:/Users/berapt/.gradle/caches/modules-2/files-2.1/org.springframework.data/spring-data-redis/2.6.4/596b51391389d8451708756dc4892da05fb363b6/spring-data-redis-2.6.4.jar:/Users/berapt/.gradle/caches/modules-2/files-2.1/io.lettuce/lettuce-core/6.1.8.RELEASE/a68e451255221a2e3b4dd774b521ba8ddb994255/lettuce-core-6.1.8.RELEASE.jar:/Users/berapt/.gradle/caches/modules-2/files-2.1/org.springframework.data/spring-data-keyvalue/2.6.4/c17dde1da8c182f32c271c6f33c8d849f998a240/spring-data-keyvalue-2.6.4.jar:/Users/berapt/.gradle/caches/modules-2/files-2.1/org.springframework/spring-context-support/5.3.20/67c501ee0f6a0a93c1a1791fb9a176e1351de538/spring-context-support-5.3.20.jar:/Users/berapt/.gradle/caches/modules-2/files-2.1/org.springframework/spring-oxm/5.3.20/c20a0baf7237d4d79939ccfe7765e1186df6488a/spring-oxm-5.3.20.jar:/Users/berapt/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlinx/kotlinx-coroutines-debug/1.5.2/d9676e67fd62e0f7ac0e6489b19224ce4cd327d3/kotlinx-coroutines-debug-1.5.2.jar:/Users/berapt/.gradle/caches/modules-2/files-2.1/net.java.dev.jna/jna-platform/5.5.0/af38e7c4d0fc73c23ecd785443705bfdee5b90bf/jna-platform-5.5.0.jar:/Users/berapt/.gradle/caches/modules-2/files-2.1/net.java.dev.jna/jna/5.5.0/e0845217c4907822403912ad6828d8e0b256208/jna-5.5.0.jar com.intellij.rt.junit.JUnitStarter -ideVersion5 -junit5 TodoSpec
Internal Error occurred.
org.junit.platform.commons.JUnitException: TestEngine with ID 'spock' failed to discover tests
at org.junit.platform.launcher.core.EngineDiscoveryOrchestrator.discoverEngineRoot(EngineDiscoveryOrchestrator.java:160)
at org.junit.platform.launcher.core.EngineDiscoveryOrchestrator.discoverSafely(EngineDiscoveryOrchestrator.java:134)
at org.junit.platform.launcher.core.EngineDiscoveryOrchestrator.discover(EngineDiscoveryOrchestrator.java:108)
at org.junit.platform.launcher.core.EngineDiscoveryOrchestrator.discover(EngineDiscoveryOrchestrator.java:80)
at org.junit.platform.launcher.core.DefaultLauncher.discover(DefaultLauncher.java:110)
at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:86)
at org.junit.platform.launcher.core.DefaultLauncherSession$DelegatingLauncher.execute(DefaultLauncherSession.java:86)
at org.junit.platform.launcher.core.SessionPerRequestLauncher.execute(SessionPerRequestLauncher.java:53)
at com.intellij.junit5.JUnit5IdeaTestRunner.startRunnerWithArgs(JUnit5IdeaTestRunner.java:57)
at com.intellij.rt.junit.IdeaTestRunner$Repeater$1.execute(IdeaTestRunner.java:38)
at com.intellij.rt.execution.junit.TestsRepeater.repeat(TestsRepeater.java:11)
at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:35)
at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:232)
at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:55)
Caused by: org.junit.platform.commons.JUnitException: ClassSelector [className = 'TodoSpec'] resolution failed
at org.junit.platform.launcher.listeners.discovery.AbortOnFailureLauncherDiscoveryListener.selectorProcessed(AbortOnFailureLauncherDiscoveryListener.java:39)
at org.junit.platform.engine.support.discovery.EngineDiscoveryRequestResolution.resolveCompletely(EngineDiscoveryRequestResolution.java:102)
at org.junit.platform.engine.support.discovery.EngineDiscoveryRequestResolution.run(EngineDiscoveryRequestResolution.java:82)
at org.junit.platform.engine.support.discovery.EngineDiscoveryRequestResolver.resolve(EngineDiscoveryRequestResolver.java:113)
at org.spockframework.runtime.SpockEngine.discover(SpockEngine.java:28)
at org.junit.platform.launcher.core.EngineDiscoveryOrchestrator.discoverEngineRoot(EngineDiscoveryOrchestrator.java:152)
... 13 more
Caused by: org.junit.platform.commons.PreconditionViolationException: Could not load class with name: TodoSpec
at org.junit.platform.engine.discovery.ClassSelector.lambda$getJavaClass$0(ClassSelector.java:75)
at org.junit.platform.commons.function.Try$Failure.getOrThrow(Try.java:335)
at org.junit.platform.engine.discovery.ClassSelector.getJavaClass(ClassSelector.java:74)
at org.spockframework.runtime.ClassSelectorResolver.resolve(ClassSelectorResolver.java:26)
at org.junit.platform.engine.support.discovery.EngineDiscoveryRequestResolution.lambda$resolve$2(EngineDiscoveryRequestResolution.java:134)
at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:197)
at java.base/java.util.ArrayList$ArrayListSpliterator.tryAdvance(ArrayList.java:1602)
at java.base/java.util.stream.ReferencePipeline.forEachWithCancel(ReferencePipeline.java:129)
at java.base/java.util.stream.AbstractPipeline.copyIntoWithCancel(AbstractPipeline.java:527)
at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:513)
at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:499)
at java.base/java.util.stream.FindOps$FindOp.evaluateSequential(FindOps.java:150)
at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
at java.base/java.util.stream.ReferencePipeline.findFirst(ReferencePipeline.java:647)
at org.junit.platform.engine.support.discovery.EngineDiscoveryRequestResolution.resolve(EngineDiscoveryRequestResolution.java:185)
at org.junit.platform.engine.support.discovery.EngineDiscoveryRequestResolution.resolve(EngineDiscoveryRequestResolution.java:125)
at org.junit.platform.engine.support.discovery.EngineDiscoveryRequestResolution.resolveCompletely(EngineDiscoveryRequestResolution.java:91)
... 17 more
Caused by: java.lang.ClassNotFoundException: TodoSpec
at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:641)
at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:188)
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:525)
at org.junit.platform.commons.util.ReflectionUtils.lambda$tryToLoadClass$9(ReflectionUtils.java:829)
at org.junit.platform.commons.function.Try.lambda$call$0(Try.java:57)
at org.junit.platform.commons.function.Try.of(Try.java:93)
at org.junit.platform.commons.function.Try.call(Try.java:57)
at org.junit.platform.commons.util.ReflectionUtils.tryToLoadClass(ReflectionUtils.java:792)
at org.junit.platform.commons.util.ReflectionUtils.tryToLoadClass(ReflectionUtils.java:748)
... 32 more
Process finished with exit code 254
- 원인과 문제해결
groovy plugin이 root module에 있고 정작 사용하는 submodule에는 없었습니다. spock 프레임워크를 사용하는 모듈에 `build.gradle`에 groovy 플러그인을 정의해 주었습니다.
plugins {
kotlin("plugin.spring") version "1.9.20"
id("groovy") // for spock
}
Refs.
- MA, MSA에 대한 이해
- https://medium.com/design-microservices-architecture-with-patterns/monolithic-architecture-is-still-worth-at-2021-98bfc112dc24
- https://medium.com/design-microservices-architecture-with-patterns/microservices-communications-f319f8d76b71
- https://ko.wikipedia.org/wiki/%EB%A7%88%EC%9D%B4%ED%81%AC%EB%A1%9C%EC%84%9C%EB%B9%84%EC%8A%A4