<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>대디장의 기억저장소</title>
    <link>https://yonguri.tistory.com/</link>
    <description>개발인 본업인, 목공과 요리를 좋아하는 중년개발자의 기억저장소입니다.</description>
    <language>ko</language>
    <pubDate>Thu, 16 Apr 2026 14:07:26 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>yorath</managingEditor>
    <image>
      <title>대디장의 기억저장소</title>
      <url>https://t1.daumcdn.net/cfile/tistory/9973F433598EAEEE16</url>
      <link>https://yonguri.tistory.com</link>
    </image>
    <item>
      <title>R2DBC의 한계와 그 사용법</title>
      <link>https://yonguri.tistory.com/150</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Webflux&amp;nbsp;기반으로&amp;nbsp;React 한&amp;nbsp;코드&amp;nbsp;-&amp;nbsp;비동기적,&amp;nbsp;넌&amp;nbsp;블럭킹-를&amp;nbsp;애플리케이션&amp;nbsp;전체에&amp;nbsp;적용하기&amp;nbsp;위해서&amp;nbsp;DB를&amp;nbsp;다루는&amp;nbsp;영역&amp;nbsp;또한&amp;nbsp;React 하게&amp;nbsp;처리되어야&amp;nbsp;한다.&lt;br /&gt;&lt;br /&gt;하지만&amp;nbsp;DB영역에서&amp;nbsp;논&amp;nbsp;블럭킹&amp;nbsp;방식을&amp;nbsp;지원하는&amp;nbsp;유일한&amp;nbsp;라이브러리인&amp;nbsp;R2DBC는&amp;nbsp;현재까지는&amp;nbsp;우리에게&amp;nbsp;익숙한&amp;nbsp;JPA에&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;제대로&amp;nbsp;사용해야&amp;nbsp;할&amp;nbsp;필요가&amp;nbsp;있다.&lt;br /&gt;&lt;br /&gt;이&amp;nbsp;글에서&amp;nbsp;R2DBC의&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;br /&gt;&lt;br /&gt;R2DBC의&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;&lt;a href=&quot;https://r2dbc.io/drivers/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://r2dbc.io/drivers/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1701667724374&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;R2DBC&quot; data-og-description=&quot;R2DBC 0.8.1.RELEASE: A standard API for reactive programming using SQL databases.&quot; data-og-host=&quot;r2dbc.io&quot; data-og-source-url=&quot;https://r2dbc.io/drivers/&quot; data-og-url=&quot;https://r2dbc.io/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/f4Ex3/hyUItyxzFd/VdEepbIVDPOVGy9FJN8yy0/img.png?width=174&amp;amp;height=192&amp;amp;face=0_0_174_192,https://scrap.kakaocdn.net/dn/biakpi/hyUFe35OGa/7fW1Zrf9Y2VkiMYSRwjPv1/img.png?width=955&amp;amp;height=200&amp;amp;face=0_0_955_200,https://scrap.kakaocdn.net/dn/dyU6he/hyUE4f8Wgk/AQMrb3BCCtuaAuo7s7oSH0/img.png?width=300&amp;amp;height=300&amp;amp;face=0_0_300_300&quot;&gt;&lt;a href=&quot;https://r2dbc.io/drivers/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://r2dbc.io/drivers/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/f4Ex3/hyUItyxzFd/VdEepbIVDPOVGy9FJN8yy0/img.png?width=174&amp;amp;height=192&amp;amp;face=0_0_174_192,https://scrap.kakaocdn.net/dn/biakpi/hyUFe35OGa/7fW1Zrf9Y2VkiMYSRwjPv1/img.png?width=955&amp;amp;height=200&amp;amp;face=0_0_955_200,https://scrap.kakaocdn.net/dn/dyU6he/hyUE4f8Wgk/AQMrb3BCCtuaAuo7s7oSH0/img.png?width=300&amp;amp;height=300&amp;amp;face=0_0_300_300');&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;R2DBC&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;R2DBC 0.8.1.RELEASE: A standard API for reactive programming using SQL databases.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;r2dbc.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;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;mysql 드라이버는 &lt;a style=&quot;background-color: #fdfdfd; color: #31408e; text-align: left;&quot; href=&quot;https://github.com/jasync-sql/jasync-sql&quot;&gt;jasync-sql, &lt;/a&gt;&lt;a style=&quot;background-color: #fdfdfd; color: #31408e; text-align: left;&quot; href=&quot;https://github.com/asyncer-io/r2dbc-mysql&quot;&gt;r2dbc-mysql&lt;/a&gt; 이 두 가지 드라이버가 많이 사용된다.&amp;nbsp;여기서는 jasync 라이브러리를 사용했다.&lt;/p&gt;
&lt;figure id=&quot;og_1701670536290&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 - jasync-sql/jasync-sql: Java &amp;amp; Kotlin Async DataBase Driver for MySQL and PostgreSQL written in Kotlin&quot; data-og-description=&quot;Java &amp;amp; Kotlin Async DataBase Driver for MySQL and PostgreSQL written in Kotlin - GitHub - jasync-sql/jasync-sql: Java &amp;amp; Kotlin Async DataBase Driver for MySQL and PostgreSQL written in Kotlin&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/jasync-sql/jasync-sql&quot; data-og-url=&quot;https://github.com/jasync-sql/jasync-sql&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://github.com/jasync-sql/jasync-sql&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/jasync-sql/jasync-sql&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;GitHub - jasync-sql/jasync-sql: Java &amp;amp; Kotlin Async DataBase Driver for MySQL and PostgreSQL written in Kotlin&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Java &amp;amp; Kotlin Async DataBase Driver for MySQL and PostgreSQL written in Kotlin - GitHub - jasync-sql/jasync-sql: Java &amp;amp; Kotlin Async DataBase Driver for MySQL and PostgreSQL written in Kotlin&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;figure id=&quot;og_1701670527873&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 - asyncer-io/r2dbc-mysql: Reactive Relational Database Connectivity for MySQL. The official successor to mirromutth/r2dbc&quot; data-og-description=&quot;Reactive Relational Database Connectivity for MySQL. The official successor to mirromutth/r2dbc-mysql(dev.miku:r2dbc-mysql). - GitHub - asyncer-io/r2dbc-mysql: Reactive Relational Database Connecti...&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/asyncer-io/r2dbc-mysql&quot; data-og-url=&quot;https://github.com/asyncer-io/r2dbc-mysql&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/uskzq/hyUIE7UxJS/tVSDrUBenxdNyCBd0F2jgK/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/asyncer-io/r2dbc-mysql&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/asyncer-io/r2dbc-mysql&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/uskzq/hyUIE7UxJS/tVSDrUBenxdNyCBd0F2jgK/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 - asyncer-io/r2dbc-mysql: Reactive Relational Database Connectivity for MySQL. The official successor to mirromutth/r2dbc&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Reactive Relational Database Connectivity for MySQL. The official successor to mirromutth/r2dbc-mysql(dev.miku:r2dbc-mysql). - GitHub - asyncer-io/r2dbc-mysql: Reactive Relational Database Connecti...&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 style=&quot;background-color: #e6f5ff; color: #0070d1; text-align: start;&quot; href=&quot;https://spring.io/projects/spring-data-r2dbc&quot;&gt;Spring Data R2DBC&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1701665062676&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 R2DBC&quot; data-og-description=&quot;Spring Data R2DBC, part of the larger Spring Data family, makes it easy to implement R2DBC based repositories. R2DBC stands for Reactive Relational Database Connectivity, a specification to integrate SQL databases using reactive drivers. Spring Data R2DBC &quot; data-og-host=&quot;spring.io&quot; data-og-source-url=&quot;https://spring.io/projects/spring-data-r2dbc&quot; data-og-url=&quot;https://spring.io/projects/spring-data-r2dbc&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/brtftr/hyUIqV8mfV/fILvxIrKjhpBItgjZoVQK0/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/bzoeUX/hyUIuxskXN/mwOK4Zy3eqxa8GleC1W3wk/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://spring.io/projects/spring-data-r2dbc&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://spring.io/projects/spring-data-r2dbc&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/brtftr/hyUIqV8mfV/fILvxIrKjhpBItgjZoVQK0/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/bzoeUX/hyUIuxskXN/mwOK4Zy3eqxa8GleC1W3wk/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;Spring Data R2DBC&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Spring Data R2DBC, part of the larger Spring Data family, makes it easy to implement R2DBC based repositories. R2DBC stands for Reactive Relational Database Connectivity, a specification to integrate SQL databases using reactive drivers. Spring Data R2DBC&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;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;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;Spring Data R2DBC aims at being conceptually easy. In order to achieve this it does NOT offer caching, lazy loading, write behind or many other features of ORM frameworks. This makes Spring Data R2DBC a simple, limited, opinionated object mapper.&lt;br /&gt;&lt;br /&gt;it does NOT offer caching, lazy loading, write behind or many other features of ORM frameworks.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Spring에서는 R2DBC의 성격과 목적에 대해 공식페이지에 상기와 같이 정의하고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;요약하면, &lt;b&gt;캐싱, 지연로딩, 쓰기 지연등 ORM의 주요 기능을 제공하지 않는다는 의미이다.&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;즉, 우리가 JPA를 사용하면서 당연하게 사용했던 영속성 컨텍스트 기반의 유용한 기능들을 사용할 수 없다는 의미이다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;그럼 우리는 R2DBC를 어떤 기준으로 써야 하는가?&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;분명한 건 기존의 JPA에서 당연하게 지원했던&amp;nbsp; 영속성 컨텍스트 - persistent Context - 에서의 주요 기능들은 지원하지 않는다는 전제로 사용해야 되는 건 확실하다.&amp;nbsp; 여기서 JPA의 영속성 컨텍스트의 주요 기능에 대해서 잠깐 알아보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;JPA 영속성 컨텍스트의 주요기능&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;1차 캐시&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;영속성 데이터가 보관되는 캐시영역, 당연히 조회가 가능하며 1차 캐시에 없으면 DB에서 조회하여 1차 캐시에 저장하게 된다.&lt;/li&gt;
&lt;li&gt;영속성 컨텍스트는 기본적으로 내부에 Map 형태로 된 1차 캐시를 가지고 있고 아래의 구조로 영속성 데이터를 보관하게 된다.&lt;br /&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;key: @Id로 선언한 필드, 데이터베이스의 기본키와 매핑됨&lt;/li&gt;
&lt;li&gt;value: 엔티티 인스턴스&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;lazy loading (지연로딩)&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;JPA의 데이타 패치전략 중 하나로 엔티티가 실제 사용(참조)될 때까지 데이터베이스 조회를 지연(Lazy loading)시키는 방법이다.&lt;/li&gt;
&lt;li&gt;주제와는 상관없지만 JPA 지연로딩에 관련된 이슈에 대한 글을 참고하면 이해하는데 좀 더 도움을 될 것 같다.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://yonguri.tistory.com/73&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://yonguri.tistory.com/73&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;figure id=&quot;og_1701671693693&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;[스프링, 스프링부트]JPA 도입 - OneToOne 관계에서의 LazyLoading #1&quot; data-og-description=&quot;JPA를 쓰다 보면, DB설계시 자연스럽게 적용했던 테이블간 1:1 관계로 인한 예상치 못한 어려움과 혼돈을 겪는 경우가 발생한다. 이 글은 1:1 관계로 인해 발생하는 이슈들과 고민, 그에 따른 여러&quot; data-og-host=&quot;yonguri.tistory.com&quot; data-og-source-url=&quot;https://yonguri.tistory.com/73&quot; data-og-url=&quot;https://yonguri.tistory.com/73&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/t5so5/hyUFfosNXe/dkPKZieztXu0wE13PJ4HbK/img.png?width=800&amp;amp;height=442&amp;amp;face=0_0_800_442,https://scrap.kakaocdn.net/dn/comGqD/hyUIzFxTPl/oab68IXbQVTp0TL9mv1Ook/img.png?width=800&amp;amp;height=442&amp;amp;face=0_0_800_442,https://scrap.kakaocdn.net/dn/kA4Lo/hyUICvvikb/ZKRtWCYLTtUYbp7qtk7ujk/img.png?width=1386&amp;amp;height=766&amp;amp;face=0_0_1386_766&quot;&gt;&lt;a href=&quot;https://yonguri.tistory.com/73&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://yonguri.tistory.com/73&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/t5so5/hyUFfosNXe/dkPKZieztXu0wE13PJ4HbK/img.png?width=800&amp;amp;height=442&amp;amp;face=0_0_800_442,https://scrap.kakaocdn.net/dn/comGqD/hyUIzFxTPl/oab68IXbQVTp0TL9mv1Ook/img.png?width=800&amp;amp;height=442&amp;amp;face=0_0_800_442,https://scrap.kakaocdn.net/dn/kA4Lo/hyUICvvikb/ZKRtWCYLTtUYbp7qtk7ujk/img.png?width=1386&amp;amp;height=766&amp;amp;face=0_0_1386_766');&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;[스프링, 스프링부트]JPA 도입 - OneToOne 관계에서의 LazyLoading #1&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;JPA를 쓰다 보면, DB설계시 자연스럽게 적용했던 테이블간 1:1 관계로 인한 예상치 못한 어려움과 혼돈을 겪는 경우가 발생한다. 이 글은 1:1 관계로 인해 발생하는 이슈들과 고민, 그에 따른 여러&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;yonguri.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; color: #333333; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;write behind (쓰기 지연)&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;쓰기 지연은&amp;nbsp; 영속성 컨텍스트에 변경이 발생했을 때, 바로 데이터베이스로 쿼리를 보내지 않고 SQL 쿼리를 버퍼에 모아놨다가 영속성 컨텍스트가 flush 하는 시점에 모아둔 SQL 쿼리를 데이터베이스로 보내는 기능.&lt;/li&gt;
&lt;li&gt;이 기능은 DB와의 불필요한 커넥션을 최소화하기 위한 목적으로 flush 되는 시점은 해당 트랜잭션이 commit시점으로 보면 된다. 즉 쓰기지연은 정확히&amp;nbsp; transcational write behind이 다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;변경 감지(Dirty Checking)&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;변경감지기능은 영속성 컨텍스트상의 Entity에 대한 변경 사항을 추적하고 필요한 경우에만 해당 데이터베이스 레코드를 업데이트합니다. 이 기능 또한, 불필요한 데이터베이스 업데이트를 방지하고 수정된 필드만 업데이트하여 성능을 최적화하는 것이 주 목적이다.&lt;/li&gt;
&lt;li&gt;변경감지 메커니즘의 작동방식은 아래와 같다.
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;엔터티 검색: JPA를 사용하여 데이터베이스에서 엔터티를 검색하면 해당 엔터티의 상태가 지속성 컨텍스트에 로드됩니다.&lt;/li&gt;
&lt;li&gt;변경 감지: 엔터티의 속성을 변경하면 JPA는 지속성 컨텍스트 내에서 이러한 변경 사항을 추적합니다.&lt;/li&gt;
&lt;li&gt;트랜잭션 커밋: 트랜잭션을 커밋할 때(또는 지속성 컨텍스트를 명시적으로 플러시할 때) JPA는 지속성 컨텍스트 내 엔터티의 변경 사항을 확인합니다.&lt;/li&gt;
&lt;li&gt;데이터베이스&amp;nbsp;업데이트:&amp;nbsp;변경&amp;nbsp;사항이&amp;nbsp;감지되면(즉,&amp;nbsp;엔터티의&amp;nbsp;일부&amp;nbsp;속성이&amp;nbsp;수정됨)&amp;nbsp;JPA는&amp;nbsp;필요한&amp;nbsp;SQL&amp;nbsp;문을&amp;nbsp;생성하고&amp;nbsp;실행하여&amp;nbsp;해당&amp;nbsp;데이터베이스&amp;nbsp;레코드에서&amp;nbsp;수정된&amp;nbsp;필드만&amp;nbsp;업데이트합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 외에 JPA의 다른 특징들도 많지만 ORM Framework에서 얘기하는 중요한 기능들은 위의 4가지 기능이 대표적인 기능이라 할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제는 지금의 R2DBC 드라이버는 상기의 4가지 기능 대부분(?)을 지원하지 않는다는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대부분이라고 한 이유는 '변경 감지'는 단일키(pk)로 구성된 Entity에서는 지원하기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 외의 언급한 나머지 유용한 JPA관련 기능은 사용할 수 없다고 보면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;R2DBC 제약사항&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;JPA 사용불가&lt;/li&gt;
&lt;li&gt;복합키 미지원 - @EmbeddedId, @IdClass&lt;/li&gt;
&lt;li&gt;연관관계 표현 미지원 (Join 표현 불가, 1:N, N:1, 1:1)&lt;/li&gt;
&lt;li&gt;JSON 컬럼 지원 불가 (현재는 PostgreSQL 만 지원)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런&amp;nbsp;제약사항이&amp;nbsp;있음에도&amp;nbsp;불구하고,&amp;nbsp;WebFlux기반의&amp;nbsp;Database&amp;nbsp;연결을&amp;nbsp;Non-blocking으로&amp;nbsp;사용하려면&amp;nbsp;아직까지는&amp;nbsp;R2DBC&amp;nbsp;드라이버를&amp;nbsp;사용하는&amp;nbsp;방법밖에는&amp;nbsp;없다.&lt;br /&gt;&lt;br /&gt;분명&amp;nbsp;한계가&amp;nbsp;명확하고&amp;nbsp;아직&amp;nbsp;많이&amp;nbsp;부족한&amp;nbsp;R2DBC이지만&amp;nbsp;다른&amp;nbsp;대안이&amp;nbsp;없는&amp;nbsp;상황에서&lt;br /&gt; 자신의&amp;nbsp;애플리케이션&amp;nbsp;환경(Webflux&amp;nbsp;기반)에&amp;nbsp;맞게&amp;nbsp;최대한&amp;nbsp;활용가능한&amp;nbsp;방법들을&amp;nbsp;정리해&amp;nbsp;보았다.&lt;br /&gt;&lt;br /&gt;Best&amp;nbsp;Practice가&amp;nbsp;아닐&amp;nbsp;수&amp;nbsp;있겠지만&amp;nbsp;R2DBC&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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;⠀&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;복합키&amp;nbsp;처리&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;R2DBC의&amp;nbsp;복합키&amp;nbsp;지원은&amp;nbsp;기대하지&amp;nbsp;않는 게&amp;nbsp;좋을&amp;nbsp;것&amp;nbsp;같다.&amp;nbsp;깃헙이슈로&amp;nbsp;등록된 지&amp;nbsp;거의&amp;nbsp;5년이&amp;nbsp;지났는데도&amp;nbsp;아직&amp;nbsp;이슈는&amp;nbsp;Open상태이다.(2019년&amp;nbsp;등록)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/spring-projects/spring-data-relational/issues/574](&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://github.com/spring-projects/spring-data-relational/issues/574&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1710724199341&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 - spring-projects/spring-data-relational: Spring Data Relational. Home of Spring Data JDBC and Spring Data R2DBC.&quot; data-og-description=&quot;Spring Data Relational. Home of Spring Data JDBC and Spring Data R2DBC. - spring-projects/spring-data-relational&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/spring-projects/spring-data-relational/issues/574](&quot; data-og-url=&quot;https://github.com/spring-projects/spring-data-relational&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/c50uKk/hyVAKUJA90/L28sDTUYsBIBmnpuktmCDk/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-relational/issues/574](&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/spring-projects/spring-data-relational/issues/574](&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/c50uKk/hyVAKUJA90/L28sDTUYsBIBmnpuktmCDk/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 - spring-projects/spring-data-relational: Spring Data Relational. Home of Spring Data JDBC and Spring Data R2DBC.&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Spring Data Relational. Home of Spring Data JDBC and Spring Data R2DBC. - spring-projects/spring-data-relational&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;R2DBC가 복합키를 지원하지 않는다고 반드시 복합키로 설계해야 할 테이블을 변경할 수는 없다. 물론 단일키로 설계가 가능하면 좋겠지만 그렇지 않는 경우, 해당 테이블은 복합키로 설계할 수밖에 없고 그렇게 해야 한다.&lt;br /&gt;&lt;br /&gt;따라서 복합키로 생성된 테이블의 Entity는 키 정의를 할 수 없다. 즉 @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;당연히 @EmbeddedId , @IdClass 와 같은 복합키에 사용하는 어노테이션도 사용불가하다.&lt;br /&gt;결국 Spring Data에서 제공하는 기본 Repository API(ex. findById등)는 사용하지 못하고, Custom Query Method나 Native Query로 처리해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1710724313520&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;...

@Table(value = &quot;DEVICE_MASTER&quot;)
public class DeviceMaster implements Persistable&amp;lt;Tuple2&amp;lt;String, String&amp;gt;&amp;gt; {

  @Column(&quot;user_id&quot;)
  private String userId;
  @Column(&quot;device_id&quot;)
  private String deviceId;
  @Column(&quot;parent_device_id&quot;)
  private String parentDeviceId;
  @Column(&quot;device_type&quot;)
  private DeviceType deviceType;
  @Column(&quot;device_nickname&quot;)
  private String deviceNickname;

  ...
   
  @Transient
  @Builder.Default
  private boolean newProduct = false;

  @Override
  public Tuple2&amp;lt;String, String&amp;gt; getId() {
    return Tuples.of(this.userId, this.deviceId);
  }
  @Override
  public boolean isNew() {
    return this.newProduct;
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1710724399110&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Repository
public interface DeviceMasterRepository extends R2dbcRepository&amp;lt;DeviceMaster, Tuple2&amp;lt;String, String&amp;gt;&amp;gt; {

  Mono&amp;lt;DeviceMaster&amp;gt; findByUserIdAndDeviceId(String userId, String deviceId); 
  
  Flux&amp;lt;DeviceMaster&amp;gt; findByUserId(String userId);

  Flux&amp;lt;DeviceMasterDto&amp;gt; findByOwnerId(String ownerId);

  &amp;lt;T&amp;gt; Flux&amp;lt;T&amp;gt; findByUserId(String userId, Class&amp;lt;T&amp;gt; type);
  
  ...
  
}&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;Entity의&amp;nbsp;유일성을&amp;nbsp;보장할&amp;nbsp;Key를&amp;nbsp;정의할&amp;nbsp;수&amp;nbsp;없기&amp;nbsp;때문에&amp;nbsp;Spring&amp;nbsp;Data에서&amp;nbsp;제공하는&amp;nbsp;Pesistable&amp;nbsp;인터페이스를&amp;nbsp;구현하도록&amp;nbsp;해서&amp;nbsp;새로운&amp;nbsp;Entity여부를&amp;nbsp;코드레벨에서&amp;nbsp;판단할&amp;nbsp;수&amp;nbsp;있도록&amp;nbsp;처리해야&amp;nbsp;한다.&lt;br /&gt;&lt;br /&gt;이&amp;nbsp;말은&amp;nbsp;위에서도&amp;nbsp;얘기했다시피&amp;nbsp;JPA의&amp;nbsp;특징인&amp;nbsp;Dirty&amp;nbsp;Checking을&amp;nbsp;활용한&amp;nbsp;업데이트용으로의&amp;nbsp;sava&amp;nbsp;API는&amp;nbsp;사용은&amp;nbsp;불가하다는&amp;nbsp;의미이다.&lt;br /&gt;Persistable 인터페이스의 isNew()를 통해 코드에서 새로운 Entity임을 판단하고, insert의 목적으로만 save API를 사용할 수 있다.&lt;br /&gt;&lt;br /&gt;참고로&amp;nbsp;의미는&amp;nbsp;없지만&amp;nbsp;형식적으로나마&amp;nbsp;Tuple2&amp;nbsp;(Reactor&amp;nbsp;라이브러리)로&amp;nbsp;해당&amp;nbsp;Entity가&amp;nbsp;복합키임을&amp;nbsp; 나타낼&amp;nbsp;수는&amp;nbsp;있다.⠀&lt;/p&gt;
&lt;pre id=&quot;code_1710724436574&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;...
return deviceMasterRepository.findByUserIdAndDeviceId(reqDeviceMetaDto.getUserId(), reqDeviceMetaDto.getDeviceId())
    .defaultIfEmpty(newDeviceMaster)
    .flatMap(deviceMaster -&amp;gt; {
        if (!deviceMaster.isNew()) {
          ...
        }
        ...
    }).then(deviceAttributesRepository.save(newDeviceAttr))
...&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;update의 경우는 무조건 직접 쿼리를 실행하는 방법( @Query 또는 DatabaseClient 활용)으로만 가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;JSON 컬럼 처리&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MySQL은&amp;nbsp;5.7.8 버전부터&amp;nbsp;JSON&amp;nbsp;컬럼유형을&amp;nbsp;지원하기&amp;nbsp;시작했다.&lt;br /&gt;&lt;br /&gt;하지만&amp;nbsp;현재&amp;nbsp;R2DBC&amp;nbsp;Driver는&amp;nbsp;Postgre&amp;nbsp;SQL에서만&amp;nbsp;JSON컬럼&amp;nbsp;타입을&amp;nbsp;지원한다.&lt;br /&gt;MySQL에서 JSON컬럼을 사용해야 하는 경우, 별도의 Custom Converter를 활용하여 Json컬럼 각각을 개별적으로 변환처리하도록 해야 한다.&lt;br /&gt;&lt;br /&gt;커스텀&amp;nbsp;컨버터는&amp;nbsp;R2dbcCustomConversions&amp;nbsp;빈을&amp;nbsp;등록할 때&amp;nbsp;지정해&amp;nbsp;주면&amp;nbsp;된다.&lt;/p&gt;
&lt;pre id=&quot;code_1710724532749&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Configuration
@EnableR2dbcAuditing
@RequiredArgsConstructor
public class R2dbcConfig extends AbstractR2dbcConfiguration {

  private final ObjectMapper objectMapper;

  ...
  
  @Override
  public R2dbcCustomConversions r2dbcCustomConversions() {
    List&amp;lt;Converter&amp;lt;?, ?&amp;gt;&amp;gt; converters = new ArrayList&amp;lt;&amp;gt;();
    converters.add(new MapToJsonConverter(objectMapper));
    converters.add(new JsonToMapConverter(objectMapper));
    ...

    return new R2dbcCustomConversions(getStoreConversions(), converters);
  }
}&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;JSON&amp;nbsp;형태의&amp;nbsp;속성은&amp;nbsp;별도의&amp;nbsp;DTO(PoJo)로&amp;nbsp;정의해서&amp;nbsp;사용하나&amp;nbsp;그럴 경우&amp;nbsp;모든&amp;nbsp;DTO에&amp;nbsp;대해서&amp;nbsp;Converter를&amp;nbsp;만들어야&amp;nbsp;하는&amp;nbsp;문제가&amp;nbsp;있다.&amp;nbsp;좋은&amp;nbsp;방법은&amp;nbsp;아니나&amp;nbsp;JSON&amp;nbsp;타입은&amp;nbsp;기본적으로&amp;nbsp;Map으로&amp;nbsp;받을&amp;nbsp;수&amp;nbsp;있기&amp;nbsp;때문에&amp;nbsp;등록&amp;nbsp;또는&amp;nbsp;수정 시에는&amp;nbsp;MapToJsonConverter&amp;nbsp;하나로&amp;nbsp;모든&amp;nbsp;JSON칼럼에&amp;nbsp;대해서&amp;nbsp;처리하도록&amp;nbsp;하는&amp;nbsp;방법으로&amp;nbsp;사용할&amp;nbsp;수&amp;nbsp;있다.&lt;/p&gt;
&lt;pre id=&quot;code_1710724691790&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Slf4j
@WritingConverter
@AllArgsConstructor
public class MapToJsonConverter implements Converter&amp;lt;Map&amp;lt;String, Object&amp;gt;, String&amp;gt; {

    private final ObjectMapper objectMapper;

    @Override
    public String convert(@NotNull Map&amp;lt;String, Object&amp;gt; source) {
        try {
            return objectMapper.writeValueAsString(source);
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }
    }
}

@Slf4j
@ReadingConverter
@AllArgsConstructor
public class JsonToMapConverter implements Converter&amp;lt;String, Map&amp;lt;String, ?&amp;gt;&amp;gt; {
    private final ObjectMapper objectMapper;
    @Override
    public Map&amp;lt;String, ?&amp;gt; convert(@NotNull String jsonStr) {
        try {
            return objectMapper.readValue(jsonStr, new TypeReference&amp;lt;&amp;gt;() {});
        } catch (IOException e) {
            log.error(&quot;Problem while parsing JSON: {}&quot;, jsonStr, e);
        }
        return new HashMap&amp;lt;&amp;gt;();
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컨버터에&amp;nbsp;특별할&amp;nbsp;로직은&amp;nbsp;없다.&amp;nbsp;ObjectMapper를&amp;nbsp;사용하여&amp;nbsp;Map&amp;nbsp;to&amp;nbsp;String(JSON),&amp;nbsp;String(JSON)&amp;nbsp;to&amp;nbsp;Map으로&amp;nbsp;변환하는&amp;nbsp;로직이&amp;nbsp;전부이다.&amp;nbsp;Spring&amp;nbsp;Data&amp;nbsp;라이브러리에서&amp;nbsp;제공하는&amp;nbsp;@ReadingConverter&amp;nbsp;어노테이션을&amp;nbsp;붙이면&amp;nbsp;데이터베이스로부터&amp;nbsp;데이터를&amp;nbsp;읽어올&amp;nbsp;때만&amp;nbsp;컨버터가&amp;nbsp;적용되고,&amp;nbsp;@WritingConverter&amp;nbsp;를&amp;nbsp;붙이면&amp;nbsp;데이터베이스에&amp;nbsp;데이터를&amp;nbsp;입력할&amp;nbsp;때&amp;nbsp;컨버터가&amp;nbsp;적용된다.&lt;br /&gt;&lt;br /&gt;즉, MapToJsonConverter는 등록 또는 수정 시, JsonToConverter는 조회 시에 적용되게 된다.&amp;nbsp;&lt;br /&gt;물론 converter는 필요에 따라 더 추가할 수 있다.&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Query 수행방안&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단순한 조회 및 삭제등의 처리는 기존과 동일하게 Spring Data기반의 Query Method (R2dbcRepository 인테이스 기반)를 사용하면 된다. 하지만 단순한 조회라는 건, 조인이 일어나지 않는 단일 테이블을 참조하는 경우를 의미하는데, 일반적인 서비스에서는 이렇게 단순한 조회업무만 존재하지 않는다. 결국 Query자체가 코드에 포함되어야 하는 경우는 필수적으로 따라올 수밖에 없다.&lt;br /&gt;&lt;br /&gt;QueryDSL과&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;즉, ORM이 가지는 장점 중에 Query에 대한 Code Safe를 보장할 수 없는 코드가 생산될 수밖에 없다는 의미이다.&lt;br /&gt;Query를 직접 사용하는 방법은 크게 두 가지이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;@Query 어노테이션 사용&lt;/li&gt;
&lt;li&gt;R2dbc 의 DatabaseClient 사용 ( JdbcTemplate과 유사)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;@Query&amp;nbsp;어노테이션&amp;nbsp;사용&lt;/p&gt;
&lt;pre id=&quot;code_1710724974892&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Repository
public interface DeviceCustomRepository extends R2dbcRepository&amp;lt;DeviceMaster, Tuple2&amp;lt;String, String&amp;gt;&amp;gt; {

  @Query(
      value = &quot;
      select m.device_id as deviceId,
               m.parent_device_id as parentDeviceId,
               JSON_UNQUOTE(a.connection_info-&amp;gt;'$.connectType') as connectType,
               JSON_UNQUOTE(a.connection_info-&amp;gt;'$.target') as target
         from DEVICE_MASTER m INNER JOIN DEVICE_ATTRIBUTES a
          on m.parent_device_id = a.device_id
        where m.user_id = :userId
          and m.device_id = :deviceId
      &quot;, nativeQuery = true)
  Mono&amp;lt;DeviceConnectionMetaDto&amp;gt; findDeviceInfoForCommand(String userId, String deviceId);

  ...
}&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;R2dbc&amp;nbsp;의&amp;nbsp;DatabaseClient&amp;nbsp;사용&lt;/p&gt;
&lt;pre id=&quot;code_1710725011560&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Slf4j
@Repository
@RequiredArgsConstructor
public class DeviceCustomRepositoryImpl implements DeviceCustomRepository {

  private final DatabaseClient databaseClient;

  @Override
  public Mono&amp;lt;DeviceConnectionMetaDto&amp;gt; findDeviceInfoForCommand(String userId, String deviceId) {

    String sql = &quot;&quot;&quot;
        select m.device_id as deviceId,
               m.parent_device_id as parentDeviceId,
               JSON_UNQUOTE(a.connection_info-&amp;gt;'$.connectType') as connectType,
               JSON_UNQUOTE(a.connection_info-&amp;gt;'$.target') as target
         from DEVICE_MASTER m INNER JOIN DEVICE_ATTRIBUTES a
          on m.parent_device_id = a.device_id
        where m.user_id = ?
          and m.device_id = ?
        &quot;&quot;&quot;;

    return databaseClient.sql(sql)
        .bind(0, userId)
        .bind(1, deviceId)
        .map((row, rowMetadata) -&amp;gt; {
          return DeviceConnectionMetaDto.builder()
              .deviceId(row.get(&quot;deviceId&quot;, String.class))
              .userId(userId)
              .parentDeviceId(row.get(&quot;parentDeviceId&quot;, String.class))
              .connectType(row.get(&quot;connectType&quot;, String.class))
              .target(row.get(&quot;target&quot;, String.class))
              .build();
        }).one();
  }
  ...
  
}&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;참고로 DatabaseClient를 사용하게 되면 위에서 설명한 컨버터 설정은 적용되지 않는다. 컨버터를 설정하려면 각각의 컬럼에 수동으로 컨버터를 적용해야 한다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1710725647054&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;...
return databaseClient.sql(sql)
        .bind(0, userId)
        .bind(1, deviceId)
        .map((row, rowMetadata) -&amp;gt; {
              ...  
              .tags(new JsonToListConverter(objectMapper).convert((Objects.requireNonNull(row.get(&quot;tags&quot;, String.class)))))
              ...
              .properties(new JsonToDevicePropertyMapConverter(objectMapper).convert((Objects.requireNonNull(row.get(&quot;properties&quot;, String.class)))))
              .additionalInfo(new JsonToMapConverter(objectMapper).convert((Objects.requireNonNull(row.get(&quot;additional_info&quot;, String.class)))))
              .connectionInfo(new JsonToMapConverter(objectMapper).convert((Objects.requireNonNull(row.get(&quot;connection_info&quot;, String.class)))))
              ...
              ...
         }).one();
  }
...
...&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;복합키 관련해서 한 번 더 짚고 넘어가자면, R2DBC의 엔티티 상태에 대한 감지전략은 아래와 같다.⠀&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;엔티티의 식별자(&lt;b&gt;identifier&lt;/b&gt;)를 @Id 어노테이션이 붙은 속성으로 판단한다.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;save API사용 시, 식별자 속성값이 null이거나 기본 유형의 경우 0이면 해당 엔티티를&amp;nbsp; 새로운&amp;nbsp; 엔티티로 간주하고 &lt;b&gt;insert&lt;/b&gt;가 수행되고&amp;nbsp; 그렇지 않으면 존재하는 엔티티로 간주하고 save API사용시 &lt;b&gt;update&lt;/b&gt;가 수행된다.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;⠀⠀&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;결론&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;단순조회 - Spring Data의&amp;nbsp;&amp;nbsp;Query Method&amp;nbsp; 활용&amp;nbsp;&lt;/li&gt;
&lt;li&gt;복잡한 조회, 조인연산 - Spring R2dbc 의 DatabaseClient 사용, @Query 어노테이션 활용&lt;/li&gt;
&lt;li&gt;복합기 미지원으로 인해 save 사용 시 Persistable 인터페이스 구현, isNew활용
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Dirty Checking을 활용한 업데이트용으로의 save API사용은 불가&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정리할수록 이렇게까지 R2DBC를 써야 하는 생각이 들다가도 아직까지는 Webflux를 적용하면서 Datasource영역까지 논블로킹을 적용하기 위해서 딱히 다른 대안이 없기 때문에, 제약사항을 잘 확인하고 사용할 필요가 있겠다.&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;참고자료&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.spring.io/spring-data/relational/reference/r2dbc/getting-started.html&quot;&gt;Getting Started :: Spring Data Relational&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://medium.com/@nikola.babic1/mapping-to-json-fields-with-spring-data-r2dbc-and-reactive-postgres-driver-1db765067dc5&quot;&gt;https://medium.com/@nikola.babic1/mapping-to-json-fields-with-spring-data-r2dbc-and-reactive-postgres-driver-1db765067dc5&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;figure id=&quot;og_1710726086177&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;Mapping to JSON Fields with Spring Data R2DBC and Reactive Postgres Driver&quot; data-og-description=&quot;With the 0.8.0.rc1 release of r2dbc-postgres reactive driver we can now use JSON and JSONB database field types and map them to our custom&amp;hellip;&quot; data-og-host=&quot;medium.com&quot; data-og-source-url=&quot;https://medium.com/@nikola.babic1/mapping-to-json-fields-with-spring-data-r2dbc-and-reactive-postgres-driver-1db765067dc5&quot; data-og-url=&quot;https://medium.com/@nikola.babic1/mapping-to-json-fields-with-spring-data-r2dbc-and-reactive-postgres-driver-1db765067dc5&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/1IsuE/hyVALlOcw7/kMNKuuWAxDcX6ciYVNF2mK/img.jpg?width=1200&amp;amp;height=801&amp;amp;face=0_0_1200_801,https://scrap.kakaocdn.net/dn/X3MRo/hyVAHKshTY/2SZ3PXLIcSkuArmJQtWfR1/img.png?width=1358&amp;amp;height=787&amp;amp;face=0_0_1358_787,https://scrap.kakaocdn.net/dn/gVAlM/hyVAJarjA6/YtJxtF3HB5npBgUQ0zgPzk/img.jpg?width=1280&amp;amp;height=720&amp;amp;face=0_0_1280_720&quot;&gt;&lt;a href=&quot;https://medium.com/@nikola.babic1/mapping-to-json-fields-with-spring-data-r2dbc-and-reactive-postgres-driver-1db765067dc5&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://medium.com/@nikola.babic1/mapping-to-json-fields-with-spring-data-r2dbc-and-reactive-postgres-driver-1db765067dc5&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/1IsuE/hyVALlOcw7/kMNKuuWAxDcX6ciYVNF2mK/img.jpg?width=1200&amp;amp;height=801&amp;amp;face=0_0_1200_801,https://scrap.kakaocdn.net/dn/X3MRo/hyVAHKshTY/2SZ3PXLIcSkuArmJQtWfR1/img.png?width=1358&amp;amp;height=787&amp;amp;face=0_0_1358_787,https://scrap.kakaocdn.net/dn/gVAlM/hyVAJarjA6/YtJxtF3HB5npBgUQ0zgPzk/img.jpg?width=1280&amp;amp;height=720&amp;amp;face=0_0_1280_720');&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;Mapping to JSON Fields with Spring Data R2DBC and Reactive Postgres Driver&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;With the 0.8.0.rc1 release of r2dbc-postgres reactive driver we can now use JSON and JSONB database field types and map them to our custom&amp;hellip;&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;medium.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;figure id=&quot;og_1710726079429&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 :: Spring Data Relational&quot; data-og-description=&quot;Spring Data R2DBC uses a Dialect to encapsulate behavior that is specific to a database or its driver. Spring Data R2DBC reacts to database specifics by inspecting the ConnectionFactory and selects the appropriate database dialect accordingly. If you use a&quot; data-og-host=&quot;docs.spring.io&quot; data-og-source-url=&quot;https://docs.spring.io/spring-data/relational/reference/r2dbc/getting-started.html&quot; data-og-url=&quot;https://docs.spring.io/spring-data/relational/reference/r2dbc/getting-started.html&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://docs.spring.io/spring-data/relational/reference/r2dbc/getting-started.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://docs.spring.io/spring-data/relational/reference/r2dbc/getting-started.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;Getting Started :: Spring Data Relational&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Spring Data R2DBC uses a Dialect to encapsulate behavior that is specific to a database or its driver. Spring Data R2DBC reacts to database specifics by inspecting the ConnectionFactory and selects the appropriate database dialect accordingly. If you use 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>개발</category>
      <category>R2DBC</category>
      <category>spring</category>
      <category>Spring Data</category>
      <category>Spring WebFlux</category>
      <category>webflux</category>
      <author>yorath</author>
      <guid isPermaLink="true">https://yonguri.tistory.com/150</guid>
      <comments>https://yonguri.tistory.com/150#entry150comment</comments>
      <pubDate>Mon, 4 Dec 2023 15:39:59 +0900</pubDate>
    </item>
    <item>
      <title>Redis Stack 설정 - Install &amp;amp; Configuration</title>
      <link>https://yonguri.tistory.com/148</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1442&quot; data-origin-height=&quot;374&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/IYfMM/btr3c29NQ2j/Ck8QKpt6SnCfsSgC9mfJx0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/IYfMM/btr3c29NQ2j/Ck8QKpt6SnCfsSgC9mfJx0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/IYfMM/btr3c29NQ2j/Ck8QKpt6SnCfsSgC9mfJx0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FIYfMM%2Fbtr3c29NQ2j%2FCk8QKpt6SnCfsSgC9mfJx0%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;1442&quot; height=&quot;374&quot; data-origin-width=&quot;1442&quot; data-origin-height=&quot;374&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-renderer-start-pos=&quot;451&quot; data-ke-size=&quot;size16&quot;&gt;Redis Stack 1편에 이어 설치 및 기본적인 Configuration에서 대해서 살펴보겠습니다.&lt;/p&gt;
&lt;p data-renderer-start-pos=&quot;451&quot; data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://yonguri.tistory.com/147&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://yonguri.tistory.com/147&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1678342394227&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;Redis Stack #1 - 소개  (feat. Redis OM Spring)&quot; data-og-description=&quot;Redis Stack Redis Stack 이 나온지는 꽤 되었지만, 그 유용함과 확장의 성격에 비해 아직까지는 공유된 사례가 많지 않아 간략히 개요정도의 수준으로 소개해 봅니다. 개인적으로 Redis라는 기술 자체&quot; data-og-host=&quot;yonguri.tistory.com&quot; data-og-source-url=&quot;https://yonguri.tistory.com/147&quot; data-og-url=&quot;https://yonguri.tistory.com/147&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/K8yhS/hyRSJYSBud/nz3sPL4iBXK75k9XJ28Rx0/img.png?width=800&amp;amp;height=209&amp;amp;face=48_38_250_154,https://scrap.kakaocdn.net/dn/bewavo/hyRSUlNoXG/fmq8mTkRXiGFNFNCTmCjok/img.png?width=800&amp;amp;height=209&amp;amp;face=48_38_250_154,https://scrap.kakaocdn.net/dn/bVcnIZ/hyRSKcpqFD/K83Y08GmlWTtk9G7qietP1/img.png?width=1166&amp;amp;height=306&amp;amp;face=0_0_1166_306&quot;&gt;&lt;a href=&quot;https://yonguri.tistory.com/147&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://yonguri.tistory.com/147&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/K8yhS/hyRSJYSBud/nz3sPL4iBXK75k9XJ28Rx0/img.png?width=800&amp;amp;height=209&amp;amp;face=48_38_250_154,https://scrap.kakaocdn.net/dn/bewavo/hyRSUlNoXG/fmq8mTkRXiGFNFNCTmCjok/img.png?width=800&amp;amp;height=209&amp;amp;face=48_38_250_154,https://scrap.kakaocdn.net/dn/bVcnIZ/hyRSKcpqFD/K83Y08GmlWTtk9G7qietP1/img.png?width=1166&amp;amp;height=306&amp;amp;face=0_0_1166_306');&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;Redis Stack #1 - 소개 (feat. Redis OM Spring)&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Redis Stack Redis Stack 이 나온지는 꽤 되었지만, 그 유용함과 확장의 성격에 비해 아직까지는 공유된 사례가 많지 않아 간략히 개요정도의 수준으로 소개해 봅니다. 개인적으로 Redis라는 기술 자체&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;yonguri.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;h4 data-renderer-start-pos=&quot;451&quot; data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 id=&quot;Redis-Stack-설치&quot; data-renderer-start-pos=&quot;451&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Redis Stack Install &amp;amp; Run&lt;/b&gt;&lt;span&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-indent-level=&quot;1&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;설치방법은 여러가지가 있으나 개발 및 버전관리 편의성을 위해 Docker 기반의 설치방법만 다루겠습니다.&lt;/li&gt;
&lt;li&gt;Docker 기반의 Redis Stack 컨테이너는 두가지 유형으로 제공합니다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;redis/redis-stack&lt;/li&gt;
&lt;li&gt;redis/redis-stack-server&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;redis-stack에는 Redis Stack 서버와 GUI기반의 클라이언트 툴인 RedisInsight가 모두 포함되어 있습니다. 따라서 이 컨테이너는 내장된 RedisInsight를 사용하여 데이터를 시각화할 수 있으므로 로컬 개발에 가장 적합합니다.&lt;/li&gt;
&lt;li&gt;redis-stack-server는 Redis Stack 서버만 제공합니다. 이 컨테이너는 일반적인 프로덕션 배포에 가장 적합합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적으로는 개발시에는&amp;nbsp; redis-stack으로 설치해서 사용하면 되겠습니다.&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;div&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;docker run -d --name redis-stack -p 6379:6379 -p 8001:8001 redis/redis-stack:latest&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;포트는 redis-server의 기본포트인 6379와 redisInsight의 웹사이트 기본포트인 8001을 그대로 사용하도록 설정하였는데, 물론 다른 포트로 매핑해서 사용할 수 있습니다.&lt;/p&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2558&quot; data-origin-height=&quot;150&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cFh6Y9/btr2QLNN33j/arKLdIHJUOIbA92AzT1uY1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cFh6Y9/btr2QLNN33j/arKLdIHJUOIbA92AzT1uY1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cFh6Y9/btr2QLNN33j/arKLdIHJUOIbA92AzT1uY1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcFh6Y9%2Fbtr2QLNN33j%2FarKLdIHJUOIbA92AzT1uY1%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;2558&quot; height=&quot;150&quot; data-origin-width=&quot;2558&quot; data-origin-height=&quot;150&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;

&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물론&amp;nbsp; 일반적인 Redis 인스턴스에 연결할 때와 마찬가지로 redis-cli를 사용하여 서버에 연결할 수 있습니다.&lt;br /&gt;redis-cli가 로컬에 설치되어 있지 않은 경우, Docker 컨테이너에서 실행할 수 있습니다:&lt;/p&gt;
&lt;pre class=&quot;shell&quot;&gt;&lt;code&gt;$ docker exec -it redis-stack redis-cli&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;/div&gt;
&lt;figure id=&quot;og_1678343967480&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;Run Redis Stack on Docker&quot; data-og-description=&quot;How to install Redis Stack using Docker&quot; data-og-host=&quot;redis.io&quot; data-og-source-url=&quot;https://redis.io/docs/stack/get-started/install/docker/&quot; data-og-url=&quot;https://redis.io/docs/stack/get-started/install/docker/&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://redis.io/docs/stack/get-started/install/docker/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://redis.io/docs/stack/get-started/install/docker/&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;Run Redis Stack on Docker&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;How to install Redis Stack using Docker&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;redis.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;p data-renderer-start-pos=&quot;862&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 id=&quot;Redis-Stack-구성&quot; data-renderer-start-pos=&quot;864&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Redis Stack Configuration&lt;/b&gt;&lt;span&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;h4 id=&quot;Persistence-설정&quot; data-renderer-start-pos=&quot;880&quot; data-ke-size=&quot;size20&quot;&gt;Persistence 설정&lt;span&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-indent-level=&quot;1&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Redis 데이터를 별도로 로컬에 저장할 수 있는데 -v옵션을 통해 저장할 로컬 볼륨(디렉토리)를 지정할 수 있습니다. 아래의 명령어는&amp;nbsp; Redis에 생성되는 모든 데이터를 로컬 디렉터리 local-path에 저장하게 됩니다. Redis내부적으로 데이타가 저장되는 위치는 /DATA입니다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&amp;nbsp;-v [저장할 로컬경로]:/DATA&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;$ docker run -v /local-path/:/DATA redis/redis-stack:latest&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h4 id=&quot;Ports-포워딩&quot; data-renderer-start-pos=&quot;1171&quot; data-ke-size=&quot;size20&quot;&gt;Ports 포워딩&lt;span&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-indent-level=&quot;1&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;다른 포트로 Redis Stack 서버 또는 RedisInsight를 접급하려면 -p 옵션의 왼쪽 부분을 변경하면 됩니다. 아래의 명령은 포트 10001에 Redis Stack 서버를, 포트 13333에 RedisInsight를 지정하도록 설정한 상태입니다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-indent-level=&quot;2&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;기본값: Redis - 6379, RedisInsight - 8001&lt;/li&gt;
&lt;li&gt;-p [기존포트]:[변경포트]&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;$ docker run -p 10001:6379 -p 13333:8001 redis/redis-stack:latest&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h4 id=&quot;Config-files-설정&quot; data-renderer-start-pos=&quot;1570&quot; data-ke-size=&quot;size20&quot;&gt;Config files 설정&lt;span&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-indent-level=&quot;1&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;기본적으로 Redis Stack 컨테이너는 Redis와 마찬가지로 Redis의 내부 구성 파일(redis-stack.conf)을 사용합니다.&amp;nbsp; 이 또한 -v옵션을 사용하여 별도의 커스터마이징한 로컬 구성 파일로 Redsi Stack을 실행시킬 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;$ docker run -v /local-redis-stack.conf:/redis-stack.conf -p 6379:6379 -p 8001:8001 redis/redis-stack:latest&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h3 data-renderer-start-pos=&quot;1912&quot; data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 id=&quot;영속성-기반-및--커스텀-설정을-위한--레디스-구동-명령&quot; data-renderer-start-pos=&quot;1912&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Redis Stack 영속성&amp;nbsp; 및 커스텀 환경 설정&lt;/b&gt;&lt;span&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 Redis의 사상으로 보면 영속성 기반의 데이타 관리는 백업용을 위한 최소한의 용도도 사용하는 것이 적합할 것입니다. 또한 아래에 소개하는 영속성 모드의 설정값을 잘못 사용하는 경우, Redis Core의 퍼포먼스에 영향을 주게 됩니다.&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;때문에 대량의 트랜잭션과 I/O가 발생하는 서비스에서 실시간성 데이타를 다루기 위한 용도로 Redis를 사용하는 경우애는 아래의 옵션을 제대로 설정해서 사용해야 합니다.&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: #ee2323;&quot;&gt;Redis를 완전한 영속성을 띈 스토리지 목적으로 사용하기 위해 아래의 옵션을 사용하는 것은 권장하지 않습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;그런 목적이라면 범용 RDBMS나&amp;nbsp; 또는 파일기반(HDFS)의 Kafka와 같은 서비스를 검토하는것을 추천합니다.&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;Redis로 이런 방법으로 데이타의 영속성 보장이 가능하다 정도로만 참고하시면 좋을것 같습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 우선 Redis 데이타가 저장될&amp;nbsp; 로컬 저장경로와 파일를 설정합니다. 위의 Persistance 설명을 참고하시면 됩니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: disc;&quot; data-indent-level=&quot;1&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;컨테이너의&amp;nbsp; 마운트 폴더는 DATA로 지정합니다.&lt;/li&gt;
&lt;li&gt;Redis에 데이타가 생성되면 아래의 환경설정에 따라 지정된 로컬 파일에 데이타가 저장됩니다.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;도커가 구동될때 해당 로컬경로의 파일을 읽어들여 Redis에 로딩합니다.&amp;nbsp;&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 데이타 저장방식을 지정할 커스텀 설정파일을 구성합니다.&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: disc;&quot; data-indent-level=&quot;1&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;로컬에 환경설정 파일 생성하고 위의 Config file 설정 설명처럼 마운트 옵션(-v)를 사용하여 컨테이너 구성시 마운트를 시켜주면 됩니다.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;ex) /path/to/redis-stack.conf&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;아래는 Redis Stack의 환경설정파일의 항목 중 영속성 모드에 관련된 일부 항목들 입니다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre id=&quot;code_1678349518951&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;save 300 1
dbfilename redis-dump.rdb
dir /data
appendonly no&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-indent-level=&quot;1&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;save 300 1&lt;br /&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-indent-level=&quot;2&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;save [초] [변경Key수] - 300초동안 1개 이상의 Key에 대한 변동이 일어난 경우 데이타가 저장되도록 설정한 상태입니다.&lt;/li&gt;
&lt;li&gt;save 옵션에 따라 저장(동기화)주기가 변하기 때문에,&amp;nbsp; 주기를 짦게 할수록 Redis의 부하는 당연히 증가하면&amp;nbsp;&lt;br /&gt;위에서 언급한대로 이런 옵션들의 설정을 잘못한 경우, Redis 성능에 심각한 성능저하 및 부하를 발생시킬 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;dbfilename redis-dump.rdb&lt;/li&gt;
&lt;li&gt;dir /data&amp;nbsp;
&lt;ul style=&quot;list-style-type: disc;&quot; data-indent-level=&quot;2&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Redis 데이타가 저장될 파일이며 영속성 모드의 형태로 사용할 경우, 즉 데이타 파일을 별도의 로컬경로의 외부파일로 설정해서 마운트한 경우에는&amp;nbsp; Redis가 구동될때 해당파일이 마운트 되면서 파일은 내용이 로드됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;appendonly no&lt;br /&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-indent-level=&quot;2&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;데이타 저장시 rdb파일 저장방식만 사용하겠다는 의미입니다. yes로 할 경우,&amp;nbsp; AOF방식을 사용하게 됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-renderer-start-pos=&quot;2424&quot; data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;데이타 저장관련 속성들에 대한 자세한 정보는 아래 링크를 참조하면 좋을것 같습니다.&lt;/p&gt;
&lt;p data-renderer-start-pos=&quot;2424&quot; data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://hoooon-s.tistory.com/25&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://hoooon-s.tistory.com/25&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1678619783358&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;[Redis] AOF vs RDB - Redis 메모리 관리 방식 비교&quot; data-og-description=&quot;# RDB (Redis Database) 특정 시점(snapshot)의 메모리에 있는 데이터 전체를 바이너리 파일로 저장한다. AOF 파일보다 사이즈가 작다. 따라서 로딩 속도가 AOF 보다 빠르다. 저장 시점은 redis.conf의 save 파라&quot; data-og-host=&quot;hoooon-s.tistory.com&quot; data-og-source-url=&quot;https://hoooon-s.tistory.com/25&quot; data-og-url=&quot;https://hoooon-s.tistory.com/25&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/1ZcP3/hyRU2XNYxy/yZFfM9VpIA9svXfPvHpi0k/img.png?width=328&amp;amp;height=296&amp;amp;face=0_0_328_296,https://scrap.kakaocdn.net/dn/bFeWod/hyRU4VCf6h/z4yJq63L2olslWI5RtZGFK/img.png?width=328&amp;amp;height=296&amp;amp;face=0_0_328_296&quot;&gt;&lt;a href=&quot;https://hoooon-s.tistory.com/25&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://hoooon-s.tistory.com/25&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/1ZcP3/hyRU2XNYxy/yZFfM9VpIA9svXfPvHpi0k/img.png?width=328&amp;amp;height=296&amp;amp;face=0_0_328_296,https://scrap.kakaocdn.net/dn/bFeWod/hyRU4VCf6h/z4yJq63L2olslWI5RtZGFK/img.png?width=328&amp;amp;height=296&amp;amp;face=0_0_328_296');&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;[Redis] AOF vs RDB - Redis 메모리 관리 방식 비교&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;# RDB (Redis Database) 특정 시점(snapshot)의 메모리에 있는 데이터 전체를 바이너리 파일로 저장한다. AOF 파일보다 사이즈가 작다. 따라서 로딩 속도가 AOF 보다 빠르다. 저장 시점은 redis.conf의 save 파라&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;hoooon-s.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-renderer-start-pos=&quot;2424&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-renderer-start-pos=&quot;2424&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-renderer-start-pos=&quot;2424&quot; data-ke-size=&quot;size16&quot;&gt;위의 내용을 정리하면,&lt;br /&gt;아래의 명령어로 영속성 데이타 저장에 필요한 구성으로 Redis-Stack(Redis)를 구동할 수 있습니다.&lt;/p&gt;
&lt;p data-renderer-start-pos=&quot;2424&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1678619913451&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ docker run -v /path/to/redis-stack.conf:/redis-stack.conf -v /path/to:/DATA -p 6379:6379 -p 8001:8001 redis/redis-stack:latest&lt;/code&gt;&lt;/pre&gt;
&lt;p data-renderer-start-pos=&quot;2424&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-renderer-start-pos=&quot;2424&quot;&gt;-v /path/to/redis-stack.conf:/redis-stack.conf
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-renderer-start-pos=&quot;2424&quot;&gt;redis-stack.conf - 로컬에서 생성/설정한 conf 파일 (/path/to/redis-stack.conf)이 &lt;br /&gt;도커 컨테이너로 마운트 됨&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li data-renderer-start-pos=&quot;2424&quot;&gt;-v /path/to:/DATA
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-renderer-start-pos=&quot;2424&quot;&gt;Redis 데이타가 로컬경로(/path/to) 에 저장되고, 구동시 해당 파일의 데이타가 로드됨&lt;/li&gt;
&lt;li data-renderer-start-pos=&quot;2424&quot;&gt;저장되는 파일은 설정파일에서 설정한 'dbfilename redis-dump.rdb' 의 파일명으로 저장됨&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;고려사항&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-indent-level=&quot;2&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;캐시의 목적보다 DB(영속성 데이타의 보관)의 목적이 높고 성능에 크리티컬한 서비스가 아닐 경우에만 사용고려&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;RDB 와 AOF방식을 같이 쓰게되면 실시간 동기화가 가능하나, 서버에 많은 부하를 발생&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;적절한 SAVE 옵션 활용 필요&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-renderer-start-pos=&quot;2882&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 id=&quot;Redis-환경설정정보-확인&quot; data-renderer-start-pos=&quot;2884&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Redis 환경 설정&amp;nbsp; 확인&lt;/b&gt;&lt;span&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;redis&amp;nbsp;cli&amp;nbsp;에서&amp;nbsp;&lt;span style=&quot;color: #006dd7;&quot;&gt;info&amp;nbsp;server&lt;/span&gt; 명령어로 현재 Redis Server의 환경구성 정보를 확인할 수 있습니다.&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1678620226137&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;gt; info server


# Server
redis_version:6.2.7
redis_git_sha1:00000000
redis_git_dirty:0
redis_build_id:adcdff4a0d295794
redis_mode:standalone
os:Linux 5.10.124-linuxkit x86_64
arch_bits:64
monotonic_clock:POSIX clock_gettime
multiplexing_api:epoll
atomicvar_api:c11-builtin
gcc_version:9.4.0
process_id:9
process_supervised:no
run_id:8736ea715dc5983131b4d201d69523bc8fa00127
tcp_port:6379
server_time_usec:1664763230283469
uptime_in_seconds:85
uptime_in_days:0
hz:10
configured_hz:10
lru_clock:3818846
executable:/opt/redis-stack/bin/redis-server
config_file:/redis-stack.conf
io_threads_active:0&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;또는 config 명령어로 특정 환경설정 항목에 대한 설정값 확인이 가능하다.&lt;/p&gt;
&lt;pre id=&quot;code_1678620260977&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;gt; config get dir


1) &quot;dir&quot;
2) &quot;/data&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설정파일을 검색(get)해서 dir 속성에 대한 정보만 보여줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1678620341727&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;gt; config get save

1) &quot;save&quot;
2) &quot;300 1&quot;



&amp;gt; config get dbfilename

1) &quot;dbfilename&quot;
2) &quot;redis-dump.rdb&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;영속성 관련한 속성값을 위와 같이 config get [속성명]으로 확인 할 수 있습니다.&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;b&gt;Redis Insight&lt;/b&gt;&amp;nbsp;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 Redis를 사용하면서 Medis와 같은 일부 유료 툴을 제외하면 사용할 만한 클라이언트 툴이 거의 없었는데, Redis-Stack이 출시되면서 꽤 괜찮은 공식 클라이언트 툴이 나왔습니다.&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; Redis-Server를 설치하면 별도의 클라이언트 툴 설치 필요없이 웹브라우저로 접속하여 동일한 수준으로 사용이 가능합니다.&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://redis.io/docs/ui/insight/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://redis.io/docs/ui/insight/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1678620767794&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;RedisInsight&quot; data-og-description=&quot;Visualize and optimize Redis data&quot; data-og-host=&quot;redis.io&quot; data-og-source-url=&quot;https://redis.io/docs/ui/insight/&quot; data-og-url=&quot;https://redis.io/docs/ui/insight/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bc0may/hyRTOfMAEw/cIWIqJdGBzl61nWgcT4B6K/img.png?width=1903&amp;amp;height=976&amp;amp;face=0_0_1903_976,https://scrap.kakaocdn.net/dn/n9ksT/hyRVaO4VTF/WbFgSLg4RmfJ5YFhwhQ5BK/img.png?width=1890&amp;amp;height=968&amp;amp;face=0_0_1890_968&quot;&gt;&lt;a href=&quot;https://redis.io/docs/ui/insight/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://redis.io/docs/ui/insight/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bc0may/hyRTOfMAEw/cIWIqJdGBzl61nWgcT4B6K/img.png?width=1903&amp;amp;height=976&amp;amp;face=0_0_1903_976,https://scrap.kakaocdn.net/dn/n9ksT/hyRVaO4VTF/WbFgSLg4RmfJ5YFhwhQ5BK/img.png?width=1890&amp;amp;height=968&amp;amp;face=0_0_1890_968');&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;RedisInsight&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Visualize and optimize Redis data&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;redis.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;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존 유료 툴과 비교해도 크게 불편하지 않는 수준의 기능과 인터페이스를 제공하고 있기 때문에 Redis 기반 개발에 많은 도움이 될 것 같습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>개발</category>
      <category>redis</category>
      <category>Redis Client</category>
      <category>Redis Insight</category>
      <category>Redis Stack</category>
      <category>Redis 클리이언트</category>
      <category>SDR</category>
      <category>Spring Data Redis</category>
      <category>Spring OM</category>
      <category>Spring OM Redis</category>
      <category>레디스</category>
      <author>yorath</author>
      <guid isPermaLink="true">https://yonguri.tistory.com/148</guid>
      <comments>https://yonguri.tistory.com/148#entry148comment</comments>
      <pubDate>Sun, 5 Mar 2023 15:59:48 +0900</pubDate>
    </item>
    <item>
      <title>Redis Stack  소개  (feat. Redis OM Spring)</title>
      <link>https://yonguri.tistory.com/147</link>
      <description>&lt;h4 id=&quot;Redis-Stack&quot; data-renderer-start-pos=&quot;3&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Redis Stack&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Redis Stack 이 나온지는 꽤 되었지만, 그 유용함과 확장의 성격에 비해 아직까지는 공유된 사례가 많지 않아 간략히 개요정도의 수준으로 소개해 봅니다. 개인적으로 Redis라는 기술 자체를 좋아하고,&amp;nbsp; 지금 실무에도 너무나 유용하게 활용하고 있기 때문에 Redis Stack 또한 지금의 Redis가 제공하는 기능 이상의 흥미있고, 유용한 기능들을 제공해 줄 수 있다고 기대하고 있습니다.&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;1166&quot; data-origin-height=&quot;306&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cKNsfR/btr18EURvtj/AIuEgpuWluQXVwEpXLKHpk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cKNsfR/btr18EURvtj/AIuEgpuWluQXVwEpXLKHpk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cKNsfR/btr18EURvtj/AIuEgpuWluQXVwEpXLKHpk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcKNsfR%2Fbtr18EURvtj%2FAIuEgpuWluQXVwEpXLKHpk%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;1166&quot; height=&quot;306&quot; data-origin-width=&quot;1166&quot; data-origin-height=&quot;306&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;개요&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Redis의 확장서비스로 최신 데이타모델 지원과 처리 엔진(데이타 처리도구)을 제공, 기존 레디스 기능을 모두 포함하고 확장기능을 제공하기 위한 5개의 모듈을 탑재.&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-indent-level=&quot;2&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;RedisSearch&lt;/span&gt; -Full-Text 검색 지원&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;RedisJSON&lt;/span&gt; - 쿼리가능한 JSON 문서 지원&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;RedisGraph&lt;/span&gt; - Cyper 쿼리언어를 사용한 그래프 데이타 모델&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;RedisTimeSeries&lt;/span&gt; - 시계열 데이타 처리 (수집 및 쿼리)&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;RedisBloom&lt;/span&gt; - 확률적 데이타 구조&lt;/li&gt;
&lt;/ul&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;918&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/zWe5j/btr2aHRwke1/hhhmV8YhGJJd1hQG0Lyack/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/zWe5j/btr2aHRwke1/hhhmV8YhGJJd1hQG0Lyack/img.png&quot; data-alt=&quot;현재 포함된 모듈 버전 정보&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/zWe5j/btr2aHRwke1/hhhmV8YhGJJd1hQG0Lyack/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FzWe5j%2Fbtr2aHRwke1%2FhhhmV8YhGJJd1hQG0Lyack%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;774&quot; height=&quot;452&quot; data-origin-width=&quot;1572&quot; data-origin-height=&quot;918&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;현재 포함된 모듈 버전 정보&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Redis Stack을 구성하는 각 모듈들이 제공하는 서비스의 범위로 봤을때는 왠만한 상용 RDBMS가 제공하는 데이타의 관리기능 및 분석 기능들은 모두 커버할 만한 수준으로 보입니다. 특히 쿼리가능한 JSON 문서를 지원하는 &lt;b&gt;RedisJSON&lt;/b&gt;이나 Text Search가 가능한 RedisSearch, 시계열 데이타 처리에 사용되는 &lt;b&gt;RedisTimeSeries&lt;/b&gt;기능은 &lt;u&gt;실시간성 서비스를 필요로 하는 서비스에는 매우 유용할 것으로 생각됩니다.&lt;/u&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-indent-level=&quot;1&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li id=&quot;c594&quot; data-selectable-paragraph=&quot;&quot;&gt;결국은 Redis Stack은 왠만한 웹서비스의 Data영역 전체를 커버하는 수준의 기능을 제공하는 서비스로 볼 수 있으며, 기존의 Redis를 사용하는 주 목적이었던 순수한 캐시용도 또는 간단한 Pub/Sub 역할을 하는 메세지 브로커 역할로 사용하기에는 오버스펙인 서비스로 보입니다.&lt;br /&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;공식 사이트의 소개글을 보면 그 목적이 기존의 Redis와는 다름을 알 수 있습니다. &lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;Redis Stack was created to allow developers to build real-time applications with a backend data platform that can reliably process requests in milliseconds or less. Redis Stack does this by taking the original Redis OSS as the core and enhancing it with modern data models, data processing tools, and continuing to fight complexity at every turn. Ultimately, the goal of Redis Stack is to build a real-time data platform that continues to fulfill the philosophy of Redis OSS: simplicity, performance, and reliability.&lt;/blockquote&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;Redis Stack unifies and simplifies the developer experience of Redis by offering all the cutting-edge capabilities provided by the leading Redis modules. Redis Stack bundles the following capabilities into Redis:&amp;nbsp;&lt;a href=&quot;https://redis.io/docs/stack/json&quot;&gt;RedisJSON&lt;/a&gt;,&amp;nbsp;&lt;a href=&quot;https://redis.io/docs/stack/search&quot;&gt;RediSearch&lt;/a&gt;,&amp;nbsp;&lt;a href=&quot;https://redis.io/docs/stack/graph&quot;&gt;RedisGraph&lt;/a&gt;,&amp;nbsp;&lt;a href=&quot;https://redis.io/docs/stack/timeseries&quot;&gt;RedisTimeSeries&lt;/a&gt;, and&amp;nbsp;&lt;a href=&quot;https://redis.io/docs/stack/bloom&quot;&gt;RedisBloom&lt;/a&gt;.&lt;/blockquote&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;The overarching vision is to provide developers with a powerful platform for all real-time data use cases. As we continue to advance the capabilities of Redis beyond caching, Redis Stack is the place to start. Redis Stack delivers the core capabilities developers love about Redis and goes beyond to help you build modern applications where performance is paramount.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;요약(번역)하자면 이런 내용입니다.&lt;/span&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;가장 중요한 비전은 개발자에게 모든 &lt;b&gt;실시간 데이터&lt;/b&gt; 사용 사례를 위한 강력한 플랫폼을 제공하는 것입니다. 캐싱을 넘어 Redis의 기능을 지속적으로 발전시켜 나가고 있으며, 그 시작은 바로 Redis Stack입니다. Redis Stack은 개발자들이 좋아하는 Redis의 핵심 기능을 제공하며, 그 이상의 기능을 제공하여 성능이 가장 중요한 최신 애플리케이션을 구축할 수 있도록 지원합니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;
&lt;script src=&quot;https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js&quot;&gt;&lt;/script&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;script&gt;
     (adsbygoogle = window.adsbygoogle || []).push({});
&lt;/script&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;div&gt;
&lt;div&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Redis Service&lt;/b&gt;&lt;/h4&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;Redis Stack은&amp;nbsp; &amp;nbsp;Redis 엔진(Redis OSS) + 확장기능을 포함한 서비스로 결국 상용 플랫폼 서비스로 가기 위한 시작점이라고 볼 수 있겠습니다. 현재 Redis는 아래의 3가지 서비스로 구분해서 제공됩니다.&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://redis.io/download/#redis-downloads&quot;&gt;Redis OSS&lt;/a&gt;: the core engine for our software and services&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://redis.io/download/#redis-stack-downloads&quot;&gt;Redis Stack&lt;/a&gt;: the starting point for developers who want all the power of Redis OSS and the the latest innovations Redis, Inc. has to offer all in an easy to use software package.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://redis.com/redis-enterprise/advantages/&quot;&gt;Redis Enterprise&lt;/a&gt;: our commercial product. Redis Enterprise maintains the simplicity and high performance of Redis, while adding many enterprise-grade capabilities including&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Redis Enterprise의 경우, 상용서비스로 아래의 기능들과 더불어 클라우드 기반의 운영기능도 같이 제공합니다.&amp;nbsp;&lt;/p&gt;
&lt;/div&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Linear scaling to hundreds of millions of operations per second&lt;/li&gt;
&lt;li&gt;Improved high availability with up to 99.999% uptime&lt;/li&gt;
&lt;li&gt;Geo-replicated, active-active data distribution&lt;/li&gt;
&lt;li&gt;Data tiering&lt;/li&gt;
&lt;li&gt;Advanced security features&lt;/li&gt;
&lt;li&gt;Several deployment options (managed cloud service, software packages, K8s)&lt;/li&gt;
&lt;li&gt;24/7 support&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;본 글은 Redis Stack소개에 대한 내용을 다루기 때문에 Redis Enterprise나 클라우드에 대한 상세한 정보는 아래의 링크를 참고하시기 바랍니다.&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;&lt;a href=&quot;https://redis.com/redis-enterprise-cloud/overview/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://redis.com/redis-enterprise-cloud/overview/&lt;/a&gt;&lt;/div&gt;
&lt;figure id=&quot;og_1677992845509&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;Redis Enterprise Cloud &amp;ndash; Fully Managed Cloud Service | Redis&quot; data-og-description=&quot;Redis Enterprise Cloud provides performance, operational simplicity, data management, and security in the cloud.&quot; data-og-host=&quot;redis.com&quot; data-og-source-url=&quot;https://redis.com/redis-enterprise-cloud/overview/&quot; data-og-url=&quot;https://redis.com/redis-enterprise-cloud/overview/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/oTTPG/hyRQsCEwn9/QCi07sIq4vnj7dP0XbKns1/img.jpg?width=1200&amp;amp;height=628&amp;amp;face=0_0_1200_628&quot;&gt;&lt;a href=&quot;https://redis.com/redis-enterprise-cloud/overview/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://redis.com/redis-enterprise-cloud/overview/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/oTTPG/hyRQsCEwn9/QCi07sIq4vnj7dP0XbKns1/img.jpg?width=1200&amp;amp;height=628&amp;amp;face=0_0_1200_628');&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;Redis Enterprise Cloud &amp;ndash; Fully Managed Cloud Service | Redis&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Redis Enterprise Cloud provides performance, operational simplicity, data management, and security in the cloud.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;redis.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;div&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Redis Client Library ( For Spring ORM )&lt;/b&gt;&lt;/h4&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Redis와 마찬가지로 여러 언어기반 Redis Client 모듈을 지원합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-indent-level=&quot;2&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;.NET, java(Spring), python, node&lt;/li&gt;
&lt;li&gt;&lt;span data-inline-card=&quot;true&quot; data-card-url=&quot;https://redis.io/docs/stack/get-started/clients/&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span data-testid=&quot;hover-card-trigger-wrapper&quot;&gt;&lt;a href=&quot;https://redis.io/docs/stack/get-started/clients/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://redis.io/docs/stack/get-started/clients/&lt;/a&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span data-inline-card=&quot;true&quot; data-card-url=&quot;https://redis.io/docs/stack/get-started/clients/&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span data-testid=&quot;hover-card-trigger-wrapper&quot;&gt;&lt;span data-testid=&quot;inline-card-icon-and-title&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;figure id=&quot;og_1677991601980&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;Redis Stack clients&quot; data-og-description=&quot;Client libraries supporting Redis Stack&quot; data-og-host=&quot;redis.io&quot; data-og-source-url=&quot;https://redis.io/docs/stack/get-started/clients/&quot; data-og-url=&quot;https://redis.io/docs/stack/get-started/clients/&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://redis.io/docs/stack/get-started/clients/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://redis.io/docs/stack/get-started/clients/&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;Redis Stack clients&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Client libraries supporting Redis Stack&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;redis.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;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;특히, Java의 경우 Spring Data기반 고수준(High-Level)의 API를 제공하고 있기 때문에 기존 Data Access 처리와 동일한 형태로 Repository와 Data Model 클래스를 구현할 수 있습니다.&amp;nbsp;&amp;nbsp;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/redis/redis-om-spring&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://github.com/redis/redis-om-spring&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1677993108333&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 - redis/redis-om-spring: Spring Data Redis extensions for better search, documents models, and more&quot; data-og-description=&quot;Spring Data Redis extensions for better search, documents models, and more - GitHub - redis/redis-om-spring: Spring Data Redis extensions for better search, documents models, and more&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/redis/redis-om-spring&quot; data-og-url=&quot;https://github.com/redis/redis-om-spring&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/Tu6fz/hyROJF1RGL/Nnt3Os8iQZd2Lm6s4viu00/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/redis/redis-om-spring&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/redis/redis-om-spring&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/Tu6fz/hyROJF1RGL/Nnt3Os8iQZd2Lm6s4viu00/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 - redis/redis-om-spring: Spring Data Redis extensions for better search, documents models, and more&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Spring Data Redis extensions for better search, documents models, and more - GitHub - redis/redis-om-spring: Spring Data Redis extensions for better search, documents models, and more&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;특히 SpringBoot 기반 프로젝트일 경우, 간단한 어노테이션 설정만으로&amp;nbsp; Spring ORM기반의 추상화된 SDR(Spring Data Redis) 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;
&lt;script src=&quot;https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js&quot;&gt;&lt;/script&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;script&gt;
     (adsbygoogle = window.adsbygoogle || []).push({});
&lt;/script&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;SpringBoot Main 클래스&lt;/p&gt;
&lt;pre id=&quot;code_1677993430419&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@SpringBootApplication
@Configuration
@EnableRedisDocumentRepositories(basePackages = &quot;com.redis.om.documents.*&quot;)
public class RedisOrmApplication {
	public static void main(String[] args) {
    	SpringApplication.run(RedisOrmApplication.class, args);
  	}
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #292c32;&quot;&gt;EnableRedisDocumentRepositories 어노테이션을 사용하여 @Document 어노테이션이 있는 Spring 모델 객체의 Scan이 가능해집니다. 그리고 CRUD 작업 및 사용자 정의 쿼리(모두 Spring 데이터 쿼리 인터페이스를 선언하여)에 사용할 수 있는RedisDocumentRepository 인터페이스를 구현하는 리포지토리 빈을 주입하여 비즈니스 로직을 구현하게 됩니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #ef5369;&quot;&gt;즉, @&lt;span style=&quot;background-color: #ffffff;&quot;&gt;EnableRedisDocumentRepositories 어노테이션만 추가하면 Redis를 SDR기반으로 사용할 준비는 끝나게 됩니다.&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;Domain Model 클래스&lt;/p&gt;
&lt;pre id=&quot;code_1677993601208&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package com.redis.om.documents.domain;

import java.util.HashSet;
import java.util.Set;

import org.springframework.data.annotation.Id;
import org.springframework.data.geo.Point;
import com.redis.om.spring.annotations.Document;
import com.redis.om.spring.annotations.Searchable;
import lombok.*;

@Data 
@NoArgsConstructor 
@RequiredArgsConstructor(staticName = &quot;of&quot;) 
@AllArgsConstructor(access = AccessLevel.PROTECTED) 
@Document 
public class Company {
  @Id private String id;
  @Searchable private String name;
  @Indexed private Point location;
  @Indexed private Set&amp;lt;String&amp;gt; tags = new HashSet&amp;lt;&amp;gt;();
  @Indexed private Integer numberOfEmployees;
  @Indexed private Integer yearFounded;
  private String url;
  private boolean publiclyListed;

  // ...
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #292c32;&quot;&gt; Redis OM Spring은 클래스 수준의 @Document 어노테이션을 제공하여 해당 어노테이션이 적용된 클래스 객체를 RedisJSON구조의 객체로 매핑해줍니다. 즉 JSON기반의 구조로 데이타가 관리되며 RedisJSON이 제공하는 쿼리기반의 JSON 문서기반을 가능하게 해줍니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #292c32;&quot;&gt;&lt;a href=&quot;https://redis.io/docs/stack/json/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://redis.io/docs/stack/json/&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1678001949941&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;RedisJSON&quot; data-og-description=&quot;JSON support for Redis&quot; data-og-host=&quot;redis.io&quot; data-og-source-url=&quot;https://redis.io/docs/stack/json/&quot; data-og-url=&quot;https://redis.io/docs/stack/json/&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://redis.io/docs/stack/json/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://redis.io/docs/stack/json/&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;RedisJSON&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;JSON support for Redis&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;redis.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;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #292c32;&quot;&gt;@Searchable이나 @Indexed와 같은 어노테이션이 그런 기능을 가능하게 해줍니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://redis.io/docs/stack/search/indexing_json/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://redis.io/docs/stack/search/indexing_json/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1678002042357&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;Index and search JSON documents&quot; data-og-description=&quot;How to index and search JSON documents&quot; data-og-host=&quot;redis.io&quot; data-og-source-url=&quot;https://redis.io/docs/stack/search/indexing_json/&quot; data-og-url=&quot;https://redis.io/docs/stack/search/indexing_json/&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://redis.io/docs/stack/search/indexing_json/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://redis.io/docs/stack/search/indexing_json/&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;Index and search JSON documents&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;How to index and search JSON documents&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;redis.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;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #292c32;&quot;&gt;@Id 어노테이션은 식별자(Identifier)를 생성하기 위한 어노테이션으로 RDMS의 테이블의 PK(Primary Key)와 유사한 역할을 합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #292c32;&quot;&gt;해당 어노테이션의 붙은 변수가 String 유형으로 정의되면 &lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #292c32;&quot;&gt;Redis OM Spring은 기존의 UUID 기본키 생성전략보다 키 생성속도가 빠르고 보기쉬운 ULID(&lt;span style=&quot;background-color: #ffffff; color: #24292f;&quot;&gt;Universally Unique Lexicographically Sortable Identifier&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;Repository 클래스&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1677993624383&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package com.redis.om.documents.repositories;

import java.util.*;

import org.springframework.data.geo.Distance;
import org.springframework.data.geo.Point;
import org.springframework.data.repository.query.Param;

import com.redis.om.documents.domain.Company;
import com.redis.om.spring.annotations.Query;
import com.redis.om.spring.repository.RedisDocumentRepository;

public interface CompanyRepository extends RedisDocumentRepository&amp;lt;Company, String&amp;gt; {
  // find one by property
  Optional&amp;lt;Company&amp;gt; findOneByName(String name);

  // geospatial query
  Iterable&amp;lt;Company&amp;gt; findByLocationNear(Point point, Distance distance);

  // find by tag field, using JRediSearch &quot;native&quot; annotation
  @Query(&quot;@tags:{$tags}&quot;)
  Iterable&amp;lt;Company&amp;gt; findByTags(@Param(&quot;tags&quot;) Set&amp;lt;String&amp;gt; tags);

  // find by numeric property
  Iterable&amp;lt;Company&amp;gt; findByNumberOfEmployees(int noe);

  // find by numeric property range
  Iterable&amp;lt;Company&amp;gt; findByNumberOfEmployeesBetween(int noeGT, int noeLT);

  // starting with/ending with
  Iterable&amp;lt;Company&amp;gt; findByNameStartingWith(String prefix);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-renderer-start-pos=&quot;451&quot; data-ke-size=&quot;size16&quot;&gt;다른 Spring 데이터 리포지토리와 마찬가지로, Redis OM Spring 데이터 리포지토리의 Interface 생성방식도 거의 동일합니다.&lt;/p&gt;
&lt;p data-renderer-start-pos=&quot;451&quot; data-ke-size=&quot;size16&quot;&gt;Repository의 대상이 되는 Domain 클래스와 Domain 클래스의 ID 유형(@Id 어노테이션이 선언된 변수의 데이타유형)을 인수로 사용하는 RedisDocumentRepository를 상속받은&amp;nbsp;인터페이스를 생성하면 됩니다.&lt;/p&gt;
&lt;p data-renderer-start-pos=&quot;451&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-renderer-start-pos=&quot;451&quot; data-ke-size=&quot;size16&quot;&gt;그리고 Query Name기반의 메소드를 정의하면 됩니다. 추가적으로 @Query나 SDR이 제공하는 어노테이션을 사용하여 커스텀하게 쿼리를 제너레이션 할 수도 있습니다.&lt;/p&gt;
&lt;p data-renderer-start-pos=&quot;451&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-renderer-start-pos=&quot;451&quot; data-ke-size=&quot;size16&quot;&gt;참고로 RedisDocumentRepository 클래스는 Spring 데이터 클래스 PagingAndSortingRepository를 확장한 클래스입니다. 즉, Spring 의 Pageble 객체를 사용해서 페이징 처리가 가능하다는 의미입니다.&lt;/p&gt;
&lt;p data-renderer-start-pos=&quot;451&quot; data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;인터페이스에 쿼리 메서드를 선언합니다. 둘 다, CRUD 메서드를 노출하거나 런타임에 Redis OM Spring이 수행할 복잡한 쿼리에 대한 선언을 생성할 수 있습니다.&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;실제 리포지토리 프록시는 메서드 명으로 부터 Query를 도출할때 두 가지 방법을 사용해서 도출합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-renderer-start-pos=&quot;451&quot;&gt;메서드 이름에서 쿼리를 직접 파생하는 방법&amp;nbsp;&lt;/li&gt;
&lt;li data-renderer-start-pos=&quot;451&quot;&gt;@Query 또는 @Aggregation 어노테이션을 사용하여 수동으로 정의한 쿼리를 사용합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;글을 적다 보니 Redis Stack 내용보다 Redis OM Spring에 대한 내용이 더 많이 나온 것 같습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음편(#2)에는 Redis Stack 설치 및 기본적인 환경구성에 대해서 다루어 보도록 하겠습니다.&lt;/p&gt;
&lt;p data-renderer-start-pos=&quot;4284&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-renderer-start-pos=&quot;4284&quot; data-ke-size=&quot;size16&quot;&gt;그리고 &lt;span&gt;&lt;span&gt;&amp;nbsp;기회가 된다면&amp;nbsp; Redis OM Spring에&amp;nbsp;&lt;/span&gt;대해서는 따로 주제를 만들어 추후에 기회가 되면 좀 더 깊게 다루어 보도록 하겠습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-renderer-start-pos=&quot;4284&quot; 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;script src=&quot;https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js&quot;&gt;&lt;/script&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;script&gt;
     (adsbygoogle = window.adsbygoogle || []).push({});
&lt;/script&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 id=&quot;Redis-Insight&quot; data-renderer-start-pos=&quot;4155&quot; data-ke-size=&quot;size23&quot;&gt;참고 문서&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Reis Stack 공식사이트 - &lt;a href=&quot;https://redis.io/docs/stack/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://redis.io/docs/stack/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1677991840928&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;Redis Stack&quot; data-og-description=&quot;Extends Redis with modern data models and processing engines&quot; data-og-host=&quot;redis.io&quot; data-og-source-url=&quot;https://redis.io/docs/stack/&quot; data-og-url=&quot;https://redis.io/docs/stack/&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://redis.io/docs/stack/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://redis.io/docs/stack/&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;Redis Stack&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Extends Redis with modern data models and processing engines&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;redis.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Redis OM Spring - &lt;a href=&quot;https://redis.io/docs/stack/get-started/tutorials/stack-spring/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://redis.io/docs/stack/get-started/tutorials/stack-spring/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1678000051106&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;Redis OM Spring&quot; data-og-description=&quot;Learn how to build with Redis Stack and Spring&quot; data-og-host=&quot;redis.io&quot; data-og-source-url=&quot;https://redis.io/docs/stack/get-started/tutorials/stack-spring/&quot; data-og-url=&quot;https://redis.io/docs/stack/get-started/tutorials/stack-spring/&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://redis.io/docs/stack/get-started/tutorials/stack-spring/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://redis.io/docs/stack/get-started/tutorials/stack-spring/&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;Redis OM Spring&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Learn how to build with Redis Stack and Spring&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;redis.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;</description>
      <category>개발</category>
      <category>ORM</category>
      <category>redis</category>
      <category>Redis OM Spring</category>
      <category>Redis Stack</category>
      <category>SDR</category>
      <category>Spring Data Redis</category>
      <category>Spring ORM</category>
      <category>SpringBoot</category>
      <category>레디스</category>
      <category>레디스 스택</category>
      <author>yorath</author>
      <guid isPermaLink="true">https://yonguri.tistory.com/147</guid>
      <comments>https://yonguri.tistory.com/147#entry147comment</comments>
      <pubDate>Sun, 5 Mar 2023 15:57:06 +0900</pubDate>
    </item>
    <item>
      <title>[API] Postman 팁 - Pre-request Script 활용</title>
      <link>https://yonguri.tistory.com/146</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;이전에 포스트맨의 Test 기능, 정확하게 얘기하면 &lt;b&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;요청 이 후&lt;/span&gt;&lt;/b&gt;의 Test Script실행으로&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;API공통으로 사용되는 환경변수의 값을 설정하는 방법을 통해, 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;&lt;a href=&quot;https://yonguri.tistory.com/140&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://yonguri.tistory.com/140&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1646128977821&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;Postman의 tests 기능을 활용한 환경변수 자동 세팅방법&quot; data-og-description=&quot;Rest API를 개발하는 경우, 이제는 백앤드 업무영역은 언어에 상관없이 포스트맨의 사용은 거의 필수가 되었다고 할 수 있습니다. 포스트맨을 어떻게 사용하느냐에 따라 개발 생산성에 큰 차이를 &quot; data-og-host=&quot;yonguri.tistory.com&quot; data-og-source-url=&quot;https://yonguri.tistory.com/140&quot; data-og-url=&quot;https://yonguri.tistory.com/140&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/tpuz1/hyNzQaYRdL/gSSebk2hYIhQinyLGP0FsK/img.png?width=391&amp;amp;height=434&amp;amp;face=0_0_391_434,https://scrap.kakaocdn.net/dn/cFimOq/hyNzYGSJgE/YKGkZXEtUfkEf9UYo8fo9K/img.png?width=391&amp;amp;height=434&amp;amp;face=0_0_391_434,https://scrap.kakaocdn.net/dn/xY6lq/hyNzNeguEC/wWaNOSDt3ZdyQOnJK5deXK/img.png?width=1367&amp;amp;height=948&amp;amp;face=0_0_1367_948&quot;&gt;&lt;a href=&quot;https://yonguri.tistory.com/140&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://yonguri.tistory.com/140&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/tpuz1/hyNzQaYRdL/gSSebk2hYIhQinyLGP0FsK/img.png?width=391&amp;amp;height=434&amp;amp;face=0_0_391_434,https://scrap.kakaocdn.net/dn/cFimOq/hyNzYGSJgE/YKGkZXEtUfkEf9UYo8fo9K/img.png?width=391&amp;amp;height=434&amp;amp;face=0_0_391_434,https://scrap.kakaocdn.net/dn/xY6lq/hyNzNeguEC/wWaNOSDt3ZdyQOnJK5deXK/img.png?width=1367&amp;amp;height=948&amp;amp;face=0_0_1367_948');&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;Postman의 tests 기능을 활용한 환경변수 자동 세팅방법&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Rest API를 개발하는 경우, 이제는 백앤드 업무영역은 언어에 상관없이 포스트맨의 사용은 거의 필수가 되었다고 할 수 있습니다. 포스트맨을 어떻게 사용하느냐에 따라 개발 생산성에 큰 차이를&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;yonguri.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 방법보다 조금 더 편리하고 적절한&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;Pre-request Script를 사용하는 방법인데,&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;API 실행전 수행해야 할 스크립트를 &lt;span&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;컬렉션 또는 컬렉션내의 폴더 단위로&lt;/b&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;Test기능과의 차이점은 명확합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Pre-request Script는 말 그대로 &lt;b&gt;API 호출 전 실행되는 스크립트&lt;/b&gt;이며&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Tests의 스크립트는 &lt;b&gt;API 호출 후 실행되는 스크립트입니다.&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;따라서, API 호출 전 공통의 특정 작업을 수행하도록 하는 방법에는 당연히 Tests 보다 지금 소개하는 Pre-request Script기능을 사용하는 게 맞습니다.&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://learning.postman.com/docs/writing-scripts/pre-request-scripts/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://learning.postman.com/docs/writing-scripts/pre-request-scripts/&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;사용목적&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적으로 특정 사용자에게 서비스를 제공하기 위한&amp;nbsp; API (REST API)는 대부분,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;본 API의 기능을 제공하기 전,&amp;nbsp; 인증/인가 등(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;실제의 인증서비스가 붙지 않는 단계인&amp;nbsp; API 개발 및 테스트 단계에서는,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각각의 API 호출시마다&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;또한, 위의 경우 외에도 컬렉션 또는 폴더 내의 모든 API들의 헤더 값을 특정값으로 지정해야 하거나&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;테스트를 위해서&amp;nbsp; 요청 항목들의 변경 작업들이 필요한 경우에는 해당 Pre-Request Script 기능은 매우 훌륭한 기능을 제공합니다.&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;h4 data-ke-size=&quot;size20&quot;&gt;사용방법&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2692&quot; data-origin-height=&quot;1794&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/wA0ii/btruLukl6YG/D3CzzSoHwNjYQlayfIoR31/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/wA0ii/btruLukl6YG/D3CzzSoHwNjYQlayfIoR31/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/wA0ii/btruLukl6YG/D3CzzSoHwNjYQlayfIoR31/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FwA0ii%2FbtruLukl6YG%2FD3CzzSoHwNjYQlayfIoR31%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;2692&quot; height=&quot;1794&quot; data-origin-width=&quot;2692&quot; data-origin-height=&quot;1794&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;몇 가지 탭이 우측에 나타나는데 거기에서 Pre-request Script를 탭을 클릭합니다.&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;/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_1646130015057&quot; class=&quot;pgsql&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;pm.sendRequest({
    url: pm.environment.get('auth-url')+'/auth/token',
    method: 'POST',
    header: {
    'Accept': 'application/json',
    'Content-Type': 'application/x-www-form-urlencoded'
    },
    body: {
        mode: 'urlencoded',
        urlencoded: [
        {
            key: &quot;grant_type&quot;, 
            value: &quot;client_credentials&quot;, 
            disabled: false
        },
        {
            key: &quot;client_id&quot;, 
            value: &quot;xxxxx&quot;, 
            disabled: false
        },
        {
            key: &quot;client_secret&quot;, 
            value: &quot;xxxx&quot;, 
            disabled: false
        }
    ]
    }
}, function (err, res) {
    res_data = res.json();
    console.log(res_data);

    // 환경변수 값 세팅 
    pm.environment.set(&quot;access_token&quot;, res_data.access_token);

    // 헤더설정
    // pm.request.headers.add({
    //     key: &quot;Authorization&quot;,
    //     value: res_data.access_token
    // });

});&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;p data-ke-size=&quot;size16&quot;&gt;1. sendRequest를 사용해서 API 호출 전 인증 API를 먼저 호출하게 합니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 842px;&quot;&gt;pm.sendRequest({&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;url:&amp;nbsp;pm.environment.get('auth-url')+'/auth/token',&lt;br /&gt;...&lt;br /&gt;...&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 컬렉션 환경변수 중 access_token값에 &lt;span&gt;인증 API의 호출로 반환된 응답항목 중 token값을 지정합니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1646130559161&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function (err, res) {
    res_data = res.json();
    console.log(res_data);

    // 환경변수 값 세팅 
    pm.environment.set(&quot;access_token&quot;, res_data.access_token);
    ...
    ...
}&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;res.json()을 통해 응답 값을 json객체로 변환 후,&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;콘솔에 출력하고 pm.environment.set() 함수를 통해 포스트맨의 환경변수 access_token항목에 값을 지정합니다.&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;이 access_token은 컬렉션 또는 폴더 내의 모든 API에서 필수로 사용하는 파라미터로&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 스크립트를 통해 API들이 호출될 때마다 실행되어 인증 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;&lt;b&gt;API 호출마다 인증 API를 별도로 호출해서 토큰 값을 따로 지정해야 할 필요가 없습니다.&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고로, console.log()를 사용하면 실제 호출 결과 데이터 값을 포스트맨의 콘솔로 확인할 수 있습니다.&lt;/p&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;502&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b2klRE/btruPbxAQ9U/gms7jhHrSQSxhptnU4R43K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b2klRE/btruPbxAQ9U/gms7jhHrSQSxhptnU4R43K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b2klRE/btruPbxAQ9U/gms7jhHrSQSxhptnU4R43K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb2klRE%2FbtruPbxAQ9U%2Fgms7jhHrSQSxhptnU4R43K%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;502&quot; data-origin-width=&quot;2096&quot; data-origin-height=&quot;502&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;포스트맨의 스크립트는 그 문법이 일반 JavaScript로 거의 유사하기 때문에 큰 어려움 없이 사용할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자주 사용되는 함수나 코드는&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;snippet을 제공해 주기 때문에 해당 기능을&amp;nbsp; 활용하면 좀 더 편하게 사용이 가능합니다.&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;494&quot; data-origin-height=&quot;884&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/IDjPe/btruE8O8Q6E/Zpi94yiYL809FKpioKSBEK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/IDjPe/btruE8O8Q6E/Zpi94yiYL809FKpioKSBEK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/IDjPe/btruE8O8Q6E/Zpi94yiYL809FKpioKSBEK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FIDjPe%2FbtruE8O8Q6E%2FZpi94yiYL809FKpioKSBEK%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;204&quot; height=&quot;884&quot; data-origin-width=&quot;494&quot; data-origin-height=&quot;884&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;환경변수뿐만이 아니라 아래의 코드와 같이 모든 API 요청 헤더에 특정 값을 추가한다던가 하는&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여러 가지 확장된 기능으로 활용할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1646131698461&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    // 헤더설정
    pm.request.headers.add({
         key: &quot;Authorization&quot;,
         value: res_data.access_token
    });&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 data-ke-size=&quot;size16&quot;&gt;지금까지 API 개발 및 테스트 작업의 효율성과 생산성을 높여줄 수 있는&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;포스트맨의 Pre-request Script기능에 대한 간단한 소개였습니다.&lt;/p&gt;</description>
      <category>개발</category>
      <category>API 테스트</category>
      <category>Postman</category>
      <category>Postman API</category>
      <category>Postman Pre-request Script</category>
      <category>Postman Script</category>
      <category>Postman 환경변수</category>
      <category>rest api</category>
      <category>포스트맨</category>
      <category>포스트맨 API</category>
      <category>포스트맨 스크립트</category>
      <author>yorath</author>
      <guid isPermaLink="true">https://yonguri.tistory.com/146</guid>
      <comments>https://yonguri.tistory.com/146#entry146comment</comments>
      <pubDate>Tue, 1 Mar 2022 20:59:18 +0900</pubDate>
    </item>
    <item>
      <title>[JAVA - Spring]Jackson의 AnnotationIntrospector을 이용한 특정 프로퍼티 마스킹처리하기</title>
      <link>https://yonguri.tistory.com/145</link>
      <description>&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;2152&quot; data-origin-height=&quot;1042&quot; width=&quot;628&quot; height=&quot;304&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/IZWmP/btraHsUTWhN/kZ8CcROFrSMHkW6JskJ810/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/IZWmP/btraHsUTWhN/kZ8CcROFrSMHkW6JskJ810/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/IZWmP/btraHsUTWhN/kZ8CcROFrSMHkW6JskJ810/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FIZWmP%2FbtraHsUTWhN%2FkZ8CcROFrSMHkW6JskJ810%2Fimg.png&quot; data-origin-width=&quot;2152&quot; data-origin-height=&quot;1042&quot; width=&quot;628&quot; height=&quot;304&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;API 어플리케이션에서 로깅 처리를 할 때&lt;br /&gt;기본적으로는 API에 대한 요청정보 - RequestBody-와 응답 정보 - ResponsBody - 는 출력하도록 설정합니다.&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;br /&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;특히 외부에 제공하는 서비스의 성격을 띤 API 애플리케이션의 경우는&lt;br /&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;물론 헬스체크 등, 비즈니스와 상관없는 로깅 자체가 불필요한 API의 경우에는&lt;br /&gt;해당 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;그 방법에 대해서는 제가 이전에 올린 글을 참고하시면 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://yonguri.tistory.com/106?category=359077&quot;&gt;https://yonguri.tistory.com/106?category=359077&lt;/a&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1627543529689&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;[스프링,스프링부트] 커스텀 어노테이션(Custom Annotation)을 활용한 AOP에서의 로깅처리&quot; data-og-description=&quot;일반적으로 어플리케이션 서비스들이 공통으로 처리해야 할 기능들은 그 성격과 범위에 따라 인터셉터나 AOP를 사용합니다. 접근권한 로깅 예외처리(에러처리) 트랜잭션 어플리케이션에서의 공&quot; data-og-host=&quot;yonguri.tistory.com&quot; data-og-source-url=&quot;https://yonguri.tistory.com/106?category=359077&quot; data-og-url=&quot;https://yonguri.tistory.com/106&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/jh13U/hyK3BF0rKf/EVZ87MPjauNZFtakjFMIFK/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/dO9jJK/hyK3EQgbuY/0nrndLGzhlB2cIw8XTq3z1/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/bFcEYO/hyK3GAy1Dm/aD2fWcMt293H3uQYNVdYm1/img.png?width=220&amp;amp;height=220&amp;amp;face=0_0_220_220&quot;&gt;&lt;a href=&quot;https://yonguri.tistory.com/106?category=359077&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://yonguri.tistory.com/106?category=359077&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/jh13U/hyK3BF0rKf/EVZ87MPjauNZFtakjFMIFK/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/dO9jJK/hyK3EQgbuY/0nrndLGzhlB2cIw8XTq3z1/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/bFcEYO/hyK3GAy1Dm/aD2fWcMt293H3uQYNVdYm1/img.png?width=220&amp;amp;height=220&amp;amp;face=0_0_220_220');&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;[스프링,스프링부트] 커스텀 어노테이션(Custom Annotation)을 활용한 AOP에서의 로깅처리&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;일반적으로 어플리케이션 서비스들이 공통으로 처리해야 할 기능들은 그 성격과 범위에 따라 인터셉터나 AOP를 사용합니다. 접근권한 로깅 예외처리(에러처리) 트랜잭션 어플리케이션에서의 공&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;yonguri.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;REST API의 경우라면&lt;br /&gt;대부분의 응답유형이 JSON이기 때문에 로깅도 JSON 구조로 출력되도록 구현합니다.&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이 기본적으로 사용하는 JSON 라이브러리인 Jackson을 사용해서&lt;br /&gt;요청과 응답을 처리하는 공통모듈 등에서 Serializaiton을 통해 JSON String으로 출력하도록 합니다.&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;br /&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;값 전체를 보여주지 않아야 하는 경우(보안상의 이유 등)&lt;/li&gt;
&lt;li&gt;항목 값이 너무 커서 (이미지 파일의 Base64 인코딩 코드 등) 로깅 파일의 용량이 불필요하게 커진다거나 그로 인한 서버의 성능에 영향을 끼칠 때&lt;/li&gt;
&lt;/ul&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;이렇게 API단위가 아닌&lt;br /&gt;응답 객체(Response DTO)의 특정항목에 대해서 필터링을 해야 할 때,&lt;br /&gt;Jackson 라이브러리의 setAnnotationIntrospector기능을 이용해 간단히 구현할 수 있는 방법을 공유합니다.&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;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;Custom Annotaion 생성 (Masking 처리를 담당)&lt;/li&gt;
&lt;li&gt;AnnotationIntospector 정의&lt;/li&gt;
&lt;li&gt;AnnotationIntospector ObjectMapper에 적용&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Custom Annotaion 생성&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;마스킹 처리를 할 어노테이션 클래스를 생성합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;crystal&quot;&gt;&lt;code&gt;import com.fasterxml.jackson.annotation.JacksonAnnotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.ANNOTATION_TYPE, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@JacksonAnnotation
public @interface MaskLogging {
  String value() default &quot;*****&quot;;
  String defaultValue() default &quot;*****&quot;;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로퍼티에서 적용할 어노테이션이기 때문에 Elementtype은 Field하나로만 정해주면 됩니다.&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;code&gt;@Target({ElementType.ANNOTATION_TYPE, ElementType.FIELD})&lt;/code&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;br /&gt;&lt;code&gt;@Retention(RetentionPolicy.RUNTIME)로&lt;/code&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;code&gt;@JacksonAnnotation&lt;/code&gt;을 추가하여 이 어노테이션은&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Jakson 라이브러리에서 사용되는 어노테이션임을 정의해줍니다.&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;br /&gt;어떤 값으로 마스킹할지 그 값을 지정하는 value 속성만 필요합니다.&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;values는 지정하지 않으면 디폴트로 &quot;*****&quot; 값이 마스킹으로 적용됩니다.&lt;br /&gt;추가적으로 value 속성 값을 생략할 수 있도록 default값을 정의했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어노테이션의 적용은 일반적인 어노테이션 사용법과 동일합니다.&lt;br /&gt;아래와 같이 Masking이 필요한 프로퍼티에서 어노테이션을 붙여주면 됩니다.&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;DTO 클래스&lt;/p&gt;
&lt;pre class=&quot;lasso&quot;&gt;&lt;code&gt;public class ResponseBodyDto {
    private int id;
    private String name;
    private String addFileNm;
    private String addFileLoc;
    private Date creDt;
    ...
    ...
    @MaskLogging
    private String visitorImage;
    ...
    ...
}&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 class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;@MaskLogging(value = &quot;*****&quot;)
private String visitorImage;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Jackson AnnotationIntospector 정의&lt;/h4&gt;
&lt;pre class=&quot;java&quot;&gt;&lt;code&gt;public static class MaskPropertyAnnotationIntrospector extends NopAnnotationIntrospector {
    private static final long serialVersionUID = 1L;

    @Override
    public Object findSerializer(Annotated am) {
      MaskLogging annotation = am.getAnnotation(MaskLogging.class);
      if (annotation != null) {
        return MaskPropertySerializer.class;
      }
      return null;
    }
  }

  public static class MaskPropertySerializer extends StdSerializer&amp;lt;String&amp;gt; implements ContextualSerializer {
    private static final long serialVersionUID = 1L;

    private String maskValue;

    public MaskPropertySerializer() {
      super(String.class);
    }

    public MaskPropertySerializer(String maskValue) {
      super(String.class);
      this.maskValue = maskValue;
    }

    @Override
    public void serialize(String value, JsonGenerator gen, SerializerProvider provider) throws IOException {
      gen.writeString(maskValue);
    }

    @Override
    public JsonSerializer&amp;lt;?&amp;gt; createContextual(SerializerProvider serializerProvider, BeanProperty beanProperty) throws JsonMappingException {
      String maskValue = null;
      MaskLogging ann = null;
      if (beanProperty != null) {
        ann =beanProperty.getAnnotation(MaskLogging.class);
      }
      if (ann != null) {
        maskValue = ann.value();
      }
      return new MaskPropertySerializer(maskValue);
    }
  } &lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;필요한 클래스는 2개입니다.&lt;br /&gt;두 클래스 모두 정적 클래스로 생성하면 됩니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;MaskPropertyAnnotationIntrospector&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;NopAnnotationIntrospector 인터페이스를 구현한 AnnotationIntrospector 구현클래스입니다.&lt;/li&gt;
&lt;li&gt;이 클래스는 findSerializer 메서드를 오버라이딩해서 마스킹어노테이션(MaskLogging 인터페이스)이 적용된 Serializer를 반환하게 됩니다.&lt;/li&gt;
&lt;li&gt;실직적으로 Jackson의 ObjectMapper에 적용될 AnnotationIntrospector 입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;MaskPropertySerializer&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;ContextualSerializer 인터페이스를 구현한 Serializer 클래스입니다.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;이 클래스는 실제로 Json의 Serialize를 담당하는 클래스로, &lt;br /&gt;Serializer를 실행하는 시점에 MaskLogging 어노테이션이 적용된 프로퍼티에 &lt;br /&gt;마스킹 값이 적용되도록 처리합니다.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Jackson ObjectMapper에 AnnotationIntrospector 적용&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최종적으로 이렇게 생성된 AnnotationIntrospector 클래스를,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용하고 있는 Jackson의 ObjectMapper에 적용합니다.&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;아래의 예시는 Object타입의 매개변수를 받아 Json String으로 변환해주는 메서드입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적으로 스프링 부트 기반에서는 ObjectMapper를 빈으로 등록시켜 놓고 &lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; letter-spacing: 0px;&quot;&gt;필요시 주입받아 사용할 텐데, &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; letter-spacing: 0px;&quot;&gt;속성을 정의하는 방식은 동일합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;적용 전 코드&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ObjectMapper 속성 중 프로퍼티의 Visiblility에 대한 속성만 적용되어 있는 상태입니다.&lt;/p&gt;
&lt;pre class=&quot;aspectj&quot;&gt;&lt;code&gt;public static String buildJsonStr(final Object src) {
  try {
    ObjectMapper mapper = Jackson2ObjectMapperBuilder.json().build();

    return mapper
        .setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.NONE)
        .setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY)
        .writeValueAsString(src);
  } catch (Exception e) {
    mLog.error(&quot;stackTrace:&quot;, e);
  }
  return null;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;적용 후 코드&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AnnotationIntrospector 가 적용된 코드입니다.&lt;br /&gt;AnnotationIntrospectorPair.pair 메서드를 통해서 MaskPropertyAnnotationIntrospector 객체를 얻어옵니다.&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;public static String buildJsonStr(final Object src) {
  try {
    ObjectMapper mapper = Jackson2ObjectMapperBuilder.json().build();
    AnnotationIntrospector sis = mapper.getSerializationConfig().getAnnotationIntrospector();
    AnnotationIntrospector ais = AnnotationIntrospectorPair.pair(sis, new MaskPropertyAnnotationIntrospector());

    return mapper
        .setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.NONE)
        .setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY)
        .setAnnotationIntrospector(ais)
        .writeValueAsString(src);
  } catch (Exception e) {
    mLog.error(&quot;stackTrace:&quot;, e);
  }
  return null;
}
&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;pre class=&quot;java&quot;&gt;&lt;code&gt;public static String buildJsonStr(final Object src) {
    try {
      ObjectMapper mapper = new ObjectMapper();
      AnnotationIntrospector sis = mapper.getSerializationConfig().getAnnotationIntrospector();
      AnnotationIntrospector ais = AnnotationIntrospectorPair.pair(sis, new MaskPropertyAnnotationIntrospector());

      return mapper
          .setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.NONE)
          .setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY)
          .setAnnotationIntrospector(ais)

          .writeValueAsString(src);
    } catch (Exception e) {
      mLog.error(&quot;stackTrace:&quot;, e);
    }
    return null;
  }

  public static class MaskPropertyAnnotationIntrospector extends NopAnnotationIntrospector {
    private static final long serialVersionUID = 1L;

    @Override
    public Object findSerializer(Annotated am) {
      MaskLogging annotation = am.getAnnotation(MaskLogging.class);
      if (annotation != null) {
        return MaskPropertySerializer.class;
      }
      return null;
    }
  }

  public static class MaskPropertySerializer extends StdSerializer&amp;lt;String&amp;gt; implements ContextualSerializer {
    private static final long serialVersionUID = 1L;

    private String maskValue;

    public MaskPropertySerializer() {
      super(String.class);
    }

    public MaskPropertySerializer(String maskValue) {
      super(String.class);
      this.maskValue = maskValue;
    }

    @Override
    public void serialize(String value, JsonGenerator gen, SerializerProvider provider) throws IOException {
      gen.writeString(maskValue);
    }

    @Override
    public JsonSerializer&amp;lt;?&amp;gt; createContextual(SerializerProvider serializerProvider, BeanProperty beanProperty) throws JsonMappingException {
      String maskValue = null;
      MaskLogging ann = null;
      if (beanProperty != null) {
        ann =beanProperty.getAnnotation(MaskLogging.class);
      }
      if (ann != null) {
        maskValue = ann.value();
      }
      return new MaskPropertySerializer(maskValue);
    }
  } &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;이제 해당 메서드를 호출해 JSON로그를 출력해보면 아래와 같이&lt;br /&gt;MaskLogging 어노테이션이 붙은 프러퍼티가 마스킹 처리된 결과를 확인할 수 있습니다.&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;아래의 JSON 전문은 visitorImage 프로퍼티에 MaskLogging 어노테이션을 적용한 결과입니다.&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;원본 JSON&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 로그에서는 출력할 필요가 없는 이미지파일 Base64 Encoding 값이 출력되고 있습니다.&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;...
...
{&quot;id&quot;:&quot;1&quot;,&quot;visitorImage&quot;:&quot;iVBORw0KGgoAAAANSUhEUgAAAIAAAACAEAYAAAHkqY0eAAAACXBIWXMAAAsTAAALEwEAmpwYAAAGc2lUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS42LWMxNDAgNzkuMTYwNDUxLCAyMDE3LzA1LzA2LTAxOjA4OjIxICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHAasdfasdfasdf6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOnhtcE1NPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvbW0vIiB4bWxuczpzdEV2dD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL3NUeXBlL1Jlc291cmNlRXZlbnQjIiB4bWxuczpwaG90b3Nob3A9Imh0dHA6Ly9ucy5hZG9iZS5jb20vcGhvdG9zaG9wLzEuMC8iIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ0MgKE1hY2ludG9zaCkiIHhtcDpDcmVhdGVEYXRlPSIyMDE4LTA3LTE0VDE2OjQ5OjQyKzA5OjAwIiB4bXA6TWV0YWRhdGFEYXRlPSIyMDE4LTA3LTE0VDE2OjQ5OjQyKzA5OjAwIiB4bXA6TW9kaWZ5RGF0ZT0iMjAxOC0wNy0xNFQxNjo0OTo0MiswOTowMCIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDo4MzM5YTFjMS1mMmYxLTQwOTgtOTdkYS05NDljNzJiOTljMjIiIHhtcE1NOkRvY3VtZW5...prxtxAJGw3bJNTckZPBSyQxxAJMS3iygkG0unVwS9FBZhUK2wyLQmglmkByAIKUZ6AJFgW3DTeXMhRzzm/H2HCsG2zxWL0a3/PvVJs8M1trFnDwYX+Y90Q49lqzrApFlz7vzTijiAVLJxIxpqcbFpTQSzyCeAIKQYcQCCkGLEAUSCbWMAggDEAQhCipFBwFTSsiVG1a+4Eukc19/z8pzSDR0r7ubH/2tQ9/WN1bbf+sr+6ueq312bfNz/d4grYIiQLeIAIsG24JEUmIOkkFbEAUQC/7y4GWjzkHtvA6VpyTPdLzk+kvT7/pD5/P4WyNzccPoLbsQBpApqWJ06YR0AhcCyH3yyFBUhZYsD+GylaQ2yRRxAJJg65ZgiAd30ezT490rM6JEUpk+H/PWNSTmKTSICRQTeYAUFSP1tLGTQkGLURf7Xu5DvLIKcOgUGWZGQI7Zqgvp7cRJSAwt4c6fQaNNmQE5R9WlbIBd+xAEIQoqRdQCCkGLEAQhCihEHIAgpRhyAIKQYcQCCkGL+Dzt5DO0YYENkAAAAAElFTkSuQmCC&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;b&gt;적용 후 Json&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- @MaskLogging어노테이션이 적용된 프로퍼티의 값이 마스킹 처리되어 출력됩니다.&amp;nbsp;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;jboss-cli&quot;&gt;&lt;code&gt;...
...
{&quot;id&quot;:&quot;1&quot;,&quot;visitorImage&quot;:&quot;*****&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;br /&gt;속성 값을 잘 이용하면 단순한 마스킹 기능 외에도&lt;br /&gt;좀 더 다양한 방법으로 응용해서 사용할 수 있을 것 같습니다.&lt;/p&gt;</description>
      <category>개발</category>
      <category>AnnotationIntrospector</category>
      <category>Jackson</category>
      <category>Jackson Annotation</category>
      <category>Java</category>
      <category>json</category>
      <category>Json Logging</category>
      <category>json 마스킹</category>
      <category>spring</category>
      <category>spring jackson</category>
      <category>스프링</category>
      <author>yorath</author>
      <guid isPermaLink="true">https://yonguri.tistory.com/145</guid>
      <comments>https://yonguri.tistory.com/145#entry145comment</comments>
      <pubDate>Thu, 29 Jul 2021 16:42:29 +0900</pubDate>
    </item>
    <item>
      <title>[애플 감성] 애플 무선 키보드 1세대 + 애플 트랙패드 1세대</title>
      <link>https://yonguri.tistory.com/144</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;맥북이 업무의 메인 장비가 된 지 12년째...&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러다 보니 일상생활에서도 데스크탑(윈도우 PC)의 역할을 조금씩 대체하기 시작했고,&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 중에서도 구입한 지 10년이 지난,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아직도 현역으로 너무 잘 사용하고 있는 애플 무선 키보드 1세대와&amp;nbsp; 트랙패드 1세대를 사진으로 담아 보았습니다.&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;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;1500&quot; data-filename=&quot;blob&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cSiIve/btq9ynlwoRm/pyF8cjB1tv54uun9b5vEwK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cSiIve/btq9ynlwoRm/pyF8cjB1tv54uun9b5vEwK/img.png&quot; data-alt=&quot;10년이 훌쩍 넘은 디자인이지만 아직까지 촌스럽지 않은 디자인&amp;amp;amp;nbsp;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cSiIve/btq9ynlwoRm/pyF8cjB1tv54uun9b5vEwK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcSiIve%2Fbtq9ynlwoRm%2FpyF8cjB1tv54uun9b5vEwK%2Fimg.png&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;1500&quot; data-filename=&quot;blob&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;figcaption&gt;10년이 훌쩍 넘은 디자인이지만 아직까지 촌스럽지 않은 디자인&amp;nbsp;&lt;/figcaption&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;너덜너덜해질 때까지 쓰는 편이긴 한데 이 녀석들은 특별히 애정(?)을 많이 주고 사용했습니다.&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;768&quot; data-filename=&quot;blob&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bpYzfg/btq9wfBtyei/YGeUe7MbVwOEuy5CAkk6yK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bpYzfg/btq9wfBtyei/YGeUe7MbVwOEuy5CAkk6yK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bpYzfg/btq9wfBtyei/YGeUe7MbVwOEuy5CAkk6yK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbpYzfg%2Fbtq9wfBtyei%2FYGeUe7MbVwOEuy5CAkk6yK%2Fimg.png&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;768&quot; data-filename=&quot;blob&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;키를 누르는 느낌이나&amp;nbsp; 반발력이 지금의 2세대보다는 훨씬 더 강합니다.&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세대보다는 더 있을 수 있지만&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개인적으로는 이런 키감이&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;10년이 지난 아직도 가장 많이 사용하는 키보드로 그 역할을 톡톡히 수행하고 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;768&quot; data-filename=&quot;blob&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Dasat/btq9sCEoffH/Ogci1OC1mdUeshtQcy1JRk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Dasat/btq9sCEoffH/Ogci1OC1mdUeshtQcy1JRk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Dasat/btq9sCEoffH/Ogci1OC1mdUeshtQcy1JRk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FDasat%2Fbtq9sCEoffH%2FOgci1OC1mdUeshtQcy1JRk%2Fimg.png&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;768&quot; data-filename=&quot;blob&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;1세대 트랙패드입니다.&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;정전식 방식의 2세대 트랙패드를 주로 사용하고 있습니다.&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;거의 아날로그와 디지털의 차이라고 볼 수 있어서 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;2세대 트랙패드를 사용하다가 1세대를 사용해 보면 클릭이 너무 힘들고 뻑뻑하다는 걸 바로 느낄 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;768&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/chNhJh/btq9ql3Ejvx/0NwxKQYa7Pr09HkgRC83yk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/chNhJh/btq9ql3Ejvx/0NwxKQYa7Pr09HkgRC83yk/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/chNhJh/btq9ql3Ejvx/0NwxKQYa7Pr09HkgRC83yk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FchNhJh%2Fbtq9ql3Ejvx%2F0NwxKQYa7Pr09HkgRC83yk%2Fimg.jpg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;768&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;768&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/OrHd9/btq9xK2kvSZ/LKqKZhMNol8X6TpO5l2mGK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/OrHd9/btq9xK2kvSZ/LKqKZhMNol8X6TpO5l2mGK/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/OrHd9/btq9xK2kvSZ/LKqKZhMNol8X6TpO5l2mGK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FOrHd9%2Fbtq9xK2kvSZ%2FLKqKZhMNol8X6TpO5l2mGK%2Fimg.jpg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;768&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;1세대 만의 패밀리 룩&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;1024&quot; data-origin-height=&quot;768&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bSbPnr/btq9x6REZUE/qDWjyQqPdX7IM3P54pBn5k/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bSbPnr/btq9x6REZUE/qDWjyQqPdX7IM3P54pBn5k/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bSbPnr/btq9x6REZUE/qDWjyQqPdX7IM3P54pBn5k/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbSbPnr%2Fbtq9x6REZUE%2FqDWjyQqPdX7IM3P54pBn5k%2Fimg.jpg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;768&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;배터리도 꽤 오래갑니다... (뭐 한 달 이상은 충분히 사용 가능합니다)&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;1024&quot; data-origin-height=&quot;768&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/L3kFB/btq9tJpxOT3/0rGHtUy98ORhK1Ub83pnEK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/L3kFB/btq9tJpxOT3/0rGHtUy98ORhK1Ub83pnEK/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/L3kFB/btq9tJpxOT3/0rGHtUy98ORhK1Ub83pnEK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FL3kFB%2Fbtq9tJpxOT3%2F0rGHtUy98ORhK1Ub83pnEK%2Fimg.jpg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;768&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;768&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Hb26j/btq9sCxDGRj/wM2zXhrOx9PJ8gks5nKmj0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Hb26j/btq9sCxDGRj/wM2zXhrOx9PJ8gks5nKmj0/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Hb26j/btq9sCxDGRj/wM2zXhrOx9PJ8gks5nKmj0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FHb26j%2Fbtq9sCxDGRj%2FwM2zXhrOx9PJ8gks5nKmj0%2Fimg.jpg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;768&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;대부분이 알루미늄으로 뒤덮여 있어, 차갑고 딱딱한 느낌을 많이 주는데&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 때는 좀 더 따뜻하고 부드러운 느낌의 디자인이었습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전체적으로 디자인이 동글동글 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;알루미늄도 지금처럼 과도(?)하게 사용하지는 않았던 것 같네요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;768&quot; data-origin-height=&quot;1024&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/yz37p/btq9x7pu4nL/p147b4kCp8jAELWvKCQrb1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/yz37p/btq9x7pu4nL/p147b4kCp8jAELWvKCQrb1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/yz37p/btq9x7pu4nL/p147b4kCp8jAELWvKCQrb1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fyz37p%2Fbtq9x7pu4nL%2Fp147b4kCp8jAELWvKCQrb1%2Fimg.jpg&quot; data-origin-width=&quot;768&quot; data-origin-height=&quot;1024&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;1세대 트랙패드는 저 고무 패드처럼 생긴 부분이&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;1024&quot; data-origin-height=&quot;768&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/BeLoY/btq9s3Pm2FO/C5JH4x8Tu8wUO2kmYRxpc0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/BeLoY/btq9s3Pm2FO/C5JH4x8Tu8wUO2kmYRxpc0/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/BeLoY/btq9s3Pm2FO/C5JH4x8Tu8wUO2kmYRxpc0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FBeLoY%2Fbtq9s3Pm2FO%2FC5JH4x8Tu8wUO2kmYRxpc0%2Fimg.jpg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;768&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;2세대 트랙패드와 비교해 보았습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;세로 사이즈는 거의 비슷하고 가로 사이즈가 2세대가 더 넓습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 트랙패드는 사이즈 하나만&amp;nbsp; 비교해 봐도 1세대와 2세대의 사용성과 편의성 차이가 많이 납니다.&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;2048&quot; data-origin-height=&quot;1536&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nSsgO/btq9ymUoOW4/gg47ryypkbBCuMNAal5I41/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nSsgO/btq9ymUoOW4/gg47ryypkbBCuMNAal5I41/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nSsgO/btq9ymUoOW4/gg47ryypkbBCuMNAal5I41/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnSsgO%2Fbtq9ymUoOW4%2Fgg47ryypkbBCuMNAal5I41%2Fimg.jpg&quot; data-origin-width=&quot;2048&quot; data-origin-height=&quot;1536&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;확실히 1세대와 디자인이 많이 다릅니다.&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-origin-width=&quot;2048&quot; data-origin-height=&quot;1536&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/zJ12W/btq9Aiju4Yg/pak0rvyUlrKqBKmOpaDyhK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/zJ12W/btq9Aiju4Yg/pak0rvyUlrKqBKmOpaDyhK/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/zJ12W/btq9Aiju4Yg/pak0rvyUlrKqBKmOpaDyhK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FzJ12W%2Fbtq9Aiju4Yg%2Fpak0rvyUlrKqBKmOpaDyhK%2Fimg.jpg&quot; data-origin-width=&quot;2048&quot; data-origin-height=&quot;1536&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;정말 군더더기 없는 단순하고 깔끔한 디자인입니다. 커팅면의 마감 상태도 수준급이고 가격도 정말 수준급(?)입니다.&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.apple.com/kr/shop/accessories/all/mice-keyboards?page=1&amp;amp;f=trackpad&amp;amp;fh=40a2%2B465e&quot;&gt;https://www.apple.com/kr/shop/accessories/all/mice-keyboards?page=1&amp;amp;f=trackpad&amp;amp;fh=40a2%2B465e&lt;/a&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1626177175221&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;트랙패드 - 마우스 &amp;amp; 키보드 - 모든 액세서리&quot; data-og-description=&quot;새로운 키보드, 트랙패드, 마우스로 Mac과 iPad를 더욱 다양하게 활용하세요. 온라인에서 구입하면 배송비가 무료입니다.&quot; data-og-host=&quot;www.apple.com&quot; data-og-source-url=&quot;https://www.apple.com/kr/shop/accessories/all/mice-keyboards?page=1&amp;amp;f=trackpad&amp;amp;fh=40a2%2B465e&quot; data-og-url=&quot;https://www.apple.com/kr/shop/accessories/all/mice-keyboards?fh=40a2%2B465e&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/b0n1mm/hyKRGJnJB8/ebrdQE1WWfwQ9IlVRropY0/img.jpg?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/BsIAp/hyKSU0jScz/Wdbbpw35JAPS7ea38lnj90/img.jpg?width=890&amp;amp;height=890&amp;amp;face=0_0_890_890,https://scrap.kakaocdn.net/dn/uqYQc/hyKSS2ue0d/YDwYN6SVWdhX4nYk9Al2tK/img.jpg?width=890&amp;amp;height=890&amp;amp;face=0_0_890_890&quot;&gt;&lt;a href=&quot;https://www.apple.com/kr/shop/accessories/all/mice-keyboards?page=1&amp;amp;f=trackpad&amp;amp;fh=40a2%2B465e&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.apple.com/kr/shop/accessories/all/mice-keyboards?page=1&amp;amp;f=trackpad&amp;amp;fh=40a2%2B465e&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/b0n1mm/hyKRGJnJB8/ebrdQE1WWfwQ9IlVRropY0/img.jpg?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/BsIAp/hyKSU0jScz/Wdbbpw35JAPS7ea38lnj90/img.jpg?width=890&amp;amp;height=890&amp;amp;face=0_0_890_890,https://scrap.kakaocdn.net/dn/uqYQc/hyKSS2ue0d/YDwYN6SVWdhX4nYk9Al2tK/img.jpg?width=890&amp;amp;height=890&amp;amp;face=0_0_890_890');&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;트랙패드 - 마우스 &amp;amp; 키보드 - 모든 액세서리&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;새로운 키보드, 트랙패드, 마우스로 Mac과 iPad를 더욱 다양하게 활용하세요. 온라인에서 구입하면 배송비가 무료입니다.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.apple.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;참고로 국내 애플 스토에서는 2세대 매직 트랙패드 그레이 색은&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;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금까지 애플 1세대 주변기기의 대표 격인&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;애플 무선 키보드와 트랙패드를 간단히 소개해 보았습니다.&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;앞으로도 최소 5년은 더 쓰지 않을까 생각합니다.&lt;/p&gt;</description>
      <category>IT_리뷰</category>
      <category>Apple</category>
      <category>Mac</category>
      <category>레트로</category>
      <category>맥북</category>
      <category>맥북 주변기기</category>
      <category>애플</category>
      <category>애플 1세대</category>
      <category>애플 무선키보드</category>
      <category>애플 주변기기</category>
      <category>애플 트랙패드</category>
      <author>yorath</author>
      <guid isPermaLink="true">https://yonguri.tistory.com/144</guid>
      <comments>https://yonguri.tistory.com/144#entry144comment</comments>
      <pubDate>Tue, 13 Jul 2021 20:54:16 +0900</pubDate>
    </item>
    <item>
      <title>[원목가구] 목공 라이프 열여덟번째 - 캣 소품 (2)</title>
      <link>https://yonguri.tistory.com/143</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;캣 소품 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;&lt;a href=&quot;https://yonguri.tistory.com/86?category=714823&quot;&gt;https://yonguri.tistory.com/86?category=714823&lt;/a&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1626014475457&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;[원목가구] 목공 라이프 열여덟번째 - 캣 소품 (1)&quot; data-og-description=&quot;정말 오랜만에 목공 글을 올립니다. 블로그를 꾸준히 한다는 건 정말 어려운 일인것 같습니다. 특히 저에게는요. 일주일에 최소한 한 개의 글은 올릴 수 있지 않을까 생각하고 시작했는데, 요즘 &quot; data-og-host=&quot;yonguri.tistory.com&quot; data-og-source-url=&quot;https://yonguri.tistory.com/86?category=714823&quot; data-og-url=&quot;https://yonguri.tistory.com/86&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/iIC2v/hyKQoonc3I/PfiE0LvDTgIMhoi6zjGUB1/img.png?width=800&amp;amp;height=600&amp;amp;face=0_0_800_600,https://scrap.kakaocdn.net/dn/dRLjaV/hyKRU0gpfd/GQ6okSm9GhDBfwVKahUZjk/img.png?width=800&amp;amp;height=600&amp;amp;face=0_0_800_600,https://scrap.kakaocdn.net/dn/cKVVYt/hyKQl6halr/JgmLtmytCkmMmlWQ8W4fpK/img.jpg?width=768&amp;amp;height=1024&amp;amp;face=0_0_768_1024&quot;&gt;&lt;a href=&quot;https://yonguri.tistory.com/86?category=714823&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://yonguri.tistory.com/86?category=714823&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/iIC2v/hyKQoonc3I/PfiE0LvDTgIMhoi6zjGUB1/img.png?width=800&amp;amp;height=600&amp;amp;face=0_0_800_600,https://scrap.kakaocdn.net/dn/dRLjaV/hyKRU0gpfd/GQ6okSm9GhDBfwVKahUZjk/img.png?width=800&amp;amp;height=600&amp;amp;face=0_0_800_600,https://scrap.kakaocdn.net/dn/cKVVYt/hyKQl6halr/JgmLtmytCkmMmlWQ8W4fpK/img.jpg?width=768&amp;amp;height=1024&amp;amp;face=0_0_768_1024');&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;[원목가구] 목공 라이프 열여덟번째 - 캣 소품 (1)&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;yonguri.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;768&quot; data-origin-height=&quot;1024&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/RMuCq/btq9nDIiU34/59Bk95aXciDei18DtUeKOK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/RMuCq/btq9nDIiU34/59Bk95aXciDei18DtUeKOK/img.jpg&quot; data-alt=&quot;지인의 집에 직접 배달, 냥이 밥통과 물통이 놓여진 모습.&amp;amp;amp;nbsp;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/RMuCq/btq9nDIiU34/59Bk95aXciDei18DtUeKOK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FRMuCq%2Fbtq9nDIiU34%2F59Bk95aXciDei18DtUeKOK%2Fimg.jpg&quot; data-origin-width=&quot;768&quot; data-origin-height=&quot;1024&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;figcaption&gt;지인의 집에 직접 배달, 냥이 밥통과 물통이 놓여진 모습.&amp;nbsp;&lt;/figcaption&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;이게 어떻게 쓰일지에 대해서는 이 모습을 보기 전까지는 잘 그려지지 않았는데요.&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;/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;/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;사진으로만 쭉 감상해 보시죠.&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1218&quot; data-origin-height=&quot;905&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bbPOcv/btq9lTERXgX/FivKXhCGV5k844TOGkegCK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bbPOcv/btq9lTERXgX/FivKXhCGV5k844TOGkegCK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bbPOcv/btq9lTERXgX/FivKXhCGV5k844TOGkegCK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbbPOcv%2Fbtq9lTERXgX%2FFivKXhCGV5k844TOGkegCK%2Fimg.png&quot; data-origin-width=&quot;1218&quot; data-origin-height=&quot;905&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;span style=&quot;color: #777777;&quot;&gt;밥통의 위치가 높은 이유는 아래 사진에서 확인할 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1176&quot; data-origin-height=&quot;554&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/940FU/btq9nDn1CCm/cvSEI7ZAIXRbYqYodunqqk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/940FU/btq9nDn1CCm/cvSEI7ZAIXRbYqYodunqqk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/940FU/btq9nDn1CCm/cvSEI7ZAIXRbYqYodunqqk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F940FU%2Fbtq9nDn1CCm%2FcvSEI7ZAIXRbYqYodunqqk%2Fimg.png&quot; data-origin-width=&quot;1176&quot; data-origin-height=&quot;554&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;1편에 이어,&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;768&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bYsb2w/btq9iAF3rWe/pfxwRwVtrUKPKdONGz9nU1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bYsb2w/btq9iAF3rWe/pfxwRwVtrUKPKdONGz9nU1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bYsb2w/btq9iAF3rWe/pfxwRwVtrUKPKdONGz9nU1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbYsb2w%2Fbtq9iAF3rWe%2FpfxwRwVtrUKPKdONGz9nU1%2Fimg.jpg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;768&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;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;768&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Y7DGi/btq9mmthVWf/0FhlPysqAJmtJ255IWKm7k/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Y7DGi/btq9mmthVWf/0FhlPysqAJmtJ255IWKm7k/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Y7DGi/btq9mmthVWf/0FhlPysqAJmtJ255IWKm7k/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FY7DGi%2Fbtq9mmthVWf%2F0FhlPysqAJmtJ255IWKm7k%2Fimg.jpg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;768&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;아리(Ari)와 샤리(Shari)입니다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;768&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/40OZw/btq9jGF2da8/44O5QWoJqMKziZqnfYGAtk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/40OZw/btq9jGF2da8/44O5QWoJqMKziZqnfYGAtk/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/40OZw/btq9jGF2da8/44O5QWoJqMKziZqnfYGAtk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F40OZw%2Fbtq9jGF2da8%2F44O5QWoJqMKziZqnfYGAtk%2Fimg.jpg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;768&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;포인트로 넣어 보았습니다.&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;1024&quot; data-origin-height=&quot;768&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bftVru/btq9gYOak8q/3fd2sOQnDskGKW4qjXpq01/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bftVru/btq9gYOak8q/3fd2sOQnDskGKW4qjXpq01/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bftVru/btq9gYOak8q/3fd2sOQnDskGKW4qjXpq01/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbftVru%2Fbtq9gYOak8q%2F3fd2sOQnDskGKW4qjXpq01%2Fimg.jpg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;768&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;768&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Qyoxt/btq9gAUiK0h/QcH1r3lx6gwS37ivwkYvOk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Qyoxt/btq9gAUiK0h/QcH1r3lx6gwS37ivwkYvOk/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Qyoxt/btq9gAUiK0h/QcH1r3lx6gwS37ivwkYvOk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FQyoxt%2Fbtq9gAUiK0h%2FQcH1r3lx6gwS37ivwkYvOk%2Fimg.jpg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;768&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;완성된 모습 2&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;768&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/sXzMI/btq9gANzIDU/THo7ijAVlQ2QXdeKrH3pY0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/sXzMI/btq9gANzIDU/THo7ijAVlQ2QXdeKrH3pY0/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/sXzMI/btq9gANzIDU/THo7ijAVlQ2QXdeKrH3pY0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FsXzMI%2Fbtq9gANzIDU%2FTHo7ijAVlQ2QXdeKrH3pY0%2Fimg.jpg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;768&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;밥통 2개, 물통 하나.&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;아시는 분, 덧글에 달아주시면&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-origin-width=&quot;1024&quot; data-origin-height=&quot;768&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Ravch/btq9jGss4Hr/mQhBSfxo4yQ6BsajG6iIkk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Ravch/btq9jGss4Hr/mQhBSfxo4yQ6BsajG6iIkk/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Ravch/btq9jGss4Hr/mQhBSfxo4yQ6BsajG6iIkk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FRavch%2Fbtq9jGss4Hr%2FmQhBSfxo4yQ6BsajG6iIkk%2Fimg.jpg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;768&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;사진으로도 잘 느껴지네요.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;2606&quot; data-origin-height=&quot;1436&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/AKCYx/btq9goFDj14/mUNN0pSeoGUudHuNYVkAPK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/AKCYx/btq9goFDj14/mUNN0pSeoGUudHuNYVkAPK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/AKCYx/btq9goFDj14/mUNN0pSeoGUudHuNYVkAPK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FAKCYx%2Fbtq9goFDj14%2FmUNN0pSeoGUudHuNYVkAPK%2Fimg.png&quot; data-origin-width=&quot;2606&quot; data-origin-height=&quot;1436&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;의뢰인이 알려주신 수치가&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;2598&quot; data-origin-height=&quot;1436&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bBeFIV/btq9lmthDBF/KaeVtk6vcfzg3MBq383uqK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bBeFIV/btq9lmthDBF/KaeVtk6vcfzg3MBq383uqK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bBeFIV/btq9lmthDBF/KaeVtk6vcfzg3MBq383uqK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbBeFIV%2Fbtq9lmthDBF%2FKaeVtk6vcfzg3MBq383uqK%2Fimg.png&quot; data-origin-width=&quot;2598&quot; data-origin-height=&quot;1436&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;2584&quot; data-origin-height=&quot;1440&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bq7eTy/btq9iAlN1X0/wBEW2yZdsU21oKf6svdLw0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bq7eTy/btq9iAlN1X0/wBEW2yZdsU21oKf6svdLw0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bq7eTy/btq9iAlN1X0/wBEW2yZdsU21oKf6svdLw0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbq7eTy%2Fbtq9iAlN1X0%2FwBEW2yZdsU21oKf6svdLw0%2Fimg.png&quot; data-origin-width=&quot;2584&quot; data-origin-height=&quot;1440&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;목공을 하면서 느끼는 건 첫째도 사이즈, 둘째도 사이즈 란 사실입니다.&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; 최종적인 퀄리티는 떨어질 수밖에 없습니다.&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;2570&quot; data-origin-height=&quot;1416&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bgkkSM/btq9gAUmhbQ/Y1kkrFMC5Y7VTXfd4kVLb0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bgkkSM/btq9gAUmhbQ/Y1kkrFMC5Y7VTXfd4kVLb0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bgkkSM/btq9gAUmhbQ/Y1kkrFMC5Y7VTXfd4kVLb0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbgkkSM%2Fbtq9gAUmhbQ%2FY1kkrFMC5Y7VTXfd4kVLb0%2Fimg.png&quot; data-origin-width=&quot;2570&quot; data-origin-height=&quot;1416&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;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;2378&quot; data-origin-height=&quot;1438&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/d1qOY0/btq9iAsFnal/VdvD0YRkr4kTYhBhSdmUeK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/d1qOY0/btq9iAsFnal/VdvD0YRkr4kTYhBhSdmUeK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/d1qOY0/btq9iAsFnal/VdvD0YRkr4kTYhBhSdmUeK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fd1qOY0%2Fbtq9iAsFnal%2FVdvD0YRkr4kTYhBhSdmUeK%2Fimg.png&quot; data-origin-width=&quot;2378&quot; data-origin-height=&quot;1438&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;사실 스테인 마감 시 색이 생각보다는 좀 진하게 나와서 신경이 쓰였는데&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;970&quot; data-origin-height=&quot;1684&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bgn7dN/btq9nhkXHGn/Riep3G4u8LQ7lZnNXG79K0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bgn7dN/btq9nhkXHGn/Riep3G4u8LQ7lZnNXG79K0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bgn7dN/btq9nhkXHGn/Riep3G4u8LQ7lZnNXG79K0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbgn7dN%2Fbtq9nhkXHGn%2FRiep3G4u8LQ7lZnNXG79K0%2Fimg.png&quot; data-origin-width=&quot;970&quot; data-origin-height=&quot;1684&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;p data-ke-size=&quot;size16&quot;&gt;신기했습니다~&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;966&quot; data-origin-height=&quot;1726&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mrK73/btq9mlnGh1K/GPcbmsYONgaTZD4O5R3Xp0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mrK73/btq9mlnGh1K/GPcbmsYONgaTZD4O5R3Xp0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mrK73/btq9mlnGh1K/GPcbmsYONgaTZD4O5R3Xp0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmrK73%2Fbtq9mlnGh1K%2FGPcbmsYONgaTZD4O5R3Xp0%2Fimg.png&quot; data-origin-width=&quot;966&quot; data-origin-height=&quot;1726&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;사진으로만 봐도 냥이가 너무 편한 게 사료를 먹고 있는 게 느껴지시죠?&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;966&quot; data-origin-height=&quot;1674&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/btubov/btq9hz8tEOR/jdcsgWwVyKHRotmkERU0l1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/btubov/btq9hz8tEOR/jdcsgWwVyKHRotmkERU0l1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/btubov/btq9hz8tEOR/jdcsgWwVyKHRotmkERU0l1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbtubov%2Fbtq9hz8tEOR%2FjdcsgWwVyKHRotmkERU0l1%2Fimg.png&quot; data-origin-width=&quot;966&quot; data-origin-height=&quot;1674&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;냥이가 사료를 먹기에 가장 편한 높이((아리, 샤리에게 맞는 사이즈)를 정확히 파악해서 알려주셨다는 것을&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;972&quot; data-origin-height=&quot;1590&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/sVG7B/btq9hO4WkkC/ACSRWc6WWVCZ7VE9p1fsak/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/sVG7B/btq9hO4WkkC/ACSRWc6WWVCZ7VE9p1fsak/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/sVG7B/btq9hO4WkkC/ACSRWc6WWVCZ7VE9p1fsak/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FsVG7B%2Fbtq9hO4WkkC%2FACSRWc6WWVCZ7VE9p1fsak%2Fimg.png&quot; data-origin-width=&quot;972&quot; data-origin-height=&quot;1590&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;'아비니시안'이라고 하는 종이라고 하던데&amp;nbsp; 자태가 너무 예쁜 냥이입니다.&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-origin-width=&quot;970&quot; data-origin-height=&quot;1606&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/IROVS/btq9gROlSiq/JwVfoCRssqiC2j7WBxadt0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/IROVS/btq9gROlSiq/JwVfoCRssqiC2j7WBxadt0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/IROVS/btq9gROlSiq/JwVfoCRssqiC2j7WBxadt0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FIROVS%2Fbtq9gROlSiq%2FJwVfoCRssqiC2j7WBxadt0%2Fimg.png&quot; data-origin-width=&quot;970&quot; data-origin-height=&quot;1606&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;아... 뿌듯합니다 ^^&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;768&quot; data-origin-height=&quot;1024&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Sb5k7/btq9gXPfwMH/KxXKZIgeXjIP6ZuPUZtYVk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Sb5k7/btq9gXPfwMH/KxXKZIgeXjIP6ZuPUZtYVk/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Sb5k7/btq9gXPfwMH/KxXKZIgeXjIP6ZuPUZtYVk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FSb5k7%2Fbtq9gXPfwMH%2FKxXKZIgeXjIP6ZuPUZtYVk%2Fimg.jpg&quot; data-origin-width=&quot;768&quot; data-origin-height=&quot;1024&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;발을 어떻게 저렇게 하고 있는지 ㅎㅎ&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;768&quot; data-origin-height=&quot;1024&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b2g32d/btq9fceOOgN/N4HPhj7HPL2PDRsMR3dQ8K/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b2g32d/btq9fceOOgN/N4HPhj7HPL2PDRsMR3dQ8K/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b2g32d/btq9fceOOgN/N4HPhj7HPL2PDRsMR3dQ8K/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb2g32d%2Fbtq9fceOOgN%2FN4HPhj7HPL2PDRsMR3dQ8K%2Fimg.jpg&quot; data-origin-width=&quot;768&quot; data-origin-height=&quot;1024&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;이 사진은 예전에 이 의뢰인(의뢰인이 회사 CTO라는 ㅎㅎ) 집에서&amp;nbsp; 팀원들끼리 코딩(사내 해커톤 형식)을 한 적이 있었는데&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코딩하는 중에 갑자기 아리가 나타나서 찍어본 사진입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;너무 절묘하게 찍혀서 이것도 같이 올려봅니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금까지&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사람이 아닌 대상이 사용할 목적으로 처음 만들어본&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;</description>
      <category>목공 이야기</category>
      <category>고양이 소품</category>
      <category>고양이 원목</category>
      <category>고양이 원목선반</category>
      <category>고양이 키우고 싶다</category>
      <category>냥이 밥그릇</category>
      <category>냥이 소품</category>
      <category>냥이 원목</category>
      <category>샴 고양이</category>
      <category>아비니시안</category>
      <category>캣 소품</category>
      <author>yorath</author>
      <guid isPermaLink="true">https://yonguri.tistory.com/143</guid>
      <comments>https://yonguri.tistory.com/143#entry143comment</comments>
      <pubDate>Sun, 11 Jul 2021 23:37:30 +0900</pubDate>
    </item>
    <item>
      <title>[인텔리J 플러그인] POJO from JSON Generator - RoboPOJO Generator</title>
      <link>https://yonguri.tistory.com/142</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;954&quot; data-origin-height=&quot;258&quot; width=&quot;516&quot; height=&quot;140&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/csLjkg/btq8BjjOcX2/LAJcQBAkPKneN4FhTyfAEK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/csLjkg/btq8BjjOcX2/LAJcQBAkPKneN4FhTyfAEK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/csLjkg/btq8BjjOcX2/LAJcQBAkPKneN4FhTyfAEK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcsLjkg%2Fbtq8BjjOcX2%2FLAJcQBAkPKneN4FhTyfAEK%2Fimg.png&quot; data-origin-width=&quot;954&quot; data-origin-height=&quot;258&quot; width=&quot;516&quot; height=&quot;140&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;일반적으로 API 어플리케이션(REST API)을 만들 때 가장 먼저 시작하는 작업이&lt;br /&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;API Endpoint 수에 따라 적게는 수십 개 , 많게는 몇백 개 이상의 요청/응답 객체 - 일반적으로 DTO로 불리는 객체-를&lt;br /&gt;POJO 클래스로 만들어야 하는데요.&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;설계된 문서 또는 JSON - 이제 거의 모든 REST API의 요청/응답유형은 JSON구조를 사용합니다 - 텍스트를 보고&lt;br /&gt;일일이 타이핑으로 수많은 요청/응답객체를 만드다는 게 매우 비효율적인 작업입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;단순 반복작업인데다가 휴먼 오류(오타)의 가능성도 크기 때문입니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 작업들을 줄일 수 있는 인텔리J의 플러그인을 하나 소개합니다.&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;인텔리 J 마켓플레이스에서 JSON 구조의 텍스트를 POJO로 Generation 해주는 플러그인은 몇 가지가 있는데요,&lt;br /&gt;그중에서 사용법이 가장 단순하면서 오류가 적다고 느꼈던 Tool입니다.&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://plugins.jetbrains.com/plugin/8634-robopojogenerator&quot;&gt;RoboPOJOGenerator - Plugins | JetBrains&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1625127263105&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;RoboPOJOGenerator - Plugins | JetBrains&quot; data-og-description=&quot;Generate Java and Kotlin POJO files from JSON: GSON, FastJSON, AutoValue (GSON), Logan Square, Jackson, Lombok, empty annotations template.&quot; data-og-host=&quot;plugins.jetbrains.com&quot; data-og-source-url=&quot;https://plugins.jetbrains.com/plugin/8634-robopojogenerator&quot; data-og-url=&quot;https://plugins.jetbrains.com/plugin/8634-robopojogenerator&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/vpRpw/hyKKxrGAsK/bz4Nqj1BcKwlJKE8E5RzJ1/img.png?width=600&amp;amp;height=600&amp;amp;face=0_0_600_600,https://scrap.kakaocdn.net/dn/FrQQ7/hyKKDk7Q8L/BuUxpfaTNV6Zo12KkLpPc0/img.png?width=600&amp;amp;height=600&amp;amp;face=0_0_600_600&quot;&gt;&lt;a href=&quot;https://plugins.jetbrains.com/plugin/8634-robopojogenerator&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://plugins.jetbrains.com/plugin/8634-robopojogenerator&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/vpRpw/hyKKxrGAsK/bz4Nqj1BcKwlJKE8E5RzJ1/img.png?width=600&amp;amp;height=600&amp;amp;face=0_0_600_600,https://scrap.kakaocdn.net/dn/FrQQ7/hyKKDk7Q8L/BuUxpfaTNV6Zo12KkLpPc0/img.png?width=600&amp;amp;height=600&amp;amp;face=0_0_600_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;RoboPOJOGenerator - Plugins | JetBrains&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Generate Java and Kotlin POJO files from JSON: GSON, FastJSON, AutoValue (GSON), Logan Square, Jackson, Lombok, empty annotations template.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;plugins.jetbrains.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/robohorse/RoboPOJOGenerator&quot;&gt;GitHub - robohorse/RoboPOJOGenerator: IntelliJ IDEA/Android studio plugin: Json to Java and Kotlin POJO (GSON, Logan Square, Jackson, FastJSON, AutoValue, Moshi, Lombok)&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1625127266315&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;robohorse/RoboPOJOGenerator&quot; data-og-description=&quot;IntelliJ IDEA/Android studio plugin: Json to Java and Kotlin POJO (GSON, Logan Square, Jackson, FastJSON, AutoValue, Moshi, Lombok) - robohorse/RoboPOJOGenerator&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/robohorse/RoboPOJOGenerator&quot; data-og-url=&quot;https://github.com/robohorse/RoboPOJOGenerator&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/dwCb3z/hyKKAhEfhG/sCzKOLVoLisPX7pE9JFmf1/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/nvhEx/hyKKzC24wX/bNTsQtkkzXEP6sOsQo8ZWK/img.png?width=1438&amp;amp;height=808&amp;amp;face=0_0_1438_808,https://scrap.kakaocdn.net/dn/hwFSP/hyKKLwHKs7/quhZe6a4bA6gkXU4CgnLKK/img.png?width=1156&amp;amp;height=742&amp;amp;face=0_0_1156_742&quot;&gt;&lt;a href=&quot;https://github.com/robohorse/RoboPOJOGenerator&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/robohorse/RoboPOJOGenerator&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/dwCb3z/hyKKAhEfhG/sCzKOLVoLisPX7pE9JFmf1/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/nvhEx/hyKKzC24wX/bNTsQtkkzXEP6sOsQo8ZWK/img.png?width=1438&amp;amp;height=808&amp;amp;face=0_0_1438_808,https://scrap.kakaocdn.net/dn/hwFSP/hyKKLwHKs7/quhZe6a4bA6gkXU4CgnLKK/img.png?width=1156&amp;amp;height=742&amp;amp;face=0_0_1156_742');&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;robohorse/RoboPOJOGenerator&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;IntelliJ IDEA/Android studio plugin: Json to Java and Kotlin POJO (GSON, Logan Square, Jackson, FastJSON, AutoValue, Moshi, Lombok) - robohorse/RoboPOJOGenerator&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;/p&gt;
&lt;ol style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;프로젝트의 설정화면으로 들어갑니다. ( 단축기: Command + , )&lt;br /&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;948&quot; data-origin-height=&quot;558&quot; width=&quot;614&quot; height=&quot;361&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/HznHB/btq8Bjc3yGT/MRJc9BWXUeV7OrC4XMOXy1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/HznHB/btq8Bjc3yGT/MRJc9BWXUeV7OrC4XMOXy1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/HznHB/btq8Bjc3yGT/MRJc9BWXUeV7OrC4XMOXy1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FHznHB%2Fbtq8Bjc3yGT%2FMRJc9BWXUeV7OrC4XMOXy1%2Fimg.png&quot; data-origin-width=&quot;948&quot; data-origin-height=&quot;558&quot; width=&quot;614&quot; height=&quot;361&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;/li&gt;
&lt;/ol&gt;
&lt;ol style=&quot;list-style-type: disc;&quot; start=&quot;2&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;PlugIns 메뉴의 마켓플레이스로 들어가서 JSON to POJO 검색하면 관련 플러그인들이 몇 개 검색됩니다.&lt;br /&gt;
&lt;div&gt;&lt;img src=&quot;%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-06-29%20%E1%84%8B%E1%85%A9%E1%84%8C%E1%85%A5%E1%86%AB%2010.44.59.png&quot; alt=&quot;&quot; /&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1528&quot; data-origin-height=&quot;948&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cEmjl0/btq8CuLV7je/kAkJqPqGN5b8GngsIZNF50/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cEmjl0/btq8CuLV7je/kAkJqPqGN5b8GngsIZNF50/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cEmjl0/btq8CuLV7je/kAkJqPqGN5b8GngsIZNF50/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcEmjl0%2Fbtq8CuLV7je%2FkAkJqPqGN5b8GngsIZNF50%2Fimg.png&quot; data-origin-width=&quot;1528&quot; data-origin-height=&quot;948&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;/div&gt;
&lt;/li&gt;
&lt;li&gt;그중에서 RoboPOJO Generator를 선택하고 설치합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: disc;&quot; start=&quot;4&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;설치가 완료되면 메뉴&amp;gt; File &amp;gt; New에 &amp;lsquo;Generatoe POJO from JSON &amp;lsquo;이라는 메뉴가 추가되고, 아래와 같이 오른쪽 마우스 버튼 메뉴에도 추가가 됩니다.&lt;br /&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1494&quot; data-origin-height=&quot;844&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/W02WZ/btq8BkiJH1W/lNIhO8NgksMw9cL61hxgH1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/W02WZ/btq8BkiJH1W/lNIhO8NgksMw9cL61hxgH1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/W02WZ/btq8BkiJH1W/lNIhO8NgksMw9cL61hxgH1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FW02WZ%2Fbtq8BkiJH1W%2FlNIhO8NgksMw9cL61hxgH1%2Fimg.png&quot; data-origin-width=&quot;1494&quot; data-origin-height=&quot;844&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;/li&gt;
&lt;/ol&gt;
&lt;ol style=&quot;list-style-type: disc;&quot; start=&quot;5&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;POJO를 만들 패키지(디렉토리)를 선택하고 클릭하면 아래의 Generation 창이 정상적으로 열립니다. 참고로 패키지를 선택하지 않은 상태에서 클릭하면 오류가 발생하니 꼭 생성할 패키지를 지정하고 사용하셔야 합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1362&quot; data-origin-height=&quot;1080&quot; width=&quot;665&quot; height=&quot;527&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/zyIjx/btq8Ct0yWOT/k9vchfw0YUCQZF10m4mP81/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/zyIjx/btq8Ct0yWOT/k9vchfw0YUCQZF10m4mP81/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/zyIjx/btq8Ct0yWOT/k9vchfw0YUCQZF10m4mP81/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FzyIjx%2Fbtq8Ct0yWOT%2Fk9vchfw0YUCQZF10m4mP81%2Fimg.png&quot; data-origin-width=&quot;1362&quot; data-origin-height=&quot;1080&quot; width=&quot;665&quot; height=&quot;527&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;ol style=&quot;list-style-type: disc;&quot; start=&quot;6&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;기능에 대한 설명을 굳이 따로 설명을 하지 않아도 될 정도로 쉽고, 직관적으로 되어 있습니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다양한 JSON 관련 라이브러리를 지원하는데,&lt;br /&gt;저는 Springboot기반이라 기본적으로 jackson 라이브러리가 사용되고 있어 none 으로 설정하고 사용합니다.&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;참고로 lombok으로 선택하고 사용하게 되면&lt;br /&gt;클래스레벨에 lombok의 @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;실제로 아래의 샘플 JSON으로 POJO를 만들어 보면 중첩 구조라&lt;br /&gt;POJO클래스가 두 개 생성됩니다.&lt;/p&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;{
    &quot;resultStatus&quot;: 1,
    &quot;resultMessage&quot;: &quot;성공&quot;,
    &quot;siteInfoList&quot;: [
        {
            &quot;siteId&quot;: 8,
            &quot;siteName&quot;: &quot;Test&quot;,
            &quot;siteAddress&quot;: &quot;Test&quot;,
            &quot;siteType&quot;: &quot;Test&quot;,
            &quot;inScheduleYmd&quot;: &quot;Test&quot;,
            &quot;installState&quot;: &quot;Test&quot;,
            &quot;accessIp&quot;: &quot;Test2&quot;,
            &quot;developerCorp&quot;: &quot;Test&quot;,
            &quot;builderCorp&quot;: &quot;Test&quot;,
            &quot;homepageDomain&quot;: &quot;Test&quot;,
            &quot;etcDesc&quot;: &quot;Test&quot;,
            &quot;ipUpdateTime&quot;: 1608022056000,
            &quot;poscoYn&quot;: &quot;N&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;기본적으로 depth를 가지는 중첩 구조의 JSON 일 경우, 이 플러그인은 각 각의 개별 클래스로 생성시켜 줍니다.&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;메인 POJO 클래스의 이름은 생성할 때 입력한 이름으로 생성되고,&lt;br /&gt;중첩 JSON으로 만들어진 POJO클래스의 이름은 해당 JSON 프로퍼티명 + &amp;lsquo;Item&amp;rsquo;의 이름으로 생성되게 됩니다.&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;inner class로 생성될 필요가 있을 때는 추가 작업이 필요해서,&lt;br /&gt;이 부분을 옵션으로 지원하지 않는 부분은 살짝 아쉬운 점이라고 생각됩니다.&lt;/p&gt;
&lt;pre class=&quot;groovy&quot;&gt;&lt;code&gt;package com.ht.parasite.core.dto;

import java.util.List;
import lombok.Data;

public @Data class SiteInfoRes{
    private int resultStatus;
    private List&amp;lt;SiteInfoListItem&amp;gt; siteInfoList;
    private String resultMessage;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;package com.ht.parasite.core.dto;

import lombok.Data;

public @Data class SiteInfoListItem{
    private String accessIp;
    private long ipUpdateTime;
    private String siteName;
    private String siteAddress;
    private String homepageDomain;
    private String inScheduleYmd;
    private String developerCorp;
    private String installState;
    private String poscoYn;
    private int siteId;
    private String builderCorp;
    private String etcDesc;
    private String siteType;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;생성된 POJO 클래스 코드입니다.&lt;br /&gt;lombok으로 지정하고 생성했기 때문에 @Data 어노테이션이 붙어 있습니다.&lt;br /&gt;즉, getter, setter, contructor 메서드 코드가 필요 없기 때문에 생성되지 않았습니다.&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;POJO를 생성할 때 멤버 변수의 데이터 타입은 json 코드의 value 값으로 판단해서 적용됩니다.&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;참고로 value가 null로 되어 있는 상태에서 생성하게 되면,&lt;br /&gt;어떤 타입인지 판단할 수 없기 때문에 Object 타입으로 생성됩니다.&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;br /&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;br /&gt;사용해보면서 자기에게 맞는 것을 선택해서 사용하시면 좋을 것 같습니다.&lt;/p&gt;</description>
      <category>개발</category>
      <category>IntelliJ</category>
      <category>IntelliJ 플러그인</category>
      <category>JSON to Object</category>
      <category>JSON to POJO</category>
      <category>POJO from JSON</category>
      <category>rest api</category>
      <category>RoboPOJO Generator</category>
      <category>SpringBoot</category>
      <category>인텔리J</category>
      <category>인텔리J 플러그인</category>
      <author>yorath</author>
      <guid isPermaLink="true">https://yonguri.tistory.com/142</guid>
      <comments>https://yonguri.tistory.com/142#entry142comment</comments>
      <pubDate>Thu, 1 Jul 2021 17:22:57 +0900</pubDate>
    </item>
    <item>
      <title>Java 날짜함수 Tip - LocalDate를 이용한 현재달의 시작일(1일), 마지막일 얻어오기</title>
      <link>https://yonguri.tistory.com/141</link>
      <description>&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;Date 관련해서는 Date, Calendar, LocalDate 등의 기본 클래스와 JodaTime와 같은 써드파티 라이브러리 등&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;pre id=&quot;code_1624613966904&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;  @Test
  public void dateTest() {

    // 현재월의 첫번째 일자 - use Calendar
    Calendar calendar = Calendar.getInstance();
    calendar.set(Calendar.DAY_OF_MONTH, 1);
    SimpleDateFormat simpleDateFormat = new SimpleDateFormat(DateUtil.YYYY_MM_DD);
    String startDate = simpleDateFormat.format(calendar.getTime());
    System.out.println(startDate);  // 2021-06-01

    // 현재월의 첫번째 일자 - use LocalDate
    LocalDate firstDate = LocalDate.now().with(TemporalAdjusters.firstDayOfMonth());
    String firstDateString = firstDate.format(DateTimeFormatter.ISO_DATE);
    System.out.println(firstDateString);  // 2021-06-01

    // 현재월의 마지막 일자
    LocalDate lastdDate = LocalDate.now().with(TemporalAdjusters.lastDayOfMonth());
    String lastDateString = lastdDate.format(DateTimeFormatter.ISO_DATE);
    System.out.println(lastDateString); // 2021-06-30

    // 현재월의 첫번째 월요일인 날짜.
    LocalDate dateOfFirstMonday = LocalDate.now().with(TemporalAdjusters.firstInMonth(DayOfWeek.MONDAY));
    System.out.println(dateOfFirstMonday);  // 2021-06-07

  }&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;현재 월의 첫 번째 날짜, 즉 1일과 말일을 가져오기 위해서&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Calendar과 LocalDate를 비교한 간단한 테스트 코드입니다.&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;LocalDate를 사용한 방법이 훨씬 간단하며, 코드가 직관적입니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 842px;&quot;&gt;&lt;span&gt;TemporalAdjusters.&lt;b&gt;firstDayOfMonth&lt;/b&gt;()&amp;nbsp; -&amp;gt; 해당월의 첫번째 날짜&lt;/span&gt;&lt;br /&gt;&lt;span&gt;TemporalAdjusters.&lt;b&gt;lastDayOfMonth&lt;/b&gt;()&amp;nbsp; &amp;nbsp;-&amp;gt; 해당월의 마지막 날짜&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&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;span style=&quot;color: #006dd7;&quot;&gt; 첫 번째 월요일인 날짜'&lt;/span&gt;등 다양한 조건의 날짜들을 가져올 수 있습니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 100%;&quot;&gt;LocalDate.now().with(TemporalAdjusters.firstInMonth(&lt;b&gt;DayOfWeek.MONDAY)&lt;/b&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&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;JodaTime 라이브러리도&amp;nbsp; 다양하고, 간편하나 날짜 관련 기능들을 제공하는 훌륭한 라이브러리이긴 하지만&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Java8 이후로 Date관련 클래스들의 기능이 많이 개선되어, 잘 찾아서 사용하면 굳이 써드파티의 힘을 빌리지 않아도 될 것 같습니다.&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;JodaTime - &lt;a href=&quot;https://www.joda.org/joda-time/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://www.joda.org/joda-time/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1624613739758&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;Joda-Time - Home&quot; data-og-description=&quot;Releases Release 2.10.10 is the current latest release. This release is considered stable and worthy of the 2.x tag. See the change notes for full details. Joda-Time requires Java SE 5 or later and has no dependencies. There is a compile-time dependency on&quot; data-og-host=&quot;www.joda.org&quot; data-og-source-url=&quot;https://www.joda.org/joda-time/&quot; data-og-url=&quot;https://www.joda.org/joda-time/&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://www.joda.org/joda-time/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.joda.org/joda-time/&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;Joda-Time - Home&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Releases Release 2.10.10 is the current latest release. This release is considered stable and worthy of the 2.x tag. See the change notes for full details. Joda-Time requires Java SE 5 or later and has no dependencies. There is a compile-time dependency on&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.joda.org&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>개발</category>
      <category>java calendar</category>
      <category>Java Date</category>
      <category>Java LocalDate</category>
      <category>Java 날짜</category>
      <category>Java 날짜함수</category>
      <category>LocalDate</category>
      <category>자바 날짜다루기</category>
      <category>자바 날짜함수</category>
      <author>yorath</author>
      <guid isPermaLink="true">https://yonguri.tistory.com/141</guid>
      <comments>https://yonguri.tistory.com/141#entry141comment</comments>
      <pubDate>Fri, 25 Jun 2021 18:51:53 +0900</pubDate>
    </item>
    <item>
      <title>[API] Postman의 tests 기능을 활용한 환경변수 자동 세팅방법</title>
      <link>https://yonguri.tistory.com/140</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;391&quot; data-origin-height=&quot;434&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Y7HPs/btq7U0rTZLg/2U2WWcUcHV0qhVUF6w0Jc1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Y7HPs/btq7U0rTZLg/2U2WWcUcHV0qhVUF6w0Jc1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Y7HPs/btq7U0rTZLg/2U2WWcUcHV0qhVUF6w0Jc1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FY7HPs%2Fbtq7U0rTZLg%2F2U2WWcUcHV0qhVUF6w0Jc1%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;391&quot; height=&quot;434&quot; data-origin-width=&quot;391&quot; data-origin-height=&quot;434&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;Rest 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;&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;API 설계시 프런트 앤드 파트와의 협업에 있어서도 매우 중요한 커뮤니케이션 도구로 활용됩니다.&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;포스트맨에서 API를 실행하고 난 뒤,&amp;nbsp; 응답값 검증 등 &amp;nbsp;후속작업을 위한 목적으로 제공되는 포스트맨의 Test 기능을 활용한 팁입니다.&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 id=&quot;Postman의tests기능을활용한환경변수자동세팅방법-목적&quot; data-ke-size=&quot;size23&quot;&gt;목적&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;API 테스트를 위해 포스트맨에서 특정 API 호출시,&amp;nbsp; &lt;span style=&quot;color: #006dd7;&quot;&gt;access_token&lt;/span&gt;과 같이 타 API - &lt;u&gt;대표적으로 인증토큰을 발행하는 auth token 발행 API 등&lt;/u&gt; - 의 결과값을 파라미터로 사용하는 경우, 결과값이 변경될 때마다(토큰 유효기간&lt;u&gt; 만료 시)&lt;/u&gt;&amp;nbsp; 포스트맨 환경변수값을 변경해줘야 하는 불편함을 해소하기 위함.&lt;/li&gt;
&lt;li&gt;일반적인&amp;nbsp; Rest API 기반 어플리케이션은 최소한의 보안을 위한 클라이언트의 인증/인가를 위한&amp;nbsp; API를 제공하며, 제공되는 대부분의 API는 인증 API를 통해 access_token등의 API 접속 권한을 얻기 위한 토큰을 발급받게 됨.&lt;/li&gt;
&lt;li&gt;발급되는 토큰은 모든 API의 필수 요청 항목(또는 헤더값)으로 설정됨&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;
&lt;li&gt;즉, 토큰이 만료되는 경우, 포스트맨에서는 아래와 같은 작업이 필요&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;토큰발행 API 호출&lt;/li&gt;
&lt;li&gt;access_token 발급&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #ef6f53;&quot;&gt;토큰 복사&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #ef6f53;&quot;&gt;테스트할 API가 있는 Collection의 환경변수 설정으로 이동&amp;nbsp;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #ef6f53;&quot;&gt;환경변수로 설정해 놓은 토큰 변수의 값 -&amp;gt;&amp;nbsp; 복사한 토큰 값으로 변경&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #ef6f53;&quot;&gt;환경변수 저장&amp;nbsp;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;테스트할 API로 이동 후 테스트&lt;/li&gt;
&lt;/ol&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;토큰이 만료될 때마다 3 ~ 6번까지의 작업을 반복적으로 한다는 게 여간 귀찮은 일이 아닙니다.&lt;br /&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;h3 id=&quot;Postman의tests기능을활용한환경변수자동세팅방법-설정방법&quot; data-ke-size=&quot;size23&quot;&gt;설정 방법&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;포스트맨의 &lt;b&gt;Test&amp;nbsp; 지원 스크립트를&lt;/b&gt; 활용하여 토큰 발급 api의&amp;nbsp; 토큰값을, 테스트할 API의 환경변수값으로 설정&lt;/li&gt;
&lt;li&gt;스크립트를 작성하는 방법은 매우 간단합니다.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;본문 제일 아래의 링크를 참고하셔서 포스트맨 스크립트의 간단한 작성법만 익히시면 바로 적용할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;ol style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: none;&quot;&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;적용절차&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1367&quot; data-origin-height=&quot;948&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bk2k73/btq7VYHa9yM/mS87Zq7Up3WAUXZeKvOtB0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bk2k73/btq7VYHa9yM/mS87Zq7Up3WAUXZeKvOtB0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bk2k73/btq7VYHa9yM/mS87Zq7Up3WAUXZeKvOtB0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbk2k73%2Fbtq7VYHa9yM%2FmS87Zq7Up3WAUXZeKvOtB0%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;1367&quot; height=&quot;948&quot; data-origin-width=&quot;1367&quot; data-origin-height=&quot;948&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;br /&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;테스트 API의 공통 변수 값으로 지정할&amp;nbsp; API(인증 토큰 발행 API) 선택&lt;/li&gt;
&lt;li&gt;Tests 탭 이동&lt;/li&gt;
&lt;li&gt;스크립트 작성
&lt;pre id=&quot;code_1624361385374&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var jsonData = pm.response.json();
pm.environment.set(&quot;access_token&quot;, jsonData.access_token);​&lt;/code&gt;&lt;/pre&gt;
포스트맨의 스크립트는 자바스크립트와 매우 유사하니, 간단한 코드는 어렵지 않게 작성할 수 있습니다.&lt;br /&gt;&lt;br /&gt;이 코드는 API의 결과값(JSON객체)을 jsonData라는 변수값으로 담아서,&lt;br /&gt;포스트맨의 글로벌 범위에서 정의된(pm)&amp;nbsp; 환경변수 중&amp;nbsp; &quot;access_token&quot; 변수(environment.set())값에&amp;nbsp; &lt;br /&gt;해당 API 결과 항목인 acess_token 항목의 값을 대입시키는, 매우 단순한 코드입니다.&amp;nbsp;&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;
&lt;li&gt;&amp;nbsp;api 실행&lt;/li&gt;
&lt;li&gt;포스트맨의 환경변수 값 확인 (아래 화면 참조)&lt;br /&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1195&quot; data-origin-height=&quot;620&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bonZpN/btq7V7YlvAf/OcQJ8XoVLaMV5vfXFBQ4Jk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bonZpN/btq7V7YlvAf/OcQJ8XoVLaMV5vfXFBQ4Jk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bonZpN/btq7V7YlvAf/OcQJ8XoVLaMV5vfXFBQ4Jk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbonZpN%2Fbtq7V7YlvAf%2FOcQJ8XoVLaMV5vfXFBQ4Jk%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;1195&quot; height=&quot;620&quot; data-origin-width=&quot;1195&quot; data-origin-height=&quot;620&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;api 실행 후, 작동되는 test 스크립트를 통해&lt;br /&gt;포스트맨의 환경변수로 정의된 'access_token'값에 api의 응답값이 바로 적용되게 됩니다.&lt;br /&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; letter-spacing: 0px;&quot;&gt;&lt;br /&gt;즉, 토큰이 만료되었다 하더라도,&amp;nbsp; 위의 과정을 반복할 필요가 없어지게 됩니다.&amp;nbsp; &lt;/span&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; letter-spacing: 0px;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;토큰 발행 API 호출&lt;/li&gt;
&lt;li&gt;access_token 발급&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #ef6f53;&quot;&gt;&lt;s&gt;토큰 복사&lt;/s&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #ef6f53;&quot;&gt;&lt;s&gt;테스트할 API가 있는 Collection의 환경변수 설정으로 이동&amp;nbsp;&lt;/s&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #ef6f53;&quot;&gt;&lt;s&gt;환경변수로 설정해 놓은 토큰변수의 값 -&amp;gt;&amp;nbsp; 복사한 토큰값으로 변경&lt;/s&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #ef6f53;&quot;&gt;&lt;s&gt;환경변수 저장&amp;nbsp;&lt;/s&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;테스트할 API로 이동 후 테스트&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3~6번의 과정이 더 이상 불필요해지고,&lt;br /&gt;토큰이 만료되었다면, 1번의 토큰발행 API만 한번 더 실행해 주면,&amp;nbsp; 실제 테스트 할 API를 바로 사용할 수 있게 됩니다.&lt;br /&gt;&lt;br /&gt;포스트맨의 다른 유용한 기능들도 많지만,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 간단한 스크립트를 활용하여&amp;nbsp; Rest 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;&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;Test 스크립트의 기능은 이런 활용방법 외에도 원래의 목적인 결과값 검증 등의 활용도가 높은 기능이니&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;h3 id=&quot;Postman의tests기능을활용한환경변수자동세팅방법-관련링크-Postman내장스크립트문법&quot; data-ke-size=&quot;size23&quot;&gt;관련링크 - Postman 내장 스크립트 문법&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://learning.postman.com/docs/writing-scripts/test-scripts/&quot;&gt;https://learning.postman.com/docs/writing-scripts/test-scripts/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://learning.postman.com/docs/writing-scripts/script-references/test-examples/&quot;&gt;https://learning.postman.com/docs/writing-scripts/script-references/test-examples/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>개발</category>
      <category>API 개발</category>
      <category>API 테스트</category>
      <category>Postman</category>
      <category>Postman API</category>
      <category>Postman Test</category>
      <category>Postman 테스트</category>
      <category>Postman 환경변수</category>
      <category>rest api</category>
      <category>포스트맨</category>
      <category>포스트맨 API</category>
      <author>yorath</author>
      <guid isPermaLink="true">https://yonguri.tistory.com/140</guid>
      <comments>https://yonguri.tistory.com/140#entry140comment</comments>
      <pubDate>Tue, 22 Jun 2021 21:45:11 +0900</pubDate>
    </item>
  </channel>
</rss>