Gradle 버전업을 하고
최근에 회사에서 주로 쓰는 자바버전을 Open Jdk11로 바꾸면서 이런저런 라이브러리 버전도 같이 올리게 되었다.
Gradle은 6.5.1을 적용했는데, build.gradle을 열어보니 이런 코드가 눈에 띈다.
implementation "io.springfox:springfox-swagger2:${swagger_version}"
implementation "io.springfox:springfox-swagger-ui:${swagger_version}"
api "org.springdoc:springdoc-openapi-ui:${springdoc_version}"
implementation, api 뭐가 다른건가?
Gradle 문서 읽어보기
구글링 해보다가 적당한 문서를 발견할 수 없어서 도큐먼트를 보기로 했다.
영문문서가 불편하여 허접한 실력이지만 관련된 내용을 한글로 정리해보자.
<Using dependency configurations / 종속성 속성 사용>
Configurations은 dependencies와 artifacts 집합인데, 3가지 주요한 목적이 있다.
1. Declaring dependencies / 종속성 선언
plugin에 정의된 작업을 실행하는데 필요로 하는 subprojects나 외부 artifacts를 쉽게 선언 할 수 있게 해준다.
예로 plugin은 소스 코드를 컴파일 하기 위해 spring web framework를 dependency로 갖는다.
(참고. Gradle plugin은 재사용 가능한 빌드 로직을 패키지화하여 여러 서로다른 프로젝트와 빌드에서 사용할 수 있도록 한 것)
2. Resolving dependencies / 종속성 해결
plugin은 configuration을 사용하여 정의된 작업에 필요한 것을 download 할 수 있다.
예를들어, Maven Central에서 spring web framework의 jar 파일을 다운로드한다.
3. Exposing artifacts for consumption / 사용되는 artifacts 노출
다른 프로젝트들에서 사용할 artifacts들을 정의할 때도 configurations이 사용된다.
예를들어 JAR파일에 패키지 된 컴파일된 소스코드를 사내 Artifactory repository에 퍼블리싱 함.
이 3가지 목적을 염두에 두고, Java 라이브러리 Plugin에 정의된 표준 속성 몇가지를 살펴보겠다.
◻︎ implementation
프로젝트에 드러나있는 api가 아닌데, 그 프로젝트 소스를 컴파일 하는데 의존성이 필요한 것을 표현한다.
예를들어, 내부 persistence layer를 구현하기 위해 Hibernate를 사용하는 프로젝트
◻︎ api
프로젝트에 의해 노출되는 API의 일부인 프로젝트의 프로덕션 소스를 컴파일하는 데 필요한 종속성입니다.
예를 들어 프로젝트는 Guava를 사용하고 메서드 서명에서 Guava 클래스가 있는 공용 인터페이스를 노출합니다.
◻︎ testImplementation
프로젝트의 테스트 소스를 컴파일하고 실행하는 데 필요한 종속성입니다.
예를 들어 프로젝트는 테스트 프레임 워크 JUnit으로 테스트 코드를 작성한다고 선언합니다.
implementation, api 차이점은 종속성이 있다는 것이 드러나냐, 아니냐의 차이인 것 같다.
근데 뭔가 아리송하고 아직 잘 모르겠다..
구글신께서 더 좋은 자료를 찾아주셨다. 제목도 딱 내가 원하던 거네!
이 글에 보면 ABI(Appliation Binary Interface)라는 개념이 나온다.
ABI에 속하는 유형
- public method parameters
- return type
- parent classes or interfaces에 사용되는 유형
ABI에 속하지 않는 유형
- method 본문에서 사용되는 유형
- private method 선언에 정의된 유형
즉, 내가 작성한 Library-A에서 Library-B, Library-C를 참조해서 쓴다고 할 경우,
파라미터나 리턴타입으로 Library-B에 있는 class를 사용했다면, 이건 ABI에 속하는 것이고
private method 내부에서 http call 등을 위해 Library-C를 사용했다면, 그건 Library-A에는 드러나지 않는거라 ABI에 속하지 않는 유형이라고 할 수 있겠다.
Library-A
|
|
|---------------------|Library-B Library-C
원문을 보면 그림과 샘플코드를 친절히 첨부해주셔서 이해하기 수월했다.
그럼 이 ABI 개념을 Gradle plugins은 어떻게 풀어냈을까?
그 방법이 바로 api, implementation 이다!
api : api의 종속성은 우리가 작성하는 라이브러리의 ABI의 일부이므로 컴파일 및 런타임 클래스 경로에 표시되어야합니다.
implementation : implementation의 종속성은 우리가 작성하는 라이브러리의 ABI의 일부가 아닙니다. 런타임 클래스 경로에만 나타납니다.
ex)
dependencies {
api 'library-b'
implementation 'library-c'
}
그래서 이런 dependencies가 생겨난 것이다.
근데, 이렇게 써서 무슨 이득이 있는 것인가?
장점은 compileClasspath, runtimeClasspath dependencies를 정확히 표현할 수 있다.
일하다 보면, A팀에서 만들어 둔 라이브러리 버전업을 해야하는데, 그 라이브러리가 B팀에서 만든 라이브러리를 참조하고..
또 C 라이브러리 참조하고...
타고, 타고, 타고 들어가다보면 버전 하나 업데이트 하다가 빡치는 경험을 하는 경우가 가~끔 생긴다.
여러 라이브러리를 연동하는 어플리케이션을 만드는 경우라면, 권장사항을 따라 만드는 것이 바람직하겠다.
댓글