개요
•
아래와 같은 형식으로 api 모듈에서 :storage 모듈을 runtimeOnly 로, :domain 모듈을 implemenation으로 참조하고 있었는데, @SpringBootApplication 을 실행하면 :storage 모듈의 코드를 전혀 참조하지 못하는 에러가 발생하였습니다.
@SpringBootApplication
class CoApplication
fun main(args: Array<String>) {
runApplication<CoApplication>(*args)
}
Kotlin
복사
/**
* build.gradle.kts
*/
dependencies {
runtimeOnly(project(":storage:core-mysql"))
implementation(project(":domain"))
implementation("org.springframework.boot:spring-boot-starter-web")
}
Groovy
복사
INFO 63377 --- [ main]
.s.d.r.c.RepositoryConfigurationDelegate :
Finished Spring Data repository scanning in 6 ms.
Found 0 JPA repository interfaces.
Kotlin
복사
•
곰곰히 생각해보면, runtimeOnly 로 참조하는 모듈의 경우, 스프링 어플리케이션이 실행될 때 컴파일 과정에 포함되지 않는 것이 당연합니다. 실행 시에만 필요하지, 컴파일 시 필요한 라이브러리는 아니니까요. 따라서 먼저 모듈을 빌드 후 실행했을 때 문제 없이 스프링부트 어플리케이션이 실행됨을 확인할 수 있었습니다.
INFO 66686 --- [ main]
.s.d.r.c.RepositoryConfigurationDelegate :
Finished Spring Data repository scanning in 26 ms.
Found 2 JPA repository interfaces.
Kotlin
복사
Gradle Dependencies
쉽게 정리하면 다음과 같다.
•
compileOnly : 컴파일 할 때만 사용
•
runtimeOnly : 런타임 할 때만 사용(컴파일 필요X)
•
annotationProcessor : lombok과 같은 어노테이션 라이브러리 사용
•
implementation : 컴파일 + 런타임
•
api : 컴파일 + 런타임 + 다른 라이브러리에서 해당 라이브러리 의존 시(a → b→ c) 추가 의존(a→c)
Storage 모듈을 runtimeOnly로 사용하는 이유
•
api 모듈은 accountRepository 를 도메인 모듈에서부터 참조하지만, storage 모듈에 구현되어 있는 db 접근 코드는 전혀 알지 못합니다.
@Service
class AccountServiceImpl(
private val accountRepository: AccountRepository
): AccountService {
override fun getByEmail(email: String): Account {
TODO("Not yet implemented")
}
}
interface AccountRepository {
fun getById(id: Long): Optional<Account>
}
Kotlin
복사
•
이러한 이점을 얻을 수 있는 이유는, 스프링 어플리케이션이 실행될 때, 런타임 온리로 작성되어있는 스토리지 모듈의 레포지토리 빈을 AccountRepository 라는 추상화된 인터페이스에 자동으로 주입하기 때문입니다.
•
때문에 DI를 통해 각 모듈별로 역할이 완전히 분리되기 때문에, 로컬 환경에서 개발 후 Main 어플리케이션 실행 시 바로 작동하지 않을 수 있다는 귀찮음이 있음에도 runtimeOnly 로 데이터 접근 로직을 참조합니다.