<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>엔지니어로 가는 길</title>
    <link>https://live-everyday.tistory.com/</link>
    <description>2년차 백엔드 개발자입니다.
p는 묵음입니다.</description>
    <language>ko</language>
    <pubDate>Sun, 16 Aug 2026 22:38:56 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>탐p슨</managingEditor>
    <image>
      <title>엔지니어로 가는 길</title>
      <url>https://tistory1.daumcdn.net/tistory/3425960/attach/2152f5ec7d1143a8b3869b908b5e9d39</url>
      <link>https://live-everyday.tistory.com</link>
    </image>
    <item>
      <title>Spring Boot에서 HttpMessageConverter를 설정하는 방법</title>
      <link>https://live-everyday.tistory.com/266</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;* HttpMessageConverter가 무엇인지에 대해서는 다음 기회에 다루기로 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;WebMvcConfigurer&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Spring은 `WebMvcConfigurer`라는 인터페이스를 통해 웹과 관련된 설정을 커스터마이징 할 수 있도록 돕는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;WebMvcConfigurer&lt;br /&gt;&lt;br /&gt;Defines callback methods to customize the Java-based configuration for Spring MVC enabled via @EnableWebMvc.&lt;br /&gt;&lt;br /&gt;@EnableWebMvc-annotated configuration classes may implement this interface to be called back and given a chance to customize the default configuration.&lt;br /&gt;&lt;br /&gt;Since: 3.1&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;@EnableWebMvc이 무엇인지에 대해서는 아래 글을 참고하자.&lt;/i&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1672199873063&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;Spring Boot에서 @EnableWebMvc 사용시 주의할 점&quot; data-og-description=&quot;@EnableWebMvc - Spring에서 제공하는 어노테이션 - &amp;#96;@Configuration&amp;#96;이 붙은 자바 클래스 설정 파일에 사용될 수 있다 - 이 어노테이션이 붙을 경우 &amp;#96;WebMvcConfigurationSupport&amp;#96; 클래스로부터 Spring MVC 설정을 import&quot; data-og-host=&quot;live-everyday.tistory.com&quot; data-og-source-url=&quot;https://live-everyday.tistory.com/265&quot; data-og-url=&quot;https://live-everyday.tistory.com/265&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bNnSqD/hyQ3U1VIwV/cr8VJu424x5l9uW3DLoWD1/img.png?width=800&amp;amp;height=146&amp;amp;face=0_0_800_146,https://scrap.kakaocdn.net/dn/mEsKS/hyQ36aftfY/RkzNGAkpDUGVSvIFOKGcAk/img.png?width=800&amp;amp;height=146&amp;amp;face=0_0_800_146,https://scrap.kakaocdn.net/dn/7F4tH/hyQ3SJOFJ3/632RKjYLHsBO4sgKKMCI0k/img.jpg?width=832&amp;amp;height=518&amp;amp;face=0_0_832_518&quot;&gt;&lt;a href=&quot;https://live-everyday.tistory.com/265&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://live-everyday.tistory.com/265&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bNnSqD/hyQ3U1VIwV/cr8VJu424x5l9uW3DLoWD1/img.png?width=800&amp;amp;height=146&amp;amp;face=0_0_800_146,https://scrap.kakaocdn.net/dn/mEsKS/hyQ36aftfY/RkzNGAkpDUGVSvIFOKGcAk/img.png?width=800&amp;amp;height=146&amp;amp;face=0_0_800_146,https://scrap.kakaocdn.net/dn/7F4tH/hyQ3SJOFJ3/632RKjYLHsBO4sgKKMCI0k/img.jpg?width=832&amp;amp;height=518&amp;amp;face=0_0_832_518');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Spring Boot에서 @EnableWebMvc 사용시 주의할 점&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;@EnableWebMvc - Spring에서 제공하는 어노테이션 - `@Configuration`이 붙은 자바 클래스 설정 파일에 사용될 수 있다 - 이 어노테이션이 붙을 경우 `WebMvcConfigurationSupport` 클래스로부터 Spring MVC 설정을 import&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;live-everyday.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;`WebMvcConfigurer` 인터페이스가 가지고 있는 메서드는 아래와 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;647&quot; data-origin-height=&quot;528&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/OjhRi/btrUOLtw5fg/wG9OZTPR6As8DAX9eX5kc1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/OjhRi/btrUOLtw5fg/wG9OZTPR6As8DAX9eX5kc1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/OjhRi/btrUOLtw5fg/wG9OZTPR6As8DAX9eX5kc1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FOjhRi%2FbtrUOLtw5fg%2FwG9OZTPR6As8DAX9eX5kc1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;647&quot; height=&quot;528&quot; data-origin-width=&quot;647&quot; data-origin-height=&quot;528&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;`HttpMessageConverter` 관련하여 커스터마이징이 필요하다면 위의 두 메서드를 이용할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;configureMessageConverters&lt;/b&gt;&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;Configure the HttpMessageConverters for reading from the request body and for writing to the response body.&lt;br /&gt;&lt;br /&gt;By default, all built-in converters are configured as long as the corresponding 3rd party libraries such Jackson JSON, JAXB2, and others are present on the classpath.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Note use of this method turns off default converter registration. Alternatively, use extendMessageConverters(List) to modify that default list of converters.&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;Params: converters &amp;ndash; initially an &lt;b&gt;empty list of converters&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;configureMessageConverter() 메서드의 인자로 비어있는 converter 리스트가 주어진다. 여기에 원하는 컨버터를 추가하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;aspectj&quot;&gt;&lt;code&gt;@Override
public void configureMessageConverters(List&amp;lt;HttpMessageConverter&amp;lt;?&amp;gt;&amp;gt; converters) {
    converters.add(new MappingJackson2HttpMessageConverter());
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;주의해야 할 점은 configureMessageConverters() 메서드를 통해 컨버터를 추가하면, 디폴트로 등록되던 컨버터들이 등록되지 않는다는 점이다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;/**
 * Provides access to the shared HttpMessageConverters used by the
 * RequestMappingHandlerAdapter and the ExceptionHandlerExceptionResolver.
 *
 * This method cannot be overridden; use configureMessageConverters instead.
 * Also see addDefaultHttpMessageConverters for adding default message converters.
 */
protected final List&amp;lt;HttpMessageConverter&amp;lt;?&amp;gt;&amp;gt; getMessageConverters() {
   if (this.messageConverters == null) {
      this.messageConverters = new ArrayList&amp;lt;&amp;gt;();
      configureMessageConverters(this.messageConverters);
      if (this.messageConverters.isEmpty()) {
         addDefaultHttpMessageConverters(this.messageConverters);
      }
      extendMessageConverters(this.messageConverters);
   }
   return this.messageConverters;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;`WebMvcConfigurationSupport`의 getMessageConverters()를 보자. configureMessageConverters()를 통해 컨버터를 설정하고, 이때 하나라도 컨버터가 등록이 되었으면 디폴트 컨버터가 추가되지 않는다. 디폴트 컨버터는 그대로 살리고 싶거나, 디폴트 컨버터 중 일부 컨버터를 조작하고 싶을 때는 extendMessageConverters()를 사용하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;extendMessageConverters&lt;/b&gt;&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;Extend or modify the list of converters after it has been, either configured or initialized with a default list.&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;Note that the order of converter registration is important. Especially in cases where clients accept org.springframework.http.MediaType.ALL the converters configured earlier will be preferred.&lt;br /&gt;&lt;br /&gt;Params: converters &amp;ndash; &lt;b&gt;the list of configured converters to be extended&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;extendMessageConverters() 메서드의 인자로는 설정되어 있는 컨버터 리스트가 주어진다. 새로운 컨버터를 추가할 수도 있고, 특정 컨버터는 제외하거나 설정을 변경할 수도 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;@Override
public void extendMessageConverters(List&amp;lt;HttpMessageConverter&amp;lt;?&amp;gt;&amp;gt; converters) {
    converters.stream()
            .filter(MappingJackson2HttpMessageConverter.class::isInstance)
            .forEach(
                    converter -&amp;gt;
                            ((MappingJackson2HttpMessageConverter) converter).setDefaultCharset(StandardCharsets.UTF_8)
            );
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;@EnableWebMvc 없이도 Spring Boot + WebConfigurer 가능한 이유&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Spring Boot에서 Spring Boot가 제공해주는 Web MVC AutoConfiguration을 살리면서 커스터마이징을 진행하기 위해서는 아래와 같이 `@EnableWebMvc` 없이 `WebMvcConfigurer`를 구현해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;@Configuration
public class WebConfig implements WebMvcConfigurer {
    
    // ...

}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 `WebMvcConfigurer` 인터페이스에 달린 주석을 보면 `@EnableWebMvc`를 통해 이루어지는 설정을 커스터마이징할 때 사용하라고 되어 있었는데 어떻게 Spring Boot에서는 `@EnableWebMvc`없이 `WebConfigurer` 사용이 가능한 걸까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2022-12-28 15.06.03.png&quot; data-origin-width=&quot;1652&quot; data-origin-height=&quot;702&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ctuH7A/btrUKW4uhpv/KFfEjwb3Ld3Hr9sFhusSW0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ctuH7A/btrUKW4uhpv/KFfEjwb3Ld3Hr9sFhusSW0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ctuH7A/btrUKW4uhpv/KFfEjwb3Ld3Hr9sFhusSW0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FctuH7A%2FbtrUKW4uhpv%2FKFfEjwb3Ld3Hr9sFhusSW0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1652&quot; height=&quot;702&quot; data-filename=&quot;스크린샷 2022-12-28 15.06.03.png&quot; data-origin-width=&quot;1652&quot; data-origin-height=&quot;702&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;`EnableWebMvcConfiguration`이 `DelegatingWebMvcConfiguration`을 구현하고 있고, `DelegatingWebMvcConfiguration`가 `WebMvcConfigurationSupport`를 구현한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;`@EnableWebMvc`는 `WebMvcConfigurationSupport`의 설정을 import 하므로 결국 `@EnableWebMvc`를 직접적으로 사용하지 않더라도 `WebMvcConfigurationSupport`의 설정을 사용하겠다고 하면 `@EnableWebMvc`를 사용한 것과 같은 효과인 셈이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 Spring Boot의 `WebMvcAutoConfiguration`에서 `EnableWebMvcConfiguration` 설정 파일이 빈으로 등록되므로 `@EnableWebMvc`가 없는데도 `WebMvcConfigurer`를 사용하여 웹 관련 설정을 커스터마이징 할 수 있다.&lt;/p&gt;</description>
      <category>프로그래밍/Spring</category>
      <category>EnableWebMvc</category>
      <category>HttpMessageConverter</category>
      <category>Spring</category>
      <category>springboot</category>
      <category>WebMvcConfigurer</category>
      <author>탐p슨</author>
      <guid isPermaLink="true">https://live-everyday.tistory.com/266</guid>
      <comments>https://live-everyday.tistory.com/266#entry266comment</comments>
      <pubDate>Thu, 29 Dec 2022 08:25:02 +0900</pubDate>
    </item>
    <item>
      <title>Spring Boot에서 @EnableWebMvc 사용시 주의할 점</title>
      <link>https://live-everyday.tistory.com/265</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;@EnableWebMvc&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Spring에서 제공하는 어노테이션&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- `@Configuration`이 붙은 자바 클래스 설정 파일에 사용될 수 있다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 이 어노테이션이 붙을 경우 `WebMvcConfigurationSupport` 클래스로부터 Spring MVC 설정을 import 한다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(`HandlerMapping`, `HandlerAdapter`, `ConversionService` 등 웹과 관련된 빈들이 등록된다)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;@WebMvcAutoConfiguration&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Spring Boot에서 제공하는 어노테이션&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 웹과 관련된 자동설정이 이루어진다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- spring-boot-starter-web 의존성을 추가하면 별도의 작업을 해주지 않아도 말 그대로 자동으로 설정된다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;Spring Boot 자동설정과 관련해서는 아래 글을 참고하자.&lt;/i&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1672199464870&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;Spring의 @Conditional과 Spring Boot의 AutoConfiguration&quot; data-og-description=&quot;Contents 1. Introduction 2. @Conditional 모든 Spring 프로젝트의 Configuration에서 공통되는 부분이 있다면? Configuration이 공유될 때 발생할 수 있는 문제 @Conditional @Conditional을 이용한 개선 3. AutoConfiguration Sprin&quot; data-og-host=&quot;live-everyday.tistory.com&quot; data-og-source-url=&quot;https://live-everyday.tistory.com/235&quot; data-og-url=&quot;https://live-everyday.tistory.com/235&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/SRbpH/hyQ34XMXj4/84r3x3hwkJCGDtHtgQCKNK/img.png?width=742&amp;amp;height=467&amp;amp;face=0_0_742_467,https://scrap.kakaocdn.net/dn/dH8lEx/hyQ3SXlaDN/e9nZAWY791kg5MCcIi8il1/img.png?width=742&amp;amp;height=467&amp;amp;face=0_0_742_467,https://scrap.kakaocdn.net/dn/chx8SZ/hyQ3QL3Kp3/WXd0R4BsJUnJDjIhzN5MX1/img.jpg?width=832&amp;amp;height=518&amp;amp;face=0_0_832_518&quot;&gt;&lt;a href=&quot;https://live-everyday.tistory.com/235&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://live-everyday.tistory.com/235&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/SRbpH/hyQ34XMXj4/84r3x3hwkJCGDtHtgQCKNK/img.png?width=742&amp;amp;height=467&amp;amp;face=0_0_742_467,https://scrap.kakaocdn.net/dn/dH8lEx/hyQ3SXlaDN/e9nZAWY791kg5MCcIi8il1/img.png?width=742&amp;amp;height=467&amp;amp;face=0_0_742_467,https://scrap.kakaocdn.net/dn/chx8SZ/hyQ3QL3Kp3/WXd0R4BsJUnJDjIhzN5MX1/img.jpg?width=832&amp;amp;height=518&amp;amp;face=0_0_832_518');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Spring의 @Conditional과 Spring Boot의 AutoConfiguration&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Contents 1. Introduction 2. @Conditional 모든 Spring 프로젝트의 Configuration에서 공통되는 부분이 있다면? Configuration이 공유될 때 발생할 수 있는 문제 @Conditional @Conditional을 이용한 개선 3. AutoConfiguration Sprin&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;live-everyday.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;`@WebMvcAutoConfiguration`을 살펴보면 아래와 같은 어노테이션들이 붙어있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;873&quot; data-origin-height=&quot;160&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/9KUKt/btrUIgIQaql/Zk2s1ZLsKEKik6K5TosSXK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/9KUKt/btrUIgIQaql/Zk2s1ZLsKEKik6K5TosSXK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/9KUKt/btrUIgIQaql/Zk2s1ZLsKEKik6K5TosSXK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F9KUKt%2FbtrUIgIQaql%2FZk2s1ZLsKEKik6K5TosSXK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;873&quot; height=&quot;160&quot; data-origin-width=&quot;873&quot; data-origin-height=&quot;160&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 중에서 `@ConditionalOnMissingBean` 어노테이션의 역할은 해당 어노테이션에서 명시한 클래스가 존재하지 않는 경우에 `WebMvcAutoConfiguration`을 적용하겠다는 의미이다. 다시 말해, `WebMvcConfigurationSupport` 클래스가 빈으로 등록되어 있다면 `WebMvcAutoConfiguration`을 적용하지 않겠다는 뜻이다. `WebMvcConfigurationSupport`는 위에서 살펴봤듯이 `@EnableWebMvc` 어노테이션을 붙이면 빈으로 등록된다. 따라서 최종적으로 아래와 같이 정리할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;`@EnableWebMvc`을 사용하면 Spring Boot의 Web MVC 자동설정이 이루어지지 않는다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 Spring Boot의 Web MVC 자동설정을 무시하고 웹과 관련된 설정을 커스터마이징 하고 싶다면 `@EnalbedWebMvc`를 붙이고, Spring Boot의 Web MVC 자동설정을 모두 적용한 채로 그 위에서 커스터마이징을 적용하고 싶다면 `@EnableWebMvc` 어노테이션을 사용하지 않고 아래와 같이 커스터마이징을 진행해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;428&quot; data-origin-height=&quot;169&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/crnqQr/btrUOMzjxZF/72oR6HuW32J0elK9oFkqEK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/crnqQr/btrUOMzjxZF/72oR6HuW32J0elK9oFkqEK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/crnqQr/btrUOMzjxZF/72oR6HuW32J0elK9oFkqEK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcrnqQr%2FbtrUOMzjxZF%2F72oR6HuW32J0elK9oFkqEK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;428&quot; height=&quot;169&quot; data-origin-width=&quot;428&quot; data-origin-height=&quot;169&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>프로그래밍/Spring</category>
      <category>AutoConfiguration</category>
      <category>EnabledWebMvc</category>
      <category>Spring</category>
      <category>springboot</category>
      <category>WEB</category>
      <author>탐p슨</author>
      <guid isPermaLink="true">https://live-everyday.tistory.com/265</guid>
      <comments>https://live-everyday.tistory.com/265#entry265comment</comments>
      <pubDate>Wed, 28 Dec 2022 12:57:16 +0900</pubDate>
    </item>
    <item>
      <title>재능 없는 사람도 뛰어난 개발자가 될 수 있다</title>
      <link>https://live-everyday.tistory.com/264</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;재능 있는 사람이란 개발과 관련된 지식 또는 능력을 다른 사람보다 빨리 습득하는 사람이라고 하고, 뛰어난 개발자를 다섯 손가락 안에 꼽히는 회사에서 일하는 개발자라고 가정한다면 &lt;b&gt;재능 없는 사람도 뛰어난 개발자가 될 수 있다고 생각한다.&lt;/b&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;개발은 시험이나 대회가 아니다&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정량적으로 점수를 매길 수도 없고, 참여자들을 변별하기 위해 누군가 억지로 만든 변별력 있는 문제를 풀어야 하는 것도 아니며, 언제나 정답이 정해져있는 것도 아니다. 개발은 현실의 문제를 해결하는 것이고, 때로는 효과적이고 효율적인 방식보다 빠른 방식이 정답일 때도 있고, 빠른 방식보다 효과적이고 효율적인 방식이 정답일 때도 있다. 따라서 &lt;b&gt;좋은 회사는 개발자를 뽑을 때 정량적으로 점수를 매기는 것이 아니라 자신들이 정한 일정한 기준을 넘는 개발자 중에서 함께 일하고 싶은지, 회사와 팀에 잘 어우러질 수 있는 사람인지를 본다고 생각한다.&lt;/b&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;회사에서 원하는 개발자는 많고 다양하다&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;다섯 손가락 안에 꼽히는 회사(이하 좋은 회사)에서 일하는 개발자는 한 두 명이 아니다. 서비스가 크고 복잡하기 때문에 &lt;b&gt;많은 개발자가 존재하고, 모두 팀으로 일한다.&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;그리고&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;회사 구성원은 서로 다른 역할을 한다.&lt;/b&gt; 리더도 있어야 하고, 팔로워도 있어야 한다. 새로운 방식, 창의적인 방식을 제시하는 사람도 필요하고, 그러한 방식을 잘 따를 수 있는 사람도 필요하며, 비판적으로 바라볼 수 있는 사람도 필요하다. 주니어도 필요하고, 시니어도 필요하다. 회사에서 새로 적용하려는 기술을 써본 사람도 필요하고, 지금 사용하고 있는 기술을 잘 하는 사람도 필요하다. 꼼꼼하게 일처리 하는 사람도 필요하고, 빠르게 일처리 하는 사람도 필요하고, 커뮤니케이션을 잘 하는 사람도 필요하고, 잘 결정할 수 있는 사람도 필요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에서 나열한 요구사항 하나만 만족할 수 있다고 회사에서 뽑는 것은 아닐 것이다. 몇 개의 요구사항이 겹쳐야 할 수도 있지만 요지는 위에 있는 모든 요구사항 중에 재능이 필요한 것은 하나도 없으며 모두 후천적으로 얻을 수 있는 것들이고, 요구도 다양하기 때문에 내가 만족시킬 수 있는 요구도 반드시 존재할 수 밖에 없다는 것이다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시험 또는 대회에서는 재능 있는 사람을 이길 수 없다. 세상에는 재능이 있는 사람이 많으며, 그중에 노력하는 사람도 있기 때문에 순위권에 드는 사람들은 언제나 재능이 있으면서 노력까지 하는 사람이기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 뛰어난 개발자가 되는 것은 정말 누구나 될 수 있다고 생각한다. 시험이나 대회도 아니며, 누구를 이기고 이겨서 순위권 안에만 들어야 이룰 수 있는 것이 아니기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시간이 지나도 변하지 않는 중요한 것을 탄탄히 익히고, 시간이 지남에 도구들이 끊임없이 변한다고 해도 새로운 기술을 익힐 수 있는 능력을 익히고, 자신의 생각을 논리적으로 말할 수 있으며 다른 사람과 원만하게 의사소통을 할 수 있고, 현실의 문제가 유연하게 변하듯이 원칙과 원론을 고집하는 것이 아니라 상황에 맞는 정답을 그때그때 고를 수 있는 유연함을 갖춘다면 이때부터는 운의 영역이고, 시간 싸움이라고 생각한다. 결국 회사가 나를 뽑아줘야 그 회사에 들어갈 수 있기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(이 글에서 정의한) 뛰어난 개발자가 되기 위해 필요한 것은 재능이 아니라 꾸준함 한 가지 아닐까.&lt;/p&gt;</description>
      <category>private note/일기?</category>
      <author>탐p슨</author>
      <guid isPermaLink="true">https://live-everyday.tistory.com/264</guid>
      <comments>https://live-everyday.tistory.com/264#entry264comment</comments>
      <pubDate>Sat, 24 Dec 2022 16:48:17 +0900</pubDate>
    </item>
    <item>
      <title>Spring Boot 버전업(2.0.1 -&amp;gt; 2.7.6) 과정에서 만난 이슈들</title>
      <link>https://live-everyday.tistory.com/263</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1671341369583&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;Spring boot 버전을 올려보자&quot; data-og-description=&quot;Spring boot 2.0을 사용 중인 프로젝트의 버전을 최신 버전으로 올려보려고 한다. 간단한 일은 아니겠지만 불가능한 일도 아니다. 수많은 문제를 끊임없이 만나게 될 텐데, 문제를 만나서 해결하거&quot; data-og-host=&quot;live-everyday.tistory.com&quot; data-og-source-url=&quot;https://live-everyday.tistory.com/248&quot; data-og-url=&quot;https://live-everyday.tistory.com/248&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cAKrbF/hyQU4qLQs6/axoOA31giMZW3kk4hdbIR1/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/jvpby/hyQWtbmaWj/2gPvZ3Naj9BcesBIFwUdj1/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/eJxwvw/hyQUZXhuOs/byBR86QU1lKVFvzzyDnDjK/img.jpg?width=832&amp;amp;height=518&amp;amp;face=0_0_832_518&quot;&gt;&lt;a href=&quot;https://live-everyday.tistory.com/248&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://live-everyday.tistory.com/248&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cAKrbF/hyQU4qLQs6/axoOA31giMZW3kk4hdbIR1/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/jvpby/hyQWtbmaWj/2gPvZ3Naj9BcesBIFwUdj1/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/eJxwvw/hyQUZXhuOs/byBR86QU1lKVFvzzyDnDjK/img.jpg?width=832&amp;amp;height=518&amp;amp;face=0_0_832_518');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Spring boot 버전을 올려보자&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Spring boot 2.0을 사용 중인 프로젝트의 버전을 최신 버전으로 올려보려고 한다. 간단한 일은 아니겠지만 불가능한 일도 아니다. 수많은 문제를 끊임없이 만나게 될 텐데, 문제를 만나서 해결하거&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;live-everyday.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;담당하고 있는 프로젝트의 Spring Boot 버전을 2.0.1에서 2.7.6까지 올렸다. 위 글에서 Spring Boot 버전을 올려보고 싶다고 말하기는 했으나 이렇게 생각보다 빠르게 마무리될 줄은 몰랐다. 이 글에서는 Spring Boot 버전업 과정에서 만난 이슈를 정리하려고 한다. (버전을 올림에 따라 발생할 수 있는 모든 이슈가 아닌, 회사 프로젝트에서 발생한 이슈만 정리하였다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;2.1.18 -&amp;gt; 2.2.13&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;&lt;b&gt;API 응답에서 한글이 깨지는 이슈(인코딩이 ISO-8859-1로 되는 이슈)&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2.2부터 Spring Web 버전이 5.2로 올라갔다. Spring Web 5.2부터 MediaType에서 아래와 같이 charset 정보를 함께 넘기는 상수가 deprecated 되었다.&lt;/p&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;APPLICATION_JSON_UTF8_VALUE&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Deprecated 된 이유는 아래와 같다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;@deprecated as of 5.2 in favor of APPLICATION_JSON_VALUE since major browsers like Chrome now comply with the specification and interpret correctly UTF-8 special characters without requiring a charset=UTF-8 parameter.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해결하기는 `MappingJackson2HttpMessageConverter`의 디폴트 charset을 UTF-8로 설정하여 해결했는데, 원인에 대해서는 다시 확인이 필요하다. 해결하는 데 너무 급급했던 것 같다. 왜 일부 요청에 대해서만 인코딩이 깨졌을까?&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;2.3.12 -&amp;gt; 2.4.13&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;u&gt;로깅이 안되는 이슈&lt;/u&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;버전을 올리고 알파 서버에 배포해서 제대로 동작하는지 테스트를 해보려는데 로깅이 제대로 되지 않았다. 알고 보니 2.4 버전부터 `logging.file` 프로퍼티가 deprecated 되어 로깅할 파일의 이름이 제대로 설정되지 않았기 때문이었다.&lt;/p&gt;
&lt;pre id=&quot;code_1671347518164&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;logging:
    file: log-filename.log&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;`application.yml` 파일에서 위와 같이 설정하고 있었는데, 아래와 같이 수정하여 해결하였다.&lt;/p&gt;
&lt;pre id=&quot;code_1671347530697&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;logging:
    file:
        name: log-filename.log&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;&lt;b&gt;특정 프로파일의 설정파일에서 다른 프로파일 활성화 불가능&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2.4 버전의 변경사항 중 하나는 더이상 특정 프로파일 설정파일에서 다른 프로파일을 활성화하는 게 불가능하다는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존에는 설정파일(`application.yml`)에서 특정 프로파일을 활성화하고 싶은 경우 아래와 같이 활성화할 수 있었다.&lt;/p&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;// application.yml
spring:
    profiles:
        include: profile1, profile2&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2.4 버전부터는 `application.yml`과 같이 프로파일에 종속적이지 않은 공통 설정파일에서는 위와 같은 설정이 가능하지만, `application-dev.yml`과 같이 특정 프로파일에 종속적인 설정파일에서는 다른 프로파일을 활성화하는 게 불가능하다.&lt;/p&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;// application-dev.yml
spring:
    profiles:
        include: file-logging # 불가능&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;So in Spring Boot 2.4 we&amp;rsquo;re planning to make two significant changes to the way the properties and YAML files are loaded:&lt;br /&gt;&lt;br /&gt;1. Documents will be loaded in the order that they&amp;rsquo;re defined.&lt;br /&gt;&lt;b&gt;2. Profiles can no longer be activated from profile specific documents.&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특정 프로파일에 종속적인 설정파일에서 다른 프로파일을 활성화하는 부분을 제거하고, 배포 시나리오에서 jar 파일을 실행할 때 프로파일을 설정하도록 수정하였다.&lt;/p&gt;
&lt;pre id=&quot;code_1671617581990&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;java -jar -Dspring.profiles.activate=file-logging&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;2.5.14 -&amp;gt; 2.6.14&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;&lt;b&gt;suffix 패턴 매칭되지 않는 이슈&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;`/api/hello.json`과 같이 suffix를 붙여서 요청하는 경우 `/api/hello`로 해석했으나, 2.6 버전부터 suffix 패턴에 대해 매칭하는 설정이 디폴트 false로 바뀌었다. 하지만 우리 프로젝트의 API를 사용하는 측에서 `/api/hello.json`과 같이 suffix를 붙여서 요청하고 있었으므로 suffix 패턴 매칭이 가능하도록 설정해야 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;`WebMvcConfigurer`의 `configurePathMatch()` 메서드를 오버라이드하여 `PathMatchConfigurer`의 `setUseSuffixPatternMatch()` 메서드를 통해 suffix 패턴 매칭에 대해 허용할지 말지를 결정할 수 있다. 해당 값을 true로 설정하였는데도 suffix 패턴을 해석하지 못해서 확인해보니&amp;nbsp; docs에 아래와 같은 설명이 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;This&amp;nbsp;property&amp;nbsp;is&amp;nbsp;mutually&amp;nbsp;exclusive&amp;nbsp;with&amp;nbsp;and&amp;nbsp;ignored&amp;nbsp;when&amp;nbsp;setPatternParser(PathPatternParser)&amp;nbsp;is&amp;nbsp;set.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;`PathMatchConfigurer`의 `setPatternParser()` 메서드를 통해 PatternParser를 null로 설정까지 해주고 나니 suffix 패턴에 대해 정상적으로 매칭이 이루어졌다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;&lt;b&gt;빌드시 war(jar) 파일이 두 개 생성되는 이슈&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;spring-boot-gradle-plugin 2.5부터 war(jar) 태스크가 디폴트로 enabled 되었는데, war(jar) 태스크가 bootWar(bootJar) 태스크와 함께 존재하는 경우 war(jar) 태스크의 산출물에는 &quot;plain&quot;이라는 prefix가 컨벤션으로 붙는다고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;By&amp;nbsp;default,&amp;nbsp;when&amp;nbsp;the&amp;nbsp;bootJar&amp;nbsp;or&amp;nbsp;bootWar&amp;nbsp;tasks&amp;nbsp;are&amp;nbsp;configured,&amp;nbsp;the&amp;nbsp;jar&amp;nbsp;or&amp;nbsp;war&amp;nbsp;tasks&amp;nbsp;are&amp;nbsp;configured&amp;nbsp;to&amp;nbsp;use&amp;nbsp;plain&amp;nbsp;as&amp;nbsp;the&amp;nbsp;convention&amp;nbsp;for&amp;nbsp;their&amp;nbsp;archive&amp;nbsp;classifier.&amp;nbsp;This&amp;nbsp;ensures&amp;nbsp;that&amp;nbsp;bootJar&amp;nbsp;and&amp;nbsp;jar&amp;nbsp;or&amp;nbsp;bootWar&amp;nbsp;and&amp;nbsp;war&amp;nbsp;have&amp;nbsp;different&amp;nbsp;output&amp;nbsp;locations,&amp;nbsp;allowing&amp;nbsp;both&amp;nbsp;the&amp;nbsp;executable&amp;nbsp;archive&amp;nbsp;and&amp;nbsp;the&amp;nbsp;plain&amp;nbsp;archive&amp;nbsp;to&amp;nbsp;be&amp;nbsp;built&amp;nbsp;at&amp;nbsp;the&amp;nbsp;same&amp;nbsp;time.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고: &lt;a href=&quot;https://docs.spring.io/spring-boot/docs/current/gradle-plugin/reference/htmlsingle/#packaging-executable.and-plain-archives&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://docs.spring.io/spring-boot/docs/current/gradle-plugin/reference/htmlsingle/#packaging-executable.and-plain-archives&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;경우에 따라서는 문제가 안될 수도 있는 내용이다. 우리는 시나리오를 통해 배포를 자동화하고 있었고, 시나리오 중에는 이전 배포 파일을 삭제하는 과정이 있었는데 해당 과정에서 이번에 처음 등장한 plain이 붙은 war(jar) 태스크의 산출물을 삭제하지 않다 보니 두 번째 배포부터 이름이 충돌하여 배포에 실패했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;build.gradle 파일에 아래와 같이 war(jar) 태스크를 사용하지 않겠다고 함으로써 해결하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1671618377443&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;war {
    enabled = false
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>private note/한 일, 할 일, 하고싶은 일</category>
      <category>springboot</category>
      <author>탐p슨</author>
      <guid isPermaLink="true">https://live-everyday.tistory.com/263</guid>
      <comments>https://live-everyday.tistory.com/263#entry263comment</comments>
      <pubDate>Wed, 21 Dec 2022 19:33:05 +0900</pubDate>
    </item>
    <item>
      <title>[Kotlin for Java Developers] Nullability</title>
      <link>https://live-everyday.tistory.com/262</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Table Of Contents&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;1. Nullable types&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;2. Nullable types under the hood&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;3. Safe casts&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;4. Importance of nullability&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;5. TMI&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;background-color: #9feec3; color: #000000;&quot;&gt;1. Nullable types&lt;/span&gt;&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;The problem of nullability is sometimes referred to as billion dollar mistake.&lt;/span&gt;&lt;/blockquote&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;`NullPointerException`&amp;nbsp;is&amp;nbsp;really&amp;nbsp;hard&amp;nbsp;to&amp;nbsp;fix.&lt;/span&gt;&lt;/blockquote&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Modern approach: to make NPE compile-time error, not run-time error&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;코틀린은&amp;nbsp;nullable&amp;nbsp;types과&amp;nbsp;non-nullable&amp;nbsp;types을&amp;nbsp;구분한다. &lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1670762900060&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;val s1: String = always not null
val s2: String = null // Kotlin compiler will complain
val s3: String? = null // 타입 뒤에 붙은 '?'가 nullable을 표현한다&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Non-nullable types의 멤버에 접근하는거나 확장 함수를 호출하는 것은 아무 문제가 없다. 하지만 nullable type의 변수를 dereference 하려고 시도하면 컴파일 에러가 발생한다. Nullable type은 `null`이 담겨 있을 수도 있으므로 바로 dereference를 시도하는 것은 `NullPointerException`(이하 NPE)을 일으킬 수도 있기 때문이다. (코틀린은&amp;nbsp;nullable&amp;nbsp;types과&amp;nbsp;non-nullable&amp;nbsp;types를&amp;nbsp;구분함으로써&amp;nbsp;NPE를&amp;nbsp;컴파일&amp;nbsp;타임&amp;nbsp;에러로&amp;nbsp;만들었다.) &lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;u&gt;&lt;b&gt;Safe access(call) expression&lt;/b&gt;&lt;/u&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Nullable type의 객체를 dereference 하고 싶을 때는 어떻게 해야할까? 가장&amp;nbsp;쉬운&amp;nbsp;방법은&amp;nbsp;명시적으로&amp;nbsp;`null`이&amp;nbsp;아님을&amp;nbsp;검사한&amp;nbsp;뒤&amp;nbsp;dereference하는&amp;nbsp;것이다. &lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1670762939630&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;val s: String?

if (s != null) {
    s.length // s is now non-nullable type
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;하지만 safe access expression이라는 더 좋은 방법이 있다. 아래의 코드를 보자. &lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1670762955196&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;s?.length // safe access(call) expression&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;safe access expression `?.`를 사용하면 receiver가 `null`이 아닐 경우 access 결과를, `null`인&amp;nbsp;경우&amp;nbsp;`null`을&amp;nbsp;표현식의&amp;nbsp;결과로&amp;nbsp;반환한다.&amp;nbsp;다시&amp;nbsp;말해,&amp;nbsp;아래와&amp;nbsp;같은&amp;nbsp;의미를&amp;nbsp;갖는다. &lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1670762965855&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;val s: String?

val length = if (s != null) s.length else null // val length = s?.length&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;u&gt;&lt;b&gt;Elvis separator&lt;/b&gt;&lt;/u&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Safe access expression의 반환 결과를 담는 변수는 nullable 타입이어야 한다. Safe access expression이 `null`을 반환할 수도 있기 때문이다. `null`이&amp;nbsp;담기길&amp;nbsp;원하지&amp;nbsp;않는다면&amp;nbsp;`null`인&amp;nbsp;경우&amp;nbsp;사용할&amp;nbsp;default&amp;nbsp;값을&amp;nbsp;지정해주어야&amp;nbsp;한다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1670762983709&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;val s: String?

val length: Int = if (s != null) s.length else 0&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;위와 같이 `null`이 아닌 값을 담을 수도 있지만, &lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Elvis&amp;nbsp;separator를&amp;nbsp;이용하면&amp;nbsp;더&amp;nbsp;세련되게&amp;nbsp;위와&amp;nbsp;똑같은&amp;nbsp;일을&amp;nbsp;할&amp;nbsp;수&amp;nbsp;있다. &lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1670762991360&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;val length: Int = s?.length ?: 0 // s가 null이 아닌 경우 s.length, null인 경우 0&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Elvis&amp;nbsp;separator는&amp;nbsp;`?:`이며&amp;nbsp;고개를&amp;nbsp;왼쪽으로&amp;nbsp;꺾어서&amp;nbsp;바라보면&amp;nbsp;엘비스&amp;nbsp;프레슬리의&amp;nbsp;앞머리와&amp;nbsp;눈처럼&amp;nbsp;보인다고&amp;nbsp;해서&amp;nbsp;Elvis라는&amp;nbsp;이름이&amp;nbsp;붙었다.&amp;nbsp;(from&amp;nbsp;Groovy) &lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;u&gt;&lt;b&gt;Control-flow analysis&lt;/b&gt;&lt;/u&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;코틀린 컴파일러는 control-flow analysis를 수행한다. 따라서 reference가 `null`인지를 명시적으로 검사하고 `fail()` 또는 `return` 같은 함수를 호출한다면 이후부터는&amp;nbsp;safe&amp;nbsp;access&amp;nbsp;없이도&amp;nbsp;접근할&amp;nbsp;수&amp;nbsp;있다. &lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1670763014557&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;val s: String?

if (s == null) return

s.length // s smart cast to non-nullable type&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;u&gt;&lt;b&gt;Not-null assertion&lt;/b&gt;&lt;/u&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Not-null assertion은 피연산자가 `null`이면 NPE를, `null`이 아니면 피연산자를 반환한다. &lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1670763024831&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;s!! // not-null assertion&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Not-null assertion 이후의 코드 역시 safe access 없이도 nullable 타입의 변수에 접근할 수 있다. 하지만 not-null assertion은 개발자가 `null`이 아님을 보장하는 것이기 때문에 실수할 여지가 있다. 지금까지 nullable을 다루는 방법들은 &quot;`null`이면 이렇게 하고, `null`이 아니면 이렇게 해주세요.&quot; 하는 태도였다면, not-null assertion은 &quot;`null`이 아니니까 이렇게 해.&quot;라고 볼 수 있다. 되도록이면 쓰지 않는 것이 좋다. 그럼에도&amp;nbsp;이런&amp;nbsp;문법이&amp;nbsp;존재하는&amp;nbsp;이유는&amp;nbsp;코틀린&amp;nbsp;컴파일러가&amp;nbsp;적절한&amp;nbsp;타입을&amp;nbsp;추론하지&amp;nbsp;못할&amp;nbsp;때도&amp;nbsp;있기&amp;nbsp;때문이다. &lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1670763039643&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class MyAction {
    fun isEnabled(): Boolean = list.selectedValue != null
    // actionPeerformed()는 항상 isEnalbe() 확인 후에 호출된다
    fun actionPerformed() {
        val value = list.selectedValue!! // 따라서 list.selectedValue는 null일 수 없다
        ...
    }
}

...
if (action.isEnabled()) {
    action.actionPerformed()
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Not-null assertion을 사용하면 명시적으로 어디서 NPE가 발생하는지 강조할 수 있다. NPE가 발생하면 무엇이 원인이었는지 바로 확인할 수 있다. 때문에&amp;nbsp;둘&amp;nbsp;이상의&amp;nbsp;not-null&amp;nbsp;assertion을&amp;nbsp;한&amp;nbsp;줄에&amp;nbsp;사용하는&amp;nbsp;것은&amp;nbsp;좋지&amp;nbsp;않은&amp;nbsp;선택이다. &lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1670763051003&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;person.company!!.address!!.country&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;위의 코드에서 NPE가 발생한다면 누가 `null`인지 NPE로부터 바로 알 수 없다.&amp;nbsp; &lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;일반적으로&amp;nbsp;not-null&amp;nbsp;assertion&amp;nbsp;보다&amp;nbsp;safe&amp;nbsp;연산자(safe&amp;nbsp;access,&amp;nbsp;Elvis&amp;nbsp;operator,&amp;nbsp;if-checks)를&amp;nbsp;사용하는&amp;nbsp;것이&amp;nbsp;낫다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;background-color: #9feec3; color: #000000;&quot;&gt;2. Nullable types under the hood&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;`under the hood,` nullable, non-nullable 타입은 `@Nullable`, `@NotNull` 어노테이션으로 구현되었다. 따라서&amp;nbsp;nullable&amp;nbsp;타입을&amp;nbsp;사용한다고&amp;nbsp;performance&amp;nbsp;overhead가&amp;nbsp;생기지는&amp;nbsp;않는다. &lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;u&gt;&lt;b&gt;Nullable types != Optional&lt;/b&gt;&lt;/u&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;- `Optional`은 Nullability 문제에 대한 다른 해결책이다. &lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;-&amp;nbsp;`Optional`은&amp;nbsp;값&amp;nbsp;또는&amp;nbsp;값의&amp;nbsp;부재를&amp;nbsp;저장하고&amp;nbsp;있는&amp;nbsp;특별한&amp;nbsp;라이브러리&amp;nbsp;클래스이다. &lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;-&amp;nbsp;`Optional`을&amp;nbsp;통해&amp;nbsp;값이&amp;nbsp;available한지&amp;nbsp;아닌지를&amp;nbsp;검사할&amp;nbsp;수&amp;nbsp;있다. &lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;-&amp;nbsp;`Optional`&amp;nbsp;타입은&amp;nbsp;초기의&amp;nbsp;객체에&amp;nbsp;대한&amp;nbsp;참조를&amp;nbsp;저장하고&amp;nbsp;있는&amp;nbsp;wrapper이다. &lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;-&amp;nbsp;`Optional`&amp;nbsp;값마다&amp;nbsp;추가적으로&amp;nbsp;객체가&amp;nbsp;생성된다. &lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;`Optional`과 nullable 타입은 같은 문제를 해결하지만 성능 관점에서 보면 매우 다르다. Nullable 타입은 어떤 wrapper도 생성하지 않는다. (어노테이션을 통해 구현되어 있다.) 또한 `Optional&amp;lt;T&amp;gt;`에 `T` 객체를 저장할 수 없는 반면(`Optional` 객체만 담을 수 있다), 코틀린의&amp;nbsp;nullable&amp;nbsp;타입에는&amp;nbsp;non-null&amp;nbsp;객체를&amp;nbsp;담을&amp;nbsp;수&amp;nbsp;있다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;When you use nullable types under the hood, the&amp;nbsp;Kotlin&amp;nbsp;compiler&amp;nbsp;adds&amp;nbsp;additional&amp;nbsp;annotations&amp;nbsp;which&amp;nbsp;are&amp;nbsp;only&amp;nbsp;checked&amp;nbsp;at&amp;nbsp;the&amp;nbsp;compilation&amp;nbsp;time.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;At run time, nullable string is the same string as regular Java String&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;u&gt;&lt;b&gt;List of nullable Int vs nullable List&lt;/b&gt;&lt;/u&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1670763142674&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;List&amp;lt;Int?&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;List of nullable Int. 리스트는 null일 수 없다. 리스트에 담기는 원소는 nullable Int이다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1670763153363&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;List&amp;lt;Int&amp;gt;?&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Nullable List. 리스트 자체가 null일 수 있다. 리스트에 담기는 원소는 Int이다. &lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;문제를&amp;nbsp;풀어보자.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Mark the lines which require a question mark to make the code compile&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1670763171937&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 연습 문제 1
fun foo(list1: List&amp;lt;Int?&amp;gt;, list2: List&amp;lt;Int&amp;gt;?) { 
    list1.size // 1 
    list2.size // 2
    
    val i: Int = // 3 
         list1.get(0) // 4 
    val j: Int = // 5
        list2.get(0) // 6
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;답은&amp;nbsp;가장&amp;nbsp;마지막&amp;nbsp;꼭지에&amp;nbsp;정리해두는&amp;nbsp;게&amp;nbsp;좋겠다. &lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;u&gt;&lt;b&gt;Nullable 타입과 확장 함수&lt;/b&gt;&lt;/u&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Nullable 타입도 확장 함수를 가질 수 있다. 처음에는 확장 함수를 사용할 때 멤버함수처럼 사용하기 때문에 receiver가&amp;nbsp;`null`일&amp;nbsp;때&amp;nbsp;`null`의&amp;nbsp;멤버&amp;nbsp;함수를&amp;nbsp;호출하려고&amp;nbsp;하는&amp;nbsp;것처럼&amp;nbsp;보여서&amp;nbsp;NPE가&amp;nbsp;발생할&amp;nbsp;것이라고&amp;nbsp;오해했다. &lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;하지만 지난 시간에 살펴봤듯 확장 함수란 결국 receiver 타입의 객체를 첫 번째 인자로 받는 `static` 함수와 다를 게 없다. 첫&amp;nbsp;번째&amp;nbsp;인자에&amp;nbsp;null이&amp;nbsp;들어온다고&amp;nbsp;해서(nullable&amp;nbsp;타입을&amp;nbsp;받는다고&amp;nbsp;해서)&amp;nbsp;`static`&amp;nbsp;함수에&amp;nbsp;문제가&amp;nbsp;생기는&amp;nbsp;것은&amp;nbsp;아니기&amp;nbsp;때문에 &lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;nullable&amp;nbsp;타입에&amp;nbsp;확장함수를&amp;nbsp;추가하는&amp;nbsp;것은&amp;nbsp;아무&amp;nbsp;문제가&amp;nbsp;없다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1670763205225&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;fun String?.isEmptyOrNull() = this == null || this.isEmpty() // smart cast

fun main() {
    val s1: String? = null
    println(s1.isEmptyOrNull()) // true
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;위의 예시에서 `s1`은 nullable 타입이지만 `isEmptyOrNull()`를 마치 보통의 reference처럼 호출하고 있다. 이로부터&amp;nbsp;보통의&amp;nbsp;reference처럼&amp;nbsp;멤버에&amp;nbsp;접근한다는&amp;nbsp;것이&amp;nbsp;꼭&amp;nbsp;그&amp;nbsp;참조변수가&amp;nbsp;`null`이&amp;nbsp;아니라는&amp;nbsp;것을&amp;nbsp;보장하지는&amp;nbsp;않는다는&amp;nbsp;사실을&amp;nbsp;알&amp;nbsp;수&amp;nbsp;있다. &lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Nullable receiver에 확장 함수를 추가할 때는 혼란을 주지 않도록 주의가 필요하다. 이름에&amp;nbsp;명시적으로&amp;nbsp;receiver가&amp;nbsp;`null`일&amp;nbsp;수&amp;nbsp;있음을&amp;nbsp;알린다거나&amp;nbsp;하는&amp;nbsp;식으로&amp;nbsp;말이다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;background-color: #9feec3; color: #000000;&quot;&gt;3. Safe casts &lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;u&gt;&lt;b&gt;Type cast: as&lt;/b&gt;&lt;/u&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1670763225977&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;if (any is String) {
    any.toUpperCase() // smart cast to String
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;u&gt;&lt;b&gt;Safe cast: as&lt;/b&gt;&lt;/u&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1670763238531&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;(any as? String)?.toUpperCase()&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;타입 캐스트 `as`는 캐스팅에 실패할 경우 예외를 던지는 반면, safe 캐스트 `as?`는 null을 반환한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;background-color: #9feec3; color: #000000;&quot;&gt;4. Importance of nullability &lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;u&gt;&lt;b&gt;Why nullability is so important?&lt;/b&gt;&lt;/u&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;NPE는 매우 흔히 일어나는 예외이므로 잘 다루는 것이 매우 중요하다. &lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;코틀린은 라이브러리 또는 이외의 공간이 아닌 타입 시스템에서 nullabiliity를 제어할 수 있도록 해두었다. non-null, nullable 타입을 포함하여 nullability를 잘 다룰 수 있게 도와주는 유용한 operators와 smart cast 등은 nullability를&amp;nbsp;제어하기&amp;nbsp;쉽게&amp;nbsp;만들었을뿐&amp;nbsp;아니라&amp;nbsp;`null`을&amp;nbsp;first-class&amp;nbsp;citizen으로&amp;nbsp;만들었다. &lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;nullability를 다룰 수 없을 때 무언가의 부재를 `null`로 나타내는 것이 안티패턴이었으나 코틀린에서는 충분히 가능한 패턴이다. 코틀린의&amp;nbsp;타입&amp;nbsp;시스템에서&amp;nbsp;`null`을&amp;nbsp;추적하는&amp;nbsp;것을&amp;nbsp;도와주기&amp;nbsp;때문이다. &lt;/span&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Nullability in the type system is also one of the key features of Kotlin.&lt;/span&gt;&lt;span style=&quot;background-color: #9feec3; color: #000000;&quot;&gt;5. TMI &lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;연습 문제 1의 정답: 4개(2번, 3번, 5번, 6번) &lt;/span&gt;&lt;/p&gt;</description>
      <category>프로그래밍/Kotlin</category>
      <category>Kotlin for Java Developers</category>
      <category>nullability</category>
      <category>코틀린</category>
      <author>탐p슨</author>
      <guid isPermaLink="true">https://live-everyday.tistory.com/262</guid>
      <comments>https://live-everyday.tistory.com/262#entry262comment</comments>
      <pubDate>Sun, 11 Dec 2022 22:00:14 +0900</pubDate>
    </item>
    <item>
      <title>커리어적으로 예리하고 긴장된 상태를 유지할 것</title>
      <link>https://live-everyday.tistory.com/261</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;커리어적으로 예리하고 긴장된 상태란 쉽게 말해서 언제든 이직할 수 있는 상태를 말한다. 언제나 가슴에 이직을 품고 있어야 한다는 뜻이 아니다. 지금 회사가 아주 마음에 들고, 가능하다면 지금 회사에서 커리어를 끝까지 보내고 싶다고 하더라도, 오히려 그럴수록 더욱 커리어적으로 예리하고 긴장된 상태를 유지하려고 노력해야 한다고 생각한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예리하고 긴장된 상태를 유지해야만&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 끊임없이 성장할 수 있다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 이직하고 싶은/이직해야 하는 상황이 찾아왔을 때도 걱정이 없다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. 회사와 나를 대등한 관계로 여기며 당당할 수 있다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;무뎌지지 말자. 더 나의 상태를 더 예리하게 만들자. 긴장을 놓지말자. 나는 더 잘할 수 있고, 지금의 조건보다 더 좋은 조건, 지금의 회사보다 더 괜찮은 회사는 항상 존재하며, 나만 준비되면 함께 즐겁게 일할 수 있는 뛰어난 실력의 개발자들이 나를 기다리고 있다.&lt;/p&gt;</description>
      <category>private note</category>
      <author>탐p슨</author>
      <guid isPermaLink="true">https://live-everyday.tistory.com/261</guid>
      <comments>https://live-everyday.tistory.com/261#entry261comment</comments>
      <pubDate>Sun, 4 Dec 2022 09:03:24 +0900</pubDate>
    </item>
    <item>
      <title>역시 건강이 최고</title>
      <link>https://live-everyday.tistory.com/260</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;아플 때마다 하는 말이지만 역시 건강이 최고다. 3년 정도 잘 피해 다니다가 최근에 코로나에 걸렸는데 꽤 아팠다. 목이 화상 입은 것처럼 아파서 바나나도 삼키지 못했는데 지금은 많이 좋아졌다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리에게 건강은 일반적으로 아주 좋은 상태로 주어진다. 건강한 것을 당연하게 여기고, 앞으로도 계속 좋을 줄로만 알고 건강을 소홀히 여기기 시작하면 언젠가 반드시 대가를 치르게 되어있다. 건강을 소홀히 해서 코로나에 걸린 것은 아니지만, 코로나로 간만에 아파봤으니 건강의 중요성을 다시 한번 깨닫고, 경각심을 가지는 기회로 삼으려고 한다. 지금 건강한 건 절대 당연한 게 아니다. 감사해야 할 일이다. 앞으로의 건강 또한 그냥 보장되는 게 아니다. 노력해야만 건강할 확률을 높일 수 있을 뿐이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나중에 돌이킬 수 없을 때가 되어서야 진즉 몸 관리좀 할걸 하고 후회하지 말고, 지금 젊고 건강할 때 이 건강을 유지하기 위해 꾸준히 투자해야만 한다.&lt;/p&gt;</description>
      <category>private note</category>
      <author>탐p슨</author>
      <guid isPermaLink="true">https://live-everyday.tistory.com/260</guid>
      <comments>https://live-everyday.tistory.com/260#entry260comment</comments>
      <pubDate>Sat, 3 Dec 2022 14:52:15 +0900</pubDate>
    </item>
    <item>
      <title>Gradle 멀티 모듈 프로젝트 빌드타임 단축하기(30분 -&amp;gt; 2분)</title>
      <link>https://live-everyday.tistory.com/259</link>
      <description>&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;* 한 두 달 전쯤 진행했던 업무&lt;/p&gt;
&lt;h2 style=&quot;text-align: left;&quot; data-ke-size=&quot;size26&quot;&gt;빌드 타임 30분&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;담당하고 있는 서비스에서 &lt;b&gt;의존성을 추가하거나 수정할 때마다 30분이 소요&lt;/b&gt;됐다. 처음엔 '원래 그런가 보다' 하고 넘어갔는데, 해당 서비스에 대해 알게 될수록 30분은 너무 과하다는 생각이 들었다. 의존성을 하나 추가하거나 수정하는 아주 간단한 수정이었고, 프로젝트도 그렇게 거대하고 복잡하지 않은 데 너무 오랜 시간이 걸린다고 생각했다. 그러던 차에 의존성을 수정할 일이 있는 업무를 맡게 되었고, 빌드 타임부터 개선해보기로 했다.&lt;br&gt; &lt;/p&gt;
&lt;h2 style=&quot;text-align: left;&quot; data-ke-size=&quot;size26&quot;&gt;원인이 뭘까&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;해당 서비스는 gradle 멀티 모듈 프로젝트였고, gradle 버전은 4.X였다. 처음에는 build.gradle 파일을 보며 빌드 타임이 느린 원인을 몇 가지 추측해보았다.&lt;br&gt; &lt;br&gt;- deprecated 저장소&lt;br&gt;- 불필요한 저장소(해당 저장소에 있는 의존성에 의존하고 있지 않음)&lt;br&gt;- 중복된 저장소&lt;br&gt;- 중복된 의존성(루트 build.gradle에서 명시한 의존성 서브 build.gradle에서 명시)&lt;br&gt; &lt;br&gt;위의 작업이 크리티컬한 영향을 줄 것 같지는 않았다. 느린 빌드 타임의 원인이라기보다는 그냥 코드 스멜에 가깝다고 생각했다. 검색을 통해 개선할 수 있는 다른 방법을 찾아보았고, 아래 글을 참고하여 추가 개선을 진행하였다.&lt;br&gt; &lt;/p&gt;
&lt;figure data-ke-type=&quot;opengraph&quot; data-og-title=&quot;Improving the Performance of Gradle Builds&quot; data-ke-align=&quot;alignCenter&quot; data-og-description=&quot;When using build scans in the context of profiling a build, the main area of interest in the early stages of diagnosis is the performance page. To get there, click &amp;quot;Performance&amp;quot;&amp;nbsp; in the left hand navigation menu, or follow the link highlighted in the foll&quot; data-og-host=&quot;docs.gradle.org&quot; data-og-source-url=&quot;https://docs.gradle.org/current/userguide/performance.html&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/dSlIs1/hyQDpWa79X/DHXnluvgKqY6v8EE8gBYKk/img.png?width=2818&amp;amp;height=1402&amp;amp;face=0_0_2818_1402,https://scrap.kakaocdn.net/dn/bd5Lxy/hyQDwATzUc/3sENVJjWo9sLBrwRytuwQk/img.png?width=2816&amp;amp;height=1332&amp;amp;face=0_0_2816_1332,https://scrap.kakaocdn.net/dn/djHNJt/hyQDtYvu3D/N207JlfXu8ungcgFB58KjK/img.png?width=2398&amp;amp;height=1264&amp;amp;face=0_0_2398_1264&quot; data-og-url=&quot;https://docs.gradle.org/current/userguide/performance.html&quot;&gt;
 &lt;a href=&quot;https://docs.gradle.org/current/userguide/performance.html&quot; target=&quot;_blank&quot; data-source-url=&quot;https://docs.gradle.org/current/userguide/performance.html&quot;&gt;
  &lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/dSlIs1/hyQDpWa79X/DHXnluvgKqY6v8EE8gBYKk/img.png?width=2818&amp;amp;height=1402&amp;amp;face=0_0_2818_1402,https://scrap.kakaocdn.net/dn/bd5Lxy/hyQDwATzUc/3sENVJjWo9sLBrwRytuwQk/img.png?width=2816&amp;amp;height=1332&amp;amp;face=0_0_2816_1332,https://scrap.kakaocdn.net/dn/djHNJt/hyQDtYvu3D/N207JlfXu8ungcgFB58KjK/img.png?width=2398&amp;amp;height=1264&amp;amp;face=0_0_2398_1264')&quot;&gt; 
  &lt;/div&gt;
  &lt;div class=&quot;og-text&quot;&gt;
   &lt;p class=&quot;og-title&quot;&gt;Improving the Performance of Gradle Builds&lt;/p&gt;
   &lt;p class=&quot;og-desc&quot;&gt;When using build scans in the context of profiling a build, the main area of interest in the early stages of diagnosis is the performance page. To get there, click &quot;Performance&quot;&amp;nbsp; in the left hand navigation menu, or follow the link highlighted in the foll&lt;/p&gt;
   &lt;p class=&quot;og-host&quot;&gt;docs.gradle.org&lt;/p&gt;
  &lt;/div&gt;&lt;/a&gt;
&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt; &lt;/p&gt;
&lt;h2 style=&quot;text-align: left;&quot; data-ke-size=&quot;size26&quot;&gt;버전을 올려보자&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&lt;b&gt;위의 글에서는 gradle과 JVM 최신 버전을 사용하는 것만으로 손쉽게 성능을 개선할 수 있다며 가장 먼저 추천했다.&lt;/b&gt; 그래서 가장 먼저 gradle 버전부터 올렸다.&lt;br&gt; &lt;br&gt;Gradle 4.X에서 최신 버전인 7.X로 버전을 올렸는데 gradle 7.X로 올렸을 때 문제가 되는 gradle plugin이 있어서 당장 최신 버전을 사용할 수는 없었고, 다른 수정 없이 올릴 수 있는 버전인 5.X로 버전을 올렸다.&lt;br&gt; &lt;br&gt;5.X 버전부터 compile()이 deprecated 되었는데, 우리 프로젝트의 모든 build.gradle에서 의존성을 추가할 때 compile()을 사용하고 있었다. compile()은 api() 또는 implementation()으로 수정해야 했기 때문에 api()와 implementation()이 각각 어떤 차이인지 확인해봤다.&lt;br&gt; &lt;br&gt;검색하면 많은 자료들이 나오는 데 그중 내게 가장 간결하고 명확한 설명으로 다가오는 것은 다음과 같았다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt; &lt;b&gt;&lt;u&gt;api()로 추가한 의존성은 public 의존성&lt;/u&gt;&lt;/b&gt;으로, 다른 프로젝트에서 나에 의존할 때 사용할 때 나의 api() 의존성까지 코드(compile)에서 사용할 수 있다. 
 &lt;br&gt; 
 &lt;br&gt;&lt;b&gt;&lt;u&gt;implementation()으로 추가한 의존성은 private 의존성&lt;/u&gt;&lt;/b&gt;으로, 다른 프로젝트에서 나에 의존할 때 나의 implemenation() 의존성은 runtime에 존재하기는 하지만 코드에서 사용할 수는 없다. 
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;위의 내용을 참고하여 compile()을 implementation()과 api()로 수정하였다(대부분이 implementation()으로 수정되었다). 그리고 숨을 고를 겸 의존성을 하나 추가하여 빌드 타임이 얼마나 단축됐는지 테스트했다.&lt;br&gt; &lt;br&gt;30분 걸리던 빌드 타임이 1분 30초 내외로 단축되었다.&lt;br&gt; &lt;/p&gt;
&lt;h2 style=&quot;text-align: left;&quot; data-ke-size=&quot;size26&quot;&gt;뭐가 가장 핵심적인 이슈였을까?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;쉽게 개선할 수 있을 거라고 기대하지 않았고, 이렇게 극단적으로 개선될 수 있으리라고도 기대하지 않았기 때문에 기쁜 동시에 당황스럽기도 했다. 많지 않은 부분을 수정했는데 그중 가장 결정적으로 빌드 타임을 느리게 만들었던 핵심 원인이 무엇인지 궁금해서 다시 하나씩 확인해보았다.&lt;br&gt; &lt;br&gt;모든 것을 원상복구 한 뒤 compile()만 api()와 implementation()으로 수정해보았다. 빌드 타임에 큰 차이가 없었다. Deprecated 저장소, 불필요한 저장소, 중복된 저장소, 중복된 의존성만 제거해보았을 때도 빌드 타임에 큰 차이가 없었다.&lt;br&gt; &lt;br&gt;버전을 5.X로 올리자(compile()이 deprecated 되어서 api()와 implementation() 사용까지 같이 수행했다) 빌드 타임이 2분으로 단축됐다. 여기에 앞서 말한 다른 개선까지 모두 적용해보니 2분에서 1분 30초 내외로 단축되었다.&lt;br&gt; &lt;/p&gt;
&lt;h2 style=&quot;text-align: left;&quot; data-ke-size=&quot;size26&quot;&gt;정리&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&lt;b&gt;낮은 gradle 버전을 사용한다면 버전업만으로 엄청난 빌드 타임 단축을 기대할 수 있다&lt;br&gt; &lt;br&gt;&lt;/b&gt;Gradle 뿐만 아니라 다른 모든 라이브러리 및 프레임워크 역시 일반적으로 버전이 올라갈 때마다 퍼포먼스가 개선되리라고 기대할 수 있는데,&lt;b&gt; &lt;/b&gt;&lt;b&gt;적어도 중요한 몇 가지 라이브러리 및 프레임워크는 동향?을 잘 추적하고 있으면 좋을 것 같다&lt;/b&gt;(현재 최신 버전이 무엇이고, 어떤 버전에서 메이저한 변화가 있었는지 등).&lt;b&gt;&lt;br&gt; &lt;br&gt;&lt;/b&gt;동향 추적을 아무리 빠삭하고 꼼꼼하게 해도 새로운 버전으로 올리지 못한다면 큰 의미가 없다. 프로젝트를 어떻게 관리해야 큰 비용 없이 새로운 버전을 잘 적용할 수 있을까?&lt;b&gt; &lt;/b&gt;&lt;b&gt;서비스 자체에 대한 풍부하고 커버리지 높은 테스트가 있고, 해당 라이브러리 또는 프레임워크 자체에 대한 테스트도 작성되어 있다면 새로운 버전을 도입하는 비용을 줄일 수 있을 것 같다.&lt;/b&gt;&lt;/p&gt;</description>
      <category>private note/한 일, 할 일, 하고싶은 일</category>
      <category>build</category>
      <category>gradle</category>
      <author>탐p슨</author>
      <guid isPermaLink="true">https://live-everyday.tistory.com/259</guid>
      <comments>https://live-everyday.tistory.com/259#entry259comment</comments>
      <pubDate>Mon, 21 Nov 2022 21:19:13 +0900</pubDate>
    </item>
    <item>
      <title>디미터 법칙: 객체의 내부 사정을 궁금해 하지 말 것</title>
      <link>https://live-everyday.tistory.com/258</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;lt;&lt;span style=&quot;color: #000000;&quot;&gt;클린 코드&amp;gt; 속 디미터 법칙에 대한 내용을 정리한 글이다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000; background-color: #f6e199;&quot;&gt;&lt;b&gt;디미터 법칙&lt;/b&gt;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;모듈은 &lt;b&gt;자신이 조작하는 객체의 속사정을 몰라야 한다는 법칙&lt;/b&gt;이다. 구체적으로 예를 들자면 어떤 클래스 C의 메서드 M에서는 아래와 같은 메서드만 호출해야 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;클래스 C의 메서드&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;클래스 C의 인스턴스 변수로 저장된 객체의 메서드&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;메서드 M 안에서 생성한 객체의 메서드&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;메서드 M의 인자로 넘어온 객체의 메서드&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;아래의 코드를 살펴보자.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1668235510329&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;ctxt.getOptions().getScratchDir().getAbsolutePath(); // 아파치 프레임워크 속 코드&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;만약 위의 코드에서 ctxt가 객체라면(자료 구조가 아니라) 이는 디미터 법칙을 어긴 것이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;메서드 체이닝이 중요한 것이 아니다.&amp;nbsp; 디미터 법칙을 어겼는지를 결정하는 요소는 ctxt, Options, ScratchDir이 객체인지 자료 구조인지에 달렸다. 객체라면 내부 구조를 숨겨야 하므로 디미터 법칙의 위반이고, 자료구조라면 내부 구조를 노출하는 것이 당연하므로 디미터 법칙을 위반하지 않은 것이다.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;(자료 구조는 함수 없이 공개 변수만 포함하고 객체는 비공개 변수와 공개 함수를 포함한다면 문제가 간단하겠지만 단순한 자료 구조에도 조회 함수와 설정 함수를 정의하라 요구하는 프레임워크와 표준이 존재한다.)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;위 코드는 아래와 같이 나누는 게 좋다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1668235520763&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Options opts = ctxt.getOptions();
File scratchDir = opts.getScratchDirs();
&amp;hellip;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;그런데 왜 디렉터리의 절대 경로(getAbsolutePath)가 필요할까? 어디에 쓰려고? 위의 코드를 발췌한 곳에서 쭉 따라가 읽다보면 아래와 같은 내용이 나온다.&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1668235967884&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;String outFile = outputDir + &quot;/&quot; + className.replace('.', '/') + &quot;.class&quot;;
FileOutputStream fout = new FileOutputStream(outFile);
BufferedOutputStream bos = new BufferedOutputStream(fout);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;절대 경로는 임시 파일을 생성하기 위해 필요한 것임을 확인할 수 있다. 그렇다면 ctxt 객체에 임시 파일을 생성하라고 시키면 어떨까?&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1668236038974&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;BufferedOutputStream bos = ctxt.createScratchFileStream(classFileName);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;ctxt는 내부 구조를 드러내지 않으며 모듈에서 해당 함수는 자신이 몰라야 하는 여러 객체를 탐색할 필요가 없다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>프로그래밍/리팩터링</category>
      <category>디미터법칙</category>
      <author>탐p슨</author>
      <guid isPermaLink="true">https://live-everyday.tistory.com/258</guid>
      <comments>https://live-everyday.tistory.com/258#entry258comment</comments>
      <pubDate>Sat, 12 Nov 2022 15:56:04 +0900</pubDate>
    </item>
    <item>
      <title>[Kotlin for Java Developers] 확장 함수</title>
      <link>https://live-everyday.tistory.com/257</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;Table of contents&lt;/span&gt;&lt;i&gt;&lt;/i&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;i&gt;1. &lt;/i&gt;Extension functions&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;i&gt;2. &lt;/i&gt;표준 라이브러리 속 확장 함수&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;i&gt;3. &lt;/i&gt;Calling Extensions&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;i&gt;4. &lt;/i&gt;확장 함수가 중요한 이유&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;i&gt;5. &lt;/i&gt;TMI&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;background-color: #f6e199; color: #000000;&quot;&gt;Extension functions&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;u&gt;&lt;b&gt;Extension functions&lt;/b&gt;&lt;/u&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Extension functions(이하 확장 함수)는 클래스 외부에서 정의되지만 클래스의 멤버 함수인 것처럼 사용할 수 있는 함수이다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1668232063799&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 확장 함수 정의
fun String.lastChar() = this.get(this.length - 1)

// 확장 함수 사용하는 예시
val c: Char = &quot;abc&quot;.lastChar()&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;위와 같이 &lt;i&gt;`lastChar()`&lt;/i&gt;라는 extension 함수를 정의하면&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;`lastChar` 함수가 마치 String 클래스에 정의되어 있는 것처럼 사용할 수 있다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;확장 함수는 클래스를 확장하는데, 확장 함수가 확장하는 타입을 receiver라고 부른다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;위의 함수의 경우 `lastChar()`의 receiver는 `String`이다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;확장 함수의 body에서는 `this`를 통해 receiver에 접근할 수 있다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;확장 함수를 사용하기 위해서는 명시적으로 import 해야 한다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;u&gt;&lt;b&gt;자바 코드에서 코틀린의 확장함수 호출하기&lt;/b&gt;&lt;/u&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;자바에서 코틀린의 top-level 함수를 호출할 때 top-level 함수는 &lt;i&gt;`static` &lt;/i&gt;함수로 번역된다. 확장 함수 역시 마찬가지이다.&lt;/span&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;under the hood it&amp;rsquo;s just a regular static function&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;따라서 확장 함수는 receiver 객체의 &lt;i&gt;`private` &lt;/i&gt;멤버에 접근할 수 없다.&lt;/span&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Kotlin extension functions are regular static functions defined in a separate auxiliary class&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;코틀린의 확장 함수는 자바에서 어떻게 해석될까? 아래의 확장 함수를 예로 들어보자.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1668232127642&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;fun String.repeat(n: Int): String {
    val sb = StringBuilder(n * length)
    for (i in 1..n) {
        sb.append(this)
    }
    return sb.toString()
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;위의 함수가 컴파일되어 &lt;i&gt;`static` &lt;/i&gt;함수가 될 때 아래와 같이 &lt;i&gt;`String` &lt;/i&gt;객체(the receiver)를 받을 인자가 맨 앞에 추가된다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1668256638618&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public static repeat(String string, int n) { ... }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;따라서 자바에서 사용할 때는 아래와 같이 호출해야 한다(아래와 같이 번역된다).&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1668256653598&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;StringUtilKt.repeat(&amp;ldquo;ab&amp;rdquo;, 3); // ababab&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;표준 라이브러리 속 확장 함수&lt;/span&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Kotlin standard library = Java standard library + extensions&lt;/span&gt;&lt;/blockquote&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Kotlin doesn&amp;rsquo;t duplicate the standard implementations from Java&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Kotlin SDK는 따로 존재하지 않는다. JDK에 확장 함수를 더한 것뿐이다. 이로써 다음과 같은 장점을 얻을 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;small runtime jar&lt;/li&gt;
&lt;li&gt;easy Java interop&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;표준 라이브러리 속 확장 함수를 몇개 살펴보자.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;u&gt;&lt;b&gt;joinToString()&lt;/b&gt;&lt;/u&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1668232225943&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;fun &amp;lt;T&amp;gt; Iterable&amp;lt;T&amp;gt;.joinToString(
        separator: CharSequence = &quot;, &quot;, 
        prefix: CharSequence = &quot;&quot;, 
        postfix: CharSequence = &quot;&quot;
): String&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;아래와 같이 사용한다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1668232244070&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;println(listOf('a', 'b', 'c')
        .joinToString(separator = &quot;&quot;, prefix = &quot;(&quot;, postfix = &quot;)&quot;)) // (abc)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;한 번만 더 반복하자면 &lt;i&gt;`joinToString()`&lt;/i&gt;은 확장 함수로 &lt;i&gt;`String` &lt;/i&gt;클래스에 정의된 함수가 아니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;u&gt;&lt;b&gt;getOrNull()&lt;/b&gt;&lt;/u&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1668232265208&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;fun &amp;lt;T&amp;gt; Array&amp;lt;T&amp;gt;.getOrNull(index: Int) = if (index in 0 until size) this[index] else null&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;u&gt;&lt;b&gt;withIndex()&lt;/b&gt;&lt;/u&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1668232291926&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;val list = listOf(&quot;a&quot;, &quot;b&quot;, &quot;c&quot;)
for ((index, element) in list.withIndex()) {
    println(&quot;$index $element&quot;)
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;u&gt;&lt;b&gt;until()&lt;/b&gt;&lt;/u&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1668256762945&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;infix fun Int.until(to: Int): IntRange&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1668232308399&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;1.until(10)
1 until 10 // infix 함수이기 때문에 가능한 문법&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;i&gt;`1 until 10`&lt;/i&gt;에서 &lt;i&gt;`until`&lt;/i&gt;은 마치 빌트인 문법인 것 같지만 사실은 infix 형태로 호출된 확장 함수이다. infix는 나중에 살펴보도록 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;u&gt;&lt;b&gt;to()&lt;/b&gt;&lt;/u&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1668232329459&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;infix fun &amp;lt;A, B&amp;gt; A.to(that: B) = Pair(this, that)&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1668232336903&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;mapOf(0 to &amp;ldquo;zero&amp;rdquo;, 1 to &amp;ldquo;one&amp;rdquo;)&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;Calling Extensions&lt;/span&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;u&gt;&lt;b&gt;확장 함수를 override 할 수 있을까?&lt;/b&gt;&lt;/u&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;확장 함수에 대해 이해했는지 확인할 수 있는 문제가 있다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1668232382814&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;open class Parent
class Child: Parent()

fun Parent.foo() = &quot;parent&quot;
fun Child.foo() = &quot;child&quot;

fun main(args: Array&amp;lt;String&amp;gt;) {
    val parent: Parent = Child()
    println(parent.foo())
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;parent가 출력될까 child가 출력될까? 확장 함수는 내부적으로 자바 &lt;i&gt;`static` &lt;/i&gt;함수이므로 다음과 같이 자바로 바꿔서 생각해보자.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1668232399294&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public static String foo(Parent parent) { return &quot;parent&quot;; }
public static String foo(Child child) { return &quot;child&quot;; }

public static void main(String[] args) {
    Parent parent = new Child();
    System.out.println(foo(parent));
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;확장 함수가 컴파일될 때 receiver의 타입은 자바 메소드의 첫 번째 매개변수로 변환된다. 자바는 &lt;i&gt;`static` &lt;/i&gt;함수를 정적으로 resolve한다. 다시 말해, 컴파일 할 때 어떤 &lt;i&gt;`static` &lt;/i&gt;함수를 호출하는 것인지 결정한다. 이때, 오직 인자의 타입만 보고 결정하기 때문에 답은 parent이다. 런타임에 실제 담겨 있는 객체는 아무 영향을 주지 못한다. &lt;i&gt;`static` &lt;/i&gt;함수의 호출은 컴파일 타임에 이미 결정되기 때문이다.&lt;/span&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Extensions are static Java functions under the hood. No override for extension functions in Kotlin&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;i&gt;`override`&lt;/i&gt;할 수 있으려면 시그니처가 같아야 하는데 receiver의 타입이 다르면 매개변수의 타입이 달라지므로 시그니처 또한 달라져서 &lt;i&gt;`override` &lt;/i&gt;될 수가 없다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;코틀린에서도 확장 함수를 다룰 때는 &lt;i&gt;`println(parent.foo())`&lt;/i&gt;에서 &lt;i&gt;`parent`&lt;/i&gt;에 어떤 객체가 담겨있는지가 아니라 &lt;i&gt;`parent`&lt;/i&gt;가 어떤 타입인지만 본다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;u&gt;&lt;b&gt;멤버 함수와 확장 함수가 싸우면 누가 이길까?&lt;/b&gt;&lt;/u&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;멤버 함수와 같은 시그니처를 갖는 extension 함수를 정의한 뒤 호출하면 어떤 함수가 호출될까?&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1668232442655&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;fun String.get(index: Int) = '*'

fun main(args: Array&amp;lt;String&amp;gt;) {
    println(&quot;abc&quot;.get(1))
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;무엇이 출력될까? &lt;i&gt;`*`&lt;/i&gt;? &lt;i&gt;`b`&lt;/i&gt;?&lt;/span&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;member always wins(extension is shadowed by a member)&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;따라서 &lt;i&gt;`b`&lt;/i&gt;가 출력된다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;하지만 확장 함수가 멤버 함수를 overload 하는 것은 가능하다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;확장 함수가 중요한 이유&lt;/span&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;u&gt;&lt;b&gt;Extensions are often named among the most important or lovable features of Kotlin. Why are they so important?&lt;/b&gt;&lt;/u&gt;&lt;/span&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;I think their main purpose is to keep your classes and interfaces APIs minimal.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;It&amp;rsquo;s important to capture the essence of the abstraction with this small set of members that covers the essential functionality.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Members for intrinsic things, extensions for everything else.&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;u&gt;&lt;b&gt;What other use cases are extensions good for?&lt;/b&gt;&lt;/u&gt;&lt;/span&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;you can even extend the APIs of existing Java libraries.&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;어떤 라이브러리가 코틀린을 전혀 염두에 두지 않고 만들어졌다고 하더라도, extension을 추가함으로써 kotlin-like, ergonomic, idiomatic API로 바꿀 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;u&gt;&lt;b&gt;조언&lt;/b&gt;&lt;/u&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;확장 함수와 해당 확장 함수가 확장하는 클래스는 가깝게 두는게 좋다. 확장 함수가 최대한 발견하기 쉽도록 말이다. 물론 확장 함수가 일반적이지 않고 다른 모듈의 특정 서브 도메인과 관련된 확장 함수라면 해당 모듈에 둔다. 그러나 어떤 경우든 확장 함수끼리는 같은 클래스나 파일에 모아주는게 좋다.&lt;/span&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;When you extend some library or some particular class, just put all the logically connected extensions into the same file so that they&amp;rsquo;re easily discoverable.&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;TMI&lt;/span&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;주제에 안맞는 내용은 TMI라는 꼭지를 만들어서 여기에 정리해두는 게 좋을 것 같다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;u&gt;&lt;b&gt;Triple-quoted string literals&lt;/b&gt;&lt;/u&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;코틀린에서 여러 줄의 문자열을 formatting 할 수 있는 방법이다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1668232642623&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;val q = &quot;&quot;&quot;To code,
    or not to code?..&quot;&quot;&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;위와 같이 사용하는 경우 indent까지 들어가서 아래와 같이 출력된다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;To code,&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;nbsp; or not to code?..&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;IDE에서 줄바꿈에 따라 추가된 indent를 무시하고 새로운 행을 시작하고 싶다면 다음과 같은 방법이 존재한다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1668232661822&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;val q = &quot;&quot;&quot;To code,
    |or not to code?..&quot;&quot;&quot;.trimMargin() // |는 default margin prefix이며 설정할 수 있다.&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1668232672304&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;val q = &quot;&quot;&quot;To code,
    or not to code?..&quot;&quot;&quot;.trimIndent()&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;결과는 둘 모두 다음과 같다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;To code,&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;or not to code?..&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;u&gt;&lt;b&gt;toIntOrNull()&lt;/b&gt;&lt;/u&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;i&gt;`String`&lt;/i&gt;의 확장 함수 중 하나이다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1668232617584&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&quot;123&quot;.toIntOrNull() // 123
&quot;xx&quot;.toIntOrNull() // null
&quot;xx&quot;.toInt() // NumberFormatException&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;처음 코틀린을 배울 때 가장 임팩트가 컸던 것을 꼽으라면 확장 함수를 꼽겠다. 당연히 클래스에 정의된 멤버 함수인 줄 알았는데, 해당 클래스와 아무 관련도 없는 사용자가 자신이 원하는 함수를 확장 함수로 만들어 해당 클래스의 멤버 함수인냥 사용하는 것이라니. 마법같은 일이라고 생각했는데 &lt;i&gt;`under the hood,` &lt;/i&gt;그저 &lt;i&gt;`static` &lt;/i&gt;함수에 불과하다니.&lt;/span&gt;&lt;/p&gt;</description>
      <category>프로그래밍/Kotlin</category>
      <category>extensionfunctions</category>
      <category>Kotlin for Java Developers</category>
      <author>탐p슨</author>
      <guid isPermaLink="true">https://live-everyday.tistory.com/257</guid>
      <comments>https://live-everyday.tistory.com/257#entry257comment</comments>
      <pubDate>Sat, 12 Nov 2022 15:00:34 +0900</pubDate>
    </item>
  </channel>
</rss>