[Spring] Multi-Module project with gradle (2)

한호성·2023년 3월 25일
0

Introduction

멀티모듈 프로젝트를 구성하고, 사용하는 와중에, 다시 한 번 각 모듈간의 의존성 관계에 있어서 고민이 생겼습니다. 이 프로젝트에서는 간단하게 두 모듈로 구성하였습니다.

1. Domain 을 관리하는 Core 모듈,
2. WAS(Tomcat)로 부터 요청을 받고, 서비스 로직을 처리하는 API 모듈.

이 두 모듈을 의존관계는 Api 모듈이 --> Core 모듈을 의존하는 상황이였습니다. 이 떄, core 모듈에서 의존하는 라이브러리를 api 모듈에 노출 시키는 것이 맞는지에 대한 고민이 있었습니다.

(이렇게 노출 시킨다면, 모듈을 나눈 것에 의미가 있는것인가?)

이번 글은 이 질문에 대한 공부한 것을 작성한 글입니다.

[목차]

  • Gradle 외부 라이브러리 depenecies를 설정할 때, 사용되는 api, implementation 의 차이
  • api, implmentation을 사용하여 의존성을 설정하였을 때, 갖게 되는 장단점

Gradle (api,implementation) keywords

각 keywords의 차이점

사진을 통해 api, implementation의 사용을 일단 보겠습니다.
해당 Gradle은 위의 설명한 core 모듈의 라이브러리 의존성 목록 입니다.

api로 외부 라이브러리의 의존성을 받게 되면, core 모듈을 의존하는 api 모듈에서도, 라이브러리 함수에 접근할 수 있습니다. api 키워드를 사용하지 않으면, 어떻게 되는지 알아봅시다.

간단한 예시를 통해 봅시다.

상황) domain model 의 spring-data-jpa의 구현체를 사용하는 Repository를 만들었습니다.
domain : Member

package com.example.contractcore.Domain;

import com.example.contractcore.Repository.MemberRepository;
import lombok.*;
import org.hibernate.envers.Audited;

import javax.persistence.*;

@Entity
@Getter
@Setter
@Audited
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class Member extends MainBaseEntity {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id ;

    @Column
    private String name;

    @Column
    private int age;

    @Column
    private String tel;


}

repository : MemberRepository (spring-data-jpa에 의해 추상화된 JpaRepository를 상속)

package com.example.contractcore.Repository;


import com.example.contractcore.Domain.Member;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface MemberRepository extends JpaRepository<Member,Long> {

}

Springboot, spring-data-jpa를 활용하여, 간단한 api를 만들 때 볼 수 이는 간단한 상황입니다.

이, 상황에서 api 모듈에서, MemberRepository를 bean 주입 받아서 사용하는 것을 보여드리겠습니다.

Case1 api('spring-data-jpa ...') 사용한 경우,

구현된 함수 내용들에 모두 직접적으로 접근할 수 있습니다.

Case2 implementation('spring-data-jpa ...') 사용한 경우,

spring-data-jpa에 구현된 많은 내용들에 접근할 수 없게 됩니다.
(jpa를 직접적으로 다룰 수 있는 @Transactional .. 기타 등등 다 사용안됩니다.)

이 경우에 맨 처음에는 api를 사용하여 api 모듈에서 core 모듈에서 의존하는 spring-data-jpa 라이브러리에 접근할 수 있도록 하면 되겠다 생각하고 끝내버렸습니다.

근데 추가적으로 생각을 해보니 이런 의존성을 갖는게 맞는지.. 이러면 core 모듈에서 의존하는 외부라이브러리를 api 모듈에서 의존하게 되는데 맞는가에 대해 고민하게 되었습니다.
(이러면 멀티모듈을 쓰는게 의미가 있나..)
이런 의문점을 갖고, 여러 자료들을 찾는 결과, 저와 같은 고민을 한 사람들의 질문을 찾을 수 있었습니다. (Reference 참조해주세요)

이제 api, implementaton 을 사용했을 때의 장단점에 대해 이야기 해보겠습니다.

api, implmentation을 사용하여 의존성을 설정하였을 때, 갖게 되는 장단점

Case1. api 키워드 사용한 경우

장점

  • 간단하게 gradle 파일에 키워드 변경만으로, 하위 모듈에서, 외부 라이브러리 사용가능

단점

  • 외부 라이브러리에 접근하면 안되는 모듈에서의 접근 가능
  • 외부 라이브러리에 접근 가능하여,외부 라이브러리와의 코드 의존성 생김
    (외부 라이브러리를 바꾸게 되면 많은 곳에서 코드 수정해야함..)
  • 개발자가 직접 구현 클래스에 access 하게 되면 직접 사용할 수 있고,의도대로 사용하지 못할 수 있고, 모듈의 역할에 문제가 생길 수 있다.
    (2번째의 문제와 비슷)

Case2. implementation을 사용하고, 중간에 wrapper 모듈을 만들어서, 구현체 함수에 접근할 수 있도록 하는 방법.

장점

  • 모듈화의 의미를 찾을 수 있음
    (의존성이 생기는 것을 방지)
  • 프로젝트 유지보수 측면에서 이점이 있음.
    (라이브러리를 바꿔서 사용하더라도 api 모듈에서는 의존성이 없기 때문에 유지보수에 이점이 있음.)

단점

  • 외부 라이브러리를 감싸는 module을 생성할 경우, 외부라이브러리가 바뀌게 되면 많은 부분 수정하게됨..
  • 새로운 중간 모듈을 만들어야함으로, 매우 시간이 많이소요됨.

나의 생각

아래의 reference의 질의응답을 봤을 때 내가 내린 결론은 다음과 같다.

  • 현재 내가 예시로 든 문제는, api를 사용하여서 의존관계를 만들거나 or api-module자체에 implementation 하자.

    이유 :
    • (spring-data-jpa)에서 제공해주는 많은 기능들이 Service-layer에서 많은부분 사용되기 때문에(@Transactional,@Auditing,interface 함수들), 의존성을 주는것이 맞다. 만약 이 interface를 다시 wrapping 해서 사용하는 중간 module을 만든다는 것은 상당히 귀찮은 일이고, 단순히 wrapping 하는 것만으로 중간 module을 만드는 것은 배보다 배꼽이 더 큰 경우라고 생각합니다.

  • spring과 관련 없는 외부라이브러리 사용할 경우, 어느 모듈에 두어야하는지 고민해야합니다. 각 의존성 관계를 잘 생각해서 설계를 해야한다 생각합니다. 위와 같은 고민을 하게 되는 경우라면, 구조 설계가 잘못됬을 것이라 생각합니다.

  • "남이 말하는 Best Practice 만을 쫒지 말고, 자신의 생각대로 만들고, 문제가 생기면 고치도록 하자"

    • 해당 reference 글에서 조언해주는 내용인데 마음에 와닿았던 말이다. 일단 내 고민을 나 자신의 생각대로 풀어내고, 문제가 생길 때, 고쳐나가는 것도 좋은 배움이 될것이라 생각합니다.
  • "Multimodule로 설계하더라도, runtime에는 필요한 의존성을 다들고 있다. compile 타임에만, 접근을 하지 못하도록 하는것이다. "
    (내가 이해한 바로는, compile 타임엔 접근하지 못하지만, runtime 에는 모든 코드를 하나로 만들어 돌리기 때문에, 접근이 가능하다고 이해했습니다. 즉 개발자가, implementaton 키워드를 쓰면 상위모듈에서 접근하지 못하게 하는 것뿐이지, application이 돌 때는, 관련 라이브러리 코드들을 다 메모리에 올려서 사용하고 있다고 이해했습니다.)

Reference

https://discuss.gradle.org/t/best-practice-for-api-vs-implementation-in-multi-module-project/30519
(친절한 설명, 각각의 장단점에 대해 자세히 설명되어 있어서 도움이 많이 되었습니다. 감사합니다~)

profile
개발자 지망생입니다.

0개의 댓글