Open rkaehdaos opened 7 months ago
org.graalvm.buildtools.native
플러그인과 spring-graalvm-native-plugin
은 GraalVM을 사용하여 Java 애플리케이션을 네이티브 이미지로 컴파일하는 과정을 단순화하고 자동화하기 위한 Gradle 플러그인입니다. 이 두 플러그인은 비슷한 목적을 가지고 있지만, 세부적인 용도와 특성에서 차이를 보입니다.
spring-graalvm-native-plugin
만으로도 Spring Boot 애플리케이션을 네이티브 이미지로 빌드하는 것이 가능합니다. 실제로 spring-graalvm-native-plugin
은 Spring Boot 애플리케이션을 네이티브 이미지로 컴파일하기 위해 필요한 모든 설정과 최적화를 포함하고 있으며, 이 과정을 단순화합니다. 따라서 org.graalvm.buildtools.native
플러그인 없이도 충분히 네이티브 이미지 빌드가 가능합니다. 그러나 일반 Java/Kotlin 애플리케이션을 네이티브 이미지로 빌드하려는 경우나, Spring Boot 이외의 프레임워크를 사용하는 경우에는 org.graalvm.buildtools.native
플러그인이 더 적합할 수 있습니다.
spring-graalvm-native-plugin
은 Spring Boot 애플리케이션에 특화되어 있으며, org.graalvm.buildtools.native
없이도 Spring Boot 애플리케이션의 네이티브 이미지 빌드를 충분히 지원합니다. 프로젝트의 요구사
항과 사용하는 기술 스택에 따라 적절한 플러그인을 선택하는 것이 중요합니다. Spring Boot 애플리케이션의 경우 spring-graalvm-native-plugin
을, 그 외의 경우에는 org.graalvm.buildtools.native
플러그인을 고려할 수 있습니다.
ref : https://github.com/ayltai/spring-graalvm-native-plugin
id 'com.github.ayltai.spring-graalvm-native-plugin' version '1.4.10'
FAILURE: Build failed with an exception.
* What went wrong:
Execution failed for task ':compileJava'.
> Could not resolve all files for configuration ':compileClasspath'.
> Could not resolve org.springframework.experimental:spring-native:0.10.0.
Required by:
project :
> Could not resolve org.springframework.experimental:spring-native:0.10.0.
> Could not get resource 'https://repo.spring.io/release/org/springframework/experimental/spring-native/0.10.0/spring-native-0.10.0.pom'.
> Could not GET 'https://repo.spring.io/release/org/springframework/experimental/spring-native/0.10.0/spring-native-0.10.0.pom'. Received status code 401 from server:
스프링 네이티브(Spring Native)를 사용하기 위해서는 해당 의존성을 프로젝트에 추가해야 합니다. 하지만, Spring Boot 2.4.x 이상 버전부터는 Spring Native가 Spring Boot와 더 밀접하게 통합되었고, Spring Native의 구성 방식이 몇 가지 변경되었습니다.
spring-native
만 합치면 되는 것 처럼 보임<dependencies>
<!-- ... -->
<dependency>
<groupId>org.springframework.experimental</groupId>
<artifactId>spring-native</artifactId>
<version>0.12.1</version>
</dependency>
</dependencies>
// Gradle에서 명시적으로 스프링 네이티브 종속성을 추가할 필요 없이 Spring AOT 플러그인이 자동으로 추가합니다.
자바의 최신 버전에서 AOT(Ahead-of-Time) 컴파일과 JIT(Just-in-Time) 컴파일은 둘 다 성능 향상을 목적으로 하는 다른 컴파일 방법입니다.
AOT 컴파일은 애플리케이션 코드를 기계어로 변환하여 실행 파일로 만드는 방법입니다. 이렇게 변환된 코드는 실행할 때마다 다시 컴파일할 필요가 없습니다.
중요 히스토리: Java 9부터 AOT 컴파일 기능이 도입되었으며, GraalVM과 같은 플랫폼을 통해 제공됩니다.
장점:
단점:
적용 시 고려 사항:
JIT 컴파일은 애플리케이션을 실행할 때 바이트 코드를 기계어로 컴파일하는 방법입니다. 이는 실행 시간에 동적으로 최적화되는 특징을 가지고 있습니다.
중요 히스토리: JIT 컴파일러는 초기부터 자바 플랫폼의 핵심 요소였으며, Java HotSpot VM에서 주로 사용되었습니다. 최근 버전에서는 GraalVM과 같은 새로운 JIT 컴파일러가 등장했습니다.
장점:
단점:
적용 시 고려 사항:
결론적으로, AOT 컴파일은 시작 시간 및 메모리 사용량을 최적화하는 데 유용하며, JIT 컴파일은 동적 최적화와 플랫폼 독립성을 제공하는 데 유용합니다. 선택은 애플리케이션의 요구 사항과 운영 환경에 따라 달라집니다.
AOT(Ahead-Of-Time) 컴파일은 프로그램을 실행하기 전에 코드를 머신 코드로 컴파일하는 프로세스입니다. 이는 JIT(Just-In-Time) 컴파일의 대안으로, JIT 컴파일은 프로그램이 실행되는 동안 코드를 컴파일합니다. AOT 컴파일은 애플리케이션의 시작 시간을 단축하고 실행 시간 성능을 향상시킬 수 있지만, 컴파일 시간이 길어지고 결과적인 바이너리 크기가 커질 수 있습니다.
장점:
단점:
Spring Boot의 "새로운" AOT 기능에 대한 언급은, Spring Framework 6와 Spring Boot 3에서 소개된 AOT 기술과 관련이 있습니다. 이전 버전의 Spring Boot에서는 AOT 기능이 명시적으로 제공되지 않았습니다. 그러나 Spring Native 프로젝트의 일환으로, Spring Boot 3와 함께 통합된 새로운 AOT 기능이 소개되었습니다.
새로운 AOT 기능:
기존 기능과의 차이점:
새로운 AOT 기능은 Spring Framework와 Spring Boot의 핵심 일부로 통합되어, AOT 컴파일을 더 자연스럽고 손쉽게
사용할 수 있게 되었습니다. 이는 네이티브 이미지 빌드뿐만 아니라, JVM에서의 성능 최적화에도 적용됩니다.
Spring Boot의 새로운 AOT 기능은 애플리케이션의 시작 시간과 성능을 개선하기 위한 중요한 발전입니다. 이 기능을 활용하면, Spring Boot 애플리케이션의 배포와 실행이 더욱 효율적이고 빨라질 수 있습니다.
spring-graalvm-native-plugin
전환은 필요한가?org.graalvm.buildtools.native
플러그인과 spring-graalvm-native-plugin
(혹은 Spring Native)를 사용하여 생성된 네이티브 이미지 사이의 크기, 성능 및 효율성에는 차이가 있을 수 있습니다. 이 차이는 사용하는 플러그인이 Spring Boot 애플리케이션의 특성을 얼마나 잘 최적화하고 이해하느냐에 따라 달라집니다.
spring-graalvm-native-plugin
은 Spring Boot 애플리케이션에 특화되어 있습니다. Spring Native는 Spring Boot의 구성, 시작 과정, 런타임 동작을 깊이 이해하고 있으며, 이에 따라 네이티브 이미지 빌드 과정에서 필요한 다양한 최적화를 수행할 수 있습니다. 이는 시작 시간 감소, 실행 성능 향상, 메모리 사용량 감소 등의 이점으로 이어질 수 있습니다.
org.graalvm.buildtools.native
플러그인은 더 일반적인 사용 사례를 대상으로 하며, Java/Kotlin 애플리케이션을 네이티브 이미지로 컴파일하는 데 사용됩니다. 이 플러그인은 Spring Boot 애플리케이션을 포함한 다양한 유형의 애플리케이션에 사용할 수 있지만, spring-graalvm-native-plugin
만큼 Spring Boot에 특화된 최적화를 제공하지는 않을 수 있습니다.
네이티브 이미지의 크기와 성능은 사용하는 플러그인뿐만 아니라 애플리케이션의 특성, 의존성, 구성 방식 등 다양한 요소에 의해 영향을 받습니다. 따라서, 두 플러그인에 의해 생성된 네이티브 이미지의 차이는 애플리케이션마다 다를 수 있습니다.
일반적으로, spring-graalvm-native-plugin
을 사용하면 Spring Boot 애플리케이션에 대한 보다 깊은 최적화가 가능하기 때문에, 네이티브 이미지의 크기가 더 작고 시작 시간이 더 짧으며 성능이 더 좋을 수 있습니다. 그러나 이는 반드시 모든 경우에 해당하는 것은 아닙니다.
현재 org.graalvm.buildtools.native
플러그인을 사용하고 있고, 프로젝트의 빌드 및 배포 과정에 만족한다면, 굳이 변경할 필요는 없을 수 있습니다. 특히, 현재 설정이 프로젝트 요구사항을 충족시키고, 빌드 및 실행 성능에 문제가 없다면, 변경에 따른 이점이 변경을 위한 시간과 노력을 정당화할 만큼 크지 않을 수 있습니다.
그러나 시작 시간, 실행 성능, 메모리 사용량 등의 면에서 개선의 여지가 있다고 생각되거나, Spring Boot의 최신 기능과 통합을 더 잘 활용하고 싶다면 spring-graalvm-native-plugin
으로의 전환을 고려할 수 있습니다. 특히, Spring Boot 3.x 이상을 사용하는 경우, Spring Native와의 통
합은 더욱 개선되어 있으며, 네이티브 이미지 생성 과정에서 여러 최적화와 이점을 제공할 수 있습니다.
결론적으로, 네이티브 이미지의 크기와 성능에 있어서 두 플러그인 사이에 차이가 있을 수 있으며, 특히 Spring Boot 애플리케이션에 대해서는 spring-graalvm-native-plugin
이 보다 많은 최적화를 제공할 가능성이 높습니다. 프로젝트의 요구사항과 현재 상황을 고려하여 최적의 선택을 하는 것이 중요합니다.
ref : https://github.com/ayltai/spring-graalvm-native-plugin#configuration
nativeImage { mainClassName = 'com.example.springboot.Application' // more option: https://github.com/ayltai/spring-graalvm-native-plugin#configuration reportExceptionStackTraces = true // Exception시 더 상세 정보 제공 removeUnusedAutoConfig = true // 사용하지 않는 구성의 제거 비활성화 removeYamlSupport = false // 스프링부트 Yaml 지원 제거->컴파일 속도↑, binary size↓ // maxHeapSize = '6G' // GraalVM native build max java heap
}
@SpringBootApplication
에 proxyBeanMethods = false
설정 필요 @SpringBootApplication(proxyBeanMethods = false)
public class TomcatApplication {
public static void main(final String[] args) {
SpringApplication.run(TomcatApplication.class, args);
}
}
buildNativeImage
<buildDir>/native
proxyBeanMethods
@SpringBootApplication
애노테이션의 proxyBeanMethods
속성은 Spring Framework에서 매우 중요한 역할일반적인 상황에서,
proxyBeanMethods=true
로 설정하면, Spring 컨테이너는 해당 구성 클래스 내의 @Bean
메서드 호출을 감시하여, 이러한 메서드로부터 반환되는 빈이 항상 싱글톤임을 보장합니다. 즉, 애플리케이션의 어느 곳에서든 해당 @Bean
메서드를 호출하더라도 항상 동일한 인스턴스가 반환되어 애플리케이션의 일관성을 유지할 수 있습니다.proxyBeanMethods=true
설정은 구성 클래스 내부에서 @Bean
메서드들 간의 호출을 통해 서로 의존하는 빈을 정의할 때 유용합니다. 이 설정을 사용하면, 각 빈의 의존성이 올바르게 처리되며, 의존성 주입에 실패하거나 빈 생성 순서 문제로 인한 오류를 방지할 수 있습니다.proxyBeanMethods=false
로 설정하면, 구성 클래스 내에서 @Bean
메서드를 직접 호출할 경우 Spring 컨테이너가 이를 관리하지 않게 됩니다. 따라서, 같은 @Bean
메서드를 여러 번 호출하면 메서드 호출마다 새로운 인스턴스가 생성될 수 있으며, 이는 의도하지 않은 결과를 초래할 수 있습니다.@Bean
메서드가 다른 @Bean
메서드를 직접 호출하는 경우, proxyBeanMethods=false
설정은 각 호출이 독립적으로 처리되기 때문에, 서로 의존하는 빈들 사이에 문제가 발생할 수 있습니다. 예를 들어, 설정 또는 상태 정보를 공유해야 하는 빈들이 서로 다른 인스턴스를 사용하게 되어 문제가 발생할 수 있습니다.proxyBeanMethods=true
의 기본 설정은 대부분의 Spring 애플리케이션에서 안전하고 일관된 빈 관리를 위해 권장됩니다. 이 설정은 싱글톤 패턴의 보장과 Spring의 전체적인 빈 생명주기 관리에 도움을 줍니다. 반면, proxyBeanMethods=false
설정은 빈 생성과 관련된 오버헤드를 줄이고 성능을 향상시킬 수 있지만, 빈의 싱글톤 보장 및 빈 간 의존성 관리에 주의해야 합니다. 따라서, proxyBeanMethods=false
를 사용할 때는 구성 클래스의 @Bean
메서드 호출이 애플리케이션의 나머지 부분에 영향을 주지 않도록 주의해야 하며, 필요한 경우 명시적인 빈 의존성을 사용하여 빈을 주입해야 합니다.
@SpringBootApplication
애노테이션의 proxyBeanMethods
속성은 Spring Framework에서 매우 중요한 역할을 합니다. 이 속성은 Spring Boot 애플리케이션의 구성 클래스에서 @Bean
메서드 간의 호출이 컨테이너를 통해 이루어져야 하는지 여부를 제어합니다.
true
로 설정되면, @Bean
메서드 간의 호출이 Spring 컨테이너를 통해 이루어집니다. 이를 통해 @Bean
메서드에서 반환된 인스턴스가 싱글톤임이 보장됩니다. 즉, 같은 Spring 컨텍스트 내에서 해당 @Bean
메서드를 여러 번 호출해도 항상 같은 인스턴스를 반환합니다.@SpringBootApplication
애노테이션의 proxyBeanMethods
속성의 기본값은 true
입니다.proxyBeanMethods
가 true
일 경우, Spring은 CGLIB 프록시를 사용하여 @Bean
메서드를 감싸서 호출할 때마다 컨테이너를 통하게 됩니다. 이는 런타임에 추가적인 프록시 호출과 관련된 오버헤드를 발생시키며, 애플리케이션의 시작 시간과 성능에 영향을 줄 수 있습니다.proxyBeanMethods
가 true
로 설정되어 있으면, 이러한 동적 프록시 생성이 네이티브 이미지 생성 과정에 부정적인 영향을 줄 수 있습니다.proxyBeanMethods
를 false
로 설정하면, @Bean
메서드 간의 직접 호출이 컨테이너를 거치지 않고, 직접 Java 메서드 호출로 처리됩니다. 이는 프록시 관련 오버헤드를 제거하여 애플리케이션의 시작 시간을 단축시키고, 런타임 성능을 향상시킵니다.proxyBeanMethods=false
설정은 네이티브 이미지 생성 시 추가적인 최적화를 가능하게 하여, 더 작고 효율적인 네이티브 이미지를 생성할 수 있게 합니다.@SpringBootApplication(proxyBeanMethods = false)
설정은 특히 Spring Boot 애플리케이션을 GraalVM 네이티브 이미지로 컴파일할 때 중요합니다. 이 설정은 애플리케이션의 시작 시간을 단축시키고 네이티브 이미지의 크기와 성능을 최적화하는 데 도움을 줍니다. 그러나 이 설정을 사용할 때는 @Bean
메서드 간에 직접 호출이 싱글톤 인스턴스를 반환하지 않음을 인지하고 있어야 합니다. 대부분의 경우, 이러한 직접 호출은 구성 클래스 내에서만 발생하며, 대부분의 애플리
케이션에서는 문제가 되지 않습니다.
네, proxyBeanMethods=true
일 때 @Bean
메서드 호출 시 컨테이너를 통한다는 말에서 언급된 "컨테이너"는 바로 Spring Framework에서 제공하는 스프링 컨테이너(Spring Container)를 말합니다. 스프링 컨테이너는 스프링 애플리케이션의 핵심이며, 스프링 빈(Bean)의 생명주기와 의존성 관리를 담당합니다.
스프링 컨테이너는 스프링 프레임워크의 핵심 부분으로, 애플리케이션 내의 객체(스프링 빈)를 생성하고, 구성하며, 조립하는 역할을 합니다. 이 컨테이너는 애플리케이션의 구성 정보(예: XML 파일, 애노테이션, Java 설정 파일 등)를 기반으로 빈을 관리합니다. 스프링 컨테이너는 다음과 같은 역할을 수행합니다:
@PostConstruct
, @PreDestroy
같은 콜백 메서드를 통해 구현됩니다.proxyBeanMethods=true
설정에서, 스프링은 @Configuration
애노테이션이 붙은 클래스 내부의 @Bean
메서드를 호출할 때마다 해당 메서드 호출을 인터셉트하여 컨테이너를 통하게 합니다. 이를 위해 스프링은 CGLIB(Code Generation Library)을 사용하여 구성 클래스의 프록시를 생성합니다. 이 프록시는 실제 @Bean
메서드 호출 대신 프록시의 메서드를 호출하게 만듭니다. 프록시 메서드는 다음과 같은 과정을 거칩니다:
@Bean
메서드를 통해 생성된 빈이 싱글톤으로 관리됨을 보장합니다.@Bean
메서드를 호출하여 빈을 생성하고, 이를 컨테이너에 등록한 후 반환합니다.이 프로세스는 @Bean
메서드 간에 서로를 호출할 때마다 컨테이너를 통해 해당 빈의 싱글톤 인스턴스가 유지되도록 보장합니다. 이는 스프링의 의존성 주입 원칙을 강화하고,
애플리케이션 내의 빈 관리를 일관되게 유지하는 데 도움을 줍니다. 그러나 이 방식은 추가적인 프록시 호출과 관련된 오버헤드를 발생시키므로, 성능에 민감한 상황에서는 proxyBeanMethods=false
설정을 고려할 수 있습니다.
객체를 스프링 빈(Spring Bean)으로 관리함으로써 얻을 수 있는 주요 장점은 다음과 같습니다:
@PostConstruct
와 @PreDestroy
)을 통해 빈의 생명주기에 맞춰 커스텀 로직을 실행할 수 있습니다.객체를 스프링
빈으로 관리함으로써 얻을 수 있는 이러한 장점들은 애플리케이션의 개발, 배포 및 유지보수 과정을 대폭 단순화하고 효율화합니다.
proxyBeanMethods=false
시 위의 장점에 미치는 영향?proxyBeanMethods=false
로 설정하더라도, 바로 위에서 언급한 스프링 빈의 6가지 장점을 모두 잃게 되는 것은 아닙니다. proxyBeanMethods
설정은 주로 스프링 빈의 생성 방식과 관련된 것이며, 스프링 컨테이너가 제공하는 다양한 기능과 서비스에 대한 접근성에는 직접적인 영향을 미치지 않습니다. 그러나 proxyBeanMethods=false
설정은 스프링 컨텍스트 내에서 @Bean
메서드 호출의 동작 방식에 영향을 미칩니다. 이 설정이 갖는 주요한 영향을 살펴보겠습니다:
proxyBeanMethods=false
설정은 @Configuration
클래스 내부에서 @Bean
메서드를 직접 호출할 때 스프링 컨테이너를 우회합니다. 하지만 이것은 스프링의 의존성 주입 기능 자체에는 영향을 미치지 않으며, 스프링 컨테이너는 여전히 필드, 생성자, 설정 메서드를 통한 의존성 주입을 지원합니다.proxyBeanMethods
설정과 관계없이 빈의 생명주기를 관리합니다. @PostConstruct
, @PreDestroy
애노테이션이나 다른 라이프사이클 인터페이스를 사용한 빈의 초기화와 소멸 콜백은 proxyBeanMethods
설정에 영향을 받지 않습니다.proxyBeanMethods
설정과 무관하게 동작합니다. proxyBeanMethods=false
로 설정되어 있어도, 스프링 컨테이너는 AOP 프록시를 통해 관점(Aspect)과 어드바이스(Advice)를 적용할 수 있습니다.proxyBeanMethods
설정에 영향을 받지 않으며, 스프링은 여전히 플랫폼별 예외를 스프링의 일관된 예외로 변환할 수 있습니다.proxyBeanMethods=false
설정이 테스트 용이성에 미치는 영향은 거의 없습니다. 스프링 테스트 컨텍스트는 빈의 모킹과 테스트 환경 구성을 지원합니다.proxyBeanMethods
설정에 의존하지 않으며, 스프링의 선언적 트랜잭션 관리 기능은 여전히 사용할 수 있습니다.proxyBeanMethods=false
설정은 주로 빈 생성과 관련된 오버헤드를 줄이는 데 목적이 있으며, 스프링 애플리케이션의 대부분의 기능과 장점에는 영향을 미치지 않습니다. 다만, 이 설정을 사용할 때는 @Configuration
클래스 내부에서 @Bean
메서드를 직접 호출할 경우 각 호출이 독립적으로 처리됨을 주의해야 합니다.
proxyBeanMethods=false
설정을 사용할 때 @Configuration
클래스 내부에서 @Bean
메서드를 직접 호출하는 경우, 각 호출이 컨테이너를 통하지 않고 직접 Java 메서드 호출로 처리되기 때문에 주의가 필요합니다. 이 설정을 사용할 때 주의해야 할 점과 명시적인 빈 의존성을 사용하는 방법에 대해 설명하겠습니다.
빈 의존성의 명확성: @Configuration
클래스 내에서 @Bean
메서드를 호출하지 않도록 주의합니다. 대신, 빈의 의존성은 주로 생성자 주입을 통해 제공하는 것이 좋습니다. 이렇게 하면 스프링 컨테이너가 자동으로 의존성을 관리하고, 필요한 빈을 주입할 수 있습니다.
단일 책임 원칙: 각 @Configuration
클래스가 단일 책임 원칙을 따르도록 설계합니다. 너무 많은 빈을 하나의 구성 클래스에 정의하는 것은 피하고, 관련된 빈들을 그룹화하여 여러 구성 클래스로 나누는 것이 좋습니다.
빈 간의 명시적 연결: @Configuration
클래스 내에서 @Bean
메서드 간에 의존성이 있는 경우, 해당 의존성을 명시적으로 선언하는 것이 중요합니다. 이는 주로 생성자 주입 또는 @Autowired
애노테이션을 사용하여 수행됩니다.
아래 예시는 proxyBeanMethods=false
설정을 사용할 때, 생성자 주입을 통해 빈 의존성을 명시적으로 주입하는 방법을 보여줍니다.
@Configuration(proxyBeanMethods = false)
public class AppConfig {
// DataSource 빈 정의
@Bean
public DataSource dataSource() {
return new HikariDataSource();
}
// JdbcTemplate 빈 정의, DataSource 의존성 주입
@Bean
public JdbcTemplate jdbcTemplate(DataSource dataSource) {
return new JdbcTemplate(dataSource);
}
}
이 예시에서 jdbcTemplate
빈은 dataSource
빈에 의존합니다. proxyBeanMethods=false
설정이 적용되어 있기 때문에, dataSource()
메서드를 직접 호출하는 대신, jdbcTemplate
빈을 정의할 때 DataSource
타입의 파라미터를 추가하여 생성자 주입을 사용합니다. 스프링 컨테이너는 dataSource
빈을 자동으로 찾아 jdbcTemplate
빈의 생성자에 주입합니다. 이 방법을 사용하면 각 @Bean
메서드 호출이 스프링 컨테이너를 통하지 않아도, 스프링이 의존성을 관리하고 주입할 수 있습니다.
이와 같이 명시적인 빈 의존성을 사용하면, proxyBeanMethods=false
설정을 사용하면서도 스프링 컨테이너의 의존성 주입 기능을 효과적으로 활용할 수 있습니다. 이는 애플리케이션의 모듈성과 테스트 용이성을 향상시키며, 스프링 빈의 관리를 더욱 명확하고 안정적으로 만듭니다.
spring.threads.virtual.enabled=true
spring.threads.virtual.enabled=true
설정가상 스레드: 동시성 처리 방식에 큰 영향을 미치는 Project Loom에 도입된 기능
자세한 정보와 지침은 공식 Spring 문서 또는 리소스를 참조하는 것이 가장
spring-graalvm-native-plugin 적용 보류
org.graalvm.buildtools.native
플러그인을 이미 사용추후에 고려사항
추후 다음의 개선 여지가 있다고 생각되면 고려한다.