주제
- gradle
impl
,compile
,api
톺아보기
목차
주제목차내용간단 용어 정리gradle dependency Cofigurateion 속성 정리💨Build-Up❓Why [왜 사용하지 말라고 권장하는가]api 적용 범위Implementation 적용 범위gradle documnent guide [공식 문서]📌 REFER
내용
간단 용어 정리
- compileClassPath
- 컴파일 시에 필요한 class path
- runtimeClassPath
- 런타임 시에 필요한 class path
- 보통 compile하면 compile path에도 있고 runtime path에도 있다.
gradle dependency Cofigurateion 속성 정리

- compileOnly
- when
- 컴파일 시에만 필요하고, 런타임시에는 불필요한 경우에 사용
- then
- 컴파일할 때는 해당 종속성을 사용하고 build 결과물에는 넣지 x
- 이점
- build의 결과물의 크기가 작아진다는 장점
- compileClassPath에만 dependency를 두는 것
- ex. Lombok → 컴파일 시에 lombok annotation을 보고 getter/setter 등 만들어주고 런타임 시에는 사용하지 않는다.
- runtimeOnly
- when
- 컴파일 시에는 불필요하고, 런타임 시에만 필요한 경우
- 이점
- 컴파일 시간이 빨라진다. → 컴파일 할 때는 사용하지 않기 때문
- runtimeClassPath에만 dependency를 두는것
- ex.
- JDBC 관련 → h2, mysql
- log 관련 → log4j
class A{ B = new B(); } class B{ int init(){ return new C().now(); } } 만약 A 클래스를 사용한다고 가정하면, 컴파일 시점에 B를 런타임 시점에 B,C
Q. 참조는 컴파일 시점에? 실제 사용은 runtime에 하는 것이군..?
.java —코드)
- implementation
- ex. A→B→C와 같은 상황
- 이떄 C를 변경하면,
B와 C만 recompile 하면
된다 → B는 C를 직접적으로 의존하고 있기 때문이다. - A입장에서는 compileClassPath에 C가 들어가지 X
- 따라서, C가 변경된다고 해도 A를 recompile 할 필요가 X
- api
- 한편, implementation과 동일한 상황에서 api는 A에서 C에 대한 접근이 가능함
- A 입장에서 compileClassPath에 C가 포함됨
- 따라서, C가 변경되면 A또한 recompile 되어야 한다.
소비자에게 유출
된다의 의미란?- A→B→C에서 api를 사용하는 경우
- A를 사용하는 소비자 입장에서 compileClassPath에 C가 들어오게 되니까 노출된다는 뜻이다.
- 물론 runtimeClassPath에는 C를 사용하니까 노출되는게 당연함 (api, implelmentation 둘다 노출됨)
- compileOnly VS compileOnlyApi
- compileOnly는 A→B→C 경우에 C가 변경되면 A는 recompile 할 필요가 없고, B랑 C랑 recompile 하면된다.
- compileOnlyApi 는 A→B→C 경우에 C가 변경되면, A,B,C 전부 recompile 해야한다 (
api가 붙었기 때문이다.
)
💨Build-Up
- build.gradle에서 사용하는 implementation과 api는 모두 라이브러리를 적용시키는 키워드이다.
- 과거에는 imple이 없어서 compile을 사용했고, 새로운 버전(6.x)에서 compile이 deprecated되면서 api 키워드가 사용되었다.
api와 compile은 같은 역할을 하지만 gradle은 api, compile을 사용하는 것을 권장하지 않는다.
왜냐하면 api를 통해 라이브러리를 가져올 경우 라이브러리가 적용되는 범위 때문이다.
❓Why [왜 사용하지 말라고 권장하는가]
api 적용 범위
해당 라이브러리는 해당 모듈을 의존하는 모듈에도 가져와진다. 예를 들어 A에서 api를 사용해 라이브러리를 가져온 다음 module B에서 api를 사용해 A를 가져올 경우 라이브러리도 같이 가져와진다.

유지보수성 측면에서 매우 좋지 않는 사이드 이펙트
가 발생한다.
- 모듈을 사용할 때 모듈의 인터페이스만이 외부에 노출되어야 하는데, 라이브러리의 인터페이스까지 같이 노출되기 때문이다.
- 또한 leak이 일어나느데, moduleA를 빌드하면서 이미 들어간 라이브러리가 중복으로 모듈 B에도 추가되기 때문이다.
- 라이브러리도 패키징된 모듈이다.
- 특히 클린 아키텍처 측면에서 에니팉 모듈은 비즈니스 로직을 만드는 애플리케이션의 use case 모듈에만 사용되고, usecase 모듈은 controller 모듈에서만 사용되는 구조로 이루어져 있어 각 레이어별로 분리가 확실하게 일어나는데, 만약 컨트롤러에서 엔티티 모듈은 알게된다면 모듈 계층을 건너뛴 참조가 일어날 수 있게되어 레이어를 나눈 의미가 사라지게 된다,
따라서 이를 해결하기 위해 한 모듈에서 사용하는 라이브러리는 그 모듈에서만 사용될 수 있도록
implementation을
써야한다Implementation 적용 범위
모듈에서 implementation을 사용해 가져오는 라이브러리는 해당 모듈을 의존하는 모듈에는 가져와지지 않는다.

A에서 impl 을 이용해 라이브러리를 가져온 다음 B에서 impl을 사용해 A를 가져오면 라이브러리는 가져와지지 않는다. 라이브러리는 A에 캡슐화 되어 B에 노출되지 않는다.
해당 implementation을 사용함으로써 코드를 모듈의 인터페이스로 제한 할 수 있어 모듈간 의존성을 줄이게 된다.
위 그림에서 모듈 B가 A와 라이브러리에 모두 의존했지만, 그림2에서는 B가 A에만 의존하는 것을 볼 수 있다. 프로그래밍 유지보수의 핵심은 모듈간 의존성을 줄이는 것이 1순위이다.
수많은 라이브러리(모듈)들이 범람하고, 하나의 프로그램에 코드가 수십만 줄이 넘어가는 현대에는 모듈간 의존성을 줄여야 프로그램을 유지보수하기 편해지기 때문이다.
implementation 말고 다른것들은 절대 쓰지말아라‼️
gradle documnent guide [공식 문서]
- api
Gradle은 컴파일 클래스 경로 및 빌드 출력에 종속성을 추가합니다. 모듈에 api 종속성을 포함하면 다른 모듈에 그 종속성을 과도적으로 내보내기를 원하며 따라서 런타임과 컴파일 시 모두 종속성을 사용할 수 있다는 사실을 Gradle에 알려줄 수 있습니다.이 구성은 compile(현재 지원 중단됨)과 똑같이 동작합니다. 다만 이것은 주의해서 사용해야 하며 다른 업스트림 소비자에게 일시적으로 내보내는 종속성만 함께 사용해야 합니다. 그 이유는 api 종속성이 외부 API를 변경하면 Gradle이 컴파일 시 해당 종속성에 액세스할 권한이 있는 모듈을 모두 다시 컴파일하기 때문입니다. 그러므로 api 종속성이 많이 있으면 빌드 시간이 상당히 증가합니다. 종속성의 API를 별도의 모듈에 노출시키고 싶은 것이 아니라면 라이브러리 모듈은 implementation 종속성을 대신 사용해야 합니다.
- implementation
Gradle은 종속성을 컴파일 클래스 경로에 추가하여 종속성을 빌드 출력에 패키징합니다. 다만 모듈이 implementation 종속성을 구성하는 경우, 이것은 Gradle에 개발자가 모듈이 컴파일 시 다른 모듈로 유출되는 것을 원치 않는다는 것을 알려줍니다. 즉, 종속성은 런타임 시 다른 모듈에서만 이용할 수 있습니다.api 또는 compile(지원 중단됨) 대신 이 종속성 구성을 사용하면 빌드 시스템이 재컴파일해야 하는 모듈의 수가 줄어들기 때문에 빌드 시간이 상당히 개선될 수 있습니다. 예를 들어, implementation 종속성이 API를 변경하면 Gradle은 해당 종속성과 그 종속성에 직접 연결된 모듈만 다시 컴파일합니다. 대부분의 앱과 테스트 모듈은 이 구성을 사용해야 합니다.