<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>훈훈훈</title>
    <link>https://wave1994.tistory.com/</link>
    <description></description>
    <language>ko</language>
    <pubDate>Tue, 12 May 2026 13:39:29 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>훈훈훈</managingEditor>
    <image>
      <title>훈훈훈</title>
      <url>https://tistory1.daumcdn.net/tistory/2953771/attach/6cec0818d5e54efebc566fd05d6d87f0</url>
      <link>https://wave1994.tistory.com</link>
    </image>
    <item>
      <title>Amazon AWS :: Aurora MySQL JDBC driver 정리</title>
      <link>https://wave1994.tistory.com/190</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Introduction&lt;/span&gt;&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 글은 AWS Aurora MySQL 를 사용할 때 어떤 MySQL JDBC driver 라이브러리 사용할지 고민하면서 찾아보게 된 내용을 정리해보기로 하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;간단하게 요약하자면 MariaDB connector / J 와 AWS MySQL JDBC 사이에 고민을 했고, 두 라이브러리를 비교해보게 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;MariaDB connector / J&lt;/span&gt;&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MariaDB Connector / J 공식문서를 보면 아래와 같이 auroa 옵션을 부여할 경우 replication 과 failover 기능을 사용할 수 있는 것을 알 수 있다. 해당 기능의 장점은 별 다른 구현 없이 failover 와 replication 를 쉽게 이용할 수 있다는 점이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1542&quot; data-origin-height=&quot;548&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/t8NwD/btrJGUIYhlL/PyFxbm0mu5LaLGVus1l6b0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/t8NwD/btrJGUIYhlL/PyFxbm0mu5LaLGVus1l6b0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/t8NwD/btrJGUIYhlL/PyFxbm0mu5LaLGVus1l6b0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Ft8NwD%2FbtrJGUIYhlL%2FPyFxbm0mu5LaLGVus1l6b0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1542&quot; height=&quot;548&quot; data-origin-width=&quot;1542&quot; data-origin-height=&quot;548&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만, MariaDB JDBC drvier 버전 3 부터는 더 이상 해당 기능을 지원하지 않는 것으로 명시되어 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MariaDB JDBC drvier 2 버전대를 사용하여 애플리케이션을 운영할 수 있겠지만, 시간이 지남에 따라 결국에는 라이브러리를 교체하게 될 것이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 기존에 사용하였던 MariaDB connector / J 를 사용하려고 하였지만, 위와 같은 문제로 대안을 찾기 위해 AWS&amp;nbsp;&lt;a href=&quot;https://aws.amazon.com/ko/blogs/database/using-the-mariadb-jdbc-driver-with-amazon-aurora-with-mysql-compatibility/&quot;&gt;공식 문서&lt;/a&gt;를 확인해보기로 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고로 MariaDB JDBC drvier 에서 지원하는 replication 기능은 애플리케이션에서 코드로 직접 구현할 수 있다. (&lt;a href=&quot;https://wave1994.tistory.com/177&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;참고 글&lt;/a&gt;)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만, slave 를 여러대 사용하는 경우 아래처럼 yml 파일에 매번 추가하고 Bean 으로 수동 등록해야하는 번거로움이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1660542270155&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;spring:
  datasource:
    master:
      hikari:
        driver-class-name: com.mysql.cj.jdbc.Driver
        jdbc-url: jdbc:mysql://127.0.0.1:3306/master?serverTimezone=UTC
        username: root
        password: 1234

    slave1:
      hikari:
        driver-class-name: com.mysql.cj.jdbc.Driver
        jdbc-url: jdbc:mysql://127.0.0.1:3306/slave1?serverTimezone=UTC
        username: root
        password: 1234
    
    slave2:
      hikari:
        driver-class-name: com.mysql.cj.jdbc.Driver
        jdbc-url: jdbc:mysql://127.0.0.1:3306/slave2?serverTimezone=UTC
        username: root
        password: 1234
        
    slave3:
      hikari:
        driver-class-name: com.mysql.cj.jdbc.Driver
        jdbc-url: jdbc:mysql://127.0.0.1:3306/slave3?serverTimezone=UTC
        username: root
        password: 1234&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재는 MariaDB JDBC drvier 를 사용하여 replication 과 failover 기능을 사용하고 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1660746645171&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;spring:
  datasource:
    hikari:
      jdbc-url: jdbc:mariadb:aurora://___.rds.amazonaws.com:3306/db_name
      driver-class-name: org.mariadb.jdbc.Driver
      username:
      password:&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;MariaDB Connector / J vs AWS MySQL JDBC&lt;/span&gt;&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;MariaDB connector / J 버전 이슈에 대한 방안을 찾기 위해 관련 AWS 공식 문서를 찾아보았고, &lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;해당 &lt;/span&gt;&lt;/span&gt;&lt;a href=&quot;https://aws.amazon.com/ko/blogs/database/using-the-mariadb-jdbc-driver-with-amazon-aurora-with-mysql-compatibility/&quot;&gt;공식 문서&lt;/a&gt;에서 찾아 볼 수 있었다. &lt;span&gt;해당 문서에는 &lt;/span&gt;Amazon AWS 에서 개발한 AWS JDBC driver for MySQL 를 사용하는 것을 권장하는 것을 알 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1640&quot; data-origin-height=&quot;1032&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bpGHK7/btrJGU3gfN8/KgAZTibegrNDiNMPZsaiW1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bpGHK7/btrJGU3gfN8/KgAZTibegrNDiNMPZsaiW1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bpGHK7/btrJGU3gfN8/KgAZTibegrNDiNMPZsaiW1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbpGHK7%2FbtrJGU3gfN8%2FKgAZTibegrNDiNMPZsaiW1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;857&quot; height=&quot;539&quot; data-origin-width=&quot;1640&quot; data-origin-height=&quot;1032&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 &lt;a href=&quot;https://docs.aws.amazon.com/ko_kr/AmazonRDS/latest/AuroraUserGuide/Aurora.Connecting.html#Aurora.Connecting.AuroraMySQL&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;문서&lt;/a&gt;에서도 관련 내용을 이야기 하고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AWS MySQL JDBC driver 를 사용하면 좀 더 향상된 failover 속도를 제공한다고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1426&quot; data-origin-height=&quot;726&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/vJPvX/btrJE3epP3R/XSOG2Nz3ogYueWicB3Fnd0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/vJPvX/btrJE3epP3R/XSOG2Nz3ogYueWicB3Fnd0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/vJPvX/btrJE3epP3R/XSOG2Nz3ogYueWicB3Fnd0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FvJPvX%2FbtrJE3epP3R%2FXSOG2Nz3ogYueWicB3Fnd0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1426&quot; height=&quot;726&quot; data-origin-width=&quot;1426&quot; data-origin-height=&quot;726&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;성능 관련해서는 아래 &lt;a href=&quot;https://aws.amazon.com/ko/blogs/database/improve-application-availability-with-the-aws-jdbc-driver-for-amazon-aurora-mysql/\&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;문서&lt;/a&gt;에서 확인할 수 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;장애가 발생했을 때, MariaDB Connector / J 보다 더 빠르게 대응하는 것을 볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1626&quot; data-origin-height=&quot;766&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bKjEmN/btrJAQAPFDQ/HtcH2j5Kd62w9jqrmKKeNK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bKjEmN/btrJAQAPFDQ/HtcH2j5Kd62w9jqrmKKeNK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bKjEmN/btrJAQAPFDQ/HtcH2j5Kd62w9jqrmKKeNK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbKjEmN%2FbtrJAQAPFDQ%2FHtcH2j5Kd62w9jqrmKKeNK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1626&quot; height=&quot;766&quot; data-origin-width=&quot;1626&quot; data-origin-height=&quot;766&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제까지 AWS 공식 문서에서 AWS JDBC driver for MySQL 에 관하여 소개하는 글들을 살펴보았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 뭔가 놓친 기분이 들었다. failover 에 관한 내용은 성능 테스트 자료까지 자세하게 설명이 되어 있었지만, replication 에 관한 내용은 볼 수 없었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 해당 프로젝트 github 을 보기로 하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1578&quot; data-origin-height=&quot;652&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bwTjEu/btrJXBuUx0G/bhvkg6Re00ANGt6ztpgpPk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bwTjEu/btrJXBuUx0G/bhvkg6Re00ANGt6ztpgpPk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bwTjEu/btrJXBuUx0G/bhvkg6Re00ANGt6ztpgpPk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbwTjEu%2FbtrJXBuUx0G%2Fbhvkg6Re00ANGt6ztpgpPk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1578&quot; height=&quot;652&quot; data-origin-width=&quot;1578&quot; data-origin-height=&quot;652&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로젝트 README 를 보면 Read-Write spliting (replication) 기능은 현재 지원하고 있지 않는 것을 알 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 MariaDB Connector / J 에서 read only 트랜잭션인 경우 reader DB 로 라우팅 해주는 기능은 없는 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 저 기능을 지원하지 않는다면 AWS JDBC driver for MySQL 를 사용할 이유는 없다고 생각한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아쉽지만, 혹시나 해당 기능은 언제 지원이 되는지 궁금하여 PR 들을 찾아보았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최근에 관련 몇몇 PR 들이 반영 된 것으로 보인다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1158&quot; data-origin-height=&quot;912&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qhzCo/btrJYx6UkSY/Oae0tl5mK5Oh1SlDm4nVp0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qhzCo/btrJYx6UkSY/Oae0tl5mK5Oh1SlDm4nVp0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qhzCo/btrJYx6UkSY/Oae0tl5mK5Oh1SlDm4nVp0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FqhzCo%2FbtrJYx6UkSY%2FOae0tl5mK5Oh1SlDm4nVp0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;846&quot; height=&quot;666&quot; data-origin-width=&quot;1158&quot; data-origin-height=&quot;912&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/awslabs/aws-mysql-jdbc/pull/252&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;해당 PR&lt;/a&gt; 을 보면 1.2.0 버전에는 Read-write splitiing 기능이 배포될 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1680&quot; data-origin-height=&quot;872&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bCdbuK/btrJYx6Us31/Dw5FN7ZbPh2dJuFuy4kPE0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bCdbuK/btrJYx6Us31/Dw5FN7ZbPh2dJuFuy4kPE0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bCdbuK/btrJYx6Us31/Dw5FN7ZbPh2dJuFuy4kPE0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbCdbuK%2FbtrJYx6Us31%2FDw5FN7ZbPh2dJuFuy4kPE0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1680&quot; height=&quot;872&quot; data-origin-width=&quot;1680&quot; data-origin-height=&quot;872&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 1.1.0 버전까지 릴리즈 되었으니, 빠르면 올해 안에 해당 기능을 사용할 수 있지 않을까 싶다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에 작성한 글을 짧게 요약하자면, 아직은 MariaDB connector / J 2 버전대를 사용하고&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;추후 &lt;span&gt;AWS JDBC driver for MySQL&lt;/span&gt; 1.2.0 버전이 나오고 별 다른 이슈가 없을 때, 라이브러리를 교체해도 좋을 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #555555;&quot;&gt;# reference&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #555555;&quot;&gt;&lt;a href=&quot;https://github.com/awslabs/aws-mysql-jdbc&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://github.com/awslabs/aws-mysql-jdbc&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1660540183971&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;GitHub - awslabs/aws-mysql-jdbc: The Amazon Web Services (AWS) JDBC Driver for MySQL is a driver that enables applications to ta&quot; data-og-description=&quot;The Amazon Web Services (AWS) JDBC Driver for MySQL is a driver that enables applications to take full advantage of the features of clustered MySQL databases. - GitHub - awslabs/aws-mysql-jdbc: Th...&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/awslabs/aws-mysql-jdbc&quot; data-og-url=&quot;https://github.com/awslabs/aws-mysql-jdbc&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/dbpRs9/hyPqVPeoi3/GtxKiX45cEPfkW7Qv3TBKk/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/awslabs/aws-mysql-jdbc&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/awslabs/aws-mysql-jdbc&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/dbpRs9/hyPqVPeoi3/GtxKiX45cEPfkW7Qv3TBKk/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;GitHub - awslabs/aws-mysql-jdbc: The Amazon Web Services (AWS) JDBC Driver for MySQL is a driver that enables applications to ta&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;The Amazon Web Services (AWS) JDBC Driver for MySQL is a driver that enables applications to take full advantage of the features of clustered MySQL databases. - GitHub - awslabs/aws-mysql-jdbc: Th...&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/mariadb-corporation/mariadb-connector-j&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://github.com/mariadb-corporation/mariadb-connector-j&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1660540197430&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;GitHub - mariadb-corporation/mariadb-connector-j: MariaDB Connector/J is used to connect applications developed in Java to Maria&quot; data-og-description=&quot;MariaDB Connector/J is used to connect applications developed in Java to MariaDB and MySQL databases. MariaDB Connector/J is LGPL licensed. - GitHub - mariadb-corporation/mariadb-connector-j: Maria...&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/mariadb-corporation/mariadb-connector-j&quot; data-og-url=&quot;https://github.com/mariadb-corporation/mariadb-connector-j&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/kZOJg/hyPsSJ6ZzY/IXcF6zpKVswSrHd9t8iU6k/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/mariadb-corporation/mariadb-connector-j&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/mariadb-corporation/mariadb-connector-j&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/kZOJg/hyPsSJ6ZzY/IXcF6zpKVswSrHd9t8iU6k/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;GitHub - mariadb-corporation/mariadb-connector-j: MariaDB Connector/J is used to connect applications developed in Java to Maria&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;MariaDB Connector/J is used to connect applications developed in Java to MariaDB and MySQL databases. MariaDB Connector/J is LGPL licensed. - GitHub - mariadb-corporation/mariadb-connector-j: Maria...&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://johngrib.github.io/wiki/mariadb-connector-j/#failover-and-load-balancing-parameters&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://johngrib.github.io/wiki/mariadb-connector-j/#failover-and-load-balancing-parameters&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1660540210278&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;MariaDB Connector/J&quot; data-og-description=&quot; &quot; data-og-host=&quot;johngrib.github.io&quot; data-og-source-url=&quot;https://johngrib.github.io/wiki/mariadb-connector-j/#failover-and-load-balancing-parameters&quot; data-og-url=&quot;https://johngrib.github.io/wiki/mariadb-connector-j/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cPwbGA/hyPqUCMRoo/D72TZIuHdE4Kp9cCyqtAIK/img.png?width=170&amp;amp;height=170&amp;amp;face=0_0_170_170,https://scrap.kakaocdn.net/dn/cFvzTj/hyPq1212Xb/Ktp3Arq7kh54pKhOOkWTn0/img.png?width=2242&amp;amp;height=256&amp;amp;face=0_0_2242_256&quot;&gt;&lt;a href=&quot;https://johngrib.github.io/wiki/mariadb-connector-j/#failover-and-load-balancing-parameters&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://johngrib.github.io/wiki/mariadb-connector-j/#failover-and-load-balancing-parameters&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cPwbGA/hyPqUCMRoo/D72TZIuHdE4Kp9cCyqtAIK/img.png?width=170&amp;amp;height=170&amp;amp;face=0_0_170_170,https://scrap.kakaocdn.net/dn/cFvzTj/hyPq1212Xb/Ktp3Arq7kh54pKhOOkWTn0/img.png?width=2242&amp;amp;height=256&amp;amp;face=0_0_2242_256');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;MariaDB Connector/J&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;johngrib.github.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://aws.amazon.com/ko/blogs/database/improve-application-availability-with-the-aws-jdbc-driver-for-amazon-aurora-mysql/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://aws.amazon.com/ko/blogs/database/improve-application-availability-with-the-aws-jdbc-driver-for-amazon-aurora-mysql/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1660540263014&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;Improve application availability with the AWS JDBC Driver for Amazon Aurora MySQL | Amazon Web Services&quot; data-og-description=&quot;In this post, we demonstrate how to use the Amazon Web Services (AWS) JDBC Driver for MySQL, which allows an application to take advantage of the features of clustered MySQL databases. You can use this driver as a drop-in replacement for the MySQL Connecto&quot; data-og-host=&quot;aws.amazon.com&quot; data-og-source-url=&quot;https://aws.amazon.com/ko/blogs/database/improve-application-availability-with-the-aws-jdbc-driver-for-amazon-aurora-mysql/&quot; data-og-url=&quot;https://aws.amazon.com/blogs/database/improve-application-availability-with-the-aws-jdbc-driver-for-amazon-aurora-mysql/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bupxgB/hyPqXM5M5O/6k8Qefe03h99StKzasKLu0/img.png?width=890&amp;amp;height=500&amp;amp;face=0_0_890_500,https://scrap.kakaocdn.net/dn/boXxyf/hyPq4yDwWm/UcXFlkUmJgumN0KyjAZbyK/img.png?width=890&amp;amp;height=500&amp;amp;face=0_0_890_500,https://scrap.kakaocdn.net/dn/n4MAM/hyPsThXgM8/bHyeyGXeFBhAB5cR4UKbb0/img.png?width=977&amp;amp;height=456&amp;amp;face=0_0_977_456&quot;&gt;&lt;a href=&quot;https://aws.amazon.com/ko/blogs/database/improve-application-availability-with-the-aws-jdbc-driver-for-amazon-aurora-mysql/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://aws.amazon.com/ko/blogs/database/improve-application-availability-with-the-aws-jdbc-driver-for-amazon-aurora-mysql/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bupxgB/hyPqXM5M5O/6k8Qefe03h99StKzasKLu0/img.png?width=890&amp;amp;height=500&amp;amp;face=0_0_890_500,https://scrap.kakaocdn.net/dn/boXxyf/hyPq4yDwWm/UcXFlkUmJgumN0KyjAZbyK/img.png?width=890&amp;amp;height=500&amp;amp;face=0_0_890_500,https://scrap.kakaocdn.net/dn/n4MAM/hyPsThXgM8/bHyeyGXeFBhAB5cR4UKbb0/img.png?width=977&amp;amp;height=456&amp;amp;face=0_0_977_456');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Improve application availability with the AWS JDBC Driver for Amazon Aurora MySQL | Amazon Web Services&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;In this post, we demonstrate how to use the Amazon Web Services (AWS) JDBC Driver for MySQL, which allows an application to take advantage of the features of clustered MySQL databases. You can use this driver as a drop-in replacement for the MySQL Connecto&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;aws.amazon.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://docs.aws.amazon.com/ko_kr/AmazonRDS/latest/AuroraUserGuide/Aurora.Connecting.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://docs.aws.amazon.com/ko_kr/AmazonRDS/latest/AuroraUserGuide/Aurora.Connecting.html&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1660540300604&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Amazon Aurora DB 클러스터에 연결 - Amazon Aurora&quot; data-og-description=&quot;SSL을 사용하여 클러스터 엔드포인트에 연결하려면 클라이언트 연결 유틸리티에서 Subject Alternative Names(SAN)를 지원해야 합니다. 클라이언트 연결 유틸리티에서 SAN을 지원하지 않는 경우, Aurora DB &quot; data-og-host=&quot;docs.aws.amazon.com&quot; data-og-source-url=&quot;https://docs.aws.amazon.com/ko_kr/AmazonRDS/latest/AuroraUserGuide/Aurora.Connecting.html&quot; data-og-url=&quot;https://docs.aws.amazon.com/ko_kr/AmazonRDS/latest/AuroraUserGuide/Aurora.Connecting.html&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bsX5DF/hyPsSXD2mB/0htdw6uLBx7pcdk9mEpwTK/img.png?width=984&amp;amp;height=812&amp;amp;face=0_0_984_812,https://scrap.kakaocdn.net/dn/icXJJ/hyPsNPzjvH/J62Kf9N2YdVZ7GkVKjZ651/img.png?width=981&amp;amp;height=770&amp;amp;face=0_0_981_770&quot;&gt;&lt;a href=&quot;https://docs.aws.amazon.com/ko_kr/AmazonRDS/latest/AuroraUserGuide/Aurora.Connecting.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://docs.aws.amazon.com/ko_kr/AmazonRDS/latest/AuroraUserGuide/Aurora.Connecting.html&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bsX5DF/hyPsSXD2mB/0htdw6uLBx7pcdk9mEpwTK/img.png?width=984&amp;amp;height=812&amp;amp;face=0_0_984_812,https://scrap.kakaocdn.net/dn/icXJJ/hyPsNPzjvH/J62Kf9N2YdVZ7GkVKjZ651/img.png?width=981&amp;amp;height=770&amp;amp;face=0_0_981_770');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Amazon Aurora DB 클러스터에 연결 - Amazon Aurora&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;SSL을 사용하여 클러스터 엔드포인트에 연결하려면 클라이언트 연결 유틸리티에서 Subject Alternative Names(SAN)를 지원해야 합니다. 클라이언트 연결 유틸리티에서 SAN을 지원하지 않는 경우, Aurora DB&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;docs.aws.amazon.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://hoing.io/archives/1285&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://hoing.io/archives/1285&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1660785549965&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;AWS JDBC Driver for Amazon Aurora MySQL&quot; data-og-description=&quot;안녕하세요&amp;nbsp;이번 포스팅에서는 Amazon AWS 에서 새롭게 출시한 Java의 MySQL 접속 드라이버인 AWS JDBC Driver 에 대해서 확인 해보려고 합니다.&amp;nbsp;드라이버의 특징AWS JDBC Driver 는 MySQL 8.0.28 community driver 를 &quot; data-og-host=&quot;hoing.io&quot; data-og-source-url=&quot;https://hoing.io/archives/1285&quot; data-og-url=&quot;https://hoing.io/archives/1285&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cOr1a5/hyPuGi6QrN/po5oQraCyU8ICJ27RlvnNk/img.png?width=512&amp;amp;height=183&amp;amp;face=0_0_512_183,https://scrap.kakaocdn.net/dn/wCpf5/hyPsOpuD6v/jzbOSrJrAWqg4acNAd35KK/img.png?width=512&amp;amp;height=183&amp;amp;face=0_0_512_183,https://scrap.kakaocdn.net/dn/epPQKG/hyPuGQXw1m/Vs3Z3jnxvaL1Zptnea8Smk/img.png?width=1250&amp;amp;height=864&amp;amp;face=0_0_1250_864&quot;&gt;&lt;a href=&quot;https://hoing.io/archives/1285&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://hoing.io/archives/1285&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cOr1a5/hyPuGi6QrN/po5oQraCyU8ICJ27RlvnNk/img.png?width=512&amp;amp;height=183&amp;amp;face=0_0_512_183,https://scrap.kakaocdn.net/dn/wCpf5/hyPsOpuD6v/jzbOSrJrAWqg4acNAd35KK/img.png?width=512&amp;amp;height=183&amp;amp;face=0_0_512_183,https://scrap.kakaocdn.net/dn/epPQKG/hyPuGQXw1m/Vs3Z3jnxvaL1Zptnea8Smk/img.png?width=1250&amp;amp;height=864&amp;amp;face=0_0_1250_864');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;AWS JDBC Driver for Amazon Aurora MySQL&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;안녕하세요&amp;nbsp;이번 포스팅에서는 Amazon AWS 에서 새롭게 출시한 Java의 MySQL 접속 드라이버인 AWS JDBC Driver 에 대해서 확인 해보려고 합니다.&amp;nbsp;드라이버의 특징AWS JDBC Driver 는 MySQL 8.0.28 community driver 를&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;hoing.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>인프라/Amazon AWS</category>
      <author>훈훈훈</author>
      <guid isPermaLink="true">https://wave1994.tistory.com/190</guid>
      <comments>https://wave1994.tistory.com/190#entry190comment</comments>
      <pubDate>Thu, 18 Aug 2022 00:45:03 +0900</pubDate>
    </item>
    <item>
      <title>Spring boot  ::  Transaction default isolation level 은 어떻게 결정이 될까</title>
      <link>https://wave1994.tistory.com/188</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Introduction&lt;/span&gt;&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/Meet-Coder-Study/posting-review&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;블로그 포스팅&lt;/a&gt;&amp;nbsp;스터디에서 스터디원과 Spring default isolation level 이 어떻게 결정 되는지 &lt;a href=&quot;https://github.com/Meet-Coder-Study/posting-review/pull/958#discussion_r824650014&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;토론&lt;/a&gt;을 하게 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;토론 도중에 스터디원분이 관련 &lt;a href=&quot;https://stackoverflow.com/questions/58472144/why-mysql-jdbc-driver-returns-transaction-read-committed-as-default-isolation-le&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;StackOverFlow&lt;/a&gt; 글을 찾고 공유해주셨는데, 글 내용은 &lt;span&gt;MySQL connectror/J 는 default isolation level 로 READ_COMMITTED 를 제공한다는 것이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제로 코드 레벨에서 살펴보니 진짜 READ_COMMITTED 로 제공이 되고 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1254&quot; data-origin-height=&quot;284&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cf3sua/btrvPgyxRAP/eanCYKwcmC3gH3T4kYJD61/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cf3sua/btrvPgyxRAP/eanCYKwcmC3gH3T4kYJD61/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cf3sua/btrvPgyxRAP/eanCYKwcmC3gH3T4kYJD61/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcf3sua%2FbtrvPgyxRAP%2FeanCYKwcmC3gH3T4kYJD61%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1254&quot; height=&quot;284&quot; data-origin-width=&quot;1254&quot; data-origin-height=&quot;284&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내가 알던 내용은 MySQL 은 storage engine 으로 innoDB 를 사용하는 경우 default isolation level 로 REPEATABLE_READ 를 제공한다는 것이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇기 때문에 MySQL connector/J 도 당연히 REPEATABLE_READ 로 제공이 될 줄 알았는데 결과는 꽤나 놀라웠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그와 반대로 MariaDB connector 는 REPEATABLE_READ 로 제공한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;904&quot; data-origin-height=&quot;280&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c39GD9/btrvSfeBTeG/lJcj1oYS3PZt8OlXyKWOfK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c39GD9/btrvSfeBTeG/lJcj1oYS3PZt8OlXyKWOfK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c39GD9/btrvSfeBTeG/lJcj1oYS3PZt8OlXyKWOfK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc39GD9%2FbtrvSfeBTeG%2FlJcj1oYS3PZt8OlXyKWOfK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;904&quot; height=&quot;280&quot; data-origin-width=&quot;904&quot; data-origin-height=&quot;280&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;테스트로 스프링 애플리케이션에서 jdbc url 을 MySQL 로 연결하였을 때 isolcation level 값을 확인해보았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 결과 REPEATABLE_READ 가 나왔다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;왜 이런 결과가 나오는지 궁금하여 스프링 코드를 따라가 보면서 어떻게 default isolation level 을 결정하는지 찾아보게 되었고, 그 과정을 정리해보려고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결론부터 먼저 말하자면 스프링에서 MySQL 을 사용하는 경우 REPEATABLE_READ 로 제공이 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(실제로는 RDBMS 와 커넥션을 맺고 정보를 가져옴)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 계기로 어설프게 대충 판단을 하면 안되는 것을 다시 한 번 느꼈다.  &lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;@Transactional test&lt;/span&gt;&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일단 스프링에서 어떻게 동작하는지 아래 코드로 테스트 해보았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(해당 코드는 예전에 예제로 만든 프로젝트이며, 전체 코드는 &lt;a href=&quot;https://github.com/Wave1994-Hoon/spring-boot-base-project&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Github&lt;/a&gt; 에서 확인할 수 있다.)&lt;/p&gt;
&lt;pre id=&quot;code_1647154572768&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Service
@RequiredArgsConstructor
public class AccountService {
  private final AccountRepository accountRepository;
  private final PasswordEncoder passwordEncoder;
  private final DataSource dataSource;

  @Transactional
  public AccountDto createAccount(@Valid final SignUpDto accountDto) throws SQLException {
    final Account existAccount = accountRepository.findAccountByEmail(accountDto.getEmail()).orElse(null);
    if (existAccount != null) {
      throw new IllegalArgumentException(&quot;duplicated&quot;);
    }

    final Account account = Account.builder()
        .name(accountDto.getName())
        .email(accountDto.getEmail())
        .password(passwordEncoder.encode(accountDto.getPassword()))
        .build();

    System.out.println(&quot;------------------------------------&quot;);
    System.out.println(dataSource.getConnection().getTransactionIsolation());
    System.out.println(&quot;------------------------------------&quot;);
    
    return AccountDto.of(accountRepository.save(account));
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;간단하게 @Transactional 이 선언 된 메소드에서 transaction isolataion level 을 조회해보았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1214&quot; data-origin-height=&quot;150&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cHZgTa/btrvOhkcpJq/k2t6S1gqTjGJZabks9Ko20/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cHZgTa/btrvOhkcpJq/k2t6S1gqTjGJZabks9Ko20/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cHZgTa/btrvOhkcpJq/k2t6S1gqTjGJZabks9Ko20/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcHZgTa%2FbtrvOhkcpJq%2Fk2t6S1gqTjGJZabks9Ko20%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1214&quot; height=&quot;150&quot; data-origin-width=&quot;1214&quot; data-origin-height=&quot;150&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;isolation level = 4로 나오고 해당 번호는 REPEATABLE_READ 를 뜻한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 값이 의미하는 바는 &lt;a href=&quot;https://docs.oracle.com/javase/7/docs/api/java/sql/Connection.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Connection interface&lt;/a&gt; 에서 확인할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1378&quot; data-origin-height=&quot;1032&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/GixPw/btrvS6Ivibk/fRkUIfj4dc6KRCQPXXMY20/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/GixPw/btrvS6Ivibk/fRkUIfj4dc6KRCQPXXMY20/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/GixPw/btrvS6Ivibk/fRkUIfj4dc6KRCQPXXMY20/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FGixPw%2FbtrvS6Ivibk%2FfRkUIfj4dc6KRCQPXXMY20%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1378&quot; height=&quot;1032&quot; data-origin-width=&quot;1378&quot; data-origin-height=&quot;1032&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Code Analysis&lt;/span&gt;&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 스프링 코드를 디버깅하면서 어떻게 동작하는지 살펴보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Spring 애플리케이션을 실행시키면 NonRegisteringDriver 클래스의 connect 메소드는 jdbc url 을 파라미터로 전달 받은 후에 Connection 객체를 생성 후에 리턴을 하게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2438&quot; data-origin-height=&quot;1148&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dc3mpN/btrvKpJxXdx/wgRwrolG77ahR1Uky56AN1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dc3mpN/btrvKpJxXdx/wgRwrolG77ahR1Uky56AN1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dc3mpN/btrvKpJxXdx/wgRwrolG77ahR1Uky56AN1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdc3mpN%2FbtrvKpJxXdx%2FwgRwrolG77ahR1Uky56AN1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2438&quot; height=&quot;1148&quot; data-origin-width=&quot;2438&quot; data-origin-height=&quot;1148&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;생성이 되는 Connection 객체는 com.mysql.cj.jdbc.ConnectionImpl 클래스이며 해당 객체가 생성이 되면서 isolation level 도 결정한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이제 ConnectionImpl 객체가 생성 되는 과정을 살펴보자&amp;nbsp;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ConnectionImpl 클래스의 getInstance 메소드는 정말 단순하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 클래스의 생성자를 호출해서 리턴하는 역할만 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1900&quot; data-origin-height=&quot;310&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bu6Aet/btrvK4ylxyh/f7NIzauei1YhxON0yz0En1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bu6Aet/btrvK4ylxyh/f7NIzauei1YhxON0yz0En1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bu6Aet/btrvK4ylxyh/f7NIzauei1YhxON0yz0En1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbu6Aet%2FbtrvK4ylxyh%2Ff7NIzauei1YhxON0yz0En1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1900&quot; height=&quot;310&quot; data-origin-width=&quot;1900&quot; data-origin-height=&quot;310&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 다음으로 ConnectionImpl 클래스 생성자에는 여러 로직들이 들어있어 다소 코드 라인이 좀 길다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 전체 코드는 첨부하지 않고 부분 부분 캡쳐해서 넣었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 살펴볼 내용은 위에 서론에서 언급한 내용이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1582&quot; data-origin-height=&quot;580&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cOVx5f/btrvPKez9XL/tdvaHerJKaQZDkuuOkxivK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cOVx5f/btrvPKez9XL/tdvaHerJKaQZDkuuOkxivK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cOVx5f/btrvPKez9XL/tdvaHerJKaQZDkuuOkxivK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcOVx5f%2FbtrvPKez9XL%2FtdvaHerJKaQZDkuuOkxivK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1582&quot; height=&quot;580&quot; data-origin-width=&quot;1582&quot; data-origin-height=&quot;580&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 라인에서 MySQL Connector/J 에서 정의한 메타 데이터를 가져오게 되는데, 여기에서는 코드에서 정의한 default isolation level 로 초기화가 된다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 라인을 실행시켰을 때 디버깅한 값을 확인해보면 isolation level = 2(READ_COMMITTED) 로 세팅된 것을 볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1290&quot; data-origin-height=&quot;786&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bp7dnQ/btrvSfyV5LY/fLsvv8upmKKUa0V4C1TIx0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bp7dnQ/btrvSfyV5LY/fLsvv8upmKKUa0V4C1TIx0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bp7dnQ/btrvSfyV5LY/fLsvv8upmKKUa0V4C1TIx0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbp7dnQ%2FbtrvSfyV5LY%2FfLsvv8upmKKUa0V4C1TIx0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1290&quot; height=&quot;786&quot; data-origin-width=&quot;1290&quot; data-origin-height=&quot;786&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 다음에는 몇 라인 아래에 있는 createNewIO 메서드를 확인해보자&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 메서드 호출 후에 isolation level = 4 (REPEATABLE_READ) 로 세팅이 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1670&quot; data-origin-height=&quot;784&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dBhpQN/btrvHi5wPYY/JljkYhULaxDEnwVR8Z6hWK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dBhpQN/btrvHi5wPYY/JljkYhULaxDEnwVR8Z6hWK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dBhpQN/btrvHi5wPYY/JljkYhULaxDEnwVR8Z6hWK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdBhpQN%2FbtrvHi5wPYY%2FJljkYhULaxDEnwVR8Z6hWK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1670&quot; height=&quot;784&quot; data-origin-width=&quot;1670&quot; data-origin-height=&quot;784&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;createNewIO 메소드르 이후 아래 흐름으로 메소드 Call 이 발생한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1190&quot; data-origin-height=&quot;864&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Alg4S/btrvNGR66r2/Lm54F23WTvpc8X8LTHKZbk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Alg4S/btrvNGR66r2/Lm54F23WTvpc8X8LTHKZbk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Alg4S/btrvNGR66r2/Lm54F23WTvpc8X8LTHKZbk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FAlg4S%2FbtrvNGR66r2%2FLm54F23WTvpc8X8LTHKZbk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;867&quot; height=&quot;630&quot; data-origin-width=&quot;1190&quot; data-origin-height=&quot;864&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막으로 checkTransactionIsolationLevel 메소드에서 MySQL 서버와 커넥션을 맺고 isolataion level 을 가져오게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1744&quot; data-origin-height=&quot;614&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dnghk0/btrvNHKeltU/rfFmJ3coTcfBIV4uwa5cU0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dnghk0/btrvNHKeltU/rfFmJ3coTcfBIV4uwa5cU0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dnghk0/btrvNHKeltU/rfFmJ3coTcfBIV4uwa5cU0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdnghk0%2FbtrvNHKeltU%2FrfFmJ3coTcfBIV4uwa5cU0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1744&quot; height=&quot;614&quot; data-origin-width=&quot;1744&quot; data-origin-height=&quot;614&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후 처리하는 과정을 더 있는 것 같지만, 목적은 이미 달성했기에 이정도에서 마무리하려고 한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Spring Framework/개념</category>
      <category>isoltation</category>
      <category>Java</category>
      <category>MySQL</category>
      <category>Spring</category>
      <author>훈훈훈</author>
      <guid isPermaLink="true">https://wave1994.tistory.com/188</guid>
      <comments>https://wave1994.tistory.com/188#entry188comment</comments>
      <pubDate>Sun, 13 Mar 2022 17:18:43 +0900</pubDate>
    </item>
    <item>
      <title>Spring Data :: Spring Data Elasticsearch refresh policy 정리</title>
      <link>https://wave1994.tistory.com/187</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이슈&lt;/span&gt;&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;얼마전에 검색 API 를 호출하면 데이터가 종종 빈 값이 반환되는 이슈가 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이슈가 발생하는 시점은 Indexer 어플리케이션에서 Elasticsearch 인덱스에 Bulk Insert 할 때 였다.&amp;nbsp;해당 시점에 데이터를 조회 하였을 때, 데이터가 비어있는 현상을 발견하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1589&quot; data-origin-height=&quot;921&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/JTMZ5/btrrPOlIMYR/S9Xv72CHWsc0WDNCDc6yxK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/JTMZ5/btrrPOlIMYR/S9Xv72CHWsc0WDNCDc6yxK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/JTMZ5/btrrPOlIMYR/S9Xv72CHWsc0WDNCDc6yxK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FJTMZ5%2FbtrrPOlIMYR%2FS9Xv72CHWsc0WDNCDc6yxK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;765&quot; height=&quot;921&quot; data-origin-width=&quot;1589&quot; data-origin-height=&quot;921&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이슈가 발생한 원인은 Elasticsearch 는 refresh interval 기능이 존재하기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.elastic.co/guide/en/elasticsearch/reference/7.16/near-real-time.html#img-pre-refresh&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;span style=&quot;color: #0593d3;&quot;&gt;Elasticsearch 공식문서&lt;/span&gt;&lt;/a&gt;를 확인해보면 아래와 같이 설명을 하고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1530&quot; data-origin-height=&quot;676&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/olYwR/btrr2sJMGnN/MTol7ik66UuLo5FZaJr83k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/olYwR/btrr2sJMGnN/MTol7ik66UuLo5FZaJr83k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/olYwR/btrr2sJMGnN/MTol7ik66UuLo5FZaJr83k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FolYwR%2Fbtrr2sJMGnN%2FMTol7ik66UuLo5FZaJr83k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1530&quot; height=&quot;676&quot; data-origin-width=&quot;1530&quot; data-origin-height=&quot;676&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 기능은 디폴트로 1s 마다 refresh interval 를 수행하게 되는데, 우리가 Elasticsearch 에서 조회하는 내용은 1s 이전 데이터를 의미한다. 엄밀하게는 지난 30s 동안 한 번 이상의 검색 요청을 받은 인덱스에 대해서만 refresh 가 진행이 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;추가로 refresh 가 어떤식으로 진행이 되는지 좀 더 자세히 살펴보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 내용은 &lt;span style=&quot;color: #0593d3;&quot;&gt;&lt;a style=&quot;color: #0593d3;&quot; href=&quot;https://qbox.io/blog/refresh-flush-operations-elasticsearch-guide/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;span style=&quot;color: #0593d3;&quot;&gt;QBOX 의 글&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;을 참고하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1688&quot; data-origin-height=&quot;1086&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dWhBaG/btrr16m863A/yPFPJ7gpCXvz9DukaqrZkk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dWhBaG/btrr16m863A/yPFPJ7gpCXvz9DukaqrZkk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dWhBaG/btrr16m863A/yPFPJ7gpCXvz9DukaqrZkk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdWhBaG%2Fbtrr16m863A%2FyPFPJ7gpCXvz9DukaqrZkk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1688&quot; height=&quot;1086&quot; data-origin-width=&quot;1688&quot; data-origin-height=&quot;1086&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터가 색인이 되면 샤드 내에 있는 Translog (트랜잭션 로그) 와 인메모리 버퍼에 저장이 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 1s 마다 (디폴트) 주기적으로 인덱스를 refresh 를 할 때, 인메모리 버퍼에 있는 데이터를 새로 생성된 세그먼트에 복사를 하며 그 이후 refresh 한 데이터를 검색할 수 있게 된다고 한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Elasticsearch 에서는 refresh 를 컨트롤하기 위해 몇가지 옵션을 제공해주는데 기능적으로 분류를 해보면 아래와 같다.&lt;/p&gt;
&lt;pre id=&quot;code_1643386738000&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;(1) 디폴트 옵션 (false)
(2) 즉시 새로고침 (true)
(3) 새로고침 될 때까지 기다림 (wait_for)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실시간을 어느정도 보장받기 원한다면, (2) 번 혹은 (3) 번을 선택하는 방법이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉시 true(새로고침을 하는 옵션) 은 매 요청마다 즉시 즉시 새로고침을 하기때문에 가장 높은 실시간을 보여줄 수 있겠지만, 매번 refresh 를 즉시 진행해버리기 때문에 높은 리소스를 많이 사용하게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반면에 wait_for 옵션은 새로고침이 될 때까지 기다리고 응답을 보내주는 정책이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 정책도 높은 실시간을 보여주지만, 즉시 새로고침에 비하면 대기 시간이 길다. 그래도 즉시 refresh 하는 것 보다는 낮은 리소스를 소비한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각각 옵션의 장단점이 있지만, 색인 속도를 포기하고 리소스를 적게 소모하는 wait_for 이 많이 사용되는 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 옵션을 비교해보고 사내에서 Bulk 작업을 수행할 때, wait_for 옵션을 지정하는 것으로 정책을 잡았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;해결 방법&lt;/span&gt;&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 이슈를 해결하기 위해 데이터를 색인할 때 RefreshPolicy 를 wait_for 옵션으로 사용하기로 하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스프링에서는 RefreshPolicy 이름을 약간 다르게 정의한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 Spring data elasticsearch 에서 제공하는 RefreshPolicy Enum 을 살펴보자.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1072&quot; data-origin-height=&quot;308&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cfxbk7/btrrKIfGcW5/gn16yT2NJUf2K5SmkGTNGk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cfxbk7/btrrKIfGcW5/gn16yT2NJUf2K5SmkGTNGk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cfxbk7/btrrKIfGcW5/gn16yT2NJUf2K5SmkGTNGk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcfxbk7%2FbtrrKIfGcW5%2Fgn16yT2NJUf2K5SmkGTNGk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;860&quot; height=&quot;308&quot; data-origin-width=&quot;1072&quot; data-origin-height=&quot;308&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이름만 보고 추론을 해보면 대충 알 것 같지만, 정확하지는 않기에 좀 더 찾아보니 org.elasticsearch.action.support 패키지에 있는 WriteRequest 인터페이스 내부에 RefreshPolicy EnumType 이 아래와 같이 동일하게 정의 되어 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1554&quot; data-origin-height=&quot;490&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bWdS6Q/btrrOdsJGmA/wC29LF2ej8yhBfCDvbsenk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bWdS6Q/btrrOdsJGmA/wC29LF2ej8yhBfCDvbsenk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bWdS6Q/btrrOdsJGmA/wC29LF2ej8yhBfCDvbsenk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbWdS6Q%2FbtrrOdsJGmA%2FwC29LF2ej8yhBfCDvbsenk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1554&quot; height=&quot;490&quot; data-origin-width=&quot;1554&quot; data-origin-height=&quot;490&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드와 주석을 보면 Spring data elasticsearch 에서 정의한 RefreshPolicy Enum 과 이름이 동일한 것을 알 수 있고, 각각 옵션에 대한 설명이 적혀있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 왜 동일한 Enum 을 새로 만들어서 사용하는지 의문이 들었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;유추해보면 Spring data elasticsearch 에서 org.elasticsearch.action.support 패키지에 있는 Enum 을 사용하려고 했지만, WriteRequest 인터페이스 내부에 있는 Enum 이기 때문에 RefreshPolicy Enum 을 가져다가 사용한다면 WriteRequest 인터페이스에 강하게 결합이 되어 ?? 새로 생성을 해서 사용하는 것인가 싶다. 같은 예시로 OpType Enum이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다시 돌아와서 코드와 주석을 읽어 보면 각각의 타입에 대한 설명은 주석에 잘 명시되어 있다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 글에서는 문제가 된 이슈를 해결하기 위해 wait_for 사용하려고 하기 때문에 RefreshPolicy 를 WAIT_UNTIL 으로 세팅하려고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://docs.spring.io/spring-data/elasticsearch/docs/current/reference/html/#reference&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;span style=&quot;color: #0593d3;&quot;&gt;공식 문서&lt;/span&gt;&lt;/a&gt;를 참고하여 코드로 작성하면 아래와 같이 할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1643209445903&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Configuration
@EnableElasticsearchRepositories(basePackages = &quot;com.hooon.dataes.repository&quot;)
@ComponentScan(basePackages = { &quot;com.hooon.dataes.service&quot; })
public class ElasticSearchConfig {

  @Bean
  public RestHighLevelClient client() {
    ClientConfiguration clientConfiguration = ClientConfiguration.builder()
        .connectedTo(&quot;localhost:9200&quot;)
        .build();

    return RestClients.create(clientConfiguration).rest();
  }

  @Bean
  public ElasticsearchOperations elasticsearchTemplate() {
    ElasticsearchRestTemplate elasticsearchRestTemplate = new ElasticsearchRestTemplate(client());
    elasticsearchRestTemplate.setRefreshPolicy(RefreshPolicy.WAIT_UNTIL); // RefreshPolicy 설정

    return elasticsearchRestTemplate;
  }

}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Spring bean 으로 ElasticsearchOperations 을 등록할 때 refreshPolicy 를 설정할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스프링 공식문서를 읽어보면 refresh policy 값은 반드시 설정하라고 말하고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;img style=&quot;text-align: center; caret-color: transparent; font-family: -apple-system, BlinkMacSystemFont, AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; letter-spacing: 0px;&quot; src=&quot;https://blog.kakaocdn.net/dn/ci7b4m/btrr1iVvbpC/C8c10AA9NI7OnQ65KWaakK/img.png&quot; data-image-src=&quot;https://blog.kakaocdn.net/dn/ci7b4m/btrr1iVvbpC/C8c10AA9NI7OnQ65KWaakK/img.png&quot; data-origin-width=&quot;1674&quot; data-origin-height=&quot;212&quot; /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(앞으로는 공식 문서를 좀 더 꼼꼼히 읽어봐야겠다..)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;# reference&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://qbox.io/blog/refresh-flush-operations-elasticsearch-guide/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://qbox.io/blog/refresh-flush-operations-elasticsearch-guide/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1643386086688&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Guide to Refresh and Flush Operations in Elasticsearch | Qbox HES&quot; data-og-description=&quot;Not yet enjoying the benefits of a hosted ELK-stack enterprise search on Qbox? Discover how easy it is to manage and scale your Elasticsearch environment.&quot; data-og-host=&quot;qbox.io&quot; data-og-source-url=&quot;https://qbox.io/blog/refresh-flush-operations-elasticsearch-guide/&quot; data-og-url=&quot;https://qbox.io/blog/refresh-flush-operations-elasticsearch-guide/&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://qbox.io/blog/refresh-flush-operations-elasticsearch-guide/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://qbox.io/blog/refresh-flush-operations-elasticsearch-guide/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Guide to Refresh and Flush Operations in Elasticsearch | Qbox HES&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Not yet enjoying the benefits of a hosted ELK-stack enterprise search on Qbox? Discover how easy it is to manage and scale your Elasticsearch environment.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;qbox.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.codetd.com/en/article/6497511&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://www.codetd.com/en/article/6497511&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1643386098360&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Elasticsearch data refresh policy brief RefreshPolicy - Code World&quot; data-og-description=&quot;Disclaimer: This article is a blogger hanchao5272 original articles, please indicate the source and leave the original link address, thank you! https://blog.csdn.net/hanchao5272/article/details/89151166 Explanation By default, ElasticSearchthe index refres&quot; data-og-host=&quot;www.codetd.com&quot; data-og-source-url=&quot;https://www.codetd.com/en/article/6497511&quot; data-og-url=&quot;https://www.codetd.com/en/article/6497511&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://www.codetd.com/en/article/6497511&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.codetd.com/en/article/6497511&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Elasticsearch data refresh policy brief RefreshPolicy - Code World&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Disclaimer: This article is a blogger hanchao5272 original articles, please indicate the source and leave the original link address, thank you! https://blog.csdn.net/hanchao5272/article/details/89151166 Explanation By default, ElasticSearchthe index refres&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.codetd.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.elastic.co/guide/en/elasticsearch/reference/7.16/near-real-time.html#img-pre-refresh&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://www.elastic.co/guide/en/elasticsearch/reference/7.16/near-real-time.html#img-pre-refresh&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1643555547507&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Near real-time search | Elasticsearch Guide [7.16] | Elastic&quot; data-og-description=&quot;The overview of documents and indices indicates that when a document is stored in Elasticsearch, it is indexed and fully searchable in near real-time--within 1 second. What defines near real-time search? Lucene, the Java libraries on which Elasticsearch is&quot; data-og-host=&quot;www.elastic.co&quot; data-og-source-url=&quot;https://www.elastic.co/guide/en/elasticsearch/reference/7.16/near-real-time.html#img-pre-refresh&quot; data-og-url=&quot;https://www.elastic.co/guide/en/elasticsearch/reference/7.16/near-real-time.html#img-pre-refresh&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://www.elastic.co/guide/en/elasticsearch/reference/7.16/near-real-time.html#img-pre-refresh&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.elastic.co/guide/en/elasticsearch/reference/7.16/near-real-time.html#img-pre-refresh&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Near real-time search | Elasticsearch Guide [7.16] | Elastic&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;The overview of documents and indices indicates that when a document is stored in Elasticsearch, it is indexed and fully searchable in near real-time--within 1 second. What defines near real-time search? Lucene, the Java libraries on which Elasticsearch is&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.elastic.co&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.elastic.co/guide/en/elasticsearch/reference/7.16/docs-refresh.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://www.elastic.co/guide/en/elasticsearch/reference/7.16/docs-refresh.html&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1643386107482&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;?refresh | Elasticsearch Guide [7.16] | Elastic&quot; data-og-description=&quot;The Index, Update, Delete, and Bulk APIs support setting refresh to control when changes made by this request are made visible to search. These are the allowed values: Empty string or true Refresh the relevant primary and replica shards (not the whole inde&quot; data-og-host=&quot;www.elastic.co&quot; data-og-source-url=&quot;https://www.elastic.co/guide/en/elasticsearch/reference/7.16/docs-refresh.html&quot; data-og-url=&quot;https://www.elastic.co/guide/en/elasticsearch/reference/7.16/docs-refresh.html&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://www.elastic.co/guide/en/elasticsearch/reference/7.16/docs-refresh.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.elastic.co/guide/en/elasticsearch/reference/7.16/docs-refresh.html&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;?refresh | Elasticsearch Guide [7.16] | Elastic&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;The Index, Update, Delete, and Bulk APIs support setting refresh to control when changes made by this request are made visible to search. These are the allowed values: Empty string or true Refresh the relevant primary and replica shards (not the whole inde&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.elastic.co&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://wedul.site/542&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://wedul.site/542&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1643387006668&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;Elasticsearch에서 refresh 정리&quot; data-og-description=&quot;Elasticsearch에서 document를 업데이트하고 바로 해당 정보를 조회하려고 했다. 하지만 조회가 되지 않았다. 분명이 업데이트가 종료된 것을 확인 했는데 왜 그런지 의문이 들었다. 그래서 찾아봤는&quot; data-og-host=&quot;wedul.site&quot; data-og-source-url=&quot;https://wedul.site/542&quot; data-og-url=&quot;https://wedul.site/542&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/wXx7J/hyNeJbIN4T/mz4hrDnrmwxrnLcKmefVp0/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/sceWi/hyNeM0ye04/WAOipX9oYlfk9M7oK9e4Vk/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/dkgqH2/hyNeA6T4UU/fmFNXzekd7JRm4KnAZBtBk/img.png?width=773&amp;amp;height=775&amp;amp;face=29_254_62_290&quot;&gt;&lt;a href=&quot;https://wedul.site/542&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://wedul.site/542&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/wXx7J/hyNeJbIN4T/mz4hrDnrmwxrnLcKmefVp0/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/sceWi/hyNeM0ye04/WAOipX9oYlfk9M7oK9e4Vk/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/dkgqH2/hyNeA6T4UU/fmFNXzekd7JRm4KnAZBtBk/img.png?width=773&amp;amp;height=775&amp;amp;face=29_254_62_290');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Elasticsearch에서 refresh 정리&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Elasticsearch에서 document를 업데이트하고 바로 해당 정보를 조회하려고 했다. 하지만 조회가 되지 않았다. 분명이 업데이트가 종료된 것을 확인 했는데 왜 그런지 의문이 들었다. 그래서 찾아봤는&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;wedul.site&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://docs.spring.io/spring-data/elasticsearch/docs/current/reference/html/#reference&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://docs.spring.io/spring-data/elasticsearch/docs/current/reference/html/#reference&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1643554724198&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Spring Data Elasticsearch - Reference Documentation&quot; data-og-description=&quot;The Spring Data infrastructure provides hooks for modifying an entity before and after certain methods are invoked. Those so called EntityCallback instances provide a convenient way to check and potentially modify an entity in a callback fashioned style. A&quot; data-og-host=&quot;docs.spring.io&quot; data-og-source-url=&quot;https://docs.spring.io/spring-data/elasticsearch/docs/current/reference/html/#reference&quot; data-og-url=&quot;https://docs.spring.io/spring-data/elasticsearch/docs/current/reference/html/#reference&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://docs.spring.io/spring-data/elasticsearch/docs/current/reference/html/#reference&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://docs.spring.io/spring-data/elasticsearch/docs/current/reference/html/#reference&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Spring Data Elasticsearch - Reference Documentation&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;The Spring Data infrastructure provides hooks for modifying an entity before and after certain methods are invoked. Those so called EntityCallback instances provide a convenient way to check and potentially modify an entity in a callback fashioned style. A&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;docs.spring.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Spring Framework/개념</category>
      <category>elasticsearch</category>
      <category>Java</category>
      <category>Spring</category>
      <author>훈훈훈</author>
      <guid isPermaLink="true">https://wave1994.tistory.com/187</guid>
      <comments>https://wave1994.tistory.com/187#entry187comment</comments>
      <pubDate>Mon, 31 Jan 2022 00:09:35 +0900</pubDate>
    </item>
    <item>
      <title>Spring Data  ::  Spring Data Elasticsearch  _class 필드 자동 생성 비활성화</title>
      <link>https://wave1994.tistory.com/185</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;이슈&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Spring data elasticsearch 를 사용하여 색인을 하면 아래와 같이 _class 라는 필드가 자동 생성이 되는 것을 볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1410&quot; data-origin-height=&quot;992&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cswUIo/btrl1YuSNkV/S3dM6OKvpyQyyiC90xKvr1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cswUIo/btrl1YuSNkV/S3dM6OKvpyQyyiC90xKvr1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cswUIo/btrl1YuSNkV/S3dM6OKvpyQyyiC90xKvr1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcswUIo%2Fbtrl1YuSNkV%2FS3dM6OKvpyQyyiC90xKvr1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1410&quot; height=&quot;992&quot; data-origin-width=&quot;1410&quot; data-origin-height=&quot;992&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 필드는 sava 메소드를 호출할 때 사용했던 도큐먼트 객체에 대한 정보를 담고 있지만, Elasticsearch 를 호출하는 클라이언트 입장에서는 불필요한 데이터이다.&amp;nbsp; 따라서 굳이 저 필드를 생성할 필요는 없다고 생각한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;_class 필드가 존재하는 목적이 궁금하여 스택오버플로우를 검색해보니, spring data elasticsearch 프로젝트를 리드하시는 분의 &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;a style=&quot;color: #006dd7;&quot; href=&quot;https://stackoverflow.com/questions/65126545/reading-from-elastic-search-using-spring-boot-where-es-record-without-class-at&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;답변&lt;/a&gt;&lt;/span&gt;을 찾을 수 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1476&quot; data-origin-height=&quot;250&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/xkmd8/btrlZNtyIHf/wmBmb5MmoQp0emvoSWSkHk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/xkmd8/btrlZNtyIHf/wmBmb5MmoQp0emvoSWSkHk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/xkmd8/btrlZNtyIHf/wmBmb5MmoQp0emvoSWSkHk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fxkmd8%2FbtrlZNtyIHf%2FwmBmb5MmoQp0emvoSWSkHk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1476&quot; height=&quot;250&quot; data-origin-width=&quot;1476&quot; data-origin-height=&quot;250&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;100% 이해가 되는 문장은 아니지만 상속 관련 된 객체를 사용할 때 사용하는 필드라고 한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면 상속을 하지 않는 도큐먼트 객체들에 대해서는 해당 필드는 사용하지 않기 떄문에 제거해도 된다고 결론을 낼 수 있을 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(정확하게 어떤 기능을 하는지는 이해가 되지는 않지만, Spring data elasticseach 에서 사용하는 메타데이터인 것 같다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;해결 방법&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 필드가 자동 생성되는 것을 막기 위해서 &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;a style=&quot;color: #006dd7;&quot; href=&quot;https://github.com/spring-projects/spring-data-elasticsearch/issues/1883&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Spring data elasticseach Github 이슈&lt;/a&gt;&lt;/span&gt;를 확인해보니 spring data elasticsearch 4.3 버전부터 서포트 하는 것을 알 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1637857295840&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Getter
@ToString
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Document(indexName = &quot;user&quot;, createIndex = false, writeTypeHint = WriteTypeHint.FALSE)
public class UserDocument {

  @Id
  private String id;

  @Field(type = FieldType.Text)
  private String name;

  public void setName(String name) {
    this.name = name;
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;@Document 어노테이션에 writeTypeHint 을 False 로 설정하면, 색인할 때 _class 필드 자동 생성을 비활성화하는 것을 볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1278&quot; data-origin-height=&quot;868&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/biY4Fr/btrmjIcCl4V/FhdsiCVCfK35g7XzIhqKBK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/biY4Fr/btrmjIcCl4V/FhdsiCVCfK35g7XzIhqKBK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/biY4Fr/btrmjIcCl4V/FhdsiCVCfK35g7XzIhqKBK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbiY4Fr%2FbtrmjIcCl4V%2FFhdsiCVCfK35g7XzIhqKBK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1278&quot; height=&quot;868&quot; data-origin-width=&quot;1278&quot; data-origin-height=&quot;868&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;** 참고&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/spring-projects/spring-data-elasticsearch/issues/1883&quot;&gt;https://github.com/spring-projects/spring-data-elasticsearch/issues/1883&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1637857315516&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;How to remove the _class field when automatically creating index and how to specify what fields we want return in query? &amp;middot; Issu&quot; data-og-description=&quot;There are two questions: A field named _class will be inserted automatically when spring-data-elasticsearch creates an index, even though it is not desired. I wonder how to avoid that. *Repository ...&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/spring-projects/spring-data-elasticsearch/issues/1883&quot; data-og-url=&quot;https://github.com/spring-projects/spring-data-elasticsearch/issues/1883&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/yeKuX/hyMuJipbjX/uKzkUpkDGsqyUwkoVh5D2k/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/spring-projects/spring-data-elasticsearch/issues/1883&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/spring-projects/spring-data-elasticsearch/issues/1883&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/yeKuX/hyMuJipbjX/uKzkUpkDGsqyUwkoVh5D2k/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;How to remove the _class field when automatically creating index and how to specify what fields we want return in query? &amp;middot; Issu&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;There are two questions: A field named _class will be inserted automatically when spring-data-elasticsearch creates an index, even though it is not desired. I wonder how to avoid that. *Repository ...&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://stackoverflow.com/questions/66062703/is-there-any-way-to-force-spring-not-to-use-create-class-field-in-the-mapping&quot;&gt;https://stackoverflow.com/questions/66062703/is-there-any-way-to-force-spring-not-to-use-create-class-field-in-the-mapping&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1637857337825&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Is there any way to force spring not to use/create '_class' field in the mapping?&quot; data-og-description=&quot;The thing is on production servers we got mapping for Elasticsearch with dynamic set to strict. Currently, we use a rest level client to communicate with Elastisearch, however, we would like to mig...&quot; data-og-host=&quot;stackoverflow.com&quot; data-og-source-url=&quot;https://stackoverflow.com/questions/66062703/is-there-any-way-to-force-spring-not-to-use-create-class-field-in-the-mapping&quot; data-og-url=&quot;https://stackoverflow.com/questions/66062703/is-there-any-way-to-force-spring-not-to-use-create-class-field-in-the-mapping&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bstkn2/hyMtJEj361/JjZCJi2JaybSbo86eEVWok/img.png?width=316&amp;amp;height=316&amp;amp;face=0_0_316_316&quot;&gt;&lt;a href=&quot;https://stackoverflow.com/questions/66062703/is-there-any-way-to-force-spring-not-to-use-create-class-field-in-the-mapping&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://stackoverflow.com/questions/66062703/is-there-any-way-to-force-spring-not-to-use-create-class-field-in-the-mapping&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bstkn2/hyMtJEj361/JjZCJi2JaybSbo86eEVWok/img.png?width=316&amp;amp;height=316&amp;amp;face=0_0_316_316');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Is there any way to force spring not to use/create '_class' field in the mapping?&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;The thing is on production servers we got mapping for Elasticsearch with dynamic set to strict. Currently, we use a rest level client to communicate with Elastisearch, however, we would like to mig...&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;stackoverflow.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://stackoverflow.com/questions/65126545/reading-from-elastic-search-using-spring-boot-where-es-record-without-class-at&quot;&gt;https://stackoverflow.com/questions/65126545/reading-from-elastic-search-using-spring-boot-where-es-record-without-class-at&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1637857344447&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Reading from elastic search using Spring boot, where ES record without _class attribute&quot; data-og-description=&quot;I am trying to read/search the existing records in Elastic Search via Spring Data + Elastic Search.The existing records in the elastic search does not have the _class attribute. { &amp;quot;_index&amp;amp;qu...&quot; data-og-host=&quot;stackoverflow.com&quot; data-og-source-url=&quot;https://stackoverflow.com/questions/65126545/reading-from-elastic-search-using-spring-boot-where-es-record-without-class-at&quot; data-og-url=&quot;https://stackoverflow.com/questions/65126545/reading-from-elastic-search-using-spring-boot-where-es-record-without-class-at&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bVXX67/hyMtDREwp3/1UOYNyXgRHL77eyc1fKPC1/img.png?width=316&amp;amp;height=316&amp;amp;face=0_0_316_316&quot;&gt;&lt;a href=&quot;https://stackoverflow.com/questions/65126545/reading-from-elastic-search-using-spring-boot-where-es-record-without-class-at&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://stackoverflow.com/questions/65126545/reading-from-elastic-search-using-spring-boot-where-es-record-without-class-at&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bVXX67/hyMtDREwp3/1UOYNyXgRHL77eyc1fKPC1/img.png?width=316&amp;amp;height=316&amp;amp;face=0_0_316_316');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Reading from elastic search using Spring boot, where ES record without _class attribute&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;I am trying to read/search the existing records in Elastic Search via Spring Data + Elastic Search.The existing records in the elastic search does not have the _class attribute. { &quot;_index&amp;amp;qu...&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;stackoverflow.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Spring Framework/개념</category>
      <category>elasticsearch</category>
      <category>Java</category>
      <category>Spring</category>
      <author>훈훈훈</author>
      <guid isPermaLink="true">https://wave1994.tistory.com/185</guid>
      <comments>https://wave1994.tistory.com/185#entry185comment</comments>
      <pubDate>Sat, 27 Nov 2021 13:36:52 +0900</pubDate>
    </item>
    <item>
      <title>Spring Data  ::  Spring Data Elasticsearch rollover index 정리</title>
      <link>https://wave1994.tistory.com/184</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Introduction&lt;/span&gt;&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에 HighLevelRestClient 를 사용하여 운영하던 Indexer 어플리케이션에 Spring Data Elasticsearch 를 적용 해보게 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;새로운 라이브러리를 적용 후 Alias 를 사용한 Rollover Index 기능 구현은 프레임워크 버전에 따라 구현 방법이 조금씩 다른 것 같아 적절한 레퍼런스를 찾기가 좀 어려웠었다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다행히 스택오버플로우를 보다가 Spring Data Elasticsearch 프로젝트 리드하시는 분의 블로그에&amp;nbsp;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;a style=&quot;color: #006dd7;&quot; href=&quot;https://www.sothawo.com/2020/11/implement-a-rolling-index-strategy-with-spring-data-elasticsearch-4-1/&quot;&gt;좋은 글&lt;/a&gt;&lt;/span&gt;을 찾아 쉽게 해결할 수 있었고, 관련해서 내용을 정리해보려고 한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;에제 코드는 &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;a style=&quot;color: #006dd7;&quot; href=&quot;https://github.com/Wave1994-Hoon/spring-data-elasticsearch-example&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Github&lt;/a&gt;&lt;/span&gt; 에서 확인해볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Rollover Index&lt;/span&gt;&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2096&quot; data-origin-height=&quot;958&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/zYhRQ/btrl4ZlOWkj/mk67FwskZxeTltVwFZz9m1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/zYhRQ/btrl4ZlOWkj/mk67FwskZxeTltVwFZz9m1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/zYhRQ/btrl4ZlOWkj/mk67FwskZxeTltVwFZz9m1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FzYhRQ%2Fbtrl4ZlOWkj%2Fmk67FwskZxeTltVwFZz9m1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2096&quot; height=&quot;958&quot; data-origin-width=&quot;2096&quot; data-origin-height=&quot;958&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 그림을 보듯이 요청을 alias 이름으로 받고 데이터는 alias 와 mapping 되어 있는 index 를 통해 데이터를 요청하는 것을 볼 수 있다.&amp;nbsp; &amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 클라이언트는 ES 에 존재하는 여러 index 의 이름을 알 필요는 없고 alias 를 통해 데이터를 받아오며, 실질적으로 gateway 역할을 한다고 볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;batch 등을 통해 데이터를 가공하여 index 를 새로 생성한다면 시간 값 등을 index 이름 뒤에 붙여주는 방식으로 index 를 생성하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 Alias 를 매핑해주면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Spring Data Elasticsearch&amp;nbsp;&lt;/span&gt;&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Spring Data Elasitcsearch 는 4.0&lt;/b&gt; 버전으로 올라오면서 많은 부분들이 변한 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;눈에 띄는 것은 ElasticsearchOperations 인터페이스이다. 해당 인터페이스를 호출하여 Elasticsearch 에 요청을 간편하게 보낼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1416&quot; data-origin-height=&quot;622&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pXKny/btrlOVi04m8/32Z7W3f2nBOlA3f80mJ380/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pXKny/btrlOVi04m8/32Z7W3f2nBOlA3f80mJ380/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pXKny/btrlOVi04m8/32Z7W3f2nBOlA3f80mJ380/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpXKny%2FbtrlOVi04m8%2F32Z7W3f2nBOlA3f80mJ380%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1416&quot; height=&quot;622&quot; data-origin-width=&quot;1416&quot; data-origin-height=&quot;622&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이전에는 직접 생성한 쿼리를 HighLevelRestClient 를 사용하여 요청 보냈지만,&amp;nbsp; ElasticsearchOperations 은 이를 wrapping 하여 간편하게 사용할 수 있도록 도와준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 ElasticsearchOperations 인터페이스에서 사용하는 IndexOperations 인터페이스를 사용하면 인덱스 생성, 삭제, Alias 설정 등 다양한 기능을 지원해준다. 아래 이미지는 해당 인터페이스의 일부분만 갭쳐하였고, 실제로 확인해보면 더 많은 메소드들을 볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1908&quot; data-origin-height=&quot;924&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/oTqpq/btrlFKcf6Ze/w9PiLAxdM35RwhLlXJJAo0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/oTqpq/btrlFKcf6Ze/w9PiLAxdM35RwhLlXJJAo0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/oTqpq/btrlFKcf6Ze/w9PiLAxdM35RwhLlXJJAo0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FoTqpq%2FbtrlFKcf6Ze%2Fw9PiLAxdM35RwhLlXJJAo0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1908&quot; height=&quot;924&quot; data-origin-width=&quot;1908&quot; data-origin-height=&quot;924&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고&lt;b&gt; Spring Data Elasitcsearch 4.1&lt;/b&gt; 버전에서도 바뀐 부분이 있는데 일단 &lt;b&gt;IndexTemplate&lt;/b&gt; 을 지원하기 시작했다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 이번에 사용할 Alias 도 변경된 부분이 있는데, 기존에 사용한 &lt;b&gt;AliasQuery 은 Deprecated 되고 AliasAction&lt;/b&gt; 을 사용하도록 권장하고 있다.&amp;nbsp; (왜 AliasQuery 가 Deprecated 가 되었는지는 좀 더 찾아봐야될 것 같다....)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1396&quot; data-origin-height=&quot;790&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/eEq28z/btrlFK4nQa1/iFp3Wg4AH9xncjHNz1aMc1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/eEq28z/btrlFK4nQa1/iFp3Wg4AH9xncjHNz1aMc1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/eEq28z/btrlFK4nQa1/iFp3Wg4AH9xncjHNz1aMc1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FeEq28z%2FbtrlFK4nQa1%2FiFp3Wg4AH9xncjHNz1aMc1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1396&quot; height=&quot;790&quot; data-origin-width=&quot;1396&quot; data-origin-height=&quot;790&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 여러 메서드들이 추가가되었는데, 이번 예제에서 사용할 메서드인 alias( ), 그리고 getAliasesForIndex( ) 가 추가되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1464&quot; data-origin-height=&quot;1086&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/MGUjD/btrlGdya2Uw/Ubl30M8OZ0LExEIpP88i1k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/MGUjD/btrlGdya2Uw/Ubl30M8OZ0LExEIpP88i1k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/MGUjD/btrlGdya2Uw/Ubl30M8OZ0LExEIpP88i1k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FMGUjD%2FbtrlGdya2Uw%2FUbl30M8OZ0LExEIpP88i1k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1464&quot; height=&quot;1086&quot; data-origin-width=&quot;1464&quot; data-origin-height=&quot;1086&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Code Example&lt;/span&gt;&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 코드 예제를 살펴보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 아래와 같이 의존성을 추가한다.&lt;/p&gt;
&lt;pre id=&quot;code_1637754443187&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-data-elasticsearch'
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Spring boot 는 2.6.0 버전을 사용하였을 때, 자동적으로 Spring data elasticsearch 는 4.3 버전이 추가되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;혹여나 Spring boot 버전이 낮더라도 2.3 버전 이상이면 starter-data-elasticsearch 가 아닌 직접 의존성을 추가하면 spring data elasticsearch 4.3 까지 적용이 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;의존성이 정상적으로 적용이 된 이후에 아래와 같이 RestHighLevelClient 와 ElasticsearchOperations 를 Bean 으로 등록한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 코드는 &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;a style=&quot;color: #006dd7;&quot; href=&quot;https://www.baeldung.com/spring-data-elasticsearch-tutorial&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Bealdung&lt;/a&gt; &lt;/span&gt;글을 참고하였다.&lt;/p&gt;
&lt;pre id=&quot;code_1637753389265&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Configuration
@EnableElasticsearchRepositories(basePackages = &quot;com.hooon.dataes.repository&quot;)
@ComponentScan(basePackages = { &quot;com.hooon.dataes.service&quot; })
public class ElasticSearchConfig {

  @Bean
  public RestHighLevelClient client() {
    ClientConfiguration clientConfiguration = ClientConfiguration.builder()
        .connectedTo(&quot;localhost:9200&quot;)
        .build();

    return RestClients.create(clientConfiguration).rest();
  }

  @Bean
  public ElasticsearchOperations elasticsearchTemplate() {
    return new ElasticsearchRestTemplate(client());
  }

}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제로 사용할 아래 User Entity 클래스는 간단하게 필드 두 개로 만들었다.&lt;/p&gt;
&lt;pre id=&quot;code_1637753445708&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Table(name = &quot;users&quot;)
public class User {

  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  @Column(name = &quot;id&quot;, nullable = false)
  private Long id;

  @Column(name = &quot;name&quot;, nullable = false, length = 10)
  private String name;

  @Builder
  public User(String name) {
    this.name = name;
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;conertDocument 메서드는 아래에서 확인할 도큐먼트 클래스로 캐스팅하는 메소드이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제로 필드만 매핑하는 거라면 modelMapper 로 대체하여도 무방하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 index 에 저장할 Doucument 객체를 만들자.&lt;/p&gt;
&lt;pre id=&quot;code_1637753476191&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Document(indexName = &quot;user&quot;, createIndex = false)
public class UserDocument {

  @Id
  private String id;

  @Field(type = FieldType.Text)
  private String name;

}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;@Document 어노테이션에는 생성할 인덱스 이름인 user 를 적었지만, 실제로는 index 이름이 아닌 alias 이름으로 사용할 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;index 이름은 user-1637745625207, user-1637745625210 등 user 를 prefix 로 사용하려고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다시 정리하자면, user 라는 이름으로 index 를 생성하지 않을 것이며 그렇기 때문에 어플리케이션이 실행될 때 자동으로 인덱스를 생성하는 옵션인 createIndex 옵션은 false 로 설정한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 클래스는 인덱스 객체를 생성하는 유틸클래스이다.&lt;/p&gt;
&lt;pre id=&quot;code_1637757972466&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class IndexUtil {

  public static IndexCoordinates createIndexNameWithPostFixWrapper(String indexName) {
    return IndexCoordinates.of(indexName + &quot;-&quot; + Instant.now().toEpochMilli());
  }

  public static IndexCoordinates createIndexNameWrapper(String indexName) {
    return IndexCoordinates.of(indexName);
  }

}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인덱스 이름을 받으면 그대로 인덱스 객체를 생성하는 메서드와 뒤에 epochMilli 를 붙여주는 메서드 두 가지를 만들었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;epochMilli 를 붙여주는 이유는 생성되는 인덱스마다 이름이 중복이 되지 않기 위함이다. 그래야 색인을 할 때마다 새로운 인덱스를 만들며 기존 인덱스랑 이름이 중복이 되지 않기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 공통으로 사용할 레포지토리를 만들어 보자.&lt;/p&gt;
&lt;pre id=&quot;code_1637753984962&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Repository
public interface BaseElasticSearchRepository&amp;lt;T&amp;gt; {

  &amp;lt;S extends T&amp;gt; S save(S entity, IndexCoordinates indexName);

  &amp;lt;S extends T&amp;gt; Iterable&amp;lt;S&amp;gt; saveAll(Iterable&amp;lt;S&amp;gt; entities, IndexCoordinates indexName);

  boolean setAlias(IndexCoordinates indexNameWrapper, IndexCoordinates aliasNameWrapper);

  Set&amp;lt;String&amp;gt; findIndexNamesByAlias(IndexCoordinates aliasNameWrapper);

  boolean deleteIndex(IndexCoordinates indexNameWrapper);

}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 예제는 &lt;a href=&quot;https://www.sothawo.com/2020/11/implement-a-rolling-index-strategy-with-spring-data-elasticsearch-4-1/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;해당 블로그&lt;/span&gt;&lt;/a&gt;에 있는 예제를 참고하였다. 참고한 예제는 하나의 Document 에 대한 케이스로 설명하고 있지만, 어플리케이션에 존재하는 모든 Document 객체에서 공통적으로 사용할 수 있는 기능이기 때문에 BaseElasticSearchRepository 로 네이밍하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 처럼 레포지토리 인터페이스를 생성한 목적은 기존에 존재하는 CrudRepository 에 정의되어 있는 save 메서드를 호출하면 @Document 에 설정한 index 이름에 저장을 한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 우리가 필요한 것은 index + XXX 에 저장하는 것이 목표이기 때문에 새로운 레포지토리 인터페이스를 만들어야 한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;750&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/STgxT/btrl3LhDXAF/jDLMBGYXKKPQeZ23knqK60/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/STgxT/btrl3LhDXAF/jDLMBGYXKKPQeZ23knqK60/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/STgxT/btrl3LhDXAF/jDLMBGYXKKPQeZ23knqK60/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FSTgxT%2Fbtrl3LhDXAF%2FjDLMBGYXKKPQeZ23knqK60%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2000&quot; height=&quot;750&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;750&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래와 같이 구현하였지만, alias 세팅, alias 로 연결 된 인덱스 찾기, 인덱스 삭제 같은 기능들도 공통적으로 사용할 것 같아 추가적으로 더 정의하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 코드는 위에 작성한 인터페이스의 구현체이다.&lt;/p&gt;
&lt;pre id=&quot;code_1637754003602&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Repository
@RequiredArgsConstructor
public class BaseElasticSearchRepositoryImpl&amp;lt;T&amp;gt; implements BaseElasticSearchRepository&amp;lt;T&amp;gt; {

  private final ElasticsearchOperations operations;

  @Override
  public &amp;lt;S extends T&amp;gt; S save(S entity, IndexCoordinates indexName) {
    return operations.save(entity, indexName);
  }

  @Override
  public &amp;lt;S extends T&amp;gt; Iterable&amp;lt;S&amp;gt; saveAll(Iterable&amp;lt;S&amp;gt; entities, IndexCoordinates indexName) {
    return operations.save(entities, indexName);
  }

  @Override
  public boolean setAlias(IndexCoordinates indexNameWrapper, IndexCoordinates aliasNameWrapper) {
    IndexOperations indexOperations = operations.indexOps(indexNameWrapper);
    AliasActions aliasActions = new AliasActions();
    aliasActions.add(new AliasAction.Add(AliasActionParameters.builder()
        .withIndices(indexOperations.getIndexCoordinates().getIndexNames())
        .withAliases(aliasNameWrapper.getIndexName())
        .build()));

    return indexOperations.alias(aliasActions);
  }

  @Override
  public Set&amp;lt;String&amp;gt; findIndexNamesByAlias(IndexCoordinates aliasNameWrapper) {
    IndexOperations indexOperations = operations.indexOps(aliasNameWrapper);
    return indexOperations.getAliasesForIndex(aliasNameWrapper.getIndexName()).keySet();
  }

  @Override
  public boolean deleteIndex(IndexCoordinates indexNameWrapper) {
    IndexOperations indexOperations = operations.indexOps(indexNameWrapper);
    return indexOperations.delete();
  }

}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Spring data elasticserach 4.0 버전에 추가 된 ElasticsearchOpertions 를 사용하여 로직을 구현하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에서도 언급하였지만, ElasticsearchOpertions 을 사용하면 HighLevelRestClient 로 직접 request 를 보내지 않고 간단하게 메서드만 호출하는 방법으로 Elasticsearch 에 요청을 보낼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;색인만 할 때는 아래 인터페이스가 필요 없을 수도 있지만, 추후에 조회 기능도 함께 사용할 수 있도록 ElasticsearchRepository 그리고 위에서 생성한 인터페이스를 확장한 레포지토리 인터페이스를 만들었다.&lt;/p&gt;
&lt;pre id=&quot;code_1637754024866&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Repository
public interface UserDocumentRepository extends
    ElasticsearchRepository&amp;lt;UserDocument, String&amp;gt;,
    BaseElasticSearchRepository&amp;lt;UserDocument&amp;gt; {

  List&amp;lt;UserDocument&amp;gt; findUserDocumentByName(String name);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 인터페이스로 서비스 레이어에서 저 인터페이스 하나로 위에서 정의한 BaseElasticSearchRepository 에 있는 메서드들과 findBy ~~ 와 같은 Spring data 프로젝트에서 지원하는 메서드 둘 다 사용할 수 있게 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 색인하는 로직을 살펴보자.&lt;/p&gt;
&lt;pre id=&quot;code_1637754169546&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Service
@RequiredArgsConstructor
public class UserIndexingService {

  private final UserRepository userRepository;
  private final UserDocumentRepository userDocumentRepository;
  private final ModelMapper modelMapper;

  private static final String INDEX_PREFIX_NAME =&quot;user&quot;;
  private static final String ALIAS_NAME = &quot;user&quot;;

  @PostConstruct
  public void indexingUserDate() {
    IndexCoordinates indexNameWrapper = IndexUtil.createIndexNameWithPostFixWrapper(INDEX_PREFIX_NAME);
    IndexCoordinates aliasNameWrapper = IndexUtil.createIndexNameWrapper(ALIAS_NAME);

    Set&amp;lt;String&amp;gt; existIndexNames = userDocumentRepository.findIndexNamesByAlias(aliasNameWrapper);
    List&amp;lt;User&amp;gt; users = userRepository.findAll();

    List&amp;lt;UserDocument&amp;gt; userDocuments = users.stream()
        .map(user -&amp;gt; modelMapper.map(user, UserDocument.class))
        .collect(Collectors.toList());


    userDocumentRepository.saveAll(userDocuments, indexNameWrapper);

    existIndexNames.forEach(indexName -&amp;gt; userDocumentRepository.deleteIndex(IndexUtil.createIndexNameWrapper(indexName)));
    userDocumentRepository.setAlias(indexNameWrapper, aliasNameWrapper);

  }

}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;색인하는 과정은 간단하게 아래와 같은 절차로 실행이 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1637758354570&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;1. user alias 에 매핑된 index 이름 조회
2. DB 에 있는 user 전체 데이터 조회 후 색인
3. (1)번에서 조회한 index 삭제
4. (2) 번에서 생성한 index 에 alias 매핑&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행시키먄 아래와 같이 정상적으로 데이터가 색인이 된 것을 볼 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1722&quot; data-origin-height=&quot;618&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/duwUbC/btrl3KJTA8l/VBrnAquKaPxYp7bEeHNIu1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/duwUbC/btrl3KJTA8l/VBrnAquKaPxYp7bEeHNIu1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/duwUbC/btrl3KJTA8l/VBrnAquKaPxYp7bEeHNIu1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FduwUbC%2Fbtrl3KJTA8l%2FVBrnAquKaPxYp7bEeHNIu1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1722&quot; height=&quot;618&quot; data-origin-width=&quot;1722&quot; data-origin-height=&quot;618&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;** 참고&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.sothawo.com/2020/11/implement-a-rolling-index-strategy-with-spring-data-elasticsearch-4-1/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://www.sothawo.com/2020/11/implement-a-rolling-index-strategy-with-spring-data-elasticsearch-4-1&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1637508200570&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Implement a rolling index strategy with Spring Data Elasticsearch 4.1 | sothawo&quot; data-og-description=&quot;With the release of version 4.1 Spring Data Elasticsearch now supports the index templates of Elasticsearch. Index templates allow the user to define settings, mappings and aliases for indices that are automatically created by Elasticsearch when documents &quot; data-og-host=&quot;www.sothawo.com&quot; data-og-source-url=&quot;https://www.sothawo.com/2020/11/implement-a-rolling-index-strategy-with-spring-data-elasticsearch-4-1/&quot; data-og-url=&quot;https://www.sothawo.com/2020/11/implement-a-rolling-index-strategy-with-spring-data-elasticsearch-4-1/&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://www.sothawo.com/2020/11/implement-a-rolling-index-strategy-with-spring-data-elasticsearch-4-1/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.sothawo.com/2020/11/implement-a-rolling-index-strategy-with-spring-data-elasticsearch-4-1/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Implement a rolling index strategy with Spring Data Elasticsearch 4.1 | sothawo&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;With the release of version 4.1 Spring Data Elasticsearch now supports the index templates of Elasticsearch. Index templates allow the user to define settings, mappings and aliases for indices that are automatically created by Elasticsearch when documents&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.sothawo.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/sothawo/blog-sde-rolling-index&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://github.com/sothawo/blog-sde-rolling-index&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1637508528576&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;GitHub - sothawo/blog-sde-rolling-index&quot; data-og-description=&quot;Contribute to sothawo/blog-sde-rolling-index development by creating an account on GitHub.&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/sothawo/blog-sde-rolling-index&quot; data-og-url=&quot;https://github.com/sothawo/blog-sde-rolling-index&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cR9X9w/hyMqXaUqH4/zUU7yw7Z3zkLv9yxxWKag1/img.png?width=1200&amp;amp;height=600&amp;amp;face=941_138_1049_256&quot;&gt;&lt;a href=&quot;https://github.com/sothawo/blog-sde-rolling-index&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/sothawo/blog-sde-rolling-index&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cR9X9w/hyMqXaUqH4/zUU7yw7Z3zkLv9yxxWKag1/img.png?width=1200&amp;amp;height=600&amp;amp;face=941_138_1049_256');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;GitHub - sothawo/blog-sde-rolling-index&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Contribute to sothawo/blog-sde-rolling-index development by creating an account on GitHub.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://stackoverflow.com/questions/65490480/aliasquery-is-deprecated-in-spring-data-elasticsearch-4-1&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://stackoverflow.com/questions/65490480/aliasquery-is-deprecated-in-spring-data-elasticsearch-4-1&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1637508228010&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;AliasQuery is deprecated in Spring Data Elasticsearch 4.1&quot; data-og-description=&quot;How do I migrate from Spring Data Elasticsearch 4.0 IndexOperations.addAlias to Spring Data Elasticsearch 4.1 IndexOperations.alias? I have the following method: @Autowired ElasticsearchOperations&quot; data-og-host=&quot;stackoverflow.com&quot; data-og-source-url=&quot;https://stackoverflow.com/questions/65490480/aliasquery-is-deprecated-in-spring-data-elasticsearch-4-1&quot; data-og-url=&quot;https://stackoverflow.com/questions/65490480/aliasquery-is-deprecated-in-spring-data-elasticsearch-4-1&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/kI0dU/hyMq1xzTfW/XpgTgEkkt2hDQGffFkTK21/img.png?width=316&amp;amp;height=316&amp;amp;face=0_0_316_316&quot;&gt;&lt;a href=&quot;https://stackoverflow.com/questions/65490480/aliasquery-is-deprecated-in-spring-data-elasticsearch-4-1&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://stackoverflow.com/questions/65490480/aliasquery-is-deprecated-in-spring-data-elasticsearch-4-1&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/kI0dU/hyMq1xzTfW/XpgTgEkkt2hDQGffFkTK21/img.png?width=316&amp;amp;height=316&amp;amp;face=0_0_316_316');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;AliasQuery is deprecated in Spring Data Elasticsearch 4.1&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;How do I migrate from Spring Data Elasticsearch 4.0 IndexOperations.addAlias to Spring Data Elasticsearch 4.1 IndexOperations.alias? I have the following method: @Autowired ElasticsearchOperations&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;stackoverflow.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.elastic.co/guide/en/elasticsearch/reference/6.8/indices-rollover-index.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://www.elastic.co/guide/en/elasticsearch/reference/6.8/indices-rollover-index.html&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1637508307680&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Rollover Index | Elasticsearch Guide [6.8] | Elastic&quot; data-og-description=&quot;The rollover index API rolls an alias over to a new index when the existing index is considered to be too large or too old. The API accepts a single alias name and a list of conditions. The alias must point to a write index for a Rollover request to be val&quot; data-og-host=&quot;www.elastic.co&quot; data-og-source-url=&quot;https://www.elastic.co/guide/en/elasticsearch/reference/6.8/indices-rollover-index.html&quot; data-og-url=&quot;https://www.elastic.co/guide/en/elasticsearch/reference/6.8/indices-rollover-index.html&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://www.elastic.co/guide/en/elasticsearch/reference/6.8/indices-rollover-index.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.elastic.co/guide/en/elasticsearch/reference/6.8/indices-rollover-index.html&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Rollover Index | Elasticsearch Guide [6.8] | Elastic&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;The rollover index API rolls an alias over to a new index when the existing index is considered to be too large or too old. The API accepts a single alias name and a list of conditions. The alias must point to a write index for a Rollover request to be val&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.elastic.co&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.baeldung.com/spring-data-elasticsearch-tutorial&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://www.baeldung.com/spring-data-elasticsearch-tutorial&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Spring Framework/개념</category>
      <category>elasticsearch</category>
      <category>Java</category>
      <category>Spring</category>
      <author>훈훈훈</author>
      <guid isPermaLink="true">https://wave1994.tistory.com/184</guid>
      <comments>https://wave1994.tistory.com/184#entry184comment</comments>
      <pubDate>Fri, 26 Nov 2021 01:23:12 +0900</pubDate>
    </item>
    <item>
      <title>Spring Cloud  ::  Spring cloud sleuth 정리</title>
      <link>https://wave1994.tistory.com/183</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Introduction&lt;/span&gt;&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시스템 로그로 디버깅을 할 때 확인하려는 메서드가 비동기로 호출되거나 멀티 스레드로 동작 한다면 히스토리를 추적하기 무척 어려울 것이다. 이때 Spring cloud sleuth 를 사용한다면 좀 더 편리한 로깅 환경을 제공 받을 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단순히 라이브러리 의존성을 추가하는 것만으로도 요청의 흐름을 추적하는데 큰 도움을 주기 때문에 이번에 도입하게 되어 공부했던 내용을 정리해보려고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Sleuth&lt;/span&gt;&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저,&amp;nbsp;Spring&amp;nbsp;cloud&amp;nbsp;sleuth&amp;nbsp;가&amp;nbsp;무엇인지&amp;nbsp;공식&amp;nbsp;문서를&amp;nbsp;통해&amp;nbsp;확인해보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2032&quot; data-origin-height=&quot;426&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/tyTji/btrkIQRWasH/NrpH24eh7SCSdY02qrXZ30/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/tyTji/btrkIQRWasH/NrpH24eh7SCSdY02qrXZ30/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/tyTji/btrkIQRWasH/NrpH24eh7SCSdY02qrXZ30/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FtyTji%2FbtrkIQRWasH%2FNrpH24eh7SCSdY02qrXZ30%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2032&quot; height=&quot;426&quot; data-origin-width=&quot;2032&quot; data-origin-height=&quot;426&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;간단하게 의역을 해보면, Sleuth 를 사용하면 요청과 메시지들을 추적할 수 있게 해준다고 적혀있다.&lt;br /&gt;이를&amp;nbsp;통해&amp;nbsp;요청에&amp;nbsp;대한&amp;nbsp;히스토리를&amp;nbsp;연관&amp;nbsp;시킬&amp;nbsp;수&amp;nbsp;있다고&amp;nbsp;소개하고&amp;nbsp;있다.&amp;nbsp;&lt;br /&gt;&lt;br /&gt;이러한&amp;nbsp;기능은&amp;nbsp;Microservice&amp;nbsp;architecture&amp;nbsp;에서&amp;nbsp;사용하였을&amp;nbsp;때&amp;nbsp;정말&amp;nbsp;유용하다고&amp;nbsp;할&amp;nbsp;수&amp;nbsp;있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;894&quot; data-origin-height=&quot;338&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/be4i6A/btrkQlpG9KI/A4bH94bdKacMY45CzSiu21/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/be4i6A/btrkQlpG9KI/A4bH94bdKacMY45CzSiu21/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/be4i6A/btrkQlpG9KI/A4bH94bdKacMY45CzSiu21/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbe4i6A%2FbtrkQlpG9KI%2FA4bH94bdKacMY45CzSiu21%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;894&quot; height=&quot;338&quot; data-origin-width=&quot;894&quot; data-origin-height=&quot;338&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위&amp;nbsp;그림을&amp;nbsp;보면,&amp;nbsp;각각의&amp;nbsp;서버는&amp;nbsp;요청을&amp;nbsp;보낼&amp;nbsp;때&amp;nbsp;추적&amp;nbsp;정보를&amp;nbsp;보내는&amp;nbsp;것을&amp;nbsp;볼&amp;nbsp;수&amp;nbsp;있다.&lt;br /&gt;&lt;br /&gt;각&amp;nbsp;정보는&amp;nbsp;Protocol&amp;nbsp;의&amp;nbsp;Header&amp;nbsp;를&amp;nbsp;통해&amp;nbsp;전달하며,&amp;nbsp;정보를&amp;nbsp;수신한&amp;nbsp;서버는&amp;nbsp;다음&amp;nbsp;서버에게&amp;nbsp;동일한&amp;nbsp;정보를&amp;nbsp;제공하는&amp;nbsp;방식으로&amp;nbsp;요청&amp;nbsp;흐름을&amp;nbsp;추적할&amp;nbsp;수&amp;nbsp;있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 Sleuth 가 우리에게 어떻게 도움을 주는지 살펴보자. 아래는 공식 문서에 있는 요청 흐름에 관한 이미지이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;890&quot; data-origin-height=&quot;476&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cQGixC/btrkHJsi6Tf/tYxJKryz3yFyajMNAIi6EK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cQGixC/btrkHJsi6Tf/tYxJKryz3yFyajMNAIi6EK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cQGixC/btrkHJsi6Tf/tYxJKryz3yFyajMNAIi6EK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcQGixC%2FbtrkHJsi6Tf%2FtYxJKryz3yFyajMNAIi6EK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;890&quot; height=&quot;476&quot; data-origin-width=&quot;890&quot; data-origin-height=&quot;476&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최초 Request 가 발생한 시점부터 마지막 Reponse 가 발생할 때까지 모두 동일한 trace id 가 할당된 것을 볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 매번 새로운 작업이 발생할 때마다 span id 는 계속 바뀌는 것을 볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정리하자면, 아래와 같이 정의할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1636813178331&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;(1) Trace Id 
전체 작업에 할당된 ID 이다.
작업은 각각의 작은 단위들로 구성되어 있다고 한다면, 각각의 작은 단위들은 공통의 Trace ID 를 갖고 있다.

(2) Span Id
각각의 작은 단위들이 가지고 있는 ID 이다.
모두 서로 다른 ID 를 가지고 있기 때문에 서로 다른 작업 단위들을 구분해주는 역할을 할 수 있다.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Example&lt;/span&gt;&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 예제 코드를 통해 한 번 살펴보자. 예제 코드는 &lt;a href=&quot;https://www.baeldung.com/spring-cloud-sleuth-single-application&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Baeldung&lt;/a&gt; 에 소개 된 코드를 가져왔다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Case&amp;nbsp;1.&amp;nbsp;싱글&amp;nbsp;스레드&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 예제 코드&lt;/p&gt;
&lt;pre id=&quot;code_1636812213030&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Slf4j
@RestController
public class HelloController {
  
  @GetMapping(&quot;/hello&quot;)
  public String helloSleuth() {
    log.info(&quot;Hello sleuth&quot;);
    log.info(&quot;Bye sleuth&quot;);
    
    return &quot;ok&quot;;
  }

}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 실행 결과&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2030&quot; data-origin-height=&quot;122&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/yrun9/btrkSMN3q9f/QX0ahGkpkBlh6uk67TsnK1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/yrun9/btrkSMN3q9f/QX0ahGkpkBlh6uk67TsnK1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/yrun9/btrkSMN3q9f/QX0ahGkpkBlh6uk67TsnK1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fyrun9%2FbtrkSMN3q9f%2FQX0ahGkpkBlh6uk67TsnK1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2030&quot; height=&quot;122&quot; data-origin-width=&quot;2030&quot; data-origin-height=&quot;122&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어플리케이션을 실행시키고 GET /hello 를 호출했을 때 위의 결과 처럼 로깅이 되는 것을 볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;출력 된 로그는 순서대로 어플리케이션 이름, Trace ID, Span ID 이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위&amp;nbsp;예제에서는&amp;nbsp;Trace&amp;nbsp;ID&amp;nbsp;와&amp;nbsp;Span&amp;nbsp;ID&amp;nbsp;가&amp;nbsp;동일한&amp;nbsp;것을&amp;nbsp;볼&amp;nbsp;수&amp;nbsp;있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Case 2. &amp;nbsp;두개&amp;nbsp;이상의&amp;nbsp;스레드&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 예제 코드&lt;/p&gt;
&lt;pre id=&quot;code_1636813054137&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Slf4j 
@RequiredArgsConstructor
@RestController
public class TestController {

  private final Executor executor;

  @GetMapping(&quot;/new-thread&quot;)
  public String newThread() {
    log.info(&quot;create newThread&quot;);

    Runnable runnable = () -&amp;gt; {
      log.info(&quot;I'm inside the new thread&quot;);
    };
    executor.execute(runnable);

    log.info(&quot;I'm done&quot;);

    return &quot;ok&quot;;
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 실행 결과&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2054&quot; data-origin-height=&quot;160&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cKp28w/btrkTaag0eE/mXpA8ekABl5I4nlgtbSIm1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cKp28w/btrkTaag0eE/mXpA8ekABl5I4nlgtbSIm1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cKp28w/btrkTaag0eE/mXpA8ekABl5I4nlgtbSIm1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcKp28w%2FbtrkTaag0eE%2FmXpA8ekABl5I4nlgtbSIm1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2054&quot; height=&quot;160&quot; data-origin-width=&quot;2054&quot; data-origin-height=&quot;160&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어플리케이션을 실행시키고 GET /new-thread 를 호출했을 때 위의 결과 처럼 로깅이 되는 것을 볼 수 있다.&lt;br /&gt;살펴보아야할&amp;nbsp;점은&amp;nbsp;서로&amp;nbsp;다른&amp;nbsp;thread&amp;nbsp;에서&amp;nbsp;실행되고&amp;nbsp;있지만,&amp;nbsp;Trace&amp;nbsp;ID&amp;nbsp;는&amp;nbsp;동일하다는&amp;nbsp;것을&amp;nbsp;볼&amp;nbsp;수&amp;nbsp;있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 MSA 환경이 아닌 모놀리스 방식의 싱글 어플리케이션이라도 Sleuth 를 적용한다면 각 요청의 흐름을 추적하기에 많은 도움을 받을 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://happycloud-lee.tistory.com/216&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://happycloud-lee.tistory.com/216&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1636811923338&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;[SC10] Spring Cloud Sleuth와 Zipkin 이란 ?&quot; data-og-description=&quot;1. Sleuth와 Zipkin 이해 1) WHY ? 마이크로서비스로 큰 서비스를 잘게 쪼개어 개발하게 되면 자연스럽게 마이크로서비스간에 연결이 많아지고 복잡하게 됩니다. 예를 들어 고객 '홍길동'이 2021-02-01 13:&quot; data-og-host=&quot;happycloud-lee.tistory.com&quot; data-og-source-url=&quot;https://happycloud-lee.tistory.com/216&quot; data-og-url=&quot;https://happycloud-lee.tistory.com/216&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/goo95/hyMkxqyzu5/MoZeXma6puyq4YYoaxeGHk/img.png?width=669&amp;amp;height=648&amp;amp;face=0_0_669_648,https://scrap.kakaocdn.net/dn/xoVhB/hyMlgtUJRn/5kI9DKCuIXIbYILNkpkYe0/img.png?width=1744&amp;amp;height=744&amp;amp;face=0_0_1744_744&quot;&gt;&lt;a href=&quot;https://happycloud-lee.tistory.com/216&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://happycloud-lee.tistory.com/216&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/goo95/hyMkxqyzu5/MoZeXma6puyq4YYoaxeGHk/img.png?width=669&amp;amp;height=648&amp;amp;face=0_0_669_648,https://scrap.kakaocdn.net/dn/xoVhB/hyMlgtUJRn/5kI9DKCuIXIbYILNkpkYe0/img.png?width=1744&amp;amp;height=744&amp;amp;face=0_0_1744_744');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;[SC10] Spring Cloud Sleuth와 Zipkin 이란 ?&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;1. Sleuth와 Zipkin 이해 1) WHY ? 마이크로서비스로 큰 서비스를 잘게 쪼개어 개발하게 되면 자연스럽게 마이크로서비스간에 연결이 많아지고 복잡하게 됩니다. 예를 들어 고객 '홍길동'이 2021-02-01 13:&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;happycloud-lee.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://dzone.com/articles/tracing-in-microservices-with-spring-cloud-sleuth&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://dzone.com/articles/tracing-in-microservices-with-spring-cloud-sleuth&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1636811935462&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;Tracing in Microservices With Spring Cloud Sleuth - DZone Java&quot; data-og-description=&quot; &quot; data-og-host=&quot;dzone.com&quot; data-og-source-url=&quot;https://dzone.com/articles/tracing-in-microservices-with-spring-cloud-sleuth&quot; data-og-url=&quot;https://dzone.com/articles/tracing-in-microservices-with-spring-cloud-sleuth&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/dmex9G/hyMkBNcRuk/vqTTKkzj5KN2yQPqiIFiP1/img.jpg?width=1024&amp;amp;height=640&amp;amp;face=0_0_1024_640,https://scrap.kakaocdn.net/dn/TrnSx/hyMkB7uZBD/vcmjiOdaW0lpyscGR7SX6k/img.jpg?width=1024&amp;amp;height=640&amp;amp;face=0_0_1024_640&quot;&gt;&lt;a href=&quot;https://dzone.com/articles/tracing-in-microservices-with-spring-cloud-sleuth&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://dzone.com/articles/tracing-in-microservices-with-spring-cloud-sleuth&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/dmex9G/hyMkBNcRuk/vqTTKkzj5KN2yQPqiIFiP1/img.jpg?width=1024&amp;amp;height=640&amp;amp;face=0_0_1024_640,https://scrap.kakaocdn.net/dn/TrnSx/hyMkB7uZBD/vcmjiOdaW0lpyscGR7SX6k/img.jpg?width=1024&amp;amp;height=640&amp;amp;face=0_0_1024_640');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Tracing in Microservices With Spring Cloud Sleuth - DZone Java&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;dzone.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://engineering.linecorp.com/ko/blog/line-ads-msa-opentracing-zipkin/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://engineering.linecorp.com/ko/blog/line-ads-msa-opentracing-zipkin/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1636811941201&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;LINE 광고 플랫폼의 MSA 환경에서 Zipkin을 활용해 로그 트레이싱하기 - LINE ENGINEERING&quot; data-og-description=&quot;안녕하세요. LINE Ads에서 DSP Manager를 담당하고 있는 김용훈입니다. LINE Ads는 일본과 태국, 대만 등 전 세계 LINE 사용자를 대상으로 하는 글로벌 광고 플랫폼을 개발하고 있습니다. LINE의 광고 플랫&quot; data-og-host=&quot;engineering.linecorp.com&quot; data-og-source-url=&quot;https://engineering.linecorp.com/ko/blog/line-ads-msa-opentracing-zipkin/&quot; data-og-url=&quot;https://engineering.linecorp.com/ko/blog/line-ads-msa-opentracing-zipkin/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bWbjvA/hyMlur9S8w/H4GnKktWS7YKYSWxoQUbAK/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/b5fIYD/hyMkzBTbdE/gOmyVkkyIedSeUWZ0kKk5K/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/fPKWI/hyMkDjWDwI/GVL2tRxV8GftJ3xKlJlnE0/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630&quot;&gt;&lt;a href=&quot;https://engineering.linecorp.com/ko/blog/line-ads-msa-opentracing-zipkin/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://engineering.linecorp.com/ko/blog/line-ads-msa-opentracing-zipkin/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bWbjvA/hyMlur9S8w/H4GnKktWS7YKYSWxoQUbAK/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/b5fIYD/hyMkzBTbdE/gOmyVkkyIedSeUWZ0kKk5K/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/fPKWI/hyMkDjWDwI/GVL2tRxV8GftJ3xKlJlnE0/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;LINE 광고 플랫폼의 MSA 환경에서 Zipkin을 활용해 로그 트레이싱하기 - LINE ENGINEERING&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;안녕하세요. LINE Ads에서 DSP Manager를 담당하고 있는 김용훈입니다. LINE Ads는 일본과 태국, 대만 등 전 세계 LINE 사용자를 대상으로 하는 글로벌 광고 플랫폼을 개발하고 있습니다. LINE의 광고 플랫&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;engineering.linecorp.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.studytonight.com/post/request-tracing-in-microservices-using-spring-cloud-sleuth-andzipkin&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://www.studytonight.com/post/request-tracing-in-microservices-using-spring-cloud-sleuth-andzipkin&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1636811953136&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Request Tracing in Microservices using Spring Cloud Sleuth and&amp;nbsp;Zipkin - Studytonight&quot; data-og-description=&quot;To understand what is Spring Cloud Sleuth and Zipkin and why we need them in a microservices architecture where a single monolithic application is divided into smaller services, we first need to understand the problem that microservices architecture leads &quot; data-og-host=&quot;www.studytonight.com&quot; data-og-source-url=&quot;https://www.studytonight.com/post/request-tracing-in-microservices-using-spring-cloud-sleuth-andzipkin&quot; data-og-url=&quot;https://www.studytonight.com/post/request-tracing-in-microservices-using-spring-cloud-sleuth-andzipkin&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/jiVN6/hyMkzaN3ZK/78MmYowHG26SqzoLYH05kk/img.jpg?width=1920&amp;amp;height=1080&amp;amp;face=0_0_1920_1080&quot;&gt;&lt;a href=&quot;https://www.studytonight.com/post/request-tracing-in-microservices-using-spring-cloud-sleuth-andzipkin&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.studytonight.com/post/request-tracing-in-microservices-using-spring-cloud-sleuth-andzipkin&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/jiVN6/hyMkzaN3ZK/78MmYowHG26SqzoLYH05kk/img.jpg?width=1920&amp;amp;height=1080&amp;amp;face=0_0_1920_1080');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Request Tracing in Microservices using Spring Cloud Sleuth and&amp;nbsp;Zipkin - Studytonight&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;To understand what is Spring Cloud Sleuth and Zipkin and why we need them in a microservices architecture where a single monolithic application is divided into smaller services, we first need to understand the problem that microservices architecture leads&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.studytonight.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://refactorfirst.com/distributed-tracing-with-spring-cloud-sleuth.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://refactorfirst.com/distributed-tracing-with-spring-cloud-sleuth.html&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1636811980013&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;Distributed Tracing with Spring Cloud Sleuth And Zipkin&quot; data-og-description=&quot;In this article, we would learn how we can implement distributed tracing and understand the key concepts of distributed tracing.&quot; data-og-host=&quot;refactorfirst.com&quot; data-og-source-url=&quot;https://refactorfirst.com/distributed-tracing-with-spring-cloud-sleuth.html&quot; data-og-url=&quot;http://refactorfirst.com/distributed-tracing-with-spring-cloud-sleuth.html&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://refactorfirst.com/distributed-tracing-with-spring-cloud-sleuth.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://refactorfirst.com/distributed-tracing-with-spring-cloud-sleuth.html&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Distributed Tracing with Spring Cloud Sleuth And Zipkin&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;In this article, we would learn how we can implement distributed tracing and understand the key concepts of distributed tracing.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;refactorfirst.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://docs.spring.io/spring-cloud-sleuth/docs/current/reference/html/getting-started.html#getting-started&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://docs.spring.io/spring-cloud-sleuth/docs/current/reference/html/getting-started.html#getting-started&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1636811970061&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Getting Started&quot; data-og-description=&quot;If you are getting started with Spring Cloud Sleuth or Spring in general, start by reading this section. It answers the basic &amp;ldquo;what?&amp;rdquo;, &amp;ldquo;how?&amp;rdquo; and &amp;ldquo;why?&amp;rdquo; questions. It includes an introduction to Spring Cloud Sleuth, along with installation inst&quot; data-og-host=&quot;docs.spring.io&quot; data-og-source-url=&quot;https://docs.spring.io/spring-cloud-sleuth/docs/current/reference/html/getting-started.html#getting-started&quot; data-og-url=&quot;https://docs.spring.io/spring-cloud-sleuth/docs/current/reference/html/getting-started.html#getting-started&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://docs.spring.io/spring-cloud-sleuth/docs/current/reference/html/getting-started.html#getting-started&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://docs.spring.io/spring-cloud-sleuth/docs/current/reference/html/getting-started.html#getting-started&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Getting Started&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;If you are getting started with Spring Cloud Sleuth or Spring in general, start by reading this section. It answers the basic &amp;ldquo;what?&amp;rdquo;, &amp;ldquo;how?&amp;rdquo; and &amp;ldquo;why?&amp;rdquo; questions. It includes an introduction to Spring Cloud Sleuth, along with installation inst&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;docs.spring.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.baeldung.com/spring-cloud-sleuth-single-application&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://www.baeldung.com/spring-cloud-sleuth-single-application&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Spring Framework/개념</category>
      <category>cloud</category>
      <category>sleuth</category>
      <category>Spring</category>
      <author>훈훈훈</author>
      <guid isPermaLink="true">https://wave1994.tistory.com/183</guid>
      <comments>https://wave1994.tistory.com/183#entry183comment</comments>
      <pubDate>Sat, 13 Nov 2021 23:41:03 +0900</pubDate>
    </item>
    <item>
      <title>Spring boot  ::  Caffeine cache 정리</title>
      <link>https://wave1994.tistory.com/182</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Introduction&lt;/span&gt;&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사내에서 Local cache 라이브러리인 Caffeine cache 를 도입하게 되어 공부한 내용을 정리하게 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Caffeine cache 를 사용하면서 EhCache 와의 차이점이 궁금하여 두 라이브러리를 비교 후 간단한 예제 코드를 살펴보려고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제 코드는 &lt;a href=&quot;https://github.com/Wave1994-Hoon/spring-cache-example&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;깃헙&lt;/a&gt;에서 확인할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Caffeine cache vs EhCache 3.xx&lt;/span&gt;&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;먼저 Caffeine cahce 에 대하여 살펴보자&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Baeldung &lt;span&gt;에서는 &lt;/span&gt;Caffeine Cache &lt;span&gt;를 아래와 같이 소개하고 있다&lt;/span&gt;.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1906&quot; data-origin-height=&quot;540&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/0gVvj/btrjxb9JZE8/VqORncqIBNvqTKnMNTOYG1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/0gVvj/btrjxb9JZE8/VqORncqIBNvqTKnMNTOYG1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/0gVvj/btrjxb9JZE8/VqORncqIBNvqTKnMNTOYG1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F0gVvj%2Fbtrjxb9JZE8%2FVqORncqIBNvqTKnMNTOYG1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1906&quot; height=&quot;540&quot; data-origin-width=&quot;1906&quot; data-origin-height=&quot;540&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Caffeine 깃헙 위키는&lt;span&gt;&amp;nbsp;아래와 같이 소개하고 있다&lt;/span&gt;.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1822&quot; data-origin-height=&quot;336&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/IdkhJ/btrjlTCJGI1/tqiiWsGa4aWKT784hooas0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/IdkhJ/btrjlTCJGI1/tqiiWsGa4aWKT784hooas0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/IdkhJ/btrjlTCJGI1/tqiiWsGa4aWKT784hooas0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FIdkhJ%2FbtrjlTCJGI1%2FtqiiWsGa4aWKT784hooas0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1822&quot; height=&quot;336&quot; data-origin-width=&quot;1822&quot; data-origin-height=&quot;336&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공통적으로 &lt;span&gt;High Performance Java caching Library &lt;/span&gt;라고 소개하고 있다&lt;span&gt;.&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;문서를 읽어보면 캐시와 &lt;span&gt;ConcurrentMap &lt;/span&gt;과의 차이점도 설명으로 덧붙이고 있다&lt;span&gt;.&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span&gt;ConcurrentMap &lt;/span&gt;에 저장된 데이터는 해당 &lt;span&gt;Map &lt;/span&gt;이 제거될 때까지 영구적으로 보관된다고 한다&lt;span&gt;. &lt;br /&gt;&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;반면에 캐시는 &lt;span&gt;evict &lt;/span&gt;로직이 &lt;span&gt;Auto &lt;/span&gt;로 동작하게끔 구성이 된다고 한다&lt;span&gt;.&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;그리고 &lt;span&gt;Caffeine Cache &lt;/span&gt;는 &lt;span&gt;eviction policy &lt;/span&gt;로 &lt;span&gt;Window TinyLfu &lt;/span&gt;라는 것을 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 알고리즘을 사용함으로써 최적의 적중률&lt;span&gt;(near-optimal hit rate)&lt;/span&gt;을 보여준다고 한다&lt;span&gt;.&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Window TinyLfu &lt;/span&gt;에 대하여 궁금하다면 &lt;a href=&quot;https://www.fatalerrors.org/a/caffeine-cache-details.htm&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;해당 링크&lt;/a&gt;를 읽어보길 바란다&lt;span&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이제 Ehcache 에 대하여 살펴보자.&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;EhCache &lt;span&gt;는 &lt;/span&gt;Java &lt;span&gt;진영에서 유명한 &lt;/span&gt;Local Cache &lt;span&gt;라이브러리 종류 중 하나이다&lt;/span&gt;.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;EhCache &lt;span&gt;는 &lt;/span&gt;Caffeine Cache &lt;span&gt;보다 더 많은 기능을 제공해준다&lt;/span&gt;.&lt;br /&gt;&lt;span&gt;분산 처리&lt;/span&gt;, Cache Listener &lt;span&gt;그리고 &lt;/span&gt;Off Heap &lt;span&gt;에 캐싱된 데이터를 저장할 수 있다&lt;/span&gt;. &lt;span&gt;그 외 더 많은 기능들은 공식문서에서 확인할 수 있다&lt;/span&gt;.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 그림은 &lt;a href=&quot;https://www.ehcache.org/documentation/3.1/clustered-cache.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Ehcache 공식문서&lt;/a&gt;에 있는 &lt;span&gt;Distributed Caching &lt;/span&gt;관련 내용이다&lt;span&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1708&quot; data-origin-height=&quot;1168&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rfFSy/btrjlyS4kwG/cQJKEN84iZp7Gnm1ehPFkk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rfFSy/btrjlyS4kwG/cQJKEN84iZp7Gnm1ehPFkk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rfFSy/btrjlyS4kwG/cQJKEN84iZp7Gnm1ehPFkk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FrfFSy%2FbtrjlyS4kwG%2FcQJKEN84iZp7Gnm1ehPFkk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1708&quot; height=&quot;1168&quot; data-origin-width=&quot;1708&quot; data-origin-height=&quot;1168&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 그림을 보면 각 어플리케이션 내에 저장되어 있는 캐시를 &lt;span&gt;Terracotta &lt;/span&gt;라는 &lt;span&gt;Hub &lt;/span&gt;역할을 하는 분산 캐시 서버에 동기화하는 과정을 볼 수 있다&lt;span&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;EhCache &lt;span&gt;의 &lt;/span&gt;Distributed Caching &lt;span&gt;에 대하여 좀 더 알아보고 싶다면 &lt;a href=&quot;https://www.nextree.co.kr/p3151&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;해당링크&lt;/a&gt;를 참고하길 바란다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 그림은 &lt;a href=&quot;https://www.ehcache.org/documentation/3.4/tiering.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;EhCache 공식문서&lt;/a&gt;에 있는 &lt;span&gt;Storage tiers hierarchy &lt;/span&gt;구조이다&lt;span&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1550&quot; data-origin-height=&quot;746&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cOA0Zm/btrjmU9xco5/B1HcKN8oPqY9EtLDnX8BUK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cOA0Zm/btrjmU9xco5/B1HcKN8oPqY9EtLDnX8BUK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cOA0Zm/btrjmU9xco5/B1HcKN8oPqY9EtLDnX8BUK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcOA0Zm%2FbtrjmU9xco5%2FB1HcKN8oPqY9EtLDnX8BUK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1550&quot; height=&quot;746&quot; data-origin-width=&quot;1550&quot; data-origin-height=&quot;746&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;EhCache &lt;span&gt;는 &lt;/span&gt;Heap &lt;span&gt;메모리 공간 이외에 데이터를 저장할 수 있는 &lt;/span&gt;Off Heap &lt;span&gt;기능을 지원한다&lt;/span&gt;.&lt;br /&gt;Off Heap &lt;span&gt;기능을 사용하면 &lt;/span&gt;GC &lt;span&gt;로 부터 자유로워 질 수 있는 장점이 있다&lt;/span&gt;. &lt;br /&gt;&lt;br /&gt;&lt;span&gt;하지만&lt;/span&gt;, Off Heap &lt;span&gt;에 저장되어 있는 데이터를 저장 및 불러올 떄는 직렬화 비용이 발생하게 된다&lt;/span&gt;.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이제 벤치마크 자료를 통해 성능을 비교해보자.&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 자료는 Caffeine cache 깃헙 위키에서 제공하는 데이터이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;측정 값과 단위는 아래와 같다.&lt;/p&gt;
&lt;pre id=&quot;code_1635684128178&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;- Throughput: 단위 시간당 디지털 데이터 전송으로 처리하는 양
- ops/s: operations per second (초당 작업)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;읽기 100% 성능 측정&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1696&quot; data-origin-height=&quot;1022&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ZMr2z/btrjxdT0TI9/s9lsuMlWR1zAZcQnTBE6S0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ZMr2z/btrjxdT0TI9/s9lsuMlWR1zAZcQnTBE6S0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ZMr2z/btrjxdT0TI9/s9lsuMlWR1zAZcQnTBE6S0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FZMr2z%2FbtrjxdT0TI9%2Fs9lsuMlWR1zAZcQnTBE6S0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1696&quot; height=&quot;1022&quot; data-origin-width=&quot;1696&quot; data-origin-height=&quot;1022&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;읽기 &lt;span&gt;100% &lt;/span&gt;성능 측정 테스트에서는 &lt;span&gt;Caffeine Cache &lt;/span&gt;가 가장 좋은 성능을 보여주었고 그 다음으로는 &lt;span&gt;ConcurrentLinkedHashMap &lt;/span&gt;이 좋은 성능을 보여주었다&lt;span&gt;.&lt;br /&gt;&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;위에서 비교했던 &lt;span&gt;EhCache &lt;/span&gt;는 다소 아쉬운 성능을 보여주었다&lt;span&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;읽기 75% 쓰기 25% 성능 측정&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1732&quot; data-origin-height=&quot;1022&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/OWBi1/btrjmWfgnLi/BwHKfZzVJwym3uyRx9JfIK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/OWBi1/btrjmWfgnLi/BwHKfZzVJwym3uyRx9JfIK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/OWBi1/btrjmWfgnLi/BwHKfZzVJwym3uyRx9JfIK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FOWBi1%2FbtrjmWfgnLi%2FBwHKfZzVJwym3uyRx9JfIK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1732&quot; height=&quot;1022&quot; data-origin-width=&quot;1732&quot; data-origin-height=&quot;1022&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;읽기 &lt;span&gt;75% &lt;/span&gt;쓰기 &lt;span&gt;25% &lt;/span&gt;성능 측정 테스트에서도 역시 &lt;span&gt;Caffeine Cache &lt;/span&gt;가 가장 좋은 성능을 보여주었고 그 다음으로는 &lt;span&gt;ConcurrentLinkedHashMap &lt;/span&gt;이 좋은 성능을 보여주었다&lt;span&gt;.&lt;br /&gt;&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;하지만&lt;span&gt;, &lt;/span&gt;읽기 &lt;span&gt;100% &lt;/span&gt;성능 측정과는 다르게 &lt;span&gt;Caffeine Cache &lt;/span&gt;와 &lt;span&gt;ConcurrentLinkedHashMap &lt;/span&gt;의 성능 차이가 &lt;span&gt;2&lt;/span&gt;배 정도 차이가 나는 것을 볼 수 있다&lt;span&gt;.&lt;br /&gt;&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;마찬가지로 위에서 비교했던 &lt;span&gt;EhCache &lt;/span&gt;는 다소 아쉬운 성능을 보여주었다&lt;span&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쓰기 100% 성능 측정&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1748&quot; data-origin-height=&quot;1014&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pkQWk/btrjlPgyuZL/rmKAbKscUDBoMQB2UKGXq0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pkQWk/btrjlPgyuZL/rmKAbKscUDBoMQB2UKGXq0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pkQWk/btrjlPgyuZL/rmKAbKscUDBoMQB2UKGXq0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpkQWk%2FbtrjlPgyuZL%2FrmKAbKscUDBoMQB2UKGXq0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1748&quot; height=&quot;1014&quot; data-origin-width=&quot;1748&quot; data-origin-height=&quot;1014&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쓰기 &lt;span&gt;100% &lt;/span&gt;성능 측정 테스트에서도 역시 &lt;span&gt;Caffeine Cache &lt;/span&gt;가 가장 좋은 성능을 보여주었고 그 다음으로는 &lt;span&gt;ConcurrentLinkedHashMap &lt;/span&gt;이 좋은 성능을 보여주었다&lt;span&gt;.&lt;br /&gt;&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;마찬가지로 &lt;span&gt;Caffeine Cache &lt;/span&gt;와 &lt;span&gt;ConcurrentLinkedHashMap &lt;/span&gt;의 성능 차이가 &lt;span&gt;2&lt;/span&gt;배 정도 차이가 나는 것을 볼 수 있다&lt;span&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정리하자면, Caffeine Cache &lt;span&gt;는 &lt;/span&gt;EhCache &lt;span&gt;처럼 다양한 기능은 제공하지는 않지만&lt;/span&gt;&amp;nbsp;&lt;span&gt;심플하게 메모리에 데이터를 캐싱하고 불러오는 작업만 한다면 가장 뛰어난 성능을 보여준다&lt;/span&gt;.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Caffeine cache example&lt;/span&gt;&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 Spring boot 환경에서 사용 예시를 간단하게 살펴보자&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Caffeine cahce 를 사용하기 위해 아래와 같이 의존성을 추가하자.&lt;/p&gt;
&lt;pre id=&quot;code_1635685816844&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-cache'
    implementation 'com.github.ben-manes.caffeine:caffeine'
    
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 다음 @EnableCaching 를 사용하여 스프링 어플리케이션이 캐시를 사용할 수 있게 하자.&lt;/p&gt;
&lt;pre id=&quot;code_1635686238469&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@EnableCaching // 캐시 기능을 활성화한다.
@SpringBootApplication
public class CacheApplication {

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

}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 캐시에 대한 Enum 값을 정의하자.&lt;/p&gt;
&lt;pre id=&quot;code_1635684592882&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Getter
public enum CacheType {
  USERS(
      &quot;users&quot;,      // 캐시 이름: users
      5 * 60,       // 만료 시간: 5 분
      10000         // 최대 갯수: 10000
  );

  CacheType(String cacheName, int expireAfterWrite, int maximumSize) {
    this.cacheName = cacheName;
    this.expireAfterWrite = expireAfterWrite;
    this.maximumSize = maximumSize;
  }

  private final String cacheName;
  private final int expireAfterWrite;
  private final int maximumSize;

}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Enum 을 사용하여 캐시 이름, 만료 시간, 저장 가능한 최대 갯수를 정의하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 다음, cacheManager 를 Bean 으로 등록하자.&lt;/p&gt;
&lt;pre id=&quot;code_1635684607059&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Configuration
public class CacheConfig {

  @Bean
  public CacheManager cacheManager() {
    List&amp;lt;CaffeineCache&amp;gt; caches = Arrays.stream(CacheType.values())
        .map(cache -&amp;gt; new CaffeineCache(cache.getCacheName(), Caffeine.newBuilder().recordStats()
                .expireAfterWrite(cache.getExpireAfterWrite(), TimeUnit.SECONDS)
                .maximumSize(cache.getMaximumSize())
                .build()
            )
        )
        .collect(Collectors.toList());

    SimpleCacheManager cacheManager = new SimpleCacheManager();
    cacheManager.setCaches(caches);

    return cacheManager;
  }

}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CacheType 에 등록한 캐시들을 Caffeine 캐시 객체로 생성 후 SimpleCacheManager 객체에 등록하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 캐시가 정상적으로 동작하는지 아래 유저 객체를 사용하여 테스트를 해보자.&lt;/p&gt;
&lt;pre id=&quot;code_1635684771916&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Getter
public class User {
  private final Long id;
  private final String email;
  private final String name;

  @Builder
  public User(Long id, String email, String name) {
    this.id = id;
    this.email = email;
    this.name = name;
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;테스트를 위해 간단한 Controller 를 작성해보았다.&lt;/p&gt;
&lt;pre id=&quot;code_1635684798492&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@RestController
public class UserController {

  Map&amp;lt;Long, User&amp;gt; userMap = new ConcurrentHashMap&amp;lt;&amp;gt;();

  @PostConstruct
  public void init() {
    User user1 = User.builder()
        .id(1L)
        .name(&quot;test1&quot;)
        .email(&quot;test1@test.com&quot;)
        .build();

    User user2 = User.builder()
        .id(2L)
        .name(&quot;test2&quot;)
        .email(&quot;test2@test.com&quot;)
        .build();

    User user3 = User.builder()
        .id(3L)
        .name(&quot;test3&quot;)
        .email(&quot;test3@test.com&quot;)
        .build();

    userMap.put(user1.getId(), user1);
    userMap.put(user2.getId(), user2);
    userMap.put(user3.getId(), user3);

  }

  @Cacheable(cacheNames = &quot;users&quot;)
  @GetMapping(&quot;/users&quot;)
  public List&amp;lt;User&amp;gt; getUser() throws InterruptedException {
    Thread.sleep(10000);
    Set&amp;lt;Long&amp;gt; userIds = userMap.keySet();

    return userIds.stream()
        .map(userId -&amp;gt; userMap.get(userId))
        .collect(Collectors.toList());

  }

}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;간단하게 코드를 작성하기 위해서 Controller, Service, Repository 로 계층을 구분하지 않고 1 Layer 로 작성하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터는 DB 는 사용하지 않고 간단하게 ConcurrentHashMap 을 사용하여 어플리케이션이 실행될 때 초기화를 하도록 만들었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 성능 측정을 위하여 &lt;b&gt;GET /users&lt;/b&gt; 가 호출되면 10s 동안 thread 를 sleep 하도록 api 를 만들었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 api 호출을 통해 캐시가 정상적으로 동작하는지 살펴보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1572&quot; data-origin-height=&quot;1238&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dgAyjb/btrjqpuqm75/XViN3YGPh4JlstGlFFTWX0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dgAyjb/btrjqpuqm75/XViN3YGPh4JlstGlFFTWX0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dgAyjb/btrjqpuqm75/XViN3YGPh4JlstGlFFTWX0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdgAyjb%2Fbtrjqpuqm75%2FXViN3YGPh4JlstGlFFTWX0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1572&quot; height=&quot;1238&quot; data-origin-width=&quot;1572&quot; data-origin-height=&quot;1238&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;첫 번째 호출할 때는 응답 시간이 약 10s 정도 걸린 것을 볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다시 한 번 api 를 호출해보자.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1570&quot; data-origin-height=&quot;1224&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/7D1Bt/btrjlxzU7Qb/4JEUqqREocqsXFc9Said11/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/7D1Bt/btrjlxzU7Qb/4JEUqqREocqsXFc9Said11/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/7D1Bt/btrjlxzU7Qb/4JEUqqREocqsXFc9Said11/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F7D1Bt%2FbtrjlxzU7Qb%2F4JEUqqREocqsXFc9Said11%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1570&quot; height=&quot;1224&quot; data-origin-width=&quot;1570&quot; data-origin-height=&quot;1224&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 번째 호출부터 약 4ms 정도 걸리는 것을 볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/ben-manes/caffeine/wiki&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://github.com/ben-manes/caffeine/wiki&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1635686609875&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;GitHub - ben-manes/caffeine: A high performance caching library for Java&quot; data-og-description=&quot;A high performance caching library for Java. Contribute to ben-manes/caffeine development by creating an account on GitHub.&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/ben-manes/caffeine/wiki&quot; data-og-url=&quot;https://github.com/ben-manes/caffeine&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/vNB0w/hyL9EQ6owq/YpR1YU3KTtEc8XpqLvBTLK/img.png?width=1200&amp;amp;height=600&amp;amp;face=969_159_1072_271&quot;&gt;&lt;a href=&quot;https://github.com/ben-manes/caffeine/wiki&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/ben-manes/caffeine/wiki&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/vNB0w/hyL9EQ6owq/YpR1YU3KTtEc8XpqLvBTLK/img.png?width=1200&amp;amp;height=600&amp;amp;face=969_159_1072_271');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;GitHub - ben-manes/caffeine: A high performance caching library for Java&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;A high performance caching library for Java. Contribute to ben-manes/caffeine development by creating an account on GitHub.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.ehcache.org/documentation/3.4/tiering.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://www.ehcache.org/documentation/3.4/tiering.html&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1635686692719&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Ehcache Tiering Options&quot; data-og-description=&quot;In order to understand what happens for different cache operations when using multiple tiers, here are examples of Put and Get operations. The sequence diagrams are oversimplified but still show the main points. Figure 2. Multiple tiers using Put Figure 3.&quot; data-og-host=&quot;www.ehcache.org&quot; data-og-source-url=&quot;https://www.ehcache.org/documentation/3.4/tiering.html&quot; data-og-url=&quot;https://www.ehcache.org/documentation/3.4/tiering.html&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://www.ehcache.org/documentation/3.4/tiering.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.ehcache.org/documentation/3.4/tiering.html&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Ehcache Tiering Options&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;In order to understand what happens for different cache operations when using multiple tiers, here are examples of Put and Get operations. The sequence diagrams are oversimplified but still show the main points. Figure 2. Multiple tiers using Put Figure 3.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.ehcache.org&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://gosunaina.medium.com/cache-redis-ehcache-or-caffeine-45b383ae85ee&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://gosunaina.medium.com/cache-redis-ehcache-or-caffeine-45b383ae85ee&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1635686650438&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;Cache &amp;mdash; Redis, EhCache or Caffeine?&quot; data-og-description=&quot;A cache is a reserved storage location that collects temporary data to help websites, browsers, and apps load faster. The data stored in a&amp;hellip;&quot; data-og-host=&quot;gosunaina.medium.com&quot; data-og-source-url=&quot;https://gosunaina.medium.com/cache-redis-ehcache-or-caffeine-45b383ae85ee&quot; data-og-url=&quot;https://gosunaina.medium.com/cache-redis-ehcache-or-caffeine-45b383ae85ee&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/buB7T4/hyMa2iwYFa/rTghN1wUy5DRhrc1PuaTm0/img.png?width=766&amp;amp;height=371&amp;amp;face=0_0_766_371,https://scrap.kakaocdn.net/dn/cKAZMU/hyMaVRgCZo/4ZEwlEx3HG59kuYUc0pwak/img.png?width=766&amp;amp;height=371&amp;amp;face=0_0_766_371&quot;&gt;&lt;a href=&quot;https://gosunaina.medium.com/cache-redis-ehcache-or-caffeine-45b383ae85ee&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://gosunaina.medium.com/cache-redis-ehcache-or-caffeine-45b383ae85ee&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/buB7T4/hyMa2iwYFa/rTghN1wUy5DRhrc1PuaTm0/img.png?width=766&amp;amp;height=371&amp;amp;face=0_0_766_371,https://scrap.kakaocdn.net/dn/cKAZMU/hyMaVRgCZo/4ZEwlEx3HG59kuYUc0pwak/img.png?width=766&amp;amp;height=371&amp;amp;face=0_0_766_371');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Cache &amp;mdash; Redis, EhCache or Caffeine?&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;A cache is a reserved storage location that collects temporary data to help websites, browsers, and apps load faster. The data stored in a&amp;hellip;&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;gosunaina.medium.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://blog.yevgnenll.me/posts/spring-boot-with-caffeine-cache&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://blog.yevgnenll.me/posts/spring-boot-with-caffeine-cache&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1635686708498&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Spring boot 에 caffeine 캐시를 적용해보자 - 어떻게하면 일을 안 할까?&quot; data-og-description=&quot;부제: 어떻게 하면 일을 조금이라도 안할까?&quot; data-og-host=&quot;blog.yevgnenll.me&quot; data-og-source-url=&quot;https://blog.yevgnenll.me/posts/spring-boot-with-caffeine-cache&quot; data-og-url=&quot;https://blog.yevgnenll.me/posts/spring-boot-with-caffeine-cache&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cESN5A/hyMa8JOBCQ/r1aFYBNv4QUqWiidEdgQ01/img.png?width=600&amp;amp;height=493&amp;amp;face=0_0_600_493,https://scrap.kakaocdn.net/dn/0x5dD/hyL9B7WLZ5/VTCWCCHAwmWJuc7ZF8inV1/img.png?width=600&amp;amp;height=493&amp;amp;face=0_0_600_493&quot;&gt;&lt;a href=&quot;https://blog.yevgnenll.me/posts/spring-boot-with-caffeine-cache&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://blog.yevgnenll.me/posts/spring-boot-with-caffeine-cache&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cESN5A/hyMa8JOBCQ/r1aFYBNv4QUqWiidEdgQ01/img.png?width=600&amp;amp;height=493&amp;amp;face=0_0_600_493,https://scrap.kakaocdn.net/dn/0x5dD/hyL9B7WLZ5/VTCWCCHAwmWJuc7ZF8inV1/img.png?width=600&amp;amp;height=493&amp;amp;face=0_0_600_493');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Spring boot 에 caffeine 캐시를 적용해보자 - 어떻게하면 일을 안 할까?&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;부제: 어떻게 하면 일을 조금이라도 안할까?&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;blog.yevgnenll.me&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Spring Framework/개념</category>
      <category>cache</category>
      <category>caffeine cache</category>
      <category>ehcache</category>
      <category>Java</category>
      <category>Spring</category>
      <category>Spring Boot</category>
      <author>훈훈훈</author>
      <guid isPermaLink="true">https://wave1994.tistory.com/182</guid>
      <comments>https://wave1994.tistory.com/182#entry182comment</comments>
      <pubDate>Sun, 31 Oct 2021 22:54:43 +0900</pubDate>
    </item>
    <item>
      <title>Spring boot  ::  Multiple DataSource 환경에서 @DataJpaTest 이슈 정리 및 스프링 코드 분석</title>
      <link>https://wave1994.tistory.com/181</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Introduction&lt;/span&gt;&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에는 &lt;b&gt;Multiple DataSource&lt;/b&gt; 환경에서 JPA 로 개발 된 Repository 테스트를 할 때 사용하는 &lt;b&gt;@DataJpaTest&lt;/b&gt; 로 테스트를 작성할 때 발생하는 문제, 그리고 어떻게 해결해야하는지에 대하여 정리해보려고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고로 MyBatis 를 사용한다면, mybatis-spring-boot-starter-test 에 있는 &lt;b&gt;@MyBatisTest&lt;/b&gt; 를 사용하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;@DataJpaTest &lt;/b&gt;는 JPA, Entity Manager 를 &lt;b&gt;@MyBatisTest&lt;/b&gt; 는 MyBatis 를 AutoConfiguration 을 하는 점 이외에는 동일하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제 코드는 &lt;a href=&quot;https://github.com/Wave1994-Hoon/spring-multiple-datasource-example&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;GitHub&lt;/a&gt; 에서 확인할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Problem&lt;/span&gt;&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결론부터 말하자면 여러 DataSource 를 구성할 때 프로퍼티를 spring.datasource.url 이 아닌 spring.datasource.master.hikari 와 같이 커스텀하게 작성해서 발생하는 이슈이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래는 일반적으로 단일 DataSource 를 사용할 때 작성하는 프로퍼티이다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1633767144727&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://127.0.0.1:3306/multiple-datesource?serverTimezone=UTC
    username: root
    password:&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Spring boot 는 DataSource 를 AutoConfiguration 을 할 때, 기본적으로 spring.datasource.url 를 가져오지만, 여러 Datasource 를 구성하는 경우 Hikari CP DataSource 를 생성하기 위해서는 커스텀하게 작성한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이제&amp;nbsp; 왜 이런 이슈가 발생하는지 예제 코드를 보면서 살펴보자.&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래는 예제로 작성한 DataSource 설정 클래스이다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1633764358673&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Configuration
public class DataSourceConfig {

    @Primary
    @Bean(name = &quot;masterDataSource&quot;)
    @ConfigurationProperties(prefix=&quot;spring.datasource.master.hikari&quot;)
    public DataSource masterDataSource() {
        return DataSourceBuilder.create()
                .type(HikariDataSource.class)
                .build();
    }

    @Bean(name = &quot;slaveDataSource&quot;)
    @ConfigurationProperties(prefix=&quot;spring.datasource.slave.hikari&quot;)
    public DataSource slaveDataSource() {
        return DataSourceBuilder.create()
                .type(HikariDataSource.class)
                .build();
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Master / Slave 두 종류의 DataSource 로 구성하였고, 프로퍼티는 spring.datasource.master(slave).hikari 하위의 값들을 가져오도록 설정하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 아래는 Master / Slave datasource 를 구성하기 위한 프로퍼티 파일이다.&lt;/p&gt;
&lt;pre id=&quot;code_1633705968027&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;spring:
  datasource:
    master:
      hikari:
        driver-class-name: com.mysql.cj.jdbc.Driver
        jdbc-url: jdbc:mysql://127.0.0.1:3306/multiple-datesource?serverTimezone=UTC
        read-only: false
        username: root
        password:

    slave:
      hikari:
        driver-class-name: com.mysql.cj.jdbc.Driver
        jdbc-url: jdbc:mysql://127.0.0.1:3306/multiple-datesource?serverTimezone=UTC
        read-only: true
        username: root
        password:&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 테스트 코드를 살펴보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 코드는 @DataJpaTest 를 사용하여 간단한 테스트 코드를 만들어 보았다.&lt;/p&gt;
&lt;pre id=&quot;code_1633706216211&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@DataJpaTest
@AutoConfigureTestDatabase(replace = Replace.NONE)
public class JpaTest {

    @Autowired
    UserRepository userRepository;

    @Test
    void test() {
        User user = userRepository.findUserByName(&quot;name&quot;).orElse(null);
        System.out.println(user);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 위 코드가 Multiple DataSource 로 구성하지 않았다면 정상적으로 테스트가 종료가 되겠지만, 현재 구조로 테스트를 실행한다면&amp;nbsp;아래와 같은 오류 메시지를 볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;845&quot; data-origin-height=&quot;253&quot; width=&quot;862&quot; height=&quot;258&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c3NvQd/btrhcFNJt9f/XADVPI1pOFWvmBgHlXNEJk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c3NvQd/btrhcFNJt9f/XADVPI1pOFWvmBgHlXNEJk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c3NvQd/btrhcFNJt9f/XADVPI1pOFWvmBgHlXNEJk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc3NvQd%2FbtrhcFNJt9f%2FXADVPI1pOFWvmBgHlXNEJk%2Fimg.png&quot; data-origin-width=&quot;845&quot; data-origin-height=&quot;253&quot; width=&quot;862&quot; height=&quot;258&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Failed to configure a DataSource: 'url' attribute is not specified&lt;/b&gt; and no embedded datasource could be configured. Reason:&amp;nbsp;Failed&amp;nbsp;to&amp;nbsp;determine&amp;nbsp;a&amp;nbsp;suitable&amp;nbsp;driver&amp;nbsp;class&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에서 언급했듯이, 프로퍼티 파일에서 spring.datasource.url 을 가져오지 못하여 어플리케이션 실행을 하지 못한다고 알려주고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이제 스프링 코드를 보면서 왜 이런 문제가 발생하는지 살펴보자.&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;테스트할 때 사용했던 &lt;b&gt;@DataJpaTest&lt;/b&gt; 어노테이션을 살펴보자&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1552&quot; data-origin-height=&quot;1258&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bytyMT/btrhdWagomT/eB6X5Hvc43sgpz2R7Eok3K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bytyMT/btrhdWagomT/eB6X5Hvc43sgpz2R7Eok3K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bytyMT/btrhdWagomT/eB6X5Hvc43sgpz2R7Eok3K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbytyMT%2FbtrhdWagomT%2FeB6X5Hvc43sgpz2R7Eok3K%2Fimg.png&quot; data-origin-width=&quot;1552&quot; data-origin-height=&quot;1258&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;@DataJpaTest&lt;/b&gt; 는 기본적으로 in-memory DB 를 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MySQL, PostgreSQL 같은 DB 를 사용하려면 &lt;b&gt;@AutoConfigureTestDatabase&lt;/b&gt; 사용하라고 주석에 명시되어 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;테스트 코드를 작성할 때, embedded database 인 H2 를 사용한다면 어플리케이션만 띄우면 되기 때문에 편리하겠지만, 데이터베이스에 따라서 SQL 이 조금씩 다르기 때문에 실제 라이브에 배포 된 어플리케이션의 테스트 정확도를 보장하지는 못한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 가지 방법 중 어떤 방법이 더 좋다는 접근 보다는 어떤 점을 더 중요하게 생각하는지에 따라서 embedded 로 사용할지 혹은 실제 database 를 사용할지 결정하는 것이 좋다고 생각한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 글에서는 &lt;b&gt;@AutoConfigureTestDatabas&lt;/b&gt; 설정을 통해 MySQL 을 사용할 것이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 해당 어노테이션을 살펴보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1550&quot; data-origin-height=&quot;1318&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/n3uGt/btrhc1wDZeb/s8TCYtLTvBhKXBAwPBRoWK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/n3uGt/btrhc1wDZeb/s8TCYtLTvBhKXBAwPBRoWK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/n3uGt/btrhc1wDZeb/s8TCYtLTvBhKXBAwPBRoWK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fn3uGt%2Fbtrhc1wDZeb%2Fs8TCYtLTvBhKXBAwPBRoWK%2Fimg.png&quot; data-origin-width=&quot;1550&quot; data-origin-height=&quot;1318&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;@AutoConfigureTestDatabas &lt;/b&gt;살펴보면, Enum 값인 Replace 가 있는 것을 볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;디폴트로 ANY 적용되고 AUTO_CONFIGURED, NONE 두 가지 타입이 더 있는 것을 볼 수 있다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주석만으로는 각각 타입에 따라 어떤 동작을 하는지 파악하기 어려워서 실제로 저 값을 사용하는 코드를 살펴보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 &lt;b&gt;TestDatabaseAutoConfiguration&lt;/b&gt; 코드를 살펴보면 Replace 가 AUTO_CONFIGURED 또는 ANY 일 경우 embedded DB 를 생성하고 NONE 일 경우 아무런 행위 없이 지나가고 &lt;b&gt;DataSourceAutoConfiguration&lt;/b&gt; 이 실행되는 것을 볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;2060&quot; data-origin-height=&quot;1078&quot; width=&quot;908&quot; height=&quot;475&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/PyTRa/btrhdeCr3Xm/xGiNkATcJf9ymCyEUVVcqK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/PyTRa/btrhdeCr3Xm/xGiNkATcJf9ymCyEUVVcqK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/PyTRa/btrhdeCr3Xm/xGiNkATcJf9ymCyEUVVcqK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FPyTRa%2FbtrhdeCr3Xm%2FxGiNkATcJf9ymCyEUVVcqK%2Fimg.png&quot; data-origin-width=&quot;2060&quot; data-origin-height=&quot;1078&quot; width=&quot;908&quot; height=&quot;475&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 과정까지가 테스트 어노테이션을 사용할 때 추가로 동작하는 부분이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 스프링이 DataSource Bean 을 등록할 때 사용하는 &lt;b&gt;DataSourceAutoConfiguration&lt;/b&gt; 을 통해 DataSource 를 Bean 으로 등록하는 과정을 보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시작하기 전에 짚고 넘어가야 될 부분은 Spring boot 2.0 부터는 Tomcat Connection Pool 이 아닌 Hikari Connection Pool 을 사용한다. 따라서 &lt;b&gt;DataSourceAutoConfiguration&lt;/b&gt; 을 통해 Hikari CP 가 등록되는 과정을 살펴보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래는 &lt;b&gt;DataSourceConfiguration&lt;/b&gt; 클래스에 있는 내부 클래스 Hikari 코드이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1686&quot; data-origin-height=&quot;738&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Q6fGI/btrhh7BEFvk/yeVXlxFyZNhcvQvTgkn5Gk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Q6fGI/btrhh7BEFvk/yeVXlxFyZNhcvQvTgkn5Gk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Q6fGI/btrhh7BEFvk/yeVXlxFyZNhcvQvTgkn5Gk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FQ6fGI%2Fbtrhh7BEFvk%2FyeVXlxFyZNhcvQvTgkn5Gk%2Fimg.png&quot; data-origin-width=&quot;1686&quot; data-origin-height=&quot;738&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드를&amp;nbsp;살펴보면 &lt;b&gt;DataSourceProperties&lt;/b&gt; 를 넘겨 받아 DataSource 를 생성하는 것을 볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 부분이 Multiple DataSource 를 사용할 때 발생하는 문제의 시작점이라고 볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 이유는 파라미터로 받는 &lt;b&gt;DataSourceProperties&lt;/b&gt; 를 보면 알 수 있는데, 코드를 살펴보면 spring.datasource 하위에 있는 값들을 가져오는 것을 볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 위의 예시처럼 spring.datasource.master.hikari 이런식으로 설정 값들을 작성을 하면 DataSourceProperties 는 값을 읽어올 방법이 없는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래는 &lt;b&gt;DataSourceProperties&lt;/b&gt; 코드이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1600&quot; data-origin-height=&quot;1340&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cZnHSJ/btrhfzy3Ses/QRiX5kwZbzV6Ts29qVnbr0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cZnHSJ/btrhfzy3Ses/QRiX5kwZbzV6Ts29qVnbr0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cZnHSJ/btrhfzy3Ses/QRiX5kwZbzV6Ts29qVnbr0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcZnHSJ%2Fbtrhfzy3Ses%2FQRiX5kwZbzV6Ts29qVnbr0%2Fimg.png&quot; data-origin-width=&quot;1600&quot; data-origin-height=&quot;1340&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래는&lt;b&gt; DataSourceConfiguration &lt;/b&gt;클래스 내에 있는 Hikari 내부 클래스에서 createDataSource( ) 메서드를 호출할 때 실질적으로 호출되는 메서드이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1744&quot; data-origin-height=&quot;358&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cQdwo7/btrhhRFxZV1/eSyvkQKgtcVUZyeZdxl3ak/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cQdwo7/btrhhRFxZV1/eSyvkQKgtcVUZyeZdxl3ak/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cQdwo7/btrhhRFxZV1/eSyvkQKgtcVUZyeZdxl3ak/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcQdwo7%2FbtrhhRFxZV1%2FeSyvkQKgtcVUZyeZdxl3ak%2Fimg.png&quot; data-origin-width=&quot;1744&quot; data-origin-height=&quot;358&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 코드는 &lt;b&gt;DataSourceBuilder&lt;/b&gt; 를 사용해서 프로퍼티와 타입을 가지고 DataSource 를 생성하는 것을 볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제의 원인은 위에서 발견했지만, DataSource 가 어떻게 생성이 되는지 궁금하여 &lt;b&gt;DataSourceBuilder&lt;/b&gt; 코드를 좀 더 살펴 보았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저, &lt;b&gt;DataSourceBuilder&lt;/b&gt; 는 스프링 부트 버전에 따라 코드가 변경되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현업에서 사용했던 2.3.10 버전 그리고 위에서 계속 살펴보았던 예제 코드는 스프링 2.5.5 버전이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서로 살펴보면서 코드를 보던 중 상당 부분이 바뀌어서 두 버전 다 살펴보려고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 2.3.10 버전의 &lt;b&gt;DataSourceBuilder &lt;/b&gt;보면 상당히 심플한 구조를 보이고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1796&quot; data-origin-height=&quot;1560&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dLQdHh/btrhcNkPWW5/uKp2ktmgpcT5bDtdasREh0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dLQdHh/btrhcNkPWW5/uKp2ktmgpcT5bDtdasREh0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dLQdHh/btrhcNkPWW5/uKp2ktmgpcT5bDtdasREh0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdLQdHh%2FbtrhcNkPWW5%2FuKp2ktmgpcT5bDtdasREh0%2Fimg.png&quot; data-origin-width=&quot;1796&quot; data-origin-height=&quot;1560&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;build( ) 메서드를 살펴보면,&amp;nbsp; DataSource Type 을 가져오고 나서 bind( ) 메서드를 호출하여 url 과 jdbc-url 을 서로 매핑하는 것을 볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그에 비하여 2.5.5 버전은 조금 복잡한 로직으로 되어 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1710&quot; data-origin-height=&quot;934&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b1UabV/btrhjsetI09/2wDSiHiRxiWxa6CYUoXGV0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b1UabV/btrhjsetI09/2wDSiHiRxiWxa6CYUoXGV0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b1UabV/btrhjsetI09/2wDSiHiRxiWxa6CYUoXGV0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb1UabV%2FbtrhjsetI09%2F2wDSiHiRxiWxa6CYUoXGV0%2Fimg.png&quot; data-origin-width=&quot;1710&quot; data-origin-height=&quot;934&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;build( ) 메서드에서 첫번째 라인 DataSourceProperties.forType( ) 메서드의 호출을 따라가다 보면 아래와 같이 bind 역할을 하는 HikariDataSourceProperties 를 볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래는 HikariDataSourceProperties 내부 클래스 코드이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1692&quot; data-origin-height=&quot;498&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kz3CF/btrhesUmXUV/GUqu3o1kfU2BZGaN2GS1A0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kz3CF/btrhesUmXUV/GUqu3o1kfU2BZGaN2GS1A0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kz3CF/btrhesUmXUV/GUqu3o1kfU2BZGaN2GS1A0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fkz3CF%2FbtrhesUmXUV%2FGUqu3o1kfU2BZGaN2GS1A0%2Fimg.png&quot; data-origin-width=&quot;1692&quot; data-origin-height=&quot;498&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드를 보면 DataSourceProperty 에 있는 값, 그리고 HikariDataSource 의 get(set) 메서드를 파라미터로 넘겨줘서 add( ) 하고 있는 것을 볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;호출되는 add( ) 는 마찬가지로 &lt;b&gt;DataSourceBuilder &lt;/b&gt;에 있는 &lt;b&gt;MappedDataSourceProperties&lt;/b&gt; 내부 클래스에 있는 메서드이다. Map 객체에 데이터를 넣어주는 것을 볼 수 있다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1730&quot; data-origin-height=&quot;1296&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/WXuep/btrhnbqpm4F/fooGa0XGoJqU7bO9PQvRV0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/WXuep/btrhnbqpm4F/fooGa0XGoJqU7bO9PQvRV0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/WXuep/btrhnbqpm4F/fooGa0XGoJqU7bO9PQvRV0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FWXuep%2Fbtrhnbqpm4F%2FfooGa0XGoJqU7bO9PQvRV0%2Fimg.png&quot; data-origin-width=&quot;1730&quot; data-origin-height=&quot;1296&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스프링 코드를 따라가 보았을 때, 저 Map 객체에서 위에서 저장 헀던, HikariDataSource 의 get(set) 메서드를 호출하면서 값들을 매핑하는 것을 볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스프링 코드는 여기까지 살펴보고 정리하자면, &lt;b&gt;@DataJpaTest&lt;/b&gt; 를 사용할 때 Spring boot 에서 기본적으로 지원하는 자동 설정으로 실행이 되기 때문에 오류가 발생한다. 이 문제를 해결하기 위해 &lt;b&gt;@ImportAutoConfiguration&lt;/b&gt; 를 사용하여 등록한 Datasource 설정 클래스를 Import 시켜줘야 한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Code Example&lt;/span&gt;&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 코드는 위에 예제에서 작성했던 Multiple DataSource 설정 클래스이다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1633882517594&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Configuration
public class DataSourceConfig {

    @Primary
    @Bean(name = &quot;masterDataSource&quot;)
    @ConfigurationProperties(prefix=&quot;spring.datasource.master.hikari&quot;)
    public DataSource masterDataSource() {
        return DataSourceBuilder.create()
                .type(HikariDataSource.class)
                .build();
    }

    @Bean(name = &quot;slaveDataSource&quot;)
    @ConfigurationProperties(prefix=&quot;spring.datasource.slave.hikari&quot;)
    public DataSource slaveDataSource() {
        return DataSourceBuilder.create()
                .type(HikariDataSource.class)
                .build();
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 설정 클래스를 아래 테스트 코드르 작성할 때, @ImportAutoConfiguration(DataSourceConfig.class) 라인을 추가해주면 정상적으로 실행이 되는 것을 볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1633771840989&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@AutoConfigureTestDatabase(replace = Replace.NONE)
@DataJpaTest
@ImportAutoConfiguration(DataSourceConfig.class)
public class JpaTest {

    @Autowired
    UserRepository userRepository;

    @Test
    void test() {
        User user = userRepository.findUserByName(&quot;name&quot;).orElse(null);
        System.out.println(user);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Spring Framework/개념</category>
      <category>Java</category>
      <category>Spring</category>
      <category>Test</category>
      <author>훈훈훈</author>
      <guid isPermaLink="true">https://wave1994.tistory.com/181</guid>
      <comments>https://wave1994.tistory.com/181#entry181comment</comments>
      <pubDate>Mon, 11 Oct 2021 01:52:42 +0900</pubDate>
    </item>
    <item>
      <title>Spring boot  ::  Mockito 로 WebClient  테스트  하기</title>
      <link>https://wave1994.tistory.com/179</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Introduction&lt;/span&gt;&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;WebClient 를 사용하여 외부 API 를 Mock 기반 테스트를 할 때,&amp;nbsp; response 값에 대한 stub 객체를 만드는 방법에 대하여 정리해보려고 한다. 전체 코드는 &lt;a href=&quot;https://github.com/Wave1994-Hoon/web-client-test&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Github&lt;/a&gt; 에서 확인할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Problems&lt;/span&gt;&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적으로 Mockito 를 사용할 때 아래와 같은 방법으로 stub 객체를 만들 수 있다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;( Mockito 는 BDD 방식을 지원하기 때문에 when( ) 대신 좀 더 가독성이 좋은 given ( ) 을 사용하였다. )&lt;/p&gt;
&lt;pre id=&quot;code_1632660143400&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;given(webClientWrapper.get()
    .uri(&quot;http://localhost:8080/test&quot;)
    .retrieve()
    .bodyToMono(String.class)
    .block()
).willReturn(&quot;ok&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 테스트를 돌릴 때 위 코드 라인이 실행이 된다면, 아래와 같이 NPE 가 발생하는 것을 볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1603&quot; data-origin-height=&quot;356&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/caUqyo/btrf5jcetr7/MFcGxFbmu3fgyFg2KfQ151/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/caUqyo/btrf5jcetr7/MFcGxFbmu3fgyFg2KfQ151/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/caUqyo/btrf5jcetr7/MFcGxFbmu3fgyFg2KfQ151/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcaUqyo%2Fbtrf5jcetr7%2FMFcGxFbmu3fgyFg2KfQ151%2Fimg.png&quot; data-origin-width=&quot;1603&quot; data-origin-height=&quot;356&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;given( ) 에서 NPE 가 발생하는 이유는 WebClient 는 &lt;b&gt;Fluent API&lt;/b&gt; 이기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 일반적인 객체에 대한 Stub 객체를 만드는 방법으로 테스트 코드를 작성한다면 정상적으로 동작하지 않게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정리하자면, 위에서 살펴본 NPE 는 Fluent API 에서 발생하는 문제인 것을 확인했으므로, 이제 해당 API 가 무엇인지 그리고 어떻게 Stubbing 을 하는지 알아보자.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Fluent API&lt;/span&gt;&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.baeldung.com/mockito-fluent-apis&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Baeldung&lt;/a&gt; 글을 보면 Fluent API는 Method chaining 을 사용하여 Builder, Factory 등을 사용하는 것으로 정리되어 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;누구나 알 수 있는 예시로는 Java 의 Stream API 가 있다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1066&quot; data-origin-height=&quot;226&quot; data-filename=&quot;fluent1.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cEws4w/btrfWMNyiZ6/7UzVeWDFKC2dCCYPa7skW0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cEws4w/btrfWMNyiZ6/7UzVeWDFKC2dCCYPa7skW0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cEws4w/btrfWMNyiZ6/7UzVeWDFKC2dCCYPa7skW0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcEws4w%2FbtrfWMNyiZ6%2F7UzVeWDFKC2dCCYPa7skW0%2Fimg.png&quot; data-origin-width=&quot;1066&quot; data-origin-height=&quot;226&quot; data-filename=&quot;fluent1.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Fluent API 는 Method Chaining 을 사용하기 때문에 가독성은 좋지만, 그 속에는 여러 객체와 메서드가 숨겨져서 사용되고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 만약 @Mock 을 사용한다면 아래와 같이 작성할 수 있다. 해당 예제는 &lt;a href=&quot;https://rieckpil.de/creating-deep-stubs-with-mockito-to-chain-method-stubbing/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;해외 블로그&lt;/a&gt; 글에서 가져왔다.&lt;/p&gt;
&lt;pre id=&quot;code_1632662300128&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@ExtendWith(MockitoExtension.class)
class ExampleTest {
 
  @Mock
  private WebClient webClient;
 
  @InjectMocks
  private InspirationalQuotesClient cut; // class under test
 
  @Test
  void test() {
 
    WebClient.RequestHeadersUriSpec requestHeadersUriSpec = Mockito.mock(WebClient.RequestHeadersUriSpec.class);
    WebClient.ResponseSpec responseSpec = Mockito.mock(WebClient.ResponseSpec.class);
 
    when(webClient.get()).thenReturn(requestHeadersUriSpec);
    when(requestHeadersUriSpec.uri(&quot;/api/quotes&quot;)).thenReturn(requestHeadersUriSpec);
    when(requestHeadersUriSpec.retrieve()).thenReturn(responseSpec);
    when(responseSpec.bodyToMono(String.class)).thenReturn(Mono.just(&quot;We've escaped hell&quot;));
 
    String result = cut.fetchRandomQuote();
 
    assertEquals(&quot;We've escaped hell&quot;, result);
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드를 보면 단순한 예제임에도 Stub 객체를 생성하기 위해 많은 코드를 작성해야되는 것을 볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 방법으로 테스트 코드를 작성한다면 너무 번거롭기 때문에 대안들을 찾아보다가 &lt;b&gt;MockWebServer&lt;/b&gt; 라이브러리를 사용하는 예제를 찾긴 했다. 하지만 Mockito 만 사용해도 가능할 것 같아서 해당 방법은 패스하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다시 돌아와서, &lt;a href=&quot;https://www.baeldung.com/mockito-fluent-apis&quot;&gt;Baeldung&lt;/a&gt; 글을 보면 Mockito 는 &lt;b&gt;Answer&lt;/b&gt; 라는 객체로 &lt;b&gt;Deep Stubbing&lt;/b&gt; 기능을 지원한다고 소개되어 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1066&quot; data-origin-height=&quot;221&quot; data-filename=&quot;deep1.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cjt85E/btrfWaumPTe/ms6vyMKdIqPGfT0Lq6hUz1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cjt85E/btrfWaumPTe/ms6vyMKdIqPGfT0Lq6hUz1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cjt85E/btrfWaumPTe/ms6vyMKdIqPGfT0Lq6hUz1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcjt85E%2FbtrfWaumPTe%2Fms6vyMKdIqPGfT0Lq6hUz1%2Fimg.png&quot; data-origin-width=&quot;1066&quot; data-origin-height=&quot;221&quot; data-filename=&quot;deep1.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Answer&lt;/b&gt; 은 enum 객체이며, 코드를 살펴 보면 여러 객체가 정의 된 것을 볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 중에서 RETURNS_DEEP_STUBS 객체를 &lt;b&gt;@Mock(answer = Answers.RETURNS_DEEP_STUBS&lt;/b&gt;) 와 같이 사용하면 Deep Stub 을 만들 수 있다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;900&quot; data-origin-height=&quot;640&quot; width=&quot;637&quot; height=&quot;453&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/1hW6I/btrfXxCC8jF/uevK5AK7uh2wKdfSvWB8XK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/1hW6I/btrfXxCC8jF/uevK5AK7uh2wKdfSvWB8XK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/1hW6I/btrfXxCC8jF/uevK5AK7uh2wKdfSvWB8XK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F1hW6I%2FbtrfXxCC8jF%2FuevK5AK7uh2wKdfSvWB8XK%2Fimg.png&quot; data-origin-width=&quot;900&quot; data-origin-height=&quot;640&quot; width=&quot;637&quot; height=&quot;453&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 위에서 봤던 내용들을 전체 코드로 살펴보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Code Example&lt;/span&gt;&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;간단하게 &quot;ok&quot; 문자열을 response 하는 API 를 만들고 WebClient 로 호출하는 service 코드를 테스트 하는 과정을 살펴보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Controller&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단순하게 &quot;ok&quot; 문자열을 response 하는 컨트롤러를 만들었다.&lt;/p&gt;
&lt;pre id=&quot;code_1632663732362&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@RestController
public class TestController {
    
    @GetMapping(&quot;/test&quot;)
    public ResponseEntity&amp;lt;String&amp;gt; test() {
        return ResponseEntity.ok().body(&quot;ok&quot;);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- WebClient Wrapper&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;WebClient 를 사용할 때, 매번 Builder 를 정의해야 되기 때문에 중복 코드가 발생헐 수 밖에 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 BuilderWrapper 클래스를 만들고 생성자에 builder 를 build 할 수 있게 만들었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제로는 여러 값들을 공통적으로 정의해서 사용할 수 있지만, 해당 예제는 간단하게 build( ) 하였다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1632663688165&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Component
public class WebClientWrapper implements WebClient {

    private final WebClient webClient;

    public WebClientWrapper(WebClient.Builder builder) {
        webClient = builder.build();
    }
    
    @Override 
    // ... 생략 
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Service&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에서 만든 WebClientWrapper 를 사용하여 &quot;/test&quot; path 로 요청을 보내는 간단한 코드를 만들었다.&lt;/p&gt;
&lt;pre id=&quot;code_1632663648377&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Service
public class WebclientWrapperService {

    private final WebClientWrapper webClientWrapper;

    public WebclientWrapperService(WebClientWrapper client) {
        this.webClientWrapper = client;
    }

    public String request() {
        return webClientWrapper.get()
                .uri(&quot;http://localhost:8080/test&quot;)
                .retrieve()
                .bodyToMono(String.class)
                .block();

    }

}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Test code&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Deep Stub 를 사용하여 WebClient 를 테스트하는 코드를 만들었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 코드를 실행해보면 정상적으로 동작하는 것을 확인할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1632663768508&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@ExtendWith(MockitoExtension.class)
class WebclientWrapperServiceTest {

    @Mock(answer = Answers.RETURNS_DEEP_STUBS) // Deep Stub
    WebClientWrapper webClientWrapper;

    @InjectMocks
    WebclientWrapperService service;

    @Test
    @DisplayName(&quot;WebClient_wrapper_객체_테스트&quot;)
    void requestTest() {
        // given
        given(webClientWrapper.get()
                .uri(&quot;http://localhost:8080/test&quot;)
                .retrieve()
                .bodyToMono(String.class)
                .block()
        ).willReturn(&quot;ok&quot;);

        // when
        String result = service.request();

        // then
        assertThat(result).isEqualTo(&quot;ok&quot;);

    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;추가로 만약 WebClient.Builder 를 Stubbing 해야 한다면 어떻게 해야할까??&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래처럼 WebClient.Buidler 를 직접 생성자로 넘겨준다면 코드가 약간 달라질 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1632663922784&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Service
public class WebClientNotWrapperService {

    private final WebClient webClient;

    public WebClientNotWrapperService(WebClient.Builder builder) {
        webClient = builder.build();
    }

    public String request() {
        return webClient.get()
                .uri(&quot;http://localhost:8080/test&quot;)
                .retrieve()
                .bodyToMono(String.class)
                .block();

    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Builder 는 자기 자신을 리턴하기 때문에 테스트 코드에서 아래 처럼 사용한다면 NPE 가 발생하게 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1632664138595&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Mock(answer = Answers.RETURNS_DEEP_STUBS)
WebClient webClient;

@Mock
WebClient.Builder builder;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 문제를 해결하기 위해서 다시 Answer 객체에 주석을 살펴보면, RETURNS_SELF 를 사용해서 Builders 를 mocking 할 수 있는 것을 확인할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1020&quot; data-origin-height=&quot;238&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/eratii/btrfVqDIXm4/JcxOvt1ZTe4hInUuehsPQ0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/eratii/btrfVqDIXm4/JcxOvt1ZTe4hInUuehsPQ0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/eratii/btrfVqDIXm4/JcxOvt1ZTe4hInUuehsPQ0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Feratii%2FbtrfVqDIXm4%2FJcxOvt1ZTe4hInUuehsPQ0%2Fimg.png&quot; data-origin-width=&quot;1020&quot; data-origin-height=&quot;238&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 아래와 같이 코드를 작성을 하면 정상적으로 테스트가 되는 것을 확인할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1632664194987&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@ExtendWith(MockitoExtension.class)
class WebClientNotWrapperServiceTest {

    @Mock(answer = Answers.RETURNS_DEEP_STUBS)
    WebClient webClient;

    @Mock(answer = Answers.RETURNS_SELF)
    WebClient.Builder builder;

    @InjectMocks
    WebClientNotWrapperService service;

    @BeforeEach
    void setUp() {
        given(builder.build()).willReturn(webClient);
        service = new WebClientNotWrapperService(builder);
    }
    
    // 중략 ...

}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;좀 더 간단한 방법은 없을까??&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막으로 가장 심플한 방법인 WebClient 를 사용하는 로직을 객체로 감싸고 해당 객체를 테스트 하는 방법을 소개하려고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;간단하게 바로 코드로 살펴보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에서 살펴봤던 WebClient 로 &quot;/test&quot; path 에 요청을 날리는 로직을 함수로 분리하였다.&lt;/p&gt;
&lt;pre id=&quot;code_1632664386911&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Component
@RequiredArgsConstructor
public class WebClientRequest {

    private final WebClientWrapper webClientWrapper;

    public String requestTest() {
        return webClientWrapper.get()
                .uri(&quot;http://localhost:8080/test&quot;)
                .retrieve()
                .bodyToMono(String.class)
                .block();
    }

}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;service 에서는 위에서 분리한 객체를 단순히 호출만 하도록 변경하였다.&lt;/p&gt;
&lt;pre id=&quot;code_1632664410660&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Service
public class WebClientObjectService {

    private final WebClientRequest webClientRequest;

    public WebClientObjectService(WebClientRequest webClientRequest) {
        this.webClientRequest = webClientRequest;
    }

    public String request() {
        return webClientRequest.requestTest();
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;테스트 코드는 간단하게 위에서 분리한 객체만 stub 객체를 만들어서 테스트를 진행하였고, 정상적으로 동작하는 것을 확인할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1632664427930&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@ExtendWith(MockitoExtension.class)
class WebClientObjectServiceTest {

    @Mock
    WebClientRequest webClientRequest;

    @InjectMocks
    WebClientObjectService service;

    @Test
    void request() {
        // given
        given(webClientRequest.requestTest())
                .willReturn(&quot;ok&quot;);

        // when
        String result = service.request();

        // then
        assertThat(result).isEqualTo(&quot;ok&quot;);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.baeldung.com/spring-mocking-webclient&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://www.baeldung.com/spring-mocking-webclient&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.baeldung.com/mockito-fluent-apis&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://www.baeldung.com/mockito-fluent-apis&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://rieckpil.de/creating-deep-stubs-with-mockito-to-chain-method-stubbing/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://rieckpil.de/creating-deep-stubs-with-mockito-to-chain-method-stubbing/&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://circlee7.medium.com/mockito-mock-answer-3212b135262a&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://circlee7.medium.com/mockito-mock-answer-3212b135262a&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;http://wonwoo.ml/index.php/post/2364&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;http://wonwoo.ml/index.php/post/2364&lt;/a&gt;&lt;/p&gt;</description>
      <category>Spring Framework/개념</category>
      <category>Java</category>
      <category>Mock</category>
      <category>mockito</category>
      <category>Spring Boot</category>
      <category>stub</category>
      <category>WebClient</category>
      <author>훈훈훈</author>
      <guid isPermaLink="true">https://wave1994.tistory.com/179</guid>
      <comments>https://wave1994.tistory.com/179#entry179comment</comments>
      <pubDate>Sun, 26 Sep 2021 23:40:49 +0900</pubDate>
    </item>
    <item>
      <title>Spring boot  ::  JPA, Mybatis Transaction Manager 정리</title>
      <link>https://wave1994.tistory.com/178</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;Introduction&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에는 Mybatis 와 JPA 를 동시에 적용한 환경에서 어떤 transactionManager 를 사용해야되는지 살펴보려고 한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 글은 스프링에서 트랜잭션을 담당하는 핵심 인터페이스인 PlatformTransactionManager 를 먼저 정리 후, Mybatis 그리고 JPA 에 대한 내용을 정리하는 순서로 작성하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;PlatformTransactionManager&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스프링 트랜잭션 추상화의 핵심 인터페이스이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 스프링 트랜잭션 기능과 코드는 이 인터페이스를 통해 트랜잭션 서비스를 이용할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래는 해당 인터페이스에 정의 된 메서드 목록이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1010&quot; data-origin-height=&quot;284&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cTlb6I/btrd7SBosqO/Cail1oDNTm56nDzcbJINN1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cTlb6I/btrd7SBosqO/Cail1oDNTm56nDzcbJINN1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cTlb6I/btrd7SBosqO/Cail1oDNTm56nDzcbJINN1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcTlb6I%2Fbtrd7SBosqO%2FCail1oDNTm56nDzcbJINN1%2Fimg.png&quot; data-origin-width=&quot;1010&quot; data-origin-height=&quot;284&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;getTransaction( ) 메서드는 트랜잭션 속성에 따라 새로 생성하거나 진행 중인 트랜잭션에 참여하거나, 진행 중인 트랜잭션을 무시하고 새로운 트랜잭션을 만드는 식으로 동작을 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 트랜잭션 전파 기법을 이용해 자유롭게 조합하고 확장할 수 있게 할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 파라미터로 받는 TransactionDefinition 은 트랜잭션 전파(propagation) 과 격리 수준(isolation) 을 나타낸다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 그림에서 TransactionDefinition 인터페이스 구조를 볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1146&quot; data-origin-height=&quot;1138&quot; width=&quot;756&quot; height=&quot;751&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/9VjVo/btreaQYnlDM/FIS5mNLHVHAgz0vrZMAqKk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/9VjVo/btreaQYnlDM/FIS5mNLHVHAgz0vrZMAqKk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/9VjVo/btreaQYnlDM/FIS5mNLHVHAgz0vrZMAqKk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F9VjVo%2FbtreaQYnlDM%2FFIS5mNLHVHAgz0vrZMAqKk%2Fimg.png&quot; data-origin-width=&quot;1146&quot; data-origin-height=&quot;1138&quot; width=&quot;756&quot; height=&quot;751&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막으로 PlatformTransactionManager 를 구현하고 있는 클래스들을 살펴보면 아래 그림과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞으로 살펴 볼 DataSourceTransactionManager 그리고 JPATransactionManager 는 PlatformTransactionManager 의 구현체인 것을 볼 수 있다. 이 외에도 다양한 구현체를 확인할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1331&quot; data-origin-height=&quot;557&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/I2O2F/btreaRPIk1X/yWtyl7OGaJQEqkNjbuIGUK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/I2O2F/btreaRPIk1X/yWtyl7OGaJQEqkNjbuIGUK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/I2O2F/btreaRPIk1X/yWtyl7OGaJQEqkNjbuIGUK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FI2O2F%2FbtreaRPIk1X%2FyWtyl7OGaJQEqkNjbuIGUK%2Fimg.png&quot; data-origin-width=&quot;1331&quot; data-origin-height=&quot;557&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Mybatis Transaction&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Mybatis 공식 문서를 살펴보면, Spring 에서 DataSourceTransactionManager 를 사용하는 것을 알 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;831&quot; data-origin-height=&quot;130&quot; data-filename=&quot;mybatis-spring &amp;amp;ndash; 2021-09-06 22-36-17.png&quot; width=&quot;882&quot; height=&quot;138&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/CjWf7/btrec5nRU8m/GaW8HqOEAIqBdUOla04j6k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/CjWf7/btrec5nRU8m/GaW8HqOEAIqBdUOla04j6k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/CjWf7/btrec5nRU8m/GaW8HqOEAIqBdUOla04j6k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FCjWf7%2Fbtrec5nRU8m%2FGaW8HqOEAIqBdUOla04j6k%2Fimg.png&quot; data-origin-width=&quot;831&quot; data-origin-height=&quot;130&quot; data-filename=&quot;mybatis-spring &amp;ndash; 2021-09-06 22-36-17.png&quot; width=&quot;882&quot; height=&quot;138&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DataSourceTransactionManager 는 아래와 같이 등록 할 수 있는 것으로 공식 문서에서 확인할 수 있다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1656&quot; data-origin-height=&quot;708&quot; data-filename=&quot;mybatis-spring &amp;amp;ndash; 2021-09-06 22-41-25.png&quot; width=&quot;892&quot; height=&quot;382&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cGzmNS/btrec5BnWUy/mYDlN5YG5kx1D7kJCchFCK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cGzmNS/btrec5BnWUy/mYDlN5YG5kx1D7kJCchFCK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cGzmNS/btrec5BnWUy/mYDlN5YG5kx1D7kJCchFCK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcGzmNS%2Fbtrec5BnWUy%2FmYDlN5YG5kx1D7kJCchFCK%2Fimg.png&quot; data-origin-width=&quot;1656&quot; data-origin-height=&quot;708&quot; data-filename=&quot;mybatis-spring &amp;ndash; 2021-09-06 22-41-25.png&quot; width=&quot;892&quot; height=&quot;382&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 Mybatis 가 사용하는 DataSourceTransactionManager 에 대하여 알아보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DataSourceTransactionManager 클래스는 아래와 같은 의존 관계를 가지고 있는 것을 볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에서 살펴 보았던 PlatformTransactionManager 를 구현한 AbstractPlatformTransactionManager 를 구현하고 있는 클래스인 것을 확인할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1900&quot; data-origin-height=&quot;792&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bDsbnE/btred1ExyaW/9lewIJcEGQ1etTV35Yqc8k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bDsbnE/btred1ExyaW/9lewIJcEGQ1etTV35Yqc8k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bDsbnE/btred1ExyaW/9lewIJcEGQ1etTV35Yqc8k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbDsbnE%2Fbtred1ExyaW%2F9lewIJcEGQ1etTV35Yqc8k%2Fimg.png&quot; data-origin-width=&quot;1900&quot; data-origin-height=&quot;792&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DataSourceTransactionManager 에 대하여 좀 더 살펴보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 클래스는 Connection 객체에 존재하는 트랜잭션 api 를 이용해서 트랜잭션을 관리해주는 트랜잭션 매니저이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 사용하기 위해서는 DataSource 가 스프링 빈으로 등록이 돼야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용할 수 있는 DAO 로는 JDBC 와 Mybatis 가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;JPA Transaction&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 JPA 에서 사용하는 JPATransactionManager 를 살펴보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 의존관계를 살펴보면 DataSourceTransactionManage 클래스와 유사한 것을 볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;2294&quot; data-origin-height=&quot;742&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c0daQx/btrehlCJMyf/XA0HdK8yK97ePHS8lr31Bk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c0daQx/btrehlCJMyf/XA0HdK8yK97ePHS8lr31Bk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c0daQx/btrehlCJMyf/XA0HdK8yK97ePHS8lr31Bk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc0daQx%2FbtrehlCJMyf%2FXA0HdK8yK97ePHS8lr31Bk%2Fimg.png&quot; data-origin-width=&quot;2294&quot; data-origin-height=&quot;742&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JPATransactionManager 는 기본적으로는 JPA API 를 이용할 때 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 JPA 의 EntityManagerFactory 가 스프링의 빈으로 등록된 DataSource 를 사용할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇기 때문에 JPA 에서 사용하는 DataSoure 를 JDBC, Mybatis 에서 사용할 수 있다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 JPATransactionManager 는 DataSourceTransactionManager 가 제공하는 DataSource 레벨의 트랜잭션 관리 기능을 동시에 제공한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과적으로 JPATransactionManager 를 사용하면서 JDBC, Mybatis 의 트랜잭션 관리도 같이 할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Conclusion&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 내용을 정리하자면 Mybatis 를 단독으로 사용할 경우 DataSourceTransactionManager 를 스프링 빈으로 등록이 필요하지만, JPA를 사용할 경우 JPATransactionManager 로 같이 관리가 가능하기 때문에 별도의 TransactionManager 를 등록할 필요는 없다.&lt;/p&gt;</description>
      <category>Spring Framework/개념</category>
      <category>JPA</category>
      <category>mybatis</category>
      <category>Spring</category>
      <category>transaction</category>
      <category>TransactionManager</category>
      <author>훈훈훈</author>
      <guid isPermaLink="true">https://wave1994.tistory.com/178</guid>
      <comments>https://wave1994.tistory.com/178#entry178comment</comments>
      <pubDate>Mon, 6 Sep 2021 23:14:39 +0900</pubDate>
    </item>
  </channel>
</rss>