<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Spring Boot on</title><link>https://taetaetae.github.io/tags/spring-boot/</link><description>Recent content in Spring Boot on</description><generator>Hugo</generator><language>en</language><lastBuildDate>Mon, 06 Apr 2020 23:59:36 +0000</lastBuildDate><atom:link href="https://taetaetae.github.io/tags/spring-boot/index.xml" rel="self" type="application/rss+xml"/><item><title>스프링 부트에 필터를 '조심해서' 사용하는 두 가지 방법</title><link>https://taetaetae.github.io/2020/04/06/spring-boot-filter/</link><pubDate>Mon, 06 Apr 2020 23:59:36 +0000</pubDate><guid>https://taetaetae.github.io/2020/04/06/spring-boot-filter/</guid><description>&lt;p>웹 어플리케이션에서 필터를 사용하면 중복으로 처리되는 내용을 한곳에서 처리할 수 있다거나 서비스의 다양한 니즈를 충족시키기에 안성맞춤인 장치인것 같다. 필터란 무엇인가 에 대한 내용은 워낙에 다른 블로그나 공식 도큐먼트에서 자세하게 그리고 다양하게 설명하고 있기에 기본 개념에 대해서는 설명하지 않도록 하려 한다. &lt;!--more -->
이번 포스팅에서는 스프링 부트를 사용하면서 어노테이션이라는 간편함에 취해(?) &amp;ldquo;돌격 앞으로, 닥공&amp;rdquo; 의 자세로 개발을 하려했던 필자를 보고 &amp;ldquo;반성&amp;quot;의 자세로 필터를 등록하는 방법에 대해 명확하게 정리를 하고자 한다. 마지막으로는 아주 간단하면서도 엄청나게 위험한 필터 설정 사례에 대해서도 짚고 넘어가보자.
그냥 넘어가면 아쉬우니, 한번이라도 &amp;lsquo;spring&amp;rsquo; 이라는 framework 를 접해본 사람이라면 봤을법한 그림을 첨부하는것으로 필터란 무엇인가 에 대한 설명을 대신하는게 좋겠다.&lt;/p>
&lt;figure>&lt;a class="lightgallery" href="https://taetaetae.github.io/images/spring-boot-filter/spring-request-lifecycle.jpg" title="/images/spring-boot-filter/spring-request-lifecycle.jpg" data-thumbnail="/images/spring-boot-filter/spring-request-lifecycle.jpg" data-sub-html="&lt;h2>출처 : https://justforchangesake.wordpress.com/2014/05/07/spring-mvc-request-life-cycle/&lt;/h2>">
 &lt;img
 class="lazyload"
 src="https://taetaetae.github.io/svg/loading.min.svg"
 data-src="https://taetaetae.github.io/images/spring-boot-filter/spring-request-lifecycle.jpg"
 data-srcset="https://taetaetae.github.io/images/spring-boot-filter/spring-request-lifecycle.jpg, https://taetaetae.github.io/images/spring-boot-filter/spring-request-lifecycle.jpg 1.5x, https://taetaetae.github.io/images/spring-boot-filter/spring-request-lifecycle.jpg 2x"
 data-sizes="auto"
 alt="/images/spring-boot-filter/spring-request-lifecycle.jpg" width="50%" />
 &lt;/a>&lt;figcaption class="image-caption">출처 : &lt;a href="https://justforchangesake.wordpress.com/2014/05/07/spring-mvc-request-life-cycle/" target="_blank" rel="noopener noreffer ">https://justforchangesake.wordpress.com/2014/05/07/spring-mvc-request-life-cycle/&lt;/a>&lt;/figcaption>
 &lt;/figure>
&lt;p>방법을 설명하기 전에 동일하게 사용될 필터와 컨트롤러 코드를 보면 다음과 같다.&lt;/p>
&lt;ul>
&lt;li>필터&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="nd">@Slf4j&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="kd">public&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">class&lt;/span> &lt;span class="nc">MyFilter&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">implements&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Filter&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">	&lt;/span>&lt;span class="nd">@Override&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">	&lt;/span>&lt;span class="kd">public&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kt">void&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nf">init&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">FilterConfig&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">filterConfig&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">throws&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">ServletException&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">		&lt;/span>&lt;span class="n">log&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">info&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;init MyFilter&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">	&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">	&lt;/span>&lt;span class="nd">@Override&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">	&lt;/span>&lt;span class="kd">public&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kt">void&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nf">doFilter&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">ServletRequest&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">servletRequest&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">ServletResponse&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">servletResponse&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">FilterChain&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">filterChain&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">		&lt;/span>&lt;span class="kd">throws&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">IOException&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">ServletException&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">		&lt;/span>&lt;span class="n">log&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">info&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;doFilter MyFilter, uri : {}&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">((&lt;/span>&lt;span class="n">HttpServletRequest&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="n">servletRequest&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="na">getRequestURI&lt;/span>&lt;span class="p">());&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">		&lt;/span>&lt;span class="n">filterChain&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">doFilter&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">servletRequest&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">servletResponse&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">	&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">	&lt;/span>&lt;span class="nd">@Override&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">	&lt;/span>&lt;span class="kd">public&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kt">void&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nf">destroy&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">		&lt;/span>&lt;span class="n">log&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">info&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;destroy MyFilter&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">	&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ul>
&lt;li>테스트 할 컨트롤러&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="nd">@Slf4j&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nd">@RestController&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="kd">public&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">class&lt;/span> &lt;span class="nc">SampleController&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">	&lt;/span>&lt;span class="nd">@GetMapping&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;/test&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">	&lt;/span>&lt;span class="kd">public&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">String&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nf">test&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">		&lt;/span>&lt;span class="k">return&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s">&amp;#34;test&amp;#34;&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">	&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">	&lt;/span>&lt;span class="nd">@GetMapping&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;/filtered/test&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">	&lt;/span>&lt;span class="kd">public&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">String&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nf">filteredTest&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">		&lt;/span>&lt;span class="k">return&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s">&amp;#34;filtered&amp;#34;&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">	&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="방법-1--filterregistrationbean">방법 1 : FilterRegistrationBean&lt;/h2>
&lt;p>아주 간단하게, 일반 url 하나와 필터에 적용할 url 두개를 만들고 설정하려 한다. FilterRegistrationBean 을 이용해서 위에서 만들었던 필터를 아래처럼 등록해보자.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="nd">@SpringBootApplication&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="kd">public&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">class&lt;/span> &lt;span class="nc">Method1Application&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">	&lt;/span>&lt;span class="kd">public&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">static&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kt">void&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nf">main&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">String&lt;/span>&lt;span class="o">[]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">args&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">		&lt;/span>&lt;span class="n">SpringApplication&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">run&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">Method1Application&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">class&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">args&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">	&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">	&lt;/span>&lt;span class="nd">@Bean&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">	&lt;/span>&lt;span class="kd">public&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">FilterRegistrationBean&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nf">setFilterRegistration&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">		&lt;/span>&lt;span class="n">FilterRegistrationBean&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">filterRegistrationBean&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">new&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">FilterRegistrationBean&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">new&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">MyFilter&lt;/span>&lt;span class="p">());&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">		&lt;/span>&lt;span class="c1">// filterRegistrationBean.setUrlPatterns(Collections.singletonList(&amp;#34;/filtered/*&amp;#34;)); // list 를 받는 메소드&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">		&lt;/span>&lt;span class="n">filterRegistrationBean&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">addUrlPatterns&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;/filtered/*&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c1">// string 여러개를 가변인자로 받는 메소드&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">		&lt;/span>&lt;span class="k">return&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">filterRegistrationBean&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">	&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>위 주석에도 적었지만 filterRegistrationBean 의 &amp;ldquo;setUrlPatterns&amp;rdquo; 와 &amp;ldquo;addUrlPatterns&amp;rdquo; 의 차이는 별거 없다. list 자체를 받을건지 아니면 가변인자로 계속 추가 할것인지. 이렇게 되면 &amp;ldquo;/filtered/&amp;ldquo;으로 &amp;ldquo;시작&amp;quot;하는 패턴의 url의 요청이 오게 되면 등록한 필터를 통과하게 된다.&lt;/p>
&lt;ul>
&lt;li>실행 : 필터 생성&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl"> /&lt;span class="se">\\&lt;/span> / ___&lt;span class="s1">&amp;#39;_ __ _ _(_)_ __ __ _ \ \ \ \
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s1">( ( )\___ | &amp;#39;&lt;/span>_ &lt;span class="p">|&lt;/span> &lt;span class="s1">&amp;#39;_| | &amp;#39;&lt;/span>_ &lt;span class="se">\/&lt;/span> _&lt;span class="sb">`&lt;/span> &lt;span class="p">|&lt;/span> &lt;span class="se">\ \ \ \
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> &lt;span class="se">\\&lt;/span>/ ___&lt;span class="o">)&lt;/span>&lt;span class="p">|&lt;/span> &lt;span class="p">|&lt;/span>_&lt;span class="o">)&lt;/span>&lt;span class="p">|&lt;/span> &lt;span class="p">|&lt;/span> &lt;span class="p">|&lt;/span> &lt;span class="p">|&lt;/span> &lt;span class="p">|&lt;/span> &lt;span class="o">||&lt;/span> &lt;span class="o">(&lt;/span>_&lt;span class="p">|&lt;/span> &lt;span class="p">|&lt;/span> &lt;span class="o">)&lt;/span> &lt;span class="o">)&lt;/span> &lt;span class="o">)&lt;/span> &lt;span class="o">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39; |____| .__|_| |_|_| |_\__, | / / / /
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s1"> =========|_|==============|___/=/_/_/_/
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s1"> :: Spring Boot :: (v2.2.6.RELEASE)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s1">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s1">2020-04-06 23:45:01.225 INFO 14672 --- [ main] c.t.s.method1.Method1Application : No active profile set, falling back to default profiles: default
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s1">2020-04-06 23:45:02.153 INFO 14672 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8080 (http)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s1">2020-04-06 23:45:02.168 INFO 14672 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat]
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s1">2020-04-06 23:45:02.168 INFO 14672 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/9.0.33]
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s1">2020-04-06 23:45:02.361 INFO 14672 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s1">2020-04-06 23:45:02.362 DEBUG 14672 --- [ main] o.s.web.context.ContextLoader : Published root WebApplicationContext as ServletContext attribute with name [org.springframework.web.context.WebApplicationContext.ROOT]
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s1">2020-04-06 23:45:02.362 INFO 14672 --- [ main] o.s.web.context.ContextLoader : Root WebApplicationContext: initialization completed in 1082 ms
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s1">2020-04-06 23:45:02.391 DEBUG 14672 --- [ main] o.s.b.w.s.ServletContextInitializerBeans : Mapping filters: filterRegistrationBean urls=[/filtered/*] order=2147483647, characterEncodingFilter urls=[/*] order=-2147483648, formContentFilter urls=[/*] order=-9900, requestContextFilter urls=[/*] order=-105
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s1">2020-04-06 23:45:02.391 DEBUG 14672 --- [ main] o.s.b.w.s.ServletContextInitializerBeans : Mapping servlets: dispatcherServlet urls=[/]
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s1">2020-04-06 23:45:02.409 DEBUG 14672 --- [ main] o.s.b.w.s.f.OrderedRequestContextFilter : Filter &amp;#39;&lt;/span>requestContextFilter&lt;span class="s1">&amp;#39; configured for use
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s1">2020-04-06 23:45:02.409 DEBUG 14672 --- [ main] s.b.w.s.f.OrderedCharacterEncodingFilter : Filter &amp;#39;&lt;/span>characterEncodingFilter&lt;span class="s1">&amp;#39; configured for use
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s1">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s1">// 필터가 생성 되었다!
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s1">2020-04-06 23:45:02.410 INFO 14672 --- [ main] c.t.springbootfilter.method1.MyFilter : init MyFilter
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s1">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s1">2020-04-06 23:45:02.410 DEBUG 14672 --- [ main] o.s.b.w.s.f.OrderedFormContentFilter : Filter &amp;#39;&lt;/span>formContentFilter&lt;span class="s1">&amp;#39; configured for use
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s1">2020-04-06 23:45:02.544 INFO 14672 --- [ main] o.s.s.concurrent.ThreadPoolTaskExecutor : Initializing ExecutorService &amp;#39;&lt;/span>applicationTaskExecutor&lt;span class="err">&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ul>
&lt;li>일반 url&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">2020-04-06 23:45:27.526 DEBUG &lt;span class="m">14672&lt;/span> --- &lt;span class="o">[&lt;/span>nio-8080-exec-1&lt;span class="o">]&lt;/span> o.s.web.servlet.DispatcherServlet : GET &lt;span class="s2">&amp;#34;/test&amp;#34;&lt;/span>, &lt;span class="nv">parameters&lt;/span>&lt;span class="o">={}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">2020-04-06 23:45:27.528 DEBUG &lt;span class="m">14672&lt;/span> --- &lt;span class="o">[&lt;/span>nio-8080-exec-1&lt;span class="o">]&lt;/span> s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped to com.taetaetae.springbootfilter.method1.SampleController#test&lt;span class="o">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">2020-04-06 23:45:27.548 DEBUG &lt;span class="m">14672&lt;/span> --- &lt;span class="o">[&lt;/span>nio-8080-exec-1&lt;span class="o">]&lt;/span> m.m.a.RequestResponseBodyMethodProcessor : Using &lt;span class="s1">&amp;#39;text/html&amp;#39;&lt;/span>, given &lt;span class="o">[&lt;/span>text/html, application/xhtml+xml, image/webp, image/apng, application/xml&lt;span class="p">;&lt;/span>&lt;span class="nv">q&lt;/span>&lt;span class="o">=&lt;/span>0.9, application/signed-exchange&lt;span class="p">;&lt;/span>&lt;span class="nv">v&lt;/span>&lt;span class="o">=&lt;/span>b3&lt;span class="p">;&lt;/span>&lt;span class="nv">q&lt;/span>&lt;span class="o">=&lt;/span>0.9, */*&lt;span class="p">;&lt;/span>&lt;span class="nv">q&lt;/span>&lt;span class="o">=&lt;/span>0.8&lt;span class="o">]&lt;/span> and supported &lt;span class="o">[&lt;/span>text/plain, */*, text/plain, */*, application/json, application/*+json, application/json, application/*+json&lt;span class="o">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">2020-04-06 23:45:27.548 DEBUG &lt;span class="m">14672&lt;/span> --- &lt;span class="o">[&lt;/span>nio-8080-exec-1&lt;span class="o">]&lt;/span> m.m.a.RequestResponseBodyMethodProcessor : Writing &lt;span class="o">[&lt;/span>&lt;span class="s2">&amp;#34;test&amp;#34;&lt;/span>&lt;span class="o">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">2020-04-06 23:45:27.555 DEBUG &lt;span class="m">14672&lt;/span> --- &lt;span class="o">[&lt;/span>nio-8080-exec-1&lt;span class="o">]&lt;/span> o.s.web.servlet.DispatcherServlet : Completed &lt;span class="m">200&lt;/span> OK
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ul>
&lt;li>필터링 url&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">// 필터에 들어온것 확인!
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">2020-04-06 23:45:37.455 INFO &lt;span class="m">14672&lt;/span> --- &lt;span class="o">[&lt;/span>nio-8080-exec-2&lt;span class="o">]&lt;/span> c.t.springbootfilter.method1.MyFilter : doFilter MyFilter, uri : /filtered/test
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">2020-04-06 23:45:37.456 DEBUG &lt;span class="m">14672&lt;/span> --- &lt;span class="o">[&lt;/span>nio-8080-exec-2&lt;span class="o">]&lt;/span> o.s.web.servlet.DispatcherServlet : GET &lt;span class="s2">&amp;#34;/filtered/test&amp;#34;&lt;/span>, &lt;span class="nv">parameters&lt;/span>&lt;span class="o">={}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">2020-04-06 23:45:37.456 DEBUG &lt;span class="m">14672&lt;/span> --- &lt;span class="o">[&lt;/span>nio-8080-exec-2&lt;span class="o">]&lt;/span> s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped to com.taetaetae.springbootfilter.method1.SampleController#filteredTest&lt;span class="o">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">2020-04-06 23:45:37.457 DEBUG &lt;span class="m">14672&lt;/span> --- &lt;span class="o">[&lt;/span>nio-8080-exec-2&lt;span class="o">]&lt;/span> m.m.a.RequestResponseBodyMethodProcessor : Using &lt;span class="s1">&amp;#39;text/html&amp;#39;&lt;/span>, given &lt;span class="o">[&lt;/span>text/html, application/xhtml+xml, image/webp, image/apng, application/xml&lt;span class="p">;&lt;/span>&lt;span class="nv">q&lt;/span>&lt;span class="o">=&lt;/span>0.9, application/signed-exchange&lt;span class="p">;&lt;/span>&lt;span class="nv">v&lt;/span>&lt;span class="o">=&lt;/span>b3&lt;span class="p">;&lt;/span>&lt;span class="nv">q&lt;/span>&lt;span class="o">=&lt;/span>0.9, */*&lt;span class="p">;&lt;/span>&lt;span class="nv">q&lt;/span>&lt;span class="o">=&lt;/span>0.8&lt;span class="o">]&lt;/span> and supported &lt;span class="o">[&lt;/span>text/plain, */*, text/plain, */*, application/json, application/*+json, application/json, application/*+json&lt;span class="o">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">2020-04-06 23:45:37.457 DEBUG &lt;span class="m">14672&lt;/span> --- &lt;span class="o">[&lt;/span>nio-8080-exec-2&lt;span class="o">]&lt;/span> m.m.a.RequestResponseBodyMethodProcessor : Writing &lt;span class="o">[&lt;/span>&lt;span class="s2">&amp;#34;filtered&amp;#34;&lt;/span>&lt;span class="o">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">2020-04-06 23:45:37.459 DEBUG &lt;span class="m">14672&lt;/span> --- &lt;span class="o">[&lt;/span>nio-8080-exec-2&lt;span class="o">]&lt;/span> o.s.web.servlet.DispatcherServlet : Completed &lt;span class="m">200&lt;/span> OK
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="방법-2--webfilter--servletcomponentscan">방법 2 : @WebFilter + @ServletComponentScan&lt;/h2>
&lt;p>@ServletComponentScan 어노테이션을 @Configuration 어노테이션이 설정되어 있는곳에 걸어준 다음 위에서 설정한 필터에 @WebFilter 어노테이션을 설정해주면 아주 간단하게 끝이 난다.&lt;/p></description></item><item><title>조금 더 괜찮은 Rest Template 2부 - Circuit-breaker</title><link>https://taetaetae.github.io/2020/03/29/better-rest-template-2-netflix-hystrix/</link><pubDate>Sun, 29 Mar 2020 23:09:16 +0000</pubDate><guid>https://taetaetae.github.io/2020/03/29/better-rest-template-2-netflix-hystrix/</guid><description>&lt;p>&lt;a href="https://taetaetae.github.io/2020/03/22/better-rest-template-1-retryable/" rel="">지난 포스팅&lt;/a>에서는 Retryable 를 활용해서 간헐적인 네트워크 오류를 &amp;ldquo;재시도&amp;quot;를 함으로써 아주 간단하면서도 강력하게 해결할 수 있는 방법에 대해 알아보았다. 실제로 필자가 운영하는 서비스 에서도 Retryable 를 이용하기 전과 후를 비교해보면 간헐적인 네트워크 오류의 빈도수가 확실히 줄어든것을 확인할 수 있었다. &lt;!--more -->&lt;/p>
&lt;p>이렇게 &amp;ldquo;재시도&amp;quot;를 해서 요청했을때 성공 응답을 받을 경우엔 문제가 안되지만 네트워크 오류가 아닌 실제로 호출을 받는 해당 서버에서 문제가 발생했다면 어떨까? 예컨대, 해당 서버에서 DB를 조회하는 API를 호출한다고 가정했을때 DB 자체에서 어떠한 오류가 난다면. 이런 경우는 단순히 &amp;ldquo;재시도&amp;quot;로 해결할 수 없는 문제다.&lt;/p>
&lt;p>물론 Retryable 의 Recover 어노테이션을 활용했기 때문에 클라이언트 즉, 사용자에게는 오류응답이 발생을 안했겠지만 호출 받는 서버 자체에서의 에러가 발생하는데 이런식의 재시도를 계속 시도한다면 호출 받는 서버 입장에서는 이 &amp;ldquo;재시도&amp;rdquo; request 또한 &amp;ldquo;부하&amp;rdquo; 로 받게 되고 결국 2차, 3차 장애가 이어질 수 밖에 없다.&lt;/p>
&lt;p>기존 한덩어리로 관리되던 Monolithic Architecture 에서는 자체적으로 관리하기 때문에 이러한 에러 컨트롤 또한 자체적으로 관리를 할 수 있지만, 모듈이 모듈을 호출하게 되는 Microservice Architecture 로 바뀌다보니 이런 &amp;ldquo;연쇄 장애(?)&amp;rdquo; 같은 현상이 발생하게 되는 경우가 있다. 호출을 받는 서버의 상태가 이상하면 (에러응답이 지정한 임계치를 벗어나는 수준으로 맞춰서 발생한다면) 적절하게 호출을 하지 않고 (2차 장애를 내지 않도록 호출 자체를 하지 않고) 어느정도 기다리다 클라이언트에게는 에러응답이 아닌 미리 정해둔 응답을 내려주고, 에러가 복구되면 다시 호출하도록 하는 &amp;ldquo;무언가&amp;rdquo; 가 필요하지 않을까?&lt;/p>
&lt;figure>&lt;a class="lightgallery" href="https://taetaetae.github.io/images/better-rest-template-2-netflix-hystrix/domino.gif" title="/images/better-rest-template-2-netflix-hystrix/domino.gif" data-thumbnail="/images/better-rest-template-2-netflix-hystrix/domino.gif" data-sub-html="&lt;h2>연쇄 장애. 제발 멈춰&amp;hellip; 출처 : http://dpg.danawa.com/mobile/community/view?boardSeq=175&amp;listSeq=4066389&lt;/h2>">
 &lt;img
 class="lazyload"
 src="https://taetaetae.github.io/svg/loading.min.svg"
 data-src="https://taetaetae.github.io/images/better-rest-template-2-netflix-hystrix/domino.gif"
 data-srcset="https://taetaetae.github.io/images/better-rest-template-2-netflix-hystrix/domino.gif, https://taetaetae.github.io/images/better-rest-template-2-netflix-hystrix/domino.gif 1.5x, https://taetaetae.github.io/images/better-rest-template-2-netflix-hystrix/domino.gif 2x"
 data-sizes="auto"
 alt="/images/better-rest-template-2-netflix-hystrix/domino.gif" width="30%" />
 &lt;/a>&lt;figcaption class="image-caption">연쇄 장애. 제발 멈춰&amp;hellip; &lt;br> 출처 : &lt;a href="http://dpg.danawa.com/mobile/community/view?boardSeq=175&amp;amp;listSeq=4066389" target="_blank" rel="noopener noreffer ">http://dpg.danawa.com/mobile/community/view?boardSeq=175&amp;listSeq=4066389&lt;/a>&lt;/figcaption>
 &lt;/figure>
&lt;p>지난 포스팅에 이어 이번 포스팅 에서는 그 &amp;ldquo;무언가&amp;rdquo;. 즉, Circuit-breaker 에 대해 알아보고 직접 구현 및 테스트 하면서 돌아가는 원리에 대해 이해 해보고자 한다. 막상 개념은 머릿속에 있지만 직접 구현해보지 않으면 내것이 아니기에, 직접 구현하고 설정값들을 바꿔가면서 언젠가 필요한 순간에 꺼내서 사용할 수 있는 나만의 &amp;ldquo;무기&amp;rdquo; 를 만들어 보고자 한다.&lt;/p>
&lt;h2 id="circuit-breaker-">Circuit breaker ?&lt;/h2>
&lt;p>(한국 발음으로) 서킷브레이커를 검색해보면 주식시장 관련된 내용이 꽤 나온다. (앗, 잠깐 눈물좀&amp;hellip;) &lt;a href="http://bitly.kr/kSm6Y" target="_blank" rel="noopener noreffer ">서킷 브레이커&lt;/a>. 이 용어는 다양한 곳에서 사용되는데 &amp;ldquo;회로 차단기&amp;rdquo; 라고도 검색이 된다. 해당 내용을 발췌해보면 다음과 같다.&lt;/p>
&lt;blockquote>
&lt;p>회로 차단기는 전기 회로에서 과부하가 걸리거나 단락으로 인한 피해를 막기 위해 자동으로 회로를 정지시키는 장치이다. 과부하 차단기와 누전 차단기로 나뉜다. 퓨즈와 다른 점은, 차단기는 어느 정도 시간이 지난 뒤, 원래의 기능이 동작하도록 복귀된다.&lt;/p>&lt;/blockquote>
&lt;p>여기서 가장 중요한 문장은 &amp;ldquo;피해를 막기 위해 자동으로 회로를 정지시키는&amp;rdquo;, &amp;ldquo;어느정도 시간이 지난뒤 원래의 기능이 동작하도록 복귀된다&amp;rdquo; 이 부분이 가장 중요한 것 같다. 시스템 구성이 점점 Microservice Architecture 로 바뀌어 가는 시점에서 이러한 &amp;ldquo;서킷브레이커&amp;quot;는 자동으로 모듈간의 호출 에러를 감지하고 위에서 말한 &amp;ldquo;연쇄 장애&amp;quot;를 사전에 막을 수 있는 아주 중요한 기능이라 생각된다.&lt;/p>
&lt;p>&amp;ldquo;circuit breaker spring&amp;rdquo; 이라는 키워드로 검색해보면 이러한 고민을 이미 Netflix 라는 회사에서 &lt;a href="https://github.com/Netflix/Hystrix" target="_blank" rel="noopener noreffer ">Hystrix&lt;/a> 라는 이름으로 개발이 된것을 알 수 있다. 이 core 모듈을 Spring 에서 한번 더 감싸서 Spring Boot 에서 사용하기 좋게 spring-cloud-starter-netflix-hystrix 라는 이름으로 만들어 둔 것이 있는데 이것을 활용해 보기로 하자.&lt;/p>
&lt;h2 id="구현">구현&lt;/h2>
&lt;p>늘 그랬듯이 SpringBoot 프로젝트를 만들고 테스트할 Controller 를 만들어 주자. 원래대로라면 호출을 하는 모듈과 호출을 받는 모듈, 2개의 모듈을 만들어서 테스트 해야 하지만 편의를 위해 하나의 모듈에서 두개의 Controller 을 만들고 테스트 해보는 것으로 하자.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="nd">@RestController&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="kd">public&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">class&lt;/span> &lt;span class="nc">MainController&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">	&lt;/span>&lt;span class="kd">private&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">final&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">MainService&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">mainService&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">	&lt;/span>&lt;span class="nd">@GetMapping&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;index&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">	&lt;/span>&lt;span class="kd">public&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">String&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nf">index&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">String&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">key&lt;/span>&lt;span class="p">){&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">		&lt;/span>&lt;span class="k">return&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">mainService&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">getResult&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">key&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">	&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">	&lt;/span>&lt;span class="kd">public&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nf">MainController&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">MainService&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">mainService&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">		&lt;/span>&lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">mainService&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">mainService&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">	&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nd">@Slf4j&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nd">@Service&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="kd">public&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">class&lt;/span> &lt;span class="nc">MainService&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">	&lt;/span>&lt;span class="kd">private&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">RestTemplate&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">restTemplate&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">	&lt;/span>&lt;span class="kd">public&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">String&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nf">getResult&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">String&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">key&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">		&lt;/span>&lt;span class="k">return&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">restTemplate&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">getForObject&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;http://localhost:8080/target?key=&amp;#34;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">key&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">String&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">class&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">	&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">	&lt;/span>&lt;span class="kd">public&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nf">MainService&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">RestTemplate&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">restTemplate&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">		&lt;/span>&lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">restTemplate&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">restTemplate&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">	&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nd">@Slf4j&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nd">@RestController&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="kd">public&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">class&lt;/span> &lt;span class="nc">TargetContoller&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">	&lt;/span>&lt;span class="nd">@GetMapping&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;/target&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">	&lt;/span>&lt;span class="kd">public&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">String&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nf">target&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">String&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">key&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">		&lt;/span>&lt;span class="n">log&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">info&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;input key : {}&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">key&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">		&lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">!&lt;/span>&lt;span class="n">StringUtils&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">equals&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;taetaetae&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">key&lt;/span>&lt;span class="p">))&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">			&lt;/span>&lt;span class="k">throw&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">new&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">RuntimeException&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;Invalid key&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">		&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">		&lt;/span>&lt;span class="k">return&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s">&amp;#34;target&amp;#34;&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">	&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>사실 설명할 부분도 없긴 하지만 그래도 적어보면, &lt;code>/index&lt;/code>라는 주소와 &lt;code>key&lt;/code>라는 파라미터로 요청을 하면 &lt;code>/target&lt;/code>이라는 컨트롤러가 받아서 &lt;code>key&lt;/code> 값에 따라 정상 응답을 줄지 아니면 에러를 응답하는 코드이다. 목표로 하는건, 파라미터를 일부러 잘못줘서 에러를 발생하는데 Hystrix를 적용해서 설정한 기준에 따라 응답은 미리 정해둔 응답을, &lt;code>TargetContoller&lt;/code>에서는 잠시동안 요청을 받지 않다가 조금있다가 다시 호출하면 요청을 받는 시나리오로 작성해 보려 한다.
우선 필요한 dependency를 추가해준다.&lt;/p></description></item><item><title>조금 더 괜찮은 Rest Template 1부 - Retryable</title><link>https://taetaetae.github.io/2020/03/22/better-rest-template-1-retryable/</link><pubDate>Sun, 22 Mar 2020 15:30:35 +0000</pubDate><guid>https://taetaetae.github.io/2020/03/22/better-rest-template-1-retryable/</guid><description>&lt;p>웹 어플리케이션을 만들면서 꼭 한번 쯤 만나게 되는 &amp;ldquo;RestTemplate&amp;rdquo;. 접근 가능한 외부 HTTP URL(보통 API)을 호출하는 방법중에 하나로 springframework 에서 제공해주는 모듈이다. 특히 큰 한덩어리로 관리되던 Monolithic Architecture 에서 요청을 하고(client) 응답을 주는(server) &lt;!--more -->즉, Endpoint가 작은 단위로 분리되는 Microservice Architecture 로 바뀌면서 각 서비스간 호출방식이 HTTP 일 경우 자주 사용되곤 하는 것 같다. (webClient 등 다른 여러 호출 방법들이 있다.)
만약, 요청을 하는 클라이언트 입장에서 응답을 주는 서버의 상태가 불안정 하다고 가정했을때, 어떤식으로 처리해야 할까? 예컨대, 요청 10번에 한번은 어떠한 이슈로 응답이 지연되거나 서버에러가 발생한다고 하면 클라이언트를 사용하는 사용자 입장에서는 간헐적인 오류응답에 답답함을 호소할 수도 있다. 그럼 잠시 눈을 감고 생각해보자.
가볍게 생각하면 아래처럼 아주 간단하게 &amp;ldquo;예외처리&amp;quot;를 이용할 수도 있다.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="k">try&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">	&lt;/span>&lt;span class="c1">// http call&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">catch&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">Exception&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">e&lt;/span>&lt;span class="p">){&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">	&lt;/span>&lt;span class="c1">// 서버에러가 아닌 약속된 에러응답을 리턴&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>하지만 이것도 정답이 아닐수 있는게, &amp;ldquo;간헐적인 오류&amp;quot;로 인해 사용자는 오류화면을 봐야하기 때문에 클라이언트에 대한 신뢰를 저버릴 수밖에 없다. 그럼 어떻게 해야할까? 여러가지 해결방법이 있겠지만 간단하면서도 강력하다고 생각되는 방법이 바로 &amp;ldquo;재시도&amp;rdquo; 라고 생각한다. 클라이언트를 사용하는 사용자가 눈치 못챌만큼 빠르게 재시도를 한다면 에러가 나도 다시한번 호출해서 성공할 수 있는 가능성이 높기 때문이다. (그치만 근본적인 원인은 해결해야&amp;hellip;)&lt;/p>
&lt;figure>&lt;a class="lightgallery" href="https://taetaetae.github.io/images/better-rest-template-1-retryable/retry.png" title="/images/better-rest-template-1-retryable/retry.png" data-thumbnail="/images/better-rest-template-1-retryable/retry.png" data-sub-html="&lt;h2>실제로 조금있다 해보면 되는 경우가 많으니 안될때는 조금 (천천히) 시도해보자. 출처 : http://www.segye.com/newsView/20200302504384&lt;/h2>">
 &lt;img
 class="lazyload"
 src="https://taetaetae.github.io/svg/loading.min.svg"
 data-src="https://taetaetae.github.io/images/better-rest-template-1-retryable/retry.png"
 data-srcset="https://taetaetae.github.io/images/better-rest-template-1-retryable/retry.png, https://taetaetae.github.io/images/better-rest-template-1-retryable/retry.png 1.5x, https://taetaetae.github.io/images/better-rest-template-1-retryable/retry.png 2x"
 data-sizes="auto"
 alt="/images/better-rest-template-1-retryable/retry.png" width="40%" />
 &lt;/a>&lt;figcaption class="image-caption">실제로 조금있다 해보면 되는 경우가 많으니 안될때는 조금 (천천히) 시도해보자. &lt;br>출처 : &lt;a href="http://www.segye.com/newsView/20200302504384" target="_blank" rel="noopener noreffer ">http://www.segye.com/newsView/20200302504384&lt;/a>&lt;/figcaption>
 &lt;/figure>
&lt;p>이번 포스팅에서는 RestTemplate 를 이용할때 &amp;ldquo;재시도&amp;rdquo; 할 수 있는 방법에 대해 알아보고자 한다. 아주 간단할지 모르지만 노력에 비해 효과가 상당하다고 생각하기 때문에 정리해 두고 싶었다.&lt;/p>
&lt;h2 id="spring-retry">Spring Retry&lt;/h2>
&lt;p>&lt;a href="https://github.com/spring-projects/spring-retry" target="_blank" rel="noopener noreffer ">공식 Github&lt;/a>에 소개를 빌리자면, Spring 어플리케이션에 대한 재시도 지원을 제공한다고 한다. 위에서 이야기 했던 &amp;ldquo;RestTemplate&amp;quot;과는 사실 무관하고, 이를 활용해서 재시도 하는 &amp;ldquo;RetryRestTemplate&amp;quot;를 구현해보려 하는것이다. 우선 이 &amp;ldquo;Spring-Retry&amp;quot;의 예제를 보면 아주 심플하게 사용할 수 있다. 우선 pom에 구현에 필요한 dependency 를 추가하고 아래 코드를 보자.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-xml" data-lang="xml">&lt;span class="line">&lt;span class="cl">&lt;span class="nt">&amp;lt;dependency&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">	&lt;span class="nt">&amp;lt;groupId&amp;gt;&lt;/span>org.springframework.retry&lt;span class="nt">&amp;lt;/groupId&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">	&lt;span class="nt">&amp;lt;artifactId&amp;gt;&lt;/span>spring-retry&lt;span class="nt">&amp;lt;/artifactId&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nt">&amp;lt;/dependency&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nt">&amp;lt;dependency&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">	&lt;span class="nt">&amp;lt;groupId&amp;gt;&lt;/span>org.springframework.boot&lt;span class="nt">&amp;lt;/groupId&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">	&lt;span class="nt">&amp;lt;artifactId&amp;gt;&lt;/span>spring-boot-starter-aop&lt;span class="nt">&amp;lt;/artifactId&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nt">&amp;lt;/dependency&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="nd">@Configuration&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nd">@EnableRetry&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c1">// 1&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="kd">public&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">class&lt;/span> &lt;span class="nc">Application&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nd">@Bean&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">public&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Service&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nf">service&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">return&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">new&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Service&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nd">@Service&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="kd">class&lt;/span> &lt;span class="nc">Service&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nd">@Retryable&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">RemoteAccessException&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">class&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c1">// 2&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">public&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kt">void&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nf">service&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">// ... do something&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nd">@Recover&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c1">// 3&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">public&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kt">void&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nf">recover&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">RemoteAccessException&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">e&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">// ... panic&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ol>
&lt;li>@EnableRetry 어노테이션을 @Configuration을 지정한 클래스 중 하나에 추가한다.&lt;/li>
&lt;li>재시도 하려는 메소드에 @Retryable 어노테이션을 지정해준다.&lt;/li>
&lt;li>재시도가 완료되는 시점에서 실행하고 싶을때 선언하는 어노테이션, @Retryable 동일한 클래스에서 선언되어야 하고 return type 은 @Retryable을 지정한 메소드와 동일해야 한다.&lt;/li>
&lt;/ol>
&lt;h2 id="retry-rest-template">Retry Rest Template&lt;/h2>
&lt;p>이렇게 springframework 에서 제공해주는 spring-retry 를 이용해서 이번 포스팅의 목표인 재시도를 하는 Retry Rest Template 를 구성해보자. 우선, RestTemplate 를 Bean 으로 등록하고, 위에서 이야기 한 어노테이션들로 구성해보자.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="nd">@EnableRetry&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nd">@Configuration&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="kd">public&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">class&lt;/span> &lt;span class="nc">RetryableRestTemplateConfiguration&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">	&lt;/span>&lt;span class="nd">@Bean&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">	&lt;/span>&lt;span class="kd">public&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">RestTemplate&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nf">retryableRestTemplate&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">		&lt;/span>&lt;span class="n">SimpleClientHttpRequestFactory&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">clientHttpRequestFactory&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">new&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">SimpleClientHttpRequestFactory&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c1">// 1&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">		&lt;/span>&lt;span class="n">clientHttpRequestFactory&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">setReadTimeout&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">2000&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">		&lt;/span>&lt;span class="n">clientHttpRequestFactory&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">setConnectTimeout&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">500&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">		&lt;/span>&lt;span class="n">RestTemplate&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">restTemplate&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">new&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">RestTemplate&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">clientHttpRequestFactory&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">			&lt;/span>&lt;span class="nd">@Override&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">			&lt;/span>&lt;span class="nd">@Retryable&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">value&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">RestClientException&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">class&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">maxAttempts&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">3&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">backoff&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nd">@Backoff&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">delay&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">1000&lt;/span>&lt;span class="p">))&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c1">// 2&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">			&lt;/span>&lt;span class="kd">public&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">T&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">ResponseEntity&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">T&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nf">exchange&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">URI&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">url&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">HttpMethod&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">method&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">HttpEntity&lt;/span>&lt;span class="o">&amp;lt;?&amp;gt;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">requestEntity&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Class&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">T&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">responseType&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">				&lt;/span>&lt;span class="kd">throws&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">RestClientException&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">				&lt;/span>&lt;span class="k">return&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">super&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">exchange&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">url&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">method&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">requestEntity&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">responseType&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w"> 
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">			&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">			&lt;/span>&lt;span class="nd">@Recover&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">			&lt;/span>&lt;span class="kd">public&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">T&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">ResponseEntity&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">String&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nf">exchangeRecover&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">RestClientException&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">e&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">				&lt;/span>&lt;span class="k">return&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">ResponseEntity&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">badRequest&lt;/span>&lt;span class="p">().&lt;/span>&lt;span class="na">body&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;bad request T.T&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c1">// 3&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">			&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">		&lt;/span>&lt;span class="p">};&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">		&lt;/span>&lt;span class="k">return&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">restTemplate&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">	&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ol>
&lt;li>SimpleClientHttpRequestFactory 를 만들고 각 타임아웃을 설정해준 다음 RestTemplate 파라미터로 넘겨준다.&lt;/li>
&lt;li>사용하는 곳에서 exchange 메소드를 이용할 것이므로 해당 메소드를 오버라이드 해준다. 먼저 해당 메소드에서 &amp;ldquo;RestClientException&amp;quot;이 발생할 경우 Retry 로직을 수행한다고 정해주고, 최대 시도는 3번, backoff 설정중 delay를 1000ms(1초)로 지정해서 재시도가 진행되도록 해준다.&lt;/li>
&lt;li>2 에서 지정한 재시도가 끝나면 (재시도를 전부 다 하면) 해당 메소드를 수행하게 되어있고, 임의로 응답에 지정한 문구를 넘겨준다.&lt;/li>
&lt;/ol>
&lt;p>이렇게 하고 실제로 사용하는 로직에서 일부러 잘못된 URL을 호출해 보도록 하자. 그리고서 로그를 자세히 보도록 application.properties 에 &amp;ldquo;debug=true&amp;rdquo; 설정을 해준다.&lt;/p></description></item><item><title>SpringRestDocs를 SpringBoot에 적용하기</title><link>https://taetaetae.github.io/2020/03/08/spring-rest-docs-in-spring-boot/</link><pubDate>Sun, 08 Mar 2020 23:16:59 +0000</pubDate><guid>https://taetaetae.github.io/2020/03/08/spring-rest-docs-in-spring-boot/</guid><description>&lt;p>API를 개발하고 제공하기 위해서는 그에 해당하는 API 명세를 작성해서 사용하는 곳에 전달하게 된다. 어떤 URL에 어떤 파라미터를 사용해서 어떻게 요청을 하면 어떤 결과를 응답으로 내려주는지에 대한 관련 정보들. 이러한 &amp;ldquo;API 문서&amp;rdquo; 를 제공하는 방식은 상황에 따라 다양한 방법으로 사용되곤 한다. &lt;!--more -->&lt;/p>
&lt;figure>&lt;a class="lightgallery" href="https://taetaetae.github.io/images/spring-rest-docs-in-spring-boot/api-document.jpg" title="/images/spring-rest-docs-in-spring-boot/api-document.jpg" data-thumbnail="/images/spring-rest-docs-in-spring-boot/api-document.jpg" data-sub-html="&lt;h2>API 코드와 해당 문서의 동기화가 자동으로 되어야 조금 편해질것 같다는 생각이 들었다. 출처 : https://dribbble.com/shots/3386291-API-Documentation&lt;/h2>">
 &lt;img
 class="lazyload"
 src="https://taetaetae.github.io/svg/loading.min.svg"
 data-src="https://taetaetae.github.io/images/spring-rest-docs-in-spring-boot/api-document.jpg"
 data-srcset="https://taetaetae.github.io/images/spring-rest-docs-in-spring-boot/api-document.jpg, https://taetaetae.github.io/images/spring-rest-docs-in-spring-boot/api-document.jpg 1.5x, https://taetaetae.github.io/images/spring-rest-docs-in-spring-boot/api-document.jpg 2x"
 data-sizes="auto"
 alt="/images/spring-rest-docs-in-spring-boot/api-document.jpg" width="50%" />
 &lt;/a>&lt;figcaption class="image-caption">API 코드와 해당 문서의 동기화가 자동으로 되어야 조금 편해질것 같다는 생각이 들었다. &lt;br>출처 : &lt;a href="https://dribbble.com/shots/3386291-API-Documentation" target="_blank" rel="noopener noreffer ">https://dribbble.com/shots/3386291-API-Documentation&lt;/a>&lt;/figcaption>
 &lt;/figure>
&lt;p>필자는 주로 &amp;ldquo;위키&amp;rdquo;(또는 일반 문서)를 활용해서 전달하곤 했었는데 API의 형태가 달라질 때마다 해당 위키를 수정해야만 하는 번거로움이 있었다. API 수정하면 위키도 수정하고. 깜박하고 위키 수정을 안하게 될 경우 왜 API 명세가 다르냐는 문의가&amp;hellip; 그러다 알게된 Spring Rest Docs. (아무리 좋은 기술, 좋은 툴 이라 해도 실제로 본인이 필요로 하고 사용을 해야하는 이유가 생길때 비로소 빛을 발하는것 같은 느낌이다.)&lt;/p>
&lt;blockquote>
&lt;p>이 포스팅에서는 swegger 와 비교하는 내용은 제외할까 한다. 워낙 유명한 두 양대 산맥(?)이라 검색해보면 각각의 장단점이 자세히 나와있기에&amp;hellip;&lt;/p>&lt;/blockquote>
&lt;p>최근 들어 TestCode 의 중요성을 절실하게 느끼고 있었고, TestCode 를 작성하면 자연스럽게 문서를 만들어 주는 부분이 가장 매력적이라고 생각이 들었다. 이를 반대로 생각하면, TestCode 가 실패할 경우 빌드 자체가 안되기에 어쩔수 없이 TestCode를 성공시켜야만 하고, 자연스럽게 정상적인(최신화 된) API 문서가 만들어지게 된다.&lt;/p>
&lt;p>이번 포스팅에서는 다음과 같은 목표를 두고 실무에서 언제든지 활용이 가능한 약간의 &amp;ldquo;가이드&amp;rdquo; 같은 내용으로 작성해 보고자 한다.&lt;/p>
&lt;ul>
&lt;li>Spring Boot 최신 버전에서 Spring Rest Docs 를 설정한다.&lt;/li>
&lt;li>임의의 API 를 만들고 그에 따른 TestCase 를 작성한다.&lt;/li>
&lt;li>Spring.profile 에 따라 Spring Rest Docs Url 을 접근 가능/불가능 할 수 있게 한다.&lt;/li>
&lt;/ul>
&lt;p>물론 필자의 방법이 다를수도 있지만, 이러한 방법을 토대로 보다 더 우아하고 아름다운 방법을 알아갈수 있지 않을까 하는 기대로.&lt;/p>
&lt;h2 id="spring-boot-에-spring-rest-docs-셋팅하고-testcase-작성하기">Spring Boot 에 Spring Rest Docs 셋팅하고 TestCase 작성하기&lt;/h2>
&lt;p>우선 Spring Boot 프로젝트를 만든다. &lt;a href="https://start.spring.io/" target="_blank" rel="noopener noreffer ">https://start.spring.io/&lt;/a> 에서 만들어도 되고 IDE 에서 제공하는 툴로 만들어도 되고. 만드는 방식은 무방하다. 그 다음 필요한 dependency 를 추가해 준다.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-xml" data-lang="xml">&lt;span class="line">&lt;span class="cl">&lt;span class="nt">&amp;lt;dependency&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">	&lt;span class="nt">&amp;lt;groupId&amp;gt;&lt;/span>org.springframework.restdocs&lt;span class="nt">&amp;lt;/groupId&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">	&lt;span class="nt">&amp;lt;artifactId&amp;gt;&lt;/span>spring-restdocs-mockmvc&lt;span class="nt">&amp;lt;/artifactId&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">	&lt;span class="nt">&amp;lt;scope&amp;gt;&lt;/span>test&lt;span class="nt">&amp;lt;/scope&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nt">&amp;lt;/dependency&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>임의로 API를 작성하고&lt;/p>
&lt;ul>
&lt;li>모델&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="nd">@Getter&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nd">@Setter&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="kd">public&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">class&lt;/span> &lt;span class="nc">Book&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">	&lt;/span>&lt;span class="kd">private&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Integer&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">id&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">	&lt;/span>&lt;span class="kd">private&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">String&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">title&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">	&lt;/span>&lt;span class="kd">private&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">String&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">author&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ul>
&lt;li>컨트롤러&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="nd">@RestController&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="kd">public&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">class&lt;/span> &lt;span class="nc">BookController&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">	&lt;/span>&lt;span class="nd">@GetMapping&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;/book/{id}&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">	&lt;/span>&lt;span class="kd">public&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Book&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nf">getABook&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nd">@PathVariable&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Integer&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">id&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">		&lt;/span>&lt;span class="n">Book&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">book&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">new&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Book&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">		&lt;/span>&lt;span class="n">book&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">setId&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">id&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">		&lt;/span>&lt;span class="n">book&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">setTitle&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;spring rest docs in spring boot&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">		&lt;/span>&lt;span class="n">book&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">setAuthor&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;taetaetae&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">		&lt;/span>&lt;span class="k">return&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">book&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">	&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>해당 컨트롤러에 대한 TestCase 를 작성하자.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="nd">@WebMvcTest&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">BookController&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">class&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nd">@AutoConfigureRestDocs&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c1">// (1)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="kd">public&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">class&lt;/span> &lt;span class="nc">BookControllerTest&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">	&lt;/span>&lt;span class="nd">@Autowired&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">	&lt;/span>&lt;span class="kd">private&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">MockMvc&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">mockMvc&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c1">// (2)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">	&lt;/span>&lt;span class="nd">@Test&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">	&lt;/span>&lt;span class="kd">public&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kt">void&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nf">test_책을_조회하면_null이_아닌_객체를_리턴한다&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">throws&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Exception&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">		&lt;/span>&lt;span class="n">mockMvc&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">perform&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">get&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;/book/{id}&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">1&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">			&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">accept&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">MediaType&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">APPLICATION_JSON&lt;/span>&lt;span class="p">))&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">			&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">andDo&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">MockMvcResultHandlers&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">print&lt;/span>&lt;span class="p">())&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">			&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">andExpect&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">MockMvcResultMatchers&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">status&lt;/span>&lt;span class="p">().&lt;/span>&lt;span class="na">isOk&lt;/span>&lt;span class="p">())&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">			&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">andDo&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">document&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;book&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c1">// (3)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">				&lt;/span>&lt;span class="n">pathParameters&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">					&lt;/span>&lt;span class="n">parameterWithName&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;id&amp;#34;&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="na">description&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;book unique id&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c1">// (4)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">				&lt;/span>&lt;span class="p">),&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">				&lt;/span>&lt;span class="n">responseFields&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">					&lt;/span>&lt;span class="n">fieldWithPath&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;id&amp;#34;&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="na">description&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;book unique id&amp;#34;&lt;/span>&lt;span class="p">),&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">					&lt;/span>&lt;span class="n">fieldWithPath&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;title&amp;#34;&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="na">description&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;title&amp;#34;&lt;/span>&lt;span class="p">),&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">					&lt;/span>&lt;span class="n">fieldWithPath&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;author&amp;#34;&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="na">description&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;author&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">				&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">			&lt;/span>&lt;span class="p">))&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">			&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">andExpect&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">jsonPath&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;$.id&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">is&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">notNullValue&lt;/span>&lt;span class="p">())))&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c1">// (5)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">			&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">andExpect&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">jsonPath&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;$.title&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">is&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">notNullValue&lt;/span>&lt;span class="p">())))&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">			&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">andExpect&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">jsonPath&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;$.author&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">is&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">notNullValue&lt;/span>&lt;span class="p">())));&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">	&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>(1) Spring Boot 에서는 해당 어노테이션으로 여러줄에 걸쳐 설정해야 할 Spring Rest Docs 관련 설정을 아주 간단하게 해결할 수 있게 된다. (&lt;a href="https://docs.spring.io/spring-boot/docs/current-SNAPSHOT/reference/htmlsingle/#boot-features-testing-spring-boot-applications-testing-autoconfigured-rest-docs" target="_blank" rel="noopener noreffer ">참고&lt;/a>)&lt;/p>
&lt;p>(2) &lt;a href="https://docs.spring.io/spring-restdocs/docs/2.0.4.RELEASE/reference/html5/#getting-started-sample-applications" target="_blank" rel="noopener noreffer ">공식 도큐먼트&lt;/a> 에서는 4가지 방식을 말하고 있는데 이 포스팅 에서는 &amp;ldquo;MockMvc&amp;rdquo; 을 사용하고자 한다.&lt;/p>
&lt;p>(3) &amp;ldquo;book&amp;rdquo; 이라는 identifier 를 지정하면 해당 TestCase 가 수행될때 snippets 가 생성되는데 해당 identifier 묶음으로 생성이 된다.&lt;/p></description></item><item><title>스프링 부트로 멀티모듈 셋팅하기</title><link>https://taetaetae.github.io/2020/01/19/spring-boot-maven-multi-module/</link><pubDate>Sun, 19 Jan 2020 10:29:03 +0000</pubDate><guid>https://taetaetae.github.io/2020/01/19/spring-boot-maven-multi-module/</guid><description>&lt;p>서비스를 처음 만들기 시작할때면 각 직군별로 생각하는 포인트가 다양하다. 설계, 기획, 디자인, 개발. 여기서 개발은 프로젝트 셋팅을 어떻게 해야하지? 하는 고민을 하기 마련이다. 아주 간단하게 하나의 모듈로 모든 기능을 담당하도록 만들 수 있지만 기능별로 모듈을 나눠서 셋팅하는게 관리측면에서 장점이라 생각한다.&lt;!--more -->&lt;/p>
&lt;p>예를 들어보자. 도서관의 들어온 책 정보를 외부에 제공하는 &amp;ldquo;API&amp;rdquo;, 주기적으로 책 정보를 업데이트 하는 &amp;ldquo;Batch&amp;rdquo;. 이렇게 크게 두가지의 모듈이 있어야 한다고 가정했을때 어떤식으로 모듈을 설계할 수 있을까?&lt;/p>
&lt;p>이번 포스팅에서는 스프링 부트와 메이븐을 활용해서 하나의 프로젝트(컴포넌트)에서 여러 모듈을 관리할 수 있는 &lt;a href="https://spring.io/guides/gs/multi-module/" target="_blank" rel="noopener noreffer ">Spring Multi Module&lt;/a>을 셋팅하는 방법에 대해 알아보고자 한다. 필자도 셋팅하기 전에는 &amp;ldquo;그냥 하면 되는거 아니야?&amp;ldquo;라며 우습게 보다 아주 사소한 부분들에서 엄청난 삽질을 해서 그런지 꼭 포스팅으로 남겨놔야 겠다고 다짐했고 이렇게 정리를 할 수 있게 되어서 다행이라 생각한다.&lt;/p>
&lt;figure>&lt;a class="lightgallery" href="https://taetaetae.github.io/images/spring-boot-maven-multi-module/team_structure.png" title="/images/spring-boot-maven-multi-module/team_structure.png" data-thumbnail="/images/spring-boot-maven-multi-module/team_structure.png" data-sub-html="&lt;h2>어쩌면 우리가 있는 팀도 멀티모듈이 아닐까? 출처 : https://bcho.tistory.com/813&lt;/h2>">
 &lt;img
 class="lazyload"
 src="https://taetaetae.github.io/svg/loading.min.svg"
 data-src="https://taetaetae.github.io/images/spring-boot-maven-multi-module/team_structure.png"
 data-srcset="https://taetaetae.github.io/images/spring-boot-maven-multi-module/team_structure.png, https://taetaetae.github.io/images/spring-boot-maven-multi-module/team_structure.png 1.5x, https://taetaetae.github.io/images/spring-boot-maven-multi-module/team_structure.png 2x"
 data-sizes="auto"
 alt="/images/spring-boot-maven-multi-module/team_structure.png" width="80%" />
 &lt;/a>&lt;figcaption class="image-caption">어쩌면 우리가 있는 팀도 멀티모듈이 아닐까? &lt;br>출처 : &lt;a href="https://bcho.tistory.com/813" target="_blank" rel="noopener noreffer ">https://bcho.tistory.com/813&lt;/a>&lt;/figcaption>
 &lt;/figure>
&lt;h2 id="왜-멀티모듈로-셋팅할까">왜 멀티모듈로 셋팅할까?&lt;/h2>
&lt;p>위에서 예시로 이야기 한것처럼 현재 우리가 셋팅해야할 모듈은 크게 두가지 이다.&lt;/p>
&lt;ul>
&lt;li>API : 외부에 도서관에 들어온 책 정보를 알려주는 모듈&lt;/li>
&lt;li>Batch : 주기적으로 도서관의 책 정보를 갱신하는 모듈&lt;/li>
&lt;/ul>
&lt;p>한번 생각을 해보자. 위에서 말한 모듈들 중에 동시에 사용할것만 같은 정보가 있다. &amp;ldquo;책 정보&amp;rdquo;. 각 모듈마다 &amp;ldquo;책 정보&amp;quot;를 가져오는 로직을 작성하는것 보다 한곳에서 해당로직을 구현하고 이를 여러곳에서 사용하는게 사용하는게 중복코드를 방지할수 있는 방법이란건 쉽게 알아차릴수 있다. 그렇다면 어떻게 모듈을 분리할수 있을까?&lt;/p>
&lt;p>필자의 경험으로 미루어 볼때 크게 두가지 방법이 있는것 같다.&lt;/p>
&lt;ul>
&lt;li>공통으로 사용하는 모듈을 jar로 만들고 이를 메이븐 원격 저장소에 deploy, 사용하는 모듈에서 디펜던시에 추가하여 사용&lt;/li>
&lt;li>멀티모듈로 구성하고 사용하는 모듈에서 디펜던시에 추가하여 사용&lt;/li>
&lt;/ul>
&lt;p>첫번째 방법의 가장 큰 단점은, 공통으로 사용하는 모듈이 변경될때마다 버전을 바꿔주고 (안바꿔도 되지만 사용하는 모듈에서 캐시 갱신을 해야하는 불편함이 생긴다.) 메이븐 원격 저장소에 deploy를 해줘야 한다. 그에 반해 두번째 방법은 이런과정없이 함께 빌드만 해주면 끝나고 IDE에서 개발시 한 모듈에서 동시에 수정과 사용이 가능하기 때문에 훨씬 편리하다.&lt;/p>
&lt;p>&lt;a href="https://johngrib.github.io/wiki/No-Silver-Bullet/" target="_blank" rel="noopener noreffer ">은총알은 없다&lt;/a> 라는 말처럼, 정답은 없다. 하지만 이런저런 방법들을 미리 알아두면 적시적소에 사용할 수 있는. 필자가 다른글들에서도 언급을 자주하던 &amp;ldquo;나만의 무기&amp;quot;가 되지 않을까?&lt;/p>
&lt;h2 id="멀티모듈-셋팅하기">멀티모듈 셋팅하기&lt;/h2>
&lt;p>위에서 이야기 했던 &amp;ldquo;API&amp;rdquo;, &amp;ldquo;Batch&amp;quot;와는 별도로 공통으로 사용하는 모듈인 &amp;ldquo;Core&amp;rdquo; 이렇게 총 3개의 모듈을 만들예정이다.&lt;/p>
&lt;blockquote>
&lt;p>다른 이야기지만, 공통으로 사용할 것 &amp;ldquo;같아서&amp;rdquo; 미리 공통로직을 작성하는 습관은 좋지 않는것 같다. 그러다보면 쓸데없이 공통로직이 무거워지므로 실제로 사용하면서 중복코드가 발생할때 그때 공통로직으로 리펙토링 해도 늦지 않는것 같다. (꼰데인가&amp;hellip;)&lt;/p>&lt;/blockquote>
&lt;p>구현하는 환경은 다음과 같다.&lt;/p>
&lt;ul>
&lt;li>Spring Boot 2.2.3&lt;/li>
&lt;li>Maven&lt;/li>
&lt;li>IntelliJ&lt;/li>
&lt;/ul>
&lt;p>우선 IDE의 힘을 빌려 하나의 스프링 부트 프로젝트를 생성해본다.&lt;/p>
&lt;figure>&lt;a class="lightgallery" href="https://taetaetae.github.io/images/spring-boot-maven-multi-module/spring_boot_init.jpg" title="/images/spring-boot-maven-multi-module/spring_boot_init.jpg" data-thumbnail="/images/spring-boot-maven-multi-module/spring_boot_init.jpg" data-sub-html="&lt;h2>다음 &amp;gt; 다음 &amp;gt; 다음&lt;/h2>">
 &lt;img
 class="lazyload"
 src="https://taetaetae.github.io/svg/loading.min.svg"
 data-src="https://taetaetae.github.io/images/spring-boot-maven-multi-module/spring_boot_init.jpg"
 data-srcset="https://taetaetae.github.io/images/spring-boot-maven-multi-module/spring_boot_init.jpg, https://taetaetae.github.io/images/spring-boot-maven-multi-module/spring_boot_init.jpg 1.5x, https://taetaetae.github.io/images/spring-boot-maven-multi-module/spring_boot_init.jpg 2x"
 data-sizes="auto"
 alt="/images/spring-boot-maven-multi-module/spring_boot_init.jpg" width="80%" />
 &lt;/a>&lt;figcaption class="image-caption">다음 &amp;gt; 다음 &amp;gt; 다음&lt;/figcaption>
 &lt;/figure>
&lt;p>그 다음 만든 프로젝트에서 우클릭 후 새로운 모듈을 선택. Maven 모듈을 선택하고 적당한 이름을 적어준다.
&lt;figure>&lt;a class="lightgallery" href="https://taetaetae.github.io/images/spring-boot-maven-multi-module/new_module.jpg" title="/images/spring-boot-maven-multi-module/new_module.jpg" data-thumbnail="/images/spring-boot-maven-multi-module/new_module.jpg" data-sub-html="&lt;h2>다음 &amp;gt; 다음 &amp;gt; 다음 222&lt;/h2>">
 &lt;img
 class="lazyload"
 src="https://taetaetae.github.io/svg/loading.min.svg"
 data-src="https://taetaetae.github.io/images/spring-boot-maven-multi-module/new_module.jpg"
 data-srcset="https://taetaetae.github.io/images/spring-boot-maven-multi-module/new_module.jpg, https://taetaetae.github.io/images/spring-boot-maven-multi-module/new_module.jpg 1.5x, https://taetaetae.github.io/images/spring-boot-maven-multi-module/new_module.jpg 2x"
 data-sizes="auto"
 alt="/images/spring-boot-maven-multi-module/new_module.jpg" width="80%" />
 &lt;/a>&lt;figcaption class="image-caption">다음 &amp;gt; 다음 &amp;gt; 다음 222&lt;/figcaption>
 &lt;/figure>&lt;/p>
&lt;p>&amp;ldquo;API&amp;rdquo;, &amp;ldquo;Batch&amp;rdquo;, &amp;ldquo;Core&amp;rdquo; 라는 모듈을 추가하고 실제 모듈이 되는 &amp;ldquo;API&amp;rdquo;, &amp;ldquo;Batch&amp;quot;에 &lt;code>parent&lt;/code> 와 &lt;code>dependencies&lt;/code> 을 설정해주자. 그렇게 하고 각 Pom.xml을 보면 아래와 같다. (&amp;ldquo;API&amp;rdquo; 모듈에 대해서만 집중적으로 이야기 하려 한다. &amp;ldquo;Batch&amp;rdquo; 모듈도 동일한 형식으로 작성하기 때문.)&lt;/p>
&lt;ul>
&lt;li>최 상위 Pom.xml (library)
modules 하위에 멀티모듈로 설정한 모듈들의 이름이 들어가 있는것을 확인할 수 있다.&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-xml" data-lang="xml">&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&amp;lt;?xml version=&amp;#34;1.0&amp;#34; encoding=&amp;#34;UTF-8&amp;#34;?&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nt">&amp;lt;project&lt;/span> &lt;span class="na">xmlns=&lt;/span>&lt;span class="s">&amp;#34;http://maven.apache.org/POM/4.0.0&amp;#34;&lt;/span> &lt;span class="na">xmlns:xsi=&lt;/span>&lt;span class="s">&amp;#34;http://www.w3.org/2001/XMLSchema-instance&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">		 &lt;span class="na">xsi:schemaLocation=&lt;/span>&lt;span class="s">&amp;#34;http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd&amp;#34;&lt;/span>&lt;span class="nt">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">	&lt;span class="nt">&amp;lt;modelVersion&amp;gt;&lt;/span>4.0.0&lt;span class="nt">&amp;lt;/modelVersion&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">	&lt;span class="nt">&amp;lt;packaging&amp;gt;&lt;/span>pom&lt;span class="nt">&amp;lt;/packaging&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">	&lt;span class="nt">&amp;lt;modules&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">		&lt;span class="nt">&amp;lt;module&amp;gt;&lt;/span>api&lt;span class="nt">&amp;lt;/module&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">		&lt;span class="nt">&amp;lt;module&amp;gt;&lt;/span>core&lt;span class="nt">&amp;lt;/module&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">		&lt;span class="nt">&amp;lt;module&amp;gt;&lt;/span>batch&lt;span class="nt">&amp;lt;/module&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">	&lt;span class="nt">&amp;lt;/modules&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">	
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">	&lt;span class="nt">&amp;lt;parent&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">		&lt;span class="nt">&amp;lt;groupId&amp;gt;&lt;/span>org.springframework.boot&lt;span class="nt">&amp;lt;/groupId&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">		&lt;span class="nt">&amp;lt;artifactId&amp;gt;&lt;/span>spring-boot-starter-parent&lt;span class="nt">&amp;lt;/artifactId&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">		&lt;span class="nt">&amp;lt;version&amp;gt;&lt;/span>2.2.3.RELEASE&lt;span class="nt">&amp;lt;/version&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">		&lt;span class="nt">&amp;lt;relativePath/&amp;gt;&lt;/span> &lt;span class="c">&amp;lt;!-- lookup parent from repository --&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">	&lt;span class="nt">&amp;lt;/parent&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">	&lt;span class="nt">&amp;lt;groupId&amp;gt;&lt;/span>com.taetaetae&lt;span class="nt">&amp;lt;/groupId&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">	&lt;span class="nt">&amp;lt;artifactId&amp;gt;&lt;/span>library&lt;span class="nt">&amp;lt;/artifactId&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">	&lt;span class="nt">&amp;lt;version&amp;gt;&lt;/span>0.0.1-SNAPSHOT&lt;span class="nt">&amp;lt;/version&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">	&lt;span class="nt">&amp;lt;name&amp;gt;&lt;/span>library&lt;span class="nt">&amp;lt;/name&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">	&lt;span class="nt">&amp;lt;description&amp;gt;&lt;/span>Demo project for Spring Boot&lt;span class="nt">&amp;lt;/description&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">	&lt;span class="nt">&amp;lt;properties&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">		&lt;span class="nt">&amp;lt;java.version&amp;gt;&lt;/span>1.8&lt;span class="nt">&amp;lt;/java.version&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">	&lt;span class="nt">&amp;lt;/properties&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">	&lt;span class="nt">&amp;lt;dependencies&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">		&lt;span class="nt">&amp;lt;dependency&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">			&lt;span class="nt">&amp;lt;groupId&amp;gt;&lt;/span>org.springframework.boot&lt;span class="nt">&amp;lt;/groupId&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">			&lt;span class="nt">&amp;lt;artifactId&amp;gt;&lt;/span>spring-boot-starter-web&lt;span class="nt">&amp;lt;/artifactId&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">		&lt;span class="nt">&amp;lt;/dependency&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">		&lt;span class="nt">&amp;lt;dependency&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">			&lt;span class="nt">&amp;lt;groupId&amp;gt;&lt;/span>org.springframework.boot&lt;span class="nt">&amp;lt;/groupId&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">			&lt;span class="nt">&amp;lt;artifactId&amp;gt;&lt;/span>spring-boot-starter-test&lt;span class="nt">&amp;lt;/artifactId&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">			&lt;span class="nt">&amp;lt;scope&amp;gt;&lt;/span>test&lt;span class="nt">&amp;lt;/scope&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">			&lt;span class="nt">&amp;lt;exclusions&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">				&lt;span class="nt">&amp;lt;exclusion&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">					&lt;span class="nt">&amp;lt;groupId&amp;gt;&lt;/span>org.junit.vintage&lt;span class="nt">&amp;lt;/groupId&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">					&lt;span class="nt">&amp;lt;artifactId&amp;gt;&lt;/span>junit-vintage-engine&lt;span class="nt">&amp;lt;/artifactId&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">				&lt;span class="nt">&amp;lt;/exclusion&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">			&lt;span class="nt">&amp;lt;/exclusions&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">		&lt;span class="nt">&amp;lt;/dependency&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">	&lt;span class="nt">&amp;lt;/dependencies&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nt">&amp;lt;/project&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ul>
&lt;li>API Pom.xml
parent 부분이 설정되어 있는 모습을 볼수 있고, core 모듈을 사용하기 위해 dependency 에 추가를 해준다.&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-xml" data-lang="xml">&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&amp;lt;?xml version=&amp;#34;1.0&amp;#34; encoding=&amp;#34;UTF-8&amp;#34;?&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nt">&amp;lt;project&lt;/span> &lt;span class="na">xmlns=&lt;/span>&lt;span class="s">&amp;#34;http://maven.apache.org/POM/4.0.0&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">		 &lt;span class="na">xmlns:xsi=&lt;/span>&lt;span class="s">&amp;#34;http://www.w3.org/2001/XMLSchema-instance&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">		 &lt;span class="na">xsi:schemaLocation=&lt;/span>&lt;span class="s">&amp;#34;http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd&amp;#34;&lt;/span>&lt;span class="nt">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">	&lt;span class="nt">&amp;lt;parent&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">		&lt;span class="nt">&amp;lt;artifactId&amp;gt;&lt;/span>library&lt;span class="nt">&amp;lt;/artifactId&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">		&lt;span class="nt">&amp;lt;groupId&amp;gt;&lt;/span>com.taetaetae&lt;span class="nt">&amp;lt;/groupId&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">		&lt;span class="nt">&amp;lt;version&amp;gt;&lt;/span>0.0.1-SNAPSHOT&lt;span class="nt">&amp;lt;/version&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">	&lt;span class="nt">&amp;lt;/parent&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">	&lt;span class="nt">&amp;lt;modelVersion&amp;gt;&lt;/span>4.0.0&lt;span class="nt">&amp;lt;/modelVersion&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">	&lt;span class="nt">&amp;lt;packaging&amp;gt;&lt;/span>jar&lt;span class="nt">&amp;lt;/packaging&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">	&lt;span class="nt">&amp;lt;artifactId&amp;gt;&lt;/span>api&lt;span class="nt">&amp;lt;/artifactId&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">	&lt;span class="nt">&amp;lt;dependencies&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">		&lt;span class="nt">&amp;lt;dependency&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">			&lt;span class="nt">&amp;lt;groupId&amp;gt;&lt;/span>com.taetaetae&lt;span class="nt">&amp;lt;/groupId&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">			&lt;span class="nt">&amp;lt;artifactId&amp;gt;&lt;/span>core&lt;span class="nt">&amp;lt;/artifactId&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">			&lt;span class="nt">&amp;lt;version&amp;gt;&lt;/span>0.0.1-SNAPSHOT&lt;span class="nt">&amp;lt;/version&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">		&lt;span class="nt">&amp;lt;/dependency&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">	&lt;span class="nt">&amp;lt;/dependencies&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">	&lt;span class="nt">&amp;lt;build&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">		&lt;span class="nt">&amp;lt;finalName&amp;gt;&lt;/span>library-api&lt;span class="nt">&amp;lt;/finalName&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">		&lt;span class="nt">&amp;lt;plugins&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">			&lt;span class="nt">&amp;lt;plugin&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">				&lt;span class="nt">&amp;lt;groupId&amp;gt;&lt;/span>org.springframework.boot&lt;span class="nt">&amp;lt;/groupId&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">				&lt;span class="nt">&amp;lt;artifactId&amp;gt;&lt;/span>spring-boot-maven-plugin&lt;span class="nt">&amp;lt;/artifactId&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">			&lt;span class="nt">&amp;lt;/plugin&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">		&lt;span class="nt">&amp;lt;/plugins&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">	&lt;span class="nt">&amp;lt;/build&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nt">&amp;lt;/project&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ul>
&lt;li>Core Pom.xml
Core 모듈은 &amp;ldquo;jar&amp;quot;로 패키징 되어 다른 곳에서 사용되어야 하기 때문에 packaging 만 설정해준다.&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-xml" data-lang="xml">&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&amp;lt;?xml version=&amp;#34;1.0&amp;#34; encoding=&amp;#34;UTF-8&amp;#34;?&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nt">&amp;lt;project&lt;/span> &lt;span class="na">xmlns=&lt;/span>&lt;span class="s">&amp;#34;http://maven.apache.org/POM/4.0.0&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">		 &lt;span class="na">xmlns:xsi=&lt;/span>&lt;span class="s">&amp;#34;http://www.w3.org/2001/XMLSchema-instance&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">		 &lt;span class="na">xsi:schemaLocation=&lt;/span>&lt;span class="s">&amp;#34;http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd&amp;#34;&lt;/span>&lt;span class="nt">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">	&lt;span class="nt">&amp;lt;parent&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">		&lt;span class="nt">&amp;lt;artifactId&amp;gt;&lt;/span>library&lt;span class="nt">&amp;lt;/artifactId&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">		&lt;span class="nt">&amp;lt;groupId&amp;gt;&lt;/span>com.taetaetae&lt;span class="nt">&amp;lt;/groupId&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">		&lt;span class="nt">&amp;lt;version&amp;gt;&lt;/span>0.0.1-SNAPSHOT&lt;span class="nt">&amp;lt;/version&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">	&lt;span class="nt">&amp;lt;/parent&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">	&lt;span class="nt">&amp;lt;modelVersion&amp;gt;&lt;/span>4.0.0&lt;span class="nt">&amp;lt;/modelVersion&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">	&lt;span class="nt">&amp;lt;packaging&amp;gt;&lt;/span>jar&lt;span class="nt">&amp;lt;/packaging&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">	&lt;span class="nt">&amp;lt;artifactId&amp;gt;&lt;/span>core&lt;span class="nt">&amp;lt;/artifactId&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">	&lt;span class="nt">&amp;lt;version&amp;gt;&lt;/span>0.0.1-SNAPSHOT&lt;span class="nt">&amp;lt;/version&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nt">&amp;lt;/project&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>이렇게 하고서 Core 모듈에 공통으로 사용될 로직을 작성하고, API 모듈에서 이를 사용하는 로직을 작성한뒤, 빌드를 해보면 에러 없이 정상 작동을 하는 모습을 볼 수 있다.&lt;/p></description></item><item><title>spring-boot에서 mybatis로 mysql 연동하기</title><link>https://taetaetae.github.io/2019/04/21/spring-boot-mybatis-mysql-xml/</link><pubDate>Sun, 21 Apr 2019 22:47:04 +0000</pubDate><guid>https://taetaetae.github.io/2019/04/21/spring-boot-mybatis-mysql-xml/</guid><description>&lt;p>실무에서 개발을 하다보면 과거 누군가 잘 구성해 놓은 밥상(legacy)에 숟가락만 얹는 느낌으로 &lt;code>로직 구현&lt;/code>만 할때가 있다. 그러다보면 각종 레이어가 어떻게 구성(설정)되어있는지도 모르고 &lt;!-- more --> 간혹 설정에서 문제가 발생하면 &amp;ldquo;아 내가 이것도 모르고 이제까지 개발을 해왔나&amp;rdquo; 하는 자괴감이 들며 몇시간을 삽질하는 경우가 있다. 그게 지금의 필자인것 같다. (눙물&amp;hellip;)&lt;/p>
&lt;figure>&lt;a class="lightgallery" href="https://taetaetae.github.io/images/spring-boot-mybatis-mysql-xml/mung.jpg" title="/images/spring-boot-mybatis-mysql-xml/mung.jpg" data-thumbnail="/images/spring-boot-mybatis-mysql-xml/mung.jpg" data-sub-html="&lt;h2>출처 : http://blog.naver.com/PostView.nhn?blogId=ondo_h&amp;logNo=221437452142&lt;/h2>">
 &lt;img
 class="lazyload"
 src="https://taetaetae.github.io/svg/loading.min.svg"
 data-src="https://taetaetae.github.io/images/spring-boot-mybatis-mysql-xml/mung.jpg"
 data-srcset="https://taetaetae.github.io/images/spring-boot-mybatis-mysql-xml/mung.jpg, https://taetaetae.github.io/images/spring-boot-mybatis-mysql-xml/mung.jpg 1.5x, https://taetaetae.github.io/images/spring-boot-mybatis-mysql-xml/mung.jpg 2x"
 data-sizes="auto"
 alt="/images/spring-boot-mybatis-mysql-xml/mung.jpg" width="30%" />
 &lt;/a>&lt;figcaption class="image-caption">출처 : &lt;a href="http://blog.naver.com/PostView.nhn?blogId=ondo_h&amp;amp;logNo=221437452142" target="_blank" rel="noopener noreffer ">http://blog.naver.com/PostView.nhn?blogId=ondo_h&amp;logNo=221437452142&lt;/a>&lt;/figcaption>
 &lt;/figure>
&lt;p>사이드 프로젝트 초기셋팅을 하며 호기롭게 spring boot 최신버전에서 db를 연동하려 했는데 막상 완전 바닥부터 해본 경험이 적다보니 (spring boot 2 버전에서는 더욱더&amp;hellip;) 어디서부터 뭘 설정을 해야할지&amp;hellip; 그리고 &lt;code>이럴때 보는&lt;/code> 도큐먼트를 봐도 잘 이해가 안되어 삽질을 해가며 당황하기 일쑤였다.
이번 포스팅에서는 아래와 같은 구성을 하는데 목표를 두고자 한다.&lt;/p>
&lt;ul>
&lt;li>Spring Boot 2 프로젝트를 처음 만들고&lt;/li>
&lt;li>mybatis 를 사용해서&lt;/li>
&lt;li>mysql 을 연동하는것 (AWS 의 RDS를 사용, 추후 RDS사용법에 대해 블로깅 예정)&lt;/li>
&lt;/ul>
&lt;p>위와 같은 상황을 처음 접하는 분들께 도움이 되었으면 하는 바램으로 짧게나마 필자의 삽질기를 여행해보자.&lt;/p>
&lt;h2 id="spring-boot-2-프로젝트-만들기">Spring boot 2 프로젝트 만들기&lt;/h2>
&lt;p>필자는 IntelliJ를 사용하고 있어서 새로 프로젝트를 만들려고 할때 클릭 몇번만으로 dependency 설정까지 다 해주기 때문에 편하고 좋았다. 혹 이클립스나 다른 IDE를 사용하고 있다면 &lt;a href="https://start.spring.io/" target="_blank" rel="noopener noreffer ">https://start.spring.io/&lt;/a> 을 참고하면 도움이 될것같다. 여기서도 클릭 몇번으로 IntelliJ 에서 해주는 것처럼 내가 사용할 모듈을 선택하고 generate 를 누르면 프로젝트가 생성되어 다운로드 받아진다. (참 좋은 세상&amp;hellip;)
우선 File → New → Project 를 눌러서 아래 창을 열어보자. 그리고 뭔가 다 해줄것 같은 (개발도 해주면 안되나&amp;hellip;) &lt;code>Spring Initializr&lt;/code>을 선택후 아래와 같은 설정을 적어준 뒤 다음을 눌러준다.&lt;/p>
&lt;a class="lightgallery" href="https://taetaetae.github.io/images/spring-boot-mybatis-mysql-xml/1.jpg" title="/images/spring-boot-mybatis-mysql-xml/1.jpg" data-thumbnail="/images/spring-boot-mybatis-mysql-xml/1.jpg">
 &lt;img
 class="lazyload"
 src="https://taetaetae.github.io/svg/loading.min.svg"
 data-src="https://taetaetae.github.io/images/spring-boot-mybatis-mysql-xml/1.jpg"
 data-srcset="https://taetaetae.github.io/images/spring-boot-mybatis-mysql-xml/1.jpg, https://taetaetae.github.io/images/spring-boot-mybatis-mysql-xml/1.jpg 1.5x, https://taetaetae.github.io/images/spring-boot-mybatis-mysql-xml/1.jpg 2x"
 data-sizes="auto"
 alt="/images/spring-boot-mybatis-mysql-xml/1.jpg" width="80%" />
 &lt;/a>
&lt;p>사용할 모듈을 선택해주자. 필자는 이것저것(?)을 도와주는 &lt;code>lombok&lt;/code>과 &lt;code>Mybatis&lt;/code>, &lt;code>MySQL&lt;/code>을 선택하고 프로젝트를 생성하였다. 그러면 이쁜(?) pom.xml 과 함께 당장 개발을 시작할 수 있는 환경이 제공된다.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-xml" data-lang="xml">&lt;span class="line">&lt;span class="cl">&lt;span class="nt">&amp;lt;dependencies&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">	&lt;span class="nt">&amp;lt;dependency&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">		&lt;span class="nt">&amp;lt;groupId&amp;gt;&lt;/span>org.mybatis.spring.boot&lt;span class="nt">&amp;lt;/groupId&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">		&lt;span class="nt">&amp;lt;artifactId&amp;gt;&lt;/span>mybatis-spring-boot-starter&lt;span class="nt">&amp;lt;/artifactId&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">		&lt;span class="nt">&amp;lt;version&amp;gt;&lt;/span>2.0.1&lt;span class="nt">&amp;lt;/version&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">	&lt;span class="nt">&amp;lt;/dependency&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">	&lt;span class="nt">&amp;lt;dependency&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">		&lt;span class="nt">&amp;lt;groupId&amp;gt;&lt;/span>mysql&lt;span class="nt">&amp;lt;/groupId&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">		&lt;span class="nt">&amp;lt;artifactId&amp;gt;&lt;/span>mysql-connector-java&lt;span class="nt">&amp;lt;/artifactId&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">	&lt;span class="nt">&amp;lt;/dependency&amp;gt;&lt;/span>	
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">	&lt;span class="nt">&amp;lt;dependency&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">		&lt;span class="nt">&amp;lt;groupId&amp;gt;&lt;/span>org.projectlombok&lt;span class="nt">&amp;lt;/groupId&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">		&lt;span class="nt">&amp;lt;artifactId&amp;gt;&lt;/span>lombok&lt;span class="nt">&amp;lt;/artifactId&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">		&lt;span class="nt">&amp;lt;optional&amp;gt;&lt;/span>true&lt;span class="nt">&amp;lt;/optional&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">	&lt;span class="nt">&amp;lt;/dependency&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nt">&amp;lt;/dependencies&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;a class="lightgallery" href="https://taetaetae.github.io/images/spring-boot-mybatis-mysql-xml/2.jpg" title="/images/spring-boot-mybatis-mysql-xml/2.jpg" data-thumbnail="/images/spring-boot-mybatis-mysql-xml/2.jpg">
 &lt;img
 class="lazyload"
 src="https://taetaetae.github.io/svg/loading.min.svg"
 data-src="https://taetaetae.github.io/images/spring-boot-mybatis-mysql-xml/2.jpg"
 data-srcset="https://taetaetae.github.io/images/spring-boot-mybatis-mysql-xml/2.jpg, https://taetaetae.github.io/images/spring-boot-mybatis-mysql-xml/2.jpg 1.5x, https://taetaetae.github.io/images/spring-boot-mybatis-mysql-xml/2.jpg 2x"
 data-sizes="auto"
 alt="/images/spring-boot-mybatis-mysql-xml/2.jpg" width="80%" />
 &lt;/a>
&lt;p>우선 여기까지 잘 되었는제 확인해보기 위해 Controller 에 현재시간을 출력하는걸 만들어 보고&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="nd">@RestController&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="kd">public&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">class&lt;/span> &lt;span class="nc">ApiController&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">	&lt;/span>&lt;span class="nd">@GetMapping&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">path&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s">&amp;#34;/helloWorld&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">	&lt;/span>&lt;span class="kd">public&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">String&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nf">helloWorld&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">		&lt;/span>&lt;span class="k">return&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">LocalDateTime&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">now&lt;/span>&lt;span class="p">().&lt;/span>&lt;span class="na">format&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">DateTimeFormatter&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">ISO_LOCAL_DATE_TIME&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">	&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>톰켓을 실행해보면 정상적으로 접속과 출력이 되는것을 확인할 수 있다.&lt;/p>
&lt;a class="lightgallery" href="https://taetaetae.github.io/images/spring-boot-mybatis-mysql-xml/3.jpg" title="/images/spring-boot-mybatis-mysql-xml/3.jpg" data-thumbnail="/images/spring-boot-mybatis-mysql-xml/3.jpg">
 &lt;img
 class="lazyload"
 src="https://taetaetae.github.io/svg/loading.min.svg"
 data-src="https://taetaetae.github.io/images/spring-boot-mybatis-mysql-xml/3.jpg"
 data-srcset="https://taetaetae.github.io/images/spring-boot-mybatis-mysql-xml/3.jpg, https://taetaetae.github.io/images/spring-boot-mybatis-mysql-xml/3.jpg 1.5x, https://taetaetae.github.io/images/spring-boot-mybatis-mysql-xml/3.jpg 2x"
 data-sizes="auto"
 alt="/images/spring-boot-mybatis-mysql-xml/3.jpg" width="80%" />
 &lt;/a>
&lt;h2 id="mysql-연동하기">MySQL 연동하기&lt;/h2>
&lt;p>필자가 허둥지둥 했던점 중 하나는 MyBatis와 MySQL을 동시에 연동하려고 하다보니 문제가 발생해도 어디서의 문제인지를 제대로 파악하지 못하고 삽질했다는 점이다. 여기서 정확히 짚고 넘어가면 우선 데이터를 연결해주는 ORM인 MyBatis를 셋팅해준 다음 MySQL을 연동해주는 식으로 분리해서 설정을 하면 햇갈리지 않고 (돌아가지 않고) 보다 빠르게 설정이 가능할것 같다. (여기서 순서는 중요하지 않고 별도로 설정해야 한다는 관점이 중요한것 같다.)
우선 &lt;code>src/main/resources&lt;/code>폴더에 있는 &lt;code>application.properties&lt;/code> 에 다음처럼 작성해주자.&lt;/p>
&lt;pre tabindex="0">&lt;code>spring.datasource.hikari.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.hikari.jdbc-url=jdbc:mysql://{url}:{port}/{db}
spring.datasource.hikari.username={id}
spring.datasource.hikari.password={password}
&lt;/code>&lt;/pre>&lt;p>위의 jdbc-url 항목에서 AWS에서 제공하는 RDS를 사용하는 경우 RDS에서 제공해주는 엔드포인트와 포트를 적어주면 된다. (추후 AWS - RDS에 대해 블로깅 예정이다.)
Spring Boot 2.0 이후부터 기본적으로 사용되는 커넥션 풀이 HikariCP로 변경되었다고 한다. (&lt;a href="https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-2.0-Release-Notes#hikaricp" target="_blank" rel="noopener noreffer ">링크&lt;/a>) 커넥션 풀 종류중 성능이 좋다고 하는데 &lt;a href="https://github.com/brettwooldridge/HikariCP" target="_blank" rel="noopener noreffer ">링크&lt;/a>를 가보면 다른 커넥션 풀 라이브러리와 성능을 비교한 벤치마크 결과를 확인할 수 있다.
위처럼 &lt;code>spring.datasource.hikari&lt;/code> 가 prefix로 붙고 각종 정보들을 적어주어 config 에서 인식될수 있도록 해주자. 그 다음 DataSource 설정을 해준다.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="nd">@Slf4j&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nd">@Configuration&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nd">@PropertySource&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;classpath:/application.properties&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="kd">public&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">class&lt;/span> &lt;span class="nc">DatabaseConfiguration&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">	&lt;/span>&lt;span class="nd">@Bean&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">	&lt;/span>&lt;span class="nd">@ConfigurationProperties&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">prefix&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s">&amp;#34;spring.datasource.hikari&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">	&lt;/span>&lt;span class="kd">public&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">HikariConfig&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nf">hikariConfig&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">		&lt;/span>&lt;span class="k">return&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">new&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">HikariConfig&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">	&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">	&lt;/span>&lt;span class="nd">@Bean&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">	&lt;/span>&lt;span class="kd">public&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">DataSource&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nf">dataSource&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">		&lt;/span>&lt;span class="n">DataSource&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">dataSource&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">new&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">HikariDataSource&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">hikariConfig&lt;/span>&lt;span class="p">());&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">		&lt;/span>&lt;span class="n">log&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">info&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;datasource : {}&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">dataSource&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">		&lt;/span>&lt;span class="k">return&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">dataSource&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">	&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>위 내용은 DataSource 를 hikariConfig에서 설정한 정보로 만들어 준다는 의미이다. 이렇게만 하고 프로젝트를 다시 실행시켜보면 logger 에 의해 datasource 의 정보를 볼수가 있다.&lt;/p></description></item><item><title>eclipse에서 spring-boot로 web 만들기</title><link>https://taetaetae.github.io/2017/02/27/spring-boot-eclipse/</link><pubDate>Mon, 27 Feb 2017 14:37:27 +0000</pubDate><guid>https://taetaetae.github.io/2017/02/27/spring-boot-eclipse/</guid><description>&lt;p>Spring 환경에서 웹 어플리케이션을 만들어야 한다면 pom.xml 에 이런저런 설정들을 적어줘야 했다. 하지만 이런 수고(?)를 덜어줄수 있는 방법중에 한가지가 바로 Spring Boot로 만드는 방법인데, 이클립스 환경에서 만드는 법을 정리하고자 한다.&lt;/p>
&lt;!-- more -->
&lt;h2 id="new--maven-project">new &amp;gt; Maven Project&lt;/h2>
&lt;p>빈 Maven Project 를 만드는 방법은 아주 간단하니 생략하고&amp;hellip; 만들게 되면 pom.xml 은 아래처럼 아주 깔끔한(?)상태로 만들어지게 된다.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-xml" data-lang="xml">&lt;span class="line">&lt;span class="cl">&lt;span class="nt">&amp;lt;project&lt;/span> &lt;span class="na">xmlns=&lt;/span>&lt;span class="s">&amp;#34;http://maven.apache.org/POM/4.0.0&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">xmlns:xsi=&lt;/span>&lt;span class="s">&amp;#34;http://www.w3.org/2001/XMLSchema-instance&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">xsi:schemaLocation=&lt;/span>&lt;span class="s">&amp;#34;http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd&amp;#34;&lt;/span>&lt;span class="nt">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;modelVersion&amp;gt;&lt;/span>4.0.0&lt;span class="nt">&amp;lt;/modelVersion&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;groupId&amp;gt;&lt;/span>com.example&lt;span class="nt">&amp;lt;/groupId&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;artifactId&amp;gt;&lt;/span>boot&lt;span class="nt">&amp;lt;/artifactId&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;version&amp;gt;&lt;/span>0.0.1-SNAPSHOT&lt;span class="nt">&amp;lt;/version&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nt">&amp;lt;/project&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>그러면 이 비어있는 pom.xml 에 Spring-Boot 에 필요한 설정들을 추가해주기로 한다.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-xml" data-lang="xml">&lt;span class="line">&lt;span class="cl">&lt;span class="nt">&amp;lt;parent&amp;gt;&lt;/span> &lt;span class="c">&amp;lt;!--boot의 스타터를 사용하겠다고 명시적으로 설정--&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;groupId&amp;gt;&lt;/span>org.springframework.boot&lt;span class="nt">&amp;lt;/groupId&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;artifactId&amp;gt;&lt;/span>spring-boot-starter-parent&lt;span class="nt">&amp;lt;/artifactId&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;version&amp;gt;&lt;/span>1.5.1.RELEASE&lt;span class="nt">&amp;lt;/version&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;relativePath&lt;/span> &lt;span class="nt">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nt">&amp;lt;/parent&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nt">&amp;lt;dependencies&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;dependency&amp;gt;&lt;/span> &lt;span class="c">&amp;lt;!--boot에서 스타터패키지로 제공해주는 것들중에 web 설정 부분 --&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;groupId&amp;gt;&lt;/span>org.springframework.boot&lt;span class="nt">&amp;lt;/groupId&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;artifactId&amp;gt;&lt;/span>spring-boot-starter-web&lt;span class="nt">&amp;lt;/artifactId&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;/dependency&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nt">&amp;lt;/dependencies&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nt">&amp;lt;build&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;plugins&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;plugin&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;groupId&amp;gt;&lt;/span>org.springframework.boot&lt;span class="nt">&amp;lt;/groupId&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;artifactId&amp;gt;&lt;/span>spring-boot-maven-plugin&lt;span class="nt">&amp;lt;/artifactId&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;/plugin&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;/plugins&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nt">&amp;lt;/build&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>그다음 임의의 java 클래스를 하나 만들고 거기에 아래처럼 설정하면 끝&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="nd">@SpringBootApplication&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c1">// @Configuration + @EnableAutoConfiguration + @ComponentScan 들의 종합 어노테이션&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="kd">public&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">class&lt;/span> &lt;span class="nc">TestApplication&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">public&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">static&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kt">void&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nf">main&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">String&lt;/span>&lt;span class="o">[]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">args&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">throws&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Exception&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">SpringApplication&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">run&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">TestApplication&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">class&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">args&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Spring Boot 에서는 내장WAS를 가지고 있기 때문에 main 메소드에서 우클릭후 &lt;code>run AS → Spring Boot App&lt;/code> 을 선택해주면 8080포트로 띄워지게 된다.&lt;/p>
&lt;h2 id="new--spring-starter-project">new &amp;gt; Spring Starter project&lt;/h2>
&lt;p>(STS가 설치되어있다는 가정하에)이 메뉴를 사용하면 위에서 했던 일련의 설정들을 자동으로 해주게 된다. 간단한 내용이니 next를 해주다 마지막에 &lt;code>Dependencies&lt;/code> 설정하는 부분에서 Web 을 체크해주고 &lt;code>Finish 버튼&lt;/code>을 누르면 끝&lt;/p>
&lt;h2 id="spring-initializr-startspringio">Spring Initializr (start.spring.io)&lt;/h2>
&lt;p>&lt;a href="http://start.spring.io/" target="_blank" rel="noopener noreffer ">http://start.spring.io/&lt;/a> 에 들어가보면 구지 설명하지 않아도 친절하게 Generate 해주는 페이지가 보인다. 여기서 web 을 &lt;code>Dependencies&lt;/code>에 추가하고 Generate를 하면 해당 프로젝트가 압축된 상태로 다운이 받아지게 되고 이를 IDE 에서 열어보면 위에서 했던 일련의 과정들이 설정되어 있는것을 확인해볼수가 있다.&lt;/p>
&lt;h2 id="내장톰켓을-사용안하고-별도-톰켓을-사용해야-하는-경우">내장톰켓을 사용안하고 별도 톰켓을 사용해야 하는 경우&lt;/h2>
&lt;p>Spring boot는 자체적으로 내장 WAS를 가지고 있다. 하지만 관리포인트나 이런저런 이유로 내장톰켓을 사용하지 못하는 환경이라면 다음과 같은 설정을 해주면 된다.&lt;/p>
&lt;ul>
&lt;li>일반적으로 빌드가 되면 &lt;code>jar&lt;/code>로 만들어 질텐데 &lt;code>war&lt;/code>로 빌드 되도록 수정을 해야한다. (was가 WAR를 물고 떠야하기 때문..)&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-xml" data-lang="xml">&lt;span class="line">&lt;span class="cl">&lt;span class="nt">&amp;lt;packaging&amp;gt;&lt;/span>war&lt;span class="nt">&amp;lt;/packaging&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ul>
&lt;li>dependency 에 tomcat을 추가해준다.&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-xml" data-lang="xml">&lt;span class="line">&lt;span class="cl">&lt;span class="nt">&amp;lt;dependency&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;groupId&amp;gt;&lt;/span>org.springframework.boot&lt;span class="nt">&amp;lt;/groupId&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;artifactId&amp;gt;&lt;/span>spring-boot-starter-tomcat&lt;span class="nt">&amp;lt;/artifactId&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;scope&amp;gt;&lt;/span>provided&lt;span class="nt">&amp;lt;/scope&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nt">&amp;lt;/dependency&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ul>
&lt;li>아래처럼 main 메소드가 있는 클래스에 &lt;code>SpringBootServletInitializer&lt;/code>를 상속받게 한 후 &lt;code>configure&lt;/code>메소드를 오버라이딩 해준다.&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="nd">@SpringBootApplication&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="kd">public&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">class&lt;/span> &lt;span class="nc">TestApplication&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">extends&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">SpringBootServletInitializer&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nd">@Override&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">protected&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">SpringApplicationBuilder&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nf">configure&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">SpringApplicationBuilder&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">builder&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">return&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">builder&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">sources&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">TestApplication&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">class&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">public&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">static&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kt">void&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nf">main&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">String&lt;/span>&lt;span class="o">[]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">args&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">throws&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Exception&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">SpringApplication&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">run&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">TestApplication&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">class&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">args&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ul>
&lt;li>톰켓에 띄우기 위하여 프로젝트 설정(Project Facets)에서 &lt;code>Dynamic Web Module&lt;/code>을 체크해준다.&lt;/li>
&lt;/ul>
&lt;p>참고 URL&lt;/p>
&lt;ul>
&lt;li>&lt;a href="http://www.donnert.net/86" target="_blank" rel="noopener noreffer ">http://www.donnert.net/86&lt;/a>&lt;/li>
&lt;li>&lt;a href="http://opennote46.tistory.com/124" target="_blank" rel="noopener noreffer ">http://opennote46.tistory.com/124&lt;/a>&lt;/li>
&lt;/ul></description></item></channel></rss>