PageSourceSearch

https://kdev.ing/assets/posts_spring-boot-immutable-configuration-properties.md.IpzzLnXJ.js

js kdev.ing collected 2026-10-06 03:40:13 UTC 14,225 bytes, 32 lines download raw bytes

1import{B as e,et as t,mt as n,tt as r}from"./chunks/framework.Bgbhl1zx.js";import"./chunks/assets.ClBA_8PZ.js";var i=JSON.parse(`{"title":"Immutable Configuration Properties","description":"","frontmatter":{"title":"Immutable Configuration Properties","date":"2024-02-01T23:00+09:00","tags":["Spring Boot","Configuration Properties","Constructor Binding"]},"headers":[],"relativePath":"posts/spring-boot-immutable-configuration-properties.md","filePath":"posts/spring-boot-immutable-configuration-properties.md","lastUpdated":1791240792000}`),a={name:`posts/spring-boot-immutable-configuration-properties.md`};function o(e,i,a,o,s,c){return n(),t(`div`,null,[...i[0]||=[r(`<p>스프링 부트 2.2 부터 @ConstructorBinding을 사용하여 생성자를 가지지 않는 클래스를 작성할 수 있으며 스프링 부트 3.0 부터는 @ConstructorBinding 선언 대상이 생성자로 축소되어 아래와 같이 스프링 부트 3 에서는 생성자가 하나인 경우에는 굳이 지정하지 않는다.</p><div class="language-java"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e;" tabindex="0" dir="ltr"><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">@</span><span style="--shiki-light:#c62739;--shiki-dark:#F97583;">Getter</span></span>
2<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">@</span><span style="--shiki-light:#c62739;--shiki-dark:#F97583;">RequiredArgsConstructor</span></span>
3<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">@</span><span style="--shiki-light:#c62739;--shiki-dark:#F97583;">ConstructorBinding</span></span>
4<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">@</span><span style="--shiki-light:#c62739;--shiki-dark:#F97583;">ConfigurationProperties</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">prefix</span><span style="--shiki-light:#c62739;--shiki-dark:#F97583;"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> &quot;feature&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">)</span></span>
5<span class="line"><span style="--shiki-light:#c62739;--shiki-dark:#F97583;">public</span><span style="--shiki-light:#c62739;--shiki-dark:#F97583;"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> FeatureProperties</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> {</span></span>
6<span class="line"><span style="--shiki-light:#c62739;--shiki-dark:#F97583;">    private</span><span style="--shiki-light:#c62739;--shiki-dark:#F97583;"> final</span><span style="--shiki-light:#c62739;--shiki-dark:#F97583;"> boolean</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> enabled;</span></span>
7<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">}</span></span>
8<span class="line"></span>
9<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">@</span><span style="--shiki-light:#c62739;--shiki-dark:#F97583;">ConfigurationPropertiesScan</span></span>
10<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">@</span><span style="--shiki-light:#c62739;--shiki-dark:#F97583;">EnableConfigurationProperties</span></span>
11<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">@</span><span style="--shiki-light:#c62739;--shiki-dark:#F97583;">SpringBootApplication</span></span>
12<span class="line"><span style="--shiki-light:#c62739;--shiki-dark:#F97583;">public</span><span style="--shiki-light:#c62739;--shiki-dark:#F97583;"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> Application</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> {</span></span>
13<span class="line"><span style="--shiki-light:#c62739;--shiki-dark:#F97583;">    public</span><span style="--shiki-light:#c62739;--shiki-dark:#F97583;"> static</span><span style="--shiki-light:#c62739;--shiki-dark:#F97583;"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> main</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(</span><span style="--shiki-light:#c62739;--shiki-dark:#F97583;">String</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">[] </span><span style="--shiki-light:#c13617;--shiki-dark:#FFAB70;">args</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">) {</span></span>
14<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">        SpringApplication.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">run</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(Application.class, args);
14</span></span>
15<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">    }</span></span>
16<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">}</span></span></code></pre></div><div class="language-java"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e;" tabindex="0" dir="ltr"><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">@</span><span style="--shiki-light:#c62739;--shiki-dark:#F97583;">Getter</span></span>
17<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">@</span><span style="--shiki-light:#c62739;--shiki-dark:#F97583;">RequiredArgsConstructor</span></span>
18<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">@</span><span style="--shiki-light:#c62739;--shiki-dark:#F97583;">ConfigurationProperties</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">prefix</span><span style="--shiki-light:#c62739;--shiki-dark:#F97583;"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> &quot;feature&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">)</span></span>
19<span class="line"><span style="--shiki-light:#c62739;--shiki-dark:#F97583;">public</span><span style="--shiki-light:#c62739;--shiki-dark:#F97583;"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> FeatureProperties</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> {</span></span>
20<span class="line"><span style="--shiki-light:#c62739;--shiki-dark:#F97583;">    private</span><span style="--shiki-light:#c62739;--shiki-dark:#F97583;"> final</span><span style="--shiki-light:#c62739;--shiki-dark:#F97583;"> boolean</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> enabled;</span></span>
21<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">}</span></span>
22<span class="line"></span>
23<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">@</span><span style="--shiki-light:#c62739;--shiki-dark:#F97583;">ConfigurationPropertiesScan</span></span>
24<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">@</span><span style="--shiki-light:#c62739;--shiki-dark:#F97583;">SpringBootApplication</span></span>
25<span class="line"><span style="--shiki-light:#c62739;--shiki-dark:#F97583;">public</span><span style="--shiki-light:#c62739;--shiki-dark:#F97583;"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> Application</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> {</span></span>
26<span class="line"><span style="--shiki-light:#c62739;--shiki-dark:#F97583;">    public</span><span style="--shiki-light:#c62739;--shiki-dark:#F97583;"> static</span><span style="--shiki-light:#c62739;--shiki-dark:#F97583;"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> main</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(</span><span style="--shiki-light:#c62739;--shiki-dark:#F97583;">String</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">[] </span><span style="--shiki-light:#c13617;--shiki-dark:#FFAB70;">args</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">) {</span></span>
27<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">        SpringApplication.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">run</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(Application.class, args);</span></span>
28<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">    }</span></span>
29<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">}</span></span></code></pre></div><h5 id="parameter-name-discovery-from-spring-boot-3-1" tabindex="-1">Parameter Name Discovery from Spring Boot 3.1 <a class="header-anchor" href="#parameter-name-discovery-from-spring-boot-3-1" aria-label="Permalink to “Parameter Name Discovery from Spring Boot 3.1”">​</a></h5><blockquote><p>Ensure that your compiler is configured to use the &#39;-parameters&#39; flag.</p></blockquote><p>스프링 부트 3.2 에서 생성자 바인딩을 통해 Immutable Configuration Properties를 만드려는 경우 위와 같은 ì˜
29¤ë¥˜ê°€ 발생할 수 있다. 위 오류 메시지는 <a href="https://github.com/spring-projects/spring-boot/issues/38603" target="_blank" rel="noreferrer">MissingParametersFailureAnalyzer</a> 에 의해 출력되는 것으로 스프링 부트 3.2 에서 의존하는 스프링 프레임워크 버전에서 바이트코드를 분석하여 파라미터 이름을 추론해오던 <a href="https://github.com/spring-projects/spring-framework/issues/29559" target="_blank" rel="noreferrer">LocalVariableTableParameterNameDiscoverer 클래스가 제거</a>되어 파라미터 이름으로 추론하는 대상에 해당하는 생성자 바인딩을 위해 <a href="https://github.com/spring-projects/spring-framework/blob/main/spring-core/src/main/java/org/springframework/core/StandardReflectionParameterNameDiscoverer.java" target="_blank" rel="noreferrer">StandardReflectionParameterNameDiscoverer</a> 가 사용될 수 있도록 컴파일러에 대해 <code>-parameters</code> 옵션을 지정해야한다.</p><div class="language-groovy"><button title="Copy Code" class="copy"></button><span class="lang">groovy</span><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e;" tabindex="0" dir="ltr"><code><span class="line"><span style="--shiki-light:#62687b;--shiki-dark:#818e99;">// NOTE: https://github.com/spring-projects/spring-framework/wiki/Upgrading-to-Spring-Framework-6.x#parameter-name-retention</span></span>
30<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">tasks</span><span style="--shiki-light:#c62739;--shiki-dark:#F97583;">.</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">withType(</span><span style="--shiki-light:#c62739;--shiki-dark:#F97583;">JavaCompile</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">)</span><span style="--shiki-light:#c62739;--shiki-dark:#F97583;">.</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">configureEach {</span></span>
31<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">    options</span><span style="--shiki-light:#c62739;--shiki-dark:#F97583;">.</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">compilerArgs</span><span style="--shiki-light:#c62739;--shiki-dark:#F97583;">.</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">add(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&quot;-parameters&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">)</span></span>
32<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">}</span></span></code></pre></div><p>만약, 인텔리제이를 사용하고 있으며 그래들이 아닌 <strong>Build and run using</strong> 옵션으로 Intellij IDEA를 지정하는 편이라면 자바 컴파일러 옵션에 <code>-parameters</code> 옵션을 별도로 설정해야한다. 자바 컴파일러 옵션에 <code>-paramters</code> 플래그를 설정했음에도 파라미터 이름 추론을 위한 오류가 출력된다면 인텔리제이가 생성한 <code>out</code> 폴더를 삭제하고 다시 빌드를 수행해보도록 하자.</p><p><img src="/images/posts/spring-boot-immutable-configuration-properties/01.png" alt="" loading="lazy"></p><blockquote><p>Spring Boot 3.2 에서 Immutable Configuration Properties 를 사용하기 위해 생각지도 못한 삽질을 했습니다.</p></blockquote><h4 id="참고-링크" tabindex="-1">참고 링크 <a class="header-anchor" href="#참고-링크" aria-label="Permalink to “참고 링크”">​</a></h4><ul><li><a href="https://www.baeldung.com/configuration-properties-in-spring-boot#immutable-configurationproperties-binding" target="_blank" rel="noreferrer">Immutable @ConfigurationProperties Binding</a></li><li><a href="https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-3.0.0-M2-Release-Notes#constructingbinding-no-longer-needed-at-the-type-level" target="_blank" rel="noreferrer">@ConstructingBinding No Longer Needed at the Type Level</a></li><li><a href="https://github.com/spring-projects/spring-framework/wiki/Upgrading-to-Spring-Framework-6.x#parameter-name-retention" target="_blank" rel="noreferrer">LocalVariableTableParameterNameDiscoverer has been removed in 6.1</a></li><li><a href="https://github.com/spring-projects/spring-framework/issues/29559" target="_blank" rel="noreferrer">Remove LocalVariableTableParameterNameDiscoverer</a></li><li><a href="https://github.com/spring-projects/spring-boot/issues/38603" target="_blank" rel="noreferrer">Failures due to code not being compiled with &#39;-parameters&#39; are hard to identify</a></li></ul>`,12)]])}var s=e(a,[[`render`,o]]);
32export{i as __pageData,s as default};

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.