Spring Boot GraalVM Native Image ๋นŒ๋“œ ํ•˜๊ธฐ

GraalVM ๋„ค์ดํ‹ฐ๋ธŒ ์ด๋ฏธ์ง€๋Š” ์ปดํŒŒ์ผ๋œ ์ž๋ฐ” ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ๋ฏธ์น˜ ์ฒ˜๋ฆฌํ•˜์—ฌ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ๋Š” ๋…๋ฆฝ ์‹คํ–‰ํ˜• ์‹คํ–‰ ํŒŒ์ผ์ด๋‹ค. ๋„์ปค ์ด๋ฏธ์ง€์™€ ๊ฐ™์€ ์ด๋ฏธ์ง€๋ฅผ ์˜๋ฏธํ•˜๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋‹ค. ๋„ค์ดํ‹ฐ๋ธŒ ์ด๋ฏธ์ง€๋Š” ์ผ๋ฐ˜์ ์œผ๋กœ ๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์šฉ ๊ณต๊ฐ„์ด ๋” ์ž‘๊ณ  JVM ์ด๋ฏธ์ง€ ๋ณด๋‹ค ๋น ๋ฅด๊ฒŒ ์‹œ์ž‘ํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ์žฅ์ ์ด ์žˆ๋‹ค. ์ปจํ…Œ์ด๋„ˆ ์ด๋ฏธ์ง€๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋ฐฐํฌํ•˜๋Š” ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์— ์ ํ•ฉํ•˜๋ฉฐ, ํŠนํžˆ ์„œ๋น„์Šคํ˜• ๊ธฐ๋Šฅ(FaaS) ํ”Œ๋žซํผ๊ณผ ๊ฒฐํ•ฉํ•  ๋•Œ ์œ ์šฉํ•˜๋‹ค. Spring Boot GraalVM ๋„ค์ดํ‹ฐ๋ธŒ ์ด๋ฏธ์ง€๋Š” ์™„์ „ํ•œ ํ”Œ๋žซํผ๋ณ„ ์‹คํ–‰ํŒŒ์ผ์ด๋‹ค. ์ด๋ฒˆ ํฌ์ŠคํŒ…์—์„œ๋Š” Spring Boot ์˜ GraalVM ๋„ค์ดํ‹ฐ๋ธŒ ์ด๋ฏธ์ง€ ๋นŒ๋“œ๋ฅผ ์œ„ํ•ด์„œ ์ง€์›๋˜๋Š” ์‚ฌํ•ญ์— ๋Œ€ํ•œ ๊ณต์‹ ๋ฌธ์„œ๋ฅผ ์ •๋ฆฌํ•˜๊ณ ์ž ํ•œ๋‹ค.

JVM ๋ฐฐํฌ์™€ ์ฐจ์ด์ 

  • ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ์ •์  ๋ถ„์„์€ ๋ฉ”์ธ ์—”ํŠธ๋ฆฌ ํฌ์ธํŠธ์—์„œ ๋นŒ๋“œ ์‹œ ์ˆ˜ํ–‰๋œ๋‹ค.
  • ๋„ค์ดํ‹ฐ๋ธŒ ์ด๋ฏธ์ง€๊ฐ€ ์ƒ์„ฑ๋  ๋•Œ ๋„๋‹ฌํ•  ์ˆ˜ ์—†๋Š” ์ฝ”๋“œ๋Š” ์ œ๊ฑฐ๋˜๋ฉฐ ์‹คํ–‰ ํŒŒ์ผ์˜ ์ผ๋ถ€๊ฐ€ ๋˜์ง€ ์•Š๋Š”๋‹ค.
  • GraalVM ์€ ์ฝ”๋“œ์˜ ๋™์  ์š”์†Œ๋ฅผ ์ง์ ‘ ์ธ์‹ํ•˜์ง€ ๋ชปํ•˜๋ฏ€๋กœ ๋ฆฌํ”Œ๋ ‰์…˜, ๋ฆฌ์†Œ์Šค, ์ง๋ ฌํ™” ๋ฐ ๋™์  ํ”„๋ก์‹œ์— ๋Œ€ํ•ด ๋ฏธ๋ฆฌ ์•Œ๋ ค์ค˜์•ผ ํ•œ๋‹ค.
  • ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ํด๋ž˜์Šค ๊ฒฝ๋กœ๋Š” ๋นŒ๋“œ ์‹œ์ ์— ๊ณ ์ •๋˜๋ฉฐ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์—†๋‹ค.
  • ์ง€์—ฐ ํด๋ž˜์Šค ๋กœ๋”ฉ์ด ์—†์œผ๋ฉฐ, ์‹คํ–‰ ํŒŒ์ผ์— ํฌํ•จ๋œ ๋ชจ๋“  ๊ฒƒ์ด ์‹œ์ž‘์‹œ ๋ฉ”๋ชจ๋ฆฌ์— ๋กœ๋“œ๋œ๋‹ค.
  • ์ž๋ฐ” ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ์ผ๋ถ€ ์ธก๋ฉด์— ๋Œ€ํ•ด ์™„์ „ํžˆ ์ง€์›๋˜์ง€ ์•Š๋Š” ๋ช‡๊ฐ€์ง€ ์ œ์•ฝ ์‚ฌํ•ญ์ด ์žˆ๋‹ค.

Spring ์‚ฌ์ „ ์ฒ˜๋ฆฌ

์ผ๋ฐ˜์ ์ธ Spring Boot ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์€ ๋งค์šฐ ๋™์ ์ด๋ฉฐ ๊ตฌ์„ฑ์€ ๋Ÿฐํƒ€์ž„์— ์ˆ˜ํ–‰๋œ๋‹ค. ์‹ค์ œ๋กœ Spring Boot Auto Configuration ์˜ ๊ฐœ๋…์€ ๋Ÿฐํƒ€์ž„์˜ ์ƒํƒœ์— ๋ฐ˜์‘ํ•˜์—ฌ ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ๊ตฌ์„ฑํ•˜๋Š”๋ฐ ํฌ๊ฒŒ ์˜์กดํ•œ๋‹ค. ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ๋™์  ์ธก๋ฉด์— ๋Œ€ํ•ด์„œ GraalVM ์— ์•Œ๋ ค์ค„ ์ˆ˜๋„ ์žˆ์ง€๋งŒ, ๊ทธ๋ ‡๊ฒŒ ํ•˜๋ฉด ์ •์  ๋ถ„์„์˜ ์ด์ ์„ ๋Œ€๋ถ€๋ถ„ ์ƒ์‹คํ•˜๊ฒŒ ๋˜๋ฏ€๋กœ Spring Boot๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋„ค์ดํ‹ฐ๋ธŒ ์ด๋ฏธ์ง€๋ฅผ ์ƒ์„ฑํ• 
๋•Œ๋Š” closed-world ๋กœ ๊ฐ€์ •๋˜๊ณ  ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ๋™์  ์ธก๋ฉด์ด ์ œํ•œ๋œ๋‹ค.

  • ํด๋ž˜์Šค ๊ฒฝ๋กœ๋Š” ๋นŒ๋“œ ์‹œ์ ์— ๊ณ ์ •๋˜๊ณ  ์™„์ „ํžˆ ์ •์˜๋œ๋‹ค.
  • ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์— ์ •์˜๋œ ๋นˆ์€ ๋Ÿฐํƒ€์ž„์— ๋ณ€๊ฒฝํ•  ์ˆ˜ ์—†๋‹ค.
    • Spring @Prifile ์–ด๋…ธํ…Œ์ด์…˜ ๋ฐ ํ”„๋กœํ•„๋ณ„ ๊ตฌ์„ฑ์—๋Š” ์ œํ•œ์ด ์žˆ๋‹ค.
    • ๋นˆ์ด ์ƒ์„ฑ๋˜๋ฉด ๋ณ€๊ฒฝ๋˜๋Š” ์†์„ฑ(@ConditionalOnProperty ๋ฐ .enable ์†์„ฑ ๋“ฑ)์€ ์ง€์›๋˜์ง€ ์•Š๋Š”๋‹ค.

์ด๋Ÿฌํ•œ ์ œ์•ฝ์‚ฌํ•ญ์ด ์ ์šฉ๋˜๋ฉด Spring ์ด ๋นŒ๋“œ ์‹œ๊ฐ„ ๋™์•ˆ ์‚ฌ์ „ ์ฒ˜๋ฆฌ๋ฅผ ์ˆ˜ํ–‰ํ•˜๊ณ  GraalVM์ด ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ์ถ”๊ฐ€ ์ž์‚ฐ์„ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ๋‹ค.
Spring AOT(Ahead-of-Time) ์ฒ˜๋ฆฌ๋œ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์€ ์ผ๋ฐ˜์ ์œผ๋กœ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ƒ์„ฑ๋œ๋‹ค.

  • Java source code
  • Bytecode (for dynmic proxies etc)
  • GraalVM JSON hint files
    • Resource hints (resource-config.json)
    • Reflection hints (reflect-config.json)
    • Serialization hints (serialization-config.json)
    • Java Proxy hints (proxy-config.json)
    • JNI hints (jni-config.json)

Source Code Generation

์•„๋ž˜๋Š” ์ผ๋ฐ˜์ ์ธ @Configuration ํด๋ž˜์Šค ์ฝ”๋“œ๋‹ค.

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration(proxyBeanMethods = false)
public class MyConfiguration {

    @Bean
    public MyBean myBean() {
        return new MyBean();
    }

}
Java

Spring ์€ MyBean ์ธ์Šคํ„ด์Šค๊ฐ€ ํ•„์š”ํ•œ ๊ฒฝ์šฐ myBean() ๋ฉ”์†Œ๋“œ๋ฅผ ํ˜ธ์ถœํ•˜๊ณ  ๊ทธ ๊ฒฐ๊ณผ๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ•œ๋‹ค๋Š” ๊ฒƒ์„ ์•Œ๊ณ  ์žˆ๋‹ค. JVM ์—์„œ ์‹คํ–‰๋˜๋Š” ๊ฒฝ์šฐ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ์‹œ์ž‘๋  ๋•Œ @Configuration ํด๋ž˜์Šค ๊ตฌ๋ฌธ ๋ถ„์„์ด ์ˆ˜ํ–‰๋˜๊ณ  ๋ฆฌํ”Œ๋ ‰์…˜์„ ์‚ฌ์šฉํ•˜์—ฌ @Bean ๋ฉ”์„œ๋“œ๊ฐ€ ํ˜ธ์ถœ๋œ๋‹ค.
๋„ค์ดํ‹ฐ๋ธŒ ์ด๋ฏธ์ง€๋ฅผ ์ƒ์„ฑํ•  ๋•Œ Spring ์€ ๋‹ค๋ฅธ ๋ฐฉ์‹์œผ๋กœ ์ž‘๋™ํ•œ๋‹ค. ๋Ÿฐํƒ€์ž„์— @Configuration ํด๋ž˜์Šค๋ฅผ ํŒŒ์‹ฑํ•˜๊ณ  ๋นˆ ์ •์˜๋ฅผ ์ƒ์„ฑํ•˜๋Š” ๋Œ€์‹  ๋นŒ๋“œ ํƒ€์ž„์— ์ด๋ฅผ ์ˆ˜ํ–‰ํ•œ๋‹ค. @Bean ์ •์˜๊ฐ€ ๋ฐœ๊ฒฌ๋˜๋ฉด ์ด๋ฅผ ์ฒ˜๋ฆฌํ•˜์—ฌ GraalVM ์ปดํŒŒ์ผ๋Ÿฌ์—์„œ ๋ถ„์„ํ•  ์ˆ˜ ์žˆ๋Š” ์†Œ์Šค ์ฝ”๋“œ๋กœ ๋ณ€ํ™˜ํ•œ๋‹ค.
Spring AOT ํ”„๋กœ์„ธ์Šค๋Š” ์œ„์˜ Configuration ํด๋ž˜์Šค๋ฅผ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์ฝ”๋“œ๋กœ ๋ณ€ํ™˜ํ•œ๋‹ค.

import org.springframework.beans.factory.aot.BeanInstanceSupplier;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.RootBeanDefinition;

/**
 * Bean definitions for {@link MyConfiguration}.
 */
public class MyConfiguration__BeanDefinitions {

    /**
     * Get the bean definition for 'myConfiguration'.
     */
    public static BeanDefinition getMyConfigurationBeanDefinition() {
        Class<?> beanType = MyConfiguration.class;
        RootBeanDefinition beanDefinition = new RootBeanDefinition(beanType);
        beanDefinition.setInstanceSupplier(MyConfiguration::new);
        return beanDefinition;
    }

    /**
     * Get the bean instance supplier for 'myBean'.
     */
    private static BeanInstanceSupplier<MyBean> getMyBeanInstanceSupplier() {
        return BeanInstanceSupplier.<MyBean>forFactoryMethod(MyConfiguration.class, "myBean")
            .withGenerator((registeredBean) -> registeredBean.getBeanFactory().getBean(MyConfiguration.class).myBean());
    }

    /**
     * Get the bean definition for 'myBean'.
     */
    public static BeanDefinition getMyBeanBeanDefinition() {
        Class<?> beanType = MyBean.class;
        RootBeanDefinition beanDefinition = new RootBeanDefinition(beanType);
        beanDefinition.setInstanceSupplier(getMyBeanInstanceSupplier());
        return beanDefinition;
    }

}
Java

Spring AOT ๋Š” ๋ชจ๋“  ๋นˆ ์ •์˜์— ๋Œ€ํ•ด์„œ ์ด์™€ ๊ฐ™์€ ์ฝ”๋“œ๋ฅผ ์ƒ์„ฑํ•œ๋‹ค. ๋นˆ ์‚ฌํ›„ ์ฒ˜๋ฆฌ๊ฐ€ ํ•„์š”ํ•œ ๊ฒฝ์šฐ(@Autowired ๋ฉ”์„œ๋“œ ํ˜ธ์ถœ ๊ฐ™์€) ์ฝ”๋“œ๋ฅผ ์ƒ์„ฑํ•œ๋‹ค. ๋˜ํ•œ AOT ์ฒ˜๋ฆฌ๋œ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ์‹ค์ œ๋กœ ์‹คํ–‰๋  ๋•Œ Spring Boot ์—์„œ ApplicationContext๋ฅผ ์ดˆ๊ธฐํ™” ํ•˜๋Š”๋ฐ ์‚ฌ์šฉ๋˜๋Š” ApplicationContextInitializer๊ฐ€ ์ƒ์„ฑ๋œ๋‹ค.
AOT๋กœ ์ƒ์„ฑ๋œ ์ฝ”๋“œ๋Š” Maven ์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ์—๋Š” target/spring-aot/main/sources ์—์„œ, gradle ์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ build/generated/aotSources ์—์„œ ์ฐพ์„ ์ˆ˜ ์žˆ๋‹ค.

Spring Boot GraalVM Native Image ๋นŒ๋“œ ํ•˜๊ธฐ - Source Code Generation
GraalVM ๋นŒ๋“œ ํ›„ ์ƒ์„ฑ๋œ source ํŒŒ์ผ

Spring Boot GraalVM Hint File Generation

์†Œ์Šค ํŒŒ์ผ์„ ์ƒ์„ฑํ•˜๋Š” ๊ฒƒ ์™ธ์—๋„ Spring AOT ์—”์ง„์€ GraalVM ์—์„œ ์‚ฌ์šฉํ•˜๋Š” ํžŒํŠธ ํŒŒ์ผ๋„ ์ƒ์„ฑํ•œ๋‹ค. ํžŒํŠธ ํŒŒ์ผ์—๋Š” ์ฝ”๋“œ๋ฅผ ์ง์ ‘ ๊ฒ€์‚ฌํ•˜์—ฌ ์ดํ•ดํ•  ์ˆ˜ ์—†๋Š” ์‚ฌํ•ญ์„ GraalVM์ด ์–ด๋–ป๊ฒŒ ์ฒ˜๋ฆฌํ•ด์•ผ ํ•˜๋Š”์ง€๋ฅผ ์„ค๋ช…ํ•˜๋Š” JSON ๋ฐ์ดํ„ฐ๊ฐ€ ํฌํ•จ๋˜์–ด ์žˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, private ๋ฉ”์†Œ๋“œ์— Spring ์–ด๋…ธํ…Œ์ด์…˜์„ ์‚ฌ์šฉํ•œ ๊ฒฝ์šฐ Spring์€ GraalVM์—์„œ๋„ private ๋ฉ”์†Œ๋“œ๋ฅผ ํ˜ธ์ถœํ•˜๊ธฐ ์œ„ํ•ด ๋ฆฌํ”Œ๋ ‰์…˜์„ ์‚ฌ์šฉํ•ด์•ผ ํ•œ๋‹ค.
์ด๋Ÿฌํ•œ ์ƒํ™ฉ์ด ๋ฐœ์ƒํ•˜๋ฉด Spring์€ ๋ฆฌํ”Œ๋ ‰์…˜ ํžŒํŠธ๋ฅผ ์ž‘์„ฑํ•˜์—ฌ private ๋ฉ”์†Œ๋“œ๊ฐ€ ์ง์ ‘ ํ˜ธ์ถœ๋˜์ง€ ์•Š๋”๋ผ๋„ ๋„ค์ดํ‹ฐ๋ธŒ ์ด๋ฏธ์ง€์—์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์–ด์•ผ ํ•œ๋‹ค๋Š” ๊ฒƒ์„ GraalVM์ด ์•Œ ์ˆ˜ ์žˆ๋„๋ก ํ•œ๋‹ค. ํžŒํŠธ ํŒŒ์ผ์€ target/spring-aot/main/resources/META-INF/native-image์— ์ƒ์„ฑ๋˜๋ฉฐ GraalVM์—์„œ ์ž๋™์œผ๋กœ ๊ฐ€์ ธ์˜จ๋‹ค.
์ƒ์„ฑ๋œ ํžŒํŠธ ํŒŒ์ผ์€ ์ƒ์„ฑ ๊ฒฝ๋กœ๋Š” ์•„๋ž˜์™€ ๊ฐ™๋‹ค.

  • Maven
    • target/spring-aot/main/resources
  • Gradle
    • build/generated/aotResources
Spring Boot GraalVM Native Image ๋นŒ๋“œ ํ•˜๊ธฐ - Hint File Generation
maven์—์„œ GraalVM ๋นŒ๋“œ ํ›„ ์ƒ์„ฑ๋œ hint ํŒŒ์ผ

Spring Boot GraalVM Proxy Class Generation

Spring์€ ์ถ”๊ฐ€ ๊ธฐ๋Šฅ์œผ๋กœ ์ž‘์„ฑํ•œ ์ฝ”๋“œ๋ฅผ ํ–ฅ์ƒ์‹œํ‚ค๊ธฐ ์œ„ํ•ด ํ”„๋ก์‹œ ํด๋ž˜์Šค๋ฅผ ์ƒ์„ฑํ•œ๋‹ค. ์ด๋ฅผ ์œ„ํ•ด ๋ฐ”์ดํŠธ์ฝ”๋“œ๋ฅผ ์ง์ ‘ ์ƒ์„ฑํ•˜๋Š” cglib ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค. ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด JVM์—์„œ ์‹คํ–‰๋  ๋•Œ ํ”„๋ก์‹œ ํด๋ž˜์Šค๋Š” ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ์‹คํ–‰๋จ์— ๋”ฐ๋ผ ๋™์ ์œผ๋กœ ์ƒ์„ฑ๋œ๋‹ค. ๋„ค์ดํ‹ฐ๋ธŒ ์ด๋ฏธ์ง€๋ฅผ ์ƒ์„ฑํ•  ๋•Œ ์ด๋Ÿฌํ•œ ํ”„๋ก์‹œ ํด๋ž˜์Šค๋Š” ๋นŒ๋“œ ํƒ€์ž„์— ์ƒ์„ฑํ•ด์•ผ GraalVM์— ํฌํ•จ๋  ์ˆ˜ ์žˆ๋‹ค. ์†Œ์Šค ์ฝ”๋“œ ์ƒ์„ฑ๊ณผ ๋‹ฌ๋ฆฌ ์ƒ์„ฑ๋œ ๋ฐ”์ดํŠธ์ฝ”๋“œ๋Š” ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ๋””๋ฒ„๊น… ํ•  ๋•Œ ํŠน๋ณ„ํžˆ ์œ ์šฉํ•˜์ง€ ์•Š๋‹ค.
๊ทธ๋Ÿฌ๋‚˜ javap์™€ ๊ฐ™์€ ๋„๊ตฌ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ .class ํŒŒ์ผ์˜ ๋‚ด์šฉ์„ ๊ฒ€์‚ฌํ•ด์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ
maven์€ target/spring-aot/main/classes, gradle์€ build/generated/aotClasses ์—์„œ ํ•ด๋‹น ํŒŒ์ผ์„ ์ฐพ์„ ์ˆ˜ ์žˆ๋‹ค.

Spring Boot GraalVM ๋„ค์ดํ‹ฐ๋ธŒ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๊ฐœ๋ฐœํ•˜๊ธฐ

๋„ค์ดํ‹ฐ๋ธŒ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ๋นŒ๋“œํ•˜๋Š” ๋ฐฉ๋ฒ•์—๋Š” ํฌ๊ฒŒ ๋‘๊ฐ€์ง€ ๋ฐฉ๋ฒ•์ด ์žˆ๋‹ค.

  • Spring Boot์—์„œ ์ง€์›ํ•˜๋Š” cloud native buildpack์„ ์ด์šฉํ•˜์—ฌ ๋„ค์ดํ‹ฐ๋ธŒ
    ์‹คํ–‰ํŒŒ์ผ์ด ํฌํ•จ๋œ ๊ฒฝ๋Ÿ‰ ์ปจํ…Œ์ด๋„ˆ๋ฅผ ์ƒ์„ฑํ•˜๋Š” ๋ฐฉ๋ฒ•
  • GraalVM ๋„ค์ดํ‹ฐ๋ธŒ ๋นŒ๋“œ ํˆด์„ ์‚ฌ์šฉํ•˜์—ฌ ๋„ค์ดํ‹ฐ๋ธŒ ์‹คํ–‰ ํŒŒ์ผ์„ ์ƒ์„ฑํ•˜๋Š” ๋ฐฉ๋ฒ•

์ƒˆ๋กœ์šด native spring boot ํ”„๋กœ์ ํŠธ๋ฅผ ์‹œ์ž‘ํ•˜๋Š” ๊ฐ€์žฅ ์‰ฌ์šด ๋ฐฉ๋ฒ•์€ start.spring.io ์‚ฌ์ดํŠธ์—์„œ ‘GraalVM Native Support’ ์ข…์†์„ฑ์„ ์ถ”๊ฐ€ํ•˜์—ฌ ํ”„๋กœ์ ํŠธ๋ฅผ ์ƒ์„ฑํ•˜๋Š” ๊ฒƒ์ด๋‹ค. ํฌํ•จ๋œ HELP.md ํŒŒ์ผ์€ ์‹œ์ž‘ ํžŒํŠธ๋ฅผ ์ œ๊ณตํ•œ๋‹ค.
์•„๋ž˜์™€ ๊ฐ™์€ ๊ธฐ๋ณธ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ฝ”๋“œ๊ฐ€ ์žˆ๋‹ค.

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@SpringBootApplication
public class MyApplication {

    @RequestMapping("/")
    String home() {
        return "Hello World!";
    }

    public static void main(String[] args) {
        SpringApplication.run(MyApplication.class, args);
    }
}
Java

BuildPacks ๋ฅผ ์ด์šฉํ•˜์—ฌ Native Image ๋นŒ๋“œํ•˜๊ธฐ

Spring Boot์—๋Š” maven๊ณผ gradle ๋ชจ๋‘์— ๋Œ€ํ•ด์„œ ๋„ค์ดํ‹ฐ๋ธŒ ์ด๋ฏธ์ง€์— ๋Œ€ํ•œ ๋นŒ๋“œํŒฉ ์ง€์›์ด ํฌํ•จ๋˜์–ด ์žˆ๋‹ค. ๋ช…๋ น์–ด ํ•˜๋‚˜๋งŒ ์ž…๋ ฅํ•˜๋ฉด ๋กœ์ปฌ์—์„œ ์‹คํ–‰ ์ค‘์ธ Docker ๋ฐ๋ชฌ์— ์ ์ ˆํ•œ ์ด๋ฏธ์ง€๋ฅผ ๋น ๋ฅด๊ฒŒ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ๋‹ค. ๊ฒฐ๊ณผ ์ด๋ฏธ์ง€์—๋Š” JVM์ด ํฌํ•จ๋˜์ง€ ์•Š๊ณ  ๋„ค์ดํ‹ฐ๋ธŒ ์ด๋ฏธ์ง€๊ฐ€ ์ •์ ์œผ๋กœ ์ปดํŒŒ์ผ๋œ๋‹ค. JVM์ด ํฌํ•จ๋˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— ์ปจํ…Œ์ด๋„ˆ ์ด๋ฏธ์ง€ ํฌ๊ธฐ๋Š” ๋” ์ž‘์•„์ง„๋‹ค.
๋นŒ๋“œ๋ฅผ ์œ„ํ•ด์„œ ์‚ฌ์šฉ๋˜๋Š” ์ด๋ฏธ์ง€๋Š” paketobuildpacks/builder:tiny ์ด๋‹ค. ๋นŒ๋“œ๋ฅผ ์œ„ํ•œ ์ด๋ฏธ์ง€์— ๋” ๋งŽ์€ ํˆด์„ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” paketobuildpacks/builder-jammy-base ํ˜น์€ paketobuildpacks/builder-jammy-full์„ ์‚ฌ์šฉํ•  ์ˆ˜๋„ ์žˆ๋‹ค.
์ด ๋ฐฉ๋ฒ•์€ Docker ๋ฐ๋ชฌ์ด ์„ค์น˜๋˜์–ด ์žˆ์–ด์•ผ ํ•œ๋‹ค.

macOS ์—์„œ๋Š” Docker์— ํ• ๋‹น๋œ ๋ฉ”๋ชจ๋ฆฌ๋ฅผ 8GB ์ด์ƒ์œผ๋กœ ๋Š˜๋ฆฌ๊ณ  CPU๋„ ๋” ์ถ”๊ฐ€ํ•  ๊ฒƒ์„ ๊ถŒ์žฅํ•œ๋‹ค.
Docker Desktop ์—์„œ ์„ค์ • > Resources > Advanced ํ•ญ๋ชฉ์—์„œ ์„ค์ •ํ•  ์ˆ˜ ์žˆ๋‹ค.
Windows์—์„œ๋Š” ๋” ๋‚˜์€ ์„ฑ๋Šฅ์„ ์œ„ํ•ด Docker WSL2 ๋ฐฑ์—”๋“œ๋ฅผ ์‚ฌ์šฉํ•˜๋„๋ก ์„ค์ •ํ•ด์•ผ ํ•œ๋‹ค.

maven์„ ์‚ฌ์šฉํ•˜์—ฌ native ์ด๋ฏธ์ง€ ์ปจํ…Œ์ด๋„ˆ ๋นŒ๋“œ

maven์„ ์‚ฌ์šฉํ•˜์—ฌ native ์ด๋ฏธ์ง€ ์ปจํ…Œ์ด๋„ˆ๋ฅผ ๋นŒ๋“œํ•˜๋ ค๋ฉด pom.xml ํŒŒ์ผ์— spring-boot-starter-parent ๋ฐ org.graalvm.buildtools:native-maven-plugin ์ด ์‚ฌ์šฉ๋˜๋„๋ก ํ•ด์•ผ ํ•œ๋‹ค.
์•„๋ž˜์™€ ๊ฐ™์€ <parent> ์„น์…˜์ด ์žˆ์–ด์•ผ ํ•œ๋‹ค.

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>3.1.3</version>
</parent>
XML

spring-boot-starter-parent ์˜ pom ํŒŒ์ผ์„ ๋ณด๋ฉด <profiles> ์„น์…˜์— ์•„๋ž˜์™€ ๊ฐ™์ด native profile ์ด ์ •์˜๋˜์–ด ์žˆ๋‹ค.

<profile>
  <id>native</id>
  <build>
    <pluginManagement>
      <plugins>
        <plugin>
          <groupId>org.apache.maven.plugins</groupId>
          <artifactId>maven-jar-plugin</artifactId>
          <configuration>
            <archive>
              <manifestEntries>
                <Spring-Boot-Native-Processed>true</Spring-Boot-Native-Processed>
              </manifestEntries>
            </archive>
          </configuration>
        </plugin>
        <plugin>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-maven-plugin</artifactId>
          <configuration>
            <image>
              <builder>paketobuildpacks/builder:tiny</builder>
              <env>
                <BP_NATIVE_IMAGE>true</BP_NATIVE_IMAGE>
              </env>
            </image>
          </configuration>
          <executions>
            <execution>
              <id>process-aot</id>
              <goals>
                <goal>process-aot</goal>
              </goals>
            </execution>
          </executions>
        </plugin>
        <plugin>
          <groupId>org.graalvm.buildtools</groupId>
          <artifactId>native-maven-plugin</artifactId>
          <configuration>
            <classesDirectory>${project.build.outputDirectory}</classesDirectory>
            <metadataRepository>
              <enabled>true</enabled>
            </metadataRepository>
            <requiredVersion>22.3</requiredVersion>
          </configuration>
          <executions>
            <execution>
              <id>add-reachability-metadata</id>
              <goals>
                <goal>add-reachability-metadata</goal>
              </goals>
            </execution>
          </executions>
        </plugin>
      </plugins>
    </pluginManagement>
  </build>
</profile>
XML

<build><plugins> ์„น์…˜์— ์•„๋ž˜์™€ ๊ฐ™์€ ํ”Œ๋Ÿฌ๊ทธ์ธ์ด ์ถ”๊ฐ€๋˜์–ด์•ผ ํ•œ๋‹ค.

<plugin>
    <groupId>org.graalvm.buildtools</groupId>
    <artifactId>native-maven-plugin</artifactId>
</plugin>
XML

spring-boot-starter-parent๋Š” ๋„ค์ดํ‹ฐ๋ธŒ ์ด๋ฏธ์ง€๋ฅผ ์ƒ์„ฑํ•˜๊ธฐ ์œ„ํ•ด ์‹คํ–‰ํ•ด์•ผ ํ•˜๋Š” ์‹คํ–‰์„ ๊ตฌ์„ฑํ•˜๋Š” ๋„ค์ดํ‹ฐ๋ธŒ ํ”„๋กœํŒŒ์ผ์„ ์„ ์–ธํ•œ๋‹ค. ๋ช…๋ น์ค„์—์„œ -P ํ”Œ๋ž˜๊ทธ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ํ”„๋กœํŒŒ์ผ์„ ํ™œ์„ฑํ™” ํ•  ์ˆ˜ ์žˆ๋‹ค.
spring-boot-starter-parent๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š์œผ๋ ค๋ฉด Spring Boot ํ”Œ๋Ÿฌ๊ทธ์ธ์˜ process-aot goal ๊ณผ ๋„ค์ดํ‹ฐ๋ธŒ ๋นŒ๋“œ ๋„๊ตฌ ํ”Œ๋Ÿฌ๊ทธ์ธ์—์„œ add-reachability-metadata goal์„ ๊ตฌ์„ฑํ•ด์•ผ ํ•œ๋‹ค.
native ์ด๋ฏธ์ง€๊ฐ€ ํฌํ•จ๋œ ์ปจํ…Œ์ด๋„ˆ ์ด๋ฏธ์ง€๋ฅผ ๋นŒ๋“œํ•˜๋ ค๋ฉด native ํ”„๋กœํ•„์ด ํ™œ์„ฑํ™”๋œ ์ƒํƒœ์—์„œ spring-boot:build-image ๋ฅผ ์‹คํ–‰ํ•œ๋‹ค.

$> mvn -Pnative spring-boot:build-image
Bash

Gradle์„ ์‚ฌ์šฉํ•˜์—ฌ native ์ด๋ฏธ์ง€ ์ปจํ…Œ์ด๋„ˆ ๋นŒ๋“œ

Spring Boot Gradle ํ”Œ๋Ÿฌ๊ทธ์ธ์€ GraalVM ๋„ค์ดํ‹ฐ๋ธŒ ์ด๋ฏธ์ง€ ํ”Œ๋Ÿฌ๊ทธ์ธ์ด ์ ์šฉ๋  ๋•Œ AOT ์ž‘์—…์„ ์ž๋™์œผ๋กœ ๊ตฌ์„ฑํ•œ๋‹ค. build.gradle ํŒŒ์ผ์—์„œ ํ”Œ๋Ÿฌ๊ทธ์ธ ๋ธ”๋ก์—์„œ org.graalvm.buildtools.native๊ฐ€ ํฌํ•จ๋˜์–ด์•ผ ํ•œ๋‹ค.
org.graalvm.buildtools.native ํ”Œ๋Ÿฌ๊ทธ์ธ์ด ์ ์šฉ๋˜์–ด ์žˆ๋Š” ํ•œ bootBuildImage ์ž‘์—…์€ JVM ์ด๋ฏธ์ง€๊ฐ€ ์•„๋‹Œ ๋„ค์ดํ‹ฐ๋ธŒ ์ด๋ฏธ์ง€ ํฌํ•จํ•œ ์ปจํ…Œ์ด๋„ˆ ์ด๋ฏธ์ง€๋ฅผ ์ƒ์„ฑํ•œ๋‹ค.

$> gradle bootBuildImage 
Bash

์•„๋ž˜ ๋ช…๋ น์œผ๋กœ ๋นŒ๋“œ๋œ ์ด๋ฏธ์ง€๋ฅผ docker ํ™˜๊ฒฝ์—์„œ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ๋‹ค.

$> docker run --rm -p 8080:8080 docker.io/library/myproject:0.0.1-SNAPSHOT 
Bash

Native Build Tools ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ Native ์ด๋ฏธ์ง€ ๋นŒ๋“œํ•˜๊ธฐ

Docker ๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š๊ณ  ๋„ค์ดํ‹ฐ๋ธŒ ์‹คํ–‰ ํŒŒ์ผ์„ ์ง์ ‘ ์ƒ์„ฑํ•˜๋ ค๋ฉด GraalVM ๋„ค์ดํ‹ฐ๋ธŒ ๋นŒ๋“œ ๋„๊ตฌ๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค. ๋„ค์ดํ‹ฐ๋ธŒ ๋นŒ๋“œ ๋„๊ตฌ๋Š” maven๊ณผ gradle ๋ชจ๋‘๋ฅผ ์œ„ํ•ด GraalVM์—์„œ ์ œ๊ณตํ•˜๋Š” ํ”Œ๋Ÿฌ๊ทธ์ธ์ด๋‹ค. ๋„ค์ดํ‹ฐ๋ธŒ ๋นŒ๋“œ ๋„๊ตฌ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋„ค์ดํ‹ฐ๋ธŒ ์ด๋ฏธ์ง€ ์ƒ์„ฑ์„ ๋น„๋กฏํ•œ ๋‹ค์–‘ํ•œ GraalVM์ž‘์—…์„ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ๋‹ค. ์—ฌ๊ธฐ์„œ ๋งํ•˜๋Š” ๋„ค์ดํ‹ฐ๋ธŒ ์ด๋ฏธ์ง€๋Š” ์ปจํ…Œ์ด๋„ˆ ์ด๋ฏธ์ง€๊ฐ€ ์•„๋‹Œ ์‹คํ–‰ํŒŒ์ผ์ด๋ผ๊ณ  ์ƒ๊ฐํ•˜๋ฉด ๋˜๊ฒ ๋‹ค.

์‚ฌ์ „์กฐ๊ฑด

๋„ค์ดํ‹ฐ๋ธŒ ๋นŒ๋“œ ํˆด์„ ์‚ฌ์šฉํ•˜์—ฌ ๋„ค์ดํ‹ฐ๋ธŒ ์ด๋ฏธ์ง€๋ฅผ ๋นŒ๋“œํ•˜๋ ค๋ฉด ๋จธ์‹ ์— GraalVM ๋ฐฐํฌํŒ์ด ํ•„์š”ํ•˜๋‹ค.
Liberica Native Image Kit page ์—์„œ ์ˆ˜๋™์œผ๋กœ ๋‹ค์šด๋กœ๋“œ ํ•˜์—ฌ ์„ค์น˜ํ•˜๊ฑฐ๋‚˜ SDKMAN๊ณผ ๊ฐ™์€ SDK ๊ด€๋ฆฌ์ž ๋„๊ตฌ๋ฅผ์‚ฌ์šฉํ•˜์—ฌ ์„ค์น˜ํ•  ์ˆ˜ ์žˆ๋‹ค.
SDKMAN ์„ค์น˜ ๋ฐ ์‚ฌ์šฉ๋ฒ•์€ ์•„๋ž˜ ํฌ์ŠคํŒ…์„ ์ฐธ๊ณ ํ•˜๊ธฐ ๋ฐ”๋ž€๋‹ค.
SDKMAN์œผ๋กœ ๊ฐœ๋ฐœ๋„๊ตฌ ๋ฒ„์ „ ์‰ฝ๊ฒŒ ๊ด€๋ฆฌํ•˜์ž

macOS ํ˜น์€ ๋ฆฌ๋ˆ…์Šค ํ™˜๊ฒฝ์—์„œ๋Š” SDKMAN ์„ ์‚ฌ์šฉํ•˜์—ฌ ๋„ค์ดํ‹ฐ๋ธŒ ์ด๋ฏธ์ง€ ์ปดํŒŒ์ผ๋Ÿฌ๋ฅผ ์„ค์น˜ํ•˜๋Š” ๊ฒƒ์ด ํŽธํ•˜๋‹ค. SDKMAN์„ ์„ค์น˜ ํ›„ ์•„๋ž˜ ๋ช…๋ น์„ ์‚ฌ์šฉํ•˜์—ฌ GraalVM ๋ฐฐํฌ๋ฅผ ์„ค์น˜ํ•œ๋‹ค.
-nik ๋Š” Liberica ์—์„œ ์ œ๊ณตํ•˜๋Š” Native Image Kit ์˜ ์•ฝ์ž์ด๋‹ค.

$> sdk install java 22.3.3.r17-nik
$> sdk use java 22.3.3.r17-nik

Using java version 22.3.3.r17-nik in this shell.
$> java -version 
openjdk version "17.0.8" 2023-07-18 LTS 
OpenJDK Runtime Environment GraalVM 22.3.3 (build 17.0.8+7-LTS) 
OpenJDK 64-Bit Server VM GraalVM 22.3.3 (build 17.0.8+7-LTS, mixed mode, sharing) 
Bash

์ฃผ์˜ ํ•  ๊ฒƒ์€ sdk use ๋ช…๋ น์œผ๋กœ java ์˜ ๋ฒ„์ „์„ 22.3.3.r17-nik ํŒจํ‚ค์ง€ ๋ฒ„์ „์œผ๋กœ ๋ณ€๊ฒฝํ•˜์˜€์ง€๋งŒ ํ•ด๋‹น ์‰˜์—๋งŒ ์ ์šฉ๋œ๋‹ค๋Š” ๊ฒƒ์ด๋‹ค. ์˜๊ตฌ์ ์œผ๋กœ ๋ณ€๊ฒฝํ•˜๋ ค๋ฉด ์•„๋ž˜ ๋ช…๋ น์„ ์‚ฌ์šฉํ•œ๋‹ค.

$> sdk default java 22.3.3.r17-nik 
Bash

์œˆ๋„์šฐ์ฆˆ์˜ ๊ฒฝ์šฐ์—๋Š” native image for window ๋ฅผ ์ฐธ๊ณ ํ•œ๋‹ค.

Maven

Spring Boot buildpack ์ง€์›๊ณผ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ ๋„ค์ดํ‹ฐ๋ธŒ ํ”„๋กœํŒŒ์ผ์„ ์ƒ์†ํ•˜๋ ค๋ฉด pom.xml์—์„œ spring-boot-starter-parent๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  org.graalvm.buildtools:native-maven-plugin ์„ ์ถ”๊ฐ€ํ•ด์•ผ ํ•œ๋‹ค.

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>3.1.3</version>
</parent>
XML
<plugin>
    <groupId>org.graalvm.buildtools</groupId>
    <artifactId>native-maven-plugin</artifactId>
</plugin>
XML

์•„๋ž˜ ๋ช…๋ น์œผ๋กœ ๋นŒ๋“œํ•˜๋ฉด target ๋””๋ ‰ํ† ๋ฆฌ์—์„œ native ์ด๋ฏธ์ง€ ์‹คํ–‰ ํŒŒ์ผ์„ ์ฐพ์„ ์ˆ˜ ์žˆ๋‹ค.

$> mvn -Pnative native:compile 
Bash
$> file target/<์‹คํ–‰ํŒŒ์ผ๋ช…>
ex)
$> file target/rest-doc
target/rest-doc: Mach-O 64-bit executable arm64 
Bash

file ๋ช…๋ น์œผ๋กœ ์‹คํ–‰ํŒŒ์ผ์ž„์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

Gradle

Native Build Tools Gradle ํ”Œ๋Ÿฌ๊ทธ์ธ์ด gradle ํ”„๋กœ์ ํŠธ์— ์ ์šฉ๋˜๋ฉด Spring Boot Gradle ํ”Œ๋Ÿฌ๊ทธ์ธ์ด ์ž๋™์œผ๋กœ Spring AOT ์—”์ง„์„ ํŠธ๋ฆฌ๊ฑฐ ํ•œ๋‹ค. task ์ข…์†์„ฑ์ด ์ž๋™์œผ๋กœ ๊ตฌ์„ฑ๋˜๋ฏ€๋กœ ํ‘œ์ค€ nativeCompile task ๋ฅผ ์‹คํ–‰ํ•˜์—ฌ ๋„ค์ดํ‹ฐ๋ธŒ ์ด๋ฏธ์ง€๋ฅผ ์ƒ์„ฑํ•œ๋‹ค.

$> gradle nativeCompile
Bash

native ์ด๋ฏธ์ง€ ์‹คํ–‰ ํŒŒ์ผ์€ build/native/nativeCompile ๋””๋ ‰ํ† ๋ฆฌ์—์„œ ์ฐพ์„ ์ˆ˜ ์žˆ๋‹ค.
์‹คํ–‰์†๋„๋Š” ํ™•์‹คํžˆ GraalVM์œผ๋กœ ๋นŒ๋“œํ•œ ๋ฐ”์ด๋„ˆ๋ฆฌ๊ฐ€ ๋น ๋ฅธ๋“ฏ ํ•˜๋‹ค.
native ์ด๋ฏธ์ง€ ์‹คํ–‰

2023-09-04T00:16:55.602+09:00  INFO 8824 --- [           main] com.example.restdoc.RestDocApplication   : Starting AOT-processed RestDocApplication using Java 17.0.8 with PID 8824
2023-09-04T00:16:55.602+09:00  INFO 8824 --- [           main] com.example.restdoc.RestDocApplication   : No active profile set, falling back to 1 default profile: "default"
2023-09-04T00:16:55.620+09:00  INFO 8824 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8080 (http)
2023-09-04T00:16:55.621+09:00  INFO 8824 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
2023-09-04T00:16:55.621+09:00  INFO 8824 --- [           main] o.apache.catalina.core.StandardEngine    : Starting Servlet engine: [Apache Tomcat/10.1.10]
2023-09-04T00:16:55.627+09:00  INFO 8824 --- [           main] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2023-09-04T00:16:55.627+09:00  INFO 8824 --- [           main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 24 ms
2023-09-04T00:16:55.665+09:00  INFO 8824 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8080 (http) with context path ''
2023-09-04T00:16:55.665+09:00  INFO 8824 --- [           main] com.example.restdoc.RestDocApplication   : Started RestDocApplication in 0.081 seconds (process running for 0.098)
Plaintext

java ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜ ์‹คํ–‰

2023-09-04T00:18:12.410+09:00  INFO 8926 --- [  restartedMain] com.example.restdoc.RestDocApplication   : Starting RestDocApplication using Java 17.0.6 with PID 8926
2023-09-04T00:18:12.411+09:00  INFO 8926 --- [  restartedMain] com.example.restdoc.RestDocApplication   : No active profile set, falling back to 1 default profile: "default"
2023-09-04T00:18:12.430+09:00  INFO 8926 --- [  restartedMain] .e.DevToolsPropertyDefaultsPostProcessor : Devtools property defaults active! Set 'spring.devtools.add-properties' to 'false' to disable
2023-09-04T00:18:12.431+09:00  INFO 8926 --- [  restartedMain] .e.DevToolsPropertyDefaultsPostProcessor : For additional web related logging consider setting the 'logging.level.web' property to 'DEBUG'
2023-09-04T00:18:12.765+09:00  INFO 8926 --- [  restartedMain] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8080 (http)
2023-09-04T00:18:12.769+09:00  INFO 8926 --- [  restartedMain] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
2023-09-04T00:18:12.769+09:00  INFO 8926 --- [  restartedMain] o.apache.catalina.core.StandardEngine    : Starting Servlet engine: [Apache Tomcat/10.1.10]
2023-09-04T00:18:12.790+09:00  INFO 8926 --- [  restartedMain] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2023-09-04T00:18:12.791+09:00  INFO 8926 --- [  restartedMain] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 359 ms
2023-09-04T00:18:12.952+09:00  INFO 8926 --- [  restartedMain] o.s.b.d.a.OptionalLiveReloadServer       : LiveReload server is running on port 35729
2023-09-04T00:18:12.964+09:00  INFO 8926 --- [  restartedMain] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8080 (http) with context path ''
2023-09-04T00:18:12.969+09:00  INFO 8926 --- [  restartedMain] com.example.restdoc.RestDocApplication   : Started RestDocApplication in 0.703 seconds (process running for 0.968)
Plaintext

์‹œ์ž‘ ์†๋„๊ฐ€ ์•ฝ 9๋ฐฐ ๊ฐ€๋Ÿ‰ ์ฐจ์ด๊ฐ€ ๋‚จ์„ ์•Œ ์ˆ˜ ์žˆ๋‹ค.
์ง€๊ธˆ๊นŒ์ง€ Spring Boot ์—์„œ ์ง€์›ํ•˜๋Š” GraalVM ๋นŒ๋“œ ๋„๊ตฌ๋ฅผ ์ด์šฉํ•œ native ์ด๋ฏธ์ง€๋ฅผ ๋นŒ๋“œ ํ•˜๋Š” ๋ฐฉ๋ฒ•์— ๋Œ€ํ•ด์„œ ๊ณต์‹ ๋ฌธ์„œ๋ฅผ ์ •๋ฆฌํ•ด ๋ณด์•˜๋‹ค. ์•„๋ž˜ ๊ด€๋ จ ๋งํฌ๋ฅผ ์ฐธ๊ณ ํ•˜๋ฉด ๋งŽ์€ ๋„์›€์ด ๋  ๊ฒƒ ๊ฐ™๋‹ค.


๊ด€๋ จ๋งํฌ

https://docs.spring.io/spring-boot/docs/current/reference/html/native-image.html#native-image.introducing-graalvm-native-images
https://www.graalvm.org/22.3/reference-manual/native-image/