본문 바로가기
Back-End/Spring

멀티 모듈에서 JPA 모듈을 사용하기 위한 설정 및 내 견해

by 어렵다어려웡 2022. 6. 23.

이전 포스트와 이어집니다.


타 모듈의 클래스들을 빈으로 등록하기 위한 설정

java-core 모듈을 빈으로 등록해서 java-api에서 사용을 하도록 해야 한다.

그래서 @SpringBootApplication으로 스캔 패키지를 설정한다.

 

java-core의 모듈들 뿐만 아니라 java-api에서 사용되는 모든 모듈들이 스프링 빈으로 등록이 되어야 하기 때문에 패키지 가장 상위를 명시했다.

스프링 빈으로 등록해두기 위해 스캔 범위를 지정.

@EntityScan("com.multi.core")
// core 모듈을 포함하여 스프링 빈으로 등록시키 위해서 패키지 탐색범위를 지정한다.
@SpringBootApplication(scanBasePackages = {"com.multi"})
public class MultiModuleJavaApiApplication {

    public static void main(String[] args) {
        SpringApplication.run(MultiModuleJavaApiApplication.class, args);
    }

}

그 이후 다음과 같이 Service를 작성합니다.

하지만 아래와 같이 Repository 관련 메서드를 호출할 때 컴파일 오류가 발생합니다.

 

이것을 해결하기 위해서는 Gradle의 api와 implementation의 차이를 알고 있어야 합니다.

처음에 java-core 의존성을 작성할 때 implementation을 선언해서 의존성을 추가했는데, 기본적으로 implementation 은 직접 의존하고 있는 자신만 그 구현을 알고 있으며, 그 자식 모듈(?) 은 해당 의존성에 대한 구현을 참조할 수 없기 때문에, 이것을 api로 변경을 해서 사용해야 한다.

dependencies {
//    implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
    api 'org.springframework.boot:spring-boot-starter-data-jpa'
    runtimeOnly 'com.h2database:h2'
    testImplementation 'org.springframework.boot:spring-boot-starter-test'
}

api는 기존의 compile과 동일하며 Gradle 7.x 버전 이후부터는 compile 대신 api를 써야 한다

이후 의존성을 반영시키면 다음과 같이 컴파일 에러가 사라지게 된다.

그리고 간단하게 BoardService 가 스프링 빈으로 등록이 잘 됐는지 로그를 찍어서 테스트를 했다.

@SpringBootTest
@Log4j2
class BoardServiceTest {
    @Autowired
    private BoardService boardService;

    @Autowired
    private BoardService boardService2;

    @Test
    public void checkBoardServiceBean() {
       log.info("boardService : {}", boardService);
       log.info("boardService2 : {}", boardService2);
    }

}

하지만 아쉽게도 예외가 발생했는데 자세한 설명은 다음과 같았다.

Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'boardService' defined in file [C:\\BootJPA\\Multi-Module-Java\\java-api\\out\\production\\classes\\com\\multi\\api\\board\\service\\BoardService.class]: Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.multi.core.domains.board.entity.BoardRepository' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}

java-core에 작성한 BoardRepository 가 스프링 빈으로 등록되어 있지 않아서 Bean을 찾지 못하고 예외를 발생시키게 되었다. 우리는 위에서 보았던 것처럼 java-core 모듈을 스프링 빈으로 등록해두기 위해서 스캔할 패키지 범위를 설정을 다음과 같이 해뒀다.

@EntityScan("com.multi.core")
// core 모듈을 포함하여 스프링 빈으로 등록시키 위해서 패키지 탐색범위를 지정한다.
@SpringBootApplication(scanBasePackages = {"com.multi"})
public class MultiModuleJavaApiApplication {

    public static void main(String[] args) {
        SpringApplication.run(MultiModuleJavaApiApplication.class, args);
    }

}

왜 이런 상황이 발생하는지는 @SpringBootApplication의 내부 코드를 살펴보아야 알 수 있다.

 

이 설정을 사용하게 되면 하면 @Entity , Spring Data Repository 관련 클래스들은 해당 패키지에 존재해도 인식을 할 수 없어서 스프링 빈으로 등록할 수가 없어서 사용이 불가능해진다.

그렇기 때문에@EntityScan을 사용할 수밖에 없었던 것이다. 그리고 JpaRepository를 상속받고 있는 BoardRepository 또한 스프링 빈으로 등록할 대상이 안된 것이다.

결국 여기서 추가해야 하는 부분은 @EnableJpaRepositories이다.

문서에 적힌 것처럼 관련 Repository를 적용시킬 수 있도록 설정을 해야 한다.

@EntityScan("com.multi.core")
@EnableJpaRepositories("com.multi.core")
@SpringBootApplication(scanBasePackages = {"com.multi"})
public class MultiModuleJavaApiApplication {

    public static void main(String[] args) {
        SpringApplication.run(MultiModuleJavaApiApplication.class, args);
    }

}

            

이후 다시 테스트를 진행해서 스프링 빈으로 등록되어 있는지 확인합니다.


마치며..

멀티 모듈을 간단하게 2개만 써서 구성을 해보았다.

 

사실 직접 만들어보면서 느낀 것은 굳이 왜 이렇게 하는가..? 라는 의문이 들기도 했다.

1. Core모듈 <- API모듈, 모듈을 의존한다는 것에 대한 해석

B모듈이 A모듈을 의존한다는 것이 구체적으로 무엇을 말하는지 아직 모르겠다.

의존성의 전파를 최대한 방지하고 구현 기술에 대한 적용 범위를 특정 모듈의 한해서 사용하도록 해서

변경이나 실수에 대한 장점을 얻기 위한 것이라고 이해를 해야 하는지,, 

 

2. 의존성 전파 범위에 대한 것

내가 잘못 이해한 것이 아닌 이상은 결국 실습을 하면서 본 결과로보아 JPA를 사용을 위한 의존성을 implementation 가 아닌 api 를 사용함으로써 다른 모듈이 해당 구현기술을 사용하게 끔 변경해야 해서 참조할 수 있는 범위가 늘어나고 다른 모듈에서 구현 기술이 사용될 가능성이 생겨버렸다고 믿을 수 밖에없다. 

 

맞는지 모르겠지만 여기서 구현 기술이 쓰였다는 것은 @EnableJpaRepositories 을 작성한 것 때문에 그렇게 생각했다.왜냐하면 이 코드를 작성하기 위해서 implementation 가 아닌 api 를 사용하게 되어 의존성의 전파 범위Core모듈에서 API모듈로 넘어오게 되었고 결국 Core모듈에서 다른 데이터 접근 기술로 바꾸게 된다면 API모듈에서 코드가 변경되어야 하기 때문에 멀티 모듈에서 추구하는 원칙에 어긋난다.