본문 바로가기

Build

Maven Scopes? Gradle Configurations?

대부분의 내용은 reflectoring.io/maven-scopes-gradle-configurations/ 개인적으로 의미를 해석한 것입니다. 

 

 

 

빌드 툴은 의존성(dependency)을 관리한다. 우리가 어떠한 라이브러리를 프로젝트에서 사용하고 싶을 때 선언만 하면, 빌드 툴은 이 라이브러리를 다운받아서 build 라이프 사이클의 적절한 시점에서 classpath에 추가해준다.

(빌드 라이프사이클 - kwonnam.pe.kr/wiki/gradle/buildlifecycle 참고)

오랜시간동안 Maven이 널리 사용되고 있었다. 안정적이고, Java 커뮤니티에서 많이 사용되어 오고 있기 때문이다.

 

그러던 중 Gradle이 Maven의 대안법으로 등장했고, dependency를 선언하는데 있어 보다 유연한 방법으로 제공하고 있다. Maven과 Gradle, 이 두가지는 dependency를 선언하는데 다른 방법으로 사용한다.

Scope? Configuration? 그게 뭐지?

Maven은 pom.xml 파일로, Gradle은 build.gradle 파일로 우리의 소스파일을 빌드함에 있어서 필요한 조치(steps)들을 명시해준다. 빌드는 프로그래밍 언어로 작성된 소스파일을 기계가 읽어 실행 가능할 수 있는 결과물을 만든다. 이 결과 파일은 Artifact라도 불리운다. Artifact의 예시를 들자면 Jar 파일이 또는 War 파일이 될 수 있다.

대부분의 작지 않은 프로젝트는 외부 라이브러리 또는 프레임워크에 의존한다. 그렇기 때문에 빌드 툴의 또다른 중요한 임무는 특정 라이브러리와 프레임워크의 dependency를 관리하는 것이다. 

 

우리가 코드에 SLF4J의 로깅 라이브러리를 사용한다고 해보자. maven의 pom.xml 파일에서는 다음과 같이 의존성을 선언할 것이다.

<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-api</artifactId>
    <version>1.7.26</version>
    <scope>compile</scope>
</dependency>

 

그리고 같은 의존성을 gradle의 build.gradle에 선언해보겠다.

implementation 'org.slf4j:slf4j-api:1.7.26'

 

빌드 툴은 해당 의존성을 적절한 시점에 추가해준다고 했다.

컴파일 시에 의존성이 필요할 수도 있고, 실행 시에만 의존성이 필요할 수도 있다. 또는 테스트 환경에서 의존성이 필요할 수도 있다. 

빌드 툴은 어떻게 이 시점을 판단하냐면 개발자가 빌드 설정파일에서 명시해주기 때문이다. 이것의 이름을 Maven의 경우에는 Scope라고 하고, Gradle의 경우에는 Configuration이라고 한다. 

 

 

Maven Scopes

compile

기본 스코프이다. 아무 scope도 명시하지 않았을 때 사용된다.

 

출처- https://reflectoring.io/maven-scopes-gradle-configurations/

 

provided

provided 스코프는 최종 Build Artifact 에 포함되지 않을 때 사용한다. 예를 들면 프로젝트가 servlet api를 의존하고 있는데, tomcat에서는 기본적으로 servlet api을 제공하고 있을 경우 artifact 에 포함시킬 필요가 없다.

 

 

출처- https://reflectoring.io/maven-scopes-gradle-configurations/

runtime

컴파일 타임에 꼭 필요하지 않고 실행 시에만 필요한 의존성일 경우 사용한다. 

아래 표와 같이 개발환경도 런타임, 테스트 환경에서도 런타임에 이용가능하다.

출처- https://reflectoring.io/maven-scopes-gradle-configurations/

 

test

테스트 시에만 필요한 의존성일 경우 이 스코프를 사용한다. 

이 스코프의 의존성을 예를 들면, JUnit, Mockito, ASsertJ 같은 테스트 프레임워크가 있다.

아래 표와 같이 테스트 환경에서만 이용가능하다.

 

출처- https://reflectoring.io/maven-scopes-gradle-configurations/

Gradle Configurations

Gradle은 더 다양한 설정들이 존재한다. 계속 더 다양한 use cases를 적용할 수 있도록 개발이 활발하다.

 

다음은 Gradle의 Java 라이브러리 플러그인의 표준 환경설정이다. configuration에 접근할 수 있도록 build script에 plugin을 선언해야만 한다.

plugins {
    id 'java-library'
}

아래 내용은 여기서(Gradle implementation vs. compile dependencies) 참고하였습니다.

 

내가 이 포스팅을 쓰려고 한 계기이다. 요즘 만드는 프로젝트는 대부분 빌드툴을 Gradle로 설정하고, 다양한 라이브러리를 사용하기 위해 gradle 설정파일에서 dependency에 매번 추가한다. 어떤 configuration을 써야할지 항상 헷갈렸기에 공부하고 넘어가고자 한다.

일단 지금 내가 사용하고 있는 configuration은 아래와 같다. configuration 대부분이 compile로 사용하고 있었다.

 

밑에서 설명하겠지만 이제 compile을 사용하면 안된다!

이대로 build하면 실행은 잘되지만 아래와 같은 경고메시지가 뜬다.

자,  BUILD SUCCESSFUL 이 나왔다. 하지만 그 위의 메시지를 보면 

Deprecated Gradle features were used in this build, making it incompatible with Gradle 7.0.
Use ‘–warning-mode all’ to show the individual deprecation warnings.

이라는 메시지가 등장한다. 

Deprecated Gradle이라니. 뭐가 deprecated 된거지?

출처 - https://docs.gradle.org/current/userguide/java_plugin.html

바로 compile 이 deprecated 되었다. 하지만 밑에 설명에서 보다시피 compile은 implemetation이 대체할 수 있다. deprecated된 configuration을 Gradle 7.0 버전 이상부터 사용할 경우 build가 제대로 안될 수 있으니 이 점 기억해야한다.

 

 

Java project를 빌드와 실행하는데 있어 두개의 classpath가 사용된다. (classpath란?)

  1. Compile classpath : JDK가 자바코드를 바이트(.class) 코드로 컴파일하는데 필요한 dependency list
  2. Runtime classpath : 컴파일된 자바코드를 실제로 실행하는데 필요한 dependency list

어떤 클래스패스에 해당 dependency가 필요하냐에 따라 달라진다. dependency에 적용할 아래의 세가지 옵션을 확인해보자.

  1. compileOnly : compile classpath 에만 추가
  2. runtimeOnly : runtime classpath 에만 추가
  3. implementation : 두가지(compile, runtime) classpath 에 추가

출처- https://tomgregory.com/gradle-implementation-vs-compile-dependencies/

 

compile부터 runtime까지 모두 필요한 dependency라면 implementation을 사용하면 된다.

여기서 의문점이 들 수 있다. 왜 특정 classpath에만 적용할 수 있는 configuration을 써야하지? 

  • 컴파일이 빠름 (compile 할 때 사용할 일 없는 dependency를 classpath에서 뺀다면 빨라질 수 있다.)
  • 코드 작성 시, runtime classpath에 있어야하는 dependency를 실수로 사용하는 것을 막아줌
  • 더 깔끔한 classpath 목록. (복잡성 ↓)