[Spring Boot] 멀티 모듈 Bean 등록, Entity 스캔 해결

boms·2023년 8월 31일
3

Trouble Shooting

목록 보기
3/4

Issue 🙋‍♂️

API, DB 멀티 모듈 설정 중 에러가 발생했다.

***************************
APPLICATION FAILED TO START
***************************

Description:

Parameter 0 of constructor in org.delivery.api.account.AccountApiController required a bean of type 'org.delivery.db.account.AccountRepository' that could not be found.


Action:

Consider defining a bean of type 'org.delivery.db.account.AccountRepository' in your configuration.


Process finished with exit code 1

Problem 🤔

api 모률의 AccountApiController에서 db 모듈의 AccountRepository를 Autowire 하는데 AccountRepository가 Bean 등록이 되어 있지 않아 에러가 발생한 것이다.

아래는 프로젝트 파일 구성이다.

위와 같이 service 모듈 속에 api 모듈과 db 모듈이 있는 멀티 모듈 방식이다. 개발에 앞서 gradle 설정할 때 api의 gradle dependency에 implementation project(':db')를 추가 했었다. 이렇게 하면 api 내부에서 db 내부의 클래스를 사용할 수 있고 Bean 등록도 될 줄 알았는데 아니였다. implementation project()는 다른 모듈에 대한 의존성을 추가하지만 Bean 등록과는 별개인 듯 하다. 그래서 왜 AccountApiController에 AccountRepository Bean이 주입되지 않는지 알아보았다.

Solution 🙌

Component 스캔 범위❓

api 모듈의 component 스캔 범위는 ApiApplication이 속해 있는 패키지 org.delivery.api의 모든 하위 패키지로 제한된다. 즉 db 모듈의 AccountRepository의 상위 패키지는 org.delivery.db이기 때문에 AccountApiController에서 사용할 수 없다.

어떻게 org.delivery.api의 하위 패키지로 제한되는지 @SpringBootApplication 속 @ComponentScan을 알아봤다.

/**
<p>Either {@link #basePackageClasses} or {@link #basePackages} (or its alias
 * {@link #value}) may be specified to define specific packages to scan. If specific
 * packages are not defined, scanning will occur from the package of the
 * class that declares this annotation.
 */

이 내용에 따르면 스캔할 basepackage를 정의 할 수 있다. 정의 안하는 경우 @ComponentScan이 달린 클래스가 속한 패키지와 그 하위 패키지에서 스캐닝한다. 내 경우 org.delivery.api가 스캔할 basepackage다.

Jpa Configuration 👍

Jpa 설정을 통해 지정한 패키지에서 Bean 등록할 수 있다.

위와 같이 org.delivery.api 패키지에 config.jpa 패키지와 그 하위에 JpaConfig를 만들어 준다. JpaConfig는 아래와 같이 구현된다.

@Configuration
@EnableJpaRepositories(basePackages = "org.delivery.db")
public class JpaConfig {

}

@Configuration은 설정 파일을 만들거나 Bean 등록을 위한 아노테이션이다.
@EnableJpaRepositories(basePackages = "org.delivery.db")는 org.delivery.db 패키지 하위의 Repository를 찾고 Bean 등록하여 사용하는 아노테이션이다. 패키지 이름을 통일 시키는 것보다 참고할 패키지를 Configuration에 등록하는 방식이 더 바람직해 보인다.

이어서 build 해봤더니 db 모듈의 AccountEntity를 인식 못하는 에러가 발생했다.

Caused by: java.lang.IllegalArgumentException: Not a managed type: class org.delivery.db.account.AccountEntity
	at org.hibernate.metamodel.internal.MetamodelImpl.managedType(MetamodelImpl.java:583) ~[hibernate-core-5.6.15.Final.jar:5.6.15.Final]
	at org.hibernate.metamodel.internal.MetamodelImpl.managedType(MetamodelImpl.java:85) ~[hibernate-core-5.6.15.Final.jar:5.6.15.Final]
	at org.springframework.data.jpa.repository.support.JpaMetamodelEntityInformation.<init>(JpaMetamodelEntityInformation.java:75) ~[spring-data-jpa-2.7.8.jar:2.7.8]
	at org.springframework.data.jpa.repository.support.JpaEntityInformationSupport.getEntityInformation(JpaEntityInformationSupport.java:66) ~[spring-data-jpa-2.7.8.jar:2.7.8]
	at org.springframework.data.jpa.repository.support.JpaRepositoryFactory.getEntityInformation(JpaRepositoryFactory.java:233) ~[spring-data-jpa-2.7.8.jar:2.7.8]
	at org.springframework.data.jpa.repository.support.JpaRepositoryFactory.getTargetRepository(JpaRepositoryFactory.java:182) ~[spring-data-jpa-2.7.8.jar:2.7.8]
	at org.springframework.data.jpa.repository.support.JpaRepositoryFactory.getTargetRepository(JpaRepositoryFactory.java:165) ~[spring-data-jpa-2.7.8.jar:2.7.8]
	at org.springframework.data.jpa.repository.support.JpaRepositoryFactory.getTargetRepository(JpaRepositoryFactory.java:76) ~[spring-data-jpa-2.7.8.jar:2.7.8]
	at org.springframework.data.repository.core.support.RepositoryFactorySupport.getRepository(RepositoryFactorySupport.java:325) ~[spring-data-commons-2.7.8.jar:2.7.8]
	at org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport.lambda$afterPropertiesSet$5(RepositoryFactoryBeanSupport.java:323) ~[spring-data-commons-2.7.8.jar:2.7.8]
	at org.springframework.data.util.Lazy.getNullable(Lazy.java:231) ~[spring-data-commons-2.7.8.jar:2.7.8]
	at org.springframework.data.util.Lazy.get(Lazy.java:115) ~[spring-data-commons-2.7.8.jar:2.7.8]
	at org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport.afterPropertiesSet(RepositoryFactoryBeanSupport.java:329) ~[spring-data-commons-2.7.8.jar:2.7.8]
	at org.springframework.data.jpa.repository.support.JpaRepositoryFactoryBean.afterPropertiesSet(JpaRepositoryFactoryBean.java:144) ~[spring-data-jpa-2.7.8.jar:2.7.8]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1863) ~[spring-beans-5.3.25.jar:5.3.25]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1800) ~[spring-beans-5.3.25.jar:5.3.25]
	... 30 common frames omitted

db 모듈의 클래스 또한 JPA 스캔 대상이 아니라서 문제가 발생했다. 아래와 같이 JpaConfig에 @EntityScan도 추가해 org.delivery.db 패키지를 스캔하여 AccountEntity를 찾으면 해결할 수 있다.

@Configuration
@EntityScan(basePackages = "org.delivery.db")
@EnableJpaRepositories(basePackages = "org.delivery.db")
public class JpaConfig {

}

Takeaway 📝

  • Bean 등록은 동일한 경로에 있는 패키지로 제한된다.
  • 멀티 모듈에서 @EntityScan과 @EnableJpaRepositories을 통해 entity를 가져오고 Bean 등록하는 방법을 배우는 계기가 되었다.
profile
2023.08.21~

0개의 댓글