<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>Contributor9</title>
    <link>https://adjh54.tistory.com/</link>
    <description>프로그래밍 기술 및 관심사 공유 목적으로 블로그를 운영하고 있습니다. 감사합니다.</description>
    <language>ko</language>
    <pubDate>Tue, 9 Jun 2026 01:43:55 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>adjh54</managingEditor>
    <image>
      <title>Contributor9</title>
      <url>https://tistory1.daumcdn.net/tistory/5166751/attach/b48c031756424e6c8471507fe99845d3</url>
      <link>https://adjh54.tistory.com</link>
    </image>
    <item>
      <title>[제작 앱 소개] 퀵 링크: 스마트 링크 매니저</title>
      <link>https://adjh54.tistory.com/762</link>
      <description>&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;해당&amp;nbsp;글에서는&amp;nbsp;Contributor9&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;&lt;br /&gt;&lt;/span&gt;
&lt;figure contenteditable=&quot;false&quot; data-ke-type=&quot;emoticon&quot; data-ke-align=&quot;alignCenter&quot; data-emoticon-type=&quot;face&quot; data-emoticon-name=&quot;062&quot; data-emoticon-isanimation=&quot;false&quot; data-emoticon-src=&quot;https://t1.daumcdn.net/axz_keditor/emoticon/face/large/062.png&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/axz_keditor/emoticon/face/large/062.png&quot; width=&quot;80&quot; /&gt;&lt;/figure&gt;
&lt;/blockquote&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span&gt;1) &lt;/span&gt;개발자는&lt;span&gt; &lt;/span&gt;왜&lt;span&gt; &lt;/span&gt;이&lt;span&gt; &lt;/span&gt;앱을&lt;span&gt; &lt;/span&gt;만들었을까&lt;span&gt;?&lt;/span&gt;&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;  개발자는 왜 이 앱을 만들었을까?&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 메모장 한 구석에 언젠가부터 저장된 '긴 링크들'이 가득이었습니다.&lt;/b&gt;&lt;br /&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1206&quot; data-origin-height=&quot;2622&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bcqdAP/dJMcahkljF9/pUFqZyJvrL6veKkKRf2o3k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bcqdAP/dJMcahkljF9/pUFqZyJvrL6veKkKRf2o3k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bcqdAP/dJMcahkljF9/pUFqZyJvrL6veKkKRf2o3k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbcqdAP%2FdJMcahkljF9%2FpUFqZyJvrL6veKkKRf2o3k%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;170&quot; height=&quot;370&quot; data-origin-width=&quot;1206&quot; data-origin-height=&quot;2622&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
 &lt;br /&gt;음.. 이 링크가 뭐였더라..? 뭔가 중요하다고 생각해서 저장을 한 것 같은데.. 무슨 목적으로 붙여 넣기를 한 거지..라고 생각을 하게 되었습니다. 이럴 때면, 어딘가 &lt;b&gt;이런 링크들을 관리해 둘 수 있는 앱이 있었으면 좋겠다고 생각이 들었습니다.&lt;br /&gt;&lt;/b&gt;&lt;br /&gt;그리고 &lt;b&gt;내 마음대로 분류를 하고, 필요할 때마다 빠르게 접근을 할 수 있는 그런 무언가가 있었으면 좋겠다고 생각이 들었습니다.&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;브라우저에서는 즐겨찾기 기능으로 쉽게 정리하고 빠르게 찾아갈 수 있는데.. &lt;b&gt;&lt;u&gt;모바일에서는 관리도 힘들고, 꼭 크롬, 사파리 등에서만 관리가 된다는 게 참 불편하다는 생각이 들어서&amp;nbsp;&lt;/u&gt;&lt;/b&gt; 이 앱을 개발하게 되었습니다.!&lt;br /&gt;&lt;br /&gt;이러한 불편함들을 '퀵 링크: 스마트 링크 매니저'에서 해결하실 수 있습니다!&lt;br /&gt;&lt;br /&gt;&lt;b&gt;이 앱에서는 '간단하게 링크를 복사하고 퀵 링크에서 붙여 넣기'를 해주세요!&lt;/b&gt;&lt;br /&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1197&quot; data-origin-height=&quot;2222&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/GTqwi/dJMcadPFpa0/WfGMkjtmqkUtHD6nhQFmFk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/GTqwi/dJMcadPFpa0/WfGMkjtmqkUtHD6nhQFmFk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/GTqwi/dJMcadPFpa0/WfGMkjtmqkUtHD6nhQFmFk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FGTqwi%2FdJMcadPFpa0%2FWfGMkjtmqkUtHD6nhQFmFk%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;240&quot; height=&quot;446&quot; data-origin-width=&quot;1197&quot; data-origin-height=&quot;2222&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;br /&gt;&lt;b&gt;퀵 링크에서는 빠르게 링크를 분석하여서 사용자에게 보기 편한 화면으로 제공을 합니다&lt;/b&gt;&lt;br /&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1206&quot; data-origin-height=&quot;2208&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cFeUgq/dJMcaiDvaJ8/FFtb4q3mdMTMpKmI8U9ZvK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cFeUgq/dJMcaiDvaJ8/FFtb4q3mdMTMpKmI8U9ZvK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cFeUgq/dJMcaiDvaJ8/FFtb4q3mdMTMpKmI8U9ZvK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcFeUgq%2FdJMcaiDvaJ8%2FFFtb4q3mdMTMpKmI8U9ZvK%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;290&quot; height=&quot;531&quot; data-origin-width=&quot;1206&quot; data-origin-height=&quot;2208&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;br /&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1198&quot; data-origin-height=&quot;2217&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/AMUiy/dJMcaiwJd7L/ErmJOmmpLnf8l01FWjeKq0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/AMUiy/dJMcaiwJd7L/ErmJOmmpLnf8l01FWjeKq0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/AMUiy/dJMcaiwJd7L/ErmJOmmpLnf8l01FWjeKq0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FAMUiy%2FdJMcaiwJd7L%2FErmJOmmpLnf8l01FWjeKq0%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;308&quot; height=&quot;570&quot; data-origin-width=&quot;1198&quot; data-origin-height=&quot;2217&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1206&quot; data-origin-height=&quot;2420&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/X08P9/dJMcabRRRR9/wjkXTB5I2KavxgVqJaWjlK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/X08P9/dJMcabRRRR9/wjkXTB5I2KavxgVqJaWjlK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/X08P9/dJMcabRRRR9/wjkXTB5I2KavxgVqJaWjlK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FX08P9%2FdJMcabRRRR9%2FwjkXTB5I2KavxgVqJaWjlK%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;310&quot; height=&quot;622&quot; data-origin-width=&quot;1206&quot; data-origin-height=&quot;2420&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;br /&gt;&lt;br /&gt;&lt;b&gt;그리고 '나만의 폴더'를 생성해 보세요! 폴더에서 저장된 링크들을 분류해서 관리해 보세요! 필요한 순간 바로바로 찾아볼 수 있습니다&lt;/b&gt;&lt;br /&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1206&quot; data-origin-height=&quot;2216&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/1GJJL/dJMcahklksP/CumGG40N5C3T4RvsZzDhhk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/1GJJL/dJMcahklksP/CumGG40N5C3T4RvsZzDhhk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/1GJJL/dJMcahklksP/CumGG40N5C3T4RvsZzDhhk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F1GJJL%2FdJMcahklksP%2FCumGG40N5C3T4RvsZzDhhk%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;439&quot; height=&quot;807&quot; data-origin-width=&quot;1206&quot; data-origin-height=&quot;2216&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;br /&gt;&lt;br /&gt;&lt;b&gt;화면에서 바로 퀵링크 버튼을 눌러서 빠르게 열어보세요!&lt;/b&gt;&lt;br /&gt;저장한 링크를 손쉽게 열어볼 수 있습니다&lt;br /&gt;&lt;br /&gt;&lt;b&gt;필요에 따라 링크를 고정하고, 다른 사람들과 공유를 할 수 있는 기능을 제공합니다.!&lt;/b&gt;&lt;br /&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1206&quot; data-origin-height=&quot;2212&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/vY5Ri/dJMcacwxpx4/PLKO4xGHSHOO1cTfIR9vk1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/vY5Ri/dJMcacwxpx4/PLKO4xGHSHOO1cTfIR9vk1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/vY5Ri/dJMcacwxpx4/PLKO4xGHSHOO1cTfIR9vk1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FvY5Ri%2FdJMcacwxpx4%2FPLKO4xGHSHOO1cTfIR9vk1%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;339&quot; height=&quot;622&quot; data-origin-width=&quot;1206&quot; data-origin-height=&quot;2212&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;br /&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1206&quot; data-origin-height=&quot;2213&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c0xcku/dJMcaf7Oj5n/6NoxBdoATp5JQORt4uOKk0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c0xcku/dJMcaf7Oj5n/6NoxBdoATp5JQORt4uOKk0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c0xcku/dJMcaf7Oj5n/6NoxBdoATp5JQORt4uOKk0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc0xcku%2FdJMcaf7Oj5n%2F6NoxBdoATp5JQORt4uOKk0%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;294&quot; height=&quot;539&quot; data-origin-width=&quot;1206&quot; data-origin-height=&quot;2213&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;br /&gt;&lt;b&gt;앱 자체도 잠금으로 다른 사람이 알 수 없게 나만의 폴더로 관리할 수 있습니다!&lt;/b&gt;&lt;br /&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1206&quot; data-origin-height=&quot;2414&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/7FbWJ/dJMb99NhIsA/xuT01G1hZ2XkUahqOL8oIK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/7FbWJ/dJMb99NhIsA/xuT01G1hZ2XkUahqOL8oIK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/7FbWJ/dJMb99NhIsA/xuT01G1hZ2XkUahqOL8oIK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F7FbWJ%2FdJMb99NhIsA%2FxuT01G1hZ2XkUahqOL8oIK%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;322&quot; height=&quot;645&quot; data-origin-width=&quot;1206&quot; data-origin-height=&quot;2414&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;br /&gt;&lt;br /&gt;&lt;b&gt;그리고, 앱 전체 말고 나만 알고 싶은 폴더들은 비밀번호로 잠가 둘 수 있습니다&lt;/b&gt;&lt;br /&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1206&quot; data-origin-height=&quot;2236&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/AMmdW/dJMcadhUyGE/4GLGf82EkpbPQOElZx8Ds0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/AMmdW/dJMcadhUyGE/4GLGf82EkpbPQOElZx8Ds0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/AMmdW/dJMcadhUyGE/4GLGf82EkpbPQOElZx8Ds0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FAMmdW%2FdJMcadhUyGE%2F4GLGf82EkpbPQOElZx8Ds0%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;195&quot; height=&quot;362&quot; data-origin-width=&quot;1206&quot; data-origin-height=&quot;2236&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;br /&gt;&lt;br /&gt;&lt;b&gt;이런 편리한 기능으로 링크를 쉽게 관리해 보세요!&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2) 앱 소개- 퀵 링크: 스마트 링크 매니저&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1267&quot; data-origin-height=&quot;586&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/7Va2q/dJMcabj7xOW/asNfyJvRsk4KhJRtb8E1J0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/7Va2q/dJMcabj7xOW/asNfyJvRsk4KhJRtb8E1J0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/7Va2q/dJMcabj7xOW/asNfyJvRsk4KhJRtb8E1J0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F7Va2q%2FdJMcabj7xOW%2FasNfyJvRsk4KhJRtb8E1J0%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;1267&quot; height=&quot;586&quot; data-origin-width=&quot;1267&quot; data-origin-height=&quot;586&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1272&quot; data-origin-height=&quot;605&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/euxZJ9/dJMcagyTPvN/dKWehRZKU9JkAfz0NxEG20/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/euxZJ9/dJMcagyTPvN/dKWehRZKU9JkAfz0NxEG20/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/euxZJ9/dJMcagyTPvN/dKWehRZKU9JkAfz0NxEG20/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FeuxZJ9%2FdJMcagyTPvN%2FdKWehRZKU9JkAfz0NxEG20%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;1272&quot; height=&quot;605&quot; data-origin-width=&quot;1272&quot; data-origin-height=&quot;605&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  앱 소개- 퀵 링크: 스마트 링크 매니저&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;- 다시 보고 싶은 링크, 정작 필요할 때 못 찾으신 적 있으신가요? 메모장 한구석에 던져둔 링크, 막상 필요한 순간엔 어디 있는지 모르고. &lt;br /&gt;&lt;br /&gt;링크 주소만 봐서는 무슨 사이트인지 기억도 안 나고. 모바일에서 북마크를 꺼내는 것도 여간 번거로운 게 아니죠. &lt;br /&gt;&lt;br /&gt;&lt;b&gt;퀵 링크 앱이 그 불편함을 해결합니다.&lt;br /&gt;&lt;/b&gt; &lt;br /&gt;복사한 링크를 붙여 넣기 한 번으로 저장하면, 앱이 자동으로 사이트 제목과 설명, 대표 이미지까지 불러와 한눈에 알아볼 수 있는 카드로 만들어줍니다. &lt;br /&gt;나중에 꺼내볼 때 URL 대신 익숙한 썸네일과 제목이 반겨주니, &amp;ldquo;이게 그 링크였지&amp;rdquo; 하는 순간이 훨씬 빨라져요. 키워드 하나로 바로 찾을 수 있는 스마트 링크 관리 앱입니다. &lt;br /&gt;나만의 폴더로 링크를 깔끔하게 분류하고, 자주 찾는 링크는 상단에 고정해 언제든 빠르게 접근할 수 있어요. &lt;br /&gt;&lt;br /&gt;업무 레퍼런스, 뉴스 아티클, 쇼핑 목록, 개발 문서까지 링크를 많이 다루는 분이라면 누구에게나 딱 맞는 도구입니다.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;1. 폴더 리스트&lt;/b&gt; &lt;br /&gt;- 링크를 체계적으로 관리하는 시작점입니다. 나만의 폴더를 만들고, 원하는 방식으로 정리하고, 중요한 것은 보호하세요. &lt;br /&gt;&lt;br /&gt;&lt;b&gt;[주요 기능 1] 폴더 관리&lt;/b&gt; &lt;br /&gt;- 원하는 이모지와 색상으로 폴더를 만들고, 드래그 앤 드롭으로 순서를 자유롭게 배치할 수 있습니다. &lt;br /&gt;- 자주 쓰는 폴더는 상단에 핀 고정해 빠르게 접근하세요. &lt;br /&gt;&lt;br /&gt;&lt;b&gt;&lt;b&gt;[주요 기능 2]&lt;/b&gt;&amp;nbsp; 보기 &amp;amp; 탐색&lt;/b&gt; &lt;br /&gt;- 리스트 보기와 앨범 보기 중 원하는 방식으로 폴더를 확인할 수 있습니다. 폴더 이름 검색과 생성일 / 이름 / 링크 수 기준 정렬도 지원합니다. &lt;br /&gt;&lt;br /&gt;&lt;b&gt;[주요 기능 3] 보안 &amp;amp; 공유&lt;/b&gt; &lt;br /&gt;- 민감한 폴더는 PIN 번호로 잠그세요. 열람, 수정, 공유 모두 잠금 해제 후에만 가능합니다. &lt;br /&gt;- 폴더 안의 링크 목록은 텍스트로 한 번에 공유할 수 있습니다. &lt;br /&gt;&lt;br /&gt;&lt;b&gt;[주요 기능 4] 다크 모드&lt;/b&gt; &lt;br /&gt;- 라이트 / 다크 모드를 모두 지원합니다. &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;2. 링크 목록&lt;/b&gt; &lt;br /&gt;- 폴더 안의 링크를 자유롭게 탐색하고 관리하는 공간입니다. 검색과 필터로 원하는 링크를 빠르게 찾고, 스와이프 한 번으로 다양한 작업을 바로 실행할 수 있습니다. &lt;br /&gt;&lt;br /&gt;&lt;b&gt;[주요 기능 1] 보기 &amp;amp; 탐색&lt;/b&gt; &lt;br /&gt;- 리스트 보기와 카드 그리드 보기를 전환할 수 있습니다. 제목과 도메인으로 실시간 검색하고, 오늘 / 1주일 / 1개월 / 3개월 또는 직접 입력한 날짜 범위로 필터링할 수 있습니다. &lt;br /&gt;- 최신순 &amp;middot; 오래된 순 &amp;middot; 이름순 &amp;middot; 조회수순 정렬도 지원하며, 핀 고정 항목은 항상 최상단에 유지됩니다. &lt;br /&gt;&lt;br /&gt;&lt;b&gt;[주요 기능 2] 빠른 액션&lt;/b&gt; &lt;br /&gt;- 링크를 스와이프 하면 핀 고정 &amp;middot; URL 복사 &amp;middot; 폴더 이동 &amp;middot; 공유 &amp;middot; 편집 &amp;middot; 삭제를 바로 실행할 수 있습니다. &lt;br /&gt;- 목록을 아래로 당기면 최신 상태로 새로고침됩니다. &lt;br /&gt;&lt;br /&gt;&lt;b&gt;[주요 기능 3] 조회수 &amp;amp; 공유&lt;/b&gt; &lt;br /&gt;- 링크를 열 때마다 조회수가 자동 기록됩니다. 폴더 안의 링크 목록은 텍스트로 한 번에 공유할 수 있습니다. &lt;br /&gt;&lt;br /&gt;&lt;b&gt;[주요 기능 4]&amp;nbsp;&amp;nbsp;잠긴 폴더 보호&lt;/b&gt; &lt;br /&gt;- 전체 보기에서는 PIN으로 잠긴 폴더의 링크가 자동으로 제외됩니다. &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;3. 통계&lt;/b&gt; &lt;br /&gt;- 내 링크 사용 습관을 한눈에 파악하세요. 저장한 링크와 폴더를 다양한 기준으로 분석해 나만의 인사이트를 발견할 수 있습니다 &lt;br /&gt;&lt;br /&gt;&lt;b&gt;[주요 기능 1]&amp;nbsp;핵심 지표 요약&lt;/b&gt; &lt;br /&gt;- 총 링크 수, 폴더 수, 전체 조회수, 고정 링크 수를 카드 형태로 확인할 수 있습니다. &lt;br /&gt;&lt;br /&gt;&lt;b&gt;[주요 기능 2]&amp;nbsp;&amp;nbsp;등록 &amp;amp; 사용 패턴&lt;/b&gt; &lt;br /&gt;- 일간 &amp;middot; 주간 &amp;middot; 월간 &amp;middot; 전체 탭으로 링크 등록 흐름을 바 차트로 시각화하고, 이전/다음 버튼으로 기간을 탐색할 수 있습니다. 요일별 저장 패턴과 미열람 링크 &amp;middot; 메모 작성 &amp;middot; 폴더 미지정 링크 비율도 함께 확인하세요. &lt;br /&gt;&lt;br /&gt;&lt;b&gt;[주요 기능 3] 폴더 &amp;amp; 사이트 분석&lt;/b&gt; &lt;br /&gt;- 도넛 차트와 막대그래프로 폴더별 링크 분포를 비교하고, 폴더 활동 점수(링크 수 + 조회수) 랭킹도 확인할 수 있습니다. &lt;br /&gt;자주 저장한 도메인 Top 8과 태그 클라우드도 제공합니다.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;[주요 기능 4]&amp;nbsp;&amp;nbsp;많이 본 링크 Top 5&lt;/b&gt; &lt;br /&gt;- 조회수 기준 상위 5개 링크를 바로 확인하고, 탭 한 번으로 열 수 있습니다. &lt;br /&gt;&lt;br /&gt;&lt;b&gt;[주요 기능 5]&amp;nbsp;&amp;nbsp;비밀 폴더 통계 포함 옵션&lt;/b&gt; &lt;br /&gt;- PIN으로 잠긴 폴더를 통계에 포함할지 직접 선택할 수 있습니다. &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;4. 설정&lt;br /&gt;&lt;/b&gt; &lt;br /&gt;- 앱을 내 취향에 맞게 조정하세요. 언어, 테마, 보안, 데이터 관리까지 모든 설정을 한 곳에서 관리할 수 있습니다. &lt;br /&gt;- 일반 언어(한국어 / English)와 테마(라이트 / 다크 모드)를 설정할 수 있습니다. &lt;br /&gt;- 보안 PIN을 설정하면 앱 실행 시마다 인증이 필요합니다. 인증 전까지 폴더와 링크가 표시되지 않아 개인 정보를 안전하게 보호할 수 있습니다. &lt;br /&gt;- 데이터 관리 링크, 폴더, 최근 검색어를 개별 또는 전체 초기화할 수 있습니다. 실수를 방지하기 위해 확인 절차가 포함되어 있습니다. &lt;br /&gt;- 지원 &amp;amp; 피드백 불편한 점은 문의하기로 알려주세요. &lt;br /&gt;- 앱 버전과 기기 정보가 자동으로 포함됩니다. 앱이 마음에 드셨다면 스토어에 리뷰를 남겨주세요. 앱 정보 버전 확인 및 업데이트, 앱 공유, 제작자 소개, 개인정보 처리방침 및 이용약관, 오픈소스 라이브러리를 확인할 수 있습니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;3)&amp;nbsp;다운로드&lt;/b&gt;&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;/b&gt;1. Google Play Store&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;450&quot; data-origin-height=&quot;450&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/GqCmz/dJMcab5rC6Z/PlETZpyy4k3nvt6z9xpK4k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/GqCmz/dJMcab5rC6Z/PlETZpyy4k3nvt6z9xpK4k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/GqCmz/dJMcab5rC6Z/PlETZpyy4k3nvt6z9xpK4k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FGqCmz%2FdJMcab5rC6Z%2FPlETZpyy4k3nvt6z9xpK4k%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;450&quot; height=&quot;450&quot; data-origin-width=&quot;450&quot; data-origin-height=&quot;450&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;figure id=&quot;og_1780039785976&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;퀵 링크: 스마트 링크 매니저 - Google Play 앱&quot; data-og-description=&quot;저장은 한 번, 검색은 한 단어로&quot; data-og-host=&quot;play.google.com&quot; data-og-source-url=&quot;https://play.google.com/store/apps/details?id=com.tha.linkmanager&quot; data-og-url=&quot;https://play.google.com/store/apps/details?id=com.tha.linkmanager&amp;amp;hl=ko&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/4XNzW/dJMb9kT9FXs/HHH0cfD3hFU0sHeZ7B0PD1/img.png?width=512&amp;amp;height=512&amp;amp;face=0_0_512_512,https://scrap.kakaocdn.net/dn/QH4lS/dJMb82eTLPU/J6tGcRUxoj1XPoY1E6vZ9K/img.png?width=600&amp;amp;height=300&amp;amp;face=0_0_600_300,https://scrap.kakaocdn.net/dn/LzXz1/dJMb9jgD4uI/z5ybFYYx2wPXD6Wlo14L61/img.png?width=240&amp;amp;height=240&amp;amp;face=0_0_240_240&quot;&gt;&lt;a href=&quot;https://play.google.com/store/apps/details?id=com.tha.linkmanager&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://play.google.com/store/apps/details?id=com.tha.linkmanager&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/4XNzW/dJMb9kT9FXs/HHH0cfD3hFU0sHeZ7B0PD1/img.png?width=512&amp;amp;height=512&amp;amp;face=0_0_512_512,https://scrap.kakaocdn.net/dn/QH4lS/dJMb82eTLPU/J6tGcRUxoj1XPoY1E6vZ9K/img.png?width=600&amp;amp;height=300&amp;amp;face=0_0_600_300,https://scrap.kakaocdn.net/dn/LzXz1/dJMb9jgD4uI/z5ybFYYx2wPXD6Wlo14L61/img.png?width=240&amp;amp;height=240&amp;amp;face=0_0_240_240');&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;퀵 링크: 스마트 링크 매니저 - Google Play 앱&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;play.google.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;h3 data-ke-size=&quot;size23&quot;&gt;2.&amp;nbsp;App&amp;nbsp;Store&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;530&quot; data-origin-height=&quot;530&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rW89v/dJMcaftes9T/nnnKOg2zkupSHrBzJjK4d0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rW89v/dJMcaftes9T/nnnKOg2zkupSHrBzJjK4d0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rW89v/dJMcaftes9T/nnnKOg2zkupSHrBzJjK4d0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FrW89v%2FdJMcaftes9T%2FnnnKOg2zkupSHrBzJjK4d0%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;530&quot; height=&quot;530&quot; data-origin-width=&quot;530&quot; data-origin-height=&quot;530&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;figure id=&quot;og_1780039818547&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;퀵 링크: 스마트 링크 매니저 앱 - App Store&quot; data-og-description=&quot;App&amp;nbsp;Store에서 EcodeLab의 퀵 링크: 스마트 링크 매니저 앱을 다운로드하십시오. 스크린샷, 평가 및 리뷰, 사용자 팁 및 퀵 링크: 스마트 링크 매니저 앱과 비슷한 다른 앱을 볼 수 있습니다.&quot; data-og-host=&quot;apps.apple.com&quot; data-og-source-url=&quot;https://apps.apple.com/kr/app/id6761520189&quot; data-og-url=&quot;https://apps.apple.com/kr/app/%ED%80%B5-%EB%A7%81%ED%81%AC-%EC%8A%A4%EB%A7%88%ED%8A%B8-%EB%A7%81%ED%81%AC-%EB%A7%A4%EB%8B%88%EC%A0%80/id6761520189&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/mB4Ka/dJMb85WZ8DS/vy9qOBnmTCCexqB6hVXxW1/img.jpg?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/ruR9C/dJMb8PGCWlM/ZcFLlovdGPupSCKMmFv0xk/img.jpg?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630&quot;&gt;&lt;a href=&quot;https://apps.apple.com/kr/app/id6761520189&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://apps.apple.com/kr/app/id6761520189&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/mB4Ka/dJMb85WZ8DS/vy9qOBnmTCCexqB6hVXxW1/img.jpg?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/ruR9C/dJMb8PGCWlM/ZcFLlovdGPupSCKMmFv0xk/img.jpg?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;퀵 링크: 스마트 링크 매니저 앱 - App Store&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;App&amp;nbsp;Store에서 EcodeLab의 퀵 링크: 스마트 링크 매니저 앱을 다운로드하십시오. 스크린샷, 평가 및 리뷰, 사용자 팁 및 퀵 링크: 스마트 링크 매니저 앱과 비슷한 다른 앱을 볼 수 있습니다.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;apps.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;&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;⬇️&amp;nbsp;개발자의&amp;nbsp;다양한&amp;nbsp;앱을&amp;nbsp;보고&amp;nbsp;싶으시면&amp;nbsp;아래의&amp;nbsp;링크를&amp;nbsp;확인해 주세요&amp;nbsp;⬇️&lt;/b&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1780039839759&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;Ecodelab - 제작 앱 소개&quot; data-og-description=&quot;Ecodelab에서 개발한 다양한 앱들을 소개합니다&quot; data-og-host=&quot;www.ecodelab.im&quot; data-og-source-url=&quot;https://ecodelab.im/main&quot; data-og-url=&quot;https://www.ecodelab.im&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bIB7Sb/dJMb8Rj8PJY/FK91SCVPUxp883o3mD2Pf0/img.png?width=1024&amp;amp;height=1024&amp;amp;face=0_0_1024_1024,https://scrap.kakaocdn.net/dn/qYTJ7/dJMb8QMiSYB/n3ESdWawrXhaeVkSFsAtCK/img.png?width=1024&amp;amp;height=1024&amp;amp;face=0_0_1024_1024&quot;&gt;&lt;a href=&quot;https://ecodelab.im/main&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://ecodelab.im/main&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bIB7Sb/dJMb8Rj8PJY/FK91SCVPUxp883o3mD2Pf0/img.png?width=1024&amp;amp;height=1024&amp;amp;face=0_0_1024_1024,https://scrap.kakaocdn.net/dn/qYTJ7/dJMb8QMiSYB/n3ESdWawrXhaeVkSFsAtCK/img.png?width=1024&amp;amp;height=1024&amp;amp;face=0_0_1024_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;Ecodelab - 제작 앱 소개&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Ecodelab에서 개발한 다양한 앱들을 소개합니다&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.ecodelab.im&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;&amp;nbsp;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;오늘도&amp;nbsp;감사합니다.&amp;nbsp; &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;</description>
      <category>Contributor9/제작 앱 소개</category>
      <category>url 관리</category>
      <category>URL 관리 앱</category>
      <category>URL 정리</category>
      <category>링크 관리</category>
      <category>링크 관리 앱</category>
      <category>링크 매니저</category>
      <category>링크 앱</category>
      <category>주소 관리</category>
      <category>주소 관리 애플리케이션</category>
      <category>주소 관리 앱</category>
      <author>adjh54</author>
      <guid isPermaLink="true">https://adjh54.tistory.com/762</guid>
      <comments>https://adjh54.tistory.com/762#entry762comment</comments>
      <pubDate>Fri, 29 May 2026 16:33:09 +0900</pubDate>
    </item>
    <item>
      <title>[Java] Google Search Console 이해하고 API 연동하기</title>
      <link>https://adjh54.tistory.com/761</link>
      <description>&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
 해당 글에서는 Google Search Console에서 수집된 데이터를 google-api-services-searchconsole 라이브러리를 활용하여서 데이터를 불러오는 API 활용 방법에 대해서 알아봅니다
 &lt;br&gt;
 &lt;br&gt; 
 &lt;figure contenteditable=&quot;false&quot; data-ke-type=&quot;emoticon&quot; data-ke-align=&quot;alignCenter&quot; data-emoticon-type=&quot;face&quot; data-emoticon-name=&quot;073&quot; data-emoticon-isanimation=&quot;false&quot; data-emoticon-src=&quot;https://t1.daumcdn.net/axz_keditor/emoticon/face/large/073.png&quot;&gt;
  &lt;img src=&quot;https://t1.daumcdn.net/axz_keditor/emoticon/face/large/073.png&quot; width=&quot;80&quot;&gt;
 &lt;/figure&gt; 
 &lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;/p&gt; 
&lt;/blockquote&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  [참고]&lt;/b&gt; 이전에 Google Analaytics를 연동한 내용에 대해 궁금하시면 아래의 글을 참고하시면 도움이 됩니다.&lt;/blockquote&gt;&lt;figure data-ke-type=&quot;opengraph&quot; data-og-title=&quot;[Java] GA4(Google Analytics) 이해하고 활용하기 -2: 수집된 데이터 조회 환경 설정 및 활용 방법&quot; data-ke-align=&quot;alignCenter&quot; data-og-description=&quot;해당 글에서는 GA에서 수집된 데이터를 API를 통해서 가져와서 화면상에 출력하는 과정을 포함한 글입니다.  [참고] React 환경에서 수집한 데이터에 대해서 Spring Boot 환경에서 이를 조회하는 방&quot; data-og-host=&quot;adjh54.tistory.com&quot; data-og-source-url=&quot;https://adjh54.tistory.com/738&quot; data-og-image=&quot;https://blog.kakaocdn.net/dna/ivql5/dJMb9frK6p3/AAAAAAAAAAAAAAAAAAAAAMUc74IQRy1MPcqo2xSxnWK4DxvXLV5MKhafOj3waOED/img.png?credential=yqXZFxpELC7KVnFOS48ylbz2pIh7yKj8&amp;amp;expires=1780239599&amp;amp;allow_ip=&amp;amp;allow_referer=&amp;amp;signature=UgpRtKdLuHuTqf5qbATcR9Ir7Rc%3D&quot; data-og-url=&quot;https://adjh54.tistory.com/738&quot;&gt;&lt;a href=&quot;https://adjh54.tistory.com/738&quot; target=&quot;_blank&quot; data-source-url=&quot;https://adjh54.tistory.com/738&quot;&gt;&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://blog.kakaocdn.net/dna/ivql5/dJMb9frK6p3/AAAAAAAAAAAAAAAAAAAAAMUc74IQRy1MPcqo2xSxnWK4DxvXLV5MKhafOj3waOED/img.png?credential=yqXZFxpELC7KVnFOS48ylbz2pIh7yKj8&amp;amp;expires=1780239599&amp;amp;allow_ip=&amp;amp;allow_referer=&amp;amp;signature=UgpRtKdLuHuTqf5qbATcR9Ir7Rc%3D')&quot;&gt; &lt;/div&gt;&lt;div class=&quot;og-text&quot;&gt;&lt;p class=&quot;og-title&quot;&gt;[Java] GA4(Google Analytics) 이해하고 활용하기 -2: 수집된 데이터 조회 환경 설정 및 활용 방법&lt;/p&gt;&lt;p class=&quot;og-desc&quot;&gt;해당 글에서는 GA에서 수집된 데이터를 API를 통해서 가져와서 화면상에 출력하는 과정을 포함한 글입니다.  [참고] React 환경에서 수집한 데이터에 대해서 Spring Boot 환경에서 이를 조회하는 방&lt;/p&gt;&lt;p class=&quot;og-host&quot;&gt;adjh54.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;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;h2 data-ke-size=&quot;size26&quot;&gt;1) GSC(Google Search Console)&lt;/h2&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot;&gt;&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;&amp;nbsp;  GSC(Google Search Console)&lt;/b&gt;&lt;br&gt;&lt;br&gt;&lt;b&gt;- Google이 무료로 제공하는 웹마스터 도구로, 내 웹사이트가 Google 검색에서 어떻게 노출되고 있는지 모니터링하고 최적화할 수 있는 서비스를 의미합니다.&lt;/b&gt;&lt;/blockquote&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. GSC 주요 기능 요약&lt;/h3&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2032&quot; data-origin-height=&quot;1078&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bHSkYA/dJMcad26BgQ/Lnhu3hAZW5UHQL5fRknZvk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bHSkYA/dJMcad26BgQ/Lnhu3hAZW5UHQL5fRknZvk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bHSkYA/dJMcad26BgQ/Lnhu3hAZW5UHQL5fRknZvk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbHSkYA%2FdJMcad26BgQ%2FLnhu3hAZW5UHQL5fRknZvk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2032&quot; height=&quot;1078&quot; data-origin-width=&quot;2032&quot; data-origin-height=&quot;1078&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-style=&quot;style2&quot; data-ke-align=&quot;alignLeft&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;b&gt;기능&lt;/b&gt;&lt;/td&gt;&lt;td&gt;&lt;b&gt;설명&lt;/b&gt;&lt;/td&gt;&lt;td&gt;&lt;b&gt;주요 사항&lt;/b&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;b&gt;실적 (Performance)&lt;/b&gt;&lt;/td&gt;&lt;td&gt;검색 결과에서의 사이트 성과 분석&lt;/td&gt;&lt;td&gt;클릭수, 노출수, CTR, 평균 게재순위&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;b&gt;URL 검사 (URL Inspection)&lt;/b&gt;&lt;/td&gt;&lt;td&gt;특정 URL의 크롤링/색인 상태 확인&lt;/td&gt;&lt;td&gt;색인 여부, 모바일 사용성, 구조화 데이터 유효성&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;b&gt;페이지 색인 생성&lt;/b&gt;&lt;/td&gt;&lt;td&gt;전체 사이트의 색인 현황 보고서&lt;/td&gt;&lt;td&gt;색인된 페이지 수, 미색인 사유&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;b&gt;사이트맵 (Sitemaps)&lt;/b&gt;&lt;/td&gt;&lt;td&gt;sitemap.xml 제출 및 상태 추적&lt;/td&gt;&lt;td&gt;제출 URL 수 vs 색인 URL 수&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;b&gt;코어 웹 바이탈&lt;/b&gt;&lt;/td&gt;&lt;td&gt;페이지 로딩/반응 성능 측정&lt;/td&gt;&lt;td&gt;LCP, INP, CLS&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;b&gt;모바일 사용성&lt;/b&gt;&lt;/td&gt;&lt;td&gt;모바일 환경 문제 감지&lt;/td&gt;&lt;td&gt;텍스트 크기, 터치 요소 간격, 뷰포트 설정&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;b&gt;구조화된 데이터&lt;/b&gt;&lt;/td&gt;&lt;td&gt;리치 결과(Rich Results) 유효성 검사&lt;/td&gt;&lt;td&gt;FAQ, 제품, 리뷰 등 스키마 오류/경고&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;b&gt;링크 (Links)&lt;/b&gt;&lt;/td&gt;&lt;td&gt;내부/외부 링크 구조 분석&lt;/td&gt;&lt;td&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;br&gt;&amp;nbsp;&lt;/p&gt;&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. 실적 보고서 주요 지표&lt;/h3&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2032&quot; data-origin-height=&quot;1078&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/byh4AP/dJMcaarQ2qv/qBYgkrbEn9KEguvRc5aL30/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/byh4AP/dJMcaarQ2qv/qBYgkrbEn9KEguvRc5aL30/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/byh4AP/dJMcaarQ2qv/qBYgkrbEn9KEguvRc5aL30/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbyh4AP%2FdJMcaarQ2qv%2FqBYgkrbEn9KEguvRc5aL30%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2032&quot; height=&quot;1078&quot; data-origin-width=&quot;2032&quot; data-origin-height=&quot;1078&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-style=&quot;style2&quot; data-ke-align=&quot;alignLeft&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;b&gt;기능&lt;/b&gt;&lt;/td&gt;&lt;td&gt;&lt;b&gt;지표 설명&lt;/b&gt;&lt;/td&gt;&lt;td&gt;&lt;b&gt;포인트&lt;/b&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;b&gt;총 클릭수&lt;/b&gt;&lt;/td&gt;&lt;td&gt;검색 결과에서 사이트를 클릭한 횟수&lt;/td&gt;&lt;td&gt;실제 유입 트래픽 파악&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;b&gt;총 노출수&lt;/b&gt;&lt;/td&gt;&lt;td&gt;검색 결과에 사이트가 표시된 횟수&lt;/td&gt;&lt;td&gt;검색 가시성 측정&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;b&gt;평균 CTR&lt;/b&gt;&lt;/td&gt;&lt;td&gt;노출 대비 클릭 비율 (%)&lt;/td&gt;&lt;td&gt;메타 타이틀/디스크립션 효과 판단&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;b&gt;평균 게재순위&lt;/b&gt;&lt;/td&gt;&lt;td&gt;검색 결과 내 평균 위치&lt;/td&gt;&lt;td&gt;SEO 개선 효과 추적&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;h2 data-ke-size=&quot;size26&quot;&gt;2) 환경 구성하기 -1 : Google Cloud 서비스 허용&lt;/h2&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. Google Cloud에 접속&lt;/h3&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2032&quot; data-origin-height=&quot;1080&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/P4veW/dJMcac4ehoa/rkpQ2D99YxEj8T7FkcBeYK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/P4veW/dJMcac4ehoa/rkpQ2D99YxEj8T7FkcBeYK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/P4veW/dJMcac4ehoa/rkpQ2D99YxEj8T7FkcBeYK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FP4veW%2FdJMcac4ehoa%2FrkpQ2D99YxEj8T7FkcBeYK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2032&quot; height=&quot;1080&quot; data-origin-width=&quot;2032&quot; data-origin-height=&quot;1080&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure data-ke-type=&quot;opengraph&quot; data-og-title=&quot;Google 클라우드 플랫폼&quot; data-ke-align=&quot;alignCenter&quot; data-og-description=&quot;로그인 Google 클라우드 플랫폼으로 이동&quot; data-og-host=&quot;accounts.google.com&quot; data-og-source-url=&quot;https://console.cloud.google.com/welcome&quot; data-og-url=&quot;https://accounts.google.com/v3/signin/identifier?continue=https%3A%2F%2Fconsole.cloud.google.com%2Fwelcome&amp;amp;dsh=S-1744417872%3A1779265852957093&amp;amp;followup=https%3A%2F%2Fconsole.cloud.google.com%2Fwelcome&amp;amp;osid=1&amp;amp;passive=1209600&amp;amp;service=cloudconsole&amp;amp;flowName=WebLiteSignIn&amp;amp;flowEntry=ServiceLogin&amp;amp;ifkv=AWa2Pat89zqGy2NUD1FdwcYrqgW8Fm5jbMOl0-viD-TItWUmuka62AghUfnlVNh21emV8pDhKMXv&quot;&gt;&lt;a href=&quot;https://accounts.google.com/v3/signin/identifier?continue=https%3A%2F%2Fconsole.cloud.google.com%2Fwelcome&amp;amp;dsh=S-1744417872%3A1779265852957093&amp;amp;followup=https%3A%2F%2Fconsole.cloud.google.com%2Fwelcome&amp;amp;osid=1&amp;amp;passive=1209600&amp;amp;service=cloudconsole&amp;amp;flowName=WebLiteSignIn&amp;amp;flowEntry=ServiceLogin&amp;amp;ifkv=AWa2Pat89zqGy2NUD1FdwcYrqgW8Fm5jbMOl0-viD-TItWUmuka62AghUfnlVNh21emV8pDhKMXv&quot; target=&quot;_blank&quot; data-source-url=&quot;https://console.cloud.google.com/welcome&quot;&gt;&lt;div class=&quot;og-image&quot;&gt;&lt;/div&gt;&lt;div class=&quot;og-text&quot;&gt;&lt;p class=&quot;og-title&quot;&gt;Google 클라우드 플랫폼&lt;/p&gt;&lt;p class=&quot;og-desc&quot;&gt;로그인 Google 클라우드 플랫폼으로 이동&lt;/p&gt;&lt;p class=&quot;og-host&quot;&gt;accounts.google.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;br&gt;&amp;nbsp;&lt;/p&gt;&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. API 및 서비스를 선택합니다&lt;/h3&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2032&quot; data-origin-height=&quot;1080&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/OXJeU/dJMcahqYXWU/OBJx1FmWcKBcGbWIljKx9k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/OXJeU/dJMcahqYXWU/OBJx1FmWcKBcGbWIljKx9k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/OXJeU/dJMcahqYXWU/OBJx1FmWcKBcGbWIljKx9k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FOXJeU%2FdJMcahqYXWU%2FOBJx1FmWcKBcGbWIljKx9k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2032&quot; height=&quot;1080&quot; data-origin-width=&quot;2032&quot; data-origin-height=&quot;1080&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. 라이브러리 &amp;gt; Google Search Console API를 선택합니다&lt;/h3&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot;&gt;&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  라이브러리 &amp;gt; Google Search Console API를 선택합니다&lt;/b&gt;&lt;/blockquote&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2032&quot; data-origin-height=&quot;1080&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/CwTEc/dJMcahqYXYa/ynCVvtbI5OdQJtOC387d4K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/CwTEc/dJMcahqYXYa/ynCVvtbI5OdQJtOC387d4K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/CwTEc/dJMcahqYXYa/ynCVvtbI5OdQJtOC387d4K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FCwTEc%2FdJMcahqYXYa%2FynCVvtbI5OdQJtOC387d4K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2032&quot; height=&quot;1080&quot; data-origin-width=&quot;2032&quot; data-origin-height=&quot;1080&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;h3 data-ke-size=&quot;size23&quot;&gt;4. ‘사용’ 버튼을 누릅니다&lt;/h3&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2032&quot; data-origin-height=&quot;1080&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ch3itK/dJMcacpFovR/0k2qQ3g69SJgEolAEteX61/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ch3itK/dJMcacpFovR/0k2qQ3g69SJgEolAEteX61/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ch3itK/dJMcacpFovR/0k2qQ3g69SJgEolAEteX61/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fch3itK%2FdJMcacpFovR%2F0k2qQ3g69SJgEolAEteX61%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2032&quot; height=&quot;1080&quot; data-origin-width=&quot;2032&quot; data-origin-height=&quot;1080&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;h3 data-ke-size=&quot;size23&quot;&gt;5. API 및 서비스 &amp;gt; 사용자 인증 정보 탭으로 이동하여 서비스 계정을 확인합니다&lt;/h3&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot;&gt;&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  API 및 서비스 &amp;gt; 사용자 인증 정보 탭으로 이동하여 서비스 계정을 확인합니다&lt;/b&gt;&lt;br&gt;&lt;br&gt;&lt;u&gt;&lt;b&gt;- 서비스 계정 이메일을 복사해 둡니다.&lt;/b&gt;&lt;/u&gt;&lt;br&gt;- GA로 이미 사용을 하는 경우, 동일한 서비스 계정을 이용해도 됩니다.&lt;/blockquote&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2032&quot; data-origin-height=&quot;1080&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/neveA/dJMcaii5CDU/AkYTrOFXqALuSt3c4Z7iv0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/neveA/dJMcaii5CDU/AkYTrOFXqALuSt3c4Z7iv0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/neveA/dJMcaii5CDU/AkYTrOFXqALuSt3c4Z7iv0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FneveA%2FdJMcaii5CDU%2FAkYTrOFXqALuSt3c4Z7iv0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2032&quot; height=&quot;1080&quot; data-origin-width=&quot;2032&quot; data-origin-height=&quot;1080&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  [참고]&lt;/b&gt; 추가 신규 사용자의 경우는 아래의 GA 설정 글을 따라서 구성하시면 됩니다.&lt;/blockquote&gt;&lt;figure data-ke-type=&quot;opengraph&quot; data-og-title=&quot;[Java] GA4(Google Analytics) 이해하고 활용하기 -2: 수집된 데이터 조회 환경 설정 및 활용 방법&quot; data-ke-align=&quot;alignCenter&quot; data-og-description=&quot;해당 글에서는 GA에서 수집된 데이터를 API를 통해서 가져와서 화면상에 출력하는 과정을 포함한 글입니다.  [참고] React 환경에서 수집한 데이터에 대해서 Spring Boot 환경에서 이를 조회하는 방&quot; data-og-host=&quot;adjh54.tistory.com&quot; data-og-source-url=&quot;https://adjh54.tistory.com/738&quot; data-og-image=&quot;https://blog.kakaocdn.net/dna/ivql5/dJMb9frK6p3/AAAAAAAAAAAAAAAAAAAAAMUc74IQRy1MPcqo2xSxnWK4DxvXLV5MKhafOj3waOED/img.png?credential=yqXZFxpELC7KVnFOS48ylbz2pIh7yKj8&amp;amp;expires=1780239599&amp;amp;allow_ip=&amp;amp;allow_referer=&amp;amp;signature=UgpRtKdLuHuTqf5qbATcR9Ir7Rc%3D&quot; data-og-url=&quot;https://adjh54.tistory.com/738&quot;&gt;&lt;a href=&quot;https://adjh54.tistory.com/738&quot; target=&quot;_blank&quot; data-source-url=&quot;https://adjh54.tistory.com/738&quot;&gt;&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://blog.kakaocdn.net/dna/ivql5/dJMb9frK6p3/AAAAAAAAAAAAAAAAAAAAAMUc74IQRy1MPcqo2xSxnWK4DxvXLV5MKhafOj3waOED/img.png?credential=yqXZFxpELC7KVnFOS48ylbz2pIh7yKj8&amp;amp;expires=1780239599&amp;amp;allow_ip=&amp;amp;allow_referer=&amp;amp;signature=UgpRtKdLuHuTqf5qbATcR9Ir7Rc%3D')&quot;&gt; &lt;/div&gt;&lt;div class=&quot;og-text&quot;&gt;&lt;p class=&quot;og-title&quot;&gt;[Java] GA4(Google Analytics) 이해하고 활용하기 -2: 수집된 데이터 조회 환경 설정 및 활용 방법&lt;/p&gt;&lt;p class=&quot;og-desc&quot;&gt;해당 글에서는 GA에서 수집된 데이터를 API를 통해서 가져와서 화면상에 출력하는 과정을 포함한 글입니다.  [참고] React 환경에서 수집한 데이터에 대해서 Spring Boot 환경에서 이를 조회하는 방&lt;/p&gt;&lt;p class=&quot;og-host&quot;&gt;adjh54.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;h2 data-ke-size=&quot;size26&quot;&gt;2) 환경 구성하기 -2 : Google Search Console 사용자 추가&lt;/h2&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. Google Search Console에 접속합니다&lt;/h3&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2032&quot; data-origin-height=&quot;1080&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/caPYoA/dJMcahYMs5a/oz1ZfmPA85MWX0k5lwcxWK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/caPYoA/dJMcahYMs5a/oz1ZfmPA85MWX0k5lwcxWK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/caPYoA/dJMcahYMs5a/oz1ZfmPA85MWX0k5lwcxWK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcaPYoA%2FdJMcahYMs5a%2Foz1ZfmPA85MWX0k5lwcxWK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2032&quot; height=&quot;1080&quot; data-origin-width=&quot;2032&quot; data-origin-height=&quot;1080&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure data-ke-type=&quot;opengraph&quot; data-og-title=&quot;Google Search Console&quot; data-ke-align=&quot;alignCenter&quot; data-og-description=&quot;Search Console 도구와 보고서를 사용하면 사이트의 검색 트래픽 및 실적을 측정하고, 문제를 해결하며, Google 검색결과에서 사이트가 돋보이게 할 수 있습니다.&quot; data-og-host=&quot;search.google.com&quot; data-og-source-url=&quot;https://search.google.com/search-console&quot; data-og-url=&quot;https://search.google.com/search-console/about&quot;&gt;&lt;a href=&quot;https://search.google.com/search-console/about&quot; target=&quot;_blank&quot; data-source-url=&quot;https://search.google.com/search-console&quot;&gt;&lt;div class=&quot;og-image&quot;&gt;&lt;/div&gt;&lt;div class=&quot;og-text&quot;&gt;&lt;p class=&quot;og-title&quot;&gt;Google Search Console&lt;/p&gt;&lt;p class=&quot;og-desc&quot;&gt;Search Console 도구와 보고서를 사용하면 사이트의 검색 트래픽 및 실적을 측정하고, 문제를 해결하며, Google 검색결과에서 사이트가 돋보이게 할 수 있습니다.&lt;/p&gt;&lt;p class=&quot;og-host&quot;&gt;search.google.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;br&gt;&amp;nbsp;&lt;/p&gt;&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. 설정 &amp;gt; 사용자 추가 버튼을 누릅니다&lt;/h3&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2032&quot; data-origin-height=&quot;1080&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/4OFYf/dJMcag6EdDn/mAnN1tsKJIV2ypjhxKT3P1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/4OFYf/dJMcag6EdDn/mAnN1tsKJIV2ypjhxKT3P1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/4OFYf/dJMcag6EdDn/mAnN1tsKJIV2ypjhxKT3P1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F4OFYf%2FdJMcag6EdDn%2FmAnN1tsKJIV2ypjhxKT3P1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2032&quot; height=&quot;1080&quot; data-origin-width=&quot;2032&quot; data-origin-height=&quot;1080&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. 이전 단계에서 복사해 둔 이메일을 추가하면 설정은 완료가 됩니다.&lt;/h3&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2032&quot; data-origin-height=&quot;1080&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bmYaVf/dJMcabK53TR/9zM7muKuypcmJPI9NEjlZk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bmYaVf/dJMcabK53TR/9zM7muKuypcmJPI9NEjlZk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bmYaVf/dJMcabK53TR/9zM7muKuypcmJPI9NEjlZk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbmYaVf%2FdJMcabK53TR%2F9zM7muKuypcmJPI9NEjlZk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2032&quot; height=&quot;1080&quot; data-origin-width=&quot;2032&quot; data-origin-height=&quot;1080&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;h2 data-ke-size=&quot;size26&quot;&gt;4) Google Search Console API&lt;/h2&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot;&gt;&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  Google Search Console API&lt;/b&gt;&lt;br&gt;&lt;br&gt;&lt;b&gt;- Google Search Console로부터 수집된 정보를 API로 전달을 받아서 프로그래밍 방식으로 접근할 수 있는 HTTP REST 서비스를 의미합니다.&lt;/b&gt;&lt;b&gt;&lt;br&gt;&lt;/b&gt;&lt;b&gt;- 속성(사이트) 관리, 사이트맵 제출, 검색 실적 데이터 조회, URL 검사를 API로 처리할 수 있습니다.&lt;/b&gt;&lt;/blockquote&gt;&lt;figure data-ke-type=&quot;opengraph&quot; data-og-title=&quot;개요 &amp;nbsp;|&amp;nbsp; Search Console API &amp;nbsp;|&amp;nbsp; Google for Developers&quot; data-ke-align=&quot;alignCenter&quot; data-og-description=&quot;속성 및 사이트맵을 확인, 추가 또는 삭제하고, Google 검색결과 데이터에서 쿼리를 실행하고, 페이지를 테스트하는 데 사용하는 API의 개요를 확인하세요.&quot; data-og-host=&quot;developers.google.com&quot; data-og-source-url=&quot;https://developers.google.com/webmaster-tools/about?hl=ko&quot; data-og-image=&quot;https://blog.kakaocdn.net/dna/bqx7x4/dJMb89yjjX8/AAAAAAAAAAAAAAAAAAAAAOMox8CpsCSkpx3Zj0NkKQJSz-yEyh2FW3gXLTQqXJMA/img.png?credential=yqXZFxpELC7KVnFOS48ylbz2pIh7yKj8&amp;amp;expires=1780239599&amp;amp;allow_ip=&amp;amp;allow_referer=&amp;amp;signature=mJbsPNJ1Au7tWwyUYKkUBX8SUIU%3D&quot; data-og-url=&quot;https://developers.google.com/webmaster-tools/about?hl=ko&quot;&gt;&lt;a href=&quot;https://developers.google.com/webmaster-tools/about?hl=ko&quot; target=&quot;_blank&quot; data-source-url=&quot;https://developers.google.com/webmaster-tools/about?hl=ko&quot;&gt;&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://blog.kakaocdn.net/dna/bqx7x4/dJMb89yjjX8/AAAAAAAAAAAAAAAAAAAAAOMox8CpsCSkpx3Zj0NkKQJSz-yEyh2FW3gXLTQqXJMA/img.png?credential=yqXZFxpELC7KVnFOS48ylbz2pIh7yKj8&amp;amp;expires=1780239599&amp;amp;allow_ip=&amp;amp;allow_referer=&amp;amp;signature=mJbsPNJ1Au7tWwyUYKkUBX8SUIU%3D')&quot;&gt; &lt;/div&gt;&lt;div class=&quot;og-text&quot;&gt;&lt;p class=&quot;og-title&quot;&gt;개요 &amp;nbsp;|&amp;nbsp; Search Console API &amp;nbsp;|&amp;nbsp; Google for Developers&lt;/p&gt;&lt;p class=&quot;og-desc&quot;&gt;속성 및 사이트맵을 확인, 추가 또는 삭제하고, Google 검색결과 데이터에서 쿼리를 실행하고, 페이지를 테스트하는 데 사용하는 API의 개요를 확인하세요.&lt;/p&gt;&lt;p class=&quot;og-host&quot;&gt;developers.google.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;h3 data-ke-size=&quot;size23&quot;&gt;1. 라이브러리 설치&lt;/h3&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;pre class=&quot;1c&quot;&gt;&lt;code&gt;implementation 'com.google.apis:google-api-services-searchconsole:v1-rev20250122-2.0.0' // Google Search Console API
&lt;/code&gt;&lt;/pre&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2032&quot; data-origin-height=&quot;1080&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/tW7HC/dJMcaaejKIm/dZyrSOwMihKzZyNzKluyy0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/tW7HC/dJMcaaejKIm/dZyrSOwMihKzZyNzKluyy0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/tW7HC/dJMcaaejKIm/dZyrSOwMihKzZyNzKluyy0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FtW7HC%2FdJMcaaejKIm%2FdZyrSOwMihKzZyNzKluyy0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2032&quot; height=&quot;1080&quot; data-origin-width=&quot;2032&quot; data-origin-height=&quot;1080&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure data-ke-type=&quot;opengraph&quot; data-og-title=&quot;Maven Repository: com.google.apis » google-api-services-searchconsole&quot; data-ke-align=&quot;alignCenter&quot; data-og-host=&quot;mvnrepository.com&quot; data-og-source-url=&quot;https://mvnrepository.com/artifact/com.google.apis/google-api-services-searchconsole&quot; data-og-url=&quot;https://mvnrepository.com/artifact/com.google.apis/google-api-services-searchconsole&quot;&gt;&lt;a href=&quot;https://mvnrepository.com/artifact/com.google.apis/google-api-services-searchconsole&quot; target=&quot;_blank&quot; data-source-url=&quot;https://mvnrepository.com/artifact/com.google.apis/google-api-services-searchconsole&quot;&gt;&lt;div class=&quot;og-image&quot;&gt;&lt;/div&gt;&lt;div class=&quot;og-text&quot;&gt;&lt;p class=&quot;og-title&quot;&gt;Maven Repository: com.google.apis » google-api-services-searchconsole&lt;/p&gt;&lt;p class=&quot;og-host&quot;&gt;mvnrepository.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;br&gt;&amp;nbsp;&lt;/p&gt;&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. SearchConsoleConfig&lt;/h3&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot;&gt;&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  SearchConsoleConfig&lt;/b&gt;&lt;br&gt;&lt;br&gt;&lt;b&gt;- Spring Boot 애플리케이션에서 Google Search Console API를 사용할 수 있도록 연동 클라이언트(SearchConsole)를 생성하고 관리하는 설정(Configuration) 클래스입니다.&lt;/b&gt;&lt;br&gt;- 내부적으로 ga-key.json 파일을 불러와서 구글 인증 및 권한 설정을 수행합니다.&lt;br&gt;- SearchConsole.Builder를 통해서 최종 클라이언트의 빌드를 수행합니다.&lt;/blockquote&gt;&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;package com.xxx.xxx.config;

import com.google.api.client.googleapis.javanet.GoogleNetHttpTransport;
import com.google.api.client.http.HttpRequestInitializer;
import com.google.api.client.json.gson.GsonFactory;
import com.google.api.services.searchconsole.v1.SearchConsole;
import com.google.api.services.searchconsole.v1.SearchConsoleScopes;
import com.google.auth.http.HttpCredentialsAdapter;
import com.google.auth.oauth2.GoogleCredentials;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;

@Configuration
public class SearchConsoleConfig {

    @Bean
    public SearchConsole searchConsoleClient() throws Exception {
        GoogleCredentials credentials = GoogleCredentials
                .fromStream(new ClassPathResource(&quot;ga-key.json&quot;).getInputStream())
                .createScoped(SearchConsoleScopes.WEBMASTERS_READONLY);

        HttpRequestInitializer requestInitializer = new HttpCredentialsAdapter(credentials);

        return new SearchConsole.Builder(
                GoogleNetHttpTransport.newTrustedTransport(),
                GsonFactory.getDefaultInstance(),
                requestInitializer
        ).setApplicationName(&quot;xxx-xxx&quot;).build();
    }
}&lt;/code&gt;&lt;/pre&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. SearchConsoleService&lt;/h3&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot;&gt;&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  SearchConsoleService&lt;/b&gt;&lt;br&gt;&lt;br&gt;&lt;b&gt;- 사전에 구성된 SearchConsoleConfig에서 생성된 클라이언트를 기반으로 이를 활용하여서 google search console로부터 조회를 해옵니다.&lt;/b&gt;&lt;/blockquote&gt;&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 171px;&quot; border=&quot;1&quot; data-ke-style=&quot;style2&quot; data-ke-align=&quot;alignLeft&quot;&gt;&lt;tbody&gt;&lt;tr style=&quot;height: 10px;&quot;&gt;&lt;td style=&quot;width: 16.3953%; height: 10px;&quot;&gt;&lt;b&gt;필드&lt;/b&gt;&lt;/td&gt;&lt;td style=&quot;width: 23.0233%; height: 10px;&quot;&gt;&lt;b&gt;dimensions 인덱스&lt;/b&gt;&lt;/td&gt;&lt;td style=&quot;width: 24.7674%; height: 10px;&quot;&gt;&lt;b&gt;값 예시&lt;/b&gt;&lt;/td&gt;&lt;td style=&quot;width: 35.5814%; height: 10px;&quot;&gt;&lt;b&gt;설명&lt;/b&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr style=&quot;height: 18px;&quot;&gt;&lt;td style=&quot;width: 16.3953%; height: 18px;&quot;&gt;&lt;b&gt;date&lt;/b&gt;&lt;/td&gt;&lt;td style=&quot;width: 23.0233%; height: 18px;&quot;&gt;keys.get(0)&lt;/td&gt;&lt;td style=&quot;width: 24.7674%; height: 18px;&quot;&gt;2026-05-20&lt;/td&gt;&lt;td style=&quot;width: 35.5814%; height: 18px;&quot;&gt;검색 발생 날짜&lt;/td&gt;&lt;/tr&gt;&lt;tr style=&quot;height: 18px;&quot;&gt;&lt;td style=&quot;width: 16.3953%; height: 18px;&quot;&gt;&lt;b&gt;query&lt;/b&gt;&lt;/td&gt;&lt;td style=&quot;width: 23.0233%; height: 18px;&quot;&gt;keys.get(1)&lt;/td&gt;&lt;td style=&quot;width: 24.7674%; height: 18px;&quot;&gt;검색&lt;/td&gt;&lt;td style=&quot;width: 35.5814%; height: 18px;&quot;&gt;사용자가 검색한 키워드&lt;/td&gt;&lt;/tr&gt;&lt;tr style=&quot;height: 18px;&quot;&gt;&lt;td style=&quot;width: 16.3953%; height: 18px;&quot;&gt;&lt;b&gt;page&lt;/b&gt;&lt;/td&gt;&lt;td style=&quot;width: 23.0233%; height: 18px;&quot;&gt;keys.get(2)&lt;/td&gt;&lt;td style=&quot;width: 24.7674%; height: 18px;&quot;&gt;&lt;a href=&quot;https://xxx.com/about&quot; target=&quot;_self&quot;&gt;&lt;span&gt;https://xxx.com/about&lt;/span&gt;&lt;/a&gt;&lt;/td&gt;&lt;td style=&quot;width: 35.5814%; height: 18px;&quot;&gt;노출된 페이지 URL&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style=&quot;width: 16.3953%;&quot;&gt;&lt;b&gt;country&lt;/b&gt;&lt;/td&gt;&lt;td style=&quot;width: 23.0233%;&quot;&gt;keys.get(3)&lt;/td&gt;&lt;td style=&quot;width: 24.7674%;&quot;&gt;kor&lt;/td&gt;&lt;td style=&quot;width: 35.5814%;&quot;&gt;국가 코드&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style=&quot;width: 16.3953%;&quot;&gt;&lt;b&gt;device&lt;/b&gt;&lt;/td&gt;&lt;td style=&quot;width: 23.0233%;&quot;&gt;keys.get(4)&lt;/td&gt;&lt;td style=&quot;width: 24.7674%;&quot;&gt;DESKTOP&lt;/td&gt;&lt;td style=&quot;width: 35.5814%;&quot;&gt;디바이스 종류&lt;/td&gt;&lt;/tr&gt;&lt;tr style=&quot;height: 17px;&quot;&gt;&lt;td style=&quot;width: 16.3953%; height: 17px;&quot;&gt;&amp;nbsp;&lt;/td&gt;&lt;td style=&quot;width: 23.0233%; height: 17px;&quot;&gt;&amp;nbsp;&lt;/td&gt;&lt;td style=&quot;width: 24.7674%; height: 17px;&quot;&gt;&amp;nbsp;&lt;/td&gt;&lt;td style=&quot;width: 35.5814%; height: 17px;&quot;&gt;&amp;nbsp;&lt;/td&gt;&lt;/tr&gt;&lt;tr style=&quot;height: 18px;&quot;&gt;&lt;td style=&quot;width: 16.3953%; height: 18px;&quot;&gt;&lt;b&gt;siteUrl&lt;/b&gt;&lt;/td&gt;&lt;td style=&quot;width: 23.0233%; height: 18px;&quot;&gt;(파라미터로 전달)&lt;/td&gt;&lt;td style=&quot;width: 24.7674%; height: 18px;&quot;&gt;&lt;a href=&quot;https://xxx.com&quot; target=&quot;_self&quot;&gt;&lt;span&gt;https://xxx.com&lt;/span&gt;&lt;/a&gt;&lt;/td&gt;&lt;td style=&quot;width: 35.5814%; height: 18px;&quot;&gt;조회 대상 사이트 URL&lt;/td&gt;&lt;/tr&gt;&lt;tr style=&quot;height: 18px;&quot;&gt;&lt;td style=&quot;width: 16.3953%; height: 18px;&quot;&gt;&lt;b&gt;clicks&lt;/b&gt;&lt;/td&gt;&lt;td style=&quot;width: 23.0233%; height: 18px;&quot;&gt;getClicks()&lt;/td&gt;&lt;td style=&quot;width: 24.7674%; height: 18px;&quot;&gt;42&lt;/td&gt;&lt;td style=&quot;width: 35.5814%; height: 18px;&quot;&gt;검색 결과 클릭 수&lt;/td&gt;&lt;/tr&gt;&lt;tr style=&quot;height: 18px;&quot;&gt;&lt;td style=&quot;width: 16.3953%; height: 18px;&quot;&gt;&lt;b&gt;impressions&lt;/b&gt;&lt;/td&gt;&lt;td style=&quot;width: 23.0233%; height: 18px;&quot;&gt;getImpressions()&lt;/td&gt;&lt;td style=&quot;width: 24.7674%; height: 18px;&quot;&gt;1500&lt;/td&gt;&lt;td style=&quot;width: 35.5814%; height: 18px;&quot;&gt;검색 결과 노출 수&lt;/td&gt;&lt;/tr&gt;&lt;tr style=&quot;height: 18px;&quot;&gt;&lt;td style=&quot;width: 16.3953%; height: 18px;&quot;&gt;&lt;b&gt;ctr&lt;/b&gt;&lt;/td&gt;&lt;td style=&quot;width: 23.0233%; height: 18px;&quot;&gt;getCtr()&lt;/td&gt;&lt;td style=&quot;width: 24.7674%; height: 18px;&quot;&gt;0.028&lt;/td&gt;&lt;td style=&quot;width: 35.5814%; height: 18px;&quot;&gt;클릭률 (clicks ÷ impressions)&lt;/td&gt;&lt;/tr&gt;&lt;tr style=&quot;height: 18px;&quot;&gt;&lt;td style=&quot;width: 16.3953%; height: 18px;&quot;&gt;&lt;b&gt;position&lt;/b&gt;&lt;/td&gt;&lt;td style=&quot;width: 23.0233%; height: 18px;&quot;&gt;getPosition()&lt;/td&gt;&lt;td style=&quot;width: 24.7674%; height: 18px;&quot;&gt;8.3&lt;/td&gt;&lt;td style=&quot;width: 35.5814%; height: 18px;&quot;&gt;평균 검색 노출 순위 (낮을수록 상위)&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;
import com.daekyocns.homepage.model.dto.SearchConsoleResponseDto;
import com.google.api.services.searchconsole.v1.SearchConsole;
import com.google.api.services.searchconsole.v1.model.ApiDataRow;
import com.google.api.services.searchconsole.v1.model.SearchAnalyticsQueryRequest;
import com.google.api.services.searchconsole.v1.model.SearchAnalyticsQueryResponse;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;

@Service
public class SearchConsoleService {

    // Google Search Console API 클라이언트 (Bean으로 주입)
    private final SearchConsole client;

    // application.yml의 search-console.site-urls 값 (여러 사이트 URL 배열)
    @Value(&quot;${search-console.site-urls}&quot;)
    private String[] siteUrls;

    public SearchConsoleService(SearchConsole client) {
        this.client = client;
    }

    /**
     * 등록된 모든 사이트의 Search Analytics 데이터를 조회한다.
     *
     * @param startDate 조회 시작일 (yyyy-MM-dd)
     * @param endDate   조회 종료일 (yyyy-MM-dd)
     * @return 전체 사이트의 검색 분석 데이터 목록
     */
    public List&amp;lt;SearchConsoleResponseDto&amp;gt; getSearchAnalytics(String startDate, String endDate) throws Exception {
        return Arrays.stream(siteUrls)
                .flatMap(siteUrl -&amp;gt; {
                    try {
                        // 사이트별로 데이터를 조회하여 하나의 스트림으로 병합
                        return fetchBySite(siteUrl, startDate, endDate).stream();
                    } catch (Exception e) {
                        // 특정 사이트 조회 실패 시 해당 사이트는 건너뛰고 계속 진행
                        return Stream.of();
                    }
                })
                .toList();
    }

    /**
     * 특정 사이트의 Search Analytics 데이터를 Google API로 조회한다.
     *
     * @param siteUrl   조회할 사이트 URL
     * @param startDate 조회 시작일
     * @param endDate   조회 종료일
     * @return 해당 사이트의 검색 분석 데이터 목록
     */
    private List&amp;lt;SearchConsoleResponseDto&amp;gt; fetchBySite(String siteUrl, String startDate, String endDate) throws Exception {
        // 쿼리 요청 구성: 날짜 범위, 집계 기준(date/query/page/country/device), 최대 행 수
        SearchAnalyticsQueryRequest request = new SearchAnalyticsQueryRequest()
                .setStartDate(startDate)
                .setEndDate(endDate)
                .setDimensions(List.of(&quot;date&quot;, &quot;query&quot;, &quot;page&quot;, &quot;country&quot;, &quot;device&quot;))
                .setRowLimit(1000); // 최대 1000행 반환

        // Google Search Console API 호출
        SearchAnalyticsQueryResponse response = client.searchanalytics()
                .query(siteUrl, request)
                .execute();

        // 데이터가 없는 경우 빈 리스트 반환
        if (response.getRows() == null) return List.of();

        // 응답 행(row)을 DTO로 변환하여 반환
        return response.getRows().stream()
                .map(row -&amp;gt; toDto(row, siteUrl))
                .toList();
    }

    /**
     * API 응답 행(ApiDataRow)을 SearchConsoleResponseDto로 변환한다.
     * dimensions 순서: [0] date, [1] query, [2] page, [3] country, [4] device
     *
     * @param row     Google API 응답의 단일 행
     * @param siteUrl 해당 행이 속한 사이트 URL
     * @return 변환된 DTO
     */
    private SearchConsoleResponseDto toDto(ApiDataRow row, String siteUrl) {
        return SearchConsoleResponseDto.builder()
                .siteUrl(siteUrl)
                .date(row.getKeys().get(0))            // 날짜
                .query(row.getKeys().get(1))           // 검색 키워드
                .page(row.getKeys().get(2))            // 노출 페이지 URL
                .country(row.getKeys().get(3))         // 국가 코드 (예: KOR)
                .device(row.getKeys().get(4))          // 디바이스 (DESKTOP / MOBILE / TABLET)
                .clicks(row.getClicks().longValue())   // 클릭 수
                .impressions(row.getImpressions().longValue()) // 노출 수
                .ctr(row.getCtr())                     // 클릭률
                .position(row.getPosition())           // 평균 검색 순위
                .build();
    }
}&lt;/code&gt;&lt;/pre&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;h3 data-ke-size=&quot;size23&quot;&gt;4. SearchConsoleResponseDto&lt;/h3&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot;&gt;&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;package com.xxx.xxx.model.dto;

import lombok.Builder;
import lombok.Getter;

/**
 * Google Search Console API 검색 분석 응답 DTO
 * dimensions: date / query / page / country / device
 * metrics: clicks / impressions / ctr / position
 *
 * @author : leejonghoon
 * @fileName : SearchConsoleResponseDto
 * @since : 26. 5. 18.
 */
@Getter
@Builder
public class SearchConsoleResponseDto {
    private String siteUrl;
    private String date;        // 날짜 (yyyy-MM-dd)
    private String query;       // 검색 키워드
    private String page;        // 노출 페이지 URL
    private String country;     // 국가 코드 (예: KOR, USA)
    private String device;      // 디바이스 (DESKTOP / MOBILE / TABLET)
    private long clicks;        // 클릭 수
    private long impressions;   // 노출 수
    private double ctr;         // 클릭률 (0.0 ~ 1.0)
    private double position;    // 평균 검색 순위
}

&lt;/code&gt;&lt;/pre&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;h3 data-ke-size=&quot;size23&quot;&gt;5. SearchConsoleController&lt;/h3&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot;&gt;&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;package com.xxx.xxx.controller;

import com.daekyocns.homepage.model.common.ApiResponseWrapper;
import com.daekyocns.homepage.model.common.SuccessCode;
import com.daekyocns.homepage.model.dto.GaRequestDto;
import com.daekyocns.homepage.model.dto.SearchConsoleResponseDto;
import com.daekyocns.homepage.service.SearchConsoleService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

@Slf4j
@RestController
@RequestMapping(value = &quot;/api/searchconsole&quot;)
public class SearchConsoleController {

    private final SearchConsoleService searchConsoleService;

    public SearchConsoleController(SearchConsoleService searchConsoleService) {
        this.searchConsoleService = searchConsoleService;
    }

    @PostMapping(&quot;/analytics&quot;)
    public ResponseEntity&amp;lt;ApiResponseWrapper&amp;lt;List&amp;lt;SearchConsoleResponseDto&amp;gt;&amp;gt;&amp;gt; getAnalytics(@RequestBody GaRequestDto request) {
        log.debug(&quot;[+] getSearchConsoleAnalytics CALL&quot;);

        try {
            List&amp;lt;SearchConsoleResponseDto&amp;gt; list = searchConsoleService.getSearchAnalytics(
                    request.getStartDate(),
                    request.getEndDate()
            );
            log.debug(&quot;list: &quot; + list);

            ApiResponseWrapper&amp;lt;List&amp;lt;SearchConsoleResponseDto&amp;gt;&amp;gt; arw = ApiResponseWrapper.&amp;lt;List&amp;lt;SearchConsoleResponseDto&amp;gt;&amp;gt;builder()
                    .result(list)
                    .resultCode(SuccessCode.SELECT_SUCCESS.getStatus())
                    .resultMsg(SuccessCode.SELECT_SUCCESS.getMessage())
                    .build();

            return new ResponseEntity&amp;lt;&amp;gt;(arw, HttpStatus.OK);

        } catch (Exception e) {
            log.error(&quot;Search Console API 호출 실패: &quot;, e);
            return new ResponseEntity&amp;lt;&amp;gt;(HttpStatus.INTERNAL_SERVER_ERROR);
        }
    }
}&lt;/code&gt;&lt;/pre&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;h3 data-ke-size=&quot;size23&quot;&gt;6. 결과 확인&lt;/h3&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot;&gt;&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  결과 확인&lt;/b&gt;&lt;br&gt;&lt;br&gt;- HTTP Client를 이용하여 아래와 같은 결과값을 얻었습니다.&lt;/blockquote&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2032&quot; data-origin-height=&quot;1080&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/JUKPm/dJMcadPy6XV/ZCZFBHws7Owf0en0etNFkK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/JUKPm/dJMcadPy6XV/ZCZFBHws7Owf0en0etNFkK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/JUKPm/dJMcadPy6XV/ZCZFBHws7Owf0en0etNFkK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FJUKPm%2FdJMcadPy6XV%2FZCZFBHws7Owf0en0etNFkK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2032&quot; height=&quot;1080&quot; data-origin-width=&quot;2032&quot; data-origin-height=&quot;1080&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;h4 data-ke-size=&quot;size20&quot;&gt;6.1. 결과화면 : 키워드 분석&lt;/h4&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1037&quot; data-origin-height=&quot;840&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/FK9IX/dJMcahdqNjf/kb34azSJkNyaFqGJiuu6T1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/FK9IX/dJMcahdqNjf/kb34azSJkNyaFqGJiuu6T1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/FK9IX/dJMcahdqNjf/kb34azSJkNyaFqGJiuu6T1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FFK9IX%2FdJMcahdqNjf%2Fkb34azSJkNyaFqGJiuu6T1%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;1037&quot; height=&quot;840&quot; data-origin-width=&quot;1037&quot; data-origin-height=&quot;840&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;h4 data-ke-size=&quot;size20&quot;&gt;6.2. 결과화면 : 기기별 성과&lt;/h4&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;950&quot; data-origin-height=&quot;692&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/8rAzc/dJMcacb6VFE/iLTmn2o4m41P7BM1vmaZr1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/8rAzc/dJMcacb6VFE/iLTmn2o4m41P7BM1vmaZr1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/8rAzc/dJMcacb6VFE/iLTmn2o4m41P7BM1vmaZr1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F8rAzc%2FdJMcacb6VFE%2FiLTmn2o4m41P7BM1vmaZr1%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;950&quot; height=&quot;692&quot; data-origin-width=&quot;950&quot; data-origin-height=&quot;692&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;h4 data-ke-size=&quot;size20&quot;&gt;6.3. 결과화면 : 국가별 트래픽&lt;/h4&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;965&quot; data-origin-height=&quot;678&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bzHl8r/dJMcagZQDt3/VilVSOSNAWpYkPlFIqoFhk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bzHl8r/dJMcagZQDt3/VilVSOSNAWpYkPlFIqoFhk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bzHl8r/dJMcagZQDt3/VilVSOSNAWpYkPlFIqoFhk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbzHl8r%2FdJMcagZQDt3%2FVilVSOSNAWpYkPlFIqoFhk%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;965&quot; height=&quot;678&quot; data-origin-width=&quot;965&quot; data-origin-height=&quot;678&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&lt;b&gt;오늘도&amp;nbsp;감사합니다.&amp;nbsp; &lt;/b&gt;&lt;br&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Java/라이브러리 활용</category>
      <category>google search console</category>
      <category>Google Search Console API</category>
      <category>Google Search Console Java 연동</category>
      <category>Google Search Console Spring Boot</category>
      <category>구글 검색</category>
      <category>구글 검색량</category>
      <category>구글 정보</category>
      <category>웹 사이트 구글 검색량</category>
      <author>adjh54</author>
      <guid isPermaLink="true">https://adjh54.tistory.com/761</guid>
      <comments>https://adjh54.tistory.com/761#entry761comment</comments>
      <pubDate>Wed, 20 May 2026 19:20:00 +0900</pubDate>
    </item>
    <item>
      <title>[CI/CD] Github Self-hosted Runner 방식 구성 및 배포 방법 -1</title>
      <link>https://adjh54.tistory.com/760</link>
      <description>&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
 &lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;해당 글에서는 Self-hosted Runner기반으로 Docker Compose로 구성된 파일을 배포하는 사용 사례입니다.&lt;br&gt;&lt;br&gt;&lt;/span&gt;&lt;/span&gt; 
 &lt;figure contenteditable=&quot;false&quot; data-ke-type=&quot;emoticon&quot; data-ke-align=&quot;alignCenter&quot; data-emoticon-type=&quot;face&quot; data-emoticon-name=&quot;073&quot; data-emoticon-isanimation=&quot;false&quot; data-emoticon-src=&quot;https://t1.daumcdn.net/axz_keditor/emoticon/face/large/073.png&quot;&gt;
  &lt;img src=&quot;https://t1.daumcdn.net/axz_keditor/emoticon/face/large/073.png&quot; width=&quot;80&quot;&gt;
 &lt;/figure&gt; &lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;/span&gt;
&lt;/blockquote&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;h2 data-ke-size=&quot;size26&quot;&gt;1) GitHub Actions Runner&lt;/h2&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot;&gt;&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  GitHub Actions Runner&lt;/b&gt;&lt;b&gt;&lt;br&gt;&lt;/b&gt;&lt;b&gt;&lt;br&gt;&lt;/b&gt;&lt;b&gt;- GitHub Actions 워크플로우를 실제로 실행하는 에이전트(프로세스)입니다. &lt;/b&gt;&lt;b&gt;&lt;br&gt;&lt;/b&gt;&lt;b&gt;- GitHub 리포지토리에 정의된 .github/workflows/*.yml 파일의 작업(Job)을 받아서, 해당 명령어들을 순차적으로 수행하는 역할을 합니다.&lt;/b&gt;&lt;/blockquote&gt;&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 238px;&quot; border=&quot;1&quot; data-ke-style=&quot;style2&quot; data-ke-align=&quot;alignLeft&quot;&gt;&lt;tbody&gt;&lt;tr style=&quot;height: 20px;&quot;&gt;&lt;td style=&quot;height: 20px;&quot;&gt;&lt;b&gt;항목&lt;/b&gt;&lt;/td&gt;&lt;td style=&quot;height: 20px;&quot;&gt;&lt;b&gt;Github-hosted Runner&lt;/b&gt;&lt;/td&gt;&lt;td style=&quot;height: 20px;&quot;&gt;&lt;b&gt;Self-hosted Runner&lt;/b&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr style=&quot;height: 20px;&quot;&gt;&lt;td style=&quot;height: 20px;&quot;&gt;&lt;b&gt;실행 주체&lt;/b&gt;&lt;/td&gt;&lt;td style=&quot;height: 20px;&quot;&gt;GitHub 클라우드 서버&lt;/td&gt;&lt;td style=&quot;height: 20px;&quot;&gt;내 서버&lt;/td&gt;&lt;/tr&gt;&lt;tr style=&quot;height: 20px;&quot;&gt;&lt;td style=&quot;height: 20px;&quot;&gt;&lt;b&gt;설정 난이도&lt;/b&gt;&lt;/td&gt;&lt;td style=&quot;height: 20px;&quot;&gt;쉬움 (바로 사용)&lt;/td&gt;&lt;td style=&quot;height: 20px;&quot;&gt;높음 (Runner 설치 필요)&lt;/td&gt;&lt;/tr&gt;&lt;tr style=&quot;height: 20px;&quot;&gt;&lt;td style=&quot;height: 20px;&quot;&gt;&lt;b&gt;비용&lt;/b&gt;&lt;/td&gt;&lt;td style=&quot;height: 20px;&quot;&gt;무료 한도 후 과금&lt;/td&gt;&lt;td style=&quot;height: 20px;&quot;&gt;서버 비용만&lt;/td&gt;&lt;/tr&gt;&lt;tr style=&quot;height: 20px;&quot;&gt;&lt;td style=&quot;height: 20px;&quot;&gt;&lt;b&gt;내부망 접근&lt;/b&gt;&lt;/td&gt;&lt;td style=&quot;height: 20px;&quot;&gt;불가&lt;/td&gt;&lt;td style=&quot;height: 20px;&quot;&gt;가능&lt;/td&gt;&lt;/tr&gt;&lt;tr style=&quot;height: 20px;&quot;&gt;&lt;td style=&quot;height: 20px;&quot;&gt;&lt;b&gt;실행 환경&lt;/b&gt;&lt;/td&gt;&lt;td style=&quot;height: 20px;&quot;&gt;매 Job마다 초기화&lt;/td&gt;&lt;td style=&quot;height: 20px;&quot;&gt;환경 유지&lt;/td&gt;&lt;/tr&gt;&lt;tr style=&quot;height: 20px;&quot;&gt;&lt;td style=&quot;height: 20px;&quot;&gt;&lt;b&gt;캐시 유지&lt;/b&gt;&lt;/td&gt;&lt;td style=&quot;height: 20px;&quot;&gt;불가 (actions/cache&amp;nbsp;필요)&lt;/td&gt;&lt;td style=&quot;height: 20px;&quot;&gt;가능&lt;/td&gt;&lt;/tr&gt;&lt;tr style=&quot;height: 20px;&quot;&gt;&lt;td style=&quot;height: 20px;&quot;&gt;&lt;b&gt;보안 관리&lt;/b&gt;&lt;/td&gt;&lt;td style=&quot;height: 20px;&quot;&gt;GitHub이 관리&lt;/td&gt;&lt;td style=&quot;height: 20px;&quot;&gt;직접 관리&lt;/td&gt;&lt;/tr&gt;&lt;tr style=&quot;height: 20px;&quot;&gt;&lt;td style=&quot;height: 20px;&quot;&gt;&lt;b&gt;OS 선택&lt;/b&gt;&lt;/td&gt;&lt;td style=&quot;height: 20px;&quot;&gt;ubuntu / windows / macos&lt;/td&gt;&lt;td style=&quot;height: 20px;&quot;&gt;자유롭게 설정&lt;/td&gt;&lt;/tr&gt;&lt;tr style=&quot;height: 20px;&quot;&gt;&lt;td style=&quot;height: 20px;&quot;&gt;&lt;b&gt;실행 시간 제한&lt;/b&gt;&lt;/td&gt;&lt;td style=&quot;height: 20px;&quot;&gt;6시간/Job&lt;/td&gt;&lt;td style=&quot;height: 20px;&quot;&gt;제한 없음&lt;/td&gt;&lt;/tr&gt;&lt;tr style=&quot;height: 20px;&quot;&gt;&lt;td style=&quot;height: 20px;&quot;&gt;&lt;b&gt;적합한 상황&lt;/b&gt;&lt;/td&gt;&lt;td style=&quot;height: 20px;&quot;&gt;오픈소스, 외부 배포&lt;/td&gt;&lt;td style=&quot;height: 20px;&quot;&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;h3 data-ke-size=&quot;size23&quot;&gt;1. GitHub-hosted Runner&lt;/h3&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot;&gt;&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  GitHub-hosted Runner&lt;/b&gt;&lt;br&gt;&lt;b&gt;&lt;br&gt;&lt;/b&gt;&lt;b&gt;- GitHub가 제공하는 클라우드 서버에서 Actions Job을 실행하는 방식을 의미하며, 별도 서버 설정 없이 바로 사용 가능합니다.&lt;/b&gt;&lt;br&gt;- Self-Hosted와 가장 큰 차이점은 실행 주체가 ‘Github 서버’가 됩니다&lt;/blockquote&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;580&quot; data-origin-height=&quot;508&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/TiKa0/dJMcaffD9WA/f8LylBXPNgROzKh08O8JkK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/TiKa0/dJMcaffD9WA/f8LylBXPNgROzKh08O8JkK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/TiKa0/dJMcaffD9WA/f8LylBXPNgROzKh08O8JkK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FTiKa0%2FdJMcaffD9WA%2Ff8LylBXPNgROzKh08O8JkK%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;580&quot; height=&quot;508&quot; data-origin-width=&quot;580&quot; data-origin-height=&quot;508&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;[처리 과정]&lt;/b&gt;&lt;br&gt;&lt;br&gt;&lt;b&gt;1. 이벤트 발생&lt;/b&gt;&lt;br&gt;&lt;br&gt;- Actions를 시작시키는 출발점으로 개발자가 코드를 push하거나 PR을 열거나, 설정한 스케줄 시간이 되면 GitHub이 이벤트를 감지합니다.&lt;br&gt;&lt;br&gt;&lt;b&gt;2. GitHub Actions 트리거&lt;/b&gt;&lt;br&gt;&lt;br&gt;- .github/workflows/*.yml 파일을 읽어서 어떤 Job을 실행할지 파악하며, on: push 같은 트리거 조건이 일치하면 워크플로우가 시작됩니다&lt;br&gt;&lt;br&gt;&lt;b&gt;3. GitHub 클라우드 서버 할당&lt;/b&gt;&lt;b&gt;&lt;br&gt;&lt;/b&gt;&lt;br&gt;- runs-on: ubuntu-latest 설정을 보고 GitHub이 클라우드 서버를 새로 생성해서 할당합니다. &lt;br&gt;- 이 서버는 Job 실행을 위해 완전히 새로운 환경으로 시작이 되며, Node, Java, Docker 등 기본 툴은 이미 설치되어 있습니다.&lt;br&gt;&lt;br&gt;&lt;b&gt;4. Job 실행 (빌드 → 테스트 → 배포)&lt;/b&gt;&lt;b&gt;&lt;br&gt;&lt;/b&gt;&lt;br&gt;- 워크플로우에 정의된 steps를 순서대로 실행하며, 각 step은 독립적으로 실행되고 이전 step이 실패하면 기본적으로 이후 step은 중단이 됩니다.&lt;br&gt;&lt;br&gt;&lt;b&gt;5. Runner 종료&lt;/b&gt;&lt;b&gt;&lt;br&gt;&lt;/b&gt;&lt;br&gt;- Job이 끝나면 할당됐던 클라우드 서버가 완전히 삭제가 됩니다. 다음 Job 실행 시 항상 새 서버에서 시작 — 이전 실행의 파일, 캐시, 환경변수 등이 전혀 남지 않습니다.&lt;/blockquote&gt;&lt;figure data-ke-type=&quot;opengraph&quot; data-og-title=&quot;GitHub 호스팅 실행기 - GitHub 문서&quot; data-ke-align=&quot;alignCenter&quot; data-og-description=&quot;GitHub 는 워크플로를 실행하기 위해 호스트된 가상 머신을 제공합니다. 가상 머신에는 사용할 수 있는 도구, 패키지 및 설정 환경이 포함되어 있습니다 GitHub Actions .&quot; data-og-host=&quot;docs.github.com&quot; data-og-source-url=&quot;https://docs.github.com/ko/actions/concepts/runners/github-hosted-runners&quot; data-og-image=&quot;https://blog.kakaocdn.net/dna/bfnrFL/dJMb8Rj7Ihd/AAAAAAAAAAAAAAAAAAAAAFbOeaJacQ17PkrD6RJVGZLL1b3jDw3pWwDyFyufIrjT/img.png?credential=yqXZFxpELC7KVnFOS48ylbz2pIh7yKj8&amp;amp;expires=1780239599&amp;amp;allow_ip=&amp;amp;allow_referer=&amp;amp;signature=l5v5WSkHksD39t4FUr3FfiuPcVw%3D&quot; data-og-url=&quot;https://docs-internal.github.com/ko/actions/concepts/runners/github-hosted-runners&quot;&gt;&lt;a href=&quot;https://docs-internal.github.com/ko/actions/concepts/runners/github-hosted-runners&quot; target=&quot;_blank&quot; data-source-url=&quot;https://docs.github.com/ko/actions/concepts/runners/github-hosted-runners&quot;&gt;&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://blog.kakaocdn.net/dna/bfnrFL/dJMb8Rj7Ihd/AAAAAAAAAAAAAAAAAAAAAFbOeaJacQ17PkrD6RJVGZLL1b3jDw3pWwDyFyufIrjT/img.png?credential=yqXZFxpELC7KVnFOS48ylbz2pIh7yKj8&amp;amp;expires=1780239599&amp;amp;allow_ip=&amp;amp;allow_referer=&amp;amp;signature=l5v5WSkHksD39t4FUr3FfiuPcVw%3D')&quot;&gt; &lt;/div&gt;&lt;div class=&quot;og-text&quot;&gt;&lt;p class=&quot;og-title&quot;&gt;GitHub 호스팅 실행기 - GitHub 문서&lt;/p&gt;&lt;p class=&quot;og-desc&quot;&gt;GitHub 는 워크플로를 실행하기 위해 호스트된 가상 머신을 제공합니다. 가상 머신에는 사용할 수 있는 도구, 패키지 및 설정 환경이 포함되어 있습니다 GitHub Actions .&lt;/p&gt;&lt;p class=&quot;og-host&quot;&gt;docs.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;br&gt;&amp;nbsp;&lt;/p&gt;&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. Self-hosted Runner&lt;/h3&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot;&gt;&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  Self-Hosted&lt;/b&gt;&lt;br&gt;&lt;br&gt;&lt;b&gt;- 소프트웨어나 서비스를 외부 클라우드 벤더에 의존하지 않고, 자신의 서버/인프라에서 직접 운영하는 방식을 의미합니다.&lt;/b&gt;&lt;/blockquote&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;617&quot; data-origin-height=&quot;570&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cy7Y1N/dJMcaicmPTj/NtSrDuk5g0HrBG3nqzZAyk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cy7Y1N/dJMcaicmPTj/NtSrDuk5g0HrBG3nqzZAyk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cy7Y1N/dJMcaicmPTj/NtSrDuk5g0HrBG3nqzZAyk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcy7Y1N%2FdJMcaicmPTj%2FNtSrDuk5g0HrBG3nqzZAyk%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;617&quot; height=&quot;570&quot; data-origin-width=&quot;617&quot; data-origin-height=&quot;570&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  Self-hosted Runner 처리 과정&lt;/b&gt;&lt;br&gt;&lt;br&gt;&lt;b&gt;1. 이벤트 발생&amp;nbsp;(개발자/저장소)&lt;/b&gt;&lt;br&gt;- GitHub-hosted와 동일하며, push, PR, schedule 등 이벤트가 발생하면 Actions가 감지합니다.&lt;br&gt;&lt;br&gt;&lt;b&gt;2. GitHub Actions 트리거&lt;/b&gt; &lt;br&gt;-&amp;nbsp;.github/workflows/*.yml에서 runs-on: self-hosted를 감지하며, 클라우드 서버 대신 등록된 Self-hosted Runner를 찾습니다.&lt;br&gt;&lt;br&gt;&lt;b&gt;3. 내 서버(Runner)로 Job 전달: GitHub → 내 서버&lt;/b&gt; &lt;br&gt;- Hosted 방식과 다르게 GitHub이 서버를 새로 만드는 게 아니라, 내 서버에서 실행 중인 Runner 프로세스가 GitHub을 주기적으로 폴링해서 Job을 가져오는 방식.&lt;br&gt;&lt;br&gt;&lt;b&gt;4. 내 서버에서 Job 실행 &lt;/b&gt;&lt;b&gt;&lt;br&gt;&lt;/b&gt;-&amp;nbsp;실제 빌드/테스트/배포가 내 서버에서 직접 실행됩니다.&lt;br&gt;&lt;br&gt;&lt;b&gt;5. 결과 GitHub에 보고: 내 서버 → GitHub&lt;/b&gt;&lt;br&gt;- Job 실행이 끝나면 성공/실패 상태를 GitHub에 전송. GitHub Actions UI에서 로그와 결과를 확인할 수 있게 됨.&lt;br&gt;&lt;br&gt;&lt;b&gt;6. Runner 대기 상태 유지&lt;/b&gt; &lt;br&gt;-&amp;nbsp;GitHub-hosted와 가장 다른 부분. 서버가 삭제되지 않고&amp;nbsp;계속 살아서 다음 Job을 기다림. &lt;br&gt;- 환경이 유지되기 때문에 이전 빌드의 캐시, 설치된 패키지 등이 그대로 남아있음.&lt;/blockquote&gt;&lt;figure data-ke-type=&quot;opengraph&quot; data-og-title=&quot;자체 호스팅 실행기 - GitHub 문서&quot; data-ke-align=&quot;alignCenter&quot; data-og-description=&quot;자체 실행기를 호스트하고 GitHub Actions 워크플로에서 작업을 실행하는 데 사용되는 환경을 사용자 지정할 수 있습니다.&quot; data-og-host=&quot;docs.github.com&quot; data-og-source-url=&quot;https://docs.github.com/ko/actions/concepts/runners/self-hosted-runners&quot; data-og-image=&quot;https://blog.kakaocdn.net/dna/bj24CP/dJMb84qejuX/AAAAAAAAAAAAAAAAAAAAAIu41nJ-NhvA-lhiml2XJzhHjBRTfJgX6hVffuYfYzpL/img.png?credential=yqXZFxpELC7KVnFOS48ylbz2pIh7yKj8&amp;amp;expires=1780239599&amp;amp;allow_ip=&amp;amp;allow_referer=&amp;amp;signature=7JskecCXaU6E4Ad%2B5hmXlyg8Aj8%3D&quot; data-og-url=&quot;https://docs-internal.github.com/ko/actions/concepts/runners/self-hosted-runners&quot;&gt;&lt;a href=&quot;https://docs-internal.github.com/ko/actions/concepts/runners/self-hosted-runners&quot; target=&quot;_blank&quot; data-source-url=&quot;https://docs.github.com/ko/actions/concepts/runners/self-hosted-runners&quot;&gt;&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://blog.kakaocdn.net/dna/bj24CP/dJMb84qejuX/AAAAAAAAAAAAAAAAAAAAAIu41nJ-NhvA-lhiml2XJzhHjBRTfJgX6hVffuYfYzpL/img.png?credential=yqXZFxpELC7KVnFOS48ylbz2pIh7yKj8&amp;amp;expires=1780239599&amp;amp;allow_ip=&amp;amp;allow_referer=&amp;amp;signature=7JskecCXaU6E4Ad%2B5hmXlyg8Aj8%3D')&quot;&gt; &lt;/div&gt;&lt;div class=&quot;og-text&quot;&gt;&lt;p class=&quot;og-title&quot;&gt;자체 호스팅 실행기 - GitHub 문서&lt;/p&gt;&lt;p class=&quot;og-desc&quot;&gt;자체 실행기를 호스트하고 GitHub Actions 워크플로에서 작업을 실행하는 데 사용되는 환경을 사용자 지정할 수 있습니다.&lt;/p&gt;&lt;p class=&quot;og-host&quot;&gt;docs.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;br&gt;&amp;nbsp;&lt;/p&gt;&lt;h2 data-ke-size=&quot;size26&quot;&gt;2) Github self-hosted 설정&lt;/h2&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  Github self-hosted 설정&lt;/b&gt;&lt;br&gt;&lt;br&gt;&lt;b&gt;- 주체가 되는 서버에 Github의 Runner를 설치하여서 서버 자체 내에서 배포를 하는 방식입니다.&lt;/b&gt;&lt;/blockquote&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. Github Settings &amp;gt; Actions &amp;gt; Runners 탭을 선택합니다&lt;/h3&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1892&quot; data-origin-height=&quot;867&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cQgDHD/dJMcabqK6PI/ovDxSbmS3Gf9RhkRt2nyH0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cQgDHD/dJMcabqK6PI/ovDxSbmS3Gf9RhkRt2nyH0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cQgDHD/dJMcabqK6PI/ovDxSbmS3Gf9RhkRt2nyH0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcQgDHD%2FdJMcabqK6PI%2FovDxSbmS3Gf9RhkRt2nyH0%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;1892&quot; height=&quot;867&quot; data-origin-width=&quot;1892&quot; data-origin-height=&quot;867&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. New self-hosted runner를 선택합니다&lt;/h3&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1891&quot; data-origin-height=&quot;873&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kNlFd/dJMb997wsFF/2c6afkRknYenSWNJmLQGeK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kNlFd/dJMb997wsFF/2c6afkRknYenSWNJmLQGeK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kNlFd/dJMb997wsFF/2c6afkRknYenSWNJmLQGeK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkNlFd%2FdJMb997wsFF%2F2c6afkRknYenSWNJmLQGeK%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;1891&quot; height=&quot;873&quot; data-origin-width=&quot;1891&quot; data-origin-height=&quot;873&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. Runner image, Architecture, Download, Configure가 출력이 됩니다&lt;/h3&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1894&quot; data-origin-height=&quot;873&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/PXnNm/dJMcabc9TnH/2mjSlcqQuPbauAuikxQkmk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/PXnNm/dJMcabc9TnH/2mjSlcqQuPbauAuikxQkmk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/PXnNm/dJMcabc9TnH/2mjSlcqQuPbauAuikxQkmk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FPXnNm%2FdJMcabc9TnH%2F2mjSlcqQuPbauAuikxQkmk%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;1894&quot; height=&quot;873&quot; data-origin-width=&quot;1894&quot; data-origin-height=&quot;873&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  [참고]&lt;/b&gt; Architecture는 서버내에서 아래의 명령어로 확인이 가능합니다&lt;/blockquote&gt;&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 94px;&quot; border=&quot;1&quot; data-ke-style=&quot;style2&quot; data-ke-align=&quot;alignLeft&quot;&gt;&lt;tbody&gt;&lt;tr style=&quot;height: 18px;&quot;&gt;&lt;td style=&quot;height: 18px;&quot;&gt;&lt;b&gt;GitHub에서 선택&lt;/b&gt;&lt;/td&gt;&lt;td style=&quot;height: 18px;&quot;&gt;&lt;b&gt;출력값&lt;/b&gt;&lt;/td&gt;&lt;td style=&quot;height: 18px;&quot;&gt;&lt;b&gt;의미&lt;/b&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr style=&quot;height: 20px;&quot;&gt;&lt;td style=&quot;height: 20px;&quot;&gt;&lt;b&gt;x64&lt;/b&gt;&lt;/td&gt;&lt;td style=&quot;height: 20px;&quot;&gt;x86_64&lt;/td&gt;&lt;td style=&quot;height: 20px;&quot;&gt;일반 Intel/AMD 64bit&lt;/td&gt;&lt;/tr&gt;&lt;tr style=&quot;height: 20px;&quot;&gt;&lt;td style=&quot;height: 20px;&quot;&gt;&lt;b&gt;ARM64&lt;/b&gt;&lt;/td&gt;&lt;td style=&quot;height: 20px;&quot;&gt;aarch64&lt;/td&gt;&lt;td style=&quot;height: 20px;&quot;&gt;ARM 64bit (AWS Graviton, 라즈베리파이 등)&lt;/td&gt;&lt;/tr&gt;&lt;tr style=&quot;height: 18px;&quot;&gt;&lt;td style=&quot;height: 18px;&quot;&gt;&lt;b&gt;ARM&lt;/b&gt;&lt;/td&gt;&lt;td style=&quot;height: 18px;&quot;&gt;armv7l&lt;/td&gt;&lt;td style=&quot;height: 18px;&quot;&gt;ARM 32bit&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;pre class=&quot;elixir&quot;&gt;&lt;code&gt;$ uname -m
&lt;/code&gt;&lt;/pre&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;956&quot; data-origin-height=&quot;268&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Gxi62/dJMcaaSUJB1/54nRt4kavMdD5sBS0KkeeK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Gxi62/dJMcaaSUJB1/54nRt4kavMdD5sBS0KkeeK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Gxi62/dJMcaaSUJB1/54nRt4kavMdD5sBS0KkeeK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FGxi62%2FdJMcaaSUJB1%2F54nRt4kavMdD5sBS0KkeeK%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;956&quot; height=&quot;268&quot; data-origin-width=&quot;956&quot; data-origin-height=&quot;268&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;h2 data-ke-size=&quot;size26&quot;&gt;3) Linux 서버에서 수행&lt;/h2&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 위에서 전달받은 명령어에 따라 다운로드(Download)를 수행합니다.&lt;/h3&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;pre class=&quot;elixir&quot;&gt;&lt;code&gt;# Create a folder
$ mkdir actions-runner &amp;amp;&amp;amp; cd actions-runnerCopied!

# Download the latest runner package
$ curl -o actions-runner-linux-arm64-2.334.0.tar.gz -L &amp;lt;https://github.com/actions/runner/releases/download/v2.334.0/actions-runner-linux-arm64-2.334.0.tar.gzCopied&amp;gt;!

# Optional: Validate the hash
$ echo &quot;xxxxxxx  actions-runner-linux-arm64-2.334.0.tar.gz&quot; | shasum -a 256 -c

# Extract the installer
$ tar xzf ./actions-runner-linux-arm64-2.334.0.tar.gz
&lt;/code&gt;&lt;/pre&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1392&quot; data-origin-height=&quot;912&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cgfL8t/dJMcaja8lCR/6vxUNxf71szdsp08sKfFn1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cgfL8t/dJMcaja8lCR/6vxUNxf71szdsp08sKfFn1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cgfL8t/dJMcaja8lCR/6vxUNxf71szdsp08sKfFn1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcgfL8t%2FdJMcaja8lCR%2F6vxUNxf71szdsp08sKfFn1%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;1392&quot; height=&quot;912&quot; data-origin-width=&quot;1392&quot; data-origin-height=&quot;912&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. 추가 설정(Configure) 명령어를 입력합니다.&lt;/h3&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot;&gt;&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  추가 설정(Configure)&amp;nbsp;명령어를 입력합니다.&lt;/b&gt;&lt;br&gt;&lt;br&gt;&lt;b&gt;- 아래와 같이 실제 수행이 됨을 확인하였습니다.&lt;/b&gt;&lt;/blockquote&gt;&lt;pre class=&quot;shell&quot; data-ke-language=&quot;shell&quot;&gt;&lt;code&gt;# Create the runner and start the configuration experience
$ ./config.sh --url https://github.com/xxxxx/xxxxx --token xxxxx

# Last step, run it!
$ ./run.sh&lt;/code&gt;&lt;/pre&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1392&quot; data-origin-height=&quot;912&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dYQdZu/dJMcabxtTY4/zAXyWEqHNUdvmSXpEpz2nk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dYQdZu/dJMcabxtTY4/zAXyWEqHNUdvmSXpEpz2nk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dYQdZu/dJMcabxtTY4/zAXyWEqHNUdvmSXpEpz2nk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdYQdZu%2FdJMcabxtTY4%2FzAXyWEqHNUdvmSXpEpz2nk%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;1392&quot; height=&quot;912&quot; data-origin-width=&quot;1392&quot; data-origin-height=&quot;912&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;h3 data-ke-size=&quot;size23&quot;&gt;3.&amp;nbsp;백그라운드에서&amp;nbsp;run.sh&amp;nbsp;파일&amp;nbsp;수행&lt;/h3&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot;&gt;&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;&amp;nbsp;  백그라운드에서 run.sh 파일 수행&lt;/b&gt;&lt;b&gt;&lt;br&gt;&lt;/b&gt;&lt;br&gt;&lt;b&gt;- ./run.sh 파일을 수행하면 터미널에서 세션이 사라지게 되면, 리스너가 종료가 됩니다. 그렇기에 백그라운드로 수행하도록 수행합니다.&lt;/b&gt;&lt;/blockquote&gt;&lt;pre class=&quot;shell&quot; data-ke-language=&quot;shell&quot;&gt;&lt;code&gt;# 터미널 종료해도 백그라운드에서 계속 실행
$ nohup ./run.sh &amp;amp;&lt;/code&gt;&lt;/pre&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  [참고]&lt;/b&gt; 실행중인 러너와 확인 후 종료 방법&lt;/blockquote&gt;&lt;pre class=&quot;perl&quot;&gt;&lt;code&gt;# 실행중인 러너를 확인
ps aux | grep Runner.Listener

# PID 확인 후 종료
kill [PID]
&lt;/code&gt;&lt;/pre&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;h2 data-ke-size=&quot;size26&quot;&gt;4) Action 구성 : Front-end&lt;/h2&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 루트 경로에 .github/workflows 폴더를 생성하고 deploy.yml 파일을 생성합니다.&lt;/h3&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;372&quot; data-origin-height=&quot;589&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/SlDdI/dJMcaiQTZIG/1CEihcEGYTVwOBDEjGQYK1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/SlDdI/dJMcaiQTZIG/1CEihcEGYTVwOBDEjGQYK1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/SlDdI/dJMcaiQTZIG/1CEihcEGYTVwOBDEjGQYK1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FSlDdI%2FdJMcaiQTZIG%2F1CEihcEGYTVwOBDEjGQYK1%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;372&quot; height=&quot;589&quot; data-origin-width=&quot;372&quot; data-origin-height=&quot;589&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. deploy.yml 구성하기&lt;/h3&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot;&gt;&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  deploy.yml 구성하기&lt;/b&gt;&lt;br&gt;&lt;br&gt;- 아래와 같은 구성을 수행하였습니다. &lt;br&gt;- 해당 처리 과정은 release-xxx 브랜치로 'pull Request'가 발생하였을 때, 아래와 같은 Action이 수행이 됩니다.&lt;/blockquote&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;461&quot; data-origin-height=&quot;755&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bLBpzE/dJMcabxtUsC/0ygS6L2tLgLk7NP8waU8c0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bLBpzE/dJMcabxtUsC/0ygS6L2tLgLk7NP8waU8c0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bLBpzE/dJMcabxtUsC/0ygS6L2tLgLk7NP8waU8c0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbLBpzE%2FdJMcabxtUsC%2F0ygS6L2tLgLk7NP8waU8c0%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;461&quot; height=&quot;755&quot; data-origin-width=&quot;461&quot; data-origin-height=&quot;755&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 200px;&quot; border=&quot;1&quot; data-ke-style=&quot;style2&quot; data-ke-align=&quot;alignLeft&quot;&gt;&lt;tbody&gt;&lt;tr style=&quot;height: 20px;&quot;&gt;&lt;td style=&quot;width: 10.1163%; height: 20px;&quot;&gt;&lt;b&gt;단계&lt;/b&gt;&lt;/td&gt;&lt;td style=&quot;width: 36.2791%; height: 20px;&quot;&gt;&lt;b&gt;이름&lt;/b&gt;&lt;/td&gt;&lt;td style=&quot;width: 53.4884%; height: 20px;&quot;&gt;&lt;b&gt;설명&lt;/b&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr style=&quot;height: 20px;&quot;&gt;&lt;td style=&quot;width: 10.1163%; height: 20px;&quot;&gt;1&lt;/td&gt;&lt;td style=&quot;width: 36.2791%; height: 20px;&quot;&gt;소스코드 체크아웃&lt;/td&gt;&lt;td style=&quot;width: 53.4884%; height: 20px;&quot;&gt;레포 코드를 Runner 서버로 내려받음&lt;/td&gt;&lt;/tr&gt;&lt;tr style=&quot;height: 20px;&quot;&gt;&lt;td style=&quot;width: 10.1163%; height: 20px;&quot;&gt;2&lt;/td&gt;&lt;td style=&quot;width: 36.2791%; height: 20px;&quot;&gt;Node.js 설정&lt;/td&gt;&lt;td style=&quot;width: 53.4884%; height: 20px;&quot;&gt;Node.js 24 버전 설치&lt;/td&gt;&lt;/tr&gt;&lt;tr style=&quot;height: 20px;&quot;&gt;&lt;td style=&quot;width: 10.1163%; height: 20px;&quot;&gt;3&lt;/td&gt;&lt;td style=&quot;width: 36.2791%; height: 20px;&quot;&gt;Yarn 활성화&lt;/td&gt;&lt;td style=&quot;width: 53.4884%; height: 20px;&quot;&gt;corepack으로 Yarn 활성화&lt;/td&gt;&lt;/tr&gt;&lt;tr style=&quot;height: 20px;&quot;&gt;&lt;td style=&quot;width: 10.1163%; height: 20px;&quot;&gt;4&lt;/td&gt;&lt;td style=&quot;width: 36.2791%; height: 20px;&quot;&gt;의존성 설치&lt;/td&gt;&lt;td style=&quot;width: 53.4884%; height: 20px;&quot;&gt;yarn.lock&amp;nbsp;기준으로 패키지 설치&lt;/td&gt;&lt;/tr&gt;&lt;tr style=&quot;height: 20px;&quot;&gt;&lt;td style=&quot;width: 10.1163%; height: 20px;&quot;&gt;5&lt;/td&gt;&lt;td style=&quot;width: 36.2791%; height: 20px;&quot;&gt;빌드&lt;/td&gt;&lt;td style=&quot;width: 53.4884%; height: 20px;&quot;&gt;Vite production 빌드 →&amp;nbsp;dist/&amp;nbsp;생성&lt;/td&gt;&lt;/tr&gt;&lt;tr style=&quot;height: 20px;&quot;&gt;&lt;td style=&quot;width: 10.1163%; height: 20px;&quot;&gt;6&lt;/td&gt;&lt;td style=&quot;width: 36.2791%; height: 20px;&quot;&gt;Docker 이미지 빌드&lt;/td&gt;&lt;td style=&quot;width: 53.4884%; height: 20px;&quot;&gt;dist/를 포함한 Docker 이미지 생성&lt;/td&gt;&lt;/tr&gt;&lt;tr style=&quot;height: 20px;&quot;&gt;&lt;td style=&quot;width: 10.1163%; height: 20px;&quot;&gt;7&lt;/td&gt;&lt;td style=&quot;width: 36.2791%; height: 20px;&quot;&gt;기존 컨테이너 중지 및 제거&lt;/td&gt;&lt;td style=&quot;width: 53.4884%; height: 20px;&quot;&gt;실행 중인 컨테이너 종료/삭제&lt;/td&gt;&lt;/tr&gt;&lt;tr style=&quot;height: 20px;&quot;&gt;&lt;td style=&quot;width: 10.1163%; height: 20px;&quot;&gt;8&lt;/td&gt;&lt;td style=&quot;width: 36.2791%; height: 20px;&quot;&gt;컨테이너 실행&lt;/td&gt;&lt;td style=&quot;width: 53.4884%; height: 20px;&quot;&gt;compose로 새 이미지 컨테이너 재시작&lt;/td&gt;&lt;/tr&gt;&lt;tr style=&quot;height: 20px;&quot;&gt;&lt;td style=&quot;width: 10.1163%; height: 20px;&quot;&gt;9&lt;/td&gt;&lt;td style=&quot;width: 36.2791%; height: 20px;&quot;&gt;오래된 이미지 정리&lt;/td&gt;&lt;td style=&quot;width: 53.4884%; height: 20px;&quot;&gt;사용하지 않는 Docker 이미지 삭제&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;pre class=&quot;http&quot;&gt;&lt;code&gt;name: Deploy xxx

on:
 push:
  branches:
   - release-xxx

jobs:
 deploy:
  runs-on: self-hosted

  steps:
   - name: 소스코드 체크아웃
     uses: actions/checkout@v4

   - name: Node.js 설정
     uses: actions/setup-node@v4
     with:
      node-version: '24'

   - name: Yarn 활성화
     run: corepack enable

   - name: 의존성 설치
     run: yarn install --immutable

   - name: 빌드
     run: yarn build --mode production

   - name: Docker 이미지 빌드
     run: docker build --platform linux/amd64 -f Dockerfile -t xxx-xxx-web .

   - name: 기존 컨테이너 중지 및 제거
     run: |
      docker stop xxx-web || true
      docker rm xxx-web || true

   - name: 컨테이너 실행
     run: docker compose -f /home/tomcat/compose.yaml up -d web
     
   - name: 오래된 이미지 정리
     run: docker image prune -f

&lt;/code&gt;&lt;/pre&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. 실행 확인&lt;/h3&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot;&gt;&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  실행 확인&lt;/b&gt;&lt;br&gt;&lt;br&gt;&lt;b&gt;- 실행한 환경에 맞게 정상적으로 수행이 됨을 확인하였습니다.&lt;/b&gt;&lt;/blockquote&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1887&quot; data-origin-height=&quot;852&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cEHlT4/dJMcadaWVdk/3dEQHsAn7akwzIXDaIGaFk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cEHlT4/dJMcadaWVdk/3dEQHsAn7akwzIXDaIGaFk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cEHlT4/dJMcadaWVdk/3dEQHsAn7akwzIXDaIGaFk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcEHlT4%2FdJMcadaWVdk%2F3dEQHsAn7akwzIXDaIGaFk%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;1887&quot; height=&quot;852&quot; data-origin-width=&quot;1887&quot; data-origin-height=&quot;852&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;h2 data-ke-size=&quot;size26&quot;&gt;5) Action 구성 : Back-end&lt;/h2&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 루트 경로에 .github/workflows 폴더를 생성하고 deploy.yml 파일을 생성합니다.&lt;/h3&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;475&quot; data-origin-height=&quot;559&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bTW0hJ/dJMcadWml4o/lJgTRZ4j9KYEPULEqru8u1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bTW0hJ/dJMcadWml4o/lJgTRZ4j9KYEPULEqru8u1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bTW0hJ/dJMcadWml4o/lJgTRZ4j9KYEPULEqru8u1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbTW0hJ%2FdJMcadWml4o%2FlJgTRZ4j9KYEPULEqru8u1%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;475&quot; height=&quot;559&quot; data-origin-width=&quot;475&quot; data-origin-height=&quot;559&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  [참고] 최초 배포를 하는 경우 아래와 같은 오류가 발생하였습니다.&lt;/b&gt;&lt;br&gt;&lt;br&gt;- Git 내에 gradle-wrapper.jar가 올라가지 않은 상태이기에 이를 포함하여 올려줍니다.&lt;/blockquote&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1506&quot; data-origin-height=&quot;331&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bFb7zH/dJMcajhYvnv/vkLabK6P5cqjyAjfhmg9Z1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bFb7zH/dJMcajhYvnv/vkLabK6P5cqjyAjfhmg9Z1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bFb7zH/dJMcajhYvnv/vkLabK6P5cqjyAjfhmg9Z1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbFb7zH%2FdJMcajhYvnv%2FvkLabK6P5cqjyAjfhmg9Z1%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;1506&quot; height=&quot;331&quot; data-origin-width=&quot;1506&quot; data-origin-height=&quot;331&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;pre data-ke-type=&quot;codeblock&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot;&gt;&lt;code&gt;git add -f gradle/wrapper/gradle-wrapper.jar&lt;/code&gt;&lt;/pre&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. deploy.yml 구성하기&lt;/h3&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot;&gt;&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  deploy.yml 구성하기&lt;/b&gt;&lt;br&gt;&lt;br&gt;&lt;b&gt;- 아래와 같은 구성을 수행하였습니다.&lt;/b&gt; &lt;br&gt;- 해당 처리 과정은 release-xxx로 pull Request가 발생하였을 때, 아래와 같은 Action이 수행이 됩니다.&lt;/blockquote&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;574&quot; data-origin-height=&quot;621&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bII9U0/dJMcaiXDZCt/TaAzOqvEoLNAIQHM57XG20/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bII9U0/dJMcaiXDZCt/TaAzOqvEoLNAIQHM57XG20/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bII9U0/dJMcaiXDZCt/TaAzOqvEoLNAIQHM57XG20/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbII9U0%2FdJMcaiXDZCt%2FTaAzOqvEoLNAIQHM57XG20%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;574&quot; height=&quot;621&quot; data-origin-width=&quot;574&quot; data-origin-height=&quot;621&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-style=&quot;style2&quot; data-ke-align=&quot;alignLeft&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;b&gt;단계&lt;/b&gt;&lt;/td&gt;&lt;td&gt;&lt;b&gt;이름&lt;/b&gt;&lt;/td&gt;&lt;td&gt;&lt;b&gt;설명&lt;/b&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;1&lt;/td&gt;&lt;td&gt;소스코드 체크아웃&lt;/td&gt;&lt;td&gt;레포 코드를 Runner 서버로 내려받음&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;2&lt;/td&gt;&lt;td&gt;Java 설정&lt;/td&gt;&lt;td&gt;Amazon Corretto 21 버전 설치&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;3&lt;/td&gt;&lt;td&gt;Gradle 빌드&lt;/td&gt;&lt;td&gt;bootJar로 실행 가능한 JAR 파일 생성&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;4&lt;/td&gt;&lt;td&gt;Docker 이미지 빌드&lt;/td&gt;&lt;td&gt;JAR를 포함한 Docker 이미지 생성&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;5&lt;/td&gt;&lt;td&gt;기존 컨테이너 중지 및 제거&lt;/td&gt;&lt;td&gt;실행 중인 컨테이너 종료/삭제&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;6&lt;/td&gt;&lt;td&gt;컨테이너 실행&lt;/td&gt;&lt;td&gt;compose로 새 이미지 컨테이너 재시작&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;7&lt;/td&gt;&lt;td&gt;오래된 이미지 정리&lt;/td&gt;&lt;td&gt;사용하지 않는 Docker 이미지 삭제&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;pre class=&quot;http&quot;&gt;&lt;code&gt;name: Deploy API

on:
  push:
    branches:
      - release-xxx

jobs:
  deploy:
    runs-on: self-hosted

    steps:
      - name: 소스코드 체크아웃
        uses: actions/checkout@v4

      - name: Java 설정
        uses: actions/setup-java@v4
        with:
          java-version: '21'
          distribution: 'corretto'

      - name: Gradle 빌드
        run: ./gradlew bootJar

      - name: Docker 이미지 빌드
        run: docker build --platform linux/amd64 -f Dockerfile -t dkcns-homepage-api .

      - name: 기존 컨테이너 중지 및 제거
        run: |
          docker stop xxx-api || true
          docker rm xxx-api || true

      - name: 컨테이너 실행
        run: |
          docker compose -f /home/tomcat/compose.yaml up -d api

      - name: 오래된 이미지 정리
        run: docker image prune -f

&lt;/code&gt;&lt;/pre&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. 실행 확인&lt;/h3&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot;&gt;&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  실행 확인&lt;/b&gt;&lt;br&gt;&lt;br&gt;&lt;b&gt;- 실행한 환경에 맞게 정상적으로 수행이 됨을 확인하였습니다.&lt;/b&gt;&lt;/blockquote&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1883&quot; data-origin-height=&quot;793&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/caAjGg/dJMcaciPAJb/t8PKeGqy7hCUSrDO5nZsx0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/caAjGg/dJMcaciPAJb/t8PKeGqy7hCUSrDO5nZsx0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/caAjGg/dJMcaciPAJb/t8PKeGqy7hCUSrDO5nZsx0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcaAjGg%2FdJMcaciPAJb%2Ft8PKeGqy7hCUSrDO5nZsx0%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;1883&quot; height=&quot;793&quot; data-origin-width=&quot;1883&quot; data-origin-height=&quot;793&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&lt;b&gt;오늘도&amp;nbsp;감사합니다.&amp;nbsp; &lt;/b&gt;&lt;/p&gt;</description>
      <category>공통/CI CD</category>
      <category>Github action</category>
      <category>GitHub Actions Runner</category>
      <category>self-hosted runner</category>
      <category>Self-hosted Runner 과정</category>
      <category>Self-hosted Runner 구성하기</category>
      <category>Self-hosted Runner 사용 사례</category>
      <category>Self-hosted Runner 설치</category>
      <category>Self-hosted Runner 환경설정</category>
      <author>adjh54</author>
      <guid isPermaLink="true">https://adjh54.tistory.com/760</guid>
      <comments>https://adjh54.tistory.com/760#entry760comment</comments>
      <pubDate>Wed, 20 May 2026 19:15:05 +0900</pubDate>
    </item>
    <item>
      <title>[CI/CD] Docker Compose + SFTP 방식 구성 및 배포 방법</title>
      <link>https://adjh54.tistory.com/759</link>
      <description>&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
 &lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;해당 글에서는 CI/CD 파이프라인이 구축되기 전에 사용자의 수동 배포를 최소한으로 줄이기 위해 임시적으로 사용하는 SFTP 방식을 이용한 사례에 대해서 작성한 글입니다.&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;/span&gt;&lt;/span&gt; 
 &lt;figure contenteditable=&quot;false&quot; data-ke-type=&quot;emoticon&quot; data-ke-align=&quot;alignCenter&quot; data-emoticon-type=&quot;face&quot; data-emoticon-name=&quot;073&quot; data-emoticon-isanimation=&quot;false&quot; data-emoticon-src=&quot;https://t1.daumcdn.net/axz_keditor/emoticon/face/large/073.png&quot;&gt;
  &lt;img src=&quot;https://t1.daumcdn.net/axz_keditor/emoticon/face/large/073.png&quot; width=&quot;80&quot;&gt;
 &lt;/figure&gt; 
&lt;/blockquote&gt;&lt;h2 data-ke-size=&quot;size26&quot;&gt;1) 배포방식&lt;/h2&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot;&gt;&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  배포방식&lt;/b&gt;&lt;br&gt;&lt;br&gt;&lt;b&gt;- 현재 배포 환경은 CI/CD 파이프라인이 구축되기 이전 단계로 GitHub Actions Runner 같은 자동화 도구를 통한 배포가 아닌, 사용자가 수동 배포 과정을 최소한으로 줄이기 위해 임시로 운영하는 방식입니다.&lt;/b&gt;&lt;b&gt;&lt;br&gt;&lt;/b&gt;&lt;br&gt;- 별도의 Docker Registry를 운영하지 않기 때문에, 개발 PC에서 Docker 이미지를 빌드한 뒤 docker save 명령으로 .tar 파일로 저장합니다. 이 파일을 SFTP를 통해 운영 서버로 전송하고, 운영 서버에서 docker load로 이미지를 로드한 뒤 docker compose up -d로 서비스를 실행하는 흐름입니다.&lt;br&gt;&lt;br&gt;- 이 방식은 Registry 구축 없이도 이미지를 전달할 수 있고, 인터넷이 차단된 폐쇄망 환경에서도 적용 가능하다는 장점이 있습니다. 반면 매 배포마다 수동 작업이 필요하고, 배포 이력 관리가 어려우며, 이미지 태그 불일치 등의 휴먼 에러가 발생할 수 있다는 한계가 있습니다.&lt;br&gt;&lt;br&gt;&lt;b&gt;- 향후 CI/CD가 구축되면, git push를 트리거로 Runner가 자동으로 이미지를 빌드하고 Registry에 push 한 뒤 운영 서버에서 pull 하여 배포하는 구조로 전환될 예정이며, 현재의 수동 과정은 그 과도기에 사용하는 임시 배포 방식입니다.&lt;/b&gt;&lt;/blockquote&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 배포 처리 과정&lt;/h3&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;689&quot; data-origin-height=&quot;421&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/d67tc7/dJMcadWmfcL/0i1bY69DWbG2GkFZmlJbX1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/d67tc7/dJMcadWmfcL/0i1bY69DWbG2GkFZmlJbX1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/d67tc7/dJMcadWmfcL/0i1bY69DWbG2GkFZmlJbX1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fd67tc7%2FdJMcadWmfcL%2F0i1bY69DWbG2GkFZmlJbX1%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;689&quot; height=&quot;421&quot; data-origin-width=&quot;689&quot; data-origin-height=&quot;421&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  배포 처리 과정&lt;/b&gt;&lt;br&gt;&lt;br&gt;&lt;b&gt;1. 소스코드&lt;/b&gt;&lt;br&gt;- 배포의 시작점으로 React, Spring Boot 등 로컬에서 개발 완료된 상태의 코드를 준비합니다.&lt;br&gt;- 또한, .env, docker-compose.yml, Dockerfile 등이 함께 준비되어 있어야 합니다.&lt;br&gt;&lt;br&gt;&lt;b&gt;2. Docker 이미지 빌드&lt;/b&gt;&lt;br&gt;- Dockerfile을 기반으로 소스코드를 컨테이너 이미지로 패키징 하는 단계를 의미합니다.&lt;br&gt;- 이 시점에 OS, 런타임, 의존성, 앱 코드가 하나의 이미지로 묶이며, 빌드 결과물은 로컬 Docker에만 존재해야 합니다.&lt;br&gt;&lt;br&gt;&lt;b&gt;3. tar 파일 Export&lt;/b&gt;&lt;br&gt;- Docker 이미지를 파일로 직렬화하는 단계를 의미합니다. Docker Hub 같은 외부 레지스트리 없이 이미지를 전송하기 위해 사용이 됩니다.&lt;br&gt;&lt;br&gt;&lt;b&gt;4. SFTP 전송&lt;/b&gt;&lt;b&gt;&lt;br&gt;&lt;/b&gt;- PC에서 압축된 tar 파일을 배포될 원격 서버로 복사를 하는 과정입니다. 이 과정에서는 FTP 또는 SFTP 전송을 하며, SFTP 클라이언트(FileZila, Termius 등)를 사용하여 전송을 합니다.&lt;b&gt;&lt;br&gt;&lt;/b&gt;&lt;br&gt;&lt;b&gt;5. 파일 수신 (서버 폴더 이동)&lt;/b&gt;&lt;br&gt;- 서버에서 전송받은 파일 위치를 확인하는 단계이며, 별도 작업이라기보다 다음 단계 전 준비 확인 과정입니다.&lt;br&gt;&lt;br&gt;&lt;b&gt;6. 이미지 로드&lt;/b&gt;&lt;br&gt;- 서버의 Docker에 이미지를 등록하는 단계를 의미합니다.&lt;br&gt;&lt;br&gt;&lt;b&gt;7. 컨테이너 실행&lt;/b&gt;&lt;br&gt;- compose.yml 기반으로 컨테이너를 백그라운드로 실행. 포트, 볼륨, 환경변수 등이 이 파일에 정의되어 있는 파일을 실행합니다.&lt;br&gt;&lt;br&gt;&lt;b&gt;8. 배포 완료&lt;/b&gt;&lt;br&gt;- 서비스 정상 동작 확인합니다.&lt;/blockquote&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. 서버 내 compose.yaml&lt;/h3&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot;&gt;&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  서버 내 compose.yaml&lt;/b&gt;&lt;br&gt;&lt;br&gt;&lt;b&gt;- 해당 서버는 배포될 리눅스 서버를 의미하며, 이를 위치하며 Docker Compose를 통하여서 배포가 이루어집니다.&lt;/b&gt;&lt;/blockquote&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;579&quot; data-origin-height=&quot;516&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bGbpED/dJMcaipRzI7/iKxPs3IMU73gnyIDCz2e30/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bGbpED/dJMcaipRzI7/iKxPs3IMU73gnyIDCz2e30/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bGbpED/dJMcaipRzI7/iKxPs3IMU73gnyIDCz2e30/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbGbpED%2FdJMcaipRzI7%2FiKxPs3IMU73gnyIDCz2e30%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;579&quot; height=&quot;516&quot; data-origin-width=&quot;579&quot; data-origin-height=&quot;516&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  depends_on 기반 서비스 실행 과정&lt;/b&gt;&lt;br&gt;&lt;br&gt;&lt;b&gt;- 해당 서비스는 db → api → web 순서로 기동 됩니다.&lt;/b&gt;&lt;br&gt;&lt;br&gt;&lt;b&gt;1) db : PostgreSQL&lt;/b&gt;&lt;b&gt;&lt;br&gt;&lt;/b&gt;&lt;br&gt;&lt;b&gt;- postgres:17.9 이미지로 컨테이너를 생성하며 기본적인 TimeZone 설정을 지정하였습니다.&lt;/b&gt;&lt;b&gt;&lt;br&gt;&lt;/b&gt;&lt;br&gt;&lt;b&gt;1. command, environment 속성:&lt;/b&gt; 기본적인 TimeZone 설정을 지정하였습니다.&lt;br&gt;&lt;b&gt;&lt;br&gt;&lt;/b&gt;&lt;b&gt;2. env_file 속성:&lt;/b&gt; 서버 내에 위치해 있는 파일로, 기본적인 db 연결을 위한 POSTGRES_DB, POSTGRES_USER, POSTGRES_PASSWORD를 지정하였습니다.&lt;br&gt;&lt;br&gt;&lt;b&gt;3. volumes 속성 :&lt;/b&gt; pgdata:/var/lib/postgresql/data 컨테이너를 삭제하더라도 데이터를 유지하기 위해 지정하였습니다.&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;b&gt;2) api : Spring Boot&lt;/b&gt;&lt;br&gt;&lt;br&gt;&lt;b&gt;- 사전에 외부에서 .tar 파일로 빌드된 이미지를 가져와, 이미지 로드로 불러온 Spring Boot 컨테이너 이미지를 사용합니다.&lt;/b&gt;&lt;br&gt;&lt;br&gt;&lt;b&gt;1. env_file 속성:&lt;/b&gt; 서버 내에 위치해 있는 파일로, DB 접속 정보, Spring 프로파일 등 환경변수 주입을 하는 속성입니다.&lt;br&gt;&lt;br&gt;&lt;b&gt;2. volumes 속성 :&amp;nbsp;&lt;/b&gt;bind mount로 호스트의 ./uploads 디렉토리를 컨테이너 /uploads에 매핑되며, 파일 업로드 데이터가 호스트에 직접 저장됩니다.&lt;br&gt;&lt;br&gt;&lt;b&gt;3. environment 속성:&lt;/b&gt; JVM/OS 레벨 타임존 설정을 합니다.&lt;br&gt;&lt;br&gt;&lt;b&gt;4. restart 속성:&lt;/b&gt; docker stop을 하기 전까지 수행이 됩니다.&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;b&gt;3) web : React&lt;/b&gt;&lt;br&gt;&lt;b&gt;- 사전에 외부에서 .tar 파일로 빌드된 이미지를 가져와, 이미지 로드로 불러온 Vite로 빌드된 정적 파일을 서빙하는 Nginx 컨테이너 이미지를 사용합니다.&lt;/b&gt;&lt;br&gt;&lt;b&gt;&lt;br&gt;&lt;/b&gt;&lt;b&gt;1. volumes 속성:&lt;/b&gt; volumes로 호스트의 default.conf를 Nginx 설정에 bind mount → 이미지 재빌드 없이 Nginx 설정 변경 가능하도록 구성하였습니다. (컨테이너 재시작만 하면 됨)&lt;br&gt;&lt;br&gt;&lt;b&gt;2. restart 속성:&lt;/b&gt; docker stop을 하기 전까지 수행이 됩니다.&lt;/blockquote&gt;&lt;pre class=&quot;dts&quot;&gt;&lt;code&gt;services:
  db:
    image: postgres:17.9
    container_name: xxx-db
    command: postgres -c timezone=Asia/Seoul
    environment:
      - TZ=Asia/Seoul
      - PGTZ=Asia/Seoul
    ports:
      - &quot;5432:5432&quot;
    env_file:
      - ./env/.env.db
    volumes:
      - pgdata:/var/lib/postgresql/data
    restart: always

  api:
    image: xxx-xxx-api
    container_name: xxx-api
    ports:
      - &quot;8001:8001&quot;
    env_file:
      - ./env/.env.dev
    depends_on:
      - db
    restart: unless-stopped
    volumes:
      - &quot;./uploads:/uploads&quot;
    environment:
      - TZ=Asia/Seoul
  web:
    image: xxx-xxx-web
    container_name: dkcns-web
    ports:
      - &quot;80:80&quot;
    depends_on:
      - api
    restart: unless-stopped
    volumes:
      - &quot;/home/tomcat/nginx/default.conf:/etc/nginx/conf.d/default.conf&quot;

volumes:
  pgdata:
&lt;/code&gt;&lt;/pre&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;&amp;nbsp;  [참고]&lt;/b&gt; 해당 과정에서 적용한 TimeZone에 대해 궁금하시면 아래의 글을 참고하시면 도움이 됩니다.&lt;/blockquote&gt;&lt;figure data-ke-type=&quot;opengraph&quot; data-og-title=&quot;[DB] PostgreSQL + Docker TimeZone 설정하기&quot; data-ke-align=&quot;alignCenter&quot; data-og-description=&quot;해당 글에서는 PostgreSQL + Docker 환경에서 TimeZone을 지정했던 방법에 대해서 공유합니다. 1) Coordinated Universal Time (UTC) / TimeZone1. Coordinated Universal Time (UTC)  Coordinated Universal Time (UTC)- 전 세계 시간대 &quot; data-og-host=&quot;adjh54.tistory.com&quot; data-og-source-url=&quot;https://adjh54.tistory.com/757&quot; data-og-image=&quot;https://blog.kakaocdn.net/dna/bbYImJ/dJMb8VNBdSa/AAAAAAAAAAAAAAAAAAAAACoatp_QwA7sVCc-ZK_9G0jPgqzXm2fZP34cwHNyX8LV/img.png?credential=yqXZFxpELC7KVnFOS48ylbz2pIh7yKj8&amp;amp;expires=1780239599&amp;amp;allow_ip=&amp;amp;allow_referer=&amp;amp;signature=WKqXS8%2BSAqvLIR66UB3QaOuTnOw%3D&quot; data-og-url=&quot;https://adjh54.tistory.com/757&quot;&gt;&lt;a href=&quot;https://adjh54.tistory.com/757&quot; target=&quot;_blank&quot; data-source-url=&quot;https://adjh54.tistory.com/757&quot;&gt;&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://blog.kakaocdn.net/dna/bbYImJ/dJMb8VNBdSa/AAAAAAAAAAAAAAAAAAAAACoatp_QwA7sVCc-ZK_9G0jPgqzXm2fZP34cwHNyX8LV/img.png?credential=yqXZFxpELC7KVnFOS48ylbz2pIh7yKj8&amp;amp;expires=1780239599&amp;amp;allow_ip=&amp;amp;allow_referer=&amp;amp;signature=WKqXS8%2BSAqvLIR66UB3QaOuTnOw%3D')&quot;&gt; &lt;/div&gt;&lt;div class=&quot;og-text&quot;&gt;&lt;p class=&quot;og-title&quot;&gt;[DB] PostgreSQL + Docker TimeZone 설정하기&lt;/p&gt;&lt;p class=&quot;og-desc&quot;&gt;해당 글에서는 PostgreSQL + Docker 환경에서 TimeZone을 지정했던 방법에 대해서 공유합니다. 1) Coordinated Universal Time (UTC) / TimeZone1. Coordinated Universal Time (UTC)  Coordinated Universal Time (UTC)- 전 세계 시간대 &lt;/p&gt;&lt;p class=&quot;og-host&quot;&gt;adjh54.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;h2 data-ke-size=&quot;size26&quot;&gt;2) API 배포(Spring Boot)&lt;/h2&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. DockerFile 구성&lt;/h3&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot;&gt;&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  DockerFile 구성&lt;/b&gt;&lt;br&gt;&lt;br&gt;&lt;b&gt;- 루트 경로에 소스코드를 이미지화시키기 위한 Dockerfile을 구성합니다.&lt;/b&gt;&lt;/blockquote&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;482&quot; data-origin-height=&quot;479&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bC1TTf/dJMcaaZEzEx/Fjgmm0KoZKz7eCLrBHnD10/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bC1TTf/dJMcaaZEzEx/Fjgmm0KoZKz7eCLrBHnD10/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bC1TTf/dJMcaaZEzEx/Fjgmm0KoZKz7eCLrBHnD10/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbC1TTf%2FdJMcaaZEzEx%2FFjgmm0KoZKz7eCLrBHnD10%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;482&quot; height=&quot;479&quot; data-origin-width=&quot;482&quot; data-origin-height=&quot;479&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  Dockerfile 파일 설명&lt;/b&gt;&lt;br&gt;&lt;b&gt;&lt;br&gt;&lt;/b&gt;&lt;b&gt;1. FROM amazoncorretto:21-alpine&lt;/b&gt;&lt;br&gt;- Amazon이 만든 JDK 21으로 지정을 합니다.&lt;br&gt;&lt;br&gt;&lt;b&gt;2. WORKDIR /app&lt;/b&gt;&lt;br&gt;- 컨테이너 안에서 작업 디렉터리를 /app으로 설정합니다.&lt;br&gt;&lt;br&gt;&lt;b&gt;3. COPY build/libs/xxxxxAPI-0.0.1-SNAPSHOT.jar app.jar&lt;/b&gt;&lt;br&gt;- 로컬(내 PC 또는 CI 서버)의 Gradle 빌드 결과물을 컨테이너 안으로 복사합니다.&lt;br&gt;&lt;br&gt;&lt;b&gt;4. EXPOSE 8001&lt;/b&gt;&lt;br&gt;- 컨테이너가 8001 포트를 사용한다고 선언합니다.&lt;br&gt;&lt;br&gt;&lt;b&gt;5. ENTRYPOINT [&quot;java&quot;, &quot;-jar&quot;, &quot;app.jar&quot;]&lt;/b&gt;&lt;br&gt;- 컨테이너 시작 시 실행할 명령어. java -jar app.jar를 실행해서 Spring Boot 앱을 구동함.&lt;/blockquote&gt;&lt;pre class=&quot;dockerfile&quot;&gt;&lt;code&gt;# 1
FROM amazoncorretto:21-alpine

# 2
WORKDIR /app

# 3
COPY build/libs/xxxxxAPI-0.0.1-SNAPSHOT.jar app.jar

# 4
EXPOSE 8001

# 5
ENTRYPOINT [&quot;java&quot;, &quot;-jar&quot;, &quot;app.jar&quot;]
&lt;/code&gt;&lt;/pre&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;h3 data-ke-size=&quot;size23&quot;&gt;2.&amp;nbsp;build-and-export.sh&amp;nbsp;구성&lt;/h3&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot;&gt;&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  build-and-export.sh&amp;nbsp;구성&lt;/b&gt;&lt;br&gt;&lt;br&gt;&lt;b&gt;- Gradle 빌드 → DockerFile 이미지 빌드 → tar 파일로 export까지 수행하는 Shell 파일입니다.&lt;/b&gt;&lt;/blockquote&gt;&lt;pre class=&quot;bash&quot;&gt;&lt;code&gt;#!/bin/bash
# build-and-export.sh

# 1. Gradle 빌드
./gradlew clean build -x test

# 2. Docker 이미지 빌드
docker build --platform linux/amd64 -t xxx-api .

# 3. tar로 export
docker save -o ~/Desktop/docker/xxx-api.tar xxx-api

echo &quot;완료: xxx-api.tar 생성됨&quot;

&lt;/code&gt;&lt;/pre&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. shell 파일 실행&lt;/h3&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot;&gt;&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  shell 파일 실행&lt;/b&gt;&lt;br&gt;&lt;br&gt;- Gradle 빌드 → DockerFile 이미지 빌드 → tar 파일로 export까지 수행을 하는 과정입니다.&lt;/blockquote&gt;&lt;pre class=&quot;cpp&quot;&gt;&lt;code&gt;# shell 파일 실행
$ ./build-and-export.sh
&lt;/code&gt;&lt;/pre&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1820&quot; data-origin-height=&quot;522&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cXz8Y2/dJMcahErQRm/cxvbunCNdS0ujBpk8m9Qq0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cXz8Y2/dJMcahErQRm/cxvbunCNdS0ujBpk8m9Qq0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cXz8Y2/dJMcahErQRm/cxvbunCNdS0ujBpk8m9Qq0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcXz8Y2%2FdJMcahErQRm%2FcxvbunCNdS0ujBpk8m9Qq0%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;1820&quot; height=&quot;522&quot; data-origin-width=&quot;1820&quot; data-origin-height=&quot;522&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  [참고] &lt;/b&gt;&lt;b&gt;Docker Desktop이 실행이 되었는지 확인을 합니다. 아래와 같이 오류가 발생함.&lt;/b&gt;&lt;br&gt;&lt;br&gt;- Consider enabling configuration cache to speed up this build: https://docs.gradle.org/9.4.1/userguide/configuration_cache_enabling.html ERROR: Cannot connect to the Docker daemon at unix:///Users/leejonghoon/.docker/run/docker.sock. Is the docker daemon running? Cannot connect to the Docker daemon at unix:///Users/leejonghoon/.docker/run/docker.sock. Is the docker daemon running?&lt;br&gt;&lt;br&gt;&lt;b&gt;- Docker Desktop을 실행시키면 됩니다.&lt;/b&gt;&lt;/blockquote&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1105&quot; data-origin-height=&quot;114&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bl6ebe/dJMcabxtPOo/MG3ppTWQ1khoUSjByZW7k1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bl6ebe/dJMcabxtPOo/MG3ppTWQ1khoUSjByZW7k1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bl6ebe/dJMcabxtPOo/MG3ppTWQ1khoUSjByZW7k1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbl6ebe%2FdJMcabxtPOo%2FMG3ppTWQ1khoUSjByZW7k1%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;1105&quot; height=&quot;114&quot; data-origin-width=&quot;1105&quot; data-origin-height=&quot;114&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;h3 data-ke-size=&quot;size23&quot;&gt;4. 아래와 같이 tar 파일이 생성되었습니다.&lt;/h3&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1229&quot; data-origin-height=&quot;514&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Q7MMx/dJMcacXtcuA/g9DpVbQjUtFqYyNuykCLQ0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Q7MMx/dJMcacXtcuA/g9DpVbQjUtFqYyNuykCLQ0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Q7MMx/dJMcacXtcuA/g9DpVbQjUtFqYyNuykCLQ0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FQ7MMx%2FdJMcacXtcuA%2Fg9DpVbQjUtFqYyNuykCLQ0%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;1229&quot; height=&quot;514&quot; data-origin-width=&quot;1229&quot; data-origin-height=&quot;514&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;h3 data-ke-size=&quot;size23&quot;&gt;5. [Termius] 연결된 리눅스 서버에 SFTP로 해당 파일을 옮겨둡니다&lt;/h3&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2032&quot; data-origin-height=&quot;1078&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/RmKTu/dJMcahRZzVW/Q4vnzGRFOAntlccglpQZ70/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/RmKTu/dJMcahRZzVW/Q4vnzGRFOAntlccglpQZ70/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/RmKTu/dJMcahRZzVW/Q4vnzGRFOAntlccglpQZ70/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FRmKTu%2FdJMcahRZzVW%2FQ4vnzGRFOAntlccglpQZ70%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2032&quot; height=&quot;1078&quot; data-origin-width=&quot;2032&quot; data-origin-height=&quot;1078&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;h3 data-ke-size=&quot;size23&quot;&gt;6. [Termius] tar 파일을 로드해 옵니다.&lt;/h3&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;$ docker load -i xxx-api.tar
&lt;/code&gt;&lt;/pre&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;758&quot; data-origin-height=&quot;108&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dGBsL0/dJMb99TYR5W/Ma5hiAjGLqUwLnL7lDkVOK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dGBsL0/dJMb99TYR5W/Ma5hiAjGLqUwLnL7lDkVOK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dGBsL0/dJMb99TYR5W/Ma5hiAjGLqUwLnL7lDkVOK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdGBsL0%2FdJMb99TYR5W%2FMa5hiAjGLqUwLnL7lDkVOK%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;758&quot; height=&quot;108&quot; data-origin-width=&quot;758&quot; data-origin-height=&quot;108&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;h3 data-ke-size=&quot;size23&quot;&gt;7. [Termius] Docker 컨테이너 배포 완료&lt;/h3&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot;&gt;&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  [Termius] Docker 컨테이너 배포 완료&lt;/b&gt;&lt;br&gt;&lt;br&gt;&lt;b&gt;- Docker 컨테이너를 재 실행하여 정상적으로 소스코드 반영이 완료되었습니다.&lt;/b&gt;&lt;/blockquote&gt;&lt;pre class=&quot;ebnf&quot;&gt;&lt;code&gt;docker compose up -d
&lt;/code&gt;&lt;/pre&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1592&quot; data-origin-height=&quot;734&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/SmgO5/dJMcabK5MV3/iN1nclAtzk403stg4quBo1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/SmgO5/dJMcabK5MV3/iN1nclAtzk403stg4quBo1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/SmgO5/dJMcabK5MV3/iN1nclAtzk403stg4quBo1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FSmgO5%2FdJMcabK5MV3%2FiN1nclAtzk403stg4quBo1%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;1592&quot; height=&quot;734&quot; data-origin-width=&quot;1592&quot; data-origin-height=&quot;734&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;h2 data-ke-size=&quot;size26&quot;&gt;3) 화면 배포(React)&lt;/h2&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. DockerFile&lt;/h3&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot;&gt;&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  DockerFile 구성&lt;/b&gt;&lt;br&gt;&lt;br&gt;&lt;b&gt;- 루트 경로에 소스코드를 이미지화 시키기 위한 Dockerfile을 구성합니다.&lt;/b&gt;&lt;/blockquote&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;378&quot; data-origin-height=&quot;566&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/6buiU/dJMcahYMdjK/Wk6LWxTA5MHCecTnDTn451/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/6buiU/dJMcahYMdjK/Wk6LWxTA5MHCecTnDTn451/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/6buiU/dJMcahYMdjK/Wk6LWxTA5MHCecTnDTn451/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F6buiU%2FdJMcahYMdjK%2FWk6LWxTA5MHCecTnDTn451%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;378&quot; height=&quot;566&quot; data-origin-width=&quot;378&quot; data-origin-height=&quot;566&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  Dockerfile 파일 설명&lt;/b&gt;&lt;br&gt;&lt;b&gt;&lt;br&gt;&lt;/b&gt;&lt;b&gt;1. FROM node:24.15.0-alpine&lt;/b&gt;&lt;br&gt;- Node.js 24.15.0 기반의 Alpine Linux 이미지 사용하며, Alpine은 경량 Linux 배포판으로 이미지 크기를 최소화하였습니다&lt;br&gt;&lt;b&gt;&lt;br&gt;&lt;/b&gt;&lt;b&gt;2. WORKDIR /app&lt;/b&gt;&lt;br&gt;- 컨테이너 안에서 작업 디렉토리를 /app으로 설정합니다.&lt;br&gt;&lt;br&gt;&lt;b&gt;3. RUN yarn global add serve&lt;/b&gt;&lt;br&gt;- 정적 파일 서버인 serve 패키지를 전역 설치하며, React 빌드 결과물(정적 HTML/JS/CSS)을 HTTP로 서빙하는 역할을 수행합니다.&lt;br&gt;&lt;b&gt;&lt;br&gt;&lt;/b&gt;&lt;b&gt;4. COPY dist/ ./dist/&lt;/b&gt;&lt;br&gt;- 로컬 머신의 dist/ 폴더(빌드 결과물)를 컨테이너의 /app/dist/로 복사합니다.&lt;br&gt;&lt;b&gt;&lt;br&gt;&lt;/b&gt;&lt;b&gt;5. EXPOSE 80&lt;/b&gt;&lt;br&gt;- 컨테이너가 80번 포트를 사용한다고 선언합니다&lt;br&gt;&lt;br&gt;&lt;b&gt;6. CMD [&quot;serve&quot;, &quot;-s&quot;, &quot;dist&quot;, &quot;-l&quot;, &quot;80&quot;]&lt;/b&gt;&lt;br&gt;- dist 폴더를 80번 포트로 웹서버처럼 서빙하도록 구성&lt;/blockquote&gt;&lt;pre class=&quot;dockerfile&quot;&gt;&lt;code&gt;# 1
FROM node:24.15.0-alpine

# 2
WORKDIR /app

# 3
RUN yarn global add serve

# 4
COPY dist/ ./dist/

# 5
EXPOSE 80

# 6
CMD [&quot;serve&quot;, &quot;-s&quot;, &quot;dist&quot;, &quot;-l&quot;, &quot;80&quot;]
&lt;/code&gt;&lt;/pre&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. package.json 내에 스크립트 구성&lt;/h3&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot;&gt;&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  package.json 내에 스크립트 구성&lt;/b&gt;&lt;br&gt;&lt;br&gt;&lt;b&gt;- 패키지 라이브러리를 기반으로 스크립트를 수행하도록 하면, 긴 수행 과정을 하나의 명령어로 처리할 수 있습니다.&lt;/b&gt;&lt;br&gt;- 4개 명령어를 &amp;amp;&amp;amp;로 순서대로 실행하는 npm 스크립트이며, 하나라도 실패하면 중단이 됩니다.&lt;/blockquote&gt;&lt;pre class=&quot;scilab&quot;&gt;&lt;code&gt;&quot;scripts&quot;: {
 &quot;build:docker&quot;: 
 &quot;vite build --mode production &amp;amp;&amp;amp; docker build --platform linux/amd64 -f Dockerfile -t xxx-xxx-web . &amp;amp;&amp;amp; docker save -o /Users/leejonghoon/Desktop/docker/xxx-xxx-web.tar xxx-xxx-web &amp;amp;&amp;amp; echo '완료: xxx-xxx-web.tar 생성됨'&quot;,
}
&lt;/code&gt;&lt;/pre&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  1단계&lt;/b&gt;&lt;br&gt;&lt;br&gt;&lt;b&gt;- 소스코드를 빌드하는 과정입니다.&lt;/b&gt; &lt;br&gt;- Vite로 프로덕션 빌드를 수행하며, .env.production 환경변수 적용해서 dist/ 폴더를 생성합니다&lt;/blockquote&gt;&lt;pre class=&quot;ada&quot;&gt;&lt;code&gt;vite build --mode production
&lt;/code&gt;&lt;/pre&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  2단계&lt;/b&gt;&lt;br&gt;&lt;b&gt;&lt;br&gt;&lt;/b&gt;&lt;b&gt;- 빌드된 소스코드를 컨테이너 이미지화 하는 과정입니다.&lt;/b&gt;&lt;br&gt;1. —platform linux/amd64 : 플랫폼을 linux/amd64로 지정&lt;br&gt;2. -f Dockerfile : 사용할 Dockerfile 지정&lt;br&gt;3. -t xxx-xxx-web: 컨테이너 이미지 이름 태그&lt;br&gt;4. . : 빌드 컨텍스트(현재 디렉토리)&lt;/blockquote&gt;&lt;pre class=&quot;ada&quot;&gt;&lt;code&gt;docker build --platform linux/amd64 -f Dockerfile -t xxx-xxx-web .
&lt;/code&gt;&lt;/pre&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  3단계&lt;/b&gt;&lt;br&gt;&lt;b&gt;&lt;br&gt;&lt;/b&gt;&lt;b&gt;- 컨테이너 이미지를 .tar 파일로 내보내서 내 로컬에 저장하는 과정입니다.&lt;/b&gt;&lt;br&gt;&lt;br&gt;1. docker save : 도커 이미지를 .tar 파일로 내보내기&lt;br&gt;2. o : 출력 파일 경로 (바탕화면 docker 폴더)&lt;br&gt;&lt;br&gt;- 이 tar 파일을 서버에 SCP로 전송해서 docker load로 불러올 수 있음&lt;/blockquote&gt;&lt;pre class=&quot;dts&quot;&gt;&lt;code&gt;docker save -o /Users/leejonghoon/Desktop/docker/xxx-xxx-web.tar xxx-xxx-web
&lt;/code&gt;&lt;/pre&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  4단계&lt;/b&gt;&lt;br&gt;&lt;br&gt;&lt;b&gt;- 모든 과정이 성공적으로 끝났을 때 완료 메시지 출력.&lt;/b&gt;&lt;/blockquote&gt;&lt;pre class=&quot;bash&quot;&gt;&lt;code&gt;echo '완료: dkcns-homepage-web.tar 생성됨'
&lt;/code&gt;&lt;/pre&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. 명령어 실행&lt;/h3&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot;&gt;&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  명령어 실행&lt;/b&gt;&lt;br&gt;&lt;br&gt;&lt;b&gt;- 명령어가 실행이 되었고, 아래와 같이 파일이 생성이 되었습니다&lt;/b&gt;&lt;/blockquote&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;842&quot; data-origin-height=&quot;691&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bPNQ0V/dJMcahLgpgv/qn8CSmqy4I7sndgyjw1O1K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bPNQ0V/dJMcahLgpgv/qn8CSmqy4I7sndgyjw1O1K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bPNQ0V/dJMcahLgpgv/qn8CSmqy4I7sndgyjw1O1K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbPNQ0V%2FdJMcahLgpgv%2Fqn8CSmqy4I7sndgyjw1O1K%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;842&quot; height=&quot;691&quot; data-origin-width=&quot;842&quot; data-origin-height=&quot;691&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1465&quot; data-origin-height=&quot;499&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nxYwY/dJMcahLgpgU/am0wChak1DKhAkfNIFqrg1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nxYwY/dJMcahLgpgU/am0wChak1DKhAkfNIFqrg1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nxYwY/dJMcahLgpgU/am0wChak1DKhAkfNIFqrg1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnxYwY%2FdJMcahLgpgU%2Fam0wChak1DKhAkfNIFqrg1%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;1465&quot; height=&quot;499&quot; data-origin-width=&quot;1465&quot; data-origin-height=&quot;499&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;h3 data-ke-size=&quot;size23&quot;&gt;4. 아래와 같이 tar 파일이 생성되었습니다.&lt;/h3&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1304&quot; data-origin-height=&quot;522&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cxzkHx/dJMcaiDn80O/sLqkKSKFiTmNS1YQm0rqsK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cxzkHx/dJMcaiDn80O/sLqkKSKFiTmNS1YQm0rqsK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cxzkHx/dJMcaiDn80O/sLqkKSKFiTmNS1YQm0rqsK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcxzkHx%2FdJMcaiDn80O%2FsLqkKSKFiTmNS1YQm0rqsK%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;1304&quot; height=&quot;522&quot; data-origin-width=&quot;1304&quot; data-origin-height=&quot;522&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;h3 data-ke-size=&quot;size23&quot;&gt;5. [Termius] 연결된 리눅스 서버에 SFTP로 해당 파일을 옮겨둡니다&lt;/h3&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2032&quot; data-origin-height=&quot;1080&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/GTnDY/dJMcaak2EQa/U2npHfxQJCJMgvkZJxwsI1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/GTnDY/dJMcaak2EQa/U2npHfxQJCJMgvkZJxwsI1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/GTnDY/dJMcaak2EQa/U2npHfxQJCJMgvkZJxwsI1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FGTnDY%2FdJMcaak2EQa%2FU2npHfxQJCJMgvkZJxwsI1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2032&quot; height=&quot;1080&quot; data-origin-width=&quot;2032&quot; data-origin-height=&quot;1080&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;h3 data-ke-size=&quot;size23&quot;&gt;6. [Termius] tar 파일을 로드해 옵니다.&lt;/h3&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;pre class=&quot;shell&quot; data-ke-language=&quot;shell&quot;&gt;&lt;code&gt;$ docker load -i xxx-xxx-web.tar&lt;/code&gt;&lt;/pre&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2032&quot; data-origin-height=&quot;1080&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cyt739/dJMcadILhI7/jzOUMtEK7FxyHB4Kzq5YI0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cyt739/dJMcadILhI7/jzOUMtEK7FxyHB4Kzq5YI0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cyt739/dJMcadILhI7/jzOUMtEK7FxyHB4Kzq5YI0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcyt739%2FdJMcadILhI7%2FjzOUMtEK7FxyHB4Kzq5YI0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2032&quot; height=&quot;1080&quot; data-origin-width=&quot;2032&quot; data-origin-height=&quot;1080&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;h3 data-ke-size=&quot;size23&quot;&gt;7. [Termius] Docker 컨테이너 배포 완료&lt;/h3&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot;&gt;&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  [Termius] Docker 컨테이너 배포 완료&lt;/b&gt;&lt;br&gt;&lt;br&gt;&lt;b&gt;- Docker 컨테이너를 재 실행하여 정상적으로 소스코드 반영이 완료되었습니다.&lt;/b&gt;&lt;/blockquote&gt;&lt;pre class=&quot;ebnf&quot;&gt;&lt;code&gt;docker compose up -d
&lt;/code&gt;&lt;/pre&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2032&quot; data-origin-height=&quot;1080&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bV4h8Y/dJMcajvwxTs/zmnUFL4EL2wwXD2pJISgZ0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bV4h8Y/dJMcajvwxTs/zmnUFL4EL2wwXD2pJISgZ0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bV4h8Y/dJMcajvwxTs/zmnUFL4EL2wwXD2pJISgZ0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbV4h8Y%2FdJMcajvwxTs%2FzmnUFL4EL2wwXD2pJISgZ0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2032&quot; height=&quot;1080&quot; data-origin-width=&quot;2032&quot; data-origin-height=&quot;1080&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&lt;b&gt;오늘도&amp;nbsp;감사합니다.&amp;nbsp; &lt;/b&gt;&lt;/p&gt;</description>
      <category>공통/CI CD</category>
      <category>CI/CD</category>
      <category>CI/CD SFTP 전송방식</category>
      <category>Docker Compose SFTP 배포</category>
      <category>Docker Compose 배포</category>
      <category>Docker tar</category>
      <category>Docker tar 파일 전송</category>
      <category>docker 배포</category>
      <category>SFTP Docker 전송 방식</category>
      <author>adjh54</author>
      <guid isPermaLink="true">https://adjh54.tistory.com/759</guid>
      <comments>https://adjh54.tistory.com/759#entry759comment</comments>
      <pubDate>Wed, 20 May 2026 19:10:47 +0900</pubDate>
    </item>
    <item>
      <title>[제작 앱 소개] 순픽: 순우리말 퀴즈</title>
      <link>https://adjh54.tistory.com/750</link>
      <description>&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;해당&amp;nbsp;글에서는&amp;nbsp;Contributor9&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;&lt;/span&gt;
&lt;figure contenteditable=&quot;false&quot; data-ke-type=&quot;emoticon&quot; data-ke-align=&quot;alignCenter&quot; data-emoticon-type=&quot;face&quot; data-emoticon-name=&quot;062&quot; data-emoticon-isanimation=&quot;false&quot; data-emoticon-src=&quot;https://t1.daumcdn.net/axz_keditor/emoticon/face/large/062.png&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/axz_keditor/emoticon/face/large/062.png&quot; width=&quot;80&quot; /&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1)&amp;nbsp;개발자는&amp;nbsp;왜&amp;nbsp;이&amp;nbsp;앱을&amp;nbsp;만들었을까?&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  개발자는 왜 이 앱을 만들었을까?&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- &quot;이 단어는 당연히 순우리말이지?&quot;라고 생각했다가 알고 보니 한자어였던 경험, 한 번쯤 있으시죠?&lt;br /&gt;&lt;/b&gt; &lt;br /&gt;- 윤슬, 가람, 늘봄, 단비. 처음 들어봤을수도 있고 어딘가에서 한 번은 들어봤을지도 모릅니다. 이 단어들은 아름다운 순우리말들입니다.&lt;br /&gt;&lt;br /&gt;- 한국어의 60~70%는 한자어로 이루어져 있으며, 학술&amp;middot;의학&amp;middot;법률 분야에서는 그 비중이 90% 이상에 달한다고 합니다. &lt;br /&gt;그렇다면 고유어, 즉 순우리말의 비중은 얼마나 될까요? 약 20~30% 수준에 불과하다고 합니다.&lt;br /&gt;&lt;br /&gt;- 잊혀져가는 아름다운 순우리말들을 기억해 보는 건 어떨까요?&amp;nbsp; 일상 속에서 문득 &amp;ldquo;이 말이 순우리말이었어?&amp;rdquo; 싶은 순간을 마주칠 때면, 작은 발견의 즐거움이 찾을 수 있습니다.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 우리말의 아름다움과 뿌리에 관심 있는 분들께 꼭 한번 들여다보시길 권합니다.&lt;/b&gt;&lt;/blockquote&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;2632&quot; data-origin-height=&quot;550&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/OkxZc/dJMcabYkjkm/a8Te7K2TeBXpyb8BX8ztk1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/OkxZc/dJMcabYkjkm/a8Te7K2TeBXpyb8BX8ztk1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/OkxZc/dJMcabYkjkm/a8Te7K2TeBXpyb8BX8ztk1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FOkxZc%2FdJMcabYkjkm%2Fa8Te7K2TeBXpyb8BX8ztk1%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;2632&quot; height=&quot;550&quot; data-origin-width=&quot;2632&quot; data-origin-height=&quot;550&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;h2 data-ke-size=&quot;size26&quot;&gt;2)&amp;nbsp;앱 소개- 순픽: 순우리말 퀴즈&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2610&quot; data-origin-height=&quot;520&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rppAJ/dJMb997gFDO/StgH4T2chGXhDTUEJr31xk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rppAJ/dJMb997gFDO/StgH4T2chGXhDTUEJr31xk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rppAJ/dJMb997gFDO/StgH4T2chGXhDTUEJr31xk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FrppAJ%2FdJMb997gFDO%2FStgH4T2chGXhDTUEJr31xk%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;2610&quot; height=&quot;520&quot; data-origin-width=&quot;2610&quot; data-origin-height=&quot;520&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  앱 소개- 순픽: 순우리말 퀴즈&lt;br /&gt;&lt;br /&gt;- 순픽: 순우리말 퀴즈는 우리말의 아름다운 순우리말 표현들을 쉽고 재미있게 학습할 수 있는 학습형 퀴즈 앱입니다.&lt;br /&gt;&lt;br /&gt;&lt;/b&gt;-&amp;nbsp;500개의&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;학습할&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;-&amp;nbsp;학습&amp;nbsp;&amp;rarr;&amp;nbsp;퀴즈&amp;nbsp;&amp;rarr;&amp;nbsp;복습&amp;nbsp;&amp;rarr;&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;&lt;b&gt;&lt;br /&gt;  홈 화면&lt;/b&gt;&lt;br /&gt;- 점수별 캐릭터, 타이틀, 획득한 뱃지를 한눈에 확인 '퀴즈 시작', '학습 모드', '오답 복습', &amp;lsquo;타임 챌린지&amp;rsquo;, &amp;lsquo;순우리말 이름들&amp;rsquo; 주요 기능으로 빠르게 진입 '숨겨진 뱃지를 찾아보세요'를 통해 획득한 뱃지와 아직 잠금 해제되지 않은 뱃지 정보 확인 &amp;lsquo;오늘의 출석 확인&amp;rsquo;을 통해 출석한 날짜를 확인 &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;  학습 모드 - 카드 형식으로 맞춤법과 예문을 통해 쉽게 학습&lt;/b&gt;&lt;br /&gt;- 난이도, 카테고리, 학습 상태별 필터 기능 제공 초급부터 특급까지 단계별로 맞춤법을 체계적으로 익힐 수 있음 &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;  퀴즈 모드&lt;/b&gt;&lt;br /&gt;- 뜻 맞추기, 순우리말 찾기, 종합모드(뜻+순우리말) 퀴즈 지원 난이도 및 카테고리별로 문제 선택 가능 &lt;br /&gt;&lt;br /&gt;&lt;b&gt;  퀴즈&lt;/b&gt;&lt;br /&gt;- 4지선다형 객관식 문제 + 30초 제한 시간 전구 버튼을 통해 현재 문제의 힌트 제공 문제에 대한 정답/오답 시 문제 풀이 제공&lt;br /&gt;&lt;br /&gt;&lt;b&gt;  오답 복습 - 틀린 문제만 모아 반복 문제 및 풀이 제공&lt;/b&gt;&lt;br /&gt;- 정답을 맞히면 자동으로 오답 목록에서 제거되어 효율적인 복습 가능 &lt;br /&gt;&lt;br /&gt;&lt;b&gt;  타임 챌린지 - 180초 동안 제한 시간 내에 최대한 많은 문제 풀기 도전&lt;/b&gt;&lt;br /&gt;- 속도와 정확도를 겨루며 나만의 최고 점수에 도전 콤보, 점수, 랭킹 시스템으로 긴장감 넘치는 재미 제공 &lt;br /&gt;&lt;br /&gt;&lt;b&gt;  나의 활동 - 학습 완료 개수, 마지막 학습일 등 학습 활동 이력 제공&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;- 퀴즈 진행 횟수, 최고 콤보, 정답률 등 퀴즈 통계 확인 점수에 따라 변화하는 캐릭터와 타이틀, 획득한 뱃지 목록 및 해제 조건 제공 &lt;br /&gt;&lt;br /&gt;&lt;b&gt;  설정 - 학습 및 퀴즈 기록 초기화 기능&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;- 개인정보 처리방침, 이용약관, 오픈소스 라이브러리 확인 현재 앱 버전 및 제작자 정보 제공 앱을 처음처럼 리셋하여 새롭게 시작 가능 &lt;br /&gt;&lt;br /&gt;&lt;b&gt;  이런 분께 추천합니다&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;- 잊혀져가는 아름다운 순우리말을 배우고 싶은 분 &lt;br /&gt;- '미쁘다', '오롯이', '애틋하다' 같은 우리말의 뜻을 알고 싶은 분 &lt;br /&gt;- 일상에서 쓸 수 있는 고운 우리말을 익히고 싶은 분 &lt;br /&gt;- 게임하듯 퀴즈 풀며 순우리말 실력을 키우고 싶은 분 &lt;br /&gt;- 순우리말 이름의 의미를 찾아보고 싶은 분 &lt;br /&gt;- 타임 챌린지로 긴장감 있게 학습하고 싶은 분 &lt;br /&gt;- 우리말의 아름다움과 깊이를 발견하고 싶은 분 뱃지 수집하며 성취감을 느끼고 싶은 분&lt;br /&gt;- 어휘력을 풍성하게 가꾸고 싶은 분 출석 체크로 꾸준한 학습 습관을 만들고 싶은 분 &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;지금 다운로드하고 순우리말 마스터에 도전하세요! 우리말의 아름다움을 발견하는 여정이 지금 시작됩니다!  ✨ &lt;/b&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2580&quot; data-origin-height=&quot;926&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/6hYNt/dJMcad2QxQ1/dDuDlcr5dKgUqVpNyKQWh1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/6hYNt/dJMcad2QxQ1/dDuDlcr5dKgUqVpNyKQWh1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/6hYNt/dJMcad2QxQ1/dDuDlcr5dKgUqVpNyKQWh1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F6hYNt%2FdJMcad2QxQ1%2FdDuDlcr5dKgUqVpNyKQWh1%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;2580&quot; height=&quot;926&quot; data-origin-width=&quot;2580&quot; data-origin-height=&quot;926&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2584&quot; data-origin-height=&quot;974&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/n4ufH/dJMcacwa4f5/DOq9ggFZIqczZkj7CCqVGk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/n4ufH/dJMcacwa4f5/DOq9ggFZIqczZkj7CCqVGk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/n4ufH/dJMcacwa4f5/DOq9ggFZIqczZkj7CCqVGk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fn4ufH%2FdJMcacwa4f5%2FDOq9ggFZIqczZkj7CCqVGk%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;2584&quot; height=&quot;974&quot; data-origin-width=&quot;2584&quot; data-origin-height=&quot;974&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;h2 data-ke-size=&quot;size26&quot;&gt;3) 다운로드&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. Google Play Store&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&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;450&quot; data-origin-height=&quot;450&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b9KZrD/dJMcabjKerB/7ygNxJApHkJo51mgHRi4P1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b9KZrD/dJMcabjKerB/7ygNxJApHkJo51mgHRi4P1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b9KZrD/dJMcabjKerB/7ygNxJApHkJo51mgHRi4P1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb9KZrD%2FdJMcabjKerB%2F7ygNxJApHkJo51mgHRi4P1%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;450&quot; height=&quot;450&quot; data-origin-width=&quot;450&quot; data-origin-height=&quot;450&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;figure id=&quot;og_1777368652650&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;순픽: 순우리말 퀴즈 - Google Play 앱&quot; data-og-description=&quot;순우리말을 게임처럼 배우는 퀴즈 학습 앱! 뱃지, 레벨, 타임 챌린지로 재미있게 어휘력 UP!&quot; data-og-host=&quot;play.google.com&quot; data-og-source-url=&quot;https://play.google.com/store/apps/details?id=com.tha.purekoreanquiz&quot; data-og-url=&quot;https://play.google.com/store/apps/details?id=com.tha.purekoreanquiz&amp;amp;hl=ko&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/baZXLO/dJMb8UHSd4g/8abh83K4t12H5Cih8mqGy1/img.png?width=512&amp;amp;height=512&amp;amp;face=394_227_467_307,https://scrap.kakaocdn.net/dn/IADf7/dJMb9cBK7VT/uGx4JBKLUe5tPu6mhYDdJk/img.png?width=600&amp;amp;height=300&amp;amp;face=379_129_423_177,https://scrap.kakaocdn.net/dn/bhzaze/dJMb9eTSHdw/ib3FNkt7sEkkkcW1OfchI1/img.png?width=240&amp;amp;height=240&amp;amp;face=185_106_215_139&quot;&gt;&lt;a href=&quot;https://play.google.com/store/apps/details?id=com.tha.purekoreanquiz&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://play.google.com/store/apps/details?id=com.tha.purekoreanquiz&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/baZXLO/dJMb8UHSd4g/8abh83K4t12H5Cih8mqGy1/img.png?width=512&amp;amp;height=512&amp;amp;face=394_227_467_307,https://scrap.kakaocdn.net/dn/IADf7/dJMb9cBK7VT/uGx4JBKLUe5tPu6mhYDdJk/img.png?width=600&amp;amp;height=300&amp;amp;face=379_129_423_177,https://scrap.kakaocdn.net/dn/bhzaze/dJMb9eTSHdw/ib3FNkt7sEkkkcW1OfchI1/img.png?width=240&amp;amp;height=240&amp;amp;face=185_106_215_139');&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;순픽: 순우리말 퀴즈 - Google Play 앱&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;순우리말을 게임처럼 배우는 퀴즈 학습 앱! 뱃지, 레벨, 타임 챌린지로 재미있게 어휘력 UP!&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;play.google.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;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. App Store&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&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;490&quot; data-origin-height=&quot;490&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/DZqfk/dJMcaaE8pwm/tweRpc5WPi7ZIdNDotqQPK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/DZqfk/dJMcaaE8pwm/tweRpc5WPi7ZIdNDotqQPK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/DZqfk/dJMcaaE8pwm/tweRpc5WPi7ZIdNDotqQPK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FDZqfk%2FdJMcaaE8pwm%2FtweRpc5WPi7ZIdNDotqQPK%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;490&quot; height=&quot;490&quot; data-origin-width=&quot;490&quot; data-origin-height=&quot;490&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;figure id=&quot;og_1777368676023&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;순픽: 순우리말 퀴즈 앱 - App Store&quot; data-og-description=&quot;App&amp;nbsp;Store에서 EcodeLab의 순픽: 순우리말 퀴즈 앱을 다운로드하십시오. 스크린샷, 평가 및 리뷰, 사용자 팁 및 순픽: 순우리말 퀴즈 앱과 비슷한 다른 앱을 볼 수 있습니다.&quot; data-og-host=&quot;apps.apple.com&quot; data-og-source-url=&quot;https://apps.apple.com/kr/app/id6759251931&quot; data-og-url=&quot;https://apps.apple.com/kr/app/%EC%88%9C%ED%94%BD-%EC%88%9C%EC%9A%B0%EB%A6%AC%EB%A7%90-%ED%80%B4%EC%A6%88/id6759251931&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/blxxBr/dJMb8PGzcen/Xi2sBfu3HApCWWKXXCTiQK/img.jpg?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/Ttajj/dJMb8U8WEDh/fc8DTb7g5MLAkvlYcsow11/img.jpg?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630&quot;&gt;&lt;a href=&quot;https://apps.apple.com/kr/app/id6759251931&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://apps.apple.com/kr/app/id6759251931&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/blxxBr/dJMb8PGzcen/Xi2sBfu3HApCWWKXXCTiQK/img.jpg?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/Ttajj/dJMb8U8WEDh/fc8DTb7g5MLAkvlYcsow11/img.jpg?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;순픽: 순우리말 퀴즈 앱 - App Store&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;App&amp;nbsp;Store에서 EcodeLab의 순픽: 순우리말 퀴즈 앱을 다운로드하십시오. 스크린샷, 평가 및 리뷰, 사용자 팁 및 순픽: 순우리말 퀴즈 앱과 비슷한 다른 앱을 볼 수 있습니다.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;apps.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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;⬇️ 개발자의 다양한 앱을 보고 싶으시면 아래의 링크를 확인해주세요&lt;span&gt;&amp;nbsp;&lt;/span&gt;⬇️&lt;/b&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1779095407459&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;Ecodelab - 제작 앱 소개&quot; data-og-description=&quot;Ecodelab에서 개발한 다양한 앱들을 소개합니다&quot; data-og-host=&quot;www.ecodelab.im&quot; data-og-source-url=&quot;https://ecodelab.im/main&quot; data-og-url=&quot;https://www.ecodelab.im&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bWuJMc/dJMb9eTUZsH/njzMvre05hFxKg2BACFmIk/img.png?width=1024&amp;amp;height=1024&amp;amp;face=0_0_1024_1024,https://scrap.kakaocdn.net/dn/eR1YMg/dJMb8RRXk8e/RlDSZRgjj03yt5Wtc1PKLK/img.png?width=1024&amp;amp;height=1024&amp;amp;face=0_0_1024_1024&quot;&gt;&lt;a href=&quot;https://ecodelab.im/main&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://ecodelab.im/main&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bWuJMc/dJMb9eTUZsH/njzMvre05hFxKg2BACFmIk/img.png?width=1024&amp;amp;height=1024&amp;amp;face=0_0_1024_1024,https://scrap.kakaocdn.net/dn/eR1YMg/dJMb8RRXk8e/RlDSZRgjj03yt5Wtc1PKLK/img.png?width=1024&amp;amp;height=1024&amp;amp;face=0_0_1024_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;Ecodelab - 제작 앱 소개&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Ecodelab에서 개발한 다양한 앱들을 소개합니다&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.ecodelab.im&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;오늘도&amp;nbsp;감사합니다.&amp;nbsp; &lt;/b&gt;&lt;/p&gt;</description>
      <category>Contributor9/제작 앱 소개</category>
      <category>순우리말</category>
      <category>순우리말 앱</category>
      <category>순우리말 퀴즈 앱</category>
      <category>순우리말 한국어</category>
      <category>순한국어</category>
      <category>한국어 공부</category>
      <category>한국어 맞추기</category>
      <category>한국어 앱</category>
      <category>한국어 퀴즈</category>
      <category>한국어 퀴즈 앱</category>
      <author>adjh54</author>
      <guid isPermaLink="true">https://adjh54.tistory.com/750</guid>
      <comments>https://adjh54.tistory.com/750#entry750comment</comments>
      <pubDate>Mon, 18 May 2026 18:12:17 +0900</pubDate>
    </item>
    <item>
      <title>[제작 앱 소개] 맞픽: 맞춤법 퀴즈</title>
      <link>https://adjh54.tistory.com/726</link>
      <description>&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;해당&amp;nbsp;글에서는&amp;nbsp;Contributor9&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;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;
&lt;figure contenteditable=&quot;false&quot; data-ke-type=&quot;emoticon&quot; data-ke-align=&quot;alignCenter&quot; data-emoticon-type=&quot;face&quot; data-emoticon-name=&quot;062&quot; data-emoticon-isanimation=&quot;false&quot; data-emoticon-src=&quot;https://t1.daumcdn.net/keditor/emoticon/face/large/062.png&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/keditor/emoticon/face/large/062.png&quot; width=&quot;80&quot; /&gt;&lt;/figure&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1)&amp;nbsp;개발자는&amp;nbsp;왜&amp;nbsp;이&amp;nbsp;앱을&amp;nbsp;만들었을까?&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  개발자는 왜 이 앱을 만들었을까?&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;- &quot;이 단어 맞춤법이 이게 맞았나..?&quot;라고 생각해 보신 적 있으신가요? &lt;br /&gt;&lt;br /&gt;- 가끔 메신저를 이용하면서 이 단어가 맞았나 헷갈릴때가 여러 번 있었던 것 같습니다. &lt;br /&gt;그럴 때면 브라우저를 통해서 검색을 하고 맞는 맞춤법으로 수정하여 메신저를 보내고 했던 것 같습니다. &lt;br /&gt;&lt;br /&gt;이전에 이러한 기사를 본 기억이 있습니다 '이성에게서 호감이 떨어지는 순간 중 하나로 맞춤법을 제대로 이용하지 못하였을 때'에 '아.. 깬다'라고 생각이 드는 순간이 있다고 합니다.&lt;br /&gt;그리고 주변 지인중에 한 분이 '나이가 들수록 적어도 맞춤법은 틀리지 말아야겠다'라는 마음가짐을 가지신 분을 보면서 맞춤법을 쉽게 검색할 수 있고 퀴즈로 익힐 수 있는 앱을 만들어보자라는 생각을 하게 되었습니다.&lt;br /&gt;&lt;br /&gt;이 단어 맞춤법이 이게 맞았나?라고 생각이 들때, 준비된 '맞춤법 사전'에서 쉽게 검색할 수 있습니다.&lt;br /&gt;500개가량에 일상생활에서 헷갈리기 쉽고 자주 사용되는 단어들을 추렸습니다. 그리고 일상 대화의 예시들로 쉽게 이해하실 수 있습니다! 이러한 맞춤법 출처는 주요 정부 기관 내에서 직접 검증을 통한 명확한 맞춤법으로 제공을 합니다.&lt;br /&gt;&lt;br /&gt;내 맞춤법 실력이 궁금하지 않으신가요? 내가 이 단어가 맞다고 사용을 하지만, 막상 알고보니 잘못된 맞춤법으로 사용하고 있지는 않으신가요? 학습 &amp;gt; 퀴즈 &amp;gt; 오답복습 &amp;gt; 타임챌린지 단계로 선순환 방식으로 맞춤법을 한번 마스터해보세요! 귀여운 캐릭터도 얻어보고, 마지막 칭호인 &amp;lsquo;전설의 맞춤법 탐정&amp;rsquo; 타이틀을 얻어서 맞춤법 마스터를 이뤄보시면 내 삶에 큰 도움이 되실 겁니다.&lt;br /&gt;&lt;br /&gt;앞으로 많은 기능 업데이트와 데이터들을 추가할 예정이니 맞픽: 맞춤법 퀴즈와 함께 해보세요!&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;  이런 분께 추천합니다 &lt;br /&gt;&lt;br /&gt;- 헷갈리는 맞춤법을 쉽고 재미있게 배우고 싶은 분 &lt;br /&gt;- 단어와 문장 속 올바른 표기를 확실히 익히고 싶은 분 &lt;br /&gt;- 자주 틀리는 표현을 퀴즈로 반복 학습하고 싶은 분 &lt;br /&gt;- 오답 복습과 타임 챌린지로 완벽한 실력을 쌓고 싶은 학습자 &lt;br /&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2) 앱 소개 - 맞픽: 맞춤법 퀴즈&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1429&quot; data-origin-height=&quot;277&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bhvjhG/dJMcaiaGf4j/ByMHiLaJrOD4N8U06aYPMk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bhvjhG/dJMcaiaGf4j/ByMHiLaJrOD4N8U06aYPMk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bhvjhG/dJMcaiaGf4j/ByMHiLaJrOD4N8U06aYPMk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbhvjhG%2FdJMcaiaGf4j%2FByMHiLaJrOD4N8U06aYPMk%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;1429&quot; height=&quot;277&quot; data-origin-width=&quot;1429&quot; data-origin-height=&quot;277&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  앱 소개 - 맞픽: 맞춤법 퀴즈&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;-&amp;nbsp;한국어&amp;nbsp;맞춤법&amp;nbsp;중&amp;nbsp;헷갈리기&amp;nbsp;쉬운&amp;nbsp;다양한&amp;nbsp;표현들을&amp;nbsp;쉽고&amp;nbsp;재미있게&amp;nbsp;학습할&amp;nbsp;수&amp;nbsp;있는&amp;nbsp;학습형&amp;nbsp;퀴즈&amp;nbsp;앱입니다.&lt;br /&gt;- 500개의 헷갈리는 맞춤법 단어와 문장을 '학습 카드' 형식으로 학습하고, '맞춤법 퀴즈'를 통해 익힌 지식을 점검할 수 있습니다. 틀린 문제는 '오답 복습' 기능으로 반복 학습할 수 있으며, 마지막에는 '타임 챌린지' 모드를 통해 실력을 점검하면서 완벽한 맞춤법 마스터에 한 걸음 더 다가갈 수 있습니다.&lt;br /&gt;&lt;br /&gt;- 맞픽: 맞춤법 퀴즈는 한국어 맞춤법 중 헷갈리기 쉬운 다양한 표현들을 쉽고 재미있게 학습할 수 있는 학습형 퀴즈 앱**입니다.&lt;br /&gt;&amp;nbsp;-&amp;nbsp;500개의&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;복습''&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;&amp;rarr;&amp;nbsp;퀴즈&amp;nbsp;&amp;rarr;&amp;nbsp;복습&amp;nbsp;&amp;rarr;&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;  주요 기능&lt;br /&gt;&lt;br /&gt;  홈 화면&lt;br /&gt;-&amp;nbsp;점수별&amp;nbsp;캐릭터,&amp;nbsp;타이틀,&amp;nbsp;획득한&amp;nbsp;뱃지를&amp;nbsp;한눈에&amp;nbsp;확인&lt;br /&gt;-&amp;nbsp;'퀴즈&amp;nbsp;시작',&amp;nbsp;'학습&amp;nbsp;모드',&amp;nbsp;'오답&amp;nbsp;복습',&amp;nbsp;&amp;lsquo;타임&amp;nbsp;챌린지&amp;rsquo;&amp;nbsp;주요&amp;nbsp;기능으로&amp;nbsp;빠르게&amp;nbsp;진입&lt;br /&gt;-&amp;nbsp;'숨겨진&amp;nbsp;뱃지를&amp;nbsp;찾아보세요'를&amp;nbsp;통해&amp;nbsp;획득한&amp;nbsp;뱃지와&amp;nbsp;아직&amp;nbsp;잠금&amp;nbsp;해제되지&amp;nbsp;않은&amp;nbsp;뱃지&amp;nbsp;정보&amp;nbsp;확인&lt;br /&gt;-&amp;nbsp;&amp;lsquo;오늘의&amp;nbsp;출석&amp;nbsp;확인&amp;rsquo;을&amp;nbsp;통해&amp;nbsp;출석한&amp;nbsp;날짜를&amp;nbsp;확인&amp;nbsp;&lt;br /&gt;&lt;br /&gt;  학습 모드&lt;br /&gt;- 카드 형식으로 맞춤법과 예문을 통해 쉽게 학습&lt;br /&gt;-&amp;nbsp;난이도,&amp;nbsp;카테고리,&amp;nbsp;학습&amp;nbsp;상태별&amp;nbsp;필터&amp;nbsp;기능&amp;nbsp;제공&lt;br /&gt;-&amp;nbsp;초급부터&amp;nbsp;특급까지&amp;nbsp;단계별로&amp;nbsp;맞춤법을&amp;nbsp;체계적으로&amp;nbsp;익힐&amp;nbsp;수&amp;nbsp;있음&lt;br /&gt;&lt;br /&gt;&lt;b&gt;  퀴즈 모드&lt;/b&gt;&lt;br /&gt;-&amp;nbsp;빈&amp;nbsp;칸&amp;nbsp;채우기,&amp;nbsp;문장&amp;nbsp;맞추기&amp;nbsp;퀴즈&amp;nbsp;지원&lt;br /&gt;-&amp;nbsp;난이도&amp;nbsp;및&amp;nbsp;카테고리별로&amp;nbsp;문제&amp;nbsp;선택&amp;nbsp;가능&lt;br /&gt;&lt;br /&gt;&lt;b&gt;  퀴즈&lt;/b&gt;&lt;br /&gt;-&amp;nbsp;2지선다형&amp;nbsp;객관식&amp;nbsp;문제&amp;nbsp;+&amp;nbsp;30초&amp;nbsp;제한&amp;nbsp;시간&lt;br /&gt;-&amp;nbsp;전구&amp;nbsp;버튼을&amp;nbsp;통해&amp;nbsp;현재&amp;nbsp;문제의&amp;nbsp;힌트&amp;nbsp;제공&lt;br /&gt;-&amp;nbsp;문제에&amp;nbsp;대한&amp;nbsp;정답/오답&amp;nbsp;시&amp;nbsp;문제&amp;nbsp;풀이&amp;nbsp;제공&lt;br /&gt;&lt;br /&gt;&lt;b&gt;  오답 복습&lt;/b&gt;&lt;br /&gt;- 틀린 문제만 모아 반복 문제 및 풀이 제공&lt;br /&gt;-&amp;nbsp;정답을&amp;nbsp;맞히면&amp;nbsp;자동으로&amp;nbsp;오답&amp;nbsp;목록에서&amp;nbsp;제거되어&amp;nbsp;효율적인&amp;nbsp;복습&amp;nbsp;가능&lt;br /&gt;&lt;br /&gt;&lt;b&gt;  타임 챌린지 모드&amp;nbsp;&lt;/b&gt;&lt;br /&gt;- 180초 동안 제한 시간 내에 최대한 많은 문제 풀기 도전&lt;br /&gt;-&amp;nbsp;속도와&amp;nbsp;정확도를&amp;nbsp;겨루며&amp;nbsp;나만의&amp;nbsp;최고&amp;nbsp;점수에&amp;nbsp;도전&lt;br /&gt;-&amp;nbsp;콤보,&amp;nbsp;점수,&amp;nbsp;랭킹&amp;nbsp;시스템으로&amp;nbsp;긴장감&amp;nbsp;넘치는&amp;nbsp;재미&amp;nbsp;제공&lt;br /&gt;&lt;br /&gt;&lt;b&gt;  나의 활동&lt;/b&gt;&lt;br /&gt;- 학습 완료 개수, 마지막 학습일 등 학습 활동 이력 제공&lt;br /&gt;-&amp;nbsp;퀴즈&amp;nbsp;진행&amp;nbsp;횟수,&amp;nbsp;최고&amp;nbsp;콤보,&amp;nbsp;정답률&amp;nbsp;등&amp;nbsp;퀴즈&amp;nbsp;통계&amp;nbsp;확인&lt;br /&gt;-&amp;nbsp;점수에&amp;nbsp;따라&amp;nbsp;변화하는&amp;nbsp;캐릭터와&amp;nbsp;타이틀,&amp;nbsp;획득한&amp;nbsp;뱃지&amp;nbsp;목록&amp;nbsp;및&amp;nbsp;해제&amp;nbsp;조건&amp;nbsp;제공&lt;br /&gt;&lt;br /&gt;&lt;b&gt;  설정&lt;/b&gt;&lt;br /&gt;- 학습 및 퀴즈 기록 초기화 기능&lt;br /&gt;-&amp;nbsp;개인정보&amp;nbsp;처리방침,&amp;nbsp;이용약관,&amp;nbsp;오픈소스&amp;nbsp;라이브러리&amp;nbsp;확인&lt;br /&gt;-&amp;nbsp;현재&amp;nbsp;앱&amp;nbsp;버전&amp;nbsp;및&amp;nbsp;제작자&amp;nbsp;정보&amp;nbsp;제공&lt;br /&gt;-&amp;nbsp;앱을&amp;nbsp;처음처럼&amp;nbsp;리셋하여&amp;nbsp;새롭게&amp;nbsp;시작&amp;nbsp;가능&lt;br /&gt;&lt;br /&gt;&lt;b&gt;  이런 분께 추천합니다&lt;/b&gt;&lt;br /&gt;- 헷갈리는 맞춤법을 쉽고 재미있게 배우고 싶은 분&lt;br /&gt;-&amp;nbsp;단어와&amp;nbsp;문장&amp;nbsp;속&amp;nbsp;올바른&amp;nbsp;표기를&amp;nbsp;확실히&amp;nbsp;익히고&amp;nbsp;싶은&amp;nbsp;분&lt;br /&gt;-&amp;nbsp;자주&amp;nbsp;틀리는&amp;nbsp;표현을&amp;nbsp;퀴즈로&amp;nbsp;반복&amp;nbsp;학습하고&amp;nbsp;싶은&amp;nbsp;분&lt;br /&gt;-&amp;nbsp;오답&amp;nbsp;복습과&amp;nbsp;타임&amp;nbsp;챌린지로&amp;nbsp;완벽한&amp;nbsp;실력을&amp;nbsp;쌓고&amp;nbsp;싶은&amp;nbsp;학습자&lt;br /&gt;-&amp;nbsp;어휘력과&amp;nbsp;문장력을&amp;nbsp;함께&amp;nbsp;향상시키고&amp;nbsp;싶은&amp;nbsp;분&lt;br /&gt;&lt;br /&gt;⸻&lt;br /&gt;&lt;br /&gt;지금 다운로드하고 맞춤법 마스터에 도전하세요!&lt;br /&gt;당신의&amp;nbsp;언어&amp;nbsp;실력이&amp;nbsp;한층&amp;nbsp;성장하는&amp;nbsp;여정이&amp;nbsp;지금&amp;nbsp;시작됩니다!&amp;nbsp; ✍️ ✨&lt;b&gt;&lt;/b&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1419&quot; data-origin-height=&quot;570&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dlmYIv/dJMcajgiUn9/wBLgDGYSxwJOQpFJoMF8P1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dlmYIv/dJMcajgiUn9/wBLgDGYSxwJOQpFJoMF8P1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dlmYIv/dJMcajgiUn9/wBLgDGYSxwJOQpFJoMF8P1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdlmYIv%2FdJMcajgiUn9%2FwBLgDGYSxwJOQpFJoMF8P1%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;1419&quot; height=&quot;570&quot; data-origin-width=&quot;1419&quot; data-origin-height=&quot;570&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1449&quot; data-origin-height=&quot;588&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cybCBh/dJMcabvRbEH/heolsNKANxmsWS5eBQqqS0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cybCBh/dJMcabvRbEH/heolsNKANxmsWS5eBQqqS0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cybCBh/dJMcabvRbEH/heolsNKANxmsWS5eBQqqS0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcybCBh%2FdJMcabvRbEH%2FheolsNKANxmsWS5eBQqqS0%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;1449&quot; height=&quot;588&quot; data-origin-width=&quot;1449&quot; data-origin-height=&quot;588&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3)&amp;nbsp;다운로드&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. Google PlayStore&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;450&quot; data-origin-height=&quot;450&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lrhPy/dJMcaa4PpMw/Sk0tGU2JurE29YPTOT5nN0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lrhPy/dJMcaa4PpMw/Sk0tGU2JurE29YPTOT5nN0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lrhPy/dJMcaa4PpMw/Sk0tGU2JurE29YPTOT5nN0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlrhPy%2FdJMcaa4PpMw%2FSk0tGU2JurE29YPTOT5nN0%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;450&quot; height=&quot;450&quot; data-origin-width=&quot;450&quot; data-origin-height=&quot;450&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;figure id=&quot;og_1764643268988&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;맞픽: 맞춤법 퀴즈 - Google Play 앱&quot; data-og-description=&quot;한국어 맞춤법 중 헷갈리기 쉬운 다양한 표현들을 쉽고 재미있게 학습할 수 있는 학습형 퀴즈 앱입니다.&quot; data-og-host=&quot;play.google.com&quot; data-og-source-url=&quot;https://play.google.com/store/apps/details?id=com.tha.spellingquiz&quot; data-og-url=&quot;https://play.google.com/store/apps/details?id=com.tha.spellingquiz&amp;amp;hl=ko&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/eGuCEw/hyZOQJlVGG/qQqJsPzRshiTzbI9IPTZZK/img.png?width=512&amp;amp;height=512&amp;amp;face=0_0_512_512,https://scrap.kakaocdn.net/dn/cQYn3T/hyZO0yqrrp/DkW4iA8WcLZ5qmMkDDwwHk/img.png?width=600&amp;amp;height=300&amp;amp;face=0_0_600_300,https://scrap.kakaocdn.net/dn/BGC07/hyZOCRMs5J/ri0Quo2Q4imEC58kk9bRc0/img.png?width=240&amp;amp;height=240&amp;amp;face=0_0_240_240&quot;&gt;&lt;a href=&quot;https://play.google.com/store/apps/details?id=com.tha.spellingquiz&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://play.google.com/store/apps/details?id=com.tha.spellingquiz&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/eGuCEw/hyZOQJlVGG/qQqJsPzRshiTzbI9IPTZZK/img.png?width=512&amp;amp;height=512&amp;amp;face=0_0_512_512,https://scrap.kakaocdn.net/dn/cQYn3T/hyZO0yqrrp/DkW4iA8WcLZ5qmMkDDwwHk/img.png?width=600&amp;amp;height=300&amp;amp;face=0_0_600_300,https://scrap.kakaocdn.net/dn/BGC07/hyZOCRMs5J/ri0Quo2Q4imEC58kk9bRc0/img.png?width=240&amp;amp;height=240&amp;amp;face=0_0_240_240');&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;맞픽: 맞춤법 퀴즈 - Google Play 앱&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;play.google.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;h3 data-ke-size=&quot;size23&quot;&gt;2.&amp;nbsp;AppStore&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;490&quot; data-origin-height=&quot;490&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/KnaIC/dJMcafLM9Iy/14zOGkS7UvJsVHnKkNOqzK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/KnaIC/dJMcafLM9Iy/14zOGkS7UvJsVHnKkNOqzK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/KnaIC/dJMcafLM9Iy/14zOGkS7UvJsVHnKkNOqzK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FKnaIC%2FdJMcafLM9Iy%2F14zOGkS7UvJsVHnKkNOqzK%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;490&quot; height=&quot;490&quot; data-origin-width=&quot;490&quot; data-origin-height=&quot;490&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;figure id=&quot;og_1763973565900&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;맞픽: 맞춤법 퀴즈 App - App Store&quot; data-og-description=&quot;Download 맞픽: 맞춤법 퀴즈 by EcodeLab on the App Store. See screenshots, ratings and reviews, user tips, and more games like 맞픽: 맞춤법 퀴즈.&quot; data-og-host=&quot;apps.apple.com&quot; data-og-source-url=&quot;https://apps.apple.com/us/app/id6753701785&quot; data-og-url=&quot;https://apps.apple.com/us/app/%EB%A7%9E%ED%94%BD-%EB%A7%9E%EC%B6%A4%EB%B2%95-%ED%80%B4%EC%A6%88/id6753701785&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/fBBhA/hyZOnGMxa2/XY4MQyeOzOtzk2aRXtROt1/img.jpg?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/cIitOB/hyZOxCEhxA/Ckbh3Pp3WczG3FjEvpj9C1/img.jpg?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630&quot;&gt;&lt;a href=&quot;https://apps.apple.com/us/app/id6753701785&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://apps.apple.com/us/app/id6753701785&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/fBBhA/hyZOnGMxa2/XY4MQyeOzOtzk2aRXtROt1/img.jpg?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/cIitOB/hyZOxCEhxA/Ckbh3Pp3WczG3FjEvpj9C1/img.jpg?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;맞픽: 맞춤법 퀴즈 App - App Store&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Download 맞픽: 맞춤법 퀴즈 by EcodeLab on the App Store. See screenshots, ratings and reviews, user tips, and more games like 맞픽: 맞춤법 퀴즈.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;apps.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;&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;⬇️ 개발자의 다양한 앱을 보고 싶으시면 아래의 링크를 확인해주세요&lt;span&gt;&amp;nbsp;&lt;/span&gt;⬇️&lt;/b&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1779095493288&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;Ecodelab - 제작 앱 소개&quot; data-og-description=&quot;Ecodelab에서 개발한 다양한 앱들을 소개합니다&quot; data-og-host=&quot;www.ecodelab.im&quot; data-og-source-url=&quot;https://ecodelab.im/main&quot; data-og-url=&quot;https://www.ecodelab.im&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bWuJMc/dJMb9eTUZsH/njzMvre05hFxKg2BACFmIk/img.png?width=1024&amp;amp;height=1024&amp;amp;face=0_0_1024_1024,https://scrap.kakaocdn.net/dn/eR1YMg/dJMb8RRXk8e/RlDSZRgjj03yt5Wtc1PKLK/img.png?width=1024&amp;amp;height=1024&amp;amp;face=0_0_1024_1024&quot;&gt;&lt;a href=&quot;https://ecodelab.im/main&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://ecodelab.im/main&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bWuJMc/dJMb9eTUZsH/njzMvre05hFxKg2BACFmIk/img.png?width=1024&amp;amp;height=1024&amp;amp;face=0_0_1024_1024,https://scrap.kakaocdn.net/dn/eR1YMg/dJMb8RRXk8e/RlDSZRgjj03yt5Wtc1PKLK/img.png?width=1024&amp;amp;height=1024&amp;amp;face=0_0_1024_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;Ecodelab - 제작 앱 소개&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Ecodelab에서 개발한 다양한 앱들을 소개합니다&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.ecodelab.im&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;오늘도&amp;nbsp;감사합니다.&amp;nbsp; &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;</description>
      <category>Contributor9/제작 앱 소개</category>
      <category>맞춤법</category>
      <category>맞춤법 검사</category>
      <category>맞춤법 공부</category>
      <category>맞춤법 사전</category>
      <category>맞춤법 사전 앱</category>
      <category>맞춤법 앱</category>
      <category>맞춤법 퀴즈 앱</category>
      <category>자주 틀리는 맞춤법</category>
      <category>헷갈리는 맞춤법</category>
      <category>혼동되는 맞춤법</category>
      <author>adjh54</author>
      <guid isPermaLink="true">https://adjh54.tistory.com/726</guid>
      <comments>https://adjh54.tistory.com/726#entry726comment</comments>
      <pubDate>Mon, 18 May 2026 18:11:45 +0900</pubDate>
    </item>
    <item>
      <title>[제작 앱 소개] 갬티콘: 갬성 이모티콘</title>
      <link>https://adjh54.tistory.com/758</link>
      <description>&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;해당&amp;nbsp;글에서는&amp;nbsp;Contributor9&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;&lt;/span&gt;
&lt;figure contenteditable=&quot;false&quot; data-ke-type=&quot;emoticon&quot; data-ke-align=&quot;alignCenter&quot; data-emoticon-type=&quot;face&quot; data-emoticon-name=&quot;073&quot; data-emoticon-isanimation=&quot;false&quot; data-emoticon-src=&quot;https://t1.daumcdn.net/axz_keditor/emoticon/face/large/073.png&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/axz_keditor/emoticon/face/large/073.png&quot; width=&quot;80&quot; /&gt;&lt;/figure&gt;
&lt;/blockquote&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;br /&gt;1) 개발자는 왜 이 앱을 만들었을까?&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  개발자는 왜 이 앱을 만들었을까?&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 고등학교 시절, 싸이월드가 한창 유행이던 때가 있었습니다. 카카오톡 이모티콘이 없던 그 시절엔 감정을 표현하는 방법이 지금과는 많이 달랐던 것 같습니다.&amp;nbsp;텍스트 이모티콘이 그 역할을 했습니다.&amp;nbsp;&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;(☆&amp;omega;☆) ٩(๑&amp;bull;̀ㅂ&amp;bull;́)و 같은 것들로 나만의 개성을 표현하고, 마음에 드는 이모티콘을 발견하면 저장해뒀다가 쓰기도 했습니다.&lt;br /&gt;&lt;br /&gt;지금은 대부분의 대화가 카카오톡으로 이루어지고 다양한 이모티콘도 넘쳐나지만, 문득 '나만의 유니크한 표현을 쓰고 싶다'거나 '늘 쓰던 이모티콘에서 벗어나 새로운 것을 찾고 싶다'는 생각이 들 때가 있지 않으신가요? (ง'̀-'́)ง &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;갬티콘은 바로 그런 분들을 위해 만들어졌습니다. &lt;br /&gt;&lt;br /&gt;옛 감성이 그리운 분들, 카카오톡 이모티콘에 살짝 지친 분들, 나만의 개성 있는 표현을 찾는 분들 모두를 위한 앱입니다. &lt;br /&gt;&lt;br /&gt;(☞ﾟヮﾟ)☞ 총 2,064개의 텍스트 이모티콘을 표정, 동물, 동작, 음악, 음식, 게임/스포츠, 자연/날씨 등 다양한 카테고리로 깔끔하게 정리했습니다.&lt;br /&gt;원하는 이모티콘을 빠르게 검색할 수 있고, 터치 한 번으로 바로 복사도 가능합니다. &lt;br /&gt;&lt;br /&gt;(๑˃ᴗ˂)ﻭ 매일 고르기 어렵다면? 랜덤 추천 기능이 대신 골라드립니다. 마음에 드는 이모티콘은 폴더를 만들어 모음집으로 관리하고, 직접 나만의 이모티콘을 만들어 저장하거나 다른 사람에게 공유할 수도 있습니다. ᕦ(&amp;ograve;_&amp;oacute;ˇ)ᕤ &lt;br /&gt;&lt;br /&gt;텍스트 이모티콘의 재미, 갬티콘과 함께 다시 느껴보세요. ( o˘◡˘o) ┌iii┐&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1383&quot; data-origin-height=&quot;680&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bMel7e/dJMcafNsHoQ/gQx45KKJoneFd8UwZqbGkK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bMel7e/dJMcafNsHoQ/gQx45KKJoneFd8UwZqbGkK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bMel7e/dJMcafNsHoQ/gQx45KKJoneFd8UwZqbGkK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbMel7e%2FdJMcafNsHoQ%2FgQx45KKJoneFd8UwZqbGkK%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;1383&quot; height=&quot;680&quot; data-origin-width=&quot;1383&quot; data-origin-height=&quot;680&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1369&quot; data-origin-height=&quot;645&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/scESD/dJMcagZO1cv/BsAdvqPEe3tEKl3x1Uh4ak/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/scESD/dJMcagZO1cv/BsAdvqPEe3tEKl3x1Uh4ak/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/scESD/dJMcagZO1cv/BsAdvqPEe3tEKl3x1Uh4ak/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FscESD%2FdJMcagZO1cv%2FBsAdvqPEe3tEKl3x1Uh4ak%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;1369&quot; height=&quot;645&quot; data-origin-width=&quot;1369&quot; data-origin-height=&quot;645&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1419&quot; data-origin-height=&quot;669&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bJw8St/dJMcaaZDa1n/iFA22Ngs9Wc7EqFyNqpSkK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bJw8St/dJMcaaZDa1n/iFA22Ngs9Wc7EqFyNqpSkK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bJw8St/dJMcaaZDa1n/iFA22Ngs9Wc7EqFyNqpSkK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbJw8St%2FdJMcaaZDa1n%2FiFA22Ngs9Wc7EqFyNqpSkK%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;1419&quot; height=&quot;669&quot; data-origin-width=&quot;1419&quot; data-origin-height=&quot;669&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2)&amp;nbsp;앱&amp;nbsp;소개-&amp;nbsp;갬티콘:&amp;nbsp;갬티콘:&amp;nbsp;갬성&amp;nbsp;이모티콘&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  갬티콘: 갬성 이모티콘&lt;/b&gt; &lt;br /&gt;&lt;br /&gt;- 지금 이 기분, 딱 맞는 이모티콘 있어요. 탭 한 번으로 복사하고, 매일 새로운 갬성을 만나보세요 (๑˃ᴗ˂)ﻭ &lt;br /&gt;&lt;br /&gt;- &quot;ㅋㅋ&quot;만으로는 부족한 순간이 있잖아요. 더 웃기고, 더 귀엽고, 더 감성 넘치는 표현이 필요한 그 순간 갬티콘: 갬성 이모티콘이 딱 맞는 이모티콘을 찾아드릴게요.&lt;br /&gt;- 2,000개 이상의 텍스트 이모티콘이 기다리고 있어요. 탭 한 번으로 바로 복사, 어디서든 바로 붙여넣기&lt;br /&gt;&lt;br /&gt;&lt;b&gt;  상세 설명&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;1. 홈 화면&lt;/b&gt; &lt;br /&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;691&quot; data-origin-height=&quot;642&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/0PMVh/dJMcadBYhWM/H3GJpxZO4uuynXbh53UHAK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/0PMVh/dJMcadBYhWM/H3GJpxZO4uuynXbh53UHAK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/0PMVh/dJMcadBYhWM/H3GJpxZO4uuynXbh53UHAK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F0PMVh%2FdJMcadBYhWM%2FH3GJpxZO4uuynXbh53UHAK%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;691&quot; height=&quot;642&quot; data-origin-width=&quot;691&quot; data-origin-height=&quot;642&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;br /&gt;&lt;u&gt;&lt;b&gt;- 매일 새로운 이모티콘과 함께 시작하고, 기분&amp;middot;카테고리별 추천과 최근 사용 기록까지 한눈에 확인할 수 있는 앱의 메인 화면입니다.&lt;/b&gt;&lt;/u&gt; &lt;br /&gt;- 매일 다른 이모티콘이 앱을 열 때마다 반겨줘요. 마음에 들면 탭 한 번으로 바로 복사할 수 있어요.&lt;br /&gt;- 아래로 스크롤하면 신규 업데이트 이모티콘, 기분별 표정 모음, 카테고리 추천이 차례로 펼쳐져요. &lt;br /&gt;- 최근에 사용한 이모티콘은 자동으로 기록되니까 &quot;아까 그거 뭐였지?&quot; 하고 찾아 헤맬 일이 없어요. &lt;br /&gt;- 하트 버튼을 누르면 마음에 드는 이모티콘을 내 모음집에 바로 담을 수 있고, &lt;br /&gt;- 오른쪽 하단 붓 아이콘으로 테마 색상과 다크모드를 취향껏 바꿀 수 있어요. &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;&lt;b&gt;2. 전체 목록 화면&lt;br /&gt;&lt;/b&gt;&lt;/b&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1242&quot; data-origin-height=&quot;2688&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cPZwNI/dJMcaiDmEnE/i5SgBCgT37agQMeDbcAu20/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cPZwNI/dJMcaiDmEnE/i5SgBCgT37agQMeDbcAu20/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cPZwNI/dJMcaiDmEnE/i5SgBCgT37agQMeDbcAu20/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcPZwNI%2FdJMcaiDmEnE%2Fi5SgBCgT37agQMeDbcAu20%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;298&quot; height=&quot;645&quot; data-origin-width=&quot;1242&quot; data-origin-height=&quot;2688&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;br /&gt;&lt;u&gt;&lt;b&gt;- 검색, 카테고리 필터, 뷰 모드 전환까지 갖춘 이모티콘 전체 탐색 화면입니다.&lt;/b&gt;&lt;/u&gt; &lt;br /&gt;- 검색창에 이름이나 태그를 입력하면 원하는 이모티콘을 바로 찾을 수 있어요. &lt;br /&gt;- 상단 카테고리 탭에서 표정&amp;middot;동물&amp;middot;상황 등 원하는 분류만 골라볼 수도 있고요. &lt;br /&gt;- 카드 모드는 이모티콘을 크게 한눈에 비교하기 좋고, 리스트 모드는 한 화면에 더 많이 훑어보기 좋아요. &lt;br /&gt;- 새로 추가된 이모티콘에는 NEW 뱃지가 붙어 있어서 업데이트된 항목을 바로 알아볼 수 있어요. &lt;br /&gt;- 최근 사용 영역은 평소에 접어두다가 필요할 때만 펼쳐볼 수 있고, &lt;br /&gt;- 스크롤을 길게 내려도 오른쪽 하단 버튼 하나면 맨 위로 바로 올라갈 수 있어요. &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;3. 모음집 화면&lt;/b&gt; &lt;br /&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;701&quot; data-origin-height=&quot;663&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cb0Njh/dJMcahxIbbP/YiK9lG7pTaKU8HoPFPTq8k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cb0Njh/dJMcahxIbbP/YiK9lG7pTaKU8HoPFPTq8k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cb0Njh/dJMcahxIbbP/YiK9lG7pTaKU8HoPFPTq8k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcb0Njh%2FdJMcahxIbbP%2FYiK9lG7pTaKU8HoPFPTq8k%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;701&quot; height=&quot;663&quot; data-origin-width=&quot;701&quot; data-origin-height=&quot;663&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;br /&gt;&lt;u&gt;&lt;b&gt;- 나만의 이모티콘 컬렉션을 만들고, 정렬&amp;middot;편집&amp;middot;공유까지 자유롭게 관리할 수 있는 화면입니다.&lt;/b&gt;&lt;/u&gt; &lt;br /&gt;- 이름과 이모지 아이콘을 직접 골라서 나만의 모음집을 만들 수 있어요. &lt;br /&gt;- 모음집은 꾹 눌러서 드래그하면 원하는 순서로 자유롭게 정렬할 수 있고, 편집 모드에서 이름 수정, 삭제, 이모티콘 순서 변경을 한 화면에서 한 번에 처리할 수 있어요. &lt;br /&gt;- 모음집 안의 이모티콘은 탭 하는 것만으로 바로 복사되고, 모음집 전체를 앱 링크와 함께 외부로 공유하는 것도 가능해요. &lt;br /&gt;- 모음집 개수가 꽉 차더라도 짧은 광고 한 편을 보면 계속 추가할 수 있어요. &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;4. 나만의 이모티콘 화면&lt;/b&gt; &lt;br /&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;691&quot; data-origin-height=&quot;670&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/TOItV/dJMcagMmiDl/iMmGna9C8663qaVzn8Zdv1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/TOItV/dJMcagMmiDl/iMmGna9C8663qaVzn8Zdv1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/TOItV/dJMcagMmiDl/iMmGna9C8663qaVzn8Zdv1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FTOItV%2FdJMcagMmiDl%2FiMmGna9C8663qaVzn8Zdv1%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;691&quot; height=&quot;670&quot; data-origin-width=&quot;691&quot; data-origin-height=&quot;670&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;br /&gt;&lt;u&gt;&lt;b&gt;- 텍스트, 이모티콘, 특수문자를 자유롭게 조합해 세상에 하나뿐인 이모티콘을 직접 만들 수 있는 편집 화면입니다.&lt;/b&gt;&lt;/u&gt; &lt;br /&gt;- 텍스트를 직접 입력하거나 이모티콘 픽커에서 골라 커서 위치에 바로 삽입할 수 있어요. &lt;br /&gt;- 특수문자 버튼으로 괄호&amp;middot;기호&amp;middot;가나&amp;middot;도형&amp;middot;하트 등 카테고리별로 원하는 문자를 찾아 붙여 넣을 수도 있고요. &lt;br /&gt;- 입력할 때마다 상단 미리 보기가 실시간으로 업데이트되어 완성된 모양을 바로 확인할 수 있어요. &lt;br /&gt;- 완성한 이모티콘은 복사, 공유, 모음집 저장 중 원하는 방식으로 활용할 수 있으며, 초기화 버튼을 누르면 언제든지 처음부터 다시 만들 수 있어요. &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;&lt;b&gt;5. 설정 화면&lt;br /&gt;&lt;/b&gt;&lt;/b&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1242&quot; data-origin-height=&quot;2688&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ek3xZh/dJMcadWkOcr/tM2NVJhHTKX0qfEvE3lpFK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ek3xZh/dJMcadWkOcr/tM2NVJhHTKX0qfEvE3lpFK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ek3xZh/dJMcadWkOcr/tM2NVJhHTKX0qfEvE3lpFK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fek3xZh%2FdJMcadWkOcr%2FtM2NVJhHTKX0qfEvE3lpFK%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;295&quot; height=&quot;638&quot; data-origin-width=&quot;1242&quot; data-origin-height=&quot;2688&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;br /&gt;&lt;u&gt;&lt;b&gt;- 앱 공유, 데이터 초기화, 문의&amp;middot;리뷰, 개인정보 및 버전 정보까지 앱 전반을 관리할 수 있는 설정 화면입니다.&lt;/b&gt;&lt;/u&gt; &lt;br /&gt;- 앱이 마음에 들었다면 공유하기 버튼으로 가족이나 친구에게 바로 소개할 수 있어요. &lt;br /&gt;- 즐겨찾기, 최근 사용 내역, 모음집은 항목별로 또는 한 번에 전부 초기화할 수 있고, &lt;br /&gt;- 앱 리뷰 남기기와 이메일 문의로 피드백도 간편하게 보낼 수 있어요. &lt;br /&gt;- 개인정보 처리방침, 이용약관, 오픈소스 라이브러리 정보는 이 화면에서 언제든지 확인할 수 있으며, &lt;br /&gt;- 최신 버전 확인 버튼으로 업데이트 여부도 바로 체크할 수 있어요. &lt;br /&gt;- 화면 하단에서는 제작자의 다른 앱들도 둘러볼 수 있어요. &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;  이런 분께 추천해요&lt;br /&gt;&lt;/b&gt; &lt;br /&gt;- 카톡&amp;middot;인스타&amp;middot;디스코드에서 이모티콘 없으면 대화가 밋밋한 분 &lt;br /&gt;- &quot;ㅋㅋ&quot;보다 더 찰진 표현을 찾고 있는 분 &lt;br /&gt;- 기분 표현에 진심인 분 &lt;br /&gt;- 매번 같은 이모티콘만 쓰다가 질린 분&lt;br /&gt;- 나만의 감성 컬렉션을 만들고 싶은 분 &lt;br /&gt;- 직접 만든 이모티콘으로 나만의 개성을 드러내고 싶은 분 &lt;br /&gt;- 귀엽고 재밌는 거라면 일단 저장하고 보는 분 &lt;br /&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;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3)&amp;nbsp;다운로드&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1.&amp;nbsp;Google&amp;nbsp;Play&amp;nbsp;Store&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;450&quot; data-origin-height=&quot;450&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/o2KKN/dJMcajhWWHN/niuiTyATD39QK23kHxyPIK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/o2KKN/dJMcajhWWHN/niuiTyATD39QK23kHxyPIK/img.png&quot; data-alt=&quot;https://play.google.com/store/apps/details?id=com.tha.emotionalemoticon&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/o2KKN/dJMcajhWWHN/niuiTyATD39QK23kHxyPIK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fo2KKN%2FdJMcajhWWHN%2FniuiTyATD39QK23kHxyPIK%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;450&quot; height=&quot;450&quot; data-origin-width=&quot;450&quot; data-origin-height=&quot;450&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://play.google.com/store/apps/details?id=com.tha.emotionalemoticon&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;figure id=&quot;og_1779094978424&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;갬티콘 : 갬성 텍스트 이모티콘 이모지 특수문자 복사 - Google Play 앱&quot; data-og-description=&quot;지금 이 기분, 딱 맞는 이모티콘 있어요. 탭 한 번으로 복사하고, 매일 새로운 갬성을 만나보세요 (๑˃ᴗ˂)ﻭ&quot; data-og-host=&quot;play.google.com&quot; data-og-source-url=&quot;https://play.google.com/store/apps/details?id=com.tha.emotionalemoticon&quot; data-og-url=&quot;https://play.google.com/store/apps/details?id=com.tha.emotionalemoticon&amp;amp;hl=ko&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/hV95W/dJMb8SXDhx1/MkQ7G97ASMMpwxinBM878K/img.png?width=512&amp;amp;height=512&amp;amp;face=0_0_512_512,https://scrap.kakaocdn.net/dn/bheLVS/dJMb8Z3wPkm/hRAmkaKRcO4Z687g7rNdk1/img.png?width=600&amp;amp;height=300&amp;amp;face=0_0_600_300,https://scrap.kakaocdn.net/dn/wvR5V/dJMb9eTUZo9/7G5B7P8OhEKzHa66n4TUyk/img.png?width=240&amp;amp;height=240&amp;amp;face=0_0_240_240&quot;&gt;&lt;a href=&quot;https://play.google.com/store/apps/details?id=com.tha.emotionalemoticon&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://play.google.com/store/apps/details?id=com.tha.emotionalemoticon&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/hV95W/dJMb8SXDhx1/MkQ7G97ASMMpwxinBM878K/img.png?width=512&amp;amp;height=512&amp;amp;face=0_0_512_512,https://scrap.kakaocdn.net/dn/bheLVS/dJMb8Z3wPkm/hRAmkaKRcO4Z687g7rNdk1/img.png?width=600&amp;amp;height=300&amp;amp;face=0_0_600_300,https://scrap.kakaocdn.net/dn/wvR5V/dJMb9eTUZo9/7G5B7P8OhEKzHa66n4TUyk/img.png?width=240&amp;amp;height=240&amp;amp;face=0_0_240_240');&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;갬티콘 : 갬성 텍스트 이모티콘 이모지 특수문자 복사 - Google Play 앱&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;play.google.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;h3 data-ke-size=&quot;size23&quot;&gt;2.&amp;nbsp;App&amp;nbsp;Store&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;610&quot; data-origin-height=&quot;610&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/YPqu8/dJMcaa6qY6A/8YkGnr0QvbFcDMC6WMVmDK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/YPqu8/dJMcaa6qY6A/8YkGnr0QvbFcDMC6WMVmDK/img.png&quot; data-alt=&quot;https://apps.apple.com/us/app/id6760441156&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/YPqu8/dJMcaa6qY6A/8YkGnr0QvbFcDMC6WMVmDK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FYPqu8%2FdJMcaa6qY6A%2F8YkGnr0QvbFcDMC6WMVmDK%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;610&quot; height=&quot;610&quot; data-origin-width=&quot;610&quot; data-origin-height=&quot;610&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://apps.apple.com/us/app/id6760441156&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;figure id=&quot;og_1779095043652&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;갬티콘 : 갬성 텍스트 이모티콘 이모지 특수문자 복사 App - App Store&quot; data-og-description=&quot;Download 갬티콘 : 갬성 텍스트 이모티콘 이모지 특수문자 복사 by EcodeLab on the App Store. See screenshots, ratings and reviews, user tips, and more apps like 갬티콘 : 갬성 텍스트 이모티콘 이모지&amp;hellip;&quot; data-og-host=&quot;apps.apple.com&quot; data-og-source-url=&quot;https://apps.apple.com/us/app/id6760441156&quot; data-og-url=&quot;https://apps.apple.com/us/app/%EA%B0%AC%ED%8B%B0%EC%BD%98-%EA%B0%AC%EC%84%B1-%ED%85%8D%EC%8A%A4%ED%8A%B8-%EC%9D%B4%EB%AA%A8%ED%8B%B0%EC%BD%98-%EC%9D%B4%EB%AA%A8%EC%A7%80-%ED%8A%B9%EC%88%98%EB%AC%B8%EC%9E%90-%EB%B3%B5%EC%82%AC/id6760441156&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/biotNZ/dJMb8VNAYXy/JWP1HvVhTbdgHVKdooSdW0/img.jpg?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/c0EcxO/dJMb89yi3qW/eNhBjz8VDBq8KvTU6a6gA0/img.jpg?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630&quot;&gt;&lt;a href=&quot;https://apps.apple.com/us/app/id6760441156&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://apps.apple.com/us/app/id6760441156&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/biotNZ/dJMb8VNAYXy/JWP1HvVhTbdgHVKdooSdW0/img.jpg?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/c0EcxO/dJMb89yi3qW/eNhBjz8VDBq8KvTU6a6gA0/img.jpg?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;갬티콘 : 갬성 텍스트 이모티콘 이모지 특수문자 복사 App - App Store&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Download 갬티콘 : 갬성 텍스트 이모티콘 이모지 특수문자 복사 by EcodeLab on the App Store. See screenshots, ratings and reviews, user tips, and more apps like 갬티콘 : 갬성 텍스트 이모티콘 이모지&amp;hellip;&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;apps.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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;⬇️ 개발자의 다양한 앱을 보고 싶으시면 아래의 링크를 확인해주세요 ⬇️&lt;/b&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1779095343782&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;Ecodelab - 제작 앱 소개&quot; data-og-description=&quot;Ecodelab에서 개발한 다양한 앱들을 소개합니다&quot; data-og-host=&quot;www.ecodelab.im&quot; data-og-source-url=&quot;https://ecodelab.im/main&quot; data-og-url=&quot;https://www.ecodelab.im&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bWuJMc/dJMb9eTUZsH/njzMvre05hFxKg2BACFmIk/img.png?width=1024&amp;amp;height=1024&amp;amp;face=0_0_1024_1024,https://scrap.kakaocdn.net/dn/eR1YMg/dJMb8RRXk8e/RlDSZRgjj03yt5Wtc1PKLK/img.png?width=1024&amp;amp;height=1024&amp;amp;face=0_0_1024_1024&quot;&gt;&lt;a href=&quot;https://ecodelab.im/main&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://ecodelab.im/main&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bWuJMc/dJMb9eTUZsH/njzMvre05hFxKg2BACFmIk/img.png?width=1024&amp;amp;height=1024&amp;amp;face=0_0_1024_1024,https://scrap.kakaocdn.net/dn/eR1YMg/dJMb8RRXk8e/RlDSZRgjj03yt5Wtc1PKLK/img.png?width=1024&amp;amp;height=1024&amp;amp;face=0_0_1024_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;Ecodelab - 제작 앱 소개&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Ecodelab에서 개발한 다양한 앱들을 소개합니다&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.ecodelab.im&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;&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;오늘도&amp;nbsp;감사합니다.&amp;nbsp; &lt;/b&gt;&lt;/p&gt;</description>
      <category>Contributor9/제작 앱 소개</category>
      <category>나만의 이모티콘</category>
      <category>나만의 텍스트 이모티콘</category>
      <category>싸이월드 기호</category>
      <category>싸이월드 이모티콘</category>
      <category>아스키코드 텍스트</category>
      <category>이모티콘 앱</category>
      <category>텍스트 이모지</category>
      <category>텍스트 이모티콘</category>
      <category>텍스트 이모티콘 앱</category>
      <category>특수문자 앱</category>
      <author>adjh54</author>
      <guid isPermaLink="true">https://adjh54.tistory.com/758</guid>
      <comments>https://adjh54.tistory.com/758#entry758comment</comments>
      <pubDate>Mon, 18 May 2026 18:07:24 +0900</pubDate>
    </item>
    <item>
      <title>[DB] PostgreSQL + Docker TimeZone 설정하기</title>
      <link>https://adjh54.tistory.com/757</link>
      <description>&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
 &lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;해당 글에서는 PostgreSQL + Docker 환경에서 TimeZone을 지정했던 방법에 대해서 공유합니다.&lt;br&gt;&lt;br&gt;&lt;/span&gt; 
 &lt;figure contenteditable=&quot;false&quot; data-ke-type=&quot;emoticon&quot; data-ke-align=&quot;alignCenter&quot; data-emoticon-type=&quot;face&quot; data-emoticon-name=&quot;073&quot; data-emoticon-isanimation=&quot;false&quot; data-emoticon-src=&quot;https://t1.daumcdn.net/axz_keditor/emoticon/face/large/073.png&quot;&gt;
  &lt;img src=&quot;https://t1.daumcdn.net/axz_keditor/emoticon/face/large/073.png&quot; width=&quot;80&quot;&gt;
 &lt;/figure&gt; 
 &lt;br&gt;
 &lt;br&gt;
&lt;/blockquote&gt;&lt;h2 data-ke-size=&quot;size26&quot;&gt;1) Coordinated Universal Time (UTC) / TimeZone&lt;/h2&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot;&gt;&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. Coordinated Universal Time&amp;nbsp;(UTC)&lt;/h3&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot;&gt;&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt; &lt;/b&gt;&lt;b&gt;&amp;nbsp;&lt;/b&gt;&lt;b&gt;Coordinated Universal Time (UTC)&lt;/b&gt;&lt;br&gt;&lt;br&gt;&lt;b&gt;- 전 세계 시간대 설정의 기준이 되는 가장 정확한 원자시 기반의 시간 표준입니다.&lt;/b&gt;&lt;br&gt;&lt;br&gt;- 본초 자오선(경도 0도)을 기준으로 하며, 서머타임(일광 절약 시간제)을 적용하지 않아 연중 일정한 시간을 유지합니다.&lt;br&gt;- 한국의 경우는 타임존(KST)을 가지며, UTC 대비 9시간이 늦은 UTC+9를 가집니다.&lt;/blockquote&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Coordinated_Universal_Time&quot; target=&quot;_self&quot;&gt;&lt;span&gt;https://en.wikipedia.org/wiki/Coordinated_Universal_Time&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;999&quot; data-origin-height=&quot;574&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/czU4iP/dJMcajhUVFB/jkQkJL8p4wZAI41OzvAqY0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/czU4iP/dJMcajhUVFB/jkQkJL8p4wZAI41OzvAqY0/img.png&quot; data-alt=&quot;https://www.timeanddate.com/time/map/&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/czU4iP/dJMcajhUVFB/jkQkJL8p4wZAI41OzvAqY0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FczU4iP%2FdJMcajhUVFB%2FjkQkJL8p4wZAI41OzvAqY0%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;999&quot; height=&quot;574&quot; data-origin-width=&quot;999&quot; data-origin-height=&quot;574&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://www.timeanddate.com/time/map/&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. TimeZone&lt;/h3&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot;&gt;&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt; &lt;/b&gt;&lt;b&gt;&amp;nbsp;&lt;/b&gt;&lt;b&gt;TimeZone&lt;/b&gt;&lt;br&gt;&lt;b&gt;&lt;br&gt;&lt;/b&gt;&lt;b&gt;- 타임존(Time Zone)은 지구를 경도 기준으로 나눈 시간 구역을 의미합니다.&lt;/b&gt;&lt;br&gt;&lt;br&gt;-&amp;nbsp;지구는 24시간에 360° 자전하므로, 경도 15°마다 1시간 차이가 발생합니다. 이를 기반으로 전 세계를 구역으로 나눠&amp;nbsp;같은 구역 내에서는 동일한 시간을 사용하도록 약속한 것이 타임존입니다.&lt;br&gt;- 예를 들어서 절대적인 시간 기준점이 0이라고 하면, TimeZone은 UTC에서 얼마나 떨어져 있는지를 오프셋을 의미합니다.&lt;br&gt;- 한국의 경우 UTC를 기준으로 +9 거리를 가지기에 UTC Offset +9입니다.&lt;/blockquote&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;999&quot; data-origin-height=&quot;574&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/baTHD4/dJMcab5hz2k/Um87En7SZ7yL2N8q4tFGYK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/baTHD4/dJMcab5hz2k/Um87En7SZ7yL2N8q4tFGYK/img.png&quot; data-alt=&quot;https://www.timeanddate.com/time/map/&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/baTHD4/dJMcab5hz2k/Um87En7SZ7yL2N8q4tFGYK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbaTHD4%2FdJMcab5hz2k%2FUm87En7SZ7yL2N8q4tFGYK%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;999&quot; height=&quot;574&quot; data-origin-width=&quot;999&quot; data-origin-height=&quot;574&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://www.timeanddate.com/time/map/&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;figure data-ke-type=&quot;opengraph&quot; data-og-title=&quot;Time Zone Map&quot; data-ke-align=&quot;alignCenter&quot; data-og-description=&quot;Current local times around the world, including (DST) changes.&quot; data-og-host=&quot;www.timeanddate.com&quot; data-og-source-url=&quot;https://www.timeanddate.com/time/map/&quot; data-og-image=&quot;https://blog.kakaocdn.net/dna/bOhUBG/dJMb8ZvGx0i/AAAAAAAAAAAAAAAAAAAAAJHJi-_2KtJjBhLW1_m1qbCmHKvoInwQy9A_SfYCHRta/img.png?credential=yqXZFxpELC7KVnFOS48ylbz2pIh7yKj8&amp;amp;expires=1780239599&amp;amp;allow_ip=&amp;amp;allow_referer=&amp;amp;signature=I7DEQdGc0ip3NB7qNcQdxPIn9vA%3D&quot; data-og-url=&quot;https://www.timeanddate.com/time/map/&quot;&gt;&lt;a href=&quot;https://www.timeanddate.com/time/map/&quot; target=&quot;_blank&quot; data-source-url=&quot;https://www.timeanddate.com/time/map/&quot;&gt;&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://blog.kakaocdn.net/dna/bOhUBG/dJMb8ZvGx0i/AAAAAAAAAAAAAAAAAAAAAJHJi-_2KtJjBhLW1_m1qbCmHKvoInwQy9A_SfYCHRta/img.png?credential=yqXZFxpELC7KVnFOS48ylbz2pIh7yKj8&amp;amp;expires=1780239599&amp;amp;allow_ip=&amp;amp;allow_referer=&amp;amp;signature=I7DEQdGc0ip3NB7qNcQdxPIn9vA%3D')&quot;&gt; &lt;/div&gt;&lt;div class=&quot;og-text&quot;&gt;&lt;p class=&quot;og-title&quot;&gt;Time Zone Map&lt;/p&gt;&lt;p class=&quot;og-desc&quot;&gt;Current local times around the world, including (DST) changes.&lt;/p&gt;&lt;p class=&quot;og-host&quot;&gt;www.timeanddate.com&lt;/p&gt;&lt;/div&gt;&lt;/a&gt;&lt;/figure&gt;&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-style=&quot;style2&quot; data-ke-align=&quot;alignLeft&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;b&gt;지역&lt;/b&gt;&lt;/td&gt;&lt;td&gt;&lt;b&gt;국가/도시&lt;/b&gt;&lt;/td&gt;&lt;td&gt;&lt;b&gt;UTC&lt;/b&gt;&lt;/td&gt;&lt;td&gt;&lt;b&gt;오프셋 현지시간(UTC 기준)&lt;/b&gt;&lt;/td&gt;&lt;td&gt;&lt;b&gt;비고&lt;/b&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;b&gt;아시아&lt;/b&gt;&lt;/td&gt;&lt;td&gt;  서울 (대한민국)&lt;/td&gt;&lt;td&gt;UTC+9&lt;/td&gt;&lt;td&gt;UTC + 9시간&lt;/td&gt;&lt;td&gt;KST&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&amp;nbsp;&lt;/td&gt;&lt;td&gt;  도쿄 (일본)&lt;/td&gt;&lt;td&gt;UTC+9&lt;/td&gt;&lt;td&gt;UTC + 9시간&lt;/td&gt;&lt;td&gt;JST&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&amp;nbsp;&lt;/td&gt;&lt;td&gt;  베이징 (중국)&lt;/td&gt;&lt;td&gt;UTC+8&lt;/td&gt;&lt;td&gt;UTC + 8시간&lt;/td&gt;&lt;td&gt;CST&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&amp;nbsp;&lt;/td&gt;&lt;td&gt;  홍콩&lt;/td&gt;&lt;td&gt;UTC+8&lt;/td&gt;&lt;td&gt;UTC + 8시간&lt;/td&gt;&lt;td&gt;HKT&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&amp;nbsp;&lt;/td&gt;&lt;td&gt;  싱가포르&lt;/td&gt;&lt;td&gt;UTC+8&lt;/td&gt;&lt;td&gt;UTC + 8시간&lt;/td&gt;&lt;td&gt;SGT&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&amp;nbsp;&lt;/td&gt;&lt;td&gt;  방콕 (태국)&lt;/td&gt;&lt;td&gt;UTC+7&lt;/td&gt;&lt;td&gt;UTC + 7시간&lt;/td&gt;&lt;td&gt;ICT&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&amp;nbsp;&lt;/td&gt;&lt;td&gt;  하노이 (베트남)&lt;/td&gt;&lt;td&gt;UTC+7&lt;/td&gt;&lt;td&gt;UTC + 7시간&lt;/td&gt;&lt;td&gt;ICT&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&amp;nbsp;&lt;/td&gt;&lt;td&gt;  뭄바이 (인도)&lt;/td&gt;&lt;td&gt;UTC+5:30&lt;/td&gt;&lt;td&gt;UTC + 5시간 30분&lt;/td&gt;&lt;td&gt;IST&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&amp;nbsp;&lt;/td&gt;&lt;td&gt;  두바이 (UAE)&lt;/td&gt;&lt;td&gt;UTC+4&lt;/td&gt;&lt;td&gt;UTC + 4시간&lt;/td&gt;&lt;td&gt;GST&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;b&gt;오세아니아&lt;/b&gt;&lt;/td&gt;&lt;td&gt;  시드니 (호주)&lt;/td&gt;&lt;td&gt;UTC+10 / +11&lt;/td&gt;&lt;td&gt;UTC + 10~11시간&lt;/td&gt;&lt;td&gt;AEST / AEDT (서머타임)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;b&gt;유럽&lt;/b&gt;&lt;/td&gt;&lt;td&gt;  런던 (영국)&lt;/td&gt;&lt;td&gt;UTC+0 / +1&lt;/td&gt;&lt;td&gt;UTC ± 0~1시간&lt;/td&gt;&lt;td&gt;GMT / BST (서머타임)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&amp;nbsp;&lt;/td&gt;&lt;td&gt;  베를린 (독일)&lt;/td&gt;&lt;td&gt;UTC+1 / +2&lt;/td&gt;&lt;td&gt;UTC + 1~2시간&lt;/td&gt;&lt;td&gt;CET / CEST (서머타임)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&amp;nbsp;&lt;/td&gt;&lt;td&gt;  파리 (프랑스)&lt;/td&gt;&lt;td&gt;UTC+1 / +2&lt;/td&gt;&lt;td&gt;UTC + 1~2시간&lt;/td&gt;&lt;td&gt;CET / CEST (서머타임)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&amp;nbsp;&lt;/td&gt;&lt;td&gt;  모스크바 (러시아)&lt;/td&gt;&lt;td&gt;UTC+3&lt;/td&gt;&lt;td&gt;UTC + 3시간&lt;/td&gt;&lt;td&gt;MSK (서머타임 없음)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;b&gt;아프리카&lt;/b&gt;&lt;/td&gt;&lt;td&gt;  요하네스버그 (남아공)&lt;/td&gt;&lt;td&gt;UTC+2&lt;/td&gt;&lt;td&gt;UTC + 2시간&lt;/td&gt;&lt;td&gt;SAST&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&amp;nbsp;&lt;/td&gt;&lt;td&gt;  카이로 (이집트)&lt;/td&gt;&lt;td&gt;UTC+2 / +3&lt;/td&gt;&lt;td&gt;UTC + 2~3시간&lt;/td&gt;&lt;td&gt;EET / EEST (서머타임)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;b&gt;아메리카&lt;/b&gt;&lt;/td&gt;&lt;td&gt;  뉴욕 (미국 동부)&lt;/td&gt;&lt;td&gt;UTC-5 / -4&lt;/td&gt;&lt;td&gt;UTC - 5~4시간&lt;/td&gt;&lt;td&gt;EST / EDT (서머타임)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&amp;nbsp;&lt;/td&gt;&lt;td&gt;  시카고 (미국 중부)&lt;/td&gt;&lt;td&gt;UTC-6 / -5&lt;/td&gt;&lt;td&gt;UTC - 6~5시간&lt;/td&gt;&lt;td&gt;CST / CDT (서머타임)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&amp;nbsp;&lt;/td&gt;&lt;td&gt;  LA (미국 서부)&lt;/td&gt;&lt;td&gt;UTC-8 / -7&lt;/td&gt;&lt;td&gt;UTC - 8~7시간&lt;/td&gt;&lt;td&gt;PST / PDT (서머타임)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&amp;nbsp;&lt;/td&gt;&lt;td&gt;  밴쿠버 (캐나다)&lt;/td&gt;&lt;td&gt;UTC-8 / -7&lt;/td&gt;&lt;td&gt;UTC - 8~7시간&lt;/td&gt;&lt;td&gt;PST / PDT (서머타임)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&amp;nbsp;&lt;/td&gt;&lt;td&gt;  멕시코시티 (멕시코)&lt;/td&gt;&lt;td&gt;UTC-6 / -5&lt;/td&gt;&lt;td&gt;UTC - 6~5시간&lt;/td&gt;&lt;td&gt;CST / CDT (서머타임)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&amp;nbsp;&lt;/td&gt;&lt;td&gt;  상파울루 (브라질)&lt;/td&gt;&lt;td&gt;UTC-3&lt;/td&gt;&lt;td&gt;UTC - 3시간&lt;/td&gt;&lt;td&gt;BRT&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;[ 더 알아보기 ]&lt;/b&gt;&lt;br&gt;&lt;b&gt;&lt;br&gt;&lt;/b&gt;&lt;b&gt; &amp;nbsp;오프셋(Offset) 이란?&lt;/b&gt;&lt;br&gt;&lt;br&gt;- 기준점으로부터 얼마나 떨어져 있는지를 나타내는 차이값을 의미합니다.&lt;br&gt;- 타임존에서만 쓰는 말이 아니라, 전반적으로 ”기준에서 얼마나 벗어나 있냐&quot;를 표현할 때 쓰는 용어를 의미합니다&lt;/blockquote&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;h2 data-ke-size=&quot;size26&quot;&gt;2) 문제점&lt;/h2&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot;&gt;&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  문제점&lt;/b&gt;&lt;br&gt;&lt;br&gt;&lt;b&gt;- 데이터베이스 내에서 now() 내장 함수를 통해서, 현재 시간을 DB에 저장 중에 있습니다.&lt;/b&gt;&lt;br&gt;- 그런데 UTC 시간으로 저장으로 되기에 05:56분과 같은 시간으로 저장이 되었습니다. 이를 대한민국 시간에 맞게 수정하기 위해서, TimeZone을 지정합니다.&lt;/blockquote&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1723&quot; data-origin-height=&quot;1064&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bhGmeW/dJMb99M73cT/Shn2X053qOdQupSFdVIYz0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bhGmeW/dJMb99M73cT/Shn2X053qOdQupSFdVIYz0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bhGmeW/dJMb99M73cT/Shn2X053qOdQupSFdVIYz0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbhGmeW%2FdJMb99M73cT%2FShn2X053qOdQupSFdVIYz0%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;1723&quot; height=&quot;1064&quot; data-origin-width=&quot;1723&quot; data-origin-height=&quot;1064&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;h2 data-ke-size=&quot;size26&quot;&gt;3) 해결방법&lt;/h2&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot;&gt;&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  해결방법&lt;/b&gt;&lt;br&gt;&lt;br&gt;&lt;b&gt;- 위에서 UTC로 지정이 되어 있는 시간을 각각 국가(Asia/Seoul)에 맞게 설정을 하여서 now() 함수를 사용하더라도 TimeZone에 맞는 시간이 나오도록 하는 방식을 사용합니다.&lt;/b&gt;&lt;/blockquote&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&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: 16.0465%;&quot;&gt;&lt;b&gt;레이어&lt;/b&gt;&lt;/td&gt;&lt;td style=&quot;width: 16.8604%;&quot;&gt;&lt;b&gt;설정 위치&lt;/b&gt;&lt;/td&gt;&lt;td style=&quot;width: 31.1628%;&quot;&gt;&lt;b&gt;설명&lt;/b&gt;&lt;/td&gt;&lt;td style=&quot;width: 35.814%;&quot;&gt;&lt;b&gt;예시&lt;/b&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style=&quot;width: 16.0465%;&quot;&gt;&lt;b&gt;JDBC TimeZone&lt;/b&gt;&lt;/td&gt;&lt;td style=&quot;width: 16.8604%;&quot;&gt;application.yml&lt;/td&gt;&lt;td style=&quot;width: 31.1628%;&quot;&gt;Java 앱이 DB에 연결할 때 사용하는 타임존. 명시 안 하면 JVM TimeZone을 따라감&lt;/td&gt;&lt;td style=&quot;width: 35.814%;&quot;&gt;- hibernate.jdbc.time_zone: UTC&lt;br&gt;- options=-c%20timezone%3DAsia/Seoul&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style=&quot;width: 16.0465%;&quot;&gt;&lt;b&gt;&amp;nbsp;&lt;/b&gt;&lt;/td&gt;&lt;td style=&quot;width: 16.8604%;&quot;&gt;&amp;nbsp;&lt;/td&gt;&lt;td style=&quot;width: 31.1628%;&quot;&gt;&amp;nbsp;&lt;/td&gt;&lt;td style=&quot;width: 35.814%;&quot;&gt;&amp;nbsp;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style=&quot;width: 16.0465%;&quot;&gt;&lt;b&gt;OS TimeZone&lt;/b&gt;&lt;/td&gt;&lt;td style=&quot;width: 16.8604%;&quot;&gt;서버 운영체제&lt;/td&gt;&lt;td style=&quot;width: 31.1628%;&quot;&gt;컨테이너/서버 운영체제의 기본 타임존. 다른 레이어의 기본값이 됨&lt;/td&gt;&lt;td style=&quot;width: 35.814%;&quot;&gt;timedatectl set-timezone UTC&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style=&quot;width: 16.0465%;&quot;&gt;&amp;nbsp;&lt;/td&gt;&lt;td style=&quot;width: 16.8604%;&quot;&gt;Docker&lt;/td&gt;&lt;td style=&quot;width: 31.1628%;&quot;&gt;db 컨테이너 OS 타임존 설정. 명시 안 하면 Docker 호스트 OS를 따라감&lt;/td&gt;&lt;td style=&quot;width: 35.814%;&quot;&gt;db: TZ=Asia/Seoul&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style=&quot;width: 16.0465%;&quot;&gt;&amp;nbsp;&lt;/td&gt;&lt;td style=&quot;width: 16.8604%;&quot;&gt;&amp;nbsp;&lt;/td&gt;&lt;td style=&quot;width: 31.1628%;&quot;&gt;&amp;nbsp;&lt;/td&gt;&lt;td style=&quot;width: 35.814%;&quot;&gt;&amp;nbsp;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style=&quot;width: 16.0465%;&quot;&gt;&lt;b&gt;서버 TimeZone&lt;/b&gt;&lt;/td&gt;&lt;td style=&quot;width: 16.8604%;&quot;&gt;postgresql.conf&lt;/td&gt;&lt;td style=&quot;width: 31.1628%;&quot;&gt;PostgreSQL 서버 전체에 적용되는 타임존. postgresql.conf로 설정하거나 실행 시 -c 옵션으로 지정&lt;/td&gt;&lt;td style=&quot;width: 35.814%;&quot;&gt;timezone = 'UTC'&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style=&quot;width: 16.0465%;&quot;&gt;&amp;nbsp;&lt;/td&gt;&lt;td style=&quot;width: 16.8604%;&quot;&gt;Docker&lt;/td&gt;&lt;td style=&quot;width: 31.1628%;&quot;&gt;postgresql.conf 없이 서버 시작 시 -c 옵션으로 바로 지정&lt;/td&gt;&lt;td style=&quot;width: 35.814%;&quot;&gt;command: postgres -c timezone=Asia/Seoul&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style=&quot;width: 16.0465%;&quot;&gt;&lt;b&gt;DB TimeZone&lt;/b&gt;&lt;/td&gt;&lt;td style=&quot;width: 16.8604%;&quot;&gt;DB 세션/전역&lt;/td&gt;&lt;td style=&quot;width: 31.1628%;&quot;&gt;특정 DB 또는 세션 단위로 적용되는 타임존. 서버 TimeZone보다 우선순위가 높음&lt;/td&gt;&lt;td style=&quot;width: 35.814%;&quot;&gt;SET timezone = 'UTC' 또는 ALTER DATABASE dbname SET timezone = 'UTC'&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style=&quot;width: 16.0465%;&quot;&gt;&lt;b&gt;&amp;nbsp;&lt;/b&gt;&lt;/td&gt;&lt;td style=&quot;width: 16.8604%;&quot;&gt;&amp;nbsp;&lt;/td&gt;&lt;td style=&quot;width: 31.1628%;&quot;&gt;&amp;nbsp;&lt;/td&gt;&lt;td style=&quot;width: 35.814%;&quot;&gt;&amp;nbsp;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style=&quot;width: 16.0465%;&quot;&gt;&lt;b&gt;JVM TimeZone&lt;/b&gt;&lt;/td&gt;&lt;td style=&quot;width: 16.8604%;&quot;&gt;Java 프로세스&lt;/td&gt;&lt;td style=&quot;width: 31.1628%;&quot;&gt;Java 프로세스 전체에 적용되는 타임존. JDBC TimeZone 미설정 시 이걸 따라감&lt;/td&gt;&lt;td style=&quot;width: 35.814%;&quot;&gt;-Duser.timezone=UTC 또는 TimeZone.setDefault(...)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style=&quot;width: 16.0465%;&quot;&gt;&amp;nbsp;&lt;/td&gt;&lt;td style=&quot;width: 16.8604%;&quot;&gt;Docker&lt;/td&gt;&lt;td style=&quot;width: 31.1628%;&quot;&gt;api 컨테이너의 JVM 타임존 설정. Spring Boot 포함 Java 전체에 적용됨&lt;/td&gt;&lt;td style=&quot;width: 35.814%;&quot;&gt;api: TZ=Asia/Seoul&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style=&quot;width: 16.0465%;&quot;&gt;&amp;nbsp;&lt;/td&gt;&lt;td style=&quot;width: 16.8604%;&quot;&gt;&amp;nbsp;&lt;/td&gt;&lt;td style=&quot;width: 31.1628%;&quot;&gt;&amp;nbsp;&lt;/td&gt;&lt;td style=&quot;width: 35.814%;&quot;&gt;&amp;nbsp;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style=&quot;width: 16.0465%;&quot;&gt;&lt;b&gt;psql 클라이언트 TimeZone&lt;/b&gt;&lt;/td&gt;&lt;td style=&quot;width: 16.8604%;&quot;&gt;환경변수&lt;/td&gt;&lt;td style=&quot;width: 31.1628%;&quot;&gt;psql 터미널 접속 시 사용하는 타임존. 서버 TimeZone과 별개로 클라이언트 표시에만 영향&lt;/td&gt;&lt;td style=&quot;width: 35.814%;&quot;&gt;PGTZ=Asia/Seoul&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. JDBC TimeZone 지정&lt;/h3&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot;&gt;&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  JDBC TimeZone 지정&lt;/b&gt;&lt;br&gt;&lt;br&gt;&lt;b&gt;- Java 내에서 DateTime, TimeStamp의 클래스의 경우는 컬럼 타입의 저장 시 타임존의 변화가 일어나는데, JDBC 드라이버에서 어떤 타임존 기준으로 변환할지 모르면, JVM 기본 타임존을 사용합니다.&lt;/b&gt;&lt;br&gt;&lt;br&gt;- Java에서 시간 데이터를 보내거나 받을 때, JDBC 드라이버가 KST(+9) 기준으로 변환해서 데이터베이스와 주고받겠다는 뜻입니다.&lt;/blockquote&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;h4 data-ke-size=&quot;size20&quot;&gt;1.1. MySQL&lt;/h4&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot;&gt;&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  MySQL&lt;/b&gt;&lt;br&gt;&lt;br&gt;&lt;b&gt;- serverTimezone=Asia/Seoul을 파라미터로 전달하는 방식으로 사용이 됨.&lt;/b&gt;&lt;/blockquote&gt;&lt;pre class=&quot;dts&quot;&gt;&lt;code&gt;# application.yml
spring:
  datasource:
    url: jdbc:mysql://xxxxx:35432/dbname?serverTimezone=Asia/Seoul&amp;amp;useUnicode=true&amp;amp;characterEncoding=UTF-8
&lt;/code&gt;&lt;/pre&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;h4 data-ke-size=&quot;size20&quot;&gt;1.2. PostgreSQL&lt;/h4&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot;&gt;&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  PostgreSQL&lt;/b&gt;&lt;br&gt;&lt;br&gt;&lt;b&gt;- 아래에서는 인코딩 된 상태로 ‘options=-c%20timezone%3DAsia/Seoul’를 적용하였습니다.&lt;/b&gt;&lt;br&gt;- 이 URL을 디코딩하게 되면 options=-c timezone=Asia/Seoul로 출력이 됩니다.&lt;/blockquote&gt;&lt;pre class=&quot;dts&quot;&gt;&lt;code&gt;# application.yml
spring:
  datasource:
    url: jdbc:postgresql://localhost:5432/dbname?options=-c%20timezone%3DAsia/Seoul
&lt;/code&gt;&lt;/pre&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;figure data-ke-type=&quot;opengraph&quot; data-og-title=&quot;Initializing the Driver | pgJDBC&quot; data-ke-align=&quot;alignCenter&quot; data-og-description=&quot;Initializing the Driver This section describes how to load and initialize the JDBC driver in your programs. Importing JDBC Any source file that uses JDBC needs to import the java.sql package, using: NOTE You should not import the org.postgresql package unl&quot; data-og-host=&quot;jdbc.postgresql.org&quot; data-og-source-url=&quot;https://jdbc.postgresql.org/documentation/use/#connection-parameters&quot; data-og-url=&quot;https://jdbc.postgresql.org/documentation/use/#connection-parameters&quot;&gt;&lt;a href=&quot;https://jdbc.postgresql.org/documentation/use/#connection-parameters&quot; target=&quot;_blank&quot; data-source-url=&quot;https://jdbc.postgresql.org/documentation/use/#connection-parameters&quot;&gt;&lt;div class=&quot;og-image&quot;&gt;&lt;/div&gt;&lt;div class=&quot;og-text&quot;&gt;&lt;p class=&quot;og-title&quot;&gt;Initializing the Driver | pgJDBC&lt;/p&gt;&lt;p class=&quot;og-desc&quot;&gt;Initializing the Driver This section describes how to load and initialize the JDBC driver in your programs. Importing JDBC Any source file that uses JDBC needs to import the java.sql package, using: NOTE You should not import the org.postgresql package unl&lt;/p&gt;&lt;p class=&quot;og-host&quot;&gt;jdbc.postgresql.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;&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;[참고]&lt;/b&gt;&lt;br&gt;&lt;br&gt;- 실제로 해당 옵션을 추가하더라도 수행하지 않았다는 글이 있습니다.&lt;br&gt;- 이는 pgJDBC 드라이버가 연결 시 JVM 타임존이 강제로 덮어버린다는 이슈가 있습니다.&lt;/blockquote&gt;&lt;figure data-ke-type=&quot;opengraph&quot; data-og-title=&quot;property &amp;quot;options&amp;quot; &amp;quot;-c timezone=America/New_York&amp;quot; does not seem to work · Issue #1626 · pgjdbc/pgjdbc&quot; data-ke-align=&quot;alignCenter&quot; data-og-description=&quot;I'm submitting a ... bug report feature request Describe the issue I'm passing the property options with the value -c timezone=America/New_York to the connection. The timezone for my session is not...&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/pgjdbc/pgjdbc/issues/1626&quot; data-og-image=&quot;https://blog.kakaocdn.net/dna/cBLMka/dJMb9kT72bG/AAAAAAAAAAAAAAAAAAAAAFutiZGX3bo4JaFHpQfjVz3SFz4MwlH6U6gO43nYyD6G/img.png?credential=yqXZFxpELC7KVnFOS48ylbz2pIh7yKj8&amp;amp;expires=1780239599&amp;amp;allow_ip=&amp;amp;allow_referer=&amp;amp;signature=YTQT%2F%2BsMaHjjlK0KqrqAWqwHu%2Bg%3D&quot; data-og-url=&quot;https://github.com/pgjdbc/pgjdbc/issues/1626&quot;&gt;&lt;a href=&quot;https://github.com/pgjdbc/pgjdbc/issues/1626&quot; target=&quot;_blank&quot; data-source-url=&quot;https://github.com/pgjdbc/pgjdbc/issues/1626&quot;&gt;&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://blog.kakaocdn.net/dna/cBLMka/dJMb9kT72bG/AAAAAAAAAAAAAAAAAAAAAFutiZGX3bo4JaFHpQfjVz3SFz4MwlH6U6gO43nYyD6G/img.png?credential=yqXZFxpELC7KVnFOS48ylbz2pIh7yKj8&amp;amp;expires=1780239599&amp;amp;allow_ip=&amp;amp;allow_referer=&amp;amp;signature=YTQT%2F%2BsMaHjjlK0KqrqAWqwHu%2Bg%3D')&quot;&gt; &lt;/div&gt;&lt;div class=&quot;og-text&quot;&gt;&lt;p class=&quot;og-title&quot;&gt;property &quot;options&quot; &quot;-c timezone=America/New_York&quot; does not seem to work · Issue #1626 · pgjdbc/pgjdbc&lt;/p&gt;&lt;p class=&quot;og-desc&quot;&gt;I'm submitting a ... bug report feature request Describe the issue I'm passing the property options with the value -c timezone=America/New_York to the connection. The timezone for my session is not...&lt;/p&gt;&lt;p class=&quot;og-host&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;h3 data-ke-size=&quot;size23&quot;&gt;2. PostgreSQL 타임존 지정&lt;/h3&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot;&gt;&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  PostgreSQL 타임존 지정&lt;/b&gt;&lt;br&gt;&lt;br&gt;- PostgreSQL 세션/서버 타임존을 지정을 합니다. 이를 지정하지 않으면, 서버 OS 타임존을 따라갑니다.&lt;/blockquote&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;h4 data-ke-size=&quot;size20&quot;&gt;2.1. 현재 타임존 확인&lt;/h4&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot;&gt;&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  현재 타임존 확인&lt;/b&gt;&lt;br&gt;&lt;br&gt;&lt;b&gt;- 지정된 타임존의 시간을 확인하고, 현재 NOW() 함수와 타임존을 지정한 시간을 비교하여 확인합니다.&lt;/b&gt;&lt;/blockquote&gt;&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;SHOW timezone;
SELECT NOW(), NOW() AT TIME ZONE 'Asia/Seoul';
&lt;/code&gt;&lt;/pre&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;922&quot; data-origin-height=&quot;375&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kQMgp/dJMcahRWqAc/s357JGSndkSfS0rv6JvTUk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kQMgp/dJMcahRWqAc/s357JGSndkSfS0rv6JvTUk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kQMgp/dJMcahRWqAc/s357JGSndkSfS0rv6JvTUk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkQMgp%2FdJMcahRWqAc%2Fs357JGSndkSfS0rv6JvTUk%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;922&quot; height=&quot;375&quot; data-origin-width=&quot;922&quot; data-origin-height=&quot;375&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;h4 data-ke-size=&quot;size20&quot;&gt;2.2. 타임존 시간 지정&lt;/h4&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot;&gt;&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  타임존 시간 지정&lt;/b&gt;&lt;br&gt;&lt;br&gt;&lt;b&gt;- 세션 레벨, DB 레벨(영구), 유저 레벨(영구)에서 수행하여 다시 조회를 하면, 타임존이 반영된 NOW()가 수행이 되었습니다.&lt;/b&gt;&lt;/blockquote&gt;&lt;pre class=&quot;sql&quot; data-ke-language=&quot;sql&quot;&gt;&lt;code&gt;-- 세션 레벨 (둘 다 동일)
SET TIME ZONE 'Asia/Seoul';      -- SQL 표준 방식
SET timezone TO 'Asia/Seoul';    -- PostgreSQL 방식

-- DB 레벨 (영구)
ALTER DATABASE dbname SET timezone TO 'Asia/Seoul';

-- 유저 레벨 (영구)
ALTER USER username SET timezone TO 'Asia/Seoul';&lt;/code&gt;&lt;/pre&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1292&quot; data-origin-height=&quot;410&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/M6fS9/dJMcad222uj/9vf2tSMYM9jCH3IDYgM20K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/M6fS9/dJMcad222uj/9vf2tSMYM9jCH3IDYgM20K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/M6fS9/dJMcad222uj/9vf2tSMYM9jCH3IDYgM20K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FM6fS9%2FdJMcad222uj%2F9vf2tSMYM9jCH3IDYgM20K%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;1292&quot; height=&quot;410&quot; data-origin-width=&quot;1292&quot; data-origin-height=&quot;410&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. Docker 내에 TimeZone 지정&lt;/h3&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot;&gt;&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  Docker 내에 TimeZone 지정&lt;/b&gt;&lt;br&gt;&lt;br&gt;&lt;b&gt;- 컨테이너 배포 시, 빌드된 이미지를 컨테이너화 하는 과정에서 설정값을 적용합니다.&lt;/b&gt;&lt;br&gt;- postgresql.conf 설정 파일 내에 지정하는 것이 베스트이지만, 배포 시에 설정값을 포함하여서 배포를 합니다.&lt;br&gt;- 아래와 같이 docker compose 과정에서 compose.yaml 파일을 수정하여 TimeZone을 지정합니다.&lt;/blockquote&gt;&lt;pre class=&quot;sql&quot; data-ke-language=&quot;sql&quot;&gt;&lt;code&gt;services:
  db:
    image: postgres:17.9
    container_name: xxxxx-db
    command: postgres -c timezone=Asia/Seoul   # PostgreSQL 서버 타임존 강제 지정
    environment:
      - TZ=Asia/Seoul    # 컨테이너 OS 타임존
      - PGTZ=Asia/Seoul  # psql 클라이언트 타임존
    ports:
      - &quot;5432:5432&quot;
    env_file:
      - ./env/.env.db
    volumes:
      - pgdata:/var/lib/postgresql/data
    restart: always

  api:
    image: xxxxx
    container_name: xxxxx-api
    depends_on:
      - db
    restart: unless-stopped
    volumes:
      - &quot;./uploads:/uploads&quot;
    environment:
      - TZ=Asia/Seoul    # JVM 타임존 → Hibernate가 이걸 기준으로 저장&lt;/code&gt;&lt;/pre&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&lt;b&gt;오늘도&amp;nbsp;감사합니다.&amp;nbsp; &lt;/b&gt;&lt;br&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>DB/이론 및 문법</category>
      <category>DB 타임존</category>
      <category>docker 타임존</category>
      <category>java timezone</category>
      <category>PostgreSQL TimeZone</category>
      <category>TimeZone 지정</category>
      <category>타임존 java</category>
      <category>타임존 postgreSQL</category>
      <category>타임존 시간 지정</category>
      <author>adjh54</author>
      <guid isPermaLink="true">https://adjh54.tistory.com/757</guid>
      <comments>https://adjh54.tistory.com/757#entry757comment</comments>
      <pubDate>Fri, 15 May 2026 18:34:39 +0900</pubDate>
    </item>
    <item>
      <title>[Java] 채널톡 이해하고 활용하기 -2: 채널톡 Open API 키 발급 및 통신 방법</title>
      <link>https://adjh54.tistory.com/756</link>
      <description>&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;해당 글에서는 채널톡 Open API와 서버 간의 통신을 통해서 채널톡에 대한 정보를 조회하는 방법과 키 발급방법에 대해서 알아봅니다&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;
&lt;figure contenteditable=&quot;false&quot; data-ke-type=&quot;emoticon&quot; data-ke-align=&quot;alignCenter&quot; data-emoticon-type=&quot;face&quot; data-emoticon-name=&quot;073&quot; data-emoticon-isanimation=&quot;false&quot; data-emoticon-src=&quot;https://t1.daumcdn.net/axz_keditor/emoticon/face/large/073.png&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/axz_keditor/emoticon/face/large/073.png&quot; width=&quot;80&quot; /&gt;&lt;/figure&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  [참고]&lt;/b&gt;&amp;nbsp;이전에 채널톡을 웹 페이지에 연결하는 방법에 대해 궁금하시면 이전글을 참고하시면 도움이 됩니다.&lt;/blockquote&gt;
&lt;figure id=&quot;og_1778226874480&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;[React] 채널톡 이해하고 활용하기 -1: 연결 및 채팅 기능 확인, @channel.io/channel-web-sdk-loader&quot; data-og-description=&quot;해당 글에서는 채널톡을 연결하고 채팅 기능을 활용하는 방법에 대해서 작성한 글입니다 1) 채널톡  채널톡- 올인원 AI 비즈니스 메신저로, 채팅 상담과 팀 메신저, 마케팅 메시지, 인터넷 전화&quot; data-og-host=&quot;adjh54.tistory.com&quot; data-og-source-url=&quot;https://adjh54.tistory.com/755&quot; data-og-url=&quot;https://adjh54.tistory.com/755&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/ea5IAI/dJMb89yhPyK/UNrcPXPb3G8k12FaHruyBK/img.png?width=800&amp;amp;height=290&amp;amp;face=0_0_800_290,https://scrap.kakaocdn.net/dn/m77c4/dJMb8957R0C/vYYSIy0JvgseB1vlfTTd7k/img.png?width=800&amp;amp;height=290&amp;amp;face=0_0_800_290,https://scrap.kakaocdn.net/dn/bnrHBq/dJMb8957R0E/QrMYOpU2k1SqE3ySDjrQd0/img.png?width=2032&amp;amp;height=1076&amp;amp;face=0_0_2032_1076&quot;&gt;&lt;a href=&quot;https://adjh54.tistory.com/755&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://adjh54.tistory.com/755&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/ea5IAI/dJMb89yhPyK/UNrcPXPb3G8k12FaHruyBK/img.png?width=800&amp;amp;height=290&amp;amp;face=0_0_800_290,https://scrap.kakaocdn.net/dn/m77c4/dJMb8957R0C/vYYSIy0JvgseB1vlfTTd7k/img.png?width=800&amp;amp;height=290&amp;amp;face=0_0_800_290,https://scrap.kakaocdn.net/dn/bnrHBq/dJMb8957R0E/QrMYOpU2k1SqE3ySDjrQd0/img.png?width=2032&amp;amp;height=1076&amp;amp;face=0_0_2032_1076');&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;[React] 채널톡 이해하고 활용하기 -1: 연결 및 채팅 기능 확인, @channel.io/channel-web-sdk-loader&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;해당 글에서는 채널톡을 연결하고 채팅 기능을 활용하는 방법에 대해서 작성한 글입니다 1) 채널톡  채널톡- 올인원 AI 비즈니스 메신저로, 채팅 상담과 팀 메신저, 마케팅 메시지, 인터넷 전화&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;adjh54.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;h2 data-ke-size=&quot;size26&quot;&gt;1) 채널톡&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  채널톡&lt;/b&gt;&lt;br /&gt;- 올인원 AI 비즈니스 메신저로, 채팅 상담과 팀 메신저, 마케팅 메시지, 인터넷 전화(미트), 시나리오형 챗봇(서포트봇), AI 에이전트(ALF) 등의 기능을 제공합니다. &lt;br /&gt;- 주로 고객과 채팅 상담에 이용하고 관리하기 위한 용도로 이용을 합니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 채널톡 Open API를 이용하려면&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  채널톡 Open API를 이용하려면&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- Open API를 이용하기 위해서는 기본적으로 &amp;lsquo;유료 플랜&amp;rsquo;을 이용해야 합니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  무료 플랜으로 유료 플랜 기능을 일부 사용이 가능하지만, Open API 연동과 관련되어서는 사용이 불가능하며, &amp;lsquo;유료플랜&amp;rsquo;을 구매해야 합니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2032&quot; data-origin-height=&quot;1075&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bIsdxV/dJMcabqCs8Y/pBpC7s9pjMxh9yqMSZYMYK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bIsdxV/dJMcabqCs8Y/pBpC7s9pjMxh9yqMSZYMYK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bIsdxV/dJMcabqCs8Y/pBpC7s9pjMxh9yqMSZYMYK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbIsdxV%2FdJMcabqCs8Y%2FpBpC7s9pjMxh9yqMSZYMYK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2032&quot; height=&quot;1075&quot; data-origin-width=&quot;2032&quot; data-origin-height=&quot;1075&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;figure id=&quot;og_1778226941238&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;가격 안내 - 비즈니스 단계에 따른 합리적인 가격&quot; data-og-description=&quot;작은 팀부터 엔터프라이즈 기업까지 단계에 맞는 플랜을 선택하세요.&quot; data-og-host=&quot;channel.io&quot; data-og-source-url=&quot;https://channel.io/ko/pricing?gad_source=1&amp;amp;gad_campaignid=22928580394&amp;amp;gbraid=0AAAAADhdjp0B_NUM-LVKGfF8cvRN6AlLP&amp;amp;gclid=Cj0KCQjwk_bPBhDXARIsACiq8R1bs3XTN_8e8jjNJO315cyNqcl5U8P0rhXOCswxill331pA4a-b2DUaApyoEALw_wcB&quot; data-og-url=&quot;https://channel.io/ko/pricing&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/dn0ADv/dJMb87gaI1F/vjKAHxtSTi8nxKVNSQZLbK/img.jpg?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630&quot;&gt;&lt;a href=&quot;https://channel.io/ko/pricing?gad_source=1&amp;amp;gad_campaignid=22928580394&amp;amp;gbraid=0AAAAADhdjp0B_NUM-LVKGfF8cvRN6AlLP&amp;amp;gclid=Cj0KCQjwk_bPBhDXARIsACiq8R1bs3XTN_8e8jjNJO315cyNqcl5U8P0rhXOCswxill331pA4a-b2DUaApyoEALw_wcB&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://channel.io/ko/pricing?gad_source=1&amp;amp;gad_campaignid=22928580394&amp;amp;gbraid=0AAAAADhdjp0B_NUM-LVKGfF8cvRN6AlLP&amp;amp;gclid=Cj0KCQjwk_bPBhDXARIsACiq8R1bs3XTN_8e8jjNJO315cyNqcl5U8P0rhXOCswxill331pA4a-b2DUaApyoEALw_wcB&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/dn0ADv/dJMb87gaI1F/vjKAHxtSTi8nxKVNSQZLbK/img.jpg?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;가격 안내 - 비즈니스 단계에 따른 합리적인 가격&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;channel.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;h2 data-ke-size=&quot;size26&quot;&gt;2) 채널톡 키 발급받기&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;&amp;nbsp;  채널톡 키 발급받기&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 채널톡의 Open API와 연동을 하기 위해서는 Access Key, Access Secret을 발급받아야 합니다&lt;/b&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;&lt;/span&gt;&lt;/blockquote&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 채널톡에서 설정 &amp;gt; 보안 및 개발 &amp;gt; API &amp;gt; 새 인증 키 만들기 버튼을 누릅니다&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2032&quot; data-origin-height=&quot;1075&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cr2bAi/dJMcaa6jkHB/C06GpSxKnHRat6eZzuLiz1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cr2bAi/dJMcaa6jkHB/C06GpSxKnHRat6eZzuLiz1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cr2bAi/dJMcaa6jkHB/C06GpSxKnHRat6eZzuLiz1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcr2bAi%2FdJMcaa6jkHB%2FC06GpSxKnHRat6eZzuLiz1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2032&quot; height=&quot;1075&quot; data-origin-width=&quot;2032&quot; data-origin-height=&quot;1075&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;h3 data-ke-size=&quot;size23&quot;&gt;2. 새 인증키를 만듭니다.&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2032&quot; data-origin-height=&quot;1075&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dJwQmu/dJMcahRRss0/kV3E0kAlCfLjA2JO8Vr8Dk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dJwQmu/dJMcahRRss0/kV3E0kAlCfLjA2JO8Vr8Dk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dJwQmu/dJMcahRRss0/kV3E0kAlCfLjA2JO8Vr8Dk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdJwQmu%2FdJMcahRRss0%2FkV3E0kAlCfLjA2JO8Vr8Dk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2032&quot; height=&quot;1075&quot; data-origin-width=&quot;2032&quot; data-origin-height=&quot;1075&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;h3 data-ke-size=&quot;size23&quot;&gt;3. 아래와 같이 Access Key, Access Secret이 발급되었습니다&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2032&quot; data-origin-height=&quot;1075&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/5owbl/dJMcaipI839/Y8xIu8dWOLvO8AQOnRWEH1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/5owbl/dJMcaipI839/Y8xIu8dWOLvO8AQOnRWEH1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/5owbl/dJMcaipI839/Y8xIu8dWOLvO8AQOnRWEH1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F5owbl%2FdJMcaipI839%2FY8xIu8dWOLvO8AQOnRWEH1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2032&quot; height=&quot;1075&quot; data-origin-width=&quot;2032&quot; data-origin-height=&quot;1075&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3) 채널톡 OPEN API&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1778227038288&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;What is Open API&quot; data-og-description=&quot;The Channel Open API allows third party developers to build applications that interact with Channel in more complex ways than the integrations we currently provide. By using the API, developers can freely build sophisticated interactions and seamless pipel&quot; data-og-host=&quot;docs.channel.io&quot; data-og-source-url=&quot;https://docs.channel.io/developers/en/articles/What-is-Open-API-c8c76fba&quot; data-og-url=&quot;https://docs.channel.io/developers/en/articles/What-is-Open-API-c8c76fba&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cpYOBI/dJMb887c4bT/GKhwqs8Tmk1OxoQr2lTMJ1/img.jpg?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/JNIzo/dJMb85WXBuk/UfiJfzL576fD8oxLlKgc2K/img.jpg?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/HHp5q/dJMb85WXBum/SofTPkPD8lBnHK3xS5G40k/img.jpg?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630&quot;&gt;&lt;a href=&quot;https://docs.channel.io/developers/en/articles/What-is-Open-API-c8c76fba&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://docs.channel.io/developers/en/articles/What-is-Open-API-c8c76fba&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cpYOBI/dJMb887c4bT/GKhwqs8Tmk1OxoQr2lTMJ1/img.jpg?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/JNIzo/dJMb85WXBuk/UfiJfzL576fD8oxLlKgc2K/img.jpg?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/HHp5q/dJMb85WXBum/SofTPkPD8lBnHK3xS5G40k/img.jpg?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;What is Open API&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;The Channel Open API allows third party developers to build applications that interact with Channel in more complex ways than the integrations we currently provide. By using the API, developers can freely build sophisticated interactions and seamless pipel&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;docs.channel.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;h3 data-ke-size=&quot;size23&quot;&gt;1. API 대분류 목록&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  API 대분류 목록&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- API의 대분류에서는 크게 Bot, UserChat, Channel 등의 Open API 정보를 가져올 수 있는 것으로 확인되었습니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2032&quot; data-origin-height=&quot;1075&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b2qM4j/dJMcafzMtMk/bEOw2Lm28TxtLgkyVFZPMk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b2qM4j/dJMcafzMtMk/bEOw2Lm28TxtLgkyVFZPMk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b2qM4j/dJMcafzMtMk/bEOw2Lm28TxtLgkyVFZPMk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb2qM4j%2FdJMcafzMtMk%2FbEOw2Lm28TxtLgkyVFZPMk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2032&quot; height=&quot;1075&quot; data-origin-width=&quot;2032&quot; data-origin-height=&quot;1075&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style2&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 20.814%;&quot;&gt;&lt;b&gt;대분류&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 79.0698%;&quot;&gt;&lt;b&gt;설명&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 20.814%;&quot;&gt;&lt;b&gt;Channel&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 79.0698%;&quot;&gt;채널(워크스페이스) 자체의 정보를 관리합니다. 채널 이름, 설정, 도메인 등 채널톡 계정 단위의 메타 정보를 조회합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 20.814%;&quot;&gt;&lt;b&gt;User&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 79.0698%;&quot;&gt;채널톡에 접속하는 고객(방문자/회원) 정보를 관리합니다. 고객 목록 조회, 상세 조회, 고객 속성(프로필) 등을 다룹니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 20.814%;&quot;&gt;&lt;b&gt;UserChat&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 79.0698%;&quot;&gt;고객과 상담원 간의 1:1 채팅 대화를 관리합니다. 채팅 생성, 목록/상세 조회, 세션 조회, 메시지 전송/조회 등 상담의 핵심 기능입니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 20.814%;&quot;&gt;&lt;b&gt;Manager&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 79.0698%;&quot;&gt;상담원(매니저) 정보를 관리합니다. 채널에 소속된 상담 담당자 목록 조회, 상세 조회 등을 제공합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 20.814%;&quot;&gt;&lt;b&gt;Bot&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 79.0698%;&quot;&gt;채널톡 내 봇 프로필을 관리합니다. 봇을 통한 자동 메시지 발송 시 사용되는 봇 목록 조회, 상세 조회 등을 제공합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 20.814%;&quot;&gt;&lt;b&gt;Group&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 79.0698%;&quot;&gt;내부 팀(상담원 간) 그룹 채팅을 관리합니다. 그룹 생성/조회/수정, 그룹 메시지 발송, 파일 URL(15분 signed URL) 조회 등을 다룹니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 20.814%;&quot;&gt;&lt;b&gt;Event&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 79.0698%;&quot;&gt;고객의 행동 로그(이벤트 트래킹)를 관리합니다. PageView, UserChatOpen, MarketingView 등 웹사이트/앱 내 사용자 행동을 기록하고 조회합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 20.814%;&quot;&gt;&lt;b&gt;Marketing&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 79.0698%;&quot;&gt;마케팅 캠페인을 관리합니다. 팝업, 인앱 메시지 등 마케팅 메시지의 생성/조회/상태 관리(draft, active, inactive, completed)를 다룹니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 20.814%;&quot;&gt;&lt;b&gt;Webhook&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 79.0698%;&quot;&gt;웹훅 설정을 관리합니다. 특정 이벤트(채팅 생성, 메시지 수신 등) 발생 시 외부 URL로 알림을 보내기 위한 웹훅 CRUD를 제공합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 20.814%;&quot;&gt;&lt;b&gt;Plugin&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 79.0698%;&quot;&gt;채널톡 SDK 연동에 필요한 플러그인을 관리합니다. 플러그인 키(pluginKey) 발급, 채팅 버튼 UI 설정, 프로필봇 설정 등을 다룹니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 20.814%;&quot;&gt;&lt;b&gt;Redirection&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 79.0698%;&quot;&gt;리다이렉션 URL을 생성합니다. 외부 링크에서 채널톡 특정 채팅이나 페이지로 자동 이동시키기 위한 signed URL을 발급합니다.&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;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  종합 API 목록&lt;/b&gt;&lt;/blockquote&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style2&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 11.7442%;&quot;&gt;&lt;b&gt;분류&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 14.8837%;&quot;&gt;&lt;b&gt;Method&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 33.6047%;&quot;&gt;&lt;b&gt;Path&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 39.6512%;&quot;&gt;&lt;b&gt;설명&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 11.7442%;&quot;&gt;&lt;b&gt;Bot&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 14.8837%;&quot;&gt;GET&lt;/td&gt;
&lt;td style=&quot;width: 33.6047%;&quot;&gt;/bots&lt;/td&gt;
&lt;td style=&quot;width: 39.6512%;&quot;&gt;봇 목록 조회&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 11.7442%;&quot;&gt;&lt;b&gt;Bot&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 14.8837%;&quot;&gt;GET&lt;/td&gt;
&lt;td style=&quot;width: 33.6047%;&quot;&gt;/bots/{botId}&lt;/td&gt;
&lt;td style=&quot;width: 39.6512%;&quot;&gt;봇 상세 조회&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 11.7442%;&quot;&gt;&lt;b&gt;Bot&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 14.8837%;&quot;&gt;POST&lt;/td&gt;
&lt;td style=&quot;width: 33.6047%;&quot;&gt;/bots&lt;/td&gt;
&lt;td style=&quot;width: 39.6512%;&quot;&gt;봇 생성&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 11.7442%;&quot;&gt;&lt;b&gt;Bot&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 14.8837%;&quot;&gt;DELETE&lt;/td&gt;
&lt;td style=&quot;width: 33.6047%;&quot;&gt;/bots/{botId}&lt;/td&gt;
&lt;td style=&quot;width: 39.6512%;&quot;&gt;봇 삭제&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 11.7442%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 14.8837%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 33.6047%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 39.6512%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 11.7442%;&quot;&gt;&lt;b&gt;UserChat&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 14.8837%;&quot;&gt;GET&lt;/td&gt;
&lt;td style=&quot;width: 33.6047%;&quot;&gt;/user-chats&lt;/td&gt;
&lt;td style=&quot;width: 39.6512%;&quot;&gt;고객 채팅 목록 조회&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 11.7442%;&quot;&gt;&lt;b&gt;UserChat&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 14.8837%;&quot;&gt;GET&lt;/td&gt;
&lt;td style=&quot;width: 33.6047%;&quot;&gt;/user-chats/{userChatId}&lt;/td&gt;
&lt;td style=&quot;width: 39.6512%;&quot;&gt;고객 채팅 상세 조회&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 11.7442%;&quot;&gt;&lt;b&gt;UserChat&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 14.8837%;&quot;&gt;GET&lt;/td&gt;
&lt;td style=&quot;width: 33.6047%;&quot;&gt;/user-chats/{userChatId}/sessions&lt;/td&gt;
&lt;td style=&quot;width: 39.6512%;&quot;&gt;고객 채팅 세션 조회&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 11.7442%;&quot;&gt;&lt;b&gt;UserChat&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 14.8837%;&quot;&gt;GET&lt;/td&gt;
&lt;td style=&quot;width: 33.6047%;&quot;&gt;/user-chats/{userChatId}/messages&lt;/td&gt;
&lt;td style=&quot;width: 39.6512%;&quot;&gt;고객 채팅 메시지 조회&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 11.7442%;&quot;&gt;&lt;b&gt;UserChat&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 14.8837%;&quot;&gt;POST&lt;/td&gt;
&lt;td style=&quot;width: 33.6047%;&quot;&gt;/user-chats/{userChatId}/messages&lt;/td&gt;
&lt;td style=&quot;width: 39.6512%;&quot;&gt;고객 채팅 메시지 전송 (봇)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 11.7442%;&quot;&gt;&lt;b&gt;UserChat&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 14.8837%;&quot;&gt;GET&lt;/td&gt;
&lt;td style=&quot;width: 33.6047%;&quot;&gt;/user-chats/{userChatId}/messages/file&lt;/td&gt;
&lt;td style=&quot;width: 39.6512%;&quot;&gt;고객 채팅 파일 URL 조회 (15분 signed URL)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 11.7442%;&quot;&gt;&lt;b&gt;UserChat&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 14.8837%;&quot;&gt;PATCH&lt;/td&gt;
&lt;td style=&quot;width: 33.6047%;&quot;&gt;/user-chats/{userChatId}/invite&lt;/td&gt;
&lt;td style=&quot;width: 39.6512%;&quot;&gt;고객 채팅에 상담원 초대&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 11.7442%;&quot;&gt;&lt;b&gt;UserChat&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 14.8837%;&quot;&gt;PATCH&lt;/td&gt;
&lt;td style=&quot;width: 33.6047%;&quot;&gt;/user-chats/{userChatId}/close&lt;/td&gt;
&lt;td style=&quot;width: 39.6512%;&quot;&gt;고객 채팅 종료&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 11.7442%;&quot;&gt;&lt;b&gt;UserChat&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 14.8837%;&quot;&gt;DELETE&lt;/td&gt;
&lt;td style=&quot;width: 33.6047%;&quot;&gt;/user-chats/{userChatId}&lt;/td&gt;
&lt;td style=&quot;width: 39.6512%;&quot;&gt;고객 채팅 삭제&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 11.7442%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 14.8837%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 33.6047%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 39.6512%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 11.7442%;&quot;&gt;&lt;b&gt;Channel&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 14.8837%;&quot;&gt;GET&lt;/td&gt;
&lt;td style=&quot;width: 33.6047%;&quot;&gt;/channel&lt;/td&gt;
&lt;td style=&quot;width: 39.6512%;&quot;&gt;채널 정보 조회&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 11.7442%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 14.8837%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 33.6047%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 39.6512%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 11.7442%;&quot;&gt;&lt;b&gt;Manager&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 14.8837%;&quot;&gt;GET&lt;/td&gt;
&lt;td style=&quot;width: 33.6047%;&quot;&gt;/managers&lt;/td&gt;
&lt;td style=&quot;width: 39.6512%;&quot;&gt;상담원 목록 조회&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 11.7442%;&quot;&gt;&lt;b&gt;Manager&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 14.8837%;&quot;&gt;GET&lt;/td&gt;
&lt;td style=&quot;width: 33.6047%;&quot;&gt;/managers/{managerId}&lt;/td&gt;
&lt;td style=&quot;width: 39.6512%;&quot;&gt;상담원 상세 조회&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 11.7442%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 14.8837%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 33.6047%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 39.6512%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 11.7442%;&quot;&gt;&lt;b&gt;User&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 14.8837%;&quot;&gt;GET&lt;/td&gt;
&lt;td style=&quot;width: 33.6047%;&quot;&gt;/users&lt;/td&gt;
&lt;td style=&quot;width: 39.6512%;&quot;&gt;고객 목록 조회&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 11.7442%;&quot;&gt;&lt;b&gt;User&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 14.8837%;&quot;&gt;GET&lt;/td&gt;
&lt;td style=&quot;width: 33.6047%;&quot;&gt;/users/{userId}&lt;/td&gt;
&lt;td style=&quot;width: 39.6512%;&quot;&gt;고객 상세 조회 (userId)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 11.7442%;&quot;&gt;&lt;b&gt;User&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 14.8837%;&quot;&gt;GET&lt;/td&gt;
&lt;td style=&quot;width: 33.6047%;&quot;&gt;/users/@{memberId}&lt;/td&gt;
&lt;td style=&quot;width: 39.6512%;&quot;&gt;고객 상세 조회 (memberId)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 11.7442%;&quot;&gt;&lt;b&gt;User&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 14.8837%;&quot;&gt;PATCH&lt;/td&gt;
&lt;td style=&quot;width: 33.6047%;&quot;&gt;/users/{userId}&lt;/td&gt;
&lt;td style=&quot;width: 39.6512%;&quot;&gt;고객 정보 수정&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 11.7442%;&quot;&gt;&lt;b&gt;User&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 14.8837%;&quot;&gt;PUT&lt;/td&gt;
&lt;td style=&quot;width: 33.6047%;&quot;&gt;/users/@{memberId}&lt;/td&gt;
&lt;td style=&quot;width: 39.6512%;&quot;&gt;고객 Upsert (없으면 생성, 있으면 수정)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 11.7442%;&quot;&gt;&lt;b&gt;User&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 14.8837%;&quot;&gt;DELETE&lt;/td&gt;
&lt;td style=&quot;width: 33.6047%;&quot;&gt;/users/{userId}&lt;/td&gt;
&lt;td style=&quot;width: 39.6512%;&quot;&gt;고객 삭제 (userId)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 11.7442%;&quot;&gt;&lt;b&gt;User&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 14.8837%;&quot;&gt;DELETE&lt;/td&gt;
&lt;td style=&quot;width: 33.6047%;&quot;&gt;/users/@{memberId}&lt;/td&gt;
&lt;td style=&quot;width: 39.6512%;&quot;&gt;고객 삭제 (memberId)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 11.7442%;&quot;&gt;&lt;b&gt;User&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 14.8837%;&quot;&gt;POST&lt;/td&gt;
&lt;td style=&quot;width: 33.6047%;&quot;&gt;/users/{userId}/block&lt;/td&gt;
&lt;td style=&quot;width: 39.6512%;&quot;&gt;고객 차단&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 11.7442%;&quot;&gt;&lt;b&gt;User&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 14.8837%;&quot;&gt;DELETE&lt;/td&gt;
&lt;td style=&quot;width: 33.6047%;&quot;&gt;/users/{userId}/block&lt;/td&gt;
&lt;td style=&quot;width: 39.6512%;&quot;&gt;고객 차단 해제&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 11.7442%;&quot;&gt;&lt;b&gt;User&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 14.8837%;&quot;&gt;POST&lt;/td&gt;
&lt;td style=&quot;width: 33.6047%;&quot;&gt;/users/{userId}/user-chats&lt;/td&gt;
&lt;td style=&quot;width: 39.6512%;&quot;&gt;고객 채팅 생성&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 11.7442%;&quot;&gt;&lt;b&gt;User&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 14.8837%;&quot;&gt;GET&lt;/td&gt;
&lt;td style=&quot;width: 33.6047%;&quot;&gt;/users/{userId}/user-chats&lt;/td&gt;
&lt;td style=&quot;width: 39.6512%;&quot;&gt;고객의 채팅 목록 조회&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 11.7442%;&quot;&gt;&lt;b&gt;User&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 14.8837%;&quot;&gt;GET&lt;/td&gt;
&lt;td style=&quot;width: 33.6047%;&quot;&gt;/users/{userId}/events&lt;/td&gt;
&lt;td style=&quot;width: 39.6512%;&quot;&gt;고객 이벤트(행동로그) 조회&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 11.7442%;&quot;&gt;&lt;b&gt;User&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 14.8837%;&quot;&gt;PUT&lt;/td&gt;
&lt;td style=&quot;width: 33.6047%;&quot;&gt;/users/@{memberId}/session-jwt/issue&lt;/td&gt;
&lt;td style=&quot;width: 39.6512%;&quot;&gt;고객 세션 JWT 토큰 발급&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 11.7442%;&quot;&gt;&lt;b&gt;User&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 14.8837%;&quot;&gt;-&lt;/td&gt;
&lt;td style=&quot;width: 33.6047%;&quot;&gt;/users/{userId}/touch&lt;/td&gt;
&lt;td style=&quot;width: 39.6512%;&quot;&gt;고객 Touch (2025.12.5 이후 사용 가능)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 11.7442%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 14.8837%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 33.6047%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 39.6512%;&quot;&gt;&amp;nbsp;&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;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  기타 내용은 API 문서에서 확인하실 수 있습니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;figure id=&quot;og_1778227153804&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;What is Open API&quot; data-og-description=&quot;The Channel Open API allows third party developers to build applications that interact with Channel in more complex ways than the integrations we currently provide. By using the API, developers can freely build sophisticated interactions and seamless pipel&quot; data-og-host=&quot;developers.channel.io&quot; data-og-source-url=&quot;https://developers.channel.io/en/articles/What-is-Open-API-c8c76fba&quot; data-og-url=&quot;https://developers.channel.io/en/articles/What-is-Open-API-c8c76fba&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/PGzVn/dJMb8QMggIj/yLbd9DqCudFaHR6I8eC2w1/img.jpg?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/bSWG25/dJMb88F9b1P/SKgpRp4kCm0dpIe9S9kE51/img.jpg?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/bhmRx4/dJMb86O5Ze0/COUEhJOrBoX8Wz2jIdYeK0/img.jpg?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630&quot;&gt;&lt;a href=&quot;https://developers.channel.io/en/articles/What-is-Open-API-c8c76fba&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://developers.channel.io/en/articles/What-is-Open-API-c8c76fba&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/PGzVn/dJMb8QMggIj/yLbd9DqCudFaHR6I8eC2w1/img.jpg?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/bSWG25/dJMb88F9b1P/SKgpRp4kCm0dpIe9S9kE51/img.jpg?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/bhmRx4/dJMb86O5Ze0/COUEhJOrBoX8Wz2jIdYeK0/img.jpg?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;What is Open API&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;The Channel Open API allows third party developers to build applications that interact with Channel in more complex ways than the integrations we currently provide. By using the API, developers can freely build sophisticated interactions and seamless pipel&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;developers.channel.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;h3 data-ke-size=&quot;size23&quot;&gt;2. Bot&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  Bot&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;- 채널톡 내 봇 프로필을 관리합니다. 봇을 통한 자동 메시지 발송 시 사용되는 봇 목록 조회, 상세 조회 등을 제공합니다.&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2032&quot; data-origin-height=&quot;1075&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bG3SBm/dJMcagenOSw/DJrKZkMlpTgK96LNhkjN01/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bG3SBm/dJMcagenOSw/DJrKZkMlpTgK96LNhkjN01/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bG3SBm/dJMcagenOSw/DJrKZkMlpTgK96LNhkjN01/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbG3SBm%2FdJMcagenOSw%2FDJrKZkMlpTgK96LNhkjN01%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2032&quot; height=&quot;1075&quot; data-origin-width=&quot;2032&quot; data-origin-height=&quot;1075&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;table id=&quot;35a16d47-b05b-8028-9714-f0d60f9dc96a&quot; style=&quot;border-collapse: collapse; width: 100%; height: 116px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style2&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;&lt;b&gt;No&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;&lt;b&gt;Method&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;&lt;b&gt;Path&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;&lt;b&gt;설명&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;35a16d47-b05b-8041-849d-dafe6168d929&quot; style=&quot;height: 20px;&quot;&gt;
&lt;td id=&quot;CJ_L&quot; style=&quot;height: 20px;&quot;&gt;&lt;b&gt;1&lt;/b&gt;&lt;/td&gt;
&lt;td id=&quot;F~Ad&quot; style=&quot;height: 20px;&quot;&gt;GET&lt;/td&gt;
&lt;td id=&quot;sJP{&quot; style=&quot;height: 20px;&quot;&gt;/bots&lt;/td&gt;
&lt;td id=&quot;Bhm{&quot; style=&quot;height: 20px;&quot;&gt;봇 목록 조회&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;35a16d47-b05b-8026-9887-dde6b75502fa&quot; style=&quot;height: 20px;&quot;&gt;
&lt;td id=&quot;CJ_L&quot; style=&quot;height: 20px;&quot;&gt;&lt;b&gt;2&lt;/b&gt;&lt;/td&gt;
&lt;td id=&quot;F~Ad&quot; style=&quot;height: 20px;&quot;&gt;GET&lt;/td&gt;
&lt;td id=&quot;sJP{&quot; style=&quot;height: 20px;&quot;&gt;/bots/{botId}&lt;/td&gt;
&lt;td id=&quot;Bhm{&quot; style=&quot;height: 20px;&quot;&gt;봇 상세 조회&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;35a16d47-b05b-804c-b1b8-d5084d953555&quot; style=&quot;height: 20px;&quot;&gt;
&lt;td id=&quot;CJ_L&quot; style=&quot;height: 20px;&quot;&gt;&lt;b&gt;3&lt;/b&gt;&lt;/td&gt;
&lt;td id=&quot;F~Ad&quot; style=&quot;height: 20px;&quot;&gt;POST&lt;/td&gt;
&lt;td id=&quot;sJP{&quot; style=&quot;height: 20px;&quot;&gt;/bots&lt;/td&gt;
&lt;td id=&quot;Bhm{&quot; style=&quot;height: 20px;&quot;&gt;봇 생성&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;35a16d47-b05b-8020-b314-cbed42855bbf&quot; style=&quot;height: 20px;&quot;&gt;
&lt;td id=&quot;CJ_L&quot; style=&quot;height: 20px;&quot;&gt;&lt;b&gt;4&lt;/b&gt;&lt;/td&gt;
&lt;td id=&quot;F~Ad&quot; style=&quot;height: 20px;&quot;&gt;DELETE&lt;/td&gt;
&lt;td id=&quot;sJP{&quot; style=&quot;height: 20px;&quot;&gt;/bots/{botId}&lt;/td&gt;
&lt;td id=&quot;Bhm{&quot; style=&quot;height: 20px;&quot;&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;h3 data-ke-size=&quot;size23&quot;&gt;3. UserChat&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  UserChat&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;- 고객과 상담원 간의 1:1 채팅 대화를 관리합니다. 채팅 생성, 목록/상세 조회, 세션 조회, 메시지 전송/조회 등 상담의 핵심 기능입니다.&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2032&quot; data-origin-height=&quot;1075&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dqibr4/dJMcaicdw6M/1yHbMLGEp0pW12MkM8ALn0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dqibr4/dJMcaicdw6M/1yHbMLGEp0pW12MkM8ALn0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dqibr4/dJMcaicdw6M/1yHbMLGEp0pW12MkM8ALn0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdqibr4%2FdJMcaicdw6M%2F1yHbMLGEp0pW12MkM8ALn0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2032&quot; height=&quot;1075&quot; data-origin-width=&quot;2032&quot; data-origin-height=&quot;1075&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style2&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 5.46512%;&quot;&gt;&lt;b&gt;No&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 13.2558%;&quot;&gt;&lt;b&gt;Method&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 38.7209%;&quot;&gt;&lt;b&gt;Paths&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 42.4419%;&quot;&gt;&lt;b&gt;설명&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 5.46512%;&quot;&gt;1&lt;/td&gt;
&lt;td style=&quot;width: 13.2558%;&quot;&gt;GET&lt;/td&gt;
&lt;td style=&quot;width: 38.7209%;&quot;&gt;/user-chats&lt;/td&gt;
&lt;td style=&quot;width: 42.4419%;&quot;&gt;고객 채팅 목록 조회&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 5.46512%;&quot;&gt;2&lt;/td&gt;
&lt;td style=&quot;width: 13.2558%;&quot;&gt;GET&lt;/td&gt;
&lt;td style=&quot;width: 38.7209%;&quot;&gt;/user-chats/{userChatId}&lt;/td&gt;
&lt;td style=&quot;width: 42.4419%;&quot;&gt;고객 채팅 상세 조회&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 5.46512%;&quot;&gt;3&lt;/td&gt;
&lt;td style=&quot;width: 13.2558%;&quot;&gt;GET&lt;/td&gt;
&lt;td style=&quot;width: 38.7209%;&quot;&gt;/user-chats/{userChatId}/sessions&lt;/td&gt;
&lt;td style=&quot;width: 42.4419%;&quot;&gt;고객 채팅 세션 조회&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 5.46512%;&quot;&gt;4&lt;/td&gt;
&lt;td style=&quot;width: 13.2558%;&quot;&gt;GET&lt;/td&gt;
&lt;td style=&quot;width: 38.7209%;&quot;&gt;/user-chats/{userChatId}/messages&lt;/td&gt;
&lt;td style=&quot;width: 42.4419%;&quot;&gt;고객 채팅 메시지 조회&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 5.46512%;&quot;&gt;5&lt;/td&gt;
&lt;td style=&quot;width: 13.2558%;&quot;&gt;POST&lt;/td&gt;
&lt;td style=&quot;width: 38.7209%;&quot;&gt;/user-chats/{userChatId}/messages&lt;/td&gt;
&lt;td style=&quot;width: 42.4419%;&quot;&gt;고객 채팅 메시지 전송 (봇)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 5.46512%;&quot;&gt;6&lt;/td&gt;
&lt;td style=&quot;width: 13.2558%;&quot;&gt;GET&lt;/td&gt;
&lt;td style=&quot;width: 38.7209%;&quot;&gt;/user-chats/{userChatId}/messages/file&lt;/td&gt;
&lt;td style=&quot;width: 42.4419%;&quot;&gt;고객 채팅 파일 URL 조회 (15분 signed URL)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 5.46512%;&quot;&gt;7&lt;/td&gt;
&lt;td style=&quot;width: 13.2558%;&quot;&gt;PATCH&lt;/td&gt;
&lt;td style=&quot;width: 38.7209%;&quot;&gt;/user-chats/{userChatId}/invite&lt;/td&gt;
&lt;td style=&quot;width: 42.4419%;&quot;&gt;고객 채팅에 상담원 초대&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 5.46512%;&quot;&gt;8&lt;/td&gt;
&lt;td style=&quot;width: 13.2558%;&quot;&gt;PATCH&lt;/td&gt;
&lt;td style=&quot;width: 38.7209%;&quot;&gt;/user-chats/{userChatId}/close&lt;/td&gt;
&lt;td style=&quot;width: 42.4419%;&quot;&gt;고객 채팅 종료&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 5.46512%;&quot;&gt;9&lt;/td&gt;
&lt;td style=&quot;width: 13.2558%;&quot;&gt;DELETE&lt;/td&gt;
&lt;td style=&quot;width: 38.7209%;&quot;&gt;/user-chats/{userChatId}&lt;/td&gt;
&lt;td style=&quot;width: 42.4419%;&quot;&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;h3 data-ke-size=&quot;size23&quot;&gt;5. Channel&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  Channel&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;- 채널(워크스페이스) 자체의 정보를 관리합니다. 채널 이름, 설정, 도메인 등 채널톡 계정 단위의 메타 정보를 조회합니다.&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2032&quot; data-origin-height=&quot;1075&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/2kEQJ/dJMcaarHOoR/ThZoCYnbGIQyeiqGnx8to0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/2kEQJ/dJMcaarHOoR/ThZoCYnbGIQyeiqGnx8to0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/2kEQJ/dJMcaarHOoR/ThZoCYnbGIQyeiqGnx8to0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F2kEQJ%2FdJMcaarHOoR%2FThZoCYnbGIQyeiqGnx8to0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2032&quot; height=&quot;1075&quot; data-origin-width=&quot;2032&quot; data-origin-height=&quot;1075&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style2&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;No&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;Method&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;Path&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;설명&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;GET&lt;/td&gt;
&lt;td&gt;/channel&lt;/td&gt;
&lt;td&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;h3 data-ke-size=&quot;size23&quot;&gt;6. Manager&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  Manager&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 상담원(매니저) 정보를 관리합니다. 채널에 소속된 상담 담당자 목록 조회, 상세 조회 등을 제공합니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 60px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style2&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;&lt;b&gt;No&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;&lt;b&gt;Method&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;&lt;b&gt;Path&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;&lt;b&gt;설명&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;1&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;GET&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;/managers&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;상담원 목록 조회&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;2&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;GET&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;/managers/{managerId}&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2032&quot; data-origin-height=&quot;1075&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c57FFk/dJMcadWdk0G/eXOGSUQC7nLEgbu4QDLW21/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c57FFk/dJMcadWdk0G/eXOGSUQC7nLEgbu4QDLW21/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c57FFk/dJMcadWdk0G/eXOGSUQC7nLEgbu4QDLW21/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc57FFk%2FdJMcadWdk0G%2FeXOGSUQC7nLEgbu4QDLW21%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2032&quot; height=&quot;1075&quot; data-origin-width=&quot;2032&quot; data-origin-height=&quot;1075&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;h3 data-ke-size=&quot;size23&quot;&gt;7. User&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  User&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 채널톡에 접속하는 고객(방문자/회원) 정보를 관리합니다. 고객 목록 조회, 상세 조회, 고객 속성(프로필) 등을 다룹니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2032&quot; data-origin-height=&quot;1075&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bOpK1H/dJMcaarHOAw/UccDrqH7NEAs2A1LNRktR1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bOpK1H/dJMcaarHOAw/UccDrqH7NEAs2A1LNRktR1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bOpK1H/dJMcaarHOAw/UccDrqH7NEAs2A1LNRktR1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbOpK1H%2FdJMcaarHOAw%2FUccDrqH7NEAs2A1LNRktR1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2032&quot; height=&quot;1075&quot; data-origin-width=&quot;2032&quot; data-origin-height=&quot;1075&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style2&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;No&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;Method&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;Path&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;설명&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;GET&lt;/td&gt;
&lt;td&gt;/users&lt;/td&gt;
&lt;td&gt;고객 목록 조회&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;GET&lt;/td&gt;
&lt;td&gt;/users/{userId}&lt;/td&gt;
&lt;td&gt;고객 상세 조회 (userId)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;GET&lt;/td&gt;
&lt;td&gt;/users/@{memberId}&lt;/td&gt;
&lt;td&gt;고객 상세 조회 (memberId)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;PATCH&lt;/td&gt;
&lt;td&gt;/users/{userId}&lt;/td&gt;
&lt;td&gt;고객 정보 수정&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;PUT&lt;/td&gt;
&lt;td&gt;/users/@{memberId}&lt;/td&gt;
&lt;td&gt;고객 Upsert (없으면 생성, 있으면 수정)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;DELETE&lt;/td&gt;
&lt;td&gt;/users/{userId}&lt;/td&gt;
&lt;td&gt;고객 삭제 (userId)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;DELETE&lt;/td&gt;
&lt;td&gt;/users/@{memberId}&lt;/td&gt;
&lt;td&gt;고객 삭제 (memberId)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;POST&lt;/td&gt;
&lt;td&gt;/users/{userId}/block&lt;/td&gt;
&lt;td&gt;고객 차단&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;td&gt;DELETE&lt;/td&gt;
&lt;td&gt;/users/{userId}/block&lt;/td&gt;
&lt;td&gt;고객 차단 해제&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;POST&lt;/td&gt;
&lt;td&gt;/users/{userId}/user-chats&lt;/td&gt;
&lt;td&gt;고객 채팅 생성&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;11&lt;/td&gt;
&lt;td&gt;GET&lt;/td&gt;
&lt;td&gt;/users/{userId}/user-chats&lt;/td&gt;
&lt;td&gt;고객의 채팅 목록 조회&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;12&lt;/td&gt;
&lt;td&gt;GET&lt;/td&gt;
&lt;td&gt;/users/{userId}/events&lt;/td&gt;
&lt;td&gt;고객 이벤트(행동로그) 조회&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;13&lt;/td&gt;
&lt;td&gt;PUT&lt;/td&gt;
&lt;td&gt;/users/@{memberId}/session-jwt/issue&lt;/td&gt;
&lt;td&gt;고객 세션 JWT 토큰 발급&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;14&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;/users/{userId}/touch&lt;/td&gt;
&lt;td&gt;고객 Touch (2025.12.5 이후 사용 가능)&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;h2 data-ke-size=&quot;size26&quot;&gt;4) 채널톡 OPEN API 활용하기&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. application.yml&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  application.yml&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;- 아래와 같이 yml 파일 내에 발급받은 access-key, access-secret을 추가하였습니다. 해당 방식은 .env 파일 내에서 불러오는 형태로 구성하였고, 이를 참조하는 형태입니다.&lt;/blockquote&gt;
&lt;pre class=&quot;sql&quot; data-ke-language=&quot;sql&quot;&gt;&lt;code&gt;channel-talk:
  access-key: ${CHANNEL_TALK_ACCESS_KEY}
  access-secret: ${CHANNEL_TALK_ACCESS_SECRET}
  base-url: https://api.channel.io/open/v5&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  [참고] .env 파일로 환경정보 관리 방법&lt;/b&gt;&lt;/blockquote&gt;
&lt;figure id=&quot;og_1778227507201&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;[Java] Spring Boot 환경에서 시스템 변수 .env 파일 지정 및 활용 방법&quot; data-og-description=&quot;해당 글에서는 Spring Boot 개발 환경에서 .env 파일을 시스템 변수로 지정하는 방법과 이를 불러오는 다양한 방법에 대해서 알아봅니다1) Spring Boot 환경에서 .env 파일을 시스템 변수로 적용하는 이&quot; data-og-host=&quot;adjh54.tistory.com&quot; data-og-source-url=&quot;https://adjh54.tistory.com/722&quot; data-og-url=&quot;https://adjh54.tistory.com/722&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/UUJku/dJMb9aKJiGS/SKubmiCVSWgv70sNKhwsz0/img.png?width=800&amp;amp;height=432&amp;amp;face=0_0_800_432,https://scrap.kakaocdn.net/dn/LEadC/dJMb82eRfpU/r1AnfmEAP3luhTHr5dFE6K/img.png?width=800&amp;amp;height=432&amp;amp;face=0_0_800_432,https://scrap.kakaocdn.net/dn/cyyopn/dJMb8957R8H/8Sk0GbPBmiHixqNjo6ccZ1/img.png?width=1165&amp;amp;height=815&amp;amp;face=0_0_1165_815&quot;&gt;&lt;a href=&quot;https://adjh54.tistory.com/722&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://adjh54.tistory.com/722&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/UUJku/dJMb9aKJiGS/SKubmiCVSWgv70sNKhwsz0/img.png?width=800&amp;amp;height=432&amp;amp;face=0_0_800_432,https://scrap.kakaocdn.net/dn/LEadC/dJMb82eRfpU/r1AnfmEAP3luhTHr5dFE6K/img.png?width=800&amp;amp;height=432&amp;amp;face=0_0_800_432,https://scrap.kakaocdn.net/dn/cyyopn/dJMb8957R8H/8Sk0GbPBmiHixqNjo6ccZ1/img.png?width=1165&amp;amp;height=815&amp;amp;face=0_0_1165_815');&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;[Java] Spring Boot 환경에서 시스템 변수 .env 파일 지정 및 활용 방법&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;해당 글에서는 Spring Boot 개발 환경에서 .env 파일을 시스템 변수로 지정하는 방법과 이를 불러오는 다양한 방법에 대해서 알아봅니다1) Spring Boot 환경에서 .env 파일을 시스템 변수로 적용하는 이&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;adjh54.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;h3 data-ke-size=&quot;size23&quot;&gt;2. ChannelTalkConfig&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  ChannelTalkConfig&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 해당 부분에서는 RestClient를 이용하여서 ChannelTalk과 연동하기 위한 클라이언트를 구성합니다.&lt;/b&gt;&lt;br /&gt;- 클라이언트는 채널톡과 통신을 위한 x-access-key, x-access-secret를 헤더에 포함하여 통신을 수행합니다.&lt;/blockquote&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;import com.service.client.ChannelTalkClient;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestClient;
import org.springframework.web.client.support.RestClientAdapter;
import org.springframework.web.service.invoker.HttpServiceProxyFactory;

/**
 * 채널톡 RestClient + HttpServiceProxyFactory 설정
 *
 * @author : leejonghoon
 * @fileName : ChannelTalkClientConfig
 * @since : 26. 5. 7.
 */
@Configuration
@Slf4j
public class ChannelTalkConfig {

    @Value(&quot;${channel-talk.access-key}&quot;)
    private String accessKey;

    @Value(&quot;${channel-talk.access-secret}&quot;)
    private String accessSecret;

    @Bean
    public ChannelTalkClient channelTalkClient() {
        RestClient restClient = RestClient.builder()
                .baseUrl(&quot;&amp;lt;https://api.channel.io&amp;gt;&quot;)
                .defaultHeader(&quot;x-access-key&quot;, accessKey)
                .defaultHeader(&quot;x-access-secret&quot;, accessSecret)
                .defaultHeader(&quot;Content-Type&quot;, &quot;application/json&quot;)
                .build();

        HttpServiceProxyFactory factory = HttpServiceProxyFactory
                .builderFor(RestClientAdapter.create(restClient))
                .build();

        ChannelTalkClient client = factory.createClient(ChannelTalkClient.class);
        return client;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. ChannelTalkClient&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  ChannelTalkClient&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;- 이전에 구성한 채널톡 인스턴스를 기반으로 헤더에 내용을 모두 포함하였습니다.&lt;br /&gt;- 이를 기반으로 Spring Boot4에서 추가된 Service Interface Clients를 통하여 외부 통신을 수행합니다.&lt;/blockquote&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;package com.daekyocns.homepage.service.client;

import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.service.annotation.*;

import java.util.List;
import java.util.Map;

/**
 * 채널톡 Open API HTTP Service Client
 *
 * @author : leejonghoon
 * @fileName : ChannelTalkClient
 * @since : 26. 5. 7.
 */
@HttpExchange(contentType = MediaType.APPLICATION_JSON_VALUE)
public interface ChannelTalkClient {

    /**
     * 채널 정보를 조회합니다.
     *
     * @return
     */
    @GetExchange(&quot;/open/v5/channel&quot;)
    Map&amp;lt;String, Object&amp;gt; getChannel();

    /**
     * 고객 정보를 모두 조회합니다.
     *
     * @param after
     * @param limit
     * @return
     */
    @GetExchange(&quot;/open/v5/users&quot;)
    Map&amp;lt;String, Object&amp;gt; getUsers(
            @RequestParam(required = false) String after,
            @RequestParam(defaultValue = &quot;30&quot;) int limit
    );

    /**
     * 고객 정보를 상세 조회합니다.
     *
     * @param userId
     * @return
     */
    @GetExchange(&quot;/open/v5/users/{userId}&quot;)
    Map&amp;lt;String, Object&amp;gt; getUser(@PathVariable String userId);

    /**
     * 사용자의 이벤트(행동로그)를 조회합니다.
     *
     * @param userId
     * @param sortOrder
     * @param since
     * @param limit
     * @return
     */
    @GetExchange(&quot;/open/v5/users/{userId}/events&quot;)
    Map&amp;lt;String, Object&amp;gt; getUserEvents(
            @PathVariable String userId,
            @RequestParam(defaultValue = &quot;desc&quot;) String sortOrder,
            @RequestParam(required = false) Long since,
            @RequestParam(defaultValue = &quot;25&quot;) int limit
    );

    /**
     * 고객과의 채팅 정보들을 조회합니다.
     *
     * @param state
     * @param sortOrder
     * @param since
     * @param limit
     * @return
     */
    @GetExchange(&quot;/open/v5/user-chats&quot;)
    Map&amp;lt;String, Object&amp;gt; getUserChats(
            @RequestParam(defaultValue = &quot;opened&quot;) String state,
            @RequestParam(defaultValue = &quot;desc&quot;) String sortOrder,
            @RequestParam(required = false) String since,
            @RequestParam(defaultValue = &quot;30&quot;) int limit
    );

    /**
     * 고객과의 채팅 정보를 상세 조회합니다.
     *
     * @param userChatId
     * @return
     */
    @GetExchange(&quot;/open/v5/user-chats/{userChatId}&quot;)
    Map&amp;lt;String, Object&amp;gt; getUserChat(@PathVariable String userChatId);

    /**
     * 사용자와의 채팅 세션을 조회합니다.
     *
     * @param userChatId
     * @return
     */
    @GetExchange(&quot;/open/v5/user-chats/{userChatId}/sessions&quot;)
    Map&amp;lt;String, Object&amp;gt; getUserChatSessions(@PathVariable String userChatId);

    /**
     * 사용자와의 채팅을 생성합니다.
     *
     * @param userId
     * @return
     */
    @PostExchange(&quot;/open/v5/users/{userId}/user-chats&quot;)
    Map&amp;lt;String, Object&amp;gt; createUserChat(@PathVariable String userId);

    /**
     * 사용자와의 메시지 정보를 조회합니다.
     *
     * @param userChatId
     * @param since
     * @param limit
     * @return
     */
    @GetExchange(&quot;/open/v5/user-chats/{userChatId}/messages&quot;)
    Map&amp;lt;String, Object&amp;gt; getUserChatMessages(
            @PathVariable String userChatId,
            @RequestParam(required = false) String since,
            @RequestParam(defaultValue = &quot;50&quot;) int limit
    );

    /**
     * 상담원(Manager)들을 조회합니다.
     *
     * @return
     */
    @GetExchange(&quot;/open/v5/managers&quot;)
    Map&amp;lt;String, Object&amp;gt; getManagers();

    /**
     * 상담원(Manager)에 대해 상세히 조회합니다.
     *
     * @param managerId
     * @return
     */
    @GetExchange(&quot;/open/v5/managers/{managerId}&quot;)
    Map&amp;lt;String, Object&amp;gt; getManager(@PathVariable String managerId);

    /**
     * 봇(Bot) 정보들을 조회합니다.
     *
     * @param since
     * @param limit
     * @return
     */
    @GetExchange(&quot;/open/v5/bots&quot;)
    Map&amp;lt;String, Object&amp;gt; getBots(
            @RequestParam(required = false) Long since,
            @RequestParam(defaultValue = &quot;25&quot;) int limit
    );

    /**
     * 봇(Bot) 정보를 상세 조회합니다.
     *
     * @param botId
     * @return
     */
    @GetExchange(&quot;/open/v5/bots/{botId}&quot;)
    Map&amp;lt;String, Object&amp;gt; getBot(@PathVariable String botId);

    /**
     * 웹훅 목록을 조회합니다.
     *
     * @param since
     * @param limit
     * @return
     */
    @GetExchange(&quot;/open/v5/webhooks&quot;)
    Map&amp;lt;String, Object&amp;gt; getWebhooks(
            @RequestParam(required = false) Long since,
            @RequestParam(defaultValue = &quot;25&quot;) int limit
    );

    /**
     * 웹훅 정보를 상세 조회합니다.
     *
     * @param webhookId
     * @return
     */
    @GetExchange(&quot;/open/v5/webhooks/{webhookId}&quot;)
    Map&amp;lt;String, Object&amp;gt; getWebhook(@PathVariable String webhookId);

    /**
     * 웹훅을 생성합니다.
     *
     * @param body
     * @return
     */
    @PostExchange(&quot;/open/v5/webhooks&quot;)
    Map&amp;lt;String, Object&amp;gt; createWebhook(@RequestBody Map&amp;lt;String, Object&amp;gt; body);

    /**
     * 웹훅 정보를 수정합니다.
     *
     * @param webhookId
     * @param body
     * @return
     */
    @PutExchange(&quot;/open/v5/webhooks/{webhookId}&quot;)
    Map&amp;lt;String, Object&amp;gt; updateWebhook(@PathVariable String webhookId, @RequestBody Map&amp;lt;String, Object&amp;gt; body);

    /**
     * 웹훅을 삭제합니다.
     *
     * @param webhookId
     * @return
     */
    @DeleteExchange(&quot;/open/v5/webhooks/{webhookId}&quot;)
    Map&amp;lt;String, Object&amp;gt; deleteWebhook(@PathVariable String webhookId);

    /**
     * 캠페인(마케팅) 목록을 조회합니다.
     *
     * @param since
     * @param limit
     * @param states draft|active|inactive|completed
     * @return
     */
    @GetExchange(&quot;/open/v5/mkt/campaigns&quot;)
    Map&amp;lt;String, Object&amp;gt; getCampaigns(
            @RequestParam(required = false) Long since,
            @RequestParam(defaultValue = &quot;25&quot;) int limit,
            @RequestParam(required = false) List&amp;lt;String&amp;gt; states
    );

    /**
     * 그룹 메시지의 파일 URL을 조회합니다. (15분 signed URL)
     *
     * @param groupId
     * @param key
     * @return
     */
    @GetExchange(&quot;/open/v5/groups/{groupId}/messages/file&quot;)
    Map&amp;lt;String, Object&amp;gt; getGroupFileUrl(
            @PathVariable String groupId,
            @RequestParam String key
    );
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  [참고]&lt;/b&gt;&amp;nbsp;Service Interface Clients에 대해 궁금하시면 아래의 글을 참고하시면 도움이 됩니다.&lt;/blockquote&gt;
&lt;figure id=&quot;og_1778227570113&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;[Trend] Spring Boot 4 Release Note 읽어보기&quot; data-og-description=&quot;해당 글에서는 Spring Boot 4가 출시되어 주요 내용에 대한 Release Note를 읽어보기 위해 작성한 글입니다. 1) Spring Boot 4  Spring Boot 4- 2025년 11월 공식 릴리스된 Spring Boot의 메이저 버전 업그레이드로, &quot; data-og-host=&quot;adjh54.tistory.com&quot; data-og-source-url=&quot;https://adjh54.tistory.com/734&quot; data-og-url=&quot;https://adjh54.tistory.com/734&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bbucGJ/dJMb9frJFfb/krOhP2UjgX6E8LrFwAfTpK/img.png?width=800&amp;amp;height=431&amp;amp;face=0_0_800_431,https://scrap.kakaocdn.net/dn/qB45t/dJMb9lMfHed/1l5OzGQ874OW1UcsurnwA1/img.png?width=800&amp;amp;height=431&amp;amp;face=0_0_800_431,https://scrap.kakaocdn.net/dn/vxxcU/dJMb8Qeqpbv/SHAOMh6hXoRxoLKkggzuN1/img.png?width=1036&amp;amp;height=624&amp;amp;face=0_0_1036_624&quot;&gt;&lt;a href=&quot;https://adjh54.tistory.com/734&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://adjh54.tistory.com/734&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bbucGJ/dJMb9frJFfb/krOhP2UjgX6E8LrFwAfTpK/img.png?width=800&amp;amp;height=431&amp;amp;face=0_0_800_431,https://scrap.kakaocdn.net/dn/qB45t/dJMb9lMfHed/1l5OzGQ874OW1UcsurnwA1/img.png?width=800&amp;amp;height=431&amp;amp;face=0_0_800_431,https://scrap.kakaocdn.net/dn/vxxcU/dJMb8Qeqpbv/SHAOMh6hXoRxoLKkggzuN1/img.png?width=1036&amp;amp;height=624&amp;amp;face=0_0_1036_624');&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;[Trend] Spring Boot 4 Release Note 읽어보기&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;해당 글에서는 Spring Boot 4가 출시되어 주요 내용에 대한 Release Note를 읽어보기 위해 작성한 글입니다. 1) Spring Boot 4  Spring Boot 4- 2025년 11월 공식 릴리스된 Spring Boot의 메이저 버전 업그레이드로,&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;adjh54.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;h3 data-ke-size=&quot;size23&quot;&gt;4. ChannelTalkService&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  ChannelTalkService&lt;br /&gt;&lt;/b&gt;&lt;br /&gt;&lt;b&gt;- 외부 통신 처리 이후에 비즈니스 로직을 처리하기 위한 Service 레이어를 구성하였습니다.&lt;/b&gt;&lt;br /&gt;- 해당 경우 비즈니스 로직을 처리하기 위한 클래스로 사용이 됩니다.&lt;/blockquote&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;package com.service;

import com.service.client.ChannelTalkClient;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Map;

/**
 * 채널톡 비즈니스 로직 처리 Service
 *
 * @author : leejonghoon
 * @fileName : ChannelTalkService
 * @since : 26. 5. 7.
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class ChannelTalkService {

    private final ChannelTalkClient channelTalkClient;

    public Map&amp;lt;String, Object&amp;gt; getChannel() {
        return channelTalkClient.getChannel();
    }

    public Map&amp;lt;String, Object&amp;gt; getUsers(String after, int limit) {
        return channelTalkClient.getUsers(after, limit);
    }

    public Map&amp;lt;String, Object&amp;gt; getUser(String userId) {
        return channelTalkClient.getUser(userId);
    }

    public Map&amp;lt;String, Object&amp;gt; getUserEvents(String userId, String sortOrder, Long since, int limit) {
        return channelTalkClient.getUserEvents(userId, sortOrder, since, limit);
    }

    public Map&amp;lt;String, Object&amp;gt; getUserChats(String state, String sortOrder, String since, int limit) {
        return channelTalkClient.getUserChats(state, sortOrder, since, limit);
    }

    public Map&amp;lt;String, Object&amp;gt; getUserChat(String userChatId) {
        return channelTalkClient.getUserChat(userChatId);
    }

    public Map&amp;lt;String, Object&amp;gt; getUserChatSessions(String userChatId) {
        return channelTalkClient.getUserChatSessions(userChatId);
    }

    public Map&amp;lt;String, Object&amp;gt; createUserChat(String userId) {
        return channelTalkClient.createUserChat(userId);
    }

    public Map&amp;lt;String, Object&amp;gt; getUserChatMessages(String userChatId, String since, int limit) {
        return channelTalkClient.getUserChatMessages(userChatId, since, limit);
    }

    public Map&amp;lt;String, Object&amp;gt; getManagers() {
        return channelTalkClient.getManagers();
    }

    public Map&amp;lt;String, Object&amp;gt; getManager(String managerId) {
        return channelTalkClient.getManager(managerId);
    }

    public Map&amp;lt;String, Object&amp;gt; getBots(Long since, int limit) {
        return channelTalkClient.getBots(since, limit);
    }

    public Map&amp;lt;String, Object&amp;gt; getBot(String botId) {
        return channelTalkClient.getBot(botId);
    }

    public Map&amp;lt;String, Object&amp;gt; getWebhooks(Long since, int limit) {
        return channelTalkClient.getWebhooks(since, limit);
    }

    public Map&amp;lt;String, Object&amp;gt; getWebhook(String webhookId) {
        return channelTalkClient.getWebhook(webhookId);
    }

    public Map&amp;lt;String, Object&amp;gt; createWebhook(Map&amp;lt;String, Object&amp;gt; body) {
        return channelTalkClient.createWebhook(body);
    }

    public Map&amp;lt;String, Object&amp;gt; updateWebhook(String webhookId, Map&amp;lt;String, Object&amp;gt; body) {
        return channelTalkClient.updateWebhook(webhookId, body);
    }

    public Map&amp;lt;String, Object&amp;gt; deleteWebhook(String webhookId) {
        return channelTalkClient.deleteWebhook(webhookId);
    }

    public Map&amp;lt;String, Object&amp;gt; getCampaigns(Long since, int limit, List&amp;lt;String&amp;gt; states) {
        return channelTalkClient.getCampaigns(since, limit, states);
    }

    public Map&amp;lt;String, Object&amp;gt; getGroupFileUrl(String groupId, String key) {
        return channelTalkClient.getGroupFileUrl(groupId, key);
    }
}&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;h3 data-ke-size=&quot;size23&quot;&gt;5. ChannelTalkController&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  ChannelTalkController&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 아래와 같은 정보들을 조회하는 엔드포인트를 구성하였습니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 430px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style2&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;No&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;Method&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;Path&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;설명&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;1&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;GET&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;/channel&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;채널 정보 조회&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;2&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;GET&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;/users&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;고객 목록 조회&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;3&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;GET&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;/users/{userId}&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;고객 상세 조회&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;4&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;GET&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;/users/{userId}/events&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;사용자 이벤트(행동로그) 조회&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;5&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;GET&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;/user-chats&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;고객 채팅 목록 조회&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;6&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;GET&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;/user-chats/{userChatId}&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;고객 채팅 상세 조회&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;7&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;GET&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;/user-chats/{userChatId}/sessions&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;고객 채팅 세션 조회&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;8&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;POST&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;/users/{userId}/user-chats&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;고객 채팅 생성&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;9&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;GET&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;/user-chats/{userChatId}/messages&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;고객 채팅 메시지 조회&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;10&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;GET&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;/managers&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;상담원 목록 조회&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;11&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;GET&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;/managers/{managerId}&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;상담원 상세 조회&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;12&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;GET&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;/bots&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;봇 목록 조회&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;13&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;GET&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;/bots/{botId}&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;봇 상세 조회&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;14&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;GET&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;/tags&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;태그 목록 조회&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;15&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;GET&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;/webhooks&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;웹훅 목록 조회&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;16&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;GET&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;/webhooks/{webhookId}&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;웹훅 상세 조회&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;17&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;POST&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;/webhooks&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;웹훅 생성&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;18&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;PUT&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;/webhooks/{webhookId}&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;웹훅 수정&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;19&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;DELETE&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;/webhooks/{webhookId}&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;웹훅 삭제&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;20&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;GET&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;/campaigns&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;캠페인(마케팅) 목록 조회&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;21&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;GET&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;/groups/{groupId}/messages/file&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;그룹 메시지 파일 URL 조회 (15분 signed URL)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;package com.controller;

import com.model.common.ApiResponseWrapper;
import com.model.common.SuccessCode;
import com.service.ChannelTalkService;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.util.List;
import java.util.Map;

/**
 * 채널톡 Open API Controller
 *
 * @author leejonghoon
 * @since 26. 5. 7.
 */
@RestController
@RequestMapping(&quot;/api/channelTalk&quot;)
@RequiredArgsConstructor
public class ChannelTalkController {

    private final ChannelTalkService channelTalkService;

    /**
     * 공통 응답 헬퍼
     *
     * @param result
     * @return
     */
    private ResponseEntity&amp;lt;ApiResponseWrapper&amp;lt;Map&amp;lt;String, Object&amp;gt;&amp;gt;&amp;gt; ok(Map&amp;lt;String, Object&amp;gt; result) {
        return new ResponseEntity&amp;lt;&amp;gt;(
                ApiResponseWrapper.&amp;lt;Map&amp;lt;String, Object&amp;gt;&amp;gt;builder()
                        .result(result)
                        .resultCode(SuccessCode.SELECT_SUCCESS.getStatus())
                        .resultMsg(SuccessCode.SELECT_SUCCESS.getMessage())
                        .build(),
                HttpStatus.OK
        );
    }

    // ─── 채널 정보 ───────────────────────────────────────

    /**
     * 채널 정보를 조회합니다.
     *
     * @return
     */
    @GetMapping(&quot;/channel&quot;)
    public ResponseEntity&amp;lt;ApiResponseWrapper&amp;lt;Map&amp;lt;String, Object&amp;gt;&amp;gt;&amp;gt; getChannel() {
        return ok(channelTalkService.getChannel());
    }

    // ─── 고객(User) ──────────────────────────────────────

    /**
     * 고객 목록을 조회합니다.
     *
     * @param after
     * @param limit
     * @return
     */
    @GetMapping(&quot;/users&quot;)
    public ResponseEntity&amp;lt;ApiResponseWrapper&amp;lt;Map&amp;lt;String, Object&amp;gt;&amp;gt;&amp;gt; getUsers(
            @RequestParam(required = false) String after,
            @RequestParam(defaultValue = &quot;30&quot;) int limit
    ) {
        return ok(channelTalkService.getUsers(after, limit));
    }

    /**
     * 고객 정보를 상세 조회합니다.
     *
     * @param userId
     * @return
     */
    @GetMapping(&quot;/users/{userId}&quot;)
    public ResponseEntity&amp;lt;ApiResponseWrapper&amp;lt;Map&amp;lt;String, Object&amp;gt;&amp;gt;&amp;gt; getUser(@PathVariable String userId) {
        return ok(channelTalkService.getUser(userId));
    }

    /**
     * 사용자의 이벤트(행동로그)를 조회합니다.
     *
     * @param userId
     * @param sortOrder
     * @param since
     * @param limit
     * @return
     */
    @GetMapping(&quot;/users/{userId}/events&quot;)
    public ResponseEntity&amp;lt;ApiResponseWrapper&amp;lt;Map&amp;lt;String, Object&amp;gt;&amp;gt;&amp;gt; getUserEvents(
            @PathVariable String userId,
            @RequestParam(defaultValue = &quot;desc&quot;) String sortOrder,
            @RequestParam(required = false) Long since,
            @RequestParam(defaultValue = &quot;25&quot;) int limit
    ) {
        return ok(channelTalkService.getUserEvents(userId, sortOrder, since, limit));
    }

    // ─── 고객 채팅(UserChat) ──────────────────────────────

    /**
     * 고객과의 채팅 목록을 조회합니다.
     *
     * @param state
     * @param sortOrder
     * @param since
     * @param limit
     * @return
     */
    @GetMapping(&quot;/user-chats&quot;)
    public ResponseEntity&amp;lt;ApiResponseWrapper&amp;lt;Map&amp;lt;String, Object&amp;gt;&amp;gt;&amp;gt; getUserChats(
            @RequestParam(defaultValue = &quot;opened&quot;) String state,
            @RequestParam(defaultValue = &quot;desc&quot;) String sortOrder,
            @RequestParam(required = false) String since,
            @RequestParam(defaultValue = &quot;30&quot;) int limit
    ) {
        return ok(channelTalkService.getUserChats(state, sortOrder, since, limit));
    }

    /**
     * 고객과의 채팅 정보를 상세 조회합니다.
     *
     * @param userChatId
     * @return
     */
    @GetMapping(&quot;/user-chats/{userChatId}&quot;)
    public ResponseEntity&amp;lt;ApiResponseWrapper&amp;lt;Map&amp;lt;String, Object&amp;gt;&amp;gt;&amp;gt; getUserChat(@PathVariable String userChatId) {
        return ok(channelTalkService.getUserChat(userChatId));
    }

    /**
     * 고객과의 채팅 세션을 조회합니다.
     *
     * @param userChatId
     * @return
     */
    @GetMapping(&quot;/user-chats/{userChatId}/sessions&quot;)
    public ResponseEntity&amp;lt;ApiResponseWrapper&amp;lt;Map&amp;lt;String, Object&amp;gt;&amp;gt;&amp;gt; getUserChatSessions(@PathVariable String userChatId) {
        return ok(channelTalkService.getUserChatSessions(userChatId));
    }

    /**
     * 고객과의 채팅을 생성합니다.
     *
     * @param userId
     * @return
     */
    @PostMapping(&quot;/users/{userId}/user-chats&quot;)
    public ResponseEntity&amp;lt;ApiResponseWrapper&amp;lt;Map&amp;lt;String, Object&amp;gt;&amp;gt;&amp;gt; createUserChat(@PathVariable String userId) {
        return ok(channelTalkService.createUserChat(userId));
    }

    // ─── 채팅 메시지 ──────────────────────────────────────

    /**
     * 고객 채팅의 메시지를 조회합니다.
     *
     * @param userChatId
     * @param since
     * @param limit
     * @return
     */
    @GetMapping(&quot;/user-chats/{userChatId}/messages&quot;)
    public ResponseEntity&amp;lt;ApiResponseWrapper&amp;lt;Map&amp;lt;String, Object&amp;gt;&amp;gt;&amp;gt; getUserChatMessages(
            @PathVariable String userChatId,
            @RequestParam(required = false) String since,
            @RequestParam(defaultValue = &quot;50&quot;) int limit
    ) {
        return ok(channelTalkService.getUserChatMessages(userChatId, since, limit));
    }

    // ─── 상담원(Manager) ──────────────────────────────────

    /**
     * 상담원(Manager) 목록을 조회합니다.
     *
     * @return
     */
    @GetMapping(&quot;/managers&quot;)
    public ResponseEntity&amp;lt;ApiResponseWrapper&amp;lt;Map&amp;lt;String, Object&amp;gt;&amp;gt;&amp;gt; getManagers() {
        return ok(channelTalkService.getManagers());
    }

    /**
     * 상담원(Manager) 정보를 상세 조회합니다.
     *
     * @param managerId
     * @return
     */
    @GetMapping(&quot;/managers/{managerId}&quot;)
    public ResponseEntity&amp;lt;ApiResponseWrapper&amp;lt;Map&amp;lt;String, Object&amp;gt;&amp;gt;&amp;gt; getManager(@PathVariable String managerId) {
        return ok(channelTalkService.getManager(managerId));
    }

    // ─── 봇(Bot) ──────────────────────────────────────────

    /**
     * 봇(Bot) 목록을 조회합니다.
     *
     * @param since
     * @param limit
     * @return
     */
    @GetMapping(&quot;/bots&quot;)
    public ResponseEntity&amp;lt;ApiResponseWrapper&amp;lt;Map&amp;lt;String, Object&amp;gt;&amp;gt;&amp;gt; getBots(
            @RequestParam(required = false) Long since,
            @RequestParam(defaultValue = &quot;25&quot;) int limit
    ) {
        return ok(channelTalkService.getBots(since, limit));
    }

    /**
     * 봇(Bot) 정보를 상세 조회합니다.
     *
     * @param botId
     * @return
     */
    @GetMapping(&quot;/bots/{botId}&quot;)
    public ResponseEntity&amp;lt;ApiResponseWrapper&amp;lt;Map&amp;lt;String, Object&amp;gt;&amp;gt;&amp;gt; getBot(@PathVariable String botId) {
        return ok(channelTalkService.getBot(botId));
    }

    // ─── 태그 ─────────────────────────────────────────────

    /**
     * 태그 목록을 조회합니다.
     *
     * @return
     */
    @GetMapping(&quot;/tags&quot;)
    public ResponseEntity&amp;lt;ApiResponseWrapper&amp;lt;Map&amp;lt;String, Object&amp;gt;&amp;gt;&amp;gt; getTags() {
        return ok(channelTalkService.getTags());
    }

    // ─── 웹훅(Webhook) ────────────────────────────────────

    /**
     * 웹훅 목록을 조회합니다.
     *
     * @param since
     * @param limit
     * @return
     */
    @GetMapping(&quot;/webhooks&quot;)
    public ResponseEntity&amp;lt;ApiResponseWrapper&amp;lt;Map&amp;lt;String, Object&amp;gt;&amp;gt;&amp;gt; getWebhooks(
            @RequestParam(required = false) Long since,
            @RequestParam(defaultValue = &quot;25&quot;) int limit
    ) {
        return ok(channelTalkService.getWebhooks(since, limit));
    }

    /**
     * 웹훅 정보를 상세 조회합니다.
     *
     * @param webhookId
     * @return
     */
    @GetMapping(&quot;/webhooks/{webhookId}&quot;)
    public ResponseEntity&amp;lt;ApiResponseWrapper&amp;lt;Map&amp;lt;String, Object&amp;gt;&amp;gt;&amp;gt; getWebhook(@PathVariable String webhookId) {
        return ok(channelTalkService.getWebhook(webhookId));
    }

    /**
     * 웹훅을 생성합니다.
     *
     * @param body
     * @return
     */
    @PostMapping(&quot;/webhooks&quot;)
    public ResponseEntity&amp;lt;ApiResponseWrapper&amp;lt;Map&amp;lt;String, Object&amp;gt;&amp;gt;&amp;gt; createWebhook(@RequestBody Map&amp;lt;String, Object&amp;gt; body) {
        return ok(channelTalkService.createWebhook(body));
    }

    /**
     * 웹훅 정보를 수정합니다.
     *
     * @param webhookId
     * @param body
     * @return
     */
    @PutMapping(&quot;/webhooks/{webhookId}&quot;)
    public ResponseEntity&amp;lt;ApiResponseWrapper&amp;lt;Map&amp;lt;String, Object&amp;gt;&amp;gt;&amp;gt; updateWebhook(
            @PathVariable String webhookId,
            @RequestBody Map&amp;lt;String, Object&amp;gt; body
    ) {
        return ok(channelTalkService.updateWebhook(webhookId, body));
    }

    /**
     * 웹훅을 삭제합니다.
     *
     * @param webhookId
     * @return
     */
    @DeleteMapping(&quot;/webhooks/{webhookId}&quot;)
    public ResponseEntity&amp;lt;ApiResponseWrapper&amp;lt;Map&amp;lt;String, Object&amp;gt;&amp;gt;&amp;gt; deleteWebhook(@PathVariable String webhookId) {
        return ok(channelTalkService.deleteWebhook(webhookId));
    }

    // ─── 캠페인(Campaign) ─────────────────────────────────

    /**
     * 캠페인(마케팅) 목록을 조회합니다.
     *
     * @param since
     * @param limit
     * @param states draft|active|inactive|completed
     * @return
     */
    @GetMapping(&quot;/campaigns&quot;)
    public ResponseEntity&amp;lt;ApiResponseWrapper&amp;lt;Map&amp;lt;String, Object&amp;gt;&amp;gt;&amp;gt; getCampaigns(
            @RequestParam(required = false) Long since,
            @RequestParam(defaultValue = &quot;25&quot;) int limit,
            @RequestParam(required = false) List&amp;lt;String&amp;gt; states
    ) {
        return ok(channelTalkService.getCampaigns(since, limit, states));
    }

    // ─── 그룹(Group) 파일 URL ─────────────────────────────

    /**
     * 그룹 메시지의 파일 URL을 조회합니다. (15분 signed URL)
     *
     * @param groupId
     * @param key
     * @return
     */
    @GetMapping(&quot;/groups/{groupId}/messages/file&quot;)
    public ResponseEntity&amp;lt;ApiResponseWrapper&amp;lt;Map&amp;lt;String, Object&amp;gt;&amp;gt;&amp;gt; getGroupFileUrl(
            @PathVariable String groupId,
            @RequestParam String key
    ) {
        return ok(channelTalkService.getGroupFileUrl(groupId, key));
    }
}&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;h3 data-ke-size=&quot;size23&quot;&gt;6. 일부 결과 확인&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  일부 결과 확인&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- IntelliJ의 HTTP Client를 활용하여서 API 호출 테스트를 수행합니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  아래와 같이 채널 목록을 조회하였을 때, 정상적으로 출력이 됨을 확인하였습니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2032&quot; data-origin-height=&quot;1076&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bpHbqf/dJMcacQy4Qe/HpzT28UuuN8olmDeGEuonK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bpHbqf/dJMcacQy4Qe/HpzT28UuuN8olmDeGEuonK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bpHbqf/dJMcacQy4Qe/HpzT28UuuN8olmDeGEuonK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbpHbqf%2FdJMcacQy4Qe%2FHpzT28UuuN8olmDeGEuonK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2032&quot; height=&quot;1076&quot; data-origin-width=&quot;2032&quot; data-origin-height=&quot;1076&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  [참고]&lt;/b&gt; HTTP Client 활용 방법&lt;/blockquote&gt;
&lt;figure id=&quot;og_1778227724949&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;[IntelliJ] HTTP Client 사용하기 : Postman 대체하기&quot; data-og-description=&quot;해당 글에서는 Postman을 대체하여 클라이언트에서 서버로 API를 전송(Request)하고 반환(Response)을 받는 테스트에 사용이 되는 HTTP Client에 대해서 공유합니다. 1) 문제사항 및 적용 계기   Client에서 &quot; data-og-host=&quot;adjh54.tistory.com&quot; data-og-source-url=&quot;https://adjh54.tistory.com/88&quot; data-og-url=&quot;https://adjh54.tistory.com/88&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/btlDrx/dJMb9cBMhFe/Sj6cKlWAPtOdLXE1yelUTk/img.png?width=800&amp;amp;height=720&amp;amp;face=0_0_800_720,https://scrap.kakaocdn.net/dn/c5Czst/dJMb9eficKb/30kXbwcs9TQiwcKJcyx9zk/img.png?width=800&amp;amp;height=720&amp;amp;face=0_0_800_720,https://scrap.kakaocdn.net/dn/d0rJiq/dJMb8WeDWpI/hSNuxIUzLvujRjl0kV1y30/img.png?width=2384&amp;amp;height=1208&amp;amp;face=0_0_2384_1208&quot;&gt;&lt;a href=&quot;https://adjh54.tistory.com/88&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://adjh54.tistory.com/88&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/btlDrx/dJMb9cBMhFe/Sj6cKlWAPtOdLXE1yelUTk/img.png?width=800&amp;amp;height=720&amp;amp;face=0_0_800_720,https://scrap.kakaocdn.net/dn/c5Czst/dJMb9eficKb/30kXbwcs9TQiwcKJcyx9zk/img.png?width=800&amp;amp;height=720&amp;amp;face=0_0_800_720,https://scrap.kakaocdn.net/dn/d0rJiq/dJMb8WeDWpI/hSNuxIUzLvujRjl0kV1y30/img.png?width=2384&amp;amp;height=1208&amp;amp;face=0_0_2384_1208');&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;[IntelliJ] HTTP Client 사용하기 : Postman 대체하기&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;해당 글에서는 Postman을 대체하여 클라이언트에서 서버로 API를 전송(Request)하고 반환(Response)을 받는 테스트에 사용이 되는 HTTP Client에 대해서 공유합니다. 1) 문제사항 및 적용 계기   Client에서&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;adjh54.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;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  화면상에서 출력도 채널정보, 상담원, 고객 채팅, 봇에 대해서도 출력이 잘됨을 확인하였습니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;940&quot; data-origin-height=&quot;745&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cMe74l/dJMcaicdx05/kZHETd3xMiQpOO8Kk9cUwk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cMe74l/dJMcaicdx05/kZHETd3xMiQpOO8Kk9cUwk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cMe74l/dJMcaicdx05/kZHETd3xMiQpOO8Kk9cUwk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcMe74l%2FdJMcaicdx05%2FkZHETd3xMiQpOO8Kk9cUwk%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;940&quot; height=&quot;745&quot; data-origin-width=&quot;940&quot; data-origin-height=&quot;745&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;오늘도&amp;nbsp;감사합니다.&amp;nbsp; &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;</description>
      <category>Java/라이브러리 활용</category>
      <category>API 채널톡 통신</category>
      <category>java</category>
      <category>java channeltalk</category>
      <category>java 채널톡</category>
      <category>java 채널톡 사용예시</category>
      <category>채널톡 API</category>
      <category>채널톡 API 키 발급</category>
      <category>채널톡 OPEN API 활용</category>
      <category>채널톡 외부 통신</category>
      <category>키 발급</category>
      <author>adjh54</author>
      <guid isPermaLink="true">https://adjh54.tistory.com/756</guid>
      <comments>https://adjh54.tistory.com/756#entry756comment</comments>
      <pubDate>Fri, 8 May 2026 20:00:03 +0900</pubDate>
    </item>
    <item>
      <title>[React] 채널톡 이해하고 활용하기 -1: 연결 및 채팅 기능 확인, @channel.io/channel-web-sdk-loader</title>
      <link>https://adjh54.tistory.com/755</link>
      <description>&lt;blockquote data-ke-style=&quot;style1&quot;&gt; &lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;해당 글에서는 채널톡을 연결하고 채팅 기능을 활용하는 방법에 대해서 작성한 글입니다&lt;br&gt;&lt;br&gt;&lt;/span&gt; 
 &lt;figure contenteditable=&quot;false&quot; data-ke-type=&quot;emoticon&quot; data-ke-align=&quot;alignCenter&quot; data-emoticon-type=&quot;face&quot; data-emoticon-name=&quot;073&quot; data-emoticon-isanimation=&quot;false&quot; data-emoticon-src=&quot;https://t1.daumcdn.net/axz_keditor/emoticon/face/large/073.png&quot;&gt; 
  &lt;img src=&quot;https://t1.daumcdn.net/axz_keditor/emoticon/face/large/073.png&quot; width=&quot;80&quot;&gt; 
 &lt;/figure&gt; 
&lt;/blockquote&gt;&lt;h2 data-ke-size=&quot;size26&quot;&gt;1) 채널톡&lt;/h2&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot;&gt;&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  채널톡&lt;/b&gt;&lt;br&gt;&lt;br&gt;&lt;b&gt;- 올인원 AI 비즈니스 메신저로, 채팅 상담과 팀 메신저, 마케팅 메시지, 인터넷 전화(미트), 시나리오형 챗봇(서포트봇), AI 에이전트(ALF) 등의 기능을 제공합니다.&lt;/b&gt;&lt;br&gt;- 주로 고객과 채팅 상담을 이용하고 관리하기 위한 용도로 이용을 합니다.&lt;/blockquote&gt;&lt;figure data-ke-type=&quot;opengraph&quot; data-og-title=&quot;고객상담의 미래는 AI 입니다 - 채널톡&quot; data-ke-align=&quot;alignCenter&quot; data-og-description=&quot;올인원 AI 메신저 채널톡과 함께 준비하세요.&quot; data-og-host=&quot;channel.io&quot; data-og-source-url=&quot;https://channel.io/ko&quot; data-og-image=&quot;https://blog.kakaocdn.net/dna/BR7oA/dJMb8XkjtRc/AAAAAAAAAAAAAAAAAAAAAESGPuELs8ca7SXRiuQxd9QtthpxJEZeXKIXGOwXgUZ8/img.jpg?credential=yqXZFxpELC7KVnFOS48ylbz2pIh7yKj8&amp;amp;expires=1780239599&amp;amp;allow_ip=&amp;amp;allow_referer=&amp;amp;signature=vZ9MPtaioUmPOPAtN4V%2BZhxZ%2Fi8%3D&quot; data-og-url=&quot;https://channel.io/ko&quot;&gt;&lt;a href=&quot;https://channel.io/ko&quot; target=&quot;_blank&quot; data-source-url=&quot;https://channel.io/ko&quot;&gt;&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://blog.kakaocdn.net/dna/BR7oA/dJMb8XkjtRc/AAAAAAAAAAAAAAAAAAAAAESGPuELs8ca7SXRiuQxd9QtthpxJEZeXKIXGOwXgUZ8/img.jpg?credential=yqXZFxpELC7KVnFOS48ylbz2pIh7yKj8&amp;amp;expires=1780239599&amp;amp;allow_ip=&amp;amp;allow_referer=&amp;amp;signature=vZ9MPtaioUmPOPAtN4V%2BZhxZ%2Fi8%3D')&quot;&gt; &lt;/div&gt;&lt;div class=&quot;og-text&quot;&gt;&lt;p class=&quot;og-title&quot;&gt;고객상담의 미래는 AI 입니다 - 채널톡&lt;/p&gt;&lt;p class=&quot;og-desc&quot;&gt;올인원 AI 메신저 채널톡과 함께 준비하세요.&lt;/p&gt;&lt;p class=&quot;og-host&quot;&gt;channel.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;h2 data-ke-size=&quot;size26&quot;&gt;2) 채널톡 연결 -1 : 키 조회&lt;/h2&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot;&gt;&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 공식사이트에 접근 및 채널 목록으로 가기 버튼을 누릅니다&lt;/h3&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2032&quot; data-origin-height=&quot;1076&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/czzVIY/dJMcabjQtkE/IjuGGXmbaLy9wdYtk8akZK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/czzVIY/dJMcabjQtkE/IjuGGXmbaLy9wdYtk8akZK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/czzVIY/dJMcabjQtkE/IjuGGXmbaLy9wdYtk8akZK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FczzVIY%2FdJMcabjQtkE%2FIjuGGXmbaLy9wdYtk8akZK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2032&quot; height=&quot;1076&quot; data-origin-width=&quot;2032&quot; data-origin-height=&quot;1076&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. 아래와 같이 출력이 됩니다&lt;/h3&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2032&quot; data-origin-height=&quot;1076&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/LMNKQ/dJMcaaFeD7W/WomkQrSsKIH84E593420jk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/LMNKQ/dJMcaaFeD7W/WomkQrSsKIH84E593420jk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/LMNKQ/dJMcaaFeD7W/WomkQrSsKIH84E593420jk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FLMNKQ%2FdJMcaaFeD7W%2FWomkQrSsKIH84E593420jk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2032&quot; height=&quot;1076&quot; data-origin-width=&quot;2032&quot; data-origin-height=&quot;1076&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. 일반 설정 &amp;gt; 버튼 설치 및 설정 &amp;gt; 채널톡 버튼 설치를 누릅니다.&lt;/h3&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2032&quot; data-origin-height=&quot;1076&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bPv9ez/dJMcabjQtDJ/VKB3DZTnOE3U75jb61enU0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bPv9ez/dJMcabjQtDJ/VKB3DZTnOE3U75jb61enU0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bPv9ez/dJMcabjQtDJ/VKB3DZTnOE3U75jb61enU0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbPv9ez%2FdJMcabjQtDJ%2FVKB3DZTnOE3U75jb61enU0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2032&quot; height=&quot;1076&quot; data-origin-width=&quot;2032&quot; data-origin-height=&quot;1076&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;h3 data-ke-size=&quot;size23&quot;&gt;4. Plugin Key를 확인합니다.&lt;/h3&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2032&quot; data-origin-height=&quot;1076&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/GIBg2/dJMcacJOIWl/9bL836A2NF8oGcXDkEIN3k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/GIBg2/dJMcacJOIWl/9bL836A2NF8oGcXDkEIN3k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/GIBg2/dJMcacJOIWl/9bL836A2NF8oGcXDkEIN3k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FGIBg2%2FdJMcacJOIWl%2F9bL836A2NF8oGcXDkEIN3k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2032&quot; height=&quot;1076&quot; data-origin-width=&quot;2032&quot; data-origin-height=&quot;1076&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  참고 사이트&lt;/b&gt;&lt;/blockquote&gt;&lt;figure data-ke-type=&quot;opengraph&quot; data-og-title=&quot;채널톡 버튼 설정&quot; data-ke-align=&quot;alignCenter&quot; data-og-description=&quot;심혈을 기울여 만든 우리 사이트에 채널톡 버튼이 잘 자연스럽게 녹아 들도록 위치를 수정하거나 커스텀 버튼을 통해 브랜드의 개성을 보여주세요.&quot; data-og-host=&quot;docs.channel.io&quot; data-og-source-url=&quot;https://docs.channel.io/help/ko/articles/94f34984&quot; data-og-image=&quot;https://blog.kakaocdn.net/dna/eAjEj/dJMb9b3V6RN/AAAAAAAAAAAAAAAAAAAAADiIPwbBuMbWHly-zqyBUvBxDlrZiD1ngc0jB11uwmGt/img.jpg?credential=yqXZFxpELC7KVnFOS48ylbz2pIh7yKj8&amp;amp;expires=1780239599&amp;amp;allow_ip=&amp;amp;allow_referer=&amp;amp;signature=NOugS%2FS66QgLE%2B49rLeR5TUzep8%3D&quot; data-og-url=&quot;https://docs.channel.io/help/ko/articles/94f34984&quot;&gt;&lt;a href=&quot;https://docs.channel.io/help/ko/articles/94f34984&quot; target=&quot;_blank&quot; data-source-url=&quot;https://docs.channel.io/help/ko/articles/94f34984&quot;&gt;&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://blog.kakaocdn.net/dna/eAjEj/dJMb9b3V6RN/AAAAAAAAAAAAAAAAAAAAADiIPwbBuMbWHly-zqyBUvBxDlrZiD1ngc0jB11uwmGt/img.jpg?credential=yqXZFxpELC7KVnFOS48ylbz2pIh7yKj8&amp;amp;expires=1780239599&amp;amp;allow_ip=&amp;amp;allow_referer=&amp;amp;signature=NOugS%2FS66QgLE%2B49rLeR5TUzep8%3D')&quot;&gt; &lt;/div&gt;&lt;div class=&quot;og-text&quot;&gt;&lt;p class=&quot;og-title&quot;&gt;채널톡 버튼 설정&lt;/p&gt;&lt;p class=&quot;og-desc&quot;&gt;심혈을 기울여 만든 우리 사이트에 채널톡 버튼이 잘 자연스럽게 녹아 들도록 위치를 수정하거나 커스텀 버튼을 통해 브랜드의 개성을 보여주세요.&lt;/p&gt;&lt;p class=&quot;og-host&quot;&gt;docs.channel.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;br&gt;&amp;nbsp;&lt;/p&gt;&lt;h2 data-ke-size=&quot;size26&quot;&gt;3) 채널톡 연결 -2 : 라이브러리 설치&lt;/h2&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot;&gt;&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. @channel.io/channel-web-sdk-loader&lt;/h3&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot;&gt;&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  @channel.io/channel-web-sdk-loader&lt;/b&gt;&lt;/blockquote&gt;&lt;pre class=&quot;elixir&quot;&gt;&lt;code&gt;# npm 설치
$ npm i @channel.io/channel-web-sdk-loader

# or

# yarn 설치
$ yarn add @channel.io/channel-web-sdk-loader
&lt;/code&gt;&lt;/pre&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. 채널톡 로드&lt;/h3&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot;&gt;&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  채널톡 로드&lt;/b&gt;&lt;br&gt;&lt;br&gt;&lt;b&gt;- 아래와 같이 확인된 플러그인 키를 추가하고, 불러오고 성공여부를 반환받습니다.&lt;/b&gt;&lt;/blockquote&gt;&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;import * as ChannelService from '@channel.io/channel-web-sdk-loader';

export const App = () =&amp;gt; {
	// 채널톡 플러그인 키를 불러옵니다
	const CHANNEL_TALK_PLUG_IN_KEY = import.meta.env.VITE_CHANNEL_TALK_PLUG_IN

	useEffect(() =&amp;gt; {
		ChannelService.loadScript();
		ChannelService.boot(
			{ pluginKey: CHANNEL_TALK_PLUG_IN_KEY },
			function onBoot(error: any, user: any) {
				if (error) {
					console.error('채널톡 boot 실패:', error);
				} else {
					console.log('채널톡 boot 성공:', user);
				}
			}
		);
	}, []);
	return (
		&amp;lt;&amp;gt;
		&amp;lt;/&amp;gt;
	)
&lt;/code&gt;&lt;/pre&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. 결과 확인 -1 : 문의하기&lt;/h3&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot;&gt;&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  결과 확인 -1 : 문의하기&lt;/b&gt;&lt;br&gt;&lt;br&gt;&lt;b&gt;- 별도의 위치를 지정하지 않더라도 아래와 같이 가장 오른쪽 하단에 모든 페이지에 들어가 있음을 확인하였습니다.&lt;/b&gt;&lt;/blockquote&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;h4 data-ke-size=&quot;size20&quot;&gt;3.1. 화면상에 출력&lt;/h4&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2032&quot; data-origin-height=&quot;1076&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/5qJj9/dJMcacwg9v8/8Ov0KCcvKcIJzffrAijgmk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/5qJj9/dJMcacwg9v8/8Ov0KCcvKcIJzffrAijgmk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/5qJj9/dJMcacwg9v8/8Ov0KCcvKcIJzffrAijgmk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F5qJj9%2FdJMcacwg9v8%2F8Ov0KCcvKcIJzffrAijgmk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2032&quot; height=&quot;1076&quot; data-origin-width=&quot;2032&quot; data-origin-height=&quot;1076&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;h4 data-ke-size=&quot;size20&quot;&gt;3.2. 대화 확인&lt;/h4&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot;&gt;&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  대화 확인&lt;/b&gt;&lt;br&gt;&lt;br&gt;- 아래와 같이 대화창이 출력이 됩니다.&lt;/blockquote&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;426&quot; data-origin-height=&quot;695&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ny1O4/dJMb99TO1N3/48T9jb4Ev99QKfKPQQKKy1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ny1O4/dJMb99TO1N3/48T9jb4Ev99QKfKPQQKKy1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ny1O4/dJMb99TO1N3/48T9jb4Ev99QKfKPQQKKy1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fny1O4%2FdJMb99TO1N3%2F48T9jb4Ev99QKfKPQQKKy1%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;426&quot; height=&quot;695&quot; data-origin-width=&quot;426&quot; data-origin-height=&quot;695&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;h4 data-ke-size=&quot;size20&quot;&gt;3.3. 아래와 같이 문의를 합니다.&lt;/h4&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;396&quot; data-origin-height=&quot;672&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/NbEzl/dJMcai4feln/P4KsRPQzKUOdCn7prhzks1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/NbEzl/dJMcai4feln/P4KsRPQzKUOdCn7prhzks1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/NbEzl/dJMcai4feln/P4KsRPQzKUOdCn7prhzks1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FNbEzl%2FdJMcai4feln%2FP4KsRPQzKUOdCn7prhzks1%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;396&quot; height=&quot;672&quot; data-origin-width=&quot;396&quot; data-origin-height=&quot;672&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;h4 data-ke-size=&quot;size20&quot;&gt;3.4. 채널톡 내에서 확인&lt;/h4&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot;&gt;&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  채널톡 내에서 확인&lt;/b&gt;&lt;br&gt;&lt;br&gt;&lt;b&gt;- 채널톡 내에 메시지가 전달됨을 확인하였습니다.&lt;/b&gt;&lt;/blockquote&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2032&quot; data-origin-height=&quot;1075&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/baUkwt/dJMcafmd54Z/zCUU1pL75sGPeoCAxk6Uj0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/baUkwt/dJMcafmd54Z/zCUU1pL75sGPeoCAxk6Uj0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/baUkwt/dJMcafmd54Z/zCUU1pL75sGPeoCAxk6Uj0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbaUkwt%2FdJMcafmd54Z%2FzCUU1pL75sGPeoCAxk6Uj0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2032&quot; height=&quot;1075&quot; data-origin-width=&quot;2032&quot; data-origin-height=&quot;1075&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;h3 data-ke-size=&quot;size23&quot;&gt;4. 결과 확인 -2 : 답변 및 답변 확인&lt;/h3&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot;&gt;&lt;h4 data-ke-size=&quot;size20&quot;&gt;4.1. 나에게 배정을 하고 답변을 했습니다&lt;/h4&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2032&quot; data-origin-height=&quot;1075&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/szLgw/dJMcab5a5Vy/VtIarYe9yRVn9z3mSKe8KK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/szLgw/dJMcab5a5Vy/VtIarYe9yRVn9z3mSKe8KK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/szLgw/dJMcab5a5Vy/VtIarYe9yRVn9z3mSKe8KK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FszLgw%2FdJMcab5a5Vy%2FVtIarYe9yRVn9z3mSKe8KK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2032&quot; height=&quot;1075&quot; data-origin-width=&quot;2032&quot; data-origin-height=&quot;1075&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;h4 data-ke-size=&quot;size20&quot;&gt;4.2. 아래와 같이 사용자에 답변이 전달됨을 확인하였습니다&lt;/h4&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;380&quot; data-origin-height=&quot;661&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/RZKKu/dJMcaffudSA/b2xXr9gOtzEtx3cLg8FpVK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/RZKKu/dJMcaffudSA/b2xXr9gOtzEtx3cLg8FpVK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/RZKKu/dJMcaffudSA/b2xXr9gOtzEtx3cLg8FpVK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FRZKKu%2FdJMcaffudSA%2Fb2xXr9gOtzEtx3cLg8FpVK%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;380&quot; height=&quot;661&quot; data-origin-width=&quot;380&quot; data-origin-height=&quot;661&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;h2 data-ke-size=&quot;size26&quot;&gt;4) 채널톡 심화 활용하기&lt;/h2&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 특정 페이지에서만 출력&lt;/h3&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot;&gt;&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  특정 페이지에서만 출력&lt;/b&gt;&lt;br&gt;&lt;b&gt;&lt;br&gt;&lt;/b&gt;&lt;b&gt;- 현재 구조에서는 모든 페이지에서 채널톡 아이콘이 보이고 있습니다. 그렇기에 특정 페이지에서 안 보이도록 하는 함수를 이용합니다.&lt;/b&gt;&lt;br&gt;- 단, 해당 함수는 전역으로 관리되기에, hideChannelButton()를 호출 한 뒤, showChannelButton()를 호출하지 않으면 채널톡 아이콘은 보이지 않습니다.&lt;/blockquote&gt;&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;// 특정 페이지에서 숨기기
ChannelService.hideChannelButton();

// 다시 보이기
ChannelService.showChannelButton();
&lt;/code&gt;&lt;/pre&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2032&quot; data-origin-height=&quot;1075&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bQiFxU/dJMcahRQmC5/5wx7JI7Rf8GkKC4DgkiKvK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bQiFxU/dJMcahRQmC5/5wx7JI7Rf8GkKC4DgkiKvK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bQiFxU/dJMcahRQmC5/5wx7JI7Rf8GkKC4DgkiKvK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbQiFxU%2FdJMcahRQmC5%2F5wx7JI7Rf8GkKC4DgkiKvK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2032&quot; height=&quot;1075&quot; data-origin-width=&quot;2032&quot; data-origin-height=&quot;1075&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  채널톡 사이트 내에서 일반 설정 &amp;gt; 버튼 설치 및 설정 &amp;gt; 고급 설정 내에서 ‘화이트 리스트’로 특정 도메인에서만 채널톡 버튼이 보이도록 할 수 있습니다.&lt;/b&gt;&lt;/blockquote&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2048&quot; data-origin-height=&quot;1337&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Ajz3b/dJMcad2WFNP/KPsJ8FWIzltGOwDcGbcmfk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Ajz3b/dJMcad2WFNP/KPsJ8FWIzltGOwDcGbcmfk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Ajz3b/dJMcad2WFNP/KPsJ8FWIzltGOwDcGbcmfk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FAjz3b%2FdJMcad2WFNP%2FKPsJ8FWIzltGOwDcGbcmfk%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;2048&quot; height=&quot;1337&quot; data-origin-width=&quot;2048&quot; data-origin-height=&quot;1337&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. 채널톡 아이콘 모양이나 위치를 변경&lt;/h3&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot;&gt;&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  채널톡 아이콘 모양이나 위치를 변경&lt;/b&gt;&lt;br&gt;&lt;br&gt;&lt;b&gt;- 상단으로 이동 아이콘과 같이 겹칠 수 있기에, 채널톡 위치를 변경합니다&lt;/b&gt;&lt;br&gt;- 채널톡 접속 &amp;gt; 채널 설정 &amp;gt; 일반 설정 &amp;gt; 버튼 설치 및 설정을 들어가면 아래와 같이 아이콘 모양이나 위치를 변경할 수 있습니다&lt;/blockquote&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2032&quot; data-origin-height=&quot;1075&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cGFmXx/dJMcaaZuLdo/bXISKVmKYDbJkKXes7uW81/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cGFmXx/dJMcaaZuLdo/bXISKVmKYDbJkKXes7uW81/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cGFmXx/dJMcaaZuLdo/bXISKVmKYDbJkKXes7uW81/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcGFmXx%2FdJMcaaZuLdo%2FbXISKVmKYDbJkKXes7uW81%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2032&quot; height=&quot;1075&quot; data-origin-width=&quot;2032&quot; data-origin-height=&quot;1075&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  아래와 같이 위치를 변경이 되었습니다.&lt;/b&gt;&lt;/blockquote&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2032&quot; data-origin-height=&quot;1076&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kW5hM/dJMcagemGRb/mw76joJ1a0ctKIxPwKS6qk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kW5hM/dJMcagemGRb/mw76joJ1a0ctKIxPwKS6qk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kW5hM/dJMcagemGRb/mw76joJ1a0ctKIxPwKS6qk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkW5hM%2FdJMcagemGRb%2Fmw76joJ1a0ctKIxPwKS6qk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2032&quot; height=&quot;1076&quot; data-origin-width=&quot;2032&quot; data-origin-height=&quot;1076&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. 버튼과 채널톡 채팅 연결&lt;/h3&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot;&gt;&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  버튼과 채널톡 채팅 연결&lt;/b&gt;&lt;br&gt;&lt;br&gt;&lt;b&gt;- 나만의 버튼을 눌렀을 때 채널톡이 열리도록 하는 방식입니다.&lt;/b&gt;&lt;br&gt;- 아래의 코드와 같이 ChannelService.showMessenger(); 를 이용하여서 연결을 합니다.&lt;/blockquote&gt;&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;import * as ChannelService from '@channel.io/channel-web-sdk-loader';

export default function Page() {
	const handleOpenChat = () =&amp;gt; {
		ChannelService.showMessenger();
	};
	return (
	&amp;lt;button onClick={handleOpenChat}&amp;gt;솔루션 도입 문의&amp;lt;/button&amp;gt;
	)
}
&lt;/code&gt;&lt;/pre&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1697&quot; data-origin-height=&quot;741&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/VqoF5/dJMcajow2nN/ZMEcZiPn2K8XUXSvmhxHX0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/VqoF5/dJMcajow2nN/ZMEcZiPn2K8XUXSvmhxHX0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/VqoF5/dJMcajow2nN/ZMEcZiPn2K8XUXSvmhxHX0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FVqoF5%2FdJMcajow2nN%2FZMEcZiPn2K8XUXSvmhxHX0%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;1697&quot; height=&quot;741&quot; data-origin-width=&quot;1697&quot; data-origin-height=&quot;741&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&lt;b&gt;오늘도&amp;nbsp;감사합니다.&amp;nbsp; &lt;/b&gt;&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Javascript &amp;amp; Typescript/라이브러리</category>
      <category>react</category>
      <category>react 채널톡</category>
      <category>react 채널톡 예시</category>
      <category>react 채널톡 활용</category>
      <category>채널톡 설치</category>
      <category>채널톡 활용방법</category>
      <author>adjh54</author>
      <guid isPermaLink="true">https://adjh54.tistory.com/755</guid>
      <comments>https://adjh54.tistory.com/755#entry755comment</comments>
      <pubDate>Thu, 7 May 2026 20:00:43 +0900</pubDate>
    </item>
    <item>
      <title>[RN/오류노트] error: call to consteval function 'fmt::basic_format_string</title>
      <link>https://adjh54.tistory.com/754</link>
      <description>&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;React Native 환경에서 XCode 업데이트 이후에 발생하는 오류에 대해서 이를 해결하는 방법에 대해서 알아봅니다.&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;
&lt;figure contenteditable=&quot;false&quot; data-ke-type=&quot;emoticon&quot; data-ke-align=&quot;alignCenter&quot; data-emoticon-type=&quot;face&quot; data-emoticon-name=&quot;073&quot; data-emoticon-isanimation=&quot;false&quot; data-emoticon-src=&quot;https://t1.daumcdn.net/axz_keditor/emoticon/face/large/073.png&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/axz_keditor/emoticon/face/large/073.png&quot; width=&quot;80&quot; /&gt;&lt;/figure&gt;
&lt;/blockquote&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1) 문제점&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  문제점&lt;/b&gt;&lt;br /&gt;&lt;b&gt;&lt;br /&gt;- 최근에 XCode 26.4.1 버전으로 업데이트를 한 이후에 아래와 같은 문제가 발생하였습니다.&lt;/b&gt; &lt;br /&gt;&lt;br /&gt;- /workspace/3ha/3ha-chon-calc-quiz/ios/Pods/fmt/include/fmt/format-inl.h:59:24 Call to consteval function 'fmt::basic_format_string&amp;lt;char, fmt::basic_string_view&amp;lt;char&amp;gt; &amp;amp;, const char (&amp;amp;)[3]&amp;gt;::basic_format_string&amp;lt;FMT_COMPILE_STRING, 0&amp;gt;' is not a constant expression&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1088&quot; data-origin-height=&quot;752&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/MMJ1Y/dJMcagejp3k/zxcCGHdlbRuMVQBxBLYNZK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/MMJ1Y/dJMcagejp3k/zxcCGHdlbRuMVQBxBLYNZK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/MMJ1Y/dJMcagejp3k/zxcCGHdlbRuMVQBxBLYNZK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FMMJ1Y%2FdJMcagejp3k%2FzxcCGHdlbRuMVQBxBLYNZK%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;1088&quot; height=&quot;752&quot; data-origin-width=&quot;1088&quot; data-origin-height=&quot;752&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;h2 data-ke-size=&quot;size26&quot;&gt;2) 해결방법&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  해결방법&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 현재의 문제는 fmt 라이브러리의 consteval / C++20 기능이 구버전 C++ 표준으로 컴파일되면서 발생한 충돌하는 문제입니다.&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;- fmt 라이브러리 최신 버전은 &lt;b&gt;&lt;u&gt;consteval&lt;/u&gt;&lt;/b&gt; 키워드를 사용하는데, 이건 C++20에서 도입된 문법이기에 이에 맞지않아 발생하는 문제입니다.&lt;br /&gt;- 현재에서 발생한 에러에서는 Xcode가 해당 파일을 C++14 또는 C++16 이하로 컴파일하려 했기 때문에 발생하는 문제이며, consteval을 모르는 컴파일러 버전 기준으로 해석하다 보니 &quot;상수 표현식이 아니다&quot;라는 엉뚱한 에러가 발생하는 것입니다.&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;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;&amp;nbsp;  그렇기에 아래와 같이 Podfile 내에 수정을 하였습니다.&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;- CLANG_CXX_LANGUAGE_STANDARD = 'c++17&amp;rsquo; : C++ 언어 표준을 C++17로 명시하였습니다.&lt;br /&gt;- CLANG_CXX_LIBRARY = 'libc++&amp;rsquo; : 표준 라이브러리 구현체를 libc++로 강제하였습니다.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- fmt 최신 버전은 최소 C++17 이상을 요구하므로, 명시적으로 올려줘야 정상 컴파일이 됩니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;pre class=&quot;lua&quot;&gt;&lt;code&gt; installer.pods_project.targets.each do |target|
      target.build_configurations.each do |config|
        config.build_settings['CLANG_CXX_LIBRARY'] = 'libc++'
        if target.name == 'fmt'
          target.build_configurations.each do |config|
            config.build_settings['CLANG_CXX_LANGUAGE_STANDARD'] = 'c++17'
          end
        end
      end
    end
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;[더 알아보기]&lt;/b&gt;&lt;br /&gt;&lt;b&gt;&lt;br /&gt; &amp;nbsp;fmt 라이브러리는 뭘까?&lt;br /&gt;&lt;/b&gt;&lt;br /&gt;- fmt는 C++에서 printf / std::string 문자열 포매팅을 타입 안전하게 해주는 라이브러리입니다.&lt;br /&gt;- 주로 React Native에서는 Folly가 내부적으로 많이 써서 RN 프로젝트에서 사용이 됩니다.&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;a href=&quot;https://github.com/fmtlib/fmt&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://github.com/fmtlib/fmt&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3) 결과 확인&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  결과 확인&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;- 아래와 같이 정상적으로 빌드가 됨을 확인하였습니다.&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2048&quot; data-origin-height=&quot;1371&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/UcshJ/dJMcagrPgFd/3Vm1gL5QaxXv1pgyG1oXZ1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/UcshJ/dJMcagrPgFd/3Vm1gL5QaxXv1pgyG1oXZ1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/UcshJ/dJMcagrPgFd/3Vm1gL5QaxXv1pgyG1oXZ1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FUcshJ%2FdJMcagrPgFd%2F3Vm1gL5QaxXv1pgyG1oXZ1%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;2048&quot; height=&quot;1371&quot; data-origin-width=&quot;2048&quot; data-origin-height=&quot;1371&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;b&gt;오늘도&amp;nbsp;감사합니다.&amp;nbsp; &lt;/b&gt;&lt;/p&gt;</description>
      <category>React &amp;amp; React Native/오류노트</category>
      <category>error: call to consteval function 'fmt::basic_format_string</category>
      <category>fmt error</category>
      <category>fmt 오류</category>
      <category>react native</category>
      <category>react native fmt</category>
      <category>react native xcode</category>
      <category>react native xcode fmt</category>
      <author>adjh54</author>
      <guid isPermaLink="true">https://adjh54.tistory.com/754</guid>
      <comments>https://adjh54.tistory.com/754#entry754comment</comments>
      <pubDate>Sat, 2 May 2026 15:27:42 +0900</pubDate>
    </item>
    <item>
      <title>[DB] PostgreSQL pgcrypto 이해하고 AES256 암복호화 활용</title>
      <link>https://adjh54.tistory.com/753</link>
      <description>&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;해당 글에서는 PostgreSQL pgcrypto 내부 모듈을 이해하고 AES256 암복호화 활용하는 방법에 대해서 알아봅니다.&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;
&lt;figure contenteditable=&quot;false&quot; data-ke-type=&quot;emoticon&quot; data-ke-align=&quot;alignCenter&quot; data-emoticon-type=&quot;face&quot; data-emoticon-name=&quot;073&quot; data-emoticon-isanimation=&quot;false&quot; data-emoticon-src=&quot;https://t1.daumcdn.net/axz_keditor/emoticon/face/large/073.png&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/axz_keditor/emoticon/face/large/073.png&quot; width=&quot;80&quot; /&gt;&lt;/figure&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1) pgcrypto&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  pgcrypto&lt;/b&gt;&lt;br /&gt;&lt;b&gt;&lt;br /&gt;- PostgreSQL 내장 확장 모듈로, DB 레벨에서 암호화/해시/난수 생성 기능을 제공합니다.&lt;/b&gt;&lt;br /&gt;- 확장 모듈이기에 스키마 단위로 설치가 가능합니다.&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;398&quot; data-origin-height=&quot;349&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bzG2S0/dJMcahdbyyj/rh83FwzWlUYvU0Gizsudsk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bzG2S0/dJMcahdbyyj/rh83FwzWlUYvU0Gizsudsk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bzG2S0/dJMcahdbyyj/rh83FwzWlUYvU0Gizsudsk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbzG2S0%2FdJMcahdbyyj%2Frh83FwzWlUYvU0Gizsudsk%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;398&quot; height=&quot;349&quot; data-origin-width=&quot;398&quot; data-origin-height=&quot;349&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2) 암호화 이해하기&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1777527646620&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;[Security] 암호화/복호화 이해하기 -1 : 기초, 암호화 종류(단방향, 양방향)&quot; data-og-description=&quot;해당 글에서는 암호화/복호화에 대해 이해하며 암호화의 종류에 대해 이해를 돕기 위해 작성한 글입니다. 1) 암호화(Encryption) / 복호화(Decryption)1. 암호화(Encryption)  암호화(Encryption)- &amp;lsquo;평문&amp;rsquo; &quot; data-og-host=&quot;adjh54.tistory.com&quot; data-og-source-url=&quot;https://adjh54.tistory.com/527&quot; data-og-url=&quot;https://adjh54.tistory.com/527&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/iF9fB/dJMb8SpLfX5/c0OFRpqrkmRHSSwzbybPjk/img.jpg?width=630&amp;amp;height=305&amp;amp;face=0_0_630_305,https://scrap.kakaocdn.net/dn/7JtQa/dJMb9eTSU1K/A9pHIOBOj07HJG4QYseVT0/img.jpg?width=630&amp;amp;height=305&amp;amp;face=0_0_630_305,https://scrap.kakaocdn.net/dn/chciEd/dJMb9jgAAGy/pvoOfoR8ojVybMjUl4pf40/img.png?width=2139&amp;amp;height=1039&amp;amp;face=0_0_2139_1039&quot;&gt;&lt;a href=&quot;https://adjh54.tistory.com/527&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://adjh54.tistory.com/527&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/iF9fB/dJMb8SpLfX5/c0OFRpqrkmRHSSwzbybPjk/img.jpg?width=630&amp;amp;height=305&amp;amp;face=0_0_630_305,https://scrap.kakaocdn.net/dn/7JtQa/dJMb9eTSU1K/A9pHIOBOj07HJG4QYseVT0/img.jpg?width=630&amp;amp;height=305&amp;amp;face=0_0_630_305,https://scrap.kakaocdn.net/dn/chciEd/dJMb9jgAAGy/pvoOfoR8ojVybMjUl4pf40/img.png?width=2139&amp;amp;height=1039&amp;amp;face=0_0_2139_1039');&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;[Security] 암호화/복호화 이해하기 -1 : 기초, 암호화 종류(단방향, 양방향)&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;해당 글에서는 암호화/복호화에 대해 이해하며 암호화의 종류에 대해 이해를 돕기 위해 작성한 글입니다. 1) 암호화(Encryption) / 복호화(Decryption)1. 암호화(Encryption)  암호화(Encryption)- &amp;lsquo;평문&amp;rsquo;&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;adjh54.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;h3 data-ke-size=&quot;size23&quot;&gt;1. 단방향 암호화&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  단방향 암호화(One-Way Encryption)&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 암호화만 할 수 있고 복호화할 수 없는 암호화 기술을 의미합니다. 즉, 암호화된 데이터를 원래의 평문으로 되돌릴 수 없는 방식의 암호화를 의미합니다. &lt;/b&gt;&lt;br /&gt;- 해당 암호화 과정에서 정보가 손실되기에 한번 해시 함수를 거친 데이터는 복호화가 불가능합니다.&lt;br /&gt;- 단방향 암호화는 데이터의 기밀성보다는 무결성과 인증을 보장하는데 중점을 둡니다&lt;/blockquote&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;785&quot; data-origin-height=&quot;286&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/xNrGP/dJMcaaZqbD0/bl3gevNNptGVkPHiOgaark/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/xNrGP/dJMcaaZqbD0/bl3gevNNptGVkPHiOgaark/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/xNrGP/dJMcaaZqbD0/bl3gevNNptGVkPHiOgaark/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FxNrGP%2FdJMcaaZqbD0%2Fbl3gevNNptGVkPHiOgaark%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;785&quot; height=&quot;286&quot; data-origin-width=&quot;785&quot; data-origin-height=&quot;286&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;h3 data-ke-size=&quot;size23&quot;&gt;2. 대칭형 암호(=비공개 키 암호화)&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  대칭형 암호(=비공개 키 암호화)&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 평문 데이터를 암호화하거나 복호화할 때 &amp;lsquo;동일한 키&amp;rsquo;를 사용하는 암호화 방식을 의미합니다.&lt;/b&gt; &lt;br /&gt;- 이 방식에서는 송신자와 수신자가 동일한 비밀 키를 공유해야 하며, 이를 이용하여 데이터를 암호화, 복호화합니다.&lt;br /&gt;- 대표적인 대칭형 암호화 알고리즘으로는 AES, DES, 3DES 등이 있습니다.&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;719&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cQuO4g/dJMcaipDuCM/QTA4Xb0oj8Ckgyw4n2Ic51/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cQuO4g/dJMcaipDuCM/QTA4Xb0oj8Ckgyw4n2Ic51/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cQuO4g/dJMcaipDuCM/QTA4Xb0oj8Ckgyw4n2Ic51/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcQuO4g%2FdJMcaipDuCM%2FQTA4Xb0oj8Ckgyw4n2Ic51%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;655&quot; height=&quot;368&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;719&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;h3 data-ke-size=&quot;size23&quot;&gt;3. 비 대칭형 암호화(=공개 키 암호화)&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  비 대칭형 암호화(=공개 키 암호화)&lt;/b&gt;&lt;br /&gt;&lt;b&gt;&lt;br /&gt;- 평문 데이터를 암호화할 때와 복호화할 때 &amp;lsquo;서로 다른 암호화 키&amp;rsquo;를 사용하는 암호화 방식을 의미합니다.&lt;/b&gt; &lt;br /&gt;- 이 방식에서는 누구나 접근이 가능한 &amp;lsquo;공개 키&amp;rsquo;로 암호화를 하며, 특정 개인이 가지고 있는 &amp;lsquo;비밀 키&amp;rsquo;로만 복호화가 가능합니다.&lt;br /&gt;- 대표적인 비 대칭형 알고리즘으로는 RSA, DSA, ECC 등이 있습니다.&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;726&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bxFiG2/dJMcaiXp19C/8Kyap2meNmuDViBFA9Eb11/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bxFiG2/dJMcaiXp19C/8Kyap2meNmuDViBFA9Eb11/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bxFiG2/dJMcaiXp19C/8Kyap2meNmuDViBFA9Eb11/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbxFiG2%2FdJMcaiXp19C%2F8Kyap2meNmuDViBFA9Eb11%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;553&quot; height=&quot;314&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;726&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2) 설치 방법&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 설치를 합니다&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  설치를 합니다.&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;- 설치를 할 때, public 스키마가 아닌 지정된 스키마로 되어 있는지 확인해야 합니다. 일반적으로 실행을 하면 public 스키마에 확장이 설치가 됩니다.&lt;/blockquote&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;# 확장 추가
CREATE EXTENSION IF NOT EXISTS pgcrypto;

# or 

# 확장 제거
DROP EXTENSION IF EXISTS pgcrypto;
&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;h3 data-ke-size=&quot;size23&quot;&gt;2. 스키마 단위에서 Extension을 확인하여 설치 확인을 할 수 있습니다.&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;398&quot; data-origin-height=&quot;349&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dCC9Fx/dJMcagyyGzF/ddTjQE5qeHrtRd2rES6MWK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dCC9Fx/dJMcagyyGzF/ddTjQE5qeHrtRd2rES6MWK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dCC9Fx/dJMcagyyGzF/ddTjQE5qeHrtRd2rES6MWK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdCC9Fx%2FdJMcagyyGzF%2FddTjQE5qeHrtRd2rES6MWK%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;398&quot; height=&quot;349&quot; data-origin-width=&quot;398&quot; data-origin-height=&quot;349&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;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  [참고]&lt;/b&gt; 아래와 같이 SQL문으로 확인이 가능합니다.&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1050&quot; data-origin-height=&quot;329&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ckwQ6j/dJMcacCX5mH/omLuFL7iEBBjJCMrel8ey0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ckwQ6j/dJMcacCX5mH/omLuFL7iEBBjJCMrel8ey0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ckwQ6j/dJMcacCX5mH/omLuFL7iEBBjJCMrel8ey0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FckwQ6j%2FdJMcacCX5mH%2FomLuFL7iEBBjJCMrel8ey0%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;1050&quot; height=&quot;329&quot; data-origin-width=&quot;1050&quot; data-origin-height=&quot;329&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3) 주요 함수&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. pgcrypto 제공 함수&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style2&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 12.093%;&quot;&gt;&lt;b&gt;카테고리&amp;nbsp;&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 28.9535%;&quot;&gt;&lt;b&gt;함수&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 47.5581%;&quot;&gt;&lt;b&gt;설명&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 11.2791%;&quot;&gt;&lt;b&gt;반환 타입&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 12.093%;&quot;&gt;&lt;b&gt;General Hashing&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 28.9535%;&quot;&gt;&lt;b&gt;digest&lt;/b&gt;(data text, type text)&lt;/td&gt;
&lt;td style=&quot;width: 47.5581%;&quot;&gt;해시값 생성 (md5, sha1, sha224, sha256, sha384, sha512 + OpenSSL 지원 알고리즘)&lt;/td&gt;
&lt;td style=&quot;width: 11.2791%;&quot;&gt;bytea&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 12.093%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 28.9535%;&quot;&gt;&lt;b&gt;digest&lt;/b&gt;(data bytea, type text)&lt;/td&gt;
&lt;td style=&quot;width: 47.5581%;&quot;&gt;바이너리 데이터 해시&lt;/td&gt;
&lt;td style=&quot;width: 11.2791%;&quot;&gt;bytea&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 12.093%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 28.9535%;&quot;&gt;&lt;b&gt;hmac&lt;/b&gt;(data text, key text, type text)&lt;/td&gt;
&lt;td style=&quot;width: 47.5581%;&quot;&gt;키 기반 MAC 해시&lt;/td&gt;
&lt;td style=&quot;width: 11.2791%;&quot;&gt;bytea&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 12.093%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 28.9535%;&quot;&gt;&lt;b&gt;hmac&lt;/b&gt;(data bytea, key bytea, type text)&lt;/td&gt;
&lt;td style=&quot;width: 47.5581%;&quot;&gt;바이너리 키 기반 MAC 해시&lt;/td&gt;
&lt;td style=&quot;width: 11.2791%;&quot;&gt;bytea&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 12.093%;&quot;&gt;&lt;b&gt;Password Hashing&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 28.9535%;&quot;&gt;&lt;b&gt;crypt&lt;/b&gt;(password text, salt text)&lt;/td&gt;
&lt;td style=&quot;width: 47.5581%;&quot;&gt;crypt(3) 스타일 패스워드 해싱/검증&lt;/td&gt;
&lt;td style=&quot;width: 11.2791%;&quot;&gt;text&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 12.093%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 28.9535%;&quot;&gt;&lt;b&gt;gen_salt&lt;/b&gt;(type text [, iter_count integer])&lt;/td&gt;
&lt;td style=&quot;width: 47.5581%;&quot;&gt;솔트 생성 (des, xdes, md5, bf, sha256crypt, sha512crypt)&lt;/td&gt;
&lt;td style=&quot;width: 11.2791%;&quot;&gt;text&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 12.093%;&quot;&gt;&lt;b&gt;PGP 대칭키&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 28.9535%;&quot;&gt;&lt;b&gt;pgp_sym_encrypt&lt;/b&gt;(data text, psw text [, options text])&lt;/td&gt;
&lt;td style=&quot;width: 47.5581%;&quot;&gt;PGP 대칭키 암호화&lt;/td&gt;
&lt;td style=&quot;width: 11.2791%;&quot;&gt;bytea&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 12.093%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 28.9535%;&quot;&gt;&lt;b&gt;pgp_sym_encrypt_bytea&lt;/b&gt;(data bytea, psw text [, options text])&lt;/td&gt;
&lt;td style=&quot;width: 47.5581%;&quot;&gt;바이너리 PGP 대칭키 암호화&lt;/td&gt;
&lt;td style=&quot;width: 11.2791%;&quot;&gt;bytea&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 12.093%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 28.9535%;&quot;&gt;&lt;b&gt;pgp_sym_decrypt&lt;/b&gt;(msg bytea, psw text [, options text])&lt;/td&gt;
&lt;td style=&quot;width: 47.5581%;&quot;&gt;PGP 대칭키 복호화&lt;/td&gt;
&lt;td style=&quot;width: 11.2791%;&quot;&gt;text&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 12.093%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 28.9535%;&quot;&gt;&lt;b&gt;pgp_sym_decrypt_bytea&lt;/b&gt;(msg bytea, psw text [, options text])&lt;/td&gt;
&lt;td style=&quot;width: 47.5581%;&quot;&gt;바이너리 PGP 대칭키 복호화&lt;/td&gt;
&lt;td style=&quot;width: 11.2791%;&quot;&gt;bytea&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 12.093%;&quot;&gt;&lt;b&gt;PGP 비대칭키&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 28.9535%;&quot;&gt;&lt;b&gt;pgp_pub_encrypt&lt;/b&gt;(data text, key bytea [, options text])&lt;/td&gt;
&lt;td style=&quot;width: 47.5581%;&quot;&gt;공개키 암호화&lt;/td&gt;
&lt;td style=&quot;width: 11.2791%;&quot;&gt;bytea&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 12.093%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 28.9535%;&quot;&gt;&lt;b&gt;pgp_pub_encrypt_bytea&lt;/b&gt;(data bytea, key bytea [, options text])&lt;/td&gt;
&lt;td style=&quot;width: 47.5581%;&quot;&gt;바이너리 공개키 암호화&lt;/td&gt;
&lt;td style=&quot;width: 11.2791%;&quot;&gt;bytea&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 12.093%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 28.9535%;&quot;&gt;&lt;b&gt;pgp_pub_decrypt&lt;/b&gt;(msg bytea, key bytea [, psw text [, options text]])&lt;/td&gt;
&lt;td style=&quot;width: 47.5581%;&quot;&gt;개인키 복호화&lt;/td&gt;
&lt;td style=&quot;width: 11.2791%;&quot;&gt;text&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 12.093%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 28.9535%;&quot;&gt;&lt;b&gt;pgp_pub_decrypt_bytea&lt;/b&gt;(msg bytea, key bytea [, psw text [, options text]])&lt;/td&gt;
&lt;td style=&quot;width: 47.5581%;&quot;&gt;바이너리 개인키 복호화&lt;/td&gt;
&lt;td style=&quot;width: 11.2791%;&quot;&gt;bytea&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 12.093%;&quot;&gt;&lt;b&gt;PGP 키/Armor&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 28.9535%;&quot;&gt;&lt;b&gt;pgp_key_id&lt;/b&gt;(bytea)&lt;/td&gt;
&lt;td style=&quot;width: 47.5581%;&quot;&gt;PGP 키 ID 추출 (SYMKEY, ANYKEY 특수값 반환 가능)&lt;/td&gt;
&lt;td style=&quot;width: 11.2791%;&quot;&gt;text&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 12.093%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 28.9535%;&quot;&gt;&lt;b&gt;armor&lt;/b&gt;(data bytea [, keys text[], values text[]])&lt;/td&gt;
&lt;td style=&quot;width: 47.5581%;&quot;&gt;바이너리 &amp;rarr; PGP ASCII Armor 변환&lt;/td&gt;
&lt;td style=&quot;width: 11.2791%;&quot;&gt;text&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 12.093%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 28.9535%;&quot;&gt;&lt;b&gt;dearmor&lt;/b&gt;(data text)&lt;/td&gt;
&lt;td style=&quot;width: 47.5581%;&quot;&gt;PGP ASCII Armor &amp;rarr; 바이너리 변환&lt;/td&gt;
&lt;td style=&quot;width: 11.2791%;&quot;&gt;bytea&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 12.093%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 28.9535%;&quot;&gt;&lt;b&gt;pgp_armor_headers&lt;/b&gt;(data text, key out text, value out text)&lt;/td&gt;
&lt;td style=&quot;width: 47.5581%;&quot;&gt;Armor 헤더 key/value 추출&lt;/td&gt;
&lt;td style=&quot;width: 11.2791%;&quot;&gt;setof record&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 12.093%;&quot;&gt;&lt;b&gt;Raw 암호화&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 28.9535%;&quot;&gt;&lt;b&gt;encrypt&lt;/b&gt;(data bytea, key bytea, type text)&lt;/td&gt;
&lt;td style=&quot;width: 47.5581%;&quot;&gt;로우레벨 암호화 (bf, aes + cbc/cfb/ecb + pkcs/none)&lt;/td&gt;
&lt;td style=&quot;width: 11.2791%;&quot;&gt;bytea&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 12.093%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 28.9535%;&quot;&gt;&lt;b&gt;decrypt&lt;/b&gt;(data bytea, key bytea, type text)&lt;/td&gt;
&lt;td style=&quot;width: 47.5581%;&quot;&gt;로우레벨 복호화&lt;/td&gt;
&lt;td style=&quot;width: 11.2791%;&quot;&gt;bytea&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 12.093%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 28.9535%;&quot;&gt;&lt;b&gt;encrypt_iv&lt;/b&gt;(data bytea, key bytea, iv bytea, type text)&lt;/td&gt;
&lt;td style=&quot;width: 47.5581%;&quot;&gt;IV 지정 암호화&lt;/td&gt;
&lt;td style=&quot;width: 11.2791%;&quot;&gt;bytea&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 12.093%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 28.9535%;&quot;&gt;&lt;b&gt;decrypt_iv&lt;/b&gt;(data bytea, key bytea, iv bytea, type text)&lt;/td&gt;
&lt;td style=&quot;width: 47.5581%;&quot;&gt;IV 지정 복호화&lt;/td&gt;
&lt;td style=&quot;width: 11.2791%;&quot;&gt;bytea&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 12.093%;&quot;&gt;&lt;b&gt;Random&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 28.9535%;&quot;&gt;&lt;b&gt;gen_random_bytes&lt;/b&gt;(count integer)&lt;/td&gt;
&lt;td style=&quot;width: 47.5581%;&quot;&gt;암호학적 랜덤 바이트 (최대 1024)&lt;/td&gt;
&lt;td style=&quot;width: 11.2791%;&quot;&gt;bytea&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 12.093%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 28.9535%;&quot;&gt;&lt;b&gt;gen_random_uuid&lt;/b&gt;()&lt;/td&gt;
&lt;td style=&quot;width: 47.5581%;&quot;&gt;UUID v4 생성 (PG 18에서는 코어 함수 래퍼, obsolete)&lt;/td&gt;
&lt;td style=&quot;width: 11.2791%;&quot;&gt;uuid&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 12.093%;&quot;&gt;&lt;b&gt;OpenSSL&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 28.9535%;&quot;&gt;&lt;b&gt;fips_mode&lt;/b&gt;()&lt;/td&gt;
&lt;td style=&quot;width: 47.5581%;&quot;&gt;OpenSSL FIPS 모드 활성화 여부&lt;/td&gt;
&lt;td style=&quot;width: 11.2791%;&quot;&gt;boolean&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;figure id=&quot;og_1777563015330&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;F.26.&amp;nbsp;pgcrypto &amp;mdash; cryptographic functions&quot; data-og-description=&quot;F.26.&amp;nbsp;pgcrypto &amp;mdash; cryptographic functions # F.26.1. General Hashing Functions F.26.2. Password Hashing Functions F.26.3. PGP Encryption Functions F.26.4. Raw Encryption &amp;hellip;&quot; data-og-host=&quot;www.postgresql.org&quot; data-og-source-url=&quot;https://www.postgresql.org/docs/current/pgcrypto.html&quot; data-og-url=&quot;https://www.postgresql.org/docs/18/pgcrypto.html&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cZF1Ni/dJMb887b7ws/LyaZkcZBk0wpLR9p7u4yjk/img.png?width=540&amp;amp;height=557&amp;amp;face=0_0_540_557,https://scrap.kakaocdn.net/dn/EyhXw/dJMb87NZPSw/vImxHwy1DnhMQOOMbonaVK/img.png?width=540&amp;amp;height=557&amp;amp;face=0_0_540_557&quot;&gt;&lt;a href=&quot;https://www.postgresql.org/docs/current/pgcrypto.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.postgresql.org/docs/current/pgcrypto.html&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cZF1Ni/dJMb887b7ws/LyaZkcZBk0wpLR9p7u4yjk/img.png?width=540&amp;amp;height=557&amp;amp;face=0_0_540_557,https://scrap.kakaocdn.net/dn/EyhXw/dJMb87NZPSw/vImxHwy1DnhMQOOMbonaVK/img.png?width=540&amp;amp;height=557&amp;amp;face=0_0_540_557');&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;F.26.&amp;nbsp;pgcrypto &amp;mdash; cryptographic functions&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;F.26.&amp;nbsp;pgcrypto &amp;mdash; cryptographic functions # F.26.1. General Hashing Functions F.26.2. Password Hashing Functions F.26.3. PGP Encryption Functions F.26.4. Raw Encryption &amp;hellip;&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.postgresql.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;
&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;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;[ 더 알아보기 ]&lt;/b&gt;&lt;br /&gt;&lt;b&gt;&lt;br /&gt; IV (Initialization Vector): 초기화 벡터&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;- 암호화할 때 맨 처음 블록에 섞어주는 랜덤 값입니다.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;[ IV가 없는 경우 ]&lt;br /&gt;&lt;/b&gt; &lt;br /&gt;- 평문: &quot;홍길동&quot; &amp;rarr; 암호화 &amp;rarr; A1B2C3... &lt;br /&gt;- 평문: &quot;홍길동&quot; &amp;rarr; 암호화 &amp;rarr; A1B2C3... &amp;larr; 항상 동일 &lt;br /&gt;- 평문: &quot;홍길동&quot; &amp;rarr; 암호화 &amp;rarr; A1B2C3... &amp;larr; 항상 동일&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;[ IV가 있는 경우]&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;- 평문: &quot;홍길동&quot; + IV(랜덤1) &amp;rarr; 암호화 &amp;rarr; A1B2C3... &lt;br /&gt;- 평문: &quot;홍길동&quot; + IV(랜덤2) &amp;rarr; 암호화 &amp;rarr; X9Y8Z7... &amp;larr; 다름 &lt;br /&gt;- 평문: &quot;홍길동&quot; + IV(랜덤3) &amp;rarr; 암호화 &amp;rarr; P3Q4R5... &amp;larr; 다름&lt;br /&gt;&lt;br /&gt;&lt;b&gt; &amp;nbsp;CBC (Cipher Block Chaining): 블록 체인 암호화&lt;br /&gt;&lt;/b&gt;&lt;br /&gt;- 평문을 일정 크기의 블록으로 쪼개서 앞 블록의 암호문을 다음 블록 암호화에 연쇄적으로 섞는 방식입니다.&lt;br /&gt;&lt;br /&gt;- 아래와 같이 수행이 됩니다.&lt;br /&gt;1. 첫 번째 블록은 IV와 XOR 후 암호화&lt;br /&gt;2. 두 번째 블록은 앞 블록의 암호문과 XOR 후 암호화&lt;br /&gt;3. 이게 계속 체인처럼 연결되며 수행이 됩니다.&lt;br /&gt;&lt;br /&gt;[IV] ──────────────────────────────┐ &lt;br /&gt;&amp;darr; │ [평문 블록1] &amp;rarr; XOR &amp;rarr; [암호화] &amp;rarr; [암호문 블록1] &lt;br /&gt;&amp;darr; [평문 블록2] &amp;rarr; XOR &amp;rarr; [암호화] &amp;rarr; [암호문 블록2] &lt;br /&gt;&amp;darr; [평문 블록3] &amp;rarr; XOR &amp;rarr; [암호화] &amp;rarr; [암호문 블록3]&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;h2 data-ke-size=&quot;size26&quot;&gt;4) 활용 예시 : ASE256 기반 암복호화&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;&amp;nbsp;  활용 예시&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 이메일을 ASE256으로 암호화하여 저장하고, 불러오는 과정을 SQL문에서 수행하도록 처리합니다&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 현재 사용자 데이터 확인&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  현재 사용자 데이터 확인&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 아래와 같은 간단한 TB_USER 테이블이 있습니다.&lt;/b&gt;&lt;br /&gt;- 해당 컬럼 중에서 user_email 컬럼에 ASE256 암호화/복호화를 이용할 예정이며, 이를 위해서는 BYTEA 타입으로 지정을 해야 합니다.&lt;/blockquote&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style2&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 18.4884%;&quot;&gt;&lt;b&gt;컬럼명&amp;nbsp;&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 21.2791%;&quot;&gt;&lt;b&gt;NULL&amp;nbsp;&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 6.16279%;&quot;&gt;&lt;b&gt;PK&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 6.04651%;&quot;&gt;&lt;b&gt;FK&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 25.2326%;&quot;&gt;&lt;b&gt;기본 값&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 22.7907%;&quot;&gt;&lt;b&gt;설명&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 18.4884%;&quot;&gt;&lt;b&gt;USER_SQ&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 21.2791%;&quot;&gt;BIGSERIAL&lt;/td&gt;
&lt;td style=&quot;width: 6.16279%;&quot;&gt;N&lt;/td&gt;
&lt;td style=&quot;width: 6.04651%;&quot;&gt;✅&lt;/td&gt;
&lt;td style=&quot;width: 25.2326%;&quot;&gt;AUTO INCREMENT&lt;/td&gt;
&lt;td style=&quot;width: 22.7907%;&quot;&gt;사용자 시퀀스 (PK)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 18.4884%;&quot;&gt;&lt;b&gt;USER_ID&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 21.2791%;&quot;&gt;VARCHAR(50)&lt;/td&gt;
&lt;td style=&quot;width: 6.16279%;&quot;&gt;N&lt;/td&gt;
&lt;td style=&quot;width: 6.04651%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 25.2326%;&quot;&gt;-&lt;/td&gt;
&lt;td style=&quot;width: 22.7907%;&quot;&gt;사용자 사번&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 18.4884%;&quot;&gt;&lt;b&gt;USER_EMAIL&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 21.2791%;&quot;&gt;&lt;u&gt;&lt;b&gt;BYTEA&lt;/b&gt;&lt;/u&gt;&lt;/td&gt;
&lt;td style=&quot;width: 6.16279%;&quot;&gt;N&lt;/td&gt;
&lt;td style=&quot;width: 6.04651%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 25.2326%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 22.7907%;&quot;&gt;사용자 이메일&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 18.4884%;&quot;&gt;&lt;b&gt;USER_PW&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 21.2791%;&quot;&gt;VARCHAR(255)&lt;/td&gt;
&lt;td style=&quot;width: 6.16279%;&quot;&gt;N&lt;/td&gt;
&lt;td style=&quot;width: 6.04651%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 25.2326%;&quot;&gt;-&lt;/td&gt;
&lt;td style=&quot;width: 22.7907%;&quot;&gt;비밀번호&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 18.4884%;&quot;&gt;&lt;b&gt;USE_YN&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 21.2791%;&quot;&gt;BOOLEAN&lt;/td&gt;
&lt;td style=&quot;width: 6.16279%;&quot;&gt;N&lt;/td&gt;
&lt;td style=&quot;width: 6.04651%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 25.2326%;&quot;&gt;TRUE&lt;/td&gt;
&lt;td style=&quot;width: 22.7907%;&quot;&gt;사용여부&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 18.4884%;&quot;&gt;&lt;b&gt;REG_TS&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 21.2791%;&quot;&gt;TIMESTAMP&lt;/td&gt;
&lt;td style=&quot;width: 6.16279%;&quot;&gt;N&lt;/td&gt;
&lt;td style=&quot;width: 6.04651%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 25.2326%;&quot;&gt;NOW()&lt;/td&gt;
&lt;td style=&quot;width: 22.7907%;&quot;&gt;생성일시&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 18.4884%;&quot;&gt;&lt;b&gt;MOD_TS&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 21.2791%;&quot;&gt;TIMESTAMP&lt;/td&gt;
&lt;td style=&quot;width: 6.16279%;&quot;&gt;N&lt;/td&gt;
&lt;td style=&quot;width: 6.04651%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 25.2326%;&quot;&gt;NOW()&lt;/td&gt;
&lt;td style=&quot;width: 22.7907%;&quot;&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;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  ASE256 암호화의 함수의 데이터 구조&lt;/b&gt;&lt;/blockquote&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;# ASE256의 암호화(입력 TEXT -&amp;gt; 출력 BYTEA)
pgp_sym_encrypt(data text, psw text [, options text ]) returns bytea

# ASE256의 암호화(입력 BYTEA -&amp;gt; 출력 BYTEA)
pgp_sym_encrypt_bytea(data bytea, psw text [, options text ]) returns bytea

# ASE256의 복호화(입력 BYTEA -&amp;gt; 출력 TEXT)
pgp_sym_decrypt(msg bytea, psw text [, options text ]) returns text

# ASE256의 복호화(입력 BYTEA -&amp;gt; 출력 BYTEA)
pgp_sym_decrypt_bytea(msg bytea, psw text [, options text ]) returns bytea
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1856&quot; data-origin-height=&quot;393&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ESnWb/dJMcah5llVF/70K2yXz8U9Keyxy1a8klNK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ESnWb/dJMcah5llVF/70K2yXz8U9Keyxy1a8klNK/img.png&quot; data-alt=&quot;https://postgresql.kr/docs/11/pgcrypto.html&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ESnWb/dJMcah5llVF/70K2yXz8U9Keyxy1a8klNK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FESnWb%2FdJMcah5llVF%2F70K2yXz8U9Keyxy1a8klNK%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;1856&quot; height=&quot;393&quot; data-origin-width=&quot;1856&quot; data-origin-height=&quot;393&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://postgresql.kr/docs/11/pgcrypto.html&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;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. 데이터 암호화 및 INSERT&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  데이터 암호화 및 INSERT&lt;/b&gt;&lt;br /&gt;&lt;b&gt;&lt;br /&gt;- 위에 데이터구조 형태로 예시로 데이터를 INSERT 합니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;pre class=&quot;1c&quot;&gt;&lt;code&gt;INSERT INTO tb_user (user_id, user_email, user_pw, use_yn, reg_ts, mod_ts)
VALUES (
     'TEST1234',
     pgp_sym_encrypt('test1234@gmail.com', 'Symmetric Key', 'cipher-algo=aes256'),
     '54507c79d2132cf8b4817d6837a04357a164989fb73e176364f64b00be15a591',
     true,
     NOW(),
     NOW()
 );
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;886&quot; data-origin-height=&quot;284&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b5XFHW/dJMcagFnzGk/KVFqLVgzQ4bXxSTGEikakK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b5XFHW/dJMcagFnzGk/KVFqLVgzQ4bXxSTGEikakK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b5XFHW/dJMcagFnzGk/KVFqLVgzQ4bXxSTGEikakK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb5XFHW%2FdJMcagFnzGk%2FKVFqLVgzQ4bXxSTGEikakK%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;886&quot; height=&quot;284&quot; data-origin-width=&quot;886&quot; data-origin-height=&quot;284&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  아래와 같이 데이터 ROW가 등록되었습니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1372&quot; data-origin-height=&quot;116&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bMwp70/dJMcadaINxK/SGAsfbh56SW2QVO6kGNKK0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bMwp70/dJMcadaINxK/SGAsfbh56SW2QVO6kGNKK0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bMwp70/dJMcadaINxK/SGAsfbh56SW2QVO6kGNKK0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbMwp70%2FdJMcadaINxK%2FSGAsfbh56SW2QVO6kGNKK0%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;1372&quot; height=&quot;116&quot; data-origin-width=&quot;1372&quot; data-origin-height=&quot;116&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;h3 data-ke-size=&quot;size23&quot;&gt;3. 데이터 복호화 및 SELECT&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  데이터 복호화 및 SELECT&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;- ASE256의 경우는 대칭형 암호화이기에 동일한 키를 사용해서 복호화를 합니다. 이를 기반으로 복호화를 하여서 수행합니다.&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;719&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dLXG8p/dJMcacCX6dh/JqsvH8s6KZSvLxajGnTg91/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dLXG8p/dJMcacCX6dh/JqsvH8s6KZSvLxajGnTg91/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dLXG8p/dJMcacCX6dh/JqsvH8s6KZSvLxajGnTg91/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdLXG8p%2FdJMcacCX6dh%2FJqsvH8s6KZSvLxajGnTg91%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;604&quot; height=&quot;339&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;719&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;pre class=&quot;sql&quot; data-ke-language=&quot;sql&quot;&gt;&lt;code&gt;SELECT
  user_sq,
  user_id,
  pgp_sym_decrypt(user_email, 'Symmetric Key') AS user_email,
  user_pw,
  use_yn,
  reg_ts,
  mod_ts
FROM tb_user;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1372&quot; data-origin-height=&quot;274&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/QjtTC/dJMcaaZqc0o/J3vW9T1gPSxfSmOV62LHvk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/QjtTC/dJMcaaZqc0o/J3vW9T1gPSxfSmOV62LHvk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/QjtTC/dJMcaaZqc0o/J3vW9T1gPSxfSmOV62LHvk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FQjtTC%2FdJMcaaZqc0o%2FJ3vW9T1gPSxfSmOV62LHvk%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;1372&quot; height=&quot;274&quot; data-origin-width=&quot;1372&quot; data-origin-height=&quot;274&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 data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;오늘도&amp;nbsp;감사합니다.&amp;nbsp; &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;&amp;nbsp;&lt;/p&gt;</description>
      <category>DB/이론 및 문법</category>
      <category>db encrypt</category>
      <category>db 복호화</category>
      <category>DB 암호화</category>
      <category>PostgreSQL</category>
      <category>PostgreSQL Extension</category>
      <category>PostgreSQL pgcrypto</category>
      <category>데이터베이스 암호화</category>
      <category>데이터베이스 암호화 함수</category>
      <author>adjh54</author>
      <guid isPermaLink="true">https://adjh54.tistory.com/753</guid>
      <comments>https://adjh54.tistory.com/753#entry753comment</comments>
      <pubDate>Fri, 1 May 2026 00:34:51 +0900</pubDate>
    </item>
    <item>
      <title>[Git] git-credential-osxkeychain이(가) 키체인에서 'github.com' 키 접근을 허용하고자 합니다. 해결방법</title>
      <link>https://adjh54.tistory.com/752</link>
      <description>&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;해당 글에서는 git-credential-osxkeychain이(가) 키체인에서 'github.com' 키 접근을 허용하고자 합니다. 매번 발생하는 오류에 대해서 해결방법에 대해 알아봅니다&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;
&lt;figure contenteditable=&quot;false&quot; data-ke-type=&quot;emoticon&quot; data-ke-align=&quot;alignCenter&quot; data-emoticon-type=&quot;face&quot; data-emoticon-name=&quot;073&quot; data-emoticon-isanimation=&quot;false&quot; data-emoticon-src=&quot;https://t1.daumcdn.net/axz_keditor/emoticon/face/large/073.png&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/axz_keditor/emoticon/face/large/073.png&quot; width=&quot;80&quot; /&gt;&lt;/figure&gt;
&lt;/blockquote&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1) 문제점 확인&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  문제점 확인&lt;/b&gt; &lt;br /&gt;&lt;b&gt;&lt;br /&gt;- git-credential-osxkeychain이(가) 키체인에서 'github.com' 키 접근을 허용하고자 합니다.&lt;/b&gt; &lt;br /&gt;- 아래와 같이 SourceTree를 이용할 때 반복적으로 발생하는 문제점이 나타나고 있습니다. &lt;br /&gt;- 비밀번호를 입력하고 &amp;lsquo;항상 허용&amp;rsquo;으로 해도 다시 접근을 하더라도 동일하게 발생하는 문제입니다.&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;546&quot; data-origin-height=&quot;294&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b6ydSL/dJMcaiXpYSf/5A4gV8XSoXORQOnAE5MelK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b6ydSL/dJMcaiXpYSf/5A4gV8XSoXORQOnAE5MelK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b6ydSL/dJMcaiXpYSf/5A4gV8XSoXORQOnAE5MelK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb6ydSL%2FdJMcaiXpYSf%2F5A4gV8XSoXORQOnAE5MelK%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;546&quot; height=&quot;294&quot; data-origin-width=&quot;546&quot; data-origin-height=&quot;294&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;h2 data-ke-size=&quot;size26&quot;&gt;2) 해결방법&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. Spotlight &amp;rarr; &quot;키체인 접근&quot; 검색 후 실행&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;686&quot; data-origin-height=&quot;513&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Wh5jf/dJMcafGsc2L/kAr2qK6uhzylpOJ46aY3kK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Wh5jf/dJMcafGsc2L/kAr2qK6uhzylpOJ46aY3kK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Wh5jf/dJMcafGsc2L/kAr2qK6uhzylpOJ46aY3kK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FWh5jf%2FdJMcafGsc2L%2FkAr2qK6uhzylpOJ46aY3kK%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;686&quot; height=&quot;513&quot; data-origin-width=&quot;686&quot; data-origin-height=&quot;513&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;h3 data-ke-size=&quot;size23&quot;&gt;2. github.com 항목 찾기&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1624&quot; data-origin-height=&quot;969&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cQyDvy/dJMcagL8IBb/hcxKC3YSQkcEkrTLeDElgk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cQyDvy/dJMcagL8IBb/hcxKC3YSQkcEkrTLeDElgk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cQyDvy/dJMcagL8IBb/hcxKC3YSQkcEkrTLeDElgk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcQyDvy%2FdJMcagL8IBb%2FhcxKC3YSQkcEkrTLeDElgk%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;1624&quot; height=&quot;969&quot; data-origin-width=&quot;1624&quot; data-origin-height=&quot;969&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;h3 data-ke-size=&quot;size23&quot;&gt;3. 더블클릭 &amp;rarr; &quot;접근 제어&quot; 탭 내에 불필요한 내용을 우선 제거합니다.&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;646&quot; data-origin-height=&quot;483&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qdw3i/dJMcadPkuGJ/UaNl5v0tB7JfvFCm8pbKrk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qdw3i/dJMcadPkuGJ/UaNl5v0tB7JfvFCm8pbKrk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qdw3i/dJMcadPkuGJ/UaNl5v0tB7JfvFCm8pbKrk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fqdw3i%2FdJMcadPkuGJ%2FUaNl5v0tB7JfvFCm8pbKrk%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;646&quot; height=&quot;483&quot; data-origin-width=&quot;646&quot; data-origin-height=&quot;483&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;h3 data-ke-size=&quot;size23&quot;&gt;4. 모든 응용 프로그램이 이 항목에 접근할 수 있도록 허용을 선택합니다.&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;646&quot; data-origin-height=&quot;483&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b9E0r5/dJMcadIzU8c/AmAIXEqpUgRj3dAsOqEbB1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b9E0r5/dJMcadIzU8c/AmAIXEqpUgRj3dAsOqEbB1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b9E0r5/dJMcadIzU8c/AmAIXEqpUgRj3dAsOqEbB1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb9E0r5%2FdJMcadIzU8c%2FAmAIXEqpUgRj3dAsOqEbB1%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;646&quot; height=&quot;483&quot; data-origin-width=&quot;646&quot; data-origin-height=&quot;483&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;h3 data-ke-size=&quot;size23&quot;&gt;5. 앞으로 또 다시또다시 나오면 &amp;lsquo;항상 허용&amp;rsquo;으로 다시 저장을 하면, 또다시 나오지 않습니다.&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;546&quot; data-origin-height=&quot;294&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/eS63ho/dJMcahdbvvG/JotF1MJlRMqu7uLfJpCDjK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/eS63ho/dJMcahdbvvG/JotF1MJlRMqu7uLfJpCDjK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/eS63ho/dJMcahdbvvG/JotF1MJlRMqu7uLfJpCDjK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FeS63ho%2FdJMcahdbvvG%2FJotF1MJlRMqu7uLfJpCDjK%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;546&quot; height=&quot;294&quot; data-origin-width=&quot;546&quot; data-origin-height=&quot;294&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;</description>
      <category>Github/이해하기</category>
      <category>git</category>
      <category>git 오류</category>
      <category>git-credential-osxkeychain</category>
      <category>git-credential-osxkeychain이(가) 키체인에서 'github.com' 키 접근을 허용하고자 합니다.</category>
      <category>github 키체인</category>
      <category>키체인에서 'github.com' 키 접근을 허용하고자 합니다.</category>
      <author>adjh54</author>
      <guid isPermaLink="true">https://adjh54.tistory.com/752</guid>
      <comments>https://adjh54.tistory.com/752#entry752comment</comments>
      <pubDate>Thu, 30 Apr 2026 20:05:22 +0900</pubDate>
    </item>
    <item>
      <title>[Git] git@github.com : Permission denied (publickey) 오류 해결 방법</title>
      <link>https://adjh54.tistory.com/751</link>
      <description>&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;해당 글에서는 github 내에서 발생하는 git@github.com : Permission denied (publickey) 오류에 대해서 해결하는 방법에 대해 알아봅니다&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;
&lt;figure contenteditable=&quot;false&quot; data-ke-type=&quot;emoticon&quot; data-ke-align=&quot;alignCenter&quot; data-emoticon-type=&quot;face&quot; data-emoticon-name=&quot;073&quot; data-emoticon-isanimation=&quot;false&quot; data-emoticon-src=&quot;https://t1.daumcdn.net/axz_keditor/emoticon/face/large/073.png&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/axz_keditor/emoticon/face/large/073.png&quot; width=&quot;80&quot; /&gt;&lt;/figure&gt;
&lt;/blockquote&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1) 문제점&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  문제점&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 아래와 같이 연결된 repository 내에 git pull 명령어를 수행하였을 때, 문제가 발생하고 있습니다.&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;git@github.com: Permission denied (publickey). &lt;br /&gt;fatal: Could not read from remote repository.&lt;br /&gt;&lt;br /&gt;Please make sure you have the correct access rights and the repository exists.&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1028&quot; data-origin-height=&quot;95&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qWwbO/dJMcahqJvMj/ipBccisRfJcPkRa9s24VGK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qWwbO/dJMcahqJvMj/ipBccisRfJcPkRa9s24VGK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qWwbO/dJMcahqJvMj/ipBccisRfJcPkRa9s24VGK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FqWwbO%2FdJMcahqJvMj%2FipBccisRfJcPkRa9s24VGK%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;1028&quot; height=&quot;95&quot; data-origin-width=&quot;1028&quot; data-origin-height=&quot;95&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;h3 data-ke-size=&quot;size23&quot;&gt;1. git 연결 테스트&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  git 연결 테스트&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 해당 repository의 문제인지 아닌지 확인을 위해 실제로도 github에 연결에 대한 테스트를 수행합니다.&lt;/b&gt;&lt;br /&gt;- 아래와 같이 permission deined(publickey) 문제가 발생하고 있습니다.&lt;/blockquote&gt;
&lt;pre class=&quot;elixir&quot;&gt;&lt;code&gt;# ssh 연결 테스트
$ ssh -T git@github.com
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;699&quot; data-origin-height=&quot;595&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dIhFdG/dJMcahqJvNZ/ctvtHxmdCXoMnMGlGH2xQK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dIhFdG/dJMcahqJvNZ/ctvtHxmdCXoMnMGlGH2xQK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dIhFdG/dJMcahqJvNZ/ctvtHxmdCXoMnMGlGH2xQK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdIhFdG%2FdJMcahqJvNZ%2FctvtHxmdCXoMnMGlGH2xQK%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;699&quot; height=&quot;595&quot; data-origin-width=&quot;699&quot; data-origin-height=&quot;595&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2) 해결방법&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  해결방법&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;- &lt;b&gt;SSH 공개키가 GitHub에 등록되어 있지 않거나, SSH 에이전트에 키가 로드되지 않아서 발생하는 문제입니다.&lt;/b&gt;&lt;br /&gt;- 새 맥/PC로 이전 후 GitHub에 새 머신의 공개키를 등록 안 한 경우, 또는 키체인 문제로 에이전트에서 키가 날아간 경우 발생하는 문제라고 합니다.&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;h3 data-ke-size=&quot;size23&quot;&gt;1. SSH 키 존재 확인&lt;/h3&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;style3&quot;&gt;&lt;b&gt;&amp;nbsp;  SSH 키 존재 확인&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;- 루트 경로에 SSH 키가 존재하는지 확인합니다.&lt;br /&gt;- 해당 경로에 id_rsa.pub 또는 id_ed25519.pub 파일이 있어야 정상입니다.&lt;/blockquote&gt;
&lt;pre class=&quot;jboss-cli&quot;&gt;&lt;code&gt;ls -al ~/.ssh
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;699&quot; data-origin-height=&quot;595&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/DLKhv/dJMcabRyI5P/KtRIUy0Er60xLOzoqj5Tpk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/DLKhv/dJMcabRyI5P/KtRIUy0Er60xLOzoqj5Tpk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/DLKhv/dJMcabRyI5P/KtRIUy0Er60xLOzoqj5Tpk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FDLKhv%2FdJMcabRyI5P%2FKtRIUy0Er60xLOzoqj5Tpk%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;699&quot; height=&quot;595&quot; data-origin-width=&quot;699&quot; data-origin-height=&quot;595&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;h3 data-ke-size=&quot;size23&quot;&gt;2. SSH 키를 생성합니다.&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  SSH 키를 생성합니다.&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;- Enter file in which to save the key &amp;rarr; 그냥 Enter(기본 경로 사용)&lt;br /&gt;- Enter passphrase &amp;rarr; 그냥 Enter (비밀번호 없음)&lt;br /&gt;- Enter same passphrase again &amp;rarr; 그냥 Enter&lt;/blockquote&gt;
&lt;pre class=&quot;elixir&quot;&gt;&lt;code&gt;# 아래의 명령어를 입력하여 SSH 키를 생성합니다.
$ ssh-keygen -t ed25519 -C &quot;your_email@example.com&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;699&quot; data-origin-height=&quot;595&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cJ37W4/dJMcajvhfzp/mkplzaIJKHf6uKlhUffGmK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cJ37W4/dJMcajvhfzp/mkplzaIJKHf6uKlhUffGmK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cJ37W4/dJMcajvhfzp/mkplzaIJKHf6uKlhUffGmK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcJ37W4%2FdJMcajvhfzp%2FmkplzaIJKHf6uKlhUffGmK%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;699&quot; height=&quot;595&quot; data-origin-width=&quot;699&quot; data-origin-height=&quot;595&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;h3 data-ke-size=&quot;size23&quot;&gt;3. 생성한 SSH 공개키 등록 확인&lt;/h3&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;style3&quot;&gt;&lt;b&gt;&amp;nbsp;  생성한 SSH 공개키 등록 확인&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 위에서 생성한 SSH 키에 대해서 생성을 확인합니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;pre class=&quot;arcade&quot;&gt;&lt;code&gt; cat ~/.ssh/id_ed25519.pub
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;699&quot; data-origin-height=&quot;595&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cxBVkW/dJMcahYxQKS/jJ15KuFZRwjPTYShmYExPk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cxBVkW/dJMcahYxQKS/jJ15KuFZRwjPTYShmYExPk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cxBVkW/dJMcahYxQKS/jJ15KuFZRwjPTYShmYExPk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcxBVkW%2FdJMcahYxQKS%2FjJ15KuFZRwjPTYShmYExPk%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;699&quot; height=&quot;595&quot; data-origin-width=&quot;699&quot; data-origin-height=&quot;595&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;h3 data-ke-size=&quot;size23&quot;&gt;4. [SSH Key 등록] github 사이트 &amp;gt; 프로필 &amp;gt; Setting을 선택합니다.&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2032&quot; data-origin-height=&quot;1078&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bygVve/dJMcad2RPt4/8pyiXkwKeofyKKAGgajU40/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bygVve/dJMcad2RPt4/8pyiXkwKeofyKKAGgajU40/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bygVve/dJMcad2RPt4/8pyiXkwKeofyKKAGgajU40/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbygVve%2FdJMcad2RPt4%2F8pyiXkwKeofyKKAGgajU40%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2032&quot; height=&quot;1078&quot; data-origin-width=&quot;2032&quot; data-origin-height=&quot;1078&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;h3 data-ke-size=&quot;size23&quot;&gt;5. [SSH Key 등록] SSH and GPG Keys를 선택합니다.&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2032&quot; data-origin-height=&quot;1078&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/9ukGM/dJMcafTYXYH/K7MLdAlWp1JN0rr6KnYxK0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/9ukGM/dJMcafTYXYH/K7MLdAlWp1JN0rr6KnYxK0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/9ukGM/dJMcafTYXYH/K7MLdAlWp1JN0rr6KnYxK0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F9ukGM%2FdJMcafTYXYH%2FK7MLdAlWp1JN0rr6KnYxK0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2032&quot; height=&quot;1078&quot; data-origin-width=&quot;2032&quot; data-origin-height=&quot;1078&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;h3 data-ke-size=&quot;size23&quot;&gt;6. [SSH Key 등록] &amp;lsquo;New SSH Key&amp;rsquo;를 선택합니다&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2032&quot; data-origin-height=&quot;1078&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/DrUWa/dJMcagk1DMW/vMtqsbHeuiekBg9O90tG70/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/DrUWa/dJMcagk1DMW/vMtqsbHeuiekBg9O90tG70/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/DrUWa/dJMcagk1DMW/vMtqsbHeuiekBg9O90tG70/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FDrUWa%2FdJMcagk1DMW%2FvMtqsbHeuiekBg9O90tG70%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2032&quot; height=&quot;1078&quot; data-origin-width=&quot;2032&quot; data-origin-height=&quot;1078&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;h3 data-ke-size=&quot;size23&quot;&gt;7. [SSH Key 등록] 등록이 완료되었습니다.&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2032&quot; data-origin-height=&quot;1078&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c550df/dJMcag6p40Q/P7QBaTCAlZKKprAfgO7nS1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c550df/dJMcag6p40Q/P7QBaTCAlZKKprAfgO7nS1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c550df/dJMcag6p40Q/P7QBaTCAlZKKprAfgO7nS1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc550df%2FdJMcag6p40Q%2FP7QBaTCAlZKKprAfgO7nS1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2032&quot; height=&quot;1078&quot; data-origin-width=&quot;2032&quot; data-origin-height=&quot;1078&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;h3 data-ke-size=&quot;size23&quot;&gt;8. SSH 에이전트에 키 로드&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;shell&quot;&gt;&lt;code&gt;$ eval &quot;$(ssh-agent -s)&quot;
$ ssh-add ~/.ssh/id_ed25519
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;699&quot; data-origin-height=&quot;595&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/d0i2pz/dJMcadu1gOZ/vgSWkoIr0QLV3ed4iNrHVK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/d0i2pz/dJMcadu1gOZ/vgSWkoIr0QLV3ed4iNrHVK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/d0i2pz/dJMcadu1gOZ/vgSWkoIr0QLV3ed4iNrHVK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fd0i2pz%2FdJMcadu1gOZ%2FvgSWkoIr0QLV3ed4iNrHVK%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;699&quot; height=&quot;595&quot; data-origin-width=&quot;699&quot; data-origin-height=&quot;595&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;h3 data-ke-size=&quot;size23&quot;&gt;9. 정상적으로 git 연결 테스트를 확인하였습니다.&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;elixir&quot;&gt;&lt;code&gt;$ ssh -T git@github.com
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;699&quot; data-origin-height=&quot;595&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cKjL3i/dJMcaarBWAC/raoHf9aIUOu0O4EnfCldM0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cKjL3i/dJMcaarBWAC/raoHf9aIUOu0O4EnfCldM0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cKjL3i/dJMcaarBWAC/raoHf9aIUOu0O4EnfCldM0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcKjL3i%2FdJMcaarBWAC%2FraoHf9aIUOu0O4EnfCldM0%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;699&quot; height=&quot;595&quot; data-origin-width=&quot;699&quot; data-origin-height=&quot;595&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;h3 data-ke-size=&quot;size23&quot;&gt;10. 다시 작업을 수행하여, 정상적으로 수행이 됨을 확인하였습니다.&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;937&quot; data-origin-height=&quot;308&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/eHPBKl/dJMcaiQE6q7/Mcldp1Q1CcQcs8G4pkHa00/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/eHPBKl/dJMcaiQE6q7/Mcldp1Q1CcQcs8G4pkHa00/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/eHPBKl/dJMcaiQE6q7/Mcldp1Q1CcQcs8G4pkHa00/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FeHPBKl%2FdJMcaiQE6q7%2FMcldp1Q1CcQcs8G4pkHa00%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;937&quot; height=&quot;308&quot; data-origin-width=&quot;937&quot; data-origin-height=&quot;308&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&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;오늘도&amp;nbsp;감사합니다.&amp;nbsp; &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;[Git]&amp;nbsp;git@github.com&amp;nbsp;:&amp;nbsp;Permission&amp;nbsp;denied&amp;nbsp;(publickey)&lt;/p&gt;</description>
      <category>Github/이해하기</category>
      <category>fatal: Could not read from remote repository.</category>
      <category>git</category>
      <category>git error</category>
      <category>git Permission denied</category>
      <category>git Permission denied (publickey)</category>
      <category>git@github.com : Permission denied (publickey)</category>
      <category>Please make sure you have the correct access rights and the repository exists.</category>
      <author>adjh54</author>
      <guid isPermaLink="true">https://adjh54.tistory.com/751</guid>
      <comments>https://adjh54.tistory.com/751#entry751comment</comments>
      <pubDate>Thu, 30 Apr 2026 20:00:41 +0900</pubDate>
    </item>
    <item>
      <title>[제작 앱 소개] 나만의 단위 계산기</title>
      <link>https://adjh54.tistory.com/749</link>
      <description>&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;해당&amp;nbsp;글에서는&amp;nbsp;Contributor9&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;&lt;/span&gt;
&lt;figure contenteditable=&quot;false&quot; data-ke-type=&quot;emoticon&quot; data-ke-align=&quot;alignCenter&quot; data-emoticon-type=&quot;face&quot; data-emoticon-name=&quot;062&quot; data-emoticon-isanimation=&quot;false&quot; data-emoticon-src=&quot;https://t1.daumcdn.net/axz_keditor/emoticon/face/large/062.png&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/axz_keditor/emoticon/face/large/062.png&quot; width=&quot;80&quot; /&gt;&lt;/figure&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1)&amp;nbsp;개발자는&amp;nbsp;왜&amp;nbsp;이&amp;nbsp;앱을&amp;nbsp;만들었을까?&lt;/h2&gt;
&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt; &amp;nbsp;개발자는&amp;nbsp;왜&amp;nbsp;이&amp;nbsp;앱을&amp;nbsp;만들었을까?&lt;/b&gt; &lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 많은 계산기 앱들이 있지만, 내가 원하는 계산을 위해서 매번 계산기 위치를 찾고, 여러 번 반복적인 화면을 터치해서 이동하는 것이 참 불편한 거 같다고 느꼈습니다.&lt;/b&gt; &lt;br /&gt;&lt;br /&gt;- 내가 자주 사용하는 계산기를 내가 원하는 순서대로 배치하여 빠르게 찾을 수 있게하고, 쉽게 검색으로 바로 접근하는 방법이 없을까?라는 아이디어에서 시작된 앱입니다. 이러한 작은 불편함에서 시작되어서 이를 개선하기 위해서 사용자의 편의성을 가장 크게 고려를 하였습니다. &lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 앱의 기능에서는 크게는 대분류로 단위, 생활, 프리미엄 계산기로 구성이 되어 있습니다.&lt;/b&gt; &lt;br /&gt;대분류 내에서는 자주 이용하는 계산기들은 편집 버튼을 눌러서 쉽게 나만의 위치로 변경할 수 있고 이 위치는 저장이 되어서 계속 유지가 됩니다. &lt;br /&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1205&quot; data-origin-height=&quot;1801&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/O6OGv/dJMcahKXBCj/8uG8qOnMfDb3IpU4hqEKn0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/O6OGv/dJMcahKXBCj/8uG8qOnMfDb3IpU4hqEKn0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/O6OGv/dJMcahKXBCj/8uG8qOnMfDb3IpU4hqEKn0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FO6OGv%2FdJMcahKXBCj%2F8uG8qOnMfDb3IpU4hqEKn0%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;353&quot; height=&quot;528&quot; data-origin-width=&quot;1205&quot; data-origin-height=&quot;1801&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;br /&gt;- 그리고 더 빠른검색을 위해서는 계산기 명이나 주요 키워드로 검색할 수 있는 &lt;b&gt;퀵 검색 기능&lt;/b&gt;을 제공하며, &lt;b&gt;주요 해시태그를 선택&lt;/b&gt;하여서 원하는 계산기를 빠르게 더욱 빠르게 접근이 가능합니다. &lt;br /&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1206&quot; data-origin-height=&quot;1193&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bjnxju/dJMcaiiL1cO/d2Jn80WMpOILPC29CpkH60/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bjnxju/dJMcaiiL1cO/d2Jn80WMpOILPC29CpkH60/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bjnxju/dJMcaiiL1cO/d2Jn80WMpOILPC29CpkH60/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbjnxju%2FdJMcaiiL1cO%2Fd2Jn80WMpOILPC29CpkH60%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;367&quot; height=&quot;363&quot; data-origin-width=&quot;1206&quot; data-origin-height=&quot;1193&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1194&quot; data-origin-height=&quot;1300&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/wpeLy/dJMcaipyJLa/ioZ5RxolsqhjxcnWSj8v91/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/wpeLy/dJMcaipyJLa/ioZ5RxolsqhjxcnWSj8v91/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/wpeLy/dJMcaipyJLa/ioZ5RxolsqhjxcnWSj8v91/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FwpeLy%2FdJMcaipyJLa%2FioZ5RxolsqhjxcnWSj8v91%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;343&quot; height=&quot;373&quot; data-origin-width=&quot;1194&quot; data-origin-height=&quot;1300&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;br /&gt;- 이러한 빠른 검색뿐만 아니라 다양한 기능들을 제공합니다. &lt;b&gt;주요 변환이 필요한 단위 계산뿐만 아니라 생활 속에서 필요한 계산들&lt;/b&gt;을 묶어둔 생활 계산기 기능도 제공을 합니다. 일반, 퍼센트, 일자, 근수, 금중량, 체질량 등 평소 일상생활 속에서 계산이 필요한 기능들을 제공합니다. &lt;br /&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1197&quot; data-origin-height=&quot;1969&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/etxmHe/dJMcafTUpy2/lUIC2WFudMX9DTAUoRDCN0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/etxmHe/dJMcafTUpy2/lUIC2WFudMX9DTAUoRDCN0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/etxmHe/dJMcafTUpy2/lUIC2WFudMX9DTAUoRDCN0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FetxmHe%2FdJMcafTUpy2%2FlUIC2WFudMX9DTAUoRDCN0%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;329&quot; height=&quot;541&quot; data-origin-width=&quot;1197&quot; data-origin-height=&quot;1969&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;- 그리고 프리미엄 계산기를 제공합니다. 이미 앱에 출시되고 운영되는 계산 앱들의 주요한 기능을 가져와서 제공이 됩니다. 평수 계산기, 나이 계산기, 촌수 계산기, 나만의 금융 계산기등의 많은 사용자들이 이용한 기능들을 특별하게 나만의 계산기에서 제공을 합니다! &lt;br /&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1206&quot; data-origin-height=&quot;1925&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/sy7yj/dJMcaaLPGqj/dzWDFXUfucoueKPVNzt5zk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/sy7yj/dJMcaaLPGqj/dzWDFXUfucoueKPVNzt5zk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/sy7yj/dJMcaaLPGqj/dzWDFXUfucoueKPVNzt5zk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fsy7yj%2FdJMcaaLPGqj%2FdzWDFXUfucoueKPVNzt5zk%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;437&quot; height=&quot;698&quot; data-origin-width=&quot;1206&quot; data-origin-height=&quot;1925&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;br /&gt;&lt;br /&gt;- 이러한 계산들은 기록으로 자동 저장이 되어서, 이전에 내가 계산했던 내용이 무엇인지 빠르게 확인이 되고, 저장, 공유 기능을 제공합니다! &lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 앞으로 업데이트 될 내용에서는 좀 더 다양한 계산기들이 추가될 예정입니다! 많은 관심 부탁드립니다&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2) 앱 소개- 나만의 단위 계산기&lt;/h2&gt;
&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1381&quot; data-origin-height=&quot;351&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/tn2yd/dJMcadIu6lp/ohq048V5f4KoULbG5xX1M0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/tn2yd/dJMcadIu6lp/ohq048V5f4KoULbG5xX1M0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/tn2yd/dJMcadIu6lp/ohq048V5f4KoULbG5xX1M0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Ftn2yd%2FdJMcadIu6lp%2Fohq048V5f4KoULbG5xX1M0%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;1381&quot; height=&quot;351&quot; data-origin-width=&quot;1381&quot; data-origin-height=&quot;351&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  앱 소개- 나만의 단위 계산기&lt;/b&gt;&lt;b&gt;&lt;br /&gt;&lt;/b&gt;&lt;b&gt;&lt;br /&gt;&lt;/b&gt;&lt;b&gt;- 단위&amp;nbsp;계산과&amp;nbsp;생활&amp;nbsp;계산&amp;nbsp;기능을&amp;nbsp;하나로&amp;nbsp;모아,&amp;nbsp;자주&amp;nbsp;쓰는&amp;nbsp;계산기를&amp;nbsp;내&amp;nbsp;사용&amp;nbsp;방식에&amp;nbsp;맞게&amp;nbsp;구성하고&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;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;✔️&amp;nbsp;여러 계산기 앱을 번갈아 쓰는 번거로움 없이, 필요한 계산을 한 화면에서 빠르게 처리할 수 있도록 설계되어서 모든 사용자들이 편하게 사용할 수 있습니다.&lt;/b&gt;&lt;br /&gt;&lt;b&gt;✔️&amp;nbsp; 자주 사용하는 계산기 기능은 사용 패턴에 맞게 정렬할 수 있고, 검색과 태그를 통해 원하는 계산기를 빠르게 찾아 나만의 계산기처럼 효율적으로 사용할 수 있습니다.&lt;/b&gt;&lt;br /&gt;&lt;b&gt;✔️&amp;nbsp; 숫자만 입력하면 길이&amp;middot;무게&amp;middot;시간&amp;middot;면적&amp;middot;데이터 용량 등 필요한 계산 결과를 즉시 확인할 수 있으며, 계산한 값은 복사&amp;middot;공유하거나 히스토리로 저장해 이후 다시 찾아보고 비교해 활용할 수 있습니다.&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;1.&amp;nbsp;홈&amp;nbsp;화면&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 홈 화면은 모든 계산기를 한 곳에서 빠르게 찾고, 내 사용 방식에 맞게 정리할 수 있는 계산기 메인 화면입니다.&lt;/b&gt;&lt;br /&gt;-&amp;nbsp;단순한&amp;nbsp;계산기&amp;nbsp;목록이&amp;nbsp;아니라&amp;nbsp;검색&amp;middot;분류&amp;middot;커스터마이징을&amp;nbsp;중심으로&amp;nbsp;실사용에&amp;nbsp;최적화된&amp;nbsp;구조로&amp;nbsp;설계되었습니다&lt;br /&gt;-&amp;nbsp;계산기&amp;nbsp;이름뿐&amp;nbsp;아니라&amp;nbsp;설명과&amp;nbsp;단위명까지&amp;nbsp;함께&amp;nbsp;검색할&amp;nbsp;수&amp;nbsp;있어&amp;nbsp;단위&amp;middot;생활&amp;middot;프리미엄&amp;nbsp;계산기를&amp;nbsp;구분&amp;nbsp;없이&amp;nbsp;한&amp;nbsp;번에&amp;nbsp;탐색할&amp;nbsp;수&amp;nbsp;있습니다&lt;br /&gt;-&amp;nbsp;길이,&amp;nbsp;무게,&amp;nbsp;시간,&amp;nbsp;날짜,&amp;nbsp;건강,&amp;nbsp;금융&amp;nbsp;등&amp;nbsp;자주&amp;nbsp;쓰는&amp;nbsp;키워드&amp;nbsp;태그를&amp;nbsp;활용해&amp;nbsp;원하는&amp;nbsp;계산기에&amp;nbsp;즉시&amp;nbsp;접근할&amp;nbsp;수&amp;nbsp;있습니다&lt;br /&gt;-&amp;nbsp;단위&amp;nbsp;계산기,&amp;nbsp;생활&amp;nbsp;계산기,&amp;nbsp;프리미엄&amp;nbsp;계산기는&amp;nbsp;요약&amp;nbsp;카드&amp;nbsp;형태로&amp;nbsp;한눈에&amp;nbsp;구분되어&amp;nbsp;각&amp;nbsp;영역별&amp;nbsp;계산기&amp;nbsp;구성과&amp;nbsp;개수를&amp;nbsp;직관적으로&amp;nbsp;확인할&amp;nbsp;수&amp;nbsp;있습니다&lt;br /&gt;-&amp;nbsp;자주&amp;nbsp;사용하는&amp;nbsp;계산기는&amp;nbsp;드래그로&amp;nbsp;직접&amp;nbsp;순서를&amp;nbsp;편집해&amp;nbsp;저장할&amp;nbsp;수&amp;nbsp;있으며,&amp;nbsp;설정한&amp;nbsp;배치는&amp;nbsp;앱을&amp;nbsp;다시&amp;nbsp;실행해도&amp;nbsp;그대로&amp;nbsp;유지됩니다&lt;br /&gt;-&amp;nbsp;프리미엄&amp;nbsp;계산기는&amp;nbsp;전용&amp;nbsp;영역으로&amp;nbsp;시각적으로&amp;nbsp;구분되어&amp;nbsp;제공되며,&amp;nbsp;안내&amp;nbsp;후&amp;nbsp;바로&amp;nbsp;해당&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;&lt;b&gt;2.&amp;nbsp;단위&amp;nbsp;계산기&amp;nbsp;공통&amp;nbsp;기능&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 단위 계산기 화면은 하나의 기준 단위 입력만으로 다양한 단위를 즉시 변환할 수 있도록 설계된 실사용 중심의 고급 단위 변환 계산기입니다.&lt;/b&gt;&lt;br /&gt;-&amp;nbsp;단순한&amp;nbsp;숫자&amp;nbsp;변환에&amp;nbsp;그치지&amp;nbsp;않고&amp;nbsp;정확성&amp;middot;가독성&amp;middot;복사&amp;nbsp;및&amp;nbsp;공유&amp;nbsp;활용성까지&amp;nbsp;종합적으로&amp;nbsp;고려해&amp;nbsp;구성되었습니다&lt;br /&gt;-&amp;nbsp;원하는&amp;nbsp;기준&amp;nbsp;단위를&amp;nbsp;선택한&amp;nbsp;뒤&amp;nbsp;숫자를&amp;nbsp;입력하고&amp;nbsp;변환을&amp;nbsp;실행하면&amp;nbsp;즉시&amp;nbsp;결과가&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;-&amp;nbsp;모든&amp;nbsp;변환&amp;nbsp;결과에는&amp;nbsp;한국식&amp;nbsp;콤마가&amp;nbsp;자동&amp;nbsp;적용되고&amp;nbsp;소수점은&amp;nbsp;최대&amp;nbsp;3자리까지&amp;nbsp;보기&amp;nbsp;좋게&amp;nbsp;정리되어&amp;nbsp;많은&amp;nbsp;숫자도&amp;nbsp;한눈에&amp;nbsp;쉽게&amp;nbsp;확인할&amp;nbsp;수&amp;nbsp;있습니다&lt;br /&gt;-&amp;nbsp;복사&amp;nbsp;기능을&amp;nbsp;통해&amp;nbsp;기준&amp;nbsp;단위,&amp;nbsp;입력값,&amp;nbsp;전체&amp;nbsp;변환&amp;nbsp;결과를&amp;nbsp;한&amp;nbsp;번에&amp;nbsp;텍스트로&amp;nbsp;복사할&amp;nbsp;수&amp;nbsp;있고,&amp;nbsp;공유&amp;nbsp;기능으로&amp;nbsp;메신저&amp;middot;메일&amp;middot;메모&amp;nbsp;앱&amp;nbsp;등으로&amp;nbsp;바로&amp;nbsp;전달할&amp;nbsp;수&amp;nbsp;있습니다&lt;br /&gt;-&amp;nbsp;변환을&amp;nbsp;실행할&amp;nbsp;때마다&amp;nbsp;계산&amp;nbsp;내역이&amp;nbsp;자동으로&amp;nbsp;저장되며&amp;nbsp;기준&amp;nbsp;단위,&amp;nbsp;입력값,&amp;nbsp;결과값이&amp;nbsp;함께&amp;nbsp;기록되어&amp;nbsp;언제든&amp;nbsp;히스토리에서&amp;nbsp;다시&amp;nbsp;확인할&amp;nbsp;수&amp;nbsp;있습니다&lt;br /&gt;_&amp;nbsp;각&amp;nbsp;단위의&amp;nbsp;의미와&amp;nbsp;기준,&amp;nbsp;사용&amp;nbsp;예를&amp;nbsp;카드&amp;nbsp;형태로&amp;nbsp;정리한&amp;nbsp;도움말을&amp;nbsp;제공해&amp;nbsp;계산&amp;nbsp;중&amp;nbsp;단위가&amp;nbsp;헷갈릴&amp;nbsp;때&amp;nbsp;언제든&amp;nbsp;쉽게&amp;nbsp;참고할&amp;nbsp;수&amp;nbsp;있습니다&lt;br /&gt;-&amp;nbsp;숫자&amp;nbsp;입력&amp;nbsp;자동&amp;nbsp;포맷,&amp;nbsp;키보드&amp;nbsp;제어,&amp;nbsp;불필요한&amp;nbsp;요소&amp;nbsp;제거&amp;nbsp;등&amp;nbsp;실사용을&amp;nbsp;고려한&amp;nbsp;UI&amp;middot;UX로&amp;nbsp;iOS와&amp;nbsp;Android&amp;nbsp;모두에서&amp;nbsp;자연스럽고&amp;nbsp;일관된&amp;nbsp;사용&amp;nbsp;경험을&amp;nbsp;제공합니다&lt;br /&gt;&lt;br /&gt;&lt;b&gt;3.&amp;nbsp;계산&amp;nbsp;기록&amp;nbsp;히스토리&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 계산 기록 히스토리는 내가 사용한 모든 계산 결과를 한곳에서 모아보고 다시 활용할 수 있도록 설계된 기록 관리 페이지입니다.&lt;/b&gt;&lt;br /&gt;-&amp;nbsp;단순&amp;nbsp;저장을&amp;nbsp;넘어&amp;nbsp;검색&amp;middot;필터&amp;middot;복사&amp;middot;공유&amp;middot;삭제까지&amp;nbsp;실사용에&amp;nbsp;꼭&amp;nbsp;필요한&amp;nbsp;기능을&amp;nbsp;중심으로&amp;nbsp;구성되었습니다&lt;br /&gt;-&amp;nbsp;입력값과&amp;nbsp;결과값을&amp;nbsp;기준으로&amp;nbsp;숫자와&amp;nbsp;텍스트를&amp;nbsp;모두&amp;nbsp;실시간&amp;nbsp;검색할&amp;nbsp;수&amp;nbsp;있어&amp;nbsp;원하는&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;-&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;기록&amp;nbsp;관리가&amp;nbsp;효율적입니다&lt;br /&gt;_&amp;nbsp;모든&amp;nbsp;기록은&amp;nbsp;최신&amp;nbsp;계산&amp;nbsp;기준으로&amp;nbsp;자동&amp;nbsp;정렬되며&amp;nbsp;계산&amp;nbsp;시각이&amp;nbsp;함께&amp;nbsp;표시되어&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;-&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;수&amp;nbsp;있습니다&lt;br /&gt;-&amp;nbsp;계산&amp;nbsp;결과&amp;nbsp;전체를&amp;nbsp;한&amp;nbsp;번에&amp;nbsp;복사하거나&amp;nbsp;메신저&amp;middot;메모&amp;nbsp;앱&amp;nbsp;등으로&amp;nbsp;즉시&amp;nbsp;공유할&amp;nbsp;수&amp;nbsp;있어&amp;nbsp;그대로&amp;nbsp;활용하기&amp;nbsp;좋습니다&lt;br /&gt;-&amp;nbsp;개별&amp;nbsp;기록을&amp;nbsp;선택해&amp;nbsp;바로&amp;nbsp;삭제할&amp;nbsp;수&amp;nbsp;있고,&amp;nbsp;삭제&amp;nbsp;즉시&amp;nbsp;화면에&amp;nbsp;반영되어&amp;nbsp;불필요한&amp;nbsp;기록도&amp;nbsp;깔끔하게&amp;nbsp;관리할&amp;nbsp;수&amp;nbsp;있습니다&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;4.&amp;nbsp;설정&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 설정 화면은 단위 계산기 앱을 더욱 편리하고 깔끔하게 사용할 수 있도록 구성된 관리 공간으로, 계산 기록 정리부터 앱 정보 확인, 문의와 피드백까지 필요한 기능을 한 곳에 모아 누구나 쉽게 사용할 수 있도록 설계되었습니다&lt;/b&gt;&lt;br /&gt;-&amp;nbsp;불필요하게&amp;nbsp;쌓인&amp;nbsp;계산&amp;nbsp;기록은&amp;nbsp;한&amp;nbsp;번의&amp;nbsp;동작으로&amp;nbsp;모두&amp;nbsp;초기화할&amp;nbsp;수&amp;nbsp;있어&amp;nbsp;길이&amp;middot;무게&amp;middot;시간&amp;middot;거리&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;정리하고&amp;nbsp;싶을&amp;nbsp;때&amp;nbsp;빠르게&amp;nbsp;초기화할&amp;nbsp;수&amp;nbsp;있습니다&lt;br /&gt;-&amp;nbsp;앱&amp;nbsp;사용&amp;nbsp;중&amp;nbsp;궁금한&amp;nbsp;점이나&amp;nbsp;개선&amp;nbsp;의견이&amp;nbsp;있을&amp;nbsp;경우&amp;nbsp;설정&amp;nbsp;화면을&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;-&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;lsquo;제작자&amp;nbsp;앱&amp;nbsp;더보기&amp;rsquo;를&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;-&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;업데이트&amp;nbsp;여부를&amp;nbsp;언제든지&amp;nbsp;쉽게&amp;nbsp;확인할&amp;nbsp;수&amp;nbsp;있습니다&lt;br /&gt;-&amp;nbsp;또한&amp;nbsp;설정&amp;nbsp;화면&amp;nbsp;맨&amp;nbsp;아래에는&amp;nbsp;제작자가&amp;nbsp;만든&amp;nbsp;다양한&amp;nbsp;퀴즈&amp;nbsp;및&amp;nbsp;유틸리티&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;&lt;br /&gt;&lt;b&gt; &amp;nbsp;이런&amp;nbsp;분께&amp;nbsp;추천해요!&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;-&amp;nbsp;길이&amp;middot;무게&amp;middot;시간&amp;middot;면적&amp;middot;데이터&amp;nbsp;용량&amp;nbsp;등&amp;nbsp;다양한&amp;nbsp;단위&amp;nbsp;변환을&amp;nbsp;자주&amp;nbsp;사용하는&amp;nbsp;분&lt;br /&gt;-&amp;nbsp;계산기&amp;nbsp;앱을&amp;nbsp;여러&amp;nbsp;개&amp;nbsp;설치하기보다,&amp;nbsp;하나로&amp;nbsp;깔끔하게&amp;nbsp;정리하고&amp;nbsp;싶은&amp;nbsp;분&lt;br /&gt;-&amp;nbsp;계산&amp;nbsp;결과를&amp;nbsp;복사하거나&amp;nbsp;공유해야&amp;nbsp;하는&amp;nbsp;일이&amp;nbsp;잦은&amp;nbsp;직장인&amp;middot;학생&lt;br /&gt;-&amp;nbsp;이전에&amp;nbsp;계산한&amp;nbsp;값들을&amp;nbsp;다시&amp;nbsp;찾아보거나&amp;nbsp;비교해서&amp;nbsp;활용하고&amp;nbsp;싶은&amp;nbsp;분&lt;br /&gt;-&amp;nbsp;자주&amp;nbsp;쓰는&amp;nbsp;계산기를&amp;nbsp;내&amp;nbsp;방식대로&amp;nbsp;정렬하고&amp;nbsp;맞춤&amp;nbsp;구성하고&amp;nbsp;싶은&amp;nbsp;분&lt;br /&gt;-&amp;nbsp;숫자가&amp;nbsp;많은&amp;nbsp;계산도&amp;nbsp;보기&amp;nbsp;쉬운&amp;nbsp;포맷으로&amp;nbsp;정확하게&amp;nbsp;확인하고&amp;nbsp;싶은&amp;nbsp;분&lt;br /&gt;-&amp;nbsp;일상&amp;nbsp;계산부터&amp;nbsp;실무&amp;middot;학습용&amp;nbsp;계산까지&amp;nbsp;한&amp;nbsp;앱에서&amp;nbsp;해결하고&amp;nbsp;싶은&amp;nbsp;분&lt;br /&gt;-&amp;nbsp;복잡한&amp;nbsp;공식&amp;nbsp;없이,&amp;nbsp;빠르고&amp;nbsp;직관적인&amp;nbsp;계산&amp;nbsp;흐름을&amp;nbsp;선호하는&amp;nbsp;분&lt;br /&gt;&lt;br /&gt;&lt;b&gt; &amp;nbsp;지금&amp;nbsp;이&amp;nbsp;계산기를&amp;nbsp;사용해&amp;nbsp;보세요!&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;필요한&amp;nbsp;계산을&amp;nbsp;찾고&amp;nbsp;&amp;rarr;&amp;nbsp;입력하고&amp;nbsp;&amp;rarr;&amp;nbsp;바로&amp;nbsp;활용하는&amp;nbsp;가장&amp;nbsp;실용적인&amp;nbsp;계산&amp;nbsp;경험을&amp;nbsp;만나보세요&amp;nbsp;✨&lt;br /&gt;오늘도&amp;nbsp;계산은&amp;nbsp;더&amp;nbsp;빠르게,&amp;nbsp;정리는&amp;nbsp;더&amp;nbsp;깔끔하게&amp;nbsp;✔️&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&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;1380&quot; data-origin-height=&quot;657&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/R67pc/dJMcabxaUk0/TmJJ4T3q4oQqN91eVNOAb0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/R67pc/dJMcabxaUk0/TmJJ4T3q4oQqN91eVNOAb0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/R67pc/dJMcabxaUk0/TmJJ4T3q4oQqN91eVNOAb0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FR67pc%2FdJMcabxaUk0%2FTmJJ4T3q4oQqN91eVNOAb0%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;1380&quot; height=&quot;657&quot; data-origin-width=&quot;1380&quot; data-origin-height=&quot;657&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1353&quot; data-origin-height=&quot;662&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/btUu3b/dJMcabRudph/3798dwJXm93LTlyn3xOphK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/btUu3b/dJMcabRudph/3798dwJXm93LTlyn3xOphK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/btUu3b/dJMcabRudph/3798dwJXm93LTlyn3xOphK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbtUu3b%2FdJMcabRudph%2F3798dwJXm93LTlyn3xOphK%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;1353&quot; height=&quot;662&quot; data-origin-width=&quot;1353&quot; data-origin-height=&quot;662&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3) 다운로드&lt;/h2&gt;
&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1.&amp;nbsp;Google&amp;nbsp;PlayStore&lt;/h3&gt;
&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;450&quot; data-origin-height=&quot;450&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/HPuTs/dJMcahc6C6I/erXinGIX3RxzTEKuIfCl60/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/HPuTs/dJMcahc6C6I/erXinGIX3RxzTEKuIfCl60/img.png&quot; data-alt=&quot;https://play.google.com/store/apps/details?id=com.tha.unitcalc&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/HPuTs/dJMcahc6C6I/erXinGIX3RxzTEKuIfCl60/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FHPuTs%2FdJMcahc6C6I%2FerXinGIX3RxzTEKuIfCl60%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;450&quot; height=&quot;450&quot; data-origin-width=&quot;450&quot; data-origin-height=&quot;450&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://play.google.com/store/apps/details?id=com.tha.unitcalc&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;figure data-ke-type=&quot;opengraph&quot; data-og-title=&quot;나만의 단위 계산기 - Google Play 앱&quot; data-ke-align=&quot;alignCenter&quot; data-og-description=&quot;검색&amp;middot;태그&amp;middot;정렬로 원하는 계산기를 즉시 찾고, 단위&amp;middot;생활&amp;middot;프리미엄 계산과 히스토리 관리까지 한 번에 해결하는 실사용 중심 올인원 계산기 앱입니다&quot; data-og-host=&quot;play.google.com&quot; data-og-source-url=&quot;https://play.google.com/store/apps/details?id=com.tha.unitcalc&quot; data-og-image=&quot;https://blog.kakaocdn.net/dna/ltW70/dJMb8U8V7sG/AAAAAAAAAAAAAAAAAAAAADnPV-KH5G0L31WbDJf0JuUiwl5f8M4bPm_PJsbrpAIr/img.png?credential=yqXZFxpELC7KVnFOS48ylbz2pIh7yKj8&amp;amp;expires=1777561199&amp;amp;allow_ip=&amp;amp;allow_referer=&amp;amp;signature=eSn3xvV1%2BpvFjlAohl%2BClat5QpE%3D&quot; data-og-url=&quot;https://play.google.com/store/apps/details?id=com.tha.unitcalc&amp;amp;hl=ko&quot;&gt;&lt;a href=&quot;https://play.google.com/store/apps/details?id=com.tha.unitcalc&amp;amp;hl=ko&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://play.google.com/store/apps/details?id=com.tha.unitcalc&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://blog.kakaocdn.net/dna/ltW70/dJMb8U8V7sG/AAAAAAAAAAAAAAAAAAAAADnPV-KH5G0L31WbDJf0JuUiwl5f8M4bPm_PJsbrpAIr/img.png?credential=yqXZFxpELC7KVnFOS48ylbz2pIh7yKj8&amp;amp;expires=1777561199&amp;amp;allow_ip=&amp;amp;allow_referer=&amp;amp;signature=eSn3xvV1%2BpvFjlAohl%2BClat5QpE%3D');&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;나만의 단위 계산기 - Google Play 앱&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;검색&amp;middot;태그&amp;middot;정렬로 원하는 계산기를 즉시 찾고, 단위&amp;middot;생활&amp;middot;프리미엄 계산과 히스토리 관리까지 한 번에 해결하는 실사용 중심 올인원 계산기 앱입니다&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;play.google.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;br /&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2.&amp;nbsp;AppStore&lt;/h3&gt;
&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;490&quot; data-origin-height=&quot;490&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bn7nyT/dJMcafNaexp/YPhf1LIdj3kacSBBcHKs71/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bn7nyT/dJMcafNaexp/YPhf1LIdj3kacSBBcHKs71/img.png&quot; data-alt=&quot;https://apps.apple.com/kr/app/id6757512881&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bn7nyT/dJMcafNaexp/YPhf1LIdj3kacSBBcHKs71/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbn7nyT%2FdJMcafNaexp%2FYPhf1LIdj3kacSBBcHKs71%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;490&quot; height=&quot;490&quot; data-origin-width=&quot;490&quot; data-origin-height=&quot;490&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://apps.apple.com/kr/app/id6757512881&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure data-ke-type=&quot;opengraph&quot; data-og-title=&quot;나만의 단위 계산기 앱 - App Store&quot; data-ke-align=&quot;alignCenter&quot; data-og-description=&quot;App&amp;nbsp;Store에서 EcodeLab의 나만의 단위 계산기 앱을 다운로드하십시오. 스크린샷, 평가 및 리뷰, 사용자 팁 및 나만의 단위 계산기 앱과 비슷한 다른 앱을 볼 수 있습니다.&quot; data-og-host=&quot;apps.apple.com&quot; data-og-source-url=&quot;https://apps.apple.com/kr/app/id6757512881&quot; data-og-image=&quot;https://blog.kakaocdn.net/dna/bzprPJ/dJMb8T912mz/AAAAAAAAAAAAAAAAAAAAAKWZi6Z1I7hS1xirhMTnrFz8U87f6W0mz2db4goMvlg6/img.jpg?credential=yqXZFxpELC7KVnFOS48ylbz2pIh7yKj8&amp;amp;expires=1777561199&amp;amp;allow_ip=&amp;amp;allow_referer=&amp;amp;signature=zyvSV4ohmLVUY7JFEaER6EOPSQ0%3D&quot; data-og-url=&quot;https://apps.apple.com/kr/app/%EB%82%98%EB%A7%8C%EC%9D%98-%EB%8B%A8%EC%9C%84-%EA%B3%84%EC%82%B0%EA%B8%B0/id6757512881&quot;&gt;&lt;a href=&quot;https://apps.apple.com/kr/app/%EB%82%98%EB%A7%8C%EC%9D%98-%EB%8B%A8%EC%9C%84-%EA%B3%84%EC%82%B0%EA%B8%B0/id6757512881&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://apps.apple.com/kr/app/id6757512881&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://blog.kakaocdn.net/dna/bzprPJ/dJMb8T912mz/AAAAAAAAAAAAAAAAAAAAAKWZi6Z1I7hS1xirhMTnrFz8U87f6W0mz2db4goMvlg6/img.jpg?credential=yqXZFxpELC7KVnFOS48ylbz2pIh7yKj8&amp;amp;expires=1777561199&amp;amp;allow_ip=&amp;amp;allow_referer=&amp;amp;signature=zyvSV4ohmLVUY7JFEaER6EOPSQ0%3D');&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;나만의 단위 계산기 앱 - App Store&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;App&amp;nbsp;Store에서 EcodeLab의 나만의 단위 계산기 앱을 다운로드하십시오. 스크린샷, 평가 및 리뷰, 사용자 팁 및 나만의 단위 계산기 앱과 비슷한 다른 앱을 볼 수 있습니다.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;apps.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;br /&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;b&gt;⬇️ 개발자의 다양한 앱을 보고 싶으시면 아래의 링크를 확인해주세요&lt;span&gt;&amp;nbsp;&lt;/span&gt;⬇️&lt;/b&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1779095431065&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;Ecodelab - 제작 앱 소개&quot; data-og-description=&quot;Ecodelab에서 개발한 다양한 앱들을 소개합니다&quot; data-og-host=&quot;www.ecodelab.im&quot; data-og-source-url=&quot;https://ecodelab.im/main&quot; data-og-url=&quot;https://www.ecodelab.im&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bWuJMc/dJMb9eTUZsH/njzMvre05hFxKg2BACFmIk/img.png?width=1024&amp;amp;height=1024&amp;amp;face=0_0_1024_1024,https://scrap.kakaocdn.net/dn/eR1YMg/dJMb8RRXk8e/RlDSZRgjj03yt5Wtc1PKLK/img.png?width=1024&amp;amp;height=1024&amp;amp;face=0_0_1024_1024&quot;&gt;&lt;a href=&quot;https://ecodelab.im/main&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://ecodelab.im/main&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bWuJMc/dJMb9eTUZsH/njzMvre05hFxKg2BACFmIk/img.png?width=1024&amp;amp;height=1024&amp;amp;face=0_0_1024_1024,https://scrap.kakaocdn.net/dn/eR1YMg/dJMb8RRXk8e/RlDSZRgjj03yt5Wtc1PKLK/img.png?width=1024&amp;amp;height=1024&amp;amp;face=0_0_1024_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;Ecodelab - 제작 앱 소개&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Ecodelab에서 개발한 다양한 앱들을 소개합니다&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.ecodelab.im&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;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Contributor9/제작 앱 소개</category>
      <category>계산기</category>
      <category>나만의 계산기</category>
      <category>나만의 단위 계산기</category>
      <category>나만의 단위 변환 앱</category>
      <category>단위 계산기</category>
      <category>단위 계산기 앱</category>
      <category>단위 변환</category>
      <category>단위 변환 앱</category>
      <category>단위 변환기 앱</category>
      <category>생활 계산기 앱</category>
      <author>adjh54</author>
      <guid isPermaLink="true">https://adjh54.tistory.com/749</guid>
      <comments>https://adjh54.tistory.com/749#entry749comment</comments>
      <pubDate>Fri, 24 Apr 2026 18:10:03 +0900</pubDate>
    </item>
    <item>
      <title>바이브 코딩 : Gemini Enterprise 기반 공통 프롬프트 활용 방법</title>
      <link>https://adjh54.tistory.com/748</link>
      <description>&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;해당 글에서는 Gemini Enterprise 기반에 공통 프롬프트를 활용하는 방법에 대해서 확인해 봅니다.&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;
&lt;figure contenteditable=&quot;false&quot; data-ke-type=&quot;emoticon&quot; data-ke-align=&quot;alignCenter&quot; data-emoticon-type=&quot;face&quot; data-emoticon-name=&quot;073&quot; data-emoticon-isanimation=&quot;false&quot; data-emoticon-src=&quot;https://t1.daumcdn.net/axz_keditor/emoticon/face/large/073.png&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/axz_keditor/emoticon/face/large/073.png&quot; width=&quot;80&quot; /&gt;&lt;/figure&gt;
&lt;/blockquote&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1) 바이브 코딩을 이용한 사례&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  바이브 코딩을 이용한 사례&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 취미 생활로 바이브 코딩을 활용하여 다양한 앱들을 만들고 있습니다.&lt;/b&gt;&lt;br /&gt;- 크로스 플랫폼을 활용하여 안드로이드, iOS에 플랫폼에 34개 앱 그리고 1개의 홈페이지를 출시가 되었고 운영 중에 있습니다. 현재에도 신규의 앱을 3개를 출시 준비 중에 있습니다.&lt;br /&gt;- 2025년 04월부터 시작하여, 총 누적 다운로드 약 5,000회를 달성했고 현재 3,822명이 앱을 이용 중에 있습니다.&lt;br /&gt;- 다양한 앱들을 바이브 코딩을 이용하여서 출시하였기에, 다양한 앱을 출시한 경험을 바탕으로 공통으로 프롬프트를 사용할 수 있는 방법에 대해 제안을 합니다.&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1675&quot; data-origin-height=&quot;541&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mflAI/dJMcagkVOgU/GktKgnpnUyOUUsl5gqcJ3k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mflAI/dJMcagkVOgU/GktKgnpnUyOUUsl5gqcJ3k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mflAI/dJMcagkVOgU/GktKgnpnUyOUUsl5gqcJ3k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmflAI%2FdJMcagkVOgU%2FGktKgnpnUyOUUsl5gqcJ3k%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;1675&quot; height=&quot;541&quot; data-origin-width=&quot;1675&quot; data-origin-height=&quot;541&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;945&quot; data-origin-height=&quot;815&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bxUQR2/dJMcaaLOihG/5csdf220ASLWKrrFCkdfz0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bxUQR2/dJMcaaLOihG/5csdf220ASLWKrrFCkdfz0/img.png&quot; data-alt=&quot;https://ecodelab.im/main&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bxUQR2/dJMcaaLOihG/5csdf220ASLWKrrFCkdfz0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbxUQR2%2FdJMcaaLOihG%2F5csdf220ASLWKrrFCkdfz0%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;945&quot; height=&quot;815&quot; data-origin-width=&quot;945&quot; data-origin-height=&quot;815&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://ecodelab.im/main&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;figure id=&quot;og_1776843657211&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;Ecodelab - 제작 앱 소개&quot; data-og-description=&quot;Ecodelab에서 개발한 다양한 앱들을 소개합니다&quot; data-og-host=&quot;www.ecodelab.im&quot; data-og-source-url=&quot;https://ecodelab.im/main&quot; data-og-url=&quot;https://www.ecodelab.im&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/yYVAd/dJMb86O33Zd/nJgiiVCFGDrYKPI1eD2xLK/img.png?width=1024&amp;amp;height=1024&amp;amp;face=0_0_1024_1024,https://scrap.kakaocdn.net/dn/nHgu1/dJMb88F7fhM/oYMDIA6JPahNvdToLsYzzk/img.png?width=1024&amp;amp;height=1024&amp;amp;face=0_0_1024_1024&quot;&gt;&lt;a href=&quot;https://ecodelab.im/main&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://ecodelab.im/main&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/yYVAd/dJMb86O33Zd/nJgiiVCFGDrYKPI1eD2xLK/img.png?width=1024&amp;amp;height=1024&amp;amp;face=0_0_1024_1024,https://scrap.kakaocdn.net/dn/nHgu1/dJMb88F7fhM/oYMDIA6JPahNvdToLsYzzk/img.png?width=1024&amp;amp;height=1024&amp;amp;face=0_0_1024_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;Ecodelab - 제작 앱 소개&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Ecodelab에서 개발한 다양한 앱들을 소개합니다&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.ecodelab.im&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;h2 data-ke-size=&quot;size26&quot;&gt;2) 프롬프트(Prompt)&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  프롬프트(Prompt)&lt;/b&gt;&lt;br /&gt;&lt;b&gt;&lt;br /&gt;- 인공지능(AI) 모델에게 &amp;lsquo;특정 작업을 수행하도록 지시하는 입력 값(명령어)&amp;rsquo; 을 의미합니다.&lt;/b&gt;&lt;br /&gt;- 어떤 프롬프트를 입력하는가&amp;rsquo;에 따라 결과물의 품질과 방향성이 크게 달라집니다.&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1914&quot; data-origin-height=&quot;848&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cx0JK2/dJMcab40fPZ/o2qSCdmNW3G9rHTeoTOjSk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cx0JK2/dJMcab40fPZ/o2qSCdmNW3G9rHTeoTOjSk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cx0JK2/dJMcab40fPZ/o2qSCdmNW3G9rHTeoTOjSk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcx0JK2%2FdJMcab40fPZ%2Fo2qSCdmNW3G9rHTeoTOjSk%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;1914&quot; height=&quot;848&quot; data-origin-width=&quot;1914&quot; data-origin-height=&quot;848&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;h3 data-ke-size=&quot;size23&quot;&gt;1. 프롬프트 템플릿(Prompt Template)&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  프롬프트 템플릿(Prompt Template)&lt;/b&gt;&lt;br /&gt;&lt;b&gt;&lt;br /&gt;- 사용자가 AI에게 지시를 내릴 때, &amp;lsquo;활용할 수 있는 반복 가능한 구조 또는 형식'을 의미합니다.&lt;/b&gt;&lt;br /&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;  'animal_name', 'animal_activity'라는 두 개의 변수를 입력받도록 설정하였습니다. &lt;br /&gt;- 입력 값으로 각각 '독수리'와 '수영'을 입력하면, '독수리는 수영을 할 수 있는가?'라는 동적인 프롬프트가 생성이 되는 구조입니다.&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;728&quot; data-origin-height=&quot;436&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/1ECPV/dJMcadV1brj/A8Pkub3milx51fZ4pnFq81/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/1ECPV/dJMcadV1brj/A8Pkub3milx51fZ4pnFq81/img.png&quot; data-alt=&quot;https://docs.cloud.google.com/vertex-ai/generative-ai/docs/learn/prompts/prompt-templates?hl=ko&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/1ECPV/dJMcadV1brj/A8Pkub3milx51fZ4pnFq81/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F1ECPV%2FdJMcadV1brj%2FA8Pkub3milx51fZ4pnFq81%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;728&quot; height=&quot;436&quot; data-origin-width=&quot;728&quot; data-origin-height=&quot;436&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://docs.cloud.google.com/vertex-ai/generative-ai/docs/learn/prompts/prompt-templates?hl=ko&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;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. 프롬프트 레시피&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  프롬프트 레시피(Prompt Recipe)&lt;/b&gt;&lt;br /&gt;&lt;b&gt;&lt;br /&gt;- 사용자가 AI에게 지시를 내릴 때, 사전에 작성한 '완성된 지시문을 활용하는 것&amp;rsquo;을 의미합니다.&lt;/b&gt;&lt;br /&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;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  [예시]&amp;nbsp;&lt;br /&gt;&lt;/b&gt;&lt;br /&gt;- 사용자는 &amp;lsquo;이를 기반으로 리스트 화면을 만들어줘&amp;rsquo;라고 입력 값으로 입력을 하였습니다. 그러면, 사전에 작성한 완성된 지시문(프롬프트 레시피)을 불러와서, 사용자의 입력 값에 추가되어서 일괄된 구조에서 프롬프트로 생성이 됩니다.&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1434&quot; data-origin-height=&quot;587&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Lo0Au/dJMcajaRJVO/NkvGByfQSDVSbtrpvO6jCK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Lo0Au/dJMcajaRJVO/NkvGByfQSDVSbtrpvO6jCK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Lo0Au/dJMcajaRJVO/NkvGByfQSDVSbtrpvO6jCK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FLo0Au%2FdJMcajaRJVO%2FNkvGByfQSDVSbtrpvO6jCK%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;1434&quot; height=&quot;587&quot; data-origin-width=&quot;1434&quot; data-origin-height=&quot;587&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;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;[ 더 알아보기 ]&lt;/b&gt;&lt;br /&gt;&lt;b&gt;&lt;br /&gt;  프롬프트 템플릿(Prompt Template)과 프롬프트 레시피(Prompt Recipe)는 무슨 차이가 있을까?&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;- 프롬프트 템플릿은 청사진으로 표현하고 '구조화된 틀'을 의미합니다. 즉, 하나의 틀 안에 빈칸을 채우듯이 변수만 바꾸어서 여러 작업을 재사용할 수 있는 틀과 같습니다.&lt;br /&gt;- 프롬프트 레시피는 이러한 템플릿에 '구체적인 값'을 모두 채워 넣은 완성본으로, 실제 AI에게 입력할 실행 가능한 '지시문 전체'를 의미합니다.&lt;br /&gt;&lt;b&gt;- 쉽게 말해, 템플릿은 &quot;빵 만드는 방법(레시피 양식)&quot;이고, 레시피는 &quot;실제 재료와 양을 모두 적어서 바로 만들 수 있는 완성된 레시피&quot;입니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;figure id=&quot;og_1776843918489&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;Prompt Templates vs Prompt Recipes in Prompt Engineering&quot; data-og-description=&quot;Discover the power of prompt templates and recipes in AI prompt engineering. Learn how these tools enable scalable, efficient, and effective AI solutions across industries and domains.&quot; data-og-host=&quot;promptengineering.org&quot; data-og-source-url=&quot;https://promptengineering.org/prompt-templates-vs-prompt-recipes-in-prompt-engineering/&quot; data-og-url=&quot;https://promptengineering.org/prompt-templates-vs-prompt-recipes-in-prompt-engineering/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/uNFBa/dJMb88F7fku/ORBy66KICJQYUMA0nIFuL1/img.jpg?width=700&amp;amp;height=700&amp;amp;face=0_0_700_700,https://scrap.kakaocdn.net/dn/mH7Bh/dJMb87f8MTV/x6x66n1JLDrhWjdqjvS2Mk/img.jpg?width=700&amp;amp;height=700&amp;amp;face=0_0_700_700,https://scrap.kakaocdn.net/dn/haQTj/dJMb81GZCAK/pROJSzUJZi0qObUfk8zupk/img.jpg?width=700&amp;amp;height=700&amp;amp;face=0_0_700_700&quot;&gt;&lt;a href=&quot;https://promptengineering.org/prompt-templates-vs-prompt-recipes-in-prompt-engineering/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://promptengineering.org/prompt-templates-vs-prompt-recipes-in-prompt-engineering/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/uNFBa/dJMb88F7fku/ORBy66KICJQYUMA0nIFuL1/img.jpg?width=700&amp;amp;height=700&amp;amp;face=0_0_700_700,https://scrap.kakaocdn.net/dn/mH7Bh/dJMb87f8MTV/x6x66n1JLDrhWjdqjvS2Mk/img.jpg?width=700&amp;amp;height=700&amp;amp;face=0_0_700_700,https://scrap.kakaocdn.net/dn/haQTj/dJMb81GZCAK/pROJSzUJZi0qObUfk8zupk/img.jpg?width=700&amp;amp;height=700&amp;amp;face=0_0_700_700');&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;Prompt Templates vs Prompt Recipes in Prompt Engineering&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Discover the power of prompt templates and recipes in AI prompt engineering. Learn how these tools enable scalable, efficient, and effective AI solutions across industries and domains.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;promptengineering.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;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. 프롬프트 래핑(Prompt Wrapping)&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  프롬프트 래핑(Prompt Wrapping)&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 사용자의 입력 값에 사전에 정의된 '프롬프트 형식(템플릿, 레시피)을 자동으로 감싸서&amp;rsquo;, AI가 더 정확하고 일관된 응답을 생성하도록 유도하는 기술입니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  [예시]&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- Gemini Cli를 기반으로 터미널에서 ./step1.sh create &amp;lsquo;로그인 화면을 만들고 싶어&amp;rsquo;라는 명령어 터미널에 입력하였습니다.&lt;/b&gt; &lt;br /&gt;- 이러면, 사전에 정의한 프롬프트 레시피, 템플릿을 가져와서 이에 맞는 공통적인 프롬프트로 구성되어 일괄된 프롬프트를 입력할 수 있습니다.&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1498&quot; data-origin-height=&quot;830&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cfE925/dJMcabRsSGu/JFuqykODKp2MXr63qkTBV1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cfE925/dJMcabRsSGu/JFuqykODKp2MXr63qkTBV1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cfE925/dJMcabRsSGu/JFuqykODKp2MXr63qkTBV1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcfE925%2FdJMcabRsSGu%2FJFuqykODKp2MXr63qkTBV1%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;1498&quot; height=&quot;830&quot; data-origin-width=&quot;1498&quot; data-origin-height=&quot;830&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;h3 data-ke-size=&quot;size23&quot;&gt;4. 프롬프트 래핑: gemini cli 활용 예시&lt;/h3&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;style3&quot;&gt;  프롬프트 래핑: gemini cli 활용 예시&lt;br /&gt;&lt;br /&gt;- 프롬프트 래핑을 이용해서 gemin cli 내에서 활용한 예시입니다.&lt;/blockquote&gt;
&lt;figure id=&quot;og_1776844183663&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;Gemini CLI와 프롬프트 래핑(Wrapping)으로 자동화 프롬프트 활용하기&quot; data-og-description=&quot;해당 글에서는 프롬프트 템플릿 + 레시피 + 래핑 + Gemini CLI를 이용하여서 프롬프트를 좀 더 정형화된 구조로 원하는 결과 값을 얻을 수 있는 방법에 대해 알아보는 방법에 대해 공유합니다.1) 주&quot; data-og-host=&quot;adjh54.tistory.com&quot; data-og-source-url=&quot;https://adjh54.tistory.com/700&quot; data-og-url=&quot;https://adjh54.tistory.com/700&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/Nz0qK/dJMb9fZxC8c/qRonRbJWsR2dJf5ka1qJQk/img.png?width=800&amp;amp;height=488&amp;amp;face=0_0_800_488,https://scrap.kakaocdn.net/dn/brBzzH/dJMb9cBKo0P/A2dEkzIIBiw3mq119wP0L0/img.png?width=800&amp;amp;height=488&amp;amp;face=0_0_800_488,https://scrap.kakaocdn.net/dn/bOhneZ/dJMb8SpKgm2/cmukElhul4uAXJHGELSVrK/img.png?width=596&amp;amp;height=1053&amp;amp;face=0_0_596_1053&quot;&gt;&lt;a href=&quot;https://adjh54.tistory.com/700&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://adjh54.tistory.com/700&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/Nz0qK/dJMb9fZxC8c/qRonRbJWsR2dJf5ka1qJQk/img.png?width=800&amp;amp;height=488&amp;amp;face=0_0_800_488,https://scrap.kakaocdn.net/dn/brBzzH/dJMb9cBKo0P/A2dEkzIIBiw3mq119wP0L0/img.png?width=800&amp;amp;height=488&amp;amp;face=0_0_800_488,https://scrap.kakaocdn.net/dn/bOhneZ/dJMb8SpKgm2/cmukElhul4uAXJHGELSVrK/img.png?width=596&amp;amp;height=1053&amp;amp;face=0_0_596_1053');&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;Gemini CLI와 프롬프트 래핑(Wrapping)으로 자동화 프롬프트 활용하기&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;해당 글에서는 프롬프트 템플릿 + 레시피 + 래핑 + Gemini CLI를 이용하여서 프롬프트를 좀 더 정형화된 구조로 원하는 결과 값을 얻을 수 있는 방법에 대해 알아보는 방법에 대해 공유합니다.1) 주&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;adjh54.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;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;1. STEP1.sh이라는 쉘 파일 내에 사전 수행 할 프롬프트 레시피(완성된 지시문)를 구성합니다&lt;/b&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;566&quot; data-origin-height=&quot;690&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/F0UuM/dJMcagZwOZD/FJWItsrkIsf01UCIKwHqTK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/F0UuM/dJMcagZwOZD/FJWItsrkIsf01UCIKwHqTK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/F0UuM/dJMcagZwOZD/FJWItsrkIsf01UCIKwHqTK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FF0UuM%2FdJMcagZwOZD%2FFJWItsrkIsf01UCIKwHqTK%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;566&quot; height=&quot;690&quot; data-origin-width=&quot;566&quot; data-origin-height=&quot;690&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;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;2. Create라는 변수에 대한 프롬프트 템플릿(상황별 지시문)을 구성합니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;568&quot; data-origin-height=&quot;707&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dtb4pU/dJMcai34Grc/uc5tNFjmC85qDJYcl1HCxk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dtb4pU/dJMcai34Grc/uc5tNFjmC85qDJYcl1HCxk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dtb4pU/dJMcai34Grc/uc5tNFjmC85qDJYcl1HCxk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdtb4pU%2FdJMcai34Grc%2Fuc5tNFjmC85qDJYcl1HCxk%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;568&quot; height=&quot;707&quot; data-origin-width=&quot;568&quot; data-origin-height=&quot;707&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;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;3. 최종적으로 사용자는 &amp;lsquo;로그인 화면을 만들고 싶어&amp;rsquo;라는 프롬프트를 입력합니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;709&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/tEMz2/dJMcadV1cbA/dKpo0K5xa1KsXeaXlkz2P1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/tEMz2/dJMcadV1cbA/dKpo0K5xa1KsXeaXlkz2P1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/tEMz2/dJMcadV1cbA/dKpo0K5xa1KsXeaXlkz2P1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FtEMz2%2FdJMcadV1cbA%2FdKpo0K5xa1KsXeaXlkz2P1%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;1280&quot; height=&quot;709&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;709&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;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;4. 결과로 완성된 지시문 + 상황별 지시문+ 추가 명령어로 수행하여 상황에 따라 변수와 추가 명령어만 바꾸어서 일괄된 처리가 가능해졌습니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1883&quot; data-origin-height=&quot;815&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/9NkcJ/dJMcahqDD6C/LWqnp70VeBQ6rw7HB2hOOk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/9NkcJ/dJMcahqDD6C/LWqnp70VeBQ6rw7HB2hOOk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/9NkcJ/dJMcahqDD6C/LWqnp70VeBQ6rw7HB2hOOk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F9NkcJ%2FdJMcahqDD6C%2FLWqnp70VeBQ6rw7HB2hOOk%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;1883&quot; height=&quot;815&quot; data-origin-width=&quot;1883&quot; data-origin-height=&quot;815&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;h3 data-ke-size=&quot;size23&quot;&gt;5. 페르소나(Persona)&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  페르소나(Persona)&lt;/b&gt;&lt;br /&gt;&lt;b&gt;&lt;br /&gt;-&amp;nbsp;'AI가 특정 역할이나 전문가처럼 행동'하도록 지시하는 기법을 의미합니다.&lt;/b&gt;&lt;br /&gt;- 예를 들어 '경력 10년 차 UX 디자이너로서', '친절한 초등학교 선생님'과 같이 Ai에게 구체적인 역할을 부여합니다.&lt;br /&gt;- 이를 통해, 답변의 품질, 방향성, 그리고 사용자 맥락에 대한 적합성을 크게 개선할 수 있습니다.&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1250&quot; data-origin-height=&quot;608&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pXfBM/dJMcaiJM2dZ/zdfILKVEA3lq4WgLuvwLKK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pXfBM/dJMcaiJM2dZ/zdfILKVEA3lq4WgLuvwLKK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pXfBM/dJMcaiJM2dZ/zdfILKVEA3lq4WgLuvwLKK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpXfBM%2FdJMcaiJM2dZ%2FzdfILKVEA3lq4WgLuvwLKK%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;1250&quot; height=&quot;608&quot; data-origin-width=&quot;1250&quot; data-origin-height=&quot;608&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;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;[더 알아보기]&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;  결론적으로 프롬프트 템플릿, 레시피, 래핑, 페르소나는 무슨 차이일까?&lt;/b&gt;&lt;/blockquote&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style2&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;구분&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;프롬프트 템플릿&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;프롬프트 레시피&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;프롬프트 래핑&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;페르소나&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;정의&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;변수가 포함된 재사용 가능한 구조화된 틀&lt;/td&gt;
&lt;td&gt;변수에 값을 채운 즉시 실행 가능한 완성본&lt;/td&gt;
&lt;td&gt;기존 프롬프트를 감싸서 추가 지시사항이나 컨텍스트를 덧붙이는 기법&lt;/td&gt;
&lt;td&gt;AI에게 특정 역할, 전문성, 관점을 부여하는 설정&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;상태&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;미완성 (변수 존재)&lt;/td&gt;
&lt;td&gt;완성 (변수 대입 완료)&lt;/td&gt;
&lt;td&gt;완성된 프롬프트 위에 추가 레이어&lt;/td&gt;
&lt;td&gt;프롬프트의 앞부분에 위치 (역할 정의)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;목적&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;반복 작업 효율화&lt;/td&gt;
&lt;td&gt;즉시 실행&lt;/td&gt;
&lt;td&gt;출력 형식 제어, 안전장치, 제약조건 추가&lt;/td&gt;
&lt;td&gt;답변의 관점과 품질 개선, 전문성 부여&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;예시&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&quot;다음 {주제}에 대해 {형식}으로 작성해줘&quot;&lt;/td&gt;
&lt;td&gt;&quot;다음 AI에 대해 블로그 글로 작성해줘&quot;&lt;/td&gt;
&lt;td&gt;&quot;위 내용을 반드시 JSON 형태로만 출력해줘&quot;&lt;/td&gt;
&lt;td&gt;&quot;당신은 10년차 시니어 백엔드 개발자입니다&quot;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;재사용성&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;높음 (여러 작업에 적용)&lt;/td&gt;
&lt;td&gt;낮음 (특정 작업 전용)&lt;/td&gt;
&lt;td&gt;중간 (다양한 프롬프트에 적용 가능)&lt;/td&gt;
&lt;td&gt;매우 높음 (모든 대화에 일관되게 적용)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;사용 시점&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;반복되는 패턴 발견 시&lt;/td&gt;
&lt;td&gt;구체적인 작업 실행 시&lt;/td&gt;
&lt;td&gt;출력 형식 통제나 제약 필요 시&lt;/td&gt;
&lt;td&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;h2 data-ke-size=&quot;size26&quot;&gt;3) 공통 프롬프트 활용-1 : 개인 맞춤 설정&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  공통 프롬프트 활용-1 : 개인 맞춤 설정&lt;/b&gt;&lt;br /&gt;&lt;b&gt;&lt;br /&gt;- 사용자가 설정한 모든 대화에 자동으로 적용되는 개인 맞춤 설정입니다.&lt;/b&gt;&lt;br /&gt;- 선호하는 답변 스타일, 역할, 요구사항 등을 한 번 정의하면 별도로 변경하기 전까지 모든 대화 세션에 지속적으로 반영됩니다.&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;890&quot; data-origin-height=&quot;468&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dkefIT/dJMcaffjumc/mIl3aQcU9tlN6bVqyV54q0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dkefIT/dJMcaffjumc/mIl3aQcU9tlN6bVqyV54q0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dkefIT/dJMcaffjumc/mIl3aQcU9tlN6bVqyV54q0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdkefIT%2FdJMcaffjumc%2FmIl3aQcU9tlN6bVqyV54q0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;890&quot; height=&quot;468&quot; data-origin-width=&quot;890&quot; data-origin-height=&quot;468&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;634&quot; data-origin-height=&quot;650&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bydNs4/dJMcadhr4H6/ahgzXIW2Mosl9jhLbKXAkK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bydNs4/dJMcadhr4H6/ahgzXIW2Mosl9jhLbKXAkK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bydNs4/dJMcadhr4H6/ahgzXIW2Mosl9jhLbKXAkK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbydNs4%2FdJMcadhr4H6%2FahgzXIW2Mosl9jhLbKXAkK%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;634&quot; height=&quot;650&quot; data-origin-width=&quot;634&quot; data-origin-height=&quot;650&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;h3 data-ke-size=&quot;size23&quot;&gt;1. 개인 맞춤 설정을 하지 않은 경우 예시&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  개인 맞춤 설정을 하지 않은 경우 예시&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;- 아래와 같이 &quot;일본 오사카 여행지를 추천해 줘.&quot; 프롬프트를 지시하였습니다.&lt;br /&gt;- 프롬프트 결과를 받았으나, '내가 원하는 구체적인 결과'를 추출하지 못하였습니다.&lt;/blockquote&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;1585&quot; data-origin-height=&quot;766&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rNV6T/dJMcahqDEqC/BhLBmzgIOzOrMb9ewYBm11/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rNV6T/dJMcahqDEqC/BhLBmzgIOzOrMb9ewYBm11/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rNV6T/dJMcahqDEqC/BhLBmzgIOzOrMb9ewYBm11/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FrNV6T%2FdJMcahqDEqC%2FBhLBmzgIOzOrMb9ewYBm11%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;1585&quot; height=&quot;766&quot; data-origin-width=&quot;1585&quot; data-origin-height=&quot;766&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;869&quot; data-origin-height=&quot;749&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/thPhk/dJMcaffjuz1/JtoKQQzeyMR6Od1KWzdvK0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/thPhk/dJMcaffjuz1/JtoKQQzeyMR6Od1KWzdvK0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/thPhk/dJMcaffjuz1/JtoKQQzeyMR6Od1KWzdvK0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FthPhk%2FdJMcaffjuz1%2FJtoKQQzeyMR6Od1KWzdvK0%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;869&quot; height=&quot;749&quot; data-origin-width=&quot;869&quot; data-origin-height=&quot;749&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;h3 data-ke-size=&quot;size23&quot;&gt;2. 개인 맞춤을 적용한 경우&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  개인 맞춤을 적용한 경우&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 개인 맞춤 설정을 적용한 경우 예시 아래와 같이 맞춤 설정 값을 지정하고 적용하였습니다.&lt;/b&gt;&lt;br /&gt;- 이전과 동일한 프롬프트를 입력했을 때, 사전에 입력한 개인 맞춤 설정에 따라 성격, 성향에 맞는 여행지를 추천해 줌을 확인하였습니다.&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1283&quot; data-origin-height=&quot;816&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bl2qr3/dJMcaf0CfPV/XXjB8qOSj27KOoGXPrvhgk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bl2qr3/dJMcaf0CfPV/XXjB8qOSj27KOoGXPrvhgk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bl2qr3/dJMcaf0CfPV/XXjB8qOSj27KOoGXPrvhgk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbl2qr3%2FdJMcaf0CfPV%2FXXjB8qOSj27KOoGXPrvhgk%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;1283&quot; height=&quot;816&quot; data-origin-width=&quot;1283&quot; data-origin-height=&quot;816&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;798&quot; data-origin-height=&quot;903&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bGrCj1/dJMcacQnwRb/bA6vA4KoOrUn0HFZbzZh5k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bGrCj1/dJMcacQnwRb/bA6vA4KoOrUn0HFZbzZh5k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bGrCj1/dJMcacQnwRb/bA6vA4KoOrUn0HFZbzZh5k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbGrCj1%2FdJMcacQnwRb%2FbA6vA4KoOrUn0HFZbzZh5k%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;798&quot; height=&quot;903&quot; data-origin-width=&quot;798&quot; data-origin-height=&quot;903&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4) 공통 프롬프트 활용-2 : 프롬프트 칩 활용&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;&amp;nbsp;  공통 프롬프트 활용-2 : 프롬프트 칩 활용&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 웹 앱에서 클릭 한 번으로 사전 정의된 프롬프트를 즉시 실행할 수 있는 버튼형 UI입니다. 사전에 정의된 추천 프롬프트가 제공되며, 클릭 시 텍스트가 자동 입력이 됩니다.&lt;/b&gt;&lt;br /&gt;- 팀 단위로 AI의 역할, 규칙, 작업 환경을 사전 정의해 두고, 버튼 클릭만으로 해당 페르소나를 적용할 수 있습니다. 이를 통해, 팀원 모두가 동일한 AI 설정을 공유하며 일관된 결과를 얻을 수 있습니다.&lt;/blockquote&gt;
&lt;figure id=&quot;og_1776844852059&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;nbsp;|&amp;nbsp; Gemini Enterprise &amp;nbsp;|&amp;nbsp; Google Cloud Documentation&quot; data-og-description=&quot;참고: 이 문서는 Gemini Enterprise의 Standard, Plus, Frontline 버전에 적용됩니다. Business 버전에 관한 자세한 내용은 Gemini Enterprise - Business 버전 고객센터를 참고하세요. 의견 보내기 프롬프트 칩 구성 컬&quot; data-og-host=&quot;docs.cloud.google.com&quot; data-og-source-url=&quot;https://docs.cloud.google.com/gemini/enterprise/docs/configure-prompt-chips?hl=ko&quot; data-og-url=&quot;https://docs.cloud.google.com/gemini/enterprise/docs/configure-prompt-chips?hl=ko&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bUfqEQ/dJMb88e2YGb/JhICbrycZlDDHXAllBkiU0/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/GOUsT/dJMb9cBKo5x/psdSMbnWFxjcu2gk3lck1k/img.png?width=1668&amp;amp;height=652&amp;amp;face=0_0_1668_652&quot;&gt;&lt;a href=&quot;https://docs.cloud.google.com/gemini/enterprise/docs/configure-prompt-chips?hl=ko&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://docs.cloud.google.com/gemini/enterprise/docs/configure-prompt-chips?hl=ko&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bUfqEQ/dJMb88e2YGb/JhICbrycZlDDHXAllBkiU0/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/GOUsT/dJMb9cBKo5x/psdSMbnWFxjcu2gk3lck1k/img.png?width=1668&amp;amp;height=652&amp;amp;face=0_0_1668_652');&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;nbsp;|&amp;nbsp; Gemini Enterprise &amp;nbsp;|&amp;nbsp; Google Cloud Documentation&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;참고: 이 문서는 Gemini Enterprise의 Standard, Plus, Frontline 버전에 적용됩니다. Business 버전에 관한 자세한 내용은 Gemini Enterprise - Business 버전 고객센터를 참고하세요. 의견 보내기 프롬프트 칩 구성 컬&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;docs.cloud.google.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2032&quot; data-origin-height=&quot;1063&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bOHB8M/dJMcaarvLQy/EndXpr6JaBKfc93FRH8O90/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bOHB8M/dJMcaarvLQy/EndXpr6JaBKfc93FRH8O90/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bOHB8M/dJMcaarvLQy/EndXpr6JaBKfc93FRH8O90/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbOHB8M%2FdJMcaarvLQy%2FEndXpr6JaBKfc93FRH8O90%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2032&quot; height=&quot;1063&quot; data-origin-width=&quot;2032&quot; data-origin-height=&quot;1063&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;h3 data-ke-size=&quot;size23&quot;&gt;1. 사전에 프롬프트 칩 내의 내용을 사전 정의(역할, 규칙, 작업 환경 등에 대한 정의)&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2032&quot; data-origin-height=&quot;1080&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dQERsd/dJMcafzABTr/9BQLB60FGKFDBejxMkj1Ck/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dQERsd/dJMcafzABTr/9BQLB60FGKFDBejxMkj1Ck/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dQERsd/dJMcafzABTr/9BQLB60FGKFDBejxMkj1Ck/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdQERsd%2FdJMcafzABTr%2F9BQLB60FGKFDBejxMkj1Ck%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2032&quot; height=&quot;1080&quot; data-origin-width=&quot;2032&quot; data-origin-height=&quot;1080&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;h3 data-ke-size=&quot;size23&quot;&gt;2. 프롬프트 칩이 출력이 되며, 이를 선택하면 프롬프트가 자동 입력&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1988&quot; data-origin-height=&quot;1019&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/djlWMR/dJMcagZwPEW/tB7cjKK94Yim9hRUdKyf11/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/djlWMR/dJMcagZwPEW/tB7cjKK94Yim9hRUdKyf11/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/djlWMR/dJMcagZwPEW/tB7cjKK94Yim9hRUdKyf11/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdjlWMR%2FdJMcagZwPEW%2FtB7cjKK94Yim9hRUdKyf11%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;1988&quot; height=&quot;1019&quot; data-origin-width=&quot;1988&quot; data-origin-height=&quot;1019&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;h3 data-ke-size=&quot;size23&quot;&gt;3. 이를 실행하여 페르소나를 지정하고, 추가 프롬프트를 입력하여 공통된 결과 출력&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2032&quot; data-origin-height=&quot;1063&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ulbtC/dJMcabYfEAw/chW8YM5WZV9VTCJ4FKjLmk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ulbtC/dJMcabYfEAw/chW8YM5WZV9VTCJ4FKjLmk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ulbtC/dJMcabYfEAw/chW8YM5WZV9VTCJ4FKjLmk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FulbtC%2FdJMcabYfEAw%2FchW8YM5WZV9VTCJ4FKjLmk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2032&quot; height=&quot;1063&quot; data-origin-width=&quot;2032&quot; data-origin-height=&quot;1063&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;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;[더 알아보기]&lt;/b&gt; &lt;br /&gt;&lt;b&gt;&lt;br /&gt; 최초 페르소나로 역할을 부여했지만, 하나의 대화창에 프롬프트를 입력하다 보면 &amp;lsquo;컨텍스트 드리프트&amp;rsquo;가 발생할 수 있습니다.&lt;br /&gt;&lt;/b&gt; &lt;br /&gt;- 컨텍스트 드리프트의 경우는 대화가 길어지면서 최초 페르소나/설정에서 벗어나는 현상을 의미합니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;5) 공통 프롬프트 활용-3 : 대화 공유&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  공통 프롬프트 활용-3 : 대화 공유&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 이전 대화를 다른 사용자와 공유할 수 있습니다. 대화를 다른 사용자와 공유하면 해당 사용자는 프롬프트, 어시스턴트의 응답, 이미지 또는 동영상과 같은 생성된 콘텐츠를 비롯한 대화의 기존 콘텐츠를 모두 볼 수 있습니다.&lt;/b&gt;&lt;br /&gt;- 효과적인 프롬프트와 결과물을 팀원과 즉시 공유하여 협업 효율성을 높이고, 성공 사례를 조직 자산으로 축적하여 반복 활용할 수 있습니다.&lt;/blockquote&gt;
&lt;figure id=&quot;og_1776844985704&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;nbsp;|&amp;nbsp; Gemini Enterprise &amp;nbsp;|&amp;nbsp; Google Cloud Documentation&quot; data-og-description=&quot;참고: 이 문서는 Gemini Enterprise의 Standard, Plus, Frontline 버전에 적용됩니다. Business 버전에 관한 자세한 내용은 Gemini Enterprise - Business 버전 고객센터를 참고하세요. 의견 보내기 대화 공유 컬렉션을 &quot; data-og-host=&quot;docs.cloud.google.com&quot; data-og-source-url=&quot;https://docs.cloud.google.com/gemini/enterprise/docs/share-conversations?hl=ko&quot; data-og-url=&quot;https://docs.cloud.google.com/gemini/enterprise/docs/share-conversations?hl=ko&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/5wiJA/dJMb9aKHpkk/RBYt8YTQqcUOoAsYV2BKU0/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/wbshl/dJMb8955Vyj/6NXKwZBHImlKFPFX4fki4K/img.png?width=2111&amp;amp;height=1421&amp;amp;face=0_0_2111_1421,https://scrap.kakaocdn.net/dn/xH5xc/dJMb9cBKo6B/dkNWqEVoMTJJUjJ9dWwEPk/img.png?width=1629&amp;amp;height=1084&amp;amp;face=0_0_1629_1084&quot;&gt;&lt;a href=&quot;https://docs.cloud.google.com/gemini/enterprise/docs/share-conversations?hl=ko&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://docs.cloud.google.com/gemini/enterprise/docs/share-conversations?hl=ko&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/5wiJA/dJMb9aKHpkk/RBYt8YTQqcUOoAsYV2BKU0/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/wbshl/dJMb8955Vyj/6NXKwZBHImlKFPFX4fki4K/img.png?width=2111&amp;amp;height=1421&amp;amp;face=0_0_2111_1421,https://scrap.kakaocdn.net/dn/xH5xc/dJMb9cBKo6B/dkNWqEVoMTJJUjJ9dWwEPk/img.png?width=1629&amp;amp;height=1084&amp;amp;face=0_0_1629_1084');&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;nbsp;|&amp;nbsp; Gemini Enterprise &amp;nbsp;|&amp;nbsp; Google Cloud Documentation&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;참고: 이 문서는 Gemini Enterprise의 Standard, Plus, Frontline 버전에 적용됩니다. Business 버전에 관한 자세한 내용은 Gemini Enterprise - Business 버전 고객센터를 참고하세요. 의견 보내기 대화 공유 컬렉션을&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;docs.cloud.google.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;h3 data-ke-size=&quot;size23&quot;&gt;1. 구성한 대화창에 공유 버튼을 누릅니다&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2032&quot; data-origin-height=&quot;1063&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/WVQ7X/dJMcah5e1C2/culV0HZMRPeuKl3MWBeafK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/WVQ7X/dJMcah5e1C2/culV0HZMRPeuKl3MWBeafK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/WVQ7X/dJMcah5e1C2/culV0HZMRPeuKl3MWBeafK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FWVQ7X%2FdJMcah5e1C2%2FculV0HZMRPeuKl3MWBeafK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2032&quot; height=&quot;1063&quot; data-origin-width=&quot;2032&quot; data-origin-height=&quot;1063&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;h3 data-ke-size=&quot;size23&quot;&gt;2. 다른 사용자에게 &amp;lsquo;대화 공유 링크&amp;rsquo;를 전달합니다&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2032&quot; data-origin-height=&quot;1063&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/NXuou/dJMcabYfEJK/6Utawi68mmumkEayG3FkFK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/NXuou/dJMcabYfEJK/6Utawi68mmumkEayG3FkFK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/NXuou/dJMcabYfEJK/6Utawi68mmumkEayG3FkFK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FNXuou%2FdJMcabYfEJK%2F6Utawi68mmumkEayG3FkFK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2032&quot; height=&quot;1063&quot; data-origin-width=&quot;2032&quot; data-origin-height=&quot;1063&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;h3 data-ke-size=&quot;size23&quot;&gt;3. 다른 사용자는 링크에 따라 접속을 합니다&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2032&quot; data-origin-height=&quot;1080&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bOISaT/dJMcaaE3M3L/GvH2fmuQIBecRksrj8WMm0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bOISaT/dJMcaaE3M3L/GvH2fmuQIBecRksrj8WMm0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bOISaT/dJMcaaE3M3L/GvH2fmuQIBecRksrj8WMm0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbOISaT%2FdJMcaaE3M3L%2FGvH2fmuQIBecRksrj8WMm0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2032&quot; height=&quot;1080&quot; data-origin-width=&quot;2032&quot; data-origin-height=&quot;1080&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;h3 data-ke-size=&quot;size23&quot;&gt;4. 이전 사용자가 프롬프트로 이용했던 내용에 대해서 이어서 작성을 할 수 있습니다.&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2032&quot; data-origin-height=&quot;1080&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b9CuNb/dJMcaipxp7z/WtKG8EKQOGvli1yg47FNY0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b9CuNb/dJMcaipxp7z/WtKG8EKQOGvli1yg47FNY0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b9CuNb/dJMcaipxp7z/WtKG8EKQOGvli1yg47FNY0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb9CuNb%2FdJMcaipxp7z%2FWtKG8EKQOGvli1yg47FNY0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2032&quot; height=&quot;1080&quot; data-origin-width=&quot;2032&quot; data-origin-height=&quot;1080&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;6) 프롬프트 활용- 4: 데이터 스토어&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  프롬프트 활용- 4: 데이터 스토어&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 사용자의 자체 데이터를 모델과 연결하여, 해당 데이터 기반의 검색, 답변, 작업을 제공하는 기능입니다.&lt;/b&gt;&lt;br /&gt;- 사용자 데이터는 Google Cloud(퍼스트 파티), JIRA, Confluence(서드 파티)등을 의미합니다.&lt;br /&gt;- 이 데이터 스토어로 이를 연결하여 연결된 데이터 범위 내에서 응답을 하는 것을 의미합니다.&lt;/blockquote&gt;
&lt;figure id=&quot;og_1776845184638&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;nbsp;|&amp;nbsp; Gemini Enterprise &amp;nbsp;|&amp;nbsp; Google Cloud Documentation&quot; data-og-description=&quot;참고: 이 문서는 Gemini Enterprise의 Standard, Plus, Frontline 버전에 적용됩니다. Business 버전에 관한 자세한 내용은 Gemini Enterprise - Business 버전 고객센터를 참고하세요. 의견 보내기 앱 및 데이터 스토어 &quot; data-og-host=&quot;docs.cloud.google.com&quot; data-og-source-url=&quot;https://docs.cloud.google.com/gemini/enterprise/docs/apps-data-stores?hl=ko&quot; data-og-url=&quot;https://docs.cloud.google.com/gemini/enterprise/docs/apps-data-stores?hl=ko&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/kkIpc/dJMb9cBKo9y/yPMzqgGRYirnexkuJyP2l1/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630&quot;&gt;&lt;a href=&quot;https://docs.cloud.google.com/gemini/enterprise/docs/apps-data-stores?hl=ko&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://docs.cloud.google.com/gemini/enterprise/docs/apps-data-stores?hl=ko&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/kkIpc/dJMb9cBKo9y/yPMzqgGRYirnexkuJyP2l1/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;앱 및 데이터 스토어 정보 &amp;nbsp;|&amp;nbsp; Gemini Enterprise &amp;nbsp;|&amp;nbsp; Google Cloud Documentation&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;참고: 이 문서는 Gemini Enterprise의 Standard, Plus, Frontline 버전에 적용됩니다. Business 버전에 관한 자세한 내용은 Gemini Enterprise - Business 버전 고객센터를 참고하세요. 의견 보내기 앱 및 데이터 스토어&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;docs.cloud.google.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1284&quot; data-origin-height=&quot;672&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/2K56f/dJMcaaSAtdO/hbARCOUVduL1r8lSUV1BK1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/2K56f/dJMcaaSAtdO/hbARCOUVduL1r8lSUV1BK1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/2K56f/dJMcaaSAtdO/hbARCOUVduL1r8lSUV1BK1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F2K56f%2FdJMcaaSAtdO%2FhbARCOUVduL1r8lSUV1BK1%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;1284&quot; height=&quot;672&quot; data-origin-width=&quot;1284&quot; data-origin-height=&quot;672&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;h3 data-ke-size=&quot;size23&quot;&gt;1. 데이터 스토어 활용: Github 설정&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;1.1.Github 내에 Github apps로 Gemini를 연결&lt;/h4&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2032&quot; data-origin-height=&quot;1083&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/btb2Tt/dJMcadItLoB/snBmo1H2wze68zrRMbNsr0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/btb2Tt/dJMcadItLoB/snBmo1H2wze68zrRMbNsr0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/btb2Tt/dJMcadItLoB/snBmo1H2wze68zrRMbNsr0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbtb2Tt%2FdJMcadItLoB%2FsnBmo1H2wze68zrRMbNsr0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2032&quot; height=&quot;1083&quot; data-origin-width=&quot;2032&quot; data-origin-height=&quot;1083&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;h4 data-ke-size=&quot;size20&quot;&gt;1.2. 각각 접근 가능한 &amp;lsquo;레포지토리 별&amp;rsquo;로 지정이 가능하며, 접근 권한에 대해서 지정 가능&lt;/h4&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2032&quot; data-origin-height=&quot;1083&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/PVSuR/dJMcaadYFEd/eabKXetUMU7fDNpH1kaI61/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/PVSuR/dJMcaadYFEd/eabKXetUMU7fDNpH1kaI61/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/PVSuR/dJMcaadYFEd/eabKXetUMU7fDNpH1kaI61/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FPVSuR%2FdJMcaadYFEd%2FeabKXetUMU7fDNpH1kaI61%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2032&quot; height=&quot;1083&quot; data-origin-width=&quot;2032&quot; data-origin-height=&quot;1083&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;h4 data-ke-size=&quot;size20&quot;&gt;1.3. Gemini 내에서 github를 연결하면 최종 연결 완료가 됩니다&lt;/h4&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2032&quot; data-origin-height=&quot;1082&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ApltK/dJMcajaRLDy/YlVoWACWLKjPNwrNOhq2RK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ApltK/dJMcajaRLDy/YlVoWACWLKjPNwrNOhq2RK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ApltK/dJMcajaRLDy/YlVoWACWLKjPNwrNOhq2RK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FApltK%2FdJMcajaRLDy%2FYlVoWACWLKjPNwrNOhq2RK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2032&quot; height=&quot;1082&quot; data-origin-width=&quot;2032&quot; data-origin-height=&quot;1082&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;h3 data-ke-size=&quot;size23&quot;&gt;2. 데이터 스토어: Gemini 활용&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2.1. 하단 메뉴에서 커넥터 &amp;gt; &amp;lsquo;github&amp;rsquo;를 활성화&lt;/h4&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2032&quot; data-origin-height=&quot;1082&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ObZ6v/dJMcahKWi2e/3GS3a56pcIckaNoXK3QVg0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ObZ6v/dJMcahKWi2e/3GS3a56pcIckaNoXK3QVg0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ObZ6v/dJMcahKWi2e/3GS3a56pcIckaNoXK3QVg0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FObZ6v%2FdJMcahKWi2e%2F3GS3a56pcIckaNoXK3QVg0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2032&quot; height=&quot;1082&quot; data-origin-width=&quot;2032&quot; data-origin-height=&quot;1082&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;h4 data-ke-size=&quot;size20&quot;&gt;2.2. 연결된 github 관련하여 프롬프트로 &amp;lsquo;연결된 레포지토리 목록&amp;rsquo;을 요청했을 때 응답 전달&lt;/h4&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2032&quot; data-origin-height=&quot;1082&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bsYb2v/dJMcahKWi2R/DMYpwfFAqbKfCHr4oJKUw1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bsYb2v/dJMcahKWi2R/DMYpwfFAqbKfCHr4oJKUw1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bsYb2v/dJMcahKWi2R/DMYpwfFAqbKfCHr4oJKUw1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbsYb2v%2FdJMcahKWi2R%2FDMYpwfFAqbKfCHr4oJKUw1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2032&quot; height=&quot;1082&quot; data-origin-width=&quot;2032&quot; data-origin-height=&quot;1082&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;h4 data-ke-size=&quot;size20&quot;&gt;2.3. 특정 파일(README.md)에 대한 요청 시, 해당 파일 읽어오기&lt;/h4&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2032&quot; data-origin-height=&quot;1082&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/DxzIx/dJMcajvbmVl/8FBPB1M5ZHiaHGyI28HSKk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/DxzIx/dJMcajvbmVl/8FBPB1M5ZHiaHGyI28HSKk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/DxzIx/dJMcajvbmVl/8FBPB1M5ZHiaHGyI28HSKk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FDxzIx%2FdJMcajvbmVl%2F8FBPB1M5ZHiaHGyI28HSKk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2032&quot; height=&quot;1082&quot; data-origin-width=&quot;2032&quot; data-origin-height=&quot;1082&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2.4. 특정 &amp;lsquo;레포지토리 내 페이지&amp;rsquo;에 대한 코드 분석 가능&lt;/h4&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2032&quot; data-origin-height=&quot;1082&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/5d7ZQ/dJMcaaE3NyE/nuDFKZKoU8qNVKCOg76W21/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/5d7ZQ/dJMcaaE3NyE/nuDFKZKoU8qNVKCOg76W21/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/5d7ZQ/dJMcaaE3NyE/nuDFKZKoU8qNVKCOg76W21/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F5d7ZQ%2FdJMcaaE3NyE%2FnuDFKZKoU8qNVKCOg76W21%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2032&quot; height=&quot;1082&quot; data-origin-width=&quot;2032&quot; data-origin-height=&quot;1082&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2.5. 레포지토리 내에 소스코드 분석하여 &amp;lsquo;개발 정책&amp;rsquo;에 대한 결과물을 반환&lt;/h4&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2032&quot; data-origin-height=&quot;1082&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/0O9nA/dJMcahRFT66/9l5nW5S4AKjPwI5z4DdGFk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/0O9nA/dJMcahRFT66/9l5nW5S4AKjPwI5z4DdGFk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/0O9nA/dJMcahRFT66/9l5nW5S4AKjPwI5z4DdGFk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F0O9nA%2FdJMcahRFT66%2F9l5nW5S4AKjPwI5z4DdGFk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2032&quot; height=&quot;1082&quot; data-origin-width=&quot;2032&quot; data-origin-height=&quot;1082&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;h4 data-ke-size=&quot;size20&quot;&gt;2.6. &amp;lsquo;레포지토리 간&amp;rsquo;의 데이터 통신 구조를 분석 가능&lt;/h4&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2032&quot; data-origin-height=&quot;1082&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/YqTma/dJMb99TEmZL/OdgdCwBVyV9J6criv3FFt0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/YqTma/dJMb99TEmZL/OdgdCwBVyV9J6criv3FFt0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/YqTma/dJMb99TEmZL/OdgdCwBVyV9J6criv3FFt0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FYqTma%2FdJMb99TEmZL%2FOdgdCwBVyV9J6criv3FFt0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2032&quot; height=&quot;1082&quot; data-origin-width=&quot;2032&quot; data-origin-height=&quot;1082&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;h3 data-ke-size=&quot;size23&quot;&gt;3. 데이터 스토어: Confluence 활용&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3.1. 접근 가능한 confluence의 스페이스들을 확인&lt;/h4&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2032&quot; data-origin-height=&quot;1082&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/NoGcE/dJMcaaSAtw7/3JBFRHs9ckL6HYVNM6xRuk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/NoGcE/dJMcaaSAtw7/3JBFRHs9ckL6HYVNM6xRuk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/NoGcE/dJMcaaSAtw7/3JBFRHs9ckL6HYVNM6xRuk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FNoGcE%2FdJMcaaSAtw7%2F3JBFRHs9ckL6HYVNM6xRuk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2032&quot; height=&quot;1082&quot; data-origin-width=&quot;2032&quot; data-origin-height=&quot;1082&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;h4 data-ke-size=&quot;size20&quot;&gt;3.2. 특정 인물에 대해서 조회 및 분석&lt;/h4&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2032&quot; data-origin-height=&quot;1082&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cWBD1r/dJMcabcQbkv/Rce2Z08jOmmC7zk4mAHFv1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cWBD1r/dJMcabcQbkv/Rce2Z08jOmmC7zk4mAHFv1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cWBD1r/dJMcabcQbkv/Rce2Z08jOmmC7zk4mAHFv1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcWBD1r%2FdJMcabcQbkv%2FRce2Z08jOmmC7zk4mAHFv1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2032&quot; height=&quot;1082&quot; data-origin-width=&quot;2032&quot; data-origin-height=&quot;1082&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;b&gt;오늘도&amp;nbsp;감사합니다.&amp;nbsp; &lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>공통/Trend</category>
      <category>Gemini enterprise</category>
      <category>Gemini 공통 프롬프트</category>
      <category>Gemini 공통 프롬프트 제안</category>
      <category>Gemini 회사에서 사용</category>
      <category>공통 프롬프트 구성</category>
      <category>페르소나</category>
      <category>프롬프트</category>
      <category>프롬프트 같이 사용</category>
      <category>프롬프트 레시피</category>
      <category>프롬프트 템플릿</category>
      <author>adjh54</author>
      <guid isPermaLink="true">https://adjh54.tistory.com/748</guid>
      <comments>https://adjh54.tistory.com/748#entry748comment</comments>
      <pubDate>Wed, 22 Apr 2026 20:00:49 +0900</pubDate>
    </item>
    <item>
      <title>BaaS 기반 Supabase, 서비스 이해부터 보안 취약점까지</title>
      <link>https://adjh54.tistory.com/747</link>
      <description>&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;해당 글에서는 &lt;span data-token-index=&quot;0&quot;&gt;BaaS, Backend as a Service 기반에 Supabase의 주요 서비스에 대한 이해를 돕기 위해 작성한 입니다&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;
&lt;figure contenteditable=&quot;false&quot; data-ke-type=&quot;emoticon&quot; data-ke-align=&quot;alignCenter&quot; data-emoticon-type=&quot;face&quot; data-emoticon-name=&quot;073&quot; data-emoticon-isanimation=&quot;false&quot; data-emoticon-src=&quot;https://t1.daumcdn.net/keditor/emoticon/face/large/073.png&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/keditor/emoticon/face/large/073.png&quot; width=&quot;80&quot; /&gt;&lt;/figure&gt;
&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;span data-token-index=&quot;0&quot;&gt;&lt;/span&gt;&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1) 백엔드 인프라(BaaS, Backend as a Service)에 대한 필요성&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  백엔드 인프라(BaaS, Backend as a Service)에 대한 필요성&lt;/b&gt;&lt;br /&gt;&lt;b&gt;&lt;br /&gt;- 현재 개발 중인 앱이 있습니다. 해당 앱은 유튜브 영상을 링크를 복사해서 해당 앱에 붙여 넣기를 하면, 영상을 분석하여서 레시피를 텍스트 형태로 보고, 구간별 영상도 확인할 수 있는 앱입니다.&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;- 이 앱에서는 사용자별 레시피 관리를 위해 로그인 기능이 필요했고, 이에 따라 백엔드 인프라(API 서버, 인증 서버, OAuth, SMTP, DB 사용자 관리 등) 다양한 인프라 구축과 배포, 그에 따른 관리 포인트가 늘어나는 문제가 있었습니다.&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1206&quot; data-origin-height=&quot;2622&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bghMfh/dJMb99MQuDW/oQQSZeLk5DxJuopDrSYia1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bghMfh/dJMb99MQuDW/oQQSZeLk5DxJuopDrSYia1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bghMfh/dJMb99MQuDW/oQQSZeLk5DxJuopDrSYia1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbghMfh%2FdJMb99MQuDW%2FoQQSZeLk5DxJuopDrSYia1%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;365&quot; height=&quot;794&quot; data-origin-width=&quot;1206&quot; data-origin-height=&quot;2622&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1206&quot; data-origin-height=&quot;2622&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/uGdwi/dJMb99MQuEK/nWKiLSZCi7CpK5iiYtpDk0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/uGdwi/dJMb99MQuEK/nWKiLSZCi7CpK5iiYtpDk0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/uGdwi/dJMb99MQuEK/nWKiLSZCi7CpK5iiYtpDk0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FuGdwi%2FdJMb99MQuEK%2FnWKiLSZCi7CpK5iiYtpDk0%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;425&quot; height=&quot;924&quot; data-origin-width=&quot;1206&quot; data-origin-height=&quot;2622&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1206&quot; data-origin-height=&quot;2622&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Ti8cv/dJMcad2K9c5/rHa0AsgpB0oe63awhQFhDk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Ti8cv/dJMcad2K9c5/rHa0AsgpB0oe63awhQFhDk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Ti8cv/dJMcad2K9c5/rHa0AsgpB0oe63awhQFhDk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FTi8cv%2FdJMcad2K9c5%2FrHa0AsgpB0oe63awhQFhDk%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;513&quot; height=&quot;1115&quot; data-origin-width=&quot;1206&quot; data-origin-height=&quot;2622&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;h2 data-ke-size=&quot;size26&quot;&gt;2) Supabase&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  Supabase&lt;/b&gt; &lt;br /&gt;&lt;br /&gt;&lt;b&gt;- &amp;lsquo;The Open Source Firebase Alternative&amp;rsquo;라는 슬로건을 내세운 오픈소스 백엔드 서비스(BaaS, Backend as a Service)입니다. &lt;/b&gt;&lt;br /&gt;- 구글의 Firebase와 유사한 기능을 제공하지만, NoSQL인 Firebase는 다르게 관계형 데이터베이스인 PostgreSQL을 기반으로 한다는 점이 가장 큰 차별점입니다. &lt;br /&gt;- Supabase의 주요한 기능으로는 PostgreSQL 데이터베이스, 사용자 인증, 즉시 사용 가능한 API, 엣지 함수, 실시간 구독, 스토리지 및 벡터 임베딩 등의 기능을 제공합니다.&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1400&quot; data-origin-height=&quot;670&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ceCS0p/dJMcabqpNX5/FKzyOXuslIY6kHgnVmc5I0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ceCS0p/dJMcabqpNX5/FKzyOXuslIY6kHgnVmc5I0/img.png&quot; data-alt=&quot;https://supabase.com/&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ceCS0p/dJMcabqpNX5/FKzyOXuslIY6kHgnVmc5I0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FceCS0p%2FdJMcabqpNX5%2FFKzyOXuslIY6kHgnVmc5I0%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;1400&quot; height=&quot;670&quot; data-origin-width=&quot;1400&quot; data-origin-height=&quot;670&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://supabase.com/&lt;/figcaption&gt;
&lt;/figure&gt;
&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;1. &lt;b&gt;백엔드 서비스(BaaS, Backend as a Service)&lt;/b&gt;&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  백엔드 서비스(BaaS, Backend as a Service)&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;-개발자가 개발에 필요한 백엔드 인프라(서버, DB, 인증, 스토리지, API 등)를 직접 구축하거나 관리하지 않고, 클라우드 업체가 제공하는 API를 통해서 필요한 기능을 API/SDK 형태로 즉시 사용을 하는 방식을 의미합니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1183&quot; data-origin-height=&quot;391&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c5TMZq/dJMcahD7tue/QdMKLD6KkbXz6oy2R70zl0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c5TMZq/dJMcahD7tue/QdMKLD6KkbXz6oy2R70zl0/img.png&quot; data-alt=&quot;https://www.back4app.com/backend-as-a-service-ko&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c5TMZq/dJMcahD7tue/QdMKLD6KkbXz6oy2R70zl0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc5TMZq%2FdJMcahD7tue%2FQdMKLD6KkbXz6oy2R70zl0%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;1183&quot; height=&quot;391&quot; data-origin-width=&quot;1183&quot; data-origin-height=&quot;391&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://www.back4app.com/backend-as-a-service-ko&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;table id=&quot;33216d47-b05b-80e4-8be7-ebfb2f0c6d89&quot; style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style2&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 32.7907%;&quot;&gt;분류&lt;/td&gt;
&lt;td style=&quot;width: 42.5582%;&quot;&gt;설명&lt;/td&gt;
&lt;td style=&quot;width: 24.5349%;&quot;&gt;&lt;b&gt;예시&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;33216d47-b05b-8040-90db-e7a9c9e40737&quot;&gt;
&lt;td id=&quot;@rIt&quot; style=&quot;width: 32.7907%;&quot;&gt;&lt;b&gt;BaaS (Backend as a Service)&lt;/b&gt;&lt;/td&gt;
&lt;td id=&quot;bI}J&quot; style=&quot;width: 42.5582%;&quot;&gt;&lt;b&gt;인증, DB, 스토리지 등 백엔드 기능을 API로 제공, 프론트엔드 개발에 집중 가능&lt;/b&gt;&lt;/td&gt;
&lt;td id=&quot;mL?}&quot; style=&quot;width: 24.5349%;&quot;&gt;&lt;b&gt;Firebase, Supabase, AWS Amplify&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;33216d47-b05b-8020-8fdd-fecf2e6b397a&quot;&gt;
&lt;td id=&quot;@rIt&quot; style=&quot;width: 32.7907%;&quot;&gt;SaaS (Software as a Service)&lt;/td&gt;
&lt;td id=&quot;bI}J&quot; style=&quot;width: 42.5582%;&quot;&gt;완성된 소프트웨어를 구독 형태로 제공&lt;/td&gt;
&lt;td id=&quot;mL?}&quot; style=&quot;width: 24.5349%;&quot;&gt;Gmail, Slack, Notion, Figma&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;33216d47-b05b-8061-b46d-e2afc7b37c21&quot;&gt;
&lt;td id=&quot;@rIt&quot; style=&quot;width: 32.7907%;&quot;&gt;IaaS (Infrastructure as a Service)&lt;/td&gt;
&lt;td id=&quot;bI}J&quot; style=&quot;width: 42.5582%;&quot;&gt;서버, 스토리지, 네트워크 등 인프라를 제공&lt;/td&gt;
&lt;td id=&quot;mL?}&quot; style=&quot;width: 24.5349%;&quot;&gt;AWS EC2, Azure VM, GCP Compute Engine&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;33216d47-b05b-80e4-a50e-ca63637ea596&quot;&gt;
&lt;td id=&quot;@rIt&quot; style=&quot;width: 32.7907%;&quot;&gt;PaaS (Platform as a Service)&lt;/td&gt;
&lt;td id=&quot;bI}J&quot; style=&quot;width: 42.5582%;&quot;&gt;개발 플랫폼(런타임, DB 등)을 제공, 인프라 관리 불필요&lt;/td&gt;
&lt;td id=&quot;mL?}&quot; style=&quot;width: 24.5349%;&quot;&gt;Heroku, Google App Engine, AWS Elastic Beanstalk&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;h3 data-ke-size=&quot;size23&quot;&gt;2. Supabase Price&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  Supabase Price&lt;/b&gt;&lt;br /&gt;&lt;b&gt;&lt;br /&gt;- BaaS, Backend as a Service 플랫폼을 사용하는 데의 Price &amp;amp; Fee입니다.&lt;/b&gt; &lt;br /&gt;- Free Plan을 사용하더라도, 최대 MAU(Monthly Active Users)를 50,000명까지 수용할 수 있으며, DB 스토리지도 500MB로 사용자가 없는 초기 운영에서 사용해 볼 수 있다는 점이 좋았던 것 같습니다.&lt;br /&gt;- 추후 Pro Plan을 구독한다면 월간 25달러(약 37,000원)의 가격에 MAU(Monthly Active Users) 10만까지 커버가 가능하며 필요에 따라 스케일 업을 할 수 있다는 점이 좋다고 판단되었습니다.&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1151&quot; data-origin-height=&quot;830&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nD4BX/dJMcaf7nxbJ/A8ZJOt5t3ZwcQOnW41GmY0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nD4BX/dJMcaf7nxbJ/A8ZJOt5t3ZwcQOnW41GmY0/img.png&quot; data-alt=&quot;https://supabase.com/pricing&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nD4BX/dJMcaf7nxbJ/A8ZJOt5t3ZwcQOnW41GmY0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnD4BX%2FdJMcaf7nxbJ%2FA8ZJOt5t3ZwcQOnW41GmY0%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;1151&quot; height=&quot;830&quot; data-origin-width=&quot;1151&quot; data-origin-height=&quot;830&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://supabase.com/pricing&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;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;[ 더 알아보기 ]&lt;/b&gt;&lt;br /&gt;&lt;b&gt;&lt;br /&gt; &amp;nbsp;MAU(Monthly Active Users)은 어떻게 측정이 될까?&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 공식 사이트에서는 로그인하거나 토큰(JWT)을 갱신하는 고유 사용자 수에 따라 요금이 부과된다고 합니다. &lt;/b&gt;각 고유 사용자는 인증 횟수와 관계없이 청구 주기당 한 번만 계산이 됩니다.&lt;br /&gt;- 예를 들어서, 청구 주기는 1월 1일부터 1월 31일까지라고 가정했을 때, 사용자 1(User-1)이 여러 번 로그인했더라도 이번 청구 주기에서는 단일 월간 활성 사용자(MAU)로 계산됩니다.&lt;/blockquote&gt;
&lt;figure id=&quot;og_1776759505069&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;Manage Monthly Active Users usage | Supabase Docs&quot; data-og-description=&quot;The count resets at the start of each billing cycle.&quot; data-og-host=&quot;supabase.com&quot; data-og-source-url=&quot;https://supabase.com/docs/guides/platform/manage-your-usage/monthly-active-users&quot; data-og-url=&quot;https://supabase.com/docs/guides/platform/manage-your-usage/monthly-active-users&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/Yfthi/dJMb8SpJ8tC/RwD1l4OE6hn1JPKCfdNZh0/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/o1fd8/dJMb8T91F3M/ijS2XmN1pcYaYjTw9nhBik/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/Drp36/dJMb9dHqkWI/9agckuRvCiszReyIzOPbC1/img.png?width=2040&amp;amp;height=878&amp;amp;face=0_0_2040_878&quot;&gt;&lt;a href=&quot;https://supabase.com/docs/guides/platform/manage-your-usage/monthly-active-users&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://supabase.com/docs/guides/platform/manage-your-usage/monthly-active-users&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/Yfthi/dJMb8SpJ8tC/RwD1l4OE6hn1JPKCfdNZh0/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/o1fd8/dJMb8T91F3M/ijS2XmN1pcYaYjTw9nhBik/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/Drp36/dJMb9dHqkWI/9agckuRvCiszReyIzOPbC1/img.png?width=2040&amp;amp;height=878&amp;amp;face=0_0_2040_878');&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;Manage Monthly Active Users usage | Supabase Docs&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;The count resets at the start of each billing cycle.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;supabase.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;h3 data-ke-size=&quot;size23&quot;&gt;2. Firebase vs Supabase&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  Firebase vs Supabase&lt;/b&gt;&lt;br /&gt;&lt;b&gt;&lt;br /&gt;- 대표적인 BaaS (Backend as a Service) 플랫폼인 Firebase와의 차이점을 확인해 봅니다.&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;- Firebase는 구글에서 제공하는 모바일 및 웹 애플리케이션 개발 플랫폼입니다.&lt;br /&gt;- 개발자가 인프라 관리나 서버 측 로직에 쏟는 시간을 줄이고, 클라이언트 앱의 기능과 사용자 경험(UX)에 집중할 수 있도록 돕는 BaaS(Backend as a Service) 모델의 대표 주자입니다.&amp;nbsp;&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;733&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bVwu23/dJMcaipwCe9/LnNcLBgEdPDriYfX1va0L1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bVwu23/dJMcaipwCe9/LnNcLBgEdPDriYfX1va0L1/img.png&quot; data-alt=&quot;https://hanbin-sla.tistory.com/entry/Supabase-vs-Firebase&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bVwu23/dJMcaipwCe9/LnNcLBgEdPDriYfX1va0L1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbVwu23%2FdJMcaipwCe9%2FLnNcLBgEdPDriYfX1va0L1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2000&quot; height=&quot;733&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;733&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://hanbin-sla.tistory.com/entry/Supabase-vs-Firebase&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 290px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;항목&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;Supabase&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;Firebase&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;&lt;b&gt;소유&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;Supabase Inc. (오픈소스)&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;Google (proprietary)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;&lt;b&gt;기반&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;PostgreSQL (SQL)&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;NoSQL (Firestore)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;&lt;b&gt;트랜잭션&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;완전 지원&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;제한적&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;&lt;b&gt;관계형 데이터 / JOIN&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;O&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;X&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;&lt;b&gt;라이센스&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;오픈소스(Apache 2.0, MIT)&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;SDK(Apache 2.0), 서버(비공개)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;&lt;b&gt;RLS(Row Level Security)&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;PostgreSQL RLS 네이티브&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;Security Rules (별도 문법)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;&lt;b&gt;실시간 동기화&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;O&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;O&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;&lt;b&gt;인증 (Auth)&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;O&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;O&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;&lt;b&gt;스토리지&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;Supabase Storage&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;Firebase Storage&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;&lt;b&gt;서버리스 함수&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;Edge Functions (Deno)&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;Cloud Functions (Node.js)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;&lt;b&gt;벡터 DB (AI)&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;pgvector 내장 지원&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;별도 연동 필요&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;&lt;b&gt;자체 호스팅&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;가능 (Docker)&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;불가&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;&lt;b&gt;가격 방식&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;고정 ($25/월~)&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;종량제&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;&lt;b&gt;무료 플랜&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;O&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;O&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;blockquote data-ke-style=&quot;style3&quot;&gt;[ 더 알아보기 ]&amp;nbsp;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;  그래서 무엇을 사용하는 것이 좋을까?&lt;/b&gt;&lt;br /&gt;&lt;b&gt;1. Supabase&lt;/b&gt;&lt;br /&gt;- SQL/관계형 DB 선호, 자체 호스팅, 오픈소스, AI 기능이 필요한 프로젝트에 적합&amp;nbsp;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;2. Firebase&lt;/b&gt; &lt;br /&gt;- 빠른 MVP, 실시간 기능, 모바일 앱, Google 생태계와 깊게 연동된 프로젝트에 적합&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3) Supabase 서비스 기능&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 관계형 데이터 베이스: Database(PostgreSQL)&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  관계형 데이터 베이스: Database(PostgreSQL)&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 완전 관리형으로 단순히 DB 래핑이 아닌 PostgreSQL 그 자체를 제공합니다.&lt;br /&gt;- 벤더 종속 없이 표준 SQL 그대로 사용 가능하며, PostgreSQL의 모든 기능 (트리거, 함수, 뷰, 인덱스 등) 사용이 가능합니다.&lt;/b&gt;&lt;br /&gt;- 데이터 간의 관계 설정(Foreign Key)이나 복잡한 쿼리 처리에 매우 강력합니다.&amp;nbsp;&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1161&quot; data-origin-height=&quot;604&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/FKT3a/dJMcahYrgPQ/28dYSlaRuf67kkizliCBw0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/FKT3a/dJMcahYrgPQ/28dYSlaRuf67kkizliCBw0/img.png&quot; data-alt=&quot;https://supabase.com/database&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/FKT3a/dJMcahYrgPQ/28dYSlaRuf67kkizliCBw0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FFKT3a%2FdJMcahYrgPQ%2F28dYSlaRuf67kkizliCBw0%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;1161&quot; height=&quot;604&quot; data-origin-width=&quot;1161&quot; data-origin-height=&quot;604&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://supabase.com/database&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;[ 더 알아보기 ]&lt;/b&gt;&lt;br /&gt;&lt;b&gt;&lt;br /&gt; &amp;nbsp;완전 관리형 PostgreSQL은 뭘 의미하는 걸까?&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;- 설치 및 설정, 백업, 업데이트/패치, 확장, 가용성에 대해서 직접 구성하고 관리를 하는 것에 대해서 클라우드사에서 이를 관리하고 처리하게끔 지원을 하는 것을 의미합니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style2&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;구분&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;직접 관리 (Self-hosted)&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;완전 관리형 (Managed)&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;설치 및 설정&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;OS 설치, 포트 설정, 방화벽 구성&lt;/td&gt;
&lt;td&gt;&lt;b&gt;클릭 한 번으로 생성 완료&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;백업 (Backup)&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;스케줄러 짜고 저장소 관리 필요&lt;/td&gt;
&lt;td&gt;&lt;b&gt;자동 백업 및 시점 복구 지원&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;업데이트/패치&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;보안 취약점 체크 및 직접 업데이트&lt;/td&gt;
&lt;td&gt;&lt;b&gt;클라우드사가 자동으로 최신 패치&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;확장 (Scaling)&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;서버 사양 수동 교체, 데이터 이전&lt;/td&gt;
&lt;td&gt;&lt;b&gt;콘솔에서 사양 클릭 시 즉시 확장&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;가용성 (HA)&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;서버 죽으면 직접 복구해야 함&lt;/td&gt;
&lt;td&gt;&lt;b&gt;다중화 구성을 통해 99.9% 가용성 보장&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;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  관리형 데이터베이스 아키텍처 구조입니다.&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;&lt;b&gt;- KONG API Gateway를 통해서 각각에 맞게 라우팅을 해주며, Supabase가 직접 만든 서비스가 아닌 PostgreSQL + 오픈소스 툴 조합 + 관리 플랫폼을 의미합니다.&lt;br /&gt;&lt;/b&gt;&lt;/b&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1600&quot; data-origin-height=&quot;767&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/NclDv/dJMcahRE6HI/YKRX1JZycqBrWcuU35nC8k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/NclDv/dJMcahRE6HI/YKRX1JZycqBrWcuU35nC8k/img.png&quot; data-alt=&quot;https://supabase.com/docs/guides/self-hosting/docker&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/NclDv/dJMcahRE6HI/YKRX1JZycqBrWcuU35nC8k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FNclDv%2FdJMcahRE6HI%2FYKRX1JZycqBrWcuU35nC8k%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;1600&quot; height=&quot;767&quot; data-origin-width=&quot;1600&quot; data-origin-height=&quot;767&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://supabase.com/docs/guides/self-hosting/docker&lt;/figcaption&gt;
&lt;/figure&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;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  Supabase 관계형 데이터베이스 둘러보기 -1: Database &amp;amp; Table&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 아래와 같이 일반 데이터베이스와 같이 Supabase 내에 데이터베이스, 테이블을 두고 데이터 관리가 가능합니다.&amp;nbsp;&lt;/b&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1877&quot; data-origin-height=&quot;748&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c4uFWD/dJMcaffiFc7/xbA4lrwy1AZNMgh5pFj94K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c4uFWD/dJMcaffiFc7/xbA4lrwy1AZNMgh5pFj94K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c4uFWD/dJMcaffiFc7/xbA4lrwy1AZNMgh5pFj94K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc4uFWD%2FdJMcaffiFc7%2FxbA4lrwy1AZNMgh5pFj94K%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;1877&quot; height=&quot;748&quot; data-origin-width=&quot;1877&quot; data-origin-height=&quot;748&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;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  Supabase 관계형 데이터베이스 둘러보기 -2: Supabase Schema Visualizer&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;- 아래와 같이 Supabase Schema Visualizer 기능으로 테이블을 E-R Diagram 형태로 가시적으로 확인이 가능합니다.&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1329&quot; data-origin-height=&quot;835&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/sQ3EH/dJMcafTSeBC/ZqavWiKwrW5WqyDvnpAmYK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/sQ3EH/dJMcafTSeBC/ZqavWiKwrW5WqyDvnpAmYK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/sQ3EH/dJMcafTSeBC/ZqavWiKwrW5WqyDvnpAmYK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FsQ3EH%2FdJMcafTSeBC%2FZqavWiKwrW5WqyDvnpAmYK%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;1329&quot; height=&quot;835&quot; data-origin-width=&quot;1329&quot; data-origin-height=&quot;835&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;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  Supabase 관계형 데이터베이스 둘러보기 -3: IDEA UI&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 테이블 데이터도 IDEA와 같이 조회할 수 있습니다.&amp;nbsp;&lt;/b&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;&lt;/span&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1447&quot; data-origin-height=&quot;752&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mau9U/dJMcahRE6Ko/fO3pWRKKKvRm77qQVf7Gg0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mau9U/dJMcahRE6Ko/fO3pWRKKKvRm77qQVf7Gg0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mau9U/dJMcahRE6Ko/fO3pWRKKKvRm77qQVf7Gg0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fmau9U%2FdJMcahRE6Ko%2FfO3pWRKKKvRm77qQVf7Gg0%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;1447&quot; height=&quot;752&quot; data-origin-width=&quot;1447&quot; data-origin-height=&quot;752&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;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  Supabase 관계형 데이터베이스 둘러보기 -4: IDEA UI&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;- 아래와 같이 SQL문을 통해서 테이블 데이터 조회도 가능합니다.&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1911&quot; data-origin-height=&quot;778&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bm3qMt/dJMcacixwRI/w5KG2TuCVutp2gMDgVRD7k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bm3qMt/dJMcacixwRI/w5KG2TuCVutp2gMDgVRD7k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bm3qMt/dJMcacixwRI/w5KG2TuCVutp2gMDgVRD7k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbm3qMt%2FdJMcacixwRI%2Fw5KG2TuCVutp2gMDgVRD7k%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;1911&quot; height=&quot;778&quot; data-origin-width=&quot;1911&quot; data-origin-height=&quot;778&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;h3 data-ke-size=&quot;size23&quot;&gt;2. 인증 : Authentication&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  인증 : Authentication&lt;/b&gt;&lt;br /&gt;&lt;b&gt;&lt;br /&gt;- Supabase의 인증은 JWT 기반 인증 시스템으로, Auth 스키마에 유저 정보를 저장하고 관리합니다. Row Level Security(RLS)와 긴밀하게 연동이 되는 서비스입니다.&lt;/b&gt;&lt;br /&gt;- 익명 로그인에서 이메일/비밀번호 기반의 일반 로그인은 물론,&amp;nbsp;Google, GitHub, Apple 등 다양한 소셜 로그인(OAuth)을 지원합니다. 세션 관리와 사용자 보안을 자동으로 처리하는 것을 제공합니다.&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1125&quot; data-origin-height=&quot;833&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bOlHYa/dJMcagrGYSL/veKdKTL57uPlVcIcbnWEsK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bOlHYa/dJMcagrGYSL/veKdKTL57uPlVcIcbnWEsK/img.png&quot; data-alt=&quot;https://supabase.com/auth&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bOlHYa/dJMcagrGYSL/veKdKTL57uPlVcIcbnWEsK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbOlHYa%2FdJMcagrGYSL%2FveKdKTL57uPlVcIcbnWEsK%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;1125&quot; height=&quot;833&quot; data-origin-width=&quot;1125&quot; data-origin-height=&quot;833&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://supabase.com/auth&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;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2.1. 일반 로그인&lt;/h4&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;style3&quot;&gt;&lt;b&gt;  일반 로그인&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- Supabase에서 제공하는 일반 로그인 인증 방식으로는 아래와 같은 방식들을 지원합니다.&amp;nbsp;&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&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&gt;&lt;b&gt;방식&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;설명&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;익명 로그인&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;UUID만 발급, 이메일이 없는 로그인 방식&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;이메일 + 비밀번호&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;전통적인 이메일/패스워드 로그인 방식&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Magic Link&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;이메일로 링크 전송 &amp;rarr; 클릭으로 로그인 방식&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;이메일 OTP&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;이메일로 6자리 코드 전송 로그인 방식&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;SMS/전화 OTP&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;문자로 6자리 코드 전송&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Web3&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Ethereum / Solana 지갑 서명&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;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  기본적으로 로그인을 수행하는 경우&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 로그인은 JWT를 기반으로 인증되는 시스템으로 Supabase에서는 사용자의 인증을 수행하면 Session이 발급이 됩니다. 이러한 세션은 Access Token, Refresh Token으로 구성이 되어 있으며 이를 통해 사용자의 유효 세션이 관리가 됩니다.&lt;/b&gt;&lt;br /&gt;- 각각 사용자의 세션 유효 시간에 대해서도 Supabase 대시보다 내에서도 관리가 가능합니다.&amp;nbsp;&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1164&quot; data-origin-height=&quot;91&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/YHB6P/dJMcaiwfTud/44kWGqoKSEeAEH2D8hWJu0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/YHB6P/dJMcaiwfTud/44kWGqoKSEeAEH2D8hWJu0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/YHB6P/dJMcaiwfTud/44kWGqoKSEeAEH2D8hWJu0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FYHB6P%2FdJMcaiwfTud%2F44kWGqoKSEeAEH2D8hWJu0%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;1164&quot; height=&quot;91&quot; data-origin-width=&quot;1164&quot; data-origin-height=&quot;91&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;926&quot; data-origin-height=&quot;696&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bHT533/dJMcaiwfTuA/lYfo845IRCcpRI9ObJ9Up0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bHT533/dJMcaiwfTuA/lYfo845IRCcpRI9ObJ9Up0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bHT533/dJMcaiwfTuA/lYfo845IRCcpRI9ObJ9Up0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbHT533%2FdJMcaiwfTuA%2FlYfo845IRCcpRI9ObJ9Up0%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;926&quot; height=&quot;696&quot; data-origin-width=&quot;926&quot; data-origin-height=&quot;696&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;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  사용자 UID&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 사용자가 회원가입을 하게 되면 auth.users 테이블에 사용자 정보가 저장이 됩니다. 그리고 이 사용자는 각각의 고유한 UID를 가지게 됩니다.&lt;/b&gt;&lt;br /&gt;- 이메일/패스워드 회원가입 후 생성, OAuth 로그인 후 생성, Masic Link 링크 선택 시 생성 등에 해당 UID가 발급이 됩니다.&amp;nbsp;&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1921&quot; data-origin-height=&quot;829&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bjrx1Y/dJMcadPdzzJ/ysCPji9LUAucQYwsr1YUc1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bjrx1Y/dJMcadPdzzJ/ysCPji9LUAucQYwsr1YUc1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bjrx1Y/dJMcadPdzzJ/ysCPji9LUAucQYwsr1YUc1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbjrx1Y%2FdJMcadPdzzJ%2FysCPji9LUAucQYwsr1YUc1%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;1921&quot; height=&quot;829&quot; data-origin-width=&quot;1921&quot; data-origin-height=&quot;829&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;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;[활용 예시]&lt;/b&gt; &lt;br /&gt;&lt;br /&gt;- 아래와 같이 이메일/비밀번호를 기반으로 하는 회원가입의 경우, BaaS를 이용하지 않는 경우는 사용자 DB, Security, SMTP 전송 프로세스, 이메일 OTP 프로세스, 이메일 템플릿 구성에 대한 추가적인 개발이 필요하지만, Supabase 내에서는 회원가입을 위한, Email 전송을 위한 메서드 호출만 하면 됩니다.&lt;br /&gt;&lt;br /&gt;- 실제적으로 관여한 Back-end 구축 및 환경 구축은 하나도 사용하지 않았습니다.&amp;nbsp;&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1206&quot; data-origin-height=&quot;2622&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cBOF9U/dJMcahqCMG1/QWoufYkPUbkaO8FY0hykD1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cBOF9U/dJMcahqCMG1/QWoufYkPUbkaO8FY0hykD1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cBOF9U/dJMcahqCMG1/QWoufYkPUbkaO8FY0hykD1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcBOF9U%2FdJMcahqCMG1%2FQWoufYkPUbkaO8FY0hykD1%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;697&quot; height=&quot;1515&quot; data-origin-width=&quot;1206&quot; data-origin-height=&quot;2622&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1206&quot; data-origin-height=&quot;2622&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/SOu0M/dJMcacv5KZx/JYmHKbExCnIOWTaM5Zjfu0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/SOu0M/dJMcacv5KZx/JYmHKbExCnIOWTaM5Zjfu0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/SOu0M/dJMcacv5KZx/JYmHKbExCnIOWTaM5Zjfu0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FSOu0M%2FdJMcacv5KZx%2FJYmHKbExCnIOWTaM5Zjfu0%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;616&quot; height=&quot;1339&quot; data-origin-width=&quot;1206&quot; data-origin-height=&quot;2622&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1206&quot; data-origin-height=&quot;2622&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ULG7R/dJMcacv5KZ7/KoAtEfgDxrTOE1qSK0Ymw1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ULG7R/dJMcacv5KZ7/KoAtEfgDxrTOE1qSK0Ymw1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ULG7R/dJMcacv5KZ7/KoAtEfgDxrTOE1qSK0Ymw1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FULG7R%2FdJMcacv5KZ7%2FKoAtEfgDxrTOE1qSK0Ymw1%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;669&quot; height=&quot;1454&quot; data-origin-width=&quot;1206&quot; data-origin-height=&quot;2622&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;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  실제적으로 아래와 같이 회원가입이 완료가 되면 Auth.users 테이블에서 관리가 됩니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1919&quot; data-origin-height=&quot;613&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/clso58/dJMcagkUZ8a/FOYeLeTskzGnPc5BzjIaLK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/clso58/dJMcagkUZ8a/FOYeLeTskzGnPc5BzjIaLK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/clso58/dJMcagkUZ8a/FOYeLeTskzGnPc5BzjIaLK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fclso58%2FdJMcagkUZ8a%2FFOYeLeTskzGnPc5BzjIaLK%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;1919&quot; height=&quot;613&quot; data-origin-width=&quot;1919&quot; data-origin-height=&quot;613&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;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;[ 더 알아보기]&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt; &amp;nbsp;일반 로그인을 할 때, 이메일 회원가입이 있던데? 그럼 이메일 전송을 위한 SMTP까지 제공해 주는 것인가?&lt;/b&gt; &lt;br /&gt;&lt;br /&gt;- Supabase에서 제공해 주는 noreply@mail.supabase.io 형태나 커스텀으로 내 이메일을 구성하여서 전송이 가능합니다. 또한, AWS SES, SendGrid, Mailgun, Resend과 연동이 가능합니다.&lt;br /&gt;&lt;br /&gt;- Custom Email 연결하면 하루 500건이 무료라고 합니다.&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1671&quot; data-origin-height=&quot;812&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/QqzU1/dJMcagkU1lv/zyneNq4pqCl1VhadH5xSO1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/QqzU1/dJMcagkU1lv/zyneNq4pqCl1VhadH5xSO1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/QqzU1/dJMcagkU1lv/zyneNq4pqCl1VhadH5xSO1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FQqzU1%2FdJMcagkU1lv%2FzyneNq4pqCl1VhadH5xSO1%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;1671&quot; height=&quot;812&quot; data-origin-width=&quot;1671&quot; data-origin-height=&quot;812&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;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  아래와 같이 이메일 템플릿도 지정이 가능합니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1908&quot; data-origin-height=&quot;835&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/btw2db/dJMcaaE2Xcw/hXkcQCq0XnxHZzr7fxcQqk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/btw2db/dJMcaaE2Xcw/hXkcQCq0XnxHZzr7fxcQqk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/btw2db/dJMcaaE2Xcw/hXkcQCq0XnxHZzr7fxcQqk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbtw2db%2FdJMcaaE2Xcw%2FhXkcQCq0XnxHZzr7fxcQqk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1908&quot; height=&quot;835&quot; data-origin-width=&quot;1908&quot; data-origin-height=&quot;835&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;h4 data-ke-size=&quot;size20&quot;&gt;2.2. 소셜 로그인&lt;/h4&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;style3&quot;&gt;&lt;b&gt;  소셜 로그인&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- Supabase에서 제공하는 소셜 로그인입니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  아래와 같은 다양한 Provider를 통해 인증(Authentication)을 위임하여 간편하게 가입 및 로그인을 수행할 수 있습니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;941&quot; data-origin-height=&quot;540&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/xbCvU/dJMcab4Zs8m/q3HapjwZ0t4P6K3gwQ7teK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/xbCvU/dJMcab4Zs8m/q3HapjwZ0t4P6K3gwQ7teK/img.png&quot; data-alt=&quot;https://supabase.com/features/social-login&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/xbCvU/dJMcab4Zs8m/q3HapjwZ0t4P6K3gwQ7teK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FxbCvU%2FdJMcab4Zs8m%2Fq3HapjwZ0t4P6K3gwQ7teK%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;941&quot; height=&quot;540&quot; data-origin-width=&quot;941&quot; data-origin-height=&quot;540&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://supabase.com/features/social-login&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  Spring Boot OAuth 2.0 프로세스를 비교하였을 때, Supabase 프로세스입니다.&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 기존 Spring Boot OAuth 2.0 내에서는 소셜 로그인을 위해서 Spring Security 기반 Auth Code 교환, JWT 발급, 검증, Refresh Token 처리, 클라이언트로 Redirect 과정을 처리해야 했습니다.&lt;/b&gt;&lt;br /&gt;- Supabase 환경에서는 인증의 복잡한 부분(OAuth 중개, JWT 발급, 유저 관리)은 Supabase에 위임하여 signInWithOAuth() 함수 호출로 이를 대체할 수 있습니다.&amp;nbsp;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  Spring Boot OAuth 2.0 프로세스&lt;/b&gt;&lt;/blockquote&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;1185&quot; data-origin-height=&quot;692&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cRW5ax/dJMcacW7Fey/4tftduKCQInc0ukJg3XTSk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cRW5ax/dJMcacW7Fey/4tftduKCQInc0ukJg3XTSk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cRW5ax/dJMcacW7Fey/4tftduKCQInc0ukJg3XTSk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcRW5ax%2FdJMcacW7Fey%2F4tftduKCQInc0ukJg3XTSk%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;1185&quot; height=&quot;692&quot; data-origin-width=&quot;1185&quot; data-origin-height=&quot;692&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;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  Supabase 프로세스&amp;nbsp;&lt;/b&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;661&quot; data-origin-height=&quot;769&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cje689/dJMcaiC2hvZ/6j2RpWKQkDbf88JeJ2zaO1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cje689/dJMcaiC2hvZ/6j2RpWKQkDbf88JeJ2zaO1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cje689/dJMcaiC2hvZ/6j2RpWKQkDbf88JeJ2zaO1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcje689%2FdJMcaiC2hvZ%2F6j2RpWKQkDbf88JeJ2zaO1%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;661&quot; height=&quot;769&quot; data-origin-width=&quot;661&quot; data-origin-height=&quot;769&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style2&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;항목&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;Spring Boot OAuth&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;Supabase OAuth&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;token 교환 주체&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Client (백엔드)&lt;/td&gt;
&lt;td&gt;Supabase Auth 서버&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;PKCE 처리&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;직접 구현&lt;/td&gt;
&lt;td&gt;자동 처리&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;사용자 DB 저장&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;직접 구현&lt;/td&gt;
&lt;td&gt;auth.users 자동 저장&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Session 발급&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;JWT 직접 생성&lt;/td&gt;
&lt;td&gt;Supabase Session 자동 발급&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;프론트 호출&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;signInWithOAuth() 한 줄&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. 엣지 함수: Edge Functions&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  엣지 함수: Edge Functions&lt;/b&gt;&lt;br /&gt;&lt;b&gt;&lt;br /&gt;- Supabase에서 제공하는 서버리스 함수 실행 환경으로, Deno 런타임 기반으로 동작하며 전 세계 엣지 서버에서 실행됩니다.&lt;/b&gt;&lt;br /&gt;- Node.js를 지원하여 100만 개 이상의 NPM 모듈 지원을 통해 함수를 구성할 수 있습니다.&lt;br /&gt;- TypeScript/JavaScript 기본 지원하며, Javascript V8 엔진 기반으로 빠른 콜드 스타트를 제공합니다.&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1197&quot; data-origin-height=&quot;728&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dHPYAW/dJMcagyrFN2/fAcAHfCVeKmTSRhi2BhO2k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dHPYAW/dJMcagyrFN2/fAcAHfCVeKmTSRhi2BhO2k/img.png&quot; data-alt=&quot;https://supabase.com/edge-functions&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dHPYAW/dJMcagyrFN2/fAcAHfCVeKmTSRhi2BhO2k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdHPYAW%2FdJMcagyrFN2%2FfAcAHfCVeKmTSRhi2BhO2k%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;1197&quot; height=&quot;728&quot; data-origin-width=&quot;1197&quot; data-origin-height=&quot;728&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://supabase.com/edge-functions&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;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  아래와 같이 각각 Edge Function를 구성을 합니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2032&quot; data-origin-height=&quot;1081&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pzcIw/dJMcabDVS0A/kP36iRC71bdD56ixbRAfPK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pzcIw/dJMcabDVS0A/kP36iRC71bdD56ixbRAfPK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pzcIw/dJMcabDVS0A/kP36iRC71bdD56ixbRAfPK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpzcIw%2FdJMcabDVS0A%2FkP36iRC71bdD56ixbRAfPK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2032&quot; height=&quot;1081&quot; data-origin-width=&quot;2032&quot; data-origin-height=&quot;1081&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;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  아래와 같이 TypeScript/JavaScript을 지원하여서 코드 구성이 가능합니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2032&quot; data-origin-height=&quot;1081&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/BGI14/dJMcacbLxuu/yLYDGpy7EDO8spIQ1z1Enk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/BGI14/dJMcacbLxuu/yLYDGpy7EDO8spIQ1z1Enk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/BGI14/dJMcacbLxuu/yLYDGpy7EDO8spIQ1z1Enk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FBGI14%2FdJMcacbLxuu%2FyLYDGpy7EDO8spIQ1z1Enk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2032&quot; height=&quot;1081&quot; data-origin-width=&quot;2032&quot; data-origin-height=&quot;1081&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;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  아래와 같이 Javascritpt에 연결하여 호출하는 방법도 제공해 줍니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2032&quot; data-origin-height=&quot;1081&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/yJeHY/dJMcagea8We/J6XA9UByIhdGKuWMnGckc0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/yJeHY/dJMcagea8We/J6XA9UByIhdGKuWMnGckc0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/yJeHY/dJMcagea8We/J6XA9UByIhdGKuWMnGckc0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FyJeHY%2FdJMcagea8We%2FJ6XA9UByIhdGKuWMnGckc0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2032&quot; height=&quot;1081&quot; data-origin-width=&quot;2032&quot; data-origin-height=&quot;1081&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;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;&amp;nbsp;  또한 추가적으로, 진행 중인 Edge Function에 대해서도 모니터링을 제공합니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1147&quot; data-origin-height=&quot;549&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bBXNly/dJMcahKVvTH/oobhE934NDGuf2Ooup4VUk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bBXNly/dJMcahKVvTH/oobhE934NDGuf2Ooup4VUk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bBXNly/dJMcahKVvTH/oobhE934NDGuf2Ooup4VUk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbBXNly%2FdJMcahKVvTH%2FoobhE934NDGuf2Ooup4VUk%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;1147&quot; height=&quot;549&quot; data-origin-width=&quot;1147&quot; data-origin-height=&quot;549&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;h3 data-ke-size=&quot;size23&quot;&gt;4. 실시간 데이터 처리 : Realtime&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  실시간 데이터 처리 : Realtime&lt;/b&gt;&lt;br /&gt;&lt;b&gt;&lt;br /&gt;- Supabase Realtime은 데이터베이스 변경사항을 WebSocket으로 실시간 전달하는 기능을 의미합니다.&lt;/b&gt; &lt;br /&gt;- 데이터베이스의 변경 사항(Insert, Update, Delete)을 클라이언트에 실시간으로 푸시해 줍니다. 채팅이나 실시간 알림 기능을 만들 때 유용합니다.&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1209&quot; data-origin-height=&quot;564&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dztBq0/dJMcaaruYhM/vf3F3SLdgBNgfFS4t3IO50/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dztBq0/dJMcaaruYhM/vf3F3SLdgBNgfFS4t3IO50/img.png&quot; data-alt=&quot;https://supabase.com/realtime&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dztBq0/dJMcaaruYhM/vf3F3SLdgBNgfFS4t3IO50/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdztBq0%2FdJMcaaruYhM%2Fvf3F3SLdgBNgfFS4t3IO50%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;1209&quot; height=&quot;564&quot; data-origin-width=&quot;1209&quot; data-origin-height=&quot;564&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://supabase.com/realtime&lt;/figcaption&gt;
&lt;/figure&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;4.1. 실시간 데이터 처리 프로세스&lt;/h4&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;style3&quot;&gt;&lt;b&gt;  실시간 데이터 처리 프로세스&lt;/b&gt;&lt;br /&gt;&lt;b&gt;&lt;br /&gt;- 앱 사용자 A, B, C가 있고 이들이 동일한 채팅방에서 채팅을 한다는 가정하에 있습니다. 사용자 A, B, C는 &amp;lsquo;open-chat&amp;rsquo;이라는 채널을 구독(subscribe)하고 있습니다.&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;1.&amp;nbsp; 사용자 A는 채팅을 위해서 &amp;ldquo;안녕하세요&amp;rdquo;라는 텍스트를 입력합니다.&lt;br /&gt;2. 이 텍스트 데이터 row는 Supabase DB에 저장이 됩니다.&lt;br /&gt;3. 저장이 되면 이를 감지하여 Supabase Realtime에서는 &amp;lsquo;open-chat&amp;rsquo; 채널로 Broadcast를 하여서 구독 중인 수신자인 사용자 B, C에게 전달을 합니다.&lt;br /&gt;&lt;br /&gt;- WebSocket에서 Publisher(발행), Subscriber(구독)의 개념이 아닌 DB INSERT로 Publish(발행)가 된다는 점이었습니다.&lt;/blockquote&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;1102&quot; data-origin-height=&quot;599&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bbIekn/dJMcafzzNNB/0hkFTrqTAUkVc8TbBoEVS1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bbIekn/dJMcafzzNNB/0hkFTrqTAUkVc8TbBoEVS1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bbIekn/dJMcafzzNNB/0hkFTrqTAUkVc8TbBoEVS1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbbIekn%2FdJMcafzzNNB%2F0hkFTrqTAUkVc8TbBoEVS1%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;1102&quot; height=&quot;599&quot; data-origin-width=&quot;1102&quot; data-origin-height=&quot;599&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;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;[Realtime 서비스를 활용한 채팅 구현 사례]&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 아래와 같이 로그인을 수행한 사용자를 기반으로 전체 채팅방을 구현을 하였습니다.&lt;/b&gt; &lt;br /&gt;- 채팅방을 구현을 위해서 Publisher(발행자)는 채널에 접근하고, Message라는 테이블 내에 데이터를 INSERT를 하고, Subscriber(구독자)는 채널에 접근하여 구독을 하면, 서로 실시간 채팅방이 구현이 되었습니다. &lt;br /&gt;- 만약 이를 이용하지 않았다면, 별도의 소켓서버, Redis 구축 등 다양한 환경을 구축해야 헀습니다.&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;640&quot; data-origin-height=&quot;303&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/xaLLk/dJMb99MQxcO/pzkKJEhFsHzhtG4LyJQbv0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/xaLLk/dJMb99MQxcO/pzkKJEhFsHzhtG4LyJQbv0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/xaLLk/dJMb99MQxcO/pzkKJEhFsHzhtG4LyJQbv0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FxaLLk%2FdJMb99MQxcO%2FpzkKJEhFsHzhtG4LyJQbv0%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;902&quot; height=&quot;427&quot; data-origin-width=&quot;640&quot; data-origin-height=&quot;303&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;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  사용자 A가 채팅으로 입력을 합니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;640&quot; data-origin-height=&quot;319&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bvDwj9/dJMcaaLNvTr/3lewGZnYD2swplKOKNGxok/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bvDwj9/dJMcaaLNvTr/3lewGZnYD2swplKOKNGxok/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bvDwj9/dJMcaaLNvTr/3lewGZnYD2swplKOKNGxok/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbvDwj9%2FdJMcaaLNvTr%2F3lewGZnYD2swplKOKNGxok%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;877&quot; height=&quot;437&quot; data-origin-width=&quot;640&quot; data-origin-height=&quot;319&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;blockquote data-ke-style=&quot;style3&quot;&gt; &amp;nbsp;&lt;b&gt;아래와 같이 텍스트가 데이터 베이스에 저장이 되고&lt;/b&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;640&quot; data-origin-height=&quot;233&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/RQdOL/dJMcajhCF8p/a9K2sFQuW3SYS8FAaS1g80/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/RQdOL/dJMcajhCF8p/a9K2sFQuW3SYS8FAaS1g80/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/RQdOL/dJMcajhCF8p/a9K2sFQuW3SYS8FAaS1g80/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FRQdOL%2FdJMcajhCF8p%2Fa9K2sFQuW3SYS8FAaS1g80%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;777&quot; height=&quot;283&quot; data-origin-width=&quot;640&quot; data-origin-height=&quot;233&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;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  이를 구독하고 있던 다른 사용자에게 전달이 됩니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;640&quot; data-origin-height=&quot;334&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bSTrqu/dJMcaiC2Emp/JppDV6LIX8ZNKU2lEQYnI1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bSTrqu/dJMcaiC2Emp/JppDV6LIX8ZNKU2lEQYnI1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bSTrqu/dJMcaiC2Emp/JppDV6LIX8ZNKU2lEQYnI1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbSTrqu%2FdJMcaiC2Emp%2FJppDV6LIX8ZNKU2lEQYnI1%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;849&quot; height=&quot;443&quot; data-origin-width=&quot;640&quot; data-origin-height=&quot;334&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;h3 data-ke-size=&quot;size23&quot;&gt;5. 저장소: Storage&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  저장소: Storage&lt;/b&gt;&lt;br /&gt;- 이미지, 동영상 등의 대용량 파일을 저장하고 관리할 수 있습니다. CDN이 기본 포함되어 있어 파일 전송 속도가 빠릅니다.&lt;br /&gt;- S3 호환 오브젝트 스토리지로, 이미지/영상/문서 등 파일을 저장&amp;middot;관리하는 서비스입니다. 내부적으로 AWS S3 또는 S3 호환 스토리지(MinIO 등) 위에서 동작을 합니다.&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1298&quot; data-origin-height=&quot;833&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ymVjD/dJMb997bI3d/qaz05LSdYBodnsJX9sL3K0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ymVjD/dJMb997bI3d/qaz05LSdYBodnsJX9sL3K0/img.png&quot; data-alt=&quot;https://supabase.com/storage&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ymVjD/dJMb997bI3d/qaz05LSdYBodnsJX9sL3K0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FymVjD%2FdJMb997bI3d%2Fqaz05LSdYBodnsJX9sL3K0%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;1298&quot; height=&quot;833&quot; data-origin-width=&quot;1298&quot; data-origin-height=&quot;833&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://supabase.com/storage&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;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  Supabase Storage가 내부적으로 AWS S3 호환 방식으로 설계되어 있어서, S3의 용어를 그대로 차용한 Burket을 이용합니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style2&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;분류&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;설명&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;예시&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Public Burket&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;URL만 알면 누구나 접근 가능하며 별도의 인증이 필요없는 공간&lt;/td&gt;
&lt;td&gt;프로필 이미지, 공개 에셋, 썸네일 등&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Private Burket&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;인증 토큰 또는 Signed URL 필요하며 별도의 인증이 필요한 공간&lt;/td&gt;
&lt;td&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;h2 data-ke-size=&quot;size26&quot;&gt;4) BaaS 플랫폼 보안적 취약점&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 크로스 플랫폼 내에서 Supabase 활용 구조&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  크로스 플랫폼 내에서 Supabase 활용 구조&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;1. 모바일 내에서 .env 환경 파일 내에 Supbase URL과 Publishable key를 관리합니다.&lt;br /&gt;2. 이를 기반으로 Supbase와의 통신을 위한 공통 Client를 구성합니다.&lt;br /&gt;3. 클라이언트로 Supabase의 조회 쿼리를 구성하여 Supabase의 Database를 직접 조회를 합니다.&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1141&quot; data-origin-height=&quot;532&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/IIW4P/dJMcaayg3B2/BLfMzKN3ZwURKs7dujdKjk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/IIW4P/dJMcaayg3B2/BLfMzKN3ZwURKs7dujdKjk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/IIW4P/dJMcaayg3B2/BLfMzKN3ZwURKs7dujdKjk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FIIW4P%2FdJMcaayg3B2%2FBLfMzKN3ZwURKs7dujdKjk%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;1141&quot; height=&quot;532&quot; data-origin-width=&quot;1141&quot; data-origin-height=&quot;532&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;h3 data-ke-size=&quot;size23&quot;&gt;2. Supabase 연결&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  Supabase 연결&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- Supabase를 연결하기 위해서 Front &amp;rarr; Supabase로 바로 연결하는 구조를 구성합니다.&lt;/b&gt;&lt;br /&gt;- 해당 부분에서는 Publishable key를 이용하여서 &amp;lsquo;anno(손님)&amp;rsquo;이라는 권한을 받아서 우선 수행을 합니다.&lt;/blockquote&gt;
&lt;table id=&quot;33016d47-b05b-80a0-8cbc-d1c16e6453a6&quot; style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style2&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 20.8139%;&quot;&gt;&lt;b&gt;분류&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 79.0698%;&quot;&gt;&lt;b&gt;설명&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;33016d47-b05b-80da-962d-d19144550b8f&quot;&gt;
&lt;td style=&quot;width: 20.8139%;&quot;&gt;&lt;b&gt;Project URL&lt;/b&gt;&lt;/td&gt;
&lt;td id=&quot;Nq~m&quot; style=&quot;width: 79.0698%;&quot;&gt;- Supabase 프로젝트의 API 엔드포인트 주소를 의미합니다.&lt;br /&gt;- REST API, Auth, Storage, Realtime 등 모든 요청이 이 URL로 들어감&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;33016d47-b05b-8078-9c27-caa0c62d90a5&quot;&gt;
&lt;td style=&quot;width: 20.8139%;&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;Publishable key&lt;/span&gt;&lt;/b&gt;&lt;/td&gt;
&lt;td id=&quot;Nq~m&quot; style=&quot;width: 79.0698%;&quot;&gt;- anon(손님) 권한으로 서명된 JWT를 의미합니다.&lt;br /&gt;- 클라이언트가 Supabase API에 요청할 때 Authorization 헤더에 자동으로 붙음&lt;br /&gt;- 프론트 상에서 사용되는 키를 의미합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;33016d47-b05b-805a-9bef-d0cc7da33f22&quot;&gt;
&lt;td style=&quot;width: 20.8139%;&quot;&gt;&lt;b&gt;Secret Key&lt;/b&gt;&lt;/td&gt;
&lt;td id=&quot;Nq~m&quot; style=&quot;width: 79.0698%;&quot;&gt;- service_role(관리자) 권한으로 서명된 JWT를 의미합니다.&lt;br /&gt;- 클라이언트가 Supabase API에 요청할 때 Authorization 헤더에 자동으로 붙음&lt;br /&gt;- 서버 상에서 사용되는 키를 의미합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1522&quot; data-origin-height=&quot;441&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/93i2E/dJMcagkVo8A/kmn5tc5d65KJlk7XmzYfk1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/93i2E/dJMcagkVo8A/kmn5tc5d65KJlk7XmzYfk1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/93i2E/dJMcagkVo8A/kmn5tc5d65KJlk7XmzYfk1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F93i2E%2FdJMcagkVo8A%2Fkmn5tc5d65KJlk7XmzYfk1%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;1522&quot; height=&quot;441&quot; data-origin-width=&quot;1522&quot; data-origin-height=&quot;441&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;h3 data-ke-size=&quot;size23&quot;&gt;3. Supabase Client 생성&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  Supabase Client 생성&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- Supabase의 키를 기반으로 통신을 위한 공통 클라이언트를 생성합니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;pre class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot;&gt;&lt;code&gt;import 'react-native-url-polyfill/auto';
import AsyncStorage from '@react-native-async-storage/async-storage';
import { createClient } from '@supabase/supabase-js';
import { SUPABASE_ANON_KEY, SUPABASE_URL } from '@env';

export const supabaseClient = createClient(SUPABASE_URL, SUPABASE_ANON_KEY, {
	auth: {
		storage: AsyncStorage,
		autoRefreshToken: true,
		persistSession: true,
		detectSessionInUrl: false,
	},
});&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;h3 data-ke-size=&quot;size23&quot;&gt;4. Supabase 호출&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  Supabase 호출&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 연결된 클라이언트를 기반으로 직접 쿼리를 구성하여서 요청을 합니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;pre class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot;&gt;&lt;code&gt;const RecipeService = {
	/**
	 * Supabase 연결 상태 확인
	 */
	checkConnection: async (): Promise&amp;lt;boolean&amp;gt; =&amp;gt; {
		const { error } = await supabaseClient.from('recipes').select('id').limit(1);
		if (error) {
			console.error('Supabase 연결 실패:', error.message);
			return false;
		}
		console.log('Supabase 연결 성공');
		return true;
	},

	/**
	 * 전체 레시피 목록 조회
	 */
	selectRecipeList: async (): Promise&amp;lt;MainDataType.Recipe[]&amp;gt; =&amp;gt; {
		const { data, error } = await supabaseClient
			.from('recipes')
			.select('*')
			.order('extracted_at', { ascending: false });

		if (error) {
			throw new Error(error.message);
		}
		return (data as TableDataType.RecipeRow[]).map(mapToRecipe);
	},

	/**
	 * 레시피 단건 조회 (id 기준)
	 */
	selectRecipeById: async (id: string): Promise&amp;lt;MainDataType.Recipe&amp;gt; =&amp;gt; {
		const { data, error } = await supabaseClient.from('recipes').select('*').eq('video_id', id).single();

		if (error) {
			throw new Error(error.message);
		}
		return mapToRecipe(data as TableDataType.RecipeRow);
	},

	/**
	 * 레시피 단건 조회 (video_id 기준)
	 */
	selectRecipeByVideoId: async (videoId: string): Promise&amp;lt;MainDataType.Recipe&amp;gt; =&amp;gt; {
		const { data, error } = await supabaseClient.from('recipes').select('*').eq('video_id', videoId).single();

		if (error) {
			throw new Error(error.message);
		}
		return mapToRecipe(data as TableDataType.RecipeRow);
	},
	
}&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;h3 data-ke-size=&quot;size23&quot;&gt;5. 보안적 취약점&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  보안적 취약점&lt;/b&gt;&lt;br /&gt;&lt;b&gt;&lt;br /&gt;- .env 파일 내에서 직접적으로 Supabase 접속 엔드포인트와 호출 및 접근 가능한 Publishable key를 이용하고 있습니다.&lt;/b&gt;&lt;br /&gt;- 해당 키가 &amp;lsquo;anno(손님) 권한&amp;rsquo;으로 서명된 JWT라고 하더라도, 손님에 대한 정책(Policy)을 지정하지 않는다면, 모든 데이터에 접근이 가능하며 노출 시 보안적인 취약점이 발생할 수 있습니다.&amp;nbsp;&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1115&quot; data-origin-height=&quot;697&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dkpkg0/dJMcacv59xk/e8A6B1sW5hJ2r4bRkN8ie0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dkpkg0/dJMcacv59xk/e8A6B1sW5hJ2r4bRkN8ie0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dkpkg0/dJMcacv59xk/e8A6B1sW5hJ2r4bRkN8ie0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdkpkg0%2FdJMcacv59xk%2Fe8A6B1sW5hJ2r4bRkN8ie0%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;1115&quot; height=&quot;697&quot; data-origin-width=&quot;1115&quot; data-origin-height=&quot;697&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;5) MoltBook으로 본 Supabase의 사례&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  MoltBook&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- MoltBook이라는 AI 에이전트를 위한 소셜 네트워크로 AI 에이전트들이 글을 올리고, 토론하고, 추천하는 플랫폼이며 인간은 관찰자로 참여가 가능한 플랫폼이 있습니다.&lt;/b&gt;&lt;br /&gt;- 즉, AI 에이전트들이 모여서 노는 SNS 공간에 인간이 관찰자로 참여를 하는 플랫폼입니다.&lt;/blockquote&gt;
&lt;figure id=&quot;og_1776830517258&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;moltbook - the front page of the agent internet&quot; data-og-description=&quot;A social network built exclusively for AI agents. Where AI agents share, discuss, and upvote. Humans welcome to observe.&quot; data-og-host=&quot;www.moltbook.com&quot; data-og-source-url=&quot;https://www.moltbook.com/&quot; data-og-url=&quot;https://www.moltbook.com&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/PaUZW/dJMb9eTRVXs/vKQ3X7Q5dB2Rk4cPNBncDK/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/pmb9M/dJMb9lMdMht/fIAX2PgqyKsnhUYVukyfq1/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630&quot;&gt;&lt;a href=&quot;https://www.moltbook.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.moltbook.com/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/PaUZW/dJMb9eTRVXs/vKQ3X7Q5dB2Rk4cPNBncDK/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/pmb9M/dJMb9lMdMht/fIAX2PgqyKsnhUYVukyfq1/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;moltbook - the front page of the agent internet&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;A social network built exclusively for AI agents. Where AI agents share, discuss, and upvote. Humans welcome to observe.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.moltbook.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;h3 data-ke-size=&quot;size23&quot;&gt;1. MoltBook 해킹: Supabase 설정 오류로 150만 개의 API 키 노출&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  MoltBook 해킹: Supabase 설정 오류로 150만 개의 API 키 노출&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- .env 파일 내에서 직접적으로 Supabase 접속 엔드포인트와 호출 및 접근 가능한 Publishable key를 이용하고 있습니다.&lt;/b&gt;&lt;br /&gt;- 해당 키가 &amp;lsquo;anno(손님) 권한&amp;rsquo;으로 서명된 JWT라고 하더라도, 손님에 대한 정책(Policy)을 지정하지 않는다면, 모든 데이터에 접근이 가능하며 노출 시 보안적인 취약점이 발생할 수 있습니다.&lt;br /&gt;- 이와 같은 사례로 MoltBook 해킹으로 150만 개의 API 키가 노출된 사례에 대해 알아봅니다.&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;995&quot; data-origin-height=&quot;281&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dy03kO/dJMb997bN68/13azRWmnd34MRflmSEtKyK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dy03kO/dJMb997bN68/13azRWmnd34MRflmSEtKyK/img.png&quot; data-alt=&quot;https://v.daum.net/v/Y1M6G1Kphd&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dy03kO/dJMb997bN68/13azRWmnd34MRflmSEtKyK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdy03kO%2FdJMb997bN68%2F13azRWmnd34MRflmSEtKyK%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;995&quot; height=&quot;281&quot; data-origin-width=&quot;995&quot; data-origin-height=&quot;281&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://v.daum.net/v/Y1M6G1Kphd&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;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  사건 요약&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;- 2026년 1월 31일, 보안 연구원 Jameson O'Reilly가 Moltbook의 전체 데이터베이스가 공개적으로 노출되어 있다는 사실을 발견했습니다. &lt;br /&gt;-노출된 키를 기반으로 이메일 주소, 인증 토큰, 모든 에이전트의 API 키에 접근 가능한 상태였습니다.&lt;br /&gt;&lt;br /&gt;- 재미있는 사항은 창업자 Matt Schlicht은 &quot;Moltbook을 위해 단 한 줄의 코드도 직접 작성하지 않았다. AI가 현실로 만들었다&quot;라고 공개적으로 밝혔습니다. 즉, 바이브 코딩만으로 구성을 하였다고 합니다.&lt;br /&gt;&lt;br /&gt;- 즉, 해당 사건의 핵심 원인은 Supabase의 이였습니다. Row Level Security(RLS)를 활성화하지 않았다고 합니다.&lt;br /&gt;- 해당 문제는 즉각적으로 해결이 되었다고 하지만, 150만 개의 키가 노출이 되었다는 큰 이슈였습니다.&lt;br /&gt;&lt;br /&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1776830828730&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;Moltbook Data Breach: Supabase RLS Security Lessons | Bastion&quot; data-og-description=&quot;Learn how Moltbook's Supabase misconfiguration exposed 1.5 million API keys and what security practices could have prevented this AI agent platform disaster.&quot; data-og-host=&quot;bastion.tech&quot; data-og-source-url=&quot;https://bastion.tech/blog/moltbook-security-lessons-ai-agents&quot; data-og-url=&quot;https://bastion.tech/blog/moltbook-security-lessons-ai-agents/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/v2dQg/dJMb8Z3tzKc/8RitBy3vDkzNMKuQTCnxKK/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/cpO5rN/dJMb81GZAEB/nhlOgj0ldpMI6x66JRS001/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630&quot;&gt;&lt;a href=&quot;https://bastion.tech/blog/moltbook-security-lessons-ai-agents&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://bastion.tech/blog/moltbook-security-lessons-ai-agents&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/v2dQg/dJMb8Z3tzKc/8RitBy3vDkzNMKuQTCnxKK/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/cpO5rN/dJMb81GZAEB/nhlOgj0ldpMI6x66JRS001/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Moltbook Data Breach: Supabase RLS Security Lessons | Bastion&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Learn how Moltbook's Supabase misconfiguration exposed 1.5 million API keys and what security practices could have prevented this AI agent platform disaster.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;bastion.tech&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;figure id=&quot;og_1776830806730&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;Hacking Moltbook: AI Social Network Reveals 1.5M API Keys | Wiz Blog&quot; data-og-description=&quot;Learn how a misconfigured Supabase database at Moltbook exposed 1.5M API keys, private messages, and user emails, enabling full AI agent takeover.&quot; data-og-host=&quot;www.wiz.io&quot; data-og-source-url=&quot;https://www.wiz.io/blog/exposed-moltbook-database-reveals-millions-of-api-keys&quot; data-og-url=&quot;https://www.wiz.io/blog/exposed-moltbook-database-reveals-millions-of-api-keys&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/flGbM/dJMb87f8KTq/mXhi77si5azBJbO0MuzrN0/img.jpg?width=1308&amp;amp;height=664&amp;amp;face=0_0_1308_664&quot;&gt;&lt;a href=&quot;https://www.wiz.io/blog/exposed-moltbook-database-reveals-millions-of-api-keys&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.wiz.io/blog/exposed-moltbook-database-reveals-millions-of-api-keys&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/flGbM/dJMb87f8KTq/mXhi77si5azBJbO0MuzrN0/img.jpg?width=1308&amp;amp;height=664&amp;amp;face=0_0_1308_664');&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;Hacking Moltbook: AI Social Network Reveals 1.5M API Keys | Wiz Blog&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Learn how a misconfigured Supabase database at Moltbook exposed 1.5M API keys, private messages, and user emails, enabling full AI agent takeover.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.wiz.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;style3&quot;&gt;&lt;b&gt;  후문이지만, 바이브 코딩으로 만든 MoltBook은 2026년 03월 10일에 Meta 내에서 인수를 하였다고 합니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;figure id=&quot;og_1776830852032&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;[AI의 종목 이야기] 메타, AI 에이전트 소셜 네트워크 '몰트북' 인수&quot; data-og-description=&quot;이 기사는 인공지능(AI) 번역으로 생산된 콘텐츠로, 원문은 3월 10일자 로이터 기사(Meta acquires AI agent social network Moltbook)입니다.[서울=뉴스핌] 김현영 기자 =&amp;nbsp;메타 플랫폼스(종목코드: META)가 인공&quot; data-og-host=&quot;www.newspim.com&quot; data-og-source-url=&quot;https://www.newspim.com/news/view/20260311000018&quot; data-og-url=&quot;https://www.newspim.com/news/view/20260311000018&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cGj6lY/dJMb9gxnH4D/5oJb8Jnf7fWkP9tIgU1wLK/img.jpg?width=400&amp;amp;height=279&amp;amp;face=65_9_140_84,https://scrap.kakaocdn.net/dn/fIUP3/dJMb9frHJJX/TJt1oQOvZDdTW35Ffgn7X1/img.jpg?width=400&amp;amp;height=279&amp;amp;face=65_9_140_84,https://scrap.kakaocdn.net/dn/w5unJ/dJMb9hC3yMb/BullaoYfp7tjkxWJvvMQl0/img.jpg?width=680&amp;amp;height=474&amp;amp;face=0_0_680_474&quot;&gt;&lt;a href=&quot;https://www.newspim.com/news/view/20260311000018&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.newspim.com/news/view/20260311000018&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cGj6lY/dJMb9gxnH4D/5oJb8Jnf7fWkP9tIgU1wLK/img.jpg?width=400&amp;amp;height=279&amp;amp;face=65_9_140_84,https://scrap.kakaocdn.net/dn/fIUP3/dJMb9frHJJX/TJt1oQOvZDdTW35Ffgn7X1/img.jpg?width=400&amp;amp;height=279&amp;amp;face=65_9_140_84,https://scrap.kakaocdn.net/dn/w5unJ/dJMb9hC3yMb/BullaoYfp7tjkxWJvvMQl0/img.jpg?width=680&amp;amp;height=474&amp;amp;face=0_0_680_474');&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;[AI의 종목 이야기] 메타, AI 에이전트 소셜 네트워크 '몰트북' 인수&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;이 기사는 인공지능(AI) 번역으로 생산된 콘텐츠로, 원문은 3월 10일자 로이터 기사(Meta acquires AI agent social network Moltbook)입니다.[서울=뉴스핌] 김현영 기자 =&amp;nbsp;메타 플랫폼스(종목코드: META)가 인공&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.newspim.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;h2 data-ke-size=&quot;size26&quot;&gt;6) Supabase RLS(Row Level Security)&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  Supabase RLS(Row Level Security)&lt;/b&gt; &lt;br /&gt;&lt;br /&gt;&lt;b&gt;- PostgreSQL의 RLS 기능을 Supabase가 활용한 것으로 테이블의 각 행(Row)에 대한 접근을 사용자별로 제어하는 보안 메커니즘을 의미합니다.&lt;/b&gt;&lt;br /&gt;- 이를 활성화하지 않으면 API 키만으로 모든 데이터에 접근할 수 있습니다.&lt;br /&gt;&lt;br /&gt;- Supabase 내에서는 클라이언트(앱) 내에 Publishable key를 포함하기에, 손님(Anno)에 대한 권한으로 제공을 하지만, 활성화가 되어 있지 않으면 사실상 데이터베이스에 모든 접근이 되기에, 이에 따르는 RLS에 대한 설정된 정책(Policy)을 지정하여 허용된 데이터만 노출되도록 해야 합니다.&lt;/blockquote&gt;
&lt;figure id=&quot;og_1776830891962&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;Row Level Security | Supabase Docs&quot; data-og-description=&quot;Secure your data using Postgres Row Level Security.&quot; data-og-host=&quot;supabase.com&quot; data-og-source-url=&quot;https://supabase.com/docs/guides/database/postgres/row-level-security&quot; data-og-url=&quot;https://supabase.com/docs/guides/database/postgres/row-level-security&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bkjitg/dJMb8WMrR5Y/uL9bqU8MHyxWYKmRbeW9pk/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/dp9QsZ/dJMb83Sk9Xp/8VJtwZSLVzFwwI4ukWKZc0/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://supabase.com/docs/guides/database/postgres/row-level-security&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://supabase.com/docs/guides/database/postgres/row-level-security&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bkjitg/dJMb8WMrR5Y/uL9bqU8MHyxWYKmRbeW9pk/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/dp9QsZ/dJMb83Sk9Xp/8VJtwZSLVzFwwI4ukWKZc0/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;Row Level Security | Supabase Docs&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Secure your data using Postgres Row Level Security.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;supabase.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;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  Client내에 포함되어 있는 Supabase Publishable key&lt;/b&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;585&quot; data-origin-height=&quot;134&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/20Efw/dJMcaduUXDA/xz4tgxkKKLfm0KMYgq9i3k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/20Efw/dJMcaduUXDA/xz4tgxkKKLfm0KMYgq9i3k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/20Efw/dJMcaduUXDA/xz4tgxkKKLfm0KMYgq9i3k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F20Efw%2FdJMcaduUXDA%2Fxz4tgxkKKLfm0KMYgq9i3k%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;585&quot; height=&quot;134&quot; data-origin-width=&quot;585&quot; data-origin-height=&quot;134&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;h3 data-ke-size=&quot;size23&quot;&gt;1. RLS 프로세스&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  RLS 프로세스&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;1. 클라이언트는 발급받은 JWT를 Supabase로 전달을 합니다.&lt;br /&gt;2. Supabase Auth Layer에서는 auth.uid() / auth.role() 추출하여 확인합니다.&lt;br /&gt;3. RLS 활성화 여부 확인하여, 비 활성화 되어 있다면, &amp;lsquo;전체 접근 허용&amp;rsquo;이 되는 것이고, 아니라면 Policy를 확인합니다.&lt;br /&gt;4. Policy를 확인하여서 허용되지 않으면 데이터는 조회되지 않을 것이고, 허용된다면 PostgreSQL을 통해서 쿼리가 실행됩니다.&lt;br /&gt;5. 결과적으로 필터링된 결과를 반환합니다. &lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 위에 Moltbook 사례를 보더라도 RLS 활성화 여부를 확인하지 않았다면, 데이터베이스 접근은 모두 &amp;lsquo;전체 접근 허용&amp;rsquo;이 되었을 것입니다.&amp;nbsp;&lt;/b&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;525&quot; data-origin-height=&quot;461&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/zQYyK/dJMcaaLNZlY/n5v3lSvauSpfD9DStyjk61/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/zQYyK/dJMcaaLNZlY/n5v3lSvauSpfD9DStyjk61/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/zQYyK/dJMcaaLNZlY/n5v3lSvauSpfD9DStyjk61/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FzQYyK%2FdJMcaaLNZlY%2Fn5v3lSvauSpfD9DStyjk61%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;525&quot; height=&quot;461&quot; data-origin-width=&quot;525&quot; data-origin-height=&quot;461&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;h3 data-ke-size=&quot;size23&quot;&gt;2. RLS 적용 방법&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2.1. Enable RLS for this table&lt;/h4&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;style3&quot;&gt;&lt;b&gt;  Enable RLS for this table&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 테이블에 대해서 RLS를 적용합니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1271&quot; data-origin-height=&quot;808&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/AFYA4/dJMcaiC2Kxz/wMwKNK7rMNyAdhEZO75Xi1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/AFYA4/dJMcaiC2Kxz/wMwKNK7rMNyAdhEZO75Xi1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/AFYA4/dJMcaiC2Kxz/wMwKNK7rMNyAdhEZO75Xi1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FAFYA4%2FdJMcaiC2Kxz%2FwMwKNK7rMNyAdhEZO75Xi1%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;1271&quot; height=&quot;808&quot; data-origin-width=&quot;1271&quot; data-origin-height=&quot;808&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;h4 data-ke-size=&quot;size20&quot;&gt;2.2. &amp;lsquo;Add RLS policy&amp;rsquo;를 적용합니다&lt;/h4&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1195&quot; data-origin-height=&quot;642&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/UcKa2/dJMcag6jJZ3/OrNd3KejiP4kRC5jSKav1K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/UcKa2/dJMcag6jJZ3/OrNd3KejiP4kRC5jSKav1K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/UcKa2/dJMcag6jJZ3/OrNd3KejiP4kRC5jSKav1K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FUcKa2%2FdJMcag6jJZ3%2FOrNd3KejiP4kRC5jSKav1K%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;642&quot; data-origin-width=&quot;1195&quot; data-origin-height=&quot;642&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;h4 data-ke-size=&quot;size20&quot;&gt;2.3. 직접 Policy를 지정할 수 있고, Template도 제공이 됩니다.&lt;/h4&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;957&quot; data-origin-height=&quot;834&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/psudD/dJMcahjS1m3/SGBPUHuizd3gZqPfabcv3K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/psudD/dJMcahjS1m3/SGBPUHuizd3gZqPfabcv3K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/psudD/dJMcahjS1m3/SGBPUHuizd3gZqPfabcv3K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpsudD%2FdJMcahjS1m3%2FSGBPUHuizd3gZqPfabcv3K%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;957&quot; height=&quot;834&quot; data-origin-width=&quot;957&quot; data-origin-height=&quot;834&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;h3 data-ke-size=&quot;size23&quot;&gt;3. RLS Policy Templates&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  RLS Policy Templates&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 기본적으로 제공하는 RLS Policy Template입니다. 이를 이용하여서 기본적인 설정 구성이 가능합니다.&amp;nbsp;&lt;/b&gt;&lt;/blockquote&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style2&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 13.1395%;&quot;&gt;&lt;b&gt;태그&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 45.6977%;&quot;&gt;&lt;b&gt;정책명&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 41.0465%;&quot;&gt;&lt;b&gt;설명&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 13.1395%;&quot;&gt;&lt;b&gt;SELECT&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 45.6977%;&quot;&gt;Enable read access for all users&lt;/td&gt;
&lt;td style=&quot;width: 41.0465%;&quot;&gt;SELECT로 모든 유저에게 읽기 권한 부여&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 13.1395%;&quot;&gt;&lt;b&gt;INSERT&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 45.6977%;&quot;&gt;Enable insert for authenticated users only&lt;/td&gt;
&lt;td style=&quot;width: 41.0465%;&quot;&gt;인증된 유저에게만 INSERT 권한 부여&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 13.1395%;&quot;&gt;&lt;b&gt;DELETE&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 45.6977%;&quot;&gt;Enable delete for users based on user_id&lt;/td&gt;
&lt;td style=&quot;width: 41.0465%;&quot;&gt;user_id 컬럼이 본인 ID와 일치하는 행만 삭제 가능&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 13.1395%;&quot;&gt;&lt;b&gt;INSERT&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 45.6977%;&quot;&gt;Enable insert for users based on user_id&lt;/td&gt;
&lt;td style=&quot;width: 41.0465%;&quot;&gt;user_id 컬럼이 본인 ID와 일치하는 행만 삽입 가능&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 13.1395%;&quot;&gt;&lt;b&gt;UPDATE&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 45.6977%;&quot;&gt;Policy with table joins&lt;/td&gt;
&lt;td style=&quot;width: 41.0465%;&quot;&gt;테이블 JOIN을 활용한 고급 RLS 규칙 (teams, members 테이블 예시)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 13.1395%;&quot;&gt;&lt;b&gt;ALL&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 45.6977%;&quot;&gt;Policy with security definer functions&lt;/td&gt;
&lt;td style=&quot;width: 41.0465%;&quot;&gt;Security Definer 함수로 다대다 관계의 링킹 테이블 접근 제어&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 13.1395%;&quot;&gt;&lt;b&gt;SELECT&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 45.6977%;&quot;&gt;Policy to implement Time To Live (TTL)&lt;/td&gt;
&lt;td style=&quot;width: 41.0465%;&quot;&gt;생성 후 24시간 이내 행만 조회 가능 (Instagram 스토리 / Snapchat 방식)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 13.1395%;&quot;&gt;&lt;b&gt;SELECT&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 45.6977%;&quot;&gt;Enable users to view their own data only&lt;/td&gt;
&lt;td style=&quot;width: 41.0465%;&quot;&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;408&quot; data-origin-height=&quot;689&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bLOrvX/dJMcajhCM51/ulol4rVQukWVJoBfZnKrR1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bLOrvX/dJMcajhCM51/ulol4rVQukWVJoBfZnKrR1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bLOrvX/dJMcajhCM51/ulol4rVQukWVJoBfZnKrR1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbLOrvX%2FdJMcajhCM51%2Fulol4rVQukWVJoBfZnKrR1%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;408&quot; height=&quot;689&quot; data-origin-width=&quot;408&quot; data-origin-height=&quot;689&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;425&quot; data-origin-height=&quot;231&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bQNzq9/dJMcahxoSfX/COMtt9lnpqiK8zmIcH2ZCK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bQNzq9/dJMcahxoSfX/COMtt9lnpqiK8zmIcH2ZCK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bQNzq9/dJMcahxoSfX/COMtt9lnpqiK8zmIcH2ZCK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbQNzq9%2FdJMcahxoSfX%2FCOMtt9lnpqiK8zmIcH2ZCK%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;425&quot; height=&quot;231&quot; data-origin-width=&quot;425&quot; data-origin-height=&quot;231&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;h3 data-ke-size=&quot;size23&quot;&gt;4. Custom Row Level Security policy&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  Custom Row Level Security policy&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 위에 템플릿을 이용한 경우 외에 커스텀으로 Policy 지정이 가능합니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  Supabase 내에서 각각에 맞는 설정이 가능합니다.&lt;/b&gt;&lt;/blockquote&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;526&quot; data-origin-height=&quot;582&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/CfwvA/dJMcaffjaP7/O2IIJMikYvJWFrshKfEEe0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/CfwvA/dJMcaffjaP7/O2IIJMikYvJWFrshKfEEe0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/CfwvA/dJMcaffjaP7/O2IIJMikYvJWFrshKfEEe0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FCfwvA%2FdJMcaffjaP7%2FO2IIJMikYvJWFrshKfEEe0%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;526&quot; height=&quot;582&quot; data-origin-width=&quot;526&quot; data-origin-height=&quot;582&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;h4 data-ke-size=&quot;size20&quot;&gt;3.1. 테이블 단위가 아닌 행(Row) 단위로 접근 제어&lt;/h4&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;style3&quot;&gt;  테이블 단위가 아닌 행(Row) 단위로 접근 제어&lt;/blockquote&gt;
&lt;pre class=&quot;sql&quot; data-ke-language=&quot;sql&quot;&gt;&lt;code&gt;-- RLS 활성화
ALTER TABLE posts ENABLE ROW LEVEL SECURITY;&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;h4 data-ke-size=&quot;size20&quot;&gt;3.2. 정책(Policy) 구조&lt;/h4&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  정책(Policy) 구조&lt;/b&gt;&lt;br /&gt;- 정책을 구성하는 SQL입니다.&lt;br /&gt;FOR: 어떤 작업에 적용할지(SELECT | INSERT | UPDATE | DELETE | ALL)&lt;br /&gt;TO: 누구에게 적용할지(authenticated | anon | public | 특정롤)&lt;br /&gt;USING vs WITH CHECKUSING(읽기 조건) &amp;mdash; &quot;이 행을 볼 수 있는가?&quot;에 대한 지정&lt;br /&gt;WITH CHECK(쓰기 조건) &amp;mdash; &quot;이 값을 쓸 수 있는가?&amp;rdquo;에 대한 지정&lt;/blockquote&gt;
&lt;pre class=&quot;sql&quot; data-ke-language=&quot;sql&quot;&gt;&lt;code&gt;CREATE POLICY &quot;정책명&quot;
ON 테이블명
FOR [SELECT | INSERT | UPDATE | DELETE | ALL]
TO [authenticated | anon | public | 특정롤]
USING (조건)           -- 읽기 조건 (SELECT, UPDATE, DELETE)
WITH CHECK (조건);     -- 쓰기 조건 (INSERT, UPDATE)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  예시 -1&lt;/b&gt;&lt;br /&gt;- post라는 테이블에 대해서 RLS를 적용합니다&lt;br /&gt;- post는 로그인된 본인의 데이터만 조회, 삽입, 수정, 삭제만 가능하도록 정책을 지정합니다.&amp;nbsp;&lt;/blockquote&gt;
&lt;pre class=&quot;sql&quot; data-ke-language=&quot;sql&quot;&gt;&lt;code&gt;ALTER TABLE posts ENABLE ROW LEVEL SECURITY;

CREATE POLICY &quot;본인 데이터 조회&quot;
ON posts FOR SELECT
TO authenticated
USING (auth.uid() = user_id);

CREATE POLICY &quot;본인 데이터 삽입&quot;
ON posts FOR INSERT
TO authenticated
WITH CHECK (auth.uid() = user_id);

CREATE POLICY &quot;본인 데이터 수정&quot;
ON posts FOR UPDATE
TO authenticated
USING (auth.uid() = user_id)
WITH CHECK (auth.uid() = user_id);

CREATE POLICY &quot;본인 데이터 삭제&quot;
ON posts FOR DELETE
TO authenticated
USING (auth.uid() = user_id);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;오늘도&amp;nbsp;감사합니다.&amp;nbsp; &lt;/b&gt;&lt;/p&gt;</description>
      <category>공통/Trend</category>
      <category>BaaS</category>
      <category>supabase</category>
      <category>Supabase Moltbook</category>
      <category>Supabase 사용 사례</category>
      <category>Supabase 예시</category>
      <category>Supabase 이해</category>
      <category>Supabase 주요 기능</category>
      <category>Supabase 활용</category>
      <category>Supabase 활용 사례</category>
      <category>Supabase란</category>
      <author>adjh54</author>
      <guid isPermaLink="true">https://adjh54.tistory.com/747</guid>
      <comments>https://adjh54.tistory.com/747#entry747comment</comments>
      <pubDate>Wed, 22 Apr 2026 13:22:24 +0900</pubDate>
    </item>
    <item>
      <title>[RN/오류노트] UnhandledPromiseRejection: The promise rejected with the reason &amp;quot;Error: Failed to build ios project. &amp;quot;xcodebuild&amp;quot; exited with error code '65'.</title>
      <link>https://adjh54.tistory.com/746</link>
      <description>&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;해당 글에서는 React Native 환경에서 발생한 This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). 오류를 해결하는 방법에 대해서 알아봅니다.&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;
&lt;figure contenteditable=&quot;false&quot; data-ke-type=&quot;emoticon&quot; data-ke-align=&quot;alignCenter&quot; data-emoticon-type=&quot;face&quot; data-emoticon-name=&quot;073&quot; data-emoticon-isanimation=&quot;false&quot; data-emoticon-src=&quot;https://t1.daumcdn.net/keditor/emoticon/face/large/073.png&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/keditor/emoticon/face/large/073.png&quot; width=&quot;80&quot; /&gt;&lt;/figure&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1) 문제점&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  문제점&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- React Native 빌드 과정에서 아래와 같은 문제점이 발생하였습니다.&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;- UnhandledPromiseRejection: This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). The promise rejected with the reason &quot;Error: Failed to build ios project. &quot;xcodebuild&quot; exited with error code '65'.&lt;/blockquote&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;yarn ios:16pro
info A dev server is already running for this project on port 8082.
info Found Xcode workspace &quot;linkManager.xcworkspace&quot;
info Launching iPhone 16 Pro (iOS 18.5)
info Building (using &quot;xcodebuild -workspace linkManager.xcworkspace -configuration Debug -scheme linkManager -destination id=D6AA575C-2DB6-4E68-B719-CCEE4BD9C5DE&quot;)

info   Tip: Make sure that you have set up your development environment correctly, by running npx react-native doctor. To read more about doctor command visit: &amp;lt;https://github.com/react-native-community/cli/blob/main/packages/cli-doctor/README.md#doctor&amp;gt;

node:internal/process/promises:392
new UnhandledPromiseRejection(reason);
^

UnhandledPromiseRejection: This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). The promise rejected with the reason &quot;Error: Failed to build ios project. &quot;xcodebuild&quot; exited with error code '65'. To debug build logs further, consider building your app with Xcode.app, by opening 'linkManager.xcworkspace'.&quot;.
at throwUnhandledRejectionsMode (node:internal/process/promises:392:7)
at processPromiseRejections (node:internal/process/promises:475:17)
at process.processTicksAndRejections (node:internal/process/task_queues:106:32) {
code: 'ERR_UNHANDLED_REJECTION'
}

Node.js v22.21.1
&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;h2 data-ke-size=&quot;size26&quot;&gt;2) 해결방법&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  해결 방법&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;- ios 폴더 내에 LinkManager.xcodeproj와 linkManager.xcworkspace 로 대문자, 소문자가 섞여서 찾지 못해서 발생하는 문제였습니다.&lt;br /&gt;&lt;b&gt;- 그리고 macOS 파일시스템이 기본적으로 case-insensitive(대소문자 구분 안 함)을 수행하기에 발생을 합니다.&lt;/b&gt;&lt;br /&gt;- 해당 과정은 대부분 react-native-rename 라이브러리를 사용하여서 두번 이상 변경을 하는 경우 발생을 하였습니다.&lt;/blockquote&gt;
&lt;figure id=&quot;og_1776752415628&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;[RN] React Native 환경에서 react-native-rename을 활용하여 프로젝트 명, 패키지 일괄 변경하기&quot; data-og-description=&quot;해당 글에서는 React Native 환경에서 react-native-rename을 이용하여서 프로젝트 명, 패키지 명을 일괄적으로 변경하는 방법에 대해 알아봅니다 1) react-native-rename   react-native-rename- React Native 프로젝트&quot; data-og-host=&quot;adjh54.tistory.com&quot; data-og-source-url=&quot;https://adjh54.tistory.com/690&quot; data-og-url=&quot;https://adjh54.tistory.com/690&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/KJa02/dJMb8QeokCO/kqGanVETRWLy67be1l2q70/img.png?width=800&amp;amp;height=452&amp;amp;face=0_0_800_452,https://scrap.kakaocdn.net/dn/NeWND/dJMb9aKHf2l/WAIEEXSgGlaQn2CmyQeiw1/img.png?width=800&amp;amp;height=452&amp;amp;face=0_0_800_452,https://scrap.kakaocdn.net/dn/MVKuW/dJMb9jOo5Tn/WEilDgnuBaDR0jUgUvnqQ1/img.png?width=2048&amp;amp;height=1028&amp;amp;face=0_0_2048_1028&quot;&gt;&lt;a href=&quot;https://adjh54.tistory.com/690&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://adjh54.tistory.com/690&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/KJa02/dJMb8QeokCO/kqGanVETRWLy67be1l2q70/img.png?width=800&amp;amp;height=452&amp;amp;face=0_0_800_452,https://scrap.kakaocdn.net/dn/NeWND/dJMb9aKHf2l/WAIEEXSgGlaQn2CmyQeiw1/img.png?width=800&amp;amp;height=452&amp;amp;face=0_0_800_452,https://scrap.kakaocdn.net/dn/MVKuW/dJMb9jOo5Tn/WEilDgnuBaDR0jUgUvnqQ1/img.png?width=2048&amp;amp;height=1028&amp;amp;face=0_0_2048_1028');&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;[RN] React Native 환경에서 react-native-rename을 활용하여 프로젝트 명, 패키지 일괄 변경하기&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;해당 글에서는 React Native 환경에서 react-native-rename을 이용하여서 프로젝트 명, 패키지 명을 일괄적으로 변경하는 방법에 대해 알아봅니다 1) react-native-rename   react-native-rename- React Native 프로젝트&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;adjh54.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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;304&quot; data-origin-height=&quot;587&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/uMLWb/dJMcaiQxX1j/BPKU0ZRQnDJJt1OnlKE2Pk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/uMLWb/dJMcaiQxX1j/BPKU0ZRQnDJJt1OnlKE2Pk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/uMLWb/dJMcaiQxX1j/BPKU0ZRQnDJJt1OnlKE2Pk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FuMLWb%2FdJMcaiQxX1j%2FBPKU0ZRQnDJJt1OnlKE2Pk%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;304&quot; height=&quot;587&quot; data-origin-width=&quot;304&quot; data-origin-height=&quot;587&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;h3 data-ke-size=&quot;size23&quot;&gt;1. 폴더&amp;nbsp;명&amp;nbsp;변경&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  macOS 파일시스템이 기본적으로 case-insensitive(대소문자 구분 안 함) 문제가 있습니다&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;- 그렇기에 macOS의 입장에서는 linkManager와 LinkManager를 동일하게 여기기에 중간에 temp를 둬서 변경을 합니다.&lt;/blockquote&gt;
&lt;pre class=&quot;shell&quot; data-ke-language=&quot;shell&quot;&gt;&lt;code&gt;# 임시 이름 거쳐서 변경
$ mv linkManager.xcodeproj temp_LinkManager.xcodeproj
$ mv temp_LinkManager.xcodeproj LinkManager.xcodeproj

$ mv linkManager.xcworkspace temp_LinkManager.xcworkspace
$ mv temp_LinkManager.xcworkspace LinkManager.xcworkspace&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;h3 data-ke-size=&quot;size23&quot;&gt;2. contents.xcworkspacedata&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  contents.xcworkspacedata&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 참조하고 있는 소문자를 대문자로 변경합니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;pre class=&quot;xml&quot;&gt;&lt;code&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&amp;gt;
&amp;lt;Workspace
   version = &quot;1.0&quot;&amp;gt;
   &amp;lt;FileRef
      location = &quot;group:LinkManager.xcodeproj&quot;&amp;gt;
   &amp;lt;/FileRef&amp;gt;
   &amp;lt;FileRef
      location = &quot;group:Pods/Pods.xcodeproj&quot;&amp;gt;
   &amp;lt;/FileRef&amp;gt;
   &amp;lt;FileRef
		   // 해당 부분 대문자로 변경
      location = &quot;group:linkManager.xcodeproj&quot;&amp;gt;
   &amp;lt;/FileRef&amp;gt;
&amp;lt;/Workspace&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;h3 data-ke-size=&quot;size23&quot;&gt;3. pod 재설치&lt;/h3&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;style3&quot;&gt;&lt;b&gt;&amp;nbsp;  pod 재설치&lt;br /&gt;&lt;br /&gt;&lt;/b&gt;- 프로젝트에 대해서 캐시를 모두 정리하고 pod을 재설치를 합니다.&lt;/blockquote&gt;
&lt;pre class=&quot;elixir&quot;&gt;&lt;code&gt;# 1. 프로젝트 ios 폴더 접근
$ cd ios

# 2. CocoaPods 캐시
$ rm -rf Pods Podfile.lock

# 3. CocoaPods 시스템 캐시 정리
$ rm -rf ~/Library/Caches/CocoaPods

# 4. CocoaPods 완전 초기화
$ pod deintegrate
&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;cmake&quot;&gt;&lt;code&gt;pod install
&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;h3 data-ke-size=&quot;size23&quot;&gt;4. XCode 프로젝트에서 스킴(Scheme) 이름을 변경작업&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  XCode 프로젝트에서 스킴(Scheme) 이름을 변경작업&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;- 위에 작업이 실패한 경우 아래와 같이 수행을 합니다.&lt;br /&gt;&lt;b&gt;&lt;br /&gt;1. sed -i '' 's/linkManager/LinkManager/g' ...xcscheme&lt;/b&gt;&lt;br /&gt;.xcscheme 파일&amp;nbsp;내부 텍스트에서 linkManager &amp;rarr; LinkManager로 치환&lt;br /&gt;&lt;br /&gt;&lt;b&gt;2. mv linkManager.xcscheme LinkManager.xcscheme&lt;/b&gt;&lt;br /&gt;- 파일명 자체를 소문자 &amp;rarr; 대문자로 변경&lt;br /&gt;&lt;br /&gt;&lt;b&gt;3. ls ...xcschemes/&lt;/b&gt;&lt;br /&gt;- 변경 결과를 확인합니다.&lt;/blockquote&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;# 파일 내부 텍스트에서 linkManager &amp;rarr; LinkManager로 치환 작업
$ sed -i '' 's/linkManager/LinkManager/g' LinkManager.xcodeproj/xcshareddata/xcschemes/linkManager.xcscheme

# 파일명 자체를 소문자에서 대문자로 변경
$ mv LinkManager.xcodeproj/xcshareddata/xcschemes/linkManager.xcscheme LinkManager.xcodeproj/xcshareddata/xcschemes/LinkManager.xcscheme

# 변경 결과 상태를 확인.
$ ls LinkManager.xcodeproj/xcshareddata/xcschemes/
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&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;오늘도&amp;nbsp;감사합니다.&amp;nbsp; &lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>React &amp;amp; React Native/오류노트</category>
      <category>&amp;quot;xcodebuild&amp;quot; exited with error code '65'.</category>
      <category>or by rejecting a promise which was not handled with .catch().</category>
      <category>react native rename 오류</category>
      <category>react native UnhandledPromiseRejection</category>
      <category>react native 오류 발생</category>
      <category>This error originated either by throwing inside of an async function without a catch block</category>
      <author>adjh54</author>
      <guid isPermaLink="true">https://adjh54.tistory.com/746</guid>
      <comments>https://adjh54.tistory.com/746#entry746comment</comments>
      <pubDate>Tue, 21 Apr 2026 20:10:26 +0900</pubDate>
    </item>
    <item>
      <title>[기타] 로지텍 마우스(MX Master) Logi Options+ : Action Ring으로 빠르게 소프트웨어 실행 방법</title>
      <link>https://adjh54.tistory.com/745</link>
      <description>&lt;blockquote data-ke-style=&quot;style1&quot;&gt;해당 글에서는 MX Master 마우스 내에서 Action Ring을 이용하여서 빠르게 소프트웨어를 실행하는 방법에 대해 알아봅니다&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;
&lt;figure contenteditable=&quot;false&quot; data-ke-type=&quot;emoticon&quot; data-ke-align=&quot;alignCenter&quot; data-emoticon-type=&quot;face&quot; data-emoticon-name=&quot;073&quot; data-emoticon-isanimation=&quot;false&quot; data-emoticon-src=&quot;https://t1.daumcdn.net/keditor/emoticon/face/large/073.png&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/keditor/emoticon/face/large/073.png&quot; width=&quot;80&quot; /&gt;&lt;/figure&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1) Logi&amp;nbsp;Options+&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  Logi Options+&lt;/b&gt; &lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 로지텍(Logitech)의 공식 마우스/키보드 설정 소프트웨어&lt;/b&gt;&lt;/blockquote&gt;
&lt;figure id=&quot;og_1776751806862&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;Logi Options+ 소프트웨어 | Logitech&quot; data-og-description=&quot;Logitech의 마우스 및 키보드용 커스터마이징 소프트웨어인 Logi Options Plus에 대해 알아보세요. 설정을 개인화하고, 단축키를 만들며, 일상의 생산성을 향상하세요. 지금 무료로 다운로드하세요.&quot; data-og-host=&quot;www.logitech.com&quot; data-og-source-url=&quot;https://www.logitech.com/ko-kr/software/logi-options-plus&quot; data-og-url=&quot;https://www.logitech.com/ko-kr/software/logi-options-plus&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bg5EKK/dJMb9fZxugu/NvsDMIMATxs8lnOktXvEy1/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/bhjMbX/dJMb9kT478V/2jXpipcx5HWu3uBkvo9cb0/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630&quot;&gt;&lt;a href=&quot;https://www.logitech.com/ko-kr/software/logi-options-plus&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.logitech.com/ko-kr/software/logi-options-plus&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bg5EKK/dJMb9fZxugu/NvsDMIMATxs8lnOktXvEy1/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/bhjMbX/dJMb9kT478V/2jXpipcx5HWu3uBkvo9cb0/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Logi Options+ 소프트웨어 | Logitech&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Logitech의 마우스 및 키보드용 커스터마이징 소프트웨어인 Logi Options Plus에 대해 알아보세요. 설정을 개인화하고, 단축키를 만들며, 일상의 생산성을 향상하세요. 지금 무료로 다운로드하세요.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.logitech.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;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. Action Ring&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  Action Ring&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;- Logi Options+에서 제공하는 원형 퀵 메뉴 기능&lt;/blockquote&gt;
&lt;figure id=&quot;og_1776751792005&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;Logi Options+의 Actions Ring | Logitech&quot; data-og-description=&quot;Logi Options+에서 지원되는 앱의 도구, 단축키 및 창의적 제어에 빠르게 접근할 수 있는 커스텀 화면 오버레이인 Actions Ring에 대해 알아보세요.&quot; data-og-host=&quot;www.logitech.com&quot; data-og-source-url=&quot;https://www.logitech.com/ko-kr/software/actions-ring&quot; data-og-url=&quot;https://www.logitech.com/ko-kr/software/actions-ring&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cMh2Da/dJMb8YpXwQK/5Uvg8YhfPTZPtjvYK8GdJ1/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/F6VHA/dJMb8WMrKTr/GTvFfTGrmxyRKjbG2pk4m1/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630&quot;&gt;&lt;a href=&quot;https://www.logitech.com/ko-kr/software/actions-ring&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.logitech.com/ko-kr/software/actions-ring&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cMh2Da/dJMb8YpXwQK/5Uvg8YhfPTZPtjvYK8GdJ1/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/F6VHA/dJMb8WMrKTr/GTvFfTGrmxyRKjbG2pk4m1/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Logi Options+의 Actions Ring | Logitech&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Logi Options+에서 지원되는 앱의 도구, 단축키 및 창의적 제어에 빠르게 접근할 수 있는 커스텀 화면 오버레이인 Actions Ring에 대해 알아보세요.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.logitech.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;h2 data-ke-size=&quot;size26&quot;&gt;2) 설정 방법&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 공식 사이트에서 소프트웨어를 다운로드 받습니다.&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1782&quot; data-origin-height=&quot;827&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cAHiR4/dJMcaiC19zJ/IbtREb00KymAw72bzzrvUK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cAHiR4/dJMcaiC19zJ/IbtREb00KymAw72bzzrvUK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cAHiR4/dJMcaiC19zJ/IbtREb00KymAw72bzzrvUK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcAHiR4%2FdJMcaiC19zJ%2FIbtREb00KymAw72bzzrvUK%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;1782&quot; height=&quot;827&quot; data-origin-width=&quot;1782&quot; data-origin-height=&quot;827&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;figure id=&quot;og_1776751843629&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;Logi Options+ 소프트웨어 | Logitech&quot; data-og-description=&quot;Logitech의 마우스 및 키보드용 커스터마이징 소프트웨어인 Logi Options Plus에 대해 알아보세요. 설정을 개인화하고, 단축키를 만들며, 일상의 생산성을 향상하세요. 지금 무료로 다운로드하세요.&quot; data-og-host=&quot;www.logitech.com&quot; data-og-source-url=&quot;https://www.logitech.com/ko-kr/software/logi-options-plus&quot; data-og-url=&quot;https://www.logitech.com/ko-kr/software/logi-options-plus&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bg5EKK/dJMb9fZxugu/NvsDMIMATxs8lnOktXvEy1/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/bhjMbX/dJMb9kT478V/2jXpipcx5HWu3uBkvo9cb0/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630&quot;&gt;&lt;a href=&quot;https://www.logitech.com/ko-kr/software/logi-options-plus&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.logitech.com/ko-kr/software/logi-options-plus&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bg5EKK/dJMb9fZxugu/NvsDMIMATxs8lnOktXvEy1/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/bhjMbX/dJMb9kT478V/2jXpipcx5HWu3uBkvo9cb0/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Logi Options+ 소프트웨어 | Logitech&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Logitech의 마우스 및 키보드용 커스터마이징 소프트웨어인 Logi Options Plus에 대해 알아보세요. 설정을 개인화하고, 단축키를 만들며, 일상의 생산성을 향상하세요. 지금 무료로 다운로드하세요.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.logitech.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;h3 data-ke-size=&quot;size23&quot;&gt;2. 앱을 실행 &amp;gt; OPTIONS+ 설치를 선택합니다.&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1312&quot; data-origin-height=&quot;792&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bR8TCV/dJMcagSMhoy/EQvvDg2huzUqIvYk9Gdfvk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bR8TCV/dJMcagSMhoy/EQvvDg2huzUqIvYk9Gdfvk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bR8TCV/dJMcagSMhoy/EQvvDg2huzUqIvYk9Gdfvk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbR8TCV%2FdJMcagSMhoy%2FEQvvDg2huzUqIvYk9Gdfvk%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;1312&quot; height=&quot;792&quot; data-origin-width=&quot;1312&quot; data-origin-height=&quot;792&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. 다운로드가 진행이 됩니다.&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1312&quot; data-origin-height=&quot;792&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/waRk3/dJMcac3SsQd/iE3VAQNv2kyNxWPqCl57b1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/waRk3/dJMcac3SsQd/iE3VAQNv2kyNxWPqCl57b1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/waRk3/dJMcac3SsQd/iE3VAQNv2kyNxWPqCl57b1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FwaRk3%2FdJMcac3SsQd%2FiE3VAQNv2kyNxWPqCl57b1%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;1312&quot; height=&quot;792&quot; data-origin-width=&quot;1312&quot; data-origin-height=&quot;792&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;h3 data-ke-size=&quot;size23&quot;&gt;4. 소프트웨어 로그인을 수행하면 현재 연결된 로지텍 마우스를 확인할 수 있습니다.&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1630&quot; data-origin-height=&quot;972&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pPL89/dJMcacbLpvz/jRBazENP8jRGi87M8dTABk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pPL89/dJMcacbLpvz/jRBazENP8jRGi87M8dTABk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pPL89/dJMcacbLpvz/jRBazENP8jRGi87M8dTABk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpPL89%2FdJMcacbLpvz%2FjRBazENP8jRGi87M8dTABk%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;1630&quot; height=&quot;972&quot; data-origin-width=&quot;1630&quot; data-origin-height=&quot;972&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;h3 data-ke-size=&quot;size23&quot;&gt;5. 로지텍 마우스를 선택합니다&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1514&quot; data-origin-height=&quot;861&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/tFEZf/dJMcaiXi1d3/7rU9EiIffjyqZjyakC27uk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/tFEZf/dJMcaiXi1d3/7rU9EiIffjyqZjyakC27uk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/tFEZf/dJMcaiXi1d3/7rU9EiIffjyqZjyakC27uk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FtFEZf%2FdJMcaiXi1d3%2F7rU9EiIffjyqZjyakC27uk%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;1514&quot; height=&quot;861&quot; data-origin-width=&quot;1514&quot; data-origin-height=&quot;861&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;h3 data-ke-size=&quot;size23&quot;&gt;6. 기능 중 &amp;lsquo;제스처&amp;rsquo; 기능에 대해서 수정을 합니다&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1630&quot; data-origin-height=&quot;972&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bnMTxP/dJMcabKJne5/EqDHCGS9uKz98VIiCZYPw1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bnMTxP/dJMcabKJne5/EqDHCGS9uKz98VIiCZYPw1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bnMTxP/dJMcabKJne5/EqDHCGS9uKz98VIiCZYPw1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbnMTxP%2FdJMcabKJne5%2FEqDHCGS9uKz98VIiCZYPw1%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;1630&quot; height=&quot;972&quot; data-origin-width=&quot;1630&quot; data-origin-height=&quot;972&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;h3 data-ke-size=&quot;size23&quot;&gt;7. 기능 중 Action Ring 표시를 선택하고 'LOGI 플러그인 서비스 활성화' 버튼을 선택합니다.&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1630&quot; data-origin-height=&quot;972&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bkqGcP/dJMcadhq9gI/uuUu1JsjXMd2N1VvTnzgLk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bkqGcP/dJMcadhq9gI/uuUu1JsjXMd2N1VvTnzgLk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bkqGcP/dJMcadhq9gI/uuUu1JsjXMd2N1VvTnzgLk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbkqGcP%2FdJMcadhq9gI%2FuuUu1JsjXMd2N1VvTnzgLk%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;1630&quot; height=&quot;972&quot; data-origin-width=&quot;1630&quot; data-origin-height=&quot;972&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;h3 data-ke-size=&quot;size23&quot;&gt;8. 모든 설정이 완료되고 &amp;lsquo;ACTION RING 구성&amp;rsquo; 버튼을 누릅니다.&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1630&quot; data-origin-height=&quot;972&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/blCRnq/dJMcaakIcy5/002ky08jJvBNHsHCZ9azoK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/blCRnq/dJMcaakIcy5/002ky08jJvBNHsHCZ9azoK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/blCRnq/dJMcaakIcy5/002ky08jJvBNHsHCZ9azoK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FblCRnq%2FdJMcaakIcy5%2F002ky08jJvBNHsHCZ9azoK%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;1630&quot; height=&quot;972&quot; data-origin-width=&quot;1630&quot; data-origin-height=&quot;972&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;h3 data-ke-size=&quot;size23&quot;&gt;9. 기본 설정 대신에 앱을 연결하는 기능을 추가합니다 : 열기 &amp;gt; 애플리케이션 열기&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1630&quot; data-origin-height=&quot;972&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/RsZsP/dJMcaf0BlwF/o0zW21dMSYd5fdM1E1PFo0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/RsZsP/dJMcaf0BlwF/o0zW21dMSYd5fdM1E1PFo0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/RsZsP/dJMcaf0BlwF/o0zW21dMSYd5fdM1E1PFo0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FRsZsP%2FdJMcaf0BlwF%2Fo0zW21dMSYd5fdM1E1PFo0%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;1630&quot; height=&quot;972&quot; data-origin-width=&quot;1630&quot; data-origin-height=&quot;972&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;h3 data-ke-size=&quot;size23&quot;&gt;10. 액션 링으로 드래그앤 드랍을 해서 위치를 바꾸면 아래와 같이 나옵니다&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1630&quot; data-origin-height=&quot;972&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/WQWCy/dJMcagL1vsn/z0ZN69kMsqh8ds6z40YKQK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/WQWCy/dJMcagL1vsn/z0ZN69kMsqh8ds6z40YKQK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/WQWCy/dJMcagL1vsn/z0ZN69kMsqh8ds6z40YKQK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FWQWCy%2FdJMcagL1vsn%2Fz0ZN69kMsqh8ds6z40YKQK%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;1630&quot; height=&quot;972&quot; data-origin-width=&quot;1630&quot; data-origin-height=&quot;972&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;h3 data-ke-size=&quot;size23&quot;&gt;11. 아래와 같이 검색해서 추가를 하면 완료가 됩니다&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1630&quot; data-origin-height=&quot;972&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/oH91M/dJMb997bft6/vkoAx4xjYZRqctVA7QLFlK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/oH91M/dJMb997bft6/vkoAx4xjYZRqctVA7QLFlK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/oH91M/dJMb997bft6/vkoAx4xjYZRqctVA7QLFlK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FoH91M%2FdJMb997bft6%2FvkoAx4xjYZRqctVA7QLFlK%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;1630&quot; height=&quot;972&quot; data-origin-width=&quot;1630&quot; data-origin-height=&quot;972&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;h3 data-ke-size=&quot;size23&quot;&gt;12. 아래와 같이 세팅을 마쳤습니다.&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1630&quot; data-origin-height=&quot;972&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/WMVcu/dJMcag6jaze/y3KScrQjotfmliKicKlbHk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/WMVcu/dJMcag6jaze/y3KScrQjotfmliKicKlbHk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/WMVcu/dJMcag6jaze/y3KScrQjotfmliKicKlbHk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FWMVcu%2FdJMcag6jaze%2Fy3KScrQjotfmliKicKlbHk%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;1630&quot; height=&quot;972&quot; data-origin-width=&quot;1630&quot; data-origin-height=&quot;972&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;h3 data-ke-size=&quot;size23&quot;&gt;13. 지정한 액션링 단축키를 누르면 아래와 같이 필요에 따라 빠르게 앱 실행이 가능합니다.&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1351&quot; data-origin-height=&quot;884&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/xiFFi/dJMcaadXJza/kHiaGufLkrKRxqhn8BBWvk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/xiFFi/dJMcaadXJza/kHiaGufLkrKRxqhn8BBWvk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/xiFFi/dJMcaadXJza/kHiaGufLkrKRxqhn8BBWvk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FxiFFi%2FdJMcaadXJza%2FkHiaGufLkrKRxqhn8BBWvk%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;1351&quot; height=&quot;884&quot; data-origin-width=&quot;1351&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;&lt;b&gt;오늘도&amp;nbsp;감사합니다.&amp;nbsp; &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;&amp;nbsp;&lt;/p&gt;</description>
      <category>개발 Tip/기타</category>
      <category>MX Master</category>
      <category>MX Master action ring</category>
      <category>MX Master 마우스 기능</category>
      <category>MX Master 액션 설정</category>
      <category>MX Master 활용방법</category>
      <category>로지텍 action ring</category>
      <author>adjh54</author>
      <guid isPermaLink="true">https://adjh54.tistory.com/745</guid>
      <comments>https://adjh54.tistory.com/745#entry745comment</comments>
      <pubDate>Tue, 21 Apr 2026 20:05:28 +0900</pubDate>
    </item>
    <item>
      <title>[RN] react-native-bootsplash 라이브러리를 활용하여 스플래시 화면 만들기</title>
      <link>https://adjh54.tistory.com/744</link>
      <description>&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;해당 글에서는 React Native 환경에서 react-native-bootsplash 라이브러리를 이용하여 스플래시 화면을 만드는 방법에 대해서 알아봅니다.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;
&lt;figure contenteditable=&quot;false&quot; data-ke-type=&quot;emoticon&quot; data-ke-align=&quot;alignCenter&quot; data-emoticon-type=&quot;face&quot; data-emoticon-name=&quot;073&quot; data-emoticon-isanimation=&quot;false&quot; data-emoticon-src=&quot;https://t1.daumcdn.net/keditor/emoticon/face/large/073.png&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/keditor/emoticon/face/large/073.png&quot; width=&quot;80&quot; /&gt;&lt;/figure&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;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1) &lt;b&gt;react-native-bootsplash&lt;/b&gt;&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  react-native-bootsplash&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;- React Native 앱 실행 시 나타나는 스플래시 스크린(Splash Screen)을 쉽게 구현하기 위한 라이브러리입니다.&lt;br /&gt;- Mathieu Acthernoene(Zoontek)이 만든 라이브러리로, 현재 RN 생태계에서 가장 널리 쓰이는 스플래시 솔루션입니다.&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;a href=&quot;https://www.npmjs.com/package/react-native-bootsplash&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.npmjs.com/package/react-native-bootsplash&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1206&quot; data-origin-height=&quot;2622&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/CgNoB/dJMcafsMhcl/zSpJ374Y5xc89DWcoPMj21/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/CgNoB/dJMcafsMhcl/zSpJ374Y5xc89DWcoPMj21/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/CgNoB/dJMcafsMhcl/zSpJ374Y5xc89DWcoPMj21/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FCgNoB%2FdJMcafsMhcl%2FzSpJ374Y5xc89DWcoPMj21%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;393&quot; height=&quot;854&quot; data-origin-width=&quot;1206&quot; data-origin-height=&quot;2622&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;figure id=&quot;og_1776748200389&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;사픽: 사자성어 퀴즈 - Google Play 앱&quot; data-og-description=&quot;사자성어를 카드로 배우고, 퀴즈와 오답 복습으로 완벽히 암기하는 학습 앱&quot; data-og-host=&quot;play.google.com&quot; data-og-source-url=&quot;https://play.google.com/store/apps/details?id=com.tha.fouridioms&quot; data-og-url=&quot;https://play.google.com/store/apps/details?id=com.tha.fouridioms&amp;amp;hl=ko&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/xoo2W/dJMb8955LVi/WkVyYKWxGuaewLjBWSqfg1/img.png?width=512&amp;amp;height=512&amp;amp;face=0_0_512_512,https://scrap.kakaocdn.net/dn/dyxza4/dJMb82eO9og/gpXg4Joaes3eDaBkSmiB10/img.png?width=600&amp;amp;height=300&amp;amp;face=0_0_600_300,https://scrap.kakaocdn.net/dn/emAo5q/dJMb85WVviY/NgACK7GsjCx0XCB8KIDH4K/img.png?width=240&amp;amp;height=240&amp;amp;face=0_0_240_240&quot;&gt;&lt;a href=&quot;https://play.google.com/store/apps/details?id=com.tha.fouridioms&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://play.google.com/store/apps/details?id=com.tha.fouridioms&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/xoo2W/dJMb8955LVi/WkVyYKWxGuaewLjBWSqfg1/img.png?width=512&amp;amp;height=512&amp;amp;face=0_0_512_512,https://scrap.kakaocdn.net/dn/dyxza4/dJMb82eO9og/gpXg4Joaes3eDaBkSmiB10/img.png?width=600&amp;amp;height=300&amp;amp;face=0_0_600_300,https://scrap.kakaocdn.net/dn/emAo5q/dJMb85WVviY/NgACK7GsjCx0XCB8KIDH4K/img.png?width=240&amp;amp;height=240&amp;amp;face=0_0_240_240');&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;사픽: 사자성어 퀴즈 - Google Play 앱&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;play.google.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;figure id=&quot;og_1776748210585&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;사픽: 사자성어 퀴즈 앱 - App Store&quot; data-og-description=&quot;App&amp;nbsp;Store에서 EcodeLab의 사픽: 사자성어 퀴즈 앱을 다운로드하십시오. 스크린샷, 평가 및 리뷰, 사용자 팁 및 사픽: 사자성어 퀴즈 앱과 비슷한 다른 앱을 볼 수 있습니다.&quot; data-og-host=&quot;apps.apple.com&quot; data-og-source-url=&quot;https://apps.apple.com/kr/app//id6747324308&quot; data-og-url=&quot;https://apps.apple.com/kr/app/%EC%82%AC%ED%94%BD-%EC%82%AC%EC%9E%90%EC%84%B1%EC%96%B4-%ED%80%B4%EC%A6%88/id6747324308&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cstv4c/dJMb8UHRikS/TWGOhE3Z8hWboMKkQf0MT0/img.jpg?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/ch46g1/dJMb8Z3trPj/KNMvT9Z43gcZ1DZVpv4BI0/img.jpg?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630&quot;&gt;&lt;a href=&quot;https://apps.apple.com/kr/app//id6747324308&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://apps.apple.com/kr/app//id6747324308&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cstv4c/dJMb8UHRikS/TWGOhE3Z8hWboMKkQf0MT0/img.jpg?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/ch46g1/dJMb8Z3trPj/KNMvT9Z43gcZ1DZVpv4BI0/img.jpg?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;사픽: 사자성어 퀴즈 앱 - App Store&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;App&amp;nbsp;Store에서 EcodeLab의 사픽: 사자성어 퀴즈 앱을 다운로드하십시오. 스크린샷, 평가 및 리뷰, 사용자 팁 및 사픽: 사자성어 퀴즈 앱과 비슷한 다른 앱을 볼 수 있습니다.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;apps.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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2) 초기 환경 적용 방법&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 라이브러리 설치&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;shell&quot; data-ke-language=&quot;shell&quot;&gt;&lt;code&gt;# npm
$ npm i react-native-bootsplash

# yarn
$ yarn add react-native-bootsplash&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.npmjs.com/package/react-native-bootsplash&quot;&gt;https://www.npmjs.com/package/react-native-bootsplash&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. [Android] MainActivity.kt&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  [Android] MainActivity.kt&lt;br /&gt;&lt;br /&gt;- &amp;nbsp;JS 번들이 로드되기 전에 잠깐 흰 화면이 뜨는데, RNBootSplash.init()이 그 시점에 스플래시 화면을 즉시 표시해 줌.&lt;br /&gt;&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;import android.os.Bundle
import com.zoontek.rnbootsplash.RNBootSplash // &amp;larr; 추가

class MainActivity : ReactActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        RNBootSplash.init(this, R.style.BootTheme) // &amp;larr; 추가
        super.onCreate(savedInstanceState)
    }
}&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;h3 data-ke-size=&quot;size23&quot;&gt;3. [Android] styles.xml&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  [Android] styles.xml&lt;br /&gt;&lt;br /&gt;- 스플래시의 디자인을 적용합니다. 배경색, 로고, 끝나면 어떤 테마로 돌아갈지에 대해 정의&amp;nbsp;&lt;/b&gt;&lt;/blockquote&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;&amp;lt;style name=&quot;BootTheme&quot; parent=&quot;Theme.BootSplash&quot;&amp;gt;
    &amp;lt;item name=&quot;bootSplashBackground&quot;&amp;gt;@color/bootsplash_background&amp;lt;/item&amp;gt;
    &amp;lt;item name=&quot;bootSplashLogo&quot;&amp;gt;@drawable/bootsplash_logo&amp;lt;/item&amp;gt;
    &amp;lt;item name=&quot;postBootSplashTheme&quot;&amp;gt;@style/AppTheme&amp;lt;/item&amp;gt;
&amp;lt;/style&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;h3 data-ke-size=&quot;size23&quot;&gt;4. [Android] Color.xml&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  [Android] Color.xml&lt;/b&gt;&lt;br /&gt;- 스플래시 백그라운 색을 정의합니다&lt;/blockquote&gt;
&lt;pre class=&quot;xml&quot;&gt;&lt;code&gt;&amp;lt;resources&amp;gt;
    &amp;lt;color name=&quot;bootsplash_background&quot;&amp;gt;#ff8c42&amp;lt;/color&amp;gt;
&amp;lt;/resources&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;h3 data-ke-size=&quot;size23&quot;&gt;5. [iOS] AppDelegate.swift&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  [iOS] AppDelegate.swift&lt;/b&gt;&lt;br /&gt;&lt;b&gt;&lt;br /&gt;- iOS 플랫폼에서 React Native 루트 뷰가 만들어지는 시점에 BootSplash.storyboard를 불러와서 스플래시 표시가 됩니다.&lt;/b&gt;&lt;br /&gt;- JS 로드 완료 후 JS 쪽에서 RNBootSplash.hide() 호출하면 사라짐&lt;/blockquote&gt;
&lt;pre class=&quot;swift&quot; data-ke-language=&quot;swift&quot;&gt;&lt;code&gt;import Firebase
import RNBootSplash // &amp;larr; 추가
import React
import ReactAppDependencyProvider
import React_RCTAppDelegate
import UIKit

@main
class AppDelegate: RCTAppDelegate {
  override func application(
    _ application: UIApplication,
    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil
  ) -&amp;gt; Bool {
    self.moduleName = &quot;EmotionalEmoticon&quot;
    self.dependencyProvider = RCTAppDependencyProvider()
    self.initialProps = [:]

    FirebaseApp.configure()
    return super.application(application, didFinishLaunchingWithOptions: launchOptions)
  }

  // ✅ 해당 부분 추가
  override func customize(_ rootView: RCTRootView!) {
    super.customize(rootView)
    RNBootSplash.initWithStoryboard(&quot;BootSplash&quot;, rootView: rootView)
  }

  override func sourceURL(for bridge: RCTBridge) -&amp;gt; URL? {
    self.bundleURL()
  }

  override func bundleURL() -&amp;gt; URL? {
    #if DEBUG
      RCTBundleURLProvider.sharedSettings().jsBundleURL(forBundleRoot: &quot;index&quot;)
    #else
      Bundle.main.url(forResource: &quot;main&quot;, withExtension: &quot;jsbundle&quot;)
    #endif
  }

  override func application(
    _ application: UIApplication, supportedInterfaceOrientationsFor window: UIWindow?
  ) -&amp;gt; UIInterfaceOrientationMask {
    return .portrait
  }
}&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;h3 data-ke-size=&quot;size23&quot;&gt;6. AppLayout.tsx&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  AppLayout.tsx&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- Navigation이 준비되는 시점에 스플래시를 fade 효과로 숨기는 과정입니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;pre class=&quot;xml&quot;&gt;&lt;code&gt;import BootSplash from 'react-native-bootsplash'; // 추가

return (
		&amp;lt;NavigationContainer
			ref={navigationRef}
			onReady={() =&amp;gt; {
				setCurrentRoute(navigationRef.current?.getCurrentRoute()?.name || '');
				BootSplash.hide({ fade: true }); // 추가
			}}
			onStateChange={() =&amp;gt; {
				const routeName = navigationRef.current?.getCurrentRoute()?.name;
				if (routeName) {
					setCurrentRoute(routeName);
				}
			}}&amp;gt;
			&amp;lt;SafeAreaView style={[styles.safeArea, { backgroundColor }]} edges={shouldShowAd ? ['top'] : []}&amp;gt;
				&amp;lt;View style={styles.container}&amp;gt;
					&amp;lt;View style={[styles.adWrapperAbsolute, !shouldShowAd &amp;amp;&amp;amp; { height: 0, opacity: 0 }]}&amp;gt;
						&amp;lt;AdmobBannerAd visible={shouldShowAd} paramMarginTop={0} paramMarginBottom={0} /&amp;gt;
					&amp;lt;/View&amp;gt;
					{shouldShowAd &amp;amp;&amp;amp; &amp;lt;View style={{ paddingTop: getAdPaddingTop() }} /&amp;gt;}
					&amp;lt;View style={[styles.navigatorWrapper, { paddingTop: getNavigatorPaddingTop(shouldShowAd), backgroundColor }]}&amp;gt;
						&amp;lt;StackNavigator /&amp;gt;
					&amp;lt;/View&amp;gt;
				&amp;lt;/View&amp;gt;
			&amp;lt;/SafeAreaView&amp;gt;
		&amp;lt;/NavigationContainer&amp;gt;
	);
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3) 스플래시 이미지 생성 과정-1: Node Server&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  스플래시 이미지 생성 과정-1: Node Server&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- Node Server를 두고 스플래시 로고 이미지를 정확하게 추출해 내기 위해서 Node Server에서 조합하여 react-native-bootsplash에서 요구하는 이미지를 추출해 내는 과정입니다.&lt;/b&gt;&lt;br /&gt;- 해당 과정에서는 메인 아이콘을 기반으로 스플래시 이미지를 간단하게 만듭니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 메인 아이콘을 아래에서 bg를 제거합니다.&lt;/h3&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;style3&quot;&gt;&lt;b&gt; 메인 아이콘을 아래에서 bg를 제거합니다&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 아래에 사픽: 사자성어 퀴즈의 메인 아이콘이 있습니다. 스플레시 이미지의 백그라운드와 겹쳐서 색 조합을 하기가 어렵다는 문제점이 있기에 bg를 빼는 사이트에서 수행을 합니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2048&quot; data-origin-height=&quot;1337&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/JD9G4/dJMcaaLNlBp/cR6HITVLDI2IjPfb5zAc40/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/JD9G4/dJMcaaLNlBp/cR6HITVLDI2IjPfb5zAc40/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/JD9G4/dJMcaaLNlBp/cR6HITVLDI2IjPfb5zAc40/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FJD9G4%2FdJMcaaLNlBp%2FcR6HITVLDI2IjPfb5zAc40%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;2048&quot; height=&quot;1337&quot; data-origin-width=&quot;2048&quot; data-origin-height=&quot;1337&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;figure id=&quot;og_1776750625157&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;ndash; remove.bg&quot; data-og-description=&quot;사진이나 이미지 배경을 한 번 클릭으로 5초만에 무료로 제거할 수 있습니다. 이미지 배경 투명하게 만드는 법. 누끼 따기 프로그램.&quot; data-og-host=&quot;www.remove.bg&quot; data-og-source-url=&quot;https://www.remove.bg/ko&quot; data-og-url=&quot;https://www.remove.bg/ko&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bRK4pP/dJMb85vQ6G6/Ir2T1NOpcfrNlondCn3S00/img.jpg?width=600&amp;amp;height=315&amp;amp;face=326_85_398_164,https://scrap.kakaocdn.net/dn/buq5yu/dJMb8VNxykI/1Zj4bWUUTX3y1TzNoWXl71/img.jpg?width=600&amp;amp;height=315&amp;amp;face=326_85_398_164&quot;&gt;&lt;a href=&quot;https://www.remove.bg/ko&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.remove.bg/ko&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bRK4pP/dJMb85vQ6G6/Ir2T1NOpcfrNlondCn3S00/img.jpg?width=600&amp;amp;height=315&amp;amp;face=326_85_398_164,https://scrap.kakaocdn.net/dn/buq5yu/dJMb8VNxykI/1Zj4bWUUTX3y1TzNoWXl71/img.jpg?width=600&amp;amp;height=315&amp;amp;face=326_85_398_164');&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;ndash; remove.bg&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;사진이나 이미지 배경을 한 번 클릭으로 5초만에 무료로 제거할 수 있습니다. 이미지 배경 투명하게 만드는 법. 누끼 따기 프로그램.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.remove.bg&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;style3&quot;&gt;&lt;b&gt;  수행 결과&lt;/b&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1434&quot; data-origin-height=&quot;710&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/oyZ6j/dJMcadIsN8z/PVTllwkvxN64VSGeoeW760/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/oyZ6j/dJMcadIsN8z/PVTllwkvxN64VSGeoeW760/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/oyZ6j/dJMcadIsN8z/PVTllwkvxN64VSGeoeW760/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FoyZ6j%2FdJMcadIsN8z%2FPVTllwkvxN64VSGeoeW760%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;1434&quot; height=&quot;710&quot; data-origin-width=&quot;1434&quot; data-origin-height=&quot;710&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;h3 data-ke-size=&quot;size23&quot;&gt;2. 노드 서버 구축&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  노드 서버 구축&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 아래와 같이 &amp;lsquo;sharp&amp;rsquo;라는 이름으로 구성하였고, image/*, main.js 파일 형태로 구성이 되어 있습니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;314&quot; data-origin-height=&quot;165&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/drQntT/dJMcacbLoeb/7cBx9rKZNRrRHFxgURF9OK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/drQntT/dJMcacbLoeb/7cBx9rKZNRrRHFxgURF9OK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/drQntT/dJMcacbLoeb/7cBx9rKZNRrRHFxgURF9OK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdrQntT%2FdJMcacbLoeb%2F7cBx9rKZNRrRHFxgURF9OK%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;314&quot; height=&quot;165&quot; data-origin-width=&quot;314&quot; data-origin-height=&quot;165&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;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  Node에서 이미지를 처리하는 라이브러리로 &amp;lsquo;sharp&amp;rsquo;를 이용하여서 구성하였습니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;pre class=&quot;coffeescript&quot;&gt;&lt;code&gt;# 아래 라이브러리를 설치하였습니다.
npm i sharp fs
&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;h3 data-ke-size=&quot;size23&quot;&gt;3. image/ 경로에 bg를 제거한 이미지를 옮겨두었습니다.&lt;/h3&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;style3&quot;&gt;&lt;b&gt; image/ 경로에 bg를 제거한 이미지를 옮겨두었습니다.&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;- 이미지 파일명은 bgRemoveImage로 지정을 하였습니다.&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;317&quot; data-origin-height=&quot;450&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/eITTiM/dJMcabqpHdG/8ZwhsmLw9yHL4OomdkgZSK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/eITTiM/dJMcabqpHdG/8ZwhsmLw9yHL4OomdkgZSK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/eITTiM/dJMcabqpHdG/8ZwhsmLw9yHL4OomdkgZSK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FeITTiM%2FdJMcabqpHdG%2F8ZwhsmLw9yHL4OomdkgZSK%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;317&quot; height=&quot;450&quot; data-origin-width=&quot;317&quot; data-origin-height=&quot;450&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;h3 data-ke-size=&quot;size23&quot;&gt;4. main.js&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  main.js&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;1. characterBuf&lt;/b&gt; 과정에서는 사전에 준비한 이미지를 리사이즈합니다.&lt;br /&gt;&lt;b&gt;2. compositeBuf&lt;/b&gt; 과정에서는 캐릭터와 SVG(배경 그라디언트, 스파클, 메인텍스트, 서브텍스트)로 구성이 되어 있고, 이를 함께 합성합니다.&lt;br /&gt;&lt;b&gt;3. sharp&lt;/b&gt; 과정에서는 두 개 캐릭터와 SVG를 합성하여서 최종적으로 react-native-bootsplash가 요구한 결과값으로 출력을 합니다.&lt;/blockquote&gt;
&lt;pre class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;import sharp from &quot;sharp&quot;;
import { mkdirSync } from &quot;fs&quot;;

const width = 500;
const height = 500;

mkdirSync(&quot;./output&quot;, { recursive: true });

const sparkles = `
  ✦import sharp from &quot;sharp&quot;;
import { mkdirSync } from &quot;fs&quot;;

const width = 500;
const height = 500;

mkdirSync(&quot;./output&quot;, { recursive: true });

const sparkles = `
  &amp;lt;text x=&quot;56&quot;  y=&quot;108&quot; font-size=&quot;22&quot; text-anchor=&quot;middle&quot; opacity=&quot;0.5&quot;  fill=&quot;#FFF5E6&quot;&amp;gt;✦&amp;lt;/text&amp;gt;
  &amp;lt;text x=&quot;448&quot; y=&quot;154&quot; font-size=&quot;18&quot; text-anchor=&quot;middle&quot; opacity=&quot;0.45&quot; fill=&quot;#FFF5E6&quot;&amp;gt;✦&amp;lt;/text&amp;gt;
  &amp;lt;text x=&quot;66&quot;  y=&quot;352&quot; font-size=&quot;14&quot; text-anchor=&quot;middle&quot; opacity=&quot;0.35&quot; fill=&quot;#FFF5E6&quot;&amp;gt;✧&amp;lt;/text&amp;gt;
  &amp;lt;text x=&quot;438&quot; y=&quot;322&quot; font-size=&quot;20&quot; text-anchor=&quot;middle&quot; opacity=&quot;0.45&quot; fill=&quot;#FFF5E6&quot;&amp;gt;✦&amp;lt;/text&amp;gt;
  &amp;lt;text x=&quot;110&quot; y=&quot;52&quot;  font-size=&quot;12&quot; text-anchor=&quot;middle&quot; opacity=&quot;0.4&quot;  fill=&quot;#FFF5E6&quot;&amp;gt;✧&amp;lt;/text&amp;gt;
  &amp;lt;text x=&quot;382&quot; y=&quot;70&quot;  font-size=&quot;16&quot; text-anchor=&quot;middle&quot; opacity=&quot;0.45&quot; fill=&quot;#FFF5E6&quot;&amp;gt;✦&amp;lt;/text&amp;gt;
  &amp;lt;text x=&quot;458&quot; y=&quot;412&quot; font-size=&quot;12&quot; text-anchor=&quot;middle&quot; opacity=&quot;0.32&quot; fill=&quot;#FFF5E6&quot;&amp;gt;✧&amp;lt;/text&amp;gt;
  &amp;lt;text x=&quot;42&quot;  y=&quot;438&quot; font-size=&quot;12&quot; text-anchor=&quot;middle&quot; opacity=&quot;0.32&quot; fill=&quot;#FFF5E6&quot;&amp;gt;✧&amp;lt;/text&amp;gt;
  &amp;lt;text x=&quot;198&quot; y=&quot;42&quot;  font-size=&quot;10&quot; text-anchor=&quot;middle&quot; opacity=&quot;0.35&quot; fill=&quot;#FFF5E6&quot;&amp;gt;&amp;middot;&amp;lt;/text&amp;gt;
  &amp;lt;text x=&quot;308&quot; y=&quot;36&quot;  font-size=&quot;11&quot; text-anchor=&quot;middle&quot; opacity=&quot;0.3&quot;  fill=&quot;#FFF5E6&quot;&amp;gt;&amp;middot;&amp;lt;/text&amp;gt;
  &amp;lt;text x=&quot;476&quot; y=&quot;234&quot; font-size=&quot;10&quot; text-anchor=&quot;middle&quot; opacity=&quot;0.25&quot; fill=&quot;#FFF5E6&quot;&amp;gt;&amp;middot;&amp;lt;/text&amp;gt;
  &amp;lt;text x=&quot;24&quot;  y=&quot;250&quot; font-size=&quot;11&quot; text-anchor=&quot;middle&quot; opacity=&quot;0.25&quot; fill=&quot;#FFF5E6&quot;&amp;gt;&amp;middot;&amp;lt;/text&amp;gt;
  &amp;lt;text x=&quot;458&quot; y=&quot;468&quot; font-size=&quot;16&quot; text-anchor=&quot;middle&quot; opacity=&quot;0.55&quot; fill=&quot;#FFF5E6&quot;&amp;gt;✦&amp;lt;/text&amp;gt;
`;


const mainTitle = &quot;사픽: 사자성어 퀴즈&quot;;
const subTitle = &quot;사자성어 퀴즈로 배우는 쉽고 재미있는 한자 학습&quot;;

const svg = `
&amp;lt;svg width=&quot;${width}&quot; height=&quot;${height}&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot;&amp;gt;
  &amp;lt;defs&amp;gt;
    &amp;lt;radialGradient id=&quot;bgGrad&quot; cx=&quot;38%&quot; cy=&quot;32%&quot; r=&quot;72%&quot;&amp;gt;
      &amp;lt;stop offset=&quot;0%&quot;   stop-color=&quot;#FFB366&quot;/&amp;gt;
      &amp;lt;stop offset=&quot;55%&quot;  stop-color=&quot;#FF8C42&quot;/&amp;gt;
      &amp;lt;stop offset=&quot;100%&quot; stop-color=&quot;#D95F1E&quot;/&amp;gt;
    &amp;lt;/radialGradient&amp;gt;
    &amp;lt;radialGradient id=&quot;glowBottom&quot; cx=&quot;50%&quot; cy=&quot;100%&quot; r=&quot;55%&quot;&amp;gt;
      &amp;lt;stop offset=&quot;0%&quot;   stop-color=&quot;#FFD4A3&quot; stop-opacity=&quot;0.55&quot;/&amp;gt;
      &amp;lt;stop offset=&quot;100%&quot; stop-color=&quot;#FF8C42&quot; stop-opacity=&quot;0&quot;/&amp;gt;
    &amp;lt;/radialGradient&amp;gt;
    &amp;lt;linearGradient id=&quot;textGrad&quot; x1=&quot;0%&quot; y1=&quot;0%&quot; x2=&quot;100%&quot; y2=&quot;0%&quot;&amp;gt;
      &amp;lt;stop offset=&quot;0%&quot;   stop-color=&quot;#FFFBF2&quot;/&amp;gt;
      &amp;lt;stop offset=&quot;100%&quot; stop-color=&quot;#FFE4C4&quot;/&amp;gt;
    &amp;lt;/linearGradient&amp;gt;
    &amp;lt;linearGradient id=&quot;lineGrad&quot; x1=&quot;0%&quot; y1=&quot;0%&quot; x2=&quot;100%&quot; y2=&quot;0%&quot;&amp;gt;
      &amp;lt;stop offset=&quot;0%&quot;   stop-color=&quot;#FFF5E6&quot; stop-opacity=&quot;0&quot;/&amp;gt;
      &amp;lt;stop offset=&quot;50%&quot;  stop-color=&quot;#FFF5E6&quot; stop-opacity=&quot;0.75&quot;/&amp;gt;
      &amp;lt;stop offset=&quot;100%&quot; stop-color=&quot;#FFF5E6&quot; stop-opacity=&quot;0&quot;/&amp;gt;
    &amp;lt;/linearGradient&amp;gt;
  &amp;lt;/defs&amp;gt;

  &amp;lt;!-- 하단 글로우 타원 --&amp;gt;
  &amp;lt;ellipse cx=&quot;250&quot; cy=&quot;455&quot; rx=&quot;210&quot; ry=&quot;50&quot; fill=&quot;url(#glowBottom)&quot; opacity=&quot;0.65&quot;/&amp;gt;

  &amp;lt;!-- 스파클 --&amp;gt;
  ${sparkles}

  &amp;lt;!-- 구분선 --&amp;gt;
  &amp;lt;line x1=&quot;125&quot; y1=&quot;410&quot; x2=&quot;375&quot; y2=&quot;410&quot;
    stroke=&quot;url(#lineGrad)&quot; stroke-width=&quot;1.2&quot; stroke-linecap=&quot;round&quot;/&amp;gt;

  &amp;lt;!-- 메인 텍스트 --&amp;gt;
  &amp;lt;text x=&quot;250&quot; y=&quot;444&quot; text-anchor=&quot;middle&quot;
    font-family=&quot;'Apple SD Gothic Neo', 'Noto Sans KR', Arial, sans-serif&quot;
    font-size=&quot;40&quot; font-weight=&quot;bold&quot;
    fill=&quot;url(#textGrad)&quot; letter-spacing=&quot;1&quot;&amp;gt;${mainTitle}&amp;lt;/text&amp;gt;

  &amp;lt;!-- 서브 텍스트 --&amp;gt;
  &amp;lt;text x=&quot;250&quot; y=&quot;478&quot; text-anchor=&quot;middle&quot;
    font-family=&quot;'Apple SD Gothic Neo', 'Noto Sans KR', Arial, sans-serif&quot;
    font-size=&quot;14&quot;
    fill=&quot;#FFE4C4&quot; letter-spacing=&quot;2.5&quot; opacity=&quot;0.95&quot;&amp;gt;${subTitle}&amp;lt;/text&amp;gt;
&amp;lt;/svg&amp;gt;`;

const characterBuf = await sharp(&quot;./image/bgRemoveImg.png&quot;)
  .resize(420, 420)
  .toBuffer();

const compositeBuf = await sharp({
  create: {
    width,
    height,
    channels: 4,
    background: { r: 0, g: 0, b: 0, alpha: 0 },
  },
})
  .composite([
    { input: characterBuf, top: 0, left: 40 },
    { input: Buffer.from(svg), top: 0, left: 0 },
  ])
  .png()
  .toBuffer();

await sharp(compositeBuf)
  .resize(500, 500, {
    fit: &quot;contain&quot;,
    background: { r: 0, g: 0, b: 0, alpha: 0 },
  })
  .png()
  .toFile(&quot;./output/bootsplash_logo.png&quot;);

console.log(&quot;완료! ./output/bootsplash_logo.png 생성됨&quot;);

  ✦
  ✧
  ✦
  ✧
  ✦
  ✧
  ✧
  &amp;middot;
  &amp;middot;
  &amp;middot;
  &amp;middot;
  ✦
`;

const mainTitle = &quot;사픽: 사자성어 퀴즈&quot;;
const subTitle = &quot;사자성어 퀴즈로 배우는 쉽고 재미있는 한자 학습&quot;;

const svg = `

  
    
      
      
      
    
    
      
      
    
    
      
      
    
    
      
      
      
    
  

  
  

  
  ${sparkles}

  
  

  
  ${mainTitle}

  
  ${subTitle}
`;

const characterBuf = await sharp(&quot;./image/bgRemoveImg.png&quot;)
  .resize(420, 420)
  .toBuffer();

const compositeBuf = await sharp({
  create: {
    width,
    height,
    channels: 4,
    background: { r: 0, g: 0, b: 0, alpha: 0 },
  },
})
  .composite([
    { input: characterBuf, top: 0, left: 40 },
    { input: Buffer.from(svg), top: 0, left: 0 },
  ])
  .png()
  .toBuffer();

await sharp(compositeBuf)
  .resize(500, 500, {
    fit: &quot;contain&quot;,
    background: { r: 0, g: 0, b: 0, alpha: 0 },
  })
  .png()
  .toFile(&quot;./output/bootsplash_logo.png&quot;);

console.log(&quot;완료! ./output/bootsplash_logo.png 생성됨&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5. node를 실행하여 결과값을 확인합니다.&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  node를 실행하여 결과값을 확인합니다.&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;- 합성된 이미지를 결과값으로 반환받습니다.&lt;/blockquote&gt;
&lt;pre class=&quot;crmsh&quot;&gt;&lt;code&gt;node main.js
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1145&quot; data-origin-height=&quot;234&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/d11gxc/dJMcaaE2Nsm/YFMK7Mqk4YirPGdJd8VPAK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/d11gxc/dJMcaaE2Nsm/YFMK7Mqk4YirPGdJd8VPAK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/d11gxc/dJMcaaE2Nsm/YFMK7Mqk4YirPGdJd8VPAK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fd11gxc%2FdJMcaaE2Nsm%2FYFMK7Mqk4YirPGdJd8VPAK%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;1145&quot; height=&quot;234&quot; data-origin-width=&quot;1145&quot; data-origin-height=&quot;234&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;h3 data-ke-size=&quot;size23&quot;&gt;6. 결과적으로 output/bootsplash_logo.png 파일이 출력이 됩니다.&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2032&quot; data-origin-height=&quot;1078&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/AZoSk/dJMcahqCEbm/Die9Iq5Y7chEdPmVGXnytK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/AZoSk/dJMcahqCEbm/Die9Iq5Y7chEdPmVGXnytK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/AZoSk/dJMcahqCEbm/Die9Iq5Y7chEdPmVGXnytK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FAZoSk%2FdJMcahqCEbm%2FDie9Iq5Y7chEdPmVGXnytK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2032&quot; height=&quot;1078&quot; data-origin-width=&quot;2032&quot; data-origin-height=&quot;1078&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3) 스플래시 이미지 생성 과정-1: React Native&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. RN 프로젝트 내에 해당 이미지를 복사 &amp;amp; 붙여 넣기 한다.&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;278&quot; data-origin-height=&quot;465&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/eFegYe/dJMcabcPgJY/DUiY0Jie4ObwnJ4unVfUO1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/eFegYe/dJMcabcPgJY/DUiY0Jie4ObwnJ4unVfUO1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/eFegYe/dJMcabcPgJY/DUiY0Jie4ObwnJ4unVfUO1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FeFegYe%2FdJMcabcPgJY%2FDUiY0Jie4ObwnJ4unVfUO1%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;278&quot; height=&quot;465&quot; data-origin-width=&quot;278&quot; data-origin-height=&quot;465&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;h3 data-ke-size=&quot;size23&quot;&gt;2. RN 프로젝트에서 아래의 명령어를 입력합니다.&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  RN 프로젝트에서 아래의 명령어를 입력합니다.&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;- react-native-bootsplash의 자산 자동 생성 CLI 명령어입니다.&lt;br /&gt;- 여기서 주요한 내용은 background입니다. 사전에 이미지는 bg를 제거한 상태입니다. &lt;br /&gt;- 그렇기에 이에 알맞은 hex 색상에 맞게 적용을 합니다.&lt;/blockquote&gt;
&lt;pre class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;# Android
yarn react-native-bootsplash generate ./src/assets/images/bootsplash_logo.png \
  --platforms=android \
  --background=&quot;#ff8c42&quot; \
  --logo-width=134 \
  --flavor=main

# iOS
yarn react-native-bootsplash generate ./src/assets/images/bootsplash_logo.png \
  --platforms=ios \
  --background=&quot;#ff8c42&quot; \
  --logo-width=400 \
  --flavor=main&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;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;[ 더 알아보기]&amp;nbsp;&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;  안드로이드, iOS 플랫폼에 분리하는 이유는 뭘까?&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;- 아래와 같이 'Logo exceeds 192x192dp and will be cropped by Android. Skipping its generation.'가 발생합니다.&lt;br /&gt;- Android는 최대 192x192dp 제한이 있어서 Android 생성을 건너뛰어지게 됩니다.&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;741&quot; data-origin-height=&quot;423&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bU7bmk/dJMcacbLoyc/UL5yD3QxfN0erhyDl7k0ek/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bU7bmk/dJMcacbLoyc/UL5yD3QxfN0erhyDl7k0ek/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bU7bmk/dJMcacbLoyc/UL5yD3QxfN0erhyDl7k0ek/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbU7bmk%2FdJMcacbLoyc%2FUL5yD3QxfN0erhyDl7k0ek%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;741&quot; height=&quot;423&quot; data-origin-width=&quot;741&quot; data-origin-height=&quot;423&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 data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. 결론적으로 아래와 같이 스플래시 결과물이 적용이 됩니다.&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2032&quot; data-origin-height=&quot;1075&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/E0IFM/dJMcacpiU1S/HH2e4k0BHSdhBhcvPaFrC1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/E0IFM/dJMcacpiU1S/HH2e4k0BHSdhBhcvPaFrC1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/E0IFM/dJMcacpiU1S/HH2e4k0BHSdhBhcvPaFrC1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FE0IFM%2FdJMcacpiU1S%2FHH2e4k0BHSdhBhcvPaFrC1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2032&quot; height=&quot;1075&quot; data-origin-width=&quot;2032&quot; data-origin-height=&quot;1075&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;h3 data-ke-size=&quot;size23&quot;&gt;4. 앱 자체를 재빌드 해야 하며 아래와 같은 결과를 얻을 수 있습니다.&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1206&quot; data-origin-height=&quot;2622&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/xDRF9/dJMcaiiJOnZ/kY93iNRcKb5UGYoHkLt4t1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/xDRF9/dJMcaiiJOnZ/kY93iNRcKb5UGYoHkLt4t1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/xDRF9/dJMcaiiJOnZ/kY93iNRcKb5UGYoHkLt4t1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FxDRF9%2FdJMcaiiJOnZ%2FkY93iNRcKb5UGYoHkLt4t1%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;640&quot; height=&quot;1391&quot; data-origin-width=&quot;1206&quot; data-origin-height=&quot;2622&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;figure id=&quot;og_1776751304555&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;사픽: 사자성어 퀴즈 - Google Play 앱&quot; data-og-description=&quot;사자성어를 카드로 배우고, 퀴즈와 오답 복습으로 완벽히 암기하는 학습 앱&quot; data-og-host=&quot;play.google.com&quot; data-og-source-url=&quot;https://play.google.com/store/apps/details?id=com.tha.fouridioms&quot; data-og-url=&quot;https://play.google.com/store/apps/details?id=com.tha.fouridioms&amp;amp;hl=ko&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/xoo2W/dJMb8955LVi/WkVyYKWxGuaewLjBWSqfg1/img.png?width=512&amp;amp;height=512&amp;amp;face=0_0_512_512,https://scrap.kakaocdn.net/dn/dyxza4/dJMb82eO9og/gpXg4Joaes3eDaBkSmiB10/img.png?width=600&amp;amp;height=300&amp;amp;face=0_0_600_300,https://scrap.kakaocdn.net/dn/emAo5q/dJMb85WVviY/NgACK7GsjCx0XCB8KIDH4K/img.png?width=240&amp;amp;height=240&amp;amp;face=0_0_240_240&quot;&gt;&lt;a href=&quot;https://play.google.com/store/apps/details?id=com.tha.fouridioms&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://play.google.com/store/apps/details?id=com.tha.fouridioms&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/xoo2W/dJMb8955LVi/WkVyYKWxGuaewLjBWSqfg1/img.png?width=512&amp;amp;height=512&amp;amp;face=0_0_512_512,https://scrap.kakaocdn.net/dn/dyxza4/dJMb82eO9og/gpXg4Joaes3eDaBkSmiB10/img.png?width=600&amp;amp;height=300&amp;amp;face=0_0_600_300,https://scrap.kakaocdn.net/dn/emAo5q/dJMb85WVviY/NgACK7GsjCx0XCB8KIDH4K/img.png?width=240&amp;amp;height=240&amp;amp;face=0_0_240_240');&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;사픽: 사자성어 퀴즈 - Google Play 앱&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;play.google.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;figure id=&quot;og_1776751312477&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;사픽: 사자성어 퀴즈 앱 - App Store&quot; data-og-description=&quot;App&amp;nbsp;Store에서 EcodeLab의 사픽: 사자성어 퀴즈 앱을 다운로드하십시오. 스크린샷, 평가 및 리뷰, 사용자 팁 및 사픽: 사자성어 퀴즈 앱과 비슷한 다른 앱을 볼 수 있습니다.&quot; data-og-host=&quot;apps.apple.com&quot; data-og-source-url=&quot;https://apps.apple.com/kr/app//id6747324308&quot; data-og-url=&quot;https://apps.apple.com/kr/app/%EC%82%AC%ED%94%BD-%EC%82%AC%EC%9E%90%EC%84%B1%EC%96%B4-%ED%80%B4%EC%A6%88/id6747324308&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cstv4c/dJMb8UHRikS/TWGOhE3Z8hWboMKkQf0MT0/img.jpg?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/ch46g1/dJMb8Z3trPj/KNMvT9Z43gcZ1DZVpv4BI0/img.jpg?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630&quot;&gt;&lt;a href=&quot;https://apps.apple.com/kr/app//id6747324308&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://apps.apple.com/kr/app//id6747324308&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cstv4c/dJMb8UHRikS/TWGOhE3Z8hWboMKkQf0MT0/img.jpg?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/ch46g1/dJMb8Z3trPj/KNMvT9Z43gcZ1DZVpv4BI0/img.jpg?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;사픽: 사자성어 퀴즈 앱 - App Store&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;App&amp;nbsp;Store에서 EcodeLab의 사픽: 사자성어 퀴즈 앱을 다운로드하십시오. 스크린샷, 평가 및 리뷰, 사용자 팁 및 사픽: 사자성어 퀴즈 앱과 비슷한 다른 앱을 볼 수 있습니다.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;apps.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;&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;오늘도&amp;nbsp;감사합니다.&amp;nbsp; &lt;/b&gt;&lt;/p&gt;</description>
      <category>React &amp;amp; React Native/라이브러리 활용</category>
      <category>react native</category>
      <category>react native 스플래시</category>
      <category>react native 앱 아이콘 스플래시</category>
      <category>react-native-bootsplash</category>
      <category>react-native-bootsplash 이미지</category>
      <category>스플래시 이미지</category>
      <author>adjh54</author>
      <guid isPermaLink="true">https://adjh54.tistory.com/744</guid>
      <comments>https://adjh54.tistory.com/744#entry744comment</comments>
      <pubDate>Tue, 21 Apr 2026 20:00:35 +0900</pubDate>
    </item>
    <item>
      <title>[Java] p6spy-spring-boot-starter 이해하고 활용하기 : MyBatis + Log4j2 + Slf4j</title>
      <link>https://adjh54.tistory.com/743</link>
      <description>&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;해당 글에서는 MyBatis를 이용하기 위해 DB Fomratter로 p6spy 적용 방법 및 활용 방법에 대해 알아봅니다&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;
&lt;figure contenteditable=&quot;false&quot; data-ke-type=&quot;emoticon&quot; data-ke-align=&quot;alignCenter&quot; data-emoticon-type=&quot;face&quot; data-emoticon-name=&quot;073&quot; data-emoticon-isanimation=&quot;false&quot; data-emoticon-src=&quot;https://t1.daumcdn.net/keditor/emoticon/face/large/073.png&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/keditor/emoticon/face/large/073.png&quot; width=&quot;80&quot; /&gt;&lt;/figure&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1) p6spy-spring-boot-starter&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  p6spy-spring-boot-starter&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- SQL 쿼리를 실제 실행되는 완성된 형태(파라미터 바인딩 포함)로 로깅해 주는 라이브러리를 의미합니다.&lt;/b&gt;&lt;br /&gt;- JPA/MyBatis 환경에서 ?로 표시되는 파라미터 값을 실제 값으로 치환해서 보여줍니다.&lt;br /&gt;- MyBatis를 이용한 경우 log4jdbc-log4j2를 이용하였지만, 2013년 이후에 업데이트가 중단되었고 설정이 복잡하다는 점이 있었습니다. 그렇기에 p6spy-spring-boot-starter를 적용해 봅니다.&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;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  [참고]&lt;/b&gt; 기존의 log4jdbc-log4j2에 대해 궁금하시면 아래의 글을 참고하시면 도움이 됩니다.&lt;/blockquote&gt;
&lt;figure id=&quot;og_1772675691395&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;[Java] MyBatis Query Formatter 이해하고 적용하기 : log4jdbc-log4j2 4.1&quot; data-og-description=&quot;해당 글에서는 MyBatis로 처리되는 Query에 대한 Formatting을 적용하기 위해 &amp;lsquo;log4 jdbc-log4j2&amp;rsquo;를 적용하는 글에 대해 공유합니다. 1) log4jdbc-log4j2   log4jdbc-log4j2 - 자바 애플리케이션에서 &amp;lsquo;JDBC 드라이&quot; data-og-host=&quot;adjh54.tistory.com&quot; data-og-source-url=&quot;https://adjh54.tistory.com/228&quot; data-og-url=&quot;https://adjh54.tistory.com/228&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/wA8M1/dJMb85vMGkh/uWklGaSug7kUzEQt0SI8PK/img.png?width=800&amp;amp;height=432&amp;amp;face=0_0_800_432,https://scrap.kakaocdn.net/dn/dvrq4K/dJMb86nVhTS/5uvGmFKEKI8Uycpz761FRK/img.png?width=800&amp;amp;height=432&amp;amp;face=0_0_800_432,https://scrap.kakaocdn.net/dn/buXNLj/dJMb86nVhTT/jxVaVJ7DdhNTVET9uR7c01/img.png?width=2286&amp;amp;height=606&amp;amp;face=0_0_2286_606&quot;&gt;&lt;a href=&quot;https://adjh54.tistory.com/228&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://adjh54.tistory.com/228&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/wA8M1/dJMb85vMGkh/uWklGaSug7kUzEQt0SI8PK/img.png?width=800&amp;amp;height=432&amp;amp;face=0_0_800_432,https://scrap.kakaocdn.net/dn/dvrq4K/dJMb86nVhTS/5uvGmFKEKI8Uycpz761FRK/img.png?width=800&amp;amp;height=432&amp;amp;face=0_0_800_432,https://scrap.kakaocdn.net/dn/buXNLj/dJMb86nVhTT/jxVaVJ7DdhNTVET9uR7c01/img.png?width=2286&amp;amp;height=606&amp;amp;face=0_0_2286_606');&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;[Java] MyBatis Query Formatter 이해하고 적용하기 : log4jdbc-log4j2 4.1&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;해당 글에서는 MyBatis로 처리되는 Query에 대한 Formatting을 적용하기 위해 &amp;lsquo;log4 jdbc-log4j2&amp;rsquo;를 적용하는 글에 대해 공유합니다. 1) log4jdbc-log4j2   log4jdbc-log4j2 - 자바 애플리케이션에서 &amp;lsquo;JDBC 드라이&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;adjh54.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;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  [참고]&lt;/b&gt; p6spy vs log4jdbc-log4j2 비교&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style2&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;항목&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;p6spy&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;log4jdbc-log4j2&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Spring Boot AutoConfig&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;자동&lt;/td&gt;
&lt;td&gt;수동 Bean 설정 필요&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;유지보수 상태&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;활발&lt;/td&gt;
&lt;td&gt;사실상 중단&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;최신 Spring Boot 호환&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;3.x 완벽 지원&lt;/td&gt;
&lt;td&gt;호환 이슈 잦음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;설정 난이도&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;쉬움&lt;/td&gt;
&lt;td&gt;복잡함&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;커스터마이징&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Java Config 가능&lt;/td&gt;
&lt;td&gt;제한적&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;실행시간 측정&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;기본 제공&lt;/td&gt;
&lt;td&gt;기본 제공&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;멀티 DataSource&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;자동 처리&lt;/td&gt;
&lt;td&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;h2 data-ke-size=&quot;size26&quot;&gt;2) p6spy-spring-boot-starter 환경 설정&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  p6spy-spring-boot-starter 환경 설정&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 의존성 추가&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;&amp;nbsp;  의존성 추가&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 가장 최신 버전을 기반으로 설치를 합니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;// Source: &amp;lt;https://mvnrepository.com/artifact/com.github.gavlyukovskiy/p6spy-spring-boot-starter&amp;gt;
implementation(&quot;com.github.gavlyukovskiy:p6spy-spring-boot-starter:2.0.0&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;a href=&quot;https://mvnrepository.com/artifact/com.github.gavlyukovskiy/p6spy-spring-boot-starter/2.0.0&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://mvnrepository.com/artifact/com.github.gavlyukovskiy/p6spy-spring-boot-starter/2.0.0&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. application.yml&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  application.yml&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 기존의 Log4j2 + Slf4J를 기반으로 로깅을 구성하였고, yml 파일 형태로 환경을 설정하였습니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;pre class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot;&gt;&lt;code&gt;# p6spy-spring-boot-starter Configuration
decorator:
  datasource:
    p6spy:
      enable-logging: true
      logging: slf4j
      # SQL을 한 줄로 정렬해서 보여줄지 여부
      multiline: true

# spring-boot-starter-log4j2 Configuration
logging:
  # default Logging 프레임워크 설정 파일을 참조
  config: classpath:config/logs/log4j2-loc.yml&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;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  [참고]&lt;/b&gt;&amp;nbsp;Spring Boot Log4j2 이해하기 -1 : 주요 특징, 구성 요소, yml 설정방법&lt;/blockquote&gt;
&lt;figure id=&quot;og_1772675951458&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;[Java] Spring Boot Log4j2 이해하기 -1 : 주요 특징, 구성 요소, yml 설정방법&quot; data-og-description=&quot;해당 글에서는 Log4j2에 대해 이해하고 Spring Boot 환경에서 Log4j2를 설정하는 방법에 대해 알아봅니다.   [참고] xml 형태로 간단한 설정을 하는 방법에 대해 알고 싶으시면 이전에 작성한 글을 참&quot; data-og-host=&quot;adjh54.tistory.com&quot; data-og-source-url=&quot;https://adjh54.tistory.com/388&quot; data-og-url=&quot;https://adjh54.tistory.com/388&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/ituKF/dJMb9efbHn4/nRbkGOOqrgdkxlnplO1xjk/img.png?width=800&amp;amp;height=431&amp;amp;face=0_0_800_431,https://scrap.kakaocdn.net/dn/MJBBF/dJMb9jOkItV/e0lW1knohRsKgElzTSPChK/img.png?width=800&amp;amp;height=431&amp;amp;face=0_0_800_431,https://scrap.kakaocdn.net/dn/ovG9t/dJMb9fZs4Bz/6Ki7N8lAvNf4fHIpipMAX0/img.png?width=1839&amp;amp;height=601&amp;amp;face=0_0_1839_601&quot;&gt;&lt;a href=&quot;https://adjh54.tistory.com/388&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://adjh54.tistory.com/388&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/ituKF/dJMb9efbHn4/nRbkGOOqrgdkxlnplO1xjk/img.png?width=800&amp;amp;height=431&amp;amp;face=0_0_800_431,https://scrap.kakaocdn.net/dn/MJBBF/dJMb9jOkItV/e0lW1knohRsKgElzTSPChK/img.png?width=800&amp;amp;height=431&amp;amp;face=0_0_800_431,https://scrap.kakaocdn.net/dn/ovG9t/dJMb9fZs4Bz/6Ki7N8lAvNf4fHIpipMAX0/img.png?width=1839&amp;amp;height=601&amp;amp;face=0_0_1839_601');&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;[Java] Spring Boot Log4j2 이해하기 -1 : 주요 특징, 구성 요소, yml 설정방법&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;해당 글에서는 Log4j2에 대해 이해하고 Spring Boot 환경에서 Log4j2를 설정하는 방법에 대해 알아봅니다.   [참고] xml 형태로 간단한 설정을 하는 방법에 대해 알고 싶으시면 이전에 작성한 글을 참&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;adjh54.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;h3 data-ke-size=&quot;size23&quot;&gt;3. logj42-loc.yml&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  log4j2-loc.yml&lt;/b&gt;&lt;br /&gt;&lt;b&gt;&lt;br /&gt;- 해당 부분에서는 log4j2 로깅 설정을 하는 파일입니다.&lt;/b&gt;&lt;br /&gt;- 이 중에서도 com.p6spy.engine.spy.appender에 대한 로깅 레벨을 지정합니다.&lt;/blockquote&gt;
&lt;pre class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot;&gt;&lt;code&gt;Configuration:
  name: spring-boot-4-local
  status: WARN

  Properties:
    Property:
      - name: &quot;log-path&quot;
        value: &quot;./logs&quot;
      - name: &quot;charset-UTF-8&quot;
        value: &quot;UTF-8&quot;
      - name: &quot;layout-pattern&quot;
        value: &quot;%clr{%d{yyyy-MM-dd'T'HH:mm:ss.SSSXXX}}{faint} %clr{%5p} %clr{%-5pid}{magenta} %clr{---}{faint} %clr{[%15.15t]}{faint} %clr{%-40.40C{1.}}{cyan} %clr{:}{faint} %m%n%wEx&quot;

  Appenders:
    Console:
      - name: console-appender
        target: SYSTEM_OUT
        PatternLayout:
          pattern: ${layout-pattern}

  Loggers:
    Root:
      level: INFO
      AppenderRef:
        - ref: console-appender

    Logger:
      - name: org.springframework
        level: INFO
        additivity: false
        AppenderRef:
          - ref: console-appender

      - name: packeage_root_path
        level: INFO
        additivity: false
        AppenderRef:
          - ref: console-appender

      # P6Spy 전용 로거 설정
      - name: com.p6spy.engine.spy.appender
        level: INFO
        additivity: false
        AppenderRef:
          - ref: console-appender&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;h3 data-ke-size=&quot;size23&quot;&gt;4. resources/spy.properties&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  resources/spy.properties&lt;/b&gt;&lt;br /&gt;&lt;b&gt;&lt;br /&gt;- 로그 포맷을 설정하고, 실행 시간을 지정합니다. 그리고 필터 내용에 대해서 지정이 가능합니다.&lt;/b&gt;&lt;br /&gt;- 또한, logMessageFormat를 통해서 커스텀 포맷터 클래스를 참조할 수 있지만, 기본만을 이용한다고 하면 customLogMessageFormat 속성값을 입력하면 기본만 적용이 가능합니다.&lt;/blockquote&gt;
&lt;pre class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot;&gt;&lt;code&gt;# 로그 포맷 설정
appender=com.p6spy.engine.spy.appender.Slf4JLogger

# 커스텀 포멧터 클래스 참조(추가 커스텀 포멧을 이용하는 경우)
# logMessageFormat=package.name.config.P6SpyFormatter

# 실행 시간 ms 기준 (0이면 전부 출력)
executionThreshold=0

# 특정 카테고리만 로깅
filter=false
include=
exclude=

# 간단 버전을 한다면, P6SpyFormatter 구성없이 기본 설정으로 가능
customLogMessageFormat=%(executionTime)ms | %(sql)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  [참고] 기본 옵션만을 저장하는 경우&lt;/b&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1632&quot; data-origin-height=&quot;177&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/vO3uV/dJMcac91Hzr/xznJxLXqnztpIk7pnHfiX1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/vO3uV/dJMcac91Hzr/xznJxLXqnztpIk7pnHfiX1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/vO3uV/dJMcac91Hzr/xznJxLXqnztpIk7pnHfiX1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FvO3uV%2FdJMcac91Hzr%2FxznJxLXqnztpIk7pnHfiX1%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;1632&quot; height=&quot;177&quot; data-origin-width=&quot;1632&quot; data-origin-height=&quot;177&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;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  [참고] logMessageFormat을 통해 적용하는 경우&amp;nbsp;&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;- 아래와 같이 커스텀으로 실행 정보, 바인딩 파라미터, 호출위치, 실행 SQL을 확인할 수 있습니다.&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;909&quot; data-origin-height=&quot;411&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b7MSuw/dJMcahDw2jU/YKgDq0mrQaFoj00adSmb1K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b7MSuw/dJMcahDw2jU/YKgDq0mrQaFoj00adSmb1K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b7MSuw/dJMcahDw2jU/YKgDq0mrQaFoj00adSmb1K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb7MSuw%2FdJMcahDw2jU%2FYKgDq0mrQaFoj00adSmb1K%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;909&quot; height=&quot;411&quot; data-origin-width=&quot;909&quot; data-origin-height=&quot;411&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;h3 data-ke-size=&quot;size23&quot;&gt;5. (선택) P6SpyFormatter&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  (선택) P6SpyFormatter&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- P6Spy에서 제공하는 기본정보에 추가적으로 커스텀으로 정보를 추가하였습니다.&lt;/b&gt; &lt;br /&gt;- 실행 정보, 바인딩 파라미터, 호출위치, 실행 SQL을 확인할 수 있습니다.&lt;/blockquote&gt;
&lt;pre class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot;&gt;&lt;code&gt;import com.p6spy.engine.spy.appender.MessageFormattingStrategy;
import org.springframework.context.annotation.Configuration;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Stack;

/**
 * P6Spy SQL 로그 포매터
 * - SQL 타입 감지 및 아이콘 표시
 * - 콜스택 기반 호출 위치 추적
 * - 바인딩 파라미터 타입 추론
 *
 * @author : leejonghoon
 * @fileName : P6SpyFormatter
 * @since : 26. 3. 3.
 */
@Configuration
public class P6SpyFormatter implements MessageFormattingStrategy {

    private static final DateTimeFormatter DATE_FORMATTER =
            DateTimeFormatter.ofPattern(&quot;yyyy-MM-dd HH:mm:ss.SSS&quot;);

    private static final String BASE_PACKAGE = &quot;com.daekyocns&quot;;

    @Override
    public String formatMessage(int connectionId, String now, long elapsed,
                                String category, String prepared, String sql, String url) {
        if (sql == null || sql.isBlank()) return &quot;&quot;;

        String sqlType = detectSqlType(sql);
        String callStack = buildCallStack();
        List&amp;lt;String&amp;gt; params = extractParams(prepared, sql);

        StringBuilder sb = new StringBuilder();
        sb.append(&quot;\\n+==============================================================&quot;);
        sb.append(&quot;\\n|  SQL &quot;).append(sqlType);
        sb.append(&quot;\\n+==============================================================&quot;);
        sb.append(&quot;\\n|  실행시각    &quot;).append(LocalDateTime.now().format(DATE_FORMATTER));
        sb.append(&quot;\\n|  실행시간    &quot;).append(elapsed).append(&quot;ms&quot;);
        sb.append(&quot;\\n|  커넥션 ID   &quot;).append(connectionId);
        sb.append(&quot;\\n|  카테고리    &quot;).append(category);

        if (!params.isEmpty()) {
            sb.append(&quot;\\n+--------------------------------------------------------------&quot;);
            sb.append(&quot;\\n|  바인딩 파라미터&quot;);
            for (int i = 0; i &amp;lt; params.size(); i++) {
                sb.append(&quot;\\n|     [&quot;).append(String.format(&quot;%02d&quot;, i + 1)).append(&quot;] &quot;).append(params.get(i));
            }
        }

        if (!callStack.isBlank()) {
            sb.append(&quot;\\n+--------------------------------------------------------------&quot;);
            sb.append(&quot;\\n|  호출 위치&quot;);
            sb.append(callStack);
        }

        sb.append(&quot;\\n+--------------------------------------------------------------&quot;);
        sb.append(&quot;\\n|  실행 SQL&quot;);
        sb.append(&quot;\\n|&quot;);
        for (String line : formatSql(sql).split(&quot;\\n&quot;)) {
            sb.append(&quot;\\n     &quot;).append(line);
        }
        sb.append(&quot;\\n|&quot;);
        sb.append(&quot;\\n+==============================================================\\n&quot;);

        return sb.toString();
    }

    /**
     * SQL 타입 감지
     *
     * @param sql
     * @return
     */
    private String detectSqlType(String sql) {
        String upper = sql.trim().toUpperCase(Locale.ROOT);
        if (upper.startsWith(&quot;SELECT&quot;)) return &quot;SELECT&quot;;
        if (upper.startsWith(&quot;INSERT&quot;)) return &quot;INSERT&quot;;
        if (upper.startsWith(&quot;UPDATE&quot;)) return &quot;UPDATE&quot;;
        if (upper.startsWith(&quot;DELETE&quot;)) return &quot;DELETE&quot;;
        if (upper.startsWith(&quot;CREATE&quot;)) return &quot;DDL &amp;middot; CREATE&quot;;
        if (upper.startsWith(&quot;ALTER&quot;)) return &quot;DDL &amp;middot; ALTER&quot;;
        if (upper.startsWith(&quot;DROP&quot;)) return &quot;DDL &amp;middot; DROP&quot;;
        if (upper.startsWith(&quot;TRUNCATE&quot;)) return &quot;DDL &amp;middot; TRUNCATE&quot;;
        return &quot;SQL&quot;;
    }

    private String getSqlIcon(String sqlType) {
        return switch (sqlType) {
            case &quot;SELECT&quot; -&amp;gt; &quot; &quot;;
            case &quot;INSERT&quot; -&amp;gt; &quot;➕&quot;;
            case &quot;UPDATE&quot; -&amp;gt; &quot;✏️&quot;;
            case &quot;DELETE&quot; -&amp;gt; &quot; ️&quot;;
            default -&amp;gt; &quot;⚙️&quot;;
        };
    }

    /**
     * 콜스택 &amp;mdash; 프로젝트 내부 호출 위치만 추출
     *
     * @return
     */
    private String buildCallStack() {
        StackTraceElement[] trace = new Throwable().getStackTrace();
        Stack&amp;lt;String&amp;gt; stack = new Stack&amp;lt;&amp;gt;();

        for (StackTraceElement el : trace) {
            String s = el.toString();
            if (s.startsWith(BASE_PACKAGE) &amp;amp;&amp;amp; !s.contains(&quot;P6SpyFormatter&quot;)) {
                stack.push(s);
            }
        }

        if (stack.isEmpty()) return &quot;&quot;;

        StringBuilder sb = new StringBuilder();
        int order = 1;
        while (!stack.isEmpty()) {
            sb.append(&quot;\\n|     &quot;).append(order++).append(&quot;. &quot;).append(stack.pop());
        }
        return sb.toString();
    }

    /**
     * 파라미터 추출
     *
     * @param prepared
     * @param sql
     * @return
     */
    private List&amp;lt;String&amp;gt; extractParams(String prepared, String sql) {
        List&amp;lt;String&amp;gt; params = new ArrayList&amp;lt;&amp;gt;();
        if (prepared == null || !prepared.contains(&quot;?&quot;)) return params;

        String[] parts = prepared.split(&quot;\\\\?&quot;, -1);
        String remaining = sql;

        for (int i = 0; i &amp;lt; parts.length - 1; i++) {
            String prefix = parts[i];
            if (remaining.startsWith(prefix)) {
                remaining = remaining.substring(prefix.length());
            } else {
                break;
            }

            String nextPrefix = (i + 1 &amp;lt; parts.length) ? parts[i + 1] : &quot;&quot;;
            String value;

            if (nextPrefix.isEmpty()) {
                value = remaining;
            } else {
                int idx = remaining.indexOf(nextPrefix);
                if (idx == -1) break;
                value = remaining.substring(0, idx);
                remaining = remaining.substring(idx);
            }
            params.add(inferType(value.trim()));
        }
        return params;
    }

    private String inferType(String v) {
        if (v.equals(&quot;null&quot;)) return v + &quot;  [NULL]&quot;;
        if (v.matches(&quot;-?\\\\d+&quot;)) return v + &quot;  [Integer]&quot;;
        if (v.matches(&quot;-?\\\\d+\\\\.\\\\d+&quot;)) return v + &quot;  [Double]&quot;;
        if (v.matches(&quot;'\\\\d{4}-\\\\d{2}-\\\\d{2}.*'&quot;)) return v + &quot;  [DateTime]&quot;;
        if (v.equalsIgnoreCase(&quot;true&quot;)
                || v.equalsIgnoreCase(&quot;false&quot;)) return v + &quot;  [Boolean]&quot;;
        return v + &quot;  [String]&quot;;
    }

    /**
     * SQL 포맷
     *
     * @param sql
     * @return
     */
    private String formatSql(String sql) {
        return sql.trim()
                .replaceAll(&quot;\\\\s+&quot;, &quot; &quot;)
                .replaceAll(&quot;(?i)\\\\b(SELECT|FROM|WHERE|AND|OR|&quot;
                                + &quot;LEFT JOIN|RIGHT JOIN|INNER JOIN|OUTER JOIN|CROSS JOIN|JOIN|&quot;
                                + &quot;GROUP BY|ORDER BY|HAVING|LIMIT|OFFSET|&quot;
                                + &quot;INSERT INTO|VALUES|UPDATE|SET|DELETE FROM|&quot;
                                + &quot;CREATE TABLE|ALTER TABLE|DROP TABLE)\\\\b&quot;,
                        &quot;\\n  $1&quot;)
                .trim();
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3) 사용 확인&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 데이터가 포함된 조회&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1160&quot; data-origin-height=&quot;571&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/de3aV8/dJMcacvviTc/pEd6Juum30CeKuzpMtlkIK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/de3aV8/dJMcacvviTc/pEd6Juum30CeKuzpMtlkIK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/de3aV8/dJMcacvviTc/pEd6Juum30CeKuzpMtlkIK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fde3aV8%2FdJMcacvviTc%2FpEd6Juum30CeKuzpMtlkIK%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;1160&quot; height=&quot;571&quot; data-origin-width=&quot;1160&quot; data-origin-height=&quot;571&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre id=&quot;code_1772676245028&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;+==============================================================
|  SQL SELECT
+==============================================================
|  실행시각    2026-03-03 17:01:15.009
|  실행시간    2ms
|  커넥션 ID   1
|  카테고리    statement
+--------------------------------------------------------------
|  바인딩 파라미터
|     [01] 'adjh54'  [String]
+--------------------------------------------------------------
|  호출 위치
|     1. com.daekyocns.clab.springboot4.config.filter.CustomAuthenticationFilter.attemptAuthentication(CustomAuthenticationFilter.java:52)
|     2. com.daekyocns.clab.springboot4.config.handler.CustomAuthenticationProvider.authenticate(CustomAuthenticationProvider.java:50)
|     3. com.daekyocns.clab.springboot4.service.impl.CustomUserDetailsService.loadUserByUsername(CustomUserDetailsService.java:48)
|     4. com.daekyocns.clab.springboot4.service.impl.UserServiceImpl.login(UserServiceImpl.java:41)
+--------------------------------------------------------------
|  실행 SQL
|
     SELECT t1.* 
       FROM test_scma.tb_user t1 
       WHERE user_id = 'adjh54'
|
+==============================================================&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. 데이터가 포함되지 않는 조회&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;774&quot; data-origin-height=&quot;386&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/UXg1E/dJMb99ZQPun/JFGWx6pkJnYAki9RDdDJI0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/UXg1E/dJMb99ZQPun/JFGWx6pkJnYAki9RDdDJI0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/UXg1E/dJMb99ZQPun/JFGWx6pkJnYAki9RDdDJI0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FUXg1E%2FdJMb99ZQPun%2FJFGWx6pkJnYAki9RDdDJI0%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;386&quot; data-origin-width=&quot;774&quot; data-origin-height=&quot;386&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;pre id=&quot;code_1772676320127&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;2026-03-03T17:15:54.948+09:00  INFO 254   --- [nio-8000-exec-1] c.p.e.s.a.Slf4JLogger                    : 
+==============================================================
|  SQL SELECT
+==============================================================
|  실행시각    2026-03-03 17:15:54.948
|  실행시간    1ms
|  커넥션 ID   1
|  카테고리    statement
+--------------------------------------------------------------
|  호출 위치
|     1. com.daekyocns.clab.springboot4.config.filter.JwtAuthorizationFilter.doFilterInternal(JwtAuthorizationFilter.java:62)
|     2. com.daekyocns.clab.springboot4.config.filter.JwtAuthorizationFilter.processToken(JwtAuthorizationFilter.java:83)
|     3. com.daekyocns.clab.springboot4.controller.template.UserController.selectUser(UserController.java:42)
|     4. com.daekyocns.clab.springboot4.service.impl.UserServiceImpl.selectUserList(UserServiceImpl.java:48)
+--------------------------------------------------------------
|  실행 SQL
|
     SELECT t1.* 
       FROM test_scma.tb_user t1 
       WHERE t1.user_st = 'S'
|
+==============================================================&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;h2 data-ke-size=&quot;size26&quot;&gt;3. 사용자 등록&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;772&quot; data-origin-height=&quot;473&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bfQyzn/dJMcaadnb1z/veCA6yYBZzE6G1MRHzFSh0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bfQyzn/dJMcaadnb1z/veCA6yYBZzE6G1MRHzFSh0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bfQyzn/dJMcaadnb1z/veCA6yYBZzE6G1MRHzFSh0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbfQyzn%2FdJMcaadnb1z%2FveCA6yYBZzE6G1MRHzFSh0%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;772&quot; height=&quot;473&quot; data-origin-width=&quot;772&quot; data-origin-height=&quot;473&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre class=&quot;asciidoc&quot;&gt;&lt;code&gt;+==============================================================
|  SQL INSERT
+==============================================================
|  실행시각    2026-03-03 17:24:06.259
|  실행시간    15ms
|  커넥션 ID   1
|  카테고리    statement
+--------------------------------------------------------------
|  바인딩 파라미터
|     [01] 'dfddd'  [String]
|     [02] '1234'  [String]
|     [03] 'dddd'  [String]
|     [04] 'S'  [String]
+--------------------------------------------------------------
|  호출 위치
|     1. com.daekyocns.clab.springboot4.config.filter.JwtAuthorizationFilter.doFilterInternal(JwtAuthorizationFilter.java:62)
|     2. com.daekyocns.clab.springboot4.config.filter.JwtAuthorizationFilter.processToken(JwtAuthorizationFilter.java:83)
|     3. com.daekyocns.clab.springboot4.controller.template.UserController.insertUser(UserController.java:39)
|     4. com.daekyocns.clab.springboot4.service.impl.UserServiceImpl.insertUser(UserServiceImpl.java:54)
+--------------------------------------------------------------
|  실행 SQL
|
     INSERT INTO test_scma.tb_user (user_id, user_pw, user_nm, user_st) 
       VALUES ('dfddd', '1234', 'dddd', 'S')
|
+==============================================================
&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;오늘도&amp;nbsp;감사합니다.&amp;nbsp; &lt;/b&gt;&lt;/p&gt;</description>
      <category>Java/MyBatis</category>
      <category>db formatter</category>
      <category>java</category>
      <category>mybatis db formatter</category>
      <category>mybatis 설정</category>
      <category>mybatis 정렬</category>
      <category>p6spy-spring-boot-starter</category>
      <category>p6spy-spring-boot-starter 구성 방법</category>
      <category>p6spy-spring-boot-starter 설정</category>
      <author>adjh54</author>
      <guid isPermaLink="true">https://adjh54.tistory.com/743</guid>
      <comments>https://adjh54.tistory.com/743#entry743comment</comments>
      <pubDate>Thu, 5 Mar 2026 20:10:13 +0900</pubDate>
    </item>
    <item>
      <title>[JS] OG 태그(Open Graph Tags) 이해하고 적용하기</title>
      <link>https://adjh54.tistory.com/742</link>
      <description>&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;해당 글에서는 OG 태그(Open Graph Tags)를 적용하고 이해하는 방법에 대해서 알아봅니다&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;
&lt;figure contenteditable=&quot;false&quot; data-ke-type=&quot;emoticon&quot; data-ke-align=&quot;alignCenter&quot; data-emoticon-type=&quot;face&quot; data-emoticon-name=&quot;073&quot; data-emoticon-isanimation=&quot;false&quot; data-emoticon-src=&quot;https://t1.daumcdn.net/keditor/emoticon/face/large/073.png&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/keditor/emoticon/face/large/073.png&quot; width=&quot;80&quot; /&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1) OG 태그(Open Graph Tags)&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  OG 태그(Open Graph Tags)&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;-&lt;b&gt; Facebook이 2010년에 만든 프로토콜로, 웹페이지가 소셜 미디어에 공유될 때 어떻게 표시될지를 제어하는 메타 태그입니다.&lt;/b&gt; &lt;br /&gt;- 현재는 Facebook, Twitter/X, KakaoTalk, LinkedIn, Slack 등 대부분의 플랫폼에서 표준처럼 사용됩니다.&lt;br /&gt;- &amp;lt;head&amp;gt; 태그 안에 &amp;lt;meta property=&quot;og:...&quot; content=&quot;...&quot;&amp;gt; 형식으로 작성합니다.&lt;/blockquote&gt;
&lt;figure id=&quot;og_1772673966440&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;Open Graph protocol&quot; data-og-description=&quot;The Open Graph protocol enables any web page to become a rich object in a social graph.&quot; data-og-host=&quot;ogp.me&quot; data-og-source-url=&quot;https://ogp.me/&quot; data-og-url=&quot;https://ogp.me/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/sakk5/dJMb9efbG6m/WhUYWfDk3aEoK9IvA0gAy0/img.png?width=300&amp;amp;height=300&amp;amp;face=0_0_300_300&quot;&gt;&lt;a href=&quot;https://ogp.me/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://ogp.me/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/sakk5/dJMb9efbG6m/WhUYWfDk3aEoK9IvA0gAy0/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;Open Graph protocol&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;The Open Graph protocol enables any web page to become a rich object in a social graph.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;ogp.me&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  아래와 같이 카카오톡에서 링크를 공유하면 아래와 같이 표시가 됩니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;330&quot; data-origin-height=&quot;261&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c2rOOQ/dJMcacPML6Y/8hLdfkR0LaP1LcDTKSxfjK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c2rOOQ/dJMcacPML6Y/8hLdfkR0LaP1LcDTKSxfjK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c2rOOQ/dJMcacPML6Y/8hLdfkR0LaP1LcDTKSxfjK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc2rOOQ%2FdJMcacPML6Y%2F8hLdfkR0LaP1LcDTKSxfjK%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;330&quot; height=&quot;261&quot; data-origin-width=&quot;330&quot; data-origin-height=&quot;261&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;blockquote data-ke-style=&quot;style3&quot;&gt;  아래와 같이 Slack에 링크를 공유하면 아래와 같이 표시가 됩니다.&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1011&quot; data-origin-height=&quot;510&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nViwg/dJMcaioWlri/eA4assJSWomlzl7lNlvpOk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nViwg/dJMcaioWlri/eA4assJSWomlzl7lNlvpOk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nViwg/dJMcaioWlri/eA4assJSWomlzl7lNlvpOk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnViwg%2FdJMcaioWlri%2FeA4assJSWomlzl7lNlvpOk%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;1011&quot; height=&quot;510&quot; data-origin-width=&quot;1011&quot; data-origin-height=&quot;510&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;h3 data-ke-size=&quot;size23&quot;&gt;1. 필수 항목&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  필수 항목&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 아래와 같이 필수 태그는 추가되지 않으면 공유시 깨지거나 나타나지 않을 수 있습니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;330&quot; data-origin-height=&quot;261&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cwrAfy/dJMcahctxeT/vjjX0vQCTku4S1n6lCcMa1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cwrAfy/dJMcahctxeT/vjjX0vQCTku4S1n6lCcMa1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cwrAfy/dJMcahctxeT/vjjX0vQCTku4S1n6lCcMa1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcwrAfy%2FdJMcahctxeT%2FvjjX0vQCTku4S1n6lCcMa1%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;330&quot; height=&quot;261&quot; data-origin-width=&quot;330&quot; data-origin-height=&quot;261&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 107px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style2&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;height: 17px;&quot;&gt;&lt;b&gt;속성(Property)&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 17px;&quot;&gt;&lt;b&gt;설명&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 17px;&quot;&gt;&lt;b&gt;사용 예시&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;&lt;b&gt;og:title&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;소셜 미디어 공유 시 노출될 메인 제목입니다.&lt;/td&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;Ecodelab - 제작 앱 소개&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;&lt;b&gt;og:description&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;제목 아래에 표시되는 페이지의 상세 요약 문구입니다.&lt;/td&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;Ecodelab에서 개발한 다양한 앱들을 소개합니다&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;&lt;b&gt;og:image&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;공유 시 미리보기로 나타날 대표 이미지 경로입니다.&lt;/td&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;&lt;a href=&quot;https://www.ecodelab.im/assets/developer.png&quot;&gt;https://www.ecodelab.im/assets/developer.png&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;&lt;b&gt;og:url&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;해당 페이지의 정식(Canonical) URL 주소입니다.&lt;/td&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;&lt;a href=&quot;https://example.com/home&quot;&gt;https://example.com/home&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;&lt;b&gt;og:type&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;콘텐츠의 성격을 정의하며, 여기서는 웹사이트로 설정되었습니다.&lt;/td&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;&lt;a href=&quot;https://www.ecodelab.im&quot;&gt;https://www.ecodelab.im&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;pre class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot;&gt;&lt;code&gt;&amp;lt;head&amp;gt;
    &amp;lt;!-- 페이지 제목 --&amp;gt;
    &amp;lt;meta property=&quot;og:title&quot; content=&quot;Ecodelab - 제작 앱 소개&quot; /&amp;gt;

    &amp;lt;!-- 페이지 설명 --&amp;gt;
    &amp;lt;meta property=&quot;og:description&quot; content=&quot;Ecodelab에서 개발한 다양한 앱들을 소개합니다&quot; /&amp;gt;

    &amp;lt;!-- 대표 이미지 (1200x630px 권장) --&amp;gt;
    &amp;lt;meta property=&quot;og:image&quot; content=&quot;https://www.ecodelab.im/assets/developer.png&quot; /&amp;gt;

    &amp;lt;!-- 페이지 URL --&amp;gt;	
    &amp;lt;meta property=&quot;og:url&quot; content=&quot;https://www.ecodelab.im&quot; /&amp;gt;

    &amp;lt;!-- 콘텐츠 타입 --&amp;gt;
    &amp;lt;meta property=&quot;og:type&quot; content=&quot;website&quot; /&amp;gt;

&amp;lt;/head&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;h3 data-ke-size=&quot;size23&quot;&gt;2. 선택적 태그&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  선택적 태그&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 필수는 아니지만 선택적으로 추가가 가능한 태그입니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 46px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style2&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;속성(Propety)&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;설명&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;사용예시&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;&lt;b&gt;og:site_name&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;사이트 이름&lt;/td&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;EcodeLab&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;&lt;b&gt;og:locale&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;언어/지역&lt;/td&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;ko_KR / en_US&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;pre class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot;&gt;&lt;code&gt;&amp;lt;head&amp;gt;
  &amp;lt;!-- 사이트 이름 --&amp;gt;
  &amp;lt;meta property=&quot;og:site_name&quot; content=&quot;Ecodelab&quot; /&amp;gt;
  &amp;lt;!-- 언어/지역 설정 --&amp;gt;
  &amp;lt;meta property=&quot;og:locale&quot; content=&quot;ko_KR&quot; /&amp;gt;
&amp;lt;/head&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;h3 data-ke-size=&quot;size23&quot;&gt;3. 이미지 상세 태그&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  이미지 상세 태그&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;- 이미지에 대해 상세로 작성이 가능한 태그를 의미합니다.&lt;/blockquote&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style2&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 19.0698%;&quot;&gt;&lt;b&gt;속성(Property)&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 49.5349%;&quot;&gt;&lt;b&gt;설명&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 31.1628%;&quot;&gt;&lt;b&gt;사용예시&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 19.0698%;&quot;&gt;&lt;b&gt;og:image&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 49.5349%;&quot;&gt;소셜 미디어에 표시될 메인 이미지의 절대 경로입니다.&lt;/td&gt;
&lt;td style=&quot;width: 31.1628%;&quot;&gt;&lt;a href=&quot;https://www.ecodelab.im/assets/developer.png&quot;&gt;https://www.ecodelab.im/assets/developer.png&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 19.0698%;&quot;&gt;&lt;b&gt;og:image:width&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 49.5349%;&quot;&gt;이미지의 가로 크기를 1200픽셀로 명시하여 미리보기 로딩 속도를 높입니다.&lt;/td&gt;
&lt;td style=&quot;width: 31.1628%;&quot;&gt;1200&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 19.0698%;&quot;&gt;&lt;b&gt;og:image:height&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 49.5349%;&quot;&gt;이미지의 세로 크기를 630픽셀로 명시합니다. (1.91:1 비율 권장)&lt;/td&gt;
&lt;td style=&quot;width: 31.1628%;&quot;&gt;630&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 19.0698%;&quot;&gt;&lt;b&gt;og:image:type&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 49.5349%;&quot;&gt;이미지 파일의 MIME 타입을 지정합니다.&lt;/td&gt;
&lt;td style=&quot;width: 31.1628%;&quot;&gt;image/png&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 19.0698%;&quot;&gt;&lt;b&gt;og:image:alt&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 49.5349%;&quot;&gt;스크린 리더나 이미지를 불러오지 못할 경우 사용되는 대체 텍스트입니다.&lt;/td&gt;
&lt;td style=&quot;width: 31.1628%;&quot;&gt;EcodeLab 대표 이미지&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;pre class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot;&gt;&lt;code&gt;&amp;lt;head&amp;gt;
  &amp;lt;!-- 대표 이미지 URL --&amp;gt;
  &amp;lt;meta property=&quot;og:image&quot; content=&quot;https://example.com/og-image.png&quot; /&amp;gt;
  
  &amp;lt;!-- 이미지 너비 (px) --&amp;gt;
  &amp;lt;meta property=&quot;og:image:width&quot; content=&quot;1200&quot; /&amp;gt;
  
  &amp;lt;!-- 이미지 높이 (px) --&amp;gt;
  &amp;lt;meta property=&quot;og:image:height&quot; content=&quot;630&quot; /&amp;gt;
  
  &amp;lt;!-- 이미지 MIME 타입 --&amp;gt;
  &amp;lt;meta property=&quot;og:image:type&quot; content=&quot;image/png&quot; /&amp;gt;
  
  &amp;lt;!-- 이미지 대체 텍스트 --&amp;gt;
  &amp;lt;meta property=&quot;og:image:alt&quot; content=&quot;로또 지니 앱 소개 이미지&quot; /&amp;gt;
&amp;lt;/head&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;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2) 기타 태그&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  기타 태그&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;1. Article 태그&lt;br /&gt;- og:type을 article로 설정했을 때 사용하는 태그입니다.&lt;/b&gt; &lt;br /&gt;- 작성자, 발행일, 카테고리 등 글에 대한 상세 정보를 추가로 표현할 수 있습니다. &lt;br /&gt;&lt;b&gt;&lt;br /&gt;2. Twitter / X Card 태그&lt;br /&gt;- Twitter(X) 전용 공유 카드를 위한 태그입니다.&lt;/b&gt; &lt;br /&gt;- OG 태그만으로도 동작하지만, 카드 타입이나 작성자 계정 등 Twitter만의 세부 설정이 필요할 때 사용합니다.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;3. App Links 태그&lt;br /&gt;- 모바일 앱이 존재할 경우 사용합니다.&lt;/b&gt; &lt;br /&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;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. &lt;b&gt;Article 태그&lt;/b&gt;&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  Article 태그&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- Facebook이나 LinkedIn에 공유했을 때 article 타입이면 &quot;언제 작성된 글인지, 누가 썼는지&quot; 같은 정보를 추가로 표시해줍니다. &lt;/b&gt;&lt;br /&gt;- 또한 Google 검색엔진도 이 태그를 참고해서 콘텐츠 신선도(최신성) 를 판단하는 데 활용합니다.&lt;/blockquote&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style2&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;속성(Property)&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;설명&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;사용 예시&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;og:type&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;콘텐츠 타입을 '기사(article)'로 지정하여 일반 웹사이트와 차별화합니다.&lt;/td&gt;
&lt;td&gt;article&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;article:author&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;이 글을 작성한 사람의 프로필 페이지 또는 SNS 계정 링크입니다.&lt;/td&gt;
&lt;td&gt;&lt;a href=&quot;https://example.com/author&quot;&gt;https://example.com/author&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;article:published_time&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;기사가 처음 발행된 날짜와 시간을 ISO 8601 형식으로 나타냅니다.&lt;/td&gt;
&lt;td&gt;2025-01-01T00:00:00Z&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;article:modified_time&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;기사가 마지막으로 수정된 날짜와 시간입니다.&lt;/td&gt;
&lt;td&gt;2025-06-01T00:00:00Z&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;article:section&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;기사의 주요 카테고리나 섹션(예: 기술, 뉴스, 요리 등)을 정의합니다.&lt;/td&gt;
&lt;td&gt;Technology&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;article:tag&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;기사와 관련된 핵심 키워드(태그)입니다.&lt;/td&gt;
&lt;td&gt;ecodelab, 앱소개&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;pre class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot;&gt;&lt;code&gt;&amp;lt;head&amp;gt;
    &amp;lt;!-- 이 페이지는 &quot;기사(article)&quot; 타입임을 선언 --&amp;gt;
    &amp;lt;meta property=&quot;og:type&quot; content=&quot;article&quot; /&amp;gt;

    &amp;lt;!-- 이 글의 작성자 프로필 URL --&amp;gt;
    &amp;lt;meta property=&quot;article:author&quot; content=&quot;https://example.com/author&quot; /&amp;gt;

    &amp;lt;!-- 최초 발행일 --&amp;gt;
    &amp;lt;meta property=&quot;article:published_time&quot; content=&quot;2025-01-01T00:00:00Z&quot; /&amp;gt;

    &amp;lt;!-- 마지막 수정일 --&amp;gt;
    &amp;lt;meta property=&quot;article:modified_time&quot; content=&quot;2025-06-01T00:00:00Z&quot; /&amp;gt;

    &amp;lt;!-- 카테고리 (섹션) --&amp;gt;
    &amp;lt;meta property=&quot;article:section&quot; content=&quot;Technology&quot; /&amp;gt;

    &amp;lt;!-- 태그/키워드 --&amp;gt;
    &amp;lt;meta property=&quot;article:tag&quot; content=&quot;ecodelab, 앱소개&quot; /&amp;gt;
&amp;lt;/head&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;h3 data-ke-size=&quot;size23&quot;&gt;2. Twitter / X Card 태그&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  Twitter / X Card 태그&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;-&lt;b&gt;OG 태그를 일부 인식하지만, 전용 태그가 따로 있습니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;563&quot; data-origin-height=&quot;501&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c7eCgZ/dJMcai3tseN/EWntCb1VtSZNNhOyGcAkgk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c7eCgZ/dJMcai3tseN/EWntCb1VtSZNNhOyGcAkgk/img.jpg&quot; data-alt=&quot;https://www.everywheremarketer.com/blog/how-to-use-twitter-cards&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c7eCgZ/dJMcai3tseN/EWntCb1VtSZNNhOyGcAkgk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc7eCgZ%2FdJMcai3tseN%2FEWntCb1VtSZNNhOyGcAkgk%2Fimg.jpg&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;563&quot; height=&quot;501&quot; data-origin-width=&quot;563&quot; data-origin-height=&quot;501&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://www.everywheremarketer.com/blog/how-to-use-twitter-cards&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style2&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;속성(Propery)&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;설명&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;사용 예시&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;twitter:card&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;트위터에서 링크 공유 시 표시될 카드의 형태입니다. (큰 이미지 타입)&lt;/td&gt;
&lt;td&gt;summary_large_image&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;twitter:site&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;웹사이트의 공식 트위터 계정 아이디입니다.&lt;/td&gt;
&lt;td&gt;@your_twitter_id&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;twitter:creator&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;콘텐츠 작성자의 트위터 계정 아이디입니다.&lt;/td&gt;
&lt;td&gt;@author_twitter_id&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;twitter:title&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;카드 상단에 노출될 제목입니다.&lt;/td&gt;
&lt;td&gt;로또 지니 - 행운의 번호 생성기&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;twitter:description&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;카드 중앙에 노출될 상세 설명 문구입니다.&lt;/td&gt;
&lt;td&gt;QR 스캔으로 당첨번호 확인, 번호 생성까지!&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;twitter:image&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;트위터 카드에 표시될 전용 이미지 경로입니다.&lt;/td&gt;
&lt;td&gt;&lt;a href=&quot;https://example.com/twitter-card.png&quot;&gt;https://example.com/twitter-card.png&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;pre class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot;&gt;&lt;code&gt;&amp;lt;head&amp;gt;
	&amp;lt;!-- 트위터 공유 시 카드 표시 형식 지정 --&amp;gt;
	&amp;lt;!-- summary: 작은 썸네일 | summary_large_image: 큰 이미지 | app: 앱 소개 | player: 동영상 --&amp;gt;
	&amp;lt;meta name=&quot;twitter:card&quot; content=&quot;summary_large_image&quot; /&amp;gt;
	
	&amp;lt;!-- 이 웹사이트/서비스의 트위터 계정 (@계정명) --&amp;gt;
	&amp;lt;meta name=&quot;twitter:site&quot; content=&quot;@your_twitter_id&quot; /&amp;gt;
	
	&amp;lt;!-- 이 콘텐츠를 작성한 사람의 트위터 계정 (@계정명) --&amp;gt;
	&amp;lt;!-- 블로그나 기사처럼 작성자가 명확할 때 사용, 서비스 페이지면 생략 가능 --&amp;gt;
	&amp;lt;meta name=&quot;twitter:creator&quot; content=&quot;@author_twitter_id&quot; /&amp;gt;
	
	&amp;lt;!-- 트위터 공유 카드에 표시될 제목 --&amp;gt;
	&amp;lt;!-- og:title과 동일하게 설정하는 것이 일반적 --&amp;gt;
	&amp;lt;meta name=&quot;twitter:title&quot; content=&quot;로또 지니 - 행운의 번호 생성기&quot; /&amp;gt;
	
	&amp;lt;!-- 트위터 공유 카드에 표시될 설명 텍스트 --&amp;gt;
	&amp;lt;!-- og:description과 동일하게 설정하는 것이 일반적 --&amp;gt;
	&amp;lt;meta name=&quot;twitter:description&quot; content=&quot;QR 스캔으로 당첨번호 확인, 번호 생성까지!&quot; /&amp;gt;
	
	&amp;lt;!-- 트위터 공유 카드에 표시될 이미지 URL (반드시 절대경로) --&amp;gt;
	&amp;lt;!-- summary_large_image 기준 권장 크기: 1200x628px, 5MB 이하 --&amp;gt;
	&amp;lt;meta name=&quot;twitter:image&quot; content=&quot;https://example.com/twitter-card.png&quot; /&amp;gt;
&amp;lt;/head&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;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  [참고]&lt;/b&gt; 아래의 사이트에서 확인해보고 테스트 할 수 있습니다.&lt;/blockquote&gt;
&lt;figure id=&quot;og_1772675005584&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;Meta Tags &amp;mdash; Preview, Edit and Generate&quot; data-og-description=&quot;With Meta Tags you can edit and experiment with your content then preview how your webpage will look on Google, Facebook, X and more!&quot; data-og-host=&quot;metatags.io&quot; data-og-source-url=&quot;https://metatags.io/&quot; data-og-url=&quot;https://metatags.io/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/91hkH/dJMb86nVhP2/n0BVRKgfOegHz7a6g5Ngt1/img.jpg?width=2400&amp;amp;height=1256&amp;amp;face=0_0_2400_1256,https://scrap.kakaocdn.net/dn/d5gmuR/dJMb85WQ2eB/sAYPKMjXgQy1NKBaczRzak/img.jpg?width=2400&amp;amp;height=1256&amp;amp;face=0_0_2400_1256,https://scrap.kakaocdn.net/dn/b6RKPS/dJMb9jOkImU/TtUrX4YRyrqOyw8jopNIB1/img.jpg?width=2400&amp;amp;height=1256&amp;amp;face=0_0_2400_1256&quot;&gt;&lt;a href=&quot;https://metatags.io/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://metatags.io/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/91hkH/dJMb86nVhP2/n0BVRKgfOegHz7a6g5Ngt1/img.jpg?width=2400&amp;amp;height=1256&amp;amp;face=0_0_2400_1256,https://scrap.kakaocdn.net/dn/d5gmuR/dJMb85WQ2eB/sAYPKMjXgQy1NKBaczRzak/img.jpg?width=2400&amp;amp;height=1256&amp;amp;face=0_0_2400_1256,https://scrap.kakaocdn.net/dn/b6RKPS/dJMb9jOkImU/TtUrX4YRyrqOyw8jopNIB1/img.jpg?width=2400&amp;amp;height=1256&amp;amp;face=0_0_2400_1256');&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;Meta Tags &amp;mdash; Preview, Edit and Generate&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;With Meta Tags you can edit and experiment with your content then preview how your webpage will look on Google, Facebook, X and more!&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;metatags.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;h3 data-ke-size=&quot;size23&quot;&gt;3. 앱 링크 태그 (App Links)&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  앱 링크 태그 (App Links)&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;- 모바일 앱이 있을 경우 딥링크 연동에 사용합니다.&lt;/blockquote&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 197px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style2&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 10.6977%; height: 18px;&quot;&gt;&lt;b&gt;구분&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 18.4884%; height: 18px;&quot;&gt;&lt;b&gt;속성(Property)&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 48.8372%; height: 18px;&quot;&gt;&lt;b&gt;설명&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 21.8605%; height: 18px;&quot;&gt;&lt;b&gt;사용 예시&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 10.6977%; height: 18px;&quot;&gt;&lt;b&gt;iOS&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 18.4884%; height: 18px;&quot;&gt;&lt;b&gt;al:ios:url&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 48.8372%; height: 18px;&quot;&gt;iOS 기기에서 앱을 실행할 때 사용할 커스텀 스킴 주소입니다.&lt;/td&gt;
&lt;td style=&quot;width: 21.8605%; height: 18px;&quot;&gt;lottogenie://home&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 10.6977%; height: 18px;&quot;&gt;&lt;b&gt;iOS&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 18.4884%; height: 18px;&quot;&gt;&lt;b&gt;al:ios:app_store_id&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 48.8372%; height: 18px;&quot;&gt;앱이 설치되지 않았을 경우 이동할 앱스토어의 고유 ID입니다.&lt;/td&gt;
&lt;td style=&quot;width: 21.8605%; height: 18px;&quot;&gt;6746621734&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 10.6977%; height: 18px;&quot;&gt;&lt;b&gt;iOS&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 18.4884%; height: 18px;&quot;&gt;&lt;b&gt;al:ios:app_name&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 48.8372%; height: 18px;&quot;&gt;앱스토어나 기기 내에서 표시될 앱의 명칭입니다.&lt;/td&gt;
&lt;td style=&quot;width: 21.8605%; height: 18px;&quot;&gt;로또 지니&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 10.6977%; height: 18px;&quot;&gt;&lt;b&gt;Android&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 18.4884%; height: 18px;&quot;&gt;&lt;b&gt;al:android:url&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 48.8372%; height: 18px;&quot;&gt;안드로이드 기기에서 앱을 실행할 때 사용할 커스텀 스킴 주소입니다.&lt;/td&gt;
&lt;td style=&quot;width: 21.8605%; height: 18px;&quot;&gt;lottogenie://home&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 10.6977%; height: 18px;&quot;&gt;&lt;b&gt;Android&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 18.4884%; height: 18px;&quot;&gt;&lt;b&gt;al:android:package&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 48.8372%; height: 18px;&quot;&gt;안드로이드 마켓(Play 스토어)의 패키지 명칭입니다.&lt;/td&gt;
&lt;td style=&quot;width: 21.8605%; height: 18px;&quot;&gt;com.tha.lottogenerator&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 35px;&quot;&gt;
&lt;td style=&quot;width: 10.6977%; height: 35px;&quot;&gt;&lt;b&gt;Android&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 18.4884%; height: 35px;&quot;&gt;&lt;b&gt;al:android:app_name&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 48.8372%; height: 35px;&quot;&gt;안드로이드 기기 내에서 표시될 앱의 명칭입니다.&lt;/td&gt;
&lt;td style=&quot;width: 21.8605%; height: 35px;&quot;&gt;로또 지니&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 37px;&quot;&gt;
&lt;td style=&quot;width: 10.6977%; height: 37px;&quot;&gt;&lt;b&gt;Web&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 18.4884%; height: 37px;&quot;&gt;&lt;b&gt;al:web:url&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 48.8372%; height: 37px;&quot;&gt;앱이 없거나 지원하지 않는 환경에서 보여줄 웹페이지 주소(Fallback)입니다.&lt;/td&gt;
&lt;td style=&quot;width: 21.8605%; height: 37px;&quot;&gt;&lt;a href=&quot;https://example.com/home&quot;&gt;https://example.com/home&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;pre id=&quot;code_1772674987283&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;head&amp;gt;
	&amp;lt;!-- iOS 앱이 설치되어 있을 때 열릴 딥링크 URL (앱 내 특정 화면으로 이동) --&amp;gt;
	&amp;lt;meta property=&quot;al:ios:url&quot; content=&quot;lottogenie://home&quot; /&amp;gt;
	
	&amp;lt;!-- App Store에 등록된 앱의 고유 ID (없으면 App Store로 이동 불가) --&amp;gt;
	&amp;lt;meta property=&quot;al:ios:app_store_id&quot; content=&quot;6746621734&quot; /&amp;gt;
	
	&amp;lt;!-- App Store에 표시될 앱 이름 --&amp;gt;
	&amp;lt;meta property=&quot;al:ios:app_name&quot; content=&quot;로또 지니&quot; /&amp;gt;
	
	&amp;lt;!-- Android 앱이 설치되어 있을 때 열릴 딥링크 URL (앱 내 특정 화면으로 이동) --&amp;gt;
	&amp;lt;meta property=&quot;al:android:url&quot; content=&quot;lottogenie://home&quot; /&amp;gt;
	
	&amp;lt;!-- Google Play Store에 등록된 앱의 패키지명 (없으면 Play Store로 이동 불가) --&amp;gt;
	&amp;lt;meta property=&quot;al:android:package&quot; content=&quot;com.lottogenie.app&quot; /&amp;gt;
	
	&amp;lt;!-- Play Store에 표시될 앱 이름 --&amp;gt;
	&amp;lt;meta property=&quot;al:android:app_name&quot; content=&quot;로또 지니&quot; /&amp;gt;
	
	&amp;lt;!-- iOS/Android 앱이 둘 다 설치되어 있지 않을 때 이동할 웹 폴백(fallback) URL --&amp;gt;
	&amp;lt;meta property=&quot;al:web:url&quot; content=&quot;https://example.com/home&quot; /&amp;gt;
&amp;lt;/head&amp;gt;&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Javascript &amp;amp; Typescript/이해하기</category>
      <author>adjh54</author>
      <guid isPermaLink="true">https://adjh54.tistory.com/742</guid>
      <comments>https://adjh54.tistory.com/742#entry742comment</comments>
      <pubDate>Thu, 5 Mar 2026 20:05:23 +0900</pubDate>
    </item>
    <item>
      <title>[JS] 스키마 마크업(Schema Markup) 이해하고 적용하기</title>
      <link>https://adjh54.tistory.com/741</link>
      <description>&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;해당 글에서는 스키마 마크업에 대해 이해하고 이를 적용하는 방법에 대해 알아봅니다&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;
&lt;figure contenteditable=&quot;false&quot; data-ke-type=&quot;emoticon&quot; data-ke-align=&quot;alignCenter&quot; data-emoticon-type=&quot;face&quot; data-emoticon-name=&quot;073&quot; data-emoticon-isanimation=&quot;false&quot; data-emoticon-src=&quot;https://t1.daumcdn.net/keditor/emoticon/face/large/073.png&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/keditor/emoticon/face/large/073.png&quot; width=&quot;80&quot; /&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1) 스키마 마크업(Schema Markup)&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  스키마 마크업(Schema Markup)&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- Schema.org에서 정의한 표준 코드를 HTML내에 추가하여 검색 엔진이 웹페이지 콘텐츠의 의미(레시피, 제품, 이벤트 등)를 정확히 이해하도록 돕는 구조화된 데이터입니다.&lt;/b&gt;&lt;br /&gt;- 예를 들어서, 검색 결과로 나타낼 때, 단순히 &quot;이 페이지에 텍스트가 있다&quot;라고 보여주는 것이 아니라, &quot;이 부분은 메인 메뉴이고, 이 부분은 하위 카테고리다&quot;라고 구조를 선언하는 행위입니다.&lt;br /&gt;- JSON-LD 형식을 가장 많이 사용하며, 이를 통해 검색 결과에 풍부한 정보(리치 스니펫)를 표시해 클릭률(CTR)과 SEO 성과를 높입니다.&lt;/blockquote&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;1700&quot; data-origin-height=&quot;1000&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bPr4rp/dJMcaiPWTer/S8zFFAnP7pTHM2x4GGyy9k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bPr4rp/dJMcaiPWTer/S8zFFAnP7pTHM2x4GGyy9k/img.png&quot; data-alt=&quot;https://developers.google.com/search/docs/appearance/structured-data/intro-structured-data?hl=ko&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bPr4rp/dJMcaiPWTer/S8zFFAnP7pTHM2x4GGyy9k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbPr4rp%2FdJMcaiPWTer%2FS8zFFAnP7pTHM2x4GGyy9k%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;1700&quot; height=&quot;1000&quot; data-origin-width=&quot;1700&quot; data-origin-height=&quot;1000&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://developers.google.com/search/docs/appearance/structured-data/intro-structured-data?hl=ko&lt;/figcaption&gt;
&lt;/figure&gt;
&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;1. 리치 결과(Rich Results)&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  리치 결과(Rich Results)&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 검색 엔진이 웹페이지의 내용을 더 잘 이해할 수 있고, 사용자에게 더욱 눈길을 끄는 검색결과를 제공하기에 추가된 스키마 마크업 정보를 바탕으로, 검색 결과 페이지에서 일반적인 텍스트 링크보다 훨씬 풍부하고 시각적인 정보를 제공하는 기능을 말합니다.&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;- 쉽게 말해, 검색 결과가 단순히 '제목 - URL - 설명'으로만 끝나는 것이 아니라, 사용자에게 도움이 되는 추가 정보를 UI 형태로 보여주는 것입니다. &lt;br /&gt;- 예로는, 검색 결과에서 별점, 가격, 조리 시간, 이벤트 날짜 등이 추가적으로 표시됩니다. 이는 클릭률(CTR)을 비약적으로 높여줍니다.&lt;/blockquote&gt;
&lt;figure id=&quot;og_1772672570542&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;리치 검색결과 테스트 - Google Search Console&quot; data-og-description=&quot;페이지에서 리치 검색결과를 지원하나요? 올바른 URL이 아닙니다.테스트에 사용할 에이전트Google 검사 도구 스마트폰Google 검사 도구 데스크톱테스트에 사용할 에이전트Google 검사 도구 &quot; data-og-host=&quot;search.google.com&quot; data-og-source-url=&quot;https://search.google.com/test/rich-results?hl=ko&quot; data-og-url=&quot;https://search.google.com/test/rich-results?hl=ko&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://search.google.com/test/rich-results?hl=ko&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://search.google.com/test/rich-results?hl=ko&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;리치 검색결과 테스트 - Google Search Console&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;페이지에서 리치 검색결과를 지원하나요? 올바른 URL이 아닙니다.테스트에 사용할 에이전트Google 검사 도구 스마트폰Google 검사 도구 데스크톱테스트에 사용할 에이전트Google 검사 도구&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;search.google.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;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  아래와 같이 리치 결과로 나오는 관련 질문(People Also Ask)으로 출력이 됩니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;595&quot; data-origin-height=&quot;372&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pZ1FG/dJMcaflq9ah/01i2Z9uFBoxUvyWlQMqux1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pZ1FG/dJMcaflq9ah/01i2Z9uFBoxUvyWlQMqux1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pZ1FG/dJMcaflq9ah/01i2Z9uFBoxUvyWlQMqux1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpZ1FG%2FdJMcaflq9ah%2F01i2Z9uFBoxUvyWlQMqux1%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;595&quot; height=&quot;372&quot; data-origin-width=&quot;595&quot; data-origin-height=&quot;372&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;h3 data-ke-size=&quot;size23&quot;&gt;2. 사이트 링크&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  사이트 링크&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- Google이 브랜드 검색 시 메인 결과 아래에 자동으로 추가해 주는 하위 페이지 링크 목록입니다.(이는 스키마 마크업과는 무관하지만 이해를 하는데 도움이 됩니다.)&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;- 해당 사이트 링크는 모든 페이지에 나오지 않고, 브랜드명 검색 시 압도적 1위이며, 사이트가 충분한 기간 동안 운영이 되었을 경우 등 일 때 Google에서 자동으로 추가해 주는 링크 목록입니다.&lt;br /&gt;- 스키마 마크업과 같이 개발자가 이를 다루는 것이 아니라 Google 내에서 이를 노출하기에 스키마 마크업과 차이가 있습니다.&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;781&quot; data-origin-height=&quot;419&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cBMiRc/dJMcaaLckwk/KCF990VGKLdyYftzTTvCs1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cBMiRc/dJMcaaLckwk/KCF990VGKLdyYftzTTvCs1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cBMiRc/dJMcaaLckwk/KCF990VGKLdyYftzTTvCs1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcBMiRc%2FdJMcaaLckwk%2FKCF990VGKLdyYftzTTvCs1%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;781&quot; height=&quot;419&quot; data-origin-width=&quot;781&quot; data-origin-height=&quot;419&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;figure id=&quot;og_1772672683305&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;사이트링크에 대해 자세히 알아보기 | Google 검색 센터 &amp;nbsp;|&amp;nbsp; Documentation &amp;nbsp;|&amp;nbsp; Google for Developers&quot; data-og-description=&quot;사이트링크란 무엇이고 자연 검색결과에 어떻게 표시되는지, 사용자가 내 사이트를 더 효과적으로 탐색하는 데 어떻게 이용될 수 있는지 알아보세요.&quot; data-og-host=&quot;developers.google.com&quot; data-og-source-url=&quot;https://developers.google.com/search/docs/appearance/sitelinks?hl=ko&quot; data-og-url=&quot;https://developers.google.com/search/docs/appearance/sitelinks?hl=ko&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/btMUit/dJMb8866BtB/lvdmxDQeczRkBlKGSW9Iq0/img.jpg?width=2494&amp;amp;height=1400&amp;amp;face=0_0_2494_1400&quot;&gt;&lt;a href=&quot;https://developers.google.com/search/docs/appearance/sitelinks?hl=ko&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://developers.google.com/search/docs/appearance/sitelinks?hl=ko&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/btMUit/dJMb8866BtB/lvdmxDQeczRkBlKGSW9Iq0/img.jpg?width=2494&amp;amp;height=1400&amp;amp;face=0_0_2494_1400');&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;사이트링크에 대해 자세히 알아보기 | Google 검색 센터 &amp;nbsp;|&amp;nbsp; Documentation &amp;nbsp;|&amp;nbsp; Google for Developers&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;developers.google.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;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  아래와 같이 &amp;lsquo;네이버&amp;rsquo;를 검색했을때, 가장 먼저 나오는 정보에 대해서 사이트 링크가 출력이 됩니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1780&quot; data-origin-height=&quot;833&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cQOObN/dJMcafyZpda/EUy0aloUwM4sX4Njkyrk8k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cQOObN/dJMcafyZpda/EUy0aloUwM4sX4Njkyrk8k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cQOObN/dJMcafyZpda/EUy0aloUwM4sX4Njkyrk8k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcQOObN%2FdJMcafyZpda%2FEUy0aloUwM4sX4Njkyrk8k%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;1780&quot; height=&quot;833&quot; data-origin-width=&quot;1780&quot; data-origin-height=&quot;833&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2) 스키마 마크업(Schema Markup) 리치 결과 종류&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style2&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 17.907%;&quot;&gt;&lt;b&gt;리치 결과 종류&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 81.9767%;&quot;&gt;&lt;b&gt;설명&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 17.907%;&quot;&gt;&lt;b&gt;지식 패널&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 81.9767%;&quot;&gt;스키마 마크업은 등록은 가능하지만, 구글에 의해서 자동으로 결정이 됨&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 17.907%;&quot;&gt;&lt;b&gt;FAQPage&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 81.9767%;&quot;&gt;스키마 마크업은 등록 가능하지만, 2023년 이후부터 정부/보건 사이트에서만 기록됨&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 17.907%;&quot;&gt;&lt;b&gt;별점/리뷰&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 81.9767%;&quot;&gt;스키마 마크업으로 등록 가능&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 17.907%;&quot;&gt;&lt;b&gt;레시피&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 81.9767%;&quot;&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;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  [참고]&lt;/b&gt; 추가 종류에 대해서는 아래에 공식 사이트에서 확인이 가능합니다&lt;/blockquote&gt;
&lt;figure id=&quot;og_1772672787434&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;Google 검색에서 지원하는 구조화된 데이터 마크업 | Google 검색 센터 &amp;nbsp;|&amp;nbsp; Documentation &amp;nbsp;|&amp;nbsp; Google for De&quot; data-og-description=&quot;검색결과에 표시되는 방식의 예를 포함하여 Google 검색에 표시될 수 있는 구조화된 데이터 지원 기능을 살펴보세요. 사이트가 Google 검색의 리치 결과에 표시되도록 구조화된 데이터를 추가하는 &quot; data-og-host=&quot;developers.google.com&quot; data-og-source-url=&quot;https://developers.google.com/search/docs/appearance/structured-data/search-gallery?hl=ko&quot; data-og-url=&quot;https://developers.google.com/search/docs/appearance/structured-data/search-gallery?hl=ko&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/ceSrl5/dJMb8RRPDYv/b66RLPr2hPBybXkjXzkjkk/img.jpg?width=2494&amp;amp;height=1400&amp;amp;face=0_0_2494_1400,https://scrap.kakaocdn.net/dn/bb3b5u/dJMb9fZs4fC/HZUnHtqGCc5Dz8eXC9UQ2K/img.png?width=1600&amp;amp;height=1700&amp;amp;face=0_0_1600_1700,https://scrap.kakaocdn.net/dn/D8G8L/dJMb9hCY3q7/Pz6UbuIQcZkAYTZbnW8tP1/img.png?width=1400&amp;amp;height=800&amp;amp;face=0_0_1400_800&quot;&gt;&lt;a href=&quot;https://developers.google.com/search/docs/appearance/structured-data/search-gallery?hl=ko&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://developers.google.com/search/docs/appearance/structured-data/search-gallery?hl=ko&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/ceSrl5/dJMb8RRPDYv/b66RLPr2hPBybXkjXzkjkk/img.jpg?width=2494&amp;amp;height=1400&amp;amp;face=0_0_2494_1400,https://scrap.kakaocdn.net/dn/bb3b5u/dJMb9fZs4fC/HZUnHtqGCc5Dz8eXC9UQ2K/img.png?width=1600&amp;amp;height=1700&amp;amp;face=0_0_1600_1700,https://scrap.kakaocdn.net/dn/D8G8L/dJMb9hCY3q7/Pz6UbuIQcZkAYTZbnW8tP1/img.png?width=1400&amp;amp;height=800&amp;amp;face=0_0_1400_800');&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;Google 검색에서 지원하는 구조화된 데이터 마크업 | Google 검색 센터 &amp;nbsp;|&amp;nbsp; Documentation &amp;nbsp;|&amp;nbsp; Google for De&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;검색결과에 표시되는 방식의 예를 포함하여 Google 검색에 표시될 수 있는 구조화된 데이터 지원 기능을 살펴보세요. 사이트가 Google 검색의 리치 결과에 표시되도록 구조화된 데이터를 추가하는&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;developers.google.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;h3 data-ke-size=&quot;size23&quot;&gt;1. 지식 패널&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  지식 패널&lt;/b&gt;&lt;br /&gt;&lt;b&gt;&lt;br /&gt;-&amp;nbsp;브랜드나 인물에 대한 정보를 구글 지식 패널(오른쪽 정보창)에 노출하는 데 기여합니다.&lt;/b&gt;&lt;br /&gt;- 실제적으로 스키마 마크업에 등록을 하였다고 하더라도, 해당 브랜드명으로 검색이 충분히 발생하는 경우에 해당 패널이 생성이 됩니다.&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1130&quot; data-origin-height=&quot;809&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cLIPt0/dJMcaaqTXJP/GTn1EwkE67RiO7myCZ8AcK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cLIPt0/dJMcaaqTXJP/GTn1EwkE67RiO7myCZ8AcK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cLIPt0/dJMcaaqTXJP/GTn1EwkE67RiO7myCZ8AcK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcLIPt0%2FdJMcaaqTXJP%2FGTn1EwkE67RiO7myCZ8AcK%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;1130&quot; height=&quot;809&quot; data-origin-width=&quot;1130&quot; data-origin-height=&quot;809&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;h3 data-ke-size=&quot;size23&quot;&gt;2. FAQPage&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  FAQPage&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 페이지에 질문과 답변 목록이 있음을 검색엔진에 알려주는 구조화 데이터입니다.&lt;/b&gt;&lt;br /&gt;- 2023년 Google이 FAQPage 리치 결과 정책을 크게 변경했습니다. 현재는 정부 또는 보건 당국 중심의 공신력 있는 사이트에만 FAQ 리치 결과를 표시합니다&lt;br /&gt;&lt;br /&gt;- 실제적으로 사용이 되지는 않지만, 검색엔진이 페이지를 더 잘 이해하고 순위를 매기는 데 도움을 주며, AI가 콘텐츠를 질문-답변 쌍으로 명확하게 인식하게 해 줘서 AI 검색(ChatGPT 등)에서 인용될 확률이 높아집니다.&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1326&quot; data-origin-height=&quot;804&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mi5eF/dJMcaiPWTkg/xqtRrPTwYNocRGpekvQ6ok/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mi5eF/dJMcaiPWTkg/xqtRrPTwYNocRGpekvQ6ok/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mi5eF/dJMcaiPWTkg/xqtRrPTwYNocRGpekvQ6ok/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fmi5eF%2FdJMcaiPWTkg%2FxqtRrPTwYNocRGpekvQ6ok%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;1326&quot; height=&quot;804&quot; data-origin-width=&quot;1326&quot; data-origin-height=&quot;804&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;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  [참고]&lt;/b&gt; FAQPage 변경사항에 대한 정보&lt;/blockquote&gt;
&lt;figure id=&quot;og_1772672883200&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;FAQ Schema in 2025: Still a Valuable SEO Asset | Epic Notion&quot; data-og-description=&quot;Learn why FAQ schema remains a powerful SEO tool in 2025 despite Google's changes. Discover how to optimize FAQ schema for better search visibility and UX.&quot; data-og-host=&quot;www.epicnotion.com&quot; data-og-source-url=&quot;https://www.epicnotion.com/blog/faq-schema-in-2025/&quot; data-og-url=&quot;https://www.epicnotion.com/blog/faq-schema-in-2025/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/YrKt4/dJMb8T9XdG8/yrGUTNA9yVXbcTmYgzCv7K/img.png?width=800&amp;amp;height=500&amp;amp;face=0_0_800_500,https://scrap.kakaocdn.net/dn/bTZr62/dJMb8XR3nx4/qpekxwgdSfyRxE2TVtDvo0/img.png?width=800&amp;amp;height=500&amp;amp;face=0_0_800_500&quot;&gt;&lt;a href=&quot;https://www.epicnotion.com/blog/faq-schema-in-2025/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.epicnotion.com/blog/faq-schema-in-2025/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/YrKt4/dJMb8T9XdG8/yrGUTNA9yVXbcTmYgzCv7K/img.png?width=800&amp;amp;height=500&amp;amp;face=0_0_800_500,https://scrap.kakaocdn.net/dn/bTZr62/dJMb8XR3nx4/qpekxwgdSfyRxE2TVtDvo0/img.png?width=800&amp;amp;height=500&amp;amp;face=0_0_800_500');&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;FAQ Schema in 2025: Still a Valuable SEO Asset | Epic Notion&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Learn why FAQ schema remains a powerful SEO tool in 2025 despite Google's changes. Discover how to optimize FAQ schema for better search visibility and UX.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.epicnotion.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;h3 data-ke-size=&quot;size23&quot;&gt;3. 별점(AggregateRating) / 리뷰(Review)&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  별점(AggregateRating) / 리뷰(Review)&lt;/b&gt;&lt;br /&gt;&lt;b&gt;&lt;br /&gt;- 구글 검색 결과를 리치 결과(Rich Results)로 만들어 클릭률을 높여주는 핵심 요소들입니다. &lt;/b&gt;이는 특정 제품이나 서비스 아래에 표시되는 별점(Star Rating)과 리뷰 개수를 사용자 클릭률(CTR)을 높이는 아주 강력한 요소입니다.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;1. 종합 별점 (AggregateRating)&lt;/b&gt; &lt;br /&gt;- 개별 리뷰가 아닌, 전체 평균 점수와 참여 인원을 보여줄 때 사용합니다.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;2. 개별 리뷰 (Review)&lt;/b&gt; &lt;br /&gt;- 특정 사용자가 남긴 구체적인 평점과 코멘트를 보여줄 때 사용합니다.&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2048&quot; data-origin-height=&quot;754&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nXhuO/dJMcahKldJQ/opxcdh5B6PgJnSdbo5pVp1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nXhuO/dJMcahKldJQ/opxcdh5B6PgJnSdbo5pVp1/img.png&quot; data-alt=&quot;https://developers.google.com/search/docs/appearance/structured-data/sd-policies&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nXhuO/dJMcahKldJQ/opxcdh5B6PgJnSdbo5pVp1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnXhuO%2FdJMcahKldJQ%2Fopxcdh5B6PgJnSdbo5pVp1%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;2048&quot; height=&quot;754&quot; data-origin-width=&quot;2048&quot; data-origin-height=&quot;754&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://developers.google.com/search/docs/appearance/structured-data/sd-policies&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;pre id=&quot;code_1772672968188&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{
  &quot;@type&quot;: &quot;SoftwareApplication&quot;,
  &quot;name&quot;: &quot;앱 이름&quot;,
  &quot;aggregateRating&quot;: {
    &quot;@type&quot;: &quot;AggregateRating&quot;,
    &quot;ratingValue&quot;: &quot;4.5&quot;,   &amp;larr; DB에서 평균값 가져와서 동적으로
    &quot;ratingCount&quot;: &quot;120&quot;,   &amp;larr; DB에서 리뷰 수 가져와서 동적으로
    &quot;bestRating&quot;: &quot;5&quot;
  },
  &quot;review&quot;: [
    {
      &quot;@type&quot;: &quot;Review&quot;,
      &quot;author&quot;: {&quot;@type&quot;: &quot;Person&quot;, &quot;name&quot;: &quot;유저명&quot;},
      &quot;reviewRating&quot;: {
        &quot;@type&quot;: &quot;Rating&quot;,
        &quot;ratingValue&quot;: &quot;5&quot;
      },
      &quot;reviewBody&quot;: &quot;리뷰 내용&quot;
    }
  ]
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;[ 더 알아보기 ]&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt; &amp;nbsp;별점/리뷰는 하드코딩으로 작성하는것 같은데? 만약에 내가 홍보하고 싶은 앱인 경우는 어떻게 하나?&lt;br /&gt;&lt;/b&gt;&lt;br /&gt;- 실제로 Store에 있는 실제 별점을 그대로 사용하는 게 가장 안전하며, 현재 별점과 리뷰 수를 확인해서 하드코딩으로 넣고, 주기적으로 업데이트해 주면 됩니다.&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;h3 data-ke-size=&quot;size23&quot;&gt;4. 레시피&lt;/h3&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;style3&quot;&gt;&lt;b&gt; &amp;nbsp;레시피&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 스키마를 잘 적용하면 구글 검색 결과에 요리 소요 시간, 칼로리, 완성 사진이 포함된 '레시피 카드' 형태로 노출되어 클릭률이 비약적으로 상승합니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2048&quot; data-origin-height=&quot;754&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/o7Fb9/dJMcaiCrbp9/qSS8W0IOCCUKUWSXNzTGj0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/o7Fb9/dJMcaiCrbp9/qSS8W0IOCCUKUWSXNzTGj0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/o7Fb9/dJMcaiCrbp9/qSS8W0IOCCUKUWSXNzTGj0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fo7Fb9%2FdJMcaiCrbp9%2FqSS8W0IOCCUKUWSXNzTGj0%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;2048&quot; height=&quot;754&quot; data-origin-width=&quot;2048&quot; data-origin-height=&quot;754&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot;&gt;&lt;code&gt;{
  &quot;@context&quot;: &quot;&amp;lt;a href=https://schema.org/&amp;gt;https://schema.org/&amp;lt;/a&amp;gt;&quot;,
  &quot;@type&quot;: &quot;Recipe&quot;,
  &quot;name&quot;: &quot;할머니 손맛 순우리말 비빔밥&quot;,
  &quot;image&quot;: [
    &quot;&amp;lt;a href=https://example.com/photos/1x1/photo.jpg&amp;gt;https://example.com/photos/1x1/photo.jpg&amp;lt;/a&amp;gt;&quot;
  ],
  &quot;author&quot;: {
    &quot;@type&quot;: &quot;Person&quot;,
    &quot;name&quot;: &quot;우리말 지킴이&quot;
  },
  &quot;datePublished&quot;: &quot;2026-02-19&quot;,
  &quot;description&quot;: &quot;전통적인 방식으로 만드는 건강한 비빔밥 레시피입니다.&quot;,
  &quot;prepTime&quot;: &quot;PT20M&quot;,
  &quot;cookTime&quot;: &quot;PT15M&quot;,
  &quot;recipeYield&quot;: &quot;2인분&quot;,
  &quot;recipeIngredient&quot;: [
    &quot;쌀 2컵&quot;,
    &quot;고추장 2큰술&quot;,
    &quot;각종 나물&quot;
  ],
  &quot;recipeInstructions&quot;: [
    {
      &quot;@type&quot;: &quot;HowToStep&quot;,
      &quot;text&quot;: &quot;쌀을 씻어 밥을 짓습니다.&quot;,
      &quot;image&quot;: &quot;&amp;lt;a href=https://example.com/step1.jpg&amp;gt;https://example.com/step1.jpg&amp;lt;/a&amp;gt;&quot;
    },
    {
      &quot;@type&quot;: &quot;HowToStep&quot;,
      &quot;text&quot;: &quot;나물을 데치고 양념합니다.&quot;
    }
  ],
  &quot;aggregateRating&quot;: {
    &quot;@type&quot;: &quot;AggregateRating&quot;,
    &quot;ratingValue&quot;: &quot;4.8&quot;,
    &quot;reviewCount&quot;: &quot;120&quot;
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3) JSON-LD(JavaScript Object Notation for Linked Data)&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  JSON-LD(JavaScript Object Notation for Linked Data)&lt;/b&gt;&lt;br /&gt;&lt;b&gt;&lt;br /&gt;- JSON 형식을 사용하여 '연결된 데이터(Linked Data)'를 표현하는 방식입니다.&lt;/b&gt;&lt;br /&gt;- 쉽게 말해, 검색 엔진(구글 등)이 웹페이지의 내용을 단순한 텍스트가 아니라 의미 있는 정보로 이해할 수 있도록 돕는 일종의 '데이터 명세서'라고 보시면 됩니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 구조 확인&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style2&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;키워드&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;의미&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;비고&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;@context&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;데이터의 의미를 해석할 표준 주소&lt;/td&gt;
&lt;td&gt;보통&amp;nbsp;&lt;a href=&quot;https://schema.org&quot;&gt;https://schema.org&lt;/a&gt;&amp;nbsp;고정&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;@graph&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;여러 개의 개체를 하나의 배열로 묶을 때 사용&lt;/td&gt;
&lt;td&gt;페이지 내 여러 구조화 데이터를 선언할 때 효율적&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;@type&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;데이터의 유형 (클래스)&lt;/td&gt;
&lt;td&gt;예:&amp;nbsp;Person,&amp;nbsp;WebSite,&amp;nbsp;LocalBusinessOrganization&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;@id&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;개체를 식별하는 고유 URI&lt;/td&gt;
&lt;td&gt;여러 데이터를 서로 연결할 때 사용&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;@language&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;값의 언어를 지정&lt;/td&gt;
&lt;td&gt;예:&amp;nbsp;&quot;@language&quot;: &quot;ko&quot;&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;h3 data-ke-size=&quot;size23&quot;&gt;2. 사용예시&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  사용예시&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;1. @context: &amp;nbsp;Schema.org를 기준으로 데이터를 해석하겠다는 선언을 의미합니다.&lt;/b&gt; &lt;br /&gt;- 일종의 &quot;이 데이터는 이 규칙을 따릅니다&quot;라는 명세를 의미합니다.&lt;br /&gt;&lt;b&gt;&lt;br /&gt;2. @graph: 여러 개의 Schema 객체를 하나의 스크립트 안에 묶어서 표현할 때 사용하는 배열을 의미합니다.&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;3. Organization: 조직/개인의 정보를 명세를 합니다.&lt;/b&gt;&lt;br /&gt;- name : 조직 이름을 의미합니다.&lt;br /&gt;- url : 대표 웹사이트 주소을 의미합니다.&lt;br /&gt;- logo : 로고 이미지를 URL을 의미합니다&lt;br /&gt;- sameAs : 이 조직과 동일한 주체임을 나타내는 외부 링크들 (GitHub, 티스토리 블로그)이며, 검색엔진이 여러 플랫폼의 정보를 하나의 엔티티로 연결하는 데 도움을 줍니다.&lt;br /&gt;- contactPoint : 연락처 정보. 이메일, 연락 유형(고객 서비스), 사용 가능한 언어를 명시하는 것을 의미합니다.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;4. WebPage: 현재 페이지 자체에 대한 정보를 명세를 합니다.&lt;/b&gt;&lt;br /&gt;- name : 페이지 제목을 의미합니다.&lt;br /&gt;- description : 페이지 설명 (검색결과 스니펫에 활용)을 의미합니다.&lt;br /&gt;- url : 페이지 주소를 의미합니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  [참고]&lt;/b&gt; 사용 예시&lt;/blockquote&gt;
&lt;pre class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot;&gt;&lt;code&gt;&amp;lt;script type=&quot;application/ld+json&quot;&amp;gt;
    {
        &quot;@context&quot;: &quot;https://schema.org&quot;,
        &quot;@graph&quot;: [
            {
                &quot;@type&quot;: &quot;Organization&quot;,
                &quot;name&quot;: &quot;Ecodelab&quot;,
                &quot;url&quot;: &quot;https://www.ecodelab.im&quot;,
                &quot;logo&quot;: &quot;https://www.ecodelab.im/assets/developer.png&quot;,
                &quot;sameAs&quot;: [&quot;https://github.com/adjh54ir&quot;, &quot;https://adjh54.tistory.com&quot;],
                &quot;contactPoint&quot;: {
                    &quot;@type&quot;: &quot;ContactPoint&quot;,
                    &quot;email&quot;: &quot;adjh54ir@gmail.com&quot;,
                    &quot;contactType&quot;: &quot;customer service&quot;,
                    &quot;availableLanguage&quot;: &quot;Korean&quot;
                }
            },
            {
                &quot;@type&quot;: &quot;WebPage&quot;,
                &quot;name&quot;: &quot;Ecodelab - 제작 앱 소개&quot;,
                &quot;description&quot;: &quot;Ecodelab에서 개발한 다양한 앱들을 소개합니다&quot;,
                &quot;url&quot;: &quot;https://www.ecodelab.im&quot;
            }
        ]
    }
&amp;lt;/script&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;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4) 검증 방법&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. Google Search Console 활용&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  Google Search Console 활용&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 아래와 같이 리치 검색결과 테스트에 직접 배포한 도메인에 대해서 테스트를 진행합니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;figure id=&quot;og_1772673389011&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;리치 검색결과 테스트 - Google Search Console&quot; data-og-description=&quot;페이지에서 리치 검색결과를 지원하나요? 올바른 URL이 아닙니다.테스트에 사용할 에이전트Google 검사 도구 스마트폰Google 검사 도구 데스크톱테스트에 사용할 에이전트Google 검사 도구 &quot; data-og-host=&quot;search.google.com&quot; data-og-source-url=&quot;https://search.google.com/test/rich-results?hl=ko&quot; data-og-url=&quot;https://search.google.com/test/rich-results?hl=ko&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://search.google.com/test/rich-results?hl=ko&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://search.google.com/test/rich-results?hl=ko&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;리치 검색결과 테스트 - Google Search Console&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;페이지에서 리치 검색결과를 지원하나요? 올바른 URL이 아닙니다.테스트에 사용할 에이전트Google 검사 도구 스마트폰Google 검사 도구 데스크톱테스트에 사용할 에이전트Google 검사 도구&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;search.google.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;h4 data-ke-size=&quot;size20&quot;&gt;1.1. 실제 사이트에서 스키마 마크업을 적용하였습니다&lt;/h4&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;style3&quot;&gt;&lt;b&gt;  실제 사이트에서 스키마 마크업을 적용하였습니다&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 배포된 사이트의 요소를 확인하면 스키마 마크업을 확인할 수 있습니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1901&quot; data-origin-height=&quot;835&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/DBH75/dJMcabJ8oo1/mdkjvKfrKPa9577bjUZsdk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/DBH75/dJMcabJ8oo1/mdkjvKfrKPa9577bjUZsdk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/DBH75/dJMcabJ8oo1/mdkjvKfrKPa9577bjUZsdk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FDBH75%2FdJMcabJ8oo1%2FmdkjvKfrKPa9577bjUZsdk%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;1901&quot; height=&quot;835&quot; data-origin-width=&quot;1901&quot; data-origin-height=&quot;835&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;h4 data-ke-size=&quot;size20&quot;&gt;1.2. 스키마 마크업을 적용한 도메인 주소를 검사합니다&lt;/h4&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1913&quot; data-origin-height=&quot;832&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cpjHCq/dJMcajuzAXy/gwlhsw2o1MGsKGHN8NPLPk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cpjHCq/dJMcajuzAXy/gwlhsw2o1MGsKGHN8NPLPk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cpjHCq/dJMcajuzAXy/gwlhsw2o1MGsKGHN8NPLPk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcpjHCq%2FdJMcajuzAXy%2Fgwlhsw2o1MGsKGHN8NPLPk%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;1913&quot; height=&quot;832&quot; data-origin-width=&quot;1913&quot; data-origin-height=&quot;832&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;h4 data-ke-size=&quot;size20&quot;&gt;1.3. 아래와 같이 결과로 실제 발견이 되었음을 확인하였습니다&lt;/h4&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1914&quot; data-origin-height=&quot;838&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/5rJKn/dJMcabwyxQc/LOb5qZebAjZkpqrCvuK7o1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/5rJKn/dJMcabwyxQc/LOb5qZebAjZkpqrCvuK7o1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/5rJKn/dJMcabwyxQc/LOb5qZebAjZkpqrCvuK7o1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F5rJKn%2FdJMcabwyxQc%2FLOb5qZebAjZkpqrCvuK7o1%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;1914&quot; height=&quot;838&quot; data-origin-width=&quot;1914&quot; data-origin-height=&quot;838&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;h4 data-ke-size=&quot;size20&quot;&gt;1.4. 해당 사이트를 확인하였을 때, 스키마 마크업은 출력이 안되고 있습니다.&lt;/h4&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;style3&quot;&gt;&lt;b&gt;  해당 사이트를 확인하였을때, 스키마 마크업은 출력이 안되고 있습니다.&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 이는 즉시 적용되는 것이 아니고 일정 시간이 지나 크롤링되는 과정에 적용이 된다고 합니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1068&quot; data-origin-height=&quot;841&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Cmt9I/dJMcahwNQGF/RQ4csqyj3lJMQsPHO98kDk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Cmt9I/dJMcahwNQGF/RQ4csqyj3lJMQsPHO98kDk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Cmt9I/dJMcahwNQGF/RQ4csqyj3lJMQsPHO98kDk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FCmt9I%2FdJMcahwNQGF%2FRQ4csqyj3lJMQsPHO98kDk%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;1068&quot; height=&quot;841&quot; data-origin-width=&quot;1068&quot; data-origin-height=&quot;841&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;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  적용한 사이트는 아래에서 확인이 가능합니다!&lt;/b&gt;&lt;/blockquote&gt;
&lt;figure id=&quot;og_1772673549737&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;Ecodelab - 제작 앱 소개&quot; data-og-description=&quot;Ecodelab에서 개발한 다양한 앱들을 소개합니다&quot; data-og-host=&quot;www.ecodelab.im&quot; data-og-source-url=&quot;https://ecodelab.im/main&quot; data-og-url=&quot;https://www.ecodelab.im&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/tN3Ig/dJMb84p6s3d/nz4Nfn5pPVENsvXMKqWAt1/img.png?width=1024&amp;amp;height=1024&amp;amp;face=0_0_1024_1024,https://scrap.kakaocdn.net/dn/LtZqJ/dJMb85vMFZp/uiDEzJDKlwMa5rOk1rMKg1/img.png?width=1024&amp;amp;height=1024&amp;amp;face=0_0_1024_1024&quot;&gt;&lt;a href=&quot;https://ecodelab.im/main&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://ecodelab.im/main&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/tN3Ig/dJMb84p6s3d/nz4Nfn5pPVENsvXMKqWAt1/img.png?width=1024&amp;amp;height=1024&amp;amp;face=0_0_1024_1024,https://scrap.kakaocdn.net/dn/LtZqJ/dJMb85vMFZp/uiDEzJDKlwMa5rOk1rMKg1/img.png?width=1024&amp;amp;height=1024&amp;amp;face=0_0_1024_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;Ecodelab - 제작 앱 소개&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Ecodelab에서 개발한 다양한 앱들을 소개합니다&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.ecodelab.im&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;&lt;b&gt;오늘도&amp;nbsp;감사합니다.&amp;nbsp; &lt;/b&gt;&lt;/p&gt;</description>
      <category>Javascript &amp;amp; Typescript/이해하기</category>
      <category>js 스키마 마크업</category>
      <category>react 스키마 마크업</category>
      <category>schema markup</category>
      <category>스키마 마크업</category>
      <category>스키마 마크업 적용 방법</category>
      <category>스키마 마크업이란</category>
      <category>웹 페이지 노출</category>
      <author>adjh54</author>
      <guid isPermaLink="true">https://adjh54.tistory.com/741</guid>
      <comments>https://adjh54.tistory.com/741#entry741comment</comments>
      <pubDate>Thu, 5 Mar 2026 20:00:01 +0900</pubDate>
    </item>
    <item>
      <title>[JS] PWA(Progressive Web App) 이해하고 활용하기 -1: 초기 구성</title>
      <link>https://adjh54.tistory.com/740</link>
      <description>&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;해당 글에서는 PWA에 대해 이해하고 활용하는 기본 방법에 대해 알아봅니다.&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;
&lt;figure contenteditable=&quot;false&quot; data-ke-type=&quot;emoticon&quot; data-ke-align=&quot;alignCenter&quot; data-emoticon-type=&quot;face&quot; data-emoticon-name=&quot;073&quot; data-emoticon-isanimation=&quot;false&quot; data-emoticon-src=&quot;https://t1.daumcdn.net/keditor/emoticon/face/large/073.png&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/keditor/emoticon/face/large/073.png&quot; width=&quot;80&quot; /&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1) PWA(Progressive Web App)&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  PWA(Progressive Web App)&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 웹 기술로 구축되지만 네이티브 앱처럼 작동하는 애플리케이션입니다.&amp;nbsp;전통적인 웹사이트와 네이티브 앱의 장점을 결합한 형태라고 할 수 있습니다&amp;nbsp;앱처럼 아이콘을 추가하고,&amp;nbsp;푸시 알림을 보내며,&amp;nbsp;오프라인에서도 작동하게 만드는 기술입니다.&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;- 구글의 엔지니어 알렉스 러셀(Alex Russell)이 처음 제안한 개념으로, 별도의 스토어 설치 과정 없이 웹 브라우저를 통해 앱과 같은 사용자 경험(UX)을 제공합니다.&lt;/blockquote&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style2&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;구분&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;일반 웹 사이트&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;PWA&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;네이티브 앱 (AOS/iOS)&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;설치 방식&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;URL 접속&lt;/td&gt;
&lt;td&gt;브라우저 내 추가&lt;/td&gt;
&lt;td&gt;앱스토어 다운로드&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;오프라인 사용&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;불가&lt;/td&gt;
&lt;td&gt;가능 (제한적)&lt;/td&gt;
&lt;td&gt;가능&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;푸시 알림&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;불가&lt;/td&gt;
&lt;td&gt;가능&lt;/td&gt;
&lt;td&gt;가능&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;배포/업데이트&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;실시간&lt;/td&gt;
&lt;td&gt;실시간&lt;/td&gt;
&lt;td&gt;스토어 심사 필요&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;개발 비용&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;낮음&lt;/td&gt;
&lt;td&gt;중간&lt;/td&gt;
&lt;td&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;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2) PWA(Progressive Web App) 핵심 기술&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. Service Worker (서비스 워커)&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  Service Worker (서비스 워커)&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;- 브라우저 백그라운드에서 실행되는 스크립트 파일입니다. 네트워크 요청을 가로채거나 캐싱을 제어하여 오프라인 모드를 지원하고 푸시 알림을 처리하는 핵심 엔진 역할을 합니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  아래와 같이 Chrome 내에서 Service workers 설치가 되면 확인이 가능합니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;717&quot; data-origin-height=&quot;704&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b2E9pJ/dJMcac9R6OX/QR344JsnpIWk5nvrPqo8V1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b2E9pJ/dJMcac9R6OX/QR344JsnpIWk5nvrPqo8V1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b2E9pJ/dJMcac9R6OX/QR344JsnpIWk5nvrPqo8V1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb2E9pJ%2FdJMcac9R6OX%2FQR344JsnpIWk5nvrPqo8V1%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;717&quot; height=&quot;704&quot; data-origin-width=&quot;717&quot; data-origin-height=&quot;704&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;h3 data-ke-size=&quot;size23&quot;&gt;2. Web App Manifest (매니페스트 파일)&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  Web App Manifest (매니페스트 파일)&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 앱의 이름, 아이콘, 배경색, 시작 URL 등을 메타 데이터를 정의하는 JSON 파일입니다.&lt;/b&gt; &lt;br /&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;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  아래와 같이 파일 내에 manifest.json 파일로 메타데이터를 정의합니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;564&quot; data-origin-height=&quot;394&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/sAP1b/dJMcaaEh0O3/E0pSDX67xU4cG6hlYkb9N1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/sAP1b/dJMcaaEh0O3/E0pSDX67xU4cG6hlYkb9N1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/sAP1b/dJMcaaEh0O3/E0pSDX67xU4cG6hlYkb9N1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FsAP1b%2FdJMcaaEh0O3%2FE0pSDX67xU4cG6hlYkb9N1%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;564&quot; height=&quot;394&quot; data-origin-width=&quot;564&quot; data-origin-height=&quot;394&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;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  실제로 실행하였을 시, 아래와 같이 &amp;lsquo;앱에서 열기&amp;rsquo; 버튼이 생성됩니다. 그리고 실행하였을 때, manifest.json에서 정의한 내용이 출력이 됩니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1920&quot; data-origin-height=&quot;949&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/vPd8g/dJMcaivu895/lvlg4ivMNnvm4HmWZMmNtk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/vPd8g/dJMcaivu895/lvlg4ivMNnvm4HmWZMmNtk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/vPd8g/dJMcaivu895/lvlg4ivMNnvm4HmWZMmNtk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FvPd8g%2FdJMcaivu895%2Flvlg4ivMNnvm4HmWZMmNtk%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;1920&quot; height=&quot;949&quot; data-origin-width=&quot;1920&quot; data-origin-height=&quot;949&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;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  [참고]&lt;/b&gt; manifest.json 파일 내에 정의할 수 있는 정보들은 아래에서 확인이 가능합니다.&lt;/blockquote&gt;
&lt;figure id=&quot;og_1771488869649&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;Web application manifest - Progressive web apps | MDN&quot; data-og-description=&quot;&quot; data-og-host=&quot;developer.mozilla.org&quot; data-og-source-url=&quot;https://developer.mozilla.org/en-US/docs/Web/Progressive_web_apps/Manifest&quot; data-og-url=&quot;https://developer.mozilla.org/en-US/docs/Web/Progressive_web_apps/Manifest&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/Progressive_web_apps/Manifest&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://developer.mozilla.org/en-US/docs/Web/Progressive_web_apps/Manifest&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;Web application manifest - Progressive web apps | MDN&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;developer.mozilla.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;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. HTTPS&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  HTTPS&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- PWA는 보안을 위해 반드시 HTTPS 환경에서만 작동합니다. 서비스 워커가 네트워크 요청을 제어하기 때문에 강력한 보안 프로토콜에서만 작동합니다.&lt;/b&gt;&lt;br /&gt;- 예외적으로 http://localhost 환경에서는 동작을 수행합니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3) PWA(Progressive Web App) 초기 구성&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. manifest.json 정의&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  manifest.json 정의&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 프로젝트의 루트 경로 위치하에 PWA의 메타 데이터를 정의하는 JSON 파일을 정의합니다.&lt;/b&gt;&lt;br /&gt;- 또한, 아이콘을 192x192 사이즈 아이콘을 정의했기에 icon-192.png 파일을 루트경로에 위치하게 두었습니다.&lt;/blockquote&gt;
&lt;pre id=&quot;code_1771489048077&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{
  &quot;name&quot;: &quot;나의 첫 PWA 앱&quot;,
  &quot;lang&quot;: &quot;ko-KR&quot;,
  &quot;description&quot;: &quot;이 앱은 PWA 학습을 위한 샘플 애플리케이션입니다.&quot;,
  &quot;short_name&quot;: &quot;PWA 앱&quot;,
  &quot;start_url&quot;: &quot;/index.html&quot;,
  &quot;display&quot;: &quot;standalone&quot;,
  &quot;background_color&quot;: &quot;#ffffff&quot;,
  &quot;theme_color&quot;: &quot;#000000&quot;,
  &quot;icons&quot;: [
    {
      &quot;src&quot;: &quot;icon-192.png&quot;,
      &quot;sizes&quot;: &quot;192x192&quot;,
      &quot;type&quot;: &quot;image/png&quot;
    }
  ]
}&lt;/code&gt;&lt;/pre&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;833&quot; data-origin-height=&quot;677&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dEOEQ7/dJMcagkaYY3/oRlEK8Xf6jIUzmcHlSpiek/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dEOEQ7/dJMcagkaYY3/oRlEK8Xf6jIUzmcHlSpiek/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dEOEQ7/dJMcagkaYY3/oRlEK8Xf6jIUzmcHlSpiek/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdEOEQ7%2FdJMcagkaYY3%2FoRlEK8Xf6jIUzmcHlSpiek%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;833&quot; height=&quot;677&quot; data-origin-width=&quot;833&quot; data-origin-height=&quot;677&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;table id=&quot;30316d47-b05b-80e5-b7fc-e238e0cc705d&quot; style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style2&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 22.4418%;&quot;&gt;&lt;b&gt;속성&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 21.5117%;&quot;&gt;&lt;b&gt;필수/선택&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 33.7209%;&quot;&gt;&lt;b&gt;설명&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 22.2093%;&quot;&gt;&lt;b&gt;예시 값&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;30316d47-b05b-80fc-bfc8-db9c8a645f36&quot;&gt;
&lt;td id=&quot;VU}}&quot; style=&quot;width: 22.4418%;&quot;&gt;&lt;b&gt;name&lt;/b&gt;&lt;/td&gt;
&lt;td id=&quot;iowx&quot; style=&quot;width: 21.5117%;&quot;&gt;필수&lt;/td&gt;
&lt;td id=&quot;&amp;lt;S^c&quot; style=&quot;width: 33.7209%;&quot;&gt;앱의 전체 이름 (설치 프롬프트, 스플래시 화면에 표시)&lt;/td&gt;
&lt;td id=&quot;SyJN&quot; style=&quot;width: 22.2093%;&quot;&gt;&quot;나의 첫 PWA 앱&quot;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;30316d47-b05b-80dd-8f4f-ce3eab6ca93f&quot;&gt;
&lt;td id=&quot;VU}}&quot; style=&quot;width: 22.4418%;&quot;&gt;&lt;b&gt;short_name&lt;/b&gt;&lt;/td&gt;
&lt;td id=&quot;iowx&quot; style=&quot;width: 21.5117%;&quot;&gt;권장&lt;/td&gt;
&lt;td id=&quot;&amp;lt;S^c&quot; style=&quot;width: 33.7209%;&quot;&gt;홈 화면에 표시될 짧은 이름 (12자 이하 권장)&lt;/td&gt;
&lt;td id=&quot;SyJN&quot; style=&quot;width: 22.2093%;&quot;&gt;&quot;PWA 앱&quot;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;30316d47-b05b-8060-a5b0-fae6e8eb56c2&quot;&gt;
&lt;td id=&quot;VU}}&quot; style=&quot;width: 22.4418%;&quot;&gt;&lt;b&gt;start_url&lt;/b&gt;&lt;/td&gt;
&lt;td id=&quot;iowx&quot; style=&quot;width: 21.5117%;&quot;&gt;필수&lt;/td&gt;
&lt;td id=&quot;&amp;lt;S^c&quot; style=&quot;width: 33.7209%;&quot;&gt;앱 시작 시 로드될 URL&lt;/td&gt;
&lt;td id=&quot;SyJN&quot; style=&quot;width: 22.2093%;&quot;&gt;&quot;/&quot;, &quot;/index.html&quot;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;30316d47-b05b-8020-a63d-d57d76e5d37a&quot;&gt;
&lt;td id=&quot;VU}}&quot; style=&quot;width: 22.4418%;&quot;&gt;&lt;b&gt;display&lt;/b&gt;&lt;/td&gt;
&lt;td id=&quot;iowx&quot; style=&quot;width: 21.5117%;&quot;&gt;필수&lt;/td&gt;
&lt;td id=&quot;&amp;lt;S^c&quot; style=&quot;width: 33.7209%;&quot;&gt;앱 표시 모드&lt;/td&gt;
&lt;td id=&quot;SyJN&quot; style=&quot;width: 22.2093%;&quot;&gt;&quot;standalone&quot;, &quot;fullscreen&quot;, &quot;minimal-ui&quot;, &quot;browser&quot;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;30316d47-b05b-80d0-9752-df519fa26bf9&quot;&gt;
&lt;td id=&quot;VU}}&quot; style=&quot;width: 22.4418%;&quot;&gt;&lt;b&gt;icons&lt;/b&gt;&lt;/td&gt;
&lt;td id=&quot;iowx&quot; style=&quot;width: 21.5117%;&quot;&gt;필수&lt;/td&gt;
&lt;td id=&quot;&amp;lt;S^c&quot; style=&quot;width: 33.7209%;&quot;&gt;앱 아이콘 배열 (최소 192x192, 512x512 권장)&lt;/td&gt;
&lt;td id=&quot;SyJN&quot; style=&quot;width: 22.2093%;&quot;&gt;[{src, sizes, type}]&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;30316d47-b05b-80d3-8a2c-e2dc022d6556&quot;&gt;
&lt;td id=&quot;VU}}&quot; style=&quot;width: 22.4418%;&quot;&gt;&lt;b&gt;background_color&lt;/b&gt;&lt;/td&gt;
&lt;td id=&quot;iowx&quot; style=&quot;width: 21.5117%;&quot;&gt;권장&lt;/td&gt;
&lt;td id=&quot;&amp;lt;S^c&quot; style=&quot;width: 33.7209%;&quot;&gt;스플래시 화면 배경색&lt;/td&gt;
&lt;td id=&quot;SyJN&quot; style=&quot;width: 22.2093%;&quot;&gt;&quot;#ffffff&quot;, &quot;#000000&quot;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;30316d47-b05b-807c-b006-c6fb2bd98173&quot;&gt;
&lt;td id=&quot;VU}}&quot; style=&quot;width: 22.4418%;&quot;&gt;&lt;b&gt;theme_color&lt;/b&gt;&lt;/td&gt;
&lt;td id=&quot;iowx&quot; style=&quot;width: 21.5117%;&quot;&gt;권장&lt;/td&gt;
&lt;td id=&quot;&amp;lt;S^c&quot; style=&quot;width: 33.7209%;&quot;&gt;브라우저 UI 색상 (주소창, 상태바)&lt;/td&gt;
&lt;td id=&quot;SyJN&quot; style=&quot;width: 22.2093%;&quot;&gt;&quot;#000000&quot;, &quot;#3f51b5&quot;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;30316d47-b05b-8011-b3f5-f234261fa524&quot;&gt;
&lt;td id=&quot;VU}}&quot; style=&quot;width: 22.4418%;&quot;&gt;&lt;b&gt;description&lt;/b&gt;&lt;/td&gt;
&lt;td id=&quot;iowx&quot; style=&quot;width: 21.5117%;&quot;&gt;선택&lt;/td&gt;
&lt;td id=&quot;&amp;lt;S^c&quot; style=&quot;width: 33.7209%;&quot;&gt;앱 설명&lt;/td&gt;
&lt;td id=&quot;SyJN&quot; style=&quot;width: 22.2093%;&quot;&gt;&quot;할 일 관리 앱입니다&quot;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;30316d47-b05b-80a2-990b-eafcc140c1ea&quot;&gt;
&lt;td id=&quot;VU}}&quot; style=&quot;width: 22.4418%;&quot;&gt;&lt;b&gt;orientation&lt;/b&gt;&lt;/td&gt;
&lt;td id=&quot;iowx&quot; style=&quot;width: 21.5117%;&quot;&gt;선택&lt;/td&gt;
&lt;td id=&quot;&amp;lt;S^c&quot; style=&quot;width: 33.7209%;&quot;&gt;기본 화면 방향&lt;/td&gt;
&lt;td id=&quot;SyJN&quot; style=&quot;width: 22.2093%;&quot;&gt;&quot;portrait&quot;, &quot;landscape&quot;, &quot;any&quot;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;30316d47-b05b-808b-b24b-dbf5c811bf65&quot;&gt;
&lt;td id=&quot;VU}}&quot; style=&quot;width: 22.4418%;&quot;&gt;&lt;b&gt;scope&lt;/b&gt;&lt;/td&gt;
&lt;td id=&quot;iowx&quot; style=&quot;width: 21.5117%;&quot;&gt;선택&lt;/td&gt;
&lt;td id=&quot;&amp;lt;S^c&quot; style=&quot;width: 33.7209%;&quot;&gt;PWA로 간주될 URL 범위&lt;/td&gt;
&lt;td id=&quot;SyJN&quot; style=&quot;width: 22.2093%;&quot;&gt;&quot;/&quot;, &quot;/app/&quot;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;30316d47-b05b-804a-aea4-c1755de7aab5&quot;&gt;
&lt;td id=&quot;VU}}&quot; style=&quot;width: 22.4418%;&quot;&gt;&lt;b&gt;lang&lt;/b&gt;&lt;/td&gt;
&lt;td id=&quot;iowx&quot; style=&quot;width: 21.5117%;&quot;&gt;선택&lt;/td&gt;
&lt;td id=&quot;&amp;lt;S^c&quot; style=&quot;width: 33.7209%;&quot;&gt;기본 언어&lt;/td&gt;
&lt;td id=&quot;SyJN&quot; style=&quot;width: 22.2093%;&quot;&gt;&quot;ko-KR&quot;, &quot;en-US&quot;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;30316d47-b05b-8083-a34e-ec70ddf79279&quot;&gt;
&lt;td id=&quot;VU}}&quot; style=&quot;width: 22.4418%;&quot;&gt;&lt;b&gt;dir&lt;/b&gt;&lt;/td&gt;
&lt;td id=&quot;iowx&quot; style=&quot;width: 21.5117%;&quot;&gt;선택&lt;/td&gt;
&lt;td id=&quot;&amp;lt;S^c&quot; style=&quot;width: 33.7209%;&quot;&gt;텍스트 방향&lt;/td&gt;
&lt;td id=&quot;SyJN&quot; style=&quot;width: 22.2093%;&quot;&gt;&quot;ltr&quot;, &quot;rtl&quot;, &quot;auto&quot;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;30316d47-b05b-80a6-a61e-cd8917b4ae6c&quot;&gt;
&lt;td id=&quot;VU}}&quot; style=&quot;width: 22.4418%;&quot;&gt;&lt;b&gt;categories&lt;/b&gt;&lt;/td&gt;
&lt;td id=&quot;iowx&quot; style=&quot;width: 21.5117%;&quot;&gt;선택&lt;/td&gt;
&lt;td id=&quot;&amp;lt;S^c&quot; style=&quot;width: 33.7209%;&quot;&gt;앱 카테고리&lt;/td&gt;
&lt;td id=&quot;SyJN&quot; style=&quot;width: 22.2093%;&quot;&gt;[&quot;productivity&quot;, &quot;education&quot;]&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;30316d47-b05b-80a1-ba82-e88ddf5b384d&quot;&gt;
&lt;td id=&quot;VU}}&quot; style=&quot;width: 22.4418%;&quot;&gt;&lt;b&gt;screenshots&lt;/b&gt;&lt;/td&gt;
&lt;td id=&quot;iowx&quot; style=&quot;width: 21.5117%;&quot;&gt;선택&lt;/td&gt;
&lt;td id=&quot;&amp;lt;S^c&quot; style=&quot;width: 33.7209%;&quot;&gt;앱 스크린샷 배열&lt;/td&gt;
&lt;td id=&quot;SyJN&quot; style=&quot;width: 22.2093%;&quot;&gt;[{src, sizes, type}]&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;30316d47-b05b-80ac-833b-f72866c16d98&quot;&gt;
&lt;td id=&quot;VU}}&quot; style=&quot;width: 22.4418%;&quot;&gt;&lt;b&gt;shortcuts&lt;/b&gt;&lt;/td&gt;
&lt;td id=&quot;iowx&quot; style=&quot;width: 21.5117%;&quot;&gt;선택&lt;/td&gt;
&lt;td id=&quot;&amp;lt;S^c&quot; style=&quot;width: 33.7209%;&quot;&gt;앱 바로가기 메뉴&lt;/td&gt;
&lt;td id=&quot;SyJN&quot; style=&quot;width: 22.2093%;&quot;&gt;[{name, url, icons}]&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;30316d47-b05b-8096-a807-c59fc0cfc7fa&quot;&gt;
&lt;td id=&quot;VU}}&quot; style=&quot;width: 22.4418%;&quot;&gt;&lt;b&gt;related_applications&lt;/b&gt;&lt;/td&gt;
&lt;td id=&quot;iowx&quot; style=&quot;width: 21.5117%;&quot;&gt;선택&lt;/td&gt;
&lt;td id=&quot;&amp;lt;S^c&quot; style=&quot;width: 33.7209%;&quot;&gt;관련 네이티브 앱 정보&lt;/td&gt;
&lt;td id=&quot;SyJN&quot; style=&quot;width: 22.2093%;&quot;&gt;[{platform, url, id}]&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;30316d47-b05b-802d-b6a5-cb6073c18afd&quot;&gt;
&lt;td id=&quot;VU}}&quot; style=&quot;width: 22.4418%;&quot;&gt;&lt;b&gt;prefer_related_applications&lt;/b&gt;&lt;/td&gt;
&lt;td id=&quot;iowx&quot; style=&quot;width: 21.5117%;&quot;&gt;선택&lt;/td&gt;
&lt;td id=&quot;&amp;lt;S^c&quot; style=&quot;width: 33.7209%;&quot;&gt;네이티브 앱 우선 설치 여부&lt;/td&gt;
&lt;td id=&quot;SyJN&quot; style=&quot;width: 22.2093%;&quot;&gt;true, false&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;30316d47-b05b-807f-801f-d0709c1a8f5d&quot;&gt;
&lt;td id=&quot;VU}}&quot; style=&quot;width: 22.4418%;&quot;&gt;&lt;b&gt;display_override&lt;/b&gt;&lt;/td&gt;
&lt;td id=&quot;iowx&quot; style=&quot;width: 21.5117%;&quot;&gt;선택&lt;/td&gt;
&lt;td id=&quot;&amp;lt;S^c&quot; style=&quot;width: 33.7209%;&quot;&gt;표시 모드 우선순위 목록&lt;/td&gt;
&lt;td id=&quot;SyJN&quot; style=&quot;width: 22.2093%;&quot;&gt;[&quot;window-controls-overlay&quot;, &quot;standalone&quot;]&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;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  [참고]&lt;/b&gt; manifest.json 파일 내에 정의할 수 있는 정보들은 아래에서 확인이 가능합니다.&lt;/blockquote&gt;
&lt;figure id=&quot;og_1771489098972&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;Web application manifest - Progressive web apps | MDN&quot; data-og-description=&quot;&quot; data-og-host=&quot;developer.mozilla.org&quot; data-og-source-url=&quot;https://developer.mozilla.org/en-US/docs/Web/Progressive_web_apps/Manifest&quot; data-og-url=&quot;https://developer.mozilla.org/en-US/docs/Web/Progressive_web_apps/Manifest&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/Progressive_web_apps/Manifest&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://developer.mozilla.org/en-US/docs/Web/Progressive_web_apps/Manifest&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;Web application manifest - Progressive web apps | MDN&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;developer.mozilla.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;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. service-worker.js&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  service-worker.js&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- public/service-worker.js 파일 내에 위치하였고, index.html 파일 실행 시 해당 스크립트 파일을 실행시킵니다.&lt;/b&gt;&lt;br /&gt;- 실행이 되면, &amp;lsquo;1. 서비스 워커 설치 (최초 1회 실행)가 실행&amp;rsquo;되어서 서비스 워커가 실행이 됩니다. 그리고 활성화되었을 때, 활성화 이벤트를 추가하였습니다.&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1023&quot; data-origin-height=&quot;679&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/W6CE3/dJMcadAVEKx/5oNfMOKVMlyC2HHM00JaD1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/W6CE3/dJMcadAVEKx/5oNfMOKVMlyC2HHM00JaD1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/W6CE3/dJMcadAVEKx/5oNfMOKVMlyC2HHM00JaD1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FW6CE3%2FdJMcadAVEKx%2F5oNfMOKVMlyC2HHM00JaD1%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;1023&quot; height=&quot;679&quot; data-origin-width=&quot;1023&quot; data-origin-height=&quot;679&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot;&gt;&lt;code&gt;/**
 * 1. 서비스 워커 설치 (최초 1회 실행)
 */
self.addEventListener(&quot;install&quot;, (event) =&amp;gt; {
  console.log(&quot;서비스 워커가 설치되었습니다!&quot;);
  // 대기 중인 서비스 워커를 즉시 활성화
  self.skipWaiting();
});

/**
 * 2. 서비스 워커 활성화
 */
self.addEventListener(&quot;activate&quot;, (event) =&amp;gt; {
  console.log(&quot;서비스 워커가 활성화되었습니다!&quot;);
});

/**
 * 3. 네트워크 요청 가로채기 (PWA 오프라인 작동의 핵심)
 */
self.addEventListener(&quot;fetch&quot;, (event) =&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;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. index.html&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  index.html&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 최초 실행되는 페이지 내에 서비스 워커 지원여부에 따라, 이전에 생성한 파일을 기반으로 서비스 워커 등록을 수행하도록 처리합니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;pre class=&quot;xml&quot;&gt;&lt;code&gt;&amp;lt;!doctype html&amp;gt;
&amp;lt;html lang=&quot;en&quot;&amp;gt;
  &amp;lt;head&amp;gt;
    &amp;lt;meta charset=&quot;UTF-8&quot; /&amp;gt;
    &amp;lt;link rel=&quot;icon&quot; type=&quot;image/svg+xml&quot; href=&quot;/vite.svg&quot; /&amp;gt;
    &amp;lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1.0&quot; /&amp;gt;
    &amp;lt;title&amp;gt;prototype-react-csr&amp;lt;/title&amp;gt;
  &amp;lt;/head&amp;gt;
  &amp;lt;body&amp;gt;
    &amp;lt;div id=&quot;root&quot;&amp;gt;&amp;lt;/div&amp;gt;
    &amp;lt;script&amp;gt;
    // 브라우저가 서비스 워커를 지원하는지 확인 후 등록
    if ('serviceWorker' in navigator) {
      navigator.serviceWorker.register('/service-worker.js')
        .then(() =&amp;gt; console.log(&quot;서비스 워커 등록 완료!&quot;))
        .catch((err) =&amp;gt; console.log(&quot;등록 실패:&quot;, err));
    }
  &amp;lt;/script&amp;gt;
    &amp;lt;script type=&quot;module&quot; src=&quot;/src/main.tsx&quot;&amp;gt;&amp;lt;/script&amp;gt;
  &amp;lt;/body&amp;gt;
&amp;lt;/html&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;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4. Chrome 결과 확인&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  Chrome 결과 확인&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 관리자모드 (F12)로 실행하여 애플리케이션(Application) &amp;gt; Service workers로 이동하여서 확인을 하면 정상적으로 실행이 됨을 확인하였습니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1912&quot; data-origin-height=&quot;913&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cGCX5S/dJMcaaYy5lN/oGvPdAWV4qllgOC2Mm9K8k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cGCX5S/dJMcaaYy5lN/oGvPdAWV4qllgOC2Mm9K8k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cGCX5S/dJMcaaYy5lN/oGvPdAWV4qllgOC2Mm9K8k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcGCX5S%2FdJMcaaYy5lN%2FoGvPdAWV4qllgOC2Mm9K8k%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;1912&quot; height=&quot;913&quot; data-origin-width=&quot;1912&quot; data-origin-height=&quot;913&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;h3 data-ke-size=&quot;size23&quot;&gt;5. 앱으로 실행하기&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  앱으로 실행하기&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;- Chrome 가장 오른쪽 점 3개 아이콘(⋮)을 누르고 &amp;gt; 전송, 저장, 공유 &amp;gt; 페이지를 앱으로 설치를 선택합니다.&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1913&quot; data-origin-height=&quot;956&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mOafY/dJMcahi7OJL/DkLpNjzl3vLbiqWi9nsrS0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mOafY/dJMcahi7OJL/DkLpNjzl3vLbiqWi9nsrS0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mOafY/dJMcahi7OJL/DkLpNjzl3vLbiqWi9nsrS0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmOafY%2FdJMcahi7OJL%2FDkLpNjzl3vLbiqWi9nsrS0%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;1913&quot; height=&quot;956&quot; data-origin-width=&quot;1913&quot; data-origin-height=&quot;956&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;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  설치 버튼을 누릅니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;754&quot; data-origin-height=&quot;345&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ckZa9E/dJMcahi7OKD/51N2LtRn7wREBseoqJTXBk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ckZa9E/dJMcahi7OKD/51N2LtRn7wREBseoqJTXBk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ckZa9E/dJMcahi7OKD/51N2LtRn7wREBseoqJTXBk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FckZa9E%2FdJMcahi7OKD%2F51N2LtRn7wREBseoqJTXBk%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;754&quot; height=&quot;345&quot; data-origin-width=&quot;754&quot; data-origin-height=&quot;345&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  아래와 같이 실행이 되었습니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1211&quot; data-origin-height=&quot;932&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bQcxPf/dJMcaflhll9/a8EYLEF3kly6w3AvEqqUa1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bQcxPf/dJMcaflhll9/a8EYLEF3kly6w3AvEqqUa1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bQcxPf/dJMcaflhll9/a8EYLEF3kly6w3AvEqqUa1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbQcxPf%2FdJMcaflhll9%2Fa8EYLEF3kly6w3AvEqqUa1%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;1211&quot; height=&quot;932&quot; data-origin-width=&quot;1211&quot; data-origin-height=&quot;932&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;h2 data-ke-size=&quot;size26&quot;&gt;4) PWA 푸시 메시지 테스트&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  PWA 푸시메시지 테스트&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 푸시 메시지 권한 요청&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  푸시 메시지 권한 요청&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;- 아래와 같이 화면 실행 시 권한을 체크하는 부분을 추가하였습니다.&lt;br /&gt;- 상태를 체크하고, 권한이 없는 경우 Notification.requestPermission()를 통해서 권한 요청을 수행합니다.&lt;/blockquote&gt;
&lt;pre class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot;&gt;&lt;code&gt; useEffect(() =&amp;gt; {
      requestNotificationPermission();
  }, []);

  const requestNotificationPermission = async () =&amp;gt; {
      // 브라우저가 알림을 지원하는지 확인
      if (!('Notification' in window)) {
          console.log('이 브라우저는 알림을 지원하지 않습니다.');
          return false;
      }

      // 이미 권한이 있는 경우
      if (Notification.permission === 'granted') {
          console.log('알림 권한이 이미 허용되어 있습니다.');
          return true;
      }

      // 권한 거부된 경우
      if (Notification.permission === 'denied') {
          console.log('알림 권한이 거부되었습니다.');
          return false;
      }

      // 권한 요청
      const permission = await Notification.requestPermission();

      if (permission === 'granted') {
          console.log('알림 권한이 허용되었습니다.');
          return true;
      } else {
          console.log('알림 권한이 거부되었습니다.');
          return false;
      }
  }&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;h3 data-ke-size=&quot;size23&quot;&gt;2. 푸시 메시지 전송&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  푸시 메시지 전송&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 아래의 함수에서 푸시메시지 전송 테스트를 수행합니다.&lt;/b&gt;&lt;br /&gt;- 권한 체크를 한번 더 수행한 이후에 navigator.serviceWorker.getRegistration() 함수로 서비스 워커 등록 상태를 확인합니다.&lt;br /&gt;- 서비스 워커 등록이 완료된 경우, registration.showNotification()를 통해서 전송하려는 메시지를 구성합니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt; const sendTestPush = async () =&amp;gt; {
  if (permission !== &quot;granted&quot;) {
      alert(&quot;알림 권한이 허용되지 않았습니다.&quot;);
      return;
  }

  try {
      const registration = await navigator.serviceWorker.getRegistration();

      if (registration) {
          await registration.showNotification(&quot;로컬 테스트 알림&quot;, {
              body: &quot;Service Worker를 통한 직접 알림입니다!&quot;,
              icon: &quot;/icon-192.png&quot;,
              badge: &quot;/icon-192.png&quot;,
              requireInteraction: true, // 자동으로 안 사라지게
              data: { url: &quot;/&quot; }
          });
          console.log(&quot;✅ 알림 전송 완료!&quot;);
      }
  } catch (error) {
      console.error(&quot;❌ 알림 전송 실패:&quot;, error);
  }
};
&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;h3 data-ke-size=&quot;size23&quot;&gt;3. 전송 테스트&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  전송 테스트&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 구성한 버튼을 클릭하거나 Service Worker 메뉴에서 선택하여서 푸시 메시지를 전송합니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  아래와 같이 버튼을 눌렀을 때, 푸시 메시지가 정상적으로 출력이 됨을 확인하였습니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1918&quot; data-origin-height=&quot;956&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/q5MsS/dJMcafZREKT/7rtzwHg1HC0LQy6oPEACq1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/q5MsS/dJMcafZREKT/7rtzwHg1HC0LQy6oPEACq1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/q5MsS/dJMcafZREKT/7rtzwHg1HC0LQy6oPEACq1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fq5MsS%2FdJMcafZREKT%2F7rtzwHg1HC0LQy6oPEACq1%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;1918&quot; height=&quot;956&quot; data-origin-width=&quot;1918&quot; data-origin-height=&quot;956&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;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  아래와 같이 Service Worker 메뉴에서 푸시 메시지가 정상적으로 출력이 됨을 확인하였습니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1919&quot; data-origin-height=&quot;953&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dimtGU/dJMcacvlwkk/8B7Gl9eNotBdWn1mKU0J4k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dimtGU/dJMcacvlwkk/8B7Gl9eNotBdWn1mKU0J4k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dimtGU/dJMcacvlwkk/8B7Gl9eNotBdWn1mKU0J4k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdimtGU%2FdJMcacvlwkk%2F8B7Gl9eNotBdWn1mKU0J4k%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;1919&quot; height=&quot;953&quot; data-origin-width=&quot;1919&quot; data-origin-height=&quot;953&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;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;[더 알아보기]&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt; &amp;nbsp;PWA를 구성한 상태에서 알람을 다른 다른 사람에게 보내려면 어떻게 해야 할까?&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;- 사용자 A의 PWA를 백엔드 서버를 통해서 사용자 B의 PWA로 전송하는 &amp;lsquo;web-push&amp;rsquo;를 사용합니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;5) PWA 미디어 쿼리&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  PWA 미디어 쿼리&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 미디어 쿼리는 반응형 디자인의 핵심으로, CSS @media를 사용하여 화면 크기(모바일/데스크톱)에 맞춰 레이아웃을 조정하고 설치형 앱 모드(display-mode)에 따라 스타일을 차별화합니다.&lt;/b&gt;&lt;br /&gt;- display-mode: standalone 등을 통해 브라우저 UI 없이 네이티브 앱처럼 보이도록 사용자 환경을 최적화할 수 있습니다.&lt;/blockquote&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 코드 확인&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  아래와 같은 미디어 쿼리를 적용하였습니다.&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- @media (min-width: 768px)와 @media (min-width: 1200px)에 맞게 각각 적용하여서 반응형 디자인이 적용되었습니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;/* --- 모바일 우선 기본 스타일 --- */
.container {
    min-height: 100vh;
    background-color: #f5f5f7;
    display: flex;
    flex-direction: column;
    align-items: center;
}

.header {
    width: 100%;
    background-color: #fff;
    border-bottom: 1px solid #d2d2d7;
    display: flex;
    justify-content: center;
}

.header-inner {
    width: 100%;
    max-width: 100%;
    padding: 50px 20px 15px; /* 모바일 노치 고려 */
    display: flex;
    justify-content: space-between;
    align-items: center;
}

.main {
    flex: 1;
    width: 100%;
    max-width: 100%;
    padding: 20px;
}

.card {
    background: #fff;
    border-radius: 20px;
    padding: 30px;
    box-shadow: 0 10px 30px rgba(0,0,0,0.05);
    text-align: center;
}

.word {
    font-size: 32px;
}

.action-section {
    display: flex;
    justify-content: center;
    width: 100%;
    margin-top: 20px;
}

.button {
    width: 100%;
    padding: 18px;
    border-radius: 12px;
    border: none;
    background-color: #007aff;
    color: #fff;
    font-size: 18px;
    font-weight: bold;
    cursor: pointer;
    transition: transform 0.2s, background-color 0.2s;
}

.button:active {
    transform: scale(0.98);
    background-color: #0056b3;
}

/* 모바일에서 하단 탭바 노출 */
.nav {
    display: flex;
    position: fixed;
    bottom: 0;
    width: 100%;
    height: 80px;
    background: #fff;
    border-top: 1px solid #d2d2d7;
    justify-content: space-around;
    align-items: center;
    padding-bottom: env(safe-area-inset-bottom);
}

/* --- 태블릿 이상 (768px 이상) --- */
@media (min-width: 768px) {
    .header-inner {
        max-width: 1024px;
        padding: 20px;
    }

    .main {
        max-width: 1024px;
        padding: 40px 20px;
    }

    .card {
        border-radius: 24px;
        padding: 60px;
    }

    .word {
        font-size: inherit; /* 기본 폰트 사이즈로 복원 */
    }

    .button {
        max-width: 300px;
        border-radius: 15px;
    }

    /* PC에서는 하단 탭바 숨김 */
    .nav {
        display: none;
    }
}

/* --- 대형 화면 (1200px 이상, 선택사항) --- */
@media (min-width: 1200px) {
    .header-inner,
    .main {
        max-width: 1200px;
    }

    .card {
        padding: 80px;
    }
}
&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;h3 data-ke-size=&quot;size23&quot;&gt;3. 결과 확인&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  브라우저에서 아래와 같이 보이는 형태입니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1919&quot; data-origin-height=&quot;955&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/sWjtG/dJMcahXHwUu/oBqR0guaKDrvUDcxVHsJ91/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/sWjtG/dJMcahXHwUu/oBqR0guaKDrvUDcxVHsJ91/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/sWjtG/dJMcahXHwUu/oBqR0guaKDrvUDcxVHsJ91/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FsWjtG%2FdJMcahXHwUu%2FoBqR0guaKDrvUDcxVHsJ91%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;1919&quot; height=&quot;955&quot; data-origin-width=&quot;1919&quot; data-origin-height=&quot;955&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;blockquote data-ke-style=&quot;style3&quot;&gt; 아이폰 14 pro max와 같은 형태로 미디어 쿼리를 적용하여 반응형이 된다면, 모바일과 비슷한 형태로 출력이 됨을 확인할 수 있습니다.&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1917&quot; data-origin-height=&quot;954&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cDjGM1/dJMcagdrhLl/aqTo9p5aYhDXJUqGXIKcN0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cDjGM1/dJMcagdrhLl/aqTo9p5aYhDXJUqGXIKcN0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cDjGM1/dJMcagdrhLl/aqTo9p5aYhDXJUqGXIKcN0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcDjGM1%2FdJMcagdrhLl%2FaqTo9p5aYhDXJUqGXIKcN0%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;1917&quot; height=&quot;954&quot; data-origin-width=&quot;1917&quot; data-origin-height=&quot;954&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;h2 data-ke-size=&quot;size26&quot;&gt;5) Vite Plugin PWA 활용&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  Vite Plugin PWA&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- Vite 스타터를 이용하는 경우에 별도의 설정 없이 PWA를 이용할 수 있도록 제공해 주는 플러그인을 의미합니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.npmjs.com/package/vite-plugin-pwa&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.npmjs.com/package/vite-plugin-pwa&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 설치&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot;&gt;&lt;code&gt;# npm
npm i vite-plugin-pwa -D

# yarn
yarn add vite-plugin-pwa -D

# pnpm
pnpm add vite-plugin-pwa -D&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;h3 data-ke-size=&quot;size23&quot;&gt;2. 구성 환경&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  구성 환경&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 내장된 vite.config.js / vite.config.ts 파일 내에 아래와 같이 vite-plugin-pwa를 import 하고 VitePWA() 내에 옵션을 명시하여서 구성을 합니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;import  {  VitePWA  }  from  'vite-plugin-pwa'

export  default  { 
  plugins : [ 
    VitePWA ( ) 
  ] 
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  VitePWA의 VitePWAOptions 옵션들&lt;/b&gt;&lt;/blockquote&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 623px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style2&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 30px;&quot;&gt;
&lt;td style=&quot;height: 30px; width: 18.6047%;&quot;&gt;&lt;b&gt;옵션&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 30px; width: 29.8838%;&quot;&gt;&lt;b&gt;타입&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 30px; width: 21.9767%;&quot;&gt;&lt;b&gt;기본 값&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 30px; width: 29.4186%;&quot;&gt;&lt;b&gt;설명&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 35px;&quot;&gt;
&lt;td style=&quot;height: 35px; width: 18.6047%;&quot;&gt;mode&lt;/td&gt;
&lt;td style=&quot;height: 35px; width: 29.8838%;&quot;&gt;'development' \| 'production'&lt;/td&gt;
&lt;td style=&quot;height: 35px; width: 21.9767%;&quot;&gt;process.env.NODE_ENV&lt;/td&gt;
&lt;td style=&quot;height: 35px; width: 29.4186%;&quot;&gt;빌드 모드&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;height: 18px; width: 18.6047%;&quot;&gt;srcDir&lt;/td&gt;
&lt;td style=&quot;height: 18px; width: 29.8838%;&quot;&gt;string&lt;/td&gt;
&lt;td style=&quot;height: 18px; width: 21.9767%;&quot;&gt;'public'&lt;/td&gt;
&lt;td style=&quot;height: 18px; width: 29.4186%;&quot;&gt;소스 디렉토리&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;height: 18px; width: 18.6047%;&quot;&gt;outDir&lt;/td&gt;
&lt;td style=&quot;height: 18px; width: 29.8838%;&quot;&gt;string&lt;/td&gt;
&lt;td style=&quot;height: 18px; width: 21.9767%;&quot;&gt;'dist'&lt;/td&gt;
&lt;td style=&quot;height: 18px; width: 29.4186%;&quot;&gt;출력 디렉토리&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;height: 18px; width: 18.6047%;&quot;&gt;filename&lt;/td&gt;
&lt;td style=&quot;height: 18px; width: 29.8838%;&quot;&gt;string&lt;/td&gt;
&lt;td style=&quot;height: 18px; width: 21.9767%;&quot;&gt;'sw.js'&lt;/td&gt;
&lt;td style=&quot;height: 18px; width: 29.4186%;&quot;&gt;서비스 워커 파일명&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 35px;&quot;&gt;
&lt;td style=&quot;height: 35px; width: 18.6047%;&quot;&gt;manifestFilename&lt;/td&gt;
&lt;td style=&quot;height: 35px; width: 29.8838%;&quot;&gt;string&lt;/td&gt;
&lt;td style=&quot;height: 35px; width: 21.9767%;&quot;&gt;'manifest.webmanifest'&lt;/td&gt;
&lt;td style=&quot;height: 35px; width: 29.4186%;&quot;&gt;manifest 파일명&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;height: 18px; width: 18.6047%;&quot;&gt;strategies&lt;/td&gt;
&lt;td style=&quot;height: 18px; width: 29.8838%;&quot;&gt;'generateSW' | 'injectManifest'&lt;/td&gt;
&lt;td style=&quot;height: 18px; width: 21.9767%;&quot;&gt;'generateSW'&lt;/td&gt;
&lt;td style=&quot;height: 18px; width: 29.4186%;&quot;&gt;SW 생성 전략&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;height: 18px; width: 18.6047%;&quot;&gt;scope&lt;/td&gt;
&lt;td style=&quot;height: 18px; width: 29.8838%;&quot;&gt;string&lt;/td&gt;
&lt;td style=&quot;height: 18px; width: 21.9767%;&quot;&gt;Vite base와 동일&lt;/td&gt;
&lt;td style=&quot;height: 18px; width: 29.4186%;&quot;&gt;SW 등록 스코프&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 35px;&quot;&gt;
&lt;td style=&quot;height: 35px; width: 18.6047%;&quot;&gt;injectRegister&lt;/td&gt;
&lt;td style=&quot;height: 35px; width: 29.8838%;&quot;&gt;'inline' | 'script' | 'script-defer' | 'auto' | false&lt;/td&gt;
&lt;td style=&quot;height: 35px; width: 21.9767%;&quot;&gt;'auto'&lt;/td&gt;
&lt;td style=&quot;height: 35px; width: 29.4186%;&quot;&gt;SW 등록 방식&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;height: 18px; width: 18.6047%;&quot;&gt;registerType&lt;/td&gt;
&lt;td style=&quot;height: 18px; width: 29.8838%;&quot;&gt;'prompt' | 'autoUpdate'&lt;/td&gt;
&lt;td style=&quot;height: 18px; width: 21.9767%;&quot;&gt;'prompt'&lt;/td&gt;
&lt;td style=&quot;height: 18px; width: 29.4186%;&quot;&gt;SW 업데이트 방식&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;height: 18px; width: 18.6047%;&quot;&gt;minify&lt;/td&gt;
&lt;td style=&quot;height: 18px; width: 29.8838%;&quot;&gt;boolean&lt;/td&gt;
&lt;td style=&quot;height: 18px; width: 21.9767%;&quot;&gt;true&lt;/td&gt;
&lt;td style=&quot;height: 18px; width: 29.4186%;&quot;&gt;manifest 압축 여부&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 35px;&quot;&gt;
&lt;td style=&quot;height: 35px; width: 18.6047%;&quot;&gt;manifest&lt;/td&gt;
&lt;td style=&quot;height: 35px; width: 29.8838%;&quot;&gt;Partial&amp;lt;ManifestOptions&amp;gt; | false&lt;/td&gt;
&lt;td style=&quot;height: 35px; width: 21.9767%;&quot;&gt;-&lt;/td&gt;
&lt;td style=&quot;height: 35px; width: 29.4186%;&quot;&gt;manifest 객체&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 37px;&quot;&gt;
&lt;td style=&quot;height: 37px; width: 18.6047%;&quot;&gt;useCredentials&lt;/td&gt;
&lt;td style=&quot;height: 37px; width: 29.8838%;&quot;&gt;boolean&lt;/td&gt;
&lt;td style=&quot;height: 37px; width: 21.9767%;&quot;&gt;false&lt;/td&gt;
&lt;td style=&quot;height: 37px; width: 29.4186%;&quot;&gt;manifest link에 crossorigin=&quot;use-credentials&quot; 추가&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;height: 18px; width: 18.6047%;&quot;&gt;workbox&lt;/td&gt;
&lt;td style=&quot;height: 18px; width: 29.8838%;&quot;&gt;Partial&amp;lt;GenerateSWOptions&amp;gt;&lt;/td&gt;
&lt;td style=&quot;height: 18px; width: 21.9767%;&quot;&gt;-&lt;/td&gt;
&lt;td style=&quot;height: 18px; width: 29.4186%;&quot;&gt;generateSW 전략 workbox 설정&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 35px;&quot;&gt;
&lt;td style=&quot;height: 35px; width: 18.6047%;&quot;&gt;injectManifest&lt;/td&gt;
&lt;td style=&quot;height: 35px; width: 29.8838%;&quot;&gt;Partial&amp;lt;CustomInjectManifestOptions&amp;gt;&lt;/td&gt;
&lt;td style=&quot;height: 35px; width: 21.9767%;&quot;&gt;-&lt;/td&gt;
&lt;td style=&quot;height: 35px; width: 29.4186%;&quot;&gt;injectManifest 전략 workbox 설정&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;height: 18px; width: 18.6047%;&quot;&gt;base&lt;/td&gt;
&lt;td style=&quot;height: 18px; width: 29.8838%;&quot;&gt;string&lt;/td&gt;
&lt;td style=&quot;height: 18px; width: 21.9767%;&quot;&gt;Vite base&lt;/td&gt;
&lt;td style=&quot;height: 18px; width: 29.4186%;&quot;&gt;PWA 전용 base 경로 override&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 37px;&quot;&gt;
&lt;td style=&quot;height: 37px; width: 18.6047%;&quot;&gt;includeAssets&lt;/td&gt;
&lt;td style=&quot;height: 37px; width: 29.8838%;&quot;&gt;string \| string[]&lt;/td&gt;
&lt;td style=&quot;height: 37px; width: 21.9767%;&quot;&gt;-&lt;/td&gt;
&lt;td style=&quot;height: 37px; width: 29.4186%;&quot;&gt;SW precache에 추가할 public 리소스&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 37px;&quot;&gt;
&lt;td style=&quot;height: 37px; width: 18.6047%;&quot;&gt;includeManifestIcons&lt;/td&gt;
&lt;td style=&quot;height: 37px; width: 29.8838%;&quot;&gt;boolean&lt;/td&gt;
&lt;td style=&quot;height: 37px; width: 21.9767%;&quot;&gt;true&lt;/td&gt;
&lt;td style=&quot;height: 37px; width: 29.4186%;&quot;&gt;manifest 아이콘 SW precache 포함 여부&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;height: 18px; width: 18.6047%;&quot;&gt;&lt;b&gt;disable&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 18px; width: 29.8838%;&quot;&gt;&lt;b&gt;boolean&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 18px; width: 21.9767%;&quot;&gt;&lt;b&gt;false&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 18px; width: 29.4186%;&quot;&gt;&lt;b&gt;빌드 시 SW 생성/등록 비활성화&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;height: 18px; width: 18.6047%;&quot;&gt;&lt;b&gt;devOptions&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 18px; width: 29.8838%;&quot;&gt;&lt;b&gt;DevOptions&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 18px; width: 21.9767%;&quot;&gt;&lt;b&gt;-&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 18px; width: 29.4186%;&quot;&gt;&lt;b&gt;개발 환경 옵션&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;height: 18px; width: 18.6047%;&quot;&gt;&lt;b&gt;selfDestroying&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 18px; width: 29.8838%;&quot;&gt;&lt;b&gt;boolean&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 18px; width: 21.9767%;&quot;&gt;&lt;b&gt;false&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 18px; width: 29.4186%;&quot;&gt;&lt;b&gt;SW 등록 해제 여부&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;height: 18px; width: 18.6047%;&quot;&gt;&lt;b&gt;buildBase&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 18px; width: 29.8838%;&quot;&gt;&lt;b&gt;string&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 18px; width: 21.9767%;&quot;&gt;&lt;b&gt;vite.base&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 18px; width: 29.4186%;&quot;&gt;&lt;b&gt;빌드 폴더와 base 경로가 다를 때 설정&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 36px;&quot;&gt;
&lt;td style=&quot;height: 36px; width: 18.6047%;&quot;&gt;&lt;b&gt;pwaAssets&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 36px; width: 29.8838%;&quot;&gt;&lt;b&gt;PWAAssetsOptions&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 36px; width: 21.9767%;&quot;&gt;&lt;b&gt;-&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 36px; width: 29.4186%;&quot;&gt;&lt;b&gt;PWA 에셋 자동 생성/주입 (experimental)&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 37px;&quot;&gt;
&lt;td style=&quot;height: 37px; width: 18.6047%;&quot;&gt;&lt;b&gt;showMaximumFileSizeToCacheInBytesWarning&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 37px; width: 29.8838%;&quot;&gt;&lt;b&gt;boolean&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 37px; width: 21.9767%;&quot;&gt;&lt;b&gt;false&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 37px; width: 29.4186%;&quot;&gt;&lt;b&gt;캐시 파일 크기 초과 경고를 에러 대신 warning으로 처리&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;h3 data-ke-size=&quot;size23&quot;&gt;3. 사용 예시&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  사용 예시&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 아래와 같이 vite.cofnig.ts 파일 내에 manifest.json 파일을 불러와서 import 하도록 구성하였습니다. 또한, registerType: 'autoUpdate'를 통해서 서비스 워커 업데이트 방식을 정의하는 옵션을 지정하였습니다.&lt;/b&gt;&lt;br /&gt;- autoUpdate는 새 버전 감지 시 사용자 개입 없이 자동으로 서비스 워커 업데이트되며, 다음 페이지 reload 시 새 버전 적용되는 옵션입니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  registerType 속성&lt;/b&gt;&lt;/blockquote&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style2&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;옵션&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;동작 사용&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;시기&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;prompt (기본값)&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;새 버전 감지 시 대기, useRegisterSW 훅으로 직접 UI 구현 필요&lt;/td&gt;
&lt;td&gt;사용자에게 업데이트 시점을 알리고 싶을 때&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;autoUpdate&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;새 버전 감지 시 자동 업데이트 + 모든 탭 자동 새로고침&lt;/td&gt;
&lt;td&gt;사용자 개입 없이 항상 최신 버전 유지할 때&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;pre class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot;&gt;&lt;code&gt;import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import { VitePWA } from 'vite-plugin-pwa';
import manifestJson from './public/manifest.json';
import type { ManifestOptions } from 'vite-plugin-pwa';

const manifest = manifestJson as ManifestOptions;

export default defineConfig({
	plugins: [
		react(),
		VitePWA({
			registerType: 'autoUpdate',
			manifest,
		}),
	],
});&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  아래와 같이 manifest.json 파일을 아래와 같이 구성을 하였습니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;{
	&quot;name&quot;: &quot;React Template&quot;,
	&quot;short_name&quot;: &quot;React Template&quot;,
	&quot;description&quot;: &quot;PWA 테스트&quot;,
	&quot;id&quot;: &quot;/&quot;,
	&quot;start_url&quot;: &quot;/&quot;,
	&quot;display&quot;: &quot;standalone&quot;,
	&quot;background_color&quot;: &quot;#ffffff&quot;,
	&quot;theme_color&quot;: &quot;#007bff&quot;,
	&quot;orientation&quot;: &quot;portrait&quot;,
	&quot;icons&quot;: [
		{
			&quot;src&quot;: &quot;/assets/icon-192.png&quot;,
			&quot;sizes&quot;: &quot;192x192&quot;,
			&quot;type&quot;: &quot;image/png&quot;,
			&quot;purpose&quot;: &quot;any&quot;
		},
		{
			&quot;src&quot;: &quot;/assets/icon-512.png&quot;,
			&quot;sizes&quot;: &quot;512x512&quot;,
			&quot;type&quot;: &quot;image/png&quot;,
			&quot;purpose&quot;: &quot;any&quot;
		},
		{
			&quot;src&quot;: &quot;/assets/icon-512.png&quot;,
			&quot;sizes&quot;: &quot;512x512&quot;,
			&quot;type&quot;: &quot;image/png&quot;,
			&quot;purpose&quot;: &quot;maskable&quot;
		}
	]
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&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;오늘도&amp;nbsp;감사합니다.&amp;nbsp; &lt;/b&gt;&lt;/p&gt;</description>
      <category>Javascript &amp;amp; Typescript/이해하기</category>
      <category>javascript</category>
      <category>manifest.json</category>
      <category>progressive web app</category>
      <category>Progressive Web App JS</category>
      <category>pwa</category>
      <category>PWA JS</category>
      <category>React PWA</category>
      <category>vite-plugin-pwa</category>
      <category>vite.cofnig.js</category>
      <category>vite.cofnig.ts</category>
      <author>adjh54</author>
      <guid isPermaLink="true">https://adjh54.tistory.com/740</guid>
      <comments>https://adjh54.tistory.com/740#entry740comment</comments>
      <pubDate>Thu, 19 Feb 2026 20:00:18 +0900</pubDate>
    </item>
    <item>
      <title>[제작 앱 소개] 촌수 계산기 Plus+</title>
      <link>https://adjh54.tistory.com/739</link>
      <description>&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;해당&amp;nbsp;글에서는&amp;nbsp;Contributor9&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;&lt;/span&gt;
&lt;figure contenteditable=&quot;false&quot; data-ke-type=&quot;emoticon&quot; data-ke-align=&quot;alignCenter&quot; data-emoticon-type=&quot;face&quot; data-emoticon-name=&quot;062&quot; data-emoticon-isanimation=&quot;false&quot; data-emoticon-src=&quot;https://t1.daumcdn.net/keditor/emoticon/face/large/062.png&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/keditor/emoticon/face/large/062.png&quot; width=&quot;80&quot; /&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1) 개발자는&lt;span&gt; &lt;/span&gt;왜&lt;span&gt; &lt;/span&gt;이&lt;span&gt; &lt;/span&gt;앱을&lt;span&gt; &lt;/span&gt;만들었을까&lt;span&gt;?&lt;/span&gt;&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  개발자는 왜 이 앱을 만들었을까?&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;- 한국의 호칭과 촌수 체계는 상당히 복잡한것 같습니다. 일상에서 자주 사용하지 않다 보니 정확히 기억하기 어렵고, 특히 기준이 되는 사람의 성별에 따라 호칭이 달라지기 때문에 그 복잡성은 더욱 커지는것 같습니다. 그렇지만, 생각해보면 이렇게 헷갈리고 어려운 호칭들은 오랜 기간 동안 변하지 않고 그대로 사용하는 것들입니다.&lt;br /&gt;&lt;br /&gt;- 그렇기에 나는 똑같은 고민을 반복해서 하는 것이 아닐까라는 생각이 들었습니다. 이러한, 복잡하고 어려운 호칭과 촌수를 고민 없이 필요에 따라서 앱에서 사전과 같이 꺼내 쓰고 쉽게 바로바로 찾을 수 있는 앱을 만들어보는 게 어떨까 라는 생각을 했던 것 같습니다. &lt;br /&gt;&lt;br /&gt;- 그리고 더 나아가서는 관심이 생긴다면 퀴즈로 연습해보면서 쉽게 익혀보는 것이 어떨까라는 생각이 들었습니다. &lt;br /&gt;&lt;br /&gt;&lt;b&gt;&lt;u&gt;그래서 이 앱을 개발하게 된 것 같습니다.&lt;/u&gt;&lt;/b&gt; &lt;br /&gt;&lt;br /&gt;- 촌수 계산기 Plus+ 앱에서는 우선, &amp;rsquo;내 기준&amp;rsquo;으로 성별을 선택하고 나와의 관계를 순차적으로 따라가는 구조로 구성이 되어있습니다. &lt;br /&gt;&lt;b&gt;- 이러한 관계는 &amp;lsquo;브레드스크럼(Breadcrumb)&amp;rsquo; 형태로 순차적으로 따라가서 사용자 입장에서는 좀 더 이해하기 쉽게 도움을 줍니다. 그리고 &amp;lsquo;x 아이콘&amp;rsquo;을 통해서 이전 잘못 선택한 관계는 즉시 되돌아갈 수 있습니다.&lt;/b&gt;&lt;br /&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1203&quot; data-origin-height=&quot;1204&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bhpLka/dJMcafS3Oqi/ihHtGLYR0O1D7u9u0SCC9k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bhpLka/dJMcafS3Oqi/ihHtGLYR0O1D7u9u0SCC9k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bhpLka/dJMcafS3Oqi/ihHtGLYR0O1D7u9u0SCC9k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbhpLka%2FdJMcafS3Oqi%2FihHtGLYR0O1D7u9u0SCC9k%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;577&quot; height=&quot;577&quot; data-origin-width=&quot;1203&quot; data-origin-height=&quot;1204&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;br /&gt;- 예를 들어서 나(남자) 기준으로 최초 선택을 합니다. &lt;br /&gt;나(남자) &amp;gt; 딸 &amp;gt; 아들 &amp;gt; 딸 &amp;gt; 딸을 선택하면 &amp;lsquo;외고손녀&amp;rsquo; 깊은 관계까지 탐색하여서 확인 할 수 있습니다. &lt;br /&gt;또한, 나와의 촌수, 항렬도 확인하고 복사/공유 기능을 제공합니다&lt;br /&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1206&quot; data-origin-height=&quot;1301&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b2sitA/dJMcaiWvNiy/oGh6DZZYK0hSDzSqqUJd71/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b2sitA/dJMcaiWvNiy/oGh6DZZYK0hSDzSqqUJd71/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b2sitA/dJMcaiWvNiy/oGh6DZZYK0hSDzSqqUJd71/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb2sitA%2FdJMcaiWvNiy%2FoGh6DZZYK0hSDzSqqUJd71%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;608&quot; height=&quot;656&quot; data-origin-width=&quot;1206&quot; data-origin-height=&quot;1301&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;br /&gt;&lt;br /&gt;또한, 아래와 같이 필요에 따라서 필터를 두며, 호칭을 쉽게 검색하고 확인할 수 있습니다.&lt;br /&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1206&quot; data-origin-height=&quot;2218&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b07wzQ/dJMcacoudj4/NrkciSPqIOqOO8dHkwb2y1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b07wzQ/dJMcacoudj4/NrkciSPqIOqOO8dHkwb2y1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b07wzQ/dJMcacoudj4/NrkciSPqIOqOO8dHkwb2y1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb07wzQ%2FdJMcacoudj4%2FNrkciSPqIOqOO8dHkwb2y1%2Fimg.jpg&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;503&quot; height=&quot;925&quot; data-origin-width=&quot;1206&quot; data-origin-height=&quot;2218&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;br /&gt;&lt;br /&gt;&lt;b&gt;최종적으로, 아래와 같이 내 실력을 테스트해보기 위해서 퀴즈도 할 수 있습니다.&lt;/b&gt;&lt;br /&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1181&quot; data-origin-height=&quot;2442&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bPNp5D/dJMcaajU7hR/cfxActD3Gop6GqSCl5dfG1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bPNp5D/dJMcaajU7hR/cfxActD3Gop6GqSCl5dfG1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bPNp5D/dJMcaajU7hR/cfxActD3Gop6GqSCl5dfG1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbPNp5D%2FdJMcaajU7hR%2FcfxActD3Gop6GqSCl5dfG1%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;619&quot; height=&quot;1280&quot; data-origin-width=&quot;1181&quot; data-origin-height=&quot;2442&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;br /&gt;&lt;br /&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2) 앱 소개- 촌수&amp;nbsp;계산기&amp;nbsp;Plus+&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1392&quot; data-origin-height=&quot;269&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/SnPa0/dJMcac24g3C/LNBY1FbhtMWP1KpBe6rrq1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/SnPa0/dJMcac24g3C/LNBY1FbhtMWP1KpBe6rrq1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/SnPa0/dJMcac24g3C/LNBY1FbhtMWP1KpBe6rrq1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FSnPa0%2FdJMcac24g3C%2FLNBY1FbhtMWP1KpBe6rrq1%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;1392&quot; height=&quot;269&quot; data-origin-width=&quot;1392&quot; data-origin-height=&quot;269&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  앱 소개- 촌수 계산기 Plus+&lt;br /&gt;&lt;br /&gt;-&amp;nbsp;촌수&amp;nbsp;계산기&amp;nbsp;Plus+는&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;/b&gt;&lt;br /&gt;- 부계&amp;middot;모계&amp;middot;인척 관계까지 촌수를 자동으로 계산하고, 다양한 친족 호칭을 빠르게 검색해 즉시 확인할 수 있습니다. 또한, 재미있고 유익한 &amp;lsquo;가족 관계 퀴즈&amp;rsquo;를 통해 촌수 지식을 자연스럽게 익힐 수 있어 학습과 재미를 모두 누릴 수 있습니다.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;1.&amp;nbsp;홈&lt;/b&gt;&lt;br /&gt;-&amp;nbsp;앱의&amp;nbsp;소개&amp;nbsp;및&amp;nbsp;핵심&amp;nbsp;개념을&amp;nbsp;한눈에&amp;nbsp;이해할&amp;nbsp;수&amp;nbsp;있도록&amp;nbsp;구성된&amp;nbsp;정보&amp;nbsp;중심&amp;nbsp;메인&amp;nbsp;화면입니다.&lt;br /&gt;-&amp;nbsp;스크롤&amp;nbsp;가능한&amp;nbsp;카드&amp;nbsp;형태로&amp;nbsp;촌수&amp;middot;호칭&amp;middot;계촌법&amp;middot;친족&amp;middot;인척&amp;middot;친인척&amp;middot;항렬&amp;nbsp;개념을&amp;nbsp;정리해 주며,&amp;nbsp;각&amp;nbsp;설명은&amp;nbsp;복사&amp;middot;공유&amp;nbsp;기능을&amp;nbsp;포함해&amp;nbsp;사용자가&amp;nbsp;쉽게&amp;nbsp;활용할&amp;nbsp;수&amp;nbsp;있도록&amp;nbsp;구성되어&amp;nbsp;있습니다&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;2. 계산&lt;/b&gt;&lt;br /&gt;-&amp;nbsp;가족&amp;nbsp;관계를&amp;nbsp;단계별로&amp;nbsp;선택하면,&amp;nbsp;자동으로&amp;nbsp;촌수&amp;middot;항렬&amp;middot;호칭&amp;middot;설명까지&amp;nbsp;한눈에&amp;nbsp;정리해&amp;nbsp;보여주는&amp;nbsp;핵심&amp;nbsp;기능입니다&lt;br /&gt;-&amp;nbsp;나의&amp;nbsp;성별을&amp;nbsp;선택하면&amp;nbsp;더&amp;nbsp;정확한&amp;nbsp;관계&amp;nbsp;계산&amp;nbsp;시작&lt;br /&gt;-&amp;nbsp;부모&amp;middot;조부모&amp;middot;형제&amp;middot;인척&amp;nbsp;등&amp;nbsp;원하는&amp;nbsp;관계를&amp;nbsp;버튼만&amp;nbsp;눌러&amp;nbsp;탐색&lt;br /&gt;-&amp;nbsp;선택한&amp;nbsp;경로를&amp;nbsp;기반으로&amp;nbsp;촌수,&amp;nbsp;항렬&amp;nbsp;방향(윗항렬/아랫항렬)&amp;nbsp;자동&amp;nbsp;계산&lt;br /&gt;-&amp;nbsp;정확한&amp;nbsp;호칭,&amp;nbsp;간단&amp;nbsp;설명,&amp;nbsp;특별&amp;nbsp;호칭까지&amp;nbsp;즉시&amp;nbsp;확인&lt;br /&gt;-&amp;nbsp;계산된&amp;nbsp;결과는&amp;nbsp;복사&amp;middot;공유&amp;nbsp;버튼으로&amp;nbsp;간편하게&amp;nbsp;전달&lt;br /&gt;-&amp;nbsp;가족&amp;nbsp;관계가&amp;nbsp;복잡해&amp;nbsp;헷갈릴&amp;nbsp;때,&amp;nbsp;이&amp;nbsp;화면에서&amp;nbsp;단계별로&amp;nbsp;따라가기만&amp;nbsp;하면&amp;nbsp;누구든&amp;nbsp;쉽게&amp;nbsp;정확한&amp;nbsp;촌수를&amp;nbsp;확인할&amp;nbsp;수&amp;nbsp;있습니다.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;3. 리스트&lt;/b&gt;&lt;br /&gt;-&amp;nbsp;가족&amp;nbsp;관계가&amp;nbsp;헷갈리는&amp;nbsp;순간,&amp;nbsp;이&amp;nbsp;화면에서는&amp;nbsp;원하는&amp;nbsp;호칭을&amp;nbsp;빠르게&amp;nbsp;검색하고&amp;nbsp;촌수&amp;middot;항렬&amp;middot;인척&amp;nbsp;여부까지&amp;nbsp;정확하게&amp;nbsp;확인할&amp;nbsp;수&amp;nbsp;있습니다.&lt;br /&gt;-&amp;nbsp;이름,&amp;nbsp;기본&amp;nbsp;호칭,&amp;nbsp;별칭(특별&amp;nbsp;호칭)&amp;nbsp;등&amp;nbsp;어떤&amp;nbsp;방식으로&amp;nbsp;입력해도&amp;nbsp;즉시&amp;nbsp;검색되며,&amp;nbsp;촌수(1~6촌),&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;-&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;팝업으로&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;-&amp;nbsp;필요한&amp;nbsp;친족&amp;nbsp;호칭이&amp;nbsp;떠오르지&amp;nbsp;않을&amp;nbsp;때에도,&amp;nbsp;이&amp;nbsp;화면에서&amp;nbsp;검색만&amp;nbsp;하면&amp;nbsp;남자&amp;middot;여자&amp;nbsp;기준에&amp;nbsp;따라&amp;nbsp;정확한&amp;nbsp;관계&amp;nbsp;정보를&amp;nbsp;즉시&amp;nbsp;확인할&amp;nbsp;수&amp;nbsp;있습니다.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;4. 퀴즈 모드&lt;/b&gt;&lt;br /&gt;- 촌수&amp;middot;호칭개념을 재미있게 학습하도록 구성된 퀴즈 모드의 시작 화면입니다.&lt;br /&gt;-&amp;nbsp;먼저&amp;nbsp;성별(남자/여자)를&amp;nbsp;선택하면,&amp;nbsp;해당&amp;nbsp;성별&amp;nbsp;기준으로&amp;nbsp;호칭&amp;middot;촌수&amp;nbsp;판단&amp;nbsp;기준을&amp;nbsp;정확하게&amp;nbsp;적용하여&amp;nbsp;문제를&amp;nbsp;구성합니다.&lt;br /&gt;-&amp;nbsp;상단에는&amp;nbsp;지금까지&amp;nbsp;풀었던&amp;nbsp;퀴즈의&amp;nbsp;전체&amp;nbsp;통계가&amp;nbsp;한눈에&amp;nbsp;나타나며,&amp;nbsp;맞춘&amp;nbsp;문제&amp;nbsp;수,&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;-&amp;nbsp;중앙&amp;nbsp;캐릭터와&amp;nbsp;함께&amp;nbsp;퀴즈&amp;nbsp;설명이&amp;nbsp;표시되며,&amp;nbsp;제공되는&amp;nbsp;설명을&amp;nbsp;바탕으로&amp;nbsp;어떤&amp;nbsp;호칭&amp;middot;촌수인지&amp;nbsp;맞추는&amp;nbsp;방식의&amp;nbsp;문제임을&amp;nbsp;안내합니다.&lt;br /&gt;-&amp;nbsp;총&amp;nbsp;준비된&amp;nbsp;문제&amp;nbsp;수가&amp;nbsp;실시간으로&amp;nbsp;표시되어,&amp;nbsp;얼마나&amp;nbsp;많은&amp;nbsp;문제를&amp;nbsp;풀&amp;nbsp;수&amp;nbsp;있는지도&amp;nbsp;미리&amp;nbsp;확인할&amp;nbsp;수&amp;nbsp;있습니다.&lt;br /&gt;-&amp;nbsp;성별&amp;nbsp;선택&amp;nbsp;후&amp;nbsp;[퀴즈&amp;nbsp;시작]&amp;nbsp;버튼을&amp;nbsp;누르면,&amp;nbsp;선택된&amp;nbsp;기준(남자&amp;nbsp;또는&amp;nbsp;여자)에&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;-&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;&lt;b&gt;5.&amp;nbsp;퀴즈&lt;/b&gt;&lt;br /&gt;-&amp;nbsp;호칭&amp;nbsp;찾기&amp;nbsp;퀴즈로&amp;nbsp;문제에&amp;nbsp;대한&amp;nbsp;답을&amp;nbsp;4지선다형으로&amp;nbsp;푸는&amp;nbsp;퀴즈&amp;nbsp;화면입니다.&lt;br /&gt;-&amp;nbsp;성별&amp;nbsp;기준을&amp;nbsp;선택해&amp;nbsp;퀴즈를&amp;nbsp;시작하면,&amp;nbsp;현재&amp;nbsp;풀고&amp;nbsp;있는&amp;nbsp;문제&amp;nbsp;번호&amp;middot;누적&amp;nbsp;접수&amp;middot;콤보&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;-&amp;nbsp;문제&amp;nbsp;본문은&amp;nbsp;실제&amp;nbsp;가족&amp;nbsp;관계&amp;nbsp;설명을&amp;nbsp;기반으로&amp;nbsp;구성되어&amp;nbsp;있으며,&amp;nbsp;&quot;어떤&amp;nbsp;관계인지&amp;nbsp;이해&amp;nbsp;&amp;rarr;&amp;nbsp;맞는&amp;nbsp;호칭을&amp;nbsp;선택&quot;하는&amp;nbsp;방식의&amp;nbsp;학습&amp;nbsp;중심&amp;nbsp;문제로&amp;nbsp;설계되어&amp;nbsp;있습니다.&lt;br /&gt;-&amp;nbsp;보기&amp;nbsp;선택&amp;nbsp;시&amp;nbsp;즉시&amp;nbsp;정답&amp;nbsp;여부가&amp;nbsp;판단되며,&amp;nbsp;다음&amp;nbsp;문제로&amp;nbsp;넘어갈&amp;nbsp;수&amp;nbsp;있도록&amp;nbsp;깔끔한&amp;nbsp;흐름을&amp;nbsp;제공합니다.&lt;br /&gt;-&amp;nbsp;화면&amp;nbsp;하단의&amp;nbsp;[퀴즈&amp;nbsp;종료]&amp;nbsp;버튼을&amp;nbsp;통해&amp;nbsp;언제든&amp;nbsp;종료할&amp;nbsp;수&amp;nbsp;있으며&amp;nbsp;결과&amp;nbsp;화면으로&amp;nbsp;이동할&amp;nbsp;수&amp;nbsp;있습니다.&lt;br /&gt;&lt;br /&gt;⸻&lt;br /&gt;&lt;br /&gt;&lt;b&gt;5.1. 오답 결과 모달&lt;/b&gt;&lt;br /&gt;-&amp;nbsp;정답을&amp;nbsp;잘못&amp;nbsp;선택했을&amp;nbsp;경우,&amp;nbsp;오답&amp;nbsp;결과&amp;nbsp;모달이&amp;nbsp;출력되어,&amp;nbsp;즉시&amp;nbsp;피드백을&amp;nbsp;제공합니다.&lt;br /&gt;-&amp;nbsp;상단에는&amp;nbsp;감정&amp;nbsp;표현&amp;nbsp;캐릭터와&amp;nbsp;함께&amp;nbsp;&quot;오답입니다&quot;&amp;nbsp;메시지가&amp;nbsp;표시되며,&amp;nbsp;정답이&amp;nbsp;무엇이었는지&amp;nbsp;명확히&amp;nbsp;알려줍니다.&lt;br /&gt;-&amp;nbsp;정확한&amp;nbsp;호칭,&amp;nbsp;공식&amp;nbsp;명칭,&amp;nbsp;해당&amp;nbsp;호칭에&amp;nbsp;대한&amp;nbsp;설명문,&amp;nbsp;촌수,&amp;nbsp;관계&amp;nbsp;종류(혈족/인척)&lt;br /&gt;-&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;-&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;&lt;b&gt;5.2. 정답 결과 모달&lt;/b&gt;&lt;br /&gt;-&amp;nbsp;정답을&amp;nbsp;맞췄을&amp;nbsp;경우,&amp;nbsp;정답&amp;nbsp;결과&amp;nbsp;모달이&amp;nbsp;출력되어&amp;nbsp;즉시&amp;nbsp;피드백으로&amp;nbsp;축하&amp;nbsp;메시지를&amp;nbsp;제공합니다.&lt;br /&gt;-&amp;nbsp;정답&amp;nbsp;호칭과&amp;nbsp;공식&amp;nbsp;명칭,&amp;nbsp;해당&amp;nbsp;호칭에&amp;nbsp;대한&amp;nbsp;설명문,&amp;nbsp;다른&amp;nbsp;호칭&amp;nbsp;태그(동의&amp;nbsp;호칭&amp;middot;확장&amp;nbsp;표기&amp;nbsp;등),&amp;nbsp;촌수&amp;nbsp;및&amp;nbsp;관계&amp;nbsp;종류(혈족/인척)&lt;br /&gt;-&amp;nbsp;특히&amp;nbsp;&amp;ldquo;다른&amp;nbsp;호칭&amp;rdquo;&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;-&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;-&amp;nbsp;이&amp;nbsp;과정을&amp;nbsp;반복하며&amp;nbsp;촌수&amp;middot;호칭&amp;middot;관계&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;&lt;b&gt;6.&amp;nbsp;설정&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;-&amp;nbsp;설정&amp;nbsp;화면은&amp;nbsp;앱&amp;nbsp;사용을&amp;nbsp;위한&amp;nbsp;기본&amp;nbsp;정보&amp;nbsp;관리부터&amp;nbsp;문의,&amp;nbsp;피드백,&amp;nbsp;정책&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;-&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;앱을&amp;nbsp;전달할&amp;nbsp;수&amp;nbsp;있습니다.&lt;br /&gt;-&amp;nbsp;그&amp;nbsp;아래에는&amp;nbsp;퀴즈&amp;nbsp;데이터를&amp;nbsp;관리할&amp;nbsp;수&amp;nbsp;있는&amp;nbsp;초기화&amp;nbsp;섹션이&amp;nbsp;제공됩니다.&amp;nbsp;여기에서&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;-&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;리뷰&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;소개&amp;nbsp;메뉴에서는&amp;nbsp;앱&amp;nbsp;개발자와&amp;nbsp;관련된&amp;nbsp;정보를&amp;nbsp;확인할&amp;nbsp;수&amp;nbsp;있습니다.&lt;br /&gt;-&amp;nbsp;&amp;lsquo;제작자&amp;nbsp;앱&amp;nbsp;더보기&amp;rsquo;&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;-&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;제공됩니다.&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;-&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;b&gt;&lt;br /&gt;&lt;/b&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1359&quot; data-origin-height=&quot;674&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b4zRc1/dJMb99L27eG/1np3fW9fWb1uPjA06GAOPk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b4zRc1/dJMb99L27eG/1np3fW9fWb1uPjA06GAOPk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b4zRc1/dJMb99L27eG/1np3fW9fWb1uPjA06GAOPk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb4zRc1%2FdJMb99L27eG%2F1np3fW9fWb1uPjA06GAOPk%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;1359&quot; height=&quot;674&quot; data-origin-width=&quot;1359&quot; data-origin-height=&quot;674&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1362&quot; data-origin-height=&quot;678&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/wltWm/dJMcacB161T/iNmHxH2VtAIwPM05nBRXeK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/wltWm/dJMcacB161T/iNmHxH2VtAIwPM05nBRXeK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/wltWm/dJMcacB161T/iNmHxH2VtAIwPM05nBRXeK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FwltWm%2FdJMcacB161T%2FiNmHxH2VtAIwPM05nBRXeK%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;1362&quot; height=&quot;678&quot; data-origin-width=&quot;1362&quot; data-origin-height=&quot;678&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;h2 data-ke-size=&quot;size26&quot;&gt;3) 다운로드&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1.&amp;nbsp;Google&amp;nbsp;PlayStore&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;450&quot; data-origin-height=&quot;450&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/btXPL7/dJMcacPzzQ8/vwGOt6uPL7yDWOuykaxRVk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/btXPL7/dJMcacPzzQ8/vwGOt6uPL7yDWOuykaxRVk/img.png&quot; data-alt=&quot;https://play.google.com/store/apps/details?id=com.tha.choncalcquiz&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/btXPL7/dJMcacPzzQ8/vwGOt6uPL7yDWOuykaxRVk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbtXPL7%2FdJMcacPzzQ8%2FvwGOt6uPL7yDWOuykaxRVk%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;450&quot; height=&quot;450&quot; data-origin-width=&quot;450&quot; data-origin-height=&quot;450&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://play.google.com/store/apps/details?id=com.tha.choncalcquiz&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;figure id=&quot;og_1770796781856&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;촌수 계산기 Plus+ - Google Play 앱&quot; data-og-description=&quot;헷갈리는 가족 관계를 단계별 선택&amp;middot;검색&amp;middot;퀴즈로 바로 이해하고, 촌수&amp;middot;항렬&amp;middot;호칭을 정확하게 알려주는 가족 관계 학습 앱입니다.&quot; data-og-host=&quot;play.google.com&quot; data-og-source-url=&quot;https://play.google.com/store/apps/details?id=com.tha.choncalcquiz&quot; data-og-url=&quot;https://play.google.com/store/apps/details?id=com.tha.choncalcquiz&amp;amp;hl=ko&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/laTtw/dJMb8864QGp/AOJnASBiRXkpb1tnkOPgxK/img.png?width=512&amp;amp;height=512&amp;amp;face=0_0_512_512,https://scrap.kakaocdn.net/dn/o48rk/dJMb82eIZSr/CvheyVyRx0DDDyVhbyorD1/img.png?width=600&amp;amp;height=300&amp;amp;face=0_0_600_300,https://scrap.kakaocdn.net/dn/eqjslQ/dJMb82My01E/b1gHZSWeXirFUk5s1Vk4p1/img.png?width=240&amp;amp;height=240&amp;amp;face=0_0_240_240&quot;&gt;&lt;a href=&quot;https://play.google.com/store/apps/details?id=com.tha.choncalcquiz&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://play.google.com/store/apps/details?id=com.tha.choncalcquiz&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/laTtw/dJMb8864QGp/AOJnASBiRXkpb1tnkOPgxK/img.png?width=512&amp;amp;height=512&amp;amp;face=0_0_512_512,https://scrap.kakaocdn.net/dn/o48rk/dJMb82eIZSr/CvheyVyRx0DDDyVhbyorD1/img.png?width=600&amp;amp;height=300&amp;amp;face=0_0_600_300,https://scrap.kakaocdn.net/dn/eqjslQ/dJMb82My01E/b1gHZSWeXirFUk5s1Vk4p1/img.png?width=240&amp;amp;height=240&amp;amp;face=0_0_240_240');&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;촌수 계산기 Plus+ - Google Play 앱&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;헷갈리는 가족 관계를 단계별 선택&amp;middot;검색&amp;middot;퀴즈로 바로 이해하고, 촌수&amp;middot;항렬&amp;middot;호칭을 정확하게 알려주는 가족 관계 학습 앱입니다.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;play.google.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;h3 data-ke-size=&quot;size23&quot;&gt;2.&amp;nbsp;AppStore&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;450&quot; data-origin-height=&quot;450&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lE4a4/dJMcadVb2B6/O6ehILox8b1DjEqm4oW8Mk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lE4a4/dJMcadVb2B6/O6ehILox8b1DjEqm4oW8Mk/img.png&quot; data-alt=&quot;https://apps.apple.com/kr/app/id6755925570&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lE4a4/dJMcadVb2B6/O6ehILox8b1DjEqm4oW8Mk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlE4a4%2FdJMcadVb2B6%2FO6ehILox8b1DjEqm4oW8Mk%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;450&quot; height=&quot;450&quot; data-origin-width=&quot;450&quot; data-origin-height=&quot;450&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://apps.apple.com/kr/app/id6755925570&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;figure id=&quot;og_1770796820539&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;촌수 계산기 Plus+ 앱 - App Store&quot; data-og-description=&quot;App&amp;nbsp;Store에서 EcodeLab의 촌수 계산기 Plus+ 앱을 다운로드하십시오. 스크린샷, 평가 및 리뷰, 사용자 팁, 촌수 계산기 Plus+ 앱과 비슷한 다른 게임들도 만나볼 수 있습니다.&quot; data-og-host=&quot;apps.apple.com&quot; data-og-source-url=&quot;https://apps.apple.com/kr/app/id6755925570&quot; data-og-url=&quot;https://apps.apple.com/kr/app/%EC%B4%8C%EC%88%98-%EA%B3%84%EC%82%B0%EA%B8%B0-plus/id6755925570&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/H39kS/dJMb89x9voG/qBWGfpN6jbIMjMP5HHxGqk/img.jpg?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/cdJ54R/dJMb8Z3ndKz/yk148k4forrkOBpzKxbfQk/img.jpg?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630&quot;&gt;&lt;a href=&quot;https://apps.apple.com/kr/app/id6755925570&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://apps.apple.com/kr/app/id6755925570&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/H39kS/dJMb89x9voG/qBWGfpN6jbIMjMP5HHxGqk/img.jpg?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/cdJ54R/dJMb8Z3ndKz/yk148k4forrkOBpzKxbfQk/img.jpg?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;촌수 계산기 Plus+ 앱 - App Store&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;App&amp;nbsp;Store에서 EcodeLab의 촌수 계산기 Plus+ 앱을 다운로드하십시오. 스크린샷, 평가 및 리뷰, 사용자 팁, 촌수 계산기 Plus+ 앱과 비슷한 다른 게임들도 만나볼 수 있습니다.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;apps.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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;⬇️ 개발자의 다양한 앱을 보고 싶으시면 아래의 링크를 확인해주세요&lt;span&gt;&amp;nbsp;&lt;/span&gt;⬇️&lt;/b&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1779095449768&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;Ecodelab - 제작 앱 소개&quot; data-og-description=&quot;Ecodelab에서 개발한 다양한 앱들을 소개합니다&quot; data-og-host=&quot;www.ecodelab.im&quot; data-og-source-url=&quot;https://ecodelab.im/main&quot; data-og-url=&quot;https://www.ecodelab.im&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bWuJMc/dJMb9eTUZsH/njzMvre05hFxKg2BACFmIk/img.png?width=1024&amp;amp;height=1024&amp;amp;face=0_0_1024_1024,https://scrap.kakaocdn.net/dn/eR1YMg/dJMb8RRXk8e/RlDSZRgjj03yt5Wtc1PKLK/img.png?width=1024&amp;amp;height=1024&amp;amp;face=0_0_1024_1024&quot;&gt;&lt;a href=&quot;https://ecodelab.im/main&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://ecodelab.im/main&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bWuJMc/dJMb9eTUZsH/njzMvre05hFxKg2BACFmIk/img.png?width=1024&amp;amp;height=1024&amp;amp;face=0_0_1024_1024,https://scrap.kakaocdn.net/dn/eR1YMg/dJMb8RRXk8e/RlDSZRgjj03yt5Wtc1PKLK/img.png?width=1024&amp;amp;height=1024&amp;amp;face=0_0_1024_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;Ecodelab - 제작 앱 소개&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Ecodelab에서 개발한 다양한 앱들을 소개합니다&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.ecodelab.im&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;&lt;b&gt;오늘도&amp;nbsp;감사합니다.&amp;nbsp; &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;</description>
      <category>Contributor9/제작 앱 소개</category>
      <category>가족 관계</category>
      <category>가족 관계 확인</category>
      <category>가족 호칭 앱</category>
      <category>촌수 계산</category>
      <category>촌수 계산기</category>
      <category>촌수 퀴즈</category>
      <category>친척 호칭 앱</category>
      <category>호칭 계산</category>
      <category>호칭 계산기</category>
      <category>호칭 앱</category>
      <author>adjh54</author>
      <guid isPermaLink="true">https://adjh54.tistory.com/739</guid>
      <comments>https://adjh54.tistory.com/739#entry739comment</comments>
      <pubDate>Wed, 11 Feb 2026 20:00:13 +0900</pubDate>
    </item>
    <item>
      <title>[Java] GA4(Google Analytics) 이해하고 활용하기 -2: 수집된 데이터 조회 환경 설정 및 활용 방법</title>
      <link>https://adjh54.tistory.com/738</link>
      <description>&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;해당 글에서는 GA에서 수집된 데이터를 API를 통해서 가져와서 화면상에 출력하는 과정을 포함한 글입니다.&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;
&lt;figure contenteditable=&quot;false&quot; data-ke-type=&quot;emoticon&quot; data-ke-align=&quot;alignCenter&quot; data-emoticon-type=&quot;face&quot; data-emoticon-name=&quot;073&quot; data-emoticon-isanimation=&quot;false&quot; data-emoticon-src=&quot;https://t1.daumcdn.net/keditor/emoticon/face/large/073.png&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/keditor/emoticon/face/large/073.png&quot; width=&quot;80&quot; /&gt;&lt;/figure&gt;
&lt;/blockquote&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  [참고]&lt;/b&gt; React 환경에서 수집한 데이터에 대해서 Spring Boot 환경에서 이를 조회하는 방법에 대해 알아봅니다.&lt;/blockquote&gt;
&lt;figure id=&quot;og_1773980757218&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;[React] GA4(Google Analytics) 이해하고 활용하기-1 : 프로젝트 설정 및 데이터 수집 방법&quot; data-og-description=&quot;해당 글에서는 React 환경에서 GA4를 이해하고 설정하여 데이터를 수집하는 방법에 대해 알아봅니다. 1) Google Analytics(GA4)  Google Analytics(GA4)- Google 애널리틱스 4는 웹사이트 및 앱에서 트래픽과 참&quot; data-og-host=&quot;adjh54.tistory.com&quot; data-og-source-url=&quot;https://adjh54.tistory.com/737&quot; data-og-url=&quot;https://adjh54.tistory.com/737&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/kzFwx/dJMb83kr4qz/omL6KqxsGuBue387Xp8Ank/img.png?width=800&amp;amp;height=290&amp;amp;face=0_0_800_290,https://scrap.kakaocdn.net/dn/RK205/dJMb8867YmS/kbaLxu11U7hclaTC52yHX1/img.png?width=800&amp;amp;height=290&amp;amp;face=0_0_800_290,https://scrap.kakaocdn.net/dn/eKY5T/dJMb9frECrz/UCCBE1Leu14AeczXm3xzL1/img.png?width=1916&amp;amp;height=834&amp;amp;face=0_0_1916_834&quot;&gt;&lt;a href=&quot;https://adjh54.tistory.com/737&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://adjh54.tistory.com/737&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/kzFwx/dJMb83kr4qz/omL6KqxsGuBue387Xp8Ank/img.png?width=800&amp;amp;height=290&amp;amp;face=0_0_800_290,https://scrap.kakaocdn.net/dn/RK205/dJMb8867YmS/kbaLxu11U7hclaTC52yHX1/img.png?width=800&amp;amp;height=290&amp;amp;face=0_0_800_290,https://scrap.kakaocdn.net/dn/eKY5T/dJMb9frECrz/UCCBE1Leu14AeczXm3xzL1/img.png?width=1916&amp;amp;height=834&amp;amp;face=0_0_1916_834');&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;[React] GA4(Google Analytics) 이해하고 활용하기-1 : 프로젝트 설정 및 데이터 수집 방법&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;해당 글에서는 React 환경에서 GA4를 이해하고 설정하여 데이터를 수집하는 방법에 대해 알아봅니다. 1) Google Analytics(GA4)  Google Analytics(GA4)- Google 애널리틱스 4는 웹사이트 및 앱에서 트래픽과 참&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;adjh54.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;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1) Google Analytics(GA4)&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  Google Analytics(GA4)&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- Google 애널리틱스 4는 웹사이트 및 앱에서 트래픽과 참여도를 측정할 수 있는 분석 서비스입니다.&lt;/b&gt; &lt;br /&gt;- 웹사이트와 웹, 앱의 데이터를 수집하여 비즈니스 통찰력을 제공하는 구글의 차세대 분석 플랫폼입니다.&lt;/blockquote&gt;
&lt;figure id=&quot;og_1770362526577&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;Google 애널리틱스 4 소개 &amp;nbsp;|&amp;nbsp; Google Analytics &amp;nbsp;|&amp;nbsp; Google for Developers&quot; data-og-description=&quot;Google 애널리틱스용 MCP 서버를 사용해 보세요. GitHub에서 설치하고 공지사항에서 자세한 내용을 확인하세요. 의견 보내기 Google 애널리틱스 4 소개 컬렉션을 사용해 정리하기 내 환경설정을 기준&quot; data-og-host=&quot;developers.google.com&quot; data-og-source-url=&quot;https://developers.google.com/analytics/devguides/collection/ga4?hl=ko&quot; data-og-url=&quot;https://developers.google.com/analytics/devguides/collection/ga4?hl=ko&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/DGliR/dJMb8QehGJx/Bsqtp4QvGp29vIPdhJPsJ1/img.png?width=1200&amp;amp;height=675&amp;amp;face=0_0_1200_675&quot;&gt;&lt;a href=&quot;https://developers.google.com/analytics/devguides/collection/ga4?hl=ko&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://developers.google.com/analytics/devguides/collection/ga4?hl=ko&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/DGliR/dJMb8QehGJx/Bsqtp4QvGp29vIPdhJPsJ1/img.png?width=1200&amp;amp;height=675&amp;amp;face=0_0_1200_675');&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;Google 애널리틱스 4 소개 &amp;nbsp;|&amp;nbsp; Google Analytics &amp;nbsp;|&amp;nbsp; Google for Developers&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Google 애널리틱스용 MCP 서버를 사용해 보세요. GitHub에서 설치하고 공지사항에서 자세한 내용을 확인하세요. 의견 보내기 Google 애널리틱스 4 소개 컬렉션을 사용해 정리하기 내 환경설정을 기준&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;developers.google.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;h2 data-ke-size=&quot;size26&quot;&gt;2) 설정 과정&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  설정 과정&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 아래의 설정 과정을 통해서 GA(Google Analytics)에서 수집된 데이터를 API에서 조회하는 과정을 설정합니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 198px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style2&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;height: 18px; width: 25.1163%;&quot;&gt;&lt;b&gt;플랫폼&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 18px; width: 18.2558%;&quot;&gt;&lt;b&gt;분류&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 18px; width: 56.5117%;&quot;&gt;&lt;b&gt;설명&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 37px;&quot;&gt;
&lt;td style=&quot;height: 37px; width: 25.1163%;&quot;&gt;&lt;b&gt;Google Cloud Console&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 37px; width: 18.2558%;&quot;&gt;프로젝트 생성&lt;/td&gt;
&lt;td style=&quot;height: 37px; width: 56.5117%;&quot;&gt;Google Analytics(GA4)로 수집된 데이터를 불러오는 API 측을 만들기 위해서 프로젝트를 생성합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 37px;&quot;&gt;
&lt;td style=&quot;height: 37px; width: 25.1163%;&quot;&gt;&lt;b&gt;Google Cloud Console&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 37px; width: 18.2558%;&quot;&gt;사용자 계정 및 키 생성&lt;/td&gt;
&lt;td style=&quot;height: 37px; width: 56.5117%;&quot;&gt;Google Analytics(GA4)에 API를 호출하여서 데이터를 가져올 사용자 및 키를 생성합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 37px;&quot;&gt;
&lt;td style=&quot;height: 37px; width: 25.1163%;&quot;&gt;&lt;b&gt;GA(Google Analytics)&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 37px; width: 18.2558%;&quot;&gt;사용자 추가&lt;/td&gt;
&lt;td style=&quot;height: 37px; width: 56.5117%;&quot;&gt;위에 프로젝트에서 API에 접근하려는 사용자를 만들었고, GA(Google Analytics)내에 이를 추가하는 과정입니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 35px;&quot;&gt;
&lt;td style=&quot;height: 35px; width: 25.1163%;&quot;&gt;&lt;b&gt;GA4 Query Explorer&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 35px; width: 18.2558%;&quot;&gt;데이터 연동 테스트&lt;/td&gt;
&lt;td style=&quot;height: 35px; width: 56.5117%;&quot;&gt;GA로 API를 호출하여서 값 조회를 위한 테스트를 합니다.&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;h2 data-ke-size=&quot;size26&quot;&gt;3) Google Cloud Console: 프로젝트 생성&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  Google Cloud Console: 프로젝트 생성&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;- Google Analytics(GA4)로 수집된 데이터를 불러오는 API 측을 만들기 위해서 프로젝트를 생성합니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. Google Cloud에 접속하여 &amp;lsquo;콘솔&amp;rsquo; 버튼을 누릅니다.&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1899&quot; data-origin-height=&quot;828&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bk7VVy/dJMcahQPZEP/sx40DbX50BtLk83bx6Xux0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bk7VVy/dJMcahQPZEP/sx40DbX50BtLk83bx6Xux0/img.png&quot; data-alt=&quot;https://cloud.google.com/?hl=ko&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bk7VVy/dJMcahQPZEP/sx40DbX50BtLk83bx6Xux0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbk7VVy%2FdJMcahQPZEP%2Fsx40DbX50BtLk83bx6Xux0%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;1899&quot; height=&quot;828&quot; data-origin-width=&quot;1899&quot; data-origin-height=&quot;828&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://cloud.google.com/?hl=ko&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;figure id=&quot;og_1770362637524&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;클라우드 컴퓨팅 서비스 | Google Cloud&quot; data-og-description=&quot;데이터 관리, 하이브리드 및 멀티 클라우드, AI와 머신러닝 등 Google의 클라우드 컴퓨팅 서비스로 비즈니스 당면 과제를 해결하세요.&quot; data-og-host=&quot;cloud.google.com&quot; data-og-source-url=&quot;https://cloud.google.com/?hl=ko&quot; data-og-url=&quot;https://cloud.google.com/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cGdhYt/dJMb8QehHRV/L3PqhC7SRkesLMaYRFFmZk/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/x7o7f/dJMb8SpDrrC/wXgZshhsVt8AObi3fk0OM1/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630&quot;&gt;&lt;a href=&quot;https://cloud.google.com/?hl=ko&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://cloud.google.com/?hl=ko&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cGdhYt/dJMb8QehHRV/L3PqhC7SRkesLMaYRFFmZk/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/x7o7f/dJMb8SpDrrC/wXgZshhsVt8AObi3fk0OM1/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;클라우드 컴퓨팅 서비스 | Google Cloud&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;데이터 관리, 하이브리드 및 멀티 클라우드, AI와 머신러닝 등 Google의 클라우드 컴퓨팅 서비스로 비즈니스 당면 과제를 해결하세요.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;cloud.google.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;h3 data-ke-size=&quot;size23&quot;&gt;2. 프로젝트 버튼을 누릅니다&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1913&quot; data-origin-height=&quot;830&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rEKoe/dJMcaf6ync9/TqROWyc9CS7aKsKC6ir4fk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rEKoe/dJMcaf6ync9/TqROWyc9CS7aKsKC6ir4fk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rEKoe/dJMcaf6ync9/TqROWyc9CS7aKsKC6ir4fk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FrEKoe%2FdJMcaf6ync9%2FTqROWyc9CS7aKsKC6ir4fk%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;1913&quot; height=&quot;830&quot; data-origin-width=&quot;1913&quot; data-origin-height=&quot;830&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;h3 data-ke-size=&quot;size23&quot;&gt;3. &amp;lsquo;새 프로젝트&amp;rsquo; 버튼을 누릅니다.&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;830&quot; data-origin-height=&quot;557&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/I4r6c/dJMcafer4yY/kJYb1uFzLgFi22o7Qv4dZ1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/I4r6c/dJMcafer4yY/kJYb1uFzLgFi22o7Qv4dZ1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/I4r6c/dJMcafer4yY/kJYb1uFzLgFi22o7Qv4dZ1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FI4r6c%2FdJMcafer4yY%2FkJYb1uFzLgFi22o7Qv4dZ1%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;830&quot; height=&quot;557&quot; data-origin-width=&quot;830&quot; data-origin-height=&quot;557&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;h3 data-ke-size=&quot;size23&quot;&gt;4. 새 프로젝트를 만듭니다.&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1917&quot; data-origin-height=&quot;721&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qq2fS/dJMcaiCbsuc/eHtUxKaB2p9nXKTaJDCTeK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qq2fS/dJMcaiCbsuc/eHtUxKaB2p9nXKTaJDCTeK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qq2fS/dJMcaiCbsuc/eHtUxKaB2p9nXKTaJDCTeK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fqq2fS%2FdJMcaiCbsuc%2FeHtUxKaB2p9nXKTaJDCTeK%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;1917&quot; height=&quot;721&quot; data-origin-width=&quot;1917&quot; data-origin-height=&quot;721&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;h3 data-ke-size=&quot;size23&quot;&gt;5. API 및 서비스를 선택합니다&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1894&quot; data-origin-height=&quot;701&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bzlXLk/dJMcafrWxlN/ymObKyMtScUsCO3CJYt3Yk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bzlXLk/dJMcafrWxlN/ymObKyMtScUsCO3CJYt3Yk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bzlXLk/dJMcafrWxlN/ymObKyMtScUsCO3CJYt3Yk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbzlXLk%2FdJMcafrWxlN%2FymObKyMtScUsCO3CJYt3Yk%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;1894&quot; height=&quot;701&quot; data-origin-width=&quot;1894&quot; data-origin-height=&quot;701&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4) Google Cloud Console: 사용자 계정 및 키 생성&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  Google Cloud Console: 사용자 계정 및 키 생성&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- Google Analytics(GA4)에 API를 호출하여서 데이터를 가져올 사용자 및 키를 생성합니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. IAM 관리자 &amp;gt; 서비스 계정을 선택합니다&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1909&quot; data-origin-height=&quot;768&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/BJlgs/dJMcagqQZm2/MgWKCoKcwSQq1tfpIVJB70/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/BJlgs/dJMcagqQZm2/MgWKCoKcwSQq1tfpIVJB70/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/BJlgs/dJMcagqQZm2/MgWKCoKcwSQq1tfpIVJB70/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FBJlgs%2FdJMcagqQZm2%2FMgWKCoKcwSQq1tfpIVJB70%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;1909&quot; height=&quot;768&quot; data-origin-width=&quot;1909&quot; data-origin-height=&quot;768&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;h3 data-ke-size=&quot;size23&quot;&gt;2. &amp;lsquo;서비스 계정 만들기&amp;rsquo; 버튼을 누릅니다&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1608&quot; data-origin-height=&quot;735&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/y75M6/dJMcajujKPZ/6NfncgqxzIAvFU8v0IXhq1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/y75M6/dJMcajujKPZ/6NfncgqxzIAvFU8v0IXhq1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/y75M6/dJMcajujKPZ/6NfncgqxzIAvFU8v0IXhq1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fy75M6%2FdJMcajujKPZ%2F6NfncgqxzIAvFU8v0IXhq1%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;1608&quot; height=&quot;735&quot; data-origin-width=&quot;1608&quot; data-origin-height=&quot;735&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;h3 data-ke-size=&quot;size23&quot;&gt;3. 서비스 계정 이름을 입력하고, 모두 다음으로 계속합니다&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1914&quot; data-origin-height=&quot;717&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/TIxE7/dJMcafZLUGl/tska5beUo6LL42zlXkT6P0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/TIxE7/dJMcafZLUGl/tska5beUo6LL42zlXkT6P0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/TIxE7/dJMcafZLUGl/tska5beUo6LL42zlXkT6P0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FTIxE7%2FdJMcafZLUGl%2Ftska5beUo6LL42zlXkT6P0%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;1914&quot; height=&quot;717&quot; data-origin-width=&quot;1914&quot; data-origin-height=&quot;717&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;h3 data-ke-size=&quot;size23&quot;&gt;4. 아래와 같이 생성되었고, 이메일을 누릅니다&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1600&quot; data-origin-height=&quot;725&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/TTAX1/dJMcaihTY8v/AYeFP7i3g0aaFHWBFopnck/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/TTAX1/dJMcaihTY8v/AYeFP7i3g0aaFHWBFopnck/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/TTAX1/dJMcaihTY8v/AYeFP7i3g0aaFHWBFopnck/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FTTAX1%2FdJMcaihTY8v%2FAYeFP7i3g0aaFHWBFopnck%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;1600&quot; height=&quot;725&quot; data-origin-width=&quot;1600&quot; data-origin-height=&quot;725&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;h3 data-ke-size=&quot;size23&quot;&gt;5. 키 탭 &amp;gt; 키 추가 &amp;gt; 새 키 만들기 버튼을 누릅니다&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1874&quot; data-origin-height=&quot;710&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cGnooR/dJMcaihTY8H/MA2kLUx7hlUxMRc7HBT2yk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cGnooR/dJMcaihTY8H/MA2kLUx7hlUxMRc7HBT2yk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cGnooR/dJMcaihTY8H/MA2kLUx7hlUxMRc7HBT2yk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcGnooR%2FdJMcaihTY8H%2FMA2kLUx7hlUxMRc7HBT2yk%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;1874&quot; height=&quot;710&quot; data-origin-width=&quot;1874&quot; data-origin-height=&quot;710&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;h3 data-ke-size=&quot;size23&quot;&gt;6. &amp;lsquo;JSON&amp;rsquo; 키 유형을 선택하고 만들기를 누르면 JSON 파일이 다운로드하여집니다&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;583&quot; data-origin-height=&quot;356&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/teUQK/dJMcacvfC3I/SuBWeTr7eTKPgF4FmDs7hK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/teUQK/dJMcacvfC3I/SuBWeTr7eTKPgF4FmDs7hK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/teUQK/dJMcacvfC3I/SuBWeTr7eTKPgF4FmDs7hK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FteUQK%2FdJMcacvfC3I%2FSuBWeTr7eTKPgF4FmDs7hK%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;583&quot; height=&quot;356&quot; data-origin-width=&quot;583&quot; data-origin-height=&quot;356&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;h2 data-ke-size=&quot;size26&quot;&gt;5) GA(Google Analytics): 사용자 추가&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;&amp;nbsp;  Google Analytics: 사용자 추가&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 위에 프로젝트에서 API에 접근하려는 사용자를 만들었고, GA(Google Analytics) 내에 이를 추가하는 과정입니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 구글 애널리틱스 사이트를 접속하고, 수집하려는 프로젝트로 접근합니다&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;figure id=&quot;og_1770362831032&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;Google Analytics &amp;nbsp;|&amp;nbsp; Google for Developers&quot; data-og-description=&quot;Google 애널리틱스를 사용하면 디지털 전략을 세부적으로 조정하고, 캠페인을 최적화하며, 온라인 인지도를 한 단계 업그레이드할 수 있습니다.&quot; data-og-host=&quot;developers.google.com&quot; data-og-source-url=&quot;https://developers.google.com/analytics?hl=ko&quot; data-og-url=&quot;https://developers.google.com/analytics?hl=ko&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/z9mjf/dJMb9g46DzZ/jGNkr8kikKtNRAyYnKyu10/img.png?width=1200&amp;amp;height=675&amp;amp;face=0_0_1200_675,https://scrap.kakaocdn.net/dn/egvJxi/dJMb9iaMDmk/ddIeGrqivRNtL1bcj4mvP0/img.png?width=338&amp;amp;height=340&amp;amp;face=0_0_338_340&quot;&gt;&lt;a href=&quot;https://developers.google.com/analytics?hl=ko&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://developers.google.com/analytics?hl=ko&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/z9mjf/dJMb9g46DzZ/jGNkr8kikKtNRAyYnKyu10/img.png?width=1200&amp;amp;height=675&amp;amp;face=0_0_1200_675,https://scrap.kakaocdn.net/dn/egvJxi/dJMb9iaMDmk/ddIeGrqivRNtL1bcj4mvP0/img.png?width=338&amp;amp;height=340&amp;amp;face=0_0_338_340');&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;Google Analytics &amp;nbsp;|&amp;nbsp; Google for Developers&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Google 애널리틱스를 사용하면 디지털 전략을 세부적으로 조정하고, 캠페인을 최적화하며, 온라인 인지도를 한 단계 업그레이드할 수 있습니다.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;developers.google.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;h3 data-ke-size=&quot;size23&quot;&gt;2. 설정 &amp;gt; 계정설정 &amp;gt; 계정 &amp;gt; 계정 액세스 관리를 선택합니다&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1204&quot; data-origin-height=&quot;828&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/eu2TdB/dJMcachHl4F/Moy0kTH3MnaZoyPA20zvIK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/eu2TdB/dJMcachHl4F/Moy0kTH3MnaZoyPA20zvIK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/eu2TdB/dJMcachHl4F/Moy0kTH3MnaZoyPA20zvIK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Feu2TdB%2FdJMcachHl4F%2FMoy0kTH3MnaZoyPA20zvIK%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;1204&quot; height=&quot;828&quot; data-origin-width=&quot;1204&quot; data-origin-height=&quot;828&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;h3 data-ke-size=&quot;size23&quot;&gt;3. + 버튼 &amp;gt; 사용자 추가를 선택합니다&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1512&quot; data-origin-height=&quot;820&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cJIPwL/dJMcafyJ0TV/gbtKGqcCRqwnriQWMHEB91/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cJIPwL/dJMcafyJ0TV/gbtKGqcCRqwnriQWMHEB91/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cJIPwL/dJMcafyJ0TV/gbtKGqcCRqwnriQWMHEB91/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcJIPwL%2FdJMcafyJ0TV%2FgbtKGqcCRqwnriQWMHEB91%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;1512&quot; height=&quot;820&quot; data-origin-width=&quot;1512&quot; data-origin-height=&quot;820&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;h3 data-ke-size=&quot;size23&quot;&gt;4. Google Cloud에 추가한 사용자 이메일을 적고 &amp;lsquo;추가&amp;rsquo; 버튼을 누릅니다.&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  Google Cloud에 추가한 사용자 이메일을 적고 &amp;lsquo;추가&amp;rsquo;버튼을 누릅니다.&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 추가로 역할을 &amp;lsquo;뷰어&amp;rsquo;로 설정해서 데이터만 조회해 오는 역할을 수행하도록 합니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1520&quot; data-origin-height=&quot;828&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bZItCW/dJMcagxBGOK/vhyIKuezCbNkTo4kjPWg4k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bZItCW/dJMcagxBGOK/vhyIKuezCbNkTo4kjPWg4k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bZItCW/dJMcagxBGOK/vhyIKuezCbNkTo4kjPWg4k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbZItCW%2FdJMcagxBGOK%2FvhyIKuezCbNkTo4kjPWg4k%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;1520&quot; height=&quot;828&quot; data-origin-width=&quot;1520&quot; data-origin-height=&quot;828&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;6) GA4 Query Explorer: 데이터 연동 테스트&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  GA4 Query Explorer: 데이터 연동 테스트&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- GA로 API를 호출하여서 값 조회를 위한 테스트를 합니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 사이트에 접속하여 사람 아이콘을 눌러서 로그인을 수행합니다.&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1462&quot; data-origin-height=&quot;834&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/SHDvd/dJMcabC57lM/WmuAixpDNCCBrOckANE9I0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/SHDvd/dJMcabC57lM/WmuAixpDNCCBrOckANE9I0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/SHDvd/dJMcabC57lM/WmuAixpDNCCBrOckANE9I0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FSHDvd%2FdJMcabC57lM%2FWmuAixpDNCCBrOckANE9I0%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;1462&quot; height=&quot;834&quot; data-origin-width=&quot;1462&quot; data-origin-height=&quot;834&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;figure id=&quot;og_1770362921429&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;https://ga-dev-tools.google/ga4/&quot; data-og-description=&quot;About This Site Google Analytics Demos &amp;amp; Tools is a resource for users and developers to discover what's possible with the Google Analytics Platform. Learn how to implement GA and applications that can be built to take advantage of the flexibility and powe&quot; data-og-host=&quot;ga-dev-tools.google&quot; data-og-source-url=&quot;https://ga-dev-tools.google/ga4/&quot; data-og-url=&quot;https://ga-dev-tools.google/ga4/&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://ga-dev-tools.google/ga4/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://ga-dev-tools.google/ga4/&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;https://ga-dev-tools.google/ga4/&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;About This Site Google Analytics Demos &amp;amp; Tools is a resource for users and developers to discover what's possible with the Google Analytics Platform. Learn how to implement GA and applications that can be built to take advantage of the flexibility and powe&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;ga-dev-tools.google&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;h3 data-ke-size=&quot;size23&quot;&gt;2. Query Explorer 탭을 선택합니다&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1323&quot; data-origin-height=&quot;809&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Fe5KP/dJMcaaqEd2r/yU7Da7oDE5n7EKK7pgJquK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Fe5KP/dJMcaaqEd2r/yU7Da7oDE5n7EKK7pgJquK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Fe5KP/dJMcaaqEd2r/yU7Da7oDE5n7EKK7pgJquK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FFe5KP%2FdJMcaaqEd2r%2FyU7Da7oDE5n7EKK7pgJquK%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;1323&quot; height=&quot;809&quot; data-origin-width=&quot;1323&quot; data-origin-height=&quot;809&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;h3 data-ke-size=&quot;size23&quot;&gt;3. 구글 애널리틱스에서 수집되는 속성 및 Set Parameters를 선택합니다&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1896&quot; data-origin-height=&quot;830&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Nt8eQ/dJMcabiN1zI/qbDhpksBJqqG6ItMlt8RwK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Nt8eQ/dJMcabiN1zI/qbDhpksBJqqG6ItMlt8RwK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Nt8eQ/dJMcabiN1zI/qbDhpksBJqqG6ItMlt8RwK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FNt8eQ%2FdJMcabiN1zI%2FqbDhpksBJqqG6ItMlt8RwK%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;1896&quot; height=&quot;830&quot; data-origin-width=&quot;1896&quot; data-origin-height=&quot;830&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;h3 data-ke-size=&quot;size23&quot;&gt;4. MAKE REQUEST를 선택합니다&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;857&quot; data-origin-height=&quot;532&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b1Jz50/dJMcachHl8U/H7c9jCkpHDXvWkYrgqpfr1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b1Jz50/dJMcachHl8U/H7c9jCkpHDXvWkYrgqpfr1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b1Jz50/dJMcachHl8U/H7c9jCkpHDXvWkYrgqpfr1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb1Jz50%2FdJMcachHl8U%2FH7c9jCkpHDXvWkYrgqpfr1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;857&quot; height=&quot;532&quot; data-origin-width=&quot;857&quot; data-origin-height=&quot;532&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;h3 data-ke-size=&quot;size23&quot;&gt;5. 아래와 같이 결과를 확인하였습니다.&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;819&quot; data-origin-height=&quot;737&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kUEok/dJMcai3dY2W/awAf0xvKd0g31iN9rPBnw0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kUEok/dJMcai3dY2W/awAf0xvKd0g31iN9rPBnw0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kUEok/dJMcai3dY2W/awAf0xvKd0g31iN9rPBnw0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkUEok%2FdJMcai3dY2W%2FawAf0xvKd0g31iN9rPBnw0%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;819&quot; height=&quot;737&quot; data-origin-width=&quot;819&quot; data-origin-height=&quot;737&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;7) Spring Boot 환경 설정&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 의존성 추가&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  의존성 추가&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 의존성으로 Google Analytics Data 라이브러리를 이용하여서 클라이언트를 생성하고 통신을 하기 위함으로 이용합니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;pre class=&quot;clean&quot;&gt;&lt;code&gt;dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-webmvc'

		// GA4 Data API 추가
    implementation 'com.google.analytics:google-analytics-data:0.94.0'

    compileOnly 'org.projectlombok:lombok'
    annotationProcessor 'org.projectlombok:lombok'
    testImplementation 'org.springframework.boot:spring-boot-starter-webmvc-test'
    testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. 위에서 발급받은 JSON 키를 기반으로 resource 내에 ga-key.json 파일로 추가하였습니다&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;463&quot; data-origin-height=&quot;594&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bmRs5n/dJMcaf6ynnL/F1llpGELN5nIlx7TTXxLY0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bmRs5n/dJMcaf6ynnL/F1llpGELN5nIlx7TTXxLY0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bmRs5n/dJMcaf6ynnL/F1llpGELN5nIlx7TTXxLY0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbmRs5n%2FdJMcaf6ynnL%2FF1llpGELN5nIlx7TTXxLY0%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;463&quot; height=&quot;594&quot; data-origin-width=&quot;463&quot; data-origin-height=&quot;594&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. application.yml&lt;/h2&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;style3&quot;&gt;&lt;b&gt;  application.properties&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 아래와 같이 ga.property-id, ga.credentials를 추가하였습니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;spring:
  application:
    name: template-spring-boot4

ga:
  property-id: XXXXXXXX
  credentials: classpath:ga-key.json
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  [참고]&lt;/b&gt; ga.propertie-id로 명시한 코드는 구글 애널리틱스 내에서 아래의 속성을 확인할 수 있습니다.&lt;br /&gt;&lt;br /&gt;- 아래와 같이 계정을 선택 하면 그 아래에 각각 프로젝트가 나오는데 프로젝트의 값을 넣으면 됩니다.&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1035&quot; data-origin-height=&quot;456&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/xXLKq/dJMcabp0VCi/aU7aGNgmdaScQynXJmwoc1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/xXLKq/dJMcabp0VCi/aU7aGNgmdaScQynXJmwoc1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/xXLKq/dJMcabp0VCi/aU7aGNgmdaScQynXJmwoc1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FxXLKq%2FdJMcabp0VCi%2FaU7aGNgmdaScQynXJmwoc1%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;1035&quot; height=&quot;456&quot; data-origin-width=&quot;1035&quot; data-origin-height=&quot;456&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;755&quot; data-origin-height=&quot;522&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bDEI7N/dJMcaia9Rvb/qekGtEY38kaKDsoKJGKASk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bDEI7N/dJMcaia9Rvb/qekGtEY38kaKDsoKJGKASk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bDEI7N/dJMcaia9Rvb/qekGtEY38kaKDsoKJGKASk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbDEI7N%2FdJMcaia9Rvb%2FqekGtEY38kaKDsoKJGKASk%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;755&quot; height=&quot;522&quot; data-origin-width=&quot;755&quot; data-origin-height=&quot;522&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;h3 data-ke-size=&quot;size23&quot;&gt;4. GaConfig&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  GaConfig&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- Google Analytics 분석 데이터에 접근할 수 있도록 인증과정 및 클라이언트 구성하는 설정 파일이며 클라이언트를 생성합니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;package com.adjh.templatespringboot4.config;

import com.google.analytics.data.v1beta.BetaAnalyticsDataClient;
import com.google.analytics.data.v1beta.BetaAnalyticsDataSettings;
import com.google.api.gax.core.FixedCredentialsProvider;
import com.google.auth.oauth2.GoogleCredentials;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;

import java.io.IOException;

/**
 * Google Analytics 분석 데이터에 접근할 수 있도록 인증과정 및 클라이언트 구성
 *
 * @author : leejonghoon
 * @fileName : GaConfig
 * @since : 26. 2. 2.
 */
@Configuration
public class GaConfig {

    /**
     *
     * @return
     * @throws IOException
     */
    @Bean
    public BetaAnalyticsDataClient gaClient() throws IOException {

        // 1. ga-key.json 인증키를 로드합니다.
        GoogleCredentials credentials = GoogleCredentials.fromStream(new ClassPathResource(&quot;ga-key.json&quot;).getInputStream());

        // 2. 인증키를 기반으로 GA Client를 생성합니다,
        return BetaAnalyticsDataClient.create(
                BetaAnalyticsDataSettings.newBuilder()
                        .setCredentialsProvider(FixedCredentialsProvider.create(credentials))
                        .build()
        );
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  [참고]&lt;/b&gt; API Document&lt;/blockquote&gt;
&lt;figure id=&quot;og_1770363135026&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;Class BetaAnalyticsDataClient (0.94.0) &amp;nbsp;|&amp;nbsp; Java client libraries &amp;nbsp;|&amp;nbsp; Google Cloud Documentation&quot; data-og-description=&quot;Send feedback Class BetaAnalyticsDataClient (0.94.0) Stay organized with collections Save and categorize content based on your preferences. Version latestkeyboard_arrow_down Beta This library is covered by the Pre-GA Offerings Terms of the Terms of Service&quot; data-og-host=&quot;docs.cloud.google.com&quot; data-og-source-url=&quot;https://docs.cloud.google.com/java/docs/reference/google-analytics-data/latest/com.google.analytics.data.v1beta.BetaAnalyticsDataClient&quot; data-og-url=&quot;https://docs.cloud.google.com/java/docs/reference/google-analytics-data/latest/com.google.analytics.data.v1beta.BetaAnalyticsDataClient&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/br291X/dJMb9kl8lnF/lhu4Y50dJSwUyKRQvmitC0/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630&quot;&gt;&lt;a href=&quot;https://docs.cloud.google.com/java/docs/reference/google-analytics-data/latest/com.google.analytics.data.v1beta.BetaAnalyticsDataClient&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://docs.cloud.google.com/java/docs/reference/google-analytics-data/latest/com.google.analytics.data.v1beta.BetaAnalyticsDataClient&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/br291X/dJMb9kl8lnF/lhu4Y50dJSwUyKRQvmitC0/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Class BetaAnalyticsDataClient (0.94.0) &amp;nbsp;|&amp;nbsp; Java client libraries &amp;nbsp;|&amp;nbsp; Google Cloud Documentation&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Send feedback Class BetaAnalyticsDataClient (0.94.0) Stay organized with collections Save and categorize content based on your preferences. Version latestkeyboard_arrow_down Beta This library is covered by the Pre-GA Offerings Terms of the Terms of Service&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;docs.cloud.google.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;h3 data-ke-size=&quot;size23&quot;&gt;5. DTO 구성&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  DTO 구성&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 해당 예시에서는 일일 활성화 사용자 조회(activeUser), 유입 및 체류시간 정보를 조회하는 정보를 가져옵니다.&lt;/b&gt;&lt;br /&gt;- 그렇기에 응답 값으로 지정하여서 이로 반환받습니다.&lt;/blockquote&gt;
&lt;pre class=&quot;groovy&quot;&gt;&lt;code&gt;package com.adjh.templatespringboot4.dto;

import lombok.Builder;
import lombok.Getter;

/**
 * 일일 활성화 사용자 + 유입 및 체류시간에 대한 응답값
 *
 * @author : leejonghoon
 * @fileName : GaResponseDto
 * @since : 26. 2. 2.
 */
@Getter
@Builder
public class GaResponseDto {

    private String date;
    private long activeUsers;

    private long totalUsers;
    private long sessions;
    private long pageViews;
    private double avgSessionDuration;
    private double engagementRate;
}

&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;h3 data-ke-size=&quot;size23&quot;&gt;6. GaService&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  GaService&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 클라이언트를 통해서 직접적으로 GA와 통신을 수행하여서 데이터를 조회합니다.&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;1. DateRange 객체를 통해 조회 기간을 설정합니다. (시작일, 종료일 구성)&lt;br /&gt;2. 실행 요청 정보 Budiler로 구성합니다: 조회 범위(Range), 측정 기준(Metric), 데이터 분류 기준(Dimension)&lt;br /&gt;3. 요청 정보를 기반으로 클라이언트로 실행&lt;br /&gt;4. DTO 변환 및 결과값 반환 (Builder 사용)&lt;/blockquote&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;package com.adjh.templatespringboot4.service;

import com.adjh.templatespringboot4.dto.GaResponseDto;
import com.google.analytics.data.v1beta.*;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.util.List;

/**
 * GA Client를 기반으로 데이터를 조회해오는 서비스
 *
 * @author : leejonghoon
 * @fileName : GaServiceComponent
 * @since : 26. 2. 2.
 */
@Service
public class GaService {

    @Value(&quot;${ga.property-id}&quot;)
    private String propertyId;

    private final BetaAnalyticsDataClient client;

    public GaService(BetaAnalyticsDataClient client) {
        this.client = client;
    }

    /**
     * 구성한 클라이언트를 기반으로 유입 및 체류시간 조회 Service
     *
     * @return List&amp;lt;GaResponseDto&amp;gt;
     */
    public List&amp;lt;GaResponseDto&amp;gt; trafficAndEngagement() {

        // 1. 조회 기간을 설정합니다. (시작일, 종료일 구성)
        DateRange dateRange = DateRange.newBuilder()
                .setStartDate(&quot;7daysAgo&quot;)
                .setEndDate(&quot;today&quot;)
                .build();

        // 2. 실행 요청 정보를 구성합니다: 조회 범위(Range), 측정 기준(Metric), 데이터 분류 기준(Dimension)
        RunReportRequest request = RunReportRequest.newBuilder()
                .setProperty(&quot;properties/&quot; + propertyId)
                // 2.1. 조회 범위(Range) 지정
                .addDateRanges(dateRange)

                // 2.2. 측정 기준(Metric) 지정
                .addDimensions(Dimension.newBuilder().setName(&quot;date&quot;))

                // 2.3. 데이터 분류 기준 지정(Dimension)
                // 1. 유입 및 사용자 관련 지표 (Acquisition &amp;amp; Users)
                .addMetrics(Metric.newBuilder().setName(&quot;activeUsers&quot;))             // 활성 사용자 수
                .addMetrics(Metric.newBuilder().setName(&quot;totalUsers&quot;))              // 전체 사용자 수
                .addMetrics(Metric.newBuilder().setName(&quot;sessions&quot;))                // 세션 수
                .addMetrics(Metric.newBuilder().setName(&quot;screenPageViews&quot;))         // 페이지 조회수

                // 2. 참여 및 체류 관련 지표 (Engagement)
                .addMetrics(Metric.newBuilder().setName(&quot;averageSessionDuration&quot;))  // 평균 세션 시간
                .addMetrics(Metric.newBuilder().setName(&quot;engagementRate&quot;))          // 참여율
                .addMetrics(Metric.newBuilder().setName(&quot;bounceRate&quot;))              // 이탈률
                .addMetrics(Metric.newBuilder().setName(&quot;eventCount&quot;))              // 이벤트 수
                .build();

        // 3. 요청 정보를 기반으로 클라이언트로 실행
        RunReportResponse response = client.runReport(request);

        // 4. DTO 변환 및 결과값 반환 (Builder 사용)
        return response.getRowsList().stream()
                .map(row -&amp;gt; GaResponseDto.builder()
                        .date(row.getDimensionValues(0).getValue())
                        .activeUsers(Long.parseLong(row.getMetricValues(0).getValue()))
                        .totalUsers(Long.parseLong(row.getMetricValues(1).getValue()))
                        .sessions(Long.parseLong(row.getMetricValues(2).getValue()))
                        .pageViews(Long.parseLong(row.getMetricValues(3).getValue()))
                        .avgSessionDuration(Double.parseDouble(row.getMetricValues(4).getValue()))
                        .engagementRate(Double.parseDouble(row.getMetricValues(5).getValue()))
                        .bounceRate(Double.parseDouble(row.getMetricValues(6).getValue()))
                        .eventCount(Long.parseLong(row.getMetricValues(7).getValue()))
                        .build())
                .toList();
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  [참고]&lt;/b&gt; 반환 값을 하단의 속성을 확인할 수 있습니다.&lt;/blockquote&gt;
&lt;figure id=&quot;og_1770363216211&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;API 측정기준 및 측정항목 &amp;nbsp;|&amp;nbsp; Google Analytics &amp;nbsp;|&amp;nbsp; Google for Developers&quot; data-og-description=&quot;의견 보내기 API 측정기준 및 측정항목 컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠를 저장하고 분류하세요. Data API Core Reporting 쿼리에서 사용할 수 있는 측정기준과 측정항목입니&quot; data-og-host=&quot;developers.google.com&quot; data-og-source-url=&quot;https://developers.google.com/analytics/devguides/reporting/data/v1/api-schema&quot; data-og-url=&quot;https://developers.google.com/analytics/devguides/reporting/data/v1/api-schema?hl=ko&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/Cd88E/dJMb84p4fPj/lbCCU6KQmKhwG7u4xuLb7K/img.png?width=1200&amp;amp;height=675&amp;amp;face=0_0_1200_675&quot;&gt;&lt;a href=&quot;https://developers.google.com/analytics/devguides/reporting/data/v1/api-schema&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://developers.google.com/analytics/devguides/reporting/data/v1/api-schema&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/Cd88E/dJMb84p4fPj/lbCCU6KQmKhwG7u4xuLb7K/img.png?width=1200&amp;amp;height=675&amp;amp;face=0_0_1200_675');&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;API 측정기준 및 측정항목 &amp;nbsp;|&amp;nbsp; Google Analytics &amp;nbsp;|&amp;nbsp; Google for Developers&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;의견 보내기 API 측정기준 및 측정항목 컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠를 저장하고 분류하세요. Data API Core Reporting 쿼리에서 사용할 수 있는 측정기준과 측정항목입니&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;developers.google.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;h3 data-ke-size=&quot;size23&quot;&gt;7. GaController&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  GaController&lt;/b&gt;&lt;br /&gt;-&lt;br /&gt;&lt;b&gt;GA 서비스를 호출하여 API 엔드포인트로 제공을 합니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;package com.adjh.templatespringboot4.controller;

import com.adjh.templatespringboot4.dto.GaResponseDto;
import com.adjh.templatespringboot4.service.GaService;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

/**
 * GA 데이터 API 호출
 *
 * @author : leejonghoon
 * @fileName : GaController
 * @since : 26. 2. 2.
 */
@RestController
@RequestMapping(&quot;/api/ga&quot;)
public class GaController {

    private final GaService gaService;

    public GaController(GaService gaService) {
        this.gaService = gaService;
    }

    @GetMapping(&quot;/total-users&quot;)
    public List&amp;lt;GaResponseDto&amp;gt; getTrafficUser() {
        return gaService.trafficAndEngagement();
    }
}&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;h2 data-ke-size=&quot;size26&quot;&gt;8) 결과 확인&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  결과 확인&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- IntelliJ HTTP Client를 기반으로 호출하여 해당 값들을 조회하였습니다.&lt;/b&gt;&lt;br /&gt;- 아래와 같이 결과를 확인하였습니다.&lt;/blockquote&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;1827&quot; data-origin-height=&quot;917&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cjlY0k/dJMcahcdDY2/URiG2XVaXWkMKfUeNgH9I1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cjlY0k/dJMcahcdDY2/URiG2XVaXWkMKfUeNgH9I1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cjlY0k/dJMcahcdDY2/URiG2XVaXWkMKfUeNgH9I1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcjlY0k%2FdJMcahcdDY2%2FURiG2XVaXWkMKfUeNgH9I1%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;1827&quot; height=&quot;917&quot; data-origin-width=&quot;1827&quot; data-origin-height=&quot;917&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;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  [참고]&lt;/b&gt; IntelliJ HTTP Client&lt;/blockquote&gt;
&lt;figure id=&quot;og_1770363284746&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;[IntelliJ] HTTP Client 사용하기 : Postman 대체하기&quot; data-og-description=&quot;해당 글에서는 Postman을 대체하여 클라이언트에서 서버로 API를 전송(Request)하고 반환(Response)을 받는 테스트에 사용이 되는 HTTP Client에 대해서 공유합니다. 1) 문제사항 및 적용 계기   Client에서 &quot; data-og-host=&quot;adjh54.tistory.com&quot; data-og-source-url=&quot;https://adjh54.tistory.com/88&quot; data-og-url=&quot;https://adjh54.tistory.com/88&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cgmPND/dJMb9kTYtUV/KTfkKdke00r5Ep1EZaQfI0/img.png?width=800&amp;amp;height=720&amp;amp;face=0_0_800_720,https://scrap.kakaocdn.net/dn/yZawP/dJMb9bvXPjd/eDVLTj30IxpsH0ruv46W3k/img.png?width=800&amp;amp;height=720&amp;amp;face=0_0_800_720,https://scrap.kakaocdn.net/dn/clb3A3/dJMb8Weu6TT/vGIkjFQeymi3rPq3tXhMY0/img.png?width=2384&amp;amp;height=1208&amp;amp;face=0_0_2384_1208&quot;&gt;&lt;a href=&quot;https://adjh54.tistory.com/88&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://adjh54.tistory.com/88&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cgmPND/dJMb9kTYtUV/KTfkKdke00r5Ep1EZaQfI0/img.png?width=800&amp;amp;height=720&amp;amp;face=0_0_800_720,https://scrap.kakaocdn.net/dn/yZawP/dJMb9bvXPjd/eDVLTj30IxpsH0ruv46W3k/img.png?width=800&amp;amp;height=720&amp;amp;face=0_0_800_720,https://scrap.kakaocdn.net/dn/clb3A3/dJMb8Weu6TT/vGIkjFQeymi3rPq3tXhMY0/img.png?width=2384&amp;amp;height=1208&amp;amp;face=0_0_2384_1208');&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;[IntelliJ] HTTP Client 사용하기 : Postman 대체하기&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;해당 글에서는 Postman을 대체하여 클라이언트에서 서버로 API를 전송(Request)하고 반환(Response)을 받는 테스트에 사용이 되는 HTTP Client에 대해서 공유합니다. 1) 문제사항 및 적용 계기   Client에서&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;adjh54.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;&lt;b&gt;오늘도&amp;nbsp;감사합니다.&amp;nbsp; &lt;/b&gt;&lt;br /&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;&amp;nbsp;&lt;/p&gt;</description>
      <category>Java/라이브러리 활용</category>
      <category>GA 데이터 조회</category>
      <category>GA4(Google Analytics) 데이터 조회</category>
      <category>Google Analytics API</category>
      <category>Google Analytics API 환경설정</category>
      <category>Google Analytics 데이터 불러오는 방법</category>
      <category>Google Analytics 데이터 조회 환경 설정</category>
      <category>Google Analytics 데이터 환경설정</category>
      <category>Google Analytics 불러오기</category>
      <category>Google Analytics 연동</category>
      <category>Google Analytics 조회 방법</category>
      <author>adjh54</author>
      <guid isPermaLink="true">https://adjh54.tistory.com/738</guid>
      <comments>https://adjh54.tistory.com/738#entry738comment</comments>
      <pubDate>Fri, 6 Feb 2026 20:10:53 +0900</pubDate>
    </item>
    <item>
      <title>[React] GA4(Google Analytics) 이해하고 활용하기-1 : 프로젝트 설정 및 데이터 수집 방법</title>
      <link>https://adjh54.tistory.com/737</link>
      <description>&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;해당 글에서는 React 환경에서 GA4를 이해하고 설정하여 데이터를 수집하는 방법에 대해 알아봅니다.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;
&lt;figure contenteditable=&quot;false&quot; data-ke-type=&quot;emoticon&quot; data-ke-align=&quot;alignCenter&quot; data-emoticon-type=&quot;face&quot; data-emoticon-name=&quot;073&quot; data-emoticon-isanimation=&quot;false&quot; data-emoticon-src=&quot;https://t1.daumcdn.net/keditor/emoticon/face/large/073.png&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/keditor/emoticon/face/large/073.png&quot; width=&quot;80&quot; /&gt;&lt;/figure&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1) Google Analytics(GA4)&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  Google Analytics(GA4)&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- Google 애널리틱스 4는 웹사이트 및 앱에서 트래픽과 참여도를 측정할 수 있는 분석 서비스입니다.&lt;/b&gt;&lt;br /&gt;- 웹사이트와 웹, 앱의 데이터를 수집하여 비즈니스 통찰력을 제공하는 구글의 차세대 분석 플랫폼입니다.&lt;/blockquote&gt;
&lt;figure id=&quot;og_1770352371619&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;Google 애널리틱스 4 소개 &amp;nbsp;|&amp;nbsp; Google Analytics &amp;nbsp;|&amp;nbsp; Google for Developers&quot; data-og-description=&quot;Google 애널리틱스용 MCP 서버를 사용해 보세요. GitHub에서 설치하고 공지사항에서 자세한 내용을 확인하세요. 의견 보내기 Google 애널리틱스 4 소개 컬렉션을 사용해 정리하기 내 환경설정을 기준&quot; data-og-host=&quot;developers.google.com&quot; data-og-source-url=&quot;https://developers.google.com/analytics/devguides/collection/ga4?hl=ko&quot; data-og-url=&quot;https://developers.google.com/analytics/devguides/collection/ga4?hl=ko&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/DGliR/dJMb8QehGJx/Bsqtp4QvGp29vIPdhJPsJ1/img.png?width=1200&amp;amp;height=675&amp;amp;face=0_0_1200_675&quot;&gt;&lt;a href=&quot;https://developers.google.com/analytics/devguides/collection/ga4?hl=ko&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://developers.google.com/analytics/devguides/collection/ga4?hl=ko&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/DGliR/dJMb8QehGJx/Bsqtp4QvGp29vIPdhJPsJ1/img.png?width=1200&amp;amp;height=675&amp;amp;face=0_0_1200_675');&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;Google 애널리틱스 4 소개 &amp;nbsp;|&amp;nbsp; Google Analytics &amp;nbsp;|&amp;nbsp; Google for Developers&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Google 애널리틱스용 MCP 서버를 사용해 보세요. GitHub에서 설치하고 공지사항에서 자세한 내용을 확인하세요. 의견 보내기 Google 애널리틱스 4 소개 컬렉션을 사용해 정리하기 내 환경설정을 기준&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;developers.google.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;h3 data-ke-size=&quot;size23&quot;&gt;1. UA(Universal Analytics)와 GA(Google Analytics)&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  UA(Universal Analytics)와 GA(Google Analytics)&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;1. UA(Universal Analytics)&lt;br /&gt;&lt;/b&gt;-&amp;nbsp; 세션(Session)'과 '페이지뷰(Pageview) 중심이었습니다. 사용자가 머문 시간이나 본 페이지 수를 중요한 데이터로 이용하였습니다.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;2. GA(Google Analytics) &lt;br /&gt;&lt;/b&gt;- 모든 사용자 상호작용을 '이벤트(Event)'로 처리합니다. &lt;br /&gt;- 버튼 클릭, 스크롤, 동영상 재생, 페이지 조회 등 모든 행위가 각각의 이벤트가 되며, 여기에 '매개변수(Parameter)'를 붙여 상세 정보를 수집합니다.&lt;/blockquote&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style2&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;구분&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;UA (Universal Analytics)&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;GA4 (Google Analytics 4)&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;측정 기준&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;세션(Session), 히트(Hit) 기반&lt;/td&gt;
&lt;td&gt;이벤트(Event), 파라미터 기반&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;플랫폼&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;주로 웹(Web) 중심&lt;/td&gt;
&lt;td&gt;웹(Web) + 앱(App) 통합 측정&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;쿠키/개인정보&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;쿠키 의존도가 높음&lt;/td&gt;
&lt;td&gt;머신러닝을 활용한 데이터 보정, IP 익명화&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;ID 체계&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;속성 ID (UA-000000-0)&lt;/td&gt;
&lt;td&gt;측정 ID (G-XXXXXXX)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;보고서 구조&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;표준 보고서(획득, 행동, 전환 등) 위주&lt;/td&gt;
&lt;td&gt;탐색(Exploration) 중심의 맞춤형 보고서&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;데이터 보관&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;거의 무제한 (설정에 따라 다름)&lt;/td&gt;
&lt;td&gt;기본 2개월, 최대 14개월&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;h3 data-ke-size=&quot;size23&quot;&gt;2. GA 수집 데이터&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2.1. 이벤트&lt;/h4&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;style3&quot;&gt;&lt;b&gt;  이벤트&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 웹 사이트 또는 앱과의 기본적인 상호작용을 통해서 트리거가 됩니다.&lt;/b&gt;&lt;br /&gt;- 웹의 경우는 구글 태그(gtag.js)를 통해 자동으로, 또는 '향상된 측정' 설정을 통해 수집되는 데이터입니다.&lt;br /&gt;- 앱의 경우는 Firebase용 구글 애널리틱스 SDK를 통해 데이터를 수집하고 이를 GA4로 전송을 합니다.&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1909&quot; data-origin-height=&quot;782&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qxnAw/dJMb99SNCgu/TDWhFUJFkYT4GLjraTdjWK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qxnAw/dJMb99SNCgu/TDWhFUJFkYT4GLjraTdjWK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qxnAw/dJMb99SNCgu/TDWhFUJFkYT4GLjraTdjWK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FqxnAw%2FdJMb99SNCgu%2FTDWhFUJFkYT4GLjraTdjWK%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;1909&quot; height=&quot;782&quot; data-origin-width=&quot;1909&quot; data-origin-height=&quot;782&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;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style2&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 12.3256%;&quot;&gt;&lt;b&gt;분류&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 18.3721%;&quot;&gt;&lt;b&gt;이벤트 명&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 44.6512%;&quot;&gt;&lt;b&gt;수집 시점&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 24.6512%;&quot;&gt;주요 수집 데이터&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 12.3256%;&quot;&gt;&lt;b&gt;웹/앱 자동 수집&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 18.3721%;&quot;&gt;first_visit&lt;/td&gt;
&lt;td style=&quot;width: 44.6512%;&quot;&gt;웹/앱을 통틀어 사용자가 서비스를 처음 방문했을 때&lt;/td&gt;
&lt;td style=&quot;width: 24.6512%;&quot;&gt;소스, 매체, 캠페인&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 12.3256%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 18.3721%;&quot;&gt;session_start&lt;/td&gt;
&lt;td style=&quot;width: 44.6512%;&quot;&gt;사용자가 방문하여 새로운 세션이 시작될 때&lt;/td&gt;
&lt;td style=&quot;width: 24.6512%;&quot;&gt;세션 ID, 세션 번호&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 12.3256%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 18.3721%;&quot;&gt;user_engagement&lt;/td&gt;
&lt;td style=&quot;width: 44.6512%;&quot;&gt;사용자가 콘텐츠를 활발히 이용 중일 때 (주기적 수집)&lt;/td&gt;
&lt;td style=&quot;width: 24.6512%;&quot;&gt;참여 시간(밀리초)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 12.3256%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 18.3721%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 44.6512%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 24.6512%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 12.3256%;&quot;&gt;&lt;b&gt;웹 자동 수집&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 18.3721%;&quot;&gt;page_view&lt;/td&gt;
&lt;td style=&quot;width: 44.6512%;&quot;&gt;새로운 페이지가 로드되거나 브라우저 기록이 변경될 때&lt;/td&gt;
&lt;td style=&quot;width: 24.6512%;&quot;&gt;페이지 URL, 페이지 제목&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 12.3256%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 18.3721%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 44.6512%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 24.6512%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 12.3256%;&quot;&gt;&lt;b&gt;앱 자동 수집&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 18.3721%;&quot;&gt;screen_view&lt;/td&gt;
&lt;td style=&quot;width: 44.6512%;&quot;&gt;앱 내에서 화면 전환이 일어날 때&lt;/td&gt;
&lt;td style=&quot;width: 24.6512%;&quot;&gt;화면 이름, 클래스 이름&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 12.3256%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 18.3721%;&quot;&gt;first_open&lt;/td&gt;
&lt;td style=&quot;width: 44.6512%;&quot;&gt;앱 설치 후 사용자가 처음으로 실행했을 때&lt;/td&gt;
&lt;td style=&quot;width: 24.6512%;&quot;&gt;앱 버전, 기기 모델, OS 정보&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 12.3256%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 18.3721%;&quot;&gt;app_exception&lt;/td&gt;
&lt;td style=&quot;width: 44.6512%;&quot;&gt;앱이 **비정상 종료(Crash)**되거나 예외가 발생할 때&lt;/td&gt;
&lt;td style=&quot;width: 24.6512%;&quot;&gt;오류 메시지, 치명적 여부(fatal)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 12.3256%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 18.3721%;&quot;&gt;in_app_purchase&lt;/td&gt;
&lt;td style=&quot;width: 44.6512%;&quot;&gt;인앱 결제(App Store, Play Store)가 완료되었을 때&lt;/td&gt;
&lt;td style=&quot;width: 24.6512%;&quot;&gt;상품 ID, 가격, 통화, 수량&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 12.3256%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 18.3721%;&quot;&gt;app_update&lt;/td&gt;
&lt;td style=&quot;width: 44.6512%;&quot;&gt;사용자가 앱을 새 버전으로 업데이트하고 실행할 때&lt;/td&gt;
&lt;td style=&quot;width: 24.6512%;&quot;&gt;이전 앱 버전 정보&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 12.3256%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 18.3721%;&quot;&gt;notification_receive&lt;/td&gt;
&lt;td style=&quot;width: 44.6512%;&quot;&gt;백그라운드 상태에서 푸시 알림을 수신했을 때&lt;/td&gt;
&lt;td style=&quot;width: 24.6512%;&quot;&gt;알림 제목, 본문, 캠페인 정보&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 12.3256%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 18.3721%;&quot;&gt;notification_open&lt;/td&gt;
&lt;td style=&quot;width: 44.6512%;&quot;&gt;사용자가 푸시 알림을 클릭하여 앱을 열었을 때&lt;/td&gt;
&lt;td style=&quot;width: 24.6512%;&quot;&gt;클릭된 알림의 캠페인 정보&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 12.3256%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 18.3721%;&quot;&gt;app_remove&lt;/td&gt;
&lt;td style=&quot;width: 44.6512%;&quot;&gt;(Android 전용) 사용자가 앱을 삭제할 때&lt;/td&gt;
&lt;td style=&quot;width: 24.6512%;&quot;&gt;삭제된 앱 패키지 정보&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 12.3256%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 18.3721%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 44.6512%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 24.6512%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 12.3256%;&quot;&gt;&lt;b&gt;향상된 측정(웹)&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 18.3721%;&quot;&gt;scroll&lt;/td&gt;
&lt;td style=&quot;width: 44.6512%;&quot;&gt;사용자가 페이지의 90% 지점까지 스크롤했을 때&lt;/td&gt;
&lt;td style=&quot;width: 24.6512%;&quot;&gt;스크롤 깊이&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 12.3256%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 18.3721%;&quot;&gt;click&lt;/td&gt;
&lt;td style=&quot;width: 44.6512%;&quot;&gt;현재 도메인이 아닌 외부 링크를 클릭할 때&lt;/td&gt;
&lt;td style=&quot;width: 24.6512%;&quot;&gt;외부 URL, 링크 ID, 링크 클래스&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 12.3256%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 18.3721%;&quot;&gt;view_search_results&lt;/td&gt;
&lt;td style=&quot;width: 44.6512%;&quot;&gt;사이트 내 검색 결과 페이지가 로드될 때&lt;/td&gt;
&lt;td style=&quot;width: 24.6512%;&quot;&gt;검색어 (q, s, search_term 등)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 12.3256%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 18.3721%;&quot;&gt;video_start / progress&lt;/td&gt;
&lt;td style=&quot;width: 44.6512%;&quot;&gt;내장된 YouTube 영상이 재생되거나 특정 구간(25, 50% 등) 통과 시&lt;/td&gt;
&lt;td style=&quot;width: 24.6512%;&quot;&gt;영상 제목, 영상 URL, 진행률&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 12.3256%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 18.3721%;&quot;&gt;file_download&lt;/td&gt;
&lt;td style=&quot;width: 44.6512%;&quot;&gt;문서, 실행 파일, 압축 파일 링크를 클릭했을 때&lt;/td&gt;
&lt;td style=&quot;width: 24.6512%;&quot;&gt;파일 이름, 확장자, 파일 URL&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;figure id=&quot;og_1770352562017&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;자동 수집 이벤트 - 애널리틱스 고객센터&quot; data-og-description=&quot;도움이 되었나요? 어떻게 하면 개선할 수 있을까요?&quot; data-og-host=&quot;support.google.com&quot; data-og-source-url=&quot;https://support.google.com/analytics/answer/9234069&quot; data-og-url=&quot;https://support.google.com/analytics/answer/9234069&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://support.google.com/analytics/answer/9234069&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://support.google.com/analytics/answer/9234069&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;자동 수집 이벤트 - 애널리틱스 고객센터&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;support.google.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;h4 data-ke-size=&quot;size20&quot;&gt;2.2. 속성&lt;/h4&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;style3&quot;&gt;&lt;b&gt;  속성&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 자동 수집되는 속성과 사용자가 직접 정의하여 부여하는 속성이 있습니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1913&quot; data-origin-height=&quot;767&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bh2tIb/dJMcai9ZTxH/ySyha5A1A74VvRIgwcqmTK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bh2tIb/dJMcai9ZTxH/ySyha5A1A74VvRIgwcqmTK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bh2tIb/dJMcai9ZTxH/ySyha5A1A74VvRIgwcqmTK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbh2tIb%2FdJMcai9ZTxH%2FySyha5A1A74VvRIgwcqmTK%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;1913&quot; height=&quot;767&quot; data-origin-width=&quot;1913&quot; data-origin-height=&quot;767&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style2&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;속성명&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;데이터 내용&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;분석 활용도&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Device 정보&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;기기 모델, 브랜드, OS 버전, 브라우저&lt;/td&gt;
&lt;td&gt;기기별 최적화 및 UI/UX 개선 근거&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Geo 정보&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;국가, 지역, 도시 (IP 기반)&lt;/td&gt;
&lt;td&gt;지역별 타겟 마케팅 및 광고 집행&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;App 정보&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;앱 버전, 스토어 ID, 패키지 이름&lt;/td&gt;
&lt;td&gt;버전별 버그 추적 및 업데이트 유도&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Language&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;시스템 설정 언어&lt;/td&gt;
&lt;td&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;h2 data-ke-size=&quot;size26&quot;&gt;2) Google Analytics(GA4) 환경 설정&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 공식사이트에 접속합니다.&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&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;figure id=&quot;og_1770352642196&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;Google Analytics &amp;nbsp;|&amp;nbsp; Google for Developers&quot; data-og-description=&quot;Google 애널리틱스를 사용하면 디지털 전략을 세부적으로 조정하고, 캠페인을 최적화하며, 온라인 인지도를 한 단계 업그레이드할 수 있습니다.&quot; data-og-host=&quot;developers.google.com&quot; data-og-source-url=&quot;https://developers.google.com/analytics?hl=ko&quot; data-og-url=&quot;https://developers.google.com/analytics?hl=ko&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/z9mjf/dJMb9g46DzZ/jGNkr8kikKtNRAyYnKyu10/img.png?width=1200&amp;amp;height=675&amp;amp;face=0_0_1200_675,https://scrap.kakaocdn.net/dn/egvJxi/dJMb9iaMDmk/ddIeGrqivRNtL1bcj4mvP0/img.png?width=338&amp;amp;height=340&amp;amp;face=0_0_338_340&quot;&gt;&lt;a href=&quot;https://developers.google.com/analytics?hl=ko&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://developers.google.com/analytics?hl=ko&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/z9mjf/dJMb9g46DzZ/jGNkr8kikKtNRAyYnKyu10/img.png?width=1200&amp;amp;height=675&amp;amp;face=0_0_1200_675,https://scrap.kakaocdn.net/dn/egvJxi/dJMb9iaMDmk/ddIeGrqivRNtL1bcj4mvP0/img.png?width=338&amp;amp;height=340&amp;amp;face=0_0_338_340');&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;Google Analytics &amp;nbsp;|&amp;nbsp; Google for Developers&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Google 애널리틱스를 사용하면 디지털 전략을 세부적으로 조정하고, 캠페인을 최적화하며, 온라인 인지도를 한 단계 업그레이드할 수 있습니다.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;developers.google.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;h3 data-ke-size=&quot;size23&quot;&gt;2. 아래와 같이 속성을 만듭니다&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  아래와 같이 속성을 만듭니다&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 수집하려는 프로젝트에 대해서 생성합니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1848&quot; data-origin-height=&quot;824&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bnW4DG/dJMcabJSts2/CwOIezKEBMhha7vcs5LiLk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bnW4DG/dJMcabJSts2/CwOIezKEBMhha7vcs5LiLk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bnW4DG/dJMcabJSts2/CwOIezKEBMhha7vcs5LiLk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbnW4DG%2FdJMcabJSts2%2FCwOIezKEBMhha7vcs5LiLk%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;1848&quot; height=&quot;824&quot; data-origin-width=&quot;1848&quot; data-origin-height=&quot;824&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;h3 data-ke-size=&quot;size23&quot;&gt;3. 업종 및 카테고리를 선택합니다&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1915&quot; data-origin-height=&quot;768&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bisyyh/dJMcaaEbL2x/HBLXxORkMN4BEmlWwHzLuk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bisyyh/dJMcaaEbL2x/HBLXxORkMN4BEmlWwHzLuk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bisyyh/dJMcaaEbL2x/HBLXxORkMN4BEmlWwHzLuk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbisyyh%2FdJMcaaEbL2x%2FHBLXxORkMN4BEmlWwHzLuk%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;1915&quot; height=&quot;768&quot; data-origin-width=&quot;1915&quot; data-origin-height=&quot;768&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;h3 data-ke-size=&quot;size23&quot;&gt;4. 비즈니스 목표를 선택합니다&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1898&quot; data-origin-height=&quot;773&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bLgh89/dJMcaaEbL2Z/kkmzkQjVEC5Oh83ktMp26k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bLgh89/dJMcaaEbL2Z/kkmzkQjVEC5Oh83ktMp26k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bLgh89/dJMcaaEbL2Z/kkmzkQjVEC5Oh83ktMp26k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbLgh89%2FdJMcaaEbL2Z%2FkkmzkQjVEC5Oh83ktMp26k%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;1898&quot; height=&quot;773&quot; data-origin-width=&quot;1898&quot; data-origin-height=&quot;773&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;h3 data-ke-size=&quot;size23&quot;&gt;5. 데이터 수집을 웹으로 선택합니다&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1917&quot; data-origin-height=&quot;728&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/TRVVM/dJMcai9ZTDg/mCxkksbExQsCtGweei77bK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/TRVVM/dJMcai9ZTDg/mCxkksbExQsCtGweei77bK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/TRVVM/dJMcai9ZTDg/mCxkksbExQsCtGweei77bK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FTRVVM%2FdJMcai9ZTDg%2FmCxkksbExQsCtGweei77bK%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;1917&quot; height=&quot;728&quot; data-origin-width=&quot;1917&quot; data-origin-height=&quot;728&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;h3 data-ke-size=&quot;size23&quot;&gt;6. 웹 사이트 URL을 입력하고, 스트림 이름을 입력합니다&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  웹 사이트 URL을 입력하고, 스트림 이름을 입력합니다&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 로컬에서 테스트를 하는 경우 웹 사이트 URL의 경우 임의로 입력을 해도 됩니다!&lt;/b&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1919&quot; data-origin-height=&quot;830&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/blpsnM/dJMcadHCUwe/wAdElp4IKM7WoMc0IHWpl1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/blpsnM/dJMcadHCUwe/wAdElp4IKM7WoMc0IHWpl1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/blpsnM/dJMcadHCUwe/wAdElp4IKM7WoMc0IHWpl1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FblpsnM%2FdJMcadHCUwe%2FwAdElp4IKM7WoMc0IHWpl1%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;1919&quot; height=&quot;830&quot; data-origin-width=&quot;1919&quot; data-origin-height=&quot;830&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;h3 data-ke-size=&quot;size23&quot;&gt;7. 아래와 같이 생성이 되었습니다.&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1915&quot; data-origin-height=&quot;776&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bKSxgD/dJMcafZLRpR/yV9cB7SsweZ2MNTKmDCGBk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bKSxgD/dJMcafZLRpR/yV9cB7SsweZ2MNTKmDCGBk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bKSxgD/dJMcafZLRpR/yV9cB7SsweZ2MNTKmDCGBk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbKSxgD%2FdJMcafZLRpR%2FyV9cB7SsweZ2MNTKmDCGBk%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;1915&quot; height=&quot;776&quot; data-origin-width=&quot;1915&quot; data-origin-height=&quot;776&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;h3 data-ke-size=&quot;size23&quot;&gt;8. 태그를 확인합니다&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  태그를 확인합니다&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 아래와 같이 GA에서 제공해 주는 태그를 확인할 수 있습니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1916&quot; data-origin-height=&quot;834&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Pwd5n/dJMcajnwZyY/yIO1wmZJzKxCzNUDuN6Sk0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Pwd5n/dJMcajnwZyY/yIO1wmZJzKxCzNUDuN6Sk0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Pwd5n/dJMcajnwZyY/yIO1wmZJzKxCzNUDuN6Sk0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FPwd5n%2FdJMcajnwZyY%2FyIO1wmZJzKxCzNUDuN6Sk0%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;1916&quot; height=&quot;834&quot; data-origin-width=&quot;1916&quot; data-origin-height=&quot;834&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3) 프로젝트 설정&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. GA 라이브러리를 설치합니다&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;dockerfile&quot;&gt;&lt;code&gt;npm i react-ga4

# or

yarn add react-ga4
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.npmjs.com/package/react-ga4&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.npmjs.com/package/react-ga4&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. 로컬에서 측정을 위한 체커를 구성합니다.&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  로컬에서 측정을 위한 체커를 구성합니다.&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;- GA_ID는 위에서 발급한 아이디를 입력합니다.&lt;br /&gt;- 해당 함수에서는 추가로 &amp;lsquo;페이지에 머문 시간(체류시간)&amp;rsquo;을 측정하기 위해 page_stay라는 이벤트를 추가하는 과정입니다. &lt;br /&gt;- 기본적으로 제공하는 이벤트 외에 새로운 함수를 이용합니다.보조적으로 새로고침 및 탭 종료 시에 머문 시간을 측정하도록 추가 구성하였습니다.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;1. GA 초기화&lt;/b&gt;&lt;br /&gt;- react-ga4 라이브러리를 이용하여서 GA 사용을 초기화 합니다&lt;br /&gt;&lt;br /&gt;&lt;b&gt;2. GA 기록 -1&lt;/b&gt;&lt;br /&gt;- 기본적으로 사용자의 웹 체류시간에 대한 이벤트 기록을 추가로 수행합니다&lt;br /&gt;&lt;br /&gt;&lt;b&gt;3. GA 기록 -2&lt;/b&gt;&lt;br /&gt;- 새로고침, 탭 닫기에 대한 체류시간에 대해서도 기록을 수행합니다.&lt;/blockquote&gt;
&lt;pre class=&quot;typescript&quot;&gt;&lt;code&gt;// RouteChangeTracker.tsx
import { useEffect, useRef } from 'react';
import { useLocation } from 'react-router-dom';
import ReactGA from 'react-ga4';

const GA_ID = 'G-XXXXXXX';
const STORAGE_KEY = 'ROUTE_TRACK_LOG';
const MAX_LOG = 100;

let gaInitialized = false;

export default function RouteChangeTracker() {
    const location = useLocation();

    const enterTimeRef = useRef&amp;lt;number&amp;gt;(Date.now());
    const prevPathRef = useRef&amp;lt;string&amp;gt;(location.pathname);

    /**
     * GA4 초기화
     */
    useEffect(() =&amp;gt; {
        if (!gaInitialized) {
            ReactGA.initialize(GA_ID, {
                gaOptions: {
                    debug_mode: true,
                },
            });
            gaInitialized = true;
        }
    }, []);

    /**
     * 체류시간 이벤트 전송
     */
    const sendStayEvent = (path: string, enterAt: number) =&amp;gt; {
        const now = Date.now();
        const durationSec = Math.round((now - enterAt) / 1000);
        if (durationSec &amp;lt;= 0) return;

        ReactGA.event('page_stay', {
            page_path: path,
            engagement_time_sec: durationSec,
        });

        const log = {
            path,
            enterAt,
            leaveAt: now,
            durationSec,
        };

        const prev = JSON.parse(localStorage.getItem(STORAGE_KEY) || '[]');
        localStorage.setItem(
            STORAGE_KEY,
            JSON.stringify([...prev, log].slice(-MAX_LOG))
        );
    };

    /**
     * 라우트 변경 &amp;rarr; 이전 페이지 체류시간 기록
     */
    useEffect(() =&amp;gt; {
        sendStayEvent(prevPathRef.current, enterTimeRef.current);

        enterTimeRef.current = Date.now();
        prevPathRef.current = location.pathname;
    }, [location.pathname]);

    /**
     * 최초 진입 페이지 보정
     */
    useEffect(() =&amp;gt; {
        return () =&amp;gt; {
            sendStayEvent(prevPathRef.current, enterTimeRef.current);
        };
    }, []);

    /**
     * 새로고침 / 탭 종료 보조 처리
     */
    useEffect(() =&amp;gt; {
        const handleUnload = () =&amp;gt; {
            sendStayEvent(prevPathRef.current, enterTimeRef.current);
        };

        window.addEventListener('beforeunload', handleUnload);
        return () =&amp;gt; window.removeEventListener('beforeunload', handleUnload);
    }, []);

    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;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. Layout&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  Layout&lt;/b&gt;&lt;br /&gt;&lt;b&gt;&lt;br /&gt;- 한 번만 수행되어야 하는 컴포넌트이기에, 아래와 같이 추가가 되었습니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;import { Outlet } from &quot;react-router-dom&quot;;
import Header from &quot;./Header&quot;;
import Footer from &quot;./Footer&quot;;
import RouteChangeTracker from &quot;../ga/RouteChangeTracker&quot;;

export default function Layout() {
    return (
        &amp;lt;div style={styles.container}&amp;gt;
            &amp;lt;Header /&amp;gt;

            &amp;lt;main style={styles.main}&amp;gt;
                &amp;lt;RouteChangeTracker /&amp;gt;
                &amp;lt;Outlet /&amp;gt;
            &amp;lt;/main&amp;gt;

            &amp;lt;Footer /&amp;gt;
        &amp;lt;/div&amp;gt;
    );
}

const styles: Record&amp;lt;string, React.CSSProperties&amp;gt; = {
    container: {
        minHeight: &quot;100vh&quot;,
        display: &quot;flex&quot;,
        flexDirection: &quot;column&quot;,
    },
    main: {
        flex: 1,
        padding: 24,
        background: &quot;#f9fafb&quot;,
    },
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4) 결과&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  결과&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;- &lt;b&gt;아래와 같이 기록한 이벤트들이 잘 들어오는 것을 확인하였습니다.&lt;/b&gt;&lt;/blockquote&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;2048&quot; data-origin-height=&quot;1109&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/tSS3w/dJMcacWhfKu/vd9RPg9QP5LmcoJzRs9ODk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/tSS3w/dJMcacWhfKu/vd9RPg9QP5LmcoJzRs9ODk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/tSS3w/dJMcacWhfKu/vd9RPg9QP5LmcoJzRs9ODk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FtSS3w%2FdJMcacWhfKu%2Fvd9RPg9QP5LmcoJzRs9ODk%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;2048&quot; height=&quot;1109&quot; data-origin-width=&quot;2048&quot; data-origin-height=&quot;1109&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 data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;오늘도&amp;nbsp;감사합니다.&amp;nbsp; &lt;/b&gt;&lt;/p&gt;</description>
      <category>React &amp;amp; React Native/환경 설정 및 구성</category>
      <category>Google Analytics</category>
      <category>Google Analytics React</category>
      <category>Google Analytics 데이터 수집</category>
      <category>Google Analytics 속성</category>
      <category>Google Analytics 수집 데이터</category>
      <category>Google Analytics 이벤트</category>
      <category>Google Analytics 이벤트 기록</category>
      <category>Google Analytics 이벤트 확인</category>
      <category>Google Analytics 체류시간</category>
      <category>Google Analytics 환경설정</category>
      <author>adjh54</author>
      <guid isPermaLink="true">https://adjh54.tistory.com/737</guid>
      <comments>https://adjh54.tistory.com/737#entry737comment</comments>
      <pubDate>Fri, 6 Feb 2026 20:05:56 +0900</pubDate>
    </item>
    <item>
      <title>[기타] Gemini Mac 환경에서 바로 실행 방법</title>
      <link>https://adjh54.tistory.com/736</link>
      <description>&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;해당 글에서는 Mac 환경에서 Gemini를 바로 실행하는 방법에 대해 알아봅니다.&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;
&lt;figure contenteditable=&quot;false&quot; data-ke-type=&quot;emoticon&quot; data-ke-align=&quot;alignCenter&quot; data-emoticon-type=&quot;face&quot; data-emoticon-name=&quot;073&quot; data-emoticon-isanimation=&quot;false&quot; data-emoticon-src=&quot;https://t1.daumcdn.net/keditor/emoticon/face/large/073.png&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/keditor/emoticon/face/large/073.png&quot; width=&quot;80&quot; /&gt;&lt;/figure&gt;
&lt;/blockquote&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1) 문제점&lt;/h2&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;style3&quot;&gt;&lt;b&gt;  문제점&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;- MacOS 환경에서 Gemini를 바로 실행하는 애플리케이션이 없기에 브라우저를 통해서 실행해야 합니다.&lt;br /&gt;- 그렇기에 빠르게 브라우저를 실행하여 Gemini로 접속할 수 있는 방법에 대해 알아봅니다.&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2) Chorme에서 실행 방법&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 사이트 접속&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  사이트 접속&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- Google Chorme을 통해서&amp;nbsp; Gemini 사이트에 접속합니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://gemini.google.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://gemini.google.com/&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. Chrome 가장 오른쪽 점 3개 아이콘(⋮)을 누릅니다&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;348&quot; data-origin-height=&quot;733&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/LaOQN/dJMcag5tEKh/coIX4kaob3YHCbc7x97Rw1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/LaOQN/dJMcag5tEKh/coIX4kaob3YHCbc7x97Rw1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/LaOQN/dJMcag5tEKh/coIX4kaob3YHCbc7x97Rw1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FLaOQN%2FdJMcag5tEKh%2FcoIX4kaob3YHCbc7x97Rw1%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;348&quot; height=&quot;733&quot; data-origin-width=&quot;348&quot; data-origin-height=&quot;733&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;h3 data-ke-size=&quot;size23&quot;&gt;3. 전송, 저장, 공유 &amp;gt; 바로가기 만들기&amp;hellip; 를 선택합니다.&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;697&quot; data-origin-height=&quot;924&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/boBewe/dJMcahcdy0m/xXckSdHhrIk5J4og3Fo2tk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/boBewe/dJMcahcdy0m/xXckSdHhrIk5J4og3Fo2tk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/boBewe/dJMcahcdy0m/xXckSdHhrIk5J4og3Fo2tk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FboBewe%2FdJMcahcdy0m%2FxXckSdHhrIk5J4og3Fo2tk%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;697&quot; height=&quot;924&quot; data-origin-width=&quot;697&quot; data-origin-height=&quot;924&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4. 바로가기를 만들어줍니다&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;472&quot; data-origin-height=&quot;241&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/MWWum/dJMcaiCbn7u/DKEqdK15b99iO8sOK206c1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/MWWum/dJMcaiCbn7u/DKEqdK15b99iO8sOK206c1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/MWWum/dJMcaiCbn7u/DKEqdK15b99iO8sOK206c1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FMWWum%2FdJMcaiCbn7u%2FDKEqdK15b99iO8sOK206c1%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;472&quot; height=&quot;241&quot; data-origin-width=&quot;472&quot; data-origin-height=&quot;241&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;h3 data-ke-size=&quot;size23&quot;&gt;5. 바로가기를 Dock으로 드래그하면 완료가 됩니다.&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1311&quot; data-origin-height=&quot;872&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bri3Ed/dJMcagqQUSt/ve8MZVkNxwsTz9ps0xm4Pk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bri3Ed/dJMcagqQUSt/ve8MZVkNxwsTz9ps0xm4Pk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bri3Ed/dJMcagqQUSt/ve8MZVkNxwsTz9ps0xm4Pk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbri3Ed%2FdJMcagqQUSt%2Fve8MZVkNxwsTz9ps0xm4Pk%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;1311&quot; height=&quot;872&quot; data-origin-width=&quot;1311&quot; data-origin-height=&quot;872&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2) Safari에서 실행&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 사이트 접속&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  사이트 접속&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- Safari를 이용하여 Gemini 사이트에 접속합니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1920&quot; data-origin-height=&quot;956&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/crq5JQ/dJMcadt44ee/T8NQWTvzyGonKqldpIjpqK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/crq5JQ/dJMcadt44ee/T8NQWTvzyGonKqldpIjpqK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/crq5JQ/dJMcadt44ee/T8NQWTvzyGonKqldpIjpqK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcrq5JQ%2FdJMcadt44ee%2FT8NQWTvzyGonKqldpIjpqK%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;1920&quot; height=&quot;956&quot; data-origin-width=&quot;1920&quot; data-origin-height=&quot;956&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://gemini.google.com/app&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://gemini.google.com/app&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. 상단 메뉴에서 파일 &amp;gt; Dock에 추가.. 버튼을 누릅니다.&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;563&quot; data-origin-height=&quot;839&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dm7sDP/dJMcadHCUhV/Sw2rXW98oQz6rKuOxlmJok/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dm7sDP/dJMcadHCUhV/Sw2rXW98oQz6rKuOxlmJok/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dm7sDP/dJMcadHCUhV/Sw2rXW98oQz6rKuOxlmJok/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdm7sDP%2FdJMcadHCUhV%2FSw2rXW98oQz6rKuOxlmJok%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;563&quot; height=&quot;839&quot; data-origin-width=&quot;563&quot; data-origin-height=&quot;839&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;h3 data-ke-size=&quot;size23&quot;&gt;3. Dock에 추가를 합니다&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;626&quot; data-origin-height=&quot;279&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b0Xn4u/dJMcahJ5F05/LXAlOkm7lfibnMMVwhAgNK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b0Xn4u/dJMcahJ5F05/LXAlOkm7lfibnMMVwhAgNK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b0Xn4u/dJMcahJ5F05/LXAlOkm7lfibnMMVwhAgNK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb0Xn4u%2FdJMcahJ5F05%2FLXAlOkm7lfibnMMVwhAgNK%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;626&quot; height=&quot;279&quot; data-origin-width=&quot;626&quot; data-origin-height=&quot;279&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;h3 data-ke-size=&quot;size23&quot;&gt;4. 아래와 같이 추가가 되어 완료되었습니다&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;558&quot; data-origin-height=&quot;86&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bmuOaU/dJMcaflblSU/uoq3waDdkNLLldTDTUw5Bk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bmuOaU/dJMcaflblSU/uoq3waDdkNLLldTDTUw5Bk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bmuOaU/dJMcaflblSU/uoq3waDdkNLLldTDTUw5Bk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbmuOaU%2FdJMcaflblSU%2Fuoq3waDdkNLLldTDTUw5Bk%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;558&quot; height=&quot;86&quot; data-origin-width=&quot;558&quot; data-origin-height=&quot;86&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 data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;오늘도&amp;nbsp;감사합니다.&amp;nbsp; &lt;/b&gt;&lt;/p&gt;</description>
      <category>개발 Tip/기타</category>
      <category>Gemini</category>
      <category>Gemini Dock</category>
      <category>Gemini Dock 추가</category>
      <category>Gemini for mac</category>
      <category>Gemini 바로 실행</category>
      <category>Gemini 바로가기 설정</category>
      <category>Gemini 빠르게 실행방법</category>
      <category>Gemini 실행</category>
      <category>Gemini 실행 방법</category>
      <category>Gemini 툴</category>
      <author>adjh54</author>
      <guid isPermaLink="true">https://adjh54.tistory.com/736</guid>
      <comments>https://adjh54.tistory.com/736#entry736comment</comments>
      <pubDate>Fri, 6 Feb 2026 20:00:08 +0900</pubDate>
    </item>
    <item>
      <title>[JS] Quill Editor v2 이해하고 활용하기-1: 기본 활용</title>
      <link>https://adjh54.tistory.com/735</link>
      <description>&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;해당 글에서는 Quill Editor를 이해하고 활용하는 방법에 대해서 알아봅니다.&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;
&lt;figure contenteditable=&quot;false&quot; data-ke-type=&quot;emoticon&quot; data-ke-align=&quot;alignCenter&quot; data-emoticon-type=&quot;face&quot; data-emoticon-name=&quot;073&quot; data-emoticon-isanimation=&quot;false&quot; data-emoticon-src=&quot;https://t1.daumcdn.net/keditor/emoticon/face/large/073.png&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/keditor/emoticon/face/large/073.png&quot; width=&quot;80&quot; /&gt;&lt;/figure&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1) Quill&lt;/h2&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;style3&quot;&gt;&lt;b&gt;  Quill&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 웹 기반의 리치 텍스트 에디터를 무료로 사용할 수 있는 오픈 소스 에디터입니다. 단순한 HTML의 textarea가 아닌 문서 구조를 데이터로 관리하는 특징을 가진 에디터입니다.&lt;/b&gt;&lt;br /&gt;- 이러한 문서 구조의 데이터는 HTML 문자열이 아닌 Quill 자체의 &amp;lsquo;Delta 데이터 타입(JSON)&amp;rsquo;을 통해서 데이터가 전달되고 관리가 됩니다.&lt;br /&gt;- BSD 3-clause 라이선스로 상업 서비스&amp;middot;유료 제품&amp;middot;폐쇄 소스 프로젝트에서도 사용 가능합니다&lt;/blockquote&gt;
&lt;figure id=&quot;og_1770267764464&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;Quill - Your powerful rich text editor&quot; data-og-description=&quot;Built for Developers Granular access to the editor's content, changes and events through a simple API. Works consistently and deterministically with JSON as both input and output.&quot; data-og-host=&quot;quilljs.com&quot; data-og-source-url=&quot;https://quilljs.com/&quot; data-og-url=&quot;https://quilljs.com&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/by0UpU/dJMb8YXGR1K/UikKbmSaDQzRuaC0mK5FY0/img.png?width=300&amp;amp;height=300&amp;amp;face=0_0_300_300,https://scrap.kakaocdn.net/dn/my6pW/dJMb8QL7pcm/k4rioZdu8wOxEUOqIg1kFk/img.png?width=300&amp;amp;height=300&amp;amp;face=0_0_300_300&quot;&gt;&lt;a href=&quot;https://quilljs.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://quilljs.com/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/by0UpU/dJMb8YXGR1K/UikKbmSaDQzRuaC0mK5FY0/img.png?width=300&amp;amp;height=300&amp;amp;face=0_0_300_300,https://scrap.kakaocdn.net/dn/my6pW/dJMb8QL7pcm/k4rioZdu8wOxEUOqIg1kFk/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;Quill - Your powerful rich text editor&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Built for Developers Granular access to the editor's content, changes and events through a simple API. Works consistently and deterministically with JSON as both input and output.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;quilljs.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;h3 data-ke-size=&quot;size23&quot;&gt;1. Quill Delta&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  Quill Delta&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- Delta는 Quill이 문서를 표현하는 표준 변경 포맷(JSON)을 의미합니다.&lt;/b&gt;&lt;br /&gt;- HTML의 모호함과 복잡성 없이 모든 텍스트 및 서식 정보를 포함하는 모든 서식 있는 텍스트 문서를 설명할 수 있습니다.&lt;/blockquote&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style2&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 10.5814%;&quot;&gt;&lt;b&gt;속성&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 48.9535%;&quot;&gt;&lt;b&gt;설명&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 40.3488%;&quot;&gt;&lt;b&gt;사용예시&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 10.5814%;&quot;&gt;&lt;b&gt;insert&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 48.9535%;&quot;&gt;&lt;b&gt;현재 문서에 무언가를 추가하는 지에 대해 기록하는 방식&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 40.3488%;&quot;&gt;{ &quot;insert&quot;: &quot;제목&quot;, &quot;attributes&quot;: { &quot;header&quot;: 1 } }&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 10.5814%;&quot;&gt;&lt;b&gt;delete&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 48.9535%;&quot;&gt;&lt;b&gt;현재 문서를 기준으로 앞쪽의 내용을 제거하는 방식&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 40.3488%;&quot;&gt;{ &quot;delete&quot;: 5 }&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 10.5814%;&quot;&gt;&lt;b&gt;retain&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 48.9535%;&quot;&gt;&lt;b&gt;기존 콘텐츠를 유지하면서, 위치를 건너뛰거나 스타일을 변경하는 방식&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 40.3488%;&quot;&gt;{ &quot;retain&quot;: 3, &quot;attributes&quot;: { &quot;bold&quot;: true }&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;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  아래와 같은 데이터 구조로 저장되고 관리할 수 있습니다.&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;- 기존 html을 문자열로 저장하는 것보다 구조가 명확하다는 장점을 가지고 있습니다.&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;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  HTML 문자열 구조의 데이터 타입 형태&lt;/b&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;755&quot; data-origin-height=&quot;279&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Bw91H/dJMcafrV7oB/6awvspFOHpdhdaE2n7vWs1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Bw91H/dJMcafrV7oB/6awvspFOHpdhdaE2n7vWs1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Bw91H/dJMcafrV7oB/6awvspFOHpdhdaE2n7vWs1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FBw91H%2FdJMcafrV7oB%2F6awvspFOHpdhdaE2n7vWs1%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;755&quot; height=&quot;279&quot; data-origin-width=&quot;755&quot; data-origin-height=&quot;279&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;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  Quill의 &amp;lsquo;Delta&amp;rsquo; 데이터 타입 형태&lt;/b&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;738&quot; data-origin-height=&quot;194&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/biSnpD/dJMcabJR5nY/QNxSGvgklqDXPf6W4JI741/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/biSnpD/dJMcabJR5nY/QNxSGvgklqDXPf6W4JI741/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/biSnpD/dJMcabJR5nY/QNxSGvgklqDXPf6W4JI741/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbiSnpD%2FdJMcabJR5nY%2FQNxSGvgklqDXPf6W4JI741%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;738&quot; height=&quot;194&quot; data-origin-width=&quot;738&quot; data-origin-height=&quot;194&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;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  Quill Delta 구조&lt;/b&gt;&lt;/blockquote&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;[
    {
        &quot;insert&quot;: &quot;테스트중에 있습니다\\n\\n여러가지 효과에 대해서 저장이 가능합니다\\n\\n굵기 테스트\\n\\nString codeTest;&quot;
    },
    {
        &quot;attributes&quot;: {
            &quot;code-block&quot;: &quot;plain&quot;
        },
        &quot;insert&quot;: &quot;\\n&quot;
    },
    {
        &quot;insert&quot;: &quot;\\n인용구 테스트&quot;
    },
    {
        &quot;attributes&quot;: {
            &quot;blockquote&quot;: true
        },
        &quot;insert&quot;: &quot;\\n&quot;
    },
    {
        &quot;insert&quot;: &quot;\\n&quot;
    },
    {
        &quot;insert&quot;: {
            &quot;video&quot;: &quot;&amp;lt;https://www.youtube.com/embed/eNL_fWxWh0w?showinfo=0&amp;gt;&quot;
        }
    },
    {
        &quot;insert&quot;: &quot;\\n\\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;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2) 설치&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. react-quill&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  react-quill&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;- Quill을 이용하는데 React 16 미만 버전에 대해서 지원하는 구 버전입니다.&lt;br /&gt;- 현재는 운영유지보수가 되지 않고 있습니다.&lt;/blockquote&gt;
&lt;pre class=&quot;coffeescript&quot;&gt;&lt;code&gt;$ npm i react-quill

# or

$ yarn add npm i react-quill
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. react-quill-new (React Quill v2)&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  react-quill-new&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- Quill을 이용하는데 React 16+ 및 Typescript를 지원하는 버전입니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;pre class=&quot;coffeescript&quot;&gt;&lt;code&gt;$ npm i react-quill

# or

$ yarn add npm i react-quill
&lt;/code&gt;&lt;/pre&gt;
&lt;figure id=&quot;og_1770268082720&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 - VaguelySerious/react-quill: A Quill component for React.&quot; data-og-description=&quot;A Quill component for React. Contribute to VaguelySerious/react-quill development by creating an account on GitHub.&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/VaguelySerious/react-quill&quot; data-og-url=&quot;https://github.com/VaguelySerious/react-quill&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/z9ncS/dJMb9aKAu4A/KHewtvXWFPt8kPAR0hM9f1/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/vFpLt/dJMb9b3Nt2D/xDqlnTSVyngLZsd7qXrQFk/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/VaguelySerious/react-quill&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/VaguelySerious/react-quill&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/z9ncS/dJMb9aKAu4A/KHewtvXWFPt8kPAR0hM9f1/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/vFpLt/dJMb9b3Nt2D/xDqlnTSVyngLZsd7qXrQFk/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 - VaguelySerious/react-quill: A Quill component for React.&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;A Quill component for React. Contribute to VaguelySerious/react-quill development by creating an account on GitHub.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. &amp;nbsp;Quill&amp;nbsp;생태계&amp;nbsp;주요&amp;nbsp;라이브러리들&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style2&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;추가 라이브러리&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;설명&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;quill-delta&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Delta 자료구조 전용 라이브러리를 의미합니다. HTML 구조가 아닌 Quill만의 Delta(JSON) 구조를 통해 데이터를 저장하고 관리합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;@mgreminger/quill-image-resize-module&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;에디터에서 이미지 크기 조절 필요할 때 사용하는 라이브러리를 의미합니다. 이를 통해 이미지의 사이즈 조정이 가능합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;quill-delta-to-html&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Delta 자료구조를 HTML로 변환해주는 라이브러리를 의미합니다. 이를 통해 Viewer, SSR, 미리보기와 같은 기능에 이용이 됩니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;quill-better-table&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Quill Editor 내에서 테이블을 추가할 수 있는 라이브러리를 의미합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;quill-mention&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Quill 내에서 @멘션 및 자동완성 기능의 라이브러리를 의미합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;pre class=&quot;shell&quot; data-ke-language=&quot;shell&quot;&gt;&lt;code&gt;$ yarn add quill-delta
 
$ yarn add quill-image-resize-module quill-resize-module&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3) 사용 방법&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 환경&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style2&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 49.3023%;&quot;&gt;&lt;b&gt;추가 라이브러리&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 50.5814%;&quot;&gt;&lt;b&gt;설명&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 49.3023%;&quot;&gt;react-quill-new&lt;/td&gt;
&lt;td style=&quot;width: 50.5814%;&quot;&gt;3.7.0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 49.3023%;&quot;&gt;idb&lt;/td&gt;
&lt;td style=&quot;width: 50.5814%;&quot;&gt;8.0.3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 49.3023%;&quot;&gt;quill-delta&lt;/td&gt;
&lt;td style=&quot;width: 50.5814%;&quot;&gt;5.1.0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 49.3023%;&quot;&gt;@mgreminger/quill-image-resize-module&lt;/td&gt;
&lt;td style=&quot;width: 50.5814%;&quot;&gt;1.0.5&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;h3 data-ke-size=&quot;size23&quot;&gt;2. Quill 등록 예시&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  Quill 등록 예시&lt;/b&gt;&lt;br /&gt;&lt;b&gt;&lt;br /&gt;- 아래와 같은 Quill Editor를 이용한 Delta 데이터로 index DB에 저장하는 예시입니다.&lt;/b&gt;&lt;br /&gt;&lt;b&gt;&lt;br /&gt;- import 'react-quill-new/dist/quill.snow.css':&lt;/b&gt;&amp;nbsp; &amp;lsquo;snow 테마&amp;rsquo;를 선택하였습니다.&lt;br /&gt;&lt;b&gt;- format:&lt;/b&gt; 에디터에서 허용할 서식(format) 목록을 명시적으로 제한합니다. 즉, 에디터에서 사용할 서식을 지정합니다. &lt;br /&gt;&lt;b&gt;- toolbarOptions:&lt;/b&gt; 상단에 툴바에 나오는 옵션들을 지정합니다.&lt;br /&gt;&lt;b&gt;- modules :&lt;/b&gt; Quill의 플러그인&amp;middot;기능&amp;middot;동작 방식 설정 집합을 의미합니다.&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;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  이러한 옵션들을 통해 ReactQuill 태그 내에서 사용될 옵션들을 지정합니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;pre class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot;&gt;&lt;code&gt;import React, { useEffect, useState } from 'react';
import ReactQuill, { Quill } from 'react-quill-new';
import ImageResize from &quot;@mgreminger/quill-image-resize-module&quot;;
import 'react-quill-new/dist/quill.snow.css';

import { v4 as uuidv4 } from 'uuid';

Quill.register('modules/imageResize', ImageResize);

import Delta from 'quill-delta';
import { dbPromise } from '../../server/dbPromise';
import { useNavigate } from 'react-router-dom';

export default function QuillEditorPage() {
    const navigate = useNavigate();
    const [title, setTitle] = useState('');
    const [contentHtml, setContentHtml] = useState('');
    const [contentDelta, setContentDelta] = useState&amp;lt;Delta | null&amp;gt;(null);

    const formats = [
        'header',
        'bold', 'italic', 'underline', 'strike',
        'blockquote', 'code-block',
        'list', 'bullet', 'check',
        'indent',
        'link',
        'image',
        'video',
        'color', 'background',
        'align',
        'font',
        'size',
    ];

    const toolbarOptions = [
        ['bold', 'italic', 'underline', 'strike'],
        ['blockquote', 'code-block'],
        ['link', 'image', 'video', 'formula'],
        [{ header: 1 }, { header: 2 }],
        [{ list: 'ordered' }, { list: 'bullet' }, { list: 'check' }],
        [{ script: 'sub' }, { script: 'super' }],
        [{ indent: '-1' }, { indent: '+1' }],
        [{ direction: 'rtl' }],
        [{ size: ['small', false, 'large', 'huge'] }],
        [{ header: [1, 2, 3, 4, 5, 6, false] }],
        [{ color: [] }, { background: [] }],
        [{ font: [] }],
        [{ align: [] }],
        ['clean'],
    ];

    const modules = {
        toolbar: toolbarOptions,
        imageResize: {
            modules: ['Resize', 'DisplaySize', 'Toolbar'],
        },
    };

    const checkPosts = async () =&amp;gt; {
        const db = await dbPromise;
        const allPosts = await db.getAll('posts');
        console.log(allPosts);
    };
    useEffect(() =&amp;gt; {
        checkPosts();
    }, [])

    const handleSave = async () =&amp;gt; {
        if (!title.trim()) {
            alert('제목을 입력해 주세요.');
            return;
        }

        if (!contentDelta || !contentDelta.ops?.length) {
            alert('내용을 입력해 주세요.');
            return;
        }

        await savePostToDB({
            title,
            contentDelta,
            contentHtml,
        });

        alert('로컬에 저장되었습니다.');
        navigate('/editor/quill/list');

    };

    const savePostToDB = async ({
        title,
        contentDelta,
        contentHtml,
    }: {
        title: string;
        contentDelta: Delta;
        contentHtml: string;
    }) =&amp;gt; {
        const db = await dbPromise;

        const now = new Date().toISOString();

        const id = uuidv4();
        await db.put(&quot;posts&quot;, {
            id,
            title,
            contentDelta,
            contentHtml,
            createdAt: now,
            updatedAt: now,
        });
    };

    return (
        &amp;lt;div style={styles.page}&amp;gt;
            &amp;lt;div style={styles.container}&amp;gt;
                &amp;lt;div style={styles.header}&amp;gt;
                    &amp;lt;h1 style={styles.title}&amp;gt;게시글 작성&amp;lt;/h1&amp;gt;
                    &amp;lt;p style={styles.subtitle}&amp;gt;
                        내용을 자유롭게 작성해 주세요.
                    &amp;lt;/p&amp;gt;
                &amp;lt;/div&amp;gt;

                &amp;lt;input
                    style={styles.titleInput}
                    placeholder=&quot;제목을 입력하세요&quot;
                    value={title}
                    onChange={(e) =&amp;gt; setTitle(e.target.value)}
                /&amp;gt;

                &amp;lt;div style={styles.editorWrapper}&amp;gt;
                    &amp;lt;ReactQuill
                        theme=&quot;snow&quot;
                        value={contentHtml}
                        onChange={(html, delta, source, editor) =&amp;gt; {
                            setContentHtml(html);
                            setContentDelta(editor.getContents());
                        }}
                        modules={modules}
                        formats={formats}  
                        style={styles.editor}
                        placeholder=&quot;내용을 입력하세요...&quot;
                    /&amp;gt;
                &amp;lt;/div&amp;gt;
                &amp;lt;div style={styles.footer}&amp;gt;
                    &amp;lt;button
                        style={styles.backButton}
                        onClick={() =&amp;gt; window.history.back()}
                    &amp;gt;
                        &amp;larr; 뒤로가기
                    &amp;lt;/button&amp;gt;

                    &amp;lt;button
                        style={styles.saveButton}
                        onClick={handleSave}
                    &amp;gt;
                          저장
                    &amp;lt;/button&amp;gt;
                &amp;lt;/div&amp;gt;
                &amp;lt;div style={styles.footerNote}&amp;gt;
                    Editor powered by react-quill-new v3.7.0
                &amp;lt;/div&amp;gt;
            &amp;lt;/div&amp;gt;

        &amp;lt;/div&amp;gt;
    );
}

/* ===================== 스타일 ===================== */
const styles: Record&amp;lt;string, React.CSSProperties&amp;gt; = {
    page: {
        minHeight: '100vh',
        background: '#f5f6f8',
        padding: '40px 0',
    },
    container: {
        maxWidth: 960,
        margin: '0 auto',
        background: '#fff',
        padding: '32px 36px',
        borderRadius: 16,
        boxShadow: '0 12px 30px rgba(0,0,0,0.08)',
    },
    header: {
        marginBottom: 28,
    },
    title: {
        margin: 0,
        fontSize: 28,
        fontWeight: 700,
        color: '#111',
    },
    subtitle: {
        marginTop: 8,
        color: '#666',
        fontSize: 14,
    },
    titleInput: {
        width: '100%',
        padding: '14px 16px',
        fontSize: 18,
        borderRadius: 8,
        border: '1px solid #ddd',
        marginBottom: 20,
        outline: 'none',
    },
    editorWrapper: {
        borderRadius: 12,
        overflow: 'hidden',
        border: '1px solid #e5e7eb',
    },
    editor: {
        minHeight: 400, // ⭐ 높이 증가
        fontSize: 16,
    },
    footer: {
        marginTop: 32,
        display: 'flex',
        justifyContent: 'center',
        gap: 12,
    },

    backButton: {
        padding: '12px 20px',
        fontSize: 14,
        fontWeight: 600,
        borderRadius: 8,
        border: '1px solid #d1d5db',
        background: '#fff',
        color: '#374151',
        cursor: 'pointer',
        transition: 'all 0.2s ease',
    },
    footerNote: {
        marginTop: 12,
        fontSize: 12,
        color: '#888',
        textAlign: 'right',
    },
    saveButton: {
        padding: '12px 24px',
        fontSize: 14,
        fontWeight: 700,
        borderRadius: 8,
        border: 'none',
        background: '#2563eb', // 블루
        color: '#fff',
        cursor: 'pointer',
        transition: 'all 0.2s ease',
    },
};&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;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  아래와 같이 지정한 툴바들이 사용이 됩니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1899&quot; data-origin-height=&quot;770&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/HfBFV/dJMcadnjZUr/7BpTKfQhK78Ppwkjuffxp0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/HfBFV/dJMcadnjZUr/7BpTKfQhK78Ppwkjuffxp0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/HfBFV/dJMcadnjZUr/7BpTKfQhK78Ppwkjuffxp0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FHfBFV%2FdJMcadnjZUr%2F7BpTKfQhK78Ppwkjuffxp0%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;1899&quot; height=&quot;770&quot; data-origin-width=&quot;1899&quot; data-origin-height=&quot;770&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;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  아래와 같이 툴바를 이용하고 등록을 합니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1896&quot; data-origin-height=&quot;773&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mJXNP/dJMcab38sqK/WhK6HI6TB9mCHqCQRGBS31/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mJXNP/dJMcab38sqK/WhK6HI6TB9mCHqCQRGBS31/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mJXNP/dJMcab38sqK/WhK6HI6TB9mCHqCQRGBS31/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmJXNP%2FdJMcab38sqK%2FWhK6HI6TB9mCHqCQRGBS31%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;1896&quot; height=&quot;773&quot; data-origin-width=&quot;1896&quot; data-origin-height=&quot;773&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;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  [참고]&lt;/b&gt; 다양한 옵션은 아래의 링크에서 확인이 가능합니다.&lt;/blockquote&gt;
&lt;figure id=&quot;og_1770268401786&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;Quill - Your powerful rich text editor&quot; data-og-description=&quot;Built for Developers Granular access to the editor's content, changes and events through a simple API. Works consistently and deterministically with JSON as both input and output.&quot; data-og-host=&quot;quilljs.com&quot; data-og-source-url=&quot;https://quilljs.com/docs/modules/toolbar&quot; data-og-url=&quot;https://quilljs.com&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/2CSaS/dJMb8T9UQUA/PMTKxkyQgkEKvLHj80yjrk/img.png?width=300&amp;amp;height=300&amp;amp;face=0_0_300_300,https://scrap.kakaocdn.net/dn/cG49on/dJMb9iaMxDP/QZsSKQcynV8tFHbwlwS3kk/img.png?width=300&amp;amp;height=300&amp;amp;face=0_0_300_300&quot;&gt;&lt;a href=&quot;https://quilljs.com/docs/modules/toolbar&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://quilljs.com/docs/modules/toolbar&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/2CSaS/dJMb8T9UQUA/PMTKxkyQgkEKvLHj80yjrk/img.png?width=300&amp;amp;height=300&amp;amp;face=0_0_300_300,https://scrap.kakaocdn.net/dn/cG49on/dJMb9iaMxDP/QZsSKQcynV8tFHbwlwS3kk/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;Quill - Your powerful rich text editor&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Built for Developers Granular access to the editor's content, changes and events through a simple API. Works consistently and deterministically with JSON as both input and output.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;quilljs.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;h3 data-ke-size=&quot;size23&quot;&gt;3. Quill 상세 조회 예시&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  Quill 상세 조회 예시&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 위에서 IndexDB내에 임시로 저장한 데이터들에 대해서 상세 화면으로 출력을 합니다.&lt;/b&gt;&lt;br /&gt;- 위와 다르게 테마를 bubble을 이용하여서 화면상에 출력을 합니다.&lt;br /&gt;- 저장된 id 값을 기반으로 저장된 Delta 구조를 기반으로 ReactQuill 태그 내에 데이터를 출력하며, 화면상에 출력을 합니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  Quill의 bubble 테마를 이용하여서 에디터 내용을 상세화면으로 출력합니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;pre class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot;&gt;&lt;code&gt;import { useEffect, useState } from &quot;react&quot;;
import { useNavigate, useParams } from &quot;react-router-dom&quot;;
import ReactQuill from &quot;react-quill-new&quot;;
import &quot;react-quill-new/dist/quill.bubble.css&quot;;

import { getPostById } from &quot;../../server/dbPromise&quot;;

interface Post {
    id: string;
    title: string;
    contentDelta: any;
    contentHtml: string;
    createdAt: string;
    updatedAt: string;
}
function formatDate(iso: string) {
    return new Date(iso).toISOString().slice(0, 10);
}
export default function QuillPostDetailPage() {
    const { id } = useParams&amp;lt;{ id: string }&amp;gt;();
    const navigate = useNavigate();

    const [post, setPost] = useState&amp;lt;Post | null&amp;gt;(null);

    useEffect(() =&amp;gt; {
        if (!id) return;

        (async () =&amp;gt; {
            const data = await getPostById(id);
            setPost(data ?? null);
            console.log(&quot;data : &quot;, data)
        })();

    }, [id]);

    if (!post) {
        return &amp;lt;div style={styles.loading}&amp;gt;게시글을 불러오는 중입니다&amp;hellip;&amp;lt;/div&amp;gt;;
    }

    return (
        &amp;lt;div style={styles.page}&amp;gt;
            {/* ===== 상단 액션 ===== */}
            &amp;lt;div style={styles.topBar}&amp;gt;
                &amp;lt;button
                    style={styles.backButton}
                    onClick={() =&amp;gt; navigate(-1)}
                &amp;gt;
                    &amp;larr; 목록
                &amp;lt;/button&amp;gt;
            &amp;lt;/div&amp;gt;

            {/* ===== 제목 영역 ===== */}
            &amp;lt;h1 style={styles.title}&amp;gt;{post.title}&amp;lt;/h1&amp;gt;
            &amp;lt;div style={styles.date}&amp;gt;
                {formatDate(post.createdAt)}
            &amp;lt;/div&amp;gt;

            &amp;lt;hr style={styles.divider} /&amp;gt;

            {/* ===== Quill Viewer ===== */}
            &amp;lt;ReactQuill
                value={post.contentDelta}
                readOnly
                theme=&quot;bubble&quot;
            /&amp;gt;
        &amp;lt;/div&amp;gt;
    );
}

const styles: Record&amp;lt;string, React.CSSProperties&amp;gt; = {
    page: {
        maxWidth: 800,
        margin: &quot;40px auto&quot;,
        padding: 24,
        background: &quot;#ffffff&quot;,
        borderRadius: 12,
        boxShadow: &quot;0 8px 24px rgba(0,0,0,0.06)&quot;,
    },
    topBar: {
        marginBottom: 16,
    },
    backButton: {
        border: &quot;none&quot;,
        background: &quot;transparent&quot;,
        cursor: &quot;pointer&quot;,
        fontSize: 14,
        color: &quot;#6b7280&quot;,
    },
    title: {
        fontSize: 28,
        fontWeight: 700,
        marginBottom: 8,
    },
    date: {
        fontSize: 13,
        color: &quot;#9ca3af&quot;,
        marginBottom: 20,
    },
    divider: {
        border: &quot;none&quot;,
        borderTop: &quot;1px solid #e5e7eb&quot;,
        marginBottom: 20,
    },
    loading: {
        textAlign: &quot;center&quot;,
        padding: 40,
        color: &quot;#9ca3af&quot;,
    },
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  아래와 같이 위에서 저장한 내용이 아래와 같이 보입니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1906&quot; data-origin-height=&quot;691&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/4qe8K/dJMcaioGcpy/gUhXkZbkEjj8KG0JWI2W5k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/4qe8K/dJMcaioGcpy/gUhXkZbkEjj8KG0JWI2W5k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/4qe8K/dJMcaioGcpy/gUhXkZbkEjj8KG0JWI2W5k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F4qe8K%2FdJMcaioGcpy%2FgUhXkZbkEjj8KG0JWI2W5k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1906&quot; height=&quot;691&quot; data-origin-width=&quot;1906&quot; data-origin-height=&quot;691&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;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  추가로 툴바로 저장한 이미지, youtube 영상, 코드스니핏등 다양하게 화면상에 출력이 가능합니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;466&quot; data-origin-height=&quot;758&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/vsJFz/dJMcaaYsTt5/FFMbjnPtGo4w7ghcUYBuWk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/vsJFz/dJMcaaYsTt5/FFMbjnPtGo4w7ghcUYBuWk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/vsJFz/dJMcaaYsTt5/FFMbjnPtGo4w7ghcUYBuWk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FvsJFz%2FdJMcaaYsTt5%2FFFMbjnPtGo4w7ghcUYBuWk%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;466&quot; height=&quot;758&quot; data-origin-width=&quot;466&quot; data-origin-height=&quot;758&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4) Quill과 XSS(Cross-Site Scripting) 방지&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  Quill과 XSS(Cross-Site Scripting) 방지&lt;/b&gt;&lt;br /&gt;&lt;b&gt;&lt;br /&gt;- HTML 전송의 경우는 XSS 공격에 취약합니다. HTML 코드를 중간에 탈취하고 변경하여서 데이터베이스에 저장하여 이를 통해 공격을 합니다.&lt;/b&gt;&lt;br /&gt;- Quill의 경우는 HTML 형태로 전송되지 않고, Quill만의 &amp;lsquo;Delta&amp;rsquo;라는 JSON 구조로 데이터를 전송하기에 Delta 자체로는 XSS를 만들 수 없습니다.&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;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  [참고]&amp;nbsp;XSS(Cross-Site Scripting)&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 공격자가 취약한 웹사이트에 악성 스크립트를 삽입하여, 이를 방문하는 사용자의 브라우저에서 스크립트를 실행시키는 클라이언트 측 공격입니다. &lt;/b&gt;&lt;br /&gt;- 세션 쿠키 탈취, 데이터 도난, 웹사이트 변조, 피싱 사이트 리다이렉션 등의 피해를 유발합니다. &lt;br /&gt;- 주요 원인은 입력값 검증 미흡이며, 저장형, 반사형, DOM 기반 XSS로 나뉩니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  아래와 같이 Markup을 포함하여 저장하고 있지 않습니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;{
    &quot;insert&quot;: &quot;HTML 자체를 저장하는게 아니라\\n\\nDelta라는 데이터 구조로 저장하기에 이를 조합하여 전달할 수 없습니다.\\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;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  HTML 구조의 경우는 아래와 같은 형태로 저장되고 있습니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;pre class=&quot;dts&quot;&gt;&lt;code&gt;&amp;lt;p&amp;gt;HTML&amp;amp;nbsp;자체를&amp;amp;nbsp;저장하는게&amp;amp;nbsp;아니라&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Delta라는&amp;amp;nbsp;데이터&amp;amp;nbsp;구조로&amp;amp;nbsp;저장하기에&amp;amp;nbsp;이를&amp;amp;nbsp;조합하여&amp;amp;nbsp;전달할&amp;amp;nbsp;수&amp;amp;nbsp;없습니다.&amp;lt;/p&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;&lt;/p&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;오늘도&amp;nbsp;감사합니다.&amp;nbsp; &lt;/b&gt;&lt;/p&gt;</description>
      <category>React &amp;amp; React Native/라이브러리 활용</category>
      <category>javascript editor</category>
      <category>JS editor</category>
      <category>js editor embedded video</category>
      <category>js editor image</category>
      <category>Quill</category>
      <category>quill delta</category>
      <category>Quill Editor</category>
      <category>react editor</category>
      <author>adjh54</author>
      <guid isPermaLink="true">https://adjh54.tistory.com/735</guid>
      <comments>https://adjh54.tistory.com/735#entry735comment</comments>
      <pubDate>Thu, 5 Feb 2026 20:00:42 +0900</pubDate>
    </item>
    <item>
      <title>[Trend] Spring Boot 4 Release Note 읽어보기</title>
      <link>https://adjh54.tistory.com/734</link>
      <description>&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;해당 글에서는 Spring Boot 4가 출시되어 주요 내용에 대한 Release Note를 읽어보기 위해 작성한 글입니다.&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;
&lt;figure contenteditable=&quot;false&quot; data-ke-type=&quot;emoticon&quot; data-ke-align=&quot;alignCenter&quot; data-emoticon-type=&quot;face&quot; data-emoticon-name=&quot;073&quot; data-emoticon-isanimation=&quot;false&quot; data-emoticon-src=&quot;https://t1.daumcdn.net/keditor/emoticon/face/large/073.png&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/keditor/emoticon/face/large/073.png&quot; width=&quot;80&quot; /&gt;&lt;/figure&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1) Spring Boot 4&lt;/h2&gt;
&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  Spring Boot 4&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 2025년 11월 공식 릴리스된 Spring Boot의 메이저 버전 업그레이드로, 이전의 3.x 라인과 비교해 상당한 아키텍처 개선과 신기능이 도입된 새로운 세대의 프레임워크입니다.&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;- Spring Boot는 Java 생태계에서 가장 널리 쓰이는 백엔드 프레임워크로, 설정을 최소화하면서 Spring 기반 애플리케이션을 빠르게 만들도록 도와줍니다.&lt;br /&gt;- 버전 4.0은 Spring Framework 7을 기반으로 하며, 클라우드&amp;middot;컨테이너&amp;middot;모던 Java 생태계에 더 적합하도록 여러 부분이 근본적으로 재설계되었습니다&lt;br /&gt;- Spring Boot 4 버전에서는 Java 25에 대한 완벽한 지원을 제공하며, Java 17과의 호환성도 유지합니다.&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1036&quot; data-origin-height=&quot;624&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bes9dj/dJMcahJYpho/1BPbq598CKxEglzCIADh01/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bes9dj/dJMcahJYpho/1BPbq598CKxEglzCIADh01/img.png&quot; data-alt=&quot;https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-4.0-Release-Notes&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bes9dj/dJMcahJYpho/1BPbq598CKxEglzCIADh01/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbes9dj%2FdJMcahJYpho%2F1BPbq598CKxEglzCIADh01%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;1036&quot; height=&quot;624&quot; data-origin-width=&quot;1036&quot; data-origin-height=&quot;624&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-4.0-Release-Notes&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure data-ke-type=&quot;opengraph&quot; data-og-title=&quot;Spring Boot 4.0 Release Notes&quot; data-ke-align=&quot;alignCenter&quot; data-og-description=&quot;Spring Boot helps you to create Spring-powered, production-grade applications and services with absolute minimum fuss. - spring-projects/spring-boot&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-4.0-Release-Notes&quot; data-og-image=&quot;https://blog.kakaocdn.net/dna/ncp4H/dJMb9ee7WId/AAAAAAAAAAAAAAAAAAAAAKd_cgNyS4X74Ylh3eAUuZPktgS2G0BOz0TjSKkrkp1V/img.png?credential=yqXZFxpELC7KVnFOS48ylbz2pIh7yKj8&amp;amp;expires=1769871599&amp;amp;allow_ip=&amp;amp;allow_referer=&amp;amp;signature=JeAKd1vOR83yN6XbkNUGr4xG%2BFk%3D&quot; data-og-url=&quot;https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-4.0-Release-Notes&quot;&gt;&lt;a href=&quot;https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-4.0-Release-Notes&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-4.0-Release-Notes&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://blog.kakaocdn.net/dna/ncp4H/dJMb9ee7WId/AAAAAAAAAAAAAAAAAAAAAKd_cgNyS4X74Ylh3eAUuZPktgS2G0BOz0TjSKkrkp1V/img.png?credential=yqXZFxpELC7KVnFOS48ylbz2pIh7yKj8&amp;amp;expires=1769871599&amp;amp;allow_ip=&amp;amp;allow_referer=&amp;amp;signature=JeAKd1vOR83yN6XbkNUGr4xG%2BFk%3D');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Spring Boot 4.0 Release Notes&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Spring Boot helps you to create Spring-powered, production-grade applications and services with absolute minimum fuss. - spring-projects/spring-boot&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;br /&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. Spring Boot 4 Release Note 주요 사항&lt;/h3&gt;
&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  Spring Boot 4 Release Note 주요 사항&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;- 해당 글에서는 Release Notes에 있는 주요한 사항에 대해서만 확인해 봅니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&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: 31.5116%;&quot;&gt;&lt;b&gt;주요 내용&amp;nbsp;&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 68.3721%;&quot;&gt;&lt;b&gt;설명&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 31.5116%;&quot;&gt;&lt;b&gt;Gradle 9&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 68.3721%;&quot;&gt;- Spring Boot4 애플리케이션 빌드에 Gradle 9가 지원됩니다. &lt;br /&gt;- 기존 Gradle 8.x(8.14 이상)에 대한 지원은 계속 유지됩니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 31.5116%;&quot;&gt;&lt;b&gt;HTTP Service Interface Clients&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 68.3721%;&quot;&gt;- HTTP 서비스를 호출할 때 RestClient나 WebClient를 직접 사용하는 대신, @HttpExchange 계열 어노테이션(@GetExchange, @PostExchange, @DeleteExchange 등)이 붙은 Java 인터페이스를 통해 선언적으로 호출할 수 있습니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 31.5116%;&quot;&gt;&lt;b&gt;API Versioning&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 68.3721%;&quot;&gt;- Spring Boot 4 (Spring Framework 7.x 이후)에서는 요청 Header를 조건으로 사용하는 API 버저닝 방식을 보다 자연스럽게 적용할 수 있습니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 31.5116%;&quot;&gt;&lt;b&gt;JmsClient&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 68.3721%;&quot;&gt;- JMS(Java Message Service) 내에서 기존 JmsTemplate, JmsMessagingTemplate를 사용했던 부분에 대해서 Spring에서 RestClient / HttpService Clients 스타일에 맞춘 JmsClient API가 추가되었습니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 31.5116%;&quot;&gt;&lt;b&gt;OpenTelemetry starter&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 68.3721%;&quot;&gt;- OpenTelemetry(OTel) 는 관측성(Observability)을 위한 표준 스펙 + 라이브러리 집합이며, 내부 동작에 대해서 관측하는 라이브러리가 Spring Boot Starter로 추가되었습니다.&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;figure data-ke-type=&quot;opengraph&quot; data-og-title=&quot;Spring Boot 4.0.0 available now&quot; data-ke-align=&quot;alignCenter&quot; data-og-description=&quot;On behalf of the team and everyone who has contributed, I'm extremely happy to announce that Spring Boot 4.0.0 has been released and is now available from Maven Central. This release is the beginning of a new Spring Boot generation providing solid foundati&quot; data-og-host=&quot;spring.io&quot; data-og-source-url=&quot;https://spring.io/blog/2025/11/20/spring-boot-4-0-0-available-now&quot; data-og-image=&quot;https://blog.kakaocdn.net/dna/cfFEra/dJMb8Qegbn9/AAAAAAAAAAAAAAAAAAAAAEQ2D3D3oiOR6IbCB8UZIHNx70WsLW9Cdi_BL3RrO7XQ/img.png?credential=yqXZFxpELC7KVnFOS48ylbz2pIh7yKj8&amp;amp;expires=1769871599&amp;amp;allow_ip=&amp;amp;allow_referer=&amp;amp;signature=Poov5%2FZb3ndyHC2FDAGxcUt0yEM%3D&quot; data-og-url=&quot;https://spring.io/blog/2025/11/20/spring-boot-4-0-0-available-now&quot;&gt;&lt;a href=&quot;https://spring.io/blog/2025/11/20/spring-boot-4-0-0-available-now&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://spring.io/blog/2025/11/20/spring-boot-4-0-0-available-now&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://blog.kakaocdn.net/dna/cfFEra/dJMb8Qegbn9/AAAAAAAAAAAAAAAAAAAAAEQ2D3D3oiOR6IbCB8UZIHNx70WsLW9Cdi_BL3RrO7XQ/img.png?credential=yqXZFxpELC7KVnFOS48ylbz2pIh7yKj8&amp;amp;expires=1769871599&amp;amp;allow_ip=&amp;amp;allow_referer=&amp;amp;signature=Poov5%2FZb3ndyHC2FDAGxcUt0yEM%3D');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Spring Boot 4.0.0 available now&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;On behalf of the team and everyone who has contributed, I'm extremely happy to announce that Spring Boot 4.0.0 has been released and is now available from Maven Central. This release is the beginning of a new Spring Boot generation providing solid foundati&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;h2 data-ke-size=&quot;size26&quot;&gt;2) Gradle 9&lt;/h2&gt;
&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  Gradle 9&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;- Spring Boot 4 애플리케이션 빌드에 Gradle 9가 지원됩니다.&lt;br /&gt;- 기존 Gradle 8.x(8.14 이상)에 대한 지원은 계속 유지됩니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. Java Version 별 지원 Gradle 버전&lt;/h3&gt;
&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  Java Version 별 지원 Gradle 버전&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;- Spring Boot 4 버전에서 Java 25 버전이 지원되기에 Gradle 9까지 지원이 되는 것 같습니다.&lt;br /&gt;- 현재 2026.01.21일 기준 Gradle 9.3.0 버전까지 출시가 되었습니다.&lt;/blockquote&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 333px;&quot; border=&quot;1&quot; data-ke-style=&quot;style2&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Java Version&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;Support for toolchains&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;Support for running Gradle&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;height: 17px;&quot;&gt;8&lt;/td&gt;
&lt;td style=&quot;height: 17px;&quot;&gt;N/A&lt;/td&gt;
&lt;td style=&quot;height: 17px;&quot;&gt;2.0 to 8.14.*&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;height: 17px;&quot;&gt;9&lt;/td&gt;
&lt;td style=&quot;height: 17px;&quot;&gt;N/A&lt;/td&gt;
&lt;td style=&quot;height: 17px;&quot;&gt;4.3 to 8.14.*&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;height: 17px;&quot;&gt;10&lt;/td&gt;
&lt;td style=&quot;height: 17px;&quot;&gt;N/A&lt;/td&gt;
&lt;td style=&quot;height: 17px;&quot;&gt;4.7 to 8.14.*&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;height: 17px;&quot;&gt;&lt;b&gt;11&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 17px;&quot;&gt;&lt;b&gt;N/A&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 17px;&quot;&gt;&lt;b&gt;5.0 to 8.14.*&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;height: 17px;&quot;&gt;12&lt;/td&gt;
&lt;td style=&quot;height: 17px;&quot;&gt;N/A&lt;/td&gt;
&lt;td style=&quot;height: 17px;&quot;&gt;5.4 to 8.14.*&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;height: 17px;&quot;&gt;13&lt;/td&gt;
&lt;td style=&quot;height: 17px;&quot;&gt;N/A&lt;/td&gt;
&lt;td style=&quot;height: 17px;&quot;&gt;6.0 to 8.14.*&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;height: 17px;&quot;&gt;14&lt;/td&gt;
&lt;td style=&quot;height: 17px;&quot;&gt;N/A&lt;/td&gt;
&lt;td style=&quot;height: 17px;&quot;&gt;6.3 to 8.14.*&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;height: 17px;&quot;&gt;15&lt;/td&gt;
&lt;td style=&quot;height: 17px;&quot;&gt;6.7&lt;/td&gt;
&lt;td style=&quot;height: 17px;&quot;&gt;6.7 to 8.14.*&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;height: 17px;&quot;&gt;16&lt;/td&gt;
&lt;td style=&quot;height: 17px;&quot;&gt;7.0&lt;/td&gt;
&lt;td style=&quot;height: 17px;&quot;&gt;7.0 to 8.14.*&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;height: 17px;&quot;&gt;&lt;b&gt;17&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 17px;&quot;&gt;&lt;b&gt;7.3&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 17px;&quot;&gt;&lt;b&gt;7.3 and after&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;height: 17px;&quot;&gt;18&lt;/td&gt;
&lt;td style=&quot;height: 17px;&quot;&gt;7.5&lt;/td&gt;
&lt;td style=&quot;height: 17px;&quot;&gt;7.5 and after&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;height: 17px;&quot;&gt;19&lt;/td&gt;
&lt;td style=&quot;height: 17px;&quot;&gt;7.6&lt;/td&gt;
&lt;td style=&quot;height: 17px;&quot;&gt;7.6 and after&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;height: 17px;&quot;&gt;20&lt;/td&gt;
&lt;td style=&quot;height: 17px;&quot;&gt;8.1&lt;/td&gt;
&lt;td style=&quot;height: 17px;&quot;&gt;8.3 and after&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;height: 17px;&quot;&gt;&lt;b&gt;21&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 17px;&quot;&gt;&lt;b&gt;8.4&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 17px;&quot;&gt;&lt;b&gt;8.5 and after&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;height: 17px;&quot;&gt;22&lt;/td&gt;
&lt;td style=&quot;height: 17px;&quot;&gt;8.7&lt;/td&gt;
&lt;td style=&quot;height: 17px;&quot;&gt;8.8 and after&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;height: 17px;&quot;&gt;23&lt;/td&gt;
&lt;td style=&quot;height: 17px;&quot;&gt;8.10&lt;/td&gt;
&lt;td style=&quot;height: 17px;&quot;&gt;8.10 and after&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;height: 17px;&quot;&gt;24&lt;/td&gt;
&lt;td style=&quot;height: 17px;&quot;&gt;8.14&lt;/td&gt;
&lt;td style=&quot;height: 17px;&quot;&gt;8.14 and after&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;height: 17px;&quot;&gt;&lt;b&gt;25&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 17px;&quot;&gt;&lt;b&gt;9.1.0&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 17px;&quot;&gt;&lt;b&gt;9.1.0 and after&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;height: 17px;&quot;&gt;26&lt;/td&gt;
&lt;td style=&quot;height: 17px;&quot;&gt;N/A&lt;/td&gt;
&lt;td style=&quot;height: 17px;&quot;&gt;N/A&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;figure data-ke-type=&quot;opengraph&quot; data-og-title=&quot;Compatibility Matrix&quot; data-ke-align=&quot;alignCenter&quot; data-og-description=&quot;Gradle runs on the Java Virtual Machine (JVM), which is often provided by either a JDK or JRE. A JVM version between 17 and 25 is required to execute Gradle. JVM 26 and later versions are not yet supported. The Gradle wrapper, Gradle client, Tooling API cl&quot; data-og-host=&quot;docs.gradle.org&quot; data-og-source-url=&quot;https://docs.gradle.org/current/userguide/compatibility.html&quot; data-og-url=&quot;https://docs.gradle.org/current/userguide/compatibility.html&quot;&gt;&lt;a href=&quot;https://docs.gradle.org/current/userguide/compatibility.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://docs.gradle.org/current/userguide/compatibility.html&quot;&gt;
&lt;div class=&quot;og-image&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;Compatibility Matrix&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Gradle runs on the Java Virtual Machine (JVM), which is often provided by either a JDK or JRE. A JVM version between 17 and 25 is required to execute Gradle. JVM 26 and later versions are not yet supported. The Gradle wrapper, Gradle client, Tooling API cl&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;docs.gradle.org&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  [참고]&lt;/b&gt; Java LTS 버전 확인 : Oracle Java SE 기준&lt;br /&gt;&lt;br /&gt;- Java LTS 버전은 8, 11, 17, 21, 25입니다.&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;424&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Lh8d7/dJMcaaYm7kh/eYDtCewqdemaXaEXQBLGH0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Lh8d7/dJMcaaYm7kh/eYDtCewqdemaXaEXQBLGH0/img.png&quot; data-alt=&quot;https://www.oracle.com/kr/java/technologies/java-se-support-roadmap.html&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Lh8d7/dJMcaaYm7kh/eYDtCewqdemaXaEXQBLGH0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FLh8d7%2FdJMcaaYm7kh%2FeYDtCewqdemaXaEXQBLGH0%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;1280&quot; height=&quot;424&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;424&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://www.oracle.com/kr/java/technologies/java-se-support-roadmap.html&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;[더 알아보기]&lt;/b&gt;&lt;br /&gt;&lt;b&gt;&lt;br /&gt;&lt;/b&gt;&lt;b&gt; &amp;nbsp;왜 LTS(Long Term Support) 버전을 이용해야 할까?&lt;/b&gt;&lt;br /&gt;&lt;b&gt;&lt;br /&gt;&lt;/b&gt;&lt;b&gt;1. 운영 환경에서의 안정성&amp;middot;보안&amp;middot;유지비용을 최소화하기 위해서입니다.&lt;/b&gt;&lt;br /&gt;- LTS 버전의 경우는 3 ~ 5년 이상의 보안 패치를 제공하지만, 일반 버전의 경우는 6개월 ~ 1년 내 종료가 됩니다.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;2. 프레임워크, 라이브러리는 LTS를 기준으로 발전을 합니다&lt;/b&gt;&lt;br /&gt;- Spring Boot, Hibernate, Gradle / Maven의 경우는 새로운 버전 LTS JVM을 기준으로 지원을 합니다.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;3. 팀 조직 간의 관점에서 관리가 쉽습니다.&lt;/b&gt;&lt;br /&gt;- 팀 내에서 각각 다른 버전을 이용하는 근거가 부족하기에 조직 내에서 동일한 버전 환경에서 개발을 할 때에 유용합니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3) HTTP Service Interface Clients&lt;/h2&gt;
&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  HTTP Service Interface Clients&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- HTTP 서비스를 호출할 때 RestClient나 WebClient를 직접 사용하는 대신, @HttpExchange 계열 어노테이션 (@GetExchange, @PostExchange, @DeleteExchange 등)이 붙은 Java 인터페이스를 통해 선언적으로 호출할 수 있습니다.&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;- HTTP Service Interface Clients는 Spring Boot 4에서 자동 구성 및 구성 속성 지원이 추가되었으며, 일반 Java 인터페이스에 어노테이션을 선언하면 Spring이 런타임에 프락시 기반 구현체를 자동으로 생성합니다.&lt;br /&gt;- 기존에는 RestClient나 WebClient를 직접 사용하여 외부 HTTP 통신을 수행했다면, Spring Boot 4에서는 개발자가 HTTP Service Interface Clients의 인터페이스 메서드를 호출하면 Spring Proxy가 이를 가로어 내부적으로 RestClient(동기) 또는 WebClient(비동기)를 사용해 실제 HTTP 요청을 수행하는 구조로 동작합니다.&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;917&quot; data-origin-height=&quot;387&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/VwVq0/dJMcaaqw7Nf/PH2MJ9ybGsKQKtD43YLwTk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/VwVq0/dJMcaaqw7Nf/PH2MJ9ybGsKQKtD43YLwTk/img.png&quot; data-alt=&quot;https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-4.0-Release-Notes&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/VwVq0/dJMcaaqw7Nf/PH2MJ9ybGsKQKtD43YLwTk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FVwVq0%2FdJMcaaqw7Nf%2FPH2MJ9ybGsKQKtD43YLwTk%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;917&quot; height=&quot;387&quot; data-origin-width=&quot;917&quot; data-origin-height=&quot;387&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-4.0-Release-Notes&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure data-ke-type=&quot;opengraph&quot; data-og-title=&quot;Calling REST Services :: Spring Boot&quot; data-ke-align=&quot;alignCenter&quot; data-og-description=&quot;Both WebClient and RestClient support making versioned remote HTTP calls so that APIs can be evolved over time. Commonly this involves sending an HTTP header, a query parameter or URL path segment that indicates the version of the API that should be used. &quot; data-og-host=&quot;docs.spring.io&quot; data-og-source-url=&quot;https://docs.spring.io/spring-boot/4.0-SNAPSHOT/reference/io/rest-client.html#io.rest-client.httpservice&quot; data-og-url=&quot;https://docs.spring.io/spring-boot/4.0-SNAPSHOT/reference/io/rest-client.html#io.rest-client.httpservice&quot;&gt;&lt;a href=&quot;https://docs.spring.io/spring-boot/4.0-SNAPSHOT/reference/io/rest-client.html#io.rest-client.httpservice&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://docs.spring.io/spring-boot/4.0-SNAPSHOT/reference/io/rest-client.html#io.rest-client.httpservice&quot;&gt;
&lt;div class=&quot;og-image&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;Calling REST Services :: Spring Boot&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Both WebClient and RestClient support making versioned remote HTTP calls so that APIs can be evolved over time. Commonly this involves sending an HTTP header, a query parameter or URL path segment that indicates the version of the API that should be used.&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;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. Spring HTTP Client&lt;/h3&gt;
&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  Spring HTTP Client&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- Spring 환경에서 HTTP 통신을 위한 클라이언트들에 대해 간략하게 알아봅니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-style=&quot;style2&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 13.8372%;&quot;&gt;&lt;b&gt;Spring HTTP Client&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 12.6744%;&quot;&gt;&lt;b&gt;Client 호출 방식&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 43.2559%;&quot;&gt;&lt;b&gt;설명&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 15.1163%;&quot;&gt;&lt;b&gt;종속성 라이브러리&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 15%;&quot;&gt;&lt;b&gt;지원범위&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 13.8372%;&quot;&gt;&lt;b&gt;RestTemplate&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 12.6744%;&quot;&gt;동기식 / 블로킹&lt;/td&gt;
&lt;td style=&quot;width: 43.2559%;&quot;&gt;- 가장 오래된 전통적인 HTTP 클라이언트. 요청&amp;ndash;응답 동안 스레드를 점유하는 명령형 API이며, 코드가 단순하지만 확장성과 테스트성이 떨어짐. &lt;br /&gt;- 현재는 유지보수 모드로 신규 개발에는 권장되지 않음&lt;/td&gt;
&lt;td style=&quot;width: 15.1163%;&quot;&gt;spring-boot-starter-web (spring-web)&lt;/td&gt;
&lt;td style=&quot;width: 15%;&quot;&gt;Spring Boot 2.x+ (Spring 3~6)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 13.8372%;&quot;&gt;&lt;b&gt;RestClient&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 12.6744%;&quot;&gt;동기식&lt;/td&gt;
&lt;td style=&quot;width: 43.2559%;&quot;&gt;- RestTemplate의 공식 후속. 내부적으로는 WebClient 기반이지만 동기 호출 방식을 제공하는 최신 클라이언트. &lt;br /&gt;- Fluent API와 간결한 설정이 특징이며, Spring 팀이 권장하는 동기 HTTP 클라이언트&lt;/td&gt;
&lt;td style=&quot;width: 15.1163%;&quot;&gt;spring-boot-starter-web (spring-web)&lt;/td&gt;
&lt;td style=&quot;width: 15%;&quot;&gt;Spring Framework 6.1+ / Spring Boot 3.2+&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 13.8372%;&quot;&gt;&lt;b&gt;WebClient&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 12.6744%;&quot;&gt;비동기 / 논블로킹&lt;/td&gt;
&lt;td style=&quot;width: 43.2559%;&quot;&gt;- Reactive Streams 기반의 고성능 HTTP 클라이언트. 적은 스레드로 많은 요청을 처리할 수 있으며 스트리밍, 대량 트래픽 처리에 적합. 러닝 커브가 있으나 가장 확장성이 높음&lt;/td&gt;
&lt;td style=&quot;width: 15.1163%;&quot;&gt;spring-boot-starter-webflux (spring-webflux)&lt;/td&gt;
&lt;td style=&quot;width: 15%;&quot;&gt;Spring 5.0+ / Spring Boot 2.x+&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 13.8372%;&quot;&gt;&lt;b&gt;Spring Cloud OpenFeign&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 12.6744%;&quot;&gt;동기식 (기본)&lt;/td&gt;
&lt;td style=&quot;width: 43.2559%;&quot;&gt;- 인터페이스 + 어노테이션 기반의 선언형 HTTP 클라이언트. 내부 구현은 프록시로 생성되며, MSA 환경에서 서비스 간 통신에 최적화. &lt;br /&gt;- Retry, CircuitBreaker, LoadBalancer 등 Spring Cloud 기능과 강하게 결합&lt;/td&gt;
&lt;td style=&quot;width: 15.1163%;&quot;&gt;spring-cloud-starter-openfeign&lt;/td&gt;
&lt;td style=&quot;width: 15%;&quot;&gt;Spring Boot 2.x ~ 3.x (Spring Cloud 의존)&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;figure data-ke-type=&quot;opengraph&quot; data-og-title=&quot;[Java] Spring Boot Web 활용 : RestTemplate 이해하기&quot; data-ke-align=&quot;alignCenter&quot; data-og-description=&quot;해당 글에서는 RestTemplate에 대해 이해하고 활용 방법에 대해 확인해 봅니다.  [참고] Java에서 외부 통신을 하는 방법들에 대해 궁금하시면 아래의 글을 참고하시면 도움이 됩니다분류주제링크R&quot; data-og-host=&quot;adjh54.tistory.com&quot; data-og-source-url=&quot;https://adjh54.tistory.com/234&quot; data-og-image=&quot;https://blog.kakaocdn.net/dna/AxrE6/dJMb86nRwvn/AAAAAAAAAAAAAAAAAAAAABKzHmPma_vElNZ28iWJ9x_nb0BByzp1tMdQ2PfeZ22d/img.png?credential=yqXZFxpELC7KVnFOS48ylbz2pIh7yKj8&amp;amp;expires=1769871599&amp;amp;allow_ip=&amp;amp;allow_referer=&amp;amp;signature=m9gSTH4zP%2FgVdnI%2F0WbStlCV8Po%3D&quot; data-og-url=&quot;https://adjh54.tistory.com/234&quot;&gt;&lt;a href=&quot;https://adjh54.tistory.com/234&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://adjh54.tistory.com/234&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://blog.kakaocdn.net/dna/AxrE6/dJMb86nRwvn/AAAAAAAAAAAAAAAAAAAAABKzHmPma_vElNZ28iWJ9x_nb0BByzp1tMdQ2PfeZ22d/img.png?credential=yqXZFxpELC7KVnFOS48ylbz2pIh7yKj8&amp;amp;expires=1769871599&amp;amp;allow_ip=&amp;amp;allow_referer=&amp;amp;signature=m9gSTH4zP%2FgVdnI%2F0WbStlCV8Po%3D');&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;[Java] Spring Boot Web 활용 : RestTemplate 이해하기&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;해당 글에서는 RestTemplate에 대해 이해하고 활용 방법에 대해 확인해 봅니다.  [참고] Java에서 외부 통신을 하는 방법들에 대해 궁금하시면 아래의 글을 참고하시면 도움이 됩니다분류주제링크R&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;adjh54.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;figure data-ke-type=&quot;opengraph&quot; data-og-title=&quot;[Java] Spring Boot Webflux 이해하기 -2 : 활용하기&quot; data-ke-align=&quot;alignCenter&quot; data-og-description=&quot;해당 페이지에서는 Spring Boot Webflux를 이용하여 실제 구현하고 활용하는 방법과 WebClient를 이용한 다른 도메인 호출 방법에 대해 공유합니다.  [참고] Spring WebFlux 관련 글에 대해 궁금하시면 아&quot; data-og-host=&quot;adjh54.tistory.com&quot; data-og-source-url=&quot;https://adjh54.tistory.com/233&quot; data-og-image=&quot;https://blog.kakaocdn.net/dna/zJ5qz/dJMb82eG02R/AAAAAAAAAAAAAAAAAAAAAITKhfZpzo4tSpZLnVUruF2K6VQnsDthqUpLHjH3XwD2/img.png?credential=yqXZFxpELC7KVnFOS48ylbz2pIh7yKj8&amp;amp;expires=1769871599&amp;amp;allow_ip=&amp;amp;allow_referer=&amp;amp;signature=qIRc6fVYisGwXY4%2FhkrxZOMS5Gk%3D&quot; data-og-url=&quot;https://adjh54.tistory.com/233&quot;&gt;&lt;a href=&quot;https://adjh54.tistory.com/233&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://adjh54.tistory.com/233&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://blog.kakaocdn.net/dna/zJ5qz/dJMb82eG02R/AAAAAAAAAAAAAAAAAAAAAITKhfZpzo4tSpZLnVUruF2K6VQnsDthqUpLHjH3XwD2/img.png?credential=yqXZFxpELC7KVnFOS48ylbz2pIh7yKj8&amp;amp;expires=1769871599&amp;amp;allow_ip=&amp;amp;allow_referer=&amp;amp;signature=qIRc6fVYisGwXY4%2FhkrxZOMS5Gk%3D');&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;[Java] Spring Boot Webflux 이해하기 -2 : 활용하기&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;해당 페이지에서는 Spring Boot Webflux를 이용하여 실제 구현하고 활용하는 방법과 WebClient를 이용한 다른 도메인 호출 방법에 대해 공유합니다.  [참고] Spring WebFlux 관련 글에 대해 궁금하시면 아&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;adjh54.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;figure data-ke-type=&quot;opengraph&quot; data-og-title=&quot;[Java] Spring Cloud OpenFeign 이해하고 활용하기 -1 : 주요 개념 및 환경 구성, 활용 예시&quot; data-ke-align=&quot;alignCenter&quot; data-og-description=&quot;해당 글에서는 Spring Cloud OpenFeign을 활용하여 외부 통신을 활용하는 예시에 대해 알아봅니다.   [참고] Java에서 외부 통신을 하는 방법들에 대해 궁금하시면 아래의 글을 참고하시면 도움이 됩&quot; data-og-host=&quot;adjh54.tistory.com&quot; data-og-source-url=&quot;https://adjh54.tistory.com/616&quot; data-og-image=&quot;https://blog.kakaocdn.net/dna/bEp8JT/dJMb9kTWYEv/AAAAAAAAAAAAAAAAAAAAAB-q6C-HqJ4TBLi0AtXKBF3FHmrF63yuSzdUbb4ictzb/img.png?credential=yqXZFxpELC7KVnFOS48ylbz2pIh7yKj8&amp;amp;expires=1769871599&amp;amp;allow_ip=&amp;amp;allow_referer=&amp;amp;signature=LmNr2PftXIMXTD1Gwq627fYt9mc%3D&quot; data-og-url=&quot;https://adjh54.tistory.com/616&quot;&gt;&lt;a href=&quot;https://adjh54.tistory.com/616&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://adjh54.tistory.com/616&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://blog.kakaocdn.net/dna/bEp8JT/dJMb9kTWYEv/AAAAAAAAAAAAAAAAAAAAAB-q6C-HqJ4TBLi0AtXKBF3FHmrF63yuSzdUbb4ictzb/img.png?credential=yqXZFxpELC7KVnFOS48ylbz2pIh7yKj8&amp;amp;expires=1769871599&amp;amp;allow_ip=&amp;amp;allow_referer=&amp;amp;signature=LmNr2PftXIMXTD1Gwq627fYt9mc%3D');&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;[Java] Spring Cloud OpenFeign 이해하고 활용하기 -1 : 주요 개념 및 환경 구성, 활용 예시&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;해당 글에서는 Spring Cloud OpenFeign을 활용하여 외부 통신을 활용하는 예시에 대해 알아봅니다.   [참고] Java에서 외부 통신을 하는 방법들에 대해 궁금하시면 아래의 글을 참고하시면 도움이 됩&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;adjh54.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;h3 data-ke-size=&quot;size23&quot;&gt;2. 구조 확인&lt;/h3&gt;
&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  구조 확인&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 기존의 Spring Cloud에서 외부 통신으로 사용한 OpenFeign의 구조입니다. 이는 인터페이스 기반의 선언형 모델로 사용되고 있습니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;786&quot; data-origin-height=&quot;462&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dOzNW9/dJMcadHwEcT/hhW0U3h4obSRVPbwNnMBWK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dOzNW9/dJMcadHwEcT/hhW0U3h4obSRVPbwNnMBWK/img.png&quot; data-alt=&quot;https://adjh54.tistory.com/616&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dOzNW9/dJMcadHwEcT/hhW0U3h4obSRVPbwNnMBWK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdOzNW9%2FdJMcadHwEcT%2FhhW0U3h4obSRVPbwNnMBWK%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;786&quot; height=&quot;462&quot; data-origin-width=&quot;786&quot; data-origin-height=&quot;462&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://adjh54.tistory.com/616&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  HTTP Service Interface Clients를 확인해 봤을 때, 동일하게 인터페이스를 구성하고 이를 호출하여서 외부 HTTP 통신을 수행합니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1203&quot; data-origin-height=&quot;281&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cEjVHj/dJMcacIE9nv/MGIMrKK4Kd9RYmKbCcQThK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cEjVHj/dJMcacIE9nv/MGIMrKK4Kd9RYmKbCcQThK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cEjVHj/dJMcacIE9nv/MGIMrKK4Kd9RYmKbCcQThK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcEjVHj%2FdJMcacIE9nv%2FMGIMrKK4Kd9RYmKbCcQThK%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;1203&quot; height=&quot;281&quot; data-origin-width=&quot;1203&quot; data-origin-height=&quot;281&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. 활용&lt;/h3&gt;
&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  활용&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- Spring Boot 서버가 실행이 되고, Client가 특정 엔드포인트로 요청을 하고 Spring Boot 내부적으로 외부 API 호출을 하여 데이터를 가져오는 구조입니다.&lt;/b&gt;&lt;/blockquote&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;704&quot; data-origin-height=&quot;651&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c08KiZ/dJMcabJLkgb/377gws11dA2mT94ZWKiYDk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c08KiZ/dJMcabJLkgb/377gws11dA2mT94ZWKiYDk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c08KiZ/dJMcabJLkgb/377gws11dA2mT94ZWKiYDk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc08KiZ%2FdJMcabJLkgb%2F377gws11dA2mT94ZWKiYDk%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;704&quot; height=&quot;651&quot; data-origin-width=&quot;704&quot; data-origin-height=&quot;651&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;h4 data-ke-size=&quot;size20&quot;&gt;3.1. SpringBoot4InitApplication&lt;/h4&gt;
&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  SpringBoot4InitApplication&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 애플리케이션 서버가 실행될 때 최초 수행이 됩니다.&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;- @ImportHttpServices 어노테이션을 통해서, 패키지 별, 그룹 별로 지정된 클라이언트 프록시를 생성하고 해당 프록시를 빈으로 등록할 HTTP 서비스 유형(메서드를 포함하는 인터페이스)을 선언하기 위한 어노테이션입니다.&lt;/blockquote&gt;
&lt;pre class=&quot;java&quot;&gt;&lt;code&gt;package com.adjh.springboot4init;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.service.registry.ImportHttpServices;

@ImportHttpServices(basePackages = &quot;com.adjh.springboot4init.service.client&quot;)
@SpringBootApplication
public class SpringBoot4InitApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringBoot4InitApplication.class, args);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  [참고]&lt;/b&gt;&amp;nbsp;ImportHttpServices API Document&lt;/blockquote&gt;
&lt;figure data-ke-type=&quot;opengraph&quot; data-og-title=&quot;ImportHttpServices (Spring Framework 7.0.3 API)&quot; data-ke-align=&quot;alignCenter&quot; data-og-description=&quot;A list of HTTP Service types to include in the group.&quot; data-og-host=&quot;docs.spring.io&quot; data-og-source-url=&quot;https://docs.spring.io/spring-framework/docs/7.0.x/javadoc-api/org/springframework/web/service/registry/ImportHttpServices.html&quot; data-og-url=&quot;https://docs.spring.io/spring-framework/docs/7.0.x/javadoc-api/org/springframework/web/service/registry/ImportHttpServices.html&quot;&gt;&lt;a href=&quot;https://docs.spring.io/spring-framework/docs/7.0.x/javadoc-api/org/springframework/web/service/registry/ImportHttpServices.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://docs.spring.io/spring-framework/docs/7.0.x/javadoc-api/org/springframework/web/service/registry/ImportHttpServices.html&quot;&gt;
&lt;div class=&quot;og-image&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;ImportHttpServices (Spring Framework 7.0.3 API)&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;A list of HTTP Service types to include in the group.&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;br /&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3.2. EchoService : interface&lt;/h4&gt;
&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  EchoService : interface&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;- @HttpExchange를 통해서 특정 url을 통해서 통신을 수행합니다.&lt;br /&gt;- @PostExchange 어노테이션은 이 메서드가 HTTP POST 요청을 수행한다는 것을 선언적으로 정의합니다.&lt;/blockquote&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;package com.adjh.springboot4init.service.client;

import org.springframework.http.MediaType;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.service.annotation.HttpExchange;
import org.springframework.web.service.annotation.PostExchange;

import java.util.Map;

/**
 * HTTP Service Interface Clients 사용예시
 *
 * @author : leejonghoon
 * @fileName : EchoService
 * @since : 26. 1. 15.
 */
@Service
@HttpExchange(
        url = &quot;https://echo.zuplo.io&quot;,
        contentType = MediaType.APPLICATION_JSON_VALUE)
public interface EchoService {

    @PostExchange
    Map echo(@RequestBody Map&amp;lt;string, string=&quot;&quot;&amp;gt; message);
}
&amp;lt;/string,&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  [참고]&lt;/b&gt; @PostExchange&amp;nbsp;API Document&lt;/blockquote&gt;
&lt;figure data-ke-type=&quot;opengraph&quot; data-og-title=&quot;PostExchange (Spring Framework 7.0.3 API)&quot; data-ke-align=&quot;alignCenter&quot; data-og-host=&quot;docs.spring.io&quot; data-og-source-url=&quot;https://docs.spring.io/spring-framework/docs/7.0.x/javadoc-api/org/springframework/web/service/annotation/PostExchange.html&quot; data-og-url=&quot;https://docs.spring.io/spring-framework/docs/7.0.x/javadoc-api/org/springframework/web/service/annotation/PostExchange.html&quot;&gt;&lt;a href=&quot;https://docs.spring.io/spring-framework/docs/7.0.x/javadoc-api/org/springframework/web/service/annotation/PostExchange.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://docs.spring.io/spring-framework/docs/7.0.x/javadoc-api/org/springframework/web/service/annotation/PostExchange.html&quot;&gt;
&lt;div class=&quot;og-image&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;PostExchange (Spring Framework 7.0.3 API)&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;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3.3. EchoController&lt;/h4&gt;
&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  EchoController&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 클라이언트가 API 요청 시 이를 받아서 외부 API 호출을 하여 요청 값을 전달하고 응답값을 반환해 오는 Controller입니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;package com.adjh.springboot4init.controller;

import com.adjh.springboot4init.service.EchoCallerService;
import com.adjh.springboot4init.service.client.EchoService;
import org.springframework.web.bind.annotation.*;

import java.util.Map;

/**
 * 클라이언트의 요청 값을 통해 외부 API로 요청 값을 전달하고 결과를 응답하는 Controller 
 *
 * @author : leejonghoon
 * @fileName : EchoController
 * @since : 26. 1. 15.
 */
@RestController
@RequestMapping(&quot;/api/echo&quot;)
public class EchoController {

    private final EchoService echoService;

    public EchoController(EchoService echoService) {
        this.echoService = echoService;
    }

    @PostMapping(&quot;/test&quot;)
    public Map&amp;lt;?, ?&amp;gt; testEcho(@RequestBody Map&amp;lt;String, String&amp;gt; request) {
        return echoService.echo(request);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3.4. 추가 : EchoCallerService&lt;/h4&gt;
&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  추가 : EchoCallerService&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;- HttpExchange를 통해서 외부 통신을 한 이후에 후 처리를 하는 부분 래퍼로 구성하였습니다.&lt;/blockquote&gt;
&lt;pre class=&quot;kotlin&quot;&gt;&lt;code&gt;package com.adjh.springboot4init.service;

import com.adjh.springboot4init.service.client.EchoService;
import org.springframework.stereotype.Service;

import java.util.Map;

/**
 * HTTP Service Interface Clients 외부 통신 이후 전처리 및 로깅을 처리하는 호출 Wrapper
 *
 * @author : leejonghoon
 * @fileName : EchoCallerService
 * @since : 26. 1. 20.
 */
@Service
public class EchoCallerService {
    private final EchoService echoService;

    public EchoCallerService(EchoService echoService) {
        this.echoService = echoService;
    }

    public Map&amp;lt;?, ?&amp;gt; callEcho(Map&amp;lt;String, String&amp;gt; message) {
        return echoService.echo(message);
    }
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3.5. 정상적으로 외부 호출이 됨을 확인하였습니다.&lt;/h4&gt;
&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1174&quot; data-origin-height=&quot;891&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/TzS03/dJMcahQJd6Q/hGeS8V5auGBi5odsTN9rC1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/TzS03/dJMcahQJd6Q/hGeS8V5auGBi5odsTN9rC1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/TzS03/dJMcahQJd6Q/hGeS8V5auGBi5odsTN9rC1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FTzS03%2FdJMcahQJd6Q%2FhGeS8V5auGBi5odsTN9rC1%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;1174&quot; height=&quot;891&quot; data-origin-width=&quot;1174&quot; data-origin-height=&quot;891&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4) API Versioning&lt;/h2&gt;
&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  API Versioning&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 기존 Spring Boot 3까지는 /api/v1/users, /api/v2/users와 같이 URL Path 기반 버저닝을 사용하는 경우가 일반적이었습니다. 이 방식은 직관적이지만, 버전이 늘어날수록 Controller와 엔드포인트가 증가하여 라우팅 구조가 복잡해지고 유지보수가 어려워지는 단점이 있습니다.&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;-&amp;nbsp;Spring Boot 4 (Spring Framework 6.x 이후)에서는 요청&amp;nbsp;Header를 조건으로 사용하는 API 버저닝 방식을 보다 자연스럽게 적용할 수 있습니다.&lt;br /&gt;- 예를 들어, API 호출 시 API-Version 또는 X-API-Version과 같은 Header 값을 전달하고 해당 값에 따라 서로 다른 Controller 메서드를 매핑할 수 있습니다.&lt;br /&gt;- 이를 통해 URL 구조를 단순하게 유지하면서도 버전별 API를 명확하게 분리할 수 있어, 확장성과 유지보수 측면에서 더 유연한 설계를 할 수 있습니다.&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;920&quot; data-origin-height=&quot;223&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Hh3Km/dJMcaiB3H0K/vvbVfhZU2eEOhNEdXdE8s1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Hh3Km/dJMcaiB3H0K/vvbVfhZU2eEOhNEdXdE8s1/img.png&quot; data-alt=&quot;https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-4.0-Release-Notes&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Hh3Km/dJMcaiB3H0K/vvbVfhZU2eEOhNEdXdE8s1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FHh3Km%2FdJMcaiB3H0K%2FvvbVfhZU2eEOhNEdXdE8s1%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;920&quot; height=&quot;223&quot; data-origin-width=&quot;920&quot; data-origin-height=&quot;223&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-4.0-Release-Notes&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure data-ke-type=&quot;opengraph&quot; data-og-title=&quot;API Versioning in Spring&quot; data-ke-align=&quot;alignCenter&quot; data-og-description=&quot;In this 2nd blog post of the Road to GA series highlighting major features within the Spring portfolio for the next major versions to be released in November, I&amp;rsquo;m going to focus on the upcoming API Versioning support in Spring Framework 7. Introduction A&quot; data-og-host=&quot;spring.io&quot; data-og-source-url=&quot;https://spring.io/blog/2025/09/16/api-versioning-in-spring&quot; data-og-image=&quot;https://blog.kakaocdn.net/dna/quTWm/dJMb8U8NDPy/AAAAAAAAAAAAAAAAAAAAALzcc0REtmRh6AMdtRRPt_KQRuR8A8A_wWGbCleR1m5A/img.png?credential=yqXZFxpELC7KVnFOS48ylbz2pIh7yKj8&amp;amp;expires=1769871599&amp;amp;allow_ip=&amp;amp;allow_referer=&amp;amp;signature=H7WC7KCfstXhjfzf2fTiRLhiNL0%3D&quot; data-og-url=&quot;https://spring.io/blog/2025/09/16/api-versioning-in-spring&quot;&gt;&lt;a href=&quot;https://spring.io/blog/2025/09/16/api-versioning-in-spring&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://spring.io/blog/2025/09/16/api-versioning-in-spring&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://blog.kakaocdn.net/dna/quTWm/dJMb8U8NDPy/AAAAAAAAAAAAAAAAAAAAALzcc0REtmRh6AMdtRRPt_KQRuR8A8A_wWGbCleR1m5A/img.png?credential=yqXZFxpELC7KVnFOS48ylbz2pIh7yKj8&amp;amp;expires=1769871599&amp;amp;allow_ip=&amp;amp;allow_referer=&amp;amp;signature=H7WC7KCfstXhjfzf2fTiRLhiNL0%3D');&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;API Versioning in Spring&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;In this 2nd blog post of the Road to GA series highlighting major features within the Spring portfolio for the next major versions to be released in November, I&amp;rsquo;m going to focus on the upcoming API Versioning support in Spring Framework 7. Introduction A&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;br /&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. Server Handling&lt;/h3&gt;
&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  Server Handling&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- URL이나 파라미터가 아니라 HTTP Header 값으로 API 버전을 구분하는 방식입니다.&lt;/b&gt;&lt;br /&gt;- Spring MVC에게 API 버전을 API-Version 헤더를 기준으로 버전을 확인하는 방식을 의미합니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  Request Header 내에 API-Version 속성에 대해서 값을 1, 2, 3으로 전달하여서 버전을 확인하는 방식을 의미합니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;pre class=&quot;shell&quot; data-ke-language=&quot;shell&quot;&gt;&lt;code&gt;### Echo Test
POST http://localhost:8080/api/echo/test
Content-Type: application/json
API-Version: 1&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  아래의 코드에서도 useRequestHeader(&quot;API-Version&quot;)를 통해서 header의 속성을 지정하고 확인하도록 하였습니다.&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;- API 내에서는 @XXMapping의 속성 값인 version에 따라서 분기를 수행하였습니다. API 내의 [POST] /api/echo/test로 호출을 할 때 Header의 &amp;ldquo;API-Version&amp;rdquo; 속성 내에 &amp;ldquo;1&amp;rdquo;, &amp;ldquo;2&amp;rdquo;로 분기를 하였을 때 각기 다른 API가 호출이 됩니다.&lt;/blockquote&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;// 설정 API 구성
@Configuration
public class WebConfiguration implements WebMvcConfigurer {

	@Override
	public void configureApiVersioning(ApiVersionConfigurer configurer) {
		configurer.useRequestHeader(&quot;API-Version&quot;);
	}
}

@RestController
@RequestMapping(value = &quot;/api/echo&quot;)
public class EchoController {

    private final EchoService echoService;

    public EchoController(EchoService echoService) {
        this.echoService = echoService;
    }

    @PostMapping(value = &quot;/test&quot;, version = &quot;1&quot;)
    public Map&amp;lt;?, ?&amp;gt; testEcho(@RequestBody Map&amp;lt;String, String&amp;gt; request) {
        System.out.println(&quot;버전 1 API가 호출이 되었습니다.&quot;);
        return echoService.echo(request);
    }

    @PostMapping(value = &quot;/test&quot;, version = &quot;2&quot;)
    public Map&amp;lt;?, ?&amp;gt; testEcho2(@RequestBody Map&amp;lt;String, String&amp;gt; request) {
        System.out.println(&quot;버전 2 API가 호출이 되었습니다.&quot;);
        return echoService.echo(request);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  결과 확인&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 아래와 같이 동일한 API 내에 Header를 API-Version: 1 또는 API-Version: 2로 변경하였을 때, 각기 다른 API가 호출이 됩니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1850&quot; data-origin-height=&quot;881&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lmRiV/dJMcacBTiKk/izTZhPzcaoITMFaTOFQKK1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lmRiV/dJMcacBTiKk/izTZhPzcaoITMFaTOFQKK1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lmRiV/dJMcacBTiKk/izTZhPzcaoITMFaTOFQKK1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlmRiV%2FdJMcacBTiKk%2FizTZhPzcaoITMFaTOFQKK1%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;1850&quot; height=&quot;881&quot; data-origin-width=&quot;1850&quot; data-origin-height=&quot;881&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1874&quot; data-origin-height=&quot;892&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bC8MSo/dJMb99LUGEc/YyCT8EZr4A1zAKDELsy3W0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bC8MSo/dJMb99LUGEc/YyCT8EZr4A1zAKDELsy3W0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bC8MSo/dJMb99LUGEc/YyCT8EZr4A1zAKDELsy3W0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbC8MSo%2FdJMb99LUGEc%2FYyCT8EZr4A1zAKDELsy3W0%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;1874&quot; height=&quot;892&quot; data-origin-width=&quot;1874&quot; data-origin-height=&quot;892&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;h3 data-ke-size=&quot;size23&quot;&gt;2. Client Support&lt;/h3&gt;
&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  Client Support&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 클라이언트 입장에서 API 버전을 지정하여 요청을 수행해야 할 수 있습니다. 이를 위해 ApiVersionInserter요청에 버전을 한 번만 삽입하는 방법을 정의하는 설정이 있으며, 이후 요청을 할 때는 버전 값만 지정하면 됩니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2.1. RestClient 방식&lt;/h4&gt;
&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  RestClient 방식&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- RestClient를 이용하여서 외부 API를 호출할 때에도, 전달받는 API내에서도 .apiVersionInserter()를 통해서 header 내에 API-Version을 전달하도록 구성할 수 있습니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;// RestClient 생성자 구성
RestClient client = RestClient.builder()
		.baseUrl(&quot;&amp;lt;http://localhost:8080&amp;gt;&quot;)
		.apiVersionInserter(ApiVersionInserter.useHeader(&quot;API-Version&quot;))
		.build();
		
		
// RestClient의 값을 지정(1.1)
Account account = client.get().uri(&quot;/accounts/1&quot;)
	.apiVersion(1.1)
	.retrieve()
	.body(Account.class);
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2.2. HTTP Service Interface Clients 방식&lt;/h4&gt;
&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  HTTP Service Interface Clients 방식&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- HTTP Service Interface Clients를 이용하여서 외부 API를 호출할 때도, 버전 관리를 지원합니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;@HttpExchange(&quot;/accounts&quot;)
public interface AccountService {

	@GetExchange(url = &quot;/{id}&quot;, version = &quot;1.1&quot;)
	Account getAccount(@PathVariable int id);

}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;5) JmsClient&lt;/h2&gt;
&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  JmsClient&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;- JMS(Java Message Service) 내에서 기존 JmsTemplate, JmsMessagingTemplate를 사용했던 부분에 대해서 Spring에서 RestClient / HttpService Clients 스타일에 맞춘 JmsClient API가 추가되었다고 합니다.&lt;br /&gt;- 기존에 사용하고 있는 JmsTemplate, JmsMessagingTemplate는 유지가 된다고 합니다.&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;898&quot; data-origin-height=&quot;129&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bdFZa4/dJMcagYzckc/KWnK4PCYKgu2g59vHh4QKK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bdFZa4/dJMcagYzckc/KWnK4PCYKgu2g59vHh4QKK/img.png&quot; data-alt=&quot;https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-4.0-Release-Notes&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bdFZa4/dJMcagYzckc/KWnK4PCYKgu2g59vHh4QKK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbdFZa4%2FdJMcagYzckc%2FKWnK4PCYKgu2g59vHh4QKK%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;898&quot; height=&quot;129&quot; data-origin-width=&quot;898&quot; data-origin-height=&quot;129&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-4.0-Release-Notes&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;figure data-ke-type=&quot;opengraph&quot; data-og-title=&quot;JmsClient (Spring Framework 7.0.3 API)&quot; data-ke-align=&quot;alignCenter&quot; data-og-description=&quot;A fluent JmsClient with common send and receive operations against a JMS destination, dealing with Spring's common Message or with payload values. This is effectively an alternative to JmsMessagingTemplate, also delegating to Spring's JmsTemplate for perfo&quot; data-og-host=&quot;docs.spring.io&quot; data-og-source-url=&quot;https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/jms/core/JmsClient.html&quot; data-og-url=&quot;https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/jms/core/JmsClient.html&quot;&gt;&lt;a href=&quot;https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/jms/core/JmsClient.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/jms/core/JmsClient.html&quot;&gt;
&lt;div class=&quot;og-image&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;JmsClient (Spring Framework 7.0.3 API)&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;A fluent JmsClient with common send and receive operations against a JMS destination, dealing with Spring's common Message or with payload values. This is effectively an alternative to JmsMessagingTemplate, also delegating to Spring's JmsTemplate for perfo&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;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. JMS(Java Message Service)&lt;/h3&gt;
&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  JMS(Java Message Service)&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;- JMS(Java Message Service)는 자바 애플리케이션 간 비동기 메시지 통신을 위한 표준 API입니다. 직접적인 메서드 호출(RPC, REST)과 달리 메시지를 매개로 시스템을 느슨하게 결합시키는 것이 핵심 목적입니다. 메시지 큐(메시징 시스템)를 자바에서 사용하기 위한 표준 인터페이스(API)입니다.&lt;br /&gt;&lt;br /&gt;- 서로 다른 애플리케이션 또는 컴포넌트가 메시지(Message)를 통해 비동기적으로 통신하도록 지원합니다.&lt;/blockquote&gt;
&lt;figure data-ke-type=&quot;opengraph&quot; data-og-title=&quot;JMS (Java Message Service) :: Spring Framework&quot; data-ke-align=&quot;alignCenter&quot; data-og-description=&quot;JMS can be roughly divided into two areas of functionality, namely the production and consumption of messages. The JmsTemplate class is used for message production and synchronous message receipt. For asynchronous receipt similar to Jakarta EE&amp;rsquo;s message-&quot; data-og-host=&quot;docs.spring.io&quot; data-og-source-url=&quot;https://docs.spring.io/spring-framework/reference/integration/jms.html&quot; data-og-url=&quot;https://docs.spring.io/spring-framework/reference/integration/jms.html&quot;&gt;&lt;a href=&quot;https://docs.spring.io/spring-framework/reference/integration/jms.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://docs.spring.io/spring-framework/reference/integration/jms.html&quot;&gt;
&lt;div class=&quot;og-image&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;JMS (Java Message Service) :: Spring Framework&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;JMS can be roughly divided into two areas of functionality, namely the production and consumption of messages. The JmsTemplate class is used for message production and synchronous message receipt. For asynchronous receipt similar to Jakarta EE&amp;rsquo;s message-&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;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;[ 더 알아보기 ]&lt;/b&gt;&lt;br /&gt;&lt;b&gt;&lt;br /&gt;&lt;/b&gt;&lt;b&gt; &amp;nbsp;JMS랑 Message Queue랑은 무슨 차이가 있을까?&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;- Message Queue의 경우는 비동기 메시징 구조를 의미하며, JMS의 경우는 이러한 메시지 큐를 사용하기 위한 &amp;lsquo;표준 인터페이스(API 규격)&amp;rsquo;을 의미합니다. 특히, 메시지 큐 종류 중에서도 JMS 계열인 ActiveMQ, IBM MQ의 경우는 JMS를 이용합니다.&lt;br /&gt;&lt;br /&gt;- RabbitMQ의 경우는 AMQP라는 Advanced Message Queuing Protocol 프로토콜을 이용하여서 메시지큐를 다룹니다.&lt;/blockquote&gt;
&lt;figure data-ke-type=&quot;opengraph&quot; data-og-title=&quot;[Java] Spring Boot AMQP RabbitMQ 이해하기 -1 : 구조 및 종류&quot; data-ke-align=&quot;alignCenter&quot; data-og-description=&quot;해당 글에서는 Spring Boot RabbitMQ에 대해 이해하고 활용하는 방법에 대해서 확인합니다.&amp;nbsp;&amp;nbsp;  [참고]&amp;nbsp;RabbitMQ에 대해 궁금하시면 아래의 글이 도움이 됩니다분류링크Spring Boot AMQP RabbitMQ -1 :&amp;nbsp;구조 &quot; data-og-host=&quot;adjh54.tistory.com&quot; data-og-source-url=&quot;https://adjh54.tistory.com/284&quot; data-og-image=&quot;https://blog.kakaocdn.net/dna/u2GNH/dJMb9jgrbth/AAAAAAAAAAAAAAAAAAAAACPysZICWUTBX9FkLCB4GWyqsnwlBSeqbv2EbeXJBbXA/img.png?credential=yqXZFxpELC7KVnFOS48ylbz2pIh7yKj8&amp;amp;expires=1769871599&amp;amp;allow_ip=&amp;amp;allow_referer=&amp;amp;signature=UuTrcPv6SIPHhQYft2752Gy7jCk%3D&quot; data-og-url=&quot;https://adjh54.tistory.com/284&quot;&gt;&lt;a href=&quot;https://adjh54.tistory.com/284&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://adjh54.tistory.com/284&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://blog.kakaocdn.net/dna/u2GNH/dJMb9jgrbth/AAAAAAAAAAAAAAAAAAAAACPysZICWUTBX9FkLCB4GWyqsnwlBSeqbv2EbeXJBbXA/img.png?credential=yqXZFxpELC7KVnFOS48ylbz2pIh7yKj8&amp;amp;expires=1769871599&amp;amp;allow_ip=&amp;amp;allow_referer=&amp;amp;signature=UuTrcPv6SIPHhQYft2752Gy7jCk%3D');&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;[Java] Spring Boot AMQP RabbitMQ 이해하기 -1 : 구조 및 종류&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;해당 글에서는 Spring Boot RabbitMQ에 대해 이해하고 활용하는 방법에 대해서 확인합니다.&amp;nbsp;&amp;nbsp;  [참고]&amp;nbsp;RabbitMQ에 대해 궁금하시면 아래의 글이 도움이 됩니다분류링크Spring Boot AMQP RabbitMQ -1 :&amp;nbsp;구조&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;adjh54.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;h3 data-ke-size=&quot;size23&quot;&gt;2. JmsTemplate&lt;/h3&gt;
&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  JmsTemplate&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- JmsTemplate은 JMS를 사용하기 위한 Spring의 핵심 템플릿으로, Connection, Session, MessageProducer 등의 생성&amp;middot;관리와 메시지 전송 과정을 자동으로 처리해 주는 도구입니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;pre class=&quot;java&quot;&gt;&lt;code&gt;// 템플릿 구성(ConnectionFactory)
@Bean
public ConnectionFactory connectionFactory() {
    ActiveMQConnectionFactory cf = new ActiveMQConnectionFactory();
    cf.setBrokerURL(&quot;tcp://localhost:61616&quot;);
    cf.setUserName(&quot;admin&quot;);
    cf.setPassword(&quot;admin&quot;);
    return cf;
}

// 메시지 송신 측
@Service
public class OrderProducer {

    private final JmsTemplate jmsTemplate;

    public OrderProducer(JmsTemplate jmsTemplate) {
        this.jmsTemplate = jmsTemplate;
    }

    public void sendOrder(String orderId) {
        jmsTemplate.convertAndSend(
            &quot;queue.order&quot;,
            &quot;주문 ID = &quot; + orderId
        );
    }
}

// 메시지 수신 측
@Component
public class OrderConsumer {

    @JmsListener(destination = &quot;queue.order&quot;)
    public void receive(String message) {
        System.out.println(&quot;수신 메시지: &quot; + message);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. JmsMessagingTemplate&lt;/h3&gt;
&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  JmsMessagingTemplate&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- Spring이 제공하는 JMS 전용 메시지 전송 헬퍼로, JmsTemplate을 감싸서 메시지를 객체 기반으로 쉽게 보내도록 도와주는 추상화 계층입니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;// 송신측
@Service
public class PaymentProducer {

    private final JmsMessagingTemplate jmsMessagingTemplate;

    public PaymentProducer(JmsMessagingTemplate jmsMessagingTemplate) {
        this.jmsMessagingTemplate = jmsMessagingTemplate;
    }

    public void sendPayment(String paymentId) {
        jmsMessagingTemplate.convertAndSend(
            &quot;queue.payment&quot;,
            paymentId
        );
    }
}

// 수신 측
@Component
public class PaymentConsumer {

    @JmsListener(destination = &quot;queue.payment&quot;)
    public void receive(
        String payload,
        @Header(&quot;traceId&quot;) String traceId
    ) {
        System.out.println(&quot;payload = &quot; + payload);
        System.out.println(&quot;traceId = &quot; + traceId);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4. JmsClient&lt;/h3&gt;
&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  JmsClient&lt;/b&gt;&lt;br /&gt;&lt;b&gt;&lt;br /&gt;&lt;/b&gt;&lt;b&gt;- 기존 JmsTemplate, JmsMessagingTemplate와 다르게 플루언트 인터페이스(Fluent Interface) 형태로 구성되어 있습니다. &lt;/b&gt;&lt;br /&gt;- 소스코드의 가독성을 높이기 위한 목적으로 사용되며 인터페이스 안에 도메인 특화 언어(DSL)를 이용하여 작성합니다.&lt;/blockquote&gt;
&lt;pre class=&quot;java&quot;&gt;&lt;code&gt;@Service
public class OrderProducer {

    private final JmsClient jmsClient;

    public OrderProducer(JmsClient jmsClient) {
        this.jmsClient = jmsClient;
    }

    public void sendOrder(String orderId) {
        jmsClient.send(&quot;queue.order&quot;)
                 .withBody(&quot;주문 ID = &quot; + orderId);
    }
}

@Service
public class OrderReceiver {

    private final JmsClient jmsClient;

    public OrderReceiver(JmsClient jmsClient) {
        this.jmsClient = jmsClient;
    }

    public String receive() {
        return jmsClient.receive(&quot;queue.order&quot;, String.class);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  [참고]&lt;/b&gt; 플루언트 인터페이스 패턴&lt;/blockquote&gt;
&lt;figure data-ke-type=&quot;opengraph&quot; data-og-title=&quot;[Java/디자인 패턴] 메서드 체이닝(Method Chaining), 플루언트 인터페이스(Fluent Interface), 빌더 패턴(Buil&quot; data-ke-align=&quot;alignCenter&quot; data-og-description=&quot;해당 글에서는 메서드 체이닝(Method Chaining), 플루언트 인터페이스(Fluent Interface), 빌더 패턴(Builder Pattern)에 대해서 알아봅니다. 1) 메서드 체이닝(Method Chaining)  메서드 체이닝(Method Chaining)- 여러 &quot; data-og-host=&quot;adjh54.tistory.com&quot; data-og-source-url=&quot;https://adjh54.tistory.com/440&quot; data-og-image=&quot;https://blog.kakaocdn.net/dna/n8eCu/dJMb9lk1aWO/AAAAAAAAAAAAAAAAAAAAAA-fRl_M-hATBOgjyOSKSVNrRRa5q0JZvLhGoTyCRF2W/img.png?credential=yqXZFxpELC7KVnFOS48ylbz2pIh7yKj8&amp;amp;expires=1769871599&amp;amp;allow_ip=&amp;amp;allow_referer=&amp;amp;signature=HJmGsSMRRw6qen70PS4FFcdeTdU%3D&quot; data-og-url=&quot;https://adjh54.tistory.com/440&quot;&gt;&lt;a href=&quot;https://adjh54.tistory.com/440&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://adjh54.tistory.com/440&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://blog.kakaocdn.net/dna/n8eCu/dJMb9lk1aWO/AAAAAAAAAAAAAAAAAAAAAA-fRl_M-hATBOgjyOSKSVNrRRa5q0JZvLhGoTyCRF2W/img.png?credential=yqXZFxpELC7KVnFOS48ylbz2pIh7yKj8&amp;amp;expires=1769871599&amp;amp;allow_ip=&amp;amp;allow_referer=&amp;amp;signature=HJmGsSMRRw6qen70PS4FFcdeTdU%3D');&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;[Java/디자인 패턴] 메서드 체이닝(Method Chaining), 플루언트 인터페이스(Fluent Interface), 빌더 패턴(Buil&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;해당 글에서는 메서드 체이닝(Method Chaining), 플루언트 인터페이스(Fluent Interface), 빌더 패턴(Builder Pattern)에 대해서 알아봅니다. 1) 메서드 체이닝(Method Chaining)  메서드 체이닝(Method Chaining)- 여러&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;adjh54.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;br /&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;6) OpenTelemetry starter&lt;/h2&gt;
&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  OpenTelemetry starter&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- Spring Boot 4 버전에서는 spring-boot-starter로 spring-boot-starter-opentelemetry가 추가되었습니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;965&quot; data-origin-height=&quot;123&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/UO04U/dJMcagjYRTo/iZLfhX8dkTAWNH8cOJKMf0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/UO04U/dJMcagjYRTo/iZLfhX8dkTAWNH8cOJKMf0/img.png&quot; data-alt=&quot;https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-4.0-Release-Notes&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/UO04U/dJMcagjYRTo/iZLfhX8dkTAWNH8cOJKMf0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FUO04U%2FdJMcagjYRTo%2FiZLfhX8dkTAWNH8cOJKMf0%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;965&quot; height=&quot;123&quot; data-origin-width=&quot;965&quot; data-origin-height=&quot;123&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-4.0-Release-Notes&lt;/figcaption&gt;
&lt;/figure&gt;
&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;1. spring-boot-starter-opentelemetry&lt;/h3&gt;
&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  spring-boot-starter-opentelemetry&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- OpenTelemetry(OTel)는 관측성(Observability)을 위한 표준 스펙 + 라이브러리 집합이며, 내부 동작에 대해서 관측합니다.&lt;/b&gt;&lt;br /&gt;- 일반적으로 &amp;lsquo;Trace&amp;rsquo;를 생성하고 &amp;lsquo;Metric&amp;rsquo;을 수집합니다. 그리고 &amp;lsquo;Log&amp;rsquo;로 수집된 데이터를 OTLP로 전송하는 구조를 가집니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  OpenTelemetry(OTel) 수집 대상&lt;/b&gt;&lt;/blockquote&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-style=&quot;style2&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;수집 대상&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;수집 정보&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Trace&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;요청 하나가 여러 서비스/API를 거쳐가는 흐름&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Metric&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;CPU, 메모리, 요청 수, 응답 시간 등 수치 데이터&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Log&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;로그 (Spring에서는 아직 로그는 간접 연동 위주)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  Grafana UI Trace&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;- 수집된 정보를 Grafana를 통해서 UI로 확인할 수 있습니다.&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2048&quot; data-origin-height=&quot;564&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c0qK3I/dJMcadU2Z4r/fTWT90iJOUW5Wdnt6JDEJK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c0qK3I/dJMcadU2Z4r/fTWT90iJOUW5Wdnt6JDEJK/img.png&quot; data-alt=&quot;https://spring.io/blog/2025/11/18/opentelemetry-with-spring-boot&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c0qK3I/dJMcadU2Z4r/fTWT90iJOUW5Wdnt6JDEJK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc0qK3I%2FdJMcadU2Z4r%2FfTWT90iJOUW5Wdnt6JDEJK%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;2048&quot; height=&quot;564&quot; data-origin-width=&quot;2048&quot; data-origin-height=&quot;564&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://spring.io/blog/2025/11/18/opentelemetry-with-spring-boot&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  Grafana UI Logs&amp;nbsp;분석&lt;/b&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2048&quot; data-origin-height=&quot;646&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/wdwt5/dJMcafej2FX/YShA6k0dQtzWeVjW2AJJI0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/wdwt5/dJMcafej2FX/YShA6k0dQtzWeVjW2AJJI0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/wdwt5/dJMcafej2FX/YShA6k0dQtzWeVjW2AJJI0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fwdwt5%2FdJMcafej2FX%2FYShA6k0dQtzWeVjW2AJJI0%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;2048&quot; height=&quot;646&quot; data-origin-width=&quot;2048&quot; data-origin-height=&quot;646&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. OTLP(OpenTelemetry Protocol)&lt;/h3&gt;
&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  OTLP(OpenTelemetry Protocol)&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- OTLP(OpenTelemetry Protocol)는 OpenTelemetry에서 정의한 표준 텔레메트리 전송 프로토콜입니다.&lt;/b&gt;&lt;br /&gt;- 이는 애플리케이션에서 수집한 관측 데이터(Observability data)를 외부 시스템으로 일관된 방식으로 전달하기 위해 만들어졌습니다.&lt;br /&gt;- 수집한 관측 데이터를 Jaeger, Tempo, Prometheus, Datadog, New Relic로 전송을 합니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;&amp;nbsp;  Spring Boot 내에서 전송을 하는 경우 OpenTelemetry Collector를 통해서 각각의 값을 OTLP을 통해서 전송을 합니다&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;- Metric, Trace, Logs를 각각에 맞게 수집이 되고 Grafana를 통해서 시각화를 수행합니다.&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;333&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bSjCfY/dJMcadHwEF2/4YSY7WrC3Yq3VHQyr6zkv1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bSjCfY/dJMcadHwEF2/4YSY7WrC3Yq3VHQyr6zkv1/img.png&quot; data-alt=&quot;https://dev.to/siisee11/grafana-opentelemetry-starter-df4&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bSjCfY/dJMcadHwEF2/4YSY7WrC3Yq3VHQyr6zkv1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbSjCfY%2FdJMcadHwEF2%2F4YSY7WrC3Yq3VHQyr6zkv1%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;800&quot; height=&quot;333&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;333&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://dev.to/siisee11/grafana-opentelemetry-starter-df4&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&lt;b&gt;오늘도&amp;nbsp;감사합니다.&amp;nbsp; &lt;/b&gt;&lt;/p&gt;</description>
      <category>공통/Trend</category>
      <category>API Versioning</category>
      <category>HTTP Service Interface Clients</category>
      <category>java</category>
      <category>JmsClient</category>
      <category>OpenTelemetry starter</category>
      <category>spring boot4</category>
      <category>spring7</category>
      <author>adjh54</author>
      <guid isPermaLink="true">https://adjh54.tistory.com/734</guid>
      <comments>https://adjh54.tistory.com/734#entry734comment</comments>
      <pubDate>Wed, 21 Jan 2026 19:49:30 +0900</pubDate>
    </item>
    <item>
      <title>[Opensource] Gitleaks 이해하고 활용하기 -1 : homebrew를 이용한 간단 스캔 방법</title>
      <link>https://adjh54.tistory.com/733</link>
      <description>&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;해당 글에서는 OpenSouce 중 Gitleaks를 이해하고 이를 이용하여 민감정보를 찾는 간단한 스캔방법에 대해 알아봅니다&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;
&lt;figure contenteditable=&quot;false&quot; data-ke-type=&quot;emoticon&quot; data-ke-align=&quot;alignCenter&quot; data-emoticon-type=&quot;face&quot; data-emoticon-name=&quot;073&quot; data-emoticon-isanimation=&quot;false&quot; data-emoticon-src=&quot;https://t1.daumcdn.net/keditor/emoticon/face/large/073.png&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/keditor/emoticon/face/large/073.png&quot; width=&quot;80&quot; /&gt;&lt;/figure&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1) Gitleaks&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  Gitleaks&lt;/b&gt;&lt;br /&gt;&lt;b&gt;&lt;br /&gt;- Git 저장소(Repository) 내에 포함된 민감 정보(API Key, 비밀번호, 토큰, 인증서, 개인키 등)를 탐지하는 보안 도구를 의미합니다. 민감정보를 탐지하여 실수로 소스코드 내에 커밋이 된 내용들을 빠르게 찾아낼 수 있습니다.&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;- 특히, 이미 원격 저장소 내에 Commit &amp;amp; Push 작업을 수행하면 저장소 내에 이력으로 모든 기록이 남기 때문에, 잘못 올린 한 번 잘못 올린 민감정보는 파일을 삭제하더라도 히스토리에 남아있습니다.&lt;br /&gt;- 그렇기에, 원격 저장소 내에 민감정보를 올리지 않도록 해야 합니다.&lt;/blockquote&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;    ○
    │╲
    │ ○
    ○ ░
    ░    gitleaks
&lt;/code&gt;&lt;/pre&gt;
&lt;figure id=&quot;og_1768797921988&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 - gitleaks/gitleaks: Find secrets with Gitleaks  &quot; data-og-description=&quot;Find secrets with Gitleaks  . Contribute to gitleaks/gitleaks development by creating an account on GitHub.&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/gitleaks/gitleaks&quot; data-og-url=&quot;https://github.com/gitleaks/gitleaks&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/WqKrU/dJMb9bvV6qw/PYH5iRJ6szIaEEiu0j6fmk/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/lu8to/dJMb9lL5oFO/Qs9lxsdzn1xrCwODvGP3QK/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/gitleaks/gitleaks&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/gitleaks/gitleaks&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/WqKrU/dJMb9bvV6qw/PYH5iRJ6szIaEEiu0j6fmk/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/lu8to/dJMb9lL5oFO/Qs9lxsdzn1xrCwODvGP3QK/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 - gitleaks/gitleaks: Find secrets with Gitleaks  &lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Find secrets with Gitleaks  . Contribute to gitleaks/gitleaks development by creating an account on GitHub.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  [참고]&lt;/b&gt; 아래와 같이 Commit &amp;amp; Push로 원격 저장소에 올라간 파일을 삭제하더라도, Git 히스토리 내에서는 민감정보가 남아있습니다.&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;668&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/boXAzd/dJMcab31GhZ/Q6JdWTh9OiGYq1zDV0mqb1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/boXAzd/dJMcab31GhZ/Q6JdWTh9OiGYq1zDV0mqb1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/boXAzd/dJMcab31GhZ/Q6JdWTh9OiGYq1zDV0mqb1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FboXAzd%2FdJMcab31GhZ%2FQ6JdWTh9OiGYq1zDV0mqb1%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;1280&quot; height=&quot;668&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;668&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;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt; [참고]&lt;/b&gt; 이전에 원격 저장소에 Commit &amp;amp; Push 된 히스토리를 삭제하는 방법은 아래를 이용하여서 제거하였습니다&lt;/blockquote&gt;
&lt;figure id=&quot;og_1768798030531&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;[Github] 원격 저장소의 Commit 내용 지우기 : API 키 노출 시 해결 방법&quot; data-og-description=&quot;해당 글에서는 public Repository 내에 민감한 API 키가 올라간 경우 해당 내용을 없애는 방법에 대해 확인해 봅니다.1) API 키 노출 상황&amp;nbsp;1. GitGuardian - OpenAI API Key exposed on GitHub  문제 상황- GitGuardian으&quot; data-og-host=&quot;adjh54.tistory.com&quot; data-og-source-url=&quot;https://adjh54.tistory.com/382&quot; data-og-url=&quot;https://adjh54.tistory.com/382&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/rTRH2/dJMb87NP547/AjA0JiRC8sK1ypo6Lk9LSK/img.png?width=800&amp;amp;height=447&amp;amp;face=0_0_800_447,https://scrap.kakaocdn.net/dn/cjzogB/dJMb9iaKW5t/gpL3fgBFhJLKJny5EuO1Ck/img.png?width=800&amp;amp;height=447&amp;amp;face=0_0_800_447,https://scrap.kakaocdn.net/dn/vi4NN/dJMb85vIDsb/utanWqwqWk7v5BnDQ45R61/img.png?width=1795&amp;amp;height=937&amp;amp;face=0_0_1795_937&quot;&gt;&lt;a href=&quot;https://adjh54.tistory.com/382&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://adjh54.tistory.com/382&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/rTRH2/dJMb87NP547/AjA0JiRC8sK1ypo6Lk9LSK/img.png?width=800&amp;amp;height=447&amp;amp;face=0_0_800_447,https://scrap.kakaocdn.net/dn/cjzogB/dJMb9iaKW5t/gpL3fgBFhJLKJny5EuO1Ck/img.png?width=800&amp;amp;height=447&amp;amp;face=0_0_800_447,https://scrap.kakaocdn.net/dn/vi4NN/dJMb85vIDsb/utanWqwqWk7v5BnDQ45R61/img.png?width=1795&amp;amp;height=937&amp;amp;face=0_0_1795_937');&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] 원격 저장소의 Commit 내용 지우기 : API 키 노출 시 해결 방법&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;해당 글에서는 public Repository 내에 민감한 API 키가 올라간 경우 해당 내용을 없애는 방법에 대해 확인해 봅니다.1) API 키 노출 상황&amp;nbsp;1. GitGuardian - OpenAI API Key exposed on GitHub  문제 상황- GitGuardian으&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;adjh54.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;h3 data-ke-size=&quot;size23&quot;&gt;1. 사용 목적&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  사용 목적&lt;/b&gt;&lt;br /&gt;&lt;b&gt;&lt;br /&gt;- Gitleak을 사용하는 목적은 아래와 같은 문제점을 사전에 방지할 수 있습니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style2&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;문제점&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;Gitleak 해결 방법&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;개발자가 실수로 키 커밋&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;커밋 전에 자동 탐지하여 push 차단할 수 있습니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;이미 히스토리에 포함된 키&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;전체 Repo 스캔하여 위치 표시가 됩니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;팀원들이 기준 없이 비밀정보 관리&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;.gitleaks.toml 규칙으로 일관된 정책 유지할 수 있습니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;CI/CD에서 Secret 유출 위험&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;GitHub Actions/GitLab CI에서 자동 검증을 수행 할 수 있습니다.&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;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;[더 알아보기]&lt;/b&gt;&lt;br /&gt;&lt;b&gt;&lt;br /&gt;  커밋 전에 자동 탐지를 해서 push 자체를 차단한다는 게 뭘까?&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 커밋 전에 사전에 자동으로 탐지하여 commit, push 자체를 차단하는 pre-commit, pre-push 작업을 수행할 수 있습니다.&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;1. pre-commit&lt;/b&gt;&lt;br /&gt;- 커밋(commit) 직전에 실행이 됩니다. 스테이지 된 파일의 경우 커밋을 실패하는 경우 커밋을 수행하지 못하게 막습니다. &lt;br /&gt;ex) 코드 포맷팅, lint, 테스트, Gitleaks 검사 등에 사용이 됩니다.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;2. pre-push&lt;/b&gt;&lt;br /&gt;- 푸시(push) 직전에 실행이 됩니다. 로컬 저장소에서 원격 저장소로 파일을 올리는 push 과정에서 푸시 자체를 수행하지 못하게 막습니다. &lt;br /&gt;ex) 민감 정보가 있는 커밋을 원격으로 올리는 것을 방지하고 싶은 경우에 사용이 됩니다. &lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 결론적으로, 사전에 정의해 둔 Git Hook을 통해서 각각의 단계를 감지하여서 사전 검증을 수행하는 방식으로 진행을 합니다.&lt;/b&gt;&lt;/blockquote&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;720&quot; data-origin-height=&quot;321&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cFLDb6/dJMcafZEuW0/nsLbBXxqekgI2dOelmpcK1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cFLDb6/dJMcafZEuW0/nsLbBXxqekgI2dOelmpcK1/img.png&quot; data-alt=&quot;https://azure.github.io/AKS-DevSecOps-Workshop/modules/Module2/Lab-2c.html&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cFLDb6/dJMcafZEuW0/nsLbBXxqekgI2dOelmpcK1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcFLDb6%2FdJMcafZEuW0%2FnsLbBXxqekgI2dOelmpcK1%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;720&quot; height=&quot;321&quot; data-origin-width=&quot;720&quot; data-origin-height=&quot;321&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://azure.github.io/AKS-DevSecOps-Workshop/modules/Module2/Lab-2c.html&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;[더 알아보기]&lt;/b&gt;&lt;br /&gt;&lt;b&gt;&lt;br /&gt; &amp;nbsp;pre-commit과 pre-push 과정은 언제 발생하는 것일까?&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;- Git Hook 과정에서 수행이 됩니다. Git이 내부적으로 이미 준비해 둔 특정 타이밍에 해당되는 스크립트로 pre-commit과 pre-push를 감지하여서 수행이 됩니다.&lt;br /&gt;- .git 내에 hook 파일을 구성하여서 hook을 구성할 수 있습니다.&lt;/blockquote&gt;
&lt;figure id=&quot;og_1768798220491&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;Git - Git Hooks&quot; data-og-description=&quot;여기서 한가지 알아둘 점은 저장소를 Clone 해도 클라이언트 훅은 복사되지 않는다는 점이다. 만든 정책이 반드시 적용되도록 하려면 서버 훅을 이용해야만 하며 작성은 정책 구현하기 부분을 참&quot; data-og-host=&quot;git-scm.com&quot; data-og-source-url=&quot;https://git-scm.com/book/ko/v2/Git%EB%A7%9E%EC%B6%A4-Git-Hooks&quot; data-og-url=&quot;https://git-scm.com/book/ko/v2/Git%EB%A7%9E%EC%B6%A4-Git-Hooks&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://git-scm.com/book/ko/v2/Git%EB%A7%9E%EC%B6%A4-Git-Hooks&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://git-scm.com/book/ko/v2/Git%EB%A7%9E%EC%B6%A4-Git-Hooks&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;Git - Git Hooks&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;여기서 한가지 알아둘 점은 저장소를 Clone 해도 클라이언트 훅은 복사되지 않는다는 점이다. 만든 정책이 반드시 적용되도록 하려면 서버 훅을 이용해야만 하며 작성은 정책 구현하기 부분을 참&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;git-scm.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;h3 data-ke-size=&quot;size23&quot;&gt;2. 탐지 대상&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  탐지 대상&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;- 기본적으로 Gitleaks에서 탐지하는 &amp;lsquo;민감정보&amp;rsquo; 대상입니다. 이뿐만 아니라 gitleaks의 toml을 이용하여서 특정 패턴, 특정 민감 정보, 규칙을 정의하여서 추가적 탐지 및 제외가 가능합니다.&lt;/blockquote&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style2&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 47.4419%;&quot;&gt;&lt;b&gt;항목&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 52.3256%;&quot;&gt;&lt;b&gt;설명&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 47.4419%;&quot;&gt;&lt;b&gt;AWS Access Key / Secret Key&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 52.3256%;&quot;&gt;AWS 계정에 접근하기 위한 인증 키. Secret Key가 유출되면 S3, EC2, RDS 등 전체 리소스가 탈취되거나 삭제될 수 있음.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 47.4419%;&quot;&gt;&lt;b&gt;GitHub Personal Access Token(PAT)&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 52.3256%;&quot;&gt;GitHub API 사용 및 private repo 접근을 위한 인증 토큰. 유출되면 코드 유출, 저장소 삭제, 악성코드 삽입 가능.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 47.4419%;&quot;&gt;&lt;b&gt;Database URL &amp;amp; Password&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 52.3256%;&quot;&gt;DB 접속 주소, 계정, 비밀번호. 유출되면 데이터 조회&amp;middot;변조&amp;middot;삭제 가능. 개인정보 포함 DB일수록 위험 증가.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 47.4419%;&quot;&gt;&lt;b&gt;OAuth Token (Google, Kakao, Naver 등)&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 52.3256%;&quot;&gt;외부 서비스 계정을 대신 사용하도록 허용하는 토큰. 악용 시 사용자 정보 조회, API 사용료 폭증 위험.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 47.4419%;&quot;&gt;&lt;b&gt;Private Key (id_rsa 등)&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 52.3256%;&quot;&gt;SSH 접속용 개인키. 유출되면 서버에 직접 로그인 가능. 비밀번호보다 훨씬 위험.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 47.4419%;&quot;&gt;&lt;b&gt;JWT Secret / Signing Key&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 52.3256%;&quot;&gt;JWT 서명에 사용되는 비밀 값. 유출되면 공격자가 임의의 JWT 생성 가능 &amp;rarr; 모든 인증 우회 가능.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 47.4419%;&quot;&gt;&lt;b&gt;Slack Webhook URL&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 52.3256%;&quot;&gt;Slack 특정 채널에 메시지를 보낼 수 있는 URL. 유출되면 스팸/피싱 메시지 자동 발송에 악용됨.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 47.4419%;&quot;&gt;&lt;b&gt;사용자 정의 정규식 기반 Secret (Custom Regex)&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 52.3256%;&quot;&gt;기업/프로젝트마다 특정 패턴으로 생성되는 고유 키들(API Key, 내부 인증 토큰 등). 유출 시 시스템 전체가 위험해질 수 있음.&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;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;[ 더 알아보기 ]&lt;/b&gt;&lt;br /&gt;&lt;b&gt;&lt;br /&gt;  Gitleaks Configuration File(=toml)&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;- Gitleaks가 &amp;ldquo;어떤 정보를 어떻게 탐지할지 정의하는 규칙 파일&amp;rdquo;입니다.&lt;br /&gt;- 즉, Gitleaks의 탐지 엔진을 직접 커스터마이징 하는 설정 파일을 의미합니다.&lt;/blockquote&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;title = &quot;프로젝트에 맞는 Gitleaks 설정&quot;

[[rules]]         # 탐지 규칙(여러 개 가능): API Key, Token, Password 패턴 정의
[allowlist]       # 제외 규칙: 특정 파일, 경로, 문자열 무시
[whitelist]       # 제외할 파일/폴더/정규식
[global]          # 전체 글로벌 설정
&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;h2 data-ke-size=&quot;size26&quot;&gt;2) Gitleaks 설치 및 활용 -1: homebrew&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 로컬 설치&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  로컬 설치&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;- MacOS 내에서 homebrew를 이용하여서 간단하게 설치를 합니다.&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;figure id=&quot;og_1768798339575&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;gitleaks&quot; data-og-description=&quot;Homebrew&amp;rsquo;s package index&quot; data-og-host=&quot;formulae.brew.sh&quot; data-og-source-url=&quot;https://formulae.brew.sh/formula/gitleaks&quot; data-og-url=&quot;https://formulae.brew.sh/formula/gitleaks&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cXxfQl/dJMb9g44VQJ/WEhv85ez5l74eX1KbfXPGK/img.png?width=256&amp;amp;height=256&amp;amp;face=0_0_256_256,https://scrap.kakaocdn.net/dn/GWyOB/dJMb8Xj9c1F/2IeJDT6apgNXKaV0xiHt31/img.png?width=256&amp;amp;height=256&amp;amp;face=0_0_256_256&quot;&gt;&lt;a href=&quot;https://formulae.brew.sh/formula/gitleaks&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://formulae.brew.sh/formula/gitleaks&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cXxfQl/dJMb9g44VQJ/WEhv85ez5l74eX1KbfXPGK/img.png?width=256&amp;amp;height=256&amp;amp;face=0_0_256_256,https://scrap.kakaocdn.net/dn/GWyOB/dJMb8Xj9c1F/2IeJDT6apgNXKaV0xiHt31/img.png?width=256&amp;amp;height=256&amp;amp;face=0_0_256_256');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;gitleaks&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Homebrew&amp;rsquo;s package index&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;formulae.brew.sh&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;h2 data-ke-size=&quot;size26&quot;&gt;3) Gitleak 스캔&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  Gitleak 스캔&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;- Git 저장소 또는 로컬 파일들 안에 있는 민감정보(Secrets)를 자동으로 탐지하는 보안 스캐너를 의미합니다. 이를 통해서 API Key, Password, Token, Private Key, 인증 정보가 실수로 커밋되었을 때에 대한 탐지를 수행합니다.&lt;/blockquote&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style2&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;항목&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;현재 브랜치 내 소스코만 스캔 방식&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;모든 히스토리 포함 방식&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;현재 디렉토리의 모든 파일&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;스캔&lt;/td&gt;
&lt;td&gt;스캔&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;서브폴더 모든 파일&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;스캔&lt;/td&gt;
&lt;td&gt;스캔&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;.git 내부(소스 히스토리)&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;스캔 안 함&lt;/td&gt;
&lt;td&gt;스캔&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;과거 커밋(전체 history)&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;스캔 안 함&lt;/td&gt;
&lt;td&gt;스캔&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;stash&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;스캔 안 함&lt;/td&gt;
&lt;td&gt;스캔 안 함&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;submodule&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;스캔&lt;/td&gt;
&lt;td&gt;스캔&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;LFS&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;파일 자체는 스캔(단, 히스토리 X)&lt;/td&gt;
&lt;td&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;h3 data-ke-size=&quot;size23&quot;&gt;1. 현재 브랜치 내 소스코만 스캔(Git Commit &amp;amp; Push 이력 제외)&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  현재 브랜치 내 소스코만 스캔(Git Commit &amp;amp; Push 이력 제외)&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 현재 디렉터리 전체를 스캔하지만, Git 히스토리(과거 커밋 기록)는 완전히 무시하고 현재 파일만 검사합니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style2&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 51.6279%;&quot;&gt;&lt;b&gt;항목 스캔&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 48.2558%;&quot;&gt;&lt;b&gt;범위&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 51.6279%;&quot;&gt;&lt;b&gt;현재 디렉토리의 모든 파일&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 48.2558%;&quot;&gt;스캔&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 51.6279%;&quot;&gt;&lt;b&gt;서브폴더 모든 파일&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 48.2558%;&quot;&gt;스캔&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 51.6279%;&quot;&gt;&lt;b&gt;.git 내부(소스 히스토리)&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 48.2558%;&quot;&gt;스캔 안 함&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 51.6279%;&quot;&gt;&lt;b&gt;과거 커밋(전체 history)&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 48.2558%;&quot;&gt;스캔 안 함&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 51.6279%;&quot;&gt;&lt;b&gt;stash&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 48.2558%;&quot;&gt;스캔 안 함&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 51.6279%;&quot;&gt;&lt;b&gt;submodule&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 48.2558%;&quot;&gt;스캔&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 51.6279%;&quot;&gt;&lt;b&gt;LFS&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 48.2558%;&quot;&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;pre class=&quot;shell&quot; data-ke-language=&quot;shell&quot;&gt;&lt;code&gt;# 현재 브랜치 내 소스코만 스캔(Git Commit &amp;amp; Push 이력 제외)
$ gitleaks detect --source . --no-git&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1160&quot; data-origin-height=&quot;604&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b56o5C/dJMcadgrvwo/mYrkEvXllnDmqbvJbcIxm0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b56o5C/dJMcadgrvwo/mYrkEvXllnDmqbvJbcIxm0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b56o5C/dJMcadgrvwo/mYrkEvXllnDmqbvJbcIxm0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb56o5C%2FdJMcadgrvwo%2FmYrkEvXllnDmqbvJbcIxm0%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;1160&quot; height=&quot;604&quot; data-origin-width=&quot;1160&quot; data-origin-height=&quot;604&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;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;[ 더 알아보기 ]&lt;/b&gt;&lt;br /&gt;&lt;b&gt;&lt;br /&gt; &amp;nbsp;Git history가 뭘까&lt;/b&gt;&lt;br /&gt;- 과거에 Commit &amp;amp; Push 한 기록들 전체를 의미합니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. 전체 스캔(Git Commit &amp;amp; Push 이력 포함)&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  전체 스캔(Git Commit &amp;amp; Push 이력 포함)&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 현재 디렉터리 전체 파일을 스캔합니다. 즉, 프로젝트 루트(.) 안에 모든 파일을 확인하고 수행합니다.&lt;/b&gt;&lt;br /&gt;- 숨김파일도 포함하여서 스캔을 합니다.&lt;/blockquote&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style2&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;항목&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;스캔 여부&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;현재 디렉토리의 모든 파일&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;스캔&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;서브폴더 모든 파일&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;스캔&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;.git 내부(소스 히스토리)&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;스캔&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;과거 커밋(전체 history)&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;스캔&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;stash&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;스캔 안 함&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;submodule&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;스캔&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;LFS&lt;/b&gt;&lt;/td&gt;
&lt;td&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;pre class=&quot;shell&quot;&gt;&lt;code&gt;# 전체 스캔
$ gitleaks detect --source .
&lt;/code&gt;&lt;/pre&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;1161&quot; data-origin-height=&quot;601&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/8voM5/dJMcahwpJC2/N1TK3jouUuQk4Lp8EKKz81/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/8voM5/dJMcahwpJC2/N1TK3jouUuQk4Lp8EKKz81/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/8voM5/dJMcahwpJC2/N1TK3jouUuQk4Lp8EKKz81/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F8voM5%2FdJMcahwpJC2%2FN1TK3jouUuQk4Lp8EKKz81%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;1161&quot; height=&quot;601&quot; data-origin-width=&quot;1161&quot; data-origin-height=&quot;601&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;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;[ 더 알아보기 ]&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt; &amp;nbsp;git 히스토리를 포함하지 않은 게 포함한 것보다 많이 나왔는데 왜 그럴까?&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;- gitignore로 인해 git 히스토리에는 포함하지 않지만 로컬에는 포함하는 경우에 이에 해당이 될 수 있습니다.&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;h3 data-ke-size=&quot;size23&quot;&gt;3. 전체 스캔 결과 JSON 파일로 저장&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  전체 스캔 결과 JSON 파일로 저장&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;- Commit &amp;amp; Push에 대한 Git 이력을 gitleaks-report.json 파일로 루트 경로에 저장을 합니다.&lt;/blockquote&gt;
&lt;pre class=&quot;dsconfig&quot;&gt;&lt;code&gt;# 현재 브랜치 내 소스코만 스캔(Git Commit &amp;amp; Push 이력 제외) 결과를 gitleaks-report.json 파일로 루트 경로에 저장
$ gitleaks detect --source . --no-git --report-path gitleaks-report.json

# 전체 스캔(Git Commit &amp;amp; Push 이력 포함) 결과를 gitleaks-report.json 파일로 루트 경로에 저장
$ gitleaks detect --source . --report-path gitleaks-report.json
&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;h4 data-ke-size=&quot;size20&quot;&gt;3.1. 전체 스캔 결과 및 JSON 파일 확인&lt;/h4&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;style3&quot;&gt;&lt;b&gt;  전체 스캔 결과 및 JSON 파일 확인&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;- 루트 경로에 스캔 정보를 확인할 수 있고, 리포트를 json으로 받아볼 수 있게끔 명령어를 실행하였고, 프로젝트의 루트 경로에 gitleaks-report.json 파일이 생성이 됩니다.&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1751&quot; data-origin-height=&quot;614&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b6fWUP/dJMcafeiLFh/9vkcGk6Ein5kRAlHoZz6zk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b6fWUP/dJMcafeiLFh/9vkcGk6Ein5kRAlHoZz6zk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b6fWUP/dJMcafeiLFh/9vkcGk6Ein5kRAlHoZz6zk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb6fWUP%2FdJMcafeiLFh%2F9vkcGk6Ein5kRAlHoZz6zk%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;1751&quot; height=&quot;614&quot; data-origin-width=&quot;1751&quot; data-origin-height=&quot;614&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3.2. JSON 파일 확인&lt;/h4&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;style3&quot;&gt;&lt;b&gt;  JSON 파일 확인&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;- json 파일을 확인하였을 때 아래와 같이 민감정보에 해당하는 정보들이 탐지가 되었습니다.&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1710&quot; data-origin-height=&quot;965&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/wxemy/dJMcaihMY5r/SzK1GyCKfUhuHKKgrDPXzK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/wxemy/dJMcaihMY5r/SzK1GyCKfUhuHKKgrDPXzK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/wxemy/dJMcaihMY5r/SzK1GyCKfUhuHKKgrDPXzK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fwxemy%2FdJMcaihMY5r%2FSzK1GyCKfUhuHKKgrDPXzK%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;1710&quot; height=&quot;965&quot; data-origin-width=&quot;1710&quot; data-origin-height=&quot;965&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 data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;오늘도&amp;nbsp;감사합니다.&amp;nbsp; &lt;/b&gt;&lt;/p&gt;</description>
      <category>OpenSource/Gitleaks</category>
      <category>Git 민감정보</category>
      <category>Git 민감정보 탐지</category>
      <category>Gitleaks</category>
      <category>Gitleaks homebrew 설치</category>
      <category>Gitleaks json 파일</category>
      <category>Gitleaks 명령어</category>
      <category>Gitleaks 전체 탐색</category>
      <category>Gitleaks 탐색</category>
      <author>adjh54</author>
      <guid isPermaLink="true">https://adjh54.tistory.com/733</guid>
      <comments>https://adjh54.tistory.com/733#entry733comment</comments>
      <pubDate>Mon, 19 Jan 2026 20:00:30 +0900</pubDate>
    </item>
    <item>
      <title>[Java] Spring Cloud Vault 이해하고 활용하기 -4 : Database Secret Engine 활용</title>
      <link>https://adjh54.tistory.com/732</link>
      <description>&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;해당 글에서는 Spring Cloud Vault를 활용하여서 Database Secret Engine을 이용하는 방법에 대해 알아봅니다.&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;
&lt;figure contenteditable=&quot;false&quot; data-ke-type=&quot;emoticon&quot; data-ke-align=&quot;alignCenter&quot; data-emoticon-type=&quot;face&quot; data-emoticon-name=&quot;073&quot; data-emoticon-isanimation=&quot;false&quot; data-emoticon-src=&quot;https://t1.daumcdn.net/keditor/emoticon/face/large/073.png&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/keditor/emoticon/face/large/073.png&quot; width=&quot;80&quot; /&gt;&lt;/figure&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;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  [참고]&lt;/b&gt; Vault에 대해 궁금하시면 아래의 글을 참고하시면 도움이 됩니다.&lt;/blockquote&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 196px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style2&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 65.9303%; height: 20px;&quot;&gt;&lt;b&gt;분류&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 33.9535%; height: 20px;&quot;&gt;URI&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 65.9303%; height: 20px;&quot;&gt;&lt;b&gt;[OpenSource] Vault 이해하기 -1 : 이론, 구성요소, 처리과정&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 33.9535%; height: 20px;&quot;&gt;&lt;a href=&quot;https://adjh54.tistory.com/475&quot;&gt;https://adjh54.tistory.com/475&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 65.9303%; height: 20px;&quot;&gt;&lt;b&gt;[Docker] Dockerfile을 이용한 Vault 배포 환경 구성 및 실행방법&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 33.9535%; height: 20px;&quot;&gt;&lt;a href=&quot;https://adjh54.tistory.com/415&quot;&gt;https://adjh54.tistory.com/415&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 40px;&quot;&gt;
&lt;td style=&quot;width: 65.9303%; height: 40px;&quot;&gt;&lt;b&gt;[Java] Spring Cloud Vault 이해하고 활용하기-1 : 초기 환경, KV 구성 및 Root Token 인증 방식&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 33.9535%; height: 40px;&quot;&gt;&lt;a href=&quot;https://adjh54.tistory.com/728&quot;&gt;https://adjh54.tistory.com/728&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 65.9303%; height: 20px;&quot;&gt;&lt;b&gt;[Java] Spring Cloud Vault 이해하고 활용하기-2 : 정책 기반 토큰 발급 및 인증 방식&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 33.9535%; height: 20px;&quot;&gt;&lt;a href=&quot;https://adjh54.tistory.com/729&quot;&gt;https://adjh54.tistory.com/729&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 65.9303%; height: 20px;&quot;&gt;&lt;b&gt;[Java] Spring Cloud Vault 이해하고 활용하기-3 : AppRole 인증방식&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 33.9535%; height: 20px;&quot;&gt;&lt;a href=&quot;https://adjh54.tistory.com/731&quot;&gt;https://adjh54.tistory.com/731&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 65.9303%; height: 20px;&quot;&gt;&lt;b&gt;[Java] Spring Cloud Vault 이해하고 활용하기-4 : Database Secret Engine 활용&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 33.9535%; height: 20px;&quot;&gt;&lt;a href=&quot;https://adjh54.tistory.com/732&quot;&gt;https://adjh54.tistory.com/732&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 36px;&quot;&gt;
&lt;td style=&quot;width: 65.9303%; height: 36px;&quot;&gt;&lt;b&gt;소스코드 Repository&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 33.9535%; height: 36px;&quot;&gt;&lt;a href=&quot;https://github.com/adjh54ir/blog-codes/tree/main/spring-boot3-vault&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://github.com/adjh54ir/blog-codes/tree/main/spring-boot3-vault&lt;/a&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;h2 data-ke-size=&quot;size26&quot;&gt;1) Vault&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  Vault&lt;/b&gt;&lt;br /&gt;&lt;b&gt;&lt;br /&gt;- HashCorp 사에서 만든 Vault는 다양한 환경에서 애플리케이션의 외부 비밀 속성(예: 데이터베이스 비밀번호, API 키 등)을 외부화된 구성으로 중앙에서 관리할 수 있습니다.&lt;/b&gt;&lt;br /&gt;- Spring Boot 환경에서 Vault로부터 시크릿 정보를 읽어오며 Valut에 시크릿 정보를 쓰는 것도 가능합니다. 이러한 방식으로 애플리케이션의 중요한 정보는 코드에서 분리되어 보안이 보장됩니다.&lt;br /&gt;- 기밀정보의 동적인 제공, 중앙 집중식 시크릿 관리, 즉각적인 액세스 제어, 감사 추적 기능 등을 제공하여, 기업의 보안 정책을 준수하는 데 도움이 됩니다.&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1200&quot; data-origin-height=&quot;690&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/3pVmE/dJMcabJHOK1/QvWrHTHRkVImcCtUp1bUak/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/3pVmE/dJMcabJHOK1/QvWrHTHRkVImcCtUp1bUak/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/3pVmE/dJMcabJHOK1/QvWrHTHRkVImcCtUp1bUak/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F3pVmE%2FdJMcabJHOK1%2FQvWrHTHRkVImcCtUp1bUak%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;1200&quot; height=&quot;690&quot; data-origin-width=&quot;1200&quot; data-origin-height=&quot;690&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;h3 data-ke-size=&quot;size23&quot;&gt;1. Vault의 특징&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 138px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 23.0233%; height: 20px;&quot;&gt;&lt;b&gt;특징&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 76.8604%; height: 20px;&quot;&gt;&lt;b&gt;설명&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 23.0233%; height: 20px;&quot;&gt;&lt;b&gt;기밀정보의 동적 제공&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 76.8604%; height: 20px;&quot;&gt;애플리케이션이 필요에 따라 비밀정보를 요청하고, Vault는 요청된 정보를 제공합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 23.0233%; height: 20px;&quot;&gt;&lt;b&gt;중앙 집중식 시크릿 관리&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 76.8604%; height: 20px;&quot;&gt;모든 애플리케이션의 시크릿 정보를 중앙에서 관리할 수 있습니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 40px;&quot;&gt;
&lt;td style=&quot;width: 23.0233%; height: 40px;&quot;&gt;&lt;b&gt;즉각적인 액세스 제어&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 76.8604%; height: 40px;&quot;&gt;애플리케이션이 Vault에 접근 할 수 있는 &amp;lsquo;특정 토큰 아이디&amp;rsquo;를 기반으로 Vault 서비스를 호출하고, Vault는 해당 '특정 토큰 아이디'를 수신한 뒤 요청된 '비밀정보'를 제공합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 23.0233%; height: 20px;&quot;&gt;&lt;b&gt;감사 추적 기능&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 76.8604%; height: 20px;&quot;&gt;Vault는 모든 액세스 요청을 로깅하여 감사를 위한 추적이 가능합니다.&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;h3 data-ke-size=&quot;size23&quot;&gt;2. Vault 인증 방식&lt;/h3&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;style3&quot;&gt;&lt;b&gt;&amp;nbsp;  Vault 인증 방식&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;- Vault에 접근하기 위한 인증 방식을 의미합니다. Root Token, 정책 기반 토큰 (Policy-based Token), AppRole 등의 인증 방식을 통해서 Vault를 접근합니다.&lt;/blockquote&gt;
&lt;table id=&quot;2e016d47-b05b-803e-bf3e-ff39ac2f0ca3&quot; style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style2&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 20.6976%;&quot;&gt;&lt;b&gt;인증 방식&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 79.1861%;&quot;&gt;&lt;b&gt;설명&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;2e016d47-b05b-8011-ab2a-d67a36c2b39d&quot;&gt;
&lt;td style=&quot;width: 20.6976%;&quot;&gt;&lt;b&gt;Root Token&lt;/b&gt;&lt;/td&gt;
&lt;td id=&quot;KZLa&quot; style=&quot;width: 79.1861%;&quot;&gt;모든 권한을 가진 &amp;lsquo;루트 권한 토큰&amp;rsquo;을 이용하는 인증 방식이며, 정책, 인증 방식, 시크릿 엔진, 모든 데이터 접근 가능한 인증 방식입니다.&lt;br /&gt;절대적인 권한을 가지고 있기에, 직접적으로 애플리케이션 레벨에서 인증방식으로 사용되지 않습니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;2e016d47-b05b-807a-8311-f3be68adc4ca&quot;&gt;
&lt;td style=&quot;width: 20.6976%;&quot;&gt;&lt;b&gt;정책 기반 토큰&lt;/b&gt;&lt;br /&gt;&lt;b&gt;(Policy-based Token)&lt;/b&gt;&lt;/td&gt;
&lt;td id=&quot;KZLa&quot; style=&quot;width: 79.1861%;&quot;&gt;정책(policy)에 의해 접근 범위가 제한된 &amp;lsquo;토큰&amp;rsquo;을 이용하는 인증 방식이며, 정책 범위 별로 강제 제한된 권한을 부여하고 접근하는 인증방식 입니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;2e016d47-b05b-80fc-b705-e75f52d8c418&quot;&gt;
&lt;td style=&quot;width: 20.6976%;&quot;&gt;&lt;b&gt;AppRole&lt;/b&gt;&lt;/td&gt;
&lt;td id=&quot;KZLa&quot; style=&quot;width: 79.1861%;&quot;&gt;Role 별로 지정된 정책(Policy)에 따라서 접근 범위가 제한되며, &amp;lsquo;RoleID, Secret ID&amp;rsquo;에 따라 &amp;lsquo;토큰&amp;rsquo;을 발급받아서 접근 가능한 인증 방식입니다. 해당 Role은 운영환경 혹은 팀별로 접근을 제어할 수 있으며, 사용자는 Role Id, Secret Id를 통해 인증만 수행하고, 이후 토큰에 대해서는 머신이 이를 갱신/관리하기에 별도의 Token 재발급 과정을 수행하지 않습니다.&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;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. Vault 수행 과정&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;596&quot; data-origin-height=&quot;221&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qdCaT/dJMcadtU0Fi/dO00ny00K3uDKZqiCx2ke0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qdCaT/dJMcadtU0Fi/dO00ny00K3uDKZqiCx2ke0/img.png&quot; data-alt=&quot;https://www.baeldung.com/spring-boot-3-1-connectiondetails-abstraction&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qdCaT/dJMcadtU0Fi/dO00ny00K3uDKZqiCx2ke0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FqdCaT%2FdJMcadtU0Fi%2FdO00ny00K3uDKZqiCx2ke0%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;596&quot; height=&quot;221&quot; data-origin-width=&quot;596&quot; data-origin-height=&quot;221&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://www.baeldung.com/spring-boot-3-1-connectiondetails-abstraction&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  Vault 수행 과정&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;1. Spring Boot &amp;rarr; Hashicorp Vault: Secret Request(비밀정보 요청)&lt;/b&gt; &lt;br /&gt;- Spring Boot 애플리케이션에서는 서버를 실행할 때, Valut에 접근할 수 있는 &amp;lsquo;특정 토큰 아이디&amp;rsquo;(Secret Request)&amp;rsquo; 를 기반으로 Vault 서비스를 호출하여 &amp;lsquo;비밀 정보(Secret)&amp;rsquo;를 검색합니다.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;2. Hashicorp Vault &amp;rarr; Spring Boot: Secret Response(비밀정보 응답)&lt;/b&gt; &lt;br /&gt;-&amp;nbsp;Valut 서비스는 Spring Boot 애플리케이션의 &amp;lsquo;특정 토큰 아이디&amp;rsquo;(Secret Request)&amp;rsquo;를 수신하고 요청된 &amp;lsquo;비밀 정보(Secret)&amp;rsquo;를 전달합니다.&lt;br /&gt;- 이 단계를 통해서 애플리케이션은 필요한 시크릿 정보를 안전하게 받아올 수 있습니다.&lt;br /&gt;&lt;b&gt;&lt;br /&gt;3. Spring Boot &amp;rarr; Remote Service : Connection Request(커넥션 요청)&lt;/b&gt; &lt;br /&gt;-&amp;nbsp;Spring Boot 애플리케이션이 원격 서비스와 연결을 시도하는 과정을 의미합니다.&lt;br /&gt;- 이 단계에서 애플리케이션은 Vault에서 안전하게 가져온 비밀 정보(예: 데이터베이스 비밀번호, API 키 등)를 사용하여 원격 서비스에 연결 요청을 보냅니다.&lt;br /&gt;- 이렇게 함으로써, 중요한 정보를 코드 내에 직접 작성하거나 노출시키지 않아도 되어 보안을 더 효과적으로 관리할 수 있습니다.&lt;br /&gt;&lt;b&gt;&lt;br /&gt;4. Remote Service &amp;rarr; Spring Boot : Connection Response(커넥션 연결)&lt;/b&gt; &lt;br /&gt;-&amp;nbsp;Vault 서비스가 Spring Boot 애플리케이션의 Secret Request를 수신하고, 요청된 '비밀 정보'를 애플리케이션으로 전송하는 단계입니다.&lt;br /&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;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2) Database Secret Engine&lt;/h2&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;style3&quot;&gt;&lt;b&gt;  Database Secret Engine&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- Database Secret Engine은 Vault가 DB 관리자 권한으로 접속하여 Role에 정의된 SQL 정책을 기반으로 요청 시마다 TTL(Time To Live)이 있는 임시 DB 계정을 생성하고, 만료 시 자동으로 폐기하는 동적 자격 증명 시스템을 의미합니다.&lt;/b&gt;&lt;br /&gt;- Vault는 데이터베이스에 관리자 권한을 가진 계정으로 접속하여 Role에 정의된 SQL 정책에 따라 사용자 생성 및 권한을 부여하며, TTL 만료 시 해당 계정을 자동으로 폐기한다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 설정 과정&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  설정 과정&lt;/b&gt;&lt;br /&gt;&lt;b&gt;&lt;br /&gt;1. Vaul내에 'DB 관리자 계정 설정'합니다.&lt;/b&gt;&lt;br /&gt;- Vault 내에서 '데이터베이스 관리자 계정(Admin)'을 설정합니다.&lt;br /&gt;- 해당 설정 과정을 통해서 추후에 관리자 계정을 통하여 Vault에서 동적인 임의의 계정을 생성하는 데 사용됩니다.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;2. Vault에 'DB Role'을 생성하고 정의합니다.&lt;br /&gt;&lt;/b&gt;- Vault 내에서 '데이터베이스 접근 권한(Role)'을 설정합니다.&lt;br /&gt;- 이 접근 권한 과정에서 특정 권한에 대해 읽기만(read-only) 혹은 쓰기(Write) 권한 등.. 생성되는 사용자에 대한 데이터베이스 권한에 대해 제한을 둘 수 있습니다.&lt;b&gt;&lt;br /&gt;&lt;br /&gt;3. 클라이언트에서는 Vault로 자격증명(&lt;b&gt;credential) 요청합니다.&lt;br /&gt;&lt;/b&gt;&lt;/b&gt;- 클라이언트(애플리케이션)에서 Vault 접속을 위한 자격증명을 수행을 합니다.&lt;br /&gt;- 클라이언트는 Vault로 접속을 하기 위해 사전에 구현된 인증 방식(Policy Token, AppRole)을 통해 인증을 수행합니다.&lt;b&gt;&lt;br /&gt;&lt;/b&gt;&lt;br /&gt;&lt;b&gt;4. Vault에서 DB 사용자 생성합니다.&lt;br /&gt;&lt;/b&gt;- 자격 증명이 성공하면, Vault는 Role에 맞는 권한 및 TTL(Time To Live)을 가진 DB 사용자를 생성합니다.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;5. Vault가 관리자 권한으로 DB에 접속합니다.&lt;/b&gt;&lt;br /&gt;- 생성된 동적인 사용자 정보(username, password)를 기반으로 데이터베이스에 접속을 합니다.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;7. TTL 만료 시 자동 폐기&lt;/b&gt;&lt;br /&gt;- 지정된 시간이 지난 애플리케이션의 사용자를 DB 접속 계정을 자동 폐기합니다.&lt;/blockquote&gt;
&lt;figure id=&quot;og_1768193167423&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;Database secrets engine | Vault | HashiCorp Developer&quot; data-og-description=&quot;Dynamically generate database credentials based on configured roles with the database secrets engine through a plugin interface to a number of different databases.&quot; data-og-host=&quot;developer.hashicorp.com&quot; data-og-source-url=&quot;https://developer.hashicorp.com/vault/docs/secrets/databases&quot; data-og-url=&quot;https://developer.hashicorp.com/vault/docs/secrets/databases&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/JHVQ9/hyZRozXhVr/xWkig1R7F41qVTPxg7kObk/img.jpg?width=3200&amp;amp;height=1800&amp;amp;face=0_0_3200_1800,https://scrap.kakaocdn.net/dn/e34Ix/hyZQJ4M0WP/9p4bsLjCkgrYjnNyG4kwoK/img.jpg?width=3200&amp;amp;height=1800&amp;amp;face=0_0_3200_1800&quot;&gt;&lt;a href=&quot;https://developer.hashicorp.com/vault/docs/secrets/databases&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://developer.hashicorp.com/vault/docs/secrets/databases&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/JHVQ9/hyZRozXhVr/xWkig1R7F41qVTPxg7kObk/img.jpg?width=3200&amp;amp;height=1800&amp;amp;face=0_0_3200_1800,https://scrap.kakaocdn.net/dn/e34Ix/hyZQJ4M0WP/9p4bsLjCkgrYjnNyG4kwoK/img.jpg?width=3200&amp;amp;height=1800&amp;amp;face=0_0_3200_1800');&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;Database secrets engine | Vault | HashiCorp Developer&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Dynamically generate database credentials based on configured roles with the database secrets engine through a plugin interface to a number of different databases.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;developer.hashicorp.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;h3 data-ke-size=&quot;size23&quot;&gt;2. Spring Boot AppRole + KV + Database Secret Engine 수행과정&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  Spring Boot AppRole + KV + Database Secret Engine 수행과정&lt;/b&gt;&lt;br /&gt;&lt;b&gt;&lt;br /&gt;1. Spring Boot 서버 실행&lt;/b&gt;&lt;br /&gt;- 실행 시 시스템 변수로 주요 키 값을 전달합니다.(ROLE_ID, SECRET_ID)&lt;br /&gt;&lt;br /&gt;&lt;b&gt;2. bootstrap.yml&lt;/b&gt;&lt;br /&gt;- 서버 실행 시 가장 먼저 실행이 되며, 파일 내에 구성된 Vault 접속정보를 기반으로 인증 방식 중 AppRole을 통해서 Vault에 접근을 수행합니다.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;3. Vault &amp;rarr; Vault KV Secret Engine&lt;/b&gt;&lt;br /&gt;- 접속 정보를 기반으로 각각에 접속을 하여서 데이터를 반환받습니다.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;4. Vault &amp;rarr; Vault Database Secret Engine&lt;/b&gt;&lt;br /&gt;- 접속 정보를 기반으로 각각에 접속을 하여서 데이터를 반환받습니다.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;5. Vault에서 반환받은 데이터는 시스템 환경 변수로 전달을 받습니다.&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;6. Vault KV 내에서 조회된 값은 @ConfigurationProperties를 통해서 매핑하여 객체로 주입됩니다.&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;7. Vault Database 내에 조회된 값은 @ConfigurationProperties를 통해서 매핑하여 객체로 주입됩니다.&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;8. DBConfig 내에서는 ValutKVProperties가 호출되고, KV 값 중 주요 DB 접속 정보(DB url, port, name)를 가져와서 JDBC URL을 세팅합니다&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;9. DBConfig 내에서는 ValutDBProperties가 호출되고, Database에서 동적으로 발급된 자격증명(username, password)을 가져와서 DB에 Connection을 시도합니다&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;10. 데이터베이스가 연결이 됩니다&lt;/b&gt;.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;11. 서버 실행이 완료됩니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1197&quot; data-origin-height=&quot;851&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Gx4Ya/dJMcabJIndk/X0eUtC9JYMJZ253Kfkhdk1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Gx4Ya/dJMcabJIndk/X0eUtC9JYMJZ253Kfkhdk1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Gx4Ya/dJMcabJIndk/X0eUtC9JYMJZ253Kfkhdk1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FGx4Ya%2FdJMcabJIndk%2FX0eUtC9JYMJZ253Kfkhdk1%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;1197&quot; height=&quot;851&quot; data-origin-width=&quot;1197&quot; data-origin-height=&quot;851&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3) 환경설정 -1: 로컬 데이터베이스 설정 및 권한 확인&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  환경설정 -1: 로컬 데이터베이스 설정 및 권한 확인&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 연결을 위한 로컬 데이터베이스 구성을 확인하고 관리자 계정이 확인되어야 합니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt; [참고]&lt;/b&gt;&amp;nbsp;아래의 글에서 로컬 데이터베이스를 이용하는 방법을 이용하였습니다.&lt;/blockquote&gt;
&lt;figure id=&quot;og_1768193213015&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;[DB] MacOS에서 PostgreSQL 로컬 데이터베이스 구성 방법&quot; data-og-description=&quot;해당 글에서는 PostgreSQL을 로컬 디비로 구성하는 방법에 대해서 공유합니다.&amp;nbsp;1) PostgreSQL을 설치합니다.  PostgreSQL의 버전을 확인하고 설치가 안 되어 있다면 설치를 하고 서비스를 수행합니다.# &quot; data-og-host=&quot;adjh54.tistory.com&quot; data-og-source-url=&quot;https://adjh54.tistory.com/80&quot; data-og-url=&quot;https://adjh54.tistory.com/80&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/g5DZt/hyZQTsL6XP/r92lunvYuTgC5FpeIb0K9k/img.png?width=630&amp;amp;height=588&amp;amp;face=0_0_630_588,https://scrap.kakaocdn.net/dn/BeiaJ/hyZQIY7kOi/sscxlhJlaYFnuPwK04Yrl1/img.png?width=630&amp;amp;height=588&amp;amp;face=0_0_630_588,https://scrap.kakaocdn.net/dn/dVqQKm/hyZQKCBs8M/yxySSdMNZvy98GXCEkbWv0/img.png?width=1468&amp;amp;height=1324&amp;amp;face=0_0_1468_1324&quot;&gt;&lt;a href=&quot;https://adjh54.tistory.com/80&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://adjh54.tistory.com/80&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/g5DZt/hyZQTsL6XP/r92lunvYuTgC5FpeIb0K9k/img.png?width=630&amp;amp;height=588&amp;amp;face=0_0_630_588,https://scrap.kakaocdn.net/dn/BeiaJ/hyZQIY7kOi/sscxlhJlaYFnuPwK04Yrl1/img.png?width=630&amp;amp;height=588&amp;amp;face=0_0_630_588,https://scrap.kakaocdn.net/dn/dVqQKm/hyZQKCBs8M/yxySSdMNZvy98GXCEkbWv0/img.png?width=1468&amp;amp;height=1324&amp;amp;face=0_0_1468_1324');&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;[DB] MacOS에서 PostgreSQL 로컬 데이터베이스 구성 방법&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;해당 글에서는 PostgreSQL을 로컬 디비로 구성하는 방법에 대해서 공유합니다.&amp;nbsp;1) PostgreSQL을 설치합니다.  PostgreSQL의 버전을 확인하고 설치가 안 되어 있다면 설치를 하고 서비스를 수행합니다.#&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;adjh54.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;h3 data-ke-size=&quot;size23&quot;&gt;1. 아래와 같이 로컬 데이터베이스에 접속하고, 접속 정보를 아는 상태입니다.&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;810&quot; data-origin-height=&quot;692&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/KPHvN/dJMcahXsoEf/Sh62vkL5YmpKNwWGpv8d2K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/KPHvN/dJMcahXsoEf/Sh62vkL5YmpKNwWGpv8d2K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/KPHvN/dJMcahXsoEf/Sh62vkL5YmpKNwWGpv8d2K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FKPHvN%2FdJMcahXsoEf%2FSh62vkL5YmpKNwWGpv8d2K%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;810&quot; height=&quot;692&quot; data-origin-width=&quot;810&quot; data-origin-height=&quot;692&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;h3 data-ke-size=&quot;size23&quot;&gt;2. PostgreSQL에서 관리자 권한인지 여부를 확인합니다&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  PostgreSQL에서 관리자 권한인지 여부를 확인합니다&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 생성한 사용자의 권한이 얼마나 되는지 확인을 합니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote style=&quot;background-color: #fcfcfc; color: #666666; text-align: left;&quot; data-ke-style=&quot;style3&quot;&gt; &lt;span&gt;&amp;nbsp;&lt;/span&gt;아래와 같이 권한을 부여하였습니다.&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;603&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Js3Xg/dJMcai9PsKI/GXVXGLT5Nn9J94kYiH0Hk1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Js3Xg/dJMcai9PsKI/GXVXGLT5Nn9J94kYiH0Hk1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Js3Xg/dJMcai9PsKI/GXVXGLT5Nn9J94kYiH0Hk1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FJs3Xg%2FdJMcai9PsKI%2FGXVXGLT5Nn9J94kYiH0Hk1%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;1280&quot; height=&quot;603&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;603&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;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;&amp;nbsp;  아래와 같은 권한으로 검색이 되었습니다.&lt;/b&gt; &lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 기본적으로 아래의 권한이 허용되는 유저입니다.&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;1. 새 데이터베이스 생성&lt;br /&gt;2. 새 사용자(Role) 생성&lt;br /&gt;3. 다른 사용자에게 권한 부여&lt;br /&gt;4. 복제 스트림 사용&lt;br /&gt;5. 애플리케이션 DB 운영 대부분 수행&lt;/blockquote&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;SELECT
    rolname,
    rolsuper,
    rolcreatedb,
    rolcreaterole,
    rolcanlogin,
    rolreplication,
    rolbypassrls
FROM pg_roles
WHERE rolname = 'localmaster'
&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;coq&quot;&gt;&lt;code&gt;   rolname   | rolsuper | rolcreatedb | rolcreaterole | rolcanlogin | rolreplication | rolbypassrls
-------------+----------+-------------+---------------+-------------+----------------+--------------
 localmaster | f        | t           | t             | t           | t              | f
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1378&quot; data-origin-height=&quot;573&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/tcSiG/dJMcacu3UuO/iSN7FiIMEvlGruwGnmd3q0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/tcSiG/dJMcacu3UuO/iSN7FiIMEvlGruwGnmd3q0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/tcSiG/dJMcacu3UuO/iSN7FiIMEvlGruwGnmd3q0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FtcSiG%2FdJMcacu3UuO%2FiSN7FiIMEvlGruwGnmd3q0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1378&quot; height=&quot;573&quot; data-origin-width=&quot;1378&quot; data-origin-height=&quot;573&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;h2 data-ke-size=&quot;size26&quot;&gt;3) 환경설정 : 사이트 내에서 Secrets Engine 설정&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. Secrets engines - Enable new engine 버튼을 누릅니다.&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  Secrets engines - Enable new engine 버튼을 누릅니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1907&quot; data-origin-height=&quot;650&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bWhWLg/dJMcadHsVvw/VKkAnBQdAzTWgWoUCWiC1k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bWhWLg/dJMcadHsVvw/VKkAnBQdAzTWgWoUCWiC1k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bWhWLg/dJMcadHsVvw/VKkAnBQdAzTWgWoUCWiC1k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbWhWLg%2FdJMcadHsVvw%2FVKkAnBQdAzTWgWoUCWiC1k%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;1907&quot; height=&quot;650&quot; data-origin-width=&quot;1907&quot; data-origin-height=&quot;650&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;h3 data-ke-size=&quot;size23&quot;&gt;2. 'Database'를 선택합니다&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1736&quot; data-origin-height=&quot;694&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dMnEJ4/dJMcaivepaU/r8HZDs6KnbbHnW8MLhyhe1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dMnEJ4/dJMcaivepaU/r8HZDs6KnbbHnW8MLhyhe1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dMnEJ4/dJMcaivepaU/r8HZDs6KnbbHnW8MLhyhe1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdMnEJ4%2FdJMcaivepaU%2Fr8HZDs6KnbbHnW8MLhyhe1%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;1736&quot; height=&quot;694&quot; data-origin-width=&quot;1736&quot; data-origin-height=&quot;694&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;h3 data-ke-size=&quot;size23&quot;&gt;3. 기본 옵션으로 선택하고 'Enable engine'버튼을 선택합니다.&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1740&quot; data-origin-height=&quot;576&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bnpMQg/dJMcacBPHW6/3IxDrGhDfcvkSQFDvfjNp0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bnpMQg/dJMcacBPHW6/3IxDrGhDfcvkSQFDvfjNp0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bnpMQg/dJMcacBPHW6/3IxDrGhDfcvkSQFDvfjNp0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbnpMQg%2FdJMcacBPHW6%2F3IxDrGhDfcvkSQFDvfjNp0%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;1740&quot; height=&quot;576&quot; data-origin-width=&quot;1740&quot; data-origin-height=&quot;576&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;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  [참고]&lt;/b&gt; 위에 과정을 GUI에서는 아래의 명령어로 수행이 가능합니다.&lt;/blockquote&gt;
&lt;pre class=&quot;shell&quot; data-ke-language=&quot;shell&quot;&gt;&lt;code&gt;$ vault secrets enable database
Success! Enabled the database secrets engine at: database/&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;h3 data-ke-size=&quot;size23&quot;&gt;4. 생성된 database를 선택하고 'Connections' 탭에서 'Create connection'을 선택합니다.&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1719&quot; data-origin-height=&quot;543&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bsaDVR/dJMcaiPw75V/g4NVJKvHOuy4SNtJ1mH9K1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bsaDVR/dJMcaiPw75V/g4NVJKvHOuy4SNtJ1mH9K1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bsaDVR/dJMcaiPw75V/g4NVJKvHOuy4SNtJ1mH9K1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbsaDVR%2FdJMcaiPw75V%2Fg4NVJKvHOuy4SNtJ1mH9K1%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;1719&quot; height=&quot;543&quot; data-origin-width=&quot;1719&quot; data-origin-height=&quot;543&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;h3 data-ke-size=&quot;size23&quot;&gt;5. 아래와 같이 설정하였습니다.&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1734&quot; data-origin-height=&quot;846&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b1TSj1/dJMcagxr89F/mXpANotzY6tZVefk0D4Ruk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b1TSj1/dJMcagxr89F/mXpANotzY6tZVefk0D4Ruk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b1TSj1/dJMcagxr89F/mXpANotzY6tZVefk0D4Ruk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb1TSj1%2FdJMcagxr89F%2FmXpANotzY6tZVefk0D4Ruk%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;1734&quot; height=&quot;846&quot; data-origin-width=&quot;1734&quot; data-origin-height=&quot;846&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;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  [참고]&lt;/b&gt; 'host.docker.internal'의 경우는 Docker 컨테이너 안에서 &quot;호스트 PC(내 컴퓨터)&quot;에 접근하기 위한 전용 호스트명으로 지정을 하였습니다.&lt;/blockquote&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 158px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style2&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 18.2558%; height: 20px;&quot;&gt;&lt;b&gt;분류 값&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 45.814%; height: 20px;&quot;&gt;&lt;b&gt;선택 및 입력&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 35.814%; height: 20px;&quot;&gt;&lt;b&gt;설명&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 18.2558%; height: 20px;&quot;&gt;&lt;b&gt;Database plugin&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 45.814%; height: 20px;&quot;&gt;PostgreSQL&lt;/td&gt;
&lt;td style=&quot;width: 35.814%; height: 20px;&quot;&gt;설정하려는 데이터베이스 종류를 선택합니다&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 18.2558%; height: 20px;&quot;&gt;&lt;b&gt;Connection name&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 45.814%; height: 20px;&quot;&gt;app-db&lt;/td&gt;
&lt;td style=&quot;width: 35.814%; height: 20px;&quot;&gt;설정 커넥션 명을 입력합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 40px;&quot;&gt;
&lt;td style=&quot;width: 18.2558%; height: 40px;&quot;&gt;&lt;b&gt;Connection URL&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 45.814%; height: 40px;&quot;&gt;postgresql://{{username}}:{{password}}@host.docker.internal:5432/testdb&lt;/td&gt;
&lt;td style=&quot;width: 35.814%; height: 40px;&quot;&gt;데이터베이스 접속 경로이며, 동적으로 username과 password는 생성하기에 변수로 설정합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 18.2558%; height: 20px;&quot;&gt;&lt;b&gt;Username&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 45.814%; height: 20px;&quot;&gt;localmaster&lt;/td&gt;
&lt;td style=&quot;width: 35.814%; height: 20px;&quot;&gt;Admin 계정의 아이디를 입력합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 18.2558%; height: 20px;&quot;&gt;&lt;b&gt;Password&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 45.814%; height: 20px;&quot;&gt;qwer1234&lt;/td&gt;
&lt;td style=&quot;width: 35.814%; height: 20px;&quot;&gt;Admin 계정의 비밀번호를 입력합니다.&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;h3 data-ke-size=&quot;size23&quot;&gt;6. 'Rotate and enable'을 선택합니다&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  'Rotate and enable'을 선택합니다&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;- 이를 통해서 동적인 사용자를 구성하도록 선택하였습니다.&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;658&quot; data-origin-height=&quot;331&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bFQoOM/dJMcaacWA86/Jp0gTjZvYkuXmO6DZpgUQ1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bFQoOM/dJMcaacWA86/Jp0gTjZvYkuXmO6DZpgUQ1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bFQoOM/dJMcaacWA86/Jp0gTjZvYkuXmO6DZpgUQ1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbFQoOM%2FdJMcaacWA86%2FJp0gTjZvYkuXmO6DZpgUQ1%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;658&quot; height=&quot;331&quot; data-origin-width=&quot;658&quot; data-origin-height=&quot;331&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4) 환경설정 : 사이트 내에서 Role 설정&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. Role 탭을 선택하고 'Create Role' 버튼을 선택합니다&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1914&quot; data-origin-height=&quot;730&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cbG3QB/dJMcahJUQV9/vgheqKopYYzokgs5lVA1TK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cbG3QB/dJMcahJUQV9/vgheqKopYYzokgs5lVA1TK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cbG3QB/dJMcahJUQV9/vgheqKopYYzokgs5lVA1TK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcbG3QB%2FdJMcahJUQV9%2FvgheqKopYYzokgs5lVA1TK%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;1914&quot; height=&quot;730&quot; data-origin-width=&quot;1914&quot; data-origin-height=&quot;730&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;h3 data-ke-size=&quot;size23&quot;&gt;2. Role name, Connection name, Type of role을 &amp;lsquo;dynamic&amp;rsquo;으로 입력 혹은 선택하고 완료합니다.&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;927&quot; data-origin-height=&quot;638&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/br2c9P/dJMcadAGn5r/DmOi2h3oBS26KZojhhscWK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/br2c9P/dJMcadAGn5r/DmOi2h3oBS26KZojhhscWK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/br2c9P/dJMcadAGn5r/DmOi2h3oBS26KZojhhscWK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbr2c9P%2FdJMcadAGn5r%2FDmOi2h3oBS26KZojhhscWK%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;927&quot; height=&quot;638&quot; data-origin-width=&quot;927&quot; data-origin-height=&quot;638&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;h3 data-ke-size=&quot;size23&quot;&gt;3. 아래와 같이 구성을 완료하였습니다.&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1914&quot; data-origin-height=&quot;754&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bGLT81/dJMcadAGn6b/CbCkyw3KzmgeENiklnbn60/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bGLT81/dJMcadAGn6b/CbCkyw3KzmgeENiklnbn60/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bGLT81/dJMcadAGn6b/CbCkyw3KzmgeENiklnbn60/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbGLT81%2FdJMcadAGn6b%2FCbCkyw3KzmgeENiklnbn60%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;1914&quot; height=&quot;754&quot; data-origin-width=&quot;1914&quot; data-origin-height=&quot;754&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;h2 data-ke-size=&quot;size26&quot;&gt;5) 환경설정 : AppRole 인증 방식 설정&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  환경설정 : AppRole ID 발급&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- Vault 인증 방식 중 AppRole 방식을 이용하여서 구성을 합니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  이전에 작성한 아래의 글을 기반으로 구성하였습니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;figure id=&quot;og_1768193849252&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;[Java] Spring Cloud Vault 이해하고 활용하기 -3 : AppRole 인증방식&quot; data-og-description=&quot;해당 글에서는 Vault의 인증 방식 중 &amp;lsquo;AppRole&amp;rsquo; 인증방식을 이해하고 설정하는 방법에 대해 알아봅니다.   [참고] 이전에 작성한 글들을 확인하고 오시면 큰 도움이 됩니다.분류URI[Java] Spring Cloud&quot; data-og-host=&quot;adjh54.tistory.com&quot; data-og-source-url=&quot;https://adjh54.tistory.com/731&quot; data-og-url=&quot;https://adjh54.tistory.com/731&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/x1fpa/hyZRl4hiy8/UtHfLnefzvpRNyszLvu6m0/img.png?width=800&amp;amp;height=431&amp;amp;face=0_0_800_431,https://scrap.kakaocdn.net/dn/bWxKMs/hyZRn17aka/5eoHuQoRde2xOFc0fyUKhk/img.png?width=800&amp;amp;height=431&amp;amp;face=0_0_800_431,https://scrap.kakaocdn.net/dn/cqT8t8/hyZRjZGc9k/IGKLTedHvPF3gBNFi1XPT0/img.png?width=1200&amp;amp;height=690&amp;amp;face=0_0_1200_690&quot;&gt;&lt;a href=&quot;https://adjh54.tistory.com/731&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://adjh54.tistory.com/731&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/x1fpa/hyZRl4hiy8/UtHfLnefzvpRNyszLvu6m0/img.png?width=800&amp;amp;height=431&amp;amp;face=0_0_800_431,https://scrap.kakaocdn.net/dn/bWxKMs/hyZRn17aka/5eoHuQoRde2xOFc0fyUKhk/img.png?width=800&amp;amp;height=431&amp;amp;face=0_0_800_431,https://scrap.kakaocdn.net/dn/cqT8t8/hyZRjZGc9k/IGKLTedHvPF3gBNFi1XPT0/img.png?width=1200&amp;amp;height=690&amp;amp;face=0_0_1200_690');&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;[Java] Spring Cloud Vault 이해하고 활용하기 -3 : AppRole 인증방식&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;해당 글에서는 Vault의 인증 방식 중 &amp;lsquo;AppRole&amp;rsquo; 인증방식을 이해하고 설정하는 방법에 대해 알아봅니다.   [참고] 이전에 작성한 글들을 확인하고 오시면 큰 도움이 됩니다.분류URI[Java] Spring Cloud&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;adjh54.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;h3 data-ke-size=&quot;size23&quot;&gt;1. 기존 AppRole의 Policy를 수정합니다&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  기존 AppRole의 Policy를 수정합니다&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;- 아래와 같이 기존 AppRole에 대한 ACL Policy는 아래와 같았습니다. &lt;br /&gt;- 이는 KV라는 데이터에만 접근을 할 수 있는 권한이었고, 이제 Database Secret Engine에 접근이 가능한 정책을 추가합니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1.1. ACL Policy 수정 이전&lt;/h4&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;style3&quot;&gt;&lt;b&gt;  ACL Policy 수정 이전&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;- 아래와 같이 기존 AppRole에 대한 ACL Policy는 아래와 같았습니다. 이는 KV라는 데이터에만 접근을 할 수 있는 권한이었고, 이제 Database Secret Engine에 접근이 가능한 권한을 추가합니다.&lt;/blockquote&gt;
&lt;pre class=&quot;nginx&quot;&gt;&lt;code&gt;path &quot;kv2/data/*&quot; {
  capabilities = [&quot;read&quot;, &quot;list&quot;]
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1902&quot; data-origin-height=&quot;840&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Gj32z/dJMcaacW1rZ/4ZHuw0yNswzLipvYwHu9bk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Gj32z/dJMcaacW1rZ/4ZHuw0yNswzLipvYwHu9bk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Gj32z/dJMcaacW1rZ/4ZHuw0yNswzLipvYwHu9bk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FGj32z%2FdJMcaacW1rZ%2F4ZHuw0yNswzLipvYwHu9bk%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;1902&quot; height=&quot;840&quot; data-origin-width=&quot;1902&quot; data-origin-height=&quot;840&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;h4 data-ke-size=&quot;size20&quot;&gt;1.2. ACL Policy 수정 이후&lt;/h4&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;style3&quot;&gt;&lt;b&gt;  ACL Policy 수정 이후&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;- 아래와 같이 각각에 추가 권한들을 추가해 주고, 중요한 Database Dynamic Credentials 부분을 추가합니다.&lt;/blockquote&gt;
&lt;pre class=&quot;vala&quot;&gt;&lt;code&gt;# ===============================
#   AppRole 로그인 권한
# ===============================

# AppRole 방식으로 로그인하기 위해 반드시 필요
# Spring Boot / CLI / Agent가 role_id + secret_id로 토큰을 발급받을 때 사용
path &quot;auth/approle/login&quot; {
  capabilities = [&quot;create&quot;]   # 로그인 요청은 create 권한
}

# ===============================
#   KV v2 (필수)
# ===============================

# ✅ loc 경로 자체에 대한 접근
# Spring Cloud Vault는 먼저 &quot;루트 경로&quot;를 조회함
path &quot;kv2/data/loc&quot; {
  capabilities = [&quot;read&quot;]
}

# loc 메타데이터 조회 권한
path &quot;kv2/metadata/loc&quot; {
  capabilities = [&quot;list&quot;]
}

# ===============================
#   loc 하위 Secret 접근
# ===============================

# 실제 애플리케이션 설정 값 접근
# 예: kv2/loc/db_url, kv2/loc/db_name 등
path &quot;kv2/data/loc/*&quot; {
  capabilities = [&quot;read&quot;]
}

# 하위 secret 목록 조회
# 일부 Vault 클라이언트/라이브러리에서 필요
path &quot;kv2/metadata/loc/*&quot; {
  capabilities = [&quot;list&quot;]
}

# ===============================
#   Database Dynamic Credentials
# ===============================

# database secret engine에서
# testRole 기반 동적 DB 계정 발급 권한
#
# 이 경로가 없으면:
path &quot;database/creds/testRole&quot; {
  capabilities = [&quot;read&quot;]
}

# ===============================
#   Token Self Management
# ===============================

# 현재 토큰 정보 조회
# Spring Cloud Vault가 토큰 TTL, renewable 여부 확인 시 사용
path &quot;auth/token/lookup-self&quot; {
  capabilities = [&quot;read&quot;]
}

# 토큰 갱신 권한
# renewable 토큰일 경우 TTL 자동 연장 가능
path &quot;auth/token/renew-self&quot; {
  capabilities = [&quot;update&quot;]
}

# 자기 자신 토큰 폐기 권한
# 정상 종료 시 Vault Agent / App에서 토큰 정리 가능
path &quot;auth/token/revoke-self&quot; {
  capabilities = [&quot;update&quot;]
}

# 현재 토큰이 어떤 권한을 갖고 있는지 확인
# Spring Cloud Vault 내부 검증용
path &quot;sys/capabilities-self&quot; {
  capabilities = [&quot;update&quot;]
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. 터미널에서 생성한 approle, ACL Policy를 기반으로 Role ID를 생성합니다.&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;580&quot; data-origin-height=&quot;220&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cqDBee/dJMcacu3UE0/xOjrBE7BJ8Gis0O8oLkKS0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cqDBee/dJMcacu3UE0/xOjrBE7BJ8Gis0O8oLkKS0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cqDBee/dJMcacu3UE0/xOjrBE7BJ8Gis0O8oLkKS0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcqDBee%2FdJMcacu3UE0%2FxOjrBE7BJ8Gis0O8oLkKS0%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;580&quot; height=&quot;220&quot; data-origin-width=&quot;580&quot; data-origin-height=&quot;220&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;pre class=&quot;shell&quot; data-ke-language=&quot;shell&quot;&gt;&lt;code&gt;vault write auth/approle/role/approle \
  policies=&quot;dev-approle-policy&quot; \
  token_type=batch \
  secret_id_ttl=24h \
  secret_id_num_uses=0 \
  token_ttl=1h \
  token_max_ttl=24h&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  아래의 접속 경로를 통해서 &amp;lsquo;approle&amp;rsquo;를 확인하였습니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1904&quot; data-origin-height=&quot;776&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bjimzo/dJMcaaqtxVc/xtyXZhmGkBCG1hkWmDZKi0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bjimzo/dJMcaaqtxVc/xtyXZhmGkBCG1hkWmDZKi0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bjimzo/dJMcaaqtxVc/xtyXZhmGkBCG1hkWmDZKi0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbjimzo%2FdJMcaaqtxVc%2FxtyXZhmGkBCG1hkWmDZKi0%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;1904&quot; height=&quot;776&quot; data-origin-width=&quot;1904&quot; data-origin-height=&quot;776&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;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  아래의 &amp;lsquo;ACL Policyes&amp;rsquo; 부분에서 지정한 policy를 선택하였습니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1918&quot; data-origin-height=&quot;485&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cMfHKb/dJMb99ZpKVb/M2PNPKaDZ6o1BHIHaKoOWK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cMfHKb/dJMb99ZpKVb/M2PNPKaDZ6o1BHIHaKoOWK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cMfHKb/dJMb99ZpKVb/M2PNPKaDZ6o1BHIHaKoOWK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcMfHKb%2FdJMb99ZpKVb%2FM2PNPKaDZ6o1BHIHaKoOWK%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;1918&quot; height=&quot;485&quot; data-origin-width=&quot;1918&quot; data-origin-height=&quot;485&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;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  [참고]&lt;/b&gt;&amp;nbsp;생성한 Role에 대한 조회를 하는 방법은 아래와 같습니다&lt;/blockquote&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;vault read auth/approle/role/approle
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;587&quot; data-origin-height=&quot;375&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/wmaWV/dJMcagqGwlb/frvho02tEzCePi72vKuyz0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/wmaWV/dJMcagqGwlb/frvho02tEzCePi72vKuyz0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/wmaWV/dJMcagqGwlb/frvho02tEzCePi72vKuyz0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FwmaWV%2FdJMcagqGwlb%2Ffrvho02tEzCePi72vKuyz0%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;587&quot; height=&quot;375&quot; data-origin-width=&quot;587&quot; data-origin-height=&quot;375&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;h3 data-ke-size=&quot;size23&quot;&gt;3. Role ID를 조회합니다.&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;pre class=&quot;gradle&quot;&gt;&lt;code&gt;vault read auth/approle/role/approle/role-id
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;589&quot; data-origin-height=&quot;489&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/NMd6f/dJMcadHsVCu/eUxU9aV8iRnvOUsK0191Zk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/NMd6f/dJMcadHsVCu/eUxU9aV8iRnvOUsK0191Zk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/NMd6f/dJMcadHsVCu/eUxU9aV8iRnvOUsK0191Zk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FNMd6f%2FdJMcadHsVCu%2FeUxU9aV8iRnvOUsK0191Zk%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;589&quot; height=&quot;489&quot; data-origin-width=&quot;589&quot; data-origin-height=&quot;489&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;h3 data-ke-size=&quot;size23&quot;&gt;4. Secret ID를 발급합니다&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;gradle&quot;&gt;&lt;code&gt;$ vault write -f auth/approle/role/approle/secret-id
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;580&quot; data-origin-height=&quot;481&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/O6Nom/dJMcaaRx6fn/YovwosAM0e3cvYw8ArL7E1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/O6Nom/dJMcaaRx6fn/YovwosAM0e3cvYw8ArL7E1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/O6Nom/dJMcaaRx6fn/YovwosAM0e3cvYw8ArL7E1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FO6Nom%2FdJMcaaRx6fn%2FYovwosAM0e3cvYw8ArL7E1%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;580&quot; height=&quot;481&quot; data-origin-width=&quot;580&quot; data-origin-height=&quot;481&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;6) Spring Boot 환경 구성&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 개발 환경&lt;/h3&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;style3&quot;&gt;&lt;b&gt;&lt;b&gt; &lt;/b&gt;개발&amp;nbsp;환경&lt;br /&gt;&lt;br /&gt;&lt;/b&gt;-&amp;nbsp;기본&amp;nbsp;기능과&amp;nbsp;주요&amp;nbsp;라이브러리인&amp;nbsp;spring-cloud-dependencies,&amp;nbsp;spring-cloud-starter-bootstrap,&amp;nbsp;spring-cloud-starter-vault-config,&amp;nbsp;spring-cloud-vault-config-databases를&amp;nbsp;설정하였습니다.&lt;br /&gt;- 추가로 데이터베이스 연결을 위해 PostgreSQL Driver, JDBC, MyBatis를 추가하였습니다.&lt;b&gt;&lt;br /&gt;&lt;/b&gt;&lt;/blockquote&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style2&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 45.9302%;&quot;&gt;&lt;b&gt;개발 환경&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 41.1628%;&quot;&gt;&lt;b&gt;분류&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 12.7907%;&quot;&gt;&lt;b&gt;버전&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 45.9302%;&quot;&gt;Java JDK&lt;/td&gt;
&lt;td style=&quot;width: 41.1628%;&quot;&gt;Java Version&lt;/td&gt;
&lt;td style=&quot;width: 12.7907%;&quot;&gt;17&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 45.9302%;&quot;&gt;빌드 도구&lt;/td&gt;
&lt;td style=&quot;width: 41.1628%;&quot;&gt;Gradle&lt;/td&gt;
&lt;td style=&quot;width: 12.7907%;&quot;&gt;8.14.3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 45.9302%;&quot;&gt;Spring Boot&lt;/td&gt;
&lt;td style=&quot;width: 41.1628%;&quot;&gt;Spring Boot Starter&lt;/td&gt;
&lt;td style=&quot;width: 12.7907%;&quot;&gt;3.5.8&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 45.9302%;&quot;&gt;Spring Boot Web&lt;/td&gt;
&lt;td style=&quot;width: 41.1628%;&quot;&gt;Spring Boot Starter&lt;/td&gt;
&lt;td style=&quot;width: 12.7907%;&quot;&gt;3.5.8&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 45.9302%;&quot;&gt;spring-boot-starter-jdbc&lt;/td&gt;
&lt;td style=&quot;width: 41.1628%;&quot;&gt;Spring Boot Starter&lt;/td&gt;
&lt;td style=&quot;width: 12.7907%;&quot;&gt;3.5.8&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 45.9302%;&quot;&gt;spring-cloud-starter-bootstrap&lt;/td&gt;
&lt;td style=&quot;width: 41.1628%;&quot;&gt;Spring Cloud&lt;/td&gt;
&lt;td style=&quot;width: 12.7907%;&quot;&gt;2025.0.0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 45.9302%;&quot;&gt;spring-cloud-starter-vault-config&lt;/td&gt;
&lt;td style=&quot;width: 41.1628%;&quot;&gt;Spring Cloud&lt;/td&gt;
&lt;td style=&quot;width: 12.7907%;&quot;&gt;2025.0.0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 45.9302%;&quot;&gt;spring-cloud-vault-config-databases&lt;/td&gt;
&lt;td style=&quot;width: 41.1628%;&quot;&gt;Spring Cloud&lt;/td&gt;
&lt;td style=&quot;width: 12.7907%;&quot;&gt;2025.0.0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 45.9302%;&quot;&gt;spring-cloud-dependencies&lt;/td&gt;
&lt;td style=&quot;width: 41.1628%;&quot;&gt;Spring Cloud&lt;/td&gt;
&lt;td style=&quot;width: 12.7907%;&quot;&gt;2025.0.0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 45.9302%;&quot;&gt;Lombok&lt;/td&gt;
&lt;td style=&quot;width: 41.1628%;&quot;&gt;Library&lt;/td&gt;
&lt;td style=&quot;width: 12.7907%;&quot;&gt;latest&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 45.9302%;&quot;&gt;org.postgresql:postgresql&lt;/td&gt;
&lt;td style=&quot;width: 41.1628%;&quot;&gt;Library&lt;/td&gt;
&lt;td style=&quot;width: 12.7907%;&quot;&gt;42.7.8&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 45.9302%;&quot;&gt;org.mybatis.spring.boot:mybatis-spring-boot-starter&lt;/td&gt;
&lt;td style=&quot;width: 41.1628%;&quot;&gt;Library&lt;/td&gt;
&lt;td style=&quot;width: 12.7907%;&quot;&gt;3.0.5&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;h3 data-ke-size=&quot;size23&quot;&gt;2. 라이브러리 설치 : build.gradle&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  라이브러리 설치 : build.gradle&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;- 위에서 명시한 라이브러리를 Gradle을 이용하여서 설치합니다.&lt;/blockquote&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;plugins {
    id 'java'
    id 'org.springframework.boot' version '3.5.8'
    id 'io.spring.dependency-management' version '1.1.7'
}
ext {
    springCloudVersion = &quot;2025.0.0&quot;
}

group = 'com.adjh'
version = '0.0.1-SNAPSHOT'
description = 'spring-boot3-vault'

java {
    toolchain {
        languageVersion = JavaLanguageVersion.of(17)
    }
}

configurations {
    compileOnly {
        extendsFrom annotationProcessor
    }
}

repositories {
    mavenCentral()
}

dependencies {

    // [Spring Boot Starter]
    implementation 'org.springframework.boot:spring-boot-starter-web'               // Spring Boot Web
    implementation 'org.springframework.boot:spring-boot-starter-web'               // Spring Boot Web
    implementation &quot;org.springframework.boot:spring-boot-starter-jdbc&quot;              // Spring Boot JDBC

    // [Spring Cloud]
    implementation 'org.springframework.cloud:spring-cloud-starter-bootstrap'       // Spring Cloud BootStrap
    implementation 'org.springframework.cloud:spring-cloud-starter-vault-config'    // Spring Cloud Vault
    implementation 'org.springframework.cloud:spring-cloud-vault-config-databases'  // Spring Cloud Vault Database

    // [Library]
    implementation(&quot;org.postgresql:postgresql:42.7.8&quot;)                              // PostgresSQL
    implementation 'org.mybatis.spring.boot:mybatis-spring-boot-starter:3.0.5'      // Mybatis

    compileOnly 'org.projectlombok:lombok'                                          // Lombok Compile
    annotationProcessor 'org.projectlombok:lombok'                                  // Lombok Annotation
    testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
    testImplementation 'org.springframework.boot:spring-boot-starter-test'
}
dependencyManagement {
    imports {
        mavenBom &quot;org.springframework.cloud:spring-cloud-dependencies:$springCloudVersion&quot;
    }
}

tasks.named('test') {
    useJUnitPlatform()
}&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;h3 data-ke-size=&quot;size23&quot;&gt;3. 시스템 환경변수 주입 : IntelliJ IDE 활용&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  시스템 환경변수 주입 : IntelliJ IDE 활용&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- Vault로 전달이 되는 Main Key 자체를 프로젝트 내에 넣고 Git에 올릴 수 없기에 &amp;lsquo;시스템 환경 변수&amp;rsquo; 내에 추가합니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  [참고]&lt;/b&gt;&amp;nbsp;아래의 환경 변수를 추가하는 방법의 글을 참고하셔도 도움이 됩니다.&lt;/blockquote&gt;
&lt;figure id=&quot;og_1768194095404&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;[Java] Spring Boot 환경에서 시스템 변수 .env 파일 지정 및 활용 방법&quot; data-og-description=&quot;해당 글에서는 Spring Boot 개발 환경에서 .env 파일을 시스템 변수로 지정하는 방법과 이를 불러오는 다양한 방법에 대해서 알아봅니다1) Spring Boot 환경에서 .env 파일을 시스템 변수로 적용하는 이&quot; data-og-host=&quot;adjh54.tistory.com&quot; data-og-source-url=&quot;https://adjh54.tistory.com/722&quot; data-og-url=&quot;https://adjh54.tistory.com/722&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/JqZA1/hyZRi0NhFu/PiUNpbQlD2rK190GeG7omk/img.png?width=800&amp;amp;height=432&amp;amp;face=0_0_800_432,https://scrap.kakaocdn.net/dn/bvK75B/hyZQVRHgm8/gQupkfdL9BT0CNKiwCcdv0/img.png?width=800&amp;amp;height=432&amp;amp;face=0_0_800_432,https://scrap.kakaocdn.net/dn/cpn6lC/hyZQUSMQXa/Ptx4nsvi6PfvawaZnjIrb0/img.png?width=1165&amp;amp;height=815&amp;amp;face=0_0_1165_815&quot;&gt;&lt;a href=&quot;https://adjh54.tistory.com/722&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://adjh54.tistory.com/722&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/JqZA1/hyZRi0NhFu/PiUNpbQlD2rK190GeG7omk/img.png?width=800&amp;amp;height=432&amp;amp;face=0_0_800_432,https://scrap.kakaocdn.net/dn/bvK75B/hyZQVRHgm8/gQupkfdL9BT0CNKiwCcdv0/img.png?width=800&amp;amp;height=432&amp;amp;face=0_0_800_432,https://scrap.kakaocdn.net/dn/cpn6lC/hyZQUSMQXa/Ptx4nsvi6PfvawaZnjIrb0/img.png?width=1165&amp;amp;height=815&amp;amp;face=0_0_1165_815');&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;[Java] Spring Boot 환경에서 시스템 변수 .env 파일 지정 및 활용 방법&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;해당 글에서는 Spring Boot 개발 환경에서 .env 파일을 시스템 변수로 지정하는 방법과 이를 불러오는 다양한 방법에 대해서 알아봅니다1) Spring Boot 환경에서 .env 파일을 시스템 변수로 적용하는 이&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;adjh54.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;figure id=&quot;og_1768194096697&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;[Java] Spring Cloud Vault 이해하고 활용하기-1 : 초기 환경, KV 구성 및 Root Token 인증 방식&quot; data-og-description=&quot;해당 글에서는 Spring Cloud Vault를 이해하고 Vault를 구성하고 KV Secret Engine 내에서 데이터를 조회하는 토큰 인증방식을 이용하는 방법에 대해 알아봅니다 1) Vault  Vault- HashCorp 사에서 만든 Vault는 &quot; data-og-host=&quot;adjh54.tistory.com&quot; data-og-source-url=&quot;https://adjh54.tistory.com/728&quot; data-og-url=&quot;https://adjh54.tistory.com/728&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cCZtE2/hyZRmoz6JU/bU0KVcft0ATmVb2uGfqhnK/img.png?width=800&amp;amp;height=431&amp;amp;face=0_0_800_431,https://scrap.kakaocdn.net/dn/bvJJyQ/hyZQWC4p5J/TqwaAYLqRXck0dnbsjX8vk/img.png?width=800&amp;amp;height=431&amp;amp;face=0_0_800_431,https://scrap.kakaocdn.net/dn/fm1OR/hyZRgu8asf/0L7FvUbZWZV5pkpYzPm5HK/img.png?width=1908&amp;amp;height=931&amp;amp;face=0_0_1908_931&quot;&gt;&lt;a href=&quot;https://adjh54.tistory.com/728&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://adjh54.tistory.com/728&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cCZtE2/hyZRmoz6JU/bU0KVcft0ATmVb2uGfqhnK/img.png?width=800&amp;amp;height=431&amp;amp;face=0_0_800_431,https://scrap.kakaocdn.net/dn/bvJJyQ/hyZQWC4p5J/TqwaAYLqRXck0dnbsjX8vk/img.png?width=800&amp;amp;height=431&amp;amp;face=0_0_800_431,https://scrap.kakaocdn.net/dn/fm1OR/hyZRgu8asf/0L7FvUbZWZV5pkpYzPm5HK/img.png?width=1908&amp;amp;height=931&amp;amp;face=0_0_1908_931');&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;[Java] Spring Cloud Vault 이해하고 활용하기-1 : 초기 환경, KV 구성 및 Root Token 인증 방식&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;해당 글에서는 Spring Cloud Vault를 이해하고 Vault를 구성하고 KV Secret Engine 내에서 데이터를 조회하는 토큰 인증방식을 이용하는 방법에 대해 알아봅니다 1) Vault  Vault- HashCorp 사에서 만든 Vault는&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;adjh54.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;h3 data-ke-size=&quot;size23&quot;&gt;4. bootstrap.yml 파일을 생성 및 구성합니다.&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  bootstrap.yml 파일을 생성 및 구성합니다.&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- resources 경로 하단에 bootstrap.yml 파일을 생성하였습니다&lt;/b&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;461&quot; data-origin-height=&quot;595&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/49MVv/dJMcabXd0k1/cfgYkV7r5A3M1Oxm1u825k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/49MVv/dJMcabXd0k1/cfgYkV7r5A3M1Oxm1u825k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/49MVv/dJMcabXd0k1/cfgYkV7r5A3M1Oxm1u825k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F49MVv%2FdJMcabXd0k1%2FcfgYkV7r5A3M1Oxm1u825k%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;461&quot; height=&quot;595&quot; data-origin-width=&quot;461&quot; data-origin-height=&quot;595&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;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;&amp;nbsp;  yml 파일을 아래와 같이 구성하였습니다.&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;- 사전 단계에서 발급받은 VAULT_APP_ROLE_ID, VAULT_APP_ROLE_SECRET 값을 환경 변수로 추가하였고 이를 불러옵니다.&lt;br /&gt;- 기존의 KV Engine의 경우는 이전과 동일한 구성입니다.&lt;br /&gt;- 이번 과정에서는 &amp;lsquo;Vault Database Engine 접속 정보&amp;rsquo; 부분이 추가되었습니다.&lt;br /&gt;- 해당 내용은 아래와 같습니다.&lt;/blockquote&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style2&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;속성&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;설명&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;vault.database.enabled&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Vault Database Secret Engine 사용 여부 (DB 계정을 Vault에서 동적으로 발급받아 사용)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;vault.database.role&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Vault에서 DB 계정을 생성할 때 사용할 Database Role 이름&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;vault.database.backend&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Vault에 Database Secret Engine이 마운트된 경로&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;username-property&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Vault 응답에서 DB 사용자명으로 사용할 필드 이름&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;password-property&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Vault 응답에서 DB 비밀번호로 사용할 필드 이름&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;pre class=&quot;shell&quot; data-ke-language=&quot;shell&quot;&gt;&lt;code&gt;server:
  port: 8000
spring:
  cloud:
    vault:
      uri: &amp;lt;http://localhost:8200&amp;gt;
      # 인증방식 : APPID, APPROLE, AWS_EC2, AWS_IAM, AZURE_MSI, CERT, CUBBYHOLE, GCP_GCE, GCP_IAM, KUBERNETES, NONE, PCF, TOKEN;
      authentication: APPROLE                           # Vault 인증 방식 (TOKEN, APPROLE, APPID 등)
      connection-timeout: 5000                          # Vault 서버 연결 시도 타임아웃(ms)
      read-timeout: 1000                                # Vault 데이터 읽기 타임아웃(ms)

      # Vault AppRole 인증 방식
      app-role:
        role-id: ${VAULT_APP_ROLE_ID}                   # 환경변수에서 Vault 접근 role-id를 불러옴
        secret-id: ${VAULT_APP_ROLE_SECRET}             # 환경변수에서 Vault 접근 role-secret를 불러옴
        role: dev-approle-policy                        # 사전에 할당된 role을 불러옴

      # Vault KV Engine 접속 정보
      kv:
        enabled: true                                   # KV(키-값) 비밀 엔진 사용 여부
        backend: kv2                                    # KV Secret Engine 이름(ex: kv2)
        default-context: loc                            # 기본 Secret 경로 (예: kv2/loc/data/application 같은 구조가 됨)
        application-name: ''                            # Spring 애플리케이션 이름을 Secret 경로에 추가할지 여부

      # Vault Database Engine 접속 정보
      database:
        enabled: true                                   # Vault Database Secret Engine 사용 여부 (DB 계정을 Vault에서 동적으로 발급받아 사용)
        role: testRole                                  # Vault에서 DB 계정을 생성할 때 사용할 Database Role 이름
        backend: app-db                                 # Vault에 Database Secret Engine이 마운트된 경로
        username-property: spring.datasource.username   # Vault 응답에서 DB 사용자명으로 사용할 필드 이름
        password-property: spring.datasource.password   # Vault 응답에서 DB 비밀번호로 사용할 필드 이름
logging:
  level:
    org.springframework.cloud.vault: DEBUG&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;h3 data-ke-size=&quot;size23&quot;&gt;5. 서버 실행&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  서버 실행&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;- 정상적으로 오류 없이 서버가 실행하였습니다.&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1827&quot; data-origin-height=&quot;806&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qTZrF/dJMcaacW1G1/GLKHJnZo3JbciQb7VsBXfK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qTZrF/dJMcaacW1G1/GLKHJnZo3JbciQb7VsBXfK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qTZrF/dJMcaacW1G1/GLKHJnZo3JbciQb7VsBXfK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FqTZrF%2FdJMcaacW1G1%2FGLKHJnZo3JbciQb7VsBXfK%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;1827&quot; height=&quot;806&quot; data-origin-width=&quot;1827&quot; data-origin-height=&quot;806&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;h3 data-ke-size=&quot;size23&quot;&gt;6. VaultKVProperties:&amp;nbsp;Vault&amp;nbsp;값과&amp;nbsp;객체&amp;nbsp;매핑(KV&amp;nbsp;Secret&amp;nbsp;Engine)&lt;/h3&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;style3&quot;&gt;&lt;b&gt;&amp;nbsp;  VaultKVProperties:&amp;nbsp;Vault&amp;nbsp;값과&amp;nbsp;객체&amp;nbsp;매핑(KV&amp;nbsp;Secret&amp;nbsp;Engine)&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- Vault에서 읽어온 값을 자바 클래스 필드에 자동 바인딩을 하는 클래스입니다.&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;- @Getter, @Setter 통해서 멤버 변수에 접근할 수 있도록 어노테이션을 지정하였습니다.&lt;br /&gt;- @Component를 빈으로 등록하여서, 다른 서비스나 컴포넌트에 주입하여 사용이 가능하도록 하였습니다.&lt;br /&gt;- @ConfigurationProperties(prefix = &quot;vault&quot;) : 상위 prefix 내에 하위에 동일한 이름으로 각각 자동 바인딩이 됩니다.&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1358&quot; data-origin-height=&quot;690&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/FJaQM/dJMcahwm9Pa/TBRVlrg9qrASGyYcphKPO1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/FJaQM/dJMcahwm9Pa/TBRVlrg9qrASGyYcphKPO1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/FJaQM/dJMcahwm9Pa/TBRVlrg9qrASGyYcphKPO1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FFJaQM%2FdJMcahwm9Pa%2FTBRVlrg9qrASGyYcphKPO1%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;1358&quot; height=&quot;690&quot; data-origin-width=&quot;1358&quot; data-origin-height=&quot;690&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;package com.adjh.springboot3vault.properties;

import lombok.Getter;
import lombok.Setter;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

/**
 * Vault Key-Value Properties Mapping value
 *
 * @author : leejonghoon
 * @fileName : VaultKVProperties
 * @since : 25. 11. 25.
 */
@Getter
@Setter
@Component
@ConfigurationProperties(prefix = &quot;vault&quot;)
public class VaultKVProperties {
    private String appEnv;
    private String dbHost;
    private String dbPassword;
    private String dbUser;
    private String dbUrl;
    private String dbPort;
    private String dbName;
}&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;h3 data-ke-size=&quot;size23&quot;&gt;7. VaultDBProperties: Vault 값과 객체 매핑(Database Secret Engine)&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  VaultDBProperties: Vault 값과 객체 매핑(Database Secret Engine)&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- Database Secret Engine에서 발급된 username과 password가 매핑이 됩니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1231&quot; data-origin-height=&quot;673&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/3p50m/dJMcacaLo7X/uj3pgMouSgfJ7kxa7AdRXK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/3p50m/dJMcacaLo7X/uj3pgMouSgfJ7kxa7AdRXK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/3p50m/dJMcacaLo7X/uj3pgMouSgfJ7kxa7AdRXK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F3p50m%2FdJMcacaLo7X%2Fuj3pgMouSgfJ7kxa7AdRXK%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;1231&quot; height=&quot;673&quot; data-origin-width=&quot;1231&quot; data-origin-height=&quot;673&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;h3 data-ke-size=&quot;size23&quot;&gt;8. DBConfig&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  DBConfig&lt;/b&gt;&lt;br /&gt;&lt;b&gt;&lt;br /&gt;- 서버가 실행이 되고 설정되는 Configuartion 파일입니다.&lt;/b&gt;&lt;br /&gt;- 해당 페이지에서는 Vault 접속 이후 KV Secret Engine에서 조회된 값을 기반으로 주요 정보를 가져옵니다.&lt;br /&gt;- 이를 토대로 JDBC URL을 연결하고 DataBase Secret Engine에서 동적으로 생성한 username, password를 기반으로 데이터베이스 연결을 수행합니다.&lt;/blockquote&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;package com.adjh.springboot3vault.config;

import com.adjh.springboot3vault.properties.VaultDbProperties;
import com.adjh.springboot3vault.properties.VaultKVProperties;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;

import javax.sql.DataSource;

/**
 * 최초 데이터 베이스 연결 설정을 구성하는 설정 클래스입니다.
 */
@Configuration
public class DBConfig {

    private final ApplicationContext applicationContext;
    private final VaultKVProperties kvProperties;
    private final VaultDbProperties dbProperties;

    public DBConfig(ApplicationContext applicationContext, VaultKVProperties kvProperties, VaultDbProperties dbProperties) {
        this.applicationContext = applicationContext;
        this.kvProperties = kvProperties;
        this.dbProperties = dbProperties;
    }

    /**
     * DataSource 구성
     *
     * @return
     */
    @Bean
    public DataSource dataSource() {
        HikariConfig hikariConfig = new HikariConfig();

        // 1. KV Secret Engine에서 조회된 값
        System.out.println(&quot;kvProperties = &quot; + kvProperties.getDbUrl());
        System.out.println(&quot;kvProperties = &quot; + kvProperties.getDbPort());
        System.out.println(&quot;kvProperties = &quot; + kvProperties.getDbName());

        // 2. KV Secret Engine 내에 조회한 속성을 조회하여 세팅함.
        // [구조예시] jdbc:postgresql://localhost:5432/testdb
        hikariConfig.setJdbcUrl(
                String.format(&quot;jdbc:postgresql://%s:%s/%s&quot;,
                        kvProperties.getDbUrl(),
                        kvProperties.getDbPort(),
                        kvProperties.getDbName())
        );
        // 3. DB Secret Engine 내에서 조회한 속성을 조회하여 HikariCP에 세팅함.(동적 사용자, 비밀번호)
        hikariConfig.setUsername(dbProperties.getUsername());
        hikariConfig.setPassword(dbProperties.getPassword());

        System.out.println(&quot;dbProperties = &quot; + dbProperties.getUsername());
        System.out.println(&quot;dbProperties = &quot; + dbProperties.getPassword());

        // 4. 기타 HikariCP 설정
        hikariConfig.setDriverClassName(&quot;org.postgresql.Driver&quot;);
        hikariConfig.setConnectionTimeout(10000); //커넥션 풀에서 새로운 커넥션을 가져올 때 최대 몇 ms까지 기다릴지를 설정 (단위: 밀리초)
        hikariConfig.setValidationTimeout(13000); //커넥션이 유효한지 테스트할 때 (connection.isValid()) 검증 쿼리가 완료될 때까지 기다릴 최대 시간 (단위: 밀리초)
        hikariConfig.setIdleTimeout(300000);      //풀에 있는 커넥션이 아무 작업도 하지 않고 대기(idle) 상태로 유지될 수 있는 최대 시간 (단위: 밀리초)
        hikariConfig.setMaxLifetime(600000);     // 커넥션이 생성된 후 존재할 수 있는 최대 시간 (단위: 밀리초) - 10분
        hikariConfig.setMaximumPoolSize(5);
        hikariConfig.setMinimumIdle(1);
        hikariConfig.setConnectionTestQuery(&quot;SELECT 1&quot;);
        hikariConfig.setInitializationFailTimeout(-1);
        return new HikariDataSource(hikariConfig);
    }

    /**
     * MyBatis 구성
     *
     * @param dataSource
     * @return
     * @throws Exception
     */
    @Bean
    public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
        SqlSessionFactoryBean session = new SqlSessionFactoryBean();
        session.setDataSource(dataSource);
        session.setMapperLocations(applicationContext.getResources(&quot;classpath:mapper/*.xml&quot;));
        session.setTypeAliasesPackage(&quot;com.adjh.springboot3vault.model&quot;);
        session.setConfigLocation(new PathMatchingResourcePatternResolver().getResource(&quot;classpath:config/common-mybatis-config.xml&quot;));
        return session.getObject();
    }

    @Bean
    public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
        return new SqlSessionTemplate(sqlSessionFactory);
    }

}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  아래와 같이 KV Secret Engine 값과 DB Secret Engine 값이 정상적으로 불러와졌습니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1821&quot; data-origin-height=&quot;770&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/m3ndw/dJMcajnmR3C/4jHCw1FeLrwlC5b3TEDyQ0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/m3ndw/dJMcajnmR3C/4jHCw1FeLrwlC5b3TEDyQ0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/m3ndw/dJMcajnmR3C/4jHCw1FeLrwlC5b3TEDyQ0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fm3ndw%2FdJMcajnmR3C%2F4jHCw1FeLrwlC5b3TEDyQ0%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;1821&quot; height=&quot;770&quot; data-origin-width=&quot;1821&quot; data-origin-height=&quot;770&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;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  아래와 같이 서버를 재 실행할 경우 아래와 같이 동적으로 사용자와 비밀번호가 바뀜을 확인하였습니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1829&quot; data-origin-height=&quot;716&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bmrs6D/dJMcabbQUdh/541ZENAOxZDokgP166cJPK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bmrs6D/dJMcabbQUdh/541ZENAOxZDokgP166cJPK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bmrs6D/dJMcabbQUdh/541ZENAOxZDokgP166cJPK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbmrs6D%2FdJMcabbQUdh%2F541ZENAOxZDokgP166cJPK%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;1829&quot; height=&quot;716&quot; data-origin-width=&quot;1829&quot; data-origin-height=&quot;716&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;h3 data-ke-size=&quot;size23&quot;&gt;9. 데이터베이스 연결 확인&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  데이터베이스 연결 확인&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;- 첫 번째 줄에서 KV Secret Engine과 Database Secret Engine이 연결이 됨을 확인하였습니다.&lt;br /&gt;- HikariCP Connection Pool이 연결이 됨을 확인하였습니다.&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1828&quot; data-origin-height=&quot;727&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/xgrYj/dJMb99SExHb/t7KdfImjSxch8c9VssHPEk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/xgrYj/dJMb99SExHb/t7KdfImjSxch8c9VssHPEk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/xgrYj/dJMb99SExHb/t7KdfImjSxch8c9VssHPEk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FxgrYj%2FdJMb99SExHb%2Ft7KdfImjSxch8c9VssHPEk%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;1828&quot; height=&quot;727&quot; data-origin-width=&quot;1828&quot; data-origin-height=&quot;727&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;h3 data-ke-size=&quot;size23&quot;&gt;10. 실제 데이터베이스에 접근하여 데이터를 조회합니다.&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  실제 데이터베이스에 접근하여 데이터를 조회합니다.&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;- TB_USER 테이블 내의 사용자 정보를 가져오는 비즈니스 로직을 구성합니다.&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;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;&amp;nbsp;  아래와 같이 MyBatis를 통해서 SQL문을 구성했고 이를 구성하는 비즈니스 로직은 제외하였습니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1362&quot; data-origin-height=&quot;633&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bdlhPy/dJMcac9CyGc/wAs7J1rgB0VBKfDbD9Ngsk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bdlhPy/dJMcac9CyGc/wAs7J1rgB0VBKfDbD9Ngsk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bdlhPy/dJMcac9CyGc/wAs7J1rgB0VBKfDbD9Ngsk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbdlhPy%2FdJMcac9CyGc%2FwAs7J1rgB0VBKfDbD9Ngsk%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;1362&quot; height=&quot;633&quot; data-origin-width=&quot;1362&quot; data-origin-height=&quot;633&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;h3 data-ke-size=&quot;size23&quot;&gt;11. Controller에 Endpoint로 호출하여서 데이터 조회를 확인합니다.&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  Controller에 Endpoint로 호출하여서 데이터 조회를 확인합니다&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;- 아래와 같은 API를 구성하였고 이를 호출합니다.&lt;/blockquote&gt;
&lt;pre class=&quot;kotlin&quot;&gt;&lt;code&gt;package com.adjh.springboot3vault.controller;

import com.adjh.springboot3vault.dto.UserDto;
import com.adjh.springboot3vault.service.UserService;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatusCode;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@RestController
@RequestMapping(&quot;/api/v1/user&quot;)
@RequiredArgsConstructor
public class UserController {

    private final UserService userService;

    /**
     * 사용자 PK(userSq)로 조회
     */
    @PostMapping(&quot;/{userSq}&quot;)
    public ResponseEntity&amp;lt;UserDto&amp;gt; findByUserSq(@PathVariable Long userSq) {
        UserDto userDto = userService.findByUserSq(userSq);
        return ResponseEntity.ok(userDto);
    }

    /**
     * 사용자 ID로 조회
     */
    @PostMapping(&quot;/userId&quot;)
    public ResponseEntity&amp;lt;UserDto&amp;gt; findByUserId(@RequestParam String userId) {

        UserDto userDto = userService.findByUserId(userId);
        return ResponseEntity.ok(userDto);
    }

    /**
     * 전체 사용자 조회
     */
    @GetMapping(&quot;/user&quot;)
    public ResponseEntity&amp;lt;List&amp;lt;UserDto&amp;gt;&amp;gt; findAll() {
        return ResponseEntity.ok(userService.selectUser());
    }
}

&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;h3 data-ke-size=&quot;size23&quot;&gt;12. 실제 결과를 확인합니다&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  실제 결과를 확인합니다&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- v1/user/user를 통해서 TB_USER 테이블 내의 모든 사용자를 조회하는 비즈니스 로직을 수행했을 때, 모두 잘 가져옴을 확인하였습니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1911&quot; data-origin-height=&quot;923&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rZjjK/dJMcaaD0WWw/tQ8Xrme2YVZAxX7BJjTuz1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rZjjK/dJMcaaD0WWw/tQ8Xrme2YVZAxX7BJjTuz1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rZjjK/dJMcaaD0WWw/tQ8Xrme2YVZAxX7BJjTuz1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FrZjjK%2FdJMcaaD0WWw%2FtQ8Xrme2YVZAxX7BJjTuz1%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;1911&quot; height=&quot;923&quot; data-origin-width=&quot;1911&quot; data-origin-height=&quot;923&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;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  [참고]&lt;/b&gt; SQL에서 아래와 같이 조회를 하였을 때, 연결된 사용자를 확인할 수도 있습니다&lt;/blockquote&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;SELECT * FROM pg_stat_activity;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1449&quot; data-origin-height=&quot;616&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cSsVKe/dJMcafL5U2y/HXxO7I8SgGj6pSHDW8qTF0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cSsVKe/dJMcafL5U2y/HXxO7I8SgGj6pSHDW8qTF0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cSsVKe/dJMcafL5U2y/HXxO7I8SgGj6pSHDW8qTF0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcSsVKe%2FdJMcafL5U2y%2FHXxO7I8SgGj6pSHDW8qTF0%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;1449&quot; height=&quot;616&quot; data-origin-width=&quot;1449&quot; data-origin-height=&quot;616&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;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  [참고]&lt;/b&gt; 아래와 같이 Configuration 파일을 통해서 Datasouce로 연결된 사용자를 확인할 수도 있습니다.&lt;/blockquote&gt;
&lt;div style=&quot;background-color: #2b2b2b; color: #a9b7c6;&quot;&gt;
&lt;pre class=&quot;crystal&quot;&gt;&lt;code&gt;package com.adjh.springboot3vault.config;

import org.springframework.boot.ApplicationRunner;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.sql.DataSource;
import java.sql.Connection;

@Configuration
public class VaultDebugRunner {

    @Bean
    public ApplicationRunner vaultDbUserRunner(DataSource dataSource) {
        return args -&amp;gt; {
            try (Connection conn = dataSource.getConnection()) {
                System.out.println(&quot;REAL USER = &quot; + conn.getMetaData().getUserName());
                System.out.println(&quot;REAL URL = &quot; + conn.getMetaData().getURL());
            }
        };
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1811&quot; data-origin-height=&quot;677&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lPPSp/dJMcabQscCF/naworCBdg8T8fo70gDfijk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lPPSp/dJMcabQscCF/naworCBdg8T8fo70gDfijk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lPPSp/dJMcabQscCF/naworCBdg8T8fo70gDfijk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlPPSp%2FdJMcabQscCF%2FnaworCBdg8T8fo70gDfijk%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;1811&quot; height=&quot;677&quot; data-origin-width=&quot;1811&quot; data-origin-height=&quot;677&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 data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;  [참고] 해당 코드는 아래의 Repository 내에서 확인이 가능합니다.&lt;/blockquote&gt;
&lt;figure id=&quot;og_1768280812698&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;blog-codes/spring-boot3-vault at main &amp;middot; adjh54ir/blog-codes&quot; data-og-description=&quot;Contributor9 티스토리 블로그 내에서 활용한 내용들을 담은 레포지토리입니다. Contribute to adjh54ir/blog-codes development by creating an account on GitHub.&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/adjh54ir/blog-codes/tree/main/spring-boot3-vault&quot; data-og-url=&quot;https://github.com/adjh54ir/blog-codes/tree/main/spring-boot3-vault&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bmFRRG/dJMb8QL5gJU/YxGIU9PhtP6d2kG7JSMcO0/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/b9wkYa/dJMb9g44oxe/gKIF8odIGlklQTiQPPYit0/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/adjh54ir/blog-codes/tree/main/spring-boot3-vault&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/adjh54ir/blog-codes/tree/main/spring-boot3-vault&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bmFRRG/dJMb8QL5gJU/YxGIU9PhtP6d2kG7JSMcO0/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/b9wkYa/dJMb9g44oxe/gKIF8odIGlklQTiQPPYit0/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;blog-codes/spring-boot3-vault at main &amp;middot; adjh54ir/blog-codes&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Contributor9 티스토리 블로그 내에서 활용한 내용들을 담은 레포지토리입니다. Contribute to adjh54ir/blog-codes development by creating an account on GitHub.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&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;오늘도&amp;nbsp;감사합니다.&amp;nbsp; &lt;/b&gt;&lt;/p&gt;</description>
      <category>Java/Spring Cloud</category>
      <category>spring cloud vault</category>
      <category>vault</category>
      <category>Vault AppRole</category>
      <category>Vault Database Secret Engine</category>
      <category>Vault Database 동적 사용자</category>
      <category>Vault Database 연결방법</category>
      <category>Vault 구성</category>
      <category>Vault 환경구성</category>
      <category>Vault 환경방법</category>
      <category>Vault 활용</category>
      <author>adjh54</author>
      <guid isPermaLink="true">https://adjh54.tistory.com/732</guid>
      <comments>https://adjh54.tistory.com/732#entry732comment</comments>
      <pubDate>Tue, 13 Jan 2026 14:09:19 +0900</pubDate>
    </item>
    <item>
      <title>[Java] Spring Cloud Vault 이해하고 활용하기 -3 : AppRole 인증방식</title>
      <link>https://adjh54.tistory.com/731</link>
      <description>&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;figure contenteditable=&quot;false&quot; data-ke-type=&quot;emoticon&quot; data-ke-align=&quot;alignCenter&quot; data-emoticon-type=&quot;face&quot; data-emoticon-name=&quot;073&quot; data-emoticon-isanimation=&quot;false&quot; data-emoticon-src=&quot;https://t1.daumcdn.net/keditor/emoticon/face/large/073.png&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/keditor/emoticon/face/large/073.png&quot; width=&quot;80&quot; /&gt;&lt;/figure&gt;
&lt;br /&gt;&lt;br /&gt;해당 글에서는 Vault의 인증 방식 중 &amp;lsquo;AppRole&amp;rsquo; 인증방식을 이해하고 설정하는 방법에 대해 알아봅니다.&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;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  [참고]&lt;/b&gt; 이전에 작성한 글들을 확인하고 오시면 큰 도움이 됩니다.&lt;/blockquote&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 55px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;&lt;b&gt;분류&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;&lt;b&gt;URI&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;&lt;b&gt;[Java] Spring Cloud Vault 이해하고 활용하기-1 : 초기 환경, KV 구성 및 Root Token 인증 방식&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;&lt;a href=&quot;https://adjh54.tistory.com/728&quot;&gt;https://adjh54.tistory.com/728&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;&lt;b&gt;[Java] Spring Cloud Vault 이해하고 활용하기-2 : 정책 기반 토큰 발급 및 인증 방식&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;&lt;a href=&quot;https://adjh54.tistory.com/729&quot;&gt;https://adjh54.tistory.com/729&lt;/a&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;h2 data-ke-size=&quot;size26&quot;&gt;1) Vault&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;&amp;nbsp;  Vault&lt;/b&gt;&lt;br /&gt;&lt;b&gt;- HashCorp 사에서 만든 Vault는 다양한 환경에서 애플리케이션의 외부 비밀 속성(예: 데이터베이스 비밀번호, API 키 등)을 외부화된 구성으로 중앙에서 관리할 수 있습니다.&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;- Spring Boot 환경에서 Vault로부터 시크릿 정보를 읽어오며 Valut에 시크릿 정보를 쓰는 것도 가능합니다. 이러한 방식으로 애플리케이션의 중요한 정보는 코드에서 분리되어 보안이 보장됩니다.&lt;br /&gt;- 기밀정보의 동적인 제공, 중앙 집중식 시크릿 관리, 즉각적인 액세스 제어, 감사 추적 기능 등을 제공하여, 기업의 보안 정책을 준수하는 데 도움이 됩니다.&lt;/blockquote&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;1200&quot; data-origin-height=&quot;690&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/BMFwc/dJMcajt5lfa/uhQG4DyWZY13Hj8sza6T1K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/BMFwc/dJMcajt5lfa/uhQG4DyWZY13Hj8sza6T1K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/BMFwc/dJMcajt5lfa/uhQG4DyWZY13Hj8sza6T1K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FBMFwc%2FdJMcajt5lfa%2FuhQG4DyWZY13Hj8sza6T1K%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;1200&quot; height=&quot;690&quot; data-origin-width=&quot;1200&quot; data-origin-height=&quot;690&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;h3 data-ke-size=&quot;size23&quot;&gt;1. Vault의 특징&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&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: 26.7442%;&quot;&gt;&lt;b&gt;특징&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 73.1395%;&quot;&gt;&lt;b&gt;설명&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 26.7442%;&quot;&gt;&lt;b&gt;기밀정보의 동적 제공&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 73.1395%;&quot;&gt;애플리케이션이 필요에 따라 비밀정보를 요청하고, Vault는 요청된 정보를 제공합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 26.7442%;&quot;&gt;&lt;b&gt;중앙 집중식 시크릿 관리&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 73.1395%;&quot;&gt;모든 애플리케이션의 시크릿 정보를 중앙에서 관리할 수 있습니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 26.7442%;&quot;&gt;&lt;b&gt;즉각적인 액세스 제어&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 73.1395%;&quot;&gt;애플리케이션이 Vault에 접근 할 수 있는 &amp;lsquo;특정 토큰 아이디&amp;rsquo;를 기반으로 Vault 서비스를 호출하고, Vault는 해당 '특정 토큰 아이디'를 수신한 뒤 요청된 '비밀정보'를 제공합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 26.7442%;&quot;&gt;&lt;b&gt;감사 추적 기능&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 73.1395%;&quot;&gt;Vault는 모든 액세스 요청을 로깅하여 감사를 위한 추적이 가능합니다.&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;h3 data-ke-size=&quot;size23&quot;&gt;2. Vault 인증 방식&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  Vault 인증 방식&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- Vault에 접근하기 위한 인증 방식을 의미합니다. Root Token, 정책 기반 토큰 (Policy-based Token), AppRole 등의 인증 방식을 통해서 Vault를 접근합니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style2&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 28.6047%;&quot;&gt;&lt;b&gt;인증 방식&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 71.2791%;&quot;&gt;&lt;b&gt;설명&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 28.6047%;&quot;&gt;&lt;b&gt;Root Token&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 71.2791%;&quot;&gt;- 모든 권한을 가진 &amp;lsquo;루트 권한 토큰&amp;rsquo;을 이용하는 인증 방식이며, 정책, 인증 방식, 시크릿 엔진, 모든 데이터 접근 가능한 인증 방식입니다.&lt;br /&gt;- 절대적인 권한을 가지고 있기에, 직접적으로 애플리케이션 레벨에서 인증방식으로 사용되지 않습니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 28.6047%;&quot;&gt;&lt;b&gt;정책 기반 토큰 &lt;br /&gt;(Policy-based Token)&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 71.2791%;&quot;&gt;정책(policy)에 의해 접근 범위가 제한된 &amp;lsquo;토큰&amp;rsquo;을 이용하는 인증 방식이며, 정책 범위 별로 강제 제한된 권한을 부여하고 접근하는 인증방식 입니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 28.6047%;&quot;&gt;&lt;b&gt;AppRole&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 71.2791%;&quot;&gt;- Role 별로 지정된 정책(Policy)에 따라서 접근 범위가 제한되며, &amp;lsquo;RoleID, Secret ID&amp;rsquo;에 따라 &amp;lsquo;토큰&amp;rsquo;을 발급받아서 접근 가능한 인증 방식입니다. &lt;br /&gt;- 해당 Role은 운영환경 혹은 팀별로 접근을 제어할 수 있으며, 사용자는 Role Id, Secret Id를 통해 인증만 수행하고, 이후 토큰에 대해서는 머신이 이를 갱신/관리하기에 별도의 Token 재발급 과정을 수행하지 않습니다.&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;h3 data-ke-size=&quot;size23&quot;&gt;3. Vault 수행 과정&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;596&quot; data-origin-height=&quot;221&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/45KUs/dJMcadOamdw/k1kg3R6GYiFIEqXTDs2R6K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/45KUs/dJMcadOamdw/k1kg3R6GYiFIEqXTDs2R6K/img.png&quot; data-alt=&quot;Baeldung on KotlinSpring Boot ConnectionDetails Abstraction ❘ Baeldung ​&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/45KUs/dJMcadOamdw/k1kg3R6GYiFIEqXTDs2R6K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F45KUs%2FdJMcadOamdw%2Fk1kg3R6GYiFIEqXTDs2R6K%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;596&quot; height=&quot;221&quot; data-origin-width=&quot;596&quot; data-origin-height=&quot;221&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Baeldung on KotlinSpring Boot ConnectionDetails Abstraction ❘ Baeldung ​&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  Vault 수행 과정&lt;/b&gt;&lt;br /&gt;&lt;b&gt;&lt;br /&gt;1. Spring Boot &amp;rarr; Hashicorp Vault: Secret Request(비밀정보 요청)&lt;/b&gt; &lt;br /&gt;- Spring Boot 애플리케이션에서는 서버를 실행할 때, Valut에 접근할 수 있는 &amp;lsquo;특정 토큰 아이디&amp;rsquo;(Secret Request)&amp;rsquo; 를 기반으로 Vault 서비스를 호출하여 &amp;lsquo;비밀 정보(Secret)&amp;rsquo;를 검색합니다.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;2. Hashicorp Vault &amp;rarr; Spring Boot: Secret Response(비밀정보 응답)&lt;/b&gt; &lt;br /&gt;-&amp;nbsp;Valut 서비스는 Spring Boot 애플리케이션의 &amp;lsquo;특정 토큰 아이디&amp;rsquo;(Secret Request)&amp;rsquo;를 수신하고 요청된 &amp;lsquo;비밀 정보(Secret)&amp;rsquo;를 전달합니다.&lt;br /&gt;- 이 단계를 통해서 애플리케이션은 필요한 시크릿 정보를 안전하게 받아올 수 있습니다.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;3. Spring Boot &amp;rarr; Remote Service : Connection Request(커넥션 요청)&lt;/b&gt; &lt;br /&gt;-&amp;nbsp;Spring Boot 애플리케이션이 원격 서비스와 연결을 시도하는 과정을 의미합니다.&lt;br /&gt;- 이 단계에서 애플리케이션은 Vault에서 안전하게 가져온 비밀 정보(예: 데이터베이스 비밀번호, API 키 등)를 사용하여 원격 서비스에 연결 요청을 보냅니다.&lt;br /&gt;- 이렇게 함으로써, 중요한 정보를 코드 내에 직접 작성하거나 노출시키지 않아도 되어 보안을 더 효과적으로 관리할 수 있습니다.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;4. Remote Service &amp;rarr; Spring Boot : Connection Response(커넥션 연결) &lt;br /&gt;&lt;/b&gt;-&amp;nbsp;Vault 서비스가 Spring Boot 애플리케이션의 Secret Request를 수신하고, 요청된 '비밀 정보'를 애플리케이션으로 전송하는 단계입니다.&lt;br /&gt;- 이 단계를 통해 애플리케이션은 필요한 시크릿 정보를 안전하게 받아올 수 있습니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2) Vault 인증 방식의 강점&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 소스코드 내에 민감정보 누출을 줄일 수 있습니다&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  소스코드 내에 민감정보 누출을 줄일 수 있습니다&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;- 전체 프로젝트 내에서 소스코드의 민감 정보는 포함되지 않고, 최초 서버 실행 시 Vault Secret Engine에서 불러온 값을 토대로 모두 조회해 옵니다. &lt;br /&gt;- 만약 소스코드가 누출이 되더라도 주요한 민감정보에 대한 접근 권한을 가지고 있기에 누출이 발생하지 않습니다.&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;640&quot; data-origin-height=&quot;476&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/GRMyx/dJMcah4allO/LzmJj3XuJ2cBZnkJzmLk21/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/GRMyx/dJMcah4allO/LzmJj3XuJ2cBZnkJzmLk21/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/GRMyx/dJMcah4allO/LzmJj3XuJ2cBZnkJzmLk21/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FGRMyx%2FdJMcah4allO%2FLzmJj3XuJ2cBZnkJzmLk21%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;640&quot; height=&quot;476&quot; data-origin-width=&quot;640&quot; data-origin-height=&quot;476&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. 분산된 민감 정보를 중앙 관리할 수 있습니다.&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  분산된 민감 정보를 중앙 관리할 수 있습니다.&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 해당 강점은 단순히 소스코드 내에서 보안 취약점을 숨기는 수준이 아니라 &amp;lsquo;애플리케이션 구조&amp;rsquo;와 &amp;lsquo;보안 책임&amp;rsquo; 자체를 분리하는 과정입니다.&lt;/b&gt;&lt;br /&gt;- 소스코드 내에 하드코딩 되어 있던 키 값들을 일괄적으로 Vault로 관리함으로써 분리된 &amp;lsquo;보안 책임&amp;rsquo;을 일괄 관리가 가능합니다.&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;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  아래와 같이 각각 분리된 키들이 있습니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;978&quot; data-origin-height=&quot;585&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Cab1t/dJMcaiBXqGm/gzBqhCwkXhcMiqM67HG1D1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Cab1t/dJMcaiBXqGm/gzBqhCwkXhcMiqM67HG1D1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Cab1t/dJMcaiBXqGm/gzBqhCwkXhcMiqM67HG1D1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FCab1t%2FdJMcaiBXqGm%2FgzBqhCwkXhcMiqM67HG1D1%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;978&quot; height=&quot;585&quot; data-origin-width=&quot;978&quot; data-origin-height=&quot;585&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;894&quot; data-origin-height=&quot;540&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/XgG3J/dJMcaiBXqGv/Kwkax4jMJxqG0RWEUheZVk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/XgG3J/dJMcaiBXqGv/Kwkax4jMJxqG0RWEUheZVk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/XgG3J/dJMcaiBXqGv/Kwkax4jMJxqG0RWEUheZVk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FXgG3J%2FdJMcaiBXqGv%2FKwkax4jMJxqG0RWEUheZVk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;894&quot; height=&quot;540&quot; data-origin-width=&quot;894&quot; data-origin-height=&quot;540&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  아래와 같이 KV Secret Engine 내에서 일괄된 키들을 관리할 수 있습니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;704&quot; data-origin-height=&quot;347&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rLWgW/dJMcafyvGE4/m1Kpe9VnGkckVOGH8R67C0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rLWgW/dJMcafyvGE4/m1Kpe9VnGkckVOGH8R67C0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rLWgW/dJMcafyvGE4/m1Kpe9VnGkckVOGH8R67C0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FrLWgW%2FdJMcafyvGE4%2Fm1Kpe9VnGkckVOGH8R67C0%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;704&quot; height=&quot;347&quot; data-origin-width=&quot;704&quot; data-origin-height=&quot;347&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;492&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dC3wBJ/dJMcafyvGFd/wGpvEg24f6yOkRF151Gfhk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dC3wBJ/dJMcafyvGFd/wGpvEg24f6yOkRF151Gfhk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dC3wBJ/dJMcafyvGFd/wGpvEg24f6yOkRF151Gfhk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdC3wBJ%2FdJMcafyvGFd%2FwGpvEg24f6yOkRF151Gfhk%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;1024&quot; height=&quot;492&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;492&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;h3 data-ke-size=&quot;size23&quot;&gt;3. 접근 권한을 주기적으로 회전(Rotation)하고, 필요시 즉시 폐기할 수 있어서 보안 사고 발생 시 피해를 최소화할 수 있습니다.&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  접근 권한을 주기적으로 회전(Rotation)하고, 필요 시 즉시 폐기할 수 있어서 보안 사고 발생 시 피해를 최소화할 수 있습니다.&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;- AppRole 방식을 이용하면 Secret ID에 대해서 TTL(Time To Live), Num of uses(사용 횟수 제한)을 지정할 수 있습니다.&lt;br /&gt;- 그리고, 만약 보안 사고 발생 시, Secret Id를 즉시 만료하여서 동적으로 노출이 되어도 피해가 최소화될 수 있습니다.&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;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  아래와 같이 최초 Secret Id를 발급하였습니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;773&quot; data-origin-height=&quot;485&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/baSlRt/dJMcagxpJd9/tBo76MrI0AI3e4lrrIp1K0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/baSlRt/dJMcagxpJd9/tBo76MrI0AI3e4lrrIp1K0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/baSlRt/dJMcagxpJd9/tBo76MrI0AI3e4lrrIp1K0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbaSlRt%2FdJMcagxpJd9%2FtBo76MrI0AI3e4lrrIp1K0%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;773&quot; height=&quot;485&quot; data-origin-width=&quot;773&quot; data-origin-height=&quot;485&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;blockquote data-ke-style=&quot;style3&quot;&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt; 정상적으로 Vault를 접속해서 KV 데이터를 조회해 왔습니다.&lt;/b&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;571&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bN75Bk/dJMcahwknt1/CZ27mrvy3NLQpEcUGvASPK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bN75Bk/dJMcahwknt1/CZ27mrvy3NLQpEcUGvASPK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bN75Bk/dJMcahwknt1/CZ27mrvy3NLQpEcUGvASPK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbN75Bk%2FdJMcahwknt1%2FCZ27mrvy3NLQpEcUGvASPK%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;1024&quot; height=&quot;571&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;571&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;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  아래와 같이 즉시 만료를 하였습니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;pre class=&quot;shell&quot; data-ke-language=&quot;shell&quot;&gt;&lt;code&gt;# 특정 AppRole의 secret_id 만료
vault write auth/approle/role/dev-role/secret-id/destroy secret_id=&amp;lt;SECRET_ID&amp;gt;

# or

# 모든 AppRole의 secret_id 만료
$ vault write auth/approle/role/my-role/secret-id/destroy-all&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1040&quot; data-origin-height=&quot;479&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mmuM7/dJMcadOaGEd/kyABlqcQy8s2o4cygSeVLK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mmuM7/dJMcadOaGEd/kyABlqcQy8s2o4cygSeVLK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mmuM7/dJMcadOaGEd/kyABlqcQy8s2o4cygSeVLK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmmuM7%2FdJMcadOaGEd%2FkyABlqcQy8s2o4cygSeVLK%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;1040&quot; height=&quot;479&quot; data-origin-width=&quot;1040&quot; data-origin-height=&quot;479&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;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  서버에서 Vault 접근 시 만료가 됨을 확인할 수 있습니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;625&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bBQEWy/dJMcagEda6g/TEq8IBa9K3U1FRCDhlOssk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bBQEWy/dJMcagEda6g/TEq8IBa9K3U1FRCDhlOssk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bBQEWy/dJMcagEda6g/TEq8IBa9K3U1FRCDhlOssk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbBQEWy%2FdJMcagEda6g%2FTEq8IBa9K3U1FRCDhlOssk%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;1280&quot; height=&quot;625&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;625&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;h3 data-ke-size=&quot;size23&quot;&gt;4. 각 시스템/팀별로 Role을 나눌 수 있습니다.&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  각 시스템/팀별로 Role을 나눌 수 있습니다.&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- dev-role, qa-role, prd-role, cicd-role, backend-role 등 역할 단위로 정책(Policy), TTL, Secret ID 발급 방식을 개별 설정할 수 있어, 운영 및 DevOps 환경에서 권한 관리와 보안 정책을 유연하게 적용할 수 있습니다.&lt;/b&gt;&lt;br /&gt;- 또한 팀별로 Vault 접근 권한을 명확히 제한함으로써, 필요 최소한의 권한만 부여하는 원칙(Least Privilege)을 효과적으로 구현할 수 있습니다.&lt;br /&gt;- 예를 들어서, dev-role의 경우는 kv2_dev에 대해서만 접근 가능 및 읽기, 쓰기, 수정이 가능하다고 Role을 지정할 수 있습니다. 그리고 prd-role의 경우는 오직 읽기 권한만 가능하다고 Role을 지정할 수 있습니다.&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1039&quot; data-origin-height=&quot;488&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dsuPz8/dJMcajnj3Nq/XsYLkkguLaMxwVA2OFkgh1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dsuPz8/dJMcajnj3Nq/XsYLkkguLaMxwVA2OFkgh1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dsuPz8/dJMcajnj3Nq/XsYLkkguLaMxwVA2OFkgh1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdsuPz8%2FdJMcajnj3Nq%2FXsYLkkguLaMxwVA2OFkgh1%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;1039&quot; height=&quot;488&quot; data-origin-width=&quot;1039&quot; data-origin-height=&quot;488&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3) App Role 인증방식&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  App Role 인증방식&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 조직 내 팀, 서비스, 환경 단위로 '접근 가능한 권한을 분리'하여서 Vault에서 정책(Policy)을 정의하고, 이를 기반으로 AppRole 기준으로 각각 Role ID, Secret ID를 발급받아서 Vault에 인증을 요청하면 Vault에서는 토큰을 발급해 주는 인증 방식을 의미합니다.&lt;/b&gt;&lt;br /&gt;- 사용자는 Role Id, Secret Id를 통해 인증만 수행하고, 이후 토큰에 대해서는 머신이 이를 갱신/관리하기에 별도의 Token 재발급 과정을 수행하지 않습니다&lt;br /&gt;- 예를 들어서, 개발팀, DevOps, 백엔드 서버, CI/CD 파이프라인, 특정 마이크로서비스 등 주체별로 AppRole을 각각 나누어서 각각의 정책(Policy) 및 접근 권한을 부여하여 관리합니다.&lt;/blockquote&gt;
&lt;figure id=&quot;og_1767676378081&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;Use AppRole authentication | Vault | HashiCorp Developer&quot; data-og-description=&quot;Use AppRole authentication with Vault to control how machines and services authenticate to Vault.&quot; data-og-host=&quot;developer.hashicorp.com&quot; data-og-source-url=&quot;https://developer.hashicorp.com/vault/docs/auth/approle&quot; data-og-url=&quot;https://developer.hashicorp.com/vault/docs/auth/approle&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/c0MIxK/dJMb9dHgMGC/MlwHhRWwsC27Mj7eKkLEMk/img.jpg?width=3200&amp;amp;height=1800&amp;amp;face=0_0_3200_1800,https://scrap.kakaocdn.net/dn/bo0Tuq/dJMb9lkZWTy/KsRz0Cpv14EzYKKDkoPUk0/img.jpg?width=3200&amp;amp;height=1800&amp;amp;face=0_0_3200_1800&quot;&gt;&lt;a href=&quot;https://developer.hashicorp.com/vault/docs/auth/approle&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://developer.hashicorp.com/vault/docs/auth/approle&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/c0MIxK/dJMb9dHgMGC/MlwHhRWwsC27Mj7eKkLEMk/img.jpg?width=3200&amp;amp;height=1800&amp;amp;face=0_0_3200_1800,https://scrap.kakaocdn.net/dn/bo0Tuq/dJMb9lkZWTy/KsRz0Cpv14EzYKKDkoPUk0/img.jpg?width=3200&amp;amp;height=1800&amp;amp;face=0_0_3200_1800');&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;Use AppRole authentication | Vault | HashiCorp Developer&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Use AppRole authentication with Vault to control how machines and services authenticate to Vault.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;developer.hashicorp.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;h3 data-ke-size=&quot;size23&quot;&gt;1. Role ID&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  Role ID&lt;/b&gt;&lt;br /&gt;&lt;b&gt;&lt;br /&gt;- AppRole의 고유 식별자로 어떤 역할을 수행하는지를 나타내는 아이디입니다.&lt;/b&gt;&lt;br /&gt;- 예를 들어서, 해당 Role ID 값에 따라서 조직 내 팀, 서비스, 환경 단위로 분류가 가능하며, DevOps, 백엔드 서버, CI/CD 파이프라인, 특정 마이크로서비스 등 주체별로 각각 나누어서 어떤 역할을 수행할지 정의합니다.&lt;br /&gt;- 해당 값은 역할이 무엇인지를 의미하는 ID와 같기에 유출되어도 단독으로는 아무 권한도 없습니다.&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;792&quot; data-origin-height=&quot;606&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/czxBah/dJMcaaRvPav/zeceKQFukshsAuoy4gyMC1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/czxBah/dJMcaaRvPav/zeceKQFukshsAuoy4gyMC1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/czxBah/dJMcaaRvPav/zeceKQFukshsAuoy4gyMC1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FczxBah%2FdJMcaaRvPav%2FzeceKQFukshsAuoy4gyMC1%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;792&quot; height=&quot;606&quot; data-origin-width=&quot;792&quot; data-origin-height=&quot;606&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;h3 data-ke-size=&quot;size23&quot;&gt;2. Secret ID&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  Secret ID&lt;/b&gt;&lt;br /&gt;&lt;b&gt;&lt;br /&gt;- Vault AppRole 인증에서 사용하는 비밀 키로, 해당 요청 주체가 특정 Role에 접근할 권한을 가지고 있음을 증명하는 값을 의미합니다. 또한, Role ID와 함께 사용되어야만 인증이 가능하며, 하나의 Role ID에 대해 여러 개의 Secret ID를 동시에 발급할 수 있습니다.&amp;nbsp;&amp;nbsp;&lt;/b&gt;&lt;br /&gt;- Secret ID는 새로 발급된다고 해서 기존 Secret ID가 자동으로 폐기되지는 않으며, TTL 만료 또는 명시적인 revoke가 이루어져야만 무효화됩니다.&lt;br /&gt;- 보안을 위해 Secret ID는 짧은 TTL 또는 Num of uses(사용 횟수 제한)으로 설정하여 필요한 순간에만 발급&amp;middot;사용되는 소모성 인증 수단으로 사용하는 것이 권장됩니다.&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;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  아래와 같이 최초 Secret Id를 발급하였습니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;773&quot; data-origin-height=&quot;485&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ccjqcH/dJMcahJSeOh/GVMc5kD311GLYGwHmBKdCK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ccjqcH/dJMcahJSeOh/GVMc5kD311GLYGwHmBKdCK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ccjqcH/dJMcahJSeOh/GVMc5kD311GLYGwHmBKdCK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FccjqcH%2FdJMcahJSeOh%2FGVMc5kD311GLYGwHmBKdCK%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;773&quot; height=&quot;485&quot; data-origin-width=&quot;773&quot; data-origin-height=&quot;485&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;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;&amp;nbsp;  정상적으로 Vault를 접속해서 KV 데이터를 조회해 왔습니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;571&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cIleJJ/dJMb99SBxo7/LljsbDKaHdV7hVEPYuOxF1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cIleJJ/dJMb99SBxo7/LljsbDKaHdV7hVEPYuOxF1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cIleJJ/dJMb99SBxo7/LljsbDKaHdV7hVEPYuOxF1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcIleJJ%2FdJMb99SBxo7%2FLljsbDKaHdV7hVEPYuOxF1%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;1024&quot; height=&quot;571&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;571&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;h3 data-ke-size=&quot;size23&quot;&gt;3. Token&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  Token&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 발급받은 Role ID와 Secret ID를 기반으로 Vault에 로그인을 수행하면, 토큰이 발급됩니다. 이는 요청을 하는 주체가 어떤 정책으로 얼마나, 언제까지 접근할 수 있는지를 증명하는 값을 의미합니다.&lt;/b&gt;&lt;br /&gt;- 실제 애플리케이션에서는 직접적으로 토큰을 발급받는 것이 아닌, Role ID와 Secret ID를 기반으로 자격증명을 요청하면 Vault 내에서 토큰을 발급하여 인증을 수행합니다.&lt;br /&gt;- 그렇기에, 애플리케이션은 Secret ID를 이용해 최초 한 번만 인증하고 이후에는 발급된 토큰을 사용해 Vault에 접근하며 토큰은 TTL에 따라 자동으로 만료되거나 갱신됩니다.&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;790&quot; data-origin-height=&quot;601&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bcCAdc/dJMcafyvHat/DvERLV89IlVLbaiQWaf020/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bcCAdc/dJMcafyvHat/DvERLV89IlVLbaiQWaf020/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bcCAdc/dJMcafyvHat/DvERLV89IlVLbaiQWaf020/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbcCAdc%2FdJMcafyvHat%2FDvERLV89IlVLbaiQWaf020%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;790&quot; height=&quot;601&quot; data-origin-width=&quot;790&quot; data-origin-height=&quot;601&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;h3 data-ke-size=&quot;size23&quot;&gt;4. AppRole 프로세스&lt;/h3&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;style3&quot;&gt;&lt;b&gt;&amp;nbsp;  AppRole 프로세스&lt;/b&gt;&lt;br /&gt;&lt;b&gt;&lt;br /&gt;1. Spring Boot 서버 시작: SpringApplication.run()&lt;/b&gt; &lt;br /&gt;-&amp;nbsp;최초 Spring Boot 서버가 실행이 됩니다.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;2. Spring Cloud Vault 자동 구성 로드: bootstrap.yml&lt;/b&gt; &lt;br /&gt;-&amp;nbsp;Spring Cloud Bootstrap에서 최초 Vault 설정에 대한 자동 구성이 로드가 됩니다.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;3. Vault에 '인증(AppRole)' 요청 수행(Role Id, Secret Id)&lt;/b&gt;&lt;br /&gt;- Vault의 인증 방식 중 AppRole을 이용한 방식에서 사전에 팀별로 발급된 Role Id, Secret Id를 통해서 Vault에 인증을 요청합니다.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;4. Vault '인증 정보' 검증&lt;/b&gt; &lt;br /&gt;- Vault에서는 유효한 토큰인지에 대해 인증 정보에 대해 검증을 합니다.&lt;br /&gt;- 성공 시, KV Secret Engine에 접근합니다.&lt;br /&gt;- 실패 시, 서버 시작 실패와 종료를 합니다.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;5. 조회된 Secret을 Spring Boot에 응답&lt;/b&gt; &lt;br /&gt;-&amp;nbsp;KV Secret Engine에서 조회된 정보를 응답으로 전달합니다&lt;br /&gt;&lt;br /&gt;&lt;b&gt;6. 응답받은 Secret을 Spring Environment(PropertySource)에 자동 주입&lt;/b&gt; &lt;br /&gt;-&amp;nbsp;Spring Boot에서는 전달받은 Secret을 Spring Environment(PropertySource)에 자동 주입을 합니다.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;7. @ConfigurationProperties VaultKVProperties.java 객체와 자동 매핑&lt;/b&gt; &lt;br /&gt;-&amp;nbsp;추후에 구성할 VaultKVProperties.java 파일 내에 사전에 KV와 같은 형태로 구성한 멤버 변수 각각에 데이터가 매핑이 됩니다.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;8. Spring Boot 서버 실행 완료&lt;/b&gt; &lt;br /&gt;-&amp;nbsp;최종 작업이 완료되어서 Spring Boot 서버가 실행이 완료가 됩니다.&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1096&quot; data-origin-height=&quot;720&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/AqJte/dJMcahwknYs/vikv0AEGKiR5IhGFIGXrDk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/AqJte/dJMcahwknYs/vikv0AEGKiR5IhGFIGXrDk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/AqJte/dJMcahwknYs/vikv0AEGKiR5IhGFIGXrDk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FAqJte%2FdJMcahwknYs%2Fvikv0AEGKiR5IhGFIGXrDk%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;1096&quot; height=&quot;720&quot; data-origin-width=&quot;1096&quot; data-origin-height=&quot;720&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4) Root Token 인증 방식 vs 정책 기반 토큰 인증 방식 vs AppRole 인증 방식&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&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: 16.8605%;&quot;&gt;&lt;b&gt;구분&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 27.6744%;&quot;&gt;&lt;b&gt;Root Token&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 27.093%;&quot;&gt;&lt;b&gt;정책 기반 토큰 인증 방식&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 28.2558%;&quot;&gt;&lt;b&gt;AppRole 인증 방식&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 16.8605%;&quot;&gt;&lt;b&gt;권한(Privilege)&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 27.6744%;&quot;&gt;Vault 전체 권한(슈퍼 관리자). 모든 작업 허용&lt;/td&gt;
&lt;td style=&quot;width: 27.093%;&quot;&gt;정책(policy) 범위에서만 권한 허용&lt;/td&gt;
&lt;td style=&quot;width: 28.2558%;&quot;&gt;정책(policy)로 제한되며, Role에 부여된 권한만 사용&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 16.8605%;&quot;&gt;&lt;b&gt;보안 위험도&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 27.6744%;&quot;&gt;극도로 높음 (유출 시 Vault 완전 장악)&lt;/td&gt;
&lt;td style=&quot;width: 27.093%;&quot;&gt;비교적 낮음 (정책으로 제한)&lt;/td&gt;
&lt;td style=&quot;width: 28.2558%;&quot;&gt;매우 낮음. RoleID + SecretID 조합이 필요하고, SecretID TTL/1회용 설정 가능&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 16.8605%;&quot;&gt;&lt;b&gt;용도&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 27.6744%;&quot;&gt;초기 구성, 위험한 관리 작업&lt;/td&gt;
&lt;td style=&quot;width: 27.093%;&quot;&gt;일반 사용자/서비스가 직접 토큰을 받아서 사용&lt;/td&gt;
&lt;td style=&quot;width: 28.2558%;&quot;&gt;서버/애플리케이션이 자동 인증할 때 사용 (사람이 아닌 머신/서비스용)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 16.8605%;&quot;&gt;&lt;b&gt;TTL / 만료 설정&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 27.6744%;&quot;&gt;기본 없음. 수동 회전 필요&lt;/td&gt;
&lt;td style=&quot;width: 27.093%;&quot;&gt;TTL, Max TTL, Renewable 자유 설정&lt;/td&gt;
&lt;td style=&quot;width: 28.2558%;&quot;&gt;SecretID/Token TTL, Max TTL, Renewable 모두 세밀하게 설정 가능&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 16.8605%;&quot;&gt;&lt;b&gt;정책 적용 방식&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 27.6744%;&quot;&gt;정책 무시하고 모든 권한 가능&lt;/td&gt;
&lt;td style=&quot;width: 27.093%;&quot;&gt;정책 기반으로 반드시 제한&lt;/td&gt;
&lt;td style=&quot;width: 28.2558%;&quot;&gt;Role에 연결된 정책의 권한으로 제한&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 16.8605%;&quot;&gt;&lt;b&gt;사용 주체&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 27.6744%;&quot;&gt;초기 관리자&lt;/td&gt;
&lt;td style=&quot;width: 27.093%;&quot;&gt;개발자, 운영자, 서비스 계정&lt;/td&gt;
&lt;td style=&quot;width: 28.2558%;&quot;&gt;백엔드 서버, 배치 서버, 컨테이너, CICD &amp;mdash; 사람이 아닌 머신&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 16.8605%;&quot;&gt;&lt;b&gt;재발급 / 갱신&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 27.6744%;&quot;&gt;거의 없음&lt;/td&gt;
&lt;td style=&quot;width: 27.093%;&quot;&gt;TTL 만료 시 재발급 가능&lt;/td&gt;
&lt;td style=&quot;width: 28.2558%;&quot;&gt;SecretID 자동 생성 가능, 주기적 Rotation 가능, 토큰도 자동 갱신 가능&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 16.8605%;&quot;&gt;&lt;b&gt;발급 방법&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 27.6744%;&quot;&gt;초기화 시 자동 생성&lt;/td&gt;
&lt;td style=&quot;width: 27.093%;&quot;&gt;vault token create 또는 Auth Method(OIDC, GitHub 등)&lt;/td&gt;
&lt;td style=&quot;width: 28.2558%;&quot;&gt;SecretID 생성 &amp;rarr; 서버가 RoleID + SecretID로 로그인 하여 Token 자동 발급&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 16.8605%;&quot;&gt;&lt;b&gt;운영 관점&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 27.6744%;&quot;&gt;일반 운영에서 절대 사용 금지&lt;/td&gt;
&lt;td style=&quot;width: 27.093%;&quot;&gt;사용 가능하지만 사람이 직접 관리해야 하는 불편함 있음&lt;/td&gt;
&lt;td style=&quot;width: 28.2558%;&quot;&gt;자동화에 최적, 서버 재기동해도 RoleID/SecretID로 자동 로그인&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 16.8605%;&quot;&gt;&lt;b&gt;Audit Logging&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 27.6744%;&quot;&gt;모든 작업 기록되나 너무 광범위&lt;/td&gt;
&lt;td style=&quot;width: 27.093%;&quot;&gt;정책 기반이라 세밀한 기록 가능&lt;/td&gt;
&lt;td style=&quot;width: 28.2558%;&quot;&gt;RoleID 노출되어도 SecretID 없으면 무용지물 &amp;rarr; 가장 안전한 로깅 가능&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 16.8605%;&quot;&gt;&lt;b&gt;토큰 폐기(Revocation)&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 27.6744%;&quot;&gt;폐기하면 복구가 복잡&lt;/td&gt;
&lt;td style=&quot;width: 27.093%;&quot;&gt;토큰만 폐기 가능&lt;/td&gt;
&lt;td style=&quot;width: 28.2558%;&quot;&gt;RoleID/SecretID/발급된 토큰 각각 독립적으로 폐기 가능&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 16.8605%;&quot;&gt;&lt;b&gt;적합한 상황&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 27.6744%;&quot;&gt;초기 설정, 정책 생성, 비상 상황&lt;/td&gt;
&lt;td style=&quot;width: 27.093%;&quot;&gt;단순 테스트, 개발자가 직접 토큰 사용 시&lt;/td&gt;
&lt;td style=&quot;width: 28.2558%;&quot;&gt;운영 서버 인증, 자동화, 배포 파이프라인, 백엔드 서비스 연동&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 16.8605%;&quot;&gt;&lt;b&gt;장점&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 27.6744%;&quot;&gt;무제한 권한&lt;/td&gt;
&lt;td style=&quot;width: 27.093%;&quot;&gt;설정이 직관적&lt;/td&gt;
&lt;td style=&quot;width: 28.2558%;&quot;&gt;매우 안전, 자동화 가능, SecretID 1회용/TTL로 보안 극대화&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 16.8605%;&quot;&gt;&lt;b&gt;단점&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 27.6744%;&quot;&gt;너무 위험함&lt;/td&gt;
&lt;td style=&quot;width: 27.093%;&quot;&gt;장기적으로 운영 자동화에 적합하지 않음&lt;/td&gt;
&lt;td style=&quot;width: 28.2558%;&quot;&gt;개념이 조금 복잡. RoleID + SecretID 관리 필요&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;h2 data-ke-size=&quot;size26&quot;&gt;5) App Role 구성 -1 : AppRole 활성화 &amp;amp; Policy 생성&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. Vault 탭에서 Access를 선택합니다.&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1907&quot; data-origin-height=&quot;845&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/wqe8H/dJMcahpzKmf/MTfaXt2BEwT7ijM0aDJXQ1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/wqe8H/dJMcahpzKmf/MTfaXt2BEwT7ijM0aDJXQ1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/wqe8H/dJMcahpzKmf/MTfaXt2BEwT7ijM0aDJXQ1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fwqe8H%2FdJMcahpzKmf%2FMTfaXt2BEwT7ijM0aDJXQ1%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;1907&quot; height=&quot;845&quot; data-origin-width=&quot;1907&quot; data-origin-height=&quot;845&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;h3 data-ke-size=&quot;size23&quot;&gt;2. Authentication Methods 내에서 &amp;lsquo;Enable new method&amp;rsquo;를 선택합니다.&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1922&quot; data-origin-height=&quot;851&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mp66X/dJMb9958vas/ZyyOKEJGTsT15qk6TMC5H0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mp66X/dJMb9958vas/ZyyOKEJGTsT15qk6TMC5H0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mp66X/dJMb9958vas/ZyyOKEJGTsT15qk6TMC5H0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fmp66X%2FdJMb9958vas%2FZyyOKEJGTsT15qk6TMC5H0%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;1922&quot; height=&quot;851&quot; data-origin-width=&quot;1922&quot; data-origin-height=&quot;851&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;h3 data-ke-size=&quot;size23&quot;&gt;3. &amp;lsquo;AppRole&amp;rsquo;을 선택합니다.&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1917&quot; data-origin-height=&quot;848&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bw1580/dJMcadgl07Y/KoR67jEHSaUWUC703Y6Dx0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bw1580/dJMcadgl07Y/KoR67jEHSaUWUC703Y6Dx0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bw1580/dJMcadgl07Y/KoR67jEHSaUWUC703Y6Dx0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbw1580%2FdJMcadgl07Y%2FKoR67jEHSaUWUC703Y6Dx0%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;1917&quot; height=&quot;848&quot; data-origin-width=&quot;1917&quot; data-origin-height=&quot;848&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;h3 data-ke-size=&quot;size23&quot;&gt;4. 우선 기본값으로 선택하고 &amp;lsquo;Enable method&amp;rsquo;를 누릅니다&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1918&quot; data-origin-height=&quot;684&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lRk8q/dJMcab3WXiu/tKEsVQCnekLzg4W5zqmGM1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lRk8q/dJMcab3WXiu/tKEsVQCnekLzg4W5zqmGM1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lRk8q/dJMcab3WXiu/tKEsVQCnekLzg4W5zqmGM1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlRk8q%2FdJMcab3WXiu%2FtKEsVQCnekLzg4W5zqmGM1%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;1918&quot; height=&quot;684&quot; data-origin-width=&quot;1918&quot; data-origin-height=&quot;684&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;h3 data-ke-size=&quot;size23&quot;&gt;5. 아래와 같이 생성이 되었습니다&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1916&quot; data-origin-height=&quot;675&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cJj0DJ/dJMcaiBXrjl/NZI6UofLDXA2xLjVRClf8k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cJj0DJ/dJMcaiBXrjl/NZI6UofLDXA2xLjVRClf8k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cJj0DJ/dJMcaiBXrjl/NZI6UofLDXA2xLjVRClf8k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcJj0DJ%2FdJMcaiBXrjl%2FNZI6UofLDXA2xLjVRClf8k%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;1916&quot; height=&quot;675&quot; data-origin-width=&quot;1916&quot; data-origin-height=&quot;675&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;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;&amp;nbsp;  [참고]&lt;/b&gt; 해당 과정은 위의 과정은 CLI를 통해서 수행이 가능합니다.&lt;/blockquote&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;$ vault auth enable approle
&lt;/code&gt;&lt;/pre&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;771&quot; data-origin-height=&quot;223&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dJi5eb/dJMcafL23Tg/YZGQO60aM0Rrk09KljYoq0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dJi5eb/dJMcafL23Tg/YZGQO60aM0Rrk09KljYoq0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dJi5eb/dJMcafL23Tg/YZGQO60aM0Rrk09KljYoq0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdJi5eb%2FdJMcafL23Tg%2FYZGQO60aM0Rrk09KljYoq0%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;771&quot; height=&quot;223&quot; data-origin-width=&quot;771&quot; data-origin-height=&quot;223&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;h3 data-ke-size=&quot;size23&quot;&gt;6. 홈 화면에서 &amp;lsquo;Polices&amp;rsquo;를 선택합니다&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1898&quot; data-origin-height=&quot;852&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/SFrcR/dJMcaacT0QF/0Wx2r60g6VSKLLuHURJcx0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/SFrcR/dJMcaacT0QF/0Wx2r60g6VSKLLuHURJcx0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/SFrcR/dJMcaacT0QF/0Wx2r60g6VSKLLuHURJcx0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FSFrcR%2FdJMcaacT0QF%2F0Wx2r60g6VSKLLuHURJcx0%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;1898&quot; height=&quot;852&quot; data-origin-width=&quot;1898&quot; data-origin-height=&quot;852&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;h3 data-ke-size=&quot;size23&quot;&gt;7. ACL Policies - &amp;lsquo;Create ACL policy&amp;rsquo;를 선택합니다&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1905&quot; data-origin-height=&quot;852&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bCrPIA/dJMcadUWPMX/u1uel3nk7KBxNG6IkKKAb1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bCrPIA/dJMcadUWPMX/u1uel3nk7KBxNG6IkKKAb1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bCrPIA/dJMcadUWPMX/u1uel3nk7KBxNG6IkKKAb1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbCrPIA%2FdJMcadUWPMX%2Fu1uel3nk7KBxNG6IkKKAb1%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;1905&quot; height=&quot;852&quot; data-origin-width=&quot;1905&quot; data-origin-height=&quot;852&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;h3 data-ke-size=&quot;size23&quot;&gt;8. Name, Policy를 입력하고 &amp;lsquo;Create Policy&amp;rsquo;를 선택합니다.&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  Name, Policy를 입력합니다&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;- 사전에 구성한 KV Engine의 kv2라는 이름에 대해 읽기 권한을 부여하였습니다&lt;/blockquote&gt;
&lt;pre class=&quot;nginx&quot;&gt;&lt;code&gt;path &quot;kv2/data/*&quot; {
  capabilities = [&quot;read&quot;, &quot;list&quot;]
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1904&quot; data-origin-height=&quot;846&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b6pVtD/dJMcab3WXnu/CvmsQ4tKxykBDCwsRmgrlK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b6pVtD/dJMcab3WXnu/CvmsQ4tKxykBDCwsRmgrlK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b6pVtD/dJMcab3WXnu/CvmsQ4tKxykBDCwsRmgrlK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb6pVtD%2FdJMcab3WXnu%2FCvmsQ4tKxykBDCwsRmgrlK%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;1904&quot; height=&quot;846&quot; data-origin-width=&quot;1904&quot; data-origin-height=&quot;846&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;h2 data-ke-size=&quot;size26&quot;&gt;6) App Role 구성 -2 : AppRole 구성&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  App Role 구성 -2 : AppRole 구성&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 해당 AppRole에 대해서는 AppRole 관리를 위한 GUI를 지원하지 않고 CLI 기반으로 수행을 합니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. Role을 생성합니다.&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  역할을 생성합니다.&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- approle의 role을 생성하는데 &amp;lsquo;dev-role&amp;rsquo;이라는 이름으로 생성을 합니다. 그리고 위에서 만든 &amp;lsquo;dev-approle-policy&amp;rsquo;를 연결하였습니다.&lt;/b&gt;&lt;br /&gt;- 또한, 아래와 같은 옵션을 두고 생성을 하였습니다.&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;790&quot; data-origin-height=&quot;604&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/yqwvU/dJMcabv7k8p/mP73ZxJuJSVAGMwVeqMQiK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/yqwvU/dJMcabv7k8p/mP73ZxJuJSVAGMwVeqMQiK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/yqwvU/dJMcabv7k8p/mP73ZxJuJSVAGMwVeqMQiK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FyqwvU%2FdJMcabv7k8p%2FmP73ZxJuJSVAGMwVeqMQiK%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;790&quot; height=&quot;604&quot; data-origin-width=&quot;790&quot; data-origin-height=&quot;604&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;pre class=&quot;shell&quot; data-ke-language=&quot;shell&quot;&gt;&lt;code&gt;vault write auth/approle/role/dev-role \
    policies=&quot;dev-approle-policy&quot; \
    token_type=batch \
    secret_id_ttl=10m \
    token_ttl=20m \
    token_max_ttl=30m \
    secret_id_num_uses=40&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style2&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;설정&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;의미&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;실제 동작&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;token_type=batch&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;가벼운 단기 토큰, 저장 안됨, 갱신 불가&lt;/td&gt;
&lt;td&gt;API 서버/단기 작업에 적합&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;secret_id_ttl=10m&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;SecretID는 생성 후 10분 동안만 사용 가능&lt;/td&gt;
&lt;td&gt;짧은 노출 시간이 필요한 경우&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;token_ttl=20m&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;로그인 후 발급되는 토큰의 기본 TTL&lt;/td&gt;
&lt;td&gt;20분 지나면 자동 만료&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;token_max_ttl=30m&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;토큰이 절대 넘길 수 없는 시간&lt;/td&gt;
&lt;td&gt;batch 토큰이라 갱신 불가, 의미 제한적&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;secret_id_num_uses=40&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;SecretID를 최대 40번 사용할 수 있음&lt;/td&gt;
&lt;td&gt;40번 or 10분 중 먼저 도달 시 종료&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;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;&amp;nbsp;  [참고]&lt;/b&gt; 생성한 Role에 대한 조회를 하는 방법은 아래와 같습니다.&lt;/blockquote&gt;
&lt;pre class=&quot;crmsh&quot;&gt;&lt;code&gt;vault read auth/approle/role/dev-role
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;794&quot; data-origin-height=&quot;604&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/1JIt5/dJMcah4al7Z/iW2JBCftn0GK5a8muC4VB1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/1JIt5/dJMcah4al7Z/iW2JBCftn0GK5a8muC4VB1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/1JIt5/dJMcah4al7Z/iW2JBCftn0GK5a8muC4VB1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F1JIt5%2FdJMcah4al7Z%2FiW2JBCftn0GK5a8muC4VB1%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;794&quot; height=&quot;604&quot; data-origin-width=&quot;794&quot; data-origin-height=&quot;604&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;h3 data-ke-size=&quot;size23&quot;&gt;2. 생성한 AppRole의 Role ID를 가져옵니다.&lt;/h3&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;style3&quot;&gt;&lt;b&gt;&amp;nbsp;  생성한 AppRole의 Role ID를 가져옵니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;pre class=&quot;shell&quot; data-ke-language=&quot;shell&quot;&gt;&lt;code&gt;$ vault read auth/approle/role/dev-role/role-id&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;792&quot; data-origin-height=&quot;606&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bUTTSu/dJMcacu1wZS/306PGfr1xvq77vcEN2V4Yk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bUTTSu/dJMcacu1wZS/306PGfr1xvq77vcEN2V4Yk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bUTTSu/dJMcacu1wZS/306PGfr1xvq77vcEN2V4Yk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbUTTSu%2FdJMcacu1wZS%2F306PGfr1xvq77vcEN2V4Yk%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;792&quot; height=&quot;606&quot; data-origin-width=&quot;792&quot; data-origin-height=&quot;606&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;h3 data-ke-size=&quot;size23&quot;&gt;3. 생성한 AppRole의 SecretID를 가져옵니다.&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  생성한 AppRole의 SecretID를 가져옵니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;gradle&quot;&gt;&lt;code&gt;$ vault write -f auth/approle/role/dev-role/secret-id
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;790&quot; data-origin-height=&quot;605&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/wRZh0/dJMcadOaHqY/JrjkKG02RWWGFxDXHeC6Y0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/wRZh0/dJMcadOaHqY/JrjkKG02RWWGFxDXHeC6Y0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/wRZh0/dJMcadOaHqY/JrjkKG02RWWGFxDXHeC6Y0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FwRZh0%2FdJMcadOaHqY%2FJrjkKG02RWWGFxDXHeC6Y0%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;790&quot; height=&quot;605&quot; data-origin-width=&quot;790&quot; data-origin-height=&quot;605&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;h3 data-ke-size=&quot;size23&quot;&gt;4. 토큰을 발급받습니다&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;790&quot; data-origin-height=&quot;601&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/vBlN5/dJMcahwkn8x/LDvFWNqVnkSC0j9fHmlfU1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/vBlN5/dJMcahwkn8x/LDvFWNqVnkSC0j9fHmlfU1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/vBlN5/dJMcahwkn8x/LDvFWNqVnkSC0j9fHmlfU1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FvBlN5%2FdJMcahwkn8x%2FLDvFWNqVnkSC0j9fHmlfU1%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;790&quot; height=&quot;601&quot; data-origin-width=&quot;790&quot; data-origin-height=&quot;601&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;pre id=&quot;code_1767677901354&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ vault write auth/approle/login \
    role_id=&quot;&amp;lt;ROLE_ID&amp;gt;&quot; \
    secret_id=&quot;&amp;lt;SECRET_ID&amp;gt;&quot;
    
  
 # 사용예시 
$ vault write auth/approle/login \
    role_id=&quot;f76173e0-d539-e47e-39ee-6fde6384b75d&quot; \
    secret_id=&quot;1bbee7b9-999e-33df-258f-da7583c6515b&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5. 발급받은 토큰으로 로그인합니다.&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1903&quot; data-origin-height=&quot;847&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mJXpz/dJMcai21oWv/QVO2mOkwoNybATzgT0HYU0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mJXpz/dJMcai21oWv/QVO2mOkwoNybATzgT0HYU0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mJXpz/dJMcai21oWv/QVO2mOkwoNybATzgT0HYU0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmJXpz%2FdJMcai21oWv%2FQVO2mOkwoNybATzgT0HYU0%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;1903&quot; height=&quot;847&quot; data-origin-width=&quot;1903&quot; data-origin-height=&quot;847&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;h3 data-ke-size=&quot;size23&quot;&gt;6. 접근이 완료되었습니다&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1916&quot; data-origin-height=&quot;847&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lMyQW/dJMcagc7ZqW/7nuyOhgluzuqkbPjWo7Ffk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lMyQW/dJMcagc7ZqW/7nuyOhgluzuqkbPjWo7Ffk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lMyQW/dJMcagc7ZqW/7nuyOhgluzuqkbPjWo7Ffk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlMyQW%2FdJMcagc7ZqW%2F7nuyOhgluzuqkbPjWo7Ffk%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;1916&quot; height=&quot;847&quot; data-origin-width=&quot;1916&quot; data-origin-height=&quot;847&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;h2 data-ke-size=&quot;size26&quot;&gt;7) Spring Boot 환경 구성&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 개발 환경&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  개발 환경&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 기본 기능과 주요 라이브러리인 spring-cloud-dependencies, spring-cloud-starter-bootstrap, spring-cloud-starter-vault-config를 설정하였습니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style2&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;개발 환경&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;분류&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;버전&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Java JDK&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Java Version&lt;/td&gt;
&lt;td&gt;17&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;빌드 도구&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Gradle&lt;/td&gt;
&lt;td&gt;8.14.3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Spring Boot&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Spring Boot Starter&lt;/td&gt;
&lt;td&gt;3.5.8&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Spring Boot Web&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Spring Boot Starter&lt;/td&gt;
&lt;td&gt;3.5.8&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;spring-cloud-starter-bootstrap&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Spring Boot Cloud&lt;/td&gt;
&lt;td&gt;2025.0.0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;spring-cloud-starter-vault-config&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Spring Boot Cloud&lt;/td&gt;
&lt;td&gt;2025.0.0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;spring-cloud-dependencies&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Spring Boot Cloud&lt;/td&gt;
&lt;td&gt;2025.0.0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Lombok&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Library&lt;/td&gt;
&lt;td&gt;latest&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;h3 data-ke-size=&quot;size23&quot;&gt;2. 라이브러리 설치 : build.gradle&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  라이브러리 설치 : build.gradle&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 위에서 명시한 라이브러리를 Gradle을 이용하여서 설치합니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;plugins {
    id 'java'
    id 'org.springframework.boot' version '3.5.8'
    id 'io.spring.dependency-management' version '1.1.7'
}
ext {
    springCloudVersion = &quot;2025.0.0&quot;
}

group = 'com.adjh'
version = '0.0.1-SNAPSHOT'
description = 'spring-boot3-vault'

java {
    toolchain {
        languageVersion = JavaLanguageVersion.of(17)
    }
}

configurations {
    compileOnly {
        extendsFrom annotationProcessor
    }
}

repositories {
    mavenCentral()
}

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-web'           // Spring Boot Web

    implementation 'org.springframework.cloud:spring-cloud-starter-bootstrap'   // Spring Cloud BootStrap
    implementation 'org.springframework.cloud:spring-cloud-starter-vault-config'// Spring Cloud Vault
    compileOnly 'org.projectlombok:lombok'                                      // Lombok Compile
    annotationProcessor 'org.projectlombok:lombok'                              // Lombok Annotation
    testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
    testImplementation 'org.springframework.boot:spring-boot-starter-test'
}
dependencyManagement {
    imports {
        mavenBom &quot;org.springframework.cloud:spring-cloud-dependencies:$springCloudVersion&quot;
    }
}

tasks.named('test') {
    useJUnitPlatform()
}&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;h3 data-ke-size=&quot;size23&quot;&gt;3. 시스템 환경변수 주입 : IntelliJ IDE 활용&lt;/h3&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;style3&quot;&gt;&lt;b&gt;&amp;nbsp;  시스템 환경변수 주입 : IntelliJ IDE 활용&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- Vault로 전달이 되는 Main Key 자체를 프로젝트 내에 넣고 Git에 올릴 수 없기에 &amp;lsquo;시스템 환경 변수&amp;rsquo; 내에 추가합니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  [참고]&lt;/b&gt;&amp;nbsp;아래의 환경 변수를 추가하는 방법의 글을 참고하셔도 도움이 됩니다.&lt;/blockquote&gt;
&lt;figure id=&quot;og_1767680230272&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;[Java] Spring Boot 환경에서 시스템 변수 .env 파일 지정 및 활용 방법&quot; data-og-description=&quot;해당 글에서는 Spring Boot 개발 환경에서 .env 파일을 시스템 변수로 지정하는 방법과 이를 불러오는 다양한 방법에 대해서 알아봅니다1) Spring Boot 환경에서 .env 파일을 시스템 변수로 적용하는 이&quot; data-og-host=&quot;adjh54.tistory.com&quot; data-og-source-url=&quot;https://adjh54.tistory.com/722&quot; data-og-url=&quot;https://adjh54.tistory.com/722&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/9aS7N/hyZQ8KQbjx/uc4fXTwjALK7VPliI0cUjk/img.png?width=800&amp;amp;height=432&amp;amp;face=0_0_800_432,https://scrap.kakaocdn.net/dn/ln1Ed/hyZQ7yqelQ/skud8le098ubBKF0rclX8k/img.png?width=800&amp;amp;height=432&amp;amp;face=0_0_800_432,https://scrap.kakaocdn.net/dn/ouO9v/hyZQ0F4GM9/7CSRXFQckVxeMTQf0GW0qk/img.png?width=1313&amp;amp;height=539&amp;amp;face=0_0_1313_539&quot;&gt;&lt;a href=&quot;https://adjh54.tistory.com/722&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://adjh54.tistory.com/722&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/9aS7N/hyZQ8KQbjx/uc4fXTwjALK7VPliI0cUjk/img.png?width=800&amp;amp;height=432&amp;amp;face=0_0_800_432,https://scrap.kakaocdn.net/dn/ln1Ed/hyZQ7yqelQ/skud8le098ubBKF0rclX8k/img.png?width=800&amp;amp;height=432&amp;amp;face=0_0_800_432,https://scrap.kakaocdn.net/dn/ouO9v/hyZQ0F4GM9/7CSRXFQckVxeMTQf0GW0qk/img.png?width=1313&amp;amp;height=539&amp;amp;face=0_0_1313_539');&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;[Java] Spring Boot 환경에서 시스템 변수 .env 파일 지정 및 활용 방법&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;해당 글에서는 Spring Boot 개발 환경에서 .env 파일을 시스템 변수로 지정하는 방법과 이를 불러오는 다양한 방법에 대해서 알아봅니다1) Spring Boot 환경에서 .env 파일을 시스템 변수로 적용하는 이&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;adjh54.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;figure id=&quot;og_1767680232276&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;[Java] Spring Cloud Vault 이해하고 활용하기-1 : 초기 환경, KV 구성 및 Root Token 인증 방식&quot; data-og-description=&quot;해당 글에서는 Spring Cloud Vault를 이해하고 Vault를 구성하고 KV Secret Engine 내에서 데이터를 조회하는 토큰 인증방식을 이용하는 방법에 대해 알아봅니다 1) Vault  Vault- HashCorp 사에서 만든 Vault는 &quot; data-og-host=&quot;adjh54.tistory.com&quot; data-og-source-url=&quot;https://adjh54.tistory.com/728&quot; data-og-url=&quot;https://adjh54.tistory.com/728&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/ctio8n/hyZRaWcoPv/37N9KjkSQD1sfCkWMUqqVk/img.png?width=800&amp;amp;height=431&amp;amp;face=0_0_800_431,https://scrap.kakaocdn.net/dn/ccFdcc/hyZQ1Zjg9g/kpZSmkexkc0QdkktTSmko0/img.png?width=800&amp;amp;height=431&amp;amp;face=0_0_800_431,https://scrap.kakaocdn.net/dn/orBiL/hyZQ8YpISD/1EsDVGlUH31nZRv5nr5P21/img.png?width=1917&amp;amp;height=932&amp;amp;face=0_0_1917_932&quot;&gt;&lt;a href=&quot;https://adjh54.tistory.com/728&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://adjh54.tistory.com/728&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/ctio8n/hyZRaWcoPv/37N9KjkSQD1sfCkWMUqqVk/img.png?width=800&amp;amp;height=431&amp;amp;face=0_0_800_431,https://scrap.kakaocdn.net/dn/ccFdcc/hyZQ1Zjg9g/kpZSmkexkc0QdkktTSmko0/img.png?width=800&amp;amp;height=431&amp;amp;face=0_0_800_431,https://scrap.kakaocdn.net/dn/orBiL/hyZQ8YpISD/1EsDVGlUH31nZRv5nr5P21/img.png?width=1917&amp;amp;height=932&amp;amp;face=0_0_1917_932');&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;[Java] Spring Cloud Vault 이해하고 활용하기-1 : 초기 환경, KV 구성 및 Root Token 인증 방식&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;해당 글에서는 Spring Cloud Vault를 이해하고 Vault를 구성하고 KV Secret Engine 내에서 데이터를 조회하는 토큰 인증방식을 이용하는 방법에 대해 알아봅니다 1) Vault  Vault- HashCorp 사에서 만든 Vault는&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;adjh54.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;h3 data-ke-size=&quot;size23&quot;&gt;4. bootstrap.yml 파일을 생성 및 구성합니다.&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  bootstrap.yml 파일을 생성 및 구성합니다.&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;- resources 경로 하단에 bootstrap.yml 파일을 생성하였습니다&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;461&quot; data-origin-height=&quot;595&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bsuR8c/dJMcagxpMyS/1Wsj8fgTsXl3Y2vgGmaf1k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bsuR8c/dJMcagxpMyS/1Wsj8fgTsXl3Y2vgGmaf1k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bsuR8c/dJMcagxpMyS/1Wsj8fgTsXl3Y2vgGmaf1k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbsuR8c%2FdJMcagxpMyS%2F1Wsj8fgTsXl3Y2vgGmaf1k%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;461&quot; height=&quot;595&quot; data-origin-width=&quot;461&quot; data-origin-height=&quot;595&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;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  yml 파일을 아래와 같이 구성하였습니다.&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 사전 단계에서 발급받은 VAULT_APP_ROLE_ID, VAULT_APP_ROLE_SECRET 값을 환경 변수로 추가하였고 이를 불러옵니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;spring:
  cloud:
    vault:
      uri: http://localhost:8200
      # 인증방식 : APPID, APPROLE, AWS_EC2, AWS_IAM, AZURE_MSI, CERT, CUBBYHOLE, GCP_GCE, GCP_IAM, KUBERNETES, NONE, PCF, TOKEN;
      authentication: APPROLE                   # Vault 인증 방식 (TOKEN, APPROLE, APPID 등)
      connection-timeout: 5000                  # Vault 서버 연결 시도 타임아웃(ms)
      read-timeout: 15000                       # Vault 데이터 읽기 타임아웃(ms)

      app-role:
        role-id: ${VAULT_APP_ROLE_ID}           # 환경변수에서 Vault 접근 role-id를 불러옴
        secret-id: ${VAULT_APP_ROLE_SECRET}     # 환경변수에서 Vault 접근 role-secret를 불러옴
        role: dev-role                          # 사전에 할당된 role을 불러옴

      kv:
        enabled: true                           # KV(키-값) 비밀 엔진 사용 여부
        backend: kv2                            # Vault에서 생성한 엔진 이름(ex: kv2), 반드시 일치해야 함
                                                # 실제 Vault 경로가 &quot;kv2/&quot; 라면 backend도 kv2로 설정해야 함

        default-context: loc                    # 기본 Secret 경로 (예: kv2/loc/data/application 같은 구조가 됨)
                                                # 즉, Vault UI에서는 /kv2/data/loc 경로와 매핑됨

        application-name: ''                    # Spring 애플리케이션 이름을 Secret 경로에 추가할지 여부
                                                # 빈 값이면 &quot;default-context&quot; 바로 아래에서 조회함
                                                # 예: default: '' &amp;rarr; kv2/loc/data&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;h3 data-ke-size=&quot;size23&quot;&gt;5. 서버 실행&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  서버 실행&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 정상적으로 오류 없이 서버가 실행하였습니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1845&quot; data-origin-height=&quot;614&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/tPM1e/dJMcafrKCln/IK0jMgopXIS7M3LEfxMiu0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/tPM1e/dJMcafrKCln/IK0jMgopXIS7M3LEfxMiu0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/tPM1e/dJMcafrKCln/IK0jMgopXIS7M3LEfxMiu0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FtPM1e%2FdJMcafrKCln%2FIK0jMgopXIS7M3LEfxMiu0%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;1845&quot; height=&quot;614&quot; data-origin-width=&quot;1845&quot; data-origin-height=&quot;614&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;6. Vault 값과 객체 매핑&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  Vault 값과 객체 매핑&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- Vault에서 읽어온 값을 자바 클래스 필드에 자동 바인딩을 하는 클래스입니다.&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;- @Getter, @Setter 통해서 멤버 변수에 접근할 수 있도록 어노테이션을 지정하였습니다.&lt;br /&gt;- @Component를 빈으로 등록하여서, 다른 서비스나 컴포넌트에 주입하여 사용이 가능하도록 하였습니다.&lt;br /&gt;- @ConfigurationProperties(prefix = &quot;vault&quot;) : 상위 prefix 내에 하위에 동일한 이름으로 각각 자동 바인딩이 됩니다.&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1879&quot; data-origin-height=&quot;616&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bimMfv/dJMcabQpzw8/AoVtxxmc4rDatBmKTRBSM0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bimMfv/dJMcabQpzw8/AoVtxxmc4rDatBmKTRBSM0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bimMfv/dJMcabQpzw8/AoVtxxmc4rDatBmKTRBSM0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbimMfv%2FdJMcabQpzw8%2FAoVtxxmc4rDatBmKTRBSM0%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;1879&quot; height=&quot;616&quot; data-origin-width=&quot;1879&quot; data-origin-height=&quot;616&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre class=&quot;kotlin&quot;&gt;&lt;code&gt;package com.adjh.springboot3vault.properties;

import lombok.Getter;
import lombok.Setter;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;

/**
 * Vault Key-Value Properties Mapping value
 *
 * @author : leejonghoon
 * @fileName : VaultKVProperties
 * @since : 25. 11. 25.
 */
@Getter
@Setter
@Component
@ConfigurationProperties(prefix = &quot;&quot;)
public class VaultKVProperties {
    private String appEnv;
    private String dbHost;
    private String dbPassword;
    private String dbUser;
}

&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;h3 data-ke-size=&quot;size23&quot;&gt;7. 콘솔을 확인하기 위해 Controller를 구성하였습니다.&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  콘솔을 확인하기 위해 Controller를 구성하였습니다.&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 생성자 패턴으로 bean을 호출하여서 Controller 내에서 활용하도록 구성하였습니다.&lt;/b&gt;&lt;br /&gt;- http://localhost:8080 url로 접근하여서 &amp;lsquo;/api/v1/vault/kv&amp;rsquo;로 호출이 된다면 각각의 vault 값이 정상적으로 출력이 됩니다.&lt;/blockquote&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;package com.adjh.springboot3vault.controller;

import com.adjh.springboot3vault.properties.VaultKVProperties;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * Bean을 호출하여서 Vault 값을 테스트하기 위한 Controller
 *
 * @author : leejonghoon
 * @fileName : VaultController
 * @since : 25. 11. 25.
 */
@RestController
@RequestMapping(&quot;/api/v1/vault&quot;)
public class VaultController {

    private final VaultKVProperties vaultKVProperties;

    public VaultController(VaultKVProperties vaultKVProperties) {
        this.vaultKVProperties = vaultKVProperties;
    }

    @GetMapping(&quot;/kv&quot;)
    public String main() {
        System.out.println(&quot;Getter appEnv : &quot; + vaultKVProperties.getAppEnv());
        System.out.println(&quot;Getter dbHost : &quot; + vaultKVProperties.getDbHost());
        System.out.println(&quot;Getter dbPassword : &quot; + vaultKVProperties.getDbPassword());
        System.out.println(&quot;Getter dbUser : &quot; + vaultKVProperties.getDbUser());
        return &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;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;8. 결과확인&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  결과확인&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 정상적으로 출력이 됨을 확인하였습니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1908&quot; data-origin-height=&quot;338&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/vHaXN/dJMb99LO8yN/fNmupORHgBapEUWinglpm1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/vHaXN/dJMb99LO8yN/fNmupORHgBapEUWinglpm1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/vHaXN/dJMb99LO8yN/fNmupORHgBapEUWinglpm1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FvHaXN%2FdJMb99LO8yN%2FfNmupORHgBapEUWinglpm1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1908&quot; height=&quot;338&quot; data-origin-width=&quot;1908&quot; data-origin-height=&quot;338&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&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;오늘도&amp;nbsp;감사합니다.&amp;nbsp; &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;</description>
      <category>Java/Spring Cloud</category>
      <category>AppRole</category>
      <category>AppRole 구성</category>
      <category>AppRole 수행과정</category>
      <category>AppRole 인증 방식</category>
      <category>Spring</category>
      <category>Spring Cloud</category>
      <category>spring cloud vault</category>
      <category>Vault AppRole</category>
      <category>Vault AppRole 인증 방식</category>
      <category>Vault AppRole 프로세스</category>
      <author>adjh54</author>
      <guid isPermaLink="true">https://adjh54.tistory.com/731</guid>
      <comments>https://adjh54.tistory.com/731#entry731comment</comments>
      <pubDate>Tue, 6 Jan 2026 20:00:50 +0900</pubDate>
    </item>
    <item>
      <title>[제작 앱 소개] 나이 계산기: 오늘의 나이</title>
      <link>https://adjh54.tistory.com/730</link>
      <description>&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;해당&amp;nbsp;글에서는&amp;nbsp;Contributor9&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;&lt;/span&gt;&lt;/span&gt;
&lt;figure contenteditable=&quot;false&quot; data-ke-type=&quot;emoticon&quot; data-ke-align=&quot;alignCenter&quot; data-emoticon-type=&quot;face&quot; data-emoticon-name=&quot;062&quot; data-emoticon-isanimation=&quot;false&quot; data-emoticon-src=&quot;https://t1.daumcdn.net/keditor/emoticon/face/large/062.png&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/keditor/emoticon/face/large/062.png&quot; width=&quot;80&quot; /&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1) 개발자는 왜 이 앱을 만들었을까?&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  개발자는 왜 이 앱을 만들었을까?&lt;/b&gt; &lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 만 나이 통일법에 대해 알고 있으신가요?&lt;/b&gt;&lt;br /&gt;2023년 시행된 법으로 기존에는 한국 나이가 출생일을 기준으로 '1살'로 시작되어서 생일이 지날 때마다 1살씩 더해지는 나이였습니다. &lt;br /&gt;해당 법이 시행된 이후에는 태어난 해는 '0살'이 되고 생일이 지날 때 1살이 더해지는 나이가 됩니다. 즉, 예전에 '만 나이'로 불리던 나이가 지금의 나이로 통일된 법입니다.&lt;br /&gt;&lt;br /&gt;&lt;a href=&quot;https://m.terms.naver.com/entry.naver?docId=6648039&amp;amp;cid=43667&amp;amp;categoryId=43667&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://m.terms.naver.com/entry.naver?docId=6648039&amp;amp;cid=43667&amp;amp;categoryId=43667&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 기존에는 나의 나이가 한국식 나이, 만 나이, '빠른'이라는 개념이 추가되어서 내 나이를 이야기하기가 참 복잡했습니다.&lt;/b&gt;&lt;br /&gt;&lt;b&gt;그렇기에 누군가가 &quot;나이가 몇 살이신가요?&quot;라고 물어보면 &quot;저는 00년생입니다&quot;라고 이야기를 해왔던 것 같습니다.&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;u&gt;&lt;b&gt;- 법이 시행된 지금은 하나로 통일된 나이가 되었지만, 아직까지 통용되는 나이인 '과거 한국식 나이'를 확인해 볼 수 있고, 실제 나이가 되는 '만 나이' 그리고 '연 나이'를 한눈에 볼 수 있는 앱입니다.&lt;/b&gt;&lt;/u&gt;&lt;br /&gt;생년월일을&amp;nbsp;입력하면&amp;nbsp;기본적인&amp;nbsp;나이에&amp;nbsp;대한&amp;nbsp;정보를&amp;nbsp;확인하는&amp;nbsp;것은&amp;nbsp;물론,&amp;nbsp;과거&amp;nbsp;조선시대의&amp;nbsp;불려졌던&amp;nbsp;나이별&amp;nbsp;이칭을&amp;nbsp;제공해 줍니다.&lt;br /&gt;또한, 나의 생년월일을 기준으로 12간지, 탄생석, 탄생화, 탄생목, 탄생석, 별자리, 수호성, 태어난 절기에 대한 탄생과 관련된 정보들을 제공해 줍니다.&lt;br /&gt;&lt;br /&gt;- 이런 정보 제공뿐만 아니라, '즐겨찾기' 기능을 통해, 내 정보뿐만 아니라 가족, 연인, 지인들에 대한 정보도 나만의 약칭을 포함하여서 관리할 수 있는 기능으로 제공을 합니다. 그리고 특별한 날에 맞는 미리 푸시메시지까지 제공합니다.&lt;br /&gt;&lt;br /&gt;주위&amp;nbsp;사람의&amp;nbsp;나이가&amp;nbsp;헷갈린다면,&amp;nbsp;나이&amp;nbsp;계산기&amp;nbsp;앱에&amp;nbsp;저장하고&amp;nbsp;관리해 보세요!&amp;nbsp;그리고&amp;nbsp;필요한&amp;nbsp;순간에&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;사람들을&amp;nbsp;알아갈 수&amp;nbsp;있는&amp;nbsp;앱이라고&amp;nbsp;생각합니다.&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;h2 data-ke-size=&quot;size26&quot;&gt;2) 앱 소개- 나이 계산기: 오늘의 나이&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1358&quot; data-origin-height=&quot;335&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nmsDi/dJMcaihBeSC/omTrMsKzC74jjK1DIKHdY1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nmsDi/dJMcaihBeSC/omTrMsKzC74jjK1DIKHdY1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nmsDi/dJMcaihBeSC/omTrMsKzC74jjK1DIKHdY1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnmsDi%2FdJMcaihBeSC%2FomTrMsKzC74jjK1DIKHdY1%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;1358&quot; height=&quot;335&quot; data-origin-width=&quot;1358&quot; data-origin-height=&quot;335&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  앱 소개- 나이 계산기: 오늘의 나이&lt;br /&gt;&lt;br /&gt;-&amp;nbsp;나이&amp;nbsp;계산기:&amp;nbsp;오늘의&amp;nbsp;나이&amp;nbsp;앱은&amp;nbsp;단순한&amp;nbsp;나이&amp;nbsp;계산&amp;nbsp;기능을&amp;nbsp;넘어,&amp;nbsp;당신의&amp;nbsp;생일에&amp;nbsp;담긴&amp;nbsp;의미와&amp;nbsp;인생의&amp;nbsp;흐름을&amp;nbsp;알려주는&amp;nbsp;앱입니다.&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;- 누군가 내 나이나 가족의 나이를 물어볼 때, &quot;음&amp;hellip; 잘 모르겠는데, 아마 00년생이었을 거예요.&quot;라고 대답한 적이 있지 않으신가요? 또는&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;계산기:&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;일수,&amp;nbsp;12지신&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;-&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;&lt;br /&gt;&lt;b&gt; &amp;nbsp;주요&amp;nbsp;기능&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;1.&amp;nbsp;나이&amp;nbsp;계산&lt;br /&gt;&lt;/b&gt;&lt;br /&gt;-&amp;nbsp;생년월일,&amp;nbsp;기준일자를&amp;nbsp;기준을&amp;nbsp;입력하여&amp;nbsp;나이&amp;nbsp;계산,&amp;nbsp;생일에&amp;nbsp;담긴&amp;nbsp;의미,&amp;nbsp;인생의&amp;nbsp;흐름을&amp;nbsp;확인할&amp;nbsp;수&amp;nbsp;있습니다.&lt;br /&gt;-&amp;nbsp;생년월일과&amp;nbsp;기준일자를&amp;nbsp;입력하면&amp;nbsp;만&amp;nbsp;나이,&amp;nbsp;연&amp;nbsp;나이,&amp;nbsp;과거&amp;nbsp;한국식&amp;nbsp;나이를&amp;nbsp;즉시&amp;nbsp;계산해&amp;nbsp;줍니다.&lt;br /&gt;-&amp;nbsp;기준일을&amp;nbsp;오늘,&amp;nbsp;한&amp;nbsp;달&amp;nbsp;전,&amp;nbsp;1년&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;생일은&amp;nbsp;'즐겨찾기&amp;nbsp;추가'&amp;nbsp;기능으로&amp;nbsp;쉽게&amp;nbsp;관리할&amp;nbsp;수&amp;nbsp;있습니다.&lt;br /&gt;-&amp;nbsp;계산된&amp;nbsp;결과는&amp;nbsp;복사하거나&amp;nbsp;공유&amp;nbsp;버튼을&amp;nbsp;통해&amp;nbsp;간편하게&amp;nbsp;다른&amp;nbsp;사람과&amp;nbsp;공유할&amp;nbsp;수&amp;nbsp;있습니다&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;2.&amp;nbsp;나이&amp;nbsp;계산&amp;nbsp;결과&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;-&amp;nbsp;나이&amp;nbsp;정보&amp;nbsp;영역에서는&amp;nbsp;생년월일을&amp;nbsp;기준으로&amp;nbsp;만&amp;nbsp;나이,&amp;nbsp;연&amp;nbsp;나이,&amp;nbsp;과거&amp;nbsp;한국식&amp;nbsp;나이를&amp;nbsp;한눈에&amp;nbsp;확인할&amp;nbsp;수&amp;nbsp;있습니다.&lt;br /&gt;-&amp;nbsp;나이별&amp;nbsp;이칭&amp;nbsp;영역에서는&amp;nbsp;과거&amp;nbsp;한국에서&amp;nbsp;나이대별로&amp;nbsp;불리던&amp;nbsp;이름(이칭)을&amp;nbsp;확인할&amp;nbsp;수&amp;nbsp;있으며,&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;-&amp;nbsp;생일&amp;nbsp;정보&amp;nbsp;영역에서는&amp;nbsp;생년월일(양력,&amp;nbsp;음력)을&amp;nbsp;표시하며,&amp;nbsp;다음&amp;nbsp;생일까지의&amp;nbsp;D-DAY,&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;-&amp;nbsp;탄생&amp;nbsp;정보&amp;nbsp;영역에서는&amp;nbsp;입력한&amp;nbsp;생년월일을&amp;nbsp;기준으로&amp;nbsp;12지신,&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;탄생&amp;nbsp;정보를&amp;nbsp;자세히&amp;nbsp;확인할&amp;nbsp;수&amp;nbsp;있습니다.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;3.&amp;nbsp;즐겨찾기&amp;nbsp;목록&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;-&amp;nbsp;즐겨찾기로&amp;nbsp;추가된&amp;nbsp;날짜를&amp;nbsp;기준으로&amp;nbsp;즐겨찾기&amp;nbsp;목록을&amp;nbsp;확인할&amp;nbsp;수&amp;nbsp;있습니다.&lt;br /&gt;-&amp;nbsp;각&amp;nbsp;항목에서는&amp;nbsp;저장된&amp;nbsp;생년월일을&amp;nbsp;기준으로&amp;nbsp;계산된&amp;nbsp;만&amp;nbsp;나이,&amp;nbsp;연나이,&amp;nbsp;한국식&amp;nbsp;나이를&amp;nbsp;바로&amp;nbsp;확인할&amp;nbsp;수&amp;nbsp;있습니다.&lt;br /&gt;-&amp;nbsp;'더&amp;nbsp;보기'&amp;nbsp;버튼을&amp;nbsp;누르면,&amp;nbsp;생년월일을&amp;nbsp;기준으로&amp;nbsp;한&amp;nbsp;탄생&amp;nbsp;정보를&amp;nbsp;카드&amp;nbsp;형태로&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;계산&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;플러스&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;-&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;-&amp;nbsp;알림&amp;nbsp;버튼을&amp;nbsp;누르면&amp;nbsp;일주일&amp;nbsp;전,&amp;nbsp;하루&amp;nbsp;전,&amp;nbsp;당일&amp;nbsp;오전&amp;nbsp;9시에&amp;nbsp;생일&amp;nbsp;알림을&amp;nbsp;받을&amp;nbsp;수&amp;nbsp;있도록&amp;nbsp;설정할&amp;nbsp;수&amp;nbsp;있습니다.&lt;br /&gt;-&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;&lt;br /&gt;&lt;b&gt;4.&amp;nbsp;설정&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;-&amp;nbsp;개인정보&amp;nbsp;처리방침,&amp;nbsp;이용약관,&amp;nbsp;오픈소스&amp;nbsp;라이브러리&amp;nbsp;확인&lt;br /&gt;-&amp;nbsp;현재&amp;nbsp;앱&amp;nbsp;버전&amp;nbsp;및&amp;nbsp;제작자&amp;nbsp;정보&amp;nbsp;제공&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt; &amp;nbsp;이런&amp;nbsp;분께&amp;nbsp;추천합니다&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;-&amp;nbsp;정확한&amp;nbsp;만&amp;nbsp;나이,&amp;nbsp;연나이,&amp;nbsp;한국식&amp;nbsp;나이를&amp;nbsp;알고&amp;nbsp;싶은&amp;nbsp;분&lt;br /&gt;-&amp;nbsp;음력&amp;nbsp;생일이나&amp;nbsp;생일까지&amp;nbsp;남은&amp;nbsp;날짜(D-DAY)를&amp;nbsp;한눈에&amp;nbsp;보고&amp;nbsp;싶은&amp;nbsp;분&lt;br /&gt;-&amp;nbsp;띠,&amp;nbsp;별자리,&amp;nbsp;탄생석,&amp;nbsp;탄생화,&amp;nbsp;탄생색&amp;nbsp;등&amp;nbsp;나만의&amp;nbsp;탄생&amp;nbsp;정보를&amp;nbsp;알고&amp;nbsp;싶은&amp;nbsp;분&lt;br /&gt;-&amp;nbsp;가족이나&amp;nbsp;친구의&amp;nbsp;생일&amp;middot;나이를&amp;nbsp;함께&amp;nbsp;관리하고&amp;nbsp;알림을&amp;nbsp;받고&amp;nbsp;싶은&amp;nbsp;분&lt;br /&gt;-&amp;nbsp;기준일을&amp;nbsp;바꿔가며&amp;nbsp;과거&amp;middot;미래의&amp;nbsp;나이&amp;nbsp;변화를&amp;nbsp;확인해&amp;nbsp;보고&amp;nbsp;싶은&amp;nbsp;분&lt;br /&gt;&lt;br /&gt;⸻&lt;br /&gt;&lt;br /&gt; &amp;nbsp;지금&amp;nbsp;다운로드하고&amp;nbsp;&amp;lsquo;오늘의&amp;nbsp;나이&amp;rsquo;를&amp;nbsp;확인하세요!&lt;br /&gt;당신의&amp;nbsp;생일&amp;nbsp;속에&amp;nbsp;숨은&amp;nbsp;의미를&amp;nbsp;만나보세요&amp;nbsp; ✨&lt;b&gt;&lt;br /&gt;&lt;/b&gt;&lt;/blockquote&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;1359&quot; data-origin-height=&quot;685&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cbzBfU/dJMcagxi3A0/ZFOEUV0YoCrLA7U3CKPjf1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cbzBfU/dJMcagxi3A0/ZFOEUV0YoCrLA7U3CKPjf1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cbzBfU/dJMcagxi3A0/ZFOEUV0YoCrLA7U3CKPjf1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcbzBfU%2FdJMcagxi3A0%2FZFOEUV0YoCrLA7U3CKPjf1%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;1359&quot; height=&quot;685&quot; data-origin-width=&quot;1359&quot; data-origin-height=&quot;685&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1353&quot; data-origin-height=&quot;657&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bkY2me/dJMcabW4v56/q03y4UYkhplY31HTncXzQ0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bkY2me/dJMcabW4v56/q03y4UYkhplY31HTncXzQ0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bkY2me/dJMcabW4v56/q03y4UYkhplY31HTncXzQ0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbkY2me%2FdJMcabW4v56%2Fq03y4UYkhplY31HTncXzQ0%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;1353&quot; height=&quot;657&quot; data-origin-width=&quot;1353&quot; data-origin-height=&quot;657&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3) 다운로드&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1.&amp;nbsp;Google&amp;nbsp;PlayStore&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;450&quot; data-origin-height=&quot;450&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/epvn6p/dJMcahwdDd3/X2dRyakCDHbpiFtsxqUzDK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/epvn6p/dJMcahwdDd3/X2dRyakCDHbpiFtsxqUzDK/img.png&quot; data-alt=&quot;https://play.google.com/store/apps/details?id=com.tha.agecalc&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/epvn6p/dJMcahwdDd3/X2dRyakCDHbpiFtsxqUzDK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fepvn6p%2FdJMcahwdDd3%2FX2dRyakCDHbpiFtsxqUzDK%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;450&quot; height=&quot;450&quot; data-origin-width=&quot;450&quot; data-origin-height=&quot;450&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://play.google.com/store/apps/details?id=com.tha.agecalc&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;figure id=&quot;og_1766117073298&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;나이 계산기: 오늘의 나이 - Google Play 앱&quot; data-og-description=&quot;단순한 나이 계산 기능을 넘어, 당신의 생일에 담긴 의미와 인생의 흐름을 알려주는 앱입니다.&quot; data-og-host=&quot;play.google.com&quot; data-og-source-url=&quot;https://play.google.com/store/apps/details?id=com.tha.agecalc&quot; data-og-url=&quot;https://play.google.com/store/apps/details?id=com.tha.agecalc&amp;amp;hl=ko&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/dGhARE/hyZPCMlfGi/n7MaGlybrhPfszXqxn9E01/img.png?width=512&amp;amp;height=512&amp;amp;face=0_0_512_512,https://scrap.kakaocdn.net/dn/bR3zW2/hyZPtojRmo/XdlPRoT2iZhwnoPJFjvB5k/img.png?width=600&amp;amp;height=300&amp;amp;face=0_0_600_300,https://scrap.kakaocdn.net/dn/bVC69J/hyZPY1kir8/43e0109pKSLdEQRv3JIECK/img.png?width=240&amp;amp;height=240&amp;amp;face=0_0_240_240&quot;&gt;&lt;a href=&quot;https://play.google.com/store/apps/details?id=com.tha.agecalc&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://play.google.com/store/apps/details?id=com.tha.agecalc&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/dGhARE/hyZPCMlfGi/n7MaGlybrhPfszXqxn9E01/img.png?width=512&amp;amp;height=512&amp;amp;face=0_0_512_512,https://scrap.kakaocdn.net/dn/bR3zW2/hyZPtojRmo/XdlPRoT2iZhwnoPJFjvB5k/img.png?width=600&amp;amp;height=300&amp;amp;face=0_0_600_300,https://scrap.kakaocdn.net/dn/bVC69J/hyZPY1kir8/43e0109pKSLdEQRv3JIECK/img.png?width=240&amp;amp;height=240&amp;amp;face=0_0_240_240');&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;나이 계산기: 오늘의 나이 - Google Play 앱&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;play.google.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;h3 data-ke-size=&quot;size23&quot;&gt;2.&amp;nbsp;AppStore&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;490&quot; data-origin-height=&quot;490&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cTh3is/dJMcaaw59zb/9BDwU7StlKIyBQqQ9leOQ0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cTh3is/dJMcaaw59zb/9BDwU7StlKIyBQqQ9leOQ0/img.png&quot; data-alt=&quot;https://apps.apple.com/us/app/id6754360556&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cTh3is/dJMcaaw59zb/9BDwU7StlKIyBQqQ9leOQ0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcTh3is%2FdJMcaaw59zb%2F9BDwU7StlKIyBQqQ9leOQ0%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;490&quot; height=&quot;490&quot; data-origin-width=&quot;490&quot; data-origin-height=&quot;490&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://apps.apple.com/us/app/id6754360556&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;figure id=&quot;og_1766117111011&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;나이 계산기: 오늘의 나이 App - App Store&quot; data-og-description=&quot;Download 나이 계산기: 오늘의 나이 by EcodeLab on the App Store. See screenshots, ratings and reviews, user tips, and more games like 나이 계산기: 오늘의 나이.&quot; data-og-host=&quot;apps.apple.com&quot; data-og-source-url=&quot;https://apps.apple.com/us/app/id6754360556&quot; data-og-url=&quot;https://apps.apple.com/us/app/%EB%82%98%EC%9D%B4-%EA%B3%84%EC%82%B0%EA%B8%B0-%EC%98%A4%EB%8A%98%EC%9D%98-%EB%82%98%EC%9D%B4/id6754360556&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bjYf7y/hyZP14O47j/eik6TXF3MnZeBvpWX4EiL0/img.jpg?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/b3sUNn/hyZOGBqgkD/A0TALGSZYgbMQWFd8p4Gbk/img.jpg?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630&quot;&gt;&lt;a href=&quot;https://apps.apple.com/us/app/id6754360556&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://apps.apple.com/us/app/id6754360556&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bjYf7y/hyZP14O47j/eik6TXF3MnZeBvpWX4EiL0/img.jpg?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/b3sUNn/hyZOGBqgkD/A0TALGSZYgbMQWFd8p4Gbk/img.jpg?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;나이 계산기: 오늘의 나이 App - App Store&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Download 나이 계산기: 오늘의 나이 by EcodeLab on the App Store. See screenshots, ratings and reviews, user tips, and more games like 나이 계산기: 오늘의 나이.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;apps.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;&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;⬇️ 개발자의 다양한 앱을 보고 싶으시면 아래의 링크를 확인해주세요&lt;span&gt;&amp;nbsp;&lt;/span&gt;⬇️&lt;/b&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1779095467074&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;Ecodelab - 제작 앱 소개&quot; data-og-description=&quot;Ecodelab에서 개발한 다양한 앱들을 소개합니다&quot; data-og-host=&quot;www.ecodelab.im&quot; data-og-source-url=&quot;https://ecodelab.im/main&quot; data-og-url=&quot;https://www.ecodelab.im&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bWuJMc/dJMb9eTUZsH/njzMvre05hFxKg2BACFmIk/img.png?width=1024&amp;amp;height=1024&amp;amp;face=0_0_1024_1024,https://scrap.kakaocdn.net/dn/eR1YMg/dJMb8RRXk8e/RlDSZRgjj03yt5Wtc1PKLK/img.png?width=1024&amp;amp;height=1024&amp;amp;face=0_0_1024_1024&quot;&gt;&lt;a href=&quot;https://ecodelab.im/main&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://ecodelab.im/main&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bWuJMc/dJMb9eTUZsH/njzMvre05hFxKg2BACFmIk/img.png?width=1024&amp;amp;height=1024&amp;amp;face=0_0_1024_1024,https://scrap.kakaocdn.net/dn/eR1YMg/dJMb8RRXk8e/RlDSZRgjj03yt5Wtc1PKLK/img.png?width=1024&amp;amp;height=1024&amp;amp;face=0_0_1024_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;Ecodelab - 제작 앱 소개&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Ecodelab에서 개발한 다양한 앱들을 소개합니다&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.ecodelab.im&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;오늘도&amp;nbsp;감사합니다.&amp;nbsp; &lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Contributor9/제작 앱 소개</category>
      <category>12지신 앱</category>
      <category>나이 계산 앱</category>
      <category>나이 계산기</category>
      <category>나이 앱</category>
      <category>나이계산 앱</category>
      <category>나이별 이칭</category>
      <category>만 나이 통일법</category>
      <category>음력 계산 앱</category>
      <category>탄생석</category>
      <category>탄생석 앱</category>
      <author>adjh54</author>
      <guid isPermaLink="true">https://adjh54.tistory.com/730</guid>
      <comments>https://adjh54.tistory.com/730#entry730comment</comments>
      <pubDate>Fri, 19 Dec 2025 13:44:01 +0900</pubDate>
    </item>
    <item>
      <title>[Java] Spring Cloud Vault 이해하고 활용하기-2 : 정책 기반 토큰 발급 및 인증 방식</title>
      <link>https://adjh54.tistory.com/729</link>
      <description>&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;해당 글에서는 Spring Cloud Vault를 활용하는 방법 중 정책 기반 토큰을 발급하고 인증을 하는 방법에 대해서 알아봅니다.&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;
&lt;figure contenteditable=&quot;false&quot; data-ke-type=&quot;emoticon&quot; data-ke-align=&quot;alignCenter&quot; data-emoticon-type=&quot;face&quot; data-emoticon-name=&quot;073&quot; data-emoticon-isanimation=&quot;false&quot; data-emoticon-src=&quot;https://t1.daumcdn.net/keditor/emoticon/face/large/073.png&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/keditor/emoticon/face/large/073.png&quot; width=&quot;80&quot; /&gt;&lt;/figure&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;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt; [참고]&lt;/b&gt; 이전에 작성한 글에서 KV Secret Engine이 구성되었다는 가정하에 이번 글이 이어집니다.&lt;/blockquote&gt;
&lt;figure id=&quot;og_1765420045458&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;[Java] Spring Cloud Vault 이해하고 활용하기-1 : 초기 환경, KV 구성 및 Root Token 인증 방식&quot; data-og-description=&quot;해당 글에서는 Spring Cloud Vault를 이해하고 Vault를 구성하고 KV Secret Engine 내에서 데이터를 조회하는 토큰 인증방식을 이용하는 방법에 대해 알아봅니다 1) Vault  Vault- HashCorp 사에서 만든 Vault는 &quot; data-og-host=&quot;adjh54.tistory.com&quot; data-og-source-url=&quot;https://adjh54.tistory.com/728&quot; data-og-url=&quot;https://adjh54.tistory.com/728&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/tYQrG/hyZPwpVzye/yinoqGFIsngpK6j3yp7mx1/img.png?width=800&amp;amp;height=431&amp;amp;face=0_0_800_431,https://scrap.kakaocdn.net/dn/MaObS/hyZPsHPwSS/9gKt1bZKRg9lk8yXbim821/img.png?width=800&amp;amp;height=431&amp;amp;face=0_0_800_431,https://scrap.kakaocdn.net/dn/csrILE/hyZO7L2ioS/fNoa5Vg1q4dWUs6ORjd9zK/img.png?width=1917&amp;amp;height=932&amp;amp;face=0_0_1917_932&quot;&gt;&lt;a href=&quot;https://adjh54.tistory.com/728&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://adjh54.tistory.com/728&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/tYQrG/hyZPwpVzye/yinoqGFIsngpK6j3yp7mx1/img.png?width=800&amp;amp;height=431&amp;amp;face=0_0_800_431,https://scrap.kakaocdn.net/dn/MaObS/hyZPsHPwSS/9gKt1bZKRg9lk8yXbim821/img.png?width=800&amp;amp;height=431&amp;amp;face=0_0_800_431,https://scrap.kakaocdn.net/dn/csrILE/hyZO7L2ioS/fNoa5Vg1q4dWUs6ORjd9zK/img.png?width=1917&amp;amp;height=932&amp;amp;face=0_0_1917_932');&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;[Java] Spring Cloud Vault 이해하고 활용하기-1 : 초기 환경, KV 구성 및 Root Token 인증 방식&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;해당 글에서는 Spring Cloud Vault를 이해하고 Vault를 구성하고 KV Secret Engine 내에서 데이터를 조회하는 토큰 인증방식을 이용하는 방법에 대해 알아봅니다 1) Vault  Vault- HashCorp 사에서 만든 Vault는&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;adjh54.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;h2 data-ke-size=&quot;size26&quot;&gt;1) Vault&lt;/h2&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;style3&quot;&gt;&lt;b&gt;  Vault&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;- HashCorp 사에서 만든 Vault는 다양한 환경에서 애플리케이션의 외부 비밀 속성(예: 데이터베이스 비밀번호, API 키 등)을 외부화된 구성으로 중앙에서 관리할 수 있습니다.&lt;br /&gt;-Spring Boot 환경에서 Vault로부터 시크릿 정보를 읽어오며 Valut에 시크릿 정보를 쓰는 것도 가능합니다. 이러한 방식으로 애플리케이션의 중요한 정보는 코드에서 분리되어 보안이 보장됩니다.&lt;br /&gt;- 기밀정보의 동적인 제공, 중앙 집중식 시크릿 관리, 즉각적인 액세스 제어, 감사 추적 기능 등을 제공하여, 기업의 보안 정책을 준수하는 데 도움이 됩니다.&lt;/blockquote&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;1200&quot; data-origin-height=&quot;690&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/doXUj6/dJMcagKNGcI/IVZpHtV7uAOXof7VpokKpK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/doXUj6/dJMcagKNGcI/IVZpHtV7uAOXof7VpokKpK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/doXUj6/dJMcagKNGcI/IVZpHtV7uAOXof7VpokKpK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdoXUj6%2FdJMcagKNGcI%2FIVZpHtV7uAOXof7VpokKpK%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;1200&quot; height=&quot;690&quot; data-origin-width=&quot;1200&quot; data-origin-height=&quot;690&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;h3 data-ke-size=&quot;size23&quot;&gt;1. Vault의 특징&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style2&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 25.3488%;&quot;&gt;&lt;b&gt;특징&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 74.5349%;&quot;&gt;&lt;b&gt;설명&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 25.3488%;&quot;&gt;&lt;b&gt;기밀정보의 동적 제공&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 74.5349%;&quot;&gt;애플리케이션이 필요에 따라 비밀정보를 요청하고, Vault는 요청된 정보를 제공합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 25.3488%;&quot;&gt;&lt;b&gt;중앙 집중식 시크릿 관리&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 74.5349%;&quot;&gt;모든 애플리케이션의 시크릿 정보를 중앙에서 관리할 수 있습니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 25.3488%;&quot;&gt;&lt;b&gt;즉각적인 액세스 제어&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 74.5349%;&quot;&gt;애플리케이션이 Vault에 접근 할 수 있는 &amp;lsquo;특정 토큰 아이디&amp;rsquo;를 기반으로 Vault 서비스를 호출하고, Vault는 해당 '특정 토큰 아이디'를 수신한 뒤 요청된 '비밀정보'를 제공합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 25.3488%;&quot;&gt;&lt;b&gt;감사 추적 기능&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 74.5349%;&quot;&gt;Vault는 모든 액세스 요청을 로깅하여 감사를 위한 추적이 가능합니다.&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;h3 data-ke-size=&quot;size23&quot;&gt;2. Vault 수행 과정&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;596&quot; data-origin-height=&quot;221&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/tEhwD/dJMcahQttzt/2VZ1PlHgJ4663uxd2tIYKK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/tEhwD/dJMcahQttzt/2VZ1PlHgJ4663uxd2tIYKK/img.png&quot; data-alt=&quot;https://www.baeldung.com/spring-boot-3-1-connectiondetails-abstraction&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/tEhwD/dJMcahQttzt/2VZ1PlHgJ4663uxd2tIYKK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FtEhwD%2FdJMcahQttzt%2F2VZ1PlHgJ4663uxd2tIYKK%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;596&quot; height=&quot;221&quot; data-origin-width=&quot;596&quot; data-origin-height=&quot;221&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://www.baeldung.com/spring-boot-3-1-connectiondetails-abstraction&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  Vault 수행 과정&lt;/b&gt;&lt;br /&gt;&lt;b&gt;&lt;br /&gt;1. Spring Boot &amp;rarr; Hashicorp Vault: Secret Request(비밀정보 요청)&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;- Spring Boot 애플리케이션에서는 서버를 실행할 때, Valut에 접근할 수 있는 &amp;lsquo;특정 토큰 아이디&amp;rsquo;(Secret Request)&amp;rsquo; 를 기반으로 Vault 서비스를 호출하여 &amp;lsquo;비밀 정보(Secret)&amp;rsquo;를 검색합니다.&lt;br /&gt;&lt;b&gt;&lt;br /&gt;2. Hashicorp Vault &amp;rarr; Spring Boot: Secret Response(비밀정보 응답)&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;- Valut 서비스는 Spring Boot 애플리케이션의 &amp;lsquo;특정 토큰 아이디&amp;rsquo;(Secret Request)&amp;rsquo;를 수신하고 요청된 &amp;lsquo;비밀 정보(Secret)&amp;rsquo;를 전달합니다.&lt;br /&gt;- 이 단계를 통해서 애플리케이션은 필요한 시크릿 정보를 안전하게 받아올 수 있습니다.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;3. Spring Boot &amp;rarr; Remote Service : Connection Request(커넥션 요청)&lt;/b&gt;&lt;br /&gt;- Spring Boot 애플리케이션이 원격 서비스와 연결을 시도하는 과정을 의미합니다. &lt;br /&gt;- 이 단계에서 애플리케이션은 Vault에서 안전하게 가져온 비밀 정보(예: 데이터베이스 비밀번호, API 키 등)를 사용하여 원격 서비스에 연결 요청을 보냅니다.&lt;br /&gt;- 이렇게 함으로써, 중요한 정보를 코드 내에 직접 작성하거나 노출시키지 않아도 되어 보안을 더 효과적으로 관리할 수 있습니다.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;4. Remote Service &amp;rarr; Spring Boot : Connection Response(커넥션 연결)&lt;/b&gt;&lt;br /&gt;- Vault 서비스가 Spring Boot 애플리케이션의 Secret Request를 수신하고, 요청된 '비밀 정보'를 애플리케이션으로 전송하는 단계입니다. &lt;br /&gt;-이 단계를 통해 애플리케이션은 필요한 시크릿 정보를 안전하게 받아올 수 있습니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;[&amp;nbsp; 더 알아보기 ]&lt;br /&gt;&lt;/b&gt;&lt;br /&gt;&lt;b&gt; Spring Cloud Vault&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;- HashiCorp Vault와 Spring 애플리케이션을 연결해 민감한 정보를 안전하게 관리할 수 있도록 돕는 프레임워크입니다.&lt;br /&gt;- 즉, 비밀번호&amp;middot;API 키&amp;middot;DB 접속 정보 같은 시크릿을 외부에 노출하지 않고 중앙에서 안전하게 관리하며, 애플리케이션은 이를 자동으로 가져와 사용할 수 있습니다.&lt;br /&gt;- Spring 애플리케이션이 Vault에 저장된 시크릿을 가져와 Spring 환경에 자동으로 주입합니다.&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;h3 data-ke-size=&quot;size23&quot;&gt;3. KV(Key-Value) Secret engine&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  KV(Key-Value) Secret engine&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;- Vault에 구성된 물리적 저장소 내에 임의의 시크릿을 저장하는 데 사용되는 일반적인 키-값(KV) 저장소입니다.&lt;br /&gt;- 가장 기본적인 정적(Static) 비밀 저장소를 의미합니다. 키-값 형태로 환경변수, API 키, 토큰, 비밀번호 등을 저장하고, 읽고, 업데이트합니다.&lt;br /&gt;- Database 엔진과 같이 동적 Credential 방식과 다르게 만료가 되지 않고, 키에 대한 단일 값을 저장하거나, 각 키에 대해 여러 버전을 저장하고 해당 버전을 기록을 할 수 있습니다.&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;815&quot; data-origin-height=&quot;358&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/UYNHi/dJMcahQttIz/3JCNxTZKeFiA6q2j7G0Zg0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/UYNHi/dJMcahQttIz/3JCNxTZKeFiA6q2j7G0Zg0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/UYNHi/dJMcahQttIz/3JCNxTZKeFiA6q2j7G0Zg0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FUYNHi%2FdJMcahQttIz%2F3JCNxTZKeFiA6q2j7G0Zg0%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;815&quot; height=&quot;358&quot; data-origin-width=&quot;815&quot; data-origin-height=&quot;358&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  KV(Key-Value) Secret engine 내에서 KV v1, KV v2가 존재합니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 126px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style2&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;&lt;b&gt;분류 항목&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;&lt;b&gt;KV v1&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;&lt;b&gt;KV v2&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;&lt;b&gt;기능&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;단순 키-값 저장&lt;/td&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;키-값 저장 + 버전관리&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;&lt;b&gt;버전 관리&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;없음&lt;/td&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;있음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;&lt;b&gt;API 구조&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;단순 &lt;br /&gt;/v1/&amp;lt;mount&amp;gt;/&amp;lt;path&amp;gt;&lt;/td&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;/data, /metadata &lt;br /&gt;&lt;span data-token-index=&quot;1&quot;&gt;- 데이터:&lt;/span&gt; /v1/&amp;lt;mount&amp;gt;/data/&amp;lt;path&amp;gt;&lt;br /&gt;&lt;span data-token-index=&quot;3&quot;&gt;- 메타데이터:&lt;/span&gt; /v1/&amp;lt;mount&amp;gt;/metadata/&amp;lt;path&amp;gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;&lt;b&gt;삭제&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;즉시 삭제&lt;/td&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;soft delete + destroy&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;&lt;b&gt;CAS(Check-And-Set)&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;제한적&lt;/td&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;정식 지원 (동시 업데이트 충돌 방지)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;&lt;b&gt;사용 사례&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;단순한 설정값, 변경 필요 없는 비밀&lt;/td&gt;
&lt;td style=&quot;height: 18px;&quot;&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;h4 data-ke-size=&quot;size20&quot;&gt;3.1. KV Version 1&lt;/h4&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;style3&quot;&gt;&lt;b&gt;  KV Version 1&lt;/b&gt;&lt;br /&gt;&lt;b&gt;&lt;br /&gt;- KV(Key-Value) Secret engine에서 물리적인 저장소에 내에 임의의 비밀(Secret)을 저장하는 데 사용이 됩니다.&lt;/b&gt;&lt;br /&gt;- KV v2에 비교하여 단순한 키-값만 저장을 하는 용도로 사용이 되며, 별도의 버전관리는 되지 않습니다.&lt;br /&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;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3.2. KV Version 2&lt;/h4&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;style3&quot;&gt;&lt;b&gt;  KV Version 2&lt;/b&gt;&lt;br /&gt;&lt;b&gt;&lt;br /&gt;- KV Version1과 동일하게 KV(Key-Value) Secret engine에서 물리적인 저장소에 내에 임의의 비밀(Secret)을 저장하는 데 사용이 됩니다.&lt;/b&gt;&lt;br /&gt;- KV v1과 비교하여 키-값 저장을 하는 용도로 사용이 되며, 별도의 버전관리를 수행하여서 이전 버전을 통해서 관리하고 복구할 수 있습니다. 또한, 자주 변경이 되는 비밀, 롤백/복구가 필요한 파일에 대해서 관리가 됩니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2) 정책 기반 토큰 발급&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  정책 기반 토큰 발급 및 이용방식&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 권한(Policy), TTL, Max TTL, Renewable 여부 등 조건을 지정한 다음 Vault가 자동으로 토큰을 발급을 하고, 사용자는 그 토큰으로만 Vault API에 접근하는 방식을 의미합니다.&lt;/b&gt;&lt;br /&gt;- 즉, Root Token(슈퍼 관리자급 권한)을 쓰지 않고, &amp;lsquo;읽기 전용&amp;rsquo;, &amp;lsquo;DB 패스워드 발급만 가능&amp;rsquo;, &amp;lsquo;TTL 하루만 사용&amp;rsquo; 등과 같은 안전한 토큰을 만들어서 쓰는 구조를 의미합니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 정책 생성&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  정책 생성&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;- Vault에서 &amp;lsquo;특정 접근 권한&amp;rsquo;에 대한 정책을 생성합니다.&lt;/blockquote&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;# name: read-only-policy 이름으로 작성
path &quot;kv2/data/*&quot; {
  capabilities = [&quot;read&quot;, &quot;list&quot;]
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. 정책 기반 토큰 발급&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  정책 기반 토큰 발급&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 정책을 기반으로 하고, 토큰 생성 시 옵션을 지정하여서 특정 기간으로 한 토큰을 발급합니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;pre class=&quot;shell&quot; data-ke-language=&quot;shell&quot;&gt;&lt;code&gt;$ vault token create \\
  -policy=&quot;read-only-policy&quot; \\
  -ttl=24h \\
  -explicit-max-ttl=720h \\
  -renewable=true&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 376px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style2&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;&lt;b&gt;옵션&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;&lt;b&gt;타입&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;&lt;b&gt;설명&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;-policy=&amp;lt;name&amp;gt;&lt;/td&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;반복 사용 가능&lt;/td&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;토큰에 부여할 정책. 여러 개 지정 가능. 예: -policy=default -policy=read-only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;-policies=&amp;lt;list&amp;gt;&lt;/td&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;문자열 리스트&lt;/td&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;쉼표로 여러 정책 지정할 때 사용. 예: -policies=&quot;default,read-only&quot;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;-orphan&lt;/td&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;boolean&lt;/td&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;부모 토큰 없이 독립적인 토큰 생성. 부모가 삭제돼도 유지.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;-period=&amp;lt;duration&amp;gt;&lt;/td&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;기간&lt;/td&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;정해진 기간마다 자동 갱신해야 하는 토큰 생성. (Periodic token)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;-ttl=&amp;lt;duration&amp;gt;&lt;/td&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;기간&lt;/td&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;토큰의 최초 TTL(수명). 예: 1h, 24h&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;-explicit-max-ttl=&amp;lt;duration&amp;gt;&lt;/td&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;기간&lt;/td&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;갱신을 포함한 토큰의 최대 TTL 제한.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;-format=&amp;lt;format&amp;gt;&lt;/td&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;string&lt;/td&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;출력 형식 (json / table / yaml 가능). 예: -format=json&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;-id=&amp;lt;token_id&amp;gt;&lt;/td&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;string&lt;/td&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;토큰 ID를 직접 지정해서 생성(보안상 거의 안 씀).&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;-display-name=&amp;lt;name&amp;gt;&lt;/td&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;string&lt;/td&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;토큰 이름. Audit log 등에 표시됨. 기본값은 &amp;ldquo;token&amp;rdquo;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;-metadata=&amp;lt;key=value&amp;gt;&lt;/td&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;key-value&lt;/td&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;토큰 metadata 설정. 여러 개 가능. 예: -metadata=user=john&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;-role=&amp;lt;role_name&amp;gt;&lt;/td&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;string&lt;/td&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;Token role 기반으로 생성. Token role 정책/TTL 규칙 적용.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;-entity=&amp;lt;entity_id&amp;gt;&lt;/td&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;string&lt;/td&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;특정 Identity Entity에 토큰을 연결함.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;-no-default-policy&lt;/td&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;boolean&lt;/td&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;default policy 자동 부여 방지. 기본은 default 포함됨.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;-use-limit=&amp;lt;number&amp;gt;&lt;/td&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;number&lt;/td&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;토큰을 사용할 수 있는 최대 횟수 지정. (Use-limit token)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;-wrap-ttl=&amp;lt;duration&amp;gt;&lt;/td&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;기간&lt;/td&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;래핑 토큰 생성 TTL 설정. 렌즈로 발급할 때 사용.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;-num-uses=&amp;lt;number&amp;gt;&lt;/td&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;number&lt;/td&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;토큰 허용 사용 횟수 (use-limit과 동일 개념)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;-namespace=&amp;lt;path&amp;gt;&lt;/td&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;string&lt;/td&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;Vault Enterprise 전용. 특정 namespace에 토큰 발급.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;-mount=&amp;lt;auth_mount_path&amp;gt;&lt;/td&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;string&lt;/td&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;Token auth mount 지정. 특별한 경우 외엔 사용 X&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;h3 data-ke-size=&quot;size23&quot;&gt;3. 사용자는 발급받은 토큰을 이용하여 Vault에 접근&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;804&quot; data-origin-height=&quot;687&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bA6QUt/dJMcabW1BAv/jjh7wC6hrrRweR05sn6xCK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bA6QUt/dJMcabW1BAv/jjh7wC6hrrRweR05sn6xCK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bA6QUt/dJMcabW1BAv/jjh7wC6hrrRweR05sn6xCK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbA6QUt%2FdJMcabW1BAv%2Fjjh7wC6hrrRweR05sn6xCK%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;804&quot; height=&quot;687&quot; data-origin-width=&quot;804&quot; data-origin-height=&quot;687&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;h3 data-ke-size=&quot;size23&quot;&gt;4. 토큰은 일정 시간이 되면 만료(TTL: Time To Live) 혹은 재갱신합니다&lt;/h3&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;style3&quot;&gt;&lt;b&gt;&amp;nbsp;  토큰은 일정 시간이 되면 만료(TTL: Time To Live) 혹은 재갱신합니다&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 토큰이 일정시간이 되었을 때, 실수로 토큰이 유출돼도 TTL이 도래해서 자동으로 폐기가 됨. 그리고 필요에 따라서 renewable=true 속성에 따라서 TTL 연장이 가능합니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;773&quot; data-origin-height=&quot;430&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bKgTiV/dJMcabW1BAX/junKQkI0Z9FfRdtUnkhTUk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bKgTiV/dJMcabW1BAX/junKQkI0Z9FfRdtUnkhTUk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bKgTiV/dJMcabW1BAX/junKQkI0Z9FfRdtUnkhTUk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbKgTiV%2FdJMcabW1BAX%2FjunKQkI0Z9FfRdtUnkhTUk%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;773&quot; height=&quot;430&quot; data-origin-width=&quot;773&quot; data-origin-height=&quot;430&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3) Root Token 이용 방식 vs 정책 기반 토큰 발급 및 이용 방식 비교&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  Root Token 이용 방식 vs 정책 기반 토큰 발급 및 이용 방식 비교&lt;/b&gt;&lt;/blockquote&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style2&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;구분&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;Root Token 이용 방식&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;Token 발급 및 이용 방식(정책 기반 토큰)&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;권한(Privilege)&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Vault 전체 권한(=슈퍼 관리자). 모든 경로&amp;middot;모든 기능 허용&lt;/td&gt;
&lt;td&gt;부여된 정책(policy)에 따라 제한된 권한만 수행&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;보안 위험도&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;매우 높음 (노출 시 Vault 전체 장악 가능)&lt;/td&gt;
&lt;td&gt;상대적으로 낮음 (정책 범위 내에서만 노출 피해)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;용도&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;초기 Vault 설정, 정책&amp;middot;엔진 구성 같은 위험 작업&lt;/td&gt;
&lt;td&gt;실제 서비스/애플리케이션/운영 시스템이 사용하는 정상적인 방식&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;TTL / 만료 설정&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;기본적으로 만료 없음(수동으로 만료시키거나 Rotate 필요)&lt;/td&gt;
&lt;td&gt;TTL, Max TTL, Renewable 여부 자유롭게 설정 가능&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;정책 적용 방식&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;정책을 무시하고 모든 작업 가능&lt;/td&gt;
&lt;td&gt;반드시 정책(policy) 기반으로 제약을 받음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;사용 주체&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;관리자(초기 구축, 정책 생성, 토큰 설정 등)&lt;/td&gt;
&lt;td&gt;애플리케이션, CICD, 개발자, 프로세스 등&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;재발급/갱신&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Root Token은 갱신할 일이 거의 없음(보통 비활성화&amp;middot;보관)&lt;/td&gt;
&lt;td&gt;TTL 만료 시 재발급, 갱신(Renewal) 가능&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;발급 방법&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Vault 초기 설정 시 자동 생성&lt;/td&gt;
&lt;td&gt;vault token create 명령 또는 Auth Method(OIDC, GitHub, AppRole 등)로 발급&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;운영 관점&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;평소에 사용하면 안 됨. 금고 열쇠를 책상 위에 두는 것과 같음&lt;/td&gt;
&lt;td&gt;실제 운영 환경은 일반 토큰 + Auth Method 조합으로 사용&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Audit Logging&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;기록은 남지만 Root라서 모든 동작이 너무 광범위함&lt;/td&gt;
&lt;td&gt;정책 기반이므로 더 세밀한 감사 추적 가능&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;토큰 폐기(Revocation)&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;폐기하면 다시 초기화 과정을 통해 복구해야 할 수 있음&lt;/td&gt;
&lt;td&gt;토큰만 폐기하고 서비스는 다른 토큰으로 계속 동작&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;적합한 상황&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;초기 설정, 정책 생성, 비상 상황&lt;/td&gt;
&lt;td&gt;API 접근, 앱 서비스, 자동화 작업(CICD), 시스템 구성&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;h3 data-ke-size=&quot;size23&quot;&gt;1. Root Token 이용 방식&lt;/h3&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;style3&quot;&gt;&lt;b&gt;&amp;nbsp;  Root Token 이용 방식&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;- 기존 방식의 경우는 Root Token을 통한 인증 방식이었기에, 전체 권한을 가진 &amp;lsquo;슈퍼 관리자&amp;rsquo; 토큰이기에 위험도가 극단적으로 높은 보안적인 큰 위험이 있습니다.&lt;br /&gt;- 단 한 번이라도 유출이 된다면 Vault 전체가 뚫리는 것과 같은 큰 문제점을 가지고 있습니다.&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1042&quot; data-origin-height=&quot;769&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/C8CKK/dJMcahQttTQ/AKIRhMtOaQJvxhTspO9SI1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/C8CKK/dJMcahQttTQ/AKIRhMtOaQJvxhTspO9SI1/img.png&quot; data-alt=&quot;https://adjh54.tistory.com/728&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/C8CKK/dJMcahQttTQ/AKIRhMtOaQJvxhTspO9SI1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FC8CKK%2FdJMcahQttTQ%2FAKIRhMtOaQJvxhTspO9SI1%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;1042&quot; height=&quot;769&quot; data-origin-width=&quot;1042&quot; data-origin-height=&quot;769&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://adjh54.tistory.com/728&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;figure id=&quot;og_1765430175120&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;[Java] Spring Cloud Vault 이해하고 활용하기-1 : 초기 환경, KV 구성 및 Root Token 인증 방식&quot; data-og-description=&quot;해당 글에서는 Spring Cloud Vault를 이해하고 Vault를 구성하고 KV Secret Engine 내에서 데이터를 조회하는 토큰 인증방식을 이용하는 방법에 대해 알아봅니다 1) Vault  Vault- HashCorp 사에서 만든 Vault는 &quot; data-og-host=&quot;adjh54.tistory.com&quot; data-og-source-url=&quot;https://adjh54.tistory.com/728&quot; data-og-url=&quot;https://adjh54.tistory.com/728&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/tYQrG/hyZPwpVzye/yinoqGFIsngpK6j3yp7mx1/img.png?width=800&amp;amp;height=431&amp;amp;face=0_0_800_431,https://scrap.kakaocdn.net/dn/MaObS/hyZPsHPwSS/9gKt1bZKRg9lk8yXbim821/img.png?width=800&amp;amp;height=431&amp;amp;face=0_0_800_431,https://scrap.kakaocdn.net/dn/csrILE/hyZO7L2ioS/fNoa5Vg1q4dWUs6ORjd9zK/img.png?width=1917&amp;amp;height=932&amp;amp;face=0_0_1917_932&quot;&gt;&lt;a href=&quot;https://adjh54.tistory.com/728&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://adjh54.tistory.com/728&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/tYQrG/hyZPwpVzye/yinoqGFIsngpK6j3yp7mx1/img.png?width=800&amp;amp;height=431&amp;amp;face=0_0_800_431,https://scrap.kakaocdn.net/dn/MaObS/hyZPsHPwSS/9gKt1bZKRg9lk8yXbim821/img.png?width=800&amp;amp;height=431&amp;amp;face=0_0_800_431,https://scrap.kakaocdn.net/dn/csrILE/hyZO7L2ioS/fNoa5Vg1q4dWUs6ORjd9zK/img.png?width=1917&amp;amp;height=932&amp;amp;face=0_0_1917_932');&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;[Java] Spring Cloud Vault 이해하고 활용하기-1 : 초기 환경, KV 구성 및 Root Token 인증 방식&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;해당 글에서는 Spring Cloud Vault를 이해하고 Vault를 구성하고 KV Secret Engine 내에서 데이터를 조회하는 토큰 인증방식을 이용하는 방법에 대해 알아봅니다 1) Vault  Vault- HashCorp 사에서 만든 Vault는&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;adjh54.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;h3 data-ke-size=&quot;size23&quot;&gt;2. 일반 Token 발급 및 이용 방식(정책 기반 토큰)&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  일반 Token 발급 및 이용 방식(정책 기반 토큰)&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 이전 Root Token을 이용하는 과정과 다르게 사전 작업에 Root Token을 통해서 전체 권한을 가진 &amp;lsquo;슈퍼 관리자&amp;rsquo; 토큰에 대한 위험도를 올리지 않습니다.&lt;/b&gt;&lt;br /&gt;- 제한된 Token을 TTL, Max TTL, Renewable &amp;lsquo;토큰 생성 옵션&amp;rsquo;을 지정하여 제한된 시간을 부여하거나, Vault의 접근 권한에 대한 정책(Policy)을 지정하고 &amp;lsquo;제한된 Token&amp;rsquo;을 두어서 시간 혹은 접근 권한에 대해서 한정하는 방식입니다.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;[ Vault Token 생성 단계 ]&lt;br /&gt;&lt;/b&gt;&lt;br /&gt;&lt;b&gt;1. [Vault CLI] Root Token에 접근을 합니다.&lt;/b&gt;&lt;br /&gt;- Root Token을 통해서 &amp;lsquo;슈퍼 관리자&amp;rsquo;가 토큰을 발급해 줍니다.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;2. [Vault CLI] 특정 기간, 정책을 기반한 Token 생성&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;- Vault Policy를 통한 Vault 접근 권한에 대한 정책을 생성하고 로드해 옵니다.&lt;br /&gt;- 특정 기간, 정책을 기반한 Token을 생성합니다.&lt;br /&gt;ex) vault token create -policy=&quot;read-only-policy&quot; -ttl=24h&lt;br /&gt;&lt;br /&gt;&lt;b&gt;3. Token을 Spring Boot 설정에 저장합니다&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;[Spring Boot 기동 및 Vault 연동 단계]&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;1. Spring Boot 서버 시작: SpringApplication.run()&lt;/b&gt;&lt;br /&gt;- 최초 Spring Boot 서버가 실행이 됩니다.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;2. Spring Cloud Vault 자동 구성 로드: bootstrap.yml&lt;/b&gt; &lt;br /&gt;-&amp;nbsp;Spring Cloud Bootstrap에서 최초 Vault 설정에 대한 자동 구성이 로드가 됩니다.&lt;br /&gt;&lt;b&gt;&lt;br /&gt;3. Vault에 '인증(토큰)' 요청 수행 : 발급된 Token&lt;/b&gt;&lt;br /&gt;- Vault의 인증 방식 중 토큰을 이용한 방식에서도 발급된 Token을 통해서 Vault에 인증을 요청합니다.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;4. Vault '인증 정보' 검증&lt;/b&gt; &lt;br /&gt;- Vault에서는 유효한 토큰인지에 대해 인증 정보에 대해 검증을 합니다.&lt;br /&gt;- 성공 시, KV Secret Engine에 접근합니다.&lt;br /&gt;- 실패 시, 서버 시작 실패와 종료를 합니다.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;5. 조회된 Secret을 Spring Boot에 응답&lt;/b&gt; &lt;br /&gt;-&amp;nbsp;KV Secret Engine에서 조회된 정보를 응답으로 전달합니다&lt;br /&gt;&lt;br /&gt;&lt;b&gt;6. 응답받은 Secret을 Spring Environment(PropertySource)에 자동 주입&lt;br /&gt;&lt;/b&gt; -&amp;nbsp;Spring Boot에서는 전달받은 Secret을 Spring Environment(PropertySource)에 자동 주입을 합니다.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;7. @ConfigurationProperties VaultKVProperties.java 객체와 자동 매핑&lt;/b&gt; &lt;br /&gt;-&amp;nbsp;추후에 구성할 VaultKVProperties.java 파일 내에 사전에 KV와 같은 형태로 구성한 멤버 변수 각각에 데이터가 매핑이 됩니다.&lt;br /&gt;&lt;b&gt;&lt;br /&gt;8. Spring Boot 서버 실행 완료&lt;/b&gt; &lt;br /&gt;-&amp;nbsp;최종 작업이 완료되어서 Spring Boot 서버가 실행이 완료가 됩니다.&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;846&quot; data-origin-height=&quot;799&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bzIFmy/dJMcaiBNIKE/3GfiDhHCLdoPAVwuoyCOik/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bzIFmy/dJMcaiBNIKE/3GfiDhHCLdoPAVwuoyCOik/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bzIFmy/dJMcaiBNIKE/3GfiDhHCLdoPAVwuoyCOik/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbzIFmy%2FdJMcaiBNIKE%2F3GfiDhHCLdoPAVwuoyCOik%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;846&quot; height=&quot;799&quot; data-origin-width=&quot;846&quot; data-origin-height=&quot;799&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;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;[ 더 알아보기 ]&lt;/b&gt;&lt;br /&gt;&lt;b&gt;&lt;br /&gt;  TTL(Time To Live)&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 토큰이 몇 시간 동안 사용할 수 있는가에 대한 초기 유효시간을 의미합니다. 이는 발급과 동시에 유효시간이 부여가 됩니다.&lt;/b&gt;&lt;br /&gt;- vault token create -ttl=1h -explicit-max-ttl=24h -renewable=true 해당 명령어와 같이 ttl은 1시간으로 지정이 됩니다.**&lt;br /&gt;&lt;b&gt;&lt;br /&gt;  Max TTL(Explicit Max TTL)&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 토큰이 갱신이 되더라도 유효한 기간을 의미합니다. TTL을 통해서 토큰의 유효기간을 지정할 수 있지만, 갱신(renew)할 수 있습니다.&lt;/b&gt;&lt;br /&gt;- 예를 들어서, TTL이 1시간이고, MAX TTL이 24시간이라고 한다면 해당 발급한 토큰이 1시간이 지나서 만료가 되었을 때, renew를 통해서 24시간 동안 갱신을 할 수 있습니다.&lt;br /&gt;vault token create -ttl=1h -explicit-max-ttl=24h -renewable=true 해당 명령어와 같이 explicit-max-ttl은 24시간으로 지정이 됩니다.&lt;br /&gt;&lt;b&gt;&lt;br /&gt; &amp;nbsp; Renewable&lt;br /&gt;&lt;/b&gt;&lt;br /&gt;&lt;b&gt;- TTL의 연장 여부를 부여함을 의미합니다. true/false의 진위형의 값을 가지고, TTL(Time To Live), Max TTL(Explicit Max TTL)을 부여했는데, Renewable를 false로 한다면 갱신이 되지 않습니다.&lt;/b&gt;&lt;br /&gt;- vault token create -ttl=1h -explicit-max-ttl=24h -renewable=false 해당 명령어와 같이 renewable이 false라면, Max TTL(Explicit Max TTL)가 남았더라도 토큰을 갱신할 수 없습니다.**&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;h2 data-ke-size=&quot;size26&quot;&gt;4) 토큰 발급을 위한 Policy 지정 및 검증&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 홈 화면에서 Policies 탭을 선택합니다.&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1913&quot; data-origin-height=&quot;834&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/T3uFf/dJMcabpbmzD/LG962Za0dDSYER2ew2zLY0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/T3uFf/dJMcabpbmzD/LG962Za0dDSYER2ew2zLY0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/T3uFf/dJMcabpbmzD/LG962Za0dDSYER2ew2zLY0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FT3uFf%2FdJMcabpbmzD%2FLG962Za0dDSYER2ew2zLY0%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;1913&quot; height=&quot;834&quot; data-origin-width=&quot;1913&quot; data-origin-height=&quot;834&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;h3 data-ke-size=&quot;size23&quot;&gt;2. &amp;lsquo;Create ACL Policy&amp;rsquo;를 선택합니다&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1917&quot; data-origin-height=&quot;834&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cEOfQt/dJMcabbEGT7/7fZlAJlc3nlQS2wtjCmI11/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cEOfQt/dJMcabbEGT7/7fZlAJlc3nlQS2wtjCmI11/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cEOfQt/dJMcabbEGT7/7fZlAJlc3nlQS2wtjCmI11/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcEOfQt%2FdJMcabbEGT7%2F7fZlAJlc3nlQS2wtjCmI11%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;1917&quot; height=&quot;834&quot; data-origin-width=&quot;1917&quot; data-origin-height=&quot;834&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;h3 data-ke-size=&quot;size23&quot;&gt;3. KV Secret Engine에 접근할 수 있는 Policy를 지정합니다&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  KV Secret Engine에 접근할 수 있는 Policy를 지정합니다&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- KV Secret Engine으로 kv2라는 공간 내에 실제 데이터가 저장되는 경로로 하위에 모든 것에 대한 권한을 부여합니다&lt;/b&gt;&lt;br /&gt;- 권한은 시크릿 내용을 조회(읽기), 목록 조회 기능에 대해서 접근 권한을 부여합니다.&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1913&quot; data-origin-height=&quot;829&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bufTNb/dJMcabpbmEB/2OGka5hxuJLtEw53mO8Edk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bufTNb/dJMcabpbmEB/2OGka5hxuJLtEw53mO8Edk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bufTNb/dJMcabpbmEB/2OGka5hxuJLtEw53mO8Edk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbufTNb%2FdJMcabpbmEB%2F2OGka5hxuJLtEw53mO8Edk%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;1913&quot; height=&quot;829&quot; data-origin-width=&quot;1913&quot; data-origin-height=&quot;829&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;h3 data-ke-size=&quot;size23&quot;&gt;4. 터미널에서 policy 리스트를 확인합니다&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  터미널에서 policy 리스트를 확인합니다&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 생성된 policy를 확인합니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;571&quot; data-origin-height=&quot;110&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ncvT7/dJMcachkhJt/Kq46UkS9Zz9PYfztGAsKhk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ncvT7/dJMcachkhJt/Kq46UkS9Zz9PYfztGAsKhk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ncvT7/dJMcachkhJt/Kq46UkS9Zz9PYfztGAsKhk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FncvT7%2FdJMcachkhJt%2FKq46UkS9Zz9PYfztGAsKhk%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;571&quot; height=&quot;110&quot; data-origin-width=&quot;571&quot; data-origin-height=&quot;110&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;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  [참고]&lt;/b&gt; Error looking up token: Error making API request. 403 Error가 발생한다면?&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;567&quot; data-origin-height=&quot;197&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/citjhV/dJMcachkhOj/INFBIp5GZZjcDCxrGqcndK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/citjhV/dJMcachkhOj/INFBIp5GZZjcDCxrGqcndK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/citjhV/dJMcachkhOj/INFBIp5GZZjcDCxrGqcndK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcitjhV%2FdJMcachkhOj%2FINFBIp5GZZjcDCxrGqcndK%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;567&quot; height=&quot;197&quot; data-origin-width=&quot;567&quot; data-origin-height=&quot;197&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;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  아래와 같이 VAULT_ADDR와 VAULT_TOKEN를 지정해주어야 합니다.&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;- 이는 클라이언트가 사용할 인증 토큰을 환경변수로 등록하는 과정입니다. 이는 루트 토큰을 의미합니다.&lt;/blockquote&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;# Vault 주소와 루트 토큰 주입
export VAULT_ADDR='&amp;lt;http://127.0.0.1:8200&amp;gt;'
export VAULT_TOKEN=&quot;hvs.qBlCBqEKIGtJfCA9grVMHbrV&quot; # 루트 토큰
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5. Token을 생성해 줍니다.&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  Token을 생성해 줍니다.&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;- &lt;b&gt;read-only-policy를 기반으로 vault token을 발급받습니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;pre class=&quot;shell&quot; data-ke-language=&quot;shell&quot;&gt;&lt;code&gt;# 기본 형식
$ vault token create -policy=&quot;&amp;lt;policy 이름&amp;gt;&quot; -ttl=24h

# 사용 예시
vault token create -policy=&quot;read-only-policy&quot; -ttl=24h&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;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  [참고] Token 생성은 왜 CLI를 통해서 해야 하는 것일까?&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 토큰 생성은 가장 민감한 보안 작업임. 지나치게 높은 권한을 가진 토큰을 클릭 한 번으로 만들어버릴 수 있기에 공식적으로도 &amp;lsquo;Vault UI는 Token 생성 기능을 제공하지 않는다&amp;rsquo;라고 이야기하고 있습니다.&lt;/b&gt;&lt;br /&gt;- 토큰은 자동화된 시스템/코드에서 발급되는 게 일반적임 사람이 일반적으로 직접 토큰을 만들지 않고 토큰은 코드, CI/CD, 서버 자동화 과정에서 발급이 됩니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;6. 생성된 토큰을 검증해 줍니다&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  생성된 토큰을 검증해 줍니다&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;- 아래와 같이 token에 대해서 검증을 수행합니다.&lt;/blockquote&gt;
&lt;pre id=&quot;code_1765430878776&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 기본 형식
$ vault token lookup &amp;lt;토큰&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1102&quot; data-origin-height=&quot;519&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kLd1h/dJMcafkO8DN/BUA7hSsCfOCibpsMBm1ae1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kLd1h/dJMcafkO8DN/BUA7hSsCfOCibpsMBm1ae1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kLd1h/dJMcafkO8DN/BUA7hSsCfOCibpsMBm1ae1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkLd1h%2FdJMcafkO8DN%2FBUA7hSsCfOCibpsMBm1ae1%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;1102&quot; height=&quot;519&quot; data-origin-width=&quot;1102&quot; data-origin-height=&quot;519&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;h3 data-ke-size=&quot;size23&quot;&gt;7. 발급된 토큰을 기반으로 GUI 내에 접근도 가능합니다.&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1906&quot; data-origin-height=&quot;925&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/BDrQW/dJMcacn581F/eYrMgTC7daGtuQQiLLtlY0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/BDrQW/dJMcacn581F/eYrMgTC7daGtuQQiLLtlY0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/BDrQW/dJMcacn581F/eYrMgTC7daGtuQQiLLtlY0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FBDrQW%2FdJMcacn581F%2FeYrMgTC7daGtuQQiLLtlY0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1906&quot; height=&quot;925&quot; data-origin-width=&quot;1906&quot; data-origin-height=&quot;925&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;blockquote data-ke-style=&quot;style3&quot;&gt;  실제 접근해서 확인해 보면, read-only-policy라는 정책에 맞게 kv2에 대한 읽기 권한만 부여가 되었습니다.&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1919&quot; data-origin-height=&quot;934&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/7tqBK/dJMcagxgc07/TfVWfG9CHSX7KwKvPR1d01/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/7tqBK/dJMcagxgc07/TfVWfG9CHSX7KwKvPR1d01/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/7tqBK/dJMcagxgc07/TfVWfG9CHSX7KwKvPR1d01/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F7tqBK%2FdJMcagxgc07%2FTfVWfG9CHSX7KwKvPR1d01%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;1919&quot; height=&quot;934&quot; data-origin-width=&quot;1919&quot; data-origin-height=&quot;934&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;h2 data-ke-size=&quot;size26&quot;&gt;5) Spring Boot 환경 구성&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 구조 확인: 일반 Token 발급 및 이용 방식(정책 기반 토큰)&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  구조 확인: 일반 Token 발급 및 이용 방식(정책 기반 토큰)&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;- 해당 방식에서는 정책에 맞게 토큰을 발급해서 사용자에게 부여를 해주는 방식입니다. &lt;br /&gt;- 기존에 Root Token에 비해 상대적으로 특정 접근 권한(Policy)에 대해서만 지정하기에 안정적입니다.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;[Spring Boot 기동 및 Vault 연동 단계]&lt;/b&gt;&lt;br /&gt;&lt;b&gt;&lt;br /&gt;1. Spring Boot 서버 시작: SpringApplication.run()&lt;/b&gt; &lt;br /&gt;- 최초 Spring Boot 서버가 실행이 됩니다.&lt;br /&gt;&lt;b&gt;&lt;br /&gt;2. Spring Cloud Vault 자동 구성 로드: bootstrap.yml&lt;br /&gt;&lt;/b&gt; -&amp;nbsp;Spring Cloud Bootstrap에서 최초 Vault 설정에 대한 자동 구성이 로드가 됩니다.&lt;br /&gt;&lt;b&gt;&lt;br /&gt;3. Vault에 '인증(토큰)' 요청 수행 : 발급된 Token&lt;/b&gt;&lt;br /&gt;- Vault의 인증 방식 중 토큰을 이용한 방식에서도 발급된 Token을 통해서 Vault에 인증을 요청합니다.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;4. Vault '인증 정보' 검증&lt;/b&gt; &lt;br /&gt;-&amp;nbsp;Vault에서는 유효한 토큰인지에 대해 인증 정보에 대해 검증을 합니다.&lt;br /&gt;- 성공 시, KV Secret Engine에 접근합니다.&lt;br /&gt;- 실패 시, 서버 시작 실패와 종료를 합니다.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;5. 조회된 Secret을 Spring Boot에 응답&lt;/b&gt; &lt;br /&gt;-&amp;nbsp;KV Secret Engine에서 조회된 정보를 응답으로 전달합니다&lt;br /&gt;&lt;br /&gt;&lt;b&gt;6. 응답받은 Secret을 Spring Environment(PropertySource)에 자동 주입&lt;/b&gt; &lt;br /&gt;-&amp;nbsp;Spring Boot에서는 전달받은 Secret을 Spring Environment(PropertySource)에 자동 주입을 합니다.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;7. @ConfigurationProperties VaultKVProperties.java 객체와 자동 매핑&lt;br /&gt;&lt;/b&gt; -&amp;nbsp;추후에 구성할 VaultKVProperties.java 파일 내에 사전에 KV와 같은 형태로 구성한 멤버 변수 각각에 데이터가 매핑이 됩니다.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;8. Spring Boot 서버 실행 완료&lt;br /&gt;&lt;/b&gt; -&amp;nbsp;최종 작업이 완료되어서 Spring Boot 서버가 실행이 완료가 됩니다.&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;846&quot; data-origin-height=&quot;797&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/exSrx8/dJMcajnayhy/iBR7Ji0dv4mvyBmNy93ek1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/exSrx8/dJMcajnayhy/iBR7Ji0dv4mvyBmNy93ek1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/exSrx8/dJMcajnayhy/iBR7Ji0dv4mvyBmNy93ek1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FexSrx8%2FdJMcajnayhy%2FiBR7Ji0dv4mvyBmNy93ek1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;846&quot; height=&quot;797&quot; data-origin-width=&quot;846&quot; data-origin-height=&quot;797&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;h3 data-ke-size=&quot;size23&quot;&gt;2. 개발 환경&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  개발 환경&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 기본 기능과 주요 라이브러리인 spring-cloud-dependencies, spring-cloud-starter-bootstrap, spring-cloud-starter-vault-config를 설정하였습니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style2&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;개발 환경&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;분류&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;버전&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Java JDK&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Java Version&lt;/td&gt;
&lt;td&gt;17&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;빌드 도구&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Gradle&lt;/td&gt;
&lt;td&gt;8.14.3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Spring Boot&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Spring Boot Starter&lt;/td&gt;
&lt;td&gt;3.5.8&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Spring Boot Web&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Spring Boot Starter&lt;/td&gt;
&lt;td&gt;3.5.8&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;spring-cloud-starter-bootstrap&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Spring Boot Cloud&lt;/td&gt;
&lt;td&gt;2025.0.0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;spring-cloud-starter-vault-config&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Spring Boot Cloud&lt;/td&gt;
&lt;td&gt;2025.0.0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;spring-cloud-dependencies&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Spring Boot Cloud&lt;/td&gt;
&lt;td&gt;2025.0.0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Lombok&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Library&lt;/td&gt;
&lt;td&gt;latest&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;h3 data-ke-size=&quot;size23&quot;&gt;3. 라이브러리 설치 : build.gradle&lt;/h3&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;style3&quot;&gt;&lt;b&gt;&amp;nbsp;  라이브러리 설치 : build.gradle&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;- 위에서 명시한 라이브러리를 Gradle을 이용하여서 설치합니다.&lt;/blockquote&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;plugins {
    id 'java'
    id 'org.springframework.boot' version '3.5.8'
    id 'io.spring.dependency-management' version '1.1.7'
}
ext {
    springCloudVersion = &quot;2025.0.0&quot;
}

group = 'com.adjh'
version = '0.0.1-SNAPSHOT'
description = 'spring-boot3-vault'

java {
    toolchain {
        languageVersion = JavaLanguageVersion.of(17)
    }
}

configurations {
    compileOnly {
        extendsFrom annotationProcessor
    }
}

repositories {
    mavenCentral()
}

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-web'           // Spring Boot Web

    implementation 'org.springframework.cloud:spring-cloud-starter-bootstrap'   // Spring Cloud BootStrap
    implementation 'org.springframework.cloud:spring-cloud-starter-vault-config'// Spring Cloud Vault
    compileOnly 'org.projectlombok:lombok'                                      // Lombok Compile
    annotationProcessor 'org.projectlombok:lombok'                              // Lombok Annotation
    testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
    testImplementation 'org.springframework.boot:spring-boot-starter-test'
}
dependencyManagement {
    imports {
        mavenBom &quot;org.springframework.cloud:spring-cloud-dependencies:$springCloudVersion&quot;
    }
}

tasks.named('test') {
    useJUnitPlatform()
}&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;h3 data-ke-size=&quot;size23&quot;&gt;4. 시스템 환경변수 주입 : IntelliJ IDE 활용&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  시스템 환경변수 주입 : IntelliJ IDE 활용&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- Vault로 전달이 되는 Main Key 자체를 프로젝트 내에 넣고 Git에 올릴 수 없기에 &amp;lsquo;시스템 환경 변수&amp;rsquo; 내에 추가합니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  [참고]&amp;nbsp;&lt;/b&gt;아래의 환경 변수를 추가하는 방법의 글을 참고하셔도 도움이 됩니다.&lt;/blockquote&gt;
&lt;figure id=&quot;og_1765431442231&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;[Java] Spring Boot 환경에서 시스템 변수 .env 파일 지정 및 활용 방법&quot; data-og-description=&quot;해당 글에서는 Spring Boot 개발 환경에서 .env 파일을 시스템 변수로 지정하는 방법과 이를 불러오는 다양한 방법에 대해서 알아봅니다1) Spring Boot 환경에서 .env 파일을 시스템 변수로 적용하는 이&quot; data-og-host=&quot;adjh54.tistory.com&quot; data-og-source-url=&quot;https://adjh54.tistory.com/722&quot; data-og-url=&quot;https://adjh54.tistory.com/722&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/biw5nD/hyZPyVBZsn/4eaRnEyXf7iQZdWZrj88wk/img.png?width=800&amp;amp;height=432&amp;amp;face=0_0_800_432,https://scrap.kakaocdn.net/dn/bprV4H/hyZPxPVXXv/0JK67UHByW1tzHZPcoMpW1/img.png?width=800&amp;amp;height=432&amp;amp;face=0_0_800_432,https://scrap.kakaocdn.net/dn/TiP7O/hyZOFaV4Qy/nZQ9Ts835fBYEHlfjnqzbK/img.png?width=1313&amp;amp;height=539&amp;amp;face=0_0_1313_539&quot;&gt;&lt;a href=&quot;https://adjh54.tistory.com/722&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://adjh54.tistory.com/722&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/biw5nD/hyZPyVBZsn/4eaRnEyXf7iQZdWZrj88wk/img.png?width=800&amp;amp;height=432&amp;amp;face=0_0_800_432,https://scrap.kakaocdn.net/dn/bprV4H/hyZPxPVXXv/0JK67UHByW1tzHZPcoMpW1/img.png?width=800&amp;amp;height=432&amp;amp;face=0_0_800_432,https://scrap.kakaocdn.net/dn/TiP7O/hyZOFaV4Qy/nZQ9Ts835fBYEHlfjnqzbK/img.png?width=1313&amp;amp;height=539&amp;amp;face=0_0_1313_539');&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;[Java] Spring Boot 환경에서 시스템 변수 .env 파일 지정 및 활용 방법&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;해당 글에서는 Spring Boot 개발 환경에서 .env 파일을 시스템 변수로 지정하는 방법과 이를 불러오는 다양한 방법에 대해서 알아봅니다1) Spring Boot 환경에서 .env 파일을 시스템 변수로 적용하는 이&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;adjh54.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;h4 data-ke-size=&quot;size20&quot;&gt;4.1. IDE툴 오른쪽 상단에 서버 &amp;gt; 구성 편집.. 버튼을 누릅니다&lt;/h4&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1245&quot; data-origin-height=&quot;282&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/QfK5i/dJMcabbEHcI/cur2ZHxRhQiOSRaDhQ0gak/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/QfK5i/dJMcabbEHcI/cur2ZHxRhQiOSRaDhQ0gak/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/QfK5i/dJMcabbEHcI/cur2ZHxRhQiOSRaDhQ0gak/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FQfK5i%2FdJMcabbEHcI%2Fcur2ZHxRhQiOSRaDhQ0gak%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;1245&quot; height=&quot;282&quot; data-origin-width=&quot;1245&quot; data-origin-height=&quot;282&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;h4 data-ke-size=&quot;size20&quot;&gt;4.2. 옵션 수정(Modify options) 버튼을 누릅니다.&lt;/h4&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&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;816&quot; data-origin-height=&quot;689&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bDPgRU/dJMcahQtuiu/kZ1kXs25Xxw2wD2oYwY3p0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bDPgRU/dJMcahQtuiu/kZ1kXs25Xxw2wD2oYwY3p0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bDPgRU/dJMcahQtuiu/kZ1kXs25Xxw2wD2oYwY3p0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbDPgRU%2FdJMcahQtuiu%2FkZ1kXs25Xxw2wD2oYwY3p0%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;816&quot; height=&quot;689&quot; data-origin-width=&quot;816&quot; data-origin-height=&quot;689&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;h4 data-ke-size=&quot;size20&quot;&gt;4.3. 운영 체제(Operating System) &amp;gt; 환경 변수(Environment variables)를 선택합니다&lt;/h4&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1124&quot; data-origin-height=&quot;838&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bfb3XI/dJMcabvXq5f/klSxPcATG4F0INwzDOR8RK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bfb3XI/dJMcabvXq5f/klSxPcATG4F0INwzDOR8RK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bfb3XI/dJMcabvXq5f/klSxPcATG4F0INwzDOR8RK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbfb3XI%2FdJMcabvXq5f%2FklSxPcATG4F0INwzDOR8RK%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;1124&quot; height=&quot;838&quot; data-origin-width=&quot;1124&quot; data-origin-height=&quot;838&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;h4 data-ke-size=&quot;size20&quot;&gt;4.4. 환경 변수를 추가했습니다. 이전 단계에서 발급받은 Token 값을 추가하였습니다.&lt;/h4&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  환경 변수를 추가했습니다. 이전 단계에서 발급받은 Token 값을 추가하였습니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;pre class=&quot;gcode&quot;&gt;&lt;code&gt;hvs.CAESIPN1xW04V5X5QEF-1HL5XUbzgydb76tkWjTvB4HgbKm9Gh4KHGh2cy5Xb1NpZHdPejFVZzRjb0RjZDJsZlBtcDM
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;804&quot; data-origin-height=&quot;687&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bjZPJS/dJMcafE7Thh/RKXM3md7J99lNJoFYXmJyK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bjZPJS/dJMcafE7Thh/RKXM3md7J99lNJoFYXmJyK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bjZPJS/dJMcafE7Thh/RKXM3md7J99lNJoFYXmJyK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbjZPJS%2FdJMcafE7Thh%2FRKXM3md7J99lNJoFYXmJyK%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;804&quot; height=&quot;687&quot; data-origin-width=&quot;804&quot; data-origin-height=&quot;687&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;h3 data-ke-size=&quot;size23&quot;&gt;5. bootstrap.yml 파일을 생성 및 구성합니다.&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  bootstrap.yml 파일을 생성 및 구성합니다.&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- resources 경로 하단에 bootstrap.yml 파일을 생성하였습니다&lt;/b&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;461&quot; data-origin-height=&quot;595&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dmjVNe/dJMcaa4SDHo/mNkUZXjtmpdJdcXc5NC8h0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dmjVNe/dJMcaa4SDHo/mNkUZXjtmpdJdcXc5NC8h0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dmjVNe/dJMcaa4SDHo/mNkUZXjtmpdJdcXc5NC8h0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdmjVNe%2FdJMcaa4SDHo%2FmNkUZXjtmpdJdcXc5NC8h0%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;461&quot; height=&quot;595&quot; data-origin-width=&quot;461&quot; data-origin-height=&quot;595&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;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  yml 파일을 아래와 같이 구성하였습니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;spring:
  cloud:
    vault:
      uri: &amp;lt;http://localhost:8200&amp;gt;   # Vault 서버 주소 (UI 주소 아님, API 엔드포인트)
      authentication: TOKEN        # Vault 인증 방식 (TOKEN, APPROLE, APPID 등)
      token: ${VAULT_MAIN_TOKEN}   # 환경변수에서 Vault 접근 토큰을 불러옴
      connection-timeout: 5000     # Vault 서버 연결 시도 타임아웃(ms)
      read-timeout: 15000          # Vault 데이터 읽기 타임아웃(ms)

      kv:
        enabled: true              # KV(키-값) 비밀 엔진 사용 여부
        backend: kv2               # Vault에서 생성한 엔진 이름(ex: kv2), 반드시 일치해야 함
                                   # 실제 Vault 경로가 &quot;kv2/&quot; 라면 backend도 kv2로 설정해야 함

        default-context: loc       # 기본 Secret 경로 (예: kv2/loc/data/application 같은 구조가 됨)
                                   # 즉, Vault UI에서는 /kv2/data/loc 경로와 매핑됨

        application-name: ''       # Spring 애플리케이션 이름을 Secret 경로에 추가할지 여부
                                   # 빈 값이면 &quot;default-context&quot; 바로 아래에서 조회함
                                   # 예: default: '' &amp;rarr; kv2/loc/data&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;h3 data-ke-size=&quot;size23&quot;&gt;6. 서버 실행&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  서버 실행&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;- 정상적으로 오류 없이 서버가 실행하였습니다.&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1787&quot; data-origin-height=&quot;593&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/srAhr/dJMcagKNGKh/mFq416MreND5ALWG1Vg1w1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/srAhr/dJMcagKNGKh/mFq416MreND5ALWG1Vg1w1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/srAhr/dJMcagKNGKh/mFq416MreND5ALWG1Vg1w1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FsrAhr%2FdJMcagKNGKh%2FmFq416MreND5ALWG1Vg1w1%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;1787&quot; height=&quot;593&quot; data-origin-width=&quot;1787&quot; data-origin-height=&quot;593&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;h3 data-ke-size=&quot;size23&quot;&gt;7. Vault 값과 객체 매핑&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  Vault 값과 객체 매핑&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- Vault에서 읽어온 값을 자바 클래스 필드에 자동 바인딩을 하는 클래스입니다.&lt;/b&gt;&lt;br /&gt;- @Getter, @Setter 통해서 멤버 변수에 접근할 수 있도록 어노테이션을 지정하였습니다.&lt;br /&gt;- @Component를 빈으로 등록하여서, 다른 서비스나 컴포넌트에 주입하여 사용이 가능하도록 하였습니다.&lt;br /&gt;- @ConfigurationProperties(prefix = &quot;vault&quot;) : 상위 prefix 내에 하위에 동일한 이름으로 각각 자동 바인딩이 됩니다.&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1879&quot; data-origin-height=&quot;616&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cuZW6g/dJMcafkO8QY/9EdWxZRMJxshXbsmbSQG2K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cuZW6g/dJMcafkO8QY/9EdWxZRMJxshXbsmbSQG2K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cuZW6g/dJMcafkO8QY/9EdWxZRMJxshXbsmbSQG2K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcuZW6g%2FdJMcafkO8QY%2F9EdWxZRMJxshXbsmbSQG2K%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;1879&quot; height=&quot;616&quot; data-origin-width=&quot;1879&quot; data-origin-height=&quot;616&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre class=&quot;kotlin&quot;&gt;&lt;code&gt;package com.adjh.springboot3vault.properties;

import lombok.Getter;
import lombok.Setter;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;

/**
 * Vault Key-Value Properties Mapping value
 *
 * @author : leejonghoon
 * @fileName : VaultKVProperties
 * @since : 25. 11. 25.
 */
@Getter
@Setter
@Component
@ConfigurationProperties(prefix = &quot;&quot;)
public class VaultKVProperties {
    private String appEnv;
    private String dbHost;
    private String dbPassword;
    private String dbUser;
}

&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;h3 data-ke-size=&quot;size23&quot;&gt;8. 콘솔을 확인하기 위해 Controller를 구성하였습니다.&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  콘솔을 확인하기 위해 Controller를 구성하였습니다.&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 생성자 패턴으로 bean을 호출하여서 Controller 내에서 활용하도록 구성하였습니다.&lt;/b&gt;&lt;br /&gt;- http://localhost:8080 url로 접근하여서 &amp;lsquo;/api/v1/vault/kv&amp;rsquo;로 호출이 된다면 각각의 vault 값이 정상적으로 출력이 됩니다.&lt;/blockquote&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;package com.adjh.springboot3vault.controller;

import com.adjh.springboot3vault.properties.VaultKVProperties;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * Bean을 호출하여서 Vault 값을 테스트하기 위한 Controller
 *
 * @author : leejonghoon
 * @fileName : VaultController
 * @since : 25. 11. 25.
 */
@RestController
@RequestMapping(&quot;/api/v1/vault&quot;)
public class VaultController {

    private final VaultKVProperties vaultKVProperties;

    public VaultController(VaultKVProperties vaultKVProperties) {
        this.vaultKVProperties = vaultKVProperties;
    }

    @GetMapping(&quot;/kv&quot;)
    public String main() {
        System.out.println(&quot;Getter appEnv : &quot; + vaultKVProperties.getAppEnv());
        System.out.println(&quot;Getter dbHost : &quot; + vaultKVProperties.getDbHost());
        System.out.println(&quot;Getter dbPassword : &quot; + vaultKVProperties.getDbPassword());
        System.out.println(&quot;Getter dbUser : &quot; + vaultKVProperties.getDbUser());
        return &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;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;9. 결과확인&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  결과확인&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 정상적으로 출력이 됨을 확인하였습니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1908&quot; data-origin-height=&quot;338&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/LdH0T/dJMcacVVL13/zvYnKdRbh3453N795xko2K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/LdH0T/dJMcacVVL13/zvYnKdRbh3453N795xko2K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/LdH0T/dJMcacVVL13/zvYnKdRbh3453N795xko2K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FLdH0T%2FdJMcacVVL13%2FzvYnKdRbh3453N795xko2K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1908&quot; height=&quot;338&quot; data-origin-width=&quot;1908&quot; data-origin-height=&quot;338&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&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;오늘도&amp;nbsp;감사합니다.&amp;nbsp; &lt;/b&gt;&lt;/p&gt;</description>
      <category>Java/Spring Cloud</category>
      <category>Spring Boot</category>
      <category>spring cloud vault</category>
      <category>spring cloud vault policy</category>
      <category>spring cloud vault 인증 방법</category>
      <category>spring vault token</category>
      <category>vault</category>
      <category>vault policy token</category>
      <category>vault token 인증</category>
      <category>vault token 인증 방식</category>
      <category>토큰 내에 정책부여</category>
      <author>adjh54</author>
      <guid isPermaLink="true">https://adjh54.tistory.com/729</guid>
      <comments>https://adjh54.tistory.com/729#entry729comment</comments>
      <pubDate>Thu, 11 Dec 2025 20:00:21 +0900</pubDate>
    </item>
    <item>
      <title>[Java] Spring Cloud Vault 이해하고 활용하기-1 : 초기 환경, KV 구성 및 Root Token 인증 방식</title>
      <link>https://adjh54.tistory.com/728</link>
      <description>&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
 &lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;해당 글에서는 Spring Cloud Vault를 이해하고 Vault를 구성하고 KV Secret Engine 내에서 데이터를 조회하는 토큰 인증방식을 이용하는 방법에 대해 알아봅니다&lt;br&gt;&lt;br&gt;&lt;/span&gt;&lt;/span&gt; 
 &lt;figure contenteditable=&quot;false&quot; data-ke-type=&quot;emoticon&quot; data-ke-align=&quot;alignCenter&quot; data-emoticon-type=&quot;face&quot; data-emoticon-name=&quot;073&quot; data-emoticon-isanimation=&quot;false&quot; data-emoticon-src=&quot;https://t1.daumcdn.net/keditor/emoticon/face/large/073.png&quot;&gt;
  &lt;img src=&quot;https://t1.daumcdn.net/keditor/emoticon/face/large/073.png&quot; width=&quot;80&quot;&gt;
 &lt;/figure&gt; 
&lt;/blockquote&gt;&lt;h2 data-ke-size=&quot;size26&quot;&gt;1) Vault&lt;/h2&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot;&gt;&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  Vault&lt;/b&gt;&lt;br&gt;&lt;br&gt;&lt;b&gt;- HashCorp 사에서 만든 Vault는 다양한 환경에서 애플리케이션의 외부 비밀 속성(예: 데이터베이스 비밀번호, API 키 등)을 외부화된 구성으로 중앙에서 관리할 수 있습니다. &lt;/b&gt;&lt;br&gt;- Spring Boot 환경에서 Vault로부터 시크릿 정보를 읽어오며 Valut에 시크릿 정보를 쓰는 것도 가능합니다. 이러한 방식으로 애플리케이션의 중요한 정보는 코드에서 분리되어 보안이 보장됩니다.&lt;br&gt;- 기밀정보의 동적인 제공, 중앙 집중식 시크릿 관리, 즉각적인 액세스 제어, 감사 추적 기능 등을 제공하여, 기업의 보안 정책을 준수하는 데 도움이 됩니다.&lt;/blockquote&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;886&quot; data-origin-height=&quot;501&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cZbir0/dJMcaiPiGjG/Tw76Ieo8cEIkv6Vkv66ZV0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cZbir0/dJMcaiPiGjG/Tw76Ieo8cEIkv6Vkv66ZV0/img.png&quot; data-alt=&quot;https://developer.hashicorp.com/&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cZbir0/dJMcaiPiGjG/Tw76Ieo8cEIkv6Vkv66ZV0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcZbir0%2FdJMcaiPiGjG%2FTw76Ieo8cEIkv6Vkv66ZV0%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;886&quot; height=&quot;501&quot; data-origin-width=&quot;886&quot; data-origin-height=&quot;501&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://developer.hashicorp.com/&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. Vault의 특징&lt;/h3&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot;&gt;&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-style=&quot;style2&quot; data-ke-align=&quot;alignLeft&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td style=&quot;width: 21.5116%;&quot;&gt;&lt;b&gt;특징&lt;/b&gt;&lt;/td&gt;&lt;td style=&quot;width: 78.3721%;&quot;&gt;&lt;b&gt;설명&lt;/b&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style=&quot;width: 21.5116%;&quot;&gt;&lt;b&gt;기밀정보의 동적 제공&lt;/b&gt;&lt;/td&gt;&lt;td style=&quot;width: 78.3721%;&quot;&gt;애플리케이션이 필요에 따라 비밀정보를 요청하고, Vault는 요청된 정보를 제공합니다.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style=&quot;width: 21.5116%;&quot;&gt;&lt;b&gt;중앙 집중식 시크릿 관리&lt;/b&gt;&lt;/td&gt;&lt;td style=&quot;width: 78.3721%;&quot;&gt;모든 애플리케이션의 시크릿 정보를 중앙에서 관리할 수 있습니다.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style=&quot;width: 21.5116%;&quot;&gt;&lt;b&gt;즉각적인 액세스 제어&lt;/b&gt;&lt;/td&gt;&lt;td style=&quot;width: 78.3721%;&quot;&gt;애플리케이션이 Vault에 접근 할 수 있는 ‘특정 토큰 아이디’를 기반으로 Vault 서비스를 호출하고, Vault는 해당 '특정 토큰 아이디'를 수신한 뒤 요청된 '비밀정보'를 제공합니다.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style=&quot;width: 21.5116%;&quot;&gt;&lt;b&gt;감사 추적 기능&lt;/b&gt;&lt;/td&gt;&lt;td style=&quot;width: 78.3721%;&quot;&gt;Vault는 모든 액세스 요청을 로깅하여 감사를 위한 추적이 가능합니다.&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;h3 data-ke-size=&quot;size23&quot;&gt;2. Vault 수행 과정&lt;/h3&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;596&quot; data-origin-height=&quot;221&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dXyScx/dJMcad1wb6f/kvj1KNulQgbpNcURaBhQx0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dXyScx/dJMcad1wb6f/kvj1KNulQgbpNcURaBhQx0/img.png&quot; data-alt=&quot;https://www.baeldung.com/spring-boot-3-1-connectiondetails-abstraction&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dXyScx/dJMcad1wb6f/kvj1KNulQgbpNcURaBhQx0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdXyScx%2FdJMcad1wb6f%2Fkvj1KNulQgbpNcURaBhQx0%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;596&quot; height=&quot;221&quot; data-origin-width=&quot;596&quot; data-origin-height=&quot;221&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://www.baeldung.com/spring-boot-3-1-connectiondetails-abstraction&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  Vault 수행 과정&lt;/b&gt;&lt;br&gt;&lt;b&gt;&lt;br&gt;&lt;/b&gt;&lt;b&gt;1. Spring Boot → Hashicorp Vault: Secret Request(비밀정보 요청)&lt;/b&gt;&lt;br&gt;- Spring Boot 애플리케이션에서는 서버를 실행할 때, Valut에 접근할 수 있는 ‘특정 토큰 아이디’(Secret Request)’ 를 기반으로 Vault 서비스를 호출하여 ‘비밀 정보(Secret)’를 검색합니다.&lt;br&gt;&lt;br&gt;&lt;b&gt;2. Hashicorp Vault → Spring Boot: Secret Response(비밀정보 응답)&lt;/b&gt;&lt;br&gt;- Valut 서비스는 Spring Boot 애플리케이션의 ‘특정 토큰 아이디’(Secret Request)’를 수신하고 요청된 ‘비밀 정보(Secret)’를 전달합니다.&lt;br&gt;- 이 단계를 통해서 애플리케이션은 필요한 시크릿 정보를 안전하게 받아올 수 있습니다.&lt;br&gt;&lt;br&gt;&lt;b&gt;3. Spring Boot → Remote Service : Connection Request(커넥션 요청)&lt;/b&gt;&lt;br&gt;- Spring Boot 애플리케이션이 원격 서비스와 연결을 시도하는 과정을 의미합니다. &lt;br&gt;- 이 단계에서 애플리케이션은 Vault에서 안전하게 가져온 비밀 정보(예: 데이터베이스 비밀번호, API 키 등)를 사용하여 원격 서비스에 연결 요청을 보냅니다.&lt;br&gt;- 이렇게 함으로써, 중요한 정보를 코드 내에 직접 작성하거나 노출시키지 않아도 되어 보안을 더 효과적으로 관리할 수 있습니다.&lt;br&gt;&lt;br&gt;&lt;b&gt;4. Remote Service → Spring Boot : Connection Response(커넥션 연결)&lt;/b&gt;&lt;br&gt;- Vault 서비스가 Spring Boot 애플리케이션의 Secret Request를 수신하고, 요청된 '비밀 정보'를 애플리케이션으로 전송하는 단계입니다. &lt;br&gt;- 이 단계를 통해 애플리케이션은 필요한 시크릿 정보를 안전하게 받아올 수 있습니다.&lt;/blockquote&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;[ 더 알아보기 ]&lt;/b&gt;&lt;br&gt;&lt;b&gt;&lt;br&gt;&lt;/b&gt;&lt;b&gt; Spring Cloud Vault&lt;/b&gt;&lt;br&gt;&lt;br&gt;&lt;b&gt;- HashiCorp Vault와 Spring 애플리케이션을 연결해 민감한 정보를 안전하게 관리할 수 있도록 돕는 프레임워크입니다.&lt;/b&gt;&lt;br&gt;- 즉, 비밀번호·API 키·DB 접속 정보 같은 시크릿을 외부에 노출하지 않고 중앙에서 안전하게 관리하며, 애플리케이션은 이를 자동으로 가져와 사용할 수 있습니다.&lt;br&gt;- Spring 애플리케이션이 Vault에 저장된 시크릿을 가져와 Spring 환경에 자동으로 주입합니다.&lt;/blockquote&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. KV(Key-Value) Secret engine&lt;/h3&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot;&gt;&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  KV(Key-Value) Secret engine&lt;/b&gt;&lt;br&gt;&lt;br&gt;&lt;b&gt;- Vault에 구성된 물리적 저장소 내에 임의의 시크릿을 저장하는 데 사용되는 일반적인 키-값(KV) 저장소입니다.&lt;/b&gt;&lt;br&gt;- 가장 기본적인 정적(Static) 비밀 저장소를 의미합니다. 키-값 형태로 환경변수, API 키, 토큰, 비밀번호 등을 저장하고, 읽고, 업데이트합니다.&lt;br&gt;- Database 엔진과 같이 동적 Credential 방식과 다르게 만료가 되지 않고, 키에 대한 단일 값을 저장하거나, 각 키에 대해 여러 버전을 저장하고 해당 버전을 기록을 할 수 있습니다.&lt;/blockquote&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;815&quot; data-origin-height=&quot;358&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/5AjNo/dJMcag44c7Q/KOKKD1VY0p7wbeFeWSSNtk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/5AjNo/dJMcag44c7Q/KOKKD1VY0p7wbeFeWSSNtk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/5AjNo/dJMcag44c7Q/KOKKD1VY0p7wbeFeWSSNtk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F5AjNo%2FdJMcag44c7Q%2FKOKKD1VY0p7wbeFeWSSNtk%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;815&quot; height=&quot;358&quot; data-origin-width=&quot;815&quot; data-origin-height=&quot;358&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  KV(Key-Value) Secret engine 내에서 KV v1, KV v2가 존재합니다.&lt;/b&gt;&lt;/blockquote&gt;&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 198px;&quot; border=&quot;1&quot; data-ke-style=&quot;style2&quot; data-ke-align=&quot;alignLeft&quot;&gt;&lt;tbody&gt;&lt;tr style=&quot;height: 20px;&quot;&gt;&lt;td style=&quot;height: 20px;&quot;&gt;&lt;b&gt;분류 항목&lt;/b&gt;&lt;/td&gt;&lt;td style=&quot;height: 20px;&quot;&gt;&lt;b&gt;KV v1&lt;/b&gt;&lt;/td&gt;&lt;td style=&quot;height: 20px;&quot;&gt;&lt;b&gt;KV v2&lt;/b&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr style=&quot;height: 20px;&quot;&gt;&lt;td style=&quot;height: 20px;&quot;&gt;&lt;b&gt;기능&lt;/b&gt;&lt;/td&gt;&lt;td style=&quot;height: 20px;&quot;&gt;단순 키-값 저장&lt;/td&gt;&lt;td style=&quot;height: 20px;&quot;&gt;키-값 저장 + 버전관리&lt;/td&gt;&lt;/tr&gt;&lt;tr style=&quot;height: 20px;&quot;&gt;&lt;td style=&quot;height: 20px;&quot;&gt;&lt;b&gt;버전 관리&lt;/b&gt;&lt;/td&gt;&lt;td style=&quot;height: 20px;&quot;&gt;없음&lt;/td&gt;&lt;td style=&quot;height: 20px;&quot;&gt;있음&lt;/td&gt;&lt;/tr&gt;&lt;tr style=&quot;height: 20px;&quot;&gt;&lt;td style=&quot;height: 20px;&quot;&gt;&lt;b&gt;API 구조&lt;/b&gt;&lt;/td&gt;&lt;td style=&quot;height: 20px;&quot;&gt;/v1/&amp;lt;mount&amp;gt;/&amp;lt;path&amp;gt;&lt;/td&gt;&lt;td style=&quot;height: 20px;&quot;&gt;/data,&amp;nbsp;/metadata&lt;br&gt;데이터:&amp;nbsp;/v1/&amp;lt;mount&amp;gt;/data/&amp;lt;path&amp;gt;&lt;br&gt;메타데이터:&amp;nbsp;/v1/&amp;lt;mount&amp;gt;/metadata/&amp;lt;path&amp;gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr style=&quot;height: 20px;&quot;&gt;&lt;td style=&quot;height: 20px;&quot;&gt;&lt;b&gt;삭제&lt;/b&gt;&lt;/td&gt;&lt;td style=&quot;height: 20px;&quot;&gt;즉시 삭제&lt;/td&gt;&lt;td style=&quot;height: 20px;&quot;&gt;soft delete + destroy&lt;/td&gt;&lt;/tr&gt;&lt;tr style=&quot;height: 20px;&quot;&gt;&lt;td style=&quot;height: 20px;&quot;&gt;&lt;b&gt;CAS(Check-And-Set)&lt;/b&gt;&lt;/td&gt;&lt;td style=&quot;height: 20px;&quot;&gt;제한적&lt;/td&gt;&lt;td style=&quot;height: 20px;&quot;&gt;&lt;b&gt;정식 지원&lt;/b&gt; (동시 업데이트 충돌 방지)&lt;/td&gt;&lt;/tr&gt;&lt;tr style=&quot;height: 20px;&quot;&gt;&lt;td style=&quot;height: 20px;&quot;&gt;&lt;b&gt;사용 사례&lt;/b&gt;&lt;/td&gt;&lt;td style=&quot;height: 20px;&quot;&gt;단순한 설정값, 변경 필요 없는 비밀&lt;/td&gt;&lt;td style=&quot;height: 20px;&quot;&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;h4 data-ke-size=&quot;size20&quot;&gt;3.1. KV Version 1&lt;/h4&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot;&gt;&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt; KV Version 1&lt;/b&gt;&lt;br&gt;&lt;b&gt;&lt;br&gt;&lt;/b&gt;&lt;b&gt;- KV(Key-Value) Secret engine에서 물리적인 저장소에 내에 임의의 비밀(Secret)을 저장하는 데 사용이 됩니다.&lt;/b&gt;&lt;br&gt;- KV v2에 비교하여 단순한 키-값만 저장을 하는 용도로 사용이 되며, 별도의 버전관리는 되지 않습니다. 또한, 주로 단순한 설정 값, 변경 필요가 없는 파일에 대해서 관리가 됩니다.&lt;/blockquote&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;h4 data-ke-size=&quot;size20&quot;&gt;3.2. KV Version 2&lt;/h4&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot;&gt;&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  KV Version 2&lt;/b&gt;&lt;br&gt;&lt;b&gt;&lt;br&gt;&lt;/b&gt;&lt;b&gt;- KV Version1과 동일하게 KV(Key-Value) Secret engine에서 물리적인 저장소에 내에 임의의 비밀(Secret)을 저장하는 데 사용이 됩니다.&lt;/b&gt;&lt;br&gt;- KV v1과 비교하여 키-값 저장을 하는 용도로 사용이 되며, 별도의 버전관리를 수행하여서 이전 버전을 통해서 관리하고 복구할 수 있습니다. 또한, 자주 변경이 되는 비밀, 롤백/복구가 필요한 파일에 대해서 관리가 됩니다.&lt;/blockquote&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;h2 data-ke-size=&quot;size26&quot;&gt;3) 환경 설정 : 로컬 Docker 설정&lt;/h2&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot;&gt;&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  환경 설정 : 로컬 Docker 설정&lt;/b&gt;&lt;br&gt;&lt;br&gt;&lt;b&gt;- 아래의 글을 통해서 Docker 기반으로 로컬 Vault 환경을 지정하여 구성하였습니다.&lt;/b&gt;&lt;/blockquote&gt;&lt;figure data-ke-type=&quot;opengraph&quot; data-og-title=&quot;[Docker] Docker로 Vault 로컬 환경 구성 방법&quot; data-ke-align=&quot;alignCenter&quot; data-og-description=&quot;해당 글에서는 Docker 환경에서 Vault를 로컬 환경에 구축하는 방법에 대해서 알아봅니다.&amp;nbsp;1) Vault  Vault- HashCorp 사에서 만든 Vault는 다양한 환경에서 애플리케이션의 외부 비밀 속성(예: 데이터베&quot; data-og-host=&quot;adjh54.tistory.com&quot; data-og-source-url=&quot;https://adjh54.tistory.com/393&quot; data-og-image=&quot;https://blog.kakaocdn.net/dna/bdWSaP/hyZO069vs8/AAAAAAAAAAAAAAAAAAAAANYAnqJL-EF-9DasojzGFj6h7gU-VMceNQ_Xf_FIR4T-/img.png?credential=yqXZFxpELC7KVnFOS48ylbz2pIh7yKj8&amp;amp;expires=1767193199&amp;amp;allow_ip=&amp;amp;allow_referer=&amp;amp;signature=F5HVpp1W3UuHF5oD1v5TEnxO7SA%3D&quot; data-og-url=&quot;https://adjh54.tistory.com/393&quot;&gt;&lt;a href=&quot;https://adjh54.tistory.com/393&quot; target=&quot;_blank&quot; data-source-url=&quot;https://adjh54.tistory.com/393&quot;&gt;&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://blog.kakaocdn.net/dna/bdWSaP/hyZO069vs8/AAAAAAAAAAAAAAAAAAAAANYAnqJL-EF-9DasojzGFj6h7gU-VMceNQ_Xf_FIR4T-/img.png?credential=yqXZFxpELC7KVnFOS48ylbz2pIh7yKj8&amp;amp;expires=1767193199&amp;amp;allow_ip=&amp;amp;allow_referer=&amp;amp;signature=F5HVpp1W3UuHF5oD1v5TEnxO7SA%3D')&quot;&gt; &lt;/div&gt;&lt;div class=&quot;og-text&quot;&gt;&lt;p class=&quot;og-title&quot;&gt;[Docker] Docker로 Vault 로컬 환경 구성 방법&lt;/p&gt;&lt;p class=&quot;og-desc&quot;&gt;해당 글에서는 Docker 환경에서 Vault를 로컬 환경에 구축하는 방법에 대해서 알아봅니다.&amp;nbsp;1) Vault  Vault- HashCorp 사에서 만든 Vault는 다양한 환경에서 애플리케이션의 외부 비밀 속성(예: 데이터베&lt;/p&gt;&lt;p class=&quot;og-host&quot;&gt;adjh54.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;h3 data-ke-size=&quot;size23&quot;&gt;1. 아래와 같이 Docker로 실행하여 접속을 완료하였습니다&lt;/h3&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1908&quot; data-origin-height=&quot;931&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/o30dB/dJMcahv8hlz/qbXF6yAylFBN9kfwV2szhK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/o30dB/dJMcahv8hlz/qbXF6yAylFBN9kfwV2szhK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/o30dB/dJMcahv8hlz/qbXF6yAylFBN9kfwV2szhK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fo30dB%2FdJMcahv8hlz%2FqbXF6yAylFBN9kfwV2szhK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1908&quot; height=&quot;931&quot; data-origin-width=&quot;1908&quot; data-origin-height=&quot;931&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;h2 data-ke-size=&quot;size26&quot;&gt;4) 환경 설정 : 사이트 내에 KV 설정&lt;/h2&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot;&gt;&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  [참고]&lt;/b&gt; CLI를 이용한 설정에 대해 궁금하시면 아래의 글을 참고하시면 도움이 됩니다.&lt;/blockquote&gt;&lt;figure data-ke-type=&quot;opengraph&quot; data-og-title=&quot;kv - Command | Vault | HashiCorp Developer&quot; data-ke-align=&quot;alignCenter&quot; data-og-description=&quot;The &amp;quot;kv&amp;quot; command groups subcommands for interacting with Vault's key/value secret engine.&quot; data-og-host=&quot;developer.hashicorp.com&quot; data-og-source-url=&quot;https://developer.hashicorp.com/vault/docs/commands/kv&quot; data-og-image=&quot;https://blog.kakaocdn.net/dna/E03di/hyZOTUs5AK/AAAAAAAAAAAAAAAAAAAAAKIvJcQ36P6W4xGRTrCydKMrJ-TjWSr5KdOlbT6crEWb/img.jpg?credential=yqXZFxpELC7KVnFOS48ylbz2pIh7yKj8&amp;amp;expires=1767193199&amp;amp;allow_ip=&amp;amp;allow_referer=&amp;amp;signature=n7D73L47Ld3rIxoCxif5I1yiM64%3D&quot; data-og-url=&quot;https://developer.hashicorp.com/vault/docs/commands/kv&quot;&gt;&lt;a href=&quot;https://developer.hashicorp.com/vault/docs/commands/kv&quot; target=&quot;_blank&quot; data-source-url=&quot;https://developer.hashicorp.com/vault/docs/commands/kv&quot;&gt;&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://blog.kakaocdn.net/dna/E03di/hyZOTUs5AK/AAAAAAAAAAAAAAAAAAAAAKIvJcQ36P6W4xGRTrCydKMrJ-TjWSr5KdOlbT6crEWb/img.jpg?credential=yqXZFxpELC7KVnFOS48ylbz2pIh7yKj8&amp;amp;expires=1767193199&amp;amp;allow_ip=&amp;amp;allow_referer=&amp;amp;signature=n7D73L47Ld3rIxoCxif5I1yiM64%3D')&quot;&gt; &lt;/div&gt;&lt;div class=&quot;og-text&quot;&gt;&lt;p class=&quot;og-title&quot;&gt;kv - Command | Vault | HashiCorp Developer&lt;/p&gt;&lt;p class=&quot;og-desc&quot;&gt;The &quot;kv&quot; command groups subcommands for interacting with Vault's key/value secret engine.&lt;/p&gt;&lt;p class=&quot;og-host&quot;&gt;developer.hashicorp.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;h3 data-ke-size=&quot;size23&quot;&gt;1. Vault에 접속 &amp;gt; Secret Engine &amp;gt; + Enable new engine을 선택합니다.&lt;/h3&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1917&quot; data-origin-height=&quot;932&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dP3ikH/dJMcaaRjDXQ/fbcwtHLUT5kU5cG7215r30/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dP3ikH/dJMcaaRjDXQ/fbcwtHLUT5kU5cG7215r30/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dP3ikH/dJMcaaRjDXQ/fbcwtHLUT5kU5cG7215r30/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdP3ikH%2FdJMcaaRjDXQ%2FfbcwtHLUT5kU5cG7215r30%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;1917&quot; height=&quot;932&quot; data-origin-width=&quot;1917&quot; data-origin-height=&quot;932&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. ‘KV’를 선택합니다.&lt;/h3&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1900&quot; data-origin-height=&quot;660&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cFfTXj/dJMcaacHFJ4/O4EPPk02PPXNyBP8feRUfk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cFfTXj/dJMcaacHFJ4/O4EPPk02PPXNyBP8feRUfk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cFfTXj/dJMcaacHFJ4/O4EPPk02PPXNyBP8feRUfk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcFfTXj%2FdJMcaacHFJ4%2FO4EPPk02PPXNyBP8feRUfk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1900&quot; height=&quot;660&quot; data-origin-width=&quot;1900&quot; data-origin-height=&quot;660&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. Path와 Maximum number of versions 버전을 입력하고 Enable engine 버튼을 누릅니다&lt;/h3&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-style=&quot;style2&quot; data-ke-align=&quot;alignLeft&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td style=&quot;width: 30.2325%;&quot;&gt;&lt;b&gt;항목&lt;/b&gt;&lt;/td&gt;&lt;td style=&quot;width: 69.6512%;&quot;&gt;&lt;b&gt;설명&lt;/b&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style=&quot;width: 30.2325%;&quot;&gt;&lt;b&gt;Path&lt;/b&gt;&lt;/td&gt;&lt;td style=&quot;width: 69.6512%;&quot;&gt;Vault에서 이 KV 엔진이 마운트되는 경로입니다. 즉, 엔진이 어떤 URL prefix 아래에 동작할지를 결정하는 항목입니다. 예를 들어서 kv로 Path를 지정하면 kv/…/ 형태로 구성이 됩니다.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style=&quot;width: 30.2325%;&quot;&gt;&lt;b&gt;Maximum number of versions&lt;/b&gt;&lt;/td&gt;&lt;td style=&quot;width: 69.6512%;&quot;&gt;KV v2의 버전 관리 기능에서 ‘한 키를 몇 개 버전까지 보관할 것인지’를 설정하는 항목입니다. 예를 들어서 10으로 설정하면, 한 키에서 새 값을 저장할때마다 버전을 증가합니다.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style=&quot;width: 30.2325%;&quot;&gt;&lt;b&gt;Require Check and Set&lt;/b&gt;&lt;/td&gt;&lt;td style=&quot;width: 69.6512%;&quot;&gt;“동시 업데이트로 인한 덮어쓰기 방지 기능”입니다. 예를 들어서, 수정으로 인해 버전이 바뀌게 되었을때, 이전 버전이 사라지는 증상을 위해서 직접 지정하는 방식을 의미합니다.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style=&quot;width: 30.2325%;&quot;&gt;&lt;b&gt;Automate secret deletion&lt;/b&gt;&lt;/td&gt;&lt;td style=&quot;width: 69.6512%;&quot;&gt;자동 삭제 기능 활성화 기능입니다. 기본 off 상태에서는 반드시 API 또는 UI로 명시적으로 삭제를 해야하고 ON의 경우는 Lifecycle 정책에 따라서 자동삭제가 수행이 됩니다.&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1894&quot; data-origin-height=&quot;531&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kNS1B/dJMcabCGnZQ/omHTb97GcNqJ7sBDk0wmt0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kNS1B/dJMcabCGnZQ/omHTb97GcNqJ7sBDk0wmt0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kNS1B/dJMcabCGnZQ/omHTb97GcNqJ7sBDk0wmt0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkNS1B%2FdJMcabCGnZQ%2FomHTb97GcNqJ7sBDk0wmt0%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;1894&quot; height=&quot;531&quot; data-origin-width=&quot;1894&quot; data-origin-height=&quot;531&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;[ 더 알아보기 ]&lt;/b&gt;&lt;br&gt;&lt;b&gt;&lt;br&gt;&lt;/b&gt;&lt;b&gt; &amp;nbsp;Vault의 KV 버전은 어떻게 관리되는 걸까?&lt;/b&gt;&lt;br&gt;&lt;br&gt;- 아래의 KV 파일 내에 변경이 발생하는 경우, 버전 이전과 버전 이후를 비교하여 변경된 값이 있으면 버전이 증가가 됩니다.&lt;br&gt;- 예를 들어서 버전을 10으로 지정한 경우, 현재 10번째 버전이라고 했을 때 수정이 발생하면, 기존에 v1은 자동 제거가 되고, 계속 뒤에 버전관리가 되는 형태입니다.&lt;br&gt;- Vault의 특정 과거 버전으로 롤백을 하는 경우에 ‘Maximum number of versions’ 값을 적게 지정한다면 자동 제거된 버전은 롤백이 불가능합니다.&lt;/blockquote&gt;&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;// 버전 이전
{
  &quot;username&quot;: &quot;admin&quot;,
  &quot;password&quot;: &quot;1234&quot;
}

// 버전 이후 
{
  &quot;username&quot;: &quot;admin&quot;,
  &quot;password&quot;: &quot;5678&quot;
}
&lt;/code&gt;&lt;/pre&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;h3 data-ke-size=&quot;size23&quot;&gt;4. 아래와 같이 KV Secret engine이 생성되었고, ‘Create secret’ 버튼을 누릅니다.&lt;/h3&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1916&quot; data-origin-height=&quot;843&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/d5qkHS/dJMcafZnSvt/V0RDjzeI8hnCeldNeCKkHK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/d5qkHS/dJMcafZnSvt/V0RDjzeI8hnCeldNeCKkHK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/d5qkHS/dJMcafZnSvt/V0RDjzeI8hnCeldNeCKkHK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fd5qkHS%2FdJMcafZnSvt%2FV0RDjzeI8hnCeldNeCKkHK%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;1916&quot; height=&quot;843&quot; data-origin-width=&quot;1916&quot; data-origin-height=&quot;843&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;h3 data-ke-size=&quot;size23&quot;&gt;5. kv2의 하위 경로로 Path for this secret을 입력합니다.&lt;/h3&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot;&gt;&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  kv2의 하위 경로로 Path for this secret을 입력합니다.&lt;/b&gt;&lt;br&gt;&lt;br&gt;&lt;b&gt;- 임시로 /loc라는 경로로 입력하였고, KV 값을 입력하고 ‘save’ 버튼을 누릅니다.&lt;/b&gt;&lt;/blockquote&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1915&quot; data-origin-height=&quot;778&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/egL0hl/dJMb99Spseq/EFP8KZUAIpdFWBXPS2eVV0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/egL0hl/dJMb99Spseq/EFP8KZUAIpdFWBXPS2eVV0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/egL0hl/dJMb99Spseq/EFP8KZUAIpdFWBXPS2eVV0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FegL0hl%2FdJMb99Spseq%2FEFP8KZUAIpdFWBXPS2eVV0%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;1915&quot; height=&quot;778&quot; data-origin-width=&quot;1915&quot; data-origin-height=&quot;778&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;h3 data-ke-size=&quot;size23&quot;&gt;6. Secret 탭을 확인하면 아래와 같이 ‘loc’ Path 내에 정상적으로 값이 정의된 것을 확인할 수 있습니다&lt;/h3&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1916&quot; data-origin-height=&quot;627&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bhy9S9/dJMcahbPqDR/kF7aKWCqaSXmB6Wv7O4BxK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bhy9S9/dJMcahbPqDR/kF7aKWCqaSXmB6Wv7O4BxK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bhy9S9/dJMcahbPqDR/kF7aKWCqaSXmB6Wv7O4BxK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbhy9S9%2FdJMcahbPqDR%2FkF7aKWCqaSXmB6Wv7O4BxK%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;1916&quot; height=&quot;627&quot; data-origin-width=&quot;1916&quot; data-origin-height=&quot;627&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;h3 data-ke-size=&quot;size23&quot;&gt;7. 상위 prefix를 지정하는 구조로 재구성하기 위해 ‘Create new version’ 버튼을 누릅니다.&lt;/h3&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;818&quot; data-origin-height=&quot;392&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/A2eyi/dJMb995WFme/3Z2N5L0equn9SFzvDKP3ak/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/A2eyi/dJMb995WFme/3Z2N5L0equn9SFzvDKP3ak/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/A2eyi/dJMb995WFme/3Z2N5L0equn9SFzvDKP3ak/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FA2eyi%2FdJMb995WFme%2F3Z2N5L0equn9SFzvDKP3ak%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;818&quot; height=&quot;392&quot; data-origin-width=&quot;818&quot; data-origin-height=&quot;392&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;7. JSON 버튼을 활성화하고, prefix로 vault를 추가하였습니다&lt;/h3&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;845&quot; data-origin-height=&quot;605&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/yGdPV/dJMcahbPqEd/hymxXRacVGsfRaJKee3QD0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/yGdPV/dJMcahbPqEd/hymxXRacVGsfRaJKee3QD0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/yGdPV/dJMcahbPqEd/hymxXRacVGsfRaJKee3QD0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FyGdPV%2FdJMcahbPqEd%2FhymxXRacVGsfRaJKee3QD0%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;845&quot; height=&quot;605&quot; data-origin-width=&quot;845&quot; data-origin-height=&quot;605&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;pre data-ke-type=&quot;codeblock&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot;&gt;&lt;code&gt;{
&amp;nbsp;&amp;nbsp;&quot;vault&quot;: {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&quot;app_env&quot;: &quot;loc&quot;,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&quot;db_host&quot;: &quot;localhost&quot;,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&quot;db_password&quot;: &quot;localmaster&quot;,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&quot;db_user&quot;: &quot;localmaster&quot;
&amp;nbsp;&amp;nbsp;}
}&lt;/code&gt;&lt;/pre&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;h3 data-ke-size=&quot;size23&quot;&gt;8. 최종적으로 아래와 같은 구조가 완성되었습니다.&lt;/h3&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;889&quot; data-origin-height=&quot;399&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dsdn4L/dJMcaiuZ6cf/1cH7pynKksKvJmxKIExjxK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dsdn4L/dJMcaiuZ6cf/1cH7pynKksKvJmxKIExjxK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dsdn4L/dJMcaiuZ6cf/1cH7pynKksKvJmxKIExjxK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdsdn4L%2FdJMcaiuZ6cf%2F1cH7pynKksKvJmxKIExjxK%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;889&quot; height=&quot;399&quot; data-origin-width=&quot;889&quot; data-origin-height=&quot;399&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;h2 data-ke-size=&quot;size26&quot;&gt;5) 환경 설정 : Spring Boot 환경에서 Vault 접근&lt;/h2&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot;&gt;&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  환경 설정 : Spring Boot 환경에서 Vault 접근&lt;/b&gt;&lt;/blockquote&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 구조 확인&lt;/h3&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot;&gt;&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  구조 확인&lt;/b&gt;&lt;br&gt;&lt;b&gt;- 최초 Spring Boot 서버가 실행되면서 아래의 프로세스가 동작을 합니다.&lt;/b&gt;&lt;br&gt;&lt;br&gt;&lt;b&gt;1. Spring Boot 서버 시작: SpringApplication.run()&lt;/b&gt;&lt;br&gt;- 최초 Spring Boot 서버가 실행이 됩니다.&lt;br&gt;&lt;br&gt;&lt;b&gt;2. Spring Cloud Vault 자동 구성 로드: bootstrap.yml&lt;/b&gt;&lt;br&gt;- Spring Cloud Bootstrap에서 최초 Vault 설정에 대한 자동 구성이 로드가 됩니다.&lt;br&gt;&lt;br&gt;&lt;b&gt;3. Vault에 '인증(토큰)' 요청 수행 : Root Token&lt;/b&gt;&lt;br&gt;- Vault의 인증 방식 중 토큰을 이용한 방식에서도 Root Token을 통해서 Vault에 인증을 요청합니다.&lt;br&gt;&lt;br&gt;&lt;b&gt;4. Vault '인증 정보' 검증&lt;/b&gt;&lt;br&gt;- Vault에서는 유효한 토큰인지에 대해 인증 정보에 대해 검증을 합니다.&lt;br&gt;- 성공 시, KV Secret Engine에 접근합니다.&lt;br&gt;- 실패 시, 서버 시작 실패와 종료를 합니다.&lt;br&gt;&lt;b&gt;&lt;br&gt;&lt;/b&gt;&lt;b&gt;5. 조회된 Secret을 Spring Boot에 응답&lt;/b&gt;&lt;br&gt;- KV Secret Engine에서 조회된 정보를 응답으로 전달합니다&lt;br&gt;&lt;br&gt;&lt;b&gt;6. 응답받은 Secret을 Spring Environment(PropertySource)에 자동 주입&lt;/b&gt;&lt;br&gt;- Spring Boot에서는 전달받은 Secret을 Spring Environment(PropertySource)에 자동 주입을 합니다.&lt;br&gt;&lt;b&gt;&lt;br&gt;&lt;/b&gt;&lt;b&gt;7. @ConfigurationProperties VaultKVProperties.java 객체와 자동 매핑&lt;/b&gt;&lt;br&gt;- 추후에 구성할 VaultKVProperties.java 파일 내에 사전에 KV와 같은 형태로 구성한 멤버 변수 각각에 데이터가 매핑이 됩니다.&lt;br&gt;&lt;br&gt;&lt;b&gt;8. Spring Boot 서버 실행 완료&lt;/b&gt;&lt;br&gt;- 최종 작업이 완료되어서 Spring Boot 서버가 실행이 완료가 됩니다.&lt;/blockquote&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1042&quot; data-origin-height=&quot;769&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cxrtu1/dJMcaaRjD6g/KIkggMd84rn0KkQgCEE0u1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cxrtu1/dJMcaaRjD6g/KIkggMd84rn0KkQgCEE0u1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cxrtu1/dJMcaaRjD6g/KIkggMd84rn0KkQgCEE0u1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcxrtu1%2FdJMcaaRjD6g%2FKIkggMd84rn0KkQgCEE0u1%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;1042&quot; height=&quot;769&quot; data-origin-width=&quot;1042&quot; data-origin-height=&quot;769&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. 개발 환경&lt;/h3&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot;&gt;&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  개발 환경&lt;/b&gt;&lt;br&gt;&lt;br&gt;- 기본 기능과 주요 라이브러리인 spring-cloud-dependencies, spring-cloud-starter-bootstrap, spring-cloud-starter-vault-config를 설정하였습니다.&lt;/blockquote&gt;&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-style=&quot;style2&quot; data-ke-align=&quot;alignLeft&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;b&gt;개발 환경&lt;/b&gt;&lt;/td&gt;&lt;td&gt;&lt;b&gt;분류&lt;/b&gt;&lt;/td&gt;&lt;td&gt;&lt;b&gt;버전&lt;/b&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;b&gt;Java JDK&lt;/b&gt;&lt;/td&gt;&lt;td&gt;Java Version&lt;/td&gt;&lt;td&gt;17&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;b&gt;빌드 도구&lt;/b&gt;&lt;/td&gt;&lt;td&gt;Gradle&lt;/td&gt;&lt;td&gt;8.14.3&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;b&gt;Spring Boot&lt;/b&gt;&lt;/td&gt;&lt;td&gt;Spring Boot Starter&lt;/td&gt;&lt;td&gt;3.5.8&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;b&gt;Spring Boot Web&lt;/b&gt;&lt;/td&gt;&lt;td&gt;Spring Boot Starter&lt;/td&gt;&lt;td&gt;3.5.8&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;b&gt;spring-cloud-starter-bootstrap&lt;/b&gt;&lt;/td&gt;&lt;td&gt;Spring Boot Cloud&lt;/td&gt;&lt;td&gt;2025.0.0&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;b&gt;spring-cloud-starter-vault-config&lt;/b&gt;&lt;/td&gt;&lt;td&gt;Spring Boot Cloud&lt;/td&gt;&lt;td&gt;2025.0.0&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;b&gt;spring-cloud-dependencies&lt;/b&gt;&lt;/td&gt;&lt;td&gt;Spring Boot Cloud&lt;/td&gt;&lt;td&gt;2025.0.0&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;b&gt;Lombok&lt;/b&gt;&lt;/td&gt;&lt;td&gt;Library&lt;/td&gt;&lt;td&gt;latest&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;h3 data-ke-size=&quot;size23&quot;&gt;3. 라이브러리 설치 : build.gradle&lt;/h3&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot;&gt;&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  라이브러리 설치 : build.gradle&lt;/b&gt;&lt;br&gt;- 위에서 명시한 라이브러리를 Gradle을 이용하여서 설치합니다.&lt;/blockquote&gt;&lt;pre class=&quot;gradle&quot;&gt;&lt;code&gt;plugins {
    id 'java'
    id 'org.springframework.boot' version '3.5.8'
    id 'io.spring.dependency-management' version '1.1.7'
}
ext {
    springCloudVersion = &quot;2025.0.0&quot;
}

group = 'com.adjh'
version = '0.0.1-SNAPSHOT'
description = 'spring-boot3-vault'

java {
    toolchain {
        languageVersion = JavaLanguageVersion.of(17)
    }
}

configurations {
    compileOnly {
        extendsFrom annotationProcessor
    }
}

repositories {
    mavenCentral()
}

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-web'           // Spring Boot Web

    implementation 'org.springframework.cloud:spring-cloud-starter-bootstrap'   // Spring Cloud BootStrap
    implementation 'org.springframework.cloud:spring-cloud-starter-vault-config'// Spring Cloud Vault
    compileOnly 'org.projectlombok:lombok'                                      // Lombok Compile
    annotationProcessor 'org.projectlombok:lombok'                              // Lombok Annotation
    testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
    testImplementation 'org.springframework.boot:spring-boot-starter-test'
}
dependencyManagement {
    imports {
        mavenBom &quot;org.springframework.cloud:spring-cloud-dependencies:$springCloudVersion&quot;
    }
}

tasks.named('test') {
    useJUnitPlatform()
}

&lt;/code&gt;&lt;/pre&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;h3 data-ke-size=&quot;size23&quot;&gt;4. 시스템 환경변수 주입 : IntelliJ IDE 활용&lt;/h3&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot;&gt;&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  시스템 환경변수 주입 : IntelliJ IDE 활용&lt;/b&gt;&lt;br&gt;&lt;br&gt;- Vault로 전달이 되는 Root Token 자체를 프로젝트 내에 넣고 Git에 올릴 수 없기에 ‘시스템 환경 변수’ 내에 추가합니다.&lt;/blockquote&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  [참고]&lt;/b&gt;&amp;nbsp;아래의 환경 변수를 추가하는 방법의 글을 참고하셔도 도움이 됩니다.&lt;/blockquote&gt;&lt;figure data-ke-type=&quot;opengraph&quot; data-og-title=&quot;[Java] Spring Boot 환경에서 시스템 변수 .env 파일 지정 및 활용 방법&quot; data-ke-align=&quot;alignCenter&quot; data-og-description=&quot;해당 글에서는 Spring Boot 개발 환경에서 .env 파일을 시스템 변수로 지정하는 방법과 이를 불러오는 다양한 방법에 대해서 알아봅니다1) Spring Boot 환경에서 .env 파일을 시스템 변수로 적용하는 이&quot; data-og-host=&quot;adjh54.tistory.com&quot; data-og-source-url=&quot;https://adjh54.tistory.com/722&quot; data-og-image=&quot;https://blog.kakaocdn.net/dna/cTlopd/hyZOJQ6Ft0/AAAAAAAAAAAAAAAAAAAAAMZUI8kDh4QFiT8441cZ7djEVpJ9TXWXapVkqA1l3Cqj/img.png?credential=yqXZFxpELC7KVnFOS48ylbz2pIh7yKj8&amp;amp;expires=1767193199&amp;amp;allow_ip=&amp;amp;allow_referer=&amp;amp;signature=OoG0dDS35XZvUkssJefxIZJQ3K8%3D&quot; data-og-url=&quot;https://adjh54.tistory.com/722&quot;&gt;&lt;a href=&quot;https://adjh54.tistory.com/722&quot; target=&quot;_blank&quot; data-source-url=&quot;https://adjh54.tistory.com/722&quot;&gt;&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://blog.kakaocdn.net/dna/cTlopd/hyZOJQ6Ft0/AAAAAAAAAAAAAAAAAAAAAMZUI8kDh4QFiT8441cZ7djEVpJ9TXWXapVkqA1l3Cqj/img.png?credential=yqXZFxpELC7KVnFOS48ylbz2pIh7yKj8&amp;amp;expires=1767193199&amp;amp;allow_ip=&amp;amp;allow_referer=&amp;amp;signature=OoG0dDS35XZvUkssJefxIZJQ3K8%3D')&quot;&gt; &lt;/div&gt;&lt;div class=&quot;og-text&quot;&gt;&lt;p class=&quot;og-title&quot;&gt;[Java] Spring Boot 환경에서 시스템 변수 .env 파일 지정 및 활용 방법&lt;/p&gt;&lt;p class=&quot;og-desc&quot;&gt;해당 글에서는 Spring Boot 개발 환경에서 .env 파일을 시스템 변수로 지정하는 방법과 이를 불러오는 다양한 방법에 대해서 알아봅니다1) Spring Boot 환경에서 .env 파일을 시스템 변수로 적용하는 이&lt;/p&gt;&lt;p class=&quot;og-host&quot;&gt;adjh54.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;h4 data-ke-size=&quot;size20&quot;&gt;4.1. IDE툴 오른쪽 상단에 서버 &amp;gt; 구성 편집.. 버튼을 누릅니다&lt;/h4&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1245&quot; data-origin-height=&quot;282&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bRVAmq/dJMcacO7Ioy/palHTMJGKFnYpEksXZTCGK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bRVAmq/dJMcacO7Ioy/palHTMJGKFnYpEksXZTCGK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bRVAmq/dJMcacO7Ioy/palHTMJGKFnYpEksXZTCGK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbRVAmq%2FdJMcacO7Ioy%2FpalHTMJGKFnYpEksXZTCGK%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;1245&quot; height=&quot;282&quot; data-origin-width=&quot;1245&quot; data-origin-height=&quot;282&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;h4 data-ke-size=&quot;size20&quot;&gt;4.2. 옵션 수정(Modify options) 버튼을 누릅니다.&lt;/h4&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;816&quot; data-origin-height=&quot;689&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/DlVsL/dJMcafSCQvg/kjHLmhhZo2vHALbE0FHnV0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/DlVsL/dJMcafSCQvg/kjHLmhhZo2vHALbE0FHnV0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/DlVsL/dJMcafSCQvg/kjHLmhhZo2vHALbE0FHnV0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FDlVsL%2FdJMcafSCQvg%2FkjHLmhhZo2vHALbE0FHnV0%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;816&quot; height=&quot;689&quot; data-origin-width=&quot;816&quot; data-origin-height=&quot;689&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;h4 data-ke-size=&quot;size20&quot;&gt;4.3. 운영 체제(Operating System) &amp;gt; 환경 변수(Environment variables)를 선택합니다&lt;/h4&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1124&quot; data-origin-height=&quot;838&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bUCc0d/dJMcahv8hz1/F8QboJgWWMGrAqnRznd56k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bUCc0d/dJMcahv8hz1/F8QboJgWWMGrAqnRznd56k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bUCc0d/dJMcahv8hz1/F8QboJgWWMGrAqnRznd56k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbUCc0d%2FdJMcahv8hz1%2FF8QboJgWWMGrAqnRznd56k%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;1124&quot; height=&quot;838&quot; data-origin-width=&quot;1124&quot; data-origin-height=&quot;838&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;h4 data-ke-size=&quot;size20&quot;&gt;4.4. 환경 변수를 추가했습니다. 값은 Docker에서 발급받은 Initial Root Token 값을 넣고 수행을 하였습니다&lt;/h4&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot;&gt;&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  환경 변수를 추가했습니다. 값은 Docker에서 발급받은 Initial Root Token 값을 넣고 수행을 하였습니다&lt;/b&gt;&lt;/blockquote&gt;&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;VAULT_MAIN_TOKEN=hvs.qBlCBqEKIGtJfCA9grVMHbrV;
&lt;/code&gt;&lt;/pre&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;814&quot; data-origin-height=&quot;693&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bBuLDE/dJMcafyjHBs/MIobK7g6VufUzfMVthkpQk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bBuLDE/dJMcafyjHBs/MIobK7g6VufUzfMVthkpQk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bBuLDE/dJMcafyjHBs/MIobK7g6VufUzfMVthkpQk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbBuLDE%2FdJMcafyjHBs%2FMIobK7g6VufUzfMVthkpQk%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;814&quot; height=&quot;693&quot; data-origin-width=&quot;814&quot; data-origin-height=&quot;693&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;&amp;nbsp;  아래에 최초 Vault init을 실행할 때 발급받은 키입니다.&lt;/b&gt;&lt;/blockquote&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1143&quot; data-origin-height=&quot;405&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c42VgS/dJMcaaDLPuQ/AKhqPiM1SEmahWwuMM4hn1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c42VgS/dJMcaaDLPuQ/AKhqPiM1SEmahWwuMM4hn1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c42VgS/dJMcaaDLPuQ/AKhqPiM1SEmahWwuMM4hn1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc42VgS%2FdJMcaaDLPuQ%2FAKhqPiM1SEmahWwuMM4hn1%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;1143&quot; height=&quot;405&quot; data-origin-width=&quot;1143&quot; data-origin-height=&quot;405&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;h3 data-ke-size=&quot;size23&quot;&gt;5. bootstrap.yml 파일을 생성 및 구성합니다.&lt;/h3&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot;&gt;&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  bootstrap.yml 파일을 생성 및 구성합니다.&lt;/b&gt;&lt;br&gt;&lt;br&gt;&lt;b&gt;- resources 경로 하단에 bootstrap.yml 파일을 생성하였습니다&lt;/b&gt;&lt;/blockquote&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;461&quot; data-origin-height=&quot;595&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cuQ74Y/dJMcaa4Qgmj/lzERh9R7Cjf8pqXLoc0w40/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cuQ74Y/dJMcaa4Qgmj/lzERh9R7Cjf8pqXLoc0w40/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cuQ74Y/dJMcaa4Qgmj/lzERh9R7Cjf8pqXLoc0w40/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcuQ74Y%2FdJMcaa4Qgmj%2FlzERh9R7Cjf8pqXLoc0w40%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;461&quot; height=&quot;595&quot; data-origin-width=&quot;461&quot; data-origin-height=&quot;595&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  yml 파일을 아래와 같이 구성하였습니다.&lt;/b&gt;&lt;/blockquote&gt;&lt;pre class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot;&gt;&lt;code&gt;spring:
  cloud:
    vault:
      uri: &amp;lt;http://localhost:8200&amp;gt;   # Vault 서버 주소 (UI 주소 아님, API 엔드포인트)
      authentication: TOKEN        # Vault 인증 방식 (TOKEN, APPROLE, APPID 등)
      token: ${VAULT_MAIN_TOKEN}   # 환경변수에서 Vault 접근 토큰을 불러옴
      connection-timeout: 5000     # Vault 서버 연결 시도 타임아웃(ms)
      read-timeout: 15000          # Vault 데이터 읽기 타임아웃(ms)

      kv:
        enabled: true              # KV(키-값) 비밀 엔진 사용 여부
        backend: kv2               # Vault에서 생성한 엔진 이름(ex: kv2), 반드시 일치해야 함
                                   # 실제 Vault 경로가 &quot;kv2/&quot; 라면 backend도 kv2로 설정해야 함

        default-context: loc       # 기본 Secret 경로 (예: kv2/loc/data/application 같은 구조가 됨)
                                   # 즉, Vault UI에서는 /kv2/data/loc 경로와 매핑됨

        application-name: ''       # Spring 애플리케이션 이름을 Secret 경로에 추가할지 여부
                                   # 빈 값이면 &quot;default-context&quot; 바로 아래에서 조회함
                                   # 예: default: '' → kv2/loc/data&lt;/code&gt;&lt;/pre&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;h3 data-ke-size=&quot;size23&quot;&gt;6. 서버 실행&lt;/h3&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot;&gt;&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  서버 실행&lt;/b&gt;&lt;br&gt;&lt;br&gt;&lt;b&gt;- 정상적으로 오류 없이 서버가 실행하였습니다.&lt;/b&gt;&lt;/blockquote&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1756&quot; data-origin-height=&quot;607&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bES0c2/dJMcagqsmIW/tMCEQLJDHsonHjLRf72aIk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bES0c2/dJMcagqsmIW/tMCEQLJDHsonHjLRf72aIk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bES0c2/dJMcagqsmIW/tMCEQLJDHsonHjLRf72aIk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbES0c2%2FdJMcagqsmIW%2FtMCEQLJDHsonHjLRf72aIk%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;1756&quot; height=&quot;607&quot; data-origin-width=&quot;1756&quot; data-origin-height=&quot;607&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;h3 data-ke-size=&quot;size23&quot;&gt;7. Vault 값과 객체 매핑: @ConfigurationProperties&lt;/h3&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot;&gt;&lt;blockquote data-ke-style=&quot;style3&quot;&gt; &lt;b&gt;Vault 값과 객체 매핑: @ConfigurationProperties&lt;/b&gt;&lt;br&gt;&lt;br&gt;&lt;b&gt;- Vault에서 읽어온 값을 자바 클래스 필드에 자동 바인딩을 하는 클래스입니다.&lt;/b&gt;&lt;br&gt;&lt;br&gt;&lt;b&gt;- @Getter, @Setter&lt;/b&gt; 통해서 멤버 변수에 접근할 수 있도록 어노테이션을 지정하였습니다.&lt;br&gt;&lt;b&gt;- @Component&lt;/b&gt;를 빈으로 등록하여서, 다른 서비스나 컴포넌트에 주입하여 사용이 가능하도록 하였습니다.&lt;br&gt;&lt;b&gt;- @ConfigurationProperties(prefix = &quot;vault&quot;)&lt;/b&gt; : 상위 prefix 내에 하위에 동일한 이름으로 각각 자동 바인딩이 됩니다.&lt;/blockquote&gt;&lt;pre data-ke-type=&quot;codeblock&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;package com.adjh.springboot3vault.properties;

import lombok.Getter;
import lombok.Setter;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;

/**
 * Vault Key-Value Properties Mapping value
 *
 * @author : leejonghoon
 * @fileName : VaultKVProperties
 * @since : 25. 11. 25.
 */
@Getter
@Setter
@Component
@ConfigurationProperties(prefix = &quot;&quot;)
public class VaultKVProperties {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;private String appEnv;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;private String dbHost;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;private String dbPassword;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;private String dbUser;
}&lt;/code&gt;&lt;/pre&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1310&quot; data-origin-height=&quot;698&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/zo0xm/dJMcacawh4Q/7Znh4MWzAKTdkKunfoW7DK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/zo0xm/dJMcacawh4Q/7Znh4MWzAKTdkKunfoW7DK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/zo0xm/dJMcacawh4Q/7Znh4MWzAKTdkKunfoW7DK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fzo0xm%2FdJMcacawh4Q%2F7Znh4MWzAKTdkKunfoW7DK%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;1310&quot; height=&quot;698&quot; data-origin-width=&quot;1310&quot; data-origin-height=&quot;698&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;h3 data-ke-size=&quot;size23&quot;&gt;8. 콘솔을 확인하기 위해 Controller를 구성하였습니다.&lt;/h3&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot;&gt;&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  콘솔을 확인하기 위해 Controller를 구성하였습니다.&lt;/b&gt;&lt;br&gt;&lt;br&gt;&lt;b&gt;- 생성자 패턴으로 bean을 호출하여서 Controller 내에서 활용하도록 구성하였습니다.&lt;/b&gt;&lt;br&gt;- http://localhost:8080 포트로 접근하여서 호출이 된다면 각각의 vault 값이 정상적으로 출력이 됩니다.&lt;/blockquote&gt;&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;package com.adjh.springboot3vault.controller;

import com.adjh.springboot3vault.properties.VaultKVProperties;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * Bean을 호출하여서 Vault 값을 테스트하기 위한 Controller
 *
 * @author : leejonghoon
 * @fileName : VaultController
 * @since : 25. 11. 25.
 */
@RestController
public class VaultController {

    private final VaultKVProperties vaultKVProperties;

    public VaultController(VaultKVProperties vaultKVProperties) {
        this.vaultKVProperties = vaultKVProperties;
    }

    @GetMapping(&quot;/&quot;)
    public String main() {
        System.out.println(&quot;Getter appEnv : &quot; + vaultKVProperties.getAppEnv());
        System.out.println(&quot;Getter dbHost : &quot; + vaultKVProperties.getDbHost());
        System.out.println(&quot;Getter dbPassword : &quot; + vaultKVProperties.getDbPassword());
        System.out.println(&quot;Getter dbUser : &quot; + vaultKVProperties.getDbUser());
        return &quot;임시 테스트 &quot;;
    }
}&lt;/code&gt;&lt;/pre&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;h3 data-ke-size=&quot;size23&quot;&gt;9. 결과확인&lt;/h3&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot;&gt;&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  결과확인&lt;/b&gt;&lt;br&gt;&lt;br&gt;- 정상적으로 출력이 됨을 확인하였습니다.&lt;/blockquote&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1473&quot; data-origin-height=&quot;903&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dD3RKg/dJMcagD0zgE/XJmI2KqTguDBqL2ijoxhHK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dD3RKg/dJMcagD0zgE/XJmI2KqTguDBqL2ijoxhHK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dD3RKg/dJMcagD0zgE/XJmI2KqTguDBqL2ijoxhHK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdD3RKg%2FdJMcagD0zgE%2FXJmI2KqTguDBqL2ijoxhHK%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;1473&quot; height=&quot;903&quot; data-origin-width=&quot;1473&quot; data-origin-height=&quot;903&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&lt;b&gt;오늘도&amp;nbsp;감사합니다.&amp;nbsp; &lt;/b&gt;&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Java/Spring Cloud</category>
      <author>adjh54</author>
      <guid isPermaLink="true">https://adjh54.tistory.com/728</guid>
      <comments>https://adjh54.tistory.com/728#entry728comment</comments>
      <pubDate>Thu, 4 Dec 2025 17:53:10 +0900</pubDate>
    </item>
    <item>
      <title>[Java] Spring Boot Dotenv 이해하고 활용하기 -1 : .env 로드, .yml 매핑 로드</title>
      <link>https://adjh54.tistory.com/727</link>
      <description>&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;해당 글에서는 Spring Boot 환경에서 Dotenv 오픈소스 라이브러리를 이용하여서 .env 파일을 불러오거나 .env 파일을 yml 파일 형태로 매핑하여 불러오는 방법에 대해 알아봅니다.&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;
&lt;figure contenteditable=&quot;false&quot; data-ke-type=&quot;emoticon&quot; data-ke-align=&quot;alignCenter&quot; data-emoticon-type=&quot;face&quot; data-emoticon-name=&quot;073&quot; data-emoticon-isanimation=&quot;false&quot; data-emoticon-src=&quot;https://t1.daumcdn.net/keditor/emoticon/face/large/073.png&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/keditor/emoticon/face/large/073.png&quot; width=&quot;80&quot; /&gt;&lt;/figure&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1) Spring Boot Dotenv&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  Spring Boot Dotenv&lt;/b&gt;&lt;br /&gt;&lt;b&gt;&lt;br /&gt;- Java와 Kotlin에서 .env 파일을 쉽게 로드하여 환경 변수처럼 사용할 수 있게 해주는 오픈소스 라이브러리를 의미합니다.&lt;/b&gt;&lt;br /&gt;- 즉, 프로젝트의 루트 경로에 .env 파일 내에 KV 형태로 변수를 두고 환경 변수로써 사용하는 방식입니다. &lt;br /&gt;- Spring Boot 내에서는 Node 환경과 다르게 .env 파일을 직접 읽어올 수 없고 대신 OS 환경 변수, application.properties, yaml 등을 읽어 올 수 있습니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  [참고]&lt;/b&gt; dotenv-java 오픈소스 라이브러리 사이트&amp;nbsp;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/cdimascio/dotenv-java&quot;&gt;https://github.com/cdimascio/dotenv-java&lt;/a&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;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  [참고]&lt;/b&gt; .env 파일 자체를 주입하는 방법에 대해 궁금하시면 아래의 글을 참고하시면 도움이 됩니다.&lt;/blockquote&gt;
&lt;figure id=&quot;og_1764665356490&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;[Java] Spring Boot 환경에서 시스템 변수 .env 파일 지정 및 활용 방법&quot; data-og-description=&quot;해당 글에서는 Spring Boot 개발 환경에서 .env 파일을 시스템 변수로 지정하는 방법과 이를 불러오는 다양한 방법에 대해서 알아봅니다1) Spring Boot 환경에서 .env 파일을 시스템 변수로 적용하는 이&quot; data-og-host=&quot;adjh54.tistory.com&quot; data-og-source-url=&quot;https://adjh54.tistory.com/722&quot; data-og-url=&quot;https://adjh54.tistory.com/722&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/OPdIC/hyZODb6BKW/pNRkrq6EZEYsSHViHnAar1/img.png?width=800&amp;amp;height=432&amp;amp;face=0_0_800_432,https://scrap.kakaocdn.net/dn/gS3Aj/hyZNHNX3Vu/TeDFnYonHiT4tuUoartOf1/img.png?width=800&amp;amp;height=432&amp;amp;face=0_0_800_432,https://scrap.kakaocdn.net/dn/rc5ax/hyZOQJsMVS/Hkrsn30W6cZoU4Hnlb2nPk/img.png?width=1313&amp;amp;height=539&amp;amp;face=0_0_1313_539&quot;&gt;&lt;a href=&quot;https://adjh54.tistory.com/722&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://adjh54.tistory.com/722&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/OPdIC/hyZODb6BKW/pNRkrq6EZEYsSHViHnAar1/img.png?width=800&amp;amp;height=432&amp;amp;face=0_0_800_432,https://scrap.kakaocdn.net/dn/gS3Aj/hyZNHNX3Vu/TeDFnYonHiT4tuUoartOf1/img.png?width=800&amp;amp;height=432&amp;amp;face=0_0_800_432,https://scrap.kakaocdn.net/dn/rc5ax/hyZOQJsMVS/Hkrsn30W6cZoU4Hnlb2nPk/img.png?width=1313&amp;amp;height=539&amp;amp;face=0_0_1313_539');&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;[Java] Spring Boot 환경에서 시스템 변수 .env 파일 지정 및 활용 방법&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;해당 글에서는 Spring Boot 개발 환경에서 .env 파일을 시스템 변수로 지정하는 방법과 이를 불러오는 다양한 방법에 대해서 알아봅니다1) Spring Boot 환경에서 .env 파일을 시스템 변수로 적용하는 이&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;adjh54.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;h2 data-ke-size=&quot;size26&quot;&gt;2) Spring Boot Dotenv 공통 환경설정&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. build.gradle&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  build.gradle&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 가장 최신버전으로 설치를 해줍니다. 참고로 Java 11 버전에서 수행이 된다고 합니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;// &amp;lt;https://mvnrepository.com/artifact/io.github.cdimascio/dotenv-java&amp;gt;
implementation(&quot;io.github.cdimascio:dotenv-java:3.2.0&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  [참고]&lt;/b&gt;&amp;nbsp;Java 8 버전에서도 실행이 될까?&lt;br /&gt;&lt;br /&gt;- Spring Boot 1.x.x 버전의 Java 8 버전에서 실행이 되는 버전은 2.3.2입니다.&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;957&quot; data-origin-height=&quot;319&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/GYq7M/dJMb99LCBXv/9TojmWkDCJpMtn0GkTZSkk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/GYq7M/dJMb99LCBXv/9TojmWkDCJpMtn0GkTZSkk/img.png&quot; data-alt=&quot;https://github.com/cdimascio/dotenv-java?tab=readme-ov-file#install&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/GYq7M/dJMb99LCBXv/9TojmWkDCJpMtn0GkTZSkk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FGYq7M%2FdJMb99LCBXv%2F9TojmWkDCJpMtn0GkTZSkk%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;957&quot; height=&quot;319&quot; data-origin-width=&quot;957&quot; data-origin-height=&quot;319&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://github.com/cdimascio/dotenv-java?tab=readme-ov-file#install&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1764665521441&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt; &amp;lt;dependency&amp;gt;
    &amp;lt;groupId&amp;gt;io.github.cdimascio&amp;lt;/groupId&amp;gt;
    &amp;lt;artifactId&amp;gt;dotenv-java&amp;lt;/artifactId&amp;gt;
    &amp;lt;version&amp;gt;2.3.2&amp;lt;/version&amp;gt; &amp;lt;!-- Java 8 호환 버전 --&amp;gt;
&amp;lt;/dependency&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;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. .env&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  .env&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;- 불러올 값들이 저장된 파일로 루트 경로에 위치해 둡니다.&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;372&quot; data-origin-height=&quot;350&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/yGsud/dJMcaaKwYcV/h3eAftO2FzqbjWx9unjcxk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/yGsud/dJMcaaKwYcV/h3eAftO2FzqbjWx9unjcxk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/yGsud/dJMcaaKwYcV/h3eAftO2FzqbjWx9unjcxk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FyGsud%2FdJMcaaKwYcV%2Fh3eAftO2FzqbjWx9unjcxk%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;372&quot; height=&quot;350&quot; data-origin-width=&quot;372&quot; data-origin-height=&quot;350&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;APP_ENV=loc
DB_HOST=8080
DB_USER=localhost
TEST_KEY=None
&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;h3 data-ke-size=&quot;size23&quot;&gt;3. application.yml&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  application.yml&lt;/b&gt;&lt;br /&gt;&lt;b&gt;&lt;br /&gt;- 해당 과정은 .env 파일을 .properties 형식으로 읽어 들이기 위한 설정입니다.&lt;/b&gt;&lt;br /&gt;&lt;b&gt;&lt;br /&gt;- config.import :&lt;/b&gt; 외부 설정 파일을 애플리케이션 설정에 추가로 로드를 하겠음을 지정합니다.&lt;br /&gt;&lt;b&gt;&lt;br /&gt;- optional:file:.env[.properties] :&lt;/b&gt;&lt;br /&gt;1. &lt;b&gt;optional&lt;/b&gt;: 해당 파일이 없어도 에러 없이 무시하고 애플리케이션을 계속 실행합니다.&lt;br /&gt;2. &lt;b&gt;file:.env[.properties]:&lt;/b&gt; .env 파일을 .properties 형식으로 해석해서 읽겠다는 의미입니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;spring:
  application:
    name: spring-boot-dotenv
  # 해당 부분 추가
  config:
    import: optional:file:.env[.properties]
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2) Spring Boot Dotenv 환경설정 및 활용 - 1: .env 파일 정보 가져오기&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  Spring Boot Dotenv 환경설정 및 활용 - 1: .env 파일 정보 가져오기&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 해당 부분에서는 .env 파일을 즉시 읽어서 각각의 값을 불러오는 과정입니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 프로세스 확인&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  프로세스 확인&lt;/b&gt;&lt;br /&gt;&lt;b&gt;&lt;br /&gt;1. Spring Boot 서버를 실행합니다&lt;/b&gt;&lt;br /&gt;- 서버 실행 이후 @Configuration 어노테이션으로 설정 정보가 세팅이 됩니다.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 사용자 지정한 DotEnvCofig.java 파일 내에 함수가 실행이 됩니다.&lt;/b&gt;&lt;br /&gt;1.1. DotEnvConfig 생성자로 Dotenv 클래스를 불러와서 로드를 수행하여서 dotenv 멤버변수에 세팅을 합니다. &lt;br /&gt;1.2. dotenv() 함수로 구성한 값을 Bean에 등록을 합니다&lt;br /&gt;- Spring Boot 서버가 실행이 완료가 되었을 때 값은 모두 세팅이 된 상태가 됩니다.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;3. 사용자가 특정 URI로 접근을 합니다.&lt;/b&gt;&lt;br /&gt;- 접근을 하는 Controller 내에서는 생성자로 Dotenv를 생성합니다. 그리고 생성한 생성자의 Dotenv의 객체의 .get()메서드를 통해서 .env 키 값을 넣고 값을 조회합니다.&lt;br /&gt;- 최종적으로 데이터가 조회가 됩니다.&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1046&quot; data-origin-height=&quot;759&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/sXvUS/dJMcaiBK8bz/JjwcYMVba32WABrGNgEuSK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/sXvUS/dJMcaiBK8bz/JjwcYMVba32WABrGNgEuSK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/sXvUS/dJMcaiBK8bz/JjwcYMVba32WABrGNgEuSK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FsXvUS%2FdJMcaiBK8bz%2FJjwcYMVba32WABrGNgEuSK%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;1046&quot; height=&quot;759&quot; data-origin-width=&quot;1046&quot; data-origin-height=&quot;759&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;h3 data-ke-size=&quot;size23&quot;&gt;2. DotEnvConfig&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  DotEnvConfig&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;- DotEnvConfig 생성자로 Dotenv 클래스를 불러와서 로드를 수행하여서 dotenv 멤버 변수에 세팅을 합니다.&lt;br /&gt;- dotenv() 함수로 구성한 값을 Bean에 등록을 합니다&lt;/blockquote&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;package com.adjh.springbootdotenv.config;

import io.github.cdimascio.dotenv.Dotenv;
import jakarta.annotation.PostConstruct;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * DotEnv 환경설정
 *
 * @author : leejonghoon
 * @fileName : DotEnvConfig
 * @since : 25. 12. 01.
 */
@Configuration
public class DotEnvConfig {

    private final Dotenv dotenv;

    /**
     * DotEnv 로드 및 데이터 조회
     */
    public DotEnvConfig() {
        this.dotenv = Dotenv.configure()
                .ignoreIfMalformed()
                .ignoreIfMissing()
                .load();
    }

    @Bean
    public Dotenv dotenv() {
        return this.dotenv;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. 데이터 불러오기&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  데이터 불러오기&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- Bean에 등록된 Dotenv 클래스를 생성자로 불러옵니다. 그리고 생성한 생성자의 Dotenv의 객체의 .get()메서드를 통해서 .env 키 값을 넣고 값을 조회합니다.&lt;/b&gt;&lt;br /&gt;- 최종적으로 데이터가 조회가 됩니다.&lt;/blockquote&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;package com.adjh.springbootdotenv.controller;

import io.github.cdimascio.dotenv.Dotenv;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * Env 불러오는 Controller
 *
 * @author : leejonghoon
 * @fileName : DotEnvController
 * @since : 25. 12. 2.
 */
@RestController
public class DotEnvController {

//    @Autowired
//    private Dotenv dotenv;

    // or

    private final Dotenv dotenv;

    public DotEnvController(Dotenv dotenv) {
        this.dotenv = dotenv;
    }

    @GetMapping(&quot;/&quot;)
    public String main() {
        String appEnv = dotenv.get(&quot;APP_ENV&quot;);
        String dbHost = dotenv.get(&quot;DB_HOST&quot;);
        String dbUser = dotenv.get(&quot;DB_USER&quot;);
        String testKey = dotenv.get(&quot;TEST_KEY&quot;);

        System.out.println(&quot;App env: &quot; + appEnv);
        System.out.println(&quot;DB host: &quot; + dbHost);
        System.out.println(&quot;DB user: &quot; + dbUser);
        System.out.println(&quot;Test key: &quot; + testKey);
        return &quot;임시 테스트 &quot;;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  아래와 같이 정상적으로 출력이 됨을 확인하였습니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1826&quot; data-origin-height=&quot;356&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/MylQN/dJMcac2BHOj/7gfgHI2QaRS9SSEqhODF7k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/MylQN/dJMcac2BHOj/7gfgHI2QaRS9SSEqhODF7k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/MylQN/dJMcac2BHOj/7gfgHI2QaRS9SSEqhODF7k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FMylQN%2FdJMcac2BHOj%2F7gfgHI2QaRS9SSEqhODF7k%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;1826&quot; height=&quot;356&quot; data-origin-width=&quot;1826&quot; data-origin-height=&quot;356&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3) Spring Boot Dotenv 환경설정 - 2: .env 파일을 yaml 파일 내 매핑(@Value 로드)&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  Spring Boot Dotenv 환경설정 - 2: .env 파일을 yaml 파일 내 매핑(@Value 로드)&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 해당 처리과정은 .env 파일 내에 값을 불러와서 yaml 파일 내에 ${&amp;rdquo;키&amp;rdquo;} 값 형태로 매핑을 하여, 최종적으로 yaml 파일 자체를 코드 내에서 이용하는 방식입니다.&lt;/b&gt;&lt;br /&gt;- 해당 방식을 이용하면 .env만을 사용하는 방식에 반해 yaml 파일 내에 명시된 값을 기반으로 관리가 편리해지고, 중앙화를 하여서 키 값을 관리할 수 있다는 점입니다. 각각의 값을 가져오는 @Value 어노테이션을 활용하여 조회합니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 프로세스&lt;/h3&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;style3&quot;&gt;&lt;b&gt; 프로세스 확인&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;1. Spring Boot 서버를 실행합니다&lt;/b&gt;&lt;br /&gt;&lt;b&gt;&lt;br /&gt;2. 서버 실행 이후 @Configuration 어노테이션으로 설정 정보가 세팅이 됩니다.&lt;/b&gt;&lt;br /&gt;- 사용자 지정한 DotEnvCofig.java 파일 내에 함수가 실행이 됩니다.&lt;br /&gt;1. 정적블록(static{})을 이용하여서 클래스가 처음 로딩될 때 한 번만 실행하도록 합니다.&amp;nbsp;&lt;br /&gt;2. Dotenv 객체를 로드해 옵니다.&amp;nbsp;&lt;br /&gt;3. DotEnv 로드된 값을 기반으로 순회하여 properties 값 세팅합니다.&lt;br /&gt;&lt;br /&gt;- Spring Boot 서버가 실행이 완료가 되었을 때, 값은 application.yml 파일 내에 모두 세팅이 된 상태입니다.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;3. 사용자가 특정 URI로 접근을 합니다.&lt;/b&gt;&lt;br /&gt;- @Value를 통해서 매핑된 yml 파일 내에 application.yml 파일 내의 속성을 각각 조회합니다.&lt;br /&gt;- 최종적으로 데이터가 조회가 됩니다.&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1037&quot; data-origin-height=&quot;780&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Ci7l0/dJMcah3XYG2/ThyKW0nCbXPK7BgYjSl1O1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Ci7l0/dJMcah3XYG2/ThyKW0nCbXPK7BgYjSl1O1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Ci7l0/dJMcah3XYG2/ThyKW0nCbXPK7BgYjSl1O1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FCi7l0%2FdJMcah3XYG2%2FThyKW0nCbXPK7BgYjSl1O1%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;1037&quot; height=&quot;780&quot; data-origin-width=&quot;1037&quot; data-origin-height=&quot;780&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;h3 data-ke-size=&quot;size23&quot;&gt;2. application.yml&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  application.yml&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- .env 키 값과 동일한 값으로 각각을 불러옵니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;pre class=&quot;dts&quot;&gt;&lt;code&gt;spring:
  application:
    name: spring-boot-dotenv
  # 해당 부분 추가
  config:
    import: optional:file:.env[.properties]

# env 파일의 key값 참조
custom:
  appEnv: ${APP_ENV}
  dbHost: ${DB_HOST}
  dbUser: ${DB_USER}
  testKey: ${TEST_KEY}

&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  [참고]&lt;/b&gt; .env 파일&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;362&quot; data-origin-height=&quot;176&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/w8N8E/dJMcaaX4hsY/a0bP9WRhVFjoY2PKgvHT21/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/w8N8E/dJMcaaX4hsY/a0bP9WRhVFjoY2PKgvHT21/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/w8N8E/dJMcaaX4hsY/a0bP9WRhVFjoY2PKgvHT21/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fw8N8E%2FdJMcaaX4hsY%2Fa0bP9WRhVFjoY2PKgvHT21%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;362&quot; height=&quot;176&quot; data-origin-width=&quot;362&quot; data-origin-height=&quot;176&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;h3 data-ke-size=&quot;size23&quot;&gt;3. DotenvConfig&lt;/h3&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;style3&quot;&gt;&lt;b&gt;&amp;nbsp;  DotenvConfig&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 사용자 지정한 DotEnvCofig.java 파일 내에 함수가 실행이 됩니다.&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;1.&amp;nbsp; 정적블록(static{})을 이용하여서 클래스가 처음 로딩될 때 한 번만 실행하도록 합니다.&lt;br /&gt;2. Dotenv 객체를 로드해옵니다.DotEnv 로드된 값을 기반으로 순회하여 System.setProperty를 통해서 값을 세팅합니다.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- Spring Boot 서버가 실행이 완료가 되었을 때, 값은 application.yml 파일 내에 모두 세팅이 된 상태입니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;package com.kids.schole.config;

import io.github.cdimascio.dotenv.Dotenv;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.annotation.PostConstruct;

/**
 * DotEnv 환경설정
 *
 * @author : leejonghoon
 * @fileName : DotEnvConfig
 * @since : 25. 12. 01.
 */
@Configuration
public class DotEnvConfig {

    static {
        /*
         * DotEnv 로드 및 데이터 조회
         */
        Dotenv dotenv = Dotenv.configure()
                .ignoreIfMalformed()
                .ignoreIfMissing()
                .load();

        /*
         * DotEnv 로드된 값을 기반으로 순회하여 properties 값 세팅
         */
        dotenv.entries().forEach(entry -&amp;gt;
                System.setProperty(entry.getKey(), entry.getValue())
        );
    }
}&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;h3 data-ke-size=&quot;size23&quot;&gt;4. Controller&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  Controller&lt;/b&gt;&lt;br /&gt;&lt;b&gt;&lt;br /&gt;- @Value를 통해서 매핑된 yml 파일 내에 application.yml 파일 내의 속성을 각각 조회합니다.&lt;/b&gt;&lt;br /&gt;- 최종적으로 데이터가 조회가 됩니다.&lt;/blockquote&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;package com.adjh.springbootdotenv.controller;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.Map;

/**
 * Please explain the class!!
 *
 * @author : leejonghoon
 * @fileName : DotEnvController
 * @since : 25. 12. 1.
 */
@RestController
public class DotEnvController {

    @Value(&quot;${custom.appEnv}&quot;)
    private String appEnv;

    @Value(&quot;${custom.dbHost}&quot;)
    private String dbHost;

    @Value(&quot;${custom.dbUser}&quot;)
    private String dbUser;

    @Value(&quot;${custom.testKey}&quot;)
    private String testkey;

    @GetMapping(&quot;/&quot;)
    public String main() {
        System.out.println(&quot;Getter appEnv : &quot; + appEnv);
        System.out.println(&quot;Getter dbHost : &quot; + dbHost);
        System.out.println(&quot;Getter dbUser : &quot; + dbUser);
        System.out.println(&quot;Getter testKey : &quot; + testkey);
        return &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;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  아래와 같이 정상적으로 .env의 값을 properties를 통해서 가져왔습니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1827&quot; data-origin-height=&quot;334&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/1pqD6/dJMcagKKr8n/pt74PP8NGjtiIcuPSdV7TK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/1pqD6/dJMcagKKr8n/pt74PP8NGjtiIcuPSdV7TK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/1pqD6/dJMcagKKr8n/pt74PP8NGjtiIcuPSdV7TK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F1pqD6%2FdJMcagKKr8n%2Fpt74PP8NGjtiIcuPSdV7TK%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;1827&quot; height=&quot;334&quot; data-origin-width=&quot;1827&quot; data-origin-height=&quot;334&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4) Spring Boot Dotenv 환경설정 - 2: .env 파일을 yaml 파일 내 매핑(Properties 로드)&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  Spring Boot Dotenv 환경설정 - 2: .env 파일을 yaml 파일 내 매핑(Properties 로드)&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 해당 처리과정은 .env 파일 내에 값을 불러와서 yaml 파일 내에 ${&amp;rdquo;키&amp;rdquo;} 값 형태로 매핑을 하여, 최종적으로 yaml 파일 자체를 코드 내에서 이용하는 방식입니다.&lt;/b&gt;&lt;br /&gt;- 해당 방식을 이용하면 .env만을 사용하는 방식에 반해 yaml 파일 내에 명시된 값을 기반으로 관리가 편리해지고, 중앙화를 하여서 키 값을 관리할 수 있다는 점입니다.&lt;br /&gt;- 각각의 값을 가져오는 @ConfigurationProperties 매핑하여 yml 파일 값을 객체의 멤버 변수로 매핑하여서 조회합니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 프로세스&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  프로세스 확인&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;1. Spring Boot 서버를 실행합니다&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;2. 서버 실행 이후 @Configuration 어노테이션으로 설정 정보가 세팅이 됩니다.&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;- 사용자 지정한 DotEnvCofig.java 파일 내에 함수가 실행이 됩니다.&lt;br /&gt;2.1. 정적블록(static{})을 이용하여서 클래스가 처음 로딩될 때 한 번만 실행하도록 합니다.&lt;br /&gt;2.2. Dotenv 객체를 로드해 옵니다.&lt;br /&gt;2.3. DotEnv 로드된 값을 기반으로 순회하여 properties 값 세팅합니다.&lt;br /&gt;&lt;br /&gt;- Spring Boot 서버가 실행이 완료가 되었을 때, 값은 application.yml 파일 내에 모두 세팅이 된 상태입니다.&lt;br /&gt;&lt;b&gt;&lt;br /&gt;3. 사용자가 특정 URI로 접근을 합니다.&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;- @Value를 통해서 매핑된 yml 파일 내에 application.yml 파일 내의 속성을 각각 조회합니다.&lt;br /&gt;- 최종적으로 데이터가 조회가 됩니다.&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1070&quot; data-origin-height=&quot;777&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bAfnmC/dJMcaihvtYq/BKSFjFsjkhNsA7sFulw150/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bAfnmC/dJMcaihvtYq/BKSFjFsjkhNsA7sFulw150/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bAfnmC/dJMcaihvtYq/BKSFjFsjkhNsA7sFulw150/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbAfnmC%2FdJMcaihvtYq%2FBKSFjFsjkhNsA7sFulw150%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;1070&quot; height=&quot;777&quot; data-origin-width=&quot;1070&quot; data-origin-height=&quot;777&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;h3 data-ke-size=&quot;size23&quot;&gt;2. application.yml&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  application.yml&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- .env 키 값과 동일한 값으로 각각을 불러옵니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;pre class=&quot;dts&quot;&gt;&lt;code&gt;spring:
  application:
    name: spring-boot-dotenv
  # 해당 부분 추가
  config:
    import: optional:file:.env[.properties]

# env 파일의 key값 참조
custom:
  appEnv: ${APP_ENV}
  dbHost: ${DB_HOST}
  dbUser: ${DB_USER}
  testKey: ${TEST_KEY}

&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  [참고]&lt;/b&gt; .env 파일&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;362&quot; data-origin-height=&quot;176&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/byRDBZ/dJMcaacGSzt/R0XUNe3KS6D2Ty1BmQCY4K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/byRDBZ/dJMcaacGSzt/R0XUNe3KS6D2Ty1BmQCY4K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/byRDBZ/dJMcaacGSzt/R0XUNe3KS6D2Ty1BmQCY4K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbyRDBZ%2FdJMcaacGSzt%2FR0XUNe3KS6D2Ty1BmQCY4K%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;362&quot; height=&quot;176&quot; data-origin-width=&quot;362&quot; data-origin-height=&quot;176&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;h3 data-ke-size=&quot;size23&quot;&gt;3. CustomProperties&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  CustomProperties&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- yaml 파일의 키 값을 기반으로 매핑된 멤버 변수들을 정의합니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;package com.adjh.springbootdotenv.config.properties;

import lombok.Getter;
import lombok.Setter;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;

/**
 * Properties 파일로 정의
 *
 * @author : leejonghoon
 * @fileName : CustomProperties
 * @since : 25. 12. 2.
 */
@Getter
@Setter
@Configuration
@ConfigurationProperties(prefix = &quot;custom&quot;)
public class CustomProperties {
    private String appEnv;
    private String dbHost;
    private String dbUser;
    private String testKey;
}&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;h3 data-ke-size=&quot;size23&quot;&gt;4. Controller&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  Controller&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 사전에 구성한&amp;nbsp;CustomProperties 클래스 파일을 생성자로 만들어서 getter()를 통해서 데이터를 불러옵니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;package com.adjh.springbootdotenv.controller;

import com.adjh.springbootdotenv.config.properties.CustomProperties;
import io.github.cdimascio.dotenv.Dotenv;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.Map;

/**
 * Please explain the class!!
 *
 * @author : leejonghoon
 * @fileName : DotEnvController
 * @since : 25. 12. 1.
 */
@RestController
public class DotEnvController {

//    @Autowired
//    private CustomProperties customProperties;

    // or

    private final CustomProperties customProperties;

    public DotEnvController(CustomProperties customProperties) {
        this.customProperties = customProperties;
    }

    @GetMapping(&quot;/&quot;)
    public String main() {

        System.out.println(&quot;Getter appEnv :&quot; + customProperties.getAppEnv());
        System.out.println(&quot;Getter dbHost :&quot; + customProperties.getDbHost());
        System.out.println(&quot;Getter dbUser :&quot; + customProperties.getDbUser());
        System.out.println(&quot;Getter testKey :&quot; + customProperties.getTestKey());
        return &quot;임시 테스트 &quot;;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  아래와 같이 정상적으로 .env의 값을 properties를 통해서 가져왔습니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1827&quot; data-origin-height=&quot;334&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ThSqX/dJMcafZnabT/k2Zmqzwfw8IJVvTyBkDWy1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ThSqX/dJMcafZnabT/k2Zmqzwfw8IJVvTyBkDWy1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ThSqX/dJMcafZnabT/k2Zmqzwfw8IJVvTyBkDWy1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FThSqX%2FdJMcafZnabT%2Fk2Zmqzwfw8IJVvTyBkDWy1%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;1827&quot; height=&quot;334&quot; data-origin-width=&quot;1827&quot; data-origin-height=&quot;334&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;b&gt;오늘도&amp;nbsp;감사합니다.&amp;nbsp; &lt;/b&gt;&lt;/p&gt;</description>
      <category>Java/라이브러리 활용</category>
      <category>env 매핑</category>
      <category>env 파일 yml 매핑</category>
      <category>java .env</category>
      <category>java Dotenv</category>
      <category>java env</category>
      <category>java env 파일</category>
      <category>java env 파일 로드</category>
      <category>java env 파일 불러오기</category>
      <category>Spring boot Dotenv</category>
      <category>yaml 파일 불러오기</category>
      <author>adjh54</author>
      <guid isPermaLink="true">https://adjh54.tistory.com/727</guid>
      <comments>https://adjh54.tistory.com/727#entry727comment</comments>
      <pubDate>Tue, 2 Dec 2025 18:19:44 +0900</pubDate>
    </item>
    <item>
      <title>[Java] 자원누수 개선 방법: finally, try-with-resources 활용</title>
      <link>https://adjh54.tistory.com/725</link>
      <description>&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;해당 글에서는 SAST를 수행하는 도중에 발생한 자원누수 문제를 해결하기 위한 방법에 대해서 알아봅니다.&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;
&lt;figure contenteditable=&quot;false&quot; data-ke-type=&quot;emoticon&quot; data-ke-align=&quot;alignCenter&quot; data-emoticon-type=&quot;face&quot; data-emoticon-name=&quot;073&quot; data-emoticon-isanimation=&quot;false&quot; data-emoticon-src=&quot;https://t1.daumcdn.net/keditor/emoticon/face/large/073.png&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/keditor/emoticon/face/large/073.png&quot; width=&quot;80&quot; /&gt;&lt;/figure&gt;
&lt;/blockquote&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1) 자원누수&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  자원누수&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 자원 누수는 파일, 소켓 핸들 등 리소스를 할당한 후에 해제를 하지 않으면 GC에서 자동으로 처리를 할 수 없어서 자원이 누수가 되는 문제를 의미합니다.&lt;/b&gt;&lt;br /&gt;- 그렇기에 개발자는 리소스를 해제하는 close()를 호출하여서 할당된 리소스를 해제하여 자원누수를 막아야 합니다.&lt;/blockquote&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 136px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style2&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;자원 종류&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;대표 클래스&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;설명&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;&lt;b&gt;파일 I/O 스트림&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;&lt;b&gt;FileInputStream&lt;/b&gt;, &lt;b&gt;FileOutputStream&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;파일을 읽고 쓰기 위한 기본 byte 스트림&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;&lt;b&gt;버퍼 기반 스트림&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;&lt;b&gt;BufferedReader&lt;/b&gt;, &lt;b&gt;BufferedWriter&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;버퍼링을 적용한 고수준 문자 스트림. 성능 향상 목적&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;&lt;b&gt;네트워크 소켓&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;&lt;b&gt;Socket&lt;/b&gt;, &lt;b&gt;ServerSocket&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;TCP 네트워크 통신을 위한 소켓&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;&lt;b&gt;기본 스트림&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;&lt;b&gt;InputStream&lt;/b&gt;, &lt;b&gt;OutputStream&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;모든 스트림의 최상위 추상 클래스&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;&lt;b&gt;JDBC 커넥션&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;&lt;b&gt;Connection&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;DB 연결 객체&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;&lt;b&gt;JDBC 명령&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;&lt;b&gt;PreparedStatement&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;SQL 실행을 위한 PreparedStatement&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;&lt;b&gt;JDBC 결과 집합&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;&lt;b&gt;ResultSet&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;SQL 실행 결과를 담는 객체&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;h3 data-ke-size=&quot;size23&quot;&gt;1. 자원누수 해결 중 문제가 되는 부분&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  자원누수 해결 중 문제가 되는 부분&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- close() 자체만을 호출만 하면 문제가 발생할 수 있습니다.&lt;/b&gt; &lt;br /&gt;- 아래의 코드와 같이 FileInputStream를 이용하고, 같은 스코프 내에서 close()를 호출하는 경우 도중에 '처리 중에 예외가 발생'하면 close()까지 도달하지 못하여 자원 누수가 발생할 수 있습니다.&lt;/blockquote&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;FileInputStream fis = new FileInputStream(&quot;a.txt&quot;);
int data = fis.read();
// 처리 중 예외 발생
fis.close(); // 도달하지 못함 &amp;rarr; 자원 누수 발생
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2) 자원 누수 해결방안-1 : finally&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;&amp;nbsp;  자원 누수 해결방안-1 : finally&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 위에서 자원누수를 해결하기 위해 close()를 수행했을 때, 블록 내에서 예외가 발생하는 경우에 close()까지 도달하지 못하여서 자원이 누수되는 문제가 있었습니다.&lt;/b&gt;&lt;br /&gt;- 이에 대해서 try ~ catch ~ finally 구문을 이용하여서 예외가 발생하더라도 finally에서 처리하는 방안을 이용합니다.&lt;/blockquote&gt;
&lt;pre id=&quot;code_1763697967897&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;try {
    // 예외가 발생할 가능성이 있는 코드
} catch (예외타입 e) {
    // 예외가 발생했을 때 실행되는 코드
} finally {
    // 예외 발생 여부와 상관없이 항상 실행되는 코드
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 적용 이전 코드&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  적용 이전 코드&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 아래와 같은 함수가 있습니다. 해당 함수에서는 Socket, DataOutputStream, DataInputStream 객체를 사용하며 리소스를 할당하고 있습니다.&lt;/b&gt;&lt;br /&gt;- 그리고 블록 내에 각각에 대해서 close()를 통해서 리소스 할당 해제를 하고 있습니다. 하지만, 이 경우에 도중에 로직 내에서 문제가 생겨서 예외가 발생한다면 close()를 수행하지 못하는 상황입니다. 이 경우에 자원이 누수가 발생합니다.&lt;/blockquote&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.net.Socket;

public void sendHello(String ip, int port) throws IOException {
      Socket socket = new Socket(ip, port);
      DataOutputStream dos = new DataOutputStream(socket.getOutputStream());
      DataInputStream dis = new DataInputStream(socket.getInputStream());

      socket.setSoTimeout(3000);

      // 메시지 전송
      dos.writeUTF(&quot;HELLO&quot;);
      dos.flush();

      // 응답 읽기
      String response = dis.readUTF();
      System.out.println(&quot;서버 응답: &quot; + response);

      socket.close();
      dos.close();
      dis.close();
  }&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;h3 data-ke-size=&quot;size23&quot;&gt;2. 적용 이후 코드&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  적용 이후 코드&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 적용 이전의 코드를 기반으로 finally를 이용하여서 로직내에서 문제가 생겨서 예외가 발생하는 경우 대비하여 예외가 발생하더라도 finally 내에서 반드시 처리하고 넘어가도록 하는 방식입니다.&lt;/b&gt;&lt;br /&gt;- 또한 close() 내에서 예외처리가 발생할 수 있기에 이 또한 try ~ catch 구문을 포함합니다. 그리고 중요한 부분은 close()는 각각 참조를 하고 있기에 역순으로 항상 닫아줍니다.&lt;/blockquote&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;

public void sendHello(String ip, int port) throws IOException {
    Socket socket = null;
    DataOutputStream dos = null;
    DataInputStream dis = null;

    try {
        socket = new Socket(ip, port);
        dos = new DataOutputStream(socket.getOutputStream());
        dis = new DataInputStream(socket.getInputStream());
        socket.setSoTimeout(3000);

        // 메시지 전송
        dos.writeUTF(&quot;HELLO&quot;);
        dos.flush();

        // 응답 읽기
        String response = dis.readUTF();
        System.out.println(&quot;서버 응답: &quot; + response);

    } catch (IOException e) {
        System.err.println(&quot;통신 중 오류 발생: &quot; + e.getMessage());
        throw e; 
    } finally {
        // 역순으로 닫기
        if (dis != null) {
            try {
                dis.close();
            } catch (IOException e) {
                System.err.println(&quot;DataInputStream close error: &quot; + e.getMessage());
            }
        }

        if (dos != null) {
            try {
                dos.close();
            } catch (IOException e) {
                System.err.println(&quot;DataOutputStream close error: &quot; + e.getMessage());
            }
        }

        if (socket != null &amp;amp;&amp;amp; !socket.isClosed()) {
            try {
                socket.close();
            } catch (IOException e) {
                System.err.println(&quot;Socket close error: &quot; + e.getMessage());
            }
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3) 자원 누수 해결방안-2: try-with-resources&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  자원 누수 해결방안-2: try-with-resources&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- Java 7부터 도입된 문법으로 try(&amp;hellip;) { } 안에 AutoCloseable 혹은 Closeable 인터페이스를 구현한 객체를 선언하면 try 블록이 끝나는 순간 자동으로 close()가 호출이 되는 형태입니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;pre id=&quot;code_1763697814225&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;try (자원 생성) {
    // 자원을 사용하는 코드
} catch (예외) {
    // 예외 처리
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 적용 이전 코드&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  적용 이전 코드&lt;/b&gt;&lt;br /&gt;- 함수 내에 블록 내에 각각에 대해서 close()를 통해서 리소스 할당 해제를 하고 있습니다. 이 경우에 도중에 로직 내에서 문제가 생겨서 예외가 발생한다면 close()를 수행하지 못하는 상황입니다. 이 경우에 자원이 누수가 발생합니다.&lt;/blockquote&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.net.Socket;

public void sendHello(String ip, int port) throws IOException {
      Socket socket = new Socket(ip, port);
      DataOutputStream dos = new DataOutputStream(socket.getOutputStream());
      DataInputStream dis = new DataInputStream(socket.getInputStream());

      socket.setSoTimeout(3000);

      // 메시지 전송
      dos.writeUTF(&quot;HELLO&quot;);
      dos.flush();

      // 응답 읽기
      String response = dis.readUTF();
      System.out.println(&quot;서버 응답: &quot; + response);

      socket.close();
      dos.close();
      dis.close();
  }&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;h3 data-ke-size=&quot;size23&quot;&gt;2. 적용 이후 코드&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  적용 이후 코드&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;- 이전 finally를 이용한 방식을 통해서도 처리가 가능하지만 소스코드가 길어지는 문제가 있고 가독성이 떨어지는 문제가 있었습니다. &lt;br /&gt;- 그렇기에 try-with-resources를 통해서 선언과 동시에 close()를 이용하는 방식을 이용합니다&lt;br /&gt;- 아래와 같이 try 구문내에서 선언하여서 블록 종료 시 close()가 수행이 되도록 하는 방식을 이용합니다.&lt;/blockquote&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;

public void sendHello(String ip, int port) throws IOException {

    try (
        Socket socket = new Socket(ip, port);
        DataOutputStream dos = new DataOutputStream(socket.getOutputStream());
        DataInputStream dis = new DataInputStream(socket.getInputStream());
    ) {

        socket.setSoTimeout(3000);

        // 메시지 전송
        dos.writeUTF(&quot;HELLO&quot;);
        dos.flush();

        // 응답 읽기
        String response = dis.readUTF();
        System.out.println(&quot;서버 응답: &quot; + response);
    }
    // 여기서 socket, dos, dis 자동으로 close됨
}&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;&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;오늘도&amp;nbsp;감사합니다.&amp;nbsp; &lt;/b&gt;&lt;/p&gt;</description>
      <category>Java/이론 및 문법</category>
      <category>java</category>
      <category>java finally</category>
      <category>java sast</category>
      <category>java try catch</category>
      <category>java try-with-resources</category>
      <category>java 자원누수</category>
      <category>java 자원누수 예시</category>
      <category>try-with-resources</category>
      <author>adjh54</author>
      <guid isPermaLink="true">https://adjh54.tistory.com/725</guid>
      <comments>https://adjh54.tistory.com/725#entry725comment</comments>
      <pubDate>Fri, 21 Nov 2025 13:07:06 +0900</pubDate>
    </item>
    <item>
      <title>[Git] Github 레포지토리 변경(이관) 방법 : 동일 계정간/다른 계정간 이관</title>
      <link>https://adjh54.tistory.com/724</link>
      <description>&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
 &lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;해당 글에서는 Github 환경에서 레포지토리의 변경(이관) 방법으로 하나의 계정 내에서 이관하는 방법과 다른 계정으로 이관하는 방법에 대해 알아봅니다&lt;br&gt;&lt;br&gt;&lt;/span&gt; 
 &lt;figure contenteditable=&quot;false&quot; data-ke-type=&quot;emoticon&quot; data-ke-align=&quot;alignCenter&quot; data-emoticon-type=&quot;face&quot; data-emoticon-name=&quot;073&quot; data-emoticon-isanimation=&quot;false&quot; data-emoticon-src=&quot;https://t1.daumcdn.net/keditor/emoticon/face/large/073.png&quot;&gt;
  &lt;img src=&quot;https://t1.daumcdn.net/keditor/emoticon/face/large/073.png&quot; width=&quot;80&quot;&gt;
 &lt;/figure&gt; 
 &lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;/p&gt; 
&lt;/blockquote&gt;&lt;h2 data-ke-size=&quot;size26&quot;&gt;1) Git 레포지토리 변경(이관) 방법 : 동일한 계정&lt;/h2&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot;&gt;&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  Git 레포지토리 변경(이관) 방법 : 동일한 계정&lt;/b&gt;&lt;br&gt;&lt;br&gt;- 이를 위해서 git의 Mirroring a repository을 이용하려고 합니다.&lt;/blockquote&gt;&lt;figure data-ke-type=&quot;opengraph&quot; data-og-title=&quot;Duplicating a repository - GitHub Docs&quot; data-ke-align=&quot;alignCenter&quot; data-og-description=&quot;To maintain a mirror of a repository without forking it, you can run a special clone command, then mirror-push to the new repository.&quot; data-og-host=&quot;docs.github.com&quot; data-og-source-url=&quot;https://docs.github.com/en/repositories/creating-and-managing-repositories/duplicating-a-repository&quot; data-og-image=&quot;https://blog.kakaocdn.net/dna/OPp2p/hyZOepsFgf/AAAAAAAAAAAAAAAAAAAAAF2ipMCXxyfgKW56pJ0GXrQR6AnJ3vo1WDGQUgYNzs-p/img.png?credential=yqXZFxpELC7KVnFOS48ylbz2pIh7yKj8&amp;amp;expires=1764514799&amp;amp;allow_ip=&amp;amp;allow_referer=&amp;amp;signature=kgF57xSTrsYYpdpqRvp2I8YOyv4%3D&quot; data-og-url=&quot;https://docs-internal.github.com/en/repositories/creating-and-managing-repositories/duplicating-a-repository&quot;&gt;&lt;a href=&quot;https://docs-internal.github.com/en/repositories/creating-and-managing-repositories/duplicating-a-repository&quot; target=&quot;_blank&quot; data-source-url=&quot;https://docs.github.com/en/repositories/creating-and-managing-repositories/duplicating-a-repository&quot;&gt;&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://blog.kakaocdn.net/dna/OPp2p/hyZOepsFgf/AAAAAAAAAAAAAAAAAAAAAF2ipMCXxyfgKW56pJ0GXrQR6AnJ3vo1WDGQUgYNzs-p/img.png?credential=yqXZFxpELC7KVnFOS48ylbz2pIh7yKj8&amp;amp;expires=1764514799&amp;amp;allow_ip=&amp;amp;allow_referer=&amp;amp;signature=kgF57xSTrsYYpdpqRvp2I8YOyv4%3D')&quot;&gt; &lt;/div&gt;&lt;div class=&quot;og-text&quot;&gt;&lt;p class=&quot;og-title&quot;&gt;Duplicating a repository - GitHub Docs&lt;/p&gt;&lt;p class=&quot;og-desc&quot;&gt;To maintain a mirror of a repository without forking it, you can run a special clone command, then mirror-push to the new repository.&lt;/p&gt;&lt;p class=&quot;og-host&quot;&gt;docs.github.com&lt;/p&gt;&lt;/div&gt;&lt;/a&gt;&lt;/figure&gt;&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-style=&quot;style2&quot; data-ke-align=&quot;alignLeft&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;b&gt;분류&lt;/b&gt;&lt;/td&gt;&lt;td&gt;&lt;b&gt;약칭&lt;/b&gt;&lt;/td&gt;&lt;td&gt;&lt;b&gt;레포지토리&amp;nbsp; 명&lt;/b&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;이관하려는 레포지토리&lt;/td&gt;&lt;td&gt;old-repo&lt;/td&gt;&lt;td&gt;spring-boot-3-vault&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;이관받는 레포지토리&lt;/td&gt;&lt;td&gt;new-repo&lt;/td&gt;&lt;td&gt;temp-spring-boot-3-vault&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. Github 내에 이관받을 레포지토리를 생성합니다.&lt;/h3&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot;&gt;&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt; Github내에 이관받을 레포지토리를 생성합니다.&lt;/b&gt;&lt;br&gt;&lt;br&gt;- 아래와 같이 Repository를 새로 생성하였습니다.&lt;/blockquote&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1494&quot; data-origin-height=&quot;732&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/3r2Rn/dJMcafydFaL/vgK0CkGK6kD5xAHh3Xok10/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/3r2Rn/dJMcafydFaL/vgK0CkGK6kD5xAHh3Xok10/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/3r2Rn/dJMcafydFaL/vgK0CkGK6kD5xAHh3Xok10/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F3r2Rn%2FdJMcafydFaL%2FvgK0CkGK6kD5xAHh3Xok10%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;1494&quot; height=&quot;732&quot; data-origin-width=&quot;1494&quot; data-origin-height=&quot;732&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. 이관되는 위치를 지정합니다&lt;/h3&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot;&gt;&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  이관되는 위치를 지정합니다&lt;/b&gt;&lt;br&gt;&lt;br&gt;&lt;b&gt;- 이관되는 레포지토리의 경로를 지정합니다.&lt;/b&gt;&lt;b&gt;&lt;br&gt;&lt;/b&gt;&lt;b&gt;- new-repo의 경우는 실제 프로젝트 루트의 디렉터리는 아니고 임의로 생성한 디렉터리입니다.&lt;/b&gt;&lt;/blockquote&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;956&quot; data-origin-height=&quot;638&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/smPKR/dJMcabbv507/akiKkMBla5UEM6nWsd0Z51/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/smPKR/dJMcabbv507/akiKkMBla5UEM6nWsd0Z51/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/smPKR/dJMcabbv507/akiKkMBla5UEM6nWsd0Z51/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FsmPKR%2FdJMcabbv507%2FakiKkMBla5UEM6nWsd0Z51%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;956&quot; height=&quot;638&quot; data-origin-width=&quot;956&quot; data-origin-height=&quot;638&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. 해당 디렉터리에 접근하고 clone --mirror를 수행합니다&lt;/h3&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot;&gt;&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  해당 디렉터리에 접근하고 clone --mirror를 수행합니다.&lt;/b&gt;&lt;br&gt;&lt;br&gt;- 아래의 명령어를 수행합니다.&lt;/blockquote&gt;&lt;pre class=&quot;shell&quot; data-ke-language=&quot;shell&quot;&gt;&lt;code&gt;# [예시] 이관하려는 디렉터리의 git 주소를 붙여넣기 합니다
$ git clone --mirror 

# 이관하려는 디렉터리의 git 주소를 붙여넣기 합니다
$ git clone --mirror https://github.com/adjh54ir/spring-boot-3-vault.git&lt;/code&gt;&lt;/pre&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;947&quot; data-origin-height=&quot;329&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/4m2bl/dJMcabihKwD/Zsn7EyyKO0QeYtXj5qOG31/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/4m2bl/dJMcabihKwD/Zsn7EyyKO0QeYtXj5qOG31/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/4m2bl/dJMcabihKwD/Zsn7EyyKO0QeYtXj5qOG31/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F4m2bl%2FdJMcabihKwD%2FZsn7EyyKO0QeYtXj5qOG31%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;947&quot; height=&quot;329&quot; data-origin-width=&quot;947&quot; data-origin-height=&quot;329&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  위에 명령어를 수행하면 실제 디렉터리 내에 아래와 같은 파일들이 생겼습니다.&lt;/b&gt;&lt;/blockquote&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;769&quot; data-origin-height=&quot;431&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ln3nQ/dJMcaajnuq4/WfhiTFshvPguPLekwp51f0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ln3nQ/dJMcaajnuq4/WfhiTFshvPguPLekwp51f0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ln3nQ/dJMcaajnuq4/WfhiTFshvPguPLekwp51f0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fln3nQ%2FdJMcaajnuq4%2FWfhiTFshvPguPLekwp51f0%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;769&quot; height=&quot;431&quot; data-origin-width=&quot;769&quot; data-origin-height=&quot;431&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;h3 data-ke-size=&quot;size23&quot;&gt;4. 새로운 레포지토리의 push --mirror를 수행합니다&lt;/h3&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot;&gt;&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  새로운 레포지토리의 mirror push를 수행합니다.&lt;/b&gt;&lt;b&gt;&lt;br&gt;&lt;/b&gt;&lt;b&gt;&lt;br&gt;&lt;/b&gt;&lt;b&gt;- git clone --mirror를 통해 복제된 git에 접근하여, git push --mirror를 수행하여서 기존에 clone 된 repo를 새로 생성된 repo로 이관이 수행이 됩니다.&lt;/b&gt;&lt;/blockquote&gt;&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;# [예시] 위에 clone --mirror로 복제된 git을 접근합니다.
$ cd xxx.git

# 위에 clone --mirror로 복제된 git을 접근합니다.
$ cd spring-boot-3-vault.git

# [예시] 이관하려는 받으려는 git 주소를 붙여넣기 합니다
$ git push --mirror 

# 이관하려는 디렉터리의 git 주소를 붙여넣기 합니다
$ git push --mirror https://github.com/adjh54ir/temp-spring-boot-3-vault.git&lt;/code&gt;&lt;/pre&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1166&quot; data-origin-height=&quot;316&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/vd5IY/dJMcahQk39g/mXWFxdQjbEX76lv0GrUGmK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/vd5IY/dJMcahQk39g/mXWFxdQjbEX76lv0GrUGmK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/vd5IY/dJMcahQk39g/mXWFxdQjbEX76lv0GrUGmK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fvd5IY%2FdJMcahQk39g%2FmXWFxdQjbEX76lv0GrUGmK%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;316&quot; data-origin-width=&quot;1166&quot; data-origin-height=&quot;316&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;h3 data-ke-size=&quot;size23&quot;&gt;5. Github내에 새로운 Repository를 확인하면 정상적으로 이관이 된 것을 확인하였습니다.&lt;/h3&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot;&gt;&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-style=&quot;style2&quot; data-ke-align=&quot;alignLeft&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;b&gt;분류&lt;/b&gt;&lt;/td&gt;&lt;td&gt;&lt;b&gt;약칭&lt;/b&gt;&lt;/td&gt;&lt;td&gt;&lt;b&gt;레포지토리&amp;nbsp; 명&lt;/b&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;b&gt;이관하려는 레포지토리&lt;/b&gt;&lt;/td&gt;&lt;td&gt;old-repo&lt;/td&gt;&lt;td&gt;spring-boot-3-vault&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;b&gt;이관받는 레포지토리&lt;/b&gt;&lt;/td&gt;&lt;td&gt;new-repo&lt;/td&gt;&lt;td&gt;temp-spring-boot-3-vault&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1900&quot; data-origin-height=&quot;889&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/3HJM2/dJMcahW6HEy/HAZX5YtpPTond6cJP9kKg1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/3HJM2/dJMcahW6HEy/HAZX5YtpPTond6cJP9kKg1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/3HJM2/dJMcahW6HEy/HAZX5YtpPTond6cJP9kKg1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F3HJM2%2FdJMcahW6HEy%2FHAZX5YtpPTond6cJP9kKg1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1900&quot; height=&quot;889&quot; data-origin-width=&quot;1900&quot; data-origin-height=&quot;889&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;h2 data-ke-size=&quot;size26&quot;&gt;2) Git 레포지토리 변경(이관) 방법 : 다른 계정&lt;/h2&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot;&gt;&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  Git 레포지토리 변경(이관) 방법 : 다른 계정&lt;/b&gt;&lt;br&gt;&lt;br&gt;&lt;b&gt;- 기존에 A 계정 내에 있던 레포지토리를 B 계정으로 변경(이관)을 할 일이 생기게 되었습니다.&lt;/b&gt;&lt;br&gt;- 요구사항으로는 A 계정내 남아있던 ‘Git 로그’나 ‘커밋 메시지’, ‘브랜치’ 등을 A에서 사용하는 그대로 B 레포지토리의 히스토리를 유지한 채 이관을 하려는 목적을 가지고 있습니다.&lt;/blockquote&gt;&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-style=&quot;style2&quot; data-ke-align=&quot;alignLeft&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td style=&quot;width: 36.9768%;&quot;&gt;&lt;b&gt;분류&lt;/b&gt;&lt;/td&gt;&lt;td style=&quot;width: 62.9069%;&quot;&gt;&lt;b&gt;약칭&lt;/b&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style=&quot;width: 36.9768%;&quot;&gt;이관하려는 사용자&lt;/td&gt;&lt;td style=&quot;width: 62.9069%;&quot;&gt;adjh54ir&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style=&quot;width: 36.9768%;&quot;&gt;이관받는 사용자&lt;/td&gt;&lt;td style=&quot;width: 62.9069%;&quot;&gt;adjh54irir&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;h3 data-ke-size=&quot;size23&quot;&gt;1. 이관하려는 레포지토리에 접근합니다.&lt;/h3&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1493&quot; data-origin-height=&quot;734&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bPAQRq/dJMcahbJxcD/WK3DWLW1qhxvrWAhNygfJ1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bPAQRq/dJMcahbJxcD/WK3DWLW1qhxvrWAhNygfJ1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bPAQRq/dJMcahbJxcD/WK3DWLW1qhxvrWAhNygfJ1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbPAQRq%2FdJMcahbJxcD%2FWK3DWLW1qhxvrWAhNygfJ1%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;1493&quot; height=&quot;734&quot; data-origin-width=&quot;1493&quot; data-origin-height=&quot;734&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. Setting 탭 &amp;gt; Transfer ownership &amp;gt; Transfer을 선택합니다.&lt;/h3&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1491&quot; data-origin-height=&quot;735&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bdW0WN/dJMcahv2ddv/K4BKtq19IpOxJmnk2CLFrk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bdW0WN/dJMcahv2ddv/K4BKtq19IpOxJmnk2CLFrk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bdW0WN/dJMcahv2ddv/K4BKtq19IpOxJmnk2CLFrk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbdW0WN%2FdJMcahv2ddv%2FK4BKtq19IpOxJmnk2CLFrk%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;1491&quot; height=&quot;735&quot; data-origin-width=&quot;1491&quot; data-origin-height=&quot;735&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. 새로운 레포지토리의 Owner를 선택합니다.&lt;/h3&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot;&gt;&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt; 새로운 레포지토리의 Owner를 선택합니다&lt;/b&gt; &lt;br&gt;&lt;b&gt;&lt;br&gt;&lt;/b&gt;&lt;b&gt;- 해당 방식에서는 ‘Specify an organization or username’를 통해서 다른 계정으로 레포지토리의 계정으로 이관하려고 합니다. &lt;/b&gt;&lt;br&gt;&lt;b&gt;&lt;br&gt;&lt;/b&gt;&lt;b&gt;1. Select one of my organizations&lt;/b&gt;&lt;br&gt;- 해당 경우는 ‘내 조직 중 하나’를 선택할 수 있습니다. 이를 위해 조직 내에 포함되어야 합니다.&lt;br&gt;&lt;br&gt;&lt;b&gt;2. Specify an organization or username&lt;/b&gt;&lt;br&gt;- 해당 경우는 내 조직 또는 Github username을 기반으로 선택할 수 있습니다.&lt;/blockquote&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1494&quot; data-origin-height=&quot;712&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/T6l3r/dJMcah3SeYB/H7nZ43NIFnYqHJ8VWD77vK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/T6l3r/dJMcah3SeYB/H7nZ43NIFnYqHJ8VWD77vK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/T6l3r/dJMcah3SeYB/H7nZ43NIFnYqHJ8VWD77vK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FT6l3r%2FdJMcah3SeYB%2FH7nZ43NIFnYqHJ8VWD77vK%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;1494&quot; height=&quot;712&quot; data-origin-width=&quot;1494&quot; data-origin-height=&quot;712&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;h3 data-ke-size=&quot;size23&quot;&gt;4. 계정을 입력하고, 확인을 입력한 뒤 ‘I understand, transfer this repository.’를 선택합니다.&lt;/h3&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1489&quot; data-origin-height=&quot;723&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cpSuOw/dJMcah3SeZI/SC6s3KWYsNst8Z2T7sKyzK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cpSuOw/dJMcah3SeZI/SC6s3KWYsNst8Z2T7sKyzK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cpSuOw/dJMcah3SeZI/SC6s3KWYsNst8Z2T7sKyzK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcpSuOw%2FdJMcah3SeZI%2FSC6s3KWYsNst8Z2T7sKyzK%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;1489&quot; height=&quot;723&quot; data-origin-width=&quot;1489&quot; data-origin-height=&quot;723&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;h3 data-ke-size=&quot;size23&quot;&gt;5. 이관을 전달받은 사용자의 메일로 이동하고, 링크를 누릅니다.&lt;/h3&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot;&gt;&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  이관을 전달받은 사용자의 메일로 이동하고, 링크를 누릅니다.&lt;/b&gt;&lt;br&gt;&lt;br&gt;아래와 같은 메일이 도착했습니다.&lt;br&gt;&lt;br&gt;안녕하세요 adjh54irir 님,&lt;br&gt;@adjh54ir은 adjh54ir/spring-boot-3-vault 저장소를 adjh54irir/spring-boot-3-vault로 전송하려고 합니다.&lt;br&gt;&lt;br&gt;이 저장소 이전을 수락하면 다음에 대한 액세스 권한을 잃게 됩니다.&lt;br&gt;- 코드 소유자 기능.&lt;br&gt;- 기존 위키.&lt;br&gt;- 인사이트 페이지의 펄스, 기여자, 커뮤니티, 트래픽, 커밋, 코드 빈도 및 네트워크.&lt;br&gt;- 초안 PR.&lt;br&gt;- 문제 및 PR에 대한 여러 담당자.&lt;br&gt;- 다수의 PR 리뷰어.&lt;br&gt;- 분기 및 태그 보호 규칙.&lt;br&gt;- 기존 페이지.&lt;br&gt;액세스 권한을 잃지 않으려면 이 저장소의 전송을 수락하기 전에 계획을 업그레이드할 수 있습니다.&lt;br&gt;이전을 수락하려면 다음 링크를 방문하세요.&lt;br&gt;이전을 수락하지 않으면 약 하루 후에 만료됩니다.&lt;br&gt;질문? 문제가 있나요? 언제든지&amp;nbsp;https://support.github.com을&amp;nbsp;방문해 주세요.&lt;br&gt;감사해요, GitHub의 친구들&lt;/blockquote&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;874&quot; data-origin-height=&quot;583&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/brgy5W/dJMcaf53ES8/DYAOhSwDjV0dmPei4pYxNk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/brgy5W/dJMcaf53ES8/DYAOhSwDjV0dmPei4pYxNk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/brgy5W/dJMcaf53ES8/DYAOhSwDjV0dmPei4pYxNk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbrgy5W%2FdJMcaf53ES8%2FDYAOhSwDjV0dmPei4pYxNk%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;874&quot; height=&quot;583&quot; data-origin-width=&quot;874&quot; data-origin-height=&quot;583&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;h3 data-ke-size=&quot;size23&quot;&gt;7. Repository를 확인하면 아래와 같이 정상적으로 새로운 계정에 이관을 확인하였습니다&lt;/h3&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot;&gt;&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-style=&quot;style2&quot; data-ke-align=&quot;alignLeft&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td style=&quot;width: 36.9768%;&quot;&gt;&lt;b&gt;분류&lt;/b&gt;&lt;/td&gt;&lt;td style=&quot;width: 62.9069%;&quot;&gt;&lt;b&gt;약칭&lt;/b&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style=&quot;width: 36.9768%;&quot;&gt;이관하려는 사용자&lt;/td&gt;&lt;td style=&quot;width: 62.9069%;&quot;&gt;adjh54ir&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style=&quot;width: 36.9768%;&quot;&gt;이관받는 사용자&lt;/td&gt;&lt;td style=&quot;width: 62.9069%;&quot;&gt;adjh54irir&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1506&quot; data-origin-height=&quot;720&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/WhtZR/dJMb99Y4xGC/birYKo8Jnt8hgwPjgJZJ91/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/WhtZR/dJMb99Y4xGC/birYKo8Jnt8hgwPjgJZJ91/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/WhtZR/dJMb99Y4xGC/birYKo8Jnt8hgwPjgJZJ91/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FWhtZR%2FdJMb99Y4xGC%2FbirYKo8Jnt8hgwPjgJZJ91%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;1506&quot; height=&quot;720&quot; data-origin-width=&quot;1506&quot; data-origin-height=&quot;720&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&lt;b&gt;오늘도&amp;nbsp;감사합니다.&amp;nbsp; &lt;/b&gt;&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Github/활용하기</category>
      <category>github 다른 계정으로 변경</category>
      <category>github 레포지토리 변경</category>
      <category>github 레포지토리 이관</category>
      <category>github 레포지토리 이동</category>
      <category>github 소스 이관</category>
      <category>github 소유 변경</category>
      <category>github 소유자 변경</category>
      <category>github 저장소 다른 계정</category>
      <category>github 저장소 이관</category>
      <category>github 저장소 이동</category>
      <author>adjh54</author>
      <guid isPermaLink="true">https://adjh54.tistory.com/724</guid>
      <comments>https://adjh54.tistory.com/724#entry724comment</comments>
      <pubDate>Tue, 18 Nov 2025 16:12:15 +0900</pubDate>
    </item>
    <item>
      <title>[RN/오류노트] Solved - Node found at: /opt/homebrew/Cellar/node@xxx</title>
      <link>https://adjh54.tistory.com/723</link>
      <description>&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;해당 글에서는 XCode를 실행하는 중에 발생하는 빌드 문제에 대해 해결방법에 대해 알아봅니다.&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;
&lt;figure contenteditable=&quot;false&quot; data-ke-type=&quot;emoticon&quot; data-ke-align=&quot;alignCenter&quot; data-emoticon-type=&quot;face&quot; data-emoticon-name=&quot;073&quot; data-emoticon-isanimation=&quot;false&quot; data-emoticon-src=&quot;https://t1.daumcdn.net/keditor/emoticon/face/large/073.png&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/keditor/emoticon/face/large/073.png&quot; width=&quot;80&quot; /&gt;&lt;/figure&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1) 문제점&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  문제점&lt;/b&gt;&lt;br /&gt;&lt;b&gt;&lt;br /&gt;- XCode를 빌드하는 과정에서 아래와 같은 오류가 발생하였습니다.&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;Node found at: /opt/homebrew/Cellar/node@22/22.14.0/bin/node /Users/leejonghoon/Library/Developer/Xcode/DerivedData/EmotionButton-fsuawxxfittqjdhewknsvhbklaiz/Build/Intermediates.noindex/Pods.build/Debug-iphoneos/hermes-engine.build/Script-46EB2E0002F110.sh: line 9: /opt/homebrew/Cellar/node@22/22.14.0/bin/node: No such file or directory Command PhaseScriptExecution failed with a nonzero exit code&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 해당 문제의 경우는 XCode내에서 Node@22 경로를 찾지 못하는 오류입니다. 이 경우에는 homebrew를 업데이트 한 이후에 발생하였습니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;850&quot; data-origin-height=&quot;637&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/sqAEp/dJMcafrrqW7/s5tygjfRTI5lAEEDZVs7O0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/sqAEp/dJMcafrrqW7/s5tygjfRTI5lAEEDZVs7O0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/sqAEp/dJMcafrrqW7/s5tygjfRTI5lAEEDZVs7O0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FsqAEp%2FdJMcafrrqW7%2Fs5tygjfRTI5lAEEDZVs7O0%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;850&quot; height=&quot;637&quot; data-origin-width=&quot;850&quot; data-origin-height=&quot;637&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2) 해결방법&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  해결방법&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 현재 버전이 모두 안 맞는 문제가 발생하고 있기에 동일한 node 버전으로 모두 통일을 합니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. Node의 실제 경로 확인&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  Node의 실제 경로 확인&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 아래의 명령어를 입력해보면 두개의 경로가 충돌됨을 확인할 수 있습니다.&lt;/b&gt;&lt;br /&gt;- 첫번쨰 터미널에서 사용하는 node의 경우는 .nvm을 바라보고 있습니다. 그리고 xcode내에서는 homebrew의 node 버전을 보고 있는 문제입니다. 그렇기에 최종적으로 node@22버전으로 일괄 맞춥니다.&lt;/blockquote&gt;
&lt;pre class=&quot;crmsh&quot;&gt;&lt;code&gt;# 터미널에서 사용하는 Node
which node

# Xcode가 사용하는 Node
ls -l /opt/homebrew/bin/node
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style2&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;분류&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;버전&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Xcode에서 바라보고 있는 node 버전&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;22.21.1 버전&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;nvm의 node 버전&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;20.19.2 버전&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;homebrew의 node 버전&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;25.0.1 버전&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;918&quot; data-origin-height=&quot;382&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qVxtn/dJMcagDTcKt/NOHioMMzSnaUY6EfFE3fY0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qVxtn/dJMcagDTcKt/NOHioMMzSnaUY6EfFE3fY0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qVxtn/dJMcagDTcKt/NOHioMMzSnaUY6EfFE3fY0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FqVxtn%2FdJMcagDTcKt%2FNOHioMMzSnaUY6EfFE3fY0%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;918&quot; height=&quot;382&quot; data-origin-width=&quot;918&quot; data-origin-height=&quot;382&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;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  아래와 같이 XCode에서 실행된 버전과 brew node 버전이 불일치함을 확인할 수 있습니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;pre class=&quot;crmsh&quot;&gt;&lt;code&gt;// Xcode에서 실행된 버전
/opt/homebrew/Cellar/node@22/.../bin/node

// brew NODE 버전
/opt/homebrew/Cellar/node/25.1.0_1/bin/node
&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;h3 data-ke-size=&quot;size23&quot;&gt;2. nvm을 이용하지 않고 homebrew에서 관리하는 해결방법&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  nvm을 이용하지 않고 homebrew에서 관리하는 해결방법&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- ZSehll에서 nvm의 우선순위를 먼저주지 않고 nvm을 삭제하고 homebrew 버전으로 맞추는 방법으로 수행합니다&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2.1. ZSell에서 nvm 우선순위를 주석처리합니다&lt;/h4&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;style3&quot;&gt;&lt;b&gt;  ZSell에서 nvm 우선순위를 주석처리합니다&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 아래와 같이 ZSell에 접속하고 아래와 같이 주석을 수행한뒤 반영을 합니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;pre class=&quot;jboss-cli&quot;&gt;&lt;code&gt;vi ~/.zshrc

source ~/.zshrc
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;905&quot; data-origin-height=&quot;549&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/PfuDh/dJMcacVM40K/jklieO5PSu42SKQmnHTFlK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/PfuDh/dJMcacVM40K/jklieO5PSu42SKQmnHTFlK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/PfuDh/dJMcacVM40K/jklieO5PSu42SKQmnHTFlK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FPfuDh%2FdJMcacVM40K%2FjklieO5PSu42SKQmnHTFlK%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;905&quot; height=&quot;549&quot; data-origin-width=&quot;905&quot; data-origin-height=&quot;549&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;h4 data-ke-size=&quot;size20&quot;&gt;2.2. nvm 자체를 삭제합니다&lt;/h4&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;style3&quot;&gt;&lt;b&gt;  nvm 자체를 삭제합니다&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- brew로 관리를 위해서 nvm을 삭제하였습니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;pre class=&quot;jboss-cli&quot;&gt;&lt;code&gt;rm -rf ~/.nvm
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;910&quot; data-origin-height=&quot;599&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bcrbEI/dJMcagKEEvc/u2IGmMSgh33tF5boDH9so1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bcrbEI/dJMcagKEEvc/u2IGmMSgh33tF5boDH9so1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bcrbEI/dJMcagKEEvc/u2IGmMSgh33tF5boDH9so1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbcrbEI%2FdJMcagKEEvc%2Fu2IGmMSgh33tF5boDH9so1%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;910&quot; height=&quot;599&quot; data-origin-width=&quot;910&quot; data-origin-height=&quot;599&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;h4 data-ke-size=&quot;size20&quot;&gt;2.3. brew node만 사용하도록 설정합니다&lt;/h4&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;style3&quot;&gt;&lt;b&gt;  brew node만 사용하도록 설정합니다&lt;/b&gt;&lt;br /&gt;- 아래와 같이 기존의 node 버전을 삭제했고, node@22 버전을 맞추었습니다.그리고 link로 node22를 연결하였습니다.&lt;/blockquote&gt;
&lt;pre class=&quot;shell&quot; data-ke-language=&quot;shell&quot;&gt;&lt;code&gt;$ brew uninstall node
$ brew install node@22
$ brew link --overwrite --force node@22&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;724&quot; data-origin-height=&quot;414&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ttP1t/dJMcagKEEvG/dohkEkqxGDYt7ok4RRo640/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ttP1t/dJMcagKEEvG/dohkEkqxGDYt7ok4RRo640/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ttP1t/dJMcagKEEvG/dohkEkqxGDYt7ok4RRo640/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FttP1t%2FdJMcagKEEvG%2FdohkEkqxGDYt7ok4RRo640%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;724&quot; height=&quot;414&quot; data-origin-width=&quot;724&quot; data-origin-height=&quot;414&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;h4 data-ke-size=&quot;size20&quot;&gt;3. node 버전 확인&lt;/h4&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;crmsh&quot;&gt;&lt;code&gt;# 터미널에서 사용하는 Node
which node

# Xcode가 사용하는 Node
ls -l /opt/homebrew/bin/node
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;915&quot; data-origin-height=&quot;699&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/r0jhe/dJMcagcONDa/zEvAOV7Ua5ZIcHyCCMm310/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/r0jhe/dJMcagcONDa/zEvAOV7Ua5ZIcHyCCMm310/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/r0jhe/dJMcagcONDa/zEvAOV7Ua5ZIcHyCCMm310/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fr0jhe%2FdJMcagcONDa%2FzEvAOV7Ua5ZIcHyCCMm310%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;915&quot; height=&quot;699&quot; data-origin-width=&quot;915&quot; data-origin-height=&quot;699&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;h3 data-ke-size=&quot;size23&quot;&gt;4. .xcode.env.local 파일 변경&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;# 기존
export NODE_BINARY=&quot;/opt/homebrew/Cellar/node@22/v20.19.2/bin/node&quot;

# 변경
export NODE_BINARY=&quot;/opt/homebrew/opt/node@22/bin/node&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5. pod 재 설정&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  pod 재 설정&lt;/b&gt;&lt;/blockquote&gt;
&lt;pre class=&quot;shell&quot; data-ke-language=&quot;shell&quot;&gt;&lt;code&gt;# DerivedData 삭제
$ rm -rf ~/Library/Developer/Xcode/DerivedData/

# 다시 pod install 
$ cd ios

# pod 초기화 
$ pod deintegrate

# pod 재설치
$ pod install&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1460&quot; data-origin-height=&quot;367&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bs8mk8/dJMcahQkDBW/WFOylH14LDPBktR8UFSHu0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bs8mk8/dJMcahQkDBW/WFOylH14LDPBktR8UFSHu0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bs8mk8/dJMcahQkDBW/WFOylH14LDPBktR8UFSHu0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbs8mk8%2FdJMcahQkDBW%2FWFOylH14LDPBktR8UFSHu0%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;1460&quot; height=&quot;367&quot; data-origin-width=&quot;1460&quot; data-origin-height=&quot;367&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;h3 data-ke-size=&quot;size23&quot;&gt;6. Xcode 실행&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  Xcode 실행&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 정상적으로 실행됨을 확인하였습니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1155&quot; data-origin-height=&quot;713&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/sQVe3/dJMcabWSLOv/d5g9f2lP2LeDZDTo0VlGi1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/sQVe3/dJMcabWSLOv/d5g9f2lP2LeDZDTo0VlGi1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/sQVe3/dJMcabWSLOv/d5g9f2lP2LeDZDTo0VlGi1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FsQVe3%2FdJMcabWSLOv%2Fd5g9f2lP2LeDZDTo0VlGi1%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;1155&quot; height=&quot;713&quot; data-origin-width=&quot;1155&quot; data-origin-height=&quot;713&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 data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;오늘도&amp;nbsp;감사합니다.&amp;nbsp; &lt;/b&gt;&lt;/p&gt;</description>
      <category>React &amp;amp; React Native/오류노트</category>
      <category>homebrew update</category>
      <category>Node found at: /opt/homebrew/Cellar/node</category>
      <category>Node found at: /opt/homebrew/Cellar/node 해결</category>
      <category>Node found at: /opt/homebrew/Cellar/node 해결방법</category>
      <category>Node found at: /opt/homebrew/Cellar/node@22/</category>
      <category>react native xcode</category>
      <category>Xcode 문제 해결</category>
      <author>adjh54</author>
      <guid isPermaLink="true">https://adjh54.tistory.com/723</guid>
      <comments>https://adjh54.tistory.com/723#entry723comment</comments>
      <pubDate>Mon, 17 Nov 2025 18:02:08 +0900</pubDate>
    </item>
    <item>
      <title>[Java] Spring Boot 환경에서 시스템 변수 .env 파일 지정 및 활용 방법</title>
      <link>https://adjh54.tistory.com/722</link>
      <description>&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;해당 글에서는 Spring Boot 개발 환경에서 .env 파일을 시스템 변수로 지정하는 방법과 이를 불러오는 다양한 방법에 대해서 알아봅니다&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;
&lt;figure contenteditable=&quot;false&quot; data-ke-type=&quot;emoticon&quot; data-ke-align=&quot;alignCenter&quot; data-emoticon-type=&quot;face&quot; data-emoticon-name=&quot;073&quot; data-emoticon-isanimation=&quot;false&quot; data-emoticon-src=&quot;https://t1.daumcdn.net/keditor/emoticon/face/large/073.png&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/keditor/emoticon/face/large/073.png&quot; width=&quot;80&quot; /&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1) Spring Boot 환경에서 .env 파일을 시스템 변수로 적용하는 이유&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 보안적으로 코드와 민감 정보를 분리하기 위해 이용합니다.&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  보안적으로 코드와 민감 정보를 분리하기 위해 이용합니다.&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- .properties나 application-xxx.yml에 주요 민감 정보들이 포함되어 있다면, Git 내에 민감정보가 커밋이 될 수 있고, 협업/오픈소스 환경에서는 유출 위험이 큽니다.&lt;/b&gt;&lt;br /&gt;- 해당 민감 정보를 기반으로 악의적인 목적으로 이용하거나 탈취를 할 수 있기에 서버를 실행할때, .env 파일을 불러오도록 수행하여서 직접적인 노출을 하지 않습니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. 운영되는 환경(dev/stg/prd)에 따라 값이 달라질수 있기에 이용합니다.&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  운영되는 환경(dev/stg/prd)에 따라 값이 달라질수 있기에 이용합니다.&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 기존에는 **.properties나 application-xxx.yml에서 분기하여서 각각을 이용하여서 application-dev.yml, application-stg.yml, application-prd.yml 방식으로 분리를 하여서 각각 파일 내에 설정 정보를 다르게 하였습니다.&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;- 이와 같이 .env 내에서도 동일하게 각각의 운영되는 환경에 맞게 각 파일들을 관리하여, 서버가 실행될 때 각 .env, .env.dev, .env.stg, .env.prd 파일을 불러오게끔 합니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. CI/CD &amp;amp; 도커, 클라우드 환경에서 표준 방식으로 이용합니다.&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  CI/CD &amp;amp; 도커, 클라우드 환경에서 표준 방식&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- Docker 내에서는 Dockerfile + .env 파일, docker-compose &amp;mdash;env-file + .env 파일과 같이 대부분의 배포 환경은 환경 변수 기반설정을 권장합니다&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4. 실운영에서 파일이 남아있지 않아 유출 위험이 더 낮기에 이용합니다&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  실운영에서 파일이 남아있지 않아 유출 위험이 더 낮음&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;-&amp;nbsp; YML/Properties 파일은 서버에 그대로 남아있지만 환경변수는 메모리 상에만 존재하기 때문에 서버 파일 유출 시에도 상대적으로 안전합니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5. Vault를 이용하는 경우, 문제 발생 시 상대적으로 빠른 대응이 가능합니다&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  Vault를 이용하는 경우, 문제 발생 시 상대적으로 빠른 대응이 가능합니다&lt;br /&gt;&lt;br /&gt;&lt;/b&gt;- Vault를 이용하여서 민감정보, 데이터베이스 접속 정보등을 관리하고 있을 때, 장애가 발생하여서 Vault 접속자체가 불 가능하여 모든 서버는 중단이 될 수 있습니다. 이때에, 복구작업으로 Vault 없이 서비스를 재 가동을 해야 하는 경우 .env 백업본을 활용하여서 이에 대한 대응을 수행할 수 있습니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2) .env 파일 생성 및 주입&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 파일 생성 및 구성&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  파일 생성 및 구성&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 아래와 같이 프로젝트 루트 경로에, 예시로 .env 파일을 구성하였습니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;APP_ENV=dev
DB_HOST=localhost
DB_USER=devuser
DB_PASSWORD=devpassword
LOG_LEVEL=debug
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;756&quot; data-origin-height=&quot;428&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/KKWfZ/dJMcafZePjT/5jYsghZzjkJHlVEHuOLVzk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/KKWfZ/dJMcafZePjT/5jYsghZzjkJHlVEHuOLVzk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/KKWfZ/dJMcafZePjT/5jYsghZzjkJHlVEHuOLVzk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FKKWfZ%2FdJMcafZePjT%2F5jYsghZzjkJHlVEHuOLVzk%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;756&quot; height=&quot;428&quot; data-origin-width=&quot;756&quot; data-origin-height=&quot;428&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;h3 data-ke-size=&quot;size23&quot;&gt;2. .gitignore에 .env 파일을 제외파일로 추가합니다&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  .gitignore에 .env 파일을 제외파일로 추가합니다&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 해당 파일이 git 내에 추가가되면 안되기에 .env 파일을 git 내에 commit &amp;amp; push 되지 않도록 합니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1313&quot; data-origin-height=&quot;539&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/TroCx/dJMcafroBV5/jQCkgoxkuDqJGlKLvRebyk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/TroCx/dJMcafroBV5/jQCkgoxkuDqJGlKLvRebyk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/TroCx/dJMcafroBV5/jQCkgoxkuDqJGlKLvRebyk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FTroCx%2FdJMcafroBV5%2FjQCkgoxkuDqJGlKLvRebyk%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;1313&quot; height=&quot;539&quot; data-origin-width=&quot;1313&quot; data-origin-height=&quot;539&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;h3 data-ke-size=&quot;size23&quot;&gt;3. 오른쪽 상단에 서버 &amp;gt; 구성 편집.. 버튼을 누릅니다.&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;507&quot; data-origin-height=&quot;418&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/4VV2U/dJMcafroBYg/9bbUULBBwkxvZKH3rdKD41/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/4VV2U/dJMcafroBYg/9bbUULBBwkxvZKH3rdKD41/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/4VV2U/dJMcafroBYg/9bbUULBBwkxvZKH3rdKD41/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F4VV2U%2FdJMcafroBYg%2F9bbUULBBwkxvZKH3rdKD41%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;507&quot; height=&quot;418&quot; data-origin-width=&quot;507&quot; data-origin-height=&quot;418&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;h3 data-ke-size=&quot;size23&quot;&gt;4. 옵션 수정(Modify options) 버튼을 누릅니다&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;816&quot; data-origin-height=&quot;689&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mbZ8w/dJMcaelIW4c/QfCaTiULzc1F9EG2KxGF1k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mbZ8w/dJMcaelIW4c/QfCaTiULzc1F9EG2KxGF1k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mbZ8w/dJMcaelIW4c/QfCaTiULzc1F9EG2KxGF1k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmbZ8w%2FdJMcaelIW4c%2FQfCaTiULzc1F9EG2KxGF1k%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;816&quot; height=&quot;689&quot; data-origin-width=&quot;816&quot; data-origin-height=&quot;689&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;h3 data-ke-size=&quot;size23&quot;&gt;5. 운영 체제(Operating System) &amp;gt; 환경 변수(Environment variables)를 선택합니다&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1165&quot; data-origin-height=&quot;815&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/VCq9j/dJMcaiuTnCJ/81oOWfvA6scPhjA8LvDYh1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/VCq9j/dJMcaiuTnCJ/81oOWfvA6scPhjA8LvDYh1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/VCq9j/dJMcaiuTnCJ/81oOWfvA6scPhjA8LvDYh1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FVCq9j%2FdJMcaiuTnCJ%2F81oOWfvA6scPhjA8LvDYh1%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;1165&quot; height=&quot;815&quot; data-origin-width=&quot;1165&quot; data-origin-height=&quot;815&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;h3 data-ke-size=&quot;size23&quot;&gt;6. 폴더 아이콘을 누르고, 구성한 .env 파일을 참조합니다&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;907&quot; data-origin-height=&quot;686&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/9DONN/dJMcagcL2Gr/R6nkBm5pT8tOvhY0Bqj5Z0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/9DONN/dJMcagcL2Gr/R6nkBm5pT8tOvhY0Bqj5Z0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/9DONN/dJMcagcL2Gr/R6nkBm5pT8tOvhY0Bqj5Z0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F9DONN%2FdJMcagcL2Gr%2FR6nkBm5pT8tOvhY0Bqj5Z0%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;907&quot; height=&quot;686&quot; data-origin-width=&quot;907&quot; data-origin-height=&quot;686&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;blockquote data-ke-style=&quot;style3&quot;&gt;- 아래와 같이 참조하였습니다.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;  [참고]&lt;/b&gt;&lt;br /&gt;- 숨김 파일로 안보일때는 맥북에서는 command + shift + . 단축키를 누르면 숨김 파일이 보입니다.&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;826&quot; data-origin-height=&quot;587&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/48CYP/dJMcacOYJ9y/ihTjoSSzxHAMgpGHxZ4dQk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/48CYP/dJMcacOYJ9y/ihTjoSSzxHAMgpGHxZ4dQk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/48CYP/dJMcacOYJ9y/ihTjoSSzxHAMgpGHxZ4dQk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F48CYP%2FdJMcacOYJ9y%2FihTjoSSzxHAMgpGHxZ4dQk%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;826&quot; height=&quot;587&quot; data-origin-width=&quot;826&quot; data-origin-height=&quot;587&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;h3 data-ke-size=&quot;size23&quot;&gt;7. 아래와 같이 참조가 완료되었고, &amp;lsquo;확인&amp;rsquo; 버튼을 누릅니다.&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;812&quot; data-origin-height=&quot;693&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bnDVvX/dJMcafStivp/6ttBJMkNj47KpJsbnKALPK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bnDVvX/dJMcafStivp/6ttBJMkNj47KpJsbnKALPK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bnDVvX/dJMcafStivp/6ttBJMkNj47KpJsbnKALPK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbnDVvX%2FdJMcafStivp%2F6ttBJMkNj47KpJsbnKALPK%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;812&quot; height=&quot;693&quot; data-origin-width=&quot;812&quot; data-origin-height=&quot;693&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3) .env 파일 불러오기 : 소스코드&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  .env 파일 불러오기 : 소스코드&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 소스코드에서 바로 .env 파일 내에 데이터를 불러오는 방식을 의미합니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;645&quot; data-origin-height=&quot;618&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bywUCB/dJMcafStjbh/7zOU9RG5I6tlIKUHpgtHQk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bywUCB/dJMcafStjbh/7zOU9RG5I6tlIKUHpgtHQk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bywUCB/dJMcafStjbh/7zOU9RG5I6tlIKUHpgtHQk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbywUCB%2FdJMcafStjbh%2F7zOU9RG5I6tlIKUHpgtHQk%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;645&quot; height=&quot;618&quot; data-origin-width=&quot;645&quot; data-origin-height=&quot;618&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. env 파일 확인&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  .env 파일 확인&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 위에서 임시로 .env 파일 내에 데이터를 정의하였습니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;APP_ENV=dev
DB_HOST=localhost
DB_USER=devuser
DB_PASSWORD=devpassword
LOG_LEVEL=debug
&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;h3 data-ke-size=&quot;size23&quot;&gt;2. xxxApplication.java 파일 내에서 환경 변수를 조회합니다&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  xxxApplication.java&amp;nbsp;파일 내에서 환경 변수를 조회합니다&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 시스템 환경 변수를 조회하는 함수인 System.getenv()를 통해서 데이터를 조회하고, 최종 콘솔 내에 출력되도록 하였습니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;package com.tha.vault;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.ConfigurationPropertiesScan;

@SpringBootApplication
public class VaultApplication {

    public static void main(String[] args) {
        String appEnv = System.getenv(&quot;APP_ENV&quot;);
        String dbHost = System.getenv(&quot;DB_HOST&quot;);
        String dbUser = System.getenv(&quot;DB_USER&quot;);
        String dbPassword = System.getenv(&quot;DB_PASSWORD&quot;);
        String logLevel = System.getenv(&quot;LOG_LEVEL&quot;);
        System.out.println(&quot;ROLE_ID : &quot; + appEnv);
        System.out.println(&quot;DB_HOST : &quot; + dbHost);
        System.out.println(&quot;DB_USER : &quot; + dbUser);
        System.out.println(&quot;DB_PASSWORD : &quot; + dbPassword);
        System.out.println(&quot;LOG_LEVEL : &quot; + logLevel);
        SpringApplication.run(VaultApplication.class, args);
    }

}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. 서버를 실행합니다&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  서버를 실행합니다&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 아래와 같이 참조한 시스템 환경 변수가 정상적으로 출력이 됨을 확인하였습니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1447&quot; data-origin-height=&quot;424&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/coxWNH/dJMcadUEDF9/qs6aTZ1Ak8YXWaoUVexa2k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/coxWNH/dJMcadUEDF9/qs6aTZ1Ak8YXWaoUVexa2k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/coxWNH/dJMcadUEDF9/qs6aTZ1Ak8YXWaoUVexa2k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcoxWNH%2FdJMcadUEDF9%2Fqs6aTZ1Ak8YXWaoUVexa2k%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;1447&quot; height=&quot;424&quot; data-origin-width=&quot;1447&quot; data-origin-height=&quot;424&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;h2 data-ke-size=&quot;size26&quot;&gt;4) .env 파일 불러오기 : application.properties&lt;/h2&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;style3&quot;&gt;&lt;b&gt;&amp;nbsp;  .env 파일 불러오기 : application.properties&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;- 시스템 환경변수 application.properties와 매핑하여서 값을 지정하도록 구성하고, 최종적으로 코드 내에서 @Value 값을 이용하여 조회하는 형태입니다.&lt;/blockquote&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;664&quot; data-origin-height=&quot;770&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/SerVW/dJMcacuF0CQ/vhb7HQUuGoP0twK507D9Kk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/SerVW/dJMcacuF0CQ/vhb7HQUuGoP0twK507D9Kk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/SerVW/dJMcacuF0CQ/vhb7HQUuGoP0twK507D9Kk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FSerVW%2FdJMcacuF0CQ%2Fvhb7HQUuGoP0twK507D9Kk%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;664&quot; height=&quot;770&quot; data-origin-width=&quot;664&quot; data-origin-height=&quot;770&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;h3 data-ke-size=&quot;size23&quot;&gt;1. env 파일 확인&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  env 파일 확인&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 위에서 임시로 .env 파일 내에 데이터를 정의하였습니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;APP_ENV=dev
DB_HOST=localhost
DB_USER=devuser
DB_PASSWORD=devpassword
LOG_LEVEL=debug
&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;h3 data-ke-size=&quot;size23&quot;&gt;2. application-loc.yml&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  application-loc.yml&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- ${변수명} 형태로 .env 파일을 참조하여서 불러오도록 하였습니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;pre class=&quot;yaml&quot;&gt;&lt;code&gt;# Spring boot Server Configuration
server:
  port: 8000

custom-info:
  app-env: ${APP_ENV}
  db:
    host: ${DB_HOST}
    user: ${DB_USER}
    password: ${DB_PASSWORD}
  log-level: ${LOG_LEVEL}

&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;h3 data-ke-size=&quot;size23&quot;&gt;3. @Value()를 기반으로 데이터를 조회합니다&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  @Value()를 기반으로 데이터를 조회합니다&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 위에서 정의한 yml 파일을 조회하여서 콘솔에 출력하도록 구성하였습니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class VaultController {

    @Value(&quot;${custom-info.app-env}&quot;)
    private String appEnv;

    @Value(&quot;${custom-info.db.host}&quot;)
    private String dbHost;

    @Value(&quot;${custom-info.db.user}&quot;)
    private String dbUser;

    @Value(&quot;${custom-info.db.password}&quot;)
    private String dbPassword;

    @Value(&quot;${custom-info.log-level}&quot;)
    private String logLevel;

    @GetMapping(&quot;/&quot;)
    public String main() {
        System.out.println(&quot;appEnv : &quot; + appEnv);
        System.out.println(&quot;dbHost : &quot; + dbHost);
        System.out.println(&quot;dbUser : &quot; + dbUser);
        System.out.println(&quot;dbPassword : &quot; + dbPassword);
        System.out.println(&quot;logLevel : &quot; + logLevel);
        return &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;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4. .env &amp;gt; .yml &amp;gt; 코드로 데이터를 조회해 왔습니다&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  .env &amp;gt; .yml &amp;gt; 코드로 데이터를 조회해 왔습니다&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 지정된 API로 호출했을 때 콘솔로 정보가 조회됨을 확인하였습니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1524&quot; data-origin-height=&quot;349&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bRTEew/dJMcacBryKl/idlNnfikqI6AYUxQaSzHb1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bRTEew/dJMcacBryKl/idlNnfikqI6AYUxQaSzHb1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bRTEew/dJMcacBryKl/idlNnfikqI6AYUxQaSzHb1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbRTEew%2FdJMcacBryKl%2FidlNnfikqI6AYUxQaSzHb1%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;1524&quot; height=&quot;349&quot; data-origin-width=&quot;1524&quot; data-origin-height=&quot;349&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;h2 data-ke-size=&quot;size26&quot;&gt;5) .env 파일 불러오기 : application.properties &amp;gt; 객체 매핑(getter)&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  .env 파일 불러오기 :&amp;nbsp;application.properties&amp;nbsp;&amp;gt; 객체 매핑(getter)&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- .env 파일에서 불러온 키 값을 application.properties에서 받고 해당 값을 기반으로 즉시 객체에 매핑하는 방식을 이용합니다&lt;/b&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;637&quot; data-origin-height=&quot;751&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bA4nqi/dJMcacOYK00/QLps1nXDWqJ2iAWdjj0P21/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bA4nqi/dJMcacOYK00/QLps1nXDWqJ2iAWdjj0P21/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bA4nqi/dJMcacOYK00/QLps1nXDWqJ2iAWdjj0P21/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbA4nqi%2FdJMcacOYK00%2FQLps1nXDWqJ2iAWdjj0P21%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;637&quot; height=&quot;751&quot; data-origin-width=&quot;637&quot; data-origin-height=&quot;751&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. env 파일 확인&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  .env 파일 확인&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 위에서 임시로 .env 파일 내에 데이터를 정의하였습니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;APP_ENV=dev
DB_HOST=localhost
DB_USER=devuser
DB_PASSWORD=devpassword
LOG_LEVEL=debug
&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;h3 data-ke-size=&quot;size23&quot;&gt;2. application-loc.yml&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  application-loc.yml&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- ${변수명} 형태로 .env 파일을 참조하여서 불러오도록 하였습니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;# Spring boot Server Configuration
server:
  port: 8000

custom-info:
  app-env: ${APP_ENV}
  db:
    host: ${DB_HOST}
    user: ${DB_USER}
    password: ${DB_PASSWORD}
  log-level: ${LOG_LEVEL}&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;h3 data-ke-size=&quot;size23&quot;&gt;3. CustomInfoProperties.java&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  CustomInfoProperties.java&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- .yml 내에 있는 설정 정보들을 @ConfigurationProperties() 어노테이션을 기반으로 객체와 매핑하고, @Getter를 통하여 객체화하여서 편리하게 이용합니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;import lombok.Getter;
import lombok.Setter;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

@Getter
@Setter
@Component
@ConfigurationProperties(prefix = &quot;custom-info&quot;)
public class CustomInfoProperties {

    private String appEnv;
    private String logLevel;
    private DB db;

    @Getter
    @Setter
    public static class DB {
        private String host;
        private String user;
        private String password;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4. getter를 이용한 조회 해왔습니다.&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  getter를 이용한 조회 해왔습니다.&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 좀 더 개발자 친화적인 객체화 스타일인 getter를 이용한 방식으로 조회를 합니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;import com.tha.vault.config.CustomInfoProperties;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class VaultController {

    private final CustomInfoProperties customInfoProperties;

    public VaultController(CustomInfoProperties customInfoProperties) {
        this.customInfoProperties = customInfoProperties;
    }

    @GetMapping(&quot;/&quot;)
    public String main() {
        System.out.println(&quot;appEnv : &quot; + customInfoProperties.getAppEnv());
        System.out.println(&quot;dbHost : &quot; + customInfoProperties.getDb().getHost());
        System.out.println(&quot;dbUser : &quot; + customInfoProperties.getDb().getUser());
        System.out.println(&quot;dbPassword : &quot; + customInfoProperties.getDb().getPassword());
        System.out.println(&quot;logLevel : &quot; + customInfoProperties.getLogLevel());
        return &quot;임시 테스트 &quot;;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  아래와 같이 정상적으로 조회가 완료되었습니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1798&quot; data-origin-height=&quot;466&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/oBcVh/dJMcadUEEjT/vULFAQKkAUij7C63sbIlg0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/oBcVh/dJMcadUEEjT/vULFAQKkAUij7C63sbIlg0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/oBcVh/dJMcadUEEjT/vULFAQKkAUij7C63sbIlg0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FoBcVh%2FdJMcadUEEjT%2FvULFAQKkAUij7C63sbIlg0%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;1798&quot; height=&quot;466&quot; data-origin-width=&quot;1798&quot; data-origin-height=&quot;466&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 data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;오늘도&amp;nbsp;감사합니다.&amp;nbsp; &lt;/b&gt;&lt;/p&gt;</description>
      <category>Java/이론 및 문법</category>
      <category>env 파일</category>
      <category>env 파일 조회방법</category>
      <category>java 시스템 변수 조회</category>
      <category>java 시스템 환경 변수</category>
      <category>Spring Boot</category>
      <category>spring boot env 불러오기</category>
      <category>spring boot env 주입</category>
      <category>spring boot env 파일 불러오기</category>
      <category>spring boot 시스템 변수</category>
      <category>spring boot 시스템 변수 조회</category>
      <author>adjh54</author>
      <guid isPermaLink="true">https://adjh54.tistory.com/722</guid>
      <comments>https://adjh54.tistory.com/722#entry722comment</comments>
      <pubDate>Tue, 11 Nov 2025 15:59:54 +0900</pubDate>
    </item>
    <item>
      <title>[RN] React Native 환경에서 Unity LevelPlay 광고 넣기 : Banner, Interstitial</title>
      <link>https://adjh54.tistory.com/721</link>
      <description>&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;해당 글에서는 Unity LevelPlay를 활용하여서 앱 내에 광고를 추가하는 방법에 대해 알아봅니다.&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;
&lt;figure contenteditable=&quot;false&quot; data-ke-type=&quot;emoticon&quot; data-ke-align=&quot;alignCenter&quot; data-emoticon-type=&quot;face&quot; data-emoticon-name=&quot;073&quot; data-emoticon-isanimation=&quot;false&quot; data-emoticon-src=&quot;https://t1.daumcdn.net/keditor/emoticon/face/large/073.png&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/keditor/emoticon/face/large/073.png&quot; width=&quot;80&quot; /&gt;&lt;/figure&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1) Unity LevelPlay&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  Unity LevelPlay&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;-&amp;nbsp; Unity Technologies(이하 Unity)가 제공하는 모바일 게임 및 앱 내 광고 수익화(모네타이제이션) 및 광고 중개(mediation) 플랫폼입니다.&lt;br /&gt;&lt;/b&gt;- 주요 목적은 여러 광고 네트워크(ad networks)를 통합하고, 입찰(bidding) 및 워터폴(waterfall) 방식으로 경쟁을 유도해 광고 단가(eCPM)와 수익을 극대화하는 것입니다.&lt;br /&gt;&lt;br /&gt;- 이와 비슷한 주요 플랫폼으로는 Google Admob, AppLovin 등이 있습니다.&lt;/blockquote&gt;
&lt;figure id=&quot;og_1762242103894&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;유니티 레벨플레이: 광고 미디에이션 플랫폼 | 유니티(Unity)&quot; data-og-description=&quot;업계를 선도하는 광고 미디에이션 플랫폼, 유니티 레벨플레이로 앱 수익을 극대화하고 유저 확보를 가속화하세요.&quot; data-og-host=&quot;unity.com&quot; data-og-source-url=&quot;https://unity.com/kr/products/levelplay&quot; data-og-url=&quot;https://unity.com/products/levelplay&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/h23f1/hyZNeXB5Av/GLNtjmQEGKk0G7IQVZztnk/img.png?width=1920&amp;amp;height=1080&amp;amp;face=0_0_1920_1080,https://scrap.kakaocdn.net/dn/o41IE/hyZMXJXwss/vTaLf8pxZesJEPKB2AM1t0/img.png?width=1920&amp;amp;height=1080&amp;amp;face=0_0_1920_1080&quot;&gt;&lt;a href=&quot;https://unity.com/kr/products/levelplay&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://unity.com/kr/products/levelplay&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/h23f1/hyZNeXB5Av/GLNtjmQEGKk0G7IQVZztnk/img.png?width=1920&amp;amp;height=1080&amp;amp;face=0_0_1920_1080,https://scrap.kakaocdn.net/dn/o41IE/hyZMXJXwss/vTaLf8pxZesJEPKB2AM1t0/img.png?width=1920&amp;amp;height=1080&amp;amp;face=0_0_1920_1080');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;유니티 레벨플레이: 광고 미디에이션 플랫폼 | 유니티(Unity)&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;unity.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;h3 data-ke-size=&quot;size23&quot;&gt;1. Unity LevelPlay 기능&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style2&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.0232%;&quot;&gt;&lt;b&gt;기능&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 66.8605%;&quot;&gt;&lt;b&gt;설명&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.0232%;&quot;&gt;&lt;b&gt;광고 중개(Mediation)&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 66.8605%;&quot;&gt;여러 광고 네트워크를 하나의 플랫폼에서 관리할 수 있습니다. 사용자는 Unity Ads, AdMob, AppLovin 등 다양한 네트워크를 연결해 수익을 높일 수 있습니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.0232%;&quot;&gt;&lt;b&gt;실시간 입찰(In-App Bidding)&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 66.8605%;&quot;&gt;광고 인벤토리(노출 기회)를 여러 광고 네트워크가 실시간으로 경쟁(bid)하는 구조로 바꿔, 전통적인 워터폴 방식보다 더 높은 수익을 얻을 수 있게 합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.0232%;&quot;&gt;&lt;b&gt;통합 대시보드 및 실시간 리포팅&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 66.8605%;&quot;&gt;광고 수익, eCPM, 사용자 세그먼트 등 다양한 지표를 실시간으로 확인하고 분석할 수 있는 리포팅 기능을 제공합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.0232%;&quot;&gt;&lt;b&gt;A/B 테스트 및 세그먼트 제어&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 66.8605%;&quot;&gt;광고 설정(광고 위치, 포맷, 네트워크 우선순위 등)을 A/B 테스트하여 최적화 가능하며, 사용자 세그먼트별로 광고 경험을 제어할 수 있습니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.0232%;&quot;&gt;&lt;b&gt;Unity Editor 및 생태계 연동&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 66.8605%;&quot;&gt;Unity Editor 내 Package Manager에서 쉽게 통합 가능하며, 개발&amp;rarr;빌드&amp;rarr;광고 수익화까지 Unity 생태계 안에서 일관된 워크플로우를 제공합니다.&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;h3 data-ke-size=&quot;size23&quot;&gt;2. Ironsource&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  Ironsource&lt;/b&gt;&lt;br /&gt;&lt;b&gt;&lt;br /&gt;- 이스라엘에서 시작된 모바일 광고 수익화 플랫폼(Ad mediation &amp;amp; monetization platform)입니다.&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;- 2022년에 Unity Technologies가 IronSource를 인수했습니다. 그 후 Unity는 IronSource의 광고 중개 플랫폼을 통합해서 IronSource LevelPlay &amp;rarr; Unity LevelPlay가 되었습니다.&lt;br /&gt;- 즉 IronSource의 Mediation 엔진과 기술력과 Unity의 생태계와 광고 네트워크가 합쳐진 것이 Unity LevelPlay입니다. &lt;br /&gt;- 현재도 Unity 문서와 SDK 내부에서는 &amp;ldquo;IronSource SDK&amp;rdquo;라는 이름이 여전히 사용되고 있어요.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1762242164683&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;유니티, 아이언소스와 합병 계약 체결&quot; data-og-description=&quot;유니티는 오늘 아이언소스와 합병 계약 체결을 발표했고, 이에 따라 아이언소스의 툴과 플랫폼, 기술, 인재를 활용하여 크리에이터들의 라이브 게임 및 실시간 인터랙티브 경험 제작과 운영, 관&quot; data-og-host=&quot;unity.com&quot; data-og-source-url=&quot;https://unity.com/kr/blog/news/welcome-ironsource&quot; data-og-url=&quot;https://unity.com/blog/news/welcome-ironsource&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bOHCCB/hyZMxXAYHT/CLBD5kLMEpoPjWg5KaQjI0/img.png?width=600&amp;amp;height=338&amp;amp;face=0_0_600_338,https://scrap.kakaocdn.net/dn/e6bag/hyZM9veBJ5/VMTl3ZEoXcApwIaiGercw0/img.png?width=600&amp;amp;height=338&amp;amp;face=0_0_600_338&quot;&gt;&lt;a href=&quot;https://unity.com/kr/blog/news/welcome-ironsource&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://unity.com/kr/blog/news/welcome-ironsource&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bOHCCB/hyZMxXAYHT/CLBD5kLMEpoPjWg5KaQjI0/img.png?width=600&amp;amp;height=338&amp;amp;face=0_0_600_338,https://scrap.kakaocdn.net/dn/e6bag/hyZM9veBJ5/VMTl3ZEoXcApwIaiGercw0/img.png?width=600&amp;amp;height=338&amp;amp;face=0_0_600_338');&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;유니티, 아이언소스와 합병 계약 체결&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;unity.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;h3 data-ke-size=&quot;size23&quot;&gt;3. Unity LevelPlay의 Mediation은 어떻게 수행이 될까?&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  Unity LevelPlay의 Mediation은 어떻게 수행이 될까?&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 앱 개발자가 여러 광고의 네트워크를 붙이는 것이 아닌 하나의 중개 플랫폼(LevelPlay)이 대신 네트워크를 연결&amp;middot;조율해 주는 구조입니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3.1. Mediation 방식&lt;/h4&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;style3&quot;&gt;&lt;b&gt;  Mediation 방식&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 직접적으로 개발자는 광고를 붙이는 것이 아니지만, 광고 플랫폼 내에서는 아래와 같은 방식으로 광고가 송출이 됩니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style2&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 25.4651%;&quot;&gt;&lt;b&gt;방식&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 50.6977%;&quot;&gt;&lt;b&gt;설명&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 23.7209%;&quot;&gt;&lt;b&gt;용도&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 25.4651%;&quot;&gt;&lt;b&gt;Waterfall 방식&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 50.6977%;&quot;&gt;'우선순위'를 정해 상단 네트워크부터 순서대로 광고 요청&lt;/td&gt;
&lt;td style=&quot;width: 23.7209%;&quot;&gt;단가가 안정적이지만 비효율적일 수 있음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 25.4651%;&quot;&gt;&lt;b&gt;Bidding 방식 (In-App Bidding)&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 50.6977%;&quot;&gt;여러 네트워크가 실시간 입찰로 경쟁하여 가장 높은 eCPM 광고를 선택&lt;/td&gt;
&lt;td style=&quot;width: 23.7209%;&quot;&gt;최신 트렌드, LevelPlay의 핵심 구조&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 25.4651%;&quot;&gt;&lt;b&gt;Hybrid Mediation 방식&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 50.6977%;&quot;&gt;- 입찰 참여 가능한 네트워크는 실시간으로 경쟁하고, 참여 불가능한 네트워크는 예비(Waterfall)로 작동합니다. &lt;br /&gt;- 해당 방식이 LevelPlay 방식입니다.&lt;/td&gt;
&lt;td style=&quot;width: 23.7209%;&quot;&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;960&quot; data-origin-height=&quot;513&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ca5Um0/dJMcaiVVJVq/vSjH4lxKzppZ9K8OKvQQt0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ca5Um0/dJMcaiVVJVq/vSjH4lxKzppZ9K8OKvQQt0/img.png&quot; data-alt=&quot;https://unity.com/blog/what-is-mobile-ad-mediation&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ca5Um0/dJMcaiVVJVq/vSjH4lxKzppZ9K8OKvQQt0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fca5Um0%2FdJMcaiVVJVq%2FvSjH4lxKzppZ9K8OKvQQt0%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;960&quot; height=&quot;513&quot; data-origin-width=&quot;960&quot; data-origin-height=&quot;513&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://unity.com/blog/what-is-mobile-ad-mediation&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  Mediation Waterfall 방식&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;- '우선순위'를 정해 상단 네트워크부터 순서대로 광고 요청&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;500&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/tzajs/dJMcahbEfAG/91m3KkdxBaMlfBJug38qXK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/tzajs/dJMcahbEfAG/91m3KkdxBaMlfBJug38qXK/img.png&quot; data-alt=&quot;https://docs.unity.com/ko-kr/grow/dashboard/waterfalls&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/tzajs/dJMcahbEfAG/91m3KkdxBaMlfBJug38qXK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Ftzajs%2FdJMcahbEfAG%2F91m3KkdxBaMlfBJug38qXK%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;1024&quot; height=&quot;500&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;500&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://docs.unity.com/ko-kr/grow/dashboard/waterfalls&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  Mediation Bindding 방식&lt;br /&gt;&lt;br /&gt;- 여러 네트워크가 실시간 입찰로 경쟁하여 가장 높은 eCPM 광고를 선택하는 방식&lt;/b&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;575&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bplG6e/dJMcad1oaE0/SKohqPTvzap7LiOv8ccLn1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bplG6e/dJMcad1oaE0/SKohqPTvzap7LiOv8ccLn1/img.jpg&quot; data-alt=&quot;https://developers.is.com/ironsource-mobile/air/level-play/#step-1&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bplG6e/dJMcad1oaE0/SKohqPTvzap7LiOv8ccLn1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbplG6e%2FdJMcad1oaE0%2FSKohqPTvzap7LiOv8ccLn1%2Fimg.jpg&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;1024&quot; height=&quot;575&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;575&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://developers.is.com/ironsource-mobile/air/level-play/#step-1&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2) 환경 구성하기&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  환경 구성하기&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 공식사이트의 가이드에 따라서 적용합니다.&lt;br /&gt;- Unity LevelPlay 설정이 완료되었고 권한을 부여받았다는 가정하에 진행을 합니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;figure id=&quot;og_1762245463412&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;React Native Plugin Integration - IronSource Knowledge Center&quot; data-og-description=&quot;React Native Plugin Integration ⚡ Before you start Our React Native plugin is supported from the native SDK versions 7.2.4.1 for Android and 7.2.4 for iOS. The plugin supports both JavaScript and TypeScript. The type declarations are included in the npm &quot; data-og-host=&quot;developers.is.com&quot; data-og-source-url=&quot;https://developers.is.com/ironsource-mobile/react-native/react-native-plugin-integration/#step-1&quot; data-og-url=&quot;https://developers.is.com/ironsource-mobile/react-native/react-native-plugin-integration/#step-1&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/dyBKFj/hyZMY9XoJ5/YqAhpebqab0FkwOS7WrYZ0/img.jpg?width=500&amp;amp;height=160&amp;amp;face=0_0_500_160&quot;&gt;&lt;a href=&quot;https://developers.is.com/ironsource-mobile/react-native/react-native-plugin-integration/#step-1&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://developers.is.com/ironsource-mobile/react-native/react-native-plugin-integration/#step-1&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/dyBKFj/hyZMY9XoJ5/YqAhpebqab0FkwOS7WrYZ0/img.jpg?width=500&amp;amp;height=160&amp;amp;face=0_0_500_160');&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;React Native Plugin Integration - IronSource Knowledge Center&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;React Native Plugin Integration ⚡ Before you start Our React Native plugin is supported from the native SDK versions 7.2.4.1 for Android and 7.2.4 for iOS. The plugin supports both JavaScript and TypeScript. The type declarations are included in the npm&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;developers.is.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;h3 data-ke-size=&quot;size23&quot;&gt;1. 라이브러리 설치&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  라이브러리 설치&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 공식 사이트 글을 참고하여서, 가장 최신 버전인 3.2.0 버전을 설치하였습니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;$ npm install ironsource-mediation@3.2.0

# or

$ yarn add ironsource-mediation@3.2.0
&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;h2 data-ke-size=&quot;size26&quot;&gt;3) 환경 구성하기 : iOS&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. Podfile 설정&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  Podfile&lt;/b&gt;&lt;br /&gt;&lt;b&gt;&lt;br /&gt;- IronSourceAdQualitySDK는 Unity LevelPlay에서 광고 품질 측정/분석하는 SDK입니다.&lt;/b&gt;&lt;br /&gt;- 앱 안에 송출되는 광고(배너, 전면 광고, 보상형 광고 등)의 품질(클릭, 전환, 수익 기여도 등)을 추적/분석하는 데 사용됩니다.&lt;/blockquote&gt;
&lt;pre class=&quot;nginx&quot;&gt;&lt;code&gt;#   IronSource 추가
pod 'IronSourceAdQualitySDK'
&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;h3 data-ke-size=&quot;size23&quot;&gt;2. plist.info 설정&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2.1. 광고 네트워크 고유 식별자 등록&lt;/h4&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;style3&quot;&gt;&lt;b&gt;  광고 네트워크 고유 식별자 등록&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- Apple의 광고 네트워크(예: IronSource, Unity Ads, Google, Facebook 등)에 부여한 고유 식별자로 IronSource의 식별자를 등록합니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;pre class=&quot;xml&quot;&gt;&lt;code&gt;&amp;lt;key&amp;gt;SKAdNetworkItems&amp;lt;/key&amp;gt;
&amp;lt;array&amp;gt;
   &amp;lt;dict&amp;gt;
      &amp;lt;key&amp;gt;SKAdNetworkIdentifier&amp;lt;/key&amp;gt;
      &amp;lt;string&amp;gt;su67r6k2v3.skadnetwork&amp;lt;/string&amp;gt;
   &amp;lt;/dict&amp;gt;
&amp;lt;/array&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;h4 data-ke-size=&quot;size20&quot;&gt;2.2. Universal SKAN Reporting 등록&lt;/h4&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;style3&quot;&gt;&lt;b&gt;  Universal SKAN Reporting 등록&lt;/b&gt;&lt;br /&gt;&lt;b&gt;&lt;br /&gt;- IronSource 같은 mediation 플랫폼도 직접 postback 사본을 수신해서, 전체 네트워크 성과를 더 정확히 집계&amp;middot;최적화할 수 있습니다.&lt;/b&gt;&lt;br /&gt;- Universal SKAN postback을 보낼 목적지 URL을 지정하는 키로 IronSource(= Unity LevelPlay)에서 제공하는 전용 엔드포인트로 지정을 합니다.&lt;/blockquote&gt;
&lt;pre class=&quot;xml&quot;&gt;&lt;code&gt;&amp;lt;key&amp;gt;NSAppTransportSecurity&amp;lt;/key&amp;gt;
&amp;lt;dict&amp;gt;
	&amp;lt;key&amp;gt;NSAllowsArbitraryLoads&amp;lt;/key&amp;gt;
  	&amp;lt;true/&amp;gt;
&amp;lt;/dict&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;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2.3. App transport security settings 지정&lt;/h4&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;style3&quot;&gt;&lt;b&gt;&amp;nbsp;  App transport security settings 지정&lt;/b&gt;&lt;br /&gt;&lt;b&gt;&lt;br /&gt;- iOS 9부터 Apple은 네트워크 보안 강화를 위해 ATS라는 정책을 도입했습니다. 기본적으로 모든 HTTP 요청은 TLS 1.2 이상을 사용하는 HTTPS만 허용됩니다.&lt;/b&gt;&lt;br /&gt;- IronSource mediation은 여러 광고 네트워크(구글, Unity, Meta, 작은 DSP 등등)의 광고를 중계합니다. 이때 모든 광고 네트워크가 항상 최신 보안 기준(HTTPS)만 쓰는 건 아니기에 ATS를 그대로 두면 광고 요청이 차단되어서 광고가 나오지 않는 문제가 발생할 수 있습니다.&lt;br /&gt;- Apple은 원칙적으로 광범위한 예외(NSAllowsArbitraryLoads = YES)를 권장하지 않습니다. 보안상 좋은 방법은 개별 도메인 예외(NSExceptionDomains)를 열어주는 방법이지만 도메인이 계속 바뀌기에 관리가 사실상 불가능합니다.&lt;/blockquote&gt;
&lt;pre class=&quot;vim&quot;&gt;&lt;code&gt;NSAdvertisingAttributionReportEndpoint
&amp;lt;https://postbacks-is.com&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;h4 data-ke-size=&quot;size20&quot;&gt;2.4. 종합&lt;/h4&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;shell&quot; data-ke-language=&quot;shell&quot;&gt;&lt;code&gt;&amp;lt;key&amp;gt;SKAdNetworkItems&amp;lt;/key&amp;gt;
&amp;lt;array&amp;gt;
   &amp;lt;dict&amp;gt;
      &amp;lt;key&amp;gt;SKAdNetworkIdentifier&amp;lt;/key&amp;gt;
      &amp;lt;string&amp;gt;su67r6k2v3.skadnetwork&amp;lt;/string&amp;gt;
   &amp;lt;/dict&amp;gt;
&amp;lt;/array&amp;gt;

&amp;lt;key&amp;gt;NSAppTransportSecurity&amp;lt;/key&amp;gt;
&amp;lt;dict&amp;gt;
	&amp;lt;key&amp;gt;NSAllowsArbitraryLoads&amp;lt;/key&amp;gt;
  	&amp;lt;true/&amp;gt;
&amp;lt;/dict&amp;gt;
&amp;lt;key&amp;gt;NSAdvertisingAttributionReportEndpoint&amp;lt;/key&amp;gt;
&amp;lt;string&amp;gt;https://postbacks-is.com&amp;lt;/string&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;h2 data-ke-size=&quot;size26&quot;&gt;4) 환경 구성하기 : Android&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. app/build.gradle&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  app/build.gradle&lt;/b&gt;&lt;br /&gt;&lt;b&gt;&lt;br /&gt;- com.unity3d.ads-mediation:adquality-sdk는 IronSourceAdQualitySDK는 Unity LevelPlay에서 광고 품질 측정/분석하는 SDK입니다.&lt;/b&gt;&lt;br /&gt;&lt;b&gt;- play-services-ads-identifier&lt;/b&gt; : Android 기기의 광고 식별자(Advertising ID, AAID/GAID)를 가져올 수 있게 해주는 라이브러리.&lt;br /&gt;&lt;b&gt;- play-services-basement :&lt;/b&gt; Google Play 서비스 SDK들의 공통 기반(core library) 역할.&lt;br /&gt;&lt;b&gt;- play-services-appset:&lt;/b&gt; App Set ID를 제공하는 라이브러리&lt;/blockquote&gt;
&lt;pre class=&quot;shell&quot; data-ke-language=&quot;shell&quot;&gt;&lt;code&gt;dependencies {
	implementation 'com.unity3d.ads-mediation:adquality-sdk:9.0.1'
	implementation 'com.google.android.gms:play-services-ads-identifier:18.0.1'
	implementation 'com.google.android.gms:play-services-basement:18.3.0'
	implementation 'com.google.android.gms:play-services-appset:16.0.2'
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1762245772839&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;React Native Plugin Integration - IronSource Knowledge Center&quot; data-og-description=&quot;React Native Plugin Integration ⚡ Before you start Our React Native plugin is supported from the native SDK versions 7.2.4.1 for Android and 7.2.4 for iOS. The plugin supports both JavaScript and TypeScript. The type declarations are included in the npm &quot; data-og-host=&quot;developers.is.com&quot; data-og-source-url=&quot;https://developers.is.com/ironsource-mobile/react-native/react-native-plugin-integration/#step-1&quot; data-og-url=&quot;https://developers.is.com/ironsource-mobile/react-native/react-native-plugin-integration/#step-1&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/dyBKFj/hyZMY9XoJ5/YqAhpebqab0FkwOS7WrYZ0/img.jpg?width=500&amp;amp;height=160&amp;amp;face=0_0_500_160&quot;&gt;&lt;a href=&quot;https://developers.is.com/ironsource-mobile/react-native/react-native-plugin-integration/#step-1&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://developers.is.com/ironsource-mobile/react-native/react-native-plugin-integration/#step-1&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/dyBKFj/hyZMY9XoJ5/YqAhpebqab0FkwOS7WrYZ0/img.jpg?width=500&amp;amp;height=160&amp;amp;face=0_0_500_160');&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;React Native Plugin Integration - IronSource Knowledge Center&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;React Native Plugin Integration ⚡ Before you start Our React Native plugin is supported from the native SDK versions 7.2.4.1 for Android and 7.2.4 for iOS. The plugin supports both JavaScript and TypeScript. The type declarations are included in the npm&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;developers.is.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;h3 data-ke-size=&quot;size23&quot;&gt;2. AndroidManifest.xml&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  AndroidManifest.xml&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 대상 API 레벨을 31(Android 12)로 업데이트하는 앱은 다음과 같이 매니페스트 파일에서 Google Play 서비스 일반 권한을 선언해야 합니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;&amp;lt;uses-permission android:name=&quot;com.google.android.gms.permission.AD_ID&quot;/&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;h3 data-ke-size=&quot;size23&quot;&gt;3. proguard-rules.pro&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  proguard-rules.pro&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- ironSource SDK와 함께 ProGuard를 사용하는 경우 다음 코드를 ProGuard 파일(Android Studio: proguard-rules.pro 또는 Eclipse: proguard-project.txt)에 추가해야 합니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;pre class=&quot;groovy&quot;&gt;&lt;code&gt;-keepclassmembers class com.ironsource.sdk.controller.IronSourceWebView$JSInterface {
    public *;
}
-keepclassmembers class * implements android.os.Parcelable {
    public static final android.os.Parcelable$Creator *;
}
-keep public class com.google.android.gms.ads.** {
   public *;
}
-keep class com.ironsource.adapters.** { *;
}
-keep class com.ironsource.unity.androidbridge.** { *;
}
-dontwarn com.ironsource.mediationsdk.**
-dontwarn com.ironsource.adapters.**
-keepattributes JavascriptInterface
-keepclassmembers class * {
    @android.webkit.JavascriptInterface &amp;lt;methods&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;blockquote data-ke-style=&quot;style3&quot;&gt;  [참고] 공식사이트에서 안드로이드 배너 광고만 출력이 안 되는 문제가 있었습니다.&lt;br /&gt;&lt;br /&gt;- 해당 문제를 찾던 중, gradle.properties 내에 속성 때문에 실행이 안된다는 것을 발견하였습니다.&lt;br /&gt;- 혹시나 위에 과정이 수행이 되지 않는 경우 아래의 옵션을 수정해 보시면 정상적으로 출력을 됨을 확인하실 수 있습니다.&lt;/blockquote&gt;
&lt;pre id=&quot;code_1762246494406&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 변경 이전
newArchEnabled=true

# 변경 이후
newArchEnabled=false&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;h2 data-ke-size=&quot;size26&quot;&gt;5) 광고 구성 : 배너 광고, Interstitial 광고&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 배너 광고&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  배너 광고&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 배너 광고는 각각 &amp;lsquo;앱의 키&amp;rsquo;와 &amp;lsquo;배너 UNIT ID&amp;rsquo;, PLACEMENT 값을 공식사이트에서 확인하여서 입력하여야 합니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;figure id=&quot;og_1762245882437&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;Banner integration for React Native &amp;bull; Unity Grow &amp;bull; Unity Docs&quot; data-og-description=&quot;Implement banner ads in your React Native app using the ironSource SDK, including setup, initialization, and listener configuration.&quot; data-og-host=&quot;docs.unity.com&quot; data-og-source-url=&quot;https://docs.unity.com/ko-kr/grow/levelplay/sdk/react/banner-integration&quot; data-og-url=&quot;https://docs.unity.com/ko-kr/grow/levelplay/sdk/react/banner-integration&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cBEiPI/hyZM0SBvYI/jtts6EaqEyj1lWqGnckfJ1/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/ctCKjz/hyZMVeghbl/Rlbefh2glaoRB72tSYdE51/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630&quot;&gt;&lt;a href=&quot;https://docs.unity.com/ko-kr/grow/levelplay/sdk/react/banner-integration&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://docs.unity.com/ko-kr/grow/levelplay/sdk/react/banner-integration&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cBEiPI/hyZM0SBvYI/jtts6EaqEyj1lWqGnckfJ1/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/ctCKjz/hyZMVeghbl/Rlbefh2glaoRB72tSYdE51/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Banner integration for React Native &amp;bull; Unity Grow &amp;bull; Unity Docs&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Implement banner ads in your React Native app using the ironSource SDK, including setup, initialization, and listener configuration.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;docs.unity.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;h4 data-ke-size=&quot;size20&quot;&gt;1.1. SDK 로드&lt;/h4&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;style3&quot;&gt;&lt;b&gt;  SDK 로드&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- useEffect를 통해서 해당 화면이 수행될 때 initIronSourceSDK 함수를 호출합니다.&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;1. APP_KEY 값을 기반으로 initRequest build를 구성합니다&lt;/b&gt;&lt;br /&gt;&lt;b&gt;2. initListener : SDK의 리스너를 등록합니다.&lt;/b&gt;&lt;br /&gt;- onInitSuccess : SDK 로드를 성공적으로 해올 때, 수행이 되는 함수입니다.&lt;br /&gt;- onInitFailed : SDK 로드를 실패하였을 때, 수행되는 함수입니다.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;2. LevelPlay.init():&lt;/b&gt; 최종 앱에 대한 키값과 리스너를 함께 SDK를 초기화하는 init() 함수입니다.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;3. 최종 SDK 로드가 완료되면 onInitSuccess 리스너가 수행이 됩니다.&lt;/b&gt;&lt;br /&gt;- 해당 시점에 배너를 로드해 옵니다.&lt;/blockquote&gt;
&lt;pre class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot;&gt;&lt;code&gt;const APP_KEY = Platform.select({
    android: '123',
    ios: '123',
    default: '',
});

useEffect(() =&amp;gt; {
    initIronSourceSDK();
}, []);

const initIronSourceSDK = async () =&amp;gt; {
    if (!APP_KEY) {
        console.error('❌ No APP_KEY found, skipping init');
        return;
    }
    try {
        const initRequest = LevelPlayInitRequest.builder(APP_KEY).build();
        const initListener: LevelPlayInitListener = {
            onInitSuccess: (config: LevelPlayConfiguration) =&amp;gt; {
                console.log('✅ IronSource Init Success:', config);
                bannerAdRef.current?.loadAd();
            },
            onInitFailed: (error: LevelPlayInitError) =&amp;gt; {
                console.error('❌ IronSource Init Failed:', error);
            },
        };
        // 디버깅용
        // LevelPlay.setAdaptersDebug(true);

        // App mount 안정화 후 실행
        requestAnimationFrame(() =&amp;gt; {
            LevelPlay.init(initRequest, initListener)
                .catch((e) =&amp;gt; {
                    console.error('  Init Exception:', e);
                });
        });
    } catch (e) {
        console.error('  JS Exception before init:', e);
    }
};&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;h4 data-ke-size=&quot;size20&quot;&gt;1.2. 배너 광고 로드&lt;/h4&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;style3&quot;&gt;&lt;b&gt;  배너 광고 로드&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- SDK가 성공적으로 수행이 된 이후에 배너 광고가 로드되어야 합니다.&lt;/b&gt;&lt;br /&gt;- onInitSuccess &amp;rarr; LevelPlayBannerAdView &amp;rarr; bannerAdRef.current?.loadAd(); 이와 같은 과정으로 수행이 됩니다.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;1. LevelPlayBannerAdView&lt;/b&gt;&lt;br /&gt;&lt;b&gt;&lt;br /&gt;- adUnitId:&lt;/b&gt; 사전에 발급받은 B- ANNER_UNIT_ID 값을 입력합니다.&lt;br /&gt;&lt;b&gt;- adSize:&lt;/b&gt; Unity LevelPlay에서 제공하는 주요한 배너 사이즈를 이용합니다.&lt;br /&gt;&lt;b&gt;- placementName:&lt;/b&gt; 배너의 어떤 배너인지를 구분하기 위한 배너입니다. &lt;br /&gt;- 예를 들어서, HOME_BANNER &amp;rarr; 홈 화면 하단에 표시되는 배너, QUIZ_REWARD &amp;rarr; 퀴즈 끝나고 보여주는 리워드 광고가 될 수 있습니다.&lt;br /&gt;-&amp;nbsp; listener: 배너 광고가 출력되고 사전에 정의한 리스너로 결과를 반환받습니다.&lt;br /&gt;&lt;b&gt;&lt;br /&gt;2. listener&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;- 성공적으로 배너가 출력이 되면 onAdLoaded &amp;rarr; onAdDisplayed와 같이 수행이 됩니다.&lt;/blockquote&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style2&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 37.4419%;&quot;&gt;&lt;b&gt;콜백 함수&amp;nbsp;&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 36.1628%;&quot;&gt;&lt;b&gt;호출 시점/의미&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 26.279%;&quot;&gt;&lt;b&gt;주로 하는 역할 예시&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 37.4419%;&quot;&gt;&lt;b&gt;onAdLoaded(info: LevelPlayAdInfo)&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 36.1628%;&quot;&gt;배너 광고가 &lt;b&gt;성공적으로 로드 완료&lt;/b&gt;됐을 때 호출&lt;/td&gt;
&lt;td style=&quot;width: 26.279%;&quot;&gt;UI에 &amp;ldquo;로드 완료&amp;rdquo; 로그, 다음 액션 가능 상태 표시&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 37.4419%;&quot;&gt;&lt;b&gt;onAdLoadFailed(error: LevelPlayAdError)&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 36.1628%;&quot;&gt;배너 광고 로드가 &lt;b&gt;실패했을 때&lt;/b&gt; 호출&lt;/td&gt;
&lt;td style=&quot;width: 26.279%;&quot;&gt;실패 로그 출력, 일정 시간 후 재시도 (setTimeout 등)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 37.4419%;&quot;&gt;&lt;b&gt;onAdDisplayed(info: LevelPlayAdInfo)&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 36.1628%;&quot;&gt;광고가 실제로 &lt;b&gt;화면에 표시되었을 때&lt;/b&gt; 호출&lt;/td&gt;
&lt;td style=&quot;width: 26.279%;&quot;&gt;노출 로그 기록, 분석/통계 전송&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 37.4419%;&quot;&gt;&lt;b&gt;onAdDisplayFailed(adInfo, error)&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 36.1628%;&quot;&gt;광고가 로드되었지만 &lt;b&gt;표시 실패&lt;/b&gt;했을 때 호출&lt;/td&gt;
&lt;td style=&quot;width: 26.279%;&quot;&gt;실패 원인 분석, 필요 시 로드 재시도&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 37.4419%;&quot;&gt;&lt;b&gt;onAdClicked(info: LevelPlayAdInfo)&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 36.1628%;&quot;&gt;사용자가 &lt;b&gt;배너를 클릭했을 때&lt;/b&gt; 호출&lt;/td&gt;
&lt;td style=&quot;width: 26.279%;&quot;&gt;클릭 이벤트 로깅, 분석/성과 데이터 전송&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 37.4419%;&quot;&gt;&lt;b&gt;onAdExpanded(adInfo)&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 36.1628%;&quot;&gt;배너가 &lt;b&gt;확장(Expand)&lt;/b&gt; 되었을 때 호출 (예: 리치미디어 광고가 배너 영역을 크게 펼치는 경우)&lt;/td&gt;
&lt;td style=&quot;width: 26.279%;&quot;&gt;UI 상태 조정, 배경 일시 정지 등&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 37.4419%;&quot;&gt;&lt;b&gt;onAdCollapsed(adInfo)&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 36.1628%;&quot;&gt;확장된 배너가 다시 &lt;b&gt;축소(Collapse)&lt;/b&gt; 되었을 때 호출&lt;/td&gt;
&lt;td style=&quot;width: 26.279%;&quot;&gt;UI 원상 복구&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 37.4419%;&quot;&gt;&lt;b&gt;onAdLeftApplication(adInfo)&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 36.1628%;&quot;&gt;사용자가 광고를 클릭해서 &lt;b&gt;앱을 떠났을 때&lt;/b&gt; 호출 (예: 브라우저 열림)&lt;/td&gt;
&lt;td style=&quot;width: 26.279%;&quot;&gt;앱 상태 관리, 로그 기록, 필요 시 pause 처리&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;pre class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot;&gt;&lt;code&gt;/* eslint-disable react-native/no-inline-styles */
import { scaleHeight } from '@/utils';
import {
    LevelPlay,
    LevelPlayAdError,
    LevelPlayAdInfo,
    LevelPlayAdSize,
    LevelPlayBannerAdView,
    LevelPlayBannerAdViewListener,
    LevelPlayBannerAdViewMethods,
    LevelPlayConfiguration,
    LevelPlayInitError,
    LevelPlayInitListener,
    LevelPlayInitRequest,
} from 'ironsource-mediation';
import React, { useCallback, useEffect, useRef, useState } from 'react';
import { Platform, View, StyleSheet } from 'react-native';

interface AdmobBannerAdProps {
    paramMarginTop?: number;
    paramMarginBottom?: number;
    visible?: boolean;
    isLoadSdk?: boolean; // ✅ init 성공 후 true
}

const BANNER_AD_UNIT_ID = Platform.select({
    android: '123',
    ios: '3344',
});

const APP_KEY = Platform.select({
    android: '555',
    ios: '6666',
    default: '',
});

const PLACEMENT_NAME = 'BANNER';

const LevelPlayBannerAd: React.FC&amp;lt;AdmobBannerAdProps&amp;gt; = ({
    paramMarginTop = 6,
    paramMarginBottom = 6,
    visible = true,
    isLoadSdk,
}) =&amp;gt; {
    const adSize = LevelPlayAdSize.BANNER
    const bannerAdRef = useRef&amp;lt;LevelPlayBannerAdViewMethods&amp;gt;(null);

    const listener: LevelPlayBannerAdViewListener = {
        onAdLoaded: (info: LevelPlayAdInfo) =&amp;gt; {
            console.log('✅ Banner loaded:', info)
        },
        onAdLoadFailed: (error) =&amp;gt; {
            console.log('❌ Banner load failed:', error);
            // setTimeout(() =&amp;gt; bannerAdRef.current?.loadAd(), 3000); // 3초 후 재시도
        },
        onAdDisplayed: (info: LevelPlayAdInfo) =&amp;gt; {
            console.log('  Banner displayed:', info)
        },
        onAdDisplayFailed: (adInfo: LevelPlayAdInfo, error: LevelPlayAdError) =&amp;gt; {
            // Implement your logic here
        },
        onAdClicked: (info: LevelPlayAdInfo) =&amp;gt; {
            console.log(' ️ Banner clicked:', info)
        },
        onAdExpanded: (adInfo: LevelPlayAdInfo) =&amp;gt; {
            // Implement your logic here
        },
        onAdCollapsed: (adInfo: LevelPlayAdInfo) =&amp;gt; {
            // Implement your logic here
        },
        onAdLeftApplication: (adInfo: LevelPlayAdInfo) =&amp;gt; {
            // Implement your logic here
        },
    };

    // Load ad
    const loadAd = useCallback(() =&amp;gt; {
        bannerAdRef.current?.loadAd();
    }, []);

    return (
        &amp;lt;View
            style={[
                styles.container,
                {
                    marginTop: scaleHeight(paramMarginTop),
                    marginBottom: scaleHeight(paramMarginBottom),
                    opacity: visible ? 1 : 0, // 렌더링 유지 + 가시성만 제어
                    height: visible ? undefined : 0,
                },
            ]}&amp;gt;

            &amp;lt;LevelPlayBannerAdView
                ref={bannerAdRef}
                adUnitId={BANNER_AD_UNIT_ID!}
                adSize={adSize}
                placementName={PLACEMENT_NAME}
                listener={listener}
                style={{ width: adSize.width, height: adSize.height, alignSelf: 'center' }}
            /&amp;gt;
        &amp;lt;/View&amp;gt;
    );
};

export default React.memo(LevelPlayBannerAd);

const styles = StyleSheet.create({
    container: {
        backgroundColor: 'transparent',
    },
    bannerAd: {
        width: 320,
        height: 50,
        alignSelf: 'center',
        position: 'absolute',
        zIndex: 10,
        bottom: 0,
    },
});&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;h4 data-ke-size=&quot;size20&quot;&gt;1.3. 최종&lt;/h4&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot;&gt;&lt;code&gt;/* eslint-disable react-native/no-inline-styles */
import { scaleHeight } from '@/utils';
import {
    LevelPlay,
    LevelPlayAdError,
    LevelPlayAdInfo,
    LevelPlayAdSize,
    LevelPlayBannerAdView,
    LevelPlayBannerAdViewListener,
    LevelPlayBannerAdViewMethods,
    LevelPlayConfiguration,
    LevelPlayInitError,
    LevelPlayInitListener,
    LevelPlayInitRequest,
} from 'ironsource-mediation';
import React, { useCallback, useEffect, useRef, useState } from 'react';
import { Platform, View, StyleSheet } from 'react-native';

interface AdmobBannerAdProps {
    paramMarginTop?: number;
    paramMarginBottom?: number;
    visible?: boolean;
    isLoadSdk?: boolean; // ✅ init 성공 후 true
}

const BANNER_AD_UNIT_ID = Platform.select({
    android: '1233',
    ios: '33444',
});

const APP_KEY = Platform.select({
    android: '55566',
    ios: '6667',
    default: '',
});

const PLACEMENT_NAME = 'BANNER';

const LevelPlayBannerAd: React.FC&amp;lt;AdmobBannerAdProps&amp;gt; = ({
    paramMarginTop = 6,
    paramMarginBottom = 6,
    visible = true,
    isLoadSdk,
}) =&amp;gt; {

    const adSize = LevelPlayAdSize.BANNER
    const bannerAdRef = useRef&amp;lt;LevelPlayBannerAdViewMethods&amp;gt;(null);

    useEffect(() =&amp;gt; {
        initIronSourceSDK();
    }, []);

    const initIronSourceSDK = async () =&amp;gt; {
        if (!APP_KEY) {
            console.error('❌ No APP_KEY found, skipping init');
            return;
        }
        try {
            const initRequest = LevelPlayInitRequest.builder(APP_KEY).build();
            const initListener: LevelPlayInitListener = {
                onInitSuccess: (config: LevelPlayConfiguration) =&amp;gt; {
                    console.log('✅ IronSource Init Success:', config);
                    bannerAdRef.current?.loadAd();
                },
                onInitFailed: (error: LevelPlayInitError) =&amp;gt; {
                    console.error('❌ IronSource Init Failed:', error);
                },
            };
            // 디버깅용
            // LevelPlay.setAdaptersDebug(true);

            // App mount 안정화 후 실행
            requestAnimationFrame(() =&amp;gt; {
                LevelPlay.init(initRequest, initListener)
                    .catch((e) =&amp;gt; {
                        console.error('  Init Exception:', e);
                    });
            });
        } catch (e) {
            console.error('  JS Exception before init:', e);
        }
    };

    const listener: LevelPlayBannerAdViewListener = {
        onAdLoaded: (info: LevelPlayAdInfo) =&amp;gt; {
            console.log('✅ Banner loaded:', info)
        },
        onAdLoadFailed: (error) =&amp;gt; {
            console.log('❌ Banner load failed:', error);
            // setTimeout(() =&amp;gt; bannerAdRef.current?.loadAd(), 3000); // 3초 후 재시도
        },
        onAdDisplayed: (info: LevelPlayAdInfo) =&amp;gt; {
            console.log('  Banner displayed:', info)
        },
        onAdDisplayFailed: (adInfo: LevelPlayAdInfo, error: LevelPlayAdError) =&amp;gt; {
            // Implement your logic here
        },
        onAdClicked: (info: LevelPlayAdInfo) =&amp;gt; {
            console.log(' ️ Banner clicked:', info)
        },
        onAdExpanded: (adInfo: LevelPlayAdInfo) =&amp;gt; {
            // Implement your logic here
        },
        onAdCollapsed: (adInfo: LevelPlayAdInfo) =&amp;gt; {
            // Implement your logic here
        },
        onAdLeftApplication: (adInfo: LevelPlayAdInfo) =&amp;gt; {
            // Implement your logic here
        },
    };

    // Load ad
    const loadAd = useCallback(() =&amp;gt; {
        bannerAdRef.current?.loadAd();
    }, []);

    return (
        &amp;lt;View
            style={[
                styles.container,
                {
                    marginTop: scaleHeight(paramMarginTop),
                    marginBottom: scaleHeight(paramMarginBottom),
                    opacity: visible ? 1 : 0, // 렌더링 유지 + 가시성만 제어
                    height: visible ? undefined : 0,
                },
            ]}&amp;gt;

            &amp;lt;LevelPlayBannerAdView
                ref={bannerAdRef}
                adUnitId={BANNER_AD_UNIT_ID!}
                adSize={adSize}
                placementName={PLACEMENT_NAME}
                listener={listener}
                style={{ width: adSize.width, height: adSize.height, alignSelf: 'center' }}
            /&amp;gt;
        &amp;lt;/View&amp;gt;
    );
};

export default React.memo(LevelPlayBannerAd);

const styles = StyleSheet.create({
    container: {
        backgroundColor: 'transparent',
    },
    bannerAd: {
        width: 320,
        height: 50,
        alignSelf: 'center',
        position: 'absolute',
        zIndex: 10,
        bottom: 0,
    },
});&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;h4 data-ke-size=&quot;size20&quot;&gt;1.4. 결과 화면 확인&lt;/h4&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;style3&quot;&gt;&lt;b&gt;  결과 화면 확인&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 아래와 같이 배너화면이 출력이 됨을 확인하였습니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1205&quot; data-origin-height=&quot;2448&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/eusEWO/dJMcafZcMOW/MNYGWR9jk9NkItALCsNSxK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/eusEWO/dJMcafZcMOW/MNYGWR9jk9NkItALCsNSxK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/eusEWO/dJMcafZcMOW/MNYGWR9jk9NkItALCsNSxK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FeusEWO%2FdJMcafZcMOW%2FMNYGWR9jk9NkItALCsNSxK%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;1205&quot; height=&quot;2448&quot; data-origin-width=&quot;1205&quot; data-origin-height=&quot;2448&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;h3 data-ke-size=&quot;size23&quot;&gt;2. Interstitial 광고&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  Interstitial 광고&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- Interstitial 광고에서 동일하게 각각 &amp;lsquo;앱의 키&amp;rsquo;와 &amp;lsquo;배너 UNIT ID&amp;rsquo;, PLACEMENT 값을 공식사이트에서 확인하여서 입력하여야 합니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;figure id=&quot;og_1762246164816&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;Interstitial integration for React Native &amp;bull; Unity Grow &amp;bull; Unity Docs&quot; data-og-description=&quot;Integrate interstitial ads in your React Native app by initializing the SDK, creating ad units, and setting up event listeners to manage ad loading and display events.&quot; data-og-host=&quot;docs.unity.com&quot; data-og-source-url=&quot;https://docs.unity.com/ko-kr/grow/levelplay/sdk/react/interstitial-integration&quot; data-og-url=&quot;https://docs.unity.com/ko-kr/grow/levelplay/sdk/react/interstitial-integration&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bgyMWH/hyZM32R6f1/UhEFIyuBCt3Mp5s6dp84r1/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/zldKY/hyZNcer5es/9Lt5mIVZFORjysq4wDuw6K/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630&quot;&gt;&lt;a href=&quot;https://docs.unity.com/ko-kr/grow/levelplay/sdk/react/interstitial-integration&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://docs.unity.com/ko-kr/grow/levelplay/sdk/react/interstitial-integration&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bgyMWH/hyZM32R6f1/UhEFIyuBCt3Mp5s6dp84r1/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/zldKY/hyZNcer5es/9Lt5mIVZFORjysq4wDuw6K/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Interstitial integration for React Native &amp;bull; Unity Grow &amp;bull; Unity Docs&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Integrate interstitial ads in your React Native app by initializing the SDK, creating ad units, and setting up event listeners to manage ad loading and display events.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;docs.unity.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;h4 data-ke-size=&quot;size20&quot;&gt;2.1. SDK 로드&lt;/h4&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;style3&quot;&gt;&lt;b&gt;  SDK 로드&lt;br /&gt;&lt;/b&gt;&lt;br /&gt;- useEffect를 통해서 해당 화면이 수행될 때 initIronSourceSDK 함수를 호출합니다.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;1. APP_KEY 값을 기반으로 initRequest build를 구성합니다.&lt;/b&gt;&lt;br /&gt;&lt;b&gt;2. initListener:&lt;/b&gt; SDK의 리스너를 등록합니다.&lt;br /&gt;- onInitSuccess : SDK 로드를 성공적으로 해올 때, 수행이 되는 함수입니다.&lt;br /&gt;- onInitFailed : SDK 로드를 실패하였을 때, 수행되는 함수입니다.&lt;br /&gt;LevelPlay.init() : 최종 앱에 대한 키값과 리스너를 함께 SDK를 초기화하는 init() 함수입니다.&lt;br /&gt;- 최종 SDK 로드가 완료되면 onInitSuccess 리스너가 수행이 됩니다.&lt;br /&gt;&lt;br /&gt;- 해당 시점에 배너를 로드해 옵니다.&lt;/blockquote&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;import { scaledSize, scaleHeight, scaleWidth } from '@/utils';
import {
	LevelPlay,
	LevelPlayAdError,
	LevelPlayAdInfo,
	LevelPlayConfiguration,
	LevelPlayInitError,
	LevelPlayInitListener,
	LevelPlayInitRequest,
	LevelPlayInterstitialAd,
	LevelPlayInterstitialAdListener,
} from 'ironsource-mediation';
import { useEffect, useState } from 'react';
import { ActivityIndicator, Platform, StyleSheet, Text, View } from 'react-native';

type Props = {
	onAdClosed?: () =&amp;gt; void;
};

const APP_KEY = Platform.select({
	android: '234234',
	ios: '2342345',
	default: '',
});

const PLACEMENT_NAME = 'FRONT';

const LevelPlayFrontAd = ({ onAdClosed }: Props) =&amp;gt; {
	const [interstitialAd, setInterstitialAd] = useState&amp;lt;LevelPlayInterstitialAd&amp;gt;(
		new LevelPlayInterstitialAd(BANNER_FRONT_UNIT_ID!),
	);
	const [isVisible, setIsVisible] = useState(true); // 처음에는 보이도록

	useEffect(() =&amp;gt; {
		initIronSourceSDK();
	}, []);

	const initIronSourceSDK = async () =&amp;gt; {
		try {
			const initRequest = LevelPlayInitRequest.builder(APP_KEY!).build();

			const initListener: LevelPlayInitListener = {
				onInitSuccess: (config: LevelPlayConfiguration) =&amp;gt; {
					console.log('✅ IronSource Init Success:', config);
					interstitialAd.setListener(listener);
					interstitialAd.loadAd();
				},
				onInitFailed: (error: LevelPlayInitError) =&amp;gt; {
					console.error('❌ IronSource Init Failed:', error);
				},
			};

			//   테스트 광고 활성화
			// LevelPlay.setMetaData('is_test_suite', ['enable']);
			// LevelPlay.setAdaptersDebug(true);

			// 2. LevelPlay.init 요청을 생성합니다&amp;nbsp;
			await LevelPlay.init(initRequest, initListener);

			//   필요하다면 Test Suite 실행 (네트워크 설정 확인용)
			// await LevelPlay.launchTestSuite();
		} catch (e) {
			console.error('  Init Exception:', e);
		}
	}

	return isVisible ? (
		&amp;lt;View style={styles.adOverlay}&amp;gt;
			&amp;lt;View style={styles.container}&amp;gt;
				&amp;lt;ActivityIndicator size=&quot;large&quot; color=&quot;#3498db&quot; /&amp;gt;
				&amp;lt;Text style={styles.loadingTxt}&amp;gt;광고를 준비 중이에요&amp;hellip;&amp;lt;/Text&amp;gt;
			&amp;lt;/View&amp;gt;
		&amp;lt;/View&amp;gt;
	) : null;
};
export default LevelPlayFrontAd;

const styles = StyleSheet.create({
	container: {
		padding: scaleHeight(24),
		backgroundColor: '#fff',
		borderRadius: scaleWidth(20),
		alignItems: 'center',
		justifyContent: 'center',
		shadowColor: '#000',
		shadowOpacity: 0.1,
		shadowOffset: { width: 0, height: 2 },
		shadowRadius: 6,
	},

	adOverlay: {
		position: 'absolute',
		top: 0,
		left: 0,
		right: 0,
		bottom: 0,
		backgroundColor: 'rgba(0,0,0,0.4)', // 더 어둡게
		justifyContent: 'center',
		alignItems: 'center',
		zIndex: 999,
	},

	loadingTxt: {
		marginTop: scaleHeight(12),
		fontSize: scaledSize(16),
		color: '#2c3e50',
		fontWeight: '600',
	},

	subTxt: {
		marginTop: scaleHeight(4),
		fontSize: scaledSize(13),
		color: '#7f8c8d',
	},

	mascotImage: {
		width: scaleWidth(80),
		height: scaleWidth(80),
		marginBottom: scaleHeight(16),
		opacity: 0.9,
	},
});

&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;h4 data-ke-size=&quot;size20&quot;&gt;1.2. Interstitial 광고 로드&lt;/h4&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;style3&quot;&gt;&lt;b&gt;  Interstitial&amp;nbsp;광고 로드&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;- SDK가 성공적으로 수행이 된 이후에 Interstitial 광고가 로드되어야 합니다.&lt;br /&gt;&lt;br /&gt;- onInitSuccess &amp;rarr; interstitialAd.setListener() &amp;rarr; interstitialAd.loadAd() &amp;rarr; interstitialAd.showAd(PLACEMENT_NAME); 이와 같은 과정으로 수행이 됩니다.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;1. interstitialAd.setListener&lt;/b&gt;&lt;br /&gt;- interstitial 광고의 리스너를 등록합니다. onAdLoaded() &amp;rarr; onAdDisplayed() 함수 순으로 수행이 됩니다.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;2. interstitialAd.loadAd()&lt;/b&gt;&lt;br /&gt;- interstitial 광고를 로드해 옵니다. 광고가 로드되면 리스너가 수행이 되며, 결과 값을 반환해 줍니다.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;3. onAdLoaded()&lt;/b&gt;&lt;br /&gt;- 리스너의 광고가 로드가 되면, 이때 interstitialAd.showAd(PLACEMENT_NAME) 함수를 호출하여 광고를 송출합니다.&lt;/blockquote&gt;
&lt;pre class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot;&gt;&lt;code&gt;import { scaledSize, scaleHeight, scaleWidth } from '@/utils';
import {
	LevelPlay,
	LevelPlayAdError,
	LevelPlayAdInfo,
	LevelPlayConfiguration,
	LevelPlayInitError,
	LevelPlayInitListener,
	LevelPlayInitRequest,
	LevelPlayInterstitialAd,
	LevelPlayInterstitialAdListener,
} from 'ironsource-mediation';
import { useEffect, useState } from 'react';
import { ActivityIndicator, Platform, StyleSheet, Text, View } from 'react-native';

type Props = {
	onAdClosed?: () =&amp;gt; void;
};

const BANNER_FRONT_UNIT_ID = Platform.select({
	android: 'uzlf2e92cvhgm8x4',
	ios: 'n2q9mb4q8zri270x',
});

const APP_KEY = Platform.select({
	android: '23a5ec315',
	ios: '23a5fa935',
	default: '',
});

const PLACEMENT_NAME = 'FRONT';

const LevelPlayFrontAd = ({ onAdClosed }: Props) =&amp;gt; {
	const [interstitialAd, setInterstitialAd] = useState&amp;lt;LevelPlayInterstitialAd&amp;gt;(
		new LevelPlayInterstitialAd(BANNER_FRONT_UNIT_ID!),
	);
	const [isVisible, setIsVisible] = useState(true); // 처음에는 보이도록

	useEffect(() =&amp;gt; {
		initIronSourceSDK();
	}, []);

	const initIronSourceSDK = async () =&amp;gt; {
		try {
			const initRequest = LevelPlayInitRequest.builder(APP_KEY!).build();

			const initListener: LevelPlayInitListener = {
				onInitSuccess: (config: LevelPlayConfiguration) =&amp;gt; {
					console.log('✅ IronSource Init Success:', config);
					interstitialAd.setListener(listener);
					interstitialAd.loadAd();
				},
				onInitFailed: (error: LevelPlayInitError) =&amp;gt; {
					console.error('❌ IronSource Init Failed:', error);
				},
			};

			//   테스트 광고 활성화
			// LevelPlay.setMetaData('is_test_suite', ['enable']);
			// LevelPlay.setAdaptersDebug(true);

			// 2. LevelPlay.init 요청을 생성합니다&amp;nbsp;
			await LevelPlay.init(initRequest, initListener);

			//   필요하다면 Test Suite 실행 (네트워크 설정 확인용)
			// await LevelPlay.launchTestSuite();
		} catch (e) {
			console.error('  Init Exception:', e);
		}
	};

	const listener: LevelPlayInterstitialAdListener = {
		onAdLoaded: (adInfo: LevelPlayAdInfo) =&amp;gt; {
			console.log('✅ Interstitial Loaded:', adInfo);
			interstitialAd.showAd(PLACEMENT_NAME);
		},
		onAdLoadFailed: (error: LevelPlayAdError) =&amp;gt; {
			console.log('❌ Interstitial Load Fail:', error);
			setIsVisible(false); // 광고 실패 &amp;rarr; 오버레이 닫기
			onAdClosed?.(); // 외부 콜백 호출
		},
		onAdDisplayed: (adInfo: LevelPlayAdInfo) =&amp;gt; {
			console.log('✅ Interstitial Displayed:', adInfo);
		},
		onAdDisplayFailed: (error: LevelPlayAdError, adInfo: LevelPlayAdInfo) =&amp;gt; {
			console.log('❌ Interstitial Display Fail:', error);
			setIsVisible(false); // 표시 실패 &amp;rarr; 오버레이 닫기
			onAdClosed?.();
		},
		onAdClosed: (adInfo: LevelPlayAdInfo) =&amp;gt; {
			console.log('  Interstitial Closed:', adInfo);
			setIsVisible(false);
			onAdClosed?.();
		},
	};

	return isVisible ? (
		&amp;lt;View style={styles.adOverlay}&amp;gt;
			&amp;lt;View style={styles.container}&amp;gt;
				&amp;lt;ActivityIndicator size=&quot;large&quot; color=&quot;#3498db&quot; /&amp;gt;
				&amp;lt;Text style={styles.loadingTxt}&amp;gt;광고를 준비 중이에요&amp;hellip;&amp;lt;/Text&amp;gt;
			&amp;lt;/View&amp;gt;
		&amp;lt;/View&amp;gt;
	) : null;
};
export default LevelPlayFrontAd;

const styles = StyleSheet.create({
	container: {
		padding: scaleHeight(24),
		backgroundColor: '#fff',
		borderRadius: scaleWidth(20),
		alignItems: 'center',
		justifyContent: 'center',
		shadowColor: '#000',
		shadowOpacity: 0.1,
		shadowOffset: { width: 0, height: 2 },
		shadowRadius: 6,
	},

	adOverlay: {
		position: 'absolute',
		top: 0,
		left: 0,
		right: 0,
		bottom: 0,
		backgroundColor: 'rgba(0,0,0,0.4)', // 더 어둡게
		justifyContent: 'center',
		alignItems: 'center',
		zIndex: 999,
	},

	loadingTxt: {
		marginTop: scaleHeight(12),
		fontSize: scaledSize(16),
		color: '#2c3e50',
		fontWeight: '600',
	},

	subTxt: {
		marginTop: scaleHeight(4),
		fontSize: scaledSize(13),
		color: '#7f8c8d',
	},

	mascotImage: {
		width: scaleWidth(80),
		height: scaleWidth(80),
		marginBottom: scaleHeight(16),
		opacity: 0.9,
	},
});&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;h4 data-ke-size=&quot;size20&quot;&gt;2.3. 결과 화면&lt;/h4&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;style3&quot;&gt;&lt;b&gt;  결과 화면&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 아래와 같이 전면 광고가 출력이 됨을 확인하였습니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1205&quot; data-origin-height=&quot;2465&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bJgAFB/dJMcaihkbau/YBKsrWq6tftSBHWQOtwFP1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bJgAFB/dJMcaihkbau/YBKsrWq6tftSBHWQOtwFP1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bJgAFB/dJMcaihkbau/YBKsrWq6tftSBHWQOtwFP1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbJgAFB%2FdJMcaihkbau%2FYBKsrWq6tftSBHWQOtwFP1%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;1205&quot; height=&quot;2465&quot; data-origin-width=&quot;1205&quot; data-origin-height=&quot;2465&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 data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;오늘도&amp;nbsp;감사합니다.&amp;nbsp; &lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>React &amp;amp; React Native/라이브러리 활용</category>
      <category>google admob 대체 플랫폼</category>
      <category>IronSource React Native 적용기</category>
      <category>LevelPlay 광고 활용 방법</category>
      <category>Unity LevelPlay</category>
      <category>Unity LevelPlay react native</category>
      <category>Unity LevelPlay react native 광고 적용기</category>
      <category>Unity LevelPlay 광고</category>
      <category>Unity LevelPlay 광고 추가 방법</category>
      <category>모바일 광고 플랫폼</category>
      <category>애드몹 대체 플랫폼</category>
      <author>adjh54</author>
      <guid isPermaLink="true">https://adjh54.tistory.com/721</guid>
      <comments>https://adjh54.tistory.com/721#entry721comment</comments>
      <pubDate>Tue, 4 Nov 2025 20:30:17 +0900</pubDate>
    </item>
    <item>
      <title>[OpenSource] sonarQube 이해하기 -2: SAST/DAST, 종류</title>
      <link>https://adjh54.tistory.com/720</link>
      <description>&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;해당 글은 sonarQube의 이해를 돕기 위해 작성한 글이며, SAST/DAST와 sonarQube의 종류에 대해 알아봅니다&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;
&lt;figure contenteditable=&quot;false&quot; data-ke-type=&quot;emoticon&quot; data-ke-align=&quot;alignCenter&quot; data-emoticon-type=&quot;face&quot; data-emoticon-name=&quot;073&quot; data-emoticon-isanimation=&quot;false&quot; data-emoticon-src=&quot;https://t1.daumcdn.net/keditor/emoticon/face/large/073.png&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/keditor/emoticon/face/large/073.png&quot; width=&quot;80&quot; /&gt;&lt;/figure&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt; [참고]&lt;/b&gt; 아래의 글에서 이어집니다.&lt;/blockquote&gt;
&lt;figure id=&quot;og_1762240058082&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;[OpenSource] sonarQube 이해하기 -1 : 정의, 주요특징, 구성요소, 수행 프로세스&quot; data-og-description=&quot;해당 글에서는 코드분석도구 중 정적분석 도구인 SonarQube에 대해 이해를 돕기 위해 작성한 글입니다. 1) SonarQube  SonarQube- 소스 코드의 품질을 분석하고 관리하기 위한 오픈 소스 플랫폼입니다.-&quot; data-og-host=&quot;adjh54.tistory.com&quot; data-og-source-url=&quot;https://adjh54.tistory.com/381&quot; data-og-url=&quot;https://adjh54.tistory.com/381&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/MVgrB/hyZMUl99mF/UH73kGHiJINgKVikM321Kk/img.png?width=800&amp;amp;height=416&amp;amp;face=0_0_800_416,https://scrap.kakaocdn.net/dn/eyfYK/hyZMznziDq/q9c7NixDHKK00APkAU0Sek/img.png?width=800&amp;amp;height=416&amp;amp;face=0_0_800_416,https://scrap.kakaocdn.net/dn/cw0Uwf/hyZMHTtmMp/sADILPttnG1shafrkwB5uK/img.png?width=1306&amp;amp;height=701&amp;amp;face=0_0_1306_701&quot;&gt;&lt;a href=&quot;https://adjh54.tistory.com/381&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://adjh54.tistory.com/381&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/MVgrB/hyZMUl99mF/UH73kGHiJINgKVikM321Kk/img.png?width=800&amp;amp;height=416&amp;amp;face=0_0_800_416,https://scrap.kakaocdn.net/dn/eyfYK/hyZMznziDq/q9c7NixDHKK00APkAU0Sek/img.png?width=800&amp;amp;height=416&amp;amp;face=0_0_800_416,https://scrap.kakaocdn.net/dn/cw0Uwf/hyZMHTtmMp/sADILPttnG1shafrkwB5uK/img.png?width=1306&amp;amp;height=701&amp;amp;face=0_0_1306_701');&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;[OpenSource] sonarQube 이해하기 -1 : 정의, 주요특징, 구성요소, 수행 프로세스&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;해당 글에서는 코드분석도구 중 정적분석 도구인 SonarQube에 대해 이해를 돕기 위해 작성한 글입니다. 1) SonarQube  SonarQube- 소스 코드의 품질을 분석하고 관리하기 위한 오픈 소스 플랫폼입니다.-&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;adjh54.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;figure id=&quot;og_1762240070780&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;[Docker] Docker로 SonarQube 로컬 설치 및 검증 수행 방법&quot; data-og-description=&quot;해당 글에서는 MacOS 내에서 SonarQube Community를 Docker로 구성하여 로컬에 설치하고 검증하는 방법에 대해 알아봅니다.   [참고] Docker Desktop이 설치가 되었다는 가정하에 해당 글이 이어집니다. 설&quot; data-og-host=&quot;adjh54.tistory.com&quot; data-og-source-url=&quot;https://adjh54.tistory.com/370&quot; data-og-url=&quot;https://adjh54.tistory.com/370&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/wZsGq/hyZM1Yg8vu/YHEYhmnfU1UUkz2COQgo11/img.png?width=700&amp;amp;height=600&amp;amp;face=0_0_700_600,https://scrap.kakaocdn.net/dn/cA72EU/hyZMUzE9iV/1cJn4lEwENa8isSGHKoZkk/img.png?width=700&amp;amp;height=600&amp;amp;face=0_0_700_600,https://scrap.kakaocdn.net/dn/ctGFnH/hyZMG76ky7/q9lN9rFFrgDgOHhWbd6wO1/img.png?width=1909&amp;amp;height=821&amp;amp;face=0_0_1909_821&quot;&gt;&lt;a href=&quot;https://adjh54.tistory.com/370&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://adjh54.tistory.com/370&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/wZsGq/hyZM1Yg8vu/YHEYhmnfU1UUkz2COQgo11/img.png?width=700&amp;amp;height=600&amp;amp;face=0_0_700_600,https://scrap.kakaocdn.net/dn/cA72EU/hyZMUzE9iV/1cJn4lEwENa8isSGHKoZkk/img.png?width=700&amp;amp;height=600&amp;amp;face=0_0_700_600,https://scrap.kakaocdn.net/dn/ctGFnH/hyZMG76ky7/q9lN9rFFrgDgOHhWbd6wO1/img.png?width=1909&amp;amp;height=821&amp;amp;face=0_0_1909_821');&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;[Docker] Docker로 SonarQube 로컬 설치 및 검증 수행 방법&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;해당 글에서는 MacOS 내에서 SonarQube Community를 Docker로 구성하여 로컬에 설치하고 검증하는 방법에 대해 알아봅니다.   [참고] Docker Desktop이 설치가 되었다는 가정하에 해당 글이 이어집니다. 설&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;adjh54.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;h2 data-ke-size=&quot;size26&quot;&gt;1) 정적 애플리케이션 보안 도구(SAST: Static Application Security Testing), 동적 애플리케이션 보안 도구(DAST: Dynamic Application Security Testing)&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  정적 애플리케이션 보안 도구(SAST: Static Application Security Testing), 동적 애플리케이션 보안 도구(DAST: Dynamic Application Security Testing), 분류 정적 애플리케이션 보안 도구(SAST: Static Application Security Testing) 동적 애플리케이션 보안 도구(DAST: Dynamic Application Security Testing)&lt;/b&gt;&lt;/blockquote&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 146px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style2&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;&lt;b&gt;목적&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;&lt;b&gt;코드 품질, 보안 취약점 조기 발견&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;&lt;b&gt;보안 취약점 탐색&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;&lt;b&gt;실행 시점&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;코드를 실행하기 이전에 실행(빌드 전/ 개발 단계)&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;코드가 실행중인 단계에 실행(테스트/런타임 단계)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;&lt;b&gt;적용 단계&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;개발(Dev) / 빌드(CI) 단계&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;테스트(QA) / 배포 직전 또는 실제 서버(Pre-prod) 단계&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 40px;&quot;&gt;
&lt;td style=&quot;height: 40px;&quot;&gt;&lt;b&gt;주요 분석 범위&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 40px;&quot;&gt;코드 결함, 보안 규칙 위반, 취약한 함수 탐지&lt;/td&gt;
&lt;td style=&quot;height: 40px;&quot;&gt;SQL Injection, XSS, CSRF, 인증/인가 결함, 서버 설정 오류, 세션 취약점 등&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 36px;&quot;&gt;
&lt;td style=&quot;height: 36px;&quot;&gt;&lt;b&gt;주요 도구&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 36px;&quot;&gt;SonarQube, Sparrow SAST, IntelliJ Qodana, Semgrep, Github CodeQL&lt;/td&gt;
&lt;td style=&quot;height: 36px;&quot;&gt;Sparrow DAST&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;h3 data-ke-size=&quot;size23&quot;&gt;1. 정적 애플리케이션 보안 도구(SAST: Static Application Security Testing)&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  정적 애플리케이션 보안 도구(SAST: Static Application Security Testing)&lt;/b&gt;&lt;br /&gt;&lt;b&gt;- '소스 코드나 프로그램을 실행하지 않고 소스 코드를 검사하는 방법'입니다. 즉, 애플리케이션이 실행되기 이전에 실행하는 보안 취약점을 식별하는 방법입니다.&lt;/b&gt;&lt;br /&gt;- 이 접근 방식에서는 자동화된 도구나 버그, 보안 취약점 또는 코딩 표준에서 벗어난 항목 등 잠재적인 문제를 검사하는 도구를 의미합니다.&lt;/blockquote&gt;
&lt;figure id=&quot;og_1762239766546&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;What is Static Code Analysis? | JetBrains Qodana&quot; data-og-description=&quot;Discover what static code analysis is, how it improves code quality, and how to choose the right tool. Learn benefits, limitations, and best practices.&quot; data-og-host=&quot;www.jetbrains.com&quot; data-og-source-url=&quot;https://www.jetbrains.com/ko-kr/pages/static-code-analysis-guide/#what-is-static-code-analysis&quot; data-og-url=&quot;https://www.jetbrains.com/pages/static-code-analysis-guide/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bJownU/hyZMSolqIA/zjfa79R7xtX2qkdftk5oRK/img.png?width=1280&amp;amp;height=720&amp;amp;face=0_0_1280_720&quot;&gt;&lt;a href=&quot;https://www.jetbrains.com/ko-kr/pages/static-code-analysis-guide/#what-is-static-code-analysis&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.jetbrains.com/ko-kr/pages/static-code-analysis-guide/#what-is-static-code-analysis&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bJownU/hyZMSolqIA/zjfa79R7xtX2qkdftk5oRK/img.png?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;What is Static Code Analysis? | JetBrains Qodana&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Discover what static code analysis is, how it improves code quality, and how to choose the right tool. Learn benefits, limitations, and best practices.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.jetbrains.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;824&quot; data-origin-height=&quot;584&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/YVMSJ/dJMcahvWYKP/cO0imNvrgWWe07DsTL57vk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/YVMSJ/dJMcahvWYKP/cO0imNvrgWWe07DsTL57vk/img.png&quot; data-alt=&quot;https://www.wiz.io/ko-kr/academy/static-application-security-testing-sast&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/YVMSJ/dJMcahvWYKP/cO0imNvrgWWe07DsTL57vk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FYVMSJ%2FdJMcahvWYKP%2FcO0imNvrgWWe07DsTL57vk%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;824&quot; height=&quot;584&quot; data-origin-width=&quot;824&quot; data-origin-height=&quot;584&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://www.wiz.io/ko-kr/academy/static-application-security-testing-sast&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;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  Sparrow SAST를 이용한 웹 취약점 분석 예시&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;- 아래와 같이 코드를 실행시키면, Sparrow SAST에서 사전에 발생할 수 있는 취약점에 해당하는 항목들이 출력됩니다.&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;937&quot; data-origin-height=&quot;653&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/8SW95/dJMcaiayxYp/U1uhHNjLfegyfXUsMCvNb1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/8SW95/dJMcaiayxYp/U1uhHNjLfegyfXUsMCvNb1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/8SW95/dJMcaiayxYp/U1uhHNjLfegyfXUsMCvNb1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F8SW95%2FdJMcaiayxYp%2FU1uhHNjLfegyfXUsMCvNb1%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;937&quot; height=&quot;653&quot; data-origin-width=&quot;937&quot; data-origin-height=&quot;653&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1593&quot; data-origin-height=&quot;526&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mjuCG/dJMcaa4FfQU/DU4DgqafQQe4Ar7rhRILx1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mjuCG/dJMcaa4FfQU/DU4DgqafQQe4Ar7rhRILx1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mjuCG/dJMcaa4FfQU/DU4DgqafQQe4Ar7rhRILx1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmjuCG%2FdJMcaa4FfQU%2FDU4DgqafQQe4Ar7rhRILx1%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;1593&quot; height=&quot;526&quot; data-origin-width=&quot;1593&quot; data-origin-height=&quot;526&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  SonarQube를 이용한 웹 취약점 분석 예시&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 아래와 같이 코드를 실행시키면, SonarQube 사전에 발생할 수 있는 취약점에 해당하는 항목들이 출력됩니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;821&quot; data-origin-height=&quot;541&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c6s5if/dJMcahpbq8V/FLKSzKnSGOppUKrKK0ZQEk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c6s5if/dJMcahpbq8V/FLKSzKnSGOppUKrKK0ZQEk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c6s5if/dJMcahpbq8V/FLKSzKnSGOppUKrKK0ZQEk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc6s5if%2FdJMcahpbq8V%2FFLKSzKnSGOppUKrKK0ZQEk%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;821&quot; height=&quot;541&quot; data-origin-width=&quot;821&quot; data-origin-height=&quot;541&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;578&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dsOdcq/dJMcacuDUVw/cPs1rKoOj7LfW6uxPYrQs1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dsOdcq/dJMcacuDUVw/cPs1rKoOj7LfW6uxPYrQs1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dsOdcq/dJMcacuDUVw/cPs1rKoOj7LfW6uxPYrQs1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdsOdcq%2FdJMcacuDUVw%2FcPs1rKoOj7LfW6uxPYrQs1%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;1280&quot; height=&quot;578&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;578&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;h3 data-ke-size=&quot;size23&quot;&gt;2. 동적 애플리케이션 보안 도구(DAST: Dynamic Application Security Testing)&lt;/h3&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;style3&quot;&gt;&lt;b&gt;&amp;nbsp;  동적 애플리케이션 보안 도구(DAST: Dynamic Application Security Testing)&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- &amp;lsquo;런타임 단계&amp;rsquo;에서 소스 코드나 프로그램이 컴파일되어 런타임 단계에서 수행이 될 때 취약점을 검사를 하는 방법입니다.&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;- 이 단계에서는 메모리 사용, CPU 사용, 네트워크 트래픽 등 다양한 측면을 &amp;lsquo;보안 취약점을 분석&amp;rsquo;합니다. 실제 공격자처럼 침투하여서 문제를 찾는 도구로써 사용이 됩니다.&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;824&quot; data-origin-height=&quot;584&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cet25b/dJMcabCu7LT/hheoiKFuvgmksXUrsN8avK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cet25b/dJMcabCu7LT/hheoiKFuvgmksXUrsN8avK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cet25b/dJMcabCu7LT/hheoiKFuvgmksXUrsN8avK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcet25b%2FdJMcabCu7LT%2FhheoiKFuvgmksXUrsN8avK%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;824&quot; height=&quot;584&quot; data-origin-width=&quot;824&quot; data-origin-height=&quot;584&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;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;[더 알아보기]&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt; &amp;nbsp;성능/부하테스트에서 사용하는 Apache JMeter도 DAST가 되지 않을까?&lt;/b&gt;&lt;br /&gt;- DAST의 경우는 보안 취약점 탐지(Injection, XSS, 취약한 인증 등)를 탐지하게 이용이 되지만, JMeter의 경우는 성능 및 부하테스트를 위해서 이용이 됩니다. 그렇기에 목적성으로 볼 때, DAST는 아니고 DAST의 보조적인 도구가 될 수 있습니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2) SonarQube&lt;/h2&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;style3&quot;&gt;&lt;b&gt;  SonarQube&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 소스 코드의 품질 및 코드 보안을 분석하고 관리하기 위한 오픈소스 플랫폼입니다.&lt;/b&gt;&lt;br /&gt;- 소스 코드 정적 분석을 수행하여 코드의 버그, 취약점, 코드 스멜(품질이 낮은 코드)을 NIST SSDF, OWASP, CWE, STIG, CASA 등 일반적인 코드 보안 표준을 준수하여 코드를 탐지하고 보고서를 제공합니다. 이를 통해 개발자들은 코드 품질을 향상하고 유지 보수성을 향상할 수 있습니다.&lt;br /&gt;- SonarQube의 제품으로는 SonarQube Server, Cloud, IDE, Advanced Security, MCP Server 등이 있습니다.&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1168&quot; data-origin-height=&quot;678&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bOoKDV/dJMcafZcKYD/m5K98QyLgyb9wewesE6x81/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bOoKDV/dJMcafZcKYD/m5K98QyLgyb9wewesE6x81/img.png&quot; data-alt=&quot;https://docs.sonarsource.com/&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bOoKDV/dJMcafZcKYD/m5K98QyLgyb9wewesE6x81/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbOoKDV%2FdJMcafZcKYD%2Fm5K98QyLgyb9wewesE6x81%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;1168&quot; height=&quot;678&quot; data-origin-width=&quot;1168&quot; data-origin-height=&quot;678&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://docs.sonarsource.com/&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1197&quot; data-origin-height=&quot;378&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/HqKah/dJMcabicrO0/TkN0MKxsEUfAio7kazVHCk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/HqKah/dJMcabicrO0/TkN0MKxsEUfAio7kazVHCk/img.png&quot; data-alt=&quot;https://www.sonarsource.com/ko/products/sonarcloud/&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/HqKah/dJMcabicrO0/TkN0MKxsEUfAio7kazVHCk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FHqKah%2FdJMcabicrO0%2FTkN0MKxsEUfAio7kazVHCk%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;1197&quot; height=&quot;378&quot; data-origin-width=&quot;1197&quot; data-origin-height=&quot;378&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://www.sonarsource.com/ko/products/sonarcloud/&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;[ 더 알아보기 ]&lt;/b&gt;&lt;br /&gt;&lt;b&gt;&lt;br /&gt; &amp;nbsp;SonarQube는 어떤 기준으로 코드 보안을 탐지하는 것일까?&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;- NIST SSDF, OWASP, CWE, STIG, CASA 등에서 지정한 기준을 통해서 탐지를 수행합니다.&lt;/blockquote&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style2&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;구분&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;전체 명칭&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;주관 기관&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;주요 목적&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;핵심 내용 / 구성요소&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;활용분야&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;NIST SSDF&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;National Institute of Standards and Technology &amp;ndash; Secure Software Development Framework&lt;/td&gt;
&lt;td&gt;NIST (미국 국립표준기술연구소)&lt;/td&gt;
&lt;td&gt;안전한 소프트웨어 개발을 위한 표준 프로세스 제시&lt;/td&gt;
&lt;td&gt;- 보안 중심의 SDLC(Secure SDLC) 지침- 개발&amp;middot;검증&amp;middot;배포 단계별 보안 통제- 개발자&amp;middot;조직의 역할 명확화&lt;/td&gt;
&lt;td&gt;정부기관, 금융, 공공, 대기업의 소프트웨어 보안 정책 수립&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;OWASP&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Open Web Application Security Project&lt;/td&gt;
&lt;td&gt;OWASP 재단 (비영리 단체)&lt;/td&gt;
&lt;td&gt;웹 애플리케이션 보안 향상 및 개발자 교육&lt;/td&gt;
&lt;td&gt;- OWASP Top 10: 주요 웹 취약점 10대 항목- ASVS, MASVS 등 검증 표준 제공- 오픈소스 기반 도구 및 가이드 제공&lt;/td&gt;
&lt;td&gt;웹/모바일 앱 개발, 보안 테스트, 개발자 교육&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;CWE&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Common Weakness Enumeration&lt;/td&gt;
&lt;td&gt;MITRE (미국 비영리 연구기관)&lt;/td&gt;
&lt;td&gt;소프트웨어의 일반적 취약점 유형 분류 및 표준화&lt;/td&gt;
&lt;td&gt;- 소스코드 레벨 취약점의 공통 분류 체계- CWE-ID 기반으로 CWE&amp;rarr;CVE 매핑- CWE Top 25 매년 발표&lt;/td&gt;
&lt;td&gt;취약점 진단, 정적 분석 도구(SAST) 기준, 보안 교육&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;STIG&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Security Technical Implementation Guide&lt;/td&gt;
&lt;td&gt;DISA (미 국방정보시스템국)&lt;/td&gt;
&lt;td&gt;시스템 및 애플리케이션의 보안 설정 기준 제공&lt;/td&gt;
&lt;td&gt;- OS, DB, 네트워크, 앱 등 구성요소별 보안 설정 가이드- DoD 보안 규정 준수 목적- 구체적 기술 설정 지침 포함&lt;/td&gt;
&lt;td&gt;국방, 정부기관, 인프라 운영 환경의 보안 설정 준수&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;CASA&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Cloud Application Security Assessment (또는 Cloud &amp;amp; Application Security Architecture)&lt;/td&gt;
&lt;td&gt;주로 클라우드 보안 연합(CSA) 및 각 기관별 프레임워크에서 활용&lt;/td&gt;
&lt;td&gt;클라우드 환경의 애플리케이션 보안성 평가 프레임워크&lt;/td&gt;
&lt;td&gt;- 클라우드 애플리케이션 구성요소의 보안성 검증- 인증, 접근제어, 데이터 보호, 네트워크 정책 포함- DevSecOps 환경에서의 보안 기준 적용&lt;/td&gt;
&lt;td&gt;클라우드 서비스, SaaS 보안 검증, CSP 평가 및 인증&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;h3 data-ke-size=&quot;size23&quot;&gt;1. SonarQube 종류&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1.1. SonarQube Server : 자체 관리형&lt;/h4&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;style3&quot;&gt;&lt;b&gt;  SonarQube Server : 자체 관리형&lt;/b&gt;&lt;br /&gt;&lt;b&gt;&lt;br /&gt;- 온프레미스 또는 클라우드, 서버 등에 Docker 또는 Kubernetes를 통해 원하는 곳에 직접 배포를 할 수 있는 &amp;lsquo;자체 관리형&amp;rsquo;을 의미합니다.&lt;/b&gt;&lt;br /&gt;- 코드 품질 및 보안 검토를 자동화하고 실행 가능한 코드 인텔리전스를 제공하여 개발자가 더 나은 코드 개발에 집중하고 더 빠르게 작업할 수 있도록 지원합니다.&lt;br /&gt;- SonarQube IDE와 연결하여 코딩 정책을 준수하며 작업을 할 수 있습니다.&lt;/blockquote&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style2&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 15.9302%;&quot;&gt;&lt;b&gt;구분&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 25.5814%;&quot;&gt;&lt;b&gt;Developer Edition&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 30.4651%;&quot;&gt;&lt;b&gt;Enterprise Edition&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 27.907%;&quot;&gt;&lt;b&gt;Data Center Edition&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 15.9302%;&quot;&gt;&lt;b&gt;가격&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 25.5814%;&quot;&gt;연간 $720부터&lt;/td&gt;
&lt;td style=&quot;width: 30.4651%;&quot;&gt;영업팀 문의 (Talk to sales)&lt;/td&gt;
&lt;td style=&quot;width: 27.907%;&quot;&gt;영업팀 문의 (Talk to sales)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 15.9302%;&quot;&gt;&lt;b&gt;대상 규모&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 25.5814%;&quot;&gt;10만+ Lines of Code&lt;/td&gt;
&lt;td style=&quot;width: 30.4651%;&quot;&gt;100만+ Lines of Code&lt;/td&gt;
&lt;td style=&quot;width: 27.907%;&quot;&gt;2천만+ Lines of Code&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 15.9302%;&quot;&gt;&lt;b&gt;언어/프레임워크 지원&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 25.5814%;&quot;&gt;33개&lt;/td&gt;
&lt;td style=&quot;width: 30.4651%;&quot;&gt;39개&lt;/td&gt;
&lt;td style=&quot;width: 27.907%;&quot;&gt;39개&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 15.9302%;&quot;&gt;&lt;b&gt;지원 방식&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 25.5814%;&quot;&gt;상용 지원 가능 (Commercial support)&lt;/td&gt;
&lt;td style=&quot;width: 30.4651%;&quot;&gt;상용 지원 가능 (24/7 지원 가능)&lt;/td&gt;
&lt;td style=&quot;width: 27.907%;&quot;&gt;상용 지원 포함 (24/7 white-glove 지원)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 15.9302%;&quot;&gt;&lt;b&gt;주요 기능&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 25.5814%;&quot;&gt;- AI 생성 코드 자동 탐지&lt;br /&gt;- AI Code Assurance&lt;br /&gt;- 고급 버그 탐지(Advanced bug detection)&lt;br /&gt;- 비밀키 탐지(Secrets detection, 개선됨)&lt;/td&gt;
&lt;td style=&quot;width: 30.4651%;&quot;&gt;- Developer Edition의 모든 기능 포함&lt;br /&gt;- AI CodeFix&lt;br /&gt;- 세부 프로젝트 건강 리포트- 향상된 인사이트 및 성능&lt;/td&gt;
&lt;td style=&quot;width: 27.907%;&quot;&gt;- Enterprise Edition의 모든 기능 포함&lt;br /&gt;- 요청 시 Autoscaling 지원&lt;br /&gt;- 분산 환경 고성능 지원(High performance for distributed teams)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 15.9302%;&quot;&gt;&lt;b&gt;추천 용도&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 25.5814%;&quot;&gt;소규모 팀 / 단일 프로젝트&lt;/td&gt;
&lt;td style=&quot;width: 30.4651%;&quot;&gt;중~대규모 조직 / 다수의 코드베이스&lt;/td&gt;
&lt;td style=&quot;width: 27.907%;&quot;&gt;대기업 / 고가용성&amp;middot;확장성 필요 조직&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 15.9302%;&quot;&gt;&lt;b&gt;기타 제공&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 25.5814%;&quot;&gt;무료 체험(Free trial) 제공&lt;/td&gt;
&lt;td style=&quot;width: 30.4651%;&quot;&gt;무료 체험 요청 가능&lt;/td&gt;
&lt;td style=&quot;width: 27.907%;&quot;&gt;데모 요청 가능(Request a demo)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;928&quot; data-origin-height=&quot;829&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c17h6B/dJMcai2HfN1/tmY252JvXktaUFgQ0fqDo0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c17h6B/dJMcai2HfN1/tmY252JvXktaUFgQ0fqDo0/img.png&quot; data-alt=&quot;https://www.sonarsource.com/products/sonarqube/server/&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c17h6B/dJMcai2HfN1/tmY252JvXktaUFgQ0fqDo0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc17h6B%2FdJMcai2HfN1%2FtmY252JvXktaUFgQ0fqDo0%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;928&quot; height=&quot;829&quot; data-origin-width=&quot;928&quot; data-origin-height=&quot;829&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://www.sonarsource.com/products/sonarqube/server/&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  [참고]&lt;/b&gt; Docker를 이용한 SonarQube 로컬 설치 및 검증 수행&lt;/blockquote&gt;
&lt;figure id=&quot;og_1762240315429&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;[Docker] Docker로 SonarQube 로컬 설치 및 검증 수행 방법&quot; data-og-description=&quot;해당 글에서는 MacOS 내에서 SonarQube Community를 Docker로 구성하여 로컬에 설치하고 검증하는 방법에 대해 알아봅니다.   [참고] Docker Desktop이 설치가 되었다는 가정하에 해당 글이 이어집니다. 설&quot; data-og-host=&quot;adjh54.tistory.com&quot; data-og-source-url=&quot;https://adjh54.tistory.com/370&quot; data-og-url=&quot;https://adjh54.tistory.com/370&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/wZsGq/hyZM1Yg8vu/YHEYhmnfU1UUkz2COQgo11/img.png?width=700&amp;amp;height=600&amp;amp;face=0_0_700_600,https://scrap.kakaocdn.net/dn/cA72EU/hyZMUzE9iV/1cJn4lEwENa8isSGHKoZkk/img.png?width=700&amp;amp;height=600&amp;amp;face=0_0_700_600,https://scrap.kakaocdn.net/dn/ctGFnH/hyZMG76ky7/q9lN9rFFrgDgOHhWbd6wO1/img.png?width=1909&amp;amp;height=821&amp;amp;face=0_0_1909_821&quot;&gt;&lt;a href=&quot;https://adjh54.tistory.com/370&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://adjh54.tistory.com/370&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/wZsGq/hyZM1Yg8vu/YHEYhmnfU1UUkz2COQgo11/img.png?width=700&amp;amp;height=600&amp;amp;face=0_0_700_600,https://scrap.kakaocdn.net/dn/cA72EU/hyZMUzE9iV/1cJn4lEwENa8isSGHKoZkk/img.png?width=700&amp;amp;height=600&amp;amp;face=0_0_700_600,https://scrap.kakaocdn.net/dn/ctGFnH/hyZMG76ky7/q9lN9rFFrgDgOHhWbd6wO1/img.png?width=1909&amp;amp;height=821&amp;amp;face=0_0_1909_821');&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;[Docker] Docker로 SonarQube 로컬 설치 및 검증 수행 방법&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;해당 글에서는 MacOS 내에서 SonarQube Community를 Docker로 구성하여 로컬에 설치하고 검증하는 방법에 대해 알아봅니다.   [참고] Docker Desktop이 설치가 되었다는 가정하에 해당 글이 이어집니다. 설&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;adjh54.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;h4 data-ke-size=&quot;size20&quot;&gt;1.2. SonarQube Cloud : SasS&lt;/h4&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;style3&quot;&gt;&lt;b&gt;  SonarQube Cloud&lt;/b&gt;&lt;br /&gt;&lt;b&gt;&lt;br /&gt;- SonarQube 서버를 별도로 설치 및 구성하지 않고, SonarQube Cloud를 활용한 &amp;lsquo;클라우드 기반형&amp;rsquo;을 의미합니다.&lt;/b&gt;&lt;br /&gt;- 동일하게 코드 품질 및 보안 검토를 자동화하고 실행 가능한 코드 인텔리전스를 제공하여 개발자가 더 나은 코드 개발에 집중하고 더 빠르게 작업할 수 있도록 지원합니다.&lt;br /&gt;- SonarQube IDE와 연결하여 코딩 정책을 준수하며 작업을 할 수 있습니다.&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;937&quot; data-origin-height=&quot;782&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bE7V4h/dJMcajmZ3PK/bxcuBketoiMKLSTI538pa0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bE7V4h/dJMcajmZ3PK/bxcuBketoiMKLSTI538pa0/img.png&quot; data-alt=&quot;https://www.sonarsource.com/products/sonarcloud/&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bE7V4h/dJMcajmZ3PK/bxcuBketoiMKLSTI538pa0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbE7V4h%2FdJMcajmZ3PK%2FbxcuBketoiMKLSTI538pa0%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;937&quot; height=&quot;782&quot; data-origin-width=&quot;937&quot; data-origin-height=&quot;782&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://www.sonarsource.com/products/sonarcloud/&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;[더 알아보기]&lt;/b&gt; &lt;br /&gt;&lt;b&gt;  SonarQube의 LOC(Lines of Code, 코드 줄 수)는 어떻게 계산이 될까?&lt;/b&gt; &lt;br /&gt;&lt;br /&gt;&lt;b&gt;- LOC(Lines of Code, 코드 줄 수)의 경우는 &amp;lsquo;소스 코드에서 실제 로직이 담긴 코드 줄&amp;rsquo;을 의미합니다. 이는 &amp;lsquo;프로젝트 단위&amp;rsquo;로 LOC가 계산이 됩니다.&lt;/b&gt; &lt;br /&gt;&lt;br /&gt;&lt;b&gt;[예시 1]&lt;/b&gt; &lt;br /&gt;- 6K LOC의 프로젝트가 있다고 가정하는데, 한 달 동안 100번을 분석하는 경우 청구 금액은 6K로 계산이 됩니다.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;[예시 2]&lt;/b&gt; &lt;br /&gt;- 10K LOC의 한계가 있다고 하면, 기간 동안 A 프로젝트에서 7K, B 프로젝트에서 3K를 사용할 수 있습니다. 그리고 모두 지우고 A, B, C에 각각 3K로 할당하여서 이용할 수 있습니다. 일종의 하드 디스크와 같은 개념입니다. &lt;br /&gt;&lt;br /&gt;- LOC의 포함이 제외된 항목은 빈 줄은 LOC 계산에서 제외가 됩니다 주석은 LOC 계산에서 제외가 됩니다 프로젝트 설정 &amp;gt; 분석대상에서 제외한 파일에 대해서는 LOC 계산에서 제외할 수 있습니다.&lt;/blockquote&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;1204&quot; data-origin-height=&quot;1614&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bpzLB2/dJMcaksFWmi/mq2JrMjOGSkSqnBK54K4fK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bpzLB2/dJMcaksFWmi/mq2JrMjOGSkSqnBK54K4fK/img.png&quot; data-alt=&quot;https://www.sonarsource.com/ko/plans-and-pricing/&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bpzLB2/dJMcaksFWmi/mq2JrMjOGSkSqnBK54K4fK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbpzLB2%2FdJMcaksFWmi%2Fmq2JrMjOGSkSqnBK54K4fK%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;1204&quot; height=&quot;1614&quot; data-origin-width=&quot;1204&quot; data-origin-height=&quot;1614&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://www.sonarsource.com/ko/plans-and-pricing/&lt;/figcaption&gt;
&lt;/figure&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;1.3. SonarQube IDE : SonarLint&lt;/h4&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;style3&quot;&gt;&lt;b&gt;  SonarQube IDE : SonarLint&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 구성한 SonarQube로 접속하여서 프로젝트 내에서 컴파일 시점에 문제를 확인할 수 있습니다. 이는 IDE툴 내의 마켓플레이스에서 다운로드를 하여서 바로 이용할 수 있는 무료 플러그인입니다.&lt;/b&gt;&lt;br /&gt;- SonarQube Server나 SonarQube Cloud에 구성된 SonarQube를 연결하여서 이용하는 구조입니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  아래와 같이, IDE 툴을 이용하는 중에 실시간으로 SonarQube의 취약점에 대해 보여줍니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;362&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/EQQ96/dJMcafkBdJw/VaHwR357X9evfA27DCuROK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/EQQ96/dJMcafkBdJw/VaHwR357X9evfA27DCuROK/img.png&quot; data-alt=&quot;https://www.sonarsource.com/products/sonarlint/&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/EQQ96/dJMcafkBdJw/VaHwR357X9evfA27DCuROK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FEQQ96%2FdJMcafkBdJw%2FVaHwR357X9evfA27DCuROK%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;1280&quot; height=&quot;362&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;362&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://www.sonarsource.com/products/sonarlint/&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  [참고]&lt;/b&gt; IntelliJ의 SonarQube Plug-in&lt;/blockquote&gt;
&lt;figure id=&quot;og_1762240577358&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;SonarQube for IDE - IntelliJ IDEs Plugin | Marketplace&quot; data-og-description=&quot;SonarQube for IDE (formerly SonarLint) by Sonar is a free, sophisticated static analysis tool that enhances both your code quality and security. Start analyzing your...&quot; data-og-host=&quot;plugins.jetbrains.com&quot; data-og-source-url=&quot;https://plugins.jetbrains.com/plugin/7973-sonarqube-for-ide&quot; data-og-url=&quot;https://plugins.jetbrains.com/plugin/7973-sonarqube-for-ide&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/WqXic/hyZMNHje1N/NmmvYIQyW7KjIHrDzpZ5hK/img.png?width=600&amp;amp;height=600&amp;amp;face=0_0_600_600,https://scrap.kakaocdn.net/dn/5Wuqo/hyZNbs2JCN/bBYOyOPyDwEiR7wUK6F0Ak/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/7973-sonarqube-for-ide&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://plugins.jetbrains.com/plugin/7973-sonarqube-for-ide&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/WqXic/hyZMNHje1N/NmmvYIQyW7KjIHrDzpZ5hK/img.png?width=600&amp;amp;height=600&amp;amp;face=0_0_600_600,https://scrap.kakaocdn.net/dn/5Wuqo/hyZNbs2JCN/bBYOyOPyDwEiR7wUK6F0Ak/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;SonarQube for IDE - IntelliJ IDEs Plugin | Marketplace&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;SonarQube for IDE (formerly SonarLint) by Sonar is a free, sophisticated static analysis tool that enhances both your code quality and security. Start analyzing your...&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;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  [참고]&lt;/b&gt; IntelliJ에서 SonarQube/SonarLint 플러그인으로 이용하는 방법&lt;/blockquote&gt;
&lt;figure id=&quot;og_1762240636135&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;[IntelliJ] IntelliJ에서 SonarQube/SonarLint 연결방법&quot; data-og-description=&quot;해당 글에서는 IntelliJ에서 구성된 SonarQube 연결하는 방법에 대해 알아봅니다.1) 해당 작업이 필요한 이유   해당 작업이 필요한 이유- IntelliJ 내에서 SonarLint로 컴파일 시점에 문제점을 찾고 해결&quot; data-og-host=&quot;adjh54.tistory.com&quot; data-og-source-url=&quot;https://adjh54.tistory.com/340&quot; data-og-url=&quot;https://adjh54.tistory.com/340&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bfbPUF/hyZM41Lh0Y/tL2QidPbJdQbsD7RKZQCCK/img.png?width=800&amp;amp;height=720&amp;amp;face=0_0_800_720,https://scrap.kakaocdn.net/dn/bpNVdh/hyZMRbRR9h/wkeaeyc6loK9DuhWbZH3r1/img.png?width=800&amp;amp;height=720&amp;amp;face=0_0_800_720,https://scrap.kakaocdn.net/dn/fE6lM/hyZM39B4wI/nde6pbu9JgvJW4EIFCJkF1/img.png?width=1601&amp;amp;height=768&amp;amp;face=0_0_1601_768&quot;&gt;&lt;a href=&quot;https://adjh54.tistory.com/340&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://adjh54.tistory.com/340&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bfbPUF/hyZM41Lh0Y/tL2QidPbJdQbsD7RKZQCCK/img.png?width=800&amp;amp;height=720&amp;amp;face=0_0_800_720,https://scrap.kakaocdn.net/dn/bpNVdh/hyZMRbRR9h/wkeaeyc6loK9DuhWbZH3r1/img.png?width=800&amp;amp;height=720&amp;amp;face=0_0_800_720,https://scrap.kakaocdn.net/dn/fE6lM/hyZM39B4wI/nde6pbu9JgvJW4EIFCJkF1/img.png?width=1601&amp;amp;height=768&amp;amp;face=0_0_1601_768');&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;[IntelliJ] IntelliJ에서 SonarQube/SonarLint 연결방법&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;해당 글에서는 IntelliJ에서 구성된 SonarQube 연결하는 방법에 대해 알아봅니다.1) 해당 작업이 필요한 이유   해당 작업이 필요한 이유- IntelliJ 내에서 SonarLint로 컴파일 시점에 문제점을 찾고 해결&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;adjh54.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;h3 data-ke-size=&quot;size23&quot;&gt;2. SonarQube 장점&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2.1. 잠재적인 문제를 포착, 코드 품질 향상&lt;/h4&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;style3&quot;&gt;&lt;b&gt;  잠재적인 문제를 포착, 코드 품질 향상&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 정적 코드 분석을 이용하면 문제를 조기에 잠재적인 문제를 포착하고 스타일 일관성을 유지하며 코드 품질을 향상할 수 있습니다.&lt;/b&gt;&lt;br /&gt;- 예를 들어 파일을 닫는 것을 잊거나 새 변수를 잘못 지정한 경우, 정적 분석 도구는 문제가 발생하기 전에 이러한 문제를 플래그로 표시합니다.&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1874&quot; data-origin-height=&quot;908&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/TNMCT/dJMcafdPCH1/gxVjGhW129QBtyy8P9Cxn0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/TNMCT/dJMcafdPCH1/gxVjGhW129QBtyy8P9Cxn0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/TNMCT/dJMcafdPCH1/gxVjGhW129QBtyy8P9Cxn0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FTNMCT%2FdJMcafdPCH1%2FgxVjGhW129QBtyy8P9Cxn0%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;1874&quot; height=&quot;908&quot; data-origin-width=&quot;1874&quot; data-origin-height=&quot;908&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;h4 data-ke-size=&quot;size20&quot;&gt;2.2. 사용자 별로 커스텀 구성이 가능함.&lt;/h4&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;style3&quot;&gt;&lt;b&gt;  사용자 별로 커스텀 구성이 가능함.&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 정적 분석으로 단지 버그를 찾는 것이 아니라, 사용자가 의도한 대로 제품이나 조직이 성장함에 따라서 확장 가능한 코드 인프라를 만드는데 도움이 됩니다.&lt;/b&gt;&lt;br /&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;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  아래와 같이 규칙으로 Rules를 정하여서 필요한 취약점 탐색을 취하고 불필요한 경우는 이를 제외할 수 있어서 사용자 별로 커스텀 환경을 이용할 수 있습니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;583&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cxq63u/dJMcacOWFmp/qMDK47sId3U9z1gPvkOly0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cxq63u/dJMcacOWFmp/qMDK47sId3U9z1gPvkOly0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cxq63u/dJMcacOWFmp/qMDK47sId3U9z1gPvkOly0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcxq63u%2FdJMcacOWFmp%2FqMDK47sId3U9z1gPvkOly0%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;1280&quot; height=&quot;583&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;583&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;h4 data-ke-size=&quot;size20&quot;&gt;2.3. 지속적인 통합(Continuous Integration)&lt;/h4&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;style3&quot;&gt;&lt;b&gt;  지속적인 통합(Continuous Integration)&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 정적 분석도구로 CI/CD 파이프라인에 직접 통합을 하면, 모든 커밋의 코딩 오류 표준을 자동으로 검사하게 되므로, 코드 품질을 높이고 유지관리하는데 도움을 줍니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;930&quot; data-origin-height=&quot;570&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/HyzlV/dJMcadAjMG2/r9PHADAZOJ2IuwcjiTPLL0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/HyzlV/dJMcadAjMG2/r9PHADAZOJ2IuwcjiTPLL0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/HyzlV/dJMcadAjMG2/r9PHADAZOJ2IuwcjiTPLL0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FHyzlV%2FdJMcadAjMG2%2Fr9PHADAZOJ2IuwcjiTPLL0%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;930&quot; height=&quot;570&quot; data-origin-width=&quot;930&quot; data-origin-height=&quot;570&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;h4 data-ke-size=&quot;size20&quot;&gt;2.4. 다양한 통합 플랫폼 &amp;amp; 언어 지원&lt;/h4&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;style3&quot;&gt;&lt;b&gt;  다양한 통합 플랫폼 &amp;amp; 언어 지원&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 다양한 CI/CD 통합 플랫폼을 지원하고, 다양한 언어에서 코드 품질을 평가할 수 있습니다.&lt;/b&gt;&lt;br /&gt;- Commuity 버전(무료)과 EnterPrice 버전(유료)에서 지원하는 언어들이 각각 다르기에 확인이 필요합니다.&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;944&quot; data-origin-height=&quot;815&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/StGVz/dJMcabP2mJX/JKRUYzRPZXHd3kc66ln3XK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/StGVz/dJMcabP2mJX/JKRUYzRPZXHd3kc66ln3XK/img.png&quot; data-alt=&quot;https://www.sonarsource.com/integrations/&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/StGVz/dJMcabP2mJX/JKRUYzRPZXHd3kc66ln3XK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FStGVz%2FdJMcabP2mJX%2FJKRUYzRPZXHd3kc66ln3XK%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;944&quot; height=&quot;815&quot; data-origin-width=&quot;944&quot; data-origin-height=&quot;815&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://www.sonarsource.com/integrations/&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;&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;912&quot; data-origin-height=&quot;575&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bSfvjA/dJMcaf5Ymy0/lAncUAimE6jH4vIfsOCyJk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bSfvjA/dJMcaf5Ymy0/lAncUAimE6jH4vIfsOCyJk/img.png&quot; data-alt=&quot;https://www.sonarsource.com/knowledge/languages/&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bSfvjA/dJMcaf5Ymy0/lAncUAimE6jH4vIfsOCyJk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbSfvjA%2FdJMcaf5Ymy0%2FlAncUAimE6jH4vIfsOCyJk%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;912&quot; height=&quot;575&quot; data-origin-width=&quot;912&quot; data-origin-height=&quot;575&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://www.sonarsource.com/knowledge/languages/&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;&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;b&gt;오늘도&amp;nbsp;감사합니다.&amp;nbsp; &lt;/b&gt;&lt;/p&gt;</description>
      <category>OpenSource/sonarQube</category>
      <category>dast</category>
      <category>sast</category>
      <category>SonarQube</category>
      <category>sonarqube loc</category>
      <category>sonarqube loc 산정 방법</category>
      <category>sonarqube 장점</category>
      <category>sonarqube 종류</category>
      <category>sonarqube 활용</category>
      <category>sparrow sast</category>
      <category>종류</category>
      <author>adjh54</author>
      <guid isPermaLink="true">https://adjh54.tistory.com/720</guid>
      <comments>https://adjh54.tistory.com/720#entry720comment</comments>
      <pubDate>Tue, 4 Nov 2025 20:20:24 +0900</pubDate>
    </item>
    <item>
      <title>[Java] Spring Boot 1.5.x 버전 프로젝트 생성 방법(Gradle/Maven) : IntelliJ 활용</title>
      <link>https://adjh54.tistory.com/719</link>
      <description>&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;해당 글에서는 Spring Boot 1.5.x 버전 기준의 프로젝트를 생성하는 방법에 대해 알아봅니다.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;
&lt;figure contenteditable=&quot;false&quot; data-ke-type=&quot;emoticon&quot; data-ke-align=&quot;alignCenter&quot; data-emoticon-type=&quot;face&quot; data-emoticon-name=&quot;073&quot; data-emoticon-isanimation=&quot;false&quot; data-emoticon-src=&quot;https://t1.daumcdn.net/keditor/emoticon/face/large/073.png&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/keditor/emoticon/face/large/073.png&quot; width=&quot;80&quot; /&gt;&lt;/figure&gt;
&lt;/blockquote&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  [참고]&lt;/b&gt; Spring Boot 2.x.x 버전 프로젝트 생성방법에 대해 궁금하시면 아래의 글을 참고하시면 도움이 됩니다.&lt;/blockquote&gt;
&lt;figure id=&quot;og_1762223789805&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;[Java] Spring Boot 2.x.x 버전 프로젝트 생성: 지원 종료 및 다운그레이드&quot; data-og-description=&quot;해당 글에서는 Spring Boot 2 버전에 대한 프로젝트를 생성하고자 하여 Spring Boot 3 버전을 다운그레이드하여 사용하는 방법에 대해 알아봅니다. 1) Spring Boot 2.x.x 지원 종료  확인 계기- IntelliJ IDEA툴&quot; data-og-host=&quot;adjh54.tistory.com&quot; data-og-source-url=&quot;https://adjh54.tistory.com/361&quot; data-og-url=&quot;https://adjh54.tistory.com/361&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/dwVKn3/hyZNaU9zZr/Czdls3mjcQqcYyG6CyTNM0/img.png?width=800&amp;amp;height=431&amp;amp;face=0_0_800_431,https://scrap.kakaocdn.net/dn/my0Rz/hyZMMIn6O9/0EiIrJEqJGmkdAw3s14zoK/img.png?width=800&amp;amp;height=431&amp;amp;face=0_0_800_431,https://scrap.kakaocdn.net/dn/qMYXO/hyZNcZKKU2/1IwN1P8OpLoLhfr7JYJE0k/img.png?width=1037&amp;amp;height=805&amp;amp;face=0_0_1037_805&quot;&gt;&lt;a href=&quot;https://adjh54.tistory.com/361&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://adjh54.tistory.com/361&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/dwVKn3/hyZNaU9zZr/Czdls3mjcQqcYyG6CyTNM0/img.png?width=800&amp;amp;height=431&amp;amp;face=0_0_800_431,https://scrap.kakaocdn.net/dn/my0Rz/hyZMMIn6O9/0EiIrJEqJGmkdAw3s14zoK/img.png?width=800&amp;amp;height=431&amp;amp;face=0_0_800_431,https://scrap.kakaocdn.net/dn/qMYXO/hyZNcZKKU2/1IwN1P8OpLoLhfr7JYJE0k/img.png?width=1037&amp;amp;height=805&amp;amp;face=0_0_1037_805');&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;[Java] Spring Boot 2.x.x 버전 프로젝트 생성: 지원 종료 및 다운그레이드&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;해당 글에서는 Spring Boot 2 버전에 대한 프로젝트를 생성하고자 하여 Spring Boot 3 버전을 다운그레이드하여 사용하는 방법에 대해 알아봅니다. 1) Spring Boot 2.x.x 지원 종료  확인 계기- IntelliJ IDEA툴&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;adjh54.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;h2 data-ke-size=&quot;size26&quot;&gt;1) Spring Boot 1.5.x 버전 확인&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  Spring Boot 1.5.x 버전&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;- Spring Boot 1.x.x 버전을 설치하게 될 일이 있었습니다. 그렇기에 현재 설치가 가능한 Spring Boot 내의 1.x.x 버전을 찾다가 1.x.x의 가장 마지막 버전이 되는 1.5.22 버전을 설치하는 과정을 확인해 봅니다.&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;962&quot; data-origin-height=&quot;611&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lQwC7/dJMcajtLCoQ/wQWKIea42HqY5CqYug3Mx1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lQwC7/dJMcajtLCoQ/wQWKIea42HqY5CqYug3Mx1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lQwC7/dJMcajtLCoQ/wQWKIea42HqY5CqYug3Mx1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlQwC7%2FdJMcajtLCoQ%2FwQWKIea42HqY5CqYug3Mx1%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;962&quot; height=&quot;611&quot; data-origin-width=&quot;962&quot; data-origin-height=&quot;611&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;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;&amp;nbsp;  Maven Repsitory 내에서 1.x.x 버전을 확인해 보았을 때 가장 최신버전이 1.5.22 버전이기에 이를 설치하려고 합니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1482&quot; data-origin-height=&quot;872&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bzYDaU/dJMcadAjIP8/lDuKzzxYeWDKPvnF1fMHn0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bzYDaU/dJMcadAjIP8/lDuKzzxYeWDKPvnF1fMHn0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bzYDaU/dJMcadAjIP8/lDuKzzxYeWDKPvnF1fMHn0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbzYDaU%2FdJMcadAjIP8%2FlDuKzzxYeWDKPvnF1fMHn0%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;1482&quot; height=&quot;872&quot; data-origin-width=&quot;1482&quot; data-origin-height=&quot;872&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;[ 더 알아보기 ]&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt; &amp;nbsp;왜 1.5.X 버전을 이용하려고 했던 걸까?&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;- Spring Boot 1.x.x 버전 내에서 Spring Boot Cloud의 Vault를 이용하기 위해서 이를 사용하려고 합니다. 공식 사이트를 방문해서 확인해 보면 1.5.x 버전부터 Spring Boot Cloud를 지원한다고 하기에 이를 이용하려고 합니다.&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;957&quot; data-origin-height=&quot;758&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/3KDxZ/dJMcai9sPQd/5hd9Fkyn3XzNnbWy5X8BkK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/3KDxZ/dJMcai9sPQd/5hd9Fkyn3XzNnbWy5X8BkK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/3KDxZ/dJMcai9sPQd/5hd9Fkyn3XzNnbWy5X8BkK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F3KDxZ%2FdJMcai9sPQd%2F5hd9Fkyn3XzNnbWy5X8BkK%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;957&quot; height=&quot;758&quot; data-origin-width=&quot;957&quot; data-origin-height=&quot;758&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;figure id=&quot;og_1762223901616&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 Cloud&quot; data-og-description=&quot;Spring Cloud provides tools for developers to quickly build some of the common patterns in distributed systems (e.g. configuration management, service discovery, circuit breakers, intelligent routing, micro-proxy, control bus, short lived microservices and&quot; data-og-host=&quot;spring.io&quot; data-og-source-url=&quot;https://spring.io/projects/spring-cloud&quot; data-og-url=&quot;https://spring.io/projects/spring-cloud&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/oxplu/hyZMXJUcBA/4xyaxKtwbEvKGjyulhvB21/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/S5G6Z/hyZNcyGmtK/qNfhMxkkWuwB2SVZLSDzC1/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-cloud&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://spring.io/projects/spring-cloud&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/oxplu/hyZMXJUcBA/4xyaxKtwbEvKGjyulhvB21/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/S5G6Z/hyZNcyGmtK/qNfhMxkkWuwB2SVZLSDzC1/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 Cloud&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Spring Cloud provides tools for developers to quickly build some of the common patterns in distributed systems (e.g. configuration management, service discovery, circuit breakers, intelligent routing, micro-proxy, control bus, short lived microservices and&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;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2) Spring Boot 1.5.22 버전 설치 : Gradle 활용&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;&amp;nbsp;  Spring Boot 1.5.22 버전 설치 : Gradle 활용&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- Spring Boot 1.5.22 버전으로 설치하기 위해서, 가장 최신 버전에서 다운그레이드를 수행하는 방식으로 진행합니다.&lt;/b&gt;&lt;br /&gt;- 이번 부분에서는 Gradle을 이용한 프로젝트 구성 및 서버 실행까지 구성을 합니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 환경 확인&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  환경 확인&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 해당 환경을 구성하기 위해서 아래와 같은 버전으로 구성을 합니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style2&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;분류&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;버전&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Spring Boot&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;1.5.22&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;java&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;1.8&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;gradle&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;gradle-4.10.2-all&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;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  공식 문서를 참고하여서 해당 환경을 구성합니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;figure id=&quot;og_1762223984949&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Spring Boot Reference Guide&quot; data-og-description=&quot;24.&amp;nbsp;Externalized Configuration Spring Boot allows you to externalize your configuration so you can work with the same application code in different environments. You can use properties files, YAML files, environment variables and command-line arguments to&quot; data-og-host=&quot;docs.spring.io&quot; data-og-source-url=&quot;https://docs.spring.io/spring-boot/docs/1.5.x/reference/htmlsingle/#getting-started-system-requirements&quot; data-og-url=&quot;https://docs.spring.io/spring-boot/docs/1.5.x/reference/htmlsingle/#getting-started-system-requirements&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://docs.spring.io/spring-boot/docs/1.5.x/reference/htmlsingle/#getting-started-system-requirements&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://docs.spring.io/spring-boot/docs/1.5.x/reference/htmlsingle/#getting-started-system-requirements&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Spring Boot Reference Guide&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;24.&amp;nbsp;Externalized Configuration Spring Boot allows you to externalize your configuration so you can work with the same application code in different environments. You can use properties files, YAML files, environment variables and command-line arguments to&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;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. 프로젝트 생성&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;805&quot; data-origin-height=&quot;643&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cskSfi/dJMcake8HCQ/7kX0x6vaCXL8kBz7kekGL1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cskSfi/dJMcake8HCQ/7kX0x6vaCXL8kBz7kekGL1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cskSfi/dJMcake8HCQ/7kX0x6vaCXL8kBz7kekGL1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcskSfi%2FdJMcake8HCQ%2F7kX0x6vaCXL8kBz7kekGL1%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;805&quot; height=&quot;643&quot; data-origin-width=&quot;805&quot; data-origin-height=&quot;643&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;h3 data-ke-size=&quot;size23&quot;&gt;3. IntelliJ 설정 내에 Gradle JVM을 변경합니다.&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  IntelliJ 설정 내에 Gradle JVM을 변경합니다.&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 빌드, 실행, 배포 &amp;gt; 빌드 도구 &amp;gt; Gradle 부분에서 Gradle JVM을 jdk 1.8로 변경합니다&lt;/b&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;984&quot; data-origin-height=&quot;720&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cMQYea/dJMcaacwpwy/1cFKnw1h6TzrvKADsVOhJ1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cMQYea/dJMcaacwpwy/1cFKnw1h6TzrvKADsVOhJ1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cMQYea/dJMcaacwpwy/1cFKnw1h6TzrvKADsVOhJ1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcMQYea%2FdJMcaacwpwy%2F1cFKnw1h6TzrvKADsVOhJ1%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;984&quot; height=&quot;720&quot; data-origin-width=&quot;984&quot; data-origin-height=&quot;720&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;h3 data-ke-size=&quot;size23&quot;&gt;4. gradle-wrapper.properties 내에 gradle 버전을 수정합니다.&lt;/h3&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;style3&quot;&gt;&lt;b&gt;&amp;nbsp;  gradle-wrapper.properties&amp;nbsp;내에 gradle 버전을 수정합니다&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;- 위에서 정의한 gradle 버전인 gradle-4.10.2-all을 이용합니다&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1222&quot; data-origin-height=&quot;528&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/meCiv/dJMcake8HC5/iTs5PG6uETxmAQZRZAU4Ik/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/meCiv/dJMcake8HC5/iTs5PG6uETxmAQZRZAU4Ik/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/meCiv/dJMcake8HC5/iTs5PG6uETxmAQZRZAU4Ik/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmeCiv%2FdJMcake8HC5%2FiTs5PG6uETxmAQZRZAU4Ik%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;1222&quot; height=&quot;528&quot; data-origin-width=&quot;1222&quot; data-origin-height=&quot;528&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
# 아래 부분을 수정합니다.
distributionUrl=https\\://services.gradle.org/distributions/gradle-4.10.2-all.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
&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;h3 data-ke-size=&quot;size23&quot;&gt;5. build.gradle 파일을 수정합니다.&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  build.gradle 파일을 수정합니다.&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- spring boot 버전을 1.5.22으로 변경 및 java 버전을 1.8로 지정합니다&lt;/b&gt;&lt;/blockquote&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;buildscript {
    ext {
        springBootVersion = '1.5.22.RELEASE'
    }
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath &quot;org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}&quot;
    }
}

apply plugin: 'java'
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'

group = 'com.test'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = 1.8

repositories {
    mavenCentral()
}

dependencies {
    compile 'org.springframework.boot:spring-boot-starter-web'
    testCompile 'org.springframework.boot:spring-boot-starter-test'
}

&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;h3 data-ke-size=&quot;size23&quot;&gt;6. gradle을 실행합니다&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1353&quot; data-origin-height=&quot;558&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dTe9dr/dJMcabbqLyn/EKrrHxWZwPwCb0KfE34Qmk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dTe9dr/dJMcabbqLyn/EKrrHxWZwPwCb0KfE34Qmk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dTe9dr/dJMcabbqLyn/EKrrHxWZwPwCb0KfE34Qmk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdTe9dr%2FdJMcabbqLyn%2FEKrrHxWZwPwCb0KfE34Qmk%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;1353&quot; height=&quot;558&quot; data-origin-width=&quot;1353&quot; data-origin-height=&quot;558&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;h3 data-ke-size=&quot;size23&quot;&gt;7. 빌드가 정상적으로 실행이 되었습니다&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1375&quot; data-origin-height=&quot;749&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/btJeXQ/dJMcaiBCQeW/3lhzrr7RANJRDYGAmwkO3K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/btJeXQ/dJMcaiBCQeW/3lhzrr7RANJRDYGAmwkO3K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/btJeXQ/dJMcaiBCQeW/3lhzrr7RANJRDYGAmwkO3K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbtJeXQ%2FdJMcaiBCQeW%2F3lhzrr7RANJRDYGAmwkO3K%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;1375&quot; height=&quot;749&quot; data-origin-width=&quot;1375&quot; data-origin-height=&quot;749&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3) Spring Boot 1.5.22 버전 실행하기: Gradle 활용&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 구성편집을 선택합니다&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1263&quot; data-origin-height=&quot;475&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dc1bwW/dJMcacIa0iP/bv1pK5TGM3Yerp8dUJq6lK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dc1bwW/dJMcacIa0iP/bv1pK5TGM3Yerp8dUJq6lK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dc1bwW/dJMcacIa0iP/bv1pK5TGM3Yerp8dUJq6lK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdc1bwW%2FdJMcacIa0iP%2Fbv1pK5TGM3Yerp8dUJq6lK%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;1263&quot; height=&quot;475&quot; data-origin-width=&quot;1263&quot; data-origin-height=&quot;475&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;h3 data-ke-size=&quot;size23&quot;&gt;2. 빌드 및 실행 부분에 java 버전을 변경해 줍니다&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;799&quot; data-origin-height=&quot;677&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/tG5Ee/dJMcahipKWq/EIXwubUFwkog9TuRa1K160/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/tG5Ee/dJMcahipKWq/EIXwubUFwkog9TuRa1K160/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/tG5Ee/dJMcahipKWq/EIXwubUFwkog9TuRa1K160/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FtG5Ee%2FdJMcahipKWq%2FEIXwubUFwkog9TuRa1K160%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;799&quot; height=&quot;677&quot; data-origin-width=&quot;799&quot; data-origin-height=&quot;677&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;h3 data-ke-size=&quot;size23&quot;&gt;3. 서버를 실행합니다.&lt;/h3&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;style3&quot;&gt;&lt;b&gt;&amp;nbsp;  서버를 실행합니다.&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 서버를 실행했을 때 아래와 같은 오류가 발생합니다.&lt;/b&gt;&lt;br /&gt;- Caused by: java.lang.NoClassDefFoundError: com/fasterxml/classmate/TypeResolver at org.hibernate.validator.internal.util.TypeResolutionHelper&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1792&quot; data-origin-height=&quot;1012&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/t3SB2/dJMcahvWVbE/pN4iX9gLs6iNxkgykuyzfk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/t3SB2/dJMcahvWVbE/pN4iX9gLs6iNxkgykuyzfk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/t3SB2/dJMcahvWVbE/pN4iX9gLs6iNxkgykuyzfk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Ft3SB2%2FdJMcahvWVbE%2FpN4iX9gLs6iNxkgykuyzfk%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;1792&quot; height=&quot;1012&quot; data-origin-width=&quot;1792&quot; data-origin-height=&quot;1012&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;h3 data-ke-size=&quot;size23&quot;&gt;4. 오류를 수정하기 위해서 아래와 같이 라이브러리를 추가합니다&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  오류를 수정하기 위해서 아래와 같이 라이브러리를 추가합니다&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- com.fasterxml:classmate 부분을 추가하였습니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;buildscript {
    ext {
        springBootVersion = '1.5.22.RELEASE'
    }
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath &quot;org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}&quot;

    }
}

apply plugin: 'java'
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'

group = 'com.test'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = 1.8

repositories {
    mavenCentral()
}

dependencies {
    compile 'org.springframework.boot:spring-boot-starter-web'

    // 해당 부분 추가 !!!
    compile 'com.fasterxml:classmate:1.3.4'

    testCompile 'org.springframework.boot:spring-boot-starter-test'
}&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;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;[ 더 알아보기 ]&lt;/b&gt;&lt;br /&gt;&lt;b&gt;&lt;br /&gt; &amp;nbsp;com.fasterxml:classmate는 왜 사용하는 걸까?&lt;/b&gt;&lt;br /&gt;- Spring Boot 1.5.x는 Hibernate Validator 5.x 버전을 사용합니다&lt;br /&gt;- 이 Hibernate Validator는 객체의 타입 정보를 분석할 때 classmate 라이브러리를 반드시 필요로 합니다.&lt;br /&gt;- @Valid, @NotNull, @Size, @RequestBody validation을 사용하는데 이용이 됩니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5. 빌드를 다시 수행합니다&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  빌드를 다시 수행합니다&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 정상적으로 빌드를 성공하였습니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1794&quot; data-origin-height=&quot;957&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/o3F2K/dJMcafLFvSC/OZeAeEe8SrhmNEpKSw9DCk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/o3F2K/dJMcafLFvSC/OZeAeEe8SrhmNEpKSw9DCk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/o3F2K/dJMcafLFvSC/OZeAeEe8SrhmNEpKSw9DCk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fo3F2K%2FdJMcafLFvSC%2FOZeAeEe8SrhmNEpKSw9DCk%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;1794&quot; height=&quot;957&quot; data-origin-width=&quot;1794&quot; data-origin-height=&quot;957&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;h3 data-ke-size=&quot;size23&quot;&gt;6. 서버를 실행합니다&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  서버를 실행합니다&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 정상적으로 서버가 실행이 됨을 확인하였습니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1807&quot; data-origin-height=&quot;982&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/MKCV8/dJMcai2Hci9/eekgk5kbkGZPLzXzps94uk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/MKCV8/dJMcai2Hci9/eekgk5kbkGZPLzXzps94uk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/MKCV8/dJMcai2Hci9/eekgk5kbkGZPLzXzps94uk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FMKCV8%2FdJMcai2Hci9%2Feekgk5kbkGZPLzXzps94uk%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;1807&quot; height=&quot;982&quot; data-origin-width=&quot;1807&quot; data-origin-height=&quot;982&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;h2 data-ke-size=&quot;size26&quot;&gt;4) Spring Boot 1.5.22 버전 설치 : Maven 활용&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  Spring Boot 1.5.22 버전 설치 : Maven 활용&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- Spring Boot 1.5.22 버전으로 설치하기 위해서, 가장 최신 버전에서 다운그레이드를 수행하는 방식으로 진행합니다.&lt;/b&gt;&lt;br /&gt;- 이번 부분에서는 Maven을 이용한 프로젝트 구성 및 서버 실행까지 구성을 합니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 환경 확인&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  환경 확인&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;- 해당 환경을 구성하기 위해서 아래와 같은 버전으로 구성을 합니다.&lt;/blockquote&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style2&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;분류&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;버전&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Spring Boot&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;1.5.22&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;java&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;1.8&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;maven&lt;/b&gt;&lt;/td&gt;
&lt;td&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;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;&amp;nbsp;  공식 문서를 참고하여서 해당 환경을 구성합니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;figure id=&quot;og_1762224385904&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Spring Boot Reference Guide&quot; data-og-description=&quot;24.&amp;nbsp;Externalized Configuration Spring Boot allows you to externalize your configuration so you can work with the same application code in different environments. You can use properties files, YAML files, environment variables and command-line arguments to&quot; data-og-host=&quot;docs.spring.io&quot; data-og-source-url=&quot;https://docs.spring.io/spring-boot/docs/1.5.x/reference/htmlsingle/#getting-started-system-requirements&quot; data-og-url=&quot;https://docs.spring.io/spring-boot/docs/1.5.x/reference/htmlsingle/#getting-started-system-requirements&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://docs.spring.io/spring-boot/docs/1.5.x/reference/htmlsingle/#getting-started-system-requirements&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://docs.spring.io/spring-boot/docs/1.5.x/reference/htmlsingle/#getting-started-system-requirements&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Spring Boot Reference Guide&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;24.&amp;nbsp;Externalized Configuration Spring Boot allows you to externalize your configuration so you can work with the same application code in different environments. You can use properties files, YAML files, environment variables and command-line arguments to&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;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. 프로젝트 생성&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  프로젝트 생성&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;- IntelliJ 내에서 3.x.x 버전을 설치합니다&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;805&quot; data-origin-height=&quot;643&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/od47A/dJMcadtx8MQ/XDklHw92HvKjdelyZRxEn0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/od47A/dJMcadtx8MQ/XDklHw92HvKjdelyZRxEn0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/od47A/dJMcadtx8MQ/XDklHw92HvKjdelyZRxEn0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fod47A%2FdJMcadtx8MQ%2FXDklHw92HvKjdelyZRxEn0%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;805&quot; height=&quot;643&quot; data-origin-width=&quot;805&quot; data-origin-height=&quot;643&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;h3 data-ke-size=&quot;size23&quot;&gt;3. IntelliJ 설정 내에 Maven JDK를 변경합니다.&lt;/h3&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;style3&quot;&gt;&lt;b&gt;&amp;nbsp;  IntelliJ 설정내에 Maven JDK를 변경합니다.&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 빌드, 실행, 배포 &amp;gt; 빌드 도구 &amp;gt; Maven &amp;gt; 가져오기 부분에서 임포터의 JDK부분을 jdk 1.8로 변경합니다&lt;/b&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;983&quot; data-origin-height=&quot;725&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/sQDXz/dJMcahCIyqt/ygYwIZeRnHA6ThiTC15kB0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/sQDXz/dJMcahCIyqt/ygYwIZeRnHA6ThiTC15kB0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/sQDXz/dJMcahCIyqt/ygYwIZeRnHA6ThiTC15kB0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FsQDXz%2FdJMcahCIyqt%2FygYwIZeRnHA6ThiTC15kB0%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;983&quot; height=&quot;725&quot; data-origin-width=&quot;983&quot; data-origin-height=&quot;725&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;h3 data-ke-size=&quot;size23&quot;&gt;4. IntelliJ 설정 내에 Maven JRE를 변경합니다.&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  IntelliJ 설정 내에 Maven JRE를 변경합니다.&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 빌드, 실행, 배포 &amp;gt; 빌드 도구 &amp;gt; Maven &amp;gt; 러너 부분에서 임포터의 JRE 부분을 jdk 1.8로 변경합니다&lt;/b&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;985&quot; data-origin-height=&quot;724&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bhghbu/dJMcafSq7vV/Yp8iwMTGekCfJyIZbHxcj0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bhghbu/dJMcafSq7vV/Yp8iwMTGekCfJyIZbHxcj0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bhghbu/dJMcafSq7vV/Yp8iwMTGekCfJyIZbHxcj0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbhghbu%2FdJMcafSq7vV%2FYp8iwMTGekCfJyIZbHxcj0%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;985&quot; height=&quot;724&quot; data-origin-width=&quot;985&quot; data-origin-height=&quot;724&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;h3 data-ke-size=&quot;size23&quot;&gt;5. pom.xml 파일을 수정합니다.&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  pom.xml 파일을 수정합니다.&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;- spring boot 버전을 1.5.22으로 변경 및 java 버전을 1.8로 지정합니다&lt;/blockquote&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&amp;gt;
&amp;lt;project xmlns=&quot;http://maven.apache.org/POM/4.0.0&quot; xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot;
         xsi:schemaLocation=&quot;http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd&quot;&amp;gt;
    &amp;lt;modelVersion&amp;gt;4.0.0&amp;lt;/modelVersion&amp;gt;

    &amp;lt;!-- ✅ Spring Boot 1.5.22.RELEASE --&amp;gt;
    &amp;lt;parent&amp;gt;
        &amp;lt;groupId&amp;gt;org.springframework.boot&amp;lt;/groupId&amp;gt;
        &amp;lt;artifactId&amp;gt;spring-boot-starter-parent&amp;lt;/artifactId&amp;gt;
        &amp;lt;version&amp;gt;1.5.22.RELEASE&amp;lt;/version&amp;gt;
    &amp;lt;/parent&amp;gt;

    &amp;lt;groupId&amp;gt;com.daekyocns&amp;lt;/groupId&amp;gt;
    &amp;lt;artifactId&amp;gt;vault&amp;lt;/artifactId&amp;gt;
    &amp;lt;version&amp;gt;0.0.1-SNAPSHOT&amp;lt;/version&amp;gt;
    &amp;lt;name&amp;gt;vault&amp;lt;/name&amp;gt;
    &amp;lt;description&amp;gt;vault&amp;lt;/description&amp;gt;

    &amp;lt;properties&amp;gt;
        &amp;lt;!-- ✅ Java 17 &amp;rarr; X, Boot 1.x는 Java 8 필요 --&amp;gt;
        &amp;lt;java.version&amp;gt;1.8&amp;lt;/java.version&amp;gt;
    &amp;lt;/properties&amp;gt;

    &amp;lt;dependencies&amp;gt;
        &amp;lt;dependency&amp;gt;
            &amp;lt;groupId&amp;gt;org.springframework.boot&amp;lt;/groupId&amp;gt;
            &amp;lt;artifactId&amp;gt;spring-boot-starter-web&amp;lt;/artifactId&amp;gt;
        &amp;lt;/dependency&amp;gt;

        &amp;lt;!-- ✅ JUnit5 지원 안 함 &amp;rarr; 기본 테스트 사용 --&amp;gt;
        &amp;lt;dependency&amp;gt;
            &amp;lt;groupId&amp;gt;org.springframework.boot&amp;lt;/groupId&amp;gt;
            &amp;lt;artifactId&amp;gt;spring-boot-starter-test&amp;lt;/artifactId&amp;gt;
            &amp;lt;scope&amp;gt;test&amp;lt;/scope&amp;gt;
        &amp;lt;/dependency&amp;gt;
    &amp;lt;/dependencies&amp;gt;

    &amp;lt;build&amp;gt;
        &amp;lt;plugins&amp;gt;
            &amp;lt;plugin&amp;gt;
                &amp;lt;groupId&amp;gt;org.springframework.boot&amp;lt;/groupId&amp;gt;
                &amp;lt;artifactId&amp;gt;spring-boot-maven-plugin&amp;lt;/artifactId&amp;gt;
            &amp;lt;/plugin&amp;gt;
        &amp;lt;/plugins&amp;gt;
    &amp;lt;/build&amp;gt;

&amp;lt;/project&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;h3 data-ke-size=&quot;size23&quot;&gt;6. Maven을 실행합니다&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1183&quot; data-origin-height=&quot;889&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kABgu/dJMcaajh4v6/T6Q8ltbrwzyo7aFAvkmgX0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kABgu/dJMcaajh4v6/T6Q8ltbrwzyo7aFAvkmgX0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kABgu/dJMcaajh4v6/T6Q8ltbrwzyo7aFAvkmgX0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkABgu%2FdJMcaajh4v6%2FT6Q8ltbrwzyo7aFAvkmgX0%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;1183&quot; height=&quot;889&quot; data-origin-width=&quot;1183&quot; data-origin-height=&quot;889&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;h3 data-ke-size=&quot;size23&quot;&gt;7. 빌드가 정상적으로 실행이 되었습니다&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1751&quot; data-origin-height=&quot;889&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cZBgtx/dJMcag4SAhY/PSfHgcgUkWHcHtAWK9XPc0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cZBgtx/dJMcag4SAhY/PSfHgcgUkWHcHtAWK9XPc0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cZBgtx/dJMcag4SAhY/PSfHgcgUkWHcHtAWK9XPc0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcZBgtx%2FdJMcag4SAhY%2FPSfHgcgUkWHcHtAWK9XPc0%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;1751&quot; height=&quot;889&quot; data-origin-width=&quot;1751&quot; data-origin-height=&quot;889&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;h2 data-ke-size=&quot;size26&quot;&gt;5) Spring Boot 1.5.22 버전 실행하기: Maven 활용&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 구성편집을 선택합니다&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1154&quot; data-origin-height=&quot;696&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bjs2nt/dJMcain5FUL/IPHQJuKY2aRUexZoLflgy1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bjs2nt/dJMcain5FUL/IPHQJuKY2aRUexZoLflgy1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bjs2nt/dJMcain5FUL/IPHQJuKY2aRUexZoLflgy1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbjs2nt%2FdJMcain5FUL%2FIPHQJuKY2aRUexZoLflgy1%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;1154&quot; height=&quot;696&quot; data-origin-width=&quot;1154&quot; data-origin-height=&quot;696&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;h3 data-ke-size=&quot;size23&quot;&gt;2. 빌드 및 실행 부분에 java 버전을 변경해 줍니다&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;799&quot; data-origin-height=&quot;677&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/beMiOF/dJMcaiIosi2/uL1j7U3ALIo7DH8tjRK6M1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/beMiOF/dJMcaiIosi2/uL1j7U3ALIo7DH8tjRK6M1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/beMiOF/dJMcaiIosi2/uL1j7U3ALIo7DH8tjRK6M1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbeMiOF%2FdJMcaiIosi2%2FuL1j7U3ALIo7DH8tjRK6M1%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;799&quot; height=&quot;677&quot; data-origin-width=&quot;799&quot; data-origin-height=&quot;677&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;h3 data-ke-size=&quot;size23&quot;&gt;3. 서버를 실행합니다.&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  서버를 실행합니다.&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 서버를 실행시켰을 때 정상적으로 수행이 됨을 확인하였습니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1791&quot; data-origin-height=&quot;917&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/KZ8vm/dJMcacnSaki/kg0eSnjZJq0G0khVxdYI1k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/KZ8vm/dJMcacnSaki/kg0eSnjZJq0G0khVxdYI1k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/KZ8vm/dJMcacnSaki/kg0eSnjZJq0G0khVxdYI1k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FKZ8vm%2FdJMcacnSaki%2Fkg0eSnjZJq0G0khVxdYI1k%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;1791&quot; height=&quot;917&quot; data-origin-width=&quot;1791&quot; data-origin-height=&quot;917&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;b&gt;오늘도&amp;nbsp;감사합니다.&amp;nbsp; &lt;/b&gt;&lt;/p&gt;</description>
      <category>Java/환경설정</category>
      <category>Spring Boot</category>
      <category>spring boot 1</category>
      <category>spring boot 1.5.22</category>
      <category>spring boot 1버전 생성</category>
      <category>spring boot downgrade</category>
      <category>spring boot 버전 다운그레이드</category>
      <category>spring boot 버전 변경</category>
      <category>spring boot1 프로젝트 생성</category>
      <category>spring1 프로젝트 생성</category>
      <author>adjh54</author>
      <guid isPermaLink="true">https://adjh54.tistory.com/719</guid>
      <comments>https://adjh54.tistory.com/719#entry719comment</comments>
      <pubDate>Tue, 4 Nov 2025 20:10:13 +0900</pubDate>
    </item>
    <item>
      <title>[RN/오류노트] Solved - Families Policy Requirements: Ad Content</title>
      <link>https://adjh54.tistory.com/718</link>
      <description>&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;React Native 환경에서 Google PlayStore에서 발생한 Families Policy Requirements: Ad Content 문제에 대해 해결 방법을 알아봅니다.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;
&lt;figure contenteditable=&quot;false&quot; data-ke-type=&quot;emoticon&quot; data-ke-align=&quot;alignCenter&quot; data-emoticon-type=&quot;face&quot; data-emoticon-name=&quot;073&quot; data-emoticon-isanimation=&quot;false&quot; data-emoticon-src=&quot;https://t1.daumcdn.net/keditor/emoticon/face/large/073.png&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/keditor/emoticon/face/large/073.png&quot; width=&quot;80&quot; /&gt;&lt;/figure&gt;
&lt;/blockquote&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1) 문제점&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  문제점&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 앱을 출시하는 과정에서 아래와 같은 문제가 발생하였습니다.&lt;/b&gt;&lt;br /&gt;- Families Policy Requirements: Ad Content 위와&amp;nbsp;같은 사항은 &quot;앱의 광고 콘텐츠가 앱의 콘텐츠 등급과 일치하지 않습니다.&quot;라는 문제였습니다.&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1282&quot; data-origin-height=&quot;720&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bAJiQn/dJMcacnR94N/p9xKqo6fplEfj5JRCITlo0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bAJiQn/dJMcacnR94N/p9xKqo6fplEfj5JRCITlo0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bAJiQn/dJMcacnR94N/p9xKqo6fplEfj5JRCITlo0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbAJiQn%2FdJMcacnR94N%2Fp9xKqo6fplEfj5JRCITlo0%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;1282&quot; height=&quot;720&quot; data-origin-width=&quot;1282&quot; data-origin-height=&quot;720&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2) 해결 방법&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  해결 방법&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 구글 플레이 콘솔에서 제안하는 해결 방법은 아래와 같다고 합니다.&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;- 콘텐츠 등급 문제를 해결하려면 앱 등급과 일치하지 않는 콘텐츠를 삭제하거나 콘텐츠 등급 설문지를 다시 작성하세요. 추가 지침은 등급 설문지 도움말 센터 페이지에서 용어 정의 및 콘텐츠 예시를 참조하세요.&lt;br /&gt;&lt;br /&gt;- 앱으로 수익을 창출하는 경우 민감한 카테고리의 광고를 제외했는지 확인하세요.&lt;br /&gt;- 자세한 내용은 가족 정책 요구 사항 의 &quot;광고&quot; 섹션을 참조할 수 있으며, 광고 및 수익 창출 정책 페이지에서 부적절한 광고 콘텐츠의 예를 볼 수 있습니다.&lt;br /&gt;&lt;br /&gt;- 위와 같은 가이드라인을 제공하고 있습니다.&lt;br /&gt;- Google의 가족 정책 요구사항(Families Policy Requirements)에서는 아동(13세 미만 또는 지역별 기준 이하 연령)을 대상으로 하는 앱 또는 가족용으로 Play 스토어에 노출되는 앱에 대해 특별한 콘텐츠 및 광고 기준을 적용합니다.&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;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  결론적으로는 기존에는 13세 미만 아동에 대해서 타겟으로 진행하고 있음에도 광고가 이에 적합하지 않은 광고가 나와서 발생하는 문제였습니다.&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;- 실제로 13세 미만을 대상으로 광고를 하는 경우, 광고를 송출하는 쪽에서 이에 대해서 수정하거나 13세 미만을 포함하지 않는 방법을 선택합니다.&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1163&quot; data-origin-height=&quot;759&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/JR9nF/dJMcafkA9lp/nQdEN1lZb3nViSrVz522Ck/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/JR9nF/dJMcafkA9lp/nQdEN1lZb3nViSrVz522Ck/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/JR9nF/dJMcafkA9lp/nQdEN1lZb3nViSrVz522Ck/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FJR9nF%2FdJMcafkA9lp%2FnQdEN1lZb3nViSrVz522Ck%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;1163&quot; height=&quot;759&quot; data-origin-width=&quot;1163&quot; data-origin-height=&quot;759&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3) 해결방법 -1: 13세 미만 아동을 타겟으로 하는 경우&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  해결방법 -1: 13세 미만 아동을 타겟으로 하는 경우&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;- 구글 애드몹 플랫폼을 이용하는 경우 &amp;lsquo;광고 콘텐츠 등급&amp;rsquo;을 지정하여서 13세 미만에게도 광고가 송출되도록 하는 방법입니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. [구글 애드몹] 앱 &amp;gt; 차단 관리 &amp;gt; 광고 콘텐츠 등급 관리 버튼을 누릅니다.&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1709&quot; data-origin-height=&quot;747&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dVl3Mj/dJMcabWNTtJ/FAdIBUlgfr6LSySpeKM55K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dVl3Mj/dJMcabWNTtJ/FAdIBUlgfr6LSySpeKM55K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dVl3Mj/dJMcabWNTtJ/FAdIBUlgfr6LSySpeKM55K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdVl3Mj%2FdJMcabWNTtJ%2FFAdIBUlgfr6LSySpeKM55K%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;1709&quot; height=&quot;747&quot; data-origin-width=&quot;1709&quot; data-origin-height=&quot;747&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;h3 data-ke-size=&quot;size23&quot;&gt;2. 계정 수준 설정 일치 스위치를 누르고, 각각 등급에 맞게 선택을 합니다&lt;/h3&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;style3&quot;&gt;&lt;b&gt;  계정 수준 설정 일치 스위치를 누르고, 각각 등급에 맞게 선택을 합니다&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;- 현재와 같은 상황이라면, 일반 잠재 고객인 G 등급을 선택합니다.&lt;/blockquote&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 201px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style2&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 37px;&quot;&gt;
&lt;td style=&quot;height: 37px; width: 15.6977%;&quot;&gt;&lt;b&gt;광고 콘텐츠&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 37px; width: 23.8371%;&quot;&gt;&lt;b&gt;등급 영어 표기&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 37px; width: 19.6512%;&quot;&gt;&lt;b&gt;권장 연령대&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 37px; width: 40.6977%;&quot;&gt;&lt;b&gt;설명&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 37px;&quot;&gt;
&lt;td style=&quot;height: 37px; width: 15.6977%;&quot;&gt;&lt;b&gt;일반 잠재고객&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 37px; width: 23.8371%;&quot;&gt;&lt;b&gt;G (General Audience)&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 37px; width: 19.6512%;&quot;&gt;전체 연령대 (모두)&lt;/td&gt;
&lt;td style=&quot;height: 37px; width: 40.6977%;&quot;&gt;가족 및 아동 포함, 누구에게나 적합한 광고 콘텐츠만 게재 가능. 폭력&amp;middot;음란&amp;middot;도박&amp;middot;주류 등 민감한 요소 전혀 없음.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 37px;&quot;&gt;
&lt;td style=&quot;height: 37px; width: 15.6977%;&quot;&gt;&lt;b&gt;부모 지도 요망&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 37px; width: 23.8371%;&quot;&gt;&lt;b&gt;PG (Parental Guidance)&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 37px; width: 19.6512%;&quot;&gt;만 7세 이상 권장&lt;/td&gt;
&lt;td style=&quot;height: 37px; width: 40.6977%;&quot;&gt;대부분의 연령대에게 적합하지만, 일부 부모의 지도가 필요한 콘텐츠 포함 가능 (예: 약한 만화 폭력, 가벼운 모험 요소 등).&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 37px;&quot;&gt;
&lt;td style=&quot;height: 37px; width: 15.6977%;&quot;&gt;&lt;b&gt;청소년&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 37px; width: 23.8371%;&quot;&gt;&lt;b&gt;T (Teen)&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 37px; width: 19.6512%;&quot;&gt;만 13세 이상 권장&lt;/td&gt;
&lt;td style=&quot;height: 37px; width: 40.6977%;&quot;&gt;청소년 이상에게 적합. 일반 보건, SNS, 무서운 이미지, 격투 스포츠 등 약간 성숙한 주제 포함 가능.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 35px;&quot;&gt;
&lt;td style=&quot;height: 35px; width: 15.6977%;&quot;&gt;&lt;b&gt;성인용&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 35px; width: 23.8371%;&quot;&gt;&lt;b&gt;MA (Mature Audience)&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 35px; width: 19.6512%;&quot;&gt;만 18세 이상&lt;/td&gt;
&lt;td style=&quot;height: 35px; width: 40.6977%;&quot;&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1914&quot; data-origin-height=&quot;684&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bwAY5h/dJMcad79HeX/6WqpGn8hkPKu1E5miA1KPk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bwAY5h/dJMcad79HeX/6WqpGn8hkPKu1E5miA1KPk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bwAY5h/dJMcad79HeX/6WqpGn8hkPKu1E5miA1KPk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbwAY5h%2FdJMcad79HeX%2F6WqpGn8hkPKu1E5miA1KPk%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;1914&quot; height=&quot;684&quot; data-origin-width=&quot;1914&quot; data-origin-height=&quot;684&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;h2 data-ke-size=&quot;size26&quot;&gt;4) 해결방법 -2: 13세 미만 아동을 타겟으로 하지 않는 경우&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  해결방법 -2: 13세 미만 아동을 타겟으로 하지 않는 경우&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;- 해당 경우는 구글 플레이 콘솔에서 설문지를 재 작성해서 13세 미만을 제외하면 됩니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 구글 플레이 콘솔 &amp;gt; 정책 및 프로그램 &amp;gt; 앱 콘텐츠를 선택합니다&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;284&quot; data-origin-height=&quot;687&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b2Xsmo/dJMcagKzNso/koBE75YWhFCye3sORDaiu1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b2Xsmo/dJMcagKzNso/koBE75YWhFCye3sORDaiu1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b2Xsmo/dJMcagKzNso/koBE75YWhFCye3sORDaiu1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb2Xsmo%2FdJMcagKzNso%2FkoBE75YWhFCye3sORDaiu1%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;284&quot; height=&quot;687&quot; data-origin-width=&quot;284&quot; data-origin-height=&quot;687&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;h3 data-ke-size=&quot;size23&quot;&gt;2. 조치됨 탭을 선택하고 콘텐츠 등급의 &amp;lsquo;관리&amp;rsquo; 버튼을 누릅니다.&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1146&quot; data-origin-height=&quot;743&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/csUiWf/dJMcaj1BxDd/IKWKKQ1Pysv8UlPhxLxiNK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/csUiWf/dJMcaj1BxDd/IKWKKQ1Pysv8UlPhxLxiNK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/csUiWf/dJMcaj1BxDd/IKWKKQ1Pysv8UlPhxLxiNK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcsUiWf%2FdJMcaj1BxDd%2FIKWKKQ1Pysv8UlPhxLxiNK%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;1146&quot; height=&quot;743&quot; data-origin-width=&quot;1146&quot; data-origin-height=&quot;743&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;h3 data-ke-size=&quot;size23&quot;&gt;3. 대상 연령대를 9~12세를 제외하여 선택하고 다음 버튼을 누릅니다.&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;869&quot; data-origin-height=&quot;754&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bxwhpb/dJMcajUPV7F/xCdSbZbOSDZPB1wriPh8G0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bxwhpb/dJMcajUPV7F/xCdSbZbOSDZPB1wriPh8G0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bxwhpb/dJMcajUPV7F/xCdSbZbOSDZPB1wriPh8G0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbxwhpb%2FdJMcajUPV7F%2FxCdSbZbOSDZPB1wriPh8G0%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;869&quot; height=&quot;754&quot; data-origin-width=&quot;869&quot; data-origin-height=&quot;754&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;h3 data-ke-size=&quot;size23&quot;&gt;4. &amp;lsquo;저장&amp;rsquo; 버튼을 누르고 재 심사를 받으면 완료됩니다.&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1453&quot; data-origin-height=&quot;789&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/R2TXK/dJMcab3ztyY/fJ0kvzEhzbrHDFtjdNHEvK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/R2TXK/dJMcab3ztyY/fJ0kvzEhzbrHDFtjdNHEvK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/R2TXK/dJMcab3ztyY/fJ0kvzEhzbrHDFtjdNHEvK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FR2TXK%2FdJMcab3ztyY%2FfJ0kvzEhzbrHDFtjdNHEvK%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;1453&quot; height=&quot;789&quot; data-origin-width=&quot;1453&quot; data-origin-height=&quot;789&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;h3 data-ke-size=&quot;size23&quot;&gt;5. 게시 개요 탭을 눌러서, 아래와 같이 게시 부분이 수정이 되었고, 재 심사를 받습니다&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1088&quot; data-origin-height=&quot;211&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/EYR1q/dJMcajmZZKU/5qmk3FzSiRIKx1BiGgNcLK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/EYR1q/dJMcajmZZKU/5qmk3FzSiRIKx1BiGgNcLK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/EYR1q/dJMcajmZZKU/5qmk3FzSiRIKx1BiGgNcLK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FEYR1q%2FdJMcajmZZKU%2F5qmk3FzSiRIKx1BiGgNcLK%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;1088&quot; height=&quot;211&quot; data-origin-width=&quot;1088&quot; data-origin-height=&quot;211&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;b&gt;오늘도&amp;nbsp;감사합니다.&amp;nbsp; &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;</description>
      <category>React &amp;amp; React Native/환경 설정 및 구성</category>
      <category>Families Policy Requirements: Ad Content</category>
      <category>google admob</category>
      <category>google play console</category>
      <category>google playstore</category>
      <category>구글 애드몹</category>
      <category>구글 플레이 스토어</category>
      <category>구글 플레이 콘솔</category>
      <category>연령</category>
      <author>adjh54</author>
      <guid isPermaLink="true">https://adjh54.tistory.com/718</guid>
      <comments>https://adjh54.tistory.com/718#entry718comment</comments>
      <pubDate>Tue, 4 Nov 2025 20:05:46 +0900</pubDate>
    </item>
  </channel>
</rss>