Gradle: Implementation과 RuntimeOnly의 차이점 및 활용 방법

2023. 12. 11. 01:12·Spring/Spring 기초 지식
반응형
 
 

SpringBoot 프로젝트의 build.gradle 파일에서 'implementation'과 'runtimeOnly' 설정의 차이점과 사용법을 알아보자

 

📌 서론

Gradle의 빌드 스크립트인 build.gradle에서 의존성을 관리할 때 runtimeOnly와 implementation은 자주 사용되는 구성이야. 각각의 키워드가 어떤 목적으로 사용되는지, 그리고 차이점은 무엇인지 알아보자.
글의 이해를 위해 가장 먼저 스프링의 "모듈"을 알아보자

build.gradle 코드
build.gradle 코드

 

1. 스프링 부트의 모듈 이해하기

1-1. 메인 모듈 (Main Module)

  • 애플리케이션의 핵심 기능을 담당한다. 주요 비즈니스 로직, 데이터베이스 연결, 웹 컨트롤러 등을 포함한다.
  • 프로젝트의 src 폴더 내에 위치한 Java 패키지들, application.properties 파일 등이 "메인 모듈"에 포함된다.
main-project/
├── src/
│   ├── main/
│   │   ├── java/
│   │   │   └── com.example.mainproject/
│   │   │       ├── MainApplication.java (애플리케이션 시작점)
│   │   │       ├── security/ (보안 관련 클래스)
│   │   │       ├── controller/ (웹 컨트롤러)
│   │   │       ├── service/ (서비스 로직)
│   │   │       └── repository/ (데이터베이스 연결)
│   │   └── resources/
│   │       └── application.properties (환경 설정 파일)
└── build.gradle (빌드 설정 파일)

 

1-2. 서브 모듈 (Sub Module)

  • 메인 모듈과는 별개의 기능을 수행하거나, 메인 모듈의 기능을 확장한다.
  • 다른 기능을 담당하는 별도의 웹 서비스, 라이브러리, 혹은 메인 프로젝트의 API를 사용하는 클라이언트 애플리케이션이 서브 프로젝트가 될 수 있다.
sub-project/
├── src/
│   ├── main/
│   │   ├── java/
│   │   │   └── com.example.subproject/
│   │   │       └── SubApplication.java (서브 프로젝트 시작점)
│   │   └── ... (추가 서브 프로젝트 관련 폴더 및 파일)
└── build.gradle (빌드 설정 파일)

 

1-3 멀티 모듈 프로젝트 (Multi-Module Project)

  • 하나의 프로젝트 내에서 메인 모듈과 하나 이상의 서브 모듈을 포함하는 구조다.
root-project/
├── main-module/ (메인 모듈)
│   ├── src/
│   │   ├── main/
│   │   │   ├── java/
│   │   │   │   └── com.example.mainproject/
│   │   │   │       ├── MainApplication.java (애플리케이션 시작점)
│   │   │   │       ├── security/ (보안 관련 클래스)
│   │   │   │       ├── controller/ (웹 컨트롤러)
│   │   │   │       ├── service/ (서비스 로직)
│   │   │   │       └── repository/ (데이터베이스 연결)
│   │   │   └── resources/
│   │   │       └── application.properties (환경 설정 파일)
│   └── build.gradle (메인 모듈 빌드 설정)
│
├── sub-module/ (서브 모듈)
│   ├── src/
│   │   ├── main/
│   │   │   ├── java/
│   │   │   │   └── com.example.subproject/
│   │   │   │       └── SubApplication.java (서브 프로젝트 시작점)
│   │   │   └── ... (추가 서브 프로젝트 관련 폴더 및 파일)
│   └── build.gradle (서브 모듈 빌드 설정)
│
└── build.gradle (루트 프로젝트 빌드 설정)

 

 

 

멀티 모듈 프로젝트에 의존성을 추가시켜 보자

 

 

1-4. 멀티 모듈 프로젝트에 web, security 의존성 추가

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-security'
    implementation 'org.springframework.boot:spring-boot-starter-web'
}

 

  1.  spring-boot-starter-web
  • 웹 애플리케이션 개발을 위한 스프링 MVC, 임베디드 톰캣 서버 등의 기능을 제공한다.
  2.  spring-boot-starter-security
  • 스프링 시큐리티를 이용한 인증과 권한 부여 기능을 제공한다.

 

1-5. 멀티 모듈 프로젝트 트리 구조

root-project/
├── main-module/ (메인 모듈)
│   ├── src/
│   │   ├── main/
│   │   │   ├── java/
│   │   │   │   └── com.example.mainproject/
│   │   │   │       ├── MainApplication.java (애플리케이션 시작점)
│   │   │   │       ├── security/ (보안 관련 클래스)
│   │   │   │       ├── controller/ (웹 컨트롤러)
│   │   │   │       ├── service/ (서비스 로직)
│   │   │   │       └── repository/ (데이터베이스 연결)
│   │   │   └── resources/
│   │   │       └── application.properties (환경 설정 파일)
│   └── build.gradle (메인 모듈 빌드 설정)
│       └── dependencies {
│           ├── implementation 'org.springframework.boot:spring-boot-starter-security'
│           └── implementation 'org.springframework.boot:spring-boot-starter-web'
│       }
│
├── sub-module/ (서브 모듈)
│   ├── src/
│   │   ├── main/
│   │   │   ├── java/
│   │   │   │   └── com.example.subproject/
│   │   │   │       └── SubApplication.java (서브 프로젝트 시작점)
│   │   │   └── ... (추가 서브 프로젝트 관련 폴더 및 파일)
│   └── build.gradle (서브 모듈 빌드 설정)
│
└── build.gradle (루트 프로젝트 빌드 설정)


1-6. 의존성 관리 및 실행 방식

  • 1. 메인 모듈 (main-module)
  build.gradle 설정
  • "implementation"을 사용하여 "spring-boot-starter-security"와 "spring-boot-starter-web" 의존성이 추가된다.
  의존성의 범위
  • 이 의존성들은 메인 모듈 내에서만 사용된다.
  • 메인 모듈에 구현된 기능(예: 보안 로직, 웹 컨트롤러)은 프로젝트 내 다른 모듈에서는 직접 사용할 수 없다. 이는 모듈 간 결합도를 낮추고, 내부 구현의 노출을 방지한다.

 

  • 2. 서브 모듈 (sub-module)
  메인 모듈과의 관계
  • 서브 모듈은 필요에 따라 메인 모듈의 API(예: REST 컨트롤러, 서비스 클래스)에 의존성을 추가할 수 있다.
  의존성 접근 제한
  • 서브 모듈은 메인 모듈의 "implementation"으로 추가된 내부 의존성(예: "spring-boot-starter-security", "spring-boot-starter-web"의 클래스나 메소드)에 직접 접근할 수 없다. 이는 서브 모듈이 메인 모듈의 기능을 활용하면서도, 메인 모듈의 내부 구현에는 의존하지 않게 함으로써, 각 모듈의 독립성을 유지한다.

 

 

예시를 통해 의존성 접근 제한을 이해해보자


  • 예시: 서브 모듈과 메인 모듈의 상호작용
  1. 메인 모듈 (Main Module)
  • spring-boot-starter-web과 spring-boot-starter-security를 implementation 의존성으로 포함하고 있다. 이 모듈은 REST API와 보안 기능을 제공한다.
  2. 서브 모듈 (Sub Module)
  • 메인 모듈에서 제공하는 API를 활용해 데이터를 처리하거나 보조 기능을 제공하는 모듈이다.

 

  • 상황 설명
  1. 메인 모듈의 기능
  • 메인 모듈은 @RestController를 사용하여 REST API를 제공한다. 또한, 스프링 시큐리티를 활용해 인증 및 권한 부여 기능을 구현한다.
  2. 서브 모듈의 활용
  • 서브 모듈은 메인 모듈에서 제공하는 REST API를 호출하여 데이터를 가져오거나 처리한다. 하지만, 서브 모듈은 메인 모듈 내부의 @RestController 클래스나 스프링 시큐리티 설정에 직접 접근할 수 없다.

 

  • 메인 모듈 코드
// Main Module
@RestController
public class MainController {
    @GetMapping("/data")
    public String getData() {
        return "Data from Main Module";
    }
}

 

  • 서브 모듈 코드
    • 서브 모듈은 메인 모듈의 /data 엔드포인트를 호출하여 데이터를 가져오지만, MainController 클래스에는 접근할 수 없다.
// Sub Module
public class SubModuleService {
    public void useMainModuleApi() {
        // 서브 모듈에서 메인 모듈의 REST API 호출
        String data = restTemplate.getForObject("http://localhost:8080/data", String.class);
        // ... 데이터 처리 로직
    }
}

 

  • 의존성과 독립성
  1. 의존성
  • 서브 모듈은 메인 모듈의 API에 의존하여 기능을 수행한다.
  2. 독립성
  • 서브 모듈은 메인 모듈의 내부 구현(@RestController, spring-boot-starter-security 구성)에 직접적으로 의존하지 않는다. 이는 서브 모듈이 메인 모듈의 변경 사항에 대해 덜 민감하게 반응하고, 각 모듈의 독립성을 유지하는 데 도움을 준다.

 

 

 

의존성 접근 제한이 이해되었다면 마지막으로 모듈의 실행 방식을 알아보자

 

 

  • 3. 실행 방식
  독립적 실행
  • 메인 모듈과 서브 모듈은 각각 자체 "main" 메서드를 통해 독립적으로 실행될 수 있다.
  통합된 실행
  • 특정 경우, 메인 모듈 실행 시 서브 모듈의 기능이나 컴포넌트가 함께 로드되고 실행될 수도 있다. 이는 프로젝트 설정에 따라 다르다.



 

 

 

지금까지의 설명으로 "모듈"을 이해했다면 implementation을 알아보자



 

2. Implementation 이해하기

다음 그래프에서는 Java Library 플러그인을 사용할 때 구성을 설정하는 방법을 설명한다.

자바 라이브러리 플러그인 구성
자바 라이브러리 플러그인 구성

 

 

2-1. implementation이란?

  • implementation 구성은 프로젝트를 빌드할 때 필요한 의존성을 정의한다. 이 의존성은 프로젝트의 빌드 결과물에 포함되기 때문에, 상용 환경에서 해당 JAR 파일을 배포하면 필요한 라이브러리가 함께 제공된다.

  • 예를 들어, Spring Boot 프로젝트에서 JPA를 사용하려면 다음과 같이 implementation 구성을 사용할 수 있다. 이렇게 하면 Spring Boot는 JPA를 사용하기 위한 모든 의존성을 자동으로 포함한다.
dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
}

 

2-2. implementation을 사용하면 얻을 수 있는 장점

  1. 결합도 감소
  • implementation 구성은 멀티 모듈 프로젝트에서 각 모듈 간의 결합도를 낮추는 데 기여한다. 예를 들어, 메인 모듈이 특정 의존성을 포함하더라도 서브 모듈에서는 이를 접근할 수 없다. 이러한 방식은 모듈 간의 결합을 방지하고 전체 시스템의 유지보수성과 확장성을 향상시킨다.
  2. 의존성의 명확성
  • implementation을 사용하면 필요한 의존성을 명확하게 선언할 수 있으며, 이를 통해 프로젝트의 구조를 보다 쉽게 이해할 수 있다.
  3. 프로젝트 확장 준비
  • 프로젝트가 성장하여 멀티 모듈 구조로 전환될 때, implementation 사용은 모듈 간 결합도 관리에 유리하게 작용한다.

 

2-3. 단일 모듈 프로젝트의 관점

  • 단일 모듈 프로젝트, 즉 루트 모듈만 있는 경우에도 implementation 사용은 중요하다. 

 

  1. 내부 의존성 관리
  • 프로젝트 빌드 시 필요한 라이브러리를 제공하며, 빌드 결과물의 크기를 최적화한다.
  2. 의존성의 명확성
  • 필요한 의존성을 명확하게 선언하여, 프로젝트의 구조를 이해하기 쉽게 만든다.
  3. 추후 확장을 위한 준비
  • 프로젝트가 성장하여 멀티 모듈 구조로 전환될 때, implementation 사용은 모듈 간 결합도 관리에 유리하게 작용한다.
결론적으로, implementation 의존성은 멀티 모듈 프로젝트뿐만 아니라 단일 모듈 프로젝트에서도 효과적인 의존성 관리 방법이다. 프로젝트의 규모나 복잡성에 관계없이 일관된 의존성 관리를 통해 프로젝트의 유지보수성과 확장성을 개선하는 데 도움이 된다.

 

 

 

지금부터 예시를 통해 implementation을 알아보자


 

3. Implementation 예시

3-1. 스프링 부트 웹 애플리케이션 개발

  • implementation 'org.springframework.boot:spring-boot-starter-web'은 프로젝트에 필요한 스프링 MVC, 임베디드 톰캣 등의 웹 개발 컴포넌트를 제공한다. 이는 웹 애플리케이션 개발에 필수적인 요소들을 포함한다.
dependencies {
    // 스프링 부트 스타터 웹은 웹 애플리케이션 개발에 필요한 스프링 MVC, 톰캣 등을 제공
    implementation 'org.springframework.boot:spring-boot-starter-web'
}

 

3-2. Implementation 의존성의 내부 사용

  • implementation으로 추가된 의존성은 해당 모듈 내에서만 사용되고, 모듈 외부로 노출되지 않는다. 이는 모듈 간 결합도를 낮추고, 각 모듈의 독립성을 유지하는 데 도움을 준다.

 

3-3. 모듈 간 의존성과 독립성

  • 예를 들어, 모듈 A가 spring-boot-starter-web을 implementation으로 포함하고 있고, 모듈 B가 모듈 A에 의존하는 경우, 모듈 B는 모듈 A의 기능을 사용할 수 있지만, spring-boot-starter-web의 클래스나 메소드를 직접 사용할 수는 없다. 이로 인해 각 모듈의 독립성이 유지되고, 모듈 간 의존성이 줄어든다.

 

 

이 내용을 조금 더 자세히 알아보자


  • 예시 모듈의 구조는 다음과 같다.
  루트 모듈 (Root Module)
  • 프로젝트의 최상위 레벨을 대표하는 모듈입니다. 이 모듈이 프로젝트 전체를 포함하고 관리한다.
  메인 모듈 (Module A)
  • 주요 기능을 담당하는 모듈로, 예를 들어 애플리케이션의 핵심 로직을 처리한다.
  서브 모듈 (Module B)
  • 메인 모듈과는 별도로 존재하며, 특정 기능이나 작업을 수행한다. 예를 들어, 다른 기능을 담당하는 웹 서비스나 라이브러리일 수 있다.

 

  • 멀티 모듈 프로젝트에서 메인 모듈(A)에 implementation 'org.springframework.boot:spring-boot-starter-web'로 선언된 경우, 이 의존성(여기서는 spring-web 관련 기능)은 메인 모듈 내에서만 사용 가능하다. 따라서 서브 모듈(B)에서는 메인 모듈(A)에 구현된 @Controller 같은 클래스나 어노테이션에 직접 접근할 수 없다.

  • 이렇게 구성하는 이유는 각 모듈의 기능과 의존성을 명확하게 분리하고, 모듈 간의 결합도를 낮추기 위함이다. 서브 모듈(B)에서 spring-web 관련 기능이 필요한 경우, 서브 모듈 자체의 build.gradle 파일에 spring-boot-starter-web을 독립적으로 추가해야 한다.

  • 이는 서브 모듈이 자신만의 의존성을 관리하고, 필요한 기능을 독립적으로 제공할 수 있도록 하여, 전체 프로젝트의 유지보수성과 확장성을 향상시키는 방식이다.

 

3-4. 프로젝트 규모와 의존성 관리의 중요성

  • 프로젝트 규모가 커질수록, implementation을 통한 의존성 관리의 중요성이 커진다. 이 방식은 각 모듈이 필요한 부분만을 사용하게 하여 불필요한 의존성으로 인한 문제를 피하고, 프로젝트의 확장성과 유지보수성을 향상시킨다.

 

 

 

다음으로 runtimeOnly에 대해서 알아보자



4. runtimeOnly 이해하기

4-1. runtimeOnly의 역할

  • runtimeOnly 의존성은 프로젝트 실행 시에만 필요하며, 컴파일 시점에는 필요하지 않은 의존성을 관리하는 데 사용된다. 이는 빌드 과정을 최적화하고, 필요하지 않은 의존성을 제외함으로써 컴파일 시간을 단축시키는 효과가 있다.

 

4-2. runtimeOnly의 장점

  • 런타임에 필요한 의존성을 분리함으로써, 애플리케이션의 시작 시간과 전반적인 성능이 개선된다. 서버나 클라우드 환경에서 애플리케이션을 운영할 때 특히 중요한 요소로, 데이터베이스 드라이버나 로깅 프레임워크와 같은 의존성이 이에 해당된다.

  • 예를 들어, MySQL JDBC 드라이버는 애플리케이션이 데이터베이스와 상호작용하는 런타임 시에는 필수적이지만, 컴파일 시에는 필요하지 않다. 이러한 의존성을 runtimeOnly로 선언함으로써, 애플리케이션의 컴파일 과정은 더 간소화되며, 런타임에만 필요한 리소스를 로드하여 애플리케이션의 효율성을 높일 수 있다.

 

 

 

runtimeOnly는 어떻게 사용될까?


 

5. runtimeOnly 예시

5-1. 데이터베이스 접근을 위한 JDBC 드라이버 설정

dependencies {
    // MySQL 데이터베이스에 접근하기 위한 JDBC 드라이버
    runtimeOnly 'mysql:mysql-connector-java'
}

 

5-2. MySQL JDBC 드라이버의 역할

  • "mysql:mysql-connector-java"는 MySQL 데이터베이스와의 통신을 위한 JDBC 드라이버로, 이 드라이버는 Java 애플리케이션이 데이터베이스와 통신하는 데 필수적이다. runtimeOnly로 지정함으로써, 런타임 시에만 이 드라이버가 로드되고 사용되며, 컴파일 시에는 포함되지 않는다.

 

5-3. RuntimeOnly의 중요성

  • 이 드라이버를 runtimeOnly로 지정하는 이유는, 애플리케이션의 컴파일 시에는 실제로 데이터베이스와의 연결이 필요하지 않기 때문이다. 컴파일 시에는 데이터베이스 접근 로직의 올바른 구문과 타입만 확인하면 되고, 실제 데이터베이스 연결은 필요하지 않다.

 

5-4. 런타임 시의 작동 방식

  • 하지만 애플리케이션이 실행되는 런타임에서는 다르다. 애플리케이션이 데이터베이스에 데이터를 조회하거나 저장하는 등의 작업을 할 때, 이 JDBC 드라이버가 필수적으로 필요하다. 이 드라이버가 없으면 Java 애플리케이션은 MySQL 데이터베이스와 통신할 수 없다.

 

 

 

이렇게 runtimeOnly를 사용함으로써 빌드 시간과 결과물의 크기를 최적화하고, 필요한 시점에만 특정 의존성을 로드하는 효율적인 방법을 구현할 수 있다.



6. Implementation과 RuntimeOnly: 차이점과 사용 상황

6-1. implementation 사용법과 특징

  • implementation 의존성은 프로젝트의 내부 모듈에서만 사용되며, 외부 모듈에는 노출되지 않는다. 이는 컴파일 시간에만 필요하며, 최종 빌드 결과물에는 포함되지 않는다.

  1. 모듈 간 의존성 감소
  • 프로젝트의 내부 구현에만 영향을 미치므로, 다른 모듈에서 이 의존성에 직접 접근할 수 없다.
  2. 변경의 영향 최소화
  • 한 모듈의 변경이 다른 모듈에 미치는 영향을 줄여, 프로젝트의 유지보수성과 확장성을 향상시킨다.

 

 

6-2. runtimeOnly의 역할과 중요성

  • runtimeOnly 의존성은 컴파일 시점에는 필요하지 않지만, 런타임 시 필요한 의존성을 정의한다. 이는 데이터베이스 드라이버나 로깅 프레임워크와 같이 실행 시에만 필요한 라이브러리들에 주로 사용된다.
  1. 컴파일 시간 단축
  • 런타임에만 필요한 의존성을 분리함으로써 컴파일 시간을 줄인다.
  2. 빌드 결과물 최적화
  • 런타임 의존성의 분리로 빌드 결과물의 크기를 줄이고 성능을 최적화한다.

 

 

6-3. 결론: 의존성 관리의 중요성

  • implementation과 runtimeOnly는 각각의 상황과 요구사항에 맞게 사용되어야 한다. 올바른 의존성 관리는 프로젝트의 성능 최적화, 유지보수의 용이성, 그리고 코드 품질 향상에 결정적인 역할을 한다. 이 두 의존성 유형을 적절히 활용함으로써, 효율적이고 유지보수가 용이한 프로젝트 구조를 만들 수 있다.

 

 

 

지금까지 build.gradle에 라이브러리 의존성을 작성하는 2가지 방법에 대해서 알아봤다. 평상시에는 신경 쓰지 않아서 몰랐는데 프로메테우스를 사용하기 위해 gradle에 의존성을 추가하던 중 runtimeOnly라는 다른 방식으로 작성하도록 되어있어서 바로 조사를 해봤고 이를 정리해서 공유한다. 이 글을 읽는 독자분께도 좋은 지식이 되었으면 한다.



 

 

The Java Library Plugin

The Java Library plugin expands the capabilities of the Java Plugin (java) by providing specific knowledge about Java libraries. In particular, a Java library exposes an API to consumers (i.e., other projects using the Java or the Java Library plugin). All

docs.gradle.org

gradle의 작동 원리가 궁금하다면? 👇🏻👇🏻

 

Gradle 작동 원리: Groovy와 Kotlin을 통한 의존성 관리

이번 포스트에서는 Gradle의 작동 원리를 알아보자 Gradle에서 Groovy나 Kotlin을 사용해 의존성을 관리하는 방식을 이해하려면, 먼저 Gradle이 어떻게 작동하는지, 그리고 이들 언어가 Java와 어떻게 상

curiousjinan.tistory.com

 

반응형

'Spring > Spring 기초 지식' 카테고리의 다른 글

주니어 개발자의 결합도(Coupling) 이해하기: 스프링에서 결합도 관리하기  (2) 2023.12.25
[Spring] RuntimeException의 메시지  (0) 2023.12.21
Spring Boot 생성자 주입 알아보기1: @ComponentScan 동작원리  (1) 2023.12.02
SpringBoot: 리소스 관리하기 (resource)  (1) 2023.11.25
Spring Boot 3 및 Spring Batch 5에서 배치 테이블 자동 생성 문제 해결하기  (0) 2023.11.23
'Spring/Spring 기초 지식' 카테고리의 다른 글
  • 주니어 개발자의 결합도(Coupling) 이해하기: 스프링에서 결합도 관리하기
  • [Spring] RuntimeException의 메시지
  • Spring Boot 생성자 주입 알아보기1: @ComponentScan 동작원리
  • SpringBoot: 리소스 관리하기 (resource)
Stark97
Stark97
문의사항 또는 커피챗 요청은 링크드인 메신저를 보내주세요! : https://www.linkedin.com/in/writedev/
  • Stark97
    오늘도 개발중입니다
    Stark97
  • 전체
    오늘
    어제
    • 분류 전체보기 (240)
      • 개발지식 (20)
        • 스레드(Thread) (8)
        • WEB, DB, GIT (3)
        • 디자인패턴 (8)
      • JAVA (21)
      • Spring (88)
        • Spring 기초 지식 (35)
        • Spring 설정 (6)
        • JPA (7)
        • Spring Security (17)
        • Spring에서 Java 활용하기 (8)
        • 테스트 코드 (15)
      • 아키텍처 (5)
      • MSA (14)
      • DDD (7)
      • gRPC (9)
      • Apache Kafka (18)
      • DevOps (23)
        • nGrinder (4)
        • Docker (1)
        • k8s (1)
        • 테라폼(Terraform) (12)
      • AWS (32)
        • ECS, ECR (14)
        • EC2 (2)
        • CodePipeline, CICD (8)
        • SNS, SQS (5)
        • RDS (2)
      • notion&obsidian (3)
  • 링크

    • notion기록
    • 깃허브
    • 링크드인
  • hELLO· Designed By정상우.v4.10.0
Stark97
Gradle: Implementation과 RuntimeOnly의 차이점 및 활용 방법
상단으로

티스토리툴바