<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>외계공룡 개발블로그</title>
    <link>https://chucoding.tistory.com/</link>
    <description>개발을 통해 얻은 지식과 경험을 공유하고 소통하고 싶습니다.</description>
    <language>ko</language>
    <pubDate>Mon, 6 Apr 2026 00:10:44 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>외계공룡</managingEditor>
    <image>
      <title>외계공룡 개발블로그</title>
      <url>https://tistory1.daumcdn.net/tistory/3842803/attach/17d4fe79da1d4114bb0e90628d486aff</url>
      <link>https://chucoding.tistory.com</link>
    </image>
    <item>
      <title>AI가 만들어내는 주석 믿고 써도될까?</title>
      <link>https://chucoding.tistory.com/165</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Gemini_Generated_Image_pb5daxpb5daxpb5d (1).png&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;1024&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/uo7JO/dJMcahDch6g/JJn26WalSljuN3cC5TBWhk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/uo7JO/dJMcahDch6g/JJn26WalSljuN3cC5TBWhk/img.png&quot; data-alt=&quot;&amp;quot;AI가 만들어내는 주석 믿고 써도될까?&amp;quot; - Nano Banana로 생성&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/uo7JO/dJMcahDch6g/JJn26WalSljuN3cC5TBWhk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fuo7JO%2FdJMcahDch6g%2FJJn26WalSljuN3cC5TBWhk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;281&quot; height=&quot;281&quot; data-filename=&quot;Gemini_Generated_Image_pb5daxpb5daxpb5d (1).png&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;1024&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;&quot;AI가 만들어내는 주석 믿고 써도될까?&quot; - Nano Banana로 생성&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&amp;nbsp;저희가 코드에 주석을 다는 이유가 뭘까요? 그 이유는 &quot;코드만으로는 전달이 어려운 맥락&quot;을 남겨서&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;미래의 나와 동료가 더 빠르고 안전하게 이해하고 변경하게 만들기 위해서입니다. 다만 주석은 잘 쓰면 자산, 못 쓰면 부채라서 &quot;무엇을 언제 주석으로 남길지&quot;가 핵심입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최근에는 AI가 만들어내는 주석들에 대해 호기심을 갖고 믿고 이를 그대로 사용해도 될지에 대한 의문점들을 가져보았는데 이번 포스팅에서는 이에 대해 고찰해 본 경험에 대해 작성해 보도록 하겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style7&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  AI가 코드에 주석을 다는 이유는?&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;9788966260959.jpg&quot; data-origin-width=&quot;458&quot; data-origin-height=&quot;603&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bte2EW/dJMcahb8qaJ/K2hrBr6YH3L0JuDTohBxnk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bte2EW/dJMcahb8qaJ/K2hrBr6YH3L0JuDTohBxnk/img.jpg&quot; data-alt=&quot;Clean Code - 로버트 C마틴&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bte2EW/dJMcahb8qaJ/K2hrBr6YH3L0JuDTohBxnk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbte2EW%2FdJMcahb8qaJ%2FK2hrBr6YH3L0JuDTohBxnk%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;207&quot; height=&quot;273&quot; data-filename=&quot;9788966260959.jpg&quot; data-origin-width=&quot;458&quot; data-origin-height=&quot;603&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Clean Code - 로버트 C마틴&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;클린코드(Clean Code)의 저자 로버트 C마틴은 주석이 사실상 필요악이다라고 주장했습니다. 주석을 사용하는 것은 대부분 실패한 경우이기 때문에 가능한 안 쓰는 게 좋고 관리포인트가 늘어서 유지보수가 어려워진다는 점을 단점으로 내세웁니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼에도 대다수의 AI들이 주석을 활용하는 이유가 무엇일까요? AI가 코드를 만들 때 주석도 같이 만드는 이유는, &amp;ldquo;주석이 좋은가?&amp;rdquo;라는 철학 문제라기보다 모델이 &amp;lsquo;사람이 기대하는 산출물&amp;rsquo;을 가장 안전하게 맞추는 습관에 가깝습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어, 대부분의 AI 모델은 인터넷 및 오픈소스에서 많이 본 코드 패턴을 학습하여 따라가는데 대부분은 다음과 같은 형식을 사용할 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;함수 위에 짧은 설명(JSDoc, docstring 등)&lt;/li&gt;
&lt;li&gt;복잡한 로직 사이에 단계 주석&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같은 케이스가 많기 때문에 모델은 &amp;ldquo;코드만 던지면 불친절하다&amp;rdquo;는 분포를 학습합니다. 따라서, 주석을 사용하지 않는 케이스 보다 주석을 사용하는 케이스가 훨씬 더 많기 때문에 AI는 주석을 사용한다는 점을 알 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style7&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;h2 data-ke-size=&quot;size26&quot;&gt;  AI가 만들어내는 주석 특징 파헤치기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면 AI가 만들어내는 주석에는 어떠한 특징들이 있을까요? 과연 믿고 써도 될 정도로 AI가 일반적인 주석 사용 패턴을 따르고 있는지 한번 알아보도록 하겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 언어가 마찬가지만 주석은 팀원, 그리고 코드를 읽는 사람들을 위해 작성하는 것이기 때문에 팀 간의 규칙을 만들어서 적용하는 것도 중요하지만 에디터(IDE)에서 지원이 되는 주석 포맷인지도 중요하다고 생각합니다. 이 부분이 결국 개발자들의 경험(DX)에 영향을 미칠 수 있기 때문에 대부분은 가장 일반적인 주석 컨벤션을 채택하여 사용합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;많이 쓰이는 언어 중 하나인 JS/TS의 가장 대중적인 주석 문서는 JSDoc/TSDoc입니다. 대부분의 에디터들은 표준 JSDoc 어노테이션을 이해해서 자동완성이나 hover 문서, 시그니처 도움말 등을 생성해 냅니다. 대부분의 AI들도 이 표준 문서에 맞게 주석을 생성해 내는 것을 볼 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/drXJ14/dJMcaajMZWm/2WXrkvk32VFh2rXTjxrlRk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/drXJ14/dJMcaajMZWm/2WXrkvk32VFh2rXTjxrlRk/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;836&quot; data-origin-height=&quot;492&quot; data-filename=&quot;스크린샷 2026-01-25 오후 2.59.26.png&quot; style=&quot;width: 40.2386%; margin-right: 10px;&quot; data-widthpercent=&quot;40.71&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/drXJ14/dJMcaajMZWm/2WXrkvk32VFh2rXTjxrlRk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdrXJ14%2FdJMcaajMZWm%2F2WXrkvk32VFh2rXTjxrlRk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;836&quot; height=&quot;492&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/87ByB/dJMcadOhOq8/c9LRDsw3mZ9raZx5eltyak/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/87ByB/dJMcadOhOq8/c9LRDsw3mZ9raZx5eltyak/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;970&quot; data-origin-height=&quot;392&quot; data-filename=&quot;스크린샷 2026-01-25 오후 3.02.28.png&quot; style=&quot;width: 58.5986%;&quot; data-widthpercent=&quot;59.29&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/87ByB/dJMcadOhOq8/c9LRDsw3mZ9raZx5eltyak/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F87ByB%2FdJMcadOhOq8%2Fc9LRDsw3mZ9raZx5eltyak%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;970&quot; height=&quot;392&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;cursor 에디터에서 나타나는 interface props hover(왼쪽)와 함수 설명 hover(오른쪽)&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;다만, 프론트엔드의 경우 JSDoc이나 TSDoc에 존재하지 않는 React 컴포넌트의 경우에는 어떻게 주석을 작성해야 할까요? 만약 컴포넌트에 주석을 만들어달라고 요청하는 순간 AI는 혼란이 오기 시작할 것입니다. 어떨 때는 @property 어노테이션을 만들어서 사용하기도 하고 어떨 때는 컴포넌트를 일반적인 함수와 비슷하다고 생각해서 @param라는 어노테이션을 사용할 때도 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;왜냐하면 AI가 알고 있는 JSDoc에는 React 컴포넌트에 대해 주석 다는 방법에 대한 정의가 없기 때문이죠. 이 때문에 사람들은 프로젝트마다 또는 팀마다 각자만의 방법들과 포맷들을 사용하기 때문에 AI가 일관되지 않는 주석을 사용하면서 우리들에게 일부 혼란을 줄 수 있습니다. 예를 들어, 유명한 오픈소스 CSS 프레임워크 중 하나인 Material UI는 컴포넌트에 다음과 같은 포맷을 주석으로 사용합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2026-01-24 오후 3.43.53.png&quot; data-origin-width=&quot;2242&quot; data-origin-height=&quot;1726&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lX3RY/dJMcaivjIv6/9vsgIZDuQPH7xBnWYZitak/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lX3RY/dJMcaivjIv6/9vsgIZDuQPH7xBnWYZitak/img.png&quot; data-alt=&quot;MUI 컴포넌트 주석 포멧 예시(Select)&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lX3RY/dJMcaivjIv6/9vsgIZDuQPH7xBnWYZitak/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlX3RY%2FdJMcaivjIv6%2F9vsgIZDuQPH7xBnWYZitak%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;611&quot; height=&quot;470&quot; data-filename=&quot;스크린샷 2026-01-24 오후 3.43.53.png&quot; data-origin-width=&quot;2242&quot; data-origin-height=&quot;1726&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;MUI 컴포넌트 주석 포멧 예시(Select)&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;위와 같은 상황에서는 사용하는 사람마다 코드 일관성이 붕괴될 수 있기 때문에 코드베이스가 어느 정도 잡히지 않은 상태에서는 AI 룰 파일에 규칙을 추가해 놓는 것이 좋습니다. 이런 식으로 일반적이지 않은 케이스들에 대해서는 팀원들과 공유하여 어떤 식으로 맞춰가면 좋을지 논의를 통해 룰 북을 완성시켜 혼란을 방지하는 것이 중요합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style7&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;✍️ 우리(Human)는 앞으로 주석을 어떻게 작성해야 할까?&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Gemini_Generated_Image_u69wdtu69wdtu69w.png&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;1024&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/AAeM4/dJMcaaYoEFS/P6vyknKRoBD4E3m5KzZNbk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/AAeM4/dJMcaaYoEFS/P6vyknKRoBD4E3m5KzZNbk/img.png&quot; data-alt=&quot;&amp;quot;AI가 알아듣지 못하는 언어로 소통하는 레지스탕스들&amp;quot; - Nano Banana로 생성&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/AAeM4/dJMcaaYoEFS/P6vyknKRoBD4E3m5KzZNbk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FAAeM4%2FdJMcaaYoEFS%2FP6vyknKRoBD4E3m5KzZNbk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;383&quot; height=&quot;383&quot; data-filename=&quot;Gemini_Generated_Image_u69wdtu69wdtu69w.png&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;1024&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;&quot;AI가 알아듣지 못하는 언어로 소통하는 레지스탕스들&quot; - Nano Banana로 생성&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;저는 AI가 만들어내는 주석을 활용해도 괜찮다고 생각합니다. 실제로 최근 사내 디자인시스템을 구현하면서 AI가 만들어낸 주석을 보고 굉장히 잘 만들어진 오픈소스를 보는 듯한 느낌을 받았습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물론 복잡한 알고리즘을 요구한 경우 저와의 대화내용을 바탕으로 이해하기 쉽도록 line-by-line으로 알고리즘을 설명할 때도 있었지만 이 주석들이 가독성을 헤친다거나 그런 느낌보다는 오히려 한국어로 주석이 작성되기 때문에 모국어가 주는 안정감으로 인해 DX가 높아지는 느낌을 전달받았습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제가 읽기에 부족함이 없었다면 다른 사람들이 보기에도 괜찮을 것이므로 일부로 주석을 지우거나 하지 않아도 된다고 생각합니다. 이 부분이 조금 껄끄러우시다면 AI가 작성한 코드라는 것을 명시할 수도 있을 것 같습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼에도 AI가 절대로 남길 수 없는 주석들이 있습니다. 바로 TODO, deprecated 같은 어노테이션인데요. 우리는 어쩔 수 없이 시간이 없어서 지금 당장 수정이 어려운 경우 또는 코드가 지저분해서 리팩토링 하고 싶은 경우 또는 다른 사람들에게 남기는 메시지 등 다양한 용도로 주석을 작성할 때도 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 AI가 코드베이스 밖의 상황(예를 들어 팀 단위 또는 스쿼드 단위의 스프린트 회의 등)과 변화에 둔감하기 때문에 미래의 나에게 또는 다른 사람들에게 어떠한 메시지를 남겨놓고 싶은 경우에는 사람이 개입해서 주석을 작성할 수밖에 없습니다. 그래서 저는 아직까지는 다음과 같은 주석들은 사람의 손이 필요하다고 생각합니다.&lt;/p&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&amp;ldquo;왜 이 선택을 했는지&amp;rdquo; (대안 대비 이유)&lt;/li&gt;
&lt;li&gt;&amp;ldquo;외부 제약/레거시/버그 회피&amp;rdquo; 기록&lt;/li&gt;
&lt;li&gt;&amp;ldquo;도메인 특성/고객 요구사항&amp;rdquo;&lt;/li&gt;
&lt;li&gt;TODO/deprecated 등의 메시지&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style7&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  정리하기&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Gemini_Generated_Image_h0m23wh0m23wh0m2.png&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;1024&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dIlHLB/dJMcaconnng/2huaAzTPdAardG45sGZnAK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dIlHLB/dJMcaconnng/2huaAzTPdAardG45sGZnAK/img.png&quot; data-alt=&quot;&amp;quot;AI와 인간의 화합&amp;quot; - Nano Banana로 생성&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dIlHLB/dJMcaconnng/2huaAzTPdAardG45sGZnAK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdIlHLB%2FdJMcaconnng%2F2huaAzTPdAardG45sGZnAK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;351&quot; height=&quot;351&quot; data-filename=&quot;Gemini_Generated_Image_h0m23wh0m23wh0m2.png&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;1024&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;&quot;AI와 인간의 화합&quot; - Nano Banana로 생성&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;ldquo;AI가 만들어내는 주석 믿고 써도 될까?&amp;rdquo;라는 주제에 대해서 깊게 고민하게 된 배경은 AI가 사람의 영역에서 어디까지 침범하는 건가 싶은 회의감도 있었고 처음에는 AI가 만들어내는 주석에 왠지 모를 거부감 같은 게 들었었습니다. 그래서 한 번은 제 생각을 정리해보고 싶었고 이번 계기를 통해 제 스스로도 AI 주석에 대해 수용하게 되는 계기가 되지 않았나 싶습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 AI가 주석을 근본 없이 작성하는 것이 아니라 사용하고 있는 코드언어에 맞게 표준화된 가이드 포맷팅에 따라 주석을 작성하기 때문에 에디터에서도 편리하게 사용할 수 있고 생각보다 잘 만들어낸다는 생각도 들었습니다. 특히 혼자 개발하는 솔로 프리너나 인디해커 분들, 특히 비개발자 분들께는 주석이 일종의 유지보수 가이드라인이 될 수 있기 때문에 AI 주석을 적극 활용하시는 방향이 좋을 수도 있겠다는 생각도 듭니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;평생 살면서 모든 언어, 모든 내장 함수를 항상 장기적으로 습득할 수 없다고 생각합니다. 특히, 인간은 망각의 동물이기 때문에 이전에 열심히 외워놓았던 함수나 로직들도 까먹기 마련입니다. 그러나, 한글이나 영어는 죽을 때까지 평생 사용하는 언어이므로 앞으로는 AI 주석도 열심히 활용해서 AI와 소통, 사람과 사람 간의 소통에 적극 활용해보고자 합니다.&lt;/p&gt;
&lt;p data-ke-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>Front-End</category>
      <author>외계공룡</author>
      <guid isPermaLink="true">https://chucoding.tistory.com/165</guid>
      <comments>https://chucoding.tistory.com/165#entry165comment</comments>
      <pubDate>Sun, 25 Jan 2026 16:56:21 +0900</pubDate>
    </item>
    <item>
      <title>6년차 개발자의 2026년 회고록</title>
      <link>https://chucoding.tistory.com/164</link>
      <description>&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;1536&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/2gxEn/dJMcafeblLf/YGiAg1vpnvWCoDkNIxUq2k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/2gxEn/dJMcafeblLf/YGiAg1vpnvWCoDkNIxUq2k/img.png&quot; data-alt=&quot;AI로 만든 이미지 입니다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/2gxEn/dJMcafeblLf/YGiAg1vpnvWCoDkNIxUq2k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F2gxEn%2FdJMcafeblLf%2FYGiAg1vpnvWCoDkNIxUq2k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;252&quot; height=&quot;378&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;1536&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;AI로 만든 이미지 입니다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;올해로 개발자의 길에 접어든 지 6년이 되었습니다. 열정 가득했었던 지난해 들과는 달리 올해는 유독 고난과 역경으로 가득했었던 한 해라는 생각이 먼저 떠오르는데요. 아무래도 힘든 환경에 많이 노출되어있었다 보니 마음가짐을 더욱 단단히 하고 견고해지는 시기를 보냈다는 생각이 듭니다.&lt;br&gt;&amp;nbsp;&lt;br&gt;그리고 환경적으로도 많은 변화가 있었는데요. 이번 회고에서는 올해 느꼈던 제 자신에 대한 새로운 변화에 대해 다시 한번 돌아보며 기록해보는 시간을 가져보도록 하겠습니다.&lt;br&gt;&lt;br&gt;&lt;/p&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style7&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;h2 data-ke-size=&quot;size26&quot;&gt; ‍♂️&amp;nbsp;리더쉽에 대한 고찰&lt;/h2&gt;&lt;figure data-ke-type=&quot;opengraph&quot; data-og-title=&quot;테크포임팩트 랩짱을 마치며&quot; data-ke-align=&quot;alignCenter&quot; data-og-description=&quot;2025년 3월 21일 커넥트데이 행사를 마지막으로 테크포임펙트에서의 랩짱 활동을 모두 마치게 되었습니다. 왠지 모를 아쉬움을 느끼면서도 번아웃이 올 정도로 그동안 정말 열심히 달려왔는데요.&quot; data-og-host=&quot;chucoding.tistory.com&quot; data-og-source-url=&quot;https://chucoding.tistory.com/158&quot; data-og-image=&quot;https://blog.kakaocdn.net/dna/jaPBI/hyZPHs79jW/AAAAAAAAAAAAAAAAAAAAAHqr92kXPxwCBI2qm9S_p_D7LJDOQLqlRmBOxoi6G1FS/img.jpg?credential=yqXZFxpELC7KVnFOS48ylbz2pIh7yKj8&amp;amp;expires=1767193199&amp;amp;allow_ip=&amp;amp;allow_referer=&amp;amp;signature=Im1AYJgebmjYqewMP5T37l%2F0VsQ%3D&quot; data-og-url=&quot;https://chucoding.tistory.com/158&quot;&gt;&lt;a href=&quot;https://chucoding.tistory.com/158&quot; target=&quot;_blank&quot; data-source-url=&quot;https://chucoding.tistory.com/158&quot;&gt;&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://blog.kakaocdn.net/dna/jaPBI/hyZPHs79jW/AAAAAAAAAAAAAAAAAAAAAHqr92kXPxwCBI2qm9S_p_D7LJDOQLqlRmBOxoi6G1FS/img.jpg?credential=yqXZFxpELC7KVnFOS48ylbz2pIh7yKj8&amp;amp;expires=1767193199&amp;amp;allow_ip=&amp;amp;allow_referer=&amp;amp;signature=Im1AYJgebmjYqewMP5T37l%2F0VsQ%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;2025년 3월 21일 커넥트데이 행사를 마지막으로 테크포임펙트에서의 랩짱 활동을 모두 마치게 되었습니다. 왠지 모를 아쉬움을 느끼면서도 번아웃이 올 정도로 그동안 정말 열심히 달려왔는데요.&lt;/p&gt;&lt;p class=&quot;og-host&quot;&gt;chucoding.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;재작년 10월부터 시작한 테크포임팩트 프로젝트가 약 10개월이라는 기간을 거쳐 올해 여름 드디어 여정을 마치게 되었습니다. 16명이라는 많은 유능한 인재들과 함께 하며 리딩할 수 있는 포지션에 배정된 것만 해도 정말 과분한 경험이었다고 생각됩니다. 제 인생 통틀어서 감사한 순간 중에 하나였습니다.&lt;br&gt;&amp;nbsp;&lt;br&gt;물론, 프로젝트의 결과물만 놓고 보았을때는 아쉬움도 많았습니다. 팀 내 열정 가득한 멤버들이 많았기 때문에 아이디어는 많이 창출되었지만 하나로 취합하기가 힘들었고 저 역시도 최종 결정권자로써 아쉬운 모습을 보인 것도 사실입니다.&lt;br&gt;&amp;nbsp;&lt;br&gt;그동안 살아오면서 교내 반장, 군 생활에서의 분대장, 대학교 연구실장을 포함해 다양한 리드 포지션을 맡아오면서 스스로 리더십에는 자신이 있다고 생각했었지만 프로젝트를 기간 내 성공적으로 이끌어야 하는 리드 포지션은 확실한 실력과 자신감을 뒷받침으로 흔들림이 없어야 한다는 점을 이번 계기를 통해 뼈저리게 느끼게 된 것 같습니다. 저는 잘 몰랐기 때문에 멤버들의 의견에 자주 흔들렸고 미움받을 용기가 부족했다고 생각합니다.&lt;br&gt;&amp;nbsp;&lt;br&gt;재미와 흥미만으로 리더에 도전했던 제 자신의 선택에 대한 아쉬움이 많이 남았지만 한편으로는 이번 계기로 제가 이 프로젝트에서 진정으로 원했던게 무엇이었는지 다시금 되돌아보게 되고 리더쉽에 대한 정의도 새롭게 정의 내릴 수 있었습니다.&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style7&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;h2 data-ke-size=&quot;size26&quot;&gt;  이직 준비 시작&lt;/h2&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;1536&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/by5EoP/dJMcadABTJd/4Kv2Vu2h300M4Lq0y4apTK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/by5EoP/dJMcadABTJd/4Kv2Vu2h300M4Lq0y4apTK/img.png&quot; data-alt=&quot;AI로 만든 이미지 입니다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/by5EoP/dJMcadABTJd/4Kv2Vu2h300M4Lq0y4apTK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fby5EoP%2FdJMcadABTJd%2F4Kv2Vu2h300M4Lq0y4apTK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/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;465&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;1536&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;AI로 만든 이미지 입니다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;이번년도에는 회사를 옮기게 되었습니다.&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;조금 더 높다는 판단이 들어서 이직을 결심하게 되었습니다.&lt;br&gt;&amp;nbsp;&lt;br&gt;하지만, IT의 붐은 지나갔고 취업의 문은 더욱 견고해졌다는 점을 몸소 체감하게 되었습니다. 작년 재작년에는 쉽게 통과되었던 서류도 올해는 갑자기 그 문턱이 비정상적으로 올라가 넣는 족족 떨어지는 경험을 하게 되었습니다. 회사가 어려운 만큼 다른 회사도 어렵다는 사실도 알고 있었어야 했는데 그러한 현실들을 간과하고 있었습니다.&lt;br&gt;&amp;nbsp;&lt;br&gt;그럼에도 포기하지 않고 일과가 끝난 후에는 항상 저녁마다 마음을 다잡고 꾸준히 이력서를 넣고 다듬는 시간을 가졌습니다. 그 결과 지원했던 25군데의 기업 중에서 한 군데서 합격 소식을 받을 수 있었고 새로운 도전을 이어나가게 될 수 있었습니다.&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style7&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;h2 data-ke-size=&quot;size26&quot;&gt;  GoodBye 챗봇&lt;/h2&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;1536&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bbtriM/dJMcagqBOUo/RgfhkcaTZ5UEj2z6Ker4C1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bbtriM/dJMcagqBOUo/RgfhkcaTZ5UEj2z6Ker4C1/img.png&quot; data-alt=&quot;AI로 만든 이미지 입니다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bbtriM/dJMcagqBOUo/RgfhkcaTZ5UEj2z6Ker4C1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbbtriM%2FdJMcagqBOUo%2FRgfhkcaTZ5UEj2z6Ker4C1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;349&quot; height=&quot;524&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;1536&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;AI로 만든 이미지 입니다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;이직할 회사가 정해지고 본격적으로 퇴사 준비를 하게되면서 가장 아쉬웠던 점은 저의 페르소나와도 같았던 챗봇을 이제는 놓아줘야 된다는 점이었습니다. 대학 때부터 정말 열정적으로 좋아했던 분야였고 해당 분야의 이해도를 바탕으로 취직도 성공해서 5년 동안 회사를 다닐 수 있었는데 이제는 다른 분야로 떠나야 한다는 현실이 저에게는 너무나도 슬펐습니다.&lt;br&gt;&amp;nbsp;&lt;br&gt;아쉽게도 챗봇에 들어가는 중요한 엔진인 NLP 기술이나 AI 모델등의 경험은 없었고 저는 어디까지나 빌더나 플랫폼, 화면 개발을 주로 했기 때문에 다른 회사에서 챗봇 개발자로 그다지 매리트있는 인재가 아니었습니다. AI 시대가 도래하면서 개발자에게 도메인 성장과 기술적 성장 중에서 도메인 성장이 더욱 중요해지는 시기가 왔다고 하지만 현실은 고급 개발자, 시니어 개발자들에게 해당되는 말이었고 저 같은 주니어 개발자를 뽑을 때는 서류나 면접에서 기술을 훨씬 더 중요하게 판단했습니다. 제가 경력으로 인정받는 기술은 아이러니하게도 프론트엔드였습니다.&lt;br&gt;&amp;nbsp;&lt;br&gt;다행이었던 점은 어느 한쪽에 취우 쳐져서 공부한 게 아니라 두루두루 공부하고 매사에 기회가 주어지면 열심히 임했기 때문에 백엔드든 프론트든 포트폴리오를 작성하거나 면접에서 할 수 있는 말들이 많았었고 그 결과 한 회사에서 프론트엔드 기술을 경력을 인정받아 원하는 연봉보다 더 높은 값어치로 일을 이어갈 수 있게 되었습니다.&lt;br&gt;&amp;nbsp;&lt;br&gt;새롭게 들어간 회사에서는 더 이상 AI 도메인이 아닌 프롭테크를 새로운 분야로 이어나가게 되어 그동안 저를 취업까지 이끌어주었고 애정했던 챗봇을 이제는 놓아주게 되었지만, 언젠가는 또 다시 접하게 되는 날도 있지 않을까 하는 생각도 듭니다. 그 동안 고마웠다 챗봇!&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style7&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;h2 data-ke-size=&quot;size26&quot;&gt;  여가활동 및 취미생활&lt;/h2&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;1536&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/GrYd8/dJMcac2MIW1/9hg5Tn3gWfKKNgFcmyIu21/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/GrYd8/dJMcac2MIW1/9hg5Tn3gWfKKNgFcmyIu21/img.png&quot; data-alt=&quot;AI로 만든 이미지 입니다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/GrYd8/dJMcac2MIW1/9hg5Tn3gWfKKNgFcmyIu21/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FGrYd8%2FdJMcac2MIW1%2F9hg5Tn3gWfKKNgFcmyIu21%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;297&quot; height=&quot;446&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;1536&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;AI로 만든 이미지 입니다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;이번연도는 워낙 스트레스 강도가 높았기 때문에 운동 강도도 비례해서 이전보다 더 높은 강도로 진행했던 것 같습니다. 작년과 동일하게 여전히 F45를 통해 운동을 진행했었고 이번에는 챌린지도 신청하면서 3년 전에 찍었었던 바디프로필 재도전에 나섰습니다.&lt;br&gt;&amp;nbsp;&lt;br&gt;그 결과 현재 체지방률 10 퍼까지 도달할 수 있게 되어서 내년에는 또다시 바디프로필 촬영을 기대해 볼 수 있는 기회를 갖추게 되었습니다.&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;1371&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/7Gerd/dJMcaaRtnQV/QuSQgR45erIMPicQmkYLKK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/7Gerd/dJMcaaRtnQV/QuSQgR45erIMPicQmkYLKK/img.jpg&quot; data-alt=&quot;인바디 측정 결과지&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/7Gerd/dJMcaaRtnQV/QuSQgR45erIMPicQmkYLKK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F7Gerd%2FdJMcaaRtnQV%2FQuSQgR45erIMPicQmkYLKK%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;419&quot; height=&quot;479&quot; data-origin-width=&quot;1200&quot; data-origin-height=&quot;1371&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;인바디 측정 결과지&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;작년과 마찬가지로 올해 춤도 꾸준히 이어왔습니다. 활동을 꾸준히 하다 보니 이전보다 촬영할 때 표정도 자연스러워졌고 주위에서 많이 늘었다는 칭찬을 받는 일도 생겨서 기분이 좋았습니다. 별 일이 없다면 내년에도 꾸준히 취미를 이어가 보고 싶은 생각이 있습니다.&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;figure data-ke-type=&quot;video&quot; data-ke-style=&quot;alignCenter&quot; data-ke-mobilestyle=&quot;widthContent&quot; data-video-host=&quot;youtube&quot; data-video-url=&quot;https://www.youtube.com/watch?v=GxY0n9TTroo&quot; data-video-thumbnail=&quot;https://blog.kakaocdn.net/dna/bYVeU9/hyZPPSeQZR/AAAAAAAAAAAAAAAAAAAAAEIO1RGAOM9bxHGe9eE-L8uAITIU_8DucI_ZA5NxxTUL/img.jpg?credential=yqXZFxpELC7KVnFOS48ylbz2pIh7yKj8&amp;amp;expires=1767193199&amp;amp;allow_ip=&amp;amp;allow_referer=&amp;amp;signature=giPVlDLcIoTDTuOmrjNJH5zXs8s%3D&quot; data-video-width=&quot;480&quot; data-video-height=&quot;360&quot; data-video-origin-width=&quot;480&quot; data-video-origin-height=&quot;360&quot; data-video-title=&quot;[직장인 댄스팀] AsdF / Choreo | LeftRightCenter - My Kinda Night&quot;&gt;&lt;iframe src=&quot;https://www.youtube.com/embed/GxY0n9TTroo&quot; width=&quot;480&quot; height=&quot;360&quot; frameborder=&quot;&quot; allowfullscreen=&quot;true&quot;&gt;&lt;/iframe&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;/figure&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;금년에 아쉬웠던 점은 블로그 포스팅을 많이 못했다는 점입니다. 새로 이직한 회사에서 적응하는 시간과 주말에도 일을 할 정도로 많이 바쁜 시간을 보냈기 때문에 올해는 총 11개의 포스팅에 그치게 되었습니다. 독서도 1권밖에 하지 못했다는 점이 조금 아쉬웠는데요.&lt;br&gt;&amp;nbsp;&lt;br&gt;그럼에도 바쁜 시간을 쪼개어 해커톤을 두 차례나 참여하면서 재작년, 작년과 마찬가지로 AI를 활용하고 접목한 새로운 서비스들을 꾸준히 계속해서 만들어보는 경험들을 챙겼다는 점에 대해서는 많은 뿌듯함을 느끼고 있습니다.&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;figure data-ke-type=&quot;opengraph&quot; data-og-title=&quot;SOULMATE - 후회를 줄이고 더 나은 선택을 돕는 AI 서비스&quot; data-ke-align=&quot;alignCenter&quot; data-og-description=&quot;후회를 줄이고 싶은 사람들을 위한, 결정의 순간을 함께하는 단 하나의 파트너&quot; data-og-host=&quot;soulmate-tenten-frontend.vercel.app&quot; data-og-source-url=&quot;https://soulmate-tenten-frontend.vercel.app/login?callbackUrl=%2F&quot; data-og-image=&quot;https://blog.kakaocdn.net/dna/bq5Fky/hyZPRP2idk/AAAAAAAAAAAAAAAAAAAAACriDdExzVCyVAdyx3fz1wqmLktdlCWbiPOJwPjxnr-U/img.png?credential=yqXZFxpELC7KVnFOS48ylbz2pIh7yKj8&amp;amp;expires=1767193199&amp;amp;allow_ip=&amp;amp;allow_referer=&amp;amp;signature=UqEEmIfzFoMEt3wEiajEDNnTXCs%3D&quot; data-og-url=&quot;https://soulmate-tenten-frontend.vercel.app/login?callbackUrl=%2F&quot;&gt;&lt;a href=&quot;https://soulmate-tenten-frontend.vercel.app/login?callbackUrl=%2F&quot; target=&quot;_blank&quot; data-source-url=&quot;https://soulmate-tenten-frontend.vercel.app/login?callbackUrl=%2F&quot;&gt;&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://blog.kakaocdn.net/dna/bq5Fky/hyZPRP2idk/AAAAAAAAAAAAAAAAAAAAACriDdExzVCyVAdyx3fz1wqmLktdlCWbiPOJwPjxnr-U/img.png?credential=yqXZFxpELC7KVnFOS48ylbz2pIh7yKj8&amp;amp;expires=1767193199&amp;amp;allow_ip=&amp;amp;allow_referer=&amp;amp;signature=UqEEmIfzFoMEt3wEiajEDNnTXCs%3D')&quot;&gt; &lt;/div&gt;&lt;div class=&quot;og-text&quot;&gt;&lt;p class=&quot;og-title&quot;&gt;SOULMATE - 후회를 줄이고 더 나은 선택을 돕는 AI 서비스&lt;/p&gt;&lt;p class=&quot;og-desc&quot;&gt;후회를 줄이고 싶은 사람들을 위한, 결정의 순간을 함께하는 단 하나의 파트너&lt;/p&gt;&lt;p class=&quot;og-host&quot;&gt;soulmate-tenten-frontend.vercel.app&lt;/p&gt;&lt;/div&gt;&lt;/a&gt;&lt;/figure&gt;&lt;figure data-ke-type=&quot;opengraph&quot; data-og-title=&quot;RecallBuddy 2.0 개발 후기&quot; data-ke-align=&quot;alignCenter&quot; data-og-description=&quot;황금연휴였던 추석이 벌써 순식간에 지나가버리고 날씨도 어느덧 쌀쌀해지기 시작한 것 같습니다. 다들 휴식도 취하고 여행도 가보고 가족들과 즐거운 시간을 보내기도 하고 평소에 하고 싶었&quot; data-og-host=&quot;chucoding.tistory.com&quot; data-og-source-url=&quot;https://chucoding.tistory.com/163&quot; data-og-image=&quot;https://blog.kakaocdn.net/dna/bckAtg/hyZQ3af080/AAAAAAAAAAAAAAAAAAAAAIjtA0kW04wvhbAON_SLcBcv_P5Ol3g9U0mNpRzufyOj/img.png?credential=yqXZFxpELC7KVnFOS48ylbz2pIh7yKj8&amp;amp;expires=1767193199&amp;amp;allow_ip=&amp;amp;allow_referer=&amp;amp;signature=DlSjA2IT0AjFUL8fJbhX%2F%2Fd8kZM%3D&quot; data-og-url=&quot;https://chucoding.tistory.com/163&quot;&gt;&lt;a href=&quot;https://chucoding.tistory.com/163&quot; target=&quot;_blank&quot; data-source-url=&quot;https://chucoding.tistory.com/163&quot;&gt;&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://blog.kakaocdn.net/dna/bckAtg/hyZQ3af080/AAAAAAAAAAAAAAAAAAAAAIjtA0kW04wvhbAON_SLcBcv_P5Ol3g9U0mNpRzufyOj/img.png?credential=yqXZFxpELC7KVnFOS48ylbz2pIh7yKj8&amp;amp;expires=1767193199&amp;amp;allow_ip=&amp;amp;allow_referer=&amp;amp;signature=DlSjA2IT0AjFUL8fJbhX%2F%2Fd8kZM%3D')&quot;&gt; &lt;/div&gt;&lt;div class=&quot;og-text&quot;&gt;&lt;p class=&quot;og-title&quot;&gt;RecallBuddy 2.0 개발 후기&lt;/p&gt;&lt;p class=&quot;og-desc&quot;&gt;황금연휴였던 추석이 벌써 순식간에 지나가버리고 날씨도 어느덧 쌀쌀해지기 시작한 것 같습니다. 다들 휴식도 취하고 여행도 가보고 가족들과 즐거운 시간을 보내기도 하고 평소에 하고 싶었&lt;/p&gt;&lt;p class=&quot;og-host&quot;&gt;chucoding.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;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style7&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;h2 data-ke-size=&quot;size26&quot;&gt;  회고하기&lt;/h2&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;1200&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mSOaR/dJMcagRF9NF/YPFvtGSA6dbArbl4y2pBO1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mSOaR/dJMcagRF9NF/YPFvtGSA6dbArbl4y2pBO1/img.jpg&quot; data-alt=&quot;AI로 만든 이미지 입니다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mSOaR/dJMcagRF9NF/YPFvtGSA6dbArbl4y2pBO1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmSOaR%2FdJMcagRF9NF%2FYPFvtGSA6dbArbl4y2pBO1%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;309&quot; height=&quot;464&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;1200&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;AI로 만든 이미지 입니다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;올 한 해는 정말 다사다난 했다고 생각합니다. 회사도 옮겼고 주변에도 많은 변화가 있었고 이전보다는 조금 더 현실과 타협하게 되는 제 자신의 모습과 마주하게 된것 같습니다.&lt;br&gt;&amp;nbsp;&lt;br&gt;특히, 나이를 먹어가면서 체력과 시간의 한계에 부딪히는 경험을 올 한해 많이 느꼈다는 생각이 듭니다. 내년에는 정말 선택과 집중을 통해서 앞으로의 삶의 방향에 있어 조금 더 도움이 되는 쪽으로 발전해 나갈 수 있는 한 해를 보낼 수 있기를 기대해 봅니다.&lt;/p&gt;</description>
      <category>독서 및 기타 활동</category>
      <author>외계공룡</author>
      <guid isPermaLink="true">https://chucoding.tistory.com/164</guid>
      <comments>https://chucoding.tistory.com/164#entry164comment</comments>
      <pubDate>Tue, 30 Dec 2025 22:54:50 +0900</pubDate>
    </item>
    <item>
      <title>RecallBuddy 2.0 개발 후기</title>
      <link>https://chucoding.tistory.com/163</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;onboarding.png&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;1024&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cJ3VXU/dJMb9PsQusB/q9GQFUOyiOcp0GNdofoyl1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cJ3VXU/dJMb9PsQusB/q9GQFUOyiOcp0GNdofoyl1/img.png&quot; data-alt=&quot;ChatGPT로 생성한 RecallBuddy 마스코트&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cJ3VXU/dJMb9PsQusB/q9GQFUOyiOcp0GNdofoyl1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcJ3VXU%2FdJMb9PsQusB%2Fq9GQFUOyiOcp0GNdofoyl1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;261&quot; height=&quot;261&quot; data-filename=&quot;onboarding.png&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;1024&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;ChatGPT로 생성한 RecallBuddy 마스코트&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;황금 연휴였던 추석이 순식간에 지나가버리고 날씨도 어느덧 쌀쌀해지기 시작한 것 같습니다. 저는 이전에 만들어놓았었던 공부 앱 서비스를 개선해보면서 연휴를 보냈었는데요. 이번 포스팅에서는 제가 만든 RecallBuddy라는 서비스를 소개드리고 개발하면서 느낀 점들에 대해 공유드리는 시간을 가져보도록 하겠습니다.&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;a href=&quot;#1&quot;&gt;1. 서비스 탄생 배경&lt;/a&gt;&lt;br /&gt;&lt;a href=&quot;#2&quot;&gt;2. 개발은 혼자지만 마음은 함께하기(solo 포텐데이 소개)&lt;/a&gt;&lt;br /&gt;&lt;a href=&quot;#3&quot;&gt;3. AI와 함께 레거시 아키텍처 뜯어고치기&lt;/a&gt;&lt;br /&gt;&lt;a href=&quot;#4&quot;&gt;4. Firebase 기반 프로젝트를 모노레포로 통합 관리하기&lt;/a&gt;&lt;br /&gt;&lt;a href=&quot;#5&quot;&gt;5. Firebase Authentication를 활용한 Github 로그인 구축하기&lt;/a&gt;&lt;br /&gt;&lt;a href=&quot;#6&quot;&gt;6. Firestore Security Rules 적용하기&lt;/a&gt;&lt;br /&gt;&lt;a href=&quot;#7&quot;&gt;7. Firebase Cloud functions를 활용한 서버리스 백엔드 구축&lt;/a&gt;&lt;br /&gt;&lt;a href=&quot;#8&quot;&gt;8. ChatGPT를 통해 마스코트 캐릭터 디자인하기&lt;/a&gt;&lt;br /&gt;&lt;a href=&quot;#9&quot;&gt;9. 마무리하며&lt;/a&gt;&lt;/blockquote&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style7&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 id=&quot;1&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span&gt; &lt;/span&gt; 서비스 탄생 배경&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RecallBuddy는 2023년 11월에 today-i-learned-alarm(til-alarm)이라는 이름으로 첫 릴리즈된 저만의 개인 공부 앱이었습니다. 말 그대로 혼자서 사용할 목적이었기 때문에 간단한 핵심 로직만 구현되어 있고 외부에 공유할 생각은 없었던 앱이었습니다. &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;그러나 2024년 초에 AI 활용 서비스가 인기가 많아지면서 이 앱에도 AI를 적용해보고 싶다는 욕심이 생겨서 HyperCLOVA X를 활용한 공부 앱 플래시카드 버전(v1.2)으로 업데이트를 진행하였습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dpssht/dJMb9Xxzlh0/wIq5pSya4tXraedsdvqBJ0/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dpssht/dJMb9Xxzlh0/wIq5pSya4tXraedsdvqBJ0/img.gif&quot; data-origin-width=&quot;720&quot; data-origin-height=&quot;1600&quot; data-is-animation=&quot;true&quot; data-filename=&quot;다운로드.gif&quot; style=&quot;width: 49.4186%; margin-right: 10px;&quot; data-widthpercent=&quot;50&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dpssht/dJMb9Xxzlh0/wIq5pSya4tXraedsdvqBJ0/img.gif&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdpssht%2FdJMb9Xxzlh0%2FwIq5pSya4tXraedsdvqBJ0%2Fimg.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/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;1600&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Jtfsj/dJMb9LqrtjN/YCXa4DeN21qKhNnhyjSj9k/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Jtfsj/dJMb9LqrtjN/YCXa4DeN21qKhNnhyjSj9k/img.gif&quot; data-origin-width=&quot;720&quot; data-origin-height=&quot;1600&quot; data-is-animation=&quot;true&quot; data-filename=&quot;다운로드 (1).gif&quot; style=&quot;width: 49.4186%;&quot; data-widthpercent=&quot;50&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Jtfsj/dJMb9LqrtjN/YCXa4DeN21qKhNnhyjSj9k/img.gif&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FJtfsj%2FdJMb9LqrtjN%2FYCXa4DeN21qKhNnhyjSj9k%2Fimg.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/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;1600&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;RecallBuddy v1.0(왼쪽) / RecallBuddy v1.2(오른쪽)&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;span style=&quot;color: #333333; text-align: start;&quot;&gt;글또 9기 커뮤니티의 월간 메이커스) 분들께서도 이 앱을 사용해보고 싶다는 요청이 많았었습니다. 하지만&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;그 당시에 로그인과 인증 기능을 추가하기에는 부담이었었고 1년이 지난 지금에서야 다시 도전할 수 있게 된 것이 이번 2.0 버전의 기획 배경입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제야 갑자기 다시 꺼내든 이유는 현업에서도 최근 프로젝트에서도 로그인, 인증 시스템을 구현하기도 했고 현시점에서는 바이브 코딩이라는 용어가 나올 정도로 AI가 코딩을 워낙 잘하기 지금이라면 빠르고 안정감 있게 구현이 가능하지 않을까? 하는 생각에 서비스를 확장해 보자는 도전을 하게 되었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style7&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 id=&quot;2&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span&gt; &amp;zwj; &amp;zwj; &lt;/span&gt; 개발은 혼자지만 마음은 함께하기&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/IwmVy/dJMb9WZJgWr/2DoF0uRlKnHsjcWzSsjObK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/IwmVy/dJMb9WZJgWr/2DoF0uRlKnHsjcWzSsjObK/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1066&quot; data-origin-height=&quot;1054&quot; data-filename=&quot;프로그램 썸네일.png&quot; style=&quot;width: 35.0025%; margin-right: 10px;&quot; data-widthpercent=&quot;35.41&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/IwmVy/dJMb9WZJgWr/2DoF0uRlKnHsjcWzSsjObK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FIwmVy%2FdJMb9WZJgWr%2F2DoF0uRlKnHsjcWzSsjObK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1066&quot; height=&quot;1054&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lsuYi/dJMb9Onapzu/CN0Oet1xO9TbMnzcXAPW10/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lsuYi/dJMb9Onapzu/CN0Oet1xO9TbMnzcXAPW10/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1103&quot; data-origin-height=&quot;598&quot; data-filename=&quot;image.png&quot; style=&quot;width: 63.8347%;&quot; data-widthpercent=&quot;64.59&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lsuYi/dJMb9Onapzu/CN0Oet1xO9TbMnzcXAPW10/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlsuYi%2FdJMb9Onapzu%2FCN0Oet1xO9TbMnzcXAPW10%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1103&quot; height=&quot;598&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;510 SOLO 포텐데이&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;연휴동안 어떻게 하면 지치지 않고 목표를 달성해 낼 수 있을까 고민하던 찰나에 때 마침 저에게 구세주 같았던 프로그램 SOLO 포텐데이가 등장하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SOLO 포텐데이는 IT 사이드 프로젝트 플랫폼인 비사이드에서 주관하는 프로젝트로 AI와 나 혼자서 도전하는 온라인 챌린지로 소개가 되었습니다. 이번에 실험용으로 공개가 되었는데 참가비는 10만 원이었고 완주하면 환급받을 수 있었기 때문에 부담 없이 참여할 수 있었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;혼자서 진행하는 만큼 굉장히 힘든 여정이 될 수 있지만 중도 포기하지 않도록 이탈방지를 위한 여러 안전장치들이 마련이 되어있었는데요. 날짜별로 간단한 데일리 미션들이 준비되어 있었고 사람들과 이야기 나눌 수 있는 소통 채널과 일정 중간중간에 온라인 모각프(모여서 각자 프로그래밍)도 제공하고 있어 상당히 잘 짜인 프로그램이라는 생각이 들었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이전에 비사이드에서 운영하는 또 다른 프로그램인 포텐데이에도 2회 가량 참여해 본 경험이 있지만, SOLO 포텐데이의 묘미는 AI 팀 빌딩이었습니다. AI 팀 빌딩은 본인이 생각했을 때 어떤 AI 도구들을 사용하여 개발할 것인지를 채널에 소개하고 공유하는 시간을 가지면 되는데요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 때 다른 분들은 어떤 AI들과 함께하는지 파악할 수 있어서 요즘 대세 AI 툴이 어떤 건지? 어떤 툴들을 선호하는지 등도 함께 파악할 수 있어서 유익한 시간이었습니다. 저는 Figma Make와 Claude를 안 써보긴 했지만, 많은 사람들이 선호하고 있다는 점도 이때 처음 깨달았었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style7&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 id=&quot;3&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span&gt; &lt;/span&gt; AI와 함께 레거시 아키텍처 뜯어고치기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로그램이 시작되면서 가장 처음에는 주요 기능들 위주로 작업 목록을 먼저 작성해보았습니다. 우선, 제가 가장 중요한 목표로 잡았던 것은 로그인&amp;amp;인증 서비스이었습니다. 이번 계기를 통해 다른 사람들도 사용할 수 있게끔 앱을 확장하는 것이 이번 2.0 버전의 목표였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나, 구현에 앞서 가장 큰 문젯거리였던 것은 프로젝트를 새로 시작하는게 아니라 2년도 가까이 된 레거시 프로젝트를 유지보수 하면서 작업하려고 하니 오히려 설계가 복잡해지고 불필요한 리스크들이 동반된다는 점이었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어, 지금은 업데이트가 중단된 React CRA(Create React App) PWA 템플릿을 사용하고 있었는데 이 템플릿 위에서 로그인 및 인증서비스를 구현하려면 코드가 더 복잡해지는 문제가 있었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 번째 문제는 PUSH 알림 서비스가 불가능한 상황이 생겨버렸는데요. 알림 서비스는 네이버클라우드 SENS(Simple &amp;amp; Easy Notification Service)를 사용 중이었는데 작년 4월부터 사업자등록을 한 사람에 한 해서만 사용이 가능하도록 정책이 바뀌어 버리는 바람에 PUSH 서비스를 더 이상 사용할 수 없게 되었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;다운로드.png&quot; data-origin-width=&quot;510&quot; data-origin-height=&quot;458&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bl1FMd/dJMb9OgoTMR/gSfnNmhkVBfAKwG4o77ARk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bl1FMd/dJMb9OgoTMR/gSfnNmhkVBfAKwG4o77ARk/img.png&quot; data-alt=&quot;초기 모델(v1) 아키텍처 ( https://chucoding.tistory.com/129 )&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bl1FMd/dJMb9OgoTMR/gSfnNmhkVBfAKwG4o77ARk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbl1FMd%2FdJMb9OgoTMR%2FgSfnNmhkVBfAKwG4o77ARk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;510&quot; height=&quot;458&quot; data-filename=&quot;다운로드.png&quot; data-origin-width=&quot;510&quot; data-origin-height=&quot;458&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;초기 모델(v1) 아키텍처 ( https://chucoding.tistory.com/129 )&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;따라서, SENS 없이도 PUSH알림을 발송할 수 있도록 아키텍처를 개조해야 했었습니다. 이 뿐만 아니라 사실, 기존의 아키텍처는 네이버클라우드 인스턴스에 앱을 직접 배포했었기 때문에 인스턴스 비용, 공인 IP 요금, 네트워크 비용을 추가로 내야 했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;깡통 서버에 Nginx를 사용하여 직접 배포했었기 때문에 도메인도 직접 구매해서 사용했어야 했는데요. 저는 NoIP라는 무료 도메인 호스팅 플랫폼을 사용했지만 달에 한번 주기적으로 만료기간을 체크해야 하는 불편함도 있었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 이번에 마이그레이션을 기획할 때는 기존 기능들은 잘 유지하면서 어떻게 하면 효율적이고 최적화된(low cost, high quality, low maintenance) 아키텍처를 설계할 수 있을지에 대한 고민을 많이 했습니다. 저는 주로 ChatGPT와 아키텍처 토론을 진행했었는데요. 저는 기본적으로 다음과 같은 조건들을 제시했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;github 로그인 및 인증기능 구현&lt;/li&gt;
&lt;li&gt;github API 호출할 수 있는 사용자 별 action token 저장&lt;/li&gt;
&lt;li&gt;PUSH 알림 수신할 수 있는 앱 구현&lt;/li&gt;
&lt;li&gt;github API, hyperCLOVA X API 호출&lt;/li&gt;
&lt;li&gt;매일 8시 알림 발송&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 조건들을 제안했을 때 ChatGPT는 다음과 같은 제안을 하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;ChatGPT Image 2025년 10월 10일 오후 03_03_44 (1).png&quot; data-origin-width=&quot;1536&quot; data-origin-height=&quot;1024&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/eaJVmT/dJMb9MplJIQ/yaTFRBz8Ce9aMzbFNBa3zk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/eaJVmT/dJMb9MplJIQ/yaTFRBz8Ce9aMzbFNBa3zk/img.png&quot; data-alt=&quot;Vercel + Next vs Firebase + Vite&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/eaJVmT/dJMb9MplJIQ/yaTFRBz8Ce9aMzbFNBa3zk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FeaJVmT%2FdJMb9MplJIQ%2FyaTFRBz8Ce9aMzbFNBa3zk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;543&quot; height=&quot;362&quot; data-filename=&quot;ChatGPT Image 2025년 10월 10일 오후 03_03_44 (1).png&quot; data-origin-width=&quot;1536&quot; data-origin-height=&quot;1024&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Vercel + Next vs Firebase + Vite&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;Vercel과 Next 조합은 워낙 유명하고 사람들이 많이 쓰는 조합이기 때문에 저도 ChatGPT에게 물어보기 전에는 이 아키텍처로 가야겠다는 생각을 어느 정도하고 있었는데요. 그러나 ChatGPT가 건네준 제안들 중에 유독 제 관심을 끌었었던 조합은 Firebase 원벤더 기반의 아키텍처였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여태 Firebase를 Messaging 기능만 사용해 보고 다른 서비스들은 사용해 본 적이 없었기 때문에 왜 Firebase기반 아키텍처를 추천하는 거지?라는 의문과 관심이 커지기 시작해서 그 뒤로부터는 ChatGPT와 계속 토론을 하면서 구글 검색도 해보고 의심을 반복하면서 여러 가지 조건들을 뺐다 넣었다 반복하면서 비교를 진행해 보았었는데요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사람이랑 토론하였으면 &amp;ldquo;이 정도까지 물어본다고?&amp;rdquo;라는 생각이 들 정도로 돌다리를 계속 두들겨보면서 씨름한 결과 시간이 굉장히 오래 걸렸지만 나름 만족스러운 이유로 Firebase기반 아키텍처를 최종적으로 선택할 수 있게 되었습니다. Vercel+Next 조합과 Firebase 원벤더 조합은 각각 나름의 장단점이 있었기 때문에 선택하기가 정말 어려웠었는데요. Firebase를 선택했던 이유는 정말 한 끗의 디테일한 차이로 인해 선택을 하게 되었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 Vercel + Next 조합으로 간다고 하면 SSR 방식을 채택하여 API 호출도 하나의 코드베이스로 통합 관리할 수 있고 NextAuth라는 강력한 인증 솔루션을 사용할 수 있기 때문에 github로그인 및 인증도 간편하게 만들 수 있고 특히, SEO에 강력하다는 장점이 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만, 이 조합이 아쉬웠던 점은 DB 보안전략이었습니다. 제가 만들려는 서비스는 최초 로그인한 사용자 계정에 대해 github 토큰을 발급받고 안전하게 저장하고 관리할 수 있어야 하는데 로컬스토리지나 쿠키에서 관리하기에는 위험성이 너무 컸기 때문에 저는 가격이 저렴하면서도 보안과 성능이 뛰어난 DB가 필요한 상황이었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물론, Vercel에서 제공 중인 스토리지 서비스 Vercel KV 또는 Blob을 사용하면 안전하게 데이터 저장은 가능하지만 Firestore만큼의 강력한 권한/보안 규칙을 제공하고 있지는 않기 때문에 약간의 아쉬움이 있었습니다. Vercel KV나 Blob은 저장소 자체에 유저 단위의 세밀한 권한 DSL이 없고 직접 세션이나 쿠키 JWT 등으로 검증하고 권한을 판단하는 로직을 매 엔드포인트마다 반복 구현해야 합니다. 이는 호출 비용 및 유지보수 리스크가 커질 수 있기 때문에 개발하는 입장에서 조금 부담스러웠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반면에 Firestore는 Security Rules 직접 지정할 수가 있는데 이를 통해 유저 단위로 DB접근 권한을 제어할 수 있습니다. 그리고 Firebase Auth랑 1급 통합되기 때문에 모바일, 웹 어떤 환경에서도 동일하게 작동합니다. 특히, 규칙에 의해 거부된 요청은 네트워크 전송량(egress) 과금에서 제외되기 때문에 비용면에서도 효율적입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 외에도 메시지 발송 시스템을 지원하는 강력한 서비스인 FCM을 사용하려면 Firebase를 사용해야 하는데 이 FCM서버가 트리거(API 또는 스케줄러, 크론)들이랑 같은 리전 같은 공간에 있을수록 조금 더 유리하기도 하고 특히, firebase는 스케줄러를 3개까지 지원한다는 장점이 있었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반면 vercel cron은 무료 플랜은 계정당 2개까지만 지원하고 PUSH 메시지들이 큐잉 처리되어 사용자마다 도착 시간도 보장되지 않기 때문에 이런 디테일한 차이점들로 인해 최종적으로 Firebase 아키텍처로 선택하게 되었습니다.&lt;/p&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;917&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/oNC7V/dJMb9QkY96N/2HHTPsjEolMej45SzwNwVk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/oNC7V/dJMb9QkY96N/2HHTPsjEolMej45SzwNwVk/img.png&quot; data-alt=&quot;Firebase 원벤더 기반 아키텍처&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/oNC7V/dJMb9QkY96N/2HHTPsjEolMej45SzwNwVk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FoNC7V%2FdJMb9QkY96N%2F2HHTPsjEolMej45SzwNwVk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;410&quot; height=&quot;367&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;917&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style7&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 id=&quot;4&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span&gt;☀️&lt;/span&gt; Firebase 기반 프로젝트를 모노레포로 통합 관리하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Next, Vercel 조합을 포기하고 Firebase 기반의 SPA 아키텍처를 선택했을 때 가장 부담스러웠던 점은 코드 통합 관리가 어려울 수 있다는 점이었는데요. 처음에는 프론트 백엔드 나누어서 레포지토리를 따로 관리해야 되나 하는 고민을 했었는데 다행히도 Firebase에서 지원하는 서비스들을 통합 배포 관리 할 수 있도록 지원을 하고 있었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 통합 관리하고 싶다면 Firebase CLI 도구를 사용하시면 편리한데요. 이 도구를 활용하면 모노레포 기반으로 프로젝트 구조를 세팅할 수 있고 functions, store, hosting을 전부 묶어서 빌드 및 배포가 가능합니다. 개발환경은 다음과 같은 순서로 셋팅하시면 됩니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;Firebase CLI 설치 npm install -g firebase-tools&lt;/li&gt;
&lt;li&gt;Firebase 프로젝트 만들기 및 앱 등록 (이전 포스팅 참고&amp;nbsp;&lt;a title=&quot;이전 포스팅&quot; href=&quot;https://chucoding.tistory.com/129&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://chucoding.tistory.com/129)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Firebase 로그인 firebase login&lt;/li&gt;
&lt;li&gt;프로젝트 초기화 firebase init&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;firebase init 명령어를 입력하면 콘솔에서 어떤 서비스를 초기화할 것인지 나타나는데 여기서 사용하고자 하는 서비스들을 골라서 사용하시면 됩니다. 만약 호스팅 서비스만 사용하고 싶다면 firebase init hosting 이런 식으로 명령어를 입력하시면 됩니다. 실행이 끝나면 다음과 같이 프로젝트 루트 경로에 firebase.json 파일이 생성됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;/* firebase.json */
{
  &quot;hosting&quot;: {
    &quot;public&quot;: &quot;public&quot;, // 배포할 디렉토리 지정
    &quot;ignore&quot;: [ // 배포시 무시할 파일 지정
      &quot;firebase.json&quot;,
      &quot;**/.*&quot;,
      &quot;**/node_modules/**&quot;
    ],
    &quot;rewrites&quot;: [ // 404방지, URL 단축 등 설정
      {
        &quot;source&quot;: &quot;**&quot;,
        &quot;destination&quot;: &quot;/index.html&quot; // 기본값 : 존재하지 않는 파일 또는 디렉터리에 대한 요청에 대해 index.html 제공
      }
    ]
  }
}
&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;firebase.json은 firebase 배포 설정 파일입니다. 이 파일이 있으면 Firebase CLI를 통해 명령어로 간단하게 통합 빌드 배포 또는 선택적 빌드 배포가 가능해집니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 이 구성에 추가로 firestore와 functions 서비스를 추가하고 싶다면 firebase init firestore와 firebase init functions를 차례대로 입력하시면 됩니다. 명령어 실행시 콘솔에 출력되는 가이드에 따라 필요한 옵션을 선택하여 파일을 구성하시면 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1771048706892&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{
  &quot;hosting&quot;: {
    &quot;public&quot;: &quot;public&quot;,
    &quot;ignore&quot;: [
      &quot;firebase.json&quot;,
      &quot;**/.*&quot;,
      &quot;**/node_modules/**&quot;
    ],
    &quot;rewrites&quot;: [
      {
        &quot;source&quot;: &quot;**&quot;,
        &quot;destination&quot;: &quot;/index.html&quot;
      }
    ]
  },
  &quot;firestore&quot;: {
    &quot;database&quot;: &quot;(default)&quot;,
    &quot;location&quot;: &quot;asia-northeast3&quot;,
    &quot;rules&quot;: &quot;firestore.rules&quot;,
    &quot;indexes&quot;: &quot;firestore.indexes.json&quot;
  },
  &quot;functions&quot;: [
    {
      &quot;source&quot;: &quot;functions&quot;,
      &quot;codebase&quot;: &quot;default&quot;,
      &quot;disallowLegacyRuntimeConfig&quot;: true,
      &quot;ignore&quot;: [
        &quot;node_modules&quot;,
        &quot;.git&quot;,
        &quot;firebase-debug.log&quot;,
        &quot;firebase-debug.*.log&quot;,
        &quot;*.local&quot;
      ],
      &quot;predeploy&quot;: [
        &quot;npm --prefix \&quot;$RESOURCE_DIR\&quot; run lint&quot;,
        &quot;npm --prefix \&quot;$RESOURCE_DIR\&quot; run build&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;만약, 폴더 경로나 불필요한 설정들이 포함됬다면 입맛에 맞게 JSON 파일을 재구성하시면 됩니다. 마찬가지로 프로젝트 구조도 같이 구성이 되는데 이 부분도 필요에 맞게 재구성하실 수도 있습니다. 다만, 재구성 하시는 경우 자동으로 생성된 CI 설정이나 설정 파일내 경로 들도 직접 수동으로 수정해주셔야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;MyProject
 |- app             /* Firebase Hosting에 배포할 frontent app */
     |- public
     |- src
     |- index.html
     |- package.json
 |- functions       /* Firebase Cloud Functions에 배포할 Backend 함수들 */
     |- src
         |- github.ts
         |- clova.ts
         |- index.ts
         |- schedule.ts
     |- .eslintrc.js
     |- .gitignore
     |- package.json
     |- tsconfig.dev.json
     |- tsconfig.json
 |- .firebaserc
 |- .gitignore
 |- firebase.json   /* firebase 설정 파일 */
 |- firestore.rules /* firestore Security Rules 설정 파일 */
 |- firestore.indexes.json /* firestore DB 인덱스 설정 파일 */
 |- package.json
 |- pnpm-lock.yaml
 |- pnpm-workspace.yaml&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;마지막으로 모노레포의 package.json 설정 파일은 다음과 같이 구성하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;/* package.json */
{
  &quot;private&quot;: true,
  &quot;packageManager&quot;: &quot;pnpm@10.18.0&quot;,
  &quot;workspaces&quot;: [
    &quot;app&quot;,
    &quot;functions&quot;
  ],
  &quot;scripts&quot;: {
    &quot;proxy&quot;: &quot;node scripts/setup-proxy.js&quot;,
    &quot;serve&quot;: &quot;firebase emulators:start --only functions&quot;,
    &quot;dev&quot;: &quot;pnpm --filter app dev&quot;,
    &quot;build&quot;: &quot;pnpm -r build&quot;,
    &quot;push&quot;: &quot;firebase deploy&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;저는 총 5가지 스크립트 명령어를 사용했는데요. proxy의 경우에는 개발환경이나 프로덕션 환경에서 프론트 앱에서 백엔드(functions) API URL을 환경변수에 자동으로 셋팅해주는 스크립트를 별도로 만들어 놓았고 serve와 dev 명령어로는 각각 functions를 실행하기 위한 애뮬레이터와 vite기반의 프론트 웹 서버를 구동시킬 수 있도록 구성해 놓았습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 serve와 dev 명령어를 하나로 합치려고 했지만 애뮬레이터와 vite 서버 둘 다 forground로 동작하는 프로세스이기 때문에 동시에 실행시키려면 concurrently 같은 별도의 모듈이 필요한 상황이었고 또 동시에 실행시키면 vite 서버가 먼저 구동되어 API 오류가 발생하는 문제가 있었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 문제를 해결하기 위해 애뮬레이터가 먼저 실행시킬 수 있도록 서버 구동 포트 오픈 감지 체크 로직을 추가하였지만 애뮬레이터가 내부적으로 준비되지 않았는지 vite 서버 구동 명령어가 지속적으로 씹히는 현상이 있어서 결국에는 나눠서 실행시키는 로직으로 롤백하게 되었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 개발할 때는 애뮬레이터가 다 켜진 것을 확인한 후에 새로운 터미널창을 열어서 vite를 기동해야 하는데요. 애뮬레이터를 기동 할 때 주의할 점은 --only 옵션을 사용하지 않으면 firebase.json 파일에 설정해 놓은 모든 firebase 서비스가 기동 되기 때문에 애뮬레이터가 무거워져서 로딩속도가 길어질 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히, firestore 애뮬레이터는 실행하려면 Java Runtime이 필요하기 때문에 Java설치 후 PATH를 설정해야 사용가능하다는 불편함이 있습니다. 반면, Functions는 Node.js 기반에서 동작하기 때문에 애뮬레이터를 기동 하려면 NodeJS가 필요합니다. 이러한 문제점들이 있어서 저는 애뮬레이터를 기동 할 때는 --only 옵션을 사용하여 firestore는 제외하고 Firebase Cloud Funtcions만 구동될 수 있도록 설정하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;build와 push는 각각 프로젝트를 빌드하고 배포하는 명령어입니다. 빌드는 tsc와 vite를 활용하여 빌드할 수 있고 배포할 때는 firebase deploy 명령어로 손쉽게 배포할 수 있습니다. 저는 pnpm 명령어 사용이 익숙하여 pnpm push로 배포될 수 있도록 명령어를 추가하였습니다. 참고로 pnpm deploy는 예약어가 이미 있기 때문에 deploy로 정의할 경우 pnpm run deploy 명령어를 사용해야 배포가 가능합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style7&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 id=&quot;5&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span&gt; &lt;/span&gt; Firebase &lt;span data-token-index=&quot;1&quot;&gt;Authentication&lt;/span&gt;를 활용한 Github 로그인 구축하기&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;image (2).png&quot; data-origin-width=&quot;1016&quot; data-origin-height=&quot;662&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/DT5c7/dJMb83Ezt5L/tkim9JRjwRDlt0HchWNZE1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/DT5c7/dJMb83Ezt5L/tkim9JRjwRDlt0HchWNZE1/img.png&quot; data-alt=&quot;RecallBuddy 로그인화면&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/DT5c7/dJMb83Ezt5L/tkim9JRjwRDlt0HchWNZE1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FDT5c7%2FdJMb83Ezt5L%2Ftkim9JRjwRDlt0HchWNZE1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1016&quot; height=&quot;662&quot; data-filename=&quot;image (2).png&quot; data-origin-width=&quot;1016&quot; data-origin-height=&quot;662&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;RecallBuddy 로그인화면&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;NextAuth나 Supabase를 쓰지 않고도 Firebase에서도 Github 소셜 로그인을 간단하게 구축할 수 있습니다. Firebase Authentication을 서비스를 사용하시면 되는데요. Github 외에도 Google이나 facebook, Twitter 등 다양한 소셜 로그인들을 지원하고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;image (3).png&quot; data-origin-width=&quot;1828&quot; data-origin-height=&quot;916&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/uzdi7/dJMb9MiAJUe/fMD8ysSKsJlBqPxAi0YLX0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/uzdi7/dJMb9MiAJUe/fMD8ysSKsJlBqPxAi0YLX0/img.png&quot; data-alt=&quot;Firebase Authentication 로그인 방법 설정&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/uzdi7/dJMb9MiAJUe/fMD8ysSKsJlBqPxAi0YLX0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fuzdi7%2FdJMb9MiAJUe%2FfMD8ysSKsJlBqPxAi0YLX0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/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;916&quot; data-filename=&quot;image (3).png&quot; data-origin-width=&quot;1828&quot; data-origin-height=&quot;916&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Firebase Authentication 로그인 방법 설정&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;제가 만들고자 하는 앱은 Github 기반으로 동작하기 때문에 처음부터 Github 소셜로그인만 생각하고 있어서 이 서비스가 적합하다고 생각했습니다. Firebase Authentication을 사용하기 위해서는 다음과 같은 순서로 온보딩 해주시면 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://firebase.google.com/docs/web/setup?authuser=0&amp;amp;hl=ko&quot;&gt;Firebase 프로젝트 및 Javascript 앱 생성&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Firebase console에서&amp;nbsp;&lt;b&gt;인증(Authentication)&lt;/b&gt;&amp;nbsp;섹션 선택&lt;/li&gt;
&lt;li&gt;&lt;b&gt;로그인 방법&lt;/b&gt;&amp;nbsp;탭에서&amp;nbsp;&lt;b&gt;GitHub&lt;/b&gt;&amp;nbsp;제공업체를 사용 설정&lt;/li&gt;
&lt;li&gt;GitHub에서 개발자 애플리케이션으로&amp;nbsp;&lt;a href=&quot;https://github.com/settings/applications/new&quot;&gt;앱을 등록&lt;/a&gt;하고 앱의 OAuth 2.0&amp;nbsp;&lt;b&gt;클라이언트 ID&lt;/b&gt;와&amp;nbsp;&lt;b&gt;클라이언트 보안 비밀번호&lt;/b&gt;를 가져와서 등록&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/settings/developers&quot;&gt;GitHub 앱 구성&lt;/a&gt;의 앱 설정 페이지에서 Firebase&amp;nbsp;&lt;b&gt;OAuth 리디렉션 URI&lt;/b&gt;(예:&amp;nbsp;&lt;b&gt;my-app-12345.firebaseapp.com/__/auth/handler&lt;/b&gt;)가&amp;nbsp;&lt;b&gt;승인 콜백 URL&lt;/b&gt;로 설정되어 있는지 확인&lt;/li&gt;
&lt;li&gt;&lt;b&gt;저장&lt;/b&gt; 클릭&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 순서는 아래 공식가이드에서도 확인하실 수 있습니다.&lt;/p&gt;
&lt;figure id=&quot;og_1760882491265&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;자바스크립트에서 GitHub를 사용하여 인증 &amp;nbsp;|&amp;nbsp; Firebase Authentication&quot; data-og-description=&quot;의견 보내기 자바스크립트에서 GitHub를 사용하여 인증 컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠를 저장하고 분류하세요. 앱에 GitHub 인증을 통합하여 사용자가 GitHub 계정을 통&quot; data-og-host=&quot;firebase.google.com&quot; data-og-source-url=&quot;https://firebase.google.com/docs/auth/web/github-auth?hl=ko&quot; data-og-url=&quot;https://firebase.google.com/docs/auth/web/github-auth?hl=ko&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://firebase.google.com/docs/auth/web/github-auth?hl=ko&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://firebase.google.com/docs/auth/web/github-auth?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;자바스크립트에서 GitHub를 사용하여 인증 &amp;nbsp;|&amp;nbsp; Firebase Authentication&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;의견 보내기 자바스크립트에서 GitHub를 사용하여 인증 컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠를 저장하고 분류하세요. 앱에 GitHub 인증을 통합하여 사용자가 GitHub 계정을 통&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;firebase.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;위 설정까지 마무리되었다면 이제 클라이언트를 개발할 차례입니다. 먼저 app 폴더 package 의존성에 다음과 같이 firebase SDK를 추가합니다.&lt;/p&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;pnpm add firebase --filter app
&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;이 SDK는 hosting에 배포할 firebase client app에서 firebase auth 또는 firestore 등에 접근할 수 있도록 도와주는 라이브러리입니다. 해당 SDK 의존성 모듈을 추가하고 나면 다음과 같이 firebase 앱을 초기화할 수 있는 로직을 추가할 수 있습니다.&lt;/p&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;/* firebase.ts */
import { initializeApp, getApps, getApp } from 'firebase/app';
import { getAuth, GithubAuthProvider } from 'firebase/auth';
import { getFirestore } from 'firebase/firestore';

const firebaseConfig = {
  apiKey: import.meta.env.VITE_API_KEY,
  authDomain: import.meta.env.VITE_AUTH_DOMAIN,
  projectId: import.meta.env.VITE_PROJECT_ID,
  storageBucket: import.meta.env.VITE_STORAGE_BUCKET,
  messagingSenderId: import.meta.env.VITE_MESSAGING_SENDER_ID,
  appId: import.meta.env.VITE_APP_ID,
  measurementId: import.meta.env.VITE_MEASUREMENT_ID,
};

// Firebase 앱 초기화 (이미 초기화된 경우 기존 앱 사용)
const app = getApps().length === 0 ? initializeApp(firebaseConfig) : getApp();

// Auth 인스턴스 생성
export const auth = getAuth(app);

// Firestore 인스턴스 생성
export const store = getFirestore(app);

// GitHub 프로바이더 생성
export const githubProvider = new GithubAuthProvider();

githubProvider.addScope('user:email');
githubProvider.addScope('repo');
&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;저는 src 폴더 하위에 firebase.ts라는 파일을 만들어서 이 앱에서 사용할 Firebase 관련 코드를 전부 한 곳으로 모아서 관리하였는데요. Firebase Authentication이나 Firestore 모듈을 사용하려면 Firebase 인스턴스를 매개변수로 전달해야 하기 때문에 메모리를 효율적으로 사용하기 위해서는 위와 같이 싱글톤 패턴으로 정의해 놓는 것이 좋습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 github 로그인 시 사용자로부터 권한을 받도록 설정할 수 있는데 이때 필요한 Scope들을 추가하실 수 있습니다. 예를 들어, 로그인 후 프로필 정보를 받아오고 싶다면 user 스코프를 적용하시면 되고 private repo 접근 권한을 얻고 싶다면 repo 스코프를 추가하면 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Github 로그인 기능은 간단하게 signInWithPopup() 함수를 호출할 수 있는 버튼만 구현해 놓으면 팝업창 형태로 github 로그인 연동이 가능합니다. 따라서 앞서 만들어놓은 firebase.ts 파일을 임포트 하여 생성한 authentication 객체와 githubProvider객체를 signInWithPopup() 함수의 매개변수로 넣어주기만 하면 코드 한 줄로 간편하게 로그인 기능을 만들 수가 있습니다.&lt;/p&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;import { auth, githubProvider } from '../firebase';

const result = await signInWithPopup(auth, githubProvider);
&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;사용자가 github 로그인을 완료했다면 result값에 앞서 설정한 scope에 따른 사용자의 정보가 담겨오는데요. 이 결괏값에 따라 추가적인 처리방식을 원하는 방향에 맞게 구현해 주시면 됩니다. 만약에 제가 만들고자 하는 앱처럼 내부적으로 github API 호출을 사용해야 된다면 OAuth 토큰이 필요하기 때문에 GithubAuthProvider를 통해 액세스토큰을 발급받아 별도로 저장해놓아야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만, 이 토큰을 발급받는 경우 로컬스토리지나 쿠키는 보안상 위험할 수 있기 때문에 DB에 저장하는 것이 가장 안전한데요. 저는 다음과 같이 firestore에 저장하는 로직을 추가했습니다. firestore는 기본적으로 Google의 at-rest encryption으로 자동 암호화 처리가 되기 때문에 안전하게 보관이 가능합니다.&lt;/p&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;/* Login.tsx */
import { signInWithPopup, signOut, GithubAuthProvider } from 'firebase/auth';
import { doc, setDoc, updateDoc, getDoc } from 'firebase/firestore';
import { auth, githubProvider, store } from '../firebase';

export default function Login {

  const handleGitHubLogin = async () =&amp;gt; {
    try {
      /* Github 로그인 팝업창 오픈 */
      const result = await signInWithPopup(auth, githubProvider);
      
      const credential = GithubAuthProvider.credentialFromResult(result);
      if (credential &amp;amp;&amp;amp; credential.accessToken &amp;amp;&amp;amp; result.user) {
        /* 로그인 성공시 firestore에서 유저 정보 조회*/
        const userDocRef = doc(store, 'users', result.user.uid);
        const userDoc = await getDoc(userDocRef);
        
        /* 유저 정보 있는 경우 토큰 갱신 */
        if (userDoc.exists()) {
          await updateDoc(userDocRef, {
            githubToken: credential.accessToken,
            updatedAt: new Date().toISOString(),
          });
        } else {
          /* 최초 로그인인 경우 토큰 저장 */
          await setDoc(userDocRef, {
            githubToken: credential.accessToken,
            updatedAt: new Date().toISOString(),
          });
        }
      }
      
      console.log('로그인 성공');
    } catch (error: any) {
      console.error('로그인 실패:', error);
    }
  };
  
  return (
	  &amp;lt;button 
      onClick={handleGitHubLogin}
      className=&quot;github-login-button&quot;
      disabled={loading}
    &amp;gt;
      &amp;lt;img src=&quot;/github-mark-white.svg&quot; alt=&quot;GitHub Logo&quot; className=&quot;github-icon&quot; /&amp;gt;
      GitHub로 로그인
    &amp;lt;/button&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;Github 로그인 UI 같은 경우에는 별도로 공식적인 디자인 가이드가 나와있지는 않지만, 아래 공식로고를 활용하여 디자인해 주시면 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1760882573209&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 &amp;middot; Build and ship software on a single, collaborative platform&quot; data-og-description=&quot;Join the world's most widely adopted, AI-powered developer platform where millions of developers, businesses, and the largest open source community build software that advances humanity.&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/logos&quot; data-og-url=&quot;https://github.com&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/beL4Oz/hyZLZlTmZH/ABKNjgWe5zRqL1g84v4K8k/img.jpg?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/cuLD8l/hyZLhHdNc5/LbYB9snuX5Rq80OX1oqdDk/img.jpg?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630&quot;&gt;&lt;a href=&quot;https://github.com/logos&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/logos&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/beL4Oz/hyZLZlTmZH/ABKNjgWe5zRqL1g84v4K8k/img.jpg?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/cuLD8l/hyZLhHdNc5/LbYB9snuX5Rq80OX1oqdDk/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;GitHub &amp;middot; Build and ship software on a single, collaborative platform&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Join the world's most widely adopted, AI-powered developer platform where millions of developers, businesses, and the largest open source community build software that advances humanity.&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;로그인 구현까지 완료가 되었다면 이제 인증 기능을 만들어주시면 되는데요. 만약에 인증이 된 유저라면 메인페이지를 보여주고 인증이 안된 유저라면 로그인 페이지를 보여주는 로직이 필요합니다. 원래는 이 로직을 SPA에서 구현하려면 손이 많이 가긴 하지만 다행히도 firebase auth 라이브러리에서 onAuthStateChanged라는 함수를 제공하고 있어 auth 상태를 손쉽게 감지할 수가 있습니다.&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;import React, { useEffect, useState } from 'react';
import { onAuthStateChanged, User } from 'firebase/auth';

const App: React.FC = () =&amp;gt; {
  const [user, setUser] = useState&amp;lt;User | null&amp;gt;(null);

  /* 인증 상태 감지 */
  useEffect(() =&amp;gt; {
    const unsubscribe = onAuthStateChanged(auth, (user) =&amp;gt; {
      setUser(user);
      setAuthLoading(false);
    });

    return () =&amp;gt; unsubscribe();
  }, []);
  
  // 로그인되지 않은 경우
  if (!user) {
    return &amp;lt;Login /&amp;gt;;
  }
  
  return (&amp;lt;main&amp;gt;...&amp;lt;/main&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;위와 같이 useEffect() 훅 내에서 onAuthStateChanged()를 구독하고 앱 종료 시 clean up 함수를 통해 구독을 해지하시면 됩니다. onAuthStateCanged() 함수가 Unsubscribe 타입을 리턴하도록 구현이 되어있는데 이 타입은 함수로 정의가 되어있어서 이 함수를 호출하면 구독이 취소되도록 subscription 패턴을 갖고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서, 앱을 다시 열 때마다 리스너가 계속 쌓여서 메모리 누수가 발생하지 않도록 위와 같은 형태로 구현해 주시면 되고 onAuthStateChanged에서 인증 상태가 감지될 때마다 useState로 정의된 user값의 상태값이 null로 변하는지를 체크해 주시면 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 firebase 및 github 설정, 로그인기능, 인증기능까지 구현을 모두 완료하시면 firebase 기반의 github 로그인 구현이 완료되었습니다. 해당 가이드는 아래 공식문서에서도 참고하실 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1760882610420&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;자바스크립트에서 GitHub를 사용하여 인증 &amp;nbsp;|&amp;nbsp; Firebase Authentication&quot; data-og-description=&quot;의견 보내기 자바스크립트에서 GitHub를 사용하여 인증 컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠를 저장하고 분류하세요. 앱에 GitHub 인증을 통합하여 사용자가 GitHub 계정을 통&quot; data-og-host=&quot;firebase.google.com&quot; data-og-source-url=&quot;https://firebase.google.com/docs/auth/web/github-auth?hl=ko&quot; data-og-url=&quot;https://firebase.google.com/docs/auth/web/github-auth?hl=ko&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://firebase.google.com/docs/auth/web/github-auth?hl=ko&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://firebase.google.com/docs/auth/web/github-auth?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;자바스크립트에서 GitHub를 사용하여 인증 &amp;nbsp;|&amp;nbsp; Firebase Authentication&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;의견 보내기 자바스크립트에서 GitHub를 사용하여 인증 컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠를 저장하고 분류하세요. 앱에 GitHub 인증을 통합하여 사용자가 GitHub 계정을 통&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;firebase.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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style7&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 id=&quot;6&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span&gt; &lt;/span&gt; Firestore Security Rules 적용하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞서 Firestore를 사용하면 Security Rules를 적용할 수 있기 때문에 vercel 대신 firebase 기반 아키텍처를 선택했었다고 말씀드렸었는데요. Security Rules는 다음과 같이 프로젝트 루트 경로에 firestore.rules라는 파일을 만들어놓고 사용하시면 됩니다. 저는 다음과 같은 규칙들을 한번 적용해 보았습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;groovy&quot;&gt;&lt;code&gt;/* firestore.rules */

/* Cloud Firestore 보안 규칙 버전 2 사용 */
rules_version = '2';

service cloud.firestore {
  match /databases/{database}/documents {
    /* 사용자 컬렉션: 본인 데이터 읽기/쓰기 가능 */
    match /users/{userId} {
      allow read, write: if request.auth != null &amp;amp;&amp;amp; request.auth.uid == userId;
    }
    
    /* 탈퇴한 사용자 기록 (재가입 방지용) */
    match /deletedUsers/{userId} {
      allow read, create: if request.auth != null &amp;amp;&amp;amp; request.auth.uid == userId;
    }
    
    /* 공지사항: 읽기 전용 (쓰기는 Firebase Console에서만) */
    match /notices/{noticeId} {
      allow read: if request.auth != 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;먼저 저는 다른 사용자들은 본인 데이터에만 접근할 수 있도록 규칙을 적용했고 로그인하지 않으면 무조건 접근할 수 없도록 조치하였습니다. 또한, 탈퇴한 사용자들이 재가입을 반복하며 call 낭비가 되지 않도록 방지하기 위해 삭제 유저들에 대한 테이블을 하나 더 만들어놓았는데요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 테이블은 12시가 지나면 초기화되어 다시 재가입을 할 수 있도록 스케줄을 구성하였습니다. 따라서 그전에 재가입을 시도하는 경우 로그인이 불가능하도록 조치하기 위해 재가입 방지용 DLS를 추가해 놓았습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막은 제가 앱 관리를 용이하게 하기 위해 사용자들에게 노티를 간편하게 줄 수 있도록 알림용 테이블을 하나 더 만들어놓았습니다. 해당 테이블은 당연히 쓰기는 허용되면 안 되므로 로그인한 유저에 한해서 읽기만 가능하도록 보안 규칙을 적용하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 처럼 모든 접근은 인증을 필수로 하고 최소 권한만 적용하여 데이터를 완전하게 격리하여 안전하게 보호되도록 구현할 수 있었습니다. firebase.rules는 별도로 빌드할 필요 없이 firebase deploy 명령어로 인해 같이 배포됩니다. 만약, Rules만 별도로 배포하고 싶다면 다음과 같이 명령어를 입력해 주시면 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;jboss-cli&quot;&gt;&lt;code&gt;firebase deploy --only firestore:rules
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style7&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 id=&quot;7&quot; data-ke-size=&quot;size26&quot;&gt;  Firebase Cloud functions를 활용한 서버리스 백엔드 구축&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Firebase용 Cloud Functions는 백그라운드 이벤트, HTTPS 요청, Admin SDK 또는 Cloud Scheduler 작업에 의해 트리거 되는 이벤트에 응답하여 백엔드 코드를 자동으로 실행할 수 있는 서버리스 프레임워크입니다. 프로젝트 초기화 명령어 사용 시 typescript, javascript, python 3가지 언어 중에서 선택하여 함수들을 구현할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;functions 프로젝트를 초기화하고 나면 src폴더 하위에 index.ts 파일이 생성되는데 이 파일에서 서버리스로 배포할 함수들을 re-export 하면 Cloud Functions로 배포할 수 있습니다. 저는 아래와 같이 github API, clova API, schedule API 3개로 구분하여 파이프라인을 구축하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;clean&quot;&gt;&lt;code&gt;import {setGlobalOptions} from &quot;firebase-functions&quot;;
import {initializeApp} from &quot;firebase-admin/app&quot;;

// For cost control, set the maximum number of containers that can be
// running at the same time.
setGlobalOptions({maxInstances: 10});

initializeApp();

export * from './github.js';
export * from './schedule.js';
export * from './clova.js';
&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;functions 모듈도 마찬가지로 index.ts 파일 내에서 initializeApp()을 호출하여 프로젝트를 초기화해주어야 하는데요. 클라이언트 SDK(firebase/app)에서 사용할 때는 프로젝트 정보를 환경변수에 저장해 놓고 불러와서 매개변수로 config를 전달했었지만 functions에서는 서버용 Admin SDK(firebase-admin/app)를 사용하기 때문에 빈 매개변수로 전달해도 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버용 Admin SDK는 Google Cloud 환경에서 실행되기 때문에 자동으로 감지가 되고 민감한 설정을 코드에 넣지 않아도 돼서 보안상에도 이점이 있습니다. 반면에 브라우저는 어디에 연결할지 모르는 신뢰할 수 없는 환경에서 사용되기 때문에 반드시 Config로 명시해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;setGlobalOptions는 동시에 실행 가능한 함수 인스턴스(컨테이너) 최대 개수를 제한하는 옵션입니다. 트래픽이 증가하면 Firebase에서 자동으로 함수 인스턴스를 늘리는데 10개를 초과하는 요청은 대기하거나 서응을 저하시킵니다. 이 옵션을 추가한 이유는 혹시 모를 비용 폭탄을 방지하기 위해 추가해 놓았습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;추가로, functions 모듈은 기본적으로 CommonJS 형태로 빌드가 됩니다. 이는 번들러(tsup, webpack)에게 빌드를 맡기는 최신 모듈러 웹 기반 처리방식과는 거리가 조금 있기 때문에 re-export 할 때는 위와 같이 .js 확장자를 붙여줘야 빌드시 오류가 발생하지 않습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물론 CommonJS 대신 module 타입의 최신 빌드 방식을 선택하고 tsup 등의 번들러를 통해 빌드되도록 변경하면 좀 더 type-safe 하도록 클라이언트와의 타입 공유도 가능한 이점들이 있지만 firebase에서는 권장하지 않는 패턴입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 공유 type이 많다면 고려해 볼 만 하지만 그렇지 않다면 자칫 오버엔지니어링이 될 수 있고 tsup의 경우 전체 번들링이 되기 때문에 증분 빌드 방식인 tsc에 비해 빌드 속도가 더 느릴 수도 있습니다. 서버리스의 경우 번들 크기 최적화가 중요하지 않기 때문에 기존 설정을 유지하는 것이 좋습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style7&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 id=&quot;8&quot; data-ke-size=&quot;size26&quot;&gt;  ChatGPT를 통해 마스코트 캐릭터 디자인하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 2.0 버전에서는 서비스 로직에 비해 디자인과 퍼블리싱은 전적으로 AI에게 맡겨보았습니다. 별도로 디자인시스템과 스타일 가이드를 주어주지 않고 AI에게 한번 창의력을 발휘하도록 맡겨 보았는데요. 마스코트로 사용할 캐릭터 디자인의 경우에는 최악의 디자인이 나왔던 것 같고 반면에 cursor에게 전체적인 컬러나 스타일링을 맡겼을 때는 만족스럽게 잘 뽑혔던 것 같습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Cursor의 경우 Agent Auto모드를 사용했는데 별도로 추가 프롬프팅을 하지 않아도 코드베이스 인덱싱이 잘 되어있어서 그런지 &amp;ldquo;로그인 페이지 만들어줘.&amp;rdquo; 등의 가벼운 질문만 던져도 전반적인 프로젝트의 분위기에 맞게 컬러와 레이아웃을 알맞게 잘 만들어냈다는 생각이 들었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/5rLob/dJMb8V0Qbjw/SQkE9wxKQhllD99NoDW9T0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/5rLob/dJMb8V0Qbjw/SQkE9wxKQhllD99NoDW9T0/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;367&quot; data-origin-height=&quot;656&quot; data-filename=&quot;image.png&quot; style=&quot;width: 32.7462%; margin-right: 10px;&quot; data-widthpercent=&quot;33.53&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/5rLob/dJMb8V0Qbjw/SQkE9wxKQhllD99NoDW9T0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F5rLob%2FdJMb8V0Qbjw%2FSQkE9wxKQhllD99NoDW9T0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/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;656&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nMZqw/dJMb9PGnQrn/1mkuwIlIbaCospTRBTTFY0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nMZqw/dJMb9PGnQrn/1mkuwIlIbaCospTRBTTFY0/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;365&quot; data-origin-height=&quot;653&quot; data-filename=&quot;image (1).png&quot; style=&quot;width: 32.7174%; margin-right: 10px;&quot; data-widthpercent=&quot;33.5&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nMZqw/dJMb9PGnQrn/1mkuwIlIbaCospTRBTTFY0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnMZqw%2FdJMb9PGnQrn%2F1mkuwIlIbaCospTRBTTFY0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/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;653&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/GAx5i/dJMb9M3XH0f/XukncEIE4uQYcORXvYdAU1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/GAx5i/dJMb9M3XH0f/XukncEIE4uQYcORXvYdAU1/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;361&quot; data-origin-height=&quot;656&quot; data-filename=&quot;image (2).png&quot; style=&quot;width: 32.2108%;&quot; data-widthpercent=&quot;32.97&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/GAx5i/dJMb9M3XH0f/XukncEIE4uQYcORXvYdAU1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FGAx5i%2FdJMb9M3XH0f%2FXukncEIE4uQYcORXvYdAU1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;361&quot; height=&quot;656&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;Cursor를 통해 디자인 및 퍼블리싱 바이브코딩 진행&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물론, 디자이너 분들이 보시기에는 이상할 수 있지만 비디자이너인 제가 보았을 때는 생각보다 괜찮네?라는 느낌이 들 정도로 만족스러웠던 것 같습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;캐릭터의 경우에는 ChatGPT 5 Thinking 모델을 활용해서 디자인을 했었는데요. 이미지를 만들어달라고 요청을 할 때 프롬프트 내용이 구체적이지 않고 추상적일수록 간단하고 못 생기게 만드는 현상이 나왔던 것 같습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;image (3).png&quot; data-origin-width=&quot;891&quot; data-origin-height=&quot;661&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ncAAr/dJMb9M3XH2f/1ErvaUm7woqVPNRmwDUZZK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ncAAr/dJMb9M3XH2f/1ErvaUm7woqVPNRmwDUZZK/img.png&quot; data-alt=&quot;요구사항이 추상적일 수록 이미지 퀄리티가 떨어진다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ncAAr/dJMb9M3XH2f/1ErvaUm7woqVPNRmwDUZZK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FncAAr%2FdJMb9M3XH2f%2F1ErvaUm7woqVPNRmwDUZZK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;435&quot; height=&quot;323&quot; data-filename=&quot;image (3).png&quot; data-origin-width=&quot;891&quot; data-origin-height=&quot;661&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;요구사항이 추상적일 수록 이미지 퀄리티가 떨어진다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로젝트 내용과 기능들을 아무리 상세히 적어도 해당 현상이 반복되어서 이번에는 제 상상 속의 캐릭터를 하나 정해서 그 특징을 묘사하여 프롬프트로 제공해 보았습니다. 그랬더니 다음과 같이 이전보다는 더 만족스러운 이미지를 생성해 내는 것을 볼 수 있었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/deW9Lt/dJMb86BisfI/GDAWH3Q8x0tPALZKBQ4ack/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/deW9Lt/dJMb86BisfI/GDAWH3Q8x0tPALZKBQ4ack/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;908&quot; data-origin-height=&quot;701&quot; data-filename=&quot;image (4).png&quot; style=&quot;width: 64.0807%; margin-right: 10px;&quot; data-widthpercent=&quot;64.83&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/deW9Lt/dJMb86BisfI/GDAWH3Q8x0tPALZKBQ4ack/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdeW9Lt%2FdJMb86BisfI%2FGDAWH3Q8x0tPALZKBQ4ack%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;908&quot; height=&quot;701&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dxeEDq/dJMb9WrTriK/Ts4Rg38F5yRq8UoggISiI1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dxeEDq/dJMb9WrTriK/Ts4Rg38F5yRq8UoggISiI1/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;496&quot; data-origin-height=&quot;706&quot; data-filename=&quot;image (5).png&quot; style=&quot;width: 34.7565%;&quot; data-widthpercent=&quot;35.17&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dxeEDq/dJMb9WrTriK/Ts4Rg38F5yRq8UoggISiI1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdxeEDq%2FdJMb9WrTriK%2FTs4Rg38F5yRq8UoggISiI1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;496&quot; height=&quot;706&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;요구사항이 구체적일 수록 이미지 퀄리티가 높아진다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주의할 점은 특정 위치나 묘사가 마음에 안 든다고 수정해 달라는 요청을 지시하면 이미지가 더 망가지는 현상이 있습니다. ChatGPT로는 아직까지는 디테일한 수정까지는 어려운 것으로 보입니다. 그래서 다른 유료 서비스도 알아보고 써보았는데 KlingAI라는 서비스를 사용해 보았다가 생성은 ChatGPT보다는 잘했지만 시간이 더 오래 걸리고 비용을 지불하지 않으면 워터마크 낙인이 찍히는 문제가 있었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 ChatGPT로 계속 만족스러운 이미지가 나올 때까지 갓차를 하는 수밖에 없겠다는 생각이 들었습니다. 그래도 ChatGPT가 좋았던 점은 답변 분기가 되기 때문에 이상한 이미지 생성 시 다시 그 위의 대화부터 분기하여 최적화된 이미지를 찾아갈 수 있다는 점인 것 같습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;한번 캐릭터가 한번 잘 뽑히고 나면 그 뒤로부터는 제스처를 조금씩 바꿔가면서 멀티턴으로 넣어주시고 언제 또 사용할지 모르니 잘 보관해 두시면 좋습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;image (6).png&quot; data-origin-width=&quot;928&quot; data-origin-height=&quot;710&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b4zMlv/dJMb9WFqBQi/XPuPy2qCVtCNjBB1KzVXd1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b4zMlv/dJMb9WFqBQi/XPuPy2qCVtCNjBB1KzVXd1/img.png&quot; data-alt=&quot;제스처를 조금씩 바꿔가면서 이미지를 변형&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b4zMlv/dJMb9WFqBQi/XPuPy2qCVtCNjBB1KzVXd1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb4zMlv%2FdJMb9WFqBQi%2FXPuPy2qCVtCNjBB1KzVXd1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/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;472&quot; data-filename=&quot;image (6).png&quot; data-origin-width=&quot;928&quot; data-origin-height=&quot;710&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;제스처를 조금씩 바꿔가면서 이미지를 변형&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style7&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 id=&quot;9&quot; data-ke-size=&quot;size26&quot;&gt;  마무리하며&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정말 오랜만에 긴 포스팅을 작성해 보는 것 같습니다. 시작을 하기 전에는 정말 간단하게만 만드려고 생각을 했었는데 만들다 보니 점점 욕심도 생기고 디테일을 계속 챙기려고 하다 보니 시간을 많이 잡아먹게 된 것 같은데요. 그래도 분명한 점은 AI가 없었으면 이 정도까지 구현하는 것은 거의 불가능하지 않았을까 하는 생각이 듭니다.&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;1015&quot; data-origin-height=&quot;876&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bj8pmB/dJMb9XdgAzb/MWnrouTH9PaoLOkUKWO4U0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bj8pmB/dJMb9XdgAzb/MWnrouTH9PaoLOkUKWO4U0/img.png&quot; data-alt=&quot;v2.0 작업목록 (WBS)&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bj8pmB/dJMb9XdgAzb/MWnrouTH9PaoLOkUKWO4U0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbj8pmB%2FdJMb9XdgAzb%2FMWnrouTH9PaoLOkUKWO4U0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;537&quot; height=&quot;463&quot; data-origin-width=&quot;1015&quot; data-origin-height=&quot;876&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;v2.0 작업목록 (WBS)&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;AI를 통해 기획부터 디자인, 개발까지 거의 대부분에서 바이브 코딩으로 시작하여 덕분에 아무것도 몰랐던 Firebase 기반의 아키텍처로 손쉽게 마이그레이션도 해보고 배운 점과 느낀 점도 많았기에 디테일을 중요시하는 제게 AI의 등장은 정말 단비 같은 존재인 것 같습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래도 바이브 코딩을 하면서 느꼈던 점은 결국 인간이 가진 창의력과 경험이 중요하다는 점인 것 같습니다. 디자인 도움을 받을 때도 캐릭터가 필요하다고 해서 뚝딱 떨어지는 것이 아니라 무언가를 창조해 내는 데는 결국에는 나의 경험과 창의력으로 상상해서 만들어낼 수밖에 없었고 AI는 제가 생각해 낸 것을 구현해 줄 능력만 갖고 있었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마찬가지로 개발 쪽에서는 5년간의 경험과 어느 정도의 지식베이스가 갖춰져 있다 보니 AI가 만들어내는 허점들이 계속해서 보일 수밖에 없었고 지속적으로 피드백을 넣어가며 프로젝트를 완성해 나가는 경험을 했습니다. 만약 저에게 개발 지식이 없었더라면 의심을 하지 못하고 자칫하면 책임질 수 없는 위험한 상황까지 닥칠 수 있을 것 같다는 생각이 듭니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 아직까지 AI는 바이브까지는 아니고 보조도구 정도로 활용할 수준이 아닐까 하는 생각이 듭니다. 특히, 의심을 많이 할수록 좋은 인사이트들을 많이 챙길 수 있었던 것 같습니다. 저는 AI와 대화를 나눌 때 &quot;나는 이렇게 생각하는데 너는 왜 그렇게 생각했어?&quot;와 같은 화법을 주로 사용하는데 그럴 때마다 제가 놓쳤던 부분들을 발견하기도 하고 몰랐던 부분들을 발견하기도 합니다. 앞으로도 개발자로 지내면서 AI를 활용한다면 이런 식으로 많이 활용하게 되지 않을까 하는 생각이 듭니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이로써 RecallBuddy 2.0 개발을 마치고 개발 후기에 대해 공유드려보았는데요. 아직 수정해야 될 부분도 많고 추가하고 싶은 기능들도 많기 때문에 다음에 기회가 된다면 또 2.1 버전이나 3 버전으로 확장해서 앱을 개선해 보는 기회가 생겼으면 좋겠습니다. 혹시 RecallBuddy 서비스가 궁금하신 분들이 계시다면 아래 링크를 통해 앱을 사용해 보셔도&amp;nbsp;좋을 것 같습니다 &lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1760884197062&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;RecallBuddy&quot; data-og-description=&quot;&quot; data-og-host=&quot;til-alarm.web.app&quot; data-og-source-url=&quot;https://til-alarm.web.app/&quot; data-og-url=&quot;https://til-alarm.web.app/&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://til-alarm.web.app/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://til-alarm.web.app/&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;RecallBuddy&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;til-alarm.web.app&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_1761056753140&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 - chucoding/recall-buddy: 에빙하우스 망각곡선(1일, 7일, 30일)에 의거하여 공부한 내용을 매일 아&quot; data-og-description=&quot;에빙하우스 망각곡선(1일, 7일, 30일)에 의거하여 공부한 내용을 매일 아침 PUSH 알림을 받을 수 있도록 해주는 프로그램 - chucoding/recall-buddy&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/chucoding/recall-buddy&quot; data-og-url=&quot;https://github.com/chucoding/recall-buddy&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/eEbbD/hyZLo0V5In/4dAVpYQJi9xO5zd0ohtGWK/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/clGllI/hyZLbN3832/M4ERrIKy15ZwIKmzUt5P31/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/chucoding/recall-buddy&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/chucoding/recall-buddy&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/eEbbD/hyZLo0V5In/4dAVpYQJi9xO5zd0ohtGWK/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/clGllI/hyZLbN3832/M4ERrIKy15ZwIKmzUt5P31/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 - chucoding/recall-buddy: 에빙하우스 망각곡선(1일, 7일, 30일)에 의거하여 공부한 내용을 매일 아&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;에빙하우스 망각곡선(1일, 7일, 30일)에 의거하여 공부한 내용을 매일 아침 PUSH 알림을 받을 수 있도록 해주는 프로그램 - chucoding/recall-buddy&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;div id=&quot;gtx-trans&quot; style=&quot;position: absolute; left: 324px; top: 25923px;&quot;&gt;
&lt;div class=&quot;gtx-trans-icon&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;</description>
      <category>독서 및 기타 활동</category>
      <author>외계공룡</author>
      <guid isPermaLink="true">https://chucoding.tistory.com/163</guid>
      <comments>https://chucoding.tistory.com/163#entry163comment</comments>
      <pubDate>Sun, 19 Oct 2025 23:38:08 +0900</pubDate>
    </item>
    <item>
      <title>Keycloakify를 활용한 로그인 화면 만들기</title>
      <link>https://chucoding.tistory.com/162</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;keycloakify_800x400.png&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;400&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bRr6vp/btsQR6vr3PV/eos9grdH05RNHvtVn0vF1K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bRr6vp/btsQR6vr3PV/eos9grdH05RNHvtVn0vF1K/img.png&quot; data-alt=&quot;Keycloakify를 활용한 로그인 화면 만들기&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bRr6vp/btsQR6vr3PV/eos9grdH05RNHvtVn0vF1K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbRr6vp%2FbtsQR6vr3PV%2Feos9grdH05RNHvtVn0vF1K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/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;400&quot; data-filename=&quot;keycloakify_800x400.png&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;400&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Keycloakify를 활용한 로그인 화면 만들기&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;로그인 기능을 만들려면 과거에는 애플리케이션 내부(Spring Security 등)에서 인증 로직을 직접 구현하는 경우가 많았지만, 요즘은 Keycloak 같은 자가 호스팅 IdP나 Auth0&amp;middot;Cognito 같은 관리형 IdP를 활용해 인증을 외부화하고 애플리케이션 레벨에서는 OIDC 표준으로 연동하는 추세가 많아지고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이처럼 인증 패러다임이 바뀌다보니 Keycloak의 인기도 계속해서 상승 중인데요. 많은 인기를 얻고 있는 만큼 인증에 필요한 여러 가지 UI 템플릿(ex, 로그인, 비밀번호 찾기, 이메일, 회원가입 등)들도 기본으로 제공하고 있기 때문에 Keycloak 서버에 UI까지 같이 포함하여 배포가 가능합니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만, Keycloak에서 제공하는 로그인/계정 UI는 Freemarker(FTL) 기반 서버 템플릿이고, 기본 스타일도 PatternFly 체계를 따르기 때문에, 요즘 프론트 메인스트림 기술들과 디자인&amp;middot;빌드 파이프라인이 달라 프론트엔드 개발자 입장에서는 해당 기능을 요청을 받았다면 무척 난감할 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 경우에는 Keycloakify라는 프레임워크를 사용하면 걱정 없이 개발하실 수 있게 되는데요. 이번 포스팅에서는 Keycloakify를 활용해서 로그인 화면을 디자인하고 설계하는 방법에 대해 포스팅 해보도록 하겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style7&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1&gt;  Keycloakify란?&lt;/h1&gt;
&lt;figure id=&quot;og_1759045060375&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;Quick Start | Keycloakify&quot; data-og-description=&quot;&quot; data-og-host=&quot;docs.keycloakify.dev&quot; data-og-source-url=&quot;https://docs.keycloakify.dev/&quot; data-og-url=&quot;https://docs.keycloakify.dev/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bB6yZ2/hyZJVkr4CS/yPH0XalbybqlS3nky6MBWk/img.jpg?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/bzzOnz/hyZJQDriK7/UiXkZBBqMKkIU5f14t9v8K/img.jpg?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://docs.keycloakify.dev/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://docs.keycloakify.dev/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bB6yZ2/hyZJVkr4CS/yPH0XalbybqlS3nky6MBWk/img.jpg?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/bzzOnz/hyZJQDriK7/UiXkZBBqMKkIU5f14t9v8K/img.jpg?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;Quick Start | Keycloakify&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;docs.keycloakify.dev&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;Keycloakify란 Keycloak의 로그인/회원가입/계정 관리 등 화면을 React로 개발해 Keycloak 테마(JAR)로 포장해주는 도구입니다. 즉, 프리마커(FTL)나 비주류 CSS를 만지지 않고도, 팀에서 사용하는 프론트 스택(React + 원하는 스타일링) 그대로 Keycloak 화면을 만들 수 있게 해 줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같은 장점들로 인해 디버깅이 불가능한 FTL파일만을 전달받아 커스텀 해달라는 난감한 요청을 받거나 백엔드 프로젝트를 클론 받아 직접 온보딩해야 하는 등의 부담감이 줄어듭니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이뿐만 아니라 Keycloakify를 사용하면 핫 리로드가 지원된다는 점이 장점입니다. Keycloakify는 두 가지 실행 방법을 지원하고 있는데요. 하나는 일반적인 vite 프론트 서버 실행방법이고 또 하나는 도커로 jar파일을 직접 배포하여 좀 더 프로덕션에 가까운 방법으로 실행하는 방법입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물론, 후자는 핫 리로드는 아니고 빌드 자동화에 페이지 새로고침이 필요해서 조금 느리긴 하지만 몇 초 정도의 차이이기 때문에 크게 불편감 없이 개발할 수 있어서 저는 두 번째 실행방법으로 개발을 진행하였습니다. 이 처럼 빌드와 배포 또한 Keycloakify 기본 커맨드로 제공하고 있으니 편하게 사용하실 수 있습니다. 특히, Dockerfile로 만들어놓으시면 추후에 CI&amp;amp;CD를 구축하여 배포 자동화까지 만드실 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-08-25 오전 10.07.50 (1).png&quot; data-origin-width=&quot;1152&quot; data-origin-height=&quot;866&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/GeJAu/btsQUdGNKUs/OKCgoBK9j3N5YnJUSXWBsK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/GeJAu/btsQUdGNKUs/OKCgoBK9j3N5YnJUSXWBsK/img.png&quot; data-alt=&quot;Keycloakify 개발 및 빌드&amp;amp;amp;배포 파이프라인&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/GeJAu/btsQUdGNKUs/OKCgoBK9j3N5YnJUSXWBsK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FGeJAu%2FbtsQUdGNKUs%2FOKCgoBK9j3N5YnJUSXWBsK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1152&quot; height=&quot;866&quot; data-filename=&quot;스크린샷 2025-08-25 오전 10.07.50 (1).png&quot; data-origin-width=&quot;1152&quot; data-origin-height=&quot;866&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Keycloakify 개발 및 빌드&amp;amp;배포 파이프라인&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style7&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span&gt; &lt;/span&gt; keycloakify-starter 프로젝트 세팅하기&lt;/h2&gt;
&lt;figure id=&quot;og_1759045165979&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 - keycloakify/keycloakify-starter:    Keycloakify Projects Starter Template&quot; data-og-description=&quot;  Keycloakify Projects Starter Template. Contribute to keycloakify/keycloakify-starter development by creating an account on GitHub.&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/keycloakify/keycloakify-starter&quot; data-og-url=&quot;https://github.com/keycloakify/keycloakify-starter&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/GwLEa/hyZJ0svtyq/0uxgzQTY9hzPafVaGWIxF0/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/3ND0k/hyZJPR4oRz/u1Ccfiy6k8UBzCcVZTK5tk/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/keycloakify/keycloakify-starter&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/keycloakify/keycloakify-starter&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/GwLEa/hyZJ0svtyq/0uxgzQTY9hzPafVaGWIxF0/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/3ND0k/hyZJPR4oRz/u1Ccfiy6k8UBzCcVZTK5tk/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 - keycloakify/keycloakify-starter:   Keycloakify Projects Starter Template&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;  Keycloakify Projects Starter Template. Contribute to keycloakify/keycloakify-starter 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;Keycloakify는 다음과 같이 keycloakify-starter라는 보일러플레이트를 공식적으로 제공하고 있습니다. 따라서 위 프로젝트를 클론 받아 README에서 제공하고 있는 튜토리얼 대로 진행하시면 간편하게 온보딩하실 수 있습니다. 아래는 포스팅 날짜 기준 최신 버전인 keycloakify v11 온보딩 방법입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로젝트 클론&lt;/p&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;git clone &amp;lt;https://github.com/keycloakify/keycloakify-starter.git&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로젝트 로컬 세팅&lt;/p&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;yarn 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;프로젝트 실행(jar 빌드 후 도커로 실행하는 방법)&lt;/p&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;npx keycloakify start-keycloak&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;사전에 메이븐, 도커데스크톱 설치되어있어야 함.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로젝트 빌드(테마 jar로 빌드)&lt;/p&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;yarn run build-keycloak-theme&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;만약, keycloakify-starter를 사용하지 않고 직접 세팅하고 싶으시다면 아래 공식문서 가이드를 참고하시면 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1759045228934&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;Keycloakify in my App | Keycloakify&quot; data-og-description=&quot;Setting up Keycloakify in your Web App&quot; data-og-host=&quot;docs.keycloakify.dev&quot; data-og-source-url=&quot;https://docs.keycloakify.dev/keycloakify/v9/keycloakify-in-my-app&quot; data-og-url=&quot;https://docs.keycloakify.dev/keycloakify/v9/keycloakify-in-my-app&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/oxdCU/hyZJYIdYV2/4lFeM9VuksT82aMt6JFPUK/img.jpg?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/EcDd0/hyZJZ8b5Mi/JMg2hGN7MXJK5Wl6DVYhNk/img.jpg?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://docs.keycloakify.dev/keycloakify/v9/keycloakify-in-my-app&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://docs.keycloakify.dev/keycloakify/v9/keycloakify-in-my-app&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/oxdCU/hyZJYIdYV2/4lFeM9VuksT82aMt6JFPUK/img.jpg?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/EcDd0/hyZJZ8b5Mi/JMg2hGN7MXJK5Wl6DVYhNk/img.jpg?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;Keycloakify in my App | Keycloakify&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Setting up Keycloakify in your Web App&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;docs.keycloakify.dev&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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style7&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span&gt; &lt;/span&gt; Keycloakify와 같이 사용할 프레임워크 선정하기&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 권장 조합(추천)&lt;br /&gt;React + TypeScript + Vite + Keycloakify&lt;/p&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;Keycloakify는 위와 같은 조합으로 구성하시는 것을 추천드립니다. 우선 빌드 도구는 공식가이드 문서와 keycloakify-starter로 제공되고 있는 boilerplate 도 Vite로 구성되어있는 만큼 Vite를 1급 지원하고 있기 때문에 가장 안정성이 뛰어나다고 볼 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제는 CSS 프레임워크일 것 같은데요. CSS 프레임워크는 정말 다양하게 많이 사용되고 있기 때문에 어떤 CSS가 가장 좋을지 고민이 많이 되실 것 같습니다. 국내에서는 가장 인기가 많은 프레임워크인 tailwindcss와 MUI는 vite에 연동 가능한 플러그인이 지원되고 있기 때문에 둘 다 손쉽게 세팅하고 사용하실 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 로그인, 회원가입 등 사용하는 페이지가 적다면 조금 더 가벼운 프레임워크인 tailwindcss를 선택하면 좋고 Dialog, Modal 등의 팝업이나 토스트 알림, 폼 에러, 포커스처리 등이 필요하다면 MUI를 채택하시면 좀 더 빠르게 구현할 수 있을 것 같습니다. (물론, shadcn을 사용하시면 tailwind에서도 유틸 컴포넌트들을 쉽게 구성하실 수 있습니다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 가장 가볍게 구축하고 싶으시다면 PatternFly나 기본 CSS를 사용하셔도 됩니다. PatternFly는 Red Hat에서 만든 CSS 프레임워크로 Keycloakify 자체 기본 테마에 사용되고 있는 프레임워크입니다. 따라서 프리마커 템플릿 파일(. ftl)을 보면 pf-, kc- 등의 prefix로 구성된 스타일 형태를 보실 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PatternFly를 기존에 알고 있었고 사용해 본 적이 있으시다면 사용하셔도 무방하지만 만약 익숙지 않은 프레임워크라면 테일윈드를 사용할 때와 마찬가지로 클래스명을 외워야 하는 러닝커브가 발생할 수 있으므로 해당 부분을 충분히 고려하셔서 사용하셔야 합니다. 권장되는 사용방법으로는 PatternFly CSS를 전체 제거한 후 본인에게 익숙한 프레임워크로 세팅하는 것이 일반적인 사용방법입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저의 경우에는 별도로 사용하던 디자인시스템이 있었기 때문에 공통 컴포넌트들은 디자인시스템에서 불러와서 사용하였고 레이아웃 등 필요한 CSS만 기본 CSS를 사용해서 스타일링을 하였습니다. CSS 레벨 vs 컴포넌트 레벨 두 가지 방식 모두 공식사이트에서 사용방법을 가이드하고 있으므로 팀원분들과 편하게 상의한 후 선택하시면 좋을 것 같습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1759045319826&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;Customization Strategies | Keycloakify&quot; data-og-description=&quot;&quot; data-og-host=&quot;docs.keycloakify.dev&quot; data-og-source-url=&quot;https://docs.keycloakify.dev/keycloakify/v10/customization-strategies&quot; data-og-url=&quot;https://docs.keycloakify.dev/keycloakify/v10/customization-strategies&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bSdVXd/hyZJGBoa9x/hzCigZ6azKQN68hczZk5rK/img.jpg?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/c5yS9k/hyZJMaw0AW/kG7LFhMg2u8mT3HvWXT0ok/img.jpg?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://docs.keycloakify.dev/keycloakify/v10/customization-strategies&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://docs.keycloakify.dev/keycloakify/v10/customization-strategies&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bSdVXd/hyZJGBoa9x/hzCigZ6azKQN68hczZk5rK/img.jpg?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/c5yS9k/hyZJMaw0AW/kG7LFhMg2u8mT3HvWXT0ok/img.jpg?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;Customization Strategies | Keycloakify&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;docs.keycloakify.dev&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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style7&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span&gt; ️&lt;/span&gt; Keycloakify 프로젝트 구조 잡기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;keycloakify-starter 프로젝트 구조는 다음과 같이 구성되어 있습니다.&lt;/p&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;my-app/                      /* keycloakify-starter 루트 */
  src/
    login/                   /* 로그인(인증) 화면 테마 소스 */
      KcContext.ts           // Keycloak이 각 페이지에 주입하는 kcContext의 타입/헬퍼.
                             // 특정 페이지(예: 로그인, 회원가입 등)에 추가 필드가 필요하면
                             // 여기서 타입 확장하면 TS로 안전하게 접근 가능.

      KcPage.tsx             // &quot;페이지 스위처&quot; 역할. kcContext.pageId에 따라
                             // 알맞은 페이지 컴포넌트를 렌더링하고, 공통 레이아웃(템플릿)을 감싼다.
                             // 커스텀 페이지/스타일 입히는 핵심 진입점.

      KcPageStory.tsx        // Storybook/로컬 프리뷰용 glue 컴포넌트.
                             // 다양한 pageId&amp;middot;언어&amp;middot;상태를 mock kcContext로 바꿔가며
                             // Keycloak 서버 없이 UI를 빠르게 검증할 수 있다.
                             
      i18n.ts                // 번역 키(문구) 오버라이드/추가 정의.
                             // messages_xx.properties를 직접 편집하지 않고 JS/TS로 관리.
                             // ?ui_locales=ko 같은 쿼리로 프리뷰 언어도 바꿀 수 있다.

    email/                   /* 이메일 템플릿 테마
                               `npx keycloakify initialize-email-theme`로 스캐폴딩.

    account/                 /* Account 템플릿 테마
                               `npx keycloakify initialize-account-theme`로 스캐폴딩.

  kc.gen.tsx                 /* keycloakify가 사용하는 엔트리/생성 코드.
                               KcPage와 테마 메타데이터를 export하는 연결 고리.
                               일반적으로 수정할 일은 거의 없고, 메인 엔트리와 결합된다.

  main.tsx                   /* 듀얼 엔트리: 실행 컨텍스트에 따라 App vs Keycloak 테마를 스위칭.
                               window.kcContext가 있으면 &amp;lt;KcPage/&amp;gt; (Keycloak 테마),
                               없으면 일반 &amp;lt;App/&amp;gt;을 렌더. 이 파일에는 무거운 Provider/로직을
                               넣지 않는 게 성능상 유리. */ 

  vite-env.d.ts              /* Vite 타입 보조 선언 파일. */
  package.json               /* 스크립트/의존성 정의.
                               - 로컬 미리보기, Storybook, 테마 빌드 등 명령 포함
                               - 테마 jar 빌드를 위해 Maven/Java 필요(환경별 설치 필요). */&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주의해야 할 점은 위 폴더 구조가 마음에 안 든다고 변경하거나 하면 안 된다는 점입니다. 아래 가이드를 보면 몇 가지 추천 구조를 제공하고 있는데 이 이외의 구조로 변경한다면 빌드 및 실행 시 오류가 발생할 수 있으므로 주의하셔야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1759045391740&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;Keycloakify in my App | Keycloakify&quot; data-og-description=&quot;Setting up Keycloakify in your Web App&quot; data-og-host=&quot;docs.keycloakify.dev&quot; data-og-source-url=&quot;https://docs.keycloakify.dev/keycloakify/v9/keycloakify-in-my-app#sources-directory-structure&quot; data-og-url=&quot;https://docs.keycloakify.dev/keycloakify/v9/keycloakify-in-my-app#sources-directory-structure&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/tGvJb/hyZJRbg7k1/vMVlENVGO3QH4SS42wMdjk/img.jpg?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/cKnVFL/hyZJNtMWcv/yNxHJZVSTZ7NSuEyLQK93K/img.jpg?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://docs.keycloakify.dev/keycloakify/v9/keycloakify-in-my-app#sources-directory-structure&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://docs.keycloakify.dev/keycloakify/v9/keycloakify-in-my-app#sources-directory-structure&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/tGvJb/hyZJRbg7k1/vMVlENVGO3QH4SS42wMdjk/img.jpg?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/cKnVFL/hyZJNtMWcv/yNxHJZVSTZ7NSuEyLQK93K/img.jpg?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;Keycloakify in my App | Keycloakify&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Setting up Keycloakify in your Web App&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;docs.keycloakify.dev&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;저도 처음에는 src 아래 login이 위치한게 마음에 안들어서 pages/login으로 옮겨놓았다가 오류가 발생해서 다시 원상복구 하는 등의 시간을 허비했었는데 저와 같은 불상사가 일어나지 않기를 바랍니다. 여기서의 login은 페이지로써의 login이 아니라 키클락 테마로써의 login이라고 보시면 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로젝트 구조를 원하는대로 커스텀하지 못하고 제약이 있다는 점은 어떻게 보면 keycloakify의 단점이라고도 볼 수 있을것 같습니다. 만약 로그인 테마에 컴포넌트랑 스타일, 에셋 등을 정의해야 한다면 다음과 같이 구성할 수는 있습니다.&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;my-app/
  src/
    login/
      assets/          /* 로고, 이미지 등 정의 */ 
      components/      /* 컴포넌트 정의 */
      pages/           /* 페이지 정의 */
      styles/          /* 스타일 정의 */
      KcContext.ts
      KcPage.tsx       /* 페이지 라우터 */
      KcPageStory.tsx
      i18n.ts
&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;만약 login 외에 accout, email 등의 테마를 추가적으로 사용하신다면 위 커스텀 폴더들을 src/ 하위에 두셔도 됩니다. 추가로, 각 테마별로 지원하고 있는 프리마커 템플릿(ex. login.ftl, login-reset-password.ftl)들이 어떤것 들이 있는지 궁금하시다면 공식사이트에서 지원하고 있는 아래 스토리북 링크를 통해 확인하실 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1759046304892&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;Keycloakify&quot; data-og-description=&quot;Creation of Keycloak Theme Made Easy&quot; data-og-host=&quot;www.keycloakify.dev&quot; data-og-source-url=&quot;https://storybook.keycloakify.dev/?path=/story/introduction--what-is-this-website&quot; data-og-url=&quot;https://www.keycloakify.dev&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cOGQfQ/hyZJRPRO68/cIiKXJpMnhydh53tScIkKk/img.png?width=1280&amp;amp;height=640&amp;amp;face=0_0_1280_640,https://scrap.kakaocdn.net/dn/ckwnoL/hyZJ1EWUQX/yuBCFsvCPKijXpK50UC5u1/img.png?width=1280&amp;amp;height=640&amp;amp;face=0_0_1280_640&quot;&gt;&lt;a href=&quot;https://storybook.keycloakify.dev/?path=/story/introduction--what-is-this-website&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://storybook.keycloakify.dev/?path=/story/introduction--what-is-this-website&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cOGQfQ/hyZJRPRO68/cIiKXJpMnhydh53tScIkKk/img.png?width=1280&amp;amp;height=640&amp;amp;face=0_0_1280_640,https://scrap.kakaocdn.net/dn/ckwnoL/hyZJ1EWUQX/yuBCFsvCPKijXpK50UC5u1/img.png?width=1280&amp;amp;height=640&amp;amp;face=0_0_1280_640');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Keycloakify&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Creation of Keycloak Theme Made Easy&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.keycloakify.dev&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;다만, 스토리북에서는 UI만 확인 가능하고 해당 템플릿들에서 사용중인 form과 input 요소들의 id, name 값이 궁금하다면 keycloak 레포지토리에서 직접 확인하셔야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1759226314147&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;keycloak/themes/src/main/resources/theme/base at 24.0.4 &amp;middot; keycloak/keycloak&quot; data-og-description=&quot;Open Source Identity and Access Management For Modern Applications and Services - keycloak/keycloak&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/keycloak/keycloak/tree/24.0.4/themes/src/main/resources/theme/base&quot; data-og-url=&quot;https://github.com/keycloak/keycloak/tree/24.0.4/themes/src/main/resources/theme/base&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/u7pXx/hyZKyIoCkB/KBrRYPtIOizDROSdQZXguK/img.png?width=1500&amp;amp;height=500&amp;amp;face=0_0_1500_500,https://scrap.kakaocdn.net/dn/Rx7HP/hyZJIzqQ2v/lvXzXfZ1VyJBbWWNDcbrKk/img.png?width=1500&amp;amp;height=500&amp;amp;face=0_0_1500_500&quot;&gt;&lt;a href=&quot;https://github.com/keycloak/keycloak/tree/24.0.4/themes/src/main/resources/theme/base&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/keycloak/keycloak/tree/24.0.4/themes/src/main/resources/theme/base&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/u7pXx/hyZKyIoCkB/KBrRYPtIOizDROSdQZXguK/img.png?width=1500&amp;amp;height=500&amp;amp;face=0_0_1500_500,https://scrap.kakaocdn.net/dn/Rx7HP/hyZJIzqQ2v/lvXzXfZ1VyJBbWWNDcbrKk/img.png?width=1500&amp;amp;height=500&amp;amp;face=0_0_1500_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;keycloak/themes/src/main/resources/theme/base at 24.0.4 &amp;middot; keycloak/keycloak&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Open Source Identity and Access Management For Modern Applications and Services - keycloak/keycloak&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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style7&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span&gt; &lt;/span&gt; 레이아웃(템플릿) 만들기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저희가 keycloakify를 사용하는 근본적인 이유는 로그인 페이지를 브랜드에 맞게 커스텀하기 위함입니다. 따라서, 각 페이지별로 로고와 헤더, 레이아웃은 기본적으로 공통된 양식을 갖고 있다면 keycloakify에서 제공하고 있는 Template 컴포넌트를 커스텀해서 사용하시면 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;커스텀 템플릿(MyTemplate) 생성&lt;/h3&gt;
&lt;pre class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot;&gt;&lt;code&gt;/* src/login/KcPage.tsx */

const MyTemplate = ({ children }: any) =&amp;gt; {
  return (
    &amp;lt;div className={pageContainer}&amp;gt;
      &amp;lt;header className={header}&amp;gt;
        &amp;lt;img src={logoImage} alt=&quot;My Logo&quot; className={logo} /&amp;gt;
      &amp;lt;/header&amp;gt;
      &amp;lt;div className={contentContainer}&amp;gt;
        {children}
      &amp;lt;/div&amp;gt;
  );
};

export default function KcPage(props: { kcContext: KcContext }) {
    const { kcContext } = props;

    const { i18n } = useI18n({ kcContext });

    return (
        &amp;lt;Suspense&amp;gt;
            {(() =&amp;gt; {
                switch (kcContext.pageId) {
                    case &quot;login.ftl&quot;:
                        return (
                            &amp;lt;Login
                                {...{ kcContext, i18n }}
                                Template={MyTemplate}
                                doUseDefaultCss={false}
                            /&amp;gt;
                        );
                        ...&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;nix&quot;&gt;&lt;code&gt;/* src/login/pages/Login.tsx */

&amp;lt;Template
    kcContext={kcContext}
    i18n={i18n}
    doUseDefaultCss={doUseDefaultCss}
    displayMessage={!messagesPerField.existsError(&quot;username&quot;, &quot;password&quot;)}
    headerNode={null}
&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;위와 같이 KcPage에서 템플릿과 컨텍스트들을 props로 전달하여 각 페이지에서 노드 가장 최상단에 Template을 위와 같이 래핑 해주시면 됩니다. 위의 정보들은 반드시 넣어주셔야 하는 필수 정보들이며 headerNode를 쓰지 않더라도 null을 꼭 넣어주셔야 타입에러가 발생하지 않습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;커스텀 템플릿 대신 headerNode 사용하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약, 커스텀 템플릿은 과한 것 같고 헤더만 필요하다고 하시면 다음과 같이 구성하실 수도 있습니다.&lt;/p&gt;
&lt;pre class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot;&gt;&lt;code&gt;/* src/login/components/header.tsx */

import logoImage from &quot;../assets/img/logo.png&quot;;

const Header = ()=&amp;gt; {
  return (
    &amp;lt;header className={header}&amp;gt;
      &amp;lt;img src={logoImage} alt=&quot;My Logo&quot; className={logo}/&amp;gt;
    &amp;lt;/header&amp;gt;
  );
};

export default Header;&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;/* src/login/pages/Login.tsx */

import Header from &quot;../components/header&quot;;

return (
    &amp;lt;Template
        kcContext={kcContext}
        i18n={i18n}
        doUseDefaultCss={doUseDefaultCss}
        displayMessage={!messagesPerField.existsError(&quot;username&quot;, &quot;password&quot;)}
        headerNode={&amp;lt;Header /&amp;gt;}
    &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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style7&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span&gt; &lt;/span&gt; 다국어 설정하기 (i18n)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;keycloakify는 기본적으로 다국어 세팅이 가능하도록 i18n도 지원을 하고 있습니다. 다만, 한 가지 아쉬운 점은 keycloakify에서 한국어 팩을 기본적으로 지원을 안 하고 있다는 점인데요. 따라서, 한국어 세팅을 하려면 별도의 커스텀 작업이 필요합니다. keycloakify는 아래 링크를 통해서 한국어 i18n을 추가로 등록하실 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1759045497785&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;Adding Support for Extra Languages | Keycloakify&quot; data-og-description=&quot;&quot; data-og-host=&quot;docs.keycloakify.dev&quot; data-og-source-url=&quot;https://docs.keycloakify.dev/features/i18n/adding-support-for-extra-languages&quot; data-og-url=&quot;https://docs.keycloakify.dev/features/i18n/adding-support-for-extra-languages&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/iJJur/hyZJR3pzj9/Dxf8ry4FbkumDzd2tnLvIK/img.jpg?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/cCwlam/hyZKewvjgk/J7BEmoqdbQTkuJm3YGVXq0/img.jpg?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://docs.keycloakify.dev/features/i18n/adding-support-for-extra-languages&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://docs.keycloakify.dev/features/i18n/adding-support-for-extra-languages&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/iJJur/hyZJR3pzj9/Dxf8ry4FbkumDzd2tnLvIK/img.jpg?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/cCwlam/hyZKewvjgk/J7BEmoqdbQTkuJm3YGVXq0/img.jpg?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;Adding Support for Extra Languages | Keycloakify&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;docs.keycloakify.dev&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;간단하게 설명드리자면 키클락 어드민에 접속하여 realms localizations 설정에서 한국어 언어팩을 추가한 뒤에 keycloakify 프로젝트에 다음과 같이 파일을 만들어주시면 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1759045522323&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;/* src/login/i18n.ko.ts */

import type { MessageKey_defaultSet } from &quot;keycloakify/login or keycloakify/accont&quot;;

const messages: Record&amp;lt;MessageKey_defaultSet, string&amp;gt; = {
    // cspell: disable
    doLogIn: &quot;로그인&quot;,
    doRegister: &quot;회원가입&quot;,
    doRegisterSecurityKey: &quot;보안 키 등록&quot;,
    // ... translation for all the other default messages
    // cspell: enable
};

export default messages;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오버라이드할 키 파일은 다음 경로에서 참고하시면 됩니다.&lt;/p&gt;
&lt;pre class=&quot;awk&quot;&gt;&lt;code&gt;node_modules/keycloakify/src/login/i18n/messages_defaultSet/en.ts
&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;마지막으로 i18n.ts 파일에서 한국어팩을 import 해서 사용해 주시면 됩니다.&lt;/p&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;/* src/login/i18n.ts */

import { i18nBuilder } from &quot;keycloakify/login or keycloakify/account&quot;;
import type { ThemeName } from &quot;../kc.gen&quot;;

/** @see: &amp;lt;https://docs.keycloakify.dev/i18n&amp;gt; */
const { useI18n, ofTypeI18n } = i18nBuilder
    .withThemeName()
    .withExtraLanguages({
        ko: {
            // cspell: disable-next-line
            label: &quot;한국어&quot;,
            getMessages: () =&amp;gt; import(&quot;./i18n.ko&quot;)
        }
    })
    .withCustomTranslations({
        /* 커스텀 메시지가 필요한 경우 */
        en: {
            forgotUsername: &quot;Forgot your username?&quot;,
            resetPassword: &quot;Reset password&quot;
        },
        ko: {
            forgotUsername: &quot;아이디를 잊으셨나요?&quot;,
            resetPassword: &quot;비밀번호 재설정&quot;
        }
    })
    .build();

type I18n = typeof ofTypeI18n;

export { useI18n, type I18n };&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;이제 설정한 i18n을 페이지 또는 컴포넌트에서 사용할 때는 다음과 같이 사용해 주시면 됩니다.&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;export default function Register(props: RegisterProps) {
    const { i18n } = props;
    
    const { msg, msgStr, advancedMsg, advancedMsgStr } = i18n;

    return (
        //...
        &amp;lt;a href={url.loginUrl}&amp;gt;
            {msg(&quot;backToLogin&quot;)}
        &amp;lt;/a&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;메시지를 사용할때는 msg()와 msgStr() 중에서 사용하시면 되는데 기본적으로 msg()는 리액트노드(ReactNode) 타입을 반환하고 msgStr()은 string 타입을 반환합니다. msg()는 내부적으로 sanitizer를 거쳐서 일부 허용된 마크업(ex. &amp;lt;strong&amp;gt;)을 제외하고는 렌더링을 허용하지 않기 때문에 조금 더 안전하게 사용할 수 있지만, 디자인시스템이나 커스텀된 컴포넌트를 가져다 쓸 때 타입 오류가 발생할 수 있으므로 상황에 맞게 msgStr()을 사용하실 수도 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style7&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1&gt;  정리하기&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금까지 keycloakify 프로젝트를 세팅하는 방법에 대해 정리해 보았는데요. keycloakify를 사용해 보면서 느낀 점은 프론트엔드 개발자 입장에서 퍼블리싱은 분명히 편리하지만 기획되어 있는 인증 로직이 keycloak 인증 로직과 많이 벗어난다면 결국 백엔드에 의존할 수 밖에 없게 되기 때문에 애매한 프레임워크라는 생각도 듭니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 아이디 찾기, 비밀번호 찾기 템플릿에 휴대폰번호 입력이 추가되어야 하는 상황이라면 이 하나의 기능을 위해서 새로운 프리마커 템플릿을 정의해달라고 백엔드쪽에 요청(Keycloak SPI(RequiredActionProvider)를 활용해 프리마커 템플릿을 정의할 수 있다.)하거나&amp;nbsp;기존 템플릿을 UI부터 form 제출 로직까지 전부 뜯어고쳐야 합니다. 물론, 뜯어고쳐서 직접 백엔드 API 호출 로직까지 하드코딩하면 구현은 가능하지만 이는 추후에 유지보수의 복잡성을 높이는 결과를 초래할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서, 저는 팀원들과 논의한 후 로그인을 제외한 나머지 페이지들은 별도의 클라이언트 서버를 사용하는 방향으로 백엔드쪽의 부담을 줄여주는 방향으로 keycloakify를 사용하였습니다. 만약 팀 내 keycloakify 도입을 고려하고 계시다면 해당 부분들을 충분히 고려하셔서 프로젝트의 방향에 맞게 기술 선정하시면 좋을 것 같습니다.&lt;/p&gt;</description>
      <category>Front-End</category>
      <author>외계공룡</author>
      <guid isPermaLink="true">https://chucoding.tistory.com/162</guid>
      <comments>https://chucoding.tistory.com/162#entry162comment</comments>
      <pubDate>Sun, 28 Sep 2025 16:54:18 +0900</pubDate>
    </item>
    <item>
      <title>HyperCLOVA X THINK 다양한 활용 방법</title>
      <link>https://chucoding.tistory.com/161</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;think.png&quot; data-origin-width=&quot;630&quot; data-origin-height=&quot;354&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/FriIA/btsPUOIAMWL/KWKnTMukUd9iIy49AXu1k0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/FriIA/btsPUOIAMWL/KWKnTMukUd9iIy49AXu1k0/img.png&quot; data-alt=&quot;HyperCLOVA X THINK&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/FriIA/btsPUOIAMWL/KWKnTMukUd9iIy49AXu1k0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FFriIA%2FbtsPUOIAMWL%2FKWKnTMukUd9iIy49AXu1k0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;630&quot; height=&quot;354&quot; data-filename=&quot;think.png&quot; data-origin-width=&quot;630&quot; data-origin-height=&quot;354&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;HyperCLOVA X THINK&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;2025년 7월 29일, 네이버클라우드에서 새로운 추론형 AI 모델 HyperCLOVA X THINK(하이퍼클로바X 씽크)를 일반 유저들도 사용할 수 있도록 클로바 스튜디오에 오픈하였습니다. HyperCLOVA X THINK는 OpenAI의 o3, DeepSeek의 DeepSeek R1, Anthropic의 Claude 3.7 Sonnet에 이어 출시된 국내 추론 모델인데요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;추론 모델의 핵심 특징은 단순히 답을 출력하는 데 그치지 않고 스스로 생각하고, 계획하며, 오류를 교정하는 과정을 통해 답을 도출한다는 점입니다. 따라서, 이 모델을 사용하면 어려운 논리 문제, 수학 문제, 복잡한 알고리즘도 높은 정확도로 해결할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 포스팅에서는 HyperCLOVA X THINK 모델을 직접 사용해 보면서 어떻게 활용할 수 있는지에 대해 알아보도록 하겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style7&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span&gt; &lt;/span&gt; HyperCLOVA X THINK 맛보기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 HyperCLOVA X THINK를 사용해 볼 수 있는 클로바 스튜디오로 접속해 보도록 하겠습니다.&lt;/p&gt;
&lt;figure id=&quot;og_1755332566210&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;CLOVA Studio&quot; data-og-description=&quot;&quot; data-og-host=&quot;clovastudio.ncloud.com&quot; data-og-source-url=&quot;https://clovastudio.ncloud.com/&quot; data-og-url=&quot;https://clovastudio.ncloud.com/&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://clovastudio.ncloud.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://clovastudio.ncloud.com/&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;CLOVA Studio&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;clovastudio.ncloud.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;로그인을 하고 나면 먼저 다음과 같이 메인 화면이 나타나게 되는데요. 이곳에서 플레이그라운드 메뉴를 선택하시면 HyperCLOVA X 모델들을 테스트해 볼 수 있는 화면이 나타나게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;image.png&quot; data-origin-width=&quot;1897&quot; data-origin-height=&quot;935&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bRXVSb/btsPUTQKfCx/LEo6lRwF8iIMOSXXapj3R0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bRXVSb/btsPUTQKfCx/LEo6lRwF8iIMOSXXapj3R0/img.png&quot; data-alt=&quot;클로바스튜디오 홈 화면&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bRXVSb/btsPUTQKfCx/LEo6lRwF8iIMOSXXapj3R0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbRXVSb%2FbtsPUTQKfCx%2FLEo6lRwF8iIMOSXXapj3R0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1897&quot; height=&quot;935&quot; data-filename=&quot;image.png&quot; data-origin-width=&quot;1897&quot; data-origin-height=&quot;935&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;클로바스튜디오 홈 화면&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;플레이그라운드에 접속한 뒤에는 다음과 같이 화면 오른쪽 메뉴바에서 네이버클라우드에서 제공 중인 모델들을 선택할 수 있습니다. HCX-007 모델이 이번에 새로 공개된 HyperCLOVA X THINK 추론 모델입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;image (1).png&quot; data-origin-width=&quot;1911&quot; data-origin-height=&quot;944&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/baA4X9/btsPVktKk3T/vPA8HMrKdsvwkB1MPyOjC1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/baA4X9/btsPVktKk3T/vPA8HMrKdsvwkB1MPyOjC1/img.png&quot; data-alt=&quot;HCX-007(추론 모델) 모델 선택 화면&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/baA4X9/btsPVktKk3T/vPA8HMrKdsvwkB1MPyOjC1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbaA4X9%2FbtsPVktKk3T%2FvPA8HMrKdsvwkB1MPyOjC1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/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;944&quot; data-filename=&quot;image (1).png&quot; data-origin-width=&quot;1911&quot; data-origin-height=&quot;944&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;HCX-007(추론 모델) 모델 선택 화면&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;추론 모델을 선택할 경우 다음과 같이 생각 길이(Thinking) 옵션을 추가로 선택할 수가 있습니다. 기본 값은 &amp;lsquo;짧게&amp;rsquo;로 설정되어 있는데 이 옵션을 통해 얼마나 깊게 추론할 것인지를 선택하여 조절이 가능합니다. 질문의 난이도와 복잡도에 따라 상황에 맞게 조절하여 사용하실 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;image (2).png&quot; data-origin-width=&quot;1910&quot; data-origin-height=&quot;988&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bvSyK7/btsPUFkXLvD/e2WnBwctAEI0HJLVaMFzq0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bvSyK7/btsPUFkXLvD/e2WnBwctAEI0HJLVaMFzq0/img.png&quot; data-alt=&quot;추론 모델 선택시 Thinking(생각 길이) 옵션 추가 선택 가능&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bvSyK7/btsPUFkXLvD/e2WnBwctAEI0HJLVaMFzq0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbvSyK7%2FbtsPUFkXLvD%2Fe2WnBwctAEI0HJLVaMFzq0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1910&quot; height=&quot;988&quot; data-filename=&quot;image (2).png&quot; data-origin-width=&quot;1910&quot; data-origin-height=&quot;988&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;추론 모델 선택시 Thinking(생각 길이) 옵션 추가 선택 가능&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음은 추론 모델의 성능을 한번 확인해 보도록 하겠습니다. 수학 또는 논리 문제를 자유롭게 입력하여 테스트해 보시면 됩니다. 저는 아래 질문을 한번 입력해 보도록 하겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1755332767807&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;어느 지점에서 영희는 남쪽으로 70m를 걷는다. 그런 다음 그녀는 오른쪽으로 돌아서 70m 더 똑바로 걷기 시작한다. 그런 다음 다시 그녀의 왼쪽으로 돌아서 60m를 걷는다. 그런 다음 그녀는 왼쪽으로 돌아서 70m를 걷는다. 그녀는 시작 지점에서 얼마나 떨어져 있는가?&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;먼저, 추론 모델이 아닌 HCX-005(일반 모델)의 답변부터 확인해 보도록 하겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;image (3).png&quot; data-origin-width=&quot;1909&quot; data-origin-height=&quot;981&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/daff1h/btsPWd11n1M/EZ7W71I4XttQAuAsU9ApXK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/daff1h/btsPWd11n1M/EZ7W71I4XttQAuAsU9ApXK/img.png&quot; data-alt=&quot;HCX-005(일반 모델) 답변 결과&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/daff1h/btsPWd11n1M/EZ7W71I4XttQAuAsU9ApXK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdaff1h%2FbtsPWd11n1M%2FEZ7W71I4XttQAuAsU9ApXK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/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;981&quot; data-filename=&quot;image (3).png&quot; data-origin-width=&quot;1909&quot; data-origin-height=&quot;981&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;HCX-005(일반 모델) 답변 결과&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;HCX-005 모델의 경우에는 대게 주어진 텍스트에 기반하여 답변을 도출해 내는 것을 알 수 있습니다. 예를 들어, 오른쪽으로 이동하라고 지시를 내렸을 때 마치 바둑돌을 옮기듯이 텍스트 그대로 좌표를 오른쪽으로 옮기는 것을 확인할 수 있었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;image (4).png&quot; data-origin-width=&quot;1911&quot; data-origin-height=&quot;989&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/UJmrp/btsPTmUc2jz/k6CDB3t2RrByGPHOIFkJMk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/UJmrp/btsPTmUc2jz/k6CDB3t2RrByGPHOIFkJMk/img.png&quot; data-alt=&quot;HCX-007(추론 모델) 답변 결과&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/UJmrp/btsPTmUc2jz/k6CDB3t2RrByGPHOIFkJMk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FUJmrp%2FbtsPTmUc2jz%2Fk6CDB3t2RrByGPHOIFkJMk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/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;989&quot; data-filename=&quot;image (4).png&quot; data-origin-width=&quot;1911&quot; data-origin-height=&quot;989&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;HCX-007(추론 모델) 답변 결과&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;반면에 HCX-007 추론모델은 질문에 주어진 영희의 입장에서 어느 경로로 움직여야 하는지 분석해 내는 현상을 볼 수 있었습니다. 관찰자의 입장으로 보았을 때 오른쪽은 동쪽이지만, 영희의 입장에서는 오른쪽이 서쪽이기 때문에 모델이 어떤 시점에서 생각해봐야 하는지에 대해 좀 더 고려해서 답변을 도출해 내었다는 사실을 확인할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style7&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span&gt; &lt;/span&gt; HyperCLOVA X THINK 활용방법&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞서, 살펴보았던 것처럼 HyperCLOVA X THINK는 어려운 논리나 수학 문제에 조금 더 깊이 생각한다는 점에서 사용에 대한 이점을 확인할 수 있었는데요. 실제 비즈니스나 서비스에서는 이 추론 모델을 어떻게 활용해 볼 수 있을까요?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존의 일반 모델은 사람들이 생각하는 복잡한 생각 과정을 잘 이해하지 못했기에 CoT(Chain of Thought) 프롬프팅을 통해 모델이 잘 답변할 수 있도록 워크플로우로 설계하여 차근차근 답변을 얻어가는 과정을 동반했다면 추론 모델은 질문과 필요한 컨텍스트들만 주어준다면 스스로 생각하고 판단하기 때문에 설계에 대한 복잡도를 낮추고 정확도를 높일 수 있게 되었습니다. 따라서, 요즘 핫한 코딩 어시스턴트의 경우에도 이러한 추론 모델과 잘 어울린다고 볼 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에는 예시로 HyperCLOVA X THINK를 활용해서 스키마 정보와 질문을 주어줬을 때 사용자가 원하는 SQL을 생성해 낼 수 있는지 한번 확인해 보도록 하겠습니다. 아래는 주니어 데이터분석가 수준의 SQL을 생성해 낼 수 있는 프롬프트 예시입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시스템 메시지를 다음과 같이 넣어주도록 하겠습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1755332974165&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;당신은 유능한 SQL 전문가 겸 데이터분석가 입니다.
아래 스키마와 주의사항을 참고하여 사용자의 질문에 대해 적절한 SQL문을 생성해주세요.
당신이 사용할 테이블명은 event 입니다.
오늘 날짜는 2025년 8월 16일 토요일 입니다.

### 스키마 ###
| 필드명 | 데이터 유형 | 설명 |
| ------ | ----------- | ---- |
| event_name | STRING | 이벤트 이름 (예: page_view, purchase) |
| event_timestamp | TIMESTAMP | 이벤트 발생 시간 |
| user_id | STRING | 사용자 고유 ID |
| session_id | STRING | 세션 ID |
| event_params | STRING | 추가적인 이벤트 정보 (JSON 형식) |

### 주의사항 ###
- 부가적인 설명과 참고자료는 생성하지 않습니다.
- 마크다운 문법이 제거된 SQL 쿼리만 생성합니다.
  - ```sql SELECT A FROM B WHERE C = D;``` (X)
  - SELECT A FROM B WHERE C = D; (O)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 사용자 메시지에 다음과 같이 질문을 넣어보겠습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1755332992083&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&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;이제 HCX-005와 HCX-007 답변 생성 결과 차이를 한번 확인해 보도록 하겠습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;image (5).png&quot; data-origin-width=&quot;1900&quot; data-origin-height=&quot;984&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cLIhxA/btsPUiwvxaS/r9xxk8kvpHEK0C6p44JNCk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cLIhxA/btsPUiwvxaS/r9xxk8kvpHEK0C6p44JNCk/img.png&quot; data-alt=&quot;HCX-005(일반 모델) 답변 결과&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cLIhxA/btsPUiwvxaS/r9xxk8kvpHEK0C6p44JNCk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcLIhxA%2FbtsPUiwvxaS%2Fr9xxk8kvpHEK0C6p44JNCk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/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;984&quot; data-filename=&quot;image (5).png&quot; data-origin-width=&quot;1900&quot; data-origin-height=&quot;984&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;HCX-005(일반 모델) 답변 결과&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;image (6).png&quot; data-origin-width=&quot;1891&quot; data-origin-height=&quot;980&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/thDmr/btsPVivU1Dp/F09tDHmmzyH2mwJ7JvUwuK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/thDmr/btsPVivU1Dp/F09tDHmmzyH2mwJ7JvUwuK/img.png&quot; data-alt=&quot;HCX-007(추론 모델) 답변 결과&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/thDmr/btsPVivU1Dp/F09tDHmmzyH2mwJ7JvUwuK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FthDmr%2FbtsPVivU1Dp%2FF09tDHmmzyH2mwJ7JvUwuK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/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;980&quot; data-filename=&quot;image (6).png&quot; data-origin-width=&quot;1891&quot; data-origin-height=&quot;980&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;HCX-007(추론 모델) 답변 결과&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;ldquo;일별 총 판매량 알려줘.&amp;rdquo; 질문은 명확한 질문이기 때문에 모델 간의 답변에 큰 차이점이 없다는 것을 확인할 수 있습니다. 이처럼 간단한 질문과 컨텍스트가 명확하게 주어지면 추론 모델을 쓰지 않고도 일반 모델로도 충분히 처리가 가능합니다. 이번에는 조금 더 애매한 질문으로 다음과 같이 테스트해보도록 하겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1755333095376&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;지난주 주말에 얼마나 방문했는지 알려줘.&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-filename=&quot;image (7).png&quot; data-origin-width=&quot;1906&quot; data-origin-height=&quot;985&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/sFX6h/btsPUP1QlT1/wV7gs7UtKNlh7GKdaWFr21/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/sFX6h/btsPUP1QlT1/wV7gs7UtKNlh7GKdaWFr21/img.png&quot; data-alt=&quot;HCX-005(일반 모델) 답변 결과&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/sFX6h/btsPUP1QlT1/wV7gs7UtKNlh7GKdaWFr21/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FsFX6h%2FbtsPUP1QlT1%2FwV7gs7UtKNlh7GKdaWFr21%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/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;985&quot; data-filename=&quot;image (7).png&quot; data-origin-width=&quot;1906&quot; data-origin-height=&quot;985&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;HCX-005(일반 모델) 답변 결과&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;image (8).png&quot; data-origin-width=&quot;1902&quot; data-origin-height=&quot;984&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dAEIe5/btsPUTb5jXZ/eeRURUlanyeTbfJWsJa2rk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dAEIe5/btsPUTb5jXZ/eeRURUlanyeTbfJWsJa2rk/img.png&quot; data-alt=&quot;HCX-007(추론 모델) 답변 결과&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dAEIe5/btsPUTb5jXZ/eeRURUlanyeTbfJWsJa2rk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdAEIe5%2FbtsPUTb5jXZ%2FeeRURUlanyeTbfJWsJa2rk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/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;984&quot; data-filename=&quot;image (8).png&quot; data-origin-width=&quot;1902&quot; data-origin-height=&quot;984&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;HCX-007(추론 모델) 답변 결과&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;ldquo;지난주 주말에 얼마나 방문했는지 알려줘&amp;rdquo;라는 질문에서 키 포인트는 바로 시점과 용어인데요. 모델이 답변 생성 시 어제라는 시점을 정확하게 이해해서 SQL 날짜로 변환하는지? 그리고 스키마 정보를 바탕으로 방문이라는 키워드를 적절한 필드값으로 찾아낼 수 있는지? 검토해 보는 것이 중요합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 HCX-005 결과를 확인해 보시면 &amp;lsquo;지난주 주말&amp;rsquo;이라는 키워드를 잘못 인지하여 오늘부터 차주까지의 일주일 기간으로 인식하였고 방문이라는 키워드를 단순히 모든 이벤트 발생 건수로 이해한 점을 볼 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반면에, HCX-007 추론 모델의 경우에는 오늘 날짜를 컨텍스트로 주어주면 주말이 언제인지를 정확히 추론해서 날짜를 찾아내었고 방문이라는 키워드는 page_view 필드를 사용해야 한다는 점 또한 명확히 추론해 낸 점을 확인할 수 있었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이처럼 Text-to-SQL(AI를 활용해 문장을 SQL로 변환하는 기술, NL2SQL이라고도 함.)에서도 추론 모델을 활용하는 경우보다 높은 이해도를 바탕으로 정확도를 높일 수 있다는 점을 확인할 수 있습니다. 특히, 한국어 이해도가 뛰어난 하이퍼클로바X이기 때문에 지난주, 주말, 방문 등의 키워드를 자연스럽게 이해하고 추론하였다고 볼 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HCX-007 추론모델은 수학, 논리 분야뿐만 아니라 공감이나 추천 시스템에서도 활용이 가능합니다. 이번에는 제 가치관과 정보를 컨텍스트로 주어주고 고민이나 선택을 대신해주고 후회 가능성을 줄여주는 어시스턴트를 한번 만들어보도록 하겠습니다. 이 어시스턴트는 실제로 최근에 직접 런칭한 서비스의 프롬프트 초안을 가져왔습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1755333267542&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;SOULMATE - 후회를 줄이고 더 나은 선택을 돕는 AI 서비스&quot; data-og-description=&quot;후회를 줄이고 싶은 사람들을 위한, 결정의 순간을 함께하는 단 하나의 파트너&quot; data-og-host=&quot;soulmate-tenten-frontend.vercel.app&quot; data-og-source-url=&quot;https://soulmate-tenten-frontend.vercel.app/&quot; data-og-url=&quot;https://soulmate-tenten-frontend.vercel.app/login?callbackUrl=%2F&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cpKS1E/hyZyh8FWOW/yipl41F9VbNThIgpGiedD1/img.png?width=1200&amp;amp;height=800&amp;amp;face=0_0_1200_800,https://scrap.kakaocdn.net/dn/BNqkw/hyZygaRLRw/x9RK1QZZaItFxiHrs2RJW1/img.png?width=1200&amp;amp;height=800&amp;amp;face=0_0_1200_800&quot;&gt;&lt;a href=&quot;https://soulmate-tenten-frontend.vercel.app/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://soulmate-tenten-frontend.vercel.app/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cpKS1E/hyZyh8FWOW/yipl41F9VbNThIgpGiedD1/img.png?width=1200&amp;amp;height=800&amp;amp;face=0_0_1200_800,https://scrap.kakaocdn.net/dn/BNqkw/hyZygaRLRw/x9RK1QZZaItFxiHrs2RJW1/img.png?width=1200&amp;amp;height=800&amp;amp;face=0_0_1200_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;SOULMATE - 후회를 줄이고 더 나은 선택을 돕는 AI 서비스&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;soulmate-tenten-frontend.vercel.app&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시스템 메시지를 다음과 같이 넣어주도록 하겠습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1755333182715&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;당신은 소울메이트(assistant)입니다. 지금부터 당신은 아래 주어진 사용자정보와 대화 내용을 바탕으로 사용자가 고민중인 선택지 중 한가지를 선택하여 답변해주세요.

### 사용자 정보 ###
- 삶에서 가장 중요한 가치 하나는 성장 입니다. 
- 결정을 내릴 때 오래 고민하고 결정을 미루는 편입니다.
- 후회할 때 가장 많이 하는 생각은 '다른걸 선택했으면 어땠을까' 입니다.
- 선택 앞에서 가장 믿는 것은 '과거 경험' 입니다.

### 답변 포맷 ###
✅ A안: OO
✅ 가치 일치도: 높음
이유 : 

❌ B안: ㅁㅁㅁ
❌ 가치 일치도: 낮음
이유 :

결론
사용자님의 핵심 가치는 ~이며 
성향을 바탕으로 분석했을 때, 이번 선택에서 후회 가능성이 더 낮은 방향은 A안(OO) 입니다.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 사용자 메시지에 다음과 같이 질문을 넣어보겠습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1755333317239&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;### 요청 ###
- 소울메이트(assistant)의 말은 제외하고, 오로지 사용자(user)의 말들 중에서 판단할 수 있는 부분을 찾아야 합니다.
- 근거는 납득 가능해야 합니다.
- 부차적인 말은 생략하고, 판단 근거에 집중해주세요.

### 사용자 질문 ###
잠이 안오는데 억지로 일찍 자는것이 중요할까? 깨어있는동안 뭐라도 생산적인 일을 하는게 좋을까?&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-filename=&quot;image (9).png&quot; data-origin-width=&quot;1903&quot; data-origin-height=&quot;979&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cifDY5/btsPUUhOnM5/KqxkEBpnpwzxDjKUhTTlN1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cifDY5/btsPUUhOnM5/KqxkEBpnpwzxDjKUhTTlN1/img.png&quot; data-alt=&quot;HCX-005(일반 모델) 답변 결과&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cifDY5/btsPUUhOnM5/KqxkEBpnpwzxDjKUhTTlN1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcifDY5%2FbtsPUUhOnM5%2FKqxkEBpnpwzxDjKUhTTlN1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/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;979&quot; data-filename=&quot;image (9).png&quot; data-origin-width=&quot;1903&quot; data-origin-height=&quot;979&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;HCX-005(일반 모델) 답변 결과&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;image.png&quot; data-origin-width=&quot;1910&quot; data-origin-height=&quot;982&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bl3CN5/btsPVdVHEQH/mdRxQDik3DsfjgoWLgxuv1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bl3CN5/btsPVdVHEQH/mdRxQDik3DsfjgoWLgxuv1/img.png&quot; data-alt=&quot;HCX-007(추론 모델) 답변 결과&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bl3CN5/btsPVdVHEQH/mdRxQDik3DsfjgoWLgxuv1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbl3CN5%2FbtsPVdVHEQH%2FmdRxQDik3DsfjgoWLgxuv1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1910&quot; height=&quot;982&quot; data-filename=&quot;image.png&quot; data-origin-width=&quot;1910&quot; data-origin-height=&quot;982&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;HCX-007(추론 모델) 답변 결과&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;여기서 재미있는 점은 서로 추천 결과가 다르다는 점인데요. 눈여겨볼 점은 HCX-005(일반 모델)의 경우에는 &amp;ldquo;잠이 안오는데 억지로 일찍 자는것이 중요할까? 깨어있는동안 뭐라도 생산적인 일을 하는게 좋을까?&amp;rdquo; 질문에 대해 컨텍스트에 포함된 &amp;lsquo;성장&amp;rsquo;이라는 키워드와 가장 확률적으로 높은 &amp;lsquo;생산적인 일&amp;rsquo;이라는 긍정 문구에 매칭이 되어 잠을 자지 말 것을 추천한 듯한 결과를 보여주고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반면에 HCX-007(추론 모델)은 &amp;lsquo;억지로&amp;rsquo;라는 부정 키워드가 들어갔음에도 일찍 자기를 추천했는데요. 이는 긍정, 부정과 관계없이 확실히 주어진 컨텍스트들을 다양한 관점에서 깊이 있게 분석하고 활용해서 추천을 한 듯한 결과를 보여주고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style7&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span&gt; &lt;/span&gt; 정리하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HyperCLOVA X THINK를 직접 사용해 보면서 여러 활용 방법들에 대해 알아보았는데요. 직접 써보면서 느꼈던 점은 이제는 복잡한 질문을 처리하기 위해 단계별로 모델을 호출하는 과정을 직접 설계하지 않고 정돈되지 않은 컨텍스트나 질문들을 마구잡이로 던져도 모델이 이를 이해하고 정확한 결과를 뽑아낸다는 점에서 조금 더 AGI 수준에 한 발짝 다가가지 않았나 하는 생각이 듭니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또 국내 모델이라는 점에서 확실히 한글에 대한 이해도가 뛰어나서 그런지 추론 능력도 한층 더 높은 것 같다는 생각이 듭니다. 한국의 경우 다양한 방언들과 유사어가 존재하는데 이러한 특성들을 반영하기 위해서 별도로 용어집을 구축한다거나 워크플로우를 설계하지 않아도 HCX-007 추론 모델을 활용하면 사용자의 의도를 올바르게 이해하고 답변을 생성해 낸다는 점에서 앞으로는 실 생활에서 AI를 사용하기가 더욱 편리해질 것 같습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>AI/HyperCLOVAX</category>
      <author>외계공룡</author>
      <guid isPermaLink="true">https://chucoding.tistory.com/161</guid>
      <comments>https://chucoding.tistory.com/161#entry161comment</comments>
      <pubDate>Sat, 16 Aug 2025 17:48:48 +0900</pubDate>
    </item>
    <item>
      <title>네이버 개발자들은 AI Agent를 어떻게 사용하고 있을까? - 네이버클라우드 AI DEVDAY 참여후기</title>
      <link>https://chucoding.tistory.com/160</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;KakaoTalk_20250720_124514099.jpg&quot; data-origin-width=&quot;4000&quot; data-origin-height=&quot;3000&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/2fGUw/btsPripNh6M/NKwo6hbfNx9QIk3JzTWT7K/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/2fGUw/btsPripNh6M/NKwo6hbfNx9QIk3JzTWT7K/img.jpg&quot; data-alt=&quot;빼곡히 쌓인 네이버클라우드 AI DevDay 컨퍼런스 좌석&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/2fGUw/btsPripNh6M/NKwo6hbfNx9QIk3JzTWT7K/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F2fGUw%2FbtsPripNh6M%2FNKwo6hbfNx9QIk3JzTWT7K%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;4000&quot; height=&quot;3000&quot; data-filename=&quot;KakaoTalk_20250720_124514099.jpg&quot; data-origin-width=&quot;4000&quot; data-origin-height=&quot;3000&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;빼곡히 쌓인 네이버클라우드 AI DevDay 컨퍼런스 좌석&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;2025년 7월 15일 네이버클라우드의 AI 개발자 컨퍼런스, AI DevDay가 진행되었습니다. 이번 컨퍼런스에서는 LLM과 AI Agent 기술 트렌드, 아키텍처 설계, 도입 사례까지 생성형 AI 기술 흐름과 실전 적용 전략을 &amp;ldquo;Production-Ready&amp;rdquo; 관점에서 깊이 있게 다룬다는 점에서 좀 더 개발자들에게 흥미가 가는 세션들이 많이 준비가 되어 있었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저도 최근에 랭그래프, 랭체인을 활용한 AI 프로젝트를 진행했었기에 이번 컨퍼런스를 굉장히 관심을 갖고 기다려왔었는데요. 이번 포스팅에는 AI DevDay에 참여하면서 보고 느꼈던 점들에 대해 공유드리도록 하겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style7&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;세션 구성&lt;/h2&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;지금 주목할 LLM 기술 흐름과 생성형 AI 적용 인사이트 - 네이버클라우드 강지나 수석&lt;/li&gt;
&lt;li&gt;Multi-AI Agent 아키텍처와 구현 전략 - 네이버클라우드 허창현 리더&lt;/li&gt;
&lt;li&gt;AI Agent 구현을 위한 MCP 활용 방안 - 네이버클라우드 최장호 수석&lt;/li&gt;
&lt;li&gt;NVIDIA NeMo 마이크로서비스를 이용한 AI Agent 고도화 - NVIDIA 나승구 데브렐&lt;/li&gt;
&lt;li&gt;대화형 에이전트를 위한 Audio LLM, HyperCLOVA X Audio - 네이버클라우드 최상혁 연구원&lt;/li&gt;
&lt;li&gt;[패널토크] 현장에서 답을 찾다: 생성형 AI 실전 도입 전략 - 클라비 기술연구소 이용준 상무, 네이버클라우드 허창현 리더&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1753017989149&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;[마감] NAVER Cloud AI DevDay (AI 개발자 컨퍼런스 - 7.15 @선릉)&quot; data-og-description=&quot;네이버클라우드의 AI 개발자 컨퍼런스, AI DevDay에 여러분을 초대합니다! 이번 컨퍼런스는에서는 L...&quot; data-og-host=&quot;blog.naver.com&quot; data-og-source-url=&quot;https://blog.naver.com/n_cloudplatform/223912089570?trackingCode=rss&quot; data-og-url=&quot;https://blog.naver.com/n_cloudplatform/223912089570&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/dEm122/hyZnfwlo7d/C7SEqtGe3onF1YnslujgPK/img.jpg?width=743&amp;amp;height=418&amp;amp;face=0_0_743_418&quot;&gt;&lt;a href=&quot;https://blog.naver.com/n_cloudplatform/223912089570?trackingCode=rss&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://blog.naver.com/n_cloudplatform/223912089570?trackingCode=rss&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/dEm122/hyZnfwlo7d/C7SEqtGe3onF1YnslujgPK/img.jpg?width=743&amp;amp;height=418&amp;amp;face=0_0_743_418');&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;[마감] NAVER Cloud AI DevDay (AI 개발자 컨퍼런스 - 7.15 @선릉)&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;네이버클라우드의 AI 개발자 컨퍼런스, AI DevDay에 여러분을 초대합니다! 이번 컨퍼런스는에서는 L...&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;blog.naver.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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style7&quot; /&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; 1. LLM 기술 흐름과 생성형 AI 적용 인사이트&lt;/b&gt;&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;image.png&quot; data-origin-width=&quot;1487&quot; data-origin-height=&quot;834&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bnwuaF/btsPqTqey3v/jRvE0yWchwV0jIb1qDjcs1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bnwuaF/btsPqTqey3v/jRvE0yWchwV0jIb1qDjcs1/img.png&quot; data-alt=&quot;지금 주목할 LLM 기술 흐름과 생성형 AI 적용 인사이트 - 네이버클라우드 강지나&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bnwuaF/btsPqTqey3v/jRvE0yWchwV0jIb1qDjcs1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbnwuaF%2FbtsPqTqey3v%2FjRvE0yWchwV0jIb1qDjcs1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1487&quot; height=&quot;834&quot; data-filename=&quot;image.png&quot; data-origin-width=&quot;1487&quot; data-origin-height=&quot;834&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;지금 주목할 LLM 기술 흐름과 생성형 AI 적용 인사이트 - 네이버클라우드 강지나&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;첫 번째 세션에서는 현재 LLM의 기술 흐름과 생성형 AI 적용 시 고려해야 할 점들에 대한 소개가 이루어졌습니다. 더불어 네이버클라우드의 Agentic AI 고객사 활용사례와 도메인 적용 시 고려사항 등에 대해 알아볼 수 있었던 점도 흥미로웠는데요. 우선 현재 AI는 어떤 분야와 산업에서 사용되고 있는지? 그 활용 사례부터 알아보도록 하겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 활용 사례로는 보안취약점 처리, 데이터 분석, QnA 등등이 소개되었는데요. GenAI를 통해 애플리케이션 취약성 경고를 분석하고 오탐률을 줄임으로써 안정적인 운영을 할 수가 있게 되었고 과거 매출, 계절별 추세, 마케팅 캠페인, 경제 지표, 소셜 미디어 여론 등을 통해 다양한 데이터를 분석하여 정확한 수요 예측을 생성할 수 있게 되었다고 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한, 고객사에서 기존의 단순한 Q&amp;amp;A 챗봇을 넘어서 사용자 요청을 이해하고 백엔드 시스템(예: 주문 시스템, 재고 관리 시스템)에 접속하여 필요한 정보를 쿼리 하거나 환불, 재주문, 기록 업데이트 등의 실제 작업을 자율적으로 수행하는 정도 수준의 Agentic AI를 요구한다는 점을 알 수 있었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;image (1).png&quot; data-origin-width=&quot;1118&quot; data-origin-height=&quot;620&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/6gJfc/btsPqRTpqNs/1tSp90AvmMaOhsoykCl8Yk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/6gJfc/btsPqRTpqNs/1tSp90AvmMaOhsoykCl8Yk/img.png&quot; data-alt=&quot;Agentic AI 구현시 고려요소 6가지&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/6gJfc/btsPqRTpqNs/1tSp90AvmMaOhsoykCl8Yk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F6gJfc%2FbtsPqRTpqNs%2F1tSp90AvmMaOhsoykCl8Yk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1118&quot; height=&quot;620&quot; data-filename=&quot;image (1).png&quot; data-origin-width=&quot;1118&quot; data-origin-height=&quot;620&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Agentic AI 구현시 고려요소 6가지&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;그에 맞게 Agentic AI 도입 시 고려사항에 대해서는 다음과 같이 6가지 요소로 구분하여 소개해주셨는데요. 먼저 어떤 분야에서 어떤 역할을 수행할지 범위(Scope)를 정하고 나서 검색엔진으로 처리해야 할지? AI 모델이 처리해야 할지 에 대한 계획(Planning)을 세우고 장기 기억과 단기 기억(Memory) 중에 어떤 것이 필요한지 정하는 등을 고려해야 한다고 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나아가 어떤 Agent 도구를 사용할 것인지? 데이터 파이프라인은 RAG를 사용할 것인지 SQL을 사용할 것인지 또 정확성, 일관성에 대한 평가지표는 어떻게 세울 것인지 등등의 전략을 세우는 것도 중요합니다. 이를 통해 첫 번째 세션에서는 AI 프로젝트 진행 시 기획&amp;amp;분석 단계에서 어떻게 진행하면 좋을지에 대한 인사이트를 얻을 수 있었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style7&quot; /&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. Multi-AI Agent 아키텍처와 구현 전략&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;image (2).png&quot; data-origin-width=&quot;1038&quot; data-origin-height=&quot;579&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/biIoPi/btsPp0p7Ork/Lkt7HDsgmkPF9h9r3AfP41/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/biIoPi/btsPp0p7Ork/Lkt7HDsgmkPF9h9r3AfP41/img.png&quot; data-alt=&quot;Multi-AI Agent 아키텍처와 구현 전략 - Cloud Solution Architect 허창현&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/biIoPi/btsPp0p7Ork/Lkt7HDsgmkPF9h9r3AfP41/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbiIoPi%2FbtsPp0p7Ork%2FLkt7HDsgmkPF9h9r3AfP41%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1038&quot; height=&quot;579&quot; data-filename=&quot;image (2).png&quot; data-origin-width=&quot;1038&quot; data-origin-height=&quot;579&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Multi-AI Agent 아키텍처와 구현 전략 - Cloud Solution Architect 허창현&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 번째 세션으로는 Multi-AI Agent 아키텍처와 구현 전략에 대한 소개가 진행되었습니다. 어떻게 보면 제가 제일 기대했던 세션이라고 볼 수 있는데요. 개발자들은 과연 생성형 AI를 어떻게 도입하고 활용할 수 있는가에 대한 내용이 주로 이루어졌습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 단일 에이전트의 한계와 멀티 에이전트의 필요성을 시작으로 A2A, MCP 등의 프로토콜을 적재적소에 활용할 수 있는 방법으로 에이전트 프레임워크의 종류에 대해 소개가 이어졌습니다. 현재 가장 많이 쓰이고 있는 Agent 프레임워크는 다음과 같다고 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;LangChain/LangGraph&lt;/li&gt;
&lt;li&gt;AutoGen&lt;/li&gt;
&lt;li&gt;OpenAI Agents SDK&lt;/li&gt;
&lt;li&gt;Google ADK&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그중에서도 저는 랭체인과 랭그래프를 사용해 보았기 때문에 꽤나 친숙했지만 다른 프레임워크들에 대해서는 사실 이름만 들어본 상태라 잘 알지는 못했는데요. 이 프레임워크들은 각각 특성이 달라서 서로 유기적으로 사용할 수 있다고 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;랭그래프는 노드 엣지 들을 연결하여 그래프들을 엮어 절차적인 수행(workflow)을 설계하는 것이고 AutoGen은 AI가 서로 주어진 프롬프트를 통해 대화하며 결괏값을 만들어낸다고 합니다.&lt;/p&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://langchain-ai.github.io/langgraph/concepts/multi_agent/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;랭그래프 가이드문서&lt;/a&gt;에도 잘 나와있지만, 네이버클라우드 SA팀에서 가장 많이 쓰고 있는 패턴은 Supervisor 패턴과 SWARM 패턴이라고 합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/B92qe/btsPqz6y16u/eKehDumRfph33Se5eKhY7k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/B92qe/btsPqz6y16u/eKehDumRfph33Se5eKhY7k/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;520&quot; data-origin-height=&quot;333&quot; data-filename=&quot;image (3).png&quot; style=&quot;width: 52.4167%; margin-right: 10px;&quot; data-widthpercent=&quot;53.03&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/B92qe/btsPqz6y16u/eKehDumRfph33Se5eKhY7k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FB92qe%2FbtsPqz6y16u%2FeKehDumRfph33Se5eKhY7k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;520&quot; height=&quot;333&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cwxAs8/btsPrMcSvT2/xQK6dflBA87J4pQGcwSrl1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cwxAs8/btsPrMcSvT2/xQK6dflBA87J4pQGcwSrl1/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;567&quot; data-origin-height=&quot;410&quot; data-filename=&quot;image (4).png&quot; style=&quot;width: 46.4205%;&quot; data-widthpercent=&quot;46.97&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cwxAs8/btsPrMcSvT2/xQK6dflBA87J4pQGcwSrl1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcwxAs8%2FbtsPrMcSvT2%2FxQK6dflBA87J4pQGcwSrl1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/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;410&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;Supervisor 패턴과 SWARM 패턴&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;Supervisor 패턴은 말 그대로 관리자(Supervisor)가 관리 감독하는 트리형(계층형) 에이전트로써 서로 통신할 때는 이러한 레이어를 지키면서 통신하는 아키텍처입니다. 이 패턴을 확장해서 Multi Supervisor 패턴도 있지만 Supervisor끼리의 라우트를 구성하는 것은 또 다른 어려움이 있어 이 또한 해결해야 하는 과제 중 하나라고 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SWARM 패턴은 현재 가장 많이 쓰이는 패턴으로 핸드오프(Handoff)라는 개념이 도입됩니다. 질문에 따라 에이전트들은 각각 다양한 tool을 사용하고 다양한 데이터를 사용할 수 있는데 이 상태를 전역적으로 저장하지 않고(Stateless) 다른 에이전트에게 작업을 넘기는 과정(Handoff)을 거치기 때문에 전체적인 메모리 관리 기능이 필요하지 않은 경우 가볍게 구현할 수 있다는 장점이 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제로 도메인이 늘어날 때마다 퍼포먼스가 어떤지를 벤치마킹해 보았을 때 supervisor와 swarm 패턴은 안정적인 반면에 single 패턴은 비용과 품질(점수)이 저하되는 현상을 보실 수 있었다고 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;image (5).png&quot; data-origin-width=&quot;757&quot; data-origin-height=&quot;268&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/XPtRO/btsPrsyT8Pb/KlT1GqaSjtrcpmpxeeB6eK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/XPtRO/btsPrsyT8Pb/KlT1GqaSjtrcpmpxeeB6eK/img.png&quot; data-alt=&quot;네이버클라우드 SA팀이 진행한 Score와 Cost에 대한 아키텍처 패턴 벤치마킹 결과&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/XPtRO/btsPrsyT8Pb/KlT1GqaSjtrcpmpxeeB6eK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FXPtRO%2FbtsPrsyT8Pb%2FKlT1GqaSjtrcpmpxeeB6eK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;757&quot; height=&quot;268&quot; data-filename=&quot;image (5).png&quot; data-origin-width=&quot;757&quot; data-origin-height=&quot;268&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;네이버클라우드 SA팀이 진행한 Score와 Cost에 대한 아키텍처 패턴 벤치마킹 결과&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style7&quot; /&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. AI Agent 구현을 위한 MCP 활용방안&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;image (6).png&quot; data-origin-width=&quot;905&quot; data-origin-height=&quot;503&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/JR7BC/btsPrp3fFlK/PrJkLceKI2F8UKA0xDkGkK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/JR7BC/btsPrp3fFlK/PrJkLceKI2F8UKA0xDkGkK/img.png&quot; data-alt=&quot;AI Agent 구현을 위한 MCP 활용방안 - Cloud Solution Architect 최창호&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/JR7BC/btsPrp3fFlK/PrJkLceKI2F8UKA0xDkGkK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FJR7BC%2FbtsPrp3fFlK%2FPrJkLceKI2F8UKA0xDkGkK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/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;503&quot; data-filename=&quot;image (6).png&quot; data-origin-width=&quot;905&quot; data-origin-height=&quot;503&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;AI Agent 구현을 위한 MCP 활용방안 - Cloud Solution Architect 최창호&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;세 번째 세션은 MCP(Model Context Protocol) 활용방안과 구현 방법에 대한 설명이 주로 이루어졌습니다. MCP는 제가 근무하고 있는 회사에서도 하반기 때 지원 예정에 있었기 때문에 좀 더 집중해서 재밌게 들었던 세션이었습니다. MCP는 클로드에서 정의한 AI 모델이 데이터소스나 도구를 연결할 수 있는 표준화된 프로토콜입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서, MCP가 제대로 흥행하려면 얼마나 많은 도구를 제공하고 있는지가 관건이었는데 smithery, glama 등에서 이미 많은 MCP 서버(도구)들이 준비되어 있었기 때문에 흥행할 수 있었고 사용하는 입장에서는 사용방법만 알면 편리하게 필요한 MCP 서버들을 가져다 쓰기만 하면 되어 더욱 손쉽게 접근할 수 있었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MCP 구현방법에 대해서는 client와 server 두 가지 사용방법에 대해 모두 알아볼 수 있었습니다. 먼저 client의 경우에는 클로드나 Cursor, ChatGPT 같은 도구에서 MCP 서버를 등록해서 사용할 수 있습니다. 만약 직접 에이전트를 구축하는 경우에는 랭그래프 또는 crewAI에서 Agent를 리액트 방식으로 많이 구현한다고 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Server를 구축하는 경우에는 FastMCP SDK를 사용하면 편리하다고 합니다. FastMCP를 사용하는 경우 데코레이터를 통해 간편하게 작성할 수 있게 됩니다. 다만, 가급적이면 컨테이너로 분리해서 사용하고 보안상 Remote MCP 서버보다는 로컬서버에 MCP서버를 구축해서 사용하는 것을 권장하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1753018601799&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from mcp.server.fastmcp import FastMCP

mcp = FastMCP(
    &quot;Math&quot;,
    instructions=&quot;You are a math assistant&quot;
)

@mcp.tool()
def mymcp() -&amp;gt; str:
    return &quot;MCP 입니다.&quot;
    
if __name__ == &quot;__main__&quot;:
    mcp.run(transport=&quot;studio&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;MCP 사용 모델을 선정할 때는 tool calling을 잘하는 모델과 Agent를 찾는 것과 tool의 Output과 LLM Input 간의 포맷을 잘 맞추는 것이 중요하고 이 모든 것들을 구동할 수 있는 프레임워크를 선정하는 것도 중요하다고 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style7&quot; /&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. NVIDIA NeMo 마이크로서비스를 이용한 AI Agent 고도화&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;image (7).png&quot; data-origin-width=&quot;903&quot; data-origin-height=&quot;502&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ceSYQU/btsPp2nDE5f/iwNvePTsH7MbqL75dmpy6K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ceSYQU/btsPp2nDE5f/iwNvePTsH7MbqL75dmpy6K/img.png&quot; data-alt=&quot;NVIDIA NeMo 마이크로서비스를 이용한 AI Agent 고도화 - 나승구 NVIDIA DevRel&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ceSYQU/btsPp2nDE5f/iwNvePTsH7MbqL75dmpy6K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FceSYQU%2FbtsPp2nDE5f%2FiwNvePTsH7MbqL75dmpy6K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;903&quot; height=&quot;502&quot; data-filename=&quot;image (7).png&quot; data-origin-width=&quot;903&quot; data-origin-height=&quot;502&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;NVIDIA NeMo 마이크로서비스를 이용한 AI Agent 고도화 - 나승구 NVIDIA DevRel&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;네 번째 세션에서는 엔비디아의 데브렐을 담당하고 계신 분께서 직접 연사를 맡아주셨습니다. NVIDIA의 후원으로 진행되는 행사였기 때문에 역시나 NVIDIA의 세션도 포함되었었는데요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;NVIDIA에서는 새로 나온 소프트웨어 NeMo에 대해 소개를 하였습니다. Nemo는 생성형 AI 모델과 AI 에이전트를 구축&amp;middot;맞춤화할 수 있는 모듈형 플랫폼이라고 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 중에서 가장 인기가 많은 제품은 NeMo Curator라고 합니다. NeMo Curator는 텍스트. 이미지, 비디오 데이터셋을 확장 가능하고 유연하게 큐레이션 하여 모델 정확도를 향상하는 python SDK이며 다양한 GPU 가속 기능을 제공하고 있어 국내 대기업들에서도 사용하고 있는 사례들을 엿볼 수 있었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;NeMo Curator 외에도 파인튜닝을 단순화하고 가속화시킬 수 있는 NeMo Customizer, 보안과 안정성을 충족시켜 주는 NeMo Guardrails, Rag에 특화된 NeMo Retriever 등도 있으니 관심 있으신 분들은 한번 찾아보아도 좋을 것 같다는 생각이 듭니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style7&quot; /&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. 대화형 에이전트를 위한 Audio LLM, HyperCLOVA X Audio&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;image (8).png&quot; data-origin-width=&quot;898&quot; data-origin-height=&quot;501&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bXGjvx/btsPqkWawBZ/D23xzXkpHwGbaoc27T5eI1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bXGjvx/btsPqkWawBZ/D23xzXkpHwGbaoc27T5eI1/img.png&quot; data-alt=&quot;대화형 에이전트를 위한 Audio LLM, HyperCLOVA X Audio - 네이버클라우드 최상혁&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bXGjvx/btsPqkWawBZ/D23xzXkpHwGbaoc27T5eI1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbXGjvx%2FbtsPqkWawBZ%2FD23xzXkpHwGbaoc27T5eI1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/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;501&quot; data-filename=&quot;image (8).png&quot; data-origin-width=&quot;898&quot; data-origin-height=&quot;501&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;대화형 에이전트를 위한 Audio LLM, HyperCLOVA X Audio - 네이버클라우드 최상혁&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다섯 번째 세션으로는 네이버클라우드의 멀티모달 LLM인 Audio Agent에 대해 소개가 되었습니다. 이번 세션에서는 영화 아이언맨의 자비스처럼 대화에 딜레이가 없고 얼마나 자연스러운 AI 모델을 만들 수 있을지에 대해 연구한 내용을 공유를 해주었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Audio Agent의 진화 과정에 대해 소개해준 부분이 인상적이었는데 가장 간단한 구현 방식은 Cascaded 방식으로 기존의 모듈들을 전부 합치는 방식이며 저도 많이 사용했던 방법입니다. 예를 들어, STT를 통해 사람의 목소리를 텍스트로 변환해서 AI 모델에게 전달하고 TTS를 통해 텍스트를 사람의 목소리로 변환하여 전달하는 방식입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 이를 넘어서 Real-Time으로 넘어가려면 사실 모델이 turn-tasking 경계에 국한되지 않고 입출력을 동시에 모델링해서 Interrupting에 반응할 수 있는 Agent까지 나아가야 한다고 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재는 그 정도까지는 아니지만 300ms 수준까지 레이턴시를 낮춰서 자연스러운 대화가 가능한 정도까지 구현이 되었다고 하는데요. 실제로 시연 영상을 보니 굉장히 자연스러워서 가장 놀라웠었던 세션 중의 하나였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style7&quot; /&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. &lt;span data-token-index=&quot;1&quot;&gt;[패널토크] 현장에서 답을 찾다: 생성형 AI 실전 도입 전략&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막으로는 패널토크가 진행되었었는데요. HyperCLOVA X 도입 첫 사례인 클라비의 기술연구소 상무님과 네이버클라우드 SA팀 리더분께서 직접 참여를 하셨고 현재 70여 건 이상의 실제 수행 사례를 기반으로, 시장이 기대하는 생성형 AI의 역할과 AI 개발자가 준비해야 할 핵심 포인트들에 대해 알 수 있었던 시간이었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제로 필요했던 기술들은 쿠버네티스의 적용, 데이터 전처리의 자동화, 랭체인 모듈화 등으로 발 빠르게 대응하는 것이 중요하며, 고객사의 기대치에 못 미치는 AI 품질들에 대해서는 점진적인 개선을 통한 약속을 통해 비즈니스를 체결하는 전략을 구사한다고 합니다. 그리고 개발자들이 모든 도메인을 전부 이해할 수는 없으므로 구축 시에는 고객사와 같이 공동작업을 하는 것도 방법이라고 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기술적으로 성능을 높였던 방법으로는 임베딩 모델을 튜닝하는 것과 리랭킹 모델로 정합성을 높이는 등의 문제를 직접 해결하는 등 클라비의 높은 기술력도 엿볼 수 있는 시간이었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style7&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: left;&quot; data-ke-size=&quot;size26&quot;&gt;  소감 및 후기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 컨퍼런스를 통해 느낀 점은 작년에는 모델의 성능과 품질이 중요했다면 이제는 다양한 에이전트들이 준비되어 있으므로 개발자들이 활발하게 움직여야 할 때가 되었다는 점입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;패널토크에서 나왔던 질문 중 하나가 인상적이었는데요. 누군가 AI기술자가 갖춰야 할 스킬 셋과 마인드셋에 대해 여쭤보았는데 네이버클라우드 SA팀 리더분께서는 개발자들도 각 모델이 가지고 있는 특성과 모델에 대한 이해가 필요하므로 튜닝도 직접 해보고 Swarm, Supervisor 패턴 등을 통해 튜닝 모델을 활용해 보는 것도 중요하다고 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서, AI 개발자를 꿈꾸는 분들에게는 이러한 점들을 고려해서 커리어를 만들어가 보면 어떨까 싶습니다. &lt;/p&gt;</description>
      <category>독서 및 기타 활동</category>
      <author>외계공룡</author>
      <guid isPermaLink="true">https://chucoding.tistory.com/160</guid>
      <comments>https://chucoding.tistory.com/160#entry160comment</comments>
      <pubDate>Sun, 20 Jul 2025 22:57:23 +0900</pubDate>
    </item>
    <item>
      <title>ncloud 서버 생성&amp;nbsp;- Classic 대신 VPC 플랫폼 사용하기</title>
      <link>https://chucoding.tistory.com/159</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;918&quot; data-origin-height=&quot;378&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cTJZlz/btsNJVdgbyl/DANRk2IFlxk3YiEKOhXdOK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cTJZlz/btsNJVdgbyl/DANRk2IFlxk3YiEKOhXdOK/img.png&quot; data-alt=&quot;네이버클라우드 플랫폼 Classic 대신 VPC 사용하기&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cTJZlz/btsNJVdgbyl/DANRk2IFlxk3YiEKOhXdOK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcTJZlz%2FbtsNJVdgbyl%2FDANRk2IFlxk3YiEKOhXdOK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/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;378&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;918&quot; data-origin-height=&quot;378&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;네이버클라우드 플랫폼 Classic 대신 VPC 사용하기&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;네이버클라우드 플랫폼에서 제공하는 서버 인스턴스는 굉장히 많고 다양합니다. 따라서 어떤 상품을 선택해야 하나 고민이 생기고 다소 복잡하다고 느낄 수가 있는데요. 3년 전까지만 해도 Classic 플랫폼에서 다양한 OS 이미지들과 스펙을 지원했는데 지금은 점점 지원되는 영역이 축소되어 VPC 플랫폼이 선택이 아닌 필수가 되었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 이번 포스팅에서는 더 이상 Classic 플랫폼을 사용하는게 아닌 VPC 플랫폼에서 간단하게 서버를 생성하는 방법에 대해 알아보도록 하겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style7&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span&gt; &lt;/span&gt; Classic 플랫폼의 Linux OS 지원 중단&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;네이버클라우드 플랫폼에서는 Classic과 VPC 두 가지 플랫폼을 지원하고 있는데요. 이전에는 Classic 플랫폼에서 좀 더 다양한 서버 타입과 저렴한 가격대를 지원했었지만 시간이 지나면서 OS 이미지들이 점차 EOS(End Of Sales) 되면서 상황이 역전되었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2025년부터는 마지막 Linux OS인 CentOS 7.3과 7.8 마저 지원이 종료되면서 이제는 더 이상 Classic 플랫폼에서 Linux OS로 인스턴스를 생성할 수 없게 되었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1746456923314&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;NAVER CLOUD PLATFORM&quot; data-og-description=&quot;cloud computing services for corporations, IaaS, PaaS, SaaS, with Global region and Security Technology Certification&quot; data-og-host=&quot;www.ncloud.com&quot; data-og-source-url=&quot;https://www.ncloud.com/support/notice/all/1821?page=6&quot; data-og-url=&quot;https://www.ncloud.com&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/ouq1J/hyYMYCFMT9/prqtupOrN5KHU6jIX6cCE1/img.jpg?width=526&amp;amp;height=274&amp;amp;face=0_0_526_274,https://scrap.kakaocdn.net/dn/dxujtE/hyYRu0ZD4O/CpEC8LFyvNEdaKlvKiLegK/img.png?width=1600&amp;amp;height=720&amp;amp;face=0_0_1600_720,https://scrap.kakaocdn.net/dn/cz1Nce/hyYRnHyJpT/228Ly4BWgxIkWl8XoNyNm0/img.png?width=1600&amp;amp;height=720&amp;amp;face=0_0_1600_720&quot;&gt;&lt;a href=&quot;https://www.ncloud.com/support/notice/all/1821?page=6&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.ncloud.com/support/notice/all/1821?page=6&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/ouq1J/hyYMYCFMT9/prqtupOrN5KHU6jIX6cCE1/img.jpg?width=526&amp;amp;height=274&amp;amp;face=0_0_526_274,https://scrap.kakaocdn.net/dn/dxujtE/hyYRu0ZD4O/CpEC8LFyvNEdaKlvKiLegK/img.png?width=1600&amp;amp;height=720&amp;amp;face=0_0_1600_720,https://scrap.kakaocdn.net/dn/cz1Nce/hyYRnHyJpT/228Ly4BWgxIkWl8XoNyNm0/img.png?width=1600&amp;amp;height=720&amp;amp;face=0_0_1600_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;NAVER CLOUD PLATFORM&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;cloud computing services for corporations, IaaS, PaaS, SaaS, with Global region and Security Technology Certification&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.ncloud.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;o CentOS 7.8 서버 종료 목적&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;안정된 서비스를 제공&lt;/li&gt;
&lt;li&gt;CentOS 7의 중요 보안 업데이트 지원 종료에 따른 보안 이슈 해결&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;o 종료 일시&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;2024년 12월 19일(목) 18시 00분 (GMT + 09:00)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;o 판매 종료 대상&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;CentOS 7.3, CentOS 7.8 서버&lt;/li&gt;
&lt;li&gt;CentOS 7.3 또는 7.8 OS 기반의 설치형 이미지 서버&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같은 상황으로 인해 현재 Classic 플랫폼에는 2027년 1월 12일까지 사용가능한 win-2016-64-en 만 남아있는 상황인데요. Window는 무료 OS가 아니기 때문에 사용하려면 20,000원가량의 OS 사용 비용을 별도로 청구해야 합니다.(Window&amp;nbsp;2016도 2027년 1월 12일에 종료될 예정 &lt;a href=&quot;https://guide.ncloud-docs.com/docs/ncp-os-lifecycle&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://guide.ncloud-docs.com/docs/ncp-os-lifecycle&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;현시점에서 Classic 플랫폼과 VPC 플랫폼에서의 가장 저렴한 Compute 가격을 비교해 보면 다음과 같습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/vQGst/btsNKhf4lpz/pa6VHhn0KL0iyEgsOcTgy1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/vQGst/btsNKhf4lpz/pa6VHhn0KL0iyEgsOcTgy1/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;962&quot; data-origin-height=&quot;724&quot; data-filename=&quot;image.png&quot; style=&quot;width: 53.2376%; margin-right: 10px;&quot; data-widthpercent=&quot;53.86&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/vQGst/btsNKhf4lpz/pa6VHhn0KL0iyEgsOcTgy1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FvQGst%2FbtsNKhf4lpz%2Fpa6VHhn0KL0iyEgsOcTgy1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/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;724&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cihg25/btsNKn78DGR/BhAwtOgo47pNOF6kedb4xk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cihg25/btsNKn78DGR/BhAwtOgo47pNOF6kedb4xk/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;956&quot; data-origin-height=&quot;840&quot; data-filename=&quot;image (1).png&quot; style=&quot;width: 45.5996%;&quot; data-widthpercent=&quot;46.14&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cihg25/btsNKn78DGR/BhAwtOgo47pNOF6kedb4xk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcihg25%2FbtsNKn78DGR%2FBhAwtOgo47pNOF6kedb4xk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/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;840&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;좌 Classic / 우 VPC (※ 요금계산기 - 시간 요금제가 아닌 월 요금제로 비교) https://www.ncloud.com/charge/calc/ko?category=compute#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;p data-ke-size=&quot;size16&quot;&gt;위 정보들을 토대로 현재 네이버클라우드 플랫폼에서 제공되는 가장 저렴한 스펙은 &lt;u&gt;VPC 플랫폼의 3세대 High CPU 서버 타입인 2 코어 4G 메모리 10GB 스토리지&lt;/u&gt;입니다.(※ Micro 서버 타입 제외)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Window 서버의 단점은 인스턴스 생성부터 부팅 시간까지 최소 30분 이상은 대기해야 하기 때문에 사용하는 데 있어 불편함을 느낄 수 있고, 서버에 접속하려면 터미널이 아닌 원격 데스크톱을 통해야만 접속이 가능하기 때문에 다소 불편함을 느낄 수 있습니다. 특히 MAC 사용하시는 분들은 별도로 MS에서 제공하고 있는 Remote Desktop 앱을 설치하여야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서, 이제는 서버 인스턴스를 생성할 때 더 이상 Classic 플랫폼을 선택할 매리트가 없기 때문에 VPC 플랫폼을 선택하시는 것이 좋습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style7&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span&gt; &lt;/span&gt; VPC 플랫폼에서 서버 생성하기&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cZvGtk/btsNL1whoTz/WvTFlb4hKhVoE9yqIfreBk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cZvGtk/btsNL1whoTz/WvTFlb4hKhVoE9yqIfreBk/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;926&quot; data-origin-height=&quot;636&quot; data-filename=&quot;다운로드 (1).png&quot; style=&quot;width: 50.8891%; margin-right: 10px;&quot; data-widthpercent=&quot;51.49&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cZvGtk/btsNL1whoTz/WvTFlb4hKhVoE9yqIfreBk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcZvGtk%2FbtsNL1whoTz%2FWvTFlb4hKhVoE9yqIfreBk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/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;636&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dZA6G4/btsNLnzNTWN/gnn03KF2KbjuzcK9bcGQr1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dZA6G4/btsNLnzNTWN/gnn03KF2KbjuzcK9bcGQr1/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;974&quot; data-origin-height=&quot;710&quot; data-filename=&quot;다운로드.png&quot; style=&quot;width: 47.9481%;&quot; data-widthpercent=&quot;48.51&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dZA6G4/btsNLnzNTWN/gnn03KF2KbjuzcK9bcGQr1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdZA6G4%2FbtsNLnzNTWN%2Fgnn03KF2KbjuzcK9bcGQr1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;974&quot; height=&quot;710&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;좌 Classic / 우 VPC&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;Classic과 VPN 플랫폼의 차이는 Classic의 경우에는 위 이미지와 같이 IP가 랜덤으로 생성되어 각 서버 간에 브릿지를 구성하기가 쉽지 않고 대형 서비스일수록 시스템 복잡도가 증가할 수 있다는 단점이 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반면 VPC는 Virtual Private Cloud의 약자로써 네트워크 공간을 처음부터 통째로 할당받기 때문에 subnet 및 IP를 직접 지정할 수 있어 scale 및 보안 측면에서 우수합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;VPC 환경에서 서버 인스턴스를 생성할 때는 조금 더 복잡한 절차가 있을 것 같다는 생각이 들 수 있을 것 같은데 생각 외로 어렵지 않습니다. 간단한 네트워크 설정을 제외하고는 기존 Classic을 사용하실 때와 동일한 절차로 손쉽게 서버를 생성할 수 있습니다. 위에서 간단히 알아본 저렴한 스펙으로 간단하게 서버를 하나 띄워보도록 하겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 네이버클라우드 플랫폼 콘솔에 로그인하여 접속해 보도록 하겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://console.ncloud.com/dashboard&quot;&gt;https://console.ncloud.com/dashboard&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;가장 먼저 왼쪽 내비게이션 바에서 플랫폼을 Classic과 VPC 중에서 VPC를 선택해 주시면 됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;image.png&quot; data-origin-width=&quot;1909&quot; data-origin-height=&quot;1054&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/9HHGW/btsNJ6lxci8/iD9EnCNCJf4BgU0lNhyeX0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/9HHGW/btsNJ6lxci8/iD9EnCNCJf4BgU0lNhyeX0/img.png&quot; data-alt=&quot;왼쪽 네비게이션 바에서 Classic과 VPC 환경 중에 선택할 수 있다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/9HHGW/btsNJ6lxci8/iD9EnCNCJf4BgU0lNhyeX0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F9HHGW%2FbtsNJ6lxci8%2FiD9EnCNCJf4BgU0lNhyeX0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/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;1054&quot; data-filename=&quot;image.png&quot; data-origin-width=&quot;1909&quot; data-origin-height=&quot;1054&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;왼쪽 네비게이션 바에서 Classic과 VPC 환경 중에 선택할 수 있다.&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;이후에는 기존 Classic 플랫폼과 달리 Server를 생성하기 전에 먼저 VPC와 Subnet을 생성해야 합니다. 따라서 Services 메뉴에서 &lt;b&gt;[VPC]&lt;/b&gt; 메뉴를 찾아서 해당 화면으로 진입합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bvTHAI/btsNKAGqJRa/DdCh9lVNuKKIpqX6QJk771/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bvTHAI/btsNKAGqJRa/DdCh9lVNuKKIpqX6QJk771/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1918&quot; data-origin-height=&quot;1062&quot; data-filename=&quot;image (1).png&quot; style=&quot;width: 49.4651%; margin-right: 10px;&quot; data-widthpercent=&quot;50.05&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bvTHAI/btsNKAGqJRa/DdCh9lVNuKKIpqX6QJk771/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbvTHAI%2FbtsNKAGqJRa%2FDdCh9lVNuKKIpqX6QJk771%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/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;1062&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cUgz1T/btsNKztYTsM/UxT1jt5B8LBk1ePkPJrF9k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cUgz1T/btsNKztYTsM/UxT1jt5B8LBk1ePkPJrF9k/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1918&quot; data-origin-height=&quot;1064&quot; data-filename=&quot;image.png&quot; style=&quot;width: 49.3721%;&quot; data-widthpercent=&quot;49.95&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cUgz1T/btsNKztYTsM/UxT1jt5B8LBk1ePkPJrF9k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcUgz1T%2FbtsNKztYTsM%2FUxT1jt5B8LBk1ePkPJrF9k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/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;1064&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;VPC 서비스 화면 진입하기&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt; [+ VPC 생성]&lt;/b&gt; 버튼을 클릭하면 다음과 같이 VPC 이름과 private 대역, 유형을 선택할 수 있는 팝업창이 나타납니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;image (2).png&quot; data-origin-width=&quot;1912&quot; data-origin-height=&quot;1064&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dOWiWT/btsNLqDfn9D/JZBiHH4r43nj2X8hPtFqI1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dOWiWT/btsNLqDfn9D/JZBiHH4r43nj2X8hPtFqI1/img.png&quot; data-alt=&quot;VPC 생성하기&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dOWiWT/btsNLqDfn9D/JZBiHH4r43nj2X8hPtFqI1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdOWiWT%2FbtsNLqDfn9D%2FJZBiHH4r43nj2X8hPtFqI1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/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;1064&quot; data-filename=&quot;image (2).png&quot; data-origin-width=&quot;1912&quot; data-origin-height=&quot;1064&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;VPC 생성하기&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;private 대역은 사용할 네트워크 규모에 따라서 적절하게 선택하시면 됩니다. IP 주소 범위는 아래 표를 참고해서 선택하시면 됩니다.&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: 83.0233%; height: 72px;&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: 21px;&quot;&gt;
&lt;td style=&quot;width: 13.7597%; height: 21px;&quot;&gt;주소 범위&lt;/td&gt;
&lt;td style=&quot;width: 8.9923%; height: 21px;&quot;&gt;IP 개수&lt;/td&gt;
&lt;td style=&quot;width: 32.6356%; height: 21px;&quot;&gt;특징&lt;/td&gt;
&lt;td style=&quot;width: 27.6357%; height: 21px;&quot;&gt;추천 사용 사례&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 13.7597%; height: 17px;&quot;&gt;10.0.0.0/8&lt;/td&gt;
&lt;td style=&quot;width: 8.9923%; height: 17px;&quot;&gt;A 클래스&lt;/td&gt;
&lt;td style=&quot;width: 32.6356%; height: 17px;&quot;&gt;가장 큰 주소 공간(자유롭게 서브넷 가능)&lt;/td&gt;
&lt;td style=&quot;width: 27.6357%; height: 17px;&quot;&gt;대규모 네트워크, 기업 단위&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 13.7597%; height: 17px;&quot;&gt;127.16.0.0/12&lt;/td&gt;
&lt;td style=&quot;width: 8.9923%; height: 17px;&quot;&gt;B 클래스&lt;/td&gt;
&lt;td style=&quot;width: 32.6356%; height: 17px;&quot;&gt;중간 크기&lt;/td&gt;
&lt;td style=&quot;width: 27.6357%; height: 17px;&quot;&gt;중간 규모 네트워크, 부서, 팀 단위&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 13.7597%; height: 17px;&quot;&gt;192.168.0.0/16&lt;/td&gt;
&lt;td style=&quot;width: 8.9923%; height: 17px;&quot;&gt;C 클래스&lt;/td&gt;
&lt;td style=&quot;width: 32.6356%; height: 17px;&quot;&gt;가장 작은 범위&lt;/td&gt;
&lt;td style=&quot;width: 27.6357%; height: 17px;&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;VPC 유형은 NORMAL과 TRANSIT 2가지를 제공하는데 TRANSIT VPC는 여러 VPC 간 네트워크 연결을 위한 허브로 일반적인 경우에는 NORMAL을 선택해 주시면 됩니다. 자세한 내용은 네이버클라우드 가이드 문서를 통해 확인하실 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://guide.ncloud-docs.com/docs/vpc-manage-vpc&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://guide.ncloud-docs.com/docs/vpc-manage-vpc&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;VPC를 생성하고 나면 다음과 같이 생성 중이라는 상태메시지가 나타나며 완전히 생성될 때까지 기다려야 합니다. VPC가 생성되기까지는 대략 2~30분 정도로 꽤 긴 시간이 소요됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;제목 없음 (1).png&quot; data-origin-width=&quot;1910&quot; data-origin-height=&quot;1058&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/6T56x/btsNJYgLARe/DAFeFcP1CDkM09Ah5K16bK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/6T56x/btsNJYgLARe/DAFeFcP1CDkM09Ah5K16bK/img.png&quot; data-alt=&quot;VPC 생성 중 (대략 2~30분 정도 소요된다)&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/6T56x/btsNJYgLARe/DAFeFcP1CDkM09Ah5K16bK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F6T56x%2FbtsNJYgLARe%2FDAFeFcP1CDkM09Ah5K16bK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1910&quot; height=&quot;1058&quot; data-filename=&quot;제목 없음 (1).png&quot; data-origin-width=&quot;1910&quot; data-origin-height=&quot;1058&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;VPC 생성 중 (대략 2~30분 정도 소요된다)&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;VPC를 생성하고 나면 다음은 바로 아래 메뉴에 있는 &lt;b&gt;[Subnet Management]&lt;/b&gt; 메뉴를 클릭하여 Subnet 서비스로 이동합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;image.png&quot; data-origin-width=&quot;1912&quot; data-origin-height=&quot;1062&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lKZ1p/btsNKieWi9h/HRMnLkXl3nN86wzDcGDm0k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lKZ1p/btsNKieWi9h/HRMnLkXl3nN86wzDcGDm0k/img.png&quot; data-alt=&quot;VPC Subnet 서비스 화면&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lKZ1p/btsNKieWi9h/HRMnLkXl3nN86wzDcGDm0k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlKZ1p%2FbtsNKieWi9h%2FHRMnLkXl3nN86wzDcGDm0k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/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;1062&quot; data-filename=&quot;image.png&quot; data-origin-width=&quot;1912&quot; data-origin-height=&quot;1062&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;VPC Subnet 서비스 화면&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;마찬가지로&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;[+ Subnet 생성]&lt;/b&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;버튼을 클릭하면 팝업창이 나타납니다. &lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;Subnet도 이름과 IP 주소 범위 등을 입력해 주시면 됩니다. 주의하실 점은 Internet Gateway 전용 여부는 Public으로 설정해주셔야 추후에 공인 IP를 발급받아 외부 접속이 가능한 서버로 만드실 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;image.png&quot; data-origin-width=&quot;1919&quot; data-origin-height=&quot;1066&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cOy2Dd/btsNLnGv9pl/rKoYJJIIoM8rIFwXfFgdf0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cOy2Dd/btsNLnGv9pl/rKoYJJIIoM8rIFwXfFgdf0/img.png&quot; data-alt=&quot;VPC Subnet 생성하기&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cOy2Dd/btsNLnGv9pl/rKoYJJIIoM8rIFwXfFgdf0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcOy2Dd%2FbtsNLnGv9pl%2FrKoYJJIIoM8rIFwXfFgdf0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/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;1066&quot; data-filename=&quot;image.png&quot; data-origin-width=&quot;1919&quot; data-origin-height=&quot;1066&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;VPC Subnet 생성하기&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;Subnet 생성은 VPC 만큼 오래 걸리지 않습니다. 생성 후에는 1분 내외로 운영 상태로 변경됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;제목 없음 (1).png&quot; data-origin-width=&quot;1906&quot; data-origin-height=&quot;1061&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cXGlrG/btsNK9VTGw0/l1MURUwgDK8IKg6ooqTqI0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cXGlrG/btsNK9VTGw0/l1MURUwgDK8IKg6ooqTqI0/img.png&quot; data-alt=&quot;VPC Subnet 생성 완료&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cXGlrG/btsNK9VTGw0/l1MURUwgDK8IKg6ooqTqI0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcXGlrG%2FbtsNK9VTGw0%2Fl1MURUwgDK8IKg6ooqTqI0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/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;1061&quot; data-filename=&quot;제목 없음 (1).png&quot; data-origin-width=&quot;1906&quot; data-origin-height=&quot;1061&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;VPC Subnet 생성 완료&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;Subnet까지 생성 완료 했다면 이제 Server를 생성하실 수 있습니다. 다시 왼쪽 내비게이션바의 &lt;b&gt;[Services]&lt;/b&gt; 메뉴를 클릭하여 Server를 찾아 선택합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;image (1).png&quot; data-origin-width=&quot;1919&quot; data-origin-height=&quot;1059&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/csm8Gn/btsNLzUjWa9/3FKiNxv5bPxKlPOebbkWT0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/csm8Gn/btsNLzUjWa9/3FKiNxv5bPxKlPOebbkWT0/img.png&quot; data-alt=&quot;VPC 플랫폼 Server 서비스 이용하기&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/csm8Gn/btsNLzUjWa9/3FKiNxv5bPxKlPOebbkWT0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcsm8Gn%2FbtsNLzUjWa9%2F3FKiNxv5bPxKlPOebbkWT0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/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;1059&quot; data-filename=&quot;image (1).png&quot; data-origin-width=&quot;1919&quot; data-origin-height=&quot;1059&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;VPC 플랫폼 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;p data-ke-size=&quot;size16&quot;&gt;그 이후에는 &lt;b&gt;&lt;span data-token-index=&quot;1&quot;&gt;[+ 서버 생성]&lt;/span&gt;&lt;/b&gt; 버튼을 클릭하여 서비스 화면으로 이동합니다. 해당 화면에서도 마찬가지로 &lt;b&gt;[+ 서버 생성]&lt;/b&gt; 버튼을 클릭합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/eEBhSX/btsNLoMfjPX/MJDk0HM49ZfKQpbSm92zk0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/eEBhSX/btsNLoMfjPX/MJDk0HM49ZfKQpbSm92zk0/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1915&quot; data-origin-height=&quot;988&quot; data-filename=&quot;image (2).png&quot; style=&quot;width: 51.1742%; margin-right: 10px;&quot; data-widthpercent=&quot;51.78&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/eEBhSX/btsNLoMfjPX/MJDk0HM49ZfKQpbSm92zk0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FeEBhSX%2FbtsNLoMfjPX%2FMJDk0HM49ZfKQpbSm92zk0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/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;988&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/btDx1e/btsNMeIVibY/kaJ3tZo8JE709VxKnmhiE0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/btDx1e/btsNMeIVibY/kaJ3tZo8JE709VxKnmhiE0/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1919&quot; data-origin-height=&quot;1063&quot; data-filename=&quot;image.png&quot; style=&quot;width: 47.663%;&quot; data-widthpercent=&quot;48.22&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/btDx1e/btsNMeIVibY/kaJ3tZo8JE709VxKnmhiE0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbtDx1e%2FbtsNMeIVibY%2FkaJ3tZo8JE709VxKnmhiE0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/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;1063&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;신규 콘솔로 서버 생성하기&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약, 서버 생성이 처음이라면 기존 콘솔 화면과 신규 콘솔 화면 중에서 선택하라는 팝업창이 나타나게 되는데요. 저희는 3세대(g3) 서버를 사용해야 하기 때문에 신규 콘솔을 클릭해주셔야 합니다. 참고로 기존 콘솔은 25년 6월 19일까지만 지원될 예정이라고 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;신규 콘솔은 다음과 같이 구성되어 있습니다. 사용하고 싶은 OS를 선택하여 &lt;b&gt;[다음 &amp;gt;]&lt;/b&gt; 버튼을 클릭하시면 됩니다. 저는 무난한 Ubuntu 이미지를 선택하도록 하겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;image (1).png&quot; data-origin-width=&quot;1915&quot; data-origin-height=&quot;1063&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/deClBM/btsNKiziFl6/Tfdx1GKBTnT0jJnEwJ0kX0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/deClBM/btsNKiziFl6/Tfdx1GKBTnT0jJnEwJ0kX0/img.png&quot; data-alt=&quot;서버 이미지 선택하기&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/deClBM/btsNKiziFl6/Tfdx1GKBTnT0jJnEwJ0kX0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdeClBM%2FbtsNKiziFl6%2FTfdx1GKBTnT0jJnEwJ0kX0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/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;1063&quot; data-filename=&quot;image (1).png&quot; data-origin-width=&quot;1915&quot; data-origin-height=&quot;1063&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;서버 이미지 선택하기&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이미지를 선택하고 나면 이제 서버 설정을 해야 합니다. VPC 플랫폼에서는 Classic 플랫폼과 다르게 VPC와 Subnet을 설정하도록 되어있습니다. 저희는 기존에 VPC와 Subnet을 미리 만들어놓고 왔기 때문에 VPC와 Subnet을 각각 만들어주신 걸로 선택해 주시면 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;image (3).png&quot; data-origin-width=&quot;1919&quot; data-origin-height=&quot;841&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/SjFCb/btsNMfgLj4P/mrY3Hm7GSpYn2P8FqIdQa0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/SjFCb/btsNMfgLj4P/mrY3Hm7GSpYn2P8FqIdQa0/img.png&quot; data-alt=&quot;서버 설정하기(VPC, Subnet, 서버 스펙)&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/SjFCb/btsNMfgLj4P/mrY3Hm7GSpYn2P8FqIdQa0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FSjFCb%2FbtsNMfgLj4P%2FmrY3Hm7GSpYn2P8FqIdQa0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/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;841&quot; data-filename=&quot;image (3).png&quot; data-origin-width=&quot;1919&quot; data-origin-height=&quot;841&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;서버 설정하기(VPC, Subnet, 서버 스펙)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 이후에는 서버 스펙을 선택해 주시고 요금제를 선택해 주시면 됩니다. 저는 앞서 살펴보았던 가장 저렴한 스펙으로 선택을 한 후에 시간 요금제로 할당을 하였습니다. 시간 요금제로 설정해 놓으면 사용하지 않을 때는 인스턴스를 중지 상태로 변경하여 사용한 만큼만 요금을 낼 수 있어 효율적인 과금이 가능합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음은 서버개수와 서버 이름을 지정하신 다음 네트워크 인터페이스를 설정합니다. 저는 한 대만 가동할 예정이므로 네트워크 인터페이스는 바로 옆의&lt;b&gt; [+ 추가]&lt;/b&gt; 버튼을 클릭하여 IP를 자동으로 할당받을 수 있도록 설정하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;image (4).png&quot; data-origin-width=&quot;1911&quot; data-origin-height=&quot;1065&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cFRIOs/btsNL0Yqvbz/2YR9XXEKZSEcjAgdGtUFyk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cFRIOs/btsNL0Yqvbz/2YR9XXEKZSEcjAgdGtUFyk/img.png&quot; data-alt=&quot;서버 설정하기(네트워크, 공인IP)&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cFRIOs/btsNL0Yqvbz/2YR9XXEKZSEcjAgdGtUFyk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcFRIOs%2FbtsNL0Yqvbz%2F2YR9XXEKZSEcjAgdGtUFyk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/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;1065&quot; data-filename=&quot;image (4).png&quot; data-origin-width=&quot;1911&quot; data-origin-height=&quot;1065&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;서버 설정하기(네트워크, 공인IP)&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;추가로 기존에 Classic에서 사용할 때와 달리 VPC에서 서버를 생성할 때는 공인 IP를 바로 할당할 수 있다는 편리함이 있습니다. 저는 외부 접속 가능한 서버로 만들기 위해 공인 IP 할당까지 체크한 후에 &lt;b&gt;[다음 &amp;gt;]&lt;/b&gt; 버튼을 클릭하도록 하겠습니다. (월 이용료: 4,032원 주의)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음은 스토리지 설정입니다. 이곳은 간단하게 확인만 하고 넘어가도록 하겠습니다. 앞서 저희가 설정한 스펙에 할당할 수 있는 스토리지는 다음과 같으며 기본 10G 이상부터 설정 가능합니다. 10G를 넘어가면 1G당 초과 비용이 발생하므로 적당한 값으로 잘 설정하여 상황에 맞게 생성하시면 좋습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;image (5).png&quot; data-origin-width=&quot;1915&quot; data-origin-height=&quot;1060&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/l9SSL/btsNMamgoB7/1VVDk8Raza6KbjQjQjo4TK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/l9SSL/btsNMamgoB7/1VVDk8Raza6KbjQjQjo4TK/img.png&quot; data-alt=&quot;스토리지 설정하기&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/l9SSL/btsNMamgoB7/1VVDk8Raza6KbjQjQjo4TK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fl9SSL%2FbtsNMamgoB7%2F1VVDk8Raza6KbjQjQjo4TK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/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;1060&quot; data-filename=&quot;image (5).png&quot; data-origin-width=&quot;1915&quot; data-origin-height=&quot;1060&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;스토리지 설정하기&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음은 인증키를 설정해 주시면 됩니다. 인증키 같은 경우에는 Classic에서 사용했던 인증키 그대로 사용할 수 있습니다. 만약, 인증키가 없으시다면 새로운 인증키 생성을 선택하신 후 새 키를 발급받아 안전한 곳에 저장해 두시면 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;image.png&quot; data-origin-width=&quot;1916&quot; data-origin-height=&quot;1062&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bVe6IA/btsNMbrWIw0/J5V8YTXFgNb4SRvOhdBRAk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bVe6IA/btsNMbrWIw0/J5V8YTXFgNb4SRvOhdBRAk/img.png&quot; data-alt=&quot;인증키 설정하기&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bVe6IA/btsNMbrWIw0/J5V8YTXFgNb4SRvOhdBRAk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbVe6IA%2FbtsNMbrWIw0%2FJ5V8YTXFgNb4SRvOhdBRAk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/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;1062&quot; data-filename=&quot;image.png&quot; data-origin-width=&quot;1916&quot; data-origin-height=&quot;1062&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;인증키 설정하기&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음은 인바운드, 아웃바운드 등을 제어할 수 있는 네트워크 접근(방화벽) 설정을 선택합니다. 기존에 만들어놓은 ACG가 없다면 왼쪽 내비게이션바의 ACG탭에서 하나 생성해주셔야 합니다. ACG를 선택한 후에는 &lt;b&gt;[다음 &amp;gt;]&lt;/b&gt; 버튼을 클릭합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;image (1).png&quot; data-origin-width=&quot;1914&quot; data-origin-height=&quot;1062&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bNivHQ/btsNKyu5qBt/uQFcl6u165ia2Ngo0IHRz1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bNivHQ/btsNKyu5qBt/uQFcl6u165ia2Ngo0IHRz1/img.png&quot; data-alt=&quot;네트워크 접근 설정하기&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bNivHQ/btsNKyu5qBt/uQFcl6u165ia2Ngo0IHRz1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbNivHQ%2FbtsNKyu5qBt%2FuQFcl6u165ia2Ngo0IHRz1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/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;1062&quot; data-filename=&quot;image (1).png&quot; data-origin-width=&quot;1914&quot; data-origin-height=&quot;1062&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;네트워크 접근 설정하기&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 최종적으로 만들고자 하는 서버의 스펙을 확인한 후에&lt;b&gt; [서버 생성]&lt;/b&gt; 버튼을 클릭하여 서버를 생성합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;image (2).png&quot; data-origin-width=&quot;1913&quot; data-origin-height=&quot;1059&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bABVrv/btsNKfbvCye/q0pmv85Gz4c7khQBjanBe1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bABVrv/btsNKfbvCye/q0pmv85Gz4c7khQBjanBe1/img.png&quot; data-alt=&quot;최종 스펙 확인하기&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bABVrv/btsNKfbvCye/q0pmv85Gz4c7khQBjanBe1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbABVrv%2FbtsNKfbvCye%2Fq0pmv85Gz4c7khQBjanBe1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/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;1059&quot; data-filename=&quot;image (2).png&quot; data-origin-width=&quot;1913&quot; data-origin-height=&quot;1059&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;최종 스펙 확인하기&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버 생성 후에는 다음과 같이 생성한 서버목록을 확인할 수 있으며, 저와 동일하게 위 스펙 기준으로 생성했다면 1~2분 이내로 서버가 생성되는 것을 확인할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;제목 없음 (1).png&quot; data-origin-width=&quot;1911&quot; data-origin-height=&quot;986&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/2dppN/btsNL2orsWH/ZhaTMtxMlfedzJ2okreXkK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/2dppN/btsNL2orsWH/ZhaTMtxMlfedzJ2okreXkK/img.png&quot; data-alt=&quot;서버 생성 완료&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/2dppN/btsNL2orsWH/ZhaTMtxMlfedzJ2okreXkK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F2dppN%2FbtsNL2orsWH%2FZhaTMtxMlfedzJ2okreXkK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/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;986&quot; data-filename=&quot;제목 없음 (1).png&quot; data-origin-width=&quot;1911&quot; data-origin-height=&quot;986&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;서버 생성 완료&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style7&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span&gt; &lt;/span&gt; 정리하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이로써 네이버클라우드 플랫폼에서 VPC 플랫폼으로 간단하게 서버 생성하는 방법에 대해 알아보았는데요. 저도 Classic 플랫폼만 사용했던 유저였기 때문에 새 인스턴스를 생성할 때 리눅스 OS가 없어져서 당혹스러움을 겪었기에 이번 경험을 토대로 VPC로 서버를 직접 생성해보고 블로그로 옮겨보았습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제 주관으로는 이제 더 이상 Classic에서 새로운 이미지를 지원하지 않고 점차적으로 서비스를 종료하여 AWS처럼 VPC 플랫폼만 지원하는 방식으로 변경되지 않을까 하는 생각이 듭니다. 저와 마찬가지로 기존에&amp;nbsp;Classic 환경 유저였거나 네이버클라우드 플랫폼이 생소하고 서버를 어떻게 생성해야 할지 감이 안 오시는 분들에게 이 글이 조금이나마 도움이 되면 좋을 것 같습니다.&lt;/p&gt;</description>
      <category>Infra/ncloud</category>
      <author>외계공룡</author>
      <guid isPermaLink="true">https://chucoding.tistory.com/159</guid>
      <comments>https://chucoding.tistory.com/159#entry159comment</comments>
      <pubDate>Tue, 6 May 2025 00:49:04 +0900</pubDate>
    </item>
    <item>
      <title>테크포임팩트 랩짱을 마치며</title>
      <link>https://chucoding.tistory.com/158</link>
      <description>&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4031&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b7pIAH/btsMT42UdEL/0wYALJBGuRle8LibHGBPZK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b7pIAH/btsMT42UdEL/0wYALJBGuRle8LibHGBPZK/img.png&quot; data-alt=&quot;테크포임팩트 커넥트데이 참여 사진&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b7pIAH/btsMT42UdEL/0wYALJBGuRle8LibHGBPZK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb7pIAH%2FbtsMT42UdEL%2F0wYALJBGuRle8LibHGBPZK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;3024&quot; height=&quot;4031&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4031&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;테크포임팩트 커넥트데이 참여 사진&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;2025년 3월 21일 커넥트데이 행사를 마지막으로 테크포임펙트에서의 랩짱 활동을 모두 마치게 되었습니다. 왠지 모를 아쉬움을 느끼면서도 번아웃이 올 정도로 그동안 정말 열심히 달려왔는데요. 이번 포스팅에서는 6개월간 팀을 리드하면서 느끼고 깨달았던 소중한 경험들을 공유드리고자 합니다.&lt;br&gt;&amp;nbsp;&lt;br&gt;그동안, 함께해 주신 랩원 분들과 브라이언 펠로우인 누구나데이터 직원분들 그리고 커뮤니티 활동을 아낌없이 지원해 주신 모두의연구소, 카카오임팩트 매니저분들에게 감사 인사를 드리고 마지막으로 저와 마찬가지로 6개월 동안 현업과 병행하면서 테크포임팩트 랩을 무사히 마치신 모든 랩짱님들에게 경의를 표합니다.&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style7&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;h2 data-ke-size=&quot;size26&quot;&gt;  테크포임팩트 랩짱의 역할 되새기기&lt;/h2&gt;&lt;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;chucoding.tistory.com&quot; data-og-source-url=&quot;https://chucoding.tistory.com/148&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bnq5eY/hyYurkWIec/cLMWXQZ5iba4c88yac4BQ0/img.jpg?width=800&amp;amp;height=457&amp;amp;face=0_0_800_457,https://scrap.kakaocdn.net/dn/czT7Uc/hyYvrkhsqM/NtKlsiewM8TOHgKVz2KNG0/img.jpg?width=800&amp;amp;height=457&amp;amp;face=0_0_800_457,https://scrap.kakaocdn.net/dn/8eqgA/hyYvigyCBm/0H7rEuOwlln2qoJhl10ye0/img.jpg?width=3024&amp;amp;height=4032&amp;amp;face=0_0_3024_4032&quot; data-og-url=&quot;https://chucoding.tistory.com/148&quot;&gt;&lt;a href=&quot;https://chucoding.tistory.com/148&quot; target=&quot;_blank&quot; data-source-url=&quot;https://chucoding.tistory.com/148&quot;&gt;&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bnq5eY/hyYurkWIec/cLMWXQZ5iba4c88yac4BQ0/img.jpg?width=800&amp;amp;height=457&amp;amp;face=0_0_800_457,https://scrap.kakaocdn.net/dn/czT7Uc/hyYvrkhsqM/NtKlsiewM8TOHgKVz2KNG0/img.jpg?width=800&amp;amp;height=457&amp;amp;face=0_0_800_457,https://scrap.kakaocdn.net/dn/8eqgA/hyYvigyCBm/0H7rEuOwlln2qoJhl10ye0/img.jpg?width=3024&amp;amp;height=4032&amp;amp;face=0_0_3024_4032')&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;chucoding.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;작년 9월 테크포임팩트를 시작했던 이유는 AI를 활용해 SQL을 만들어내는 기술(이하 Text-to-SQL)을 경험해보고 싶었던 것이 가장 큰 이유였습니다. 마침 개설된 LAB들 중에서 해당 기술로 해결할 수 있는 주제가 있었고 제 커리어 패스로 도전해 볼 수 있는 서비스를 기획하고 있었기에 망설임 없이 신청하였습니다.&lt;br&gt;&amp;nbsp;&lt;br&gt;제가 지원한 LAB은 비영리단체를 위한 디지털 모금 솔루션을 제공하는 소셜벤처 ‘누구나데이터’와 함께 ‘AI 챗봇으로 데이터 기반 의사결정 돕기’라는 주제를 해결하기 위한 목표를 가지고 활동하는 LAB으로서 제가 경험해보고 싶었던 기술을 사용할 수 있겠다는 기대감을 가지고 신청했습니다.&lt;br&gt;&amp;nbsp;&lt;br&gt;다만, 6개월이 지난 지금은 생각이 조금 달라졌는데요. 지금은 당시에 기술에만 초점을 맞춰서 무작정 지원했던 점에 대해서 굉장히 많은 반성을 느끼고 있습니다.&lt;br&gt;&amp;nbsp;&lt;br&gt;테크포임팩트(TechForImapct)의 정의는 기술 전문가(LAB 연구원)와 사회 혁신가(브라이언 펠로우)들이 ‘돕는 기술’로 함께 문제를 해결해 나가는 모임입니다.&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1034&quot; data-origin-height=&quot;776&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pJK7l/btsMUv0bJmo/oJRcDysE2aUMOAKaYkpBb1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pJK7l/btsMUv0bJmo/oJRcDysE2aUMOAKaYkpBb1/img.png&quot; data-alt=&quot;테크포임팩트에서 랩짱의 역할&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pJK7l/btsMUv0bJmo/oJRcDysE2aUMOAKaYkpBb1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpJK7l%2FbtsMUv0bJmo%2FoJRcDysE2aUMOAKaYkpBb1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;491&quot; height=&quot;368&quot; data-origin-width=&quot;1034&quot; data-origin-height=&quot;776&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;테크포임팩트에서 랩짱의 역할&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;여기서 ‘돕는 기술’이 좋은 방향으로 잘 흘러가기 위해서는 랩짱의 역할이 무척 중요합니다. 랩짱은 사회문제에 대한 정의를 명확히 알고 있어야 하고 그걸 랩원들에게 잘 전파하여 사회혁신가(이하:펠로우)와의 커뮤니케이션을 도와야 합니다. 즉, 랩짱은 기술도 알고 문제도 알아야 하는 포지션입니다.&lt;br&gt;&amp;nbsp;&lt;br&gt;부끄럽게도 저는 당시 기술에만 관심을 갖고 사회문제 해결에는 큰 관심이 없었기 때문에 좋은 랩짱이 아니었습니다. 사회문제에 좀 더 집중해서 문제를 다시 들여다본 것은 프로젝트 중 후반쯤이었기 때문에 이러한 부분들을 뒤늦게 깨달았던 점에 많은 아쉬움이 남았습니다.&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style7&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;h2 data-ke-size=&quot;size26&quot;&gt;  비영리단체 이해하기 및 문제 정의&lt;/h2&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;앞서 랩짱이란 사회문제와 기술 두 가지를 고루 알고 있어야 하는 포지션이라고 말씀드렸는데요. 사실, 이 둘을 모두 알고 있는 랩짱을 찾기란 거의 사막에서 바늘을 찾는 격이기 때문에 랩짱 선발기준에는 아무래도 기술이 조금 더 우위에 있었을 거라는 생각이 듭니다.&lt;br&gt;&amp;nbsp;&lt;br&gt;이러한 문제점을 해결하기 위해 테크포임팩트에서는 각 랩에서의 랩짱을 선발한 이후 ‘문제정의 워크숍’이라는 프로그램을 만들어서 브라이언펠로우와 만나 문제를 정의할 수 있는 시간을 가질 수 있도록 자리를 마련해 주었습니다.&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;4032&quot; data-origin-height=&quot;3024&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qrXh2/btsMTIMHNm4/lVeiiXYgk4W1MK0KngpLI0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qrXh2/btsMTIMHNm4/lVeiiXYgk4W1MK0KngpLI0/img.jpg&quot; data-alt=&quot;테크포임팩트 2기 문제정의 워크숍&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qrXh2/btsMTIMHNm4/lVeiiXYgk4W1MK0KngpLI0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FqrXh2%2FbtsMTIMHNm4%2FlVeiiXYgk4W1MK0KngpLI0%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;511&quot; height=&quot;383&quot; data-origin-width=&quot;4032&quot; data-origin-height=&quot;3024&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;테크포임팩트 2기 문제정의 워크숍&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;다만, 몇 시간 내로 문제 정의 및 기술 스택을 논의해야 하기 때문에 굉장히 짧은 시간 안에 많은 것들이 결정되어야 하는 상황이었기에 부담이 될 수밖에 없습니다. 감사하게도 저희 펠로우분들께서는 직접 따로 시간을 내주셔서 문제정의를 보충할 수 있는 시간을 마련해 주셨습니다.&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;3024&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/blxewX/btsMUtH1R1k/kvuTL2dYE5VrP8BvZ7NQKK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/blxewX/btsMUtH1R1k/kvuTL2dYE5VrP8BvZ7NQKK/img.jpg&quot; data-alt=&quot;브라이언펠로우 - 누구나데이터 김자유 대표님&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/blxewX/btsMUtH1R1k/kvuTL2dYE5VrP8BvZ7NQKK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FblxewX%2FbtsMUtH1R1k%2FkvuTL2dYE5VrP8BvZ7NQKK%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;433&quot; height=&quot;433&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;3024&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;브라이언펠로우 - 누구나데이터 김자유 대표님&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;두 차례에 걸친 문제정의 시간을 가졌음에도 불구하고 사실 비영리단체와 그들이 갖고 있는 문제들을 전부 이해하기에 저에게는 너무나도 짧은 시간이었는데요. 그 당시 김자유 대표님으로부터 『 비영리단체 성장 공식, 잠재후원자 모금:정체된 후원자 수를 돌파하는 기술 』 도서를 전달받았습니다.&lt;br&gt;&amp;nbsp;&lt;br&gt;이 책은 누구나데이터와 비영리단체 종사자 분들의 여러 디지털전환에 대한 노하우가 담긴 바이블로써 틈틈이 시간을 내어 시간 내어 읽은 결과 사회문제에 대한 관심을 가질 수 있게 되었고 그제야 비로소 비영리단체의 마케팅 전략과 프로세스를 이해하게 되고 문제를 정의해 나갈 수 있었습니다.&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;figure data-ke-type=&quot;opengraph&quot; data-og-title=&quot;개발자의 비영리(non-profit) 도메인 이해하기&quot; data-ke-align=&quot;alignCenter&quot; data-og-description=&quot;작년 10월에 테크포임팩트 커뮤니티의 랩짱으로 선발되어 모두의연구소와 카카오임팩트의 지원을 받아 누구나데이터와 함께 비영리단체를 위한 AI 솔루션을 개발해오고 있는데요. 프로젝트 시&quot; data-og-host=&quot;chucoding.tistory.com&quot; data-og-source-url=&quot;https://chucoding.tistory.com/154&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/UCxaS/hyYumDWieg/1rrhyZ7LBBy0L0B4hgruy1/img.jpg?width=800&amp;amp;height=1066&amp;amp;face=0_0_800_1066,https://scrap.kakaocdn.net/dn/bHiXj8/hyYugDMcJE/1Egf4aMNSBK5SaRIAf99h0/img.jpg?width=800&amp;amp;height=1066&amp;amp;face=0_0_800_1066,https://scrap.kakaocdn.net/dn/b4PFvY/hyYvgXlboj/DRf50Aw3QjDz6dlMRmEKs0/img.jpg?width=1411&amp;amp;height=1058&amp;amp;face=0_0_1411_1058&quot; data-og-url=&quot;https://chucoding.tistory.com/154&quot;&gt;&lt;a href=&quot;https://chucoding.tistory.com/154&quot; target=&quot;_blank&quot; data-source-url=&quot;https://chucoding.tistory.com/154&quot;&gt;&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/UCxaS/hyYumDWieg/1rrhyZ7LBBy0L0B4hgruy1/img.jpg?width=800&amp;amp;height=1066&amp;amp;face=0_0_800_1066,https://scrap.kakaocdn.net/dn/bHiXj8/hyYugDMcJE/1Egf4aMNSBK5SaRIAf99h0/img.jpg?width=800&amp;amp;height=1066&amp;amp;face=0_0_800_1066,https://scrap.kakaocdn.net/dn/b4PFvY/hyYvgXlboj/DRf50Aw3QjDz6dlMRmEKs0/img.jpg?width=1411&amp;amp;height=1058&amp;amp;face=0_0_1411_1058')&quot;&gt; &lt;/div&gt;&lt;div class=&quot;og-text&quot;&gt;&lt;p class=&quot;og-title&quot;&gt;개발자의 비영리(non-profit) 도메인 이해하기&lt;/p&gt;&lt;p class=&quot;og-desc&quot;&gt;작년 10월에 테크포임팩트 커뮤니티의 랩짱으로 선발되어 모두의연구소와 카카오임팩트의 지원을 받아 누구나데이터와 함께 비영리단체를 위한 AI 솔루션을 개발해오고 있는데요. 프로젝트 시&lt;/p&gt;&lt;p class=&quot;og-host&quot;&gt;chucoding.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;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style7&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;h2 data-ke-size=&quot;size26&quot;&gt; 나만의 랩원 선발 기준&lt;/h2&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;문제 정의가 끝난 이후에는 랩원들을 모집하였습니다. 랩원 모집에 대한 프로세스와 선발 기준은 오로지 랩짱의 몫이었기 때문에 저는 난생처음으로 인터뷰어 입장에서 랩원들을 선발하였습니다. 저의 선발 기준은 퇴근 후 랩 활동이 가능할 정도로 자기 계발에 대한 열정이 있는지를 우선적으로 판단하였습니다.&lt;br&gt;&amp;nbsp;&lt;br&gt;열정에 대한 판단 기준은 랩 활동을 통해 얻고자 하는 것과 지원 동기, 목소리 톤과 어조 그동안 쌓아온 커리어 패스가 랩의 방향과 잘 부합하는지 등으로 판단하였습니다. 그 외에도 거리가 너무 멀거나 직감적으로 맞지 않을 것 같다 판단되시는 분들도 정말 죄송하지만 불합격을 드릴 수밖에 없었습니다.&lt;br&gt;&amp;nbsp;&lt;br&gt;정말 감사하게도 저희 랩에는 29명이나 되는 분들께서 지원을 해주셨는데 저는 이 중에서 12명을 선발하였는데요. 많은 분들께서 왜 이렇게 타이트하게 선발하셨냐는 질문을 많이 주셨는데 제한을 했던 이유는 제 에너지 역량의 한계도 있었고 선발된 랩원들에게 조금 더 좋은 환경과 기회를 드리는 것이 좋겠다는 생각이 들었습니다.&lt;br&gt;&amp;nbsp;&lt;br&gt;랩 신청과 승인 모두 모두의연구소 홈페이지에서 간단하게 가능하도록 되어있지만 저는 직접 한분 한분께 메일을 드리고 별도로 인터뷰 요청을 드렸습니다. 그 결과 선발과정에만 한 달이라는 시간을 쏟을 정도로 많은 시간을 투자하였지만 최종적으로는 이탈률이 낮고 출석률이 높은 랩을 만들 수 있었습니다.&lt;/p&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style7&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;h2 data-ke-size=&quot;size26&quot;&gt; ‍➡️ 번아웃을 막기 위한 랩 조직도 구성하기&lt;/h2&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;저는 랩원 모집 시 PM 3명, 프롬프트 엔지니어 4명, 개발 3명, 데이터분석가 2명을 선발하였는데 PM을 3명씩이나 선발했던 이유도 비영리단체에 대한 도메인 지식이 부족했기 때문에 PM 분들을 의지하고자 하였던 이유도 있었고 제 스스로 실무에 좀 더 집중하기 위함도 있었습니다.&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;767&quot; data-origin-height=&quot;638&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dkUu45/btsMT85cobV/TgBo1cALhMhJcSKLy1SCI1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dkUu45/btsMT85cobV/TgBo1cALhMhJcSKLy1SCI1/img.png&quot; data-alt=&quot;처음에 원했던 의사소통 구조&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dkUu45/btsMT85cobV/TgBo1cALhMhJcSKLy1SCI1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdkUu45%2FbtsMT85cobV%2FTgBo1cALhMhJcSKLy1SCI1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;448&quot; height=&quot;373&quot; data-origin-width=&quot;767&quot; data-origin-height=&quot;638&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;처음에 원했던 의사소통 구조&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;그러나 현실은 다음과 같은 형상을 감당하게 되었습니다.&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;814&quot; data-origin-height=&quot;520&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bRRpzt/btsMVXOHhcA/THkiT13VzVGFvcEN0zCz30/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bRRpzt/btsMVXOHhcA/THkiT13VzVGFvcEN0zCz30/img.png&quot; data-alt=&quot;랩 운영시 실제 의사소통 구조&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bRRpzt/btsMVXOHhcA/THkiT13VzVGFvcEN0zCz30/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbRRpzt%2FbtsMVXOHhcA%2FTHkiT13VzVGFvcEN0zCz30%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;434&quot; height=&quot;277&quot; data-origin-width=&quot;814&quot; data-origin-height=&quot;520&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;랩 운영시 실제 의사소통 구조&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;어떻게 보면 가장 이상적인 구조로 보이지만, 랩짱이 모든 것을 감당해야 하는 구조이기 때문에 저는 퇴근 후의 삶으로 이 모든 것들을 혼자서 감당하기가 굉장히 어려웠습니다. 김자유 대표님께서는 이러한 구조적인 문제점을 일찌감치 파악하셔서 리더스 그룹을 만들어볼 것을 추천해 주셨고 그 결과 어느 정도 숨통이 트일 수 있었습니다.&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;868&quot; data-origin-height=&quot;555&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/d4NPBf/btsMUYHOo9J/6qAm87idavKdgNoxZekVlk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/d4NPBf/btsMUYHOo9J/6qAm87idavKdgNoxZekVlk/img.png&quot; data-alt=&quot;누구나리포터 LAB에서 적용한 의사소통 구조&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/d4NPBf/btsMUYHOo9J/6qAm87idavKdgNoxZekVlk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fd4NPBf%2FbtsMUYHOo9J%2F6qAm87idavKdgNoxZekVlk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;431&quot; height=&quot;276&quot; data-origin-width=&quot;868&quot; data-origin-height=&quot;555&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;누구나리포터 LAB에서 적용한 의사소통 구조&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;부랩짱은 첫 1~2주 동안 주도적으로 문제를 해결하려고 하는 모습들이 보이시는 분들로 선발하였는데 이 분들이 정말 열심히 해주셨기 때문에 저도 끝까지 에너지를 유지하면서 6개월 동안 지치지 않고 달려올 수 있었습니다.&lt;br&gt;&amp;nbsp;&lt;br&gt;특히, 개발과 데이터분석 쪽을 온전한 블랙박스로 둔 채로 운영을 해도 큰 무리가 없었다는 점에서 부랩짱 분들께서 너무나 우수하게 활동해 주셔서 정말 감사했습니다. 마찬가지로 펠로우 분들도 웬만한 요구사항이나 문의사항은 저에게 직접 요청해 주시기보다는 리더스 채널에 요청을 주셨기 때문에 정말 많은 배려를 해주셨음에 감사한 마음이 들었습니다.&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style7&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;h2 data-ke-size=&quot;size26&quot;&gt;  모든 규칙(Ground Rule)과 문서는 단순하게 정리하기&lt;/h2&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;랩 활동을 하면서 아쉬웠던 점 중에 하나는 랩원들 사이에서 규칙이 잘 지켜지지 않았다는 점이었습니다. 각 팀별 리더들은 계속해서 새로운 시도들을 추구했고 새로운 문서와 규칙들을 매주 만들어냈으나 팔로우하는 랩원들 입장에서는 많이 힘겨워하는 모습들이 보였습니다.&lt;br&gt;&amp;nbsp;&lt;br&gt;랩원들 입장에서는 현업에서도 계속해서 회사의 방침과 프로세스들을 따르며 업무를 담당하고 있는데 퇴근 후의 삶마저도 무언가의 프로세스를 따라야 하고 자유로운 표현을 억제당한다면 그마저도 큰 스트레스로 다가올 수 있기 때문에 저는 사실 보안적인 이슈를 제외하고는 최대한 하고 싶은 대로 활동할 수 있도록 배려해 주었습니다.&lt;br&gt;&amp;nbsp;&lt;br&gt;퇴근 후의 삶에서 스트레스를 덜 받으면서도 앞으로 추진해 나가려면 모든 규칙(Ground Rule)과 문서는 랩원 분들이 잘 팔로우할 수 있도록 단순하게 정의하는 게 좋습니다. 예를 들어 Git 전략 같은 경우에도 단순하게 만드는 것이 좋습니다. 저희 랩의 실패 사례 중 하나를 소개해드리도록 하겠습니다.&lt;br&gt;&amp;nbsp;&lt;br&gt;저희는 브랜치 전략을 다음과 같이 가져갔습니다.&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;784&quot; data-origin-height=&quot;343&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cglst3/btsMU9JddDJ/fE1KhJ7hn1r1cRUhqvo6M1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cglst3/btsMU9JddDJ/fE1KhJ7hn1r1cRUhqvo6M1/img.png&quot; data-alt=&quot;누구나리포터LAB에서 사용한 브랜치 전략&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cglst3/btsMU9JddDJ/fE1KhJ7hn1r1cRUhqvo6M1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcglst3%2FbtsMU9JddDJ%2FfE1KhJ7hn1r1cRUhqvo6M1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;688&quot; height=&quot;301&quot; data-origin-width=&quot;784&quot; data-origin-height=&quot;343&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;누구나리포터LAB에서 사용한 브랜치 전략&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;브랜치 전략에 익숙한 개발팀은 곧잘 규칙들을 잘 따랐지만 브랜치에 익숙하지 않은 프롬프트 엔지니어들은 6개월이 지난 지금도 규칙을 따르고 있지 않습니다. 문제는 notionID인데 이 ID를 얻으려면 노션 업무관리 보드(이하:칸반 보드)에서 문서를 만들고 번호를 복사해서 브랜치를 만들어야 합니다.&lt;br&gt;&amp;nbsp;&lt;br&gt;사실 이렇게 정했던 이유는 github에 노션 문서가 링크가 되기 때문에 채택했던 규칙이었지만 그 과정이 너무도 복잡하고 귀찮았기 때문에 잘 지켜지지 않았습니다. 사실, 칸반 보드부터 이용하는 사람만 사용했고 대부분은 잘 사용하지 않았습니다. 랩원들은 주로 Slack으로 소통하는 것을 선호했고 문서는 생각보다 잘 적지도 보지도 않았습니다.&lt;br&gt;&amp;nbsp;&lt;br&gt;Slack에 가장 많이 공유되는 문서는 사실상 가장 단순한 목적을 지녔고 직관적인 캘린더, 회의록, 프롬프트 버전관리 3개의 페이지였습니다.&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/vrSh2/btsMWmgtKGQ/KiNm7GkkK7Up2cquwJyTw1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/vrSh2/btsMWmgtKGQ/KiNm7GkkK7Up2cquwJyTw1/img.png&quot; data-origin-width=&quot;1817&quot; data-origin-height=&quot;916&quot; style=&quot;width: 41.8756%;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/vrSh2/btsMWmgtKGQ/KiNm7GkkK7Up2cquwJyTw1/img.png&quot; alt=&quot;&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FvrSh2%2FbtsMWmgtKGQ%2FKiNm7GkkK7Up2cquwJyTw1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1817&quot; height=&quot;916&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/QXgEu/btsMTHmFyr6/4rvqt3OfRCh3EyrvkUzk0k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/QXgEu/btsMTHmFyr6/4rvqt3OfRCh3EyrvkUzk0k/img.png&quot; data-origin-width=&quot;789&quot; data-origin-height=&quot;866&quot; style=&quot;width: 19.2336%;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/QXgEu/btsMTHmFyr6/4rvqt3OfRCh3EyrvkUzk0k/img.png&quot; alt=&quot;&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FQXgEu%2FbtsMTHmFyr6%2F4rvqt3OfRCh3EyrvkUzk0k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;789&quot; height=&quot;866&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bdZd86/btsMTXv1Hv0/KtQmabKZmyHCyAjKAlRZK0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bdZd86/btsMTXv1Hv0/KtQmabKZmyHCyAjKAlRZK0/img.png&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;739&quot; style=&quot;width: 36.5652%;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bdZd86/btsMTXv1Hv0/KtQmabKZmyHCyAjKAlRZK0/img.png&quot; alt=&quot;&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbdZd86%2FbtsMTXv1Hv0%2FKtQmabKZmyHCyAjKAlRZK0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/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;739&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;누구나리포터LAB 노션 왼쪽부터 랜딩페이지 및 일정, 회의록, 프롬프트 버전관리 페이지&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style7&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;h2 data-ke-size=&quot;size26&quot;&gt;  랩짱이 되며 포기했던 것들과 새로운 시도&lt;/h2&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;처음에 Text-to-SQL 기술을 경험해보고 싶어서 지원했던 제 꿈은 아이러니하게도 랩짱이 되면서 무산되었습니다. 랩짱이 되는 순간 제 포지션은 어느새 비영리단체를 위한 문제 해결에 집중하는 게 아니라 랩원들이 갖고 있는 고충과 문제를 해결하는 역할을 담당하게 되었습니다.&lt;br&gt;&amp;nbsp;&lt;br&gt;저와 마찬가지로 랩원 분들도 같은 목적으로 들어왔기에 누군가는 양보해야 랩이 원활하게 돌아갈 수 있었고 특히, 랩 활동 초반에는 활동비 신청 및 활동 계획서 등 작성할 서류들이 많았기 때문에 자연스럽게 제 꿈을 포기할 수밖에 없었습니다.&lt;br&gt;&amp;nbsp;&lt;br&gt;특히 가장 어려웠던 부분은 랩원 분들의 대다수는 생각보다 변화와 도전을 좋아하지 않는 사람들이었습니다. 몇몇 분들을 제외하고는 주도적으로 문제를 찾아서 정의하고 해결하려고 하기보다는 RnR을 부여받기를 원하고 바운더리 이상의 일에 대해서는 회의적인 반응이 많았습니다.&lt;br&gt;&amp;nbsp;&lt;br&gt;그래서 리더스 분들과 랩원들의 RnR을 어떻게 정의하면 좋을지에 대해 대부분의 시간을 많이 쏟다 보니 사실 시간도 많이 허비하고 도중에 회의감도 많이 들고 우울하기도 했지만, 나름대로 의미 부여하며 제 역할을 찾아갔던 것 같습니다.&lt;br&gt;&amp;nbsp;&lt;br&gt;프로젝트 초반에는 리서치한 결과들을 공유드리며 다 같이 프롬프트 템플릿 구조를 잡고 LangServe 프레임워크를 도입하여 테스트 환경을 빠르게 구축하고 기획팀과 데이터분석팀에서 활용할 수 있도록 구조를 잡았습니다.&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;figure data-ke-type=&quot;opengraph&quot; data-og-title=&quot;초스피드 AI 프로토타입 개발을 위한 랭서브(Langserve) 도입기&quot; data-ke-align=&quot;alignCenter&quot; data-og-description=&quot;최근에 5개월짜리 짧은 기간의 AI 프로젝트 리드를 맡게 되면서 어떤 프레임워크와 환경 셋팅을 가져가야 구현 속도면에서 이점이 있을까 고민하던 찰나에 우아한형제들의 기술블로그 중 AI 데&quot; data-og-host=&quot;chucoding.tistory.com&quot; data-og-source-url=&quot;https://chucoding.tistory.com/155&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/jy0Rv/hyYvugZIAv/mI951NtFEwpqOHMNYD0oNK/img.jpg?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/KjzXa/hyYvkd9P0y/KaHbLtO9znpIYL87Anr621/img.jpg?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/L9EqQ/hyYvrYRR98/SrD6duJsnchulC9CNsbxDk/img.png?width=1909&amp;amp;height=1118&amp;amp;face=0_0_1909_1118&quot; data-og-url=&quot;https://chucoding.tistory.com/155&quot;&gt;&lt;a href=&quot;https://chucoding.tistory.com/155&quot; target=&quot;_blank&quot; data-source-url=&quot;https://chucoding.tistory.com/155&quot;&gt;&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/jy0Rv/hyYvugZIAv/mI951NtFEwpqOHMNYD0oNK/img.jpg?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/KjzXa/hyYvkd9P0y/KaHbLtO9znpIYL87Anr621/img.jpg?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/L9EqQ/hyYvrYRR98/SrD6duJsnchulC9CNsbxDk/img.png?width=1909&amp;amp;height=1118&amp;amp;face=0_0_1909_1118')&quot;&gt; &lt;/div&gt;&lt;div class=&quot;og-text&quot;&gt;&lt;p class=&quot;og-title&quot;&gt;초스피드 AI 프로토타입 개발을 위한 랭서브(Langserve) 도입기&lt;/p&gt;&lt;p class=&quot;og-desc&quot;&gt;최근에 5개월짜리 짧은 기간의 AI 프로젝트 리드를 맡게 되면서 어떤 프레임워크와 환경 셋팅을 가져가야 구현 속도면에서 이점이 있을까 고민하던 찰나에 우아한형제들의 기술블로그 중 AI 데&lt;/p&gt;&lt;p class=&quot;og-host&quot;&gt;chucoding.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 data-ke-type=&quot;opengraph&quot; data-og-title=&quot;생성형 AI(LLM) 프롬프트 파일 관리 방법&quot; data-ke-align=&quot;alignCenter&quot; data-og-description=&quot;생성형 AI에 사용되는 프롬프트는 어떻게 관리하는 게 좋을까요? 프롬프트는 챗봇의 답변 품질에 굉장히 많은 영향을 미치는 만큼 반드시 체계적인 관리가 필요한데요. 정작, 인터넷에서 프롬프&quot; data-og-host=&quot;chucoding.tistory.com&quot; data-og-source-url=&quot;https://chucoding.tistory.com/157&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bj80iE/hyYvrqTgJb/2JVpM9jLfNHk3XMRxMiRk0/img.jpg?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/cjZmVI/hyYvtWIai2/sMPRn6owkbu9Iwxvb2HTdk/img.jpg?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/bb8WZ7/hyYxMgEUyQ/ZKbwtSisOtMLk7OTuHRzs1/img.png?width=1907&amp;amp;height=938&amp;amp;face=0_0_1907_938&quot; data-og-url=&quot;https://chucoding.tistory.com/157&quot;&gt;&lt;a href=&quot;https://chucoding.tistory.com/157&quot; target=&quot;_blank&quot; data-source-url=&quot;https://chucoding.tistory.com/157&quot;&gt;&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bj80iE/hyYvrqTgJb/2JVpM9jLfNHk3XMRxMiRk0/img.jpg?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/cjZmVI/hyYvtWIai2/sMPRn6owkbu9Iwxvb2HTdk/img.jpg?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/bb8WZ7/hyYxMgEUyQ/ZKbwtSisOtMLk7OTuHRzs1/img.png?width=1907&amp;amp;height=938&amp;amp;face=0_0_1907_938')&quot;&gt; &lt;/div&gt;&lt;div class=&quot;og-text&quot;&gt;&lt;p class=&quot;og-title&quot;&gt;생성형 AI(LLM) 프롬프트 파일 관리 방법&lt;/p&gt;&lt;p class=&quot;og-desc&quot;&gt;생성형 AI에 사용되는 프롬프트는 어떻게 관리하는 게 좋을까요? 프롬프트는 챗봇의 답변 품질에 굉장히 많은 영향을 미치는 만큼 반드시 체계적인 관리가 필요한데요. 정작, 인터넷에서 프롬프&lt;/p&gt;&lt;p class=&quot;og-host&quot;&gt;chucoding.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;어떻게 하면 랩원 분들이 빠르고 쉽게 접근할 수 있을지 그리고 효율을 낼 수 있을지 위주로 많이 고민을 많이 했었고 그 과정에서 여러 번의 핸즈온 강의도 직접 준비하며 기술 격차를 해소하고 이해하는 시간을 많이 가졌습니다.&lt;br&gt;&amp;nbsp;&lt;br&gt;그 결과 저희 프로젝트 기획자 분들은 이제 스스로 프로젝트를 띄워서 프롬프트의 문제를 파악하고 직접 수정할 수 있는 수준까지 올라왔습니다. 특히, IT프로젝트가 처음이신 비영리단체 기획자 분도 계시는데 열심히 따라오셔서 AI 파이썬 프로젝트를 스스로 띄우고 플레이그라운드를 사용하시는 경지까지 올라오시는 모습을 보면서 제 노력이 헛되지 않았음을 느끼고 많은 뿌듯함을 느꼈습니다.&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style7&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;h2 data-ke-size=&quot;size26&quot;&gt;✍️ 고생의 흔적을 남기는 방법은 결국 ‘글’이다&lt;/h2&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;이제껏 많은 사이드 프로젝트를 진행해 보았지만, 글로 남기지 않은 사이드 프로젝트 활동들은 전부 무로 돌아갔습니다. 내가 왜 이 프로젝트를 시작했고 어떤 문제를 해결하기 위해서 도전했으며 어떤 문제를 해결했는지는 어딘가에 저장해놓지 않으면 누군가에게 소개하기도 어렵고 나는 이 프로젝트에 어떤 걸 기여했다는 부분들에 대해 스스로도 당당하지 못하게 됩니다.&lt;br&gt;&amp;nbsp;&lt;br&gt;그래서 저는 랩 활동을 시작하기 전에 중요한 GroundRule으로 랩원 분들에게 ‘블로그 글 쓰기’ 활동을 시작할 것을 당부하였습니다. 물론 이 활동이 어렵다는 것을 저도 알고 있었기에 강제하지는 않았지만, 이 랩 활동이 끝났을 때 단순히 휘발성으로 사라지는 것이 아니라 어떤 형태로든 남아있기를 바랐습니다.&lt;br&gt;&amp;nbsp;&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;863&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bt8oOh/btsMVzgs9mh/Yz3BQF0l95dAzLqtqZ6Pwk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bt8oOh/btsMVzgs9mh/Yz3BQF0l95dAzLqtqZ6Pwk/img.png&quot; data-alt=&quot;누구나리포터LAB 블로그 목록&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bt8oOh/btsMVzgs9mh/Yz3BQF0l95dAzLqtqZ6Pwk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbt8oOh%2FbtsMVzgs9mh%2FYz3BQF0l95dAzLqtqZ6Pwk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;489&quot; height=&quot;437&quot; data-origin-width=&quot;965&quot; data-origin-height=&quot;863&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;누구나리포터LAB 블로그 목록&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;그 결과 저희 랩은 최종적으로 40~50개에 육박하는 포스팅을 완료했습니다. 물론 매달 꼬박꼬박 열심히 쓴 사람들도 있고 아닌 사람들도 있었지만 한 번도 제출 안 한 사람은 없었기에 나중에 언젠가 되돌아보았을 때 “맞아 내가 이런 사회기여 활동도 했었구나”를 느낄 수 있게 된다면 좋을 것 같다는 생각이 들었습니다.&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style7&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;h2 data-ke-size=&quot;size26&quot;&gt;  정리하기&lt;/h2&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;저도 대부분이 처음이었습니다. 비영리단체도 처음이었고 랭체인, 랭플로우 등의 AI 프레임워크를 사용해 보는 것도 처음이었고 이렇게 큰 규모의 프로젝트를 리드해 보는 것도 처음이었습니다. 모든 것이 부족하고 서툴렀습니다.&lt;br&gt;&amp;nbsp;&lt;br&gt;그러나 부족한 리딩에도 불구하고 불만 없이 잘 따라주신 랩원 분들과 펠로우 분들께 무한한 감사를 드립니다. 저 역시도 덕분에 한층 더 성장할 수 있는 계기가 되었습니다. 이번 랩 활동을 통해 비영리단체를 위한 사회문제 해결을 위해 한 발자국을 크게 내디뎠다는 데에서 큰 의미가 있지 않았나 하는 생각이 듭니다.&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bGxG54/btsMTJrgBby/rNHyelEww8jCclaLEr26S0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bGxG54/btsMTJrgBby/rNHyelEww8jCclaLEr26S0/img.jpg&quot; data-origin-width=&quot;2252&quot; data-origin-height=&quot;4000&quot; style=&quot;width: 42.3803%;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bGxG54/btsMTJrgBby/rNHyelEww8jCclaLEr26S0/img.jpg&quot; alt=&quot;&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbGxG54%2FbtsMTJrgBby%2FrNHyelEww8jCclaLEr26S0%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;2252&quot; height=&quot;4000&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bExm0y/btsMT3XcjDc/tiawGdUr7D1z0HMsGxqIr0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bExm0y/btsMT3XcjDc/tiawGdUr7D1z0HMsGxqIr0/img.jpg&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot; style=&quot;width: 56.4569%;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bExm0y/btsMT3XcjDc/tiawGdUr7D1z0HMsGxqIr0/img.jpg&quot; alt=&quot;&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbExm0y%2FbtsMT3XcjDc%2FtiawGdUr7D1z0HMsGxqIr0%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;3024&quot; height=&quot;4032&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;누구나리포터LAB 활동을 마무리하며&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>독서 및 기타 활동</category>
      <author>외계공룡</author>
      <guid isPermaLink="true">https://chucoding.tistory.com/158</guid>
      <comments>https://chucoding.tistory.com/158#entry158comment</comments>
      <pubDate>Tue, 25 Mar 2025 00:40:08 +0900</pubDate>
    </item>
    <item>
      <title>생성형 AI(LLM) 프롬프트 파일 관리 방법</title>
      <link>https://chucoding.tistory.com/157</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;프롬프트.webp&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;1024&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/958g7/btsMLJxDYzw/IRPbPGZAAXsuX1lzuQy8Hk/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/958g7/btsMLJxDYzw/IRPbPGZAAXsuX1lzuQy8Hk/img.webp&quot; data-alt=&quot;ChatGPT로 생성한 이미지 입니다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/958g7/btsMLJxDYzw/IRPbPGZAAXsuX1lzuQy8Hk/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F958g7%2FbtsMLJxDYzw%2FIRPbPGZAAXsuX1lzuQy8Hk%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;327&quot; height=&quot;327&quot; data-filename=&quot;프롬프트.webp&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;1024&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;ChatGPT로 생성한 이미지 입니다.&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;생성형 AI에 사용되는 프롬프트는 어떻게 관리하는 게 좋을까요? 프롬프트는 챗봇의 답변 품질에 굉장히 많은 영향을 미치는 만큼 반드시 체계적인 관리가 필요한데요. 정작, 인터넷에서 프롬프트 관리 방법에 대해 검색해 보면 공개된 자료를 거의 찾을 수 없어서 항상 아쉬움이 많이 남았습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최근 5개월짜리 짧은 기간 동안 AI 프로젝트를 리드하면서 어떻게 하면 팀의 성격에 맞게 프롬프트를 효율적으로 관리할 수 있을지 저도 직접 겪어보며 방법들을 조금씩 개선해 나갔는데요. 이번에 그 경험에 대해 공유드리고자 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style7&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span&gt; &lt;/span&gt; 프롬프트는 어떻게 관리하는 게 좋을까?&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;image (1).png&quot; data-origin-width=&quot;1021&quot; data-origin-height=&quot;406&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b04yNv/btsMNebZn4F/vGQhOn09y719hBSosKsOj0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b04yNv/btsMNebZn4F/vGQhOn09y719hBSosKsOj0/img.png&quot; data-alt=&quot;프롬프트를 파일로 관리 vs 데이터베이스로 관리&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b04yNv/btsMNebZn4F/vGQhOn09y719hBSosKsOj0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb04yNv%2FbtsMNebZn4F%2FvGQhOn09y719hBSosKsOj0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;855&quot; height=&quot;340&quot; data-filename=&quot;image (1).png&quot; data-origin-width=&quot;1021&quot; data-origin-height=&quot;406&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;프롬프트를 파일로 관리 vs 데이터베이스로 관리&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선, 프롬프트를 관리하기 위해 첫 번째로 생각해보아야 할 점은 프롬프트가 외부에 공개되어도 되는지? 아니면 외부로 유출되면 안 되는지 생각해 볼 필요가 있습니다. 만약 자사의 핵심 기술력이 되는 프롬프트이거나 보안에 주의해야 하는 프롬프트라면 안전하게 데이터베이스에 저장해 놓고 사용하는 것이 가장 좋습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약, 비용과 관리포인트를 줄이고 싶다면 파일로 관리하는 방법도 존재합니다. 다만, 파일로 관리하게 될 경우 보안에 유의해야 한다면 프롬프트가 외부로 유출되지 않도록 조심해야 합니다. 예를 들어, github에서는 레포지토리를 private으로 관리하는 등의 주의가 필요합니다.&lt;/p&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으로 버전 관리를 해도 되지만 작업자들의 커밋 권한과 규칙을 더욱 엄격하게 제한 해야하기 때문에 좋은 방법이 아닐 수 있습니다. 이 경우 lakefs 같은 오픈 소스 도구를 이용하여 스토리지를 통해 버전 관리를 하는 방법도 존재합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저의 경우에는 많은 고민 끝에 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;비영리단체를 위한 프로젝트인 만큼 비용을&lt;span&gt; 가장 아낄 수 있고&lt;/span&gt;&lt;/span&gt; 기간이 짧은 프로젝트인 만큼 속도감 있는 애플리케이션 개발과 협업을 위해 github private 레포지토리에서 파일 기반으로 관리하는 방법을 선택하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style7&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span&gt; ️&lt;/span&gt; 프롬프트 파일의 확장자 종류&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약, 프롬프트를 파일로 관리하기로 결정했다면 어떤 확장자로 관리해야될지도 고민포인트 중의 하나입니다. 프롬프트의 파일 확장자로는. txt,. py,. json,. yaml 등이 있을 것 같습니다. 제가 생각하는 각 확장자들의 장단점을 표로 정리해 보면 다음과 같습니다.&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: 88px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style8&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 13.1007%; height: 20px;&quot;&gt;확장자&lt;/td&gt;
&lt;td style=&quot;width: 40.0775%; height: 20px;&quot;&gt;장점&lt;/td&gt;
&lt;td style=&quot;width: 46.8217%; height: 20px;&quot;&gt;단점&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 13.1007%; height: 17px;&quot;&gt;.txt&lt;/td&gt;
&lt;td style=&quot;width: 40.0775%; height: 17px;&quot;&gt;프롬프트를 공유하거나 소통하기가 편리하다.&lt;/td&gt;
&lt;td style=&quot;width: 46.8217%; height: 17px;&quot;&gt;변수 치환이 어렵다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 13.1007%; height: 17px;&quot;&gt;.py&lt;/td&gt;
&lt;td style=&quot;width: 40.0775%; height: 17px;&quot;&gt;별도의 파일 Read 라이브러리를 필요로 하지 않는다.&lt;/td&gt;
&lt;td style=&quot;width: 46.8217%; height: 17px;&quot;&gt;자유도가 높아 불필요한 코드를 삽입하여 코드가 지저분해지거나 의존성이 높아져 추후 분리가 어려워질 수 있다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 13.1007%; height: 17px;&quot;&gt;.json&lt;/td&gt;
&lt;td style=&quot;width: 40.0775%; height: 17px;&quot;&gt;랭체인에서 프롬프트를 불러올 때 해당 확장자를 지원한다.&lt;/td&gt;
&lt;td style=&quot;width: 46.8217%; height: 17px;&quot;&gt;중괄호({})로 인해 가독성이 떨어진다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 13.1007%; height: 17px;&quot;&gt;.yaml&lt;/td&gt;
&lt;td style=&quot;width: 40.0775%; height: 17px;&quot;&gt;랭체인에서 프롬프트를 불러올 때 해당 확장자를 지원한다.&lt;/td&gt;
&lt;td style=&quot;width: 46.8217%; height: 17px;&quot;&gt;공백 문자 사용시 스페이스 대신 탭(\t)을 사용하면 오류가 발생한다.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;i&gt; &lt;span style=&quot;color: #787774;&quot; data-token-index=&quot;0&quot;&gt;※ TMI : yaml 대신 yml을 사용해도 됩니다. 옛날 OS (DOS 8.3) 파일명 규칙에는 확장자가 3자리까지만 허용되는 규칙이 있어서 yml로 사용했는데 공식적인 표기는 yaml입니다.&lt;/span&gt; &lt;/i&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저도 처음에는 프롬프트를 간단하게 파이썬 파일로 사용하다가 도중에 yaml 파일로 옮기게 되었는데요. 변경하게 된 이유는 시간이 지날수록 점점 프롬프트 코드에 불필요한 import 문과 랭체인 코드 등이 늘어나면서 복잡도가 증가하게 되는 문제가 발생하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 일을 하다 보면 비개발자인 기획, 데이터분석가 분들에게도 프롬프트를 공유드리는 경우가 많은데 그때마다 복잡한 파이썬 코드를 보여드리는 것이 죄송스러웠고 코드를 설명해야 하는 불필요한 의사소통이 발생했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;yaml 파일은 일정한 형식대로 작성해야 하기 때문에 위와 같은 문제들을 해결할 수 있습니다. 특히, 랭체인에서도 yaml로 작성된 프롬프트를 로드할 수 있는 함수를 지원하고 있어 코드 구현에 대한 부담 없이 사용할 수 있다는 점 역시 장점 중 하나입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style7&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span&gt; &lt;/span&gt; 랭체인 load_prompt 함수 사용해 보기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞서 살펴본 표에서 json, yaml 파일은 랭체인에서 지원이 된다고 말씀드렸는데요. 해당 확장자 파일로 저장된 프롬프트를 로드하는 함수는 load_prompt() 함수로 langchain_core 패키지에 포함되어 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;함수의 위치는 langchain_core/prompts/loading.py 파일 내에 존재합니다. 아래 API DOCS를 통해 어떻게 사용하는지 알아보도록 하겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;image.png&quot; data-origin-width=&quot;1879&quot; data-origin-height=&quot;927&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/djjJkZ/btsMLN0ZLO8/wkoRAoBawb8w2g7xh8b0ek/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/djjJkZ/btsMLN0ZLO8/wkoRAoBawb8w2g7xh8b0ek/img.png&quot; data-alt=&quot;https://api.python.langchain.com/en/latest/core/prompts/langchain_core.prompts.loading.load_prompt.html&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/djjJkZ/btsMLN0ZLO8/wkoRAoBawb8w2g7xh8b0ek/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdjjJkZ%2FbtsMLN0ZLO8%2FwkoRAoBawb8w2g7xh8b0ek%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/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;927&quot; data-filename=&quot;image.png&quot; data-origin-width=&quot;1879&quot; data-origin-height=&quot;927&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://api.python.langchain.com/en/latest/core/prompts/langchain_core.prompts.loading.load_prompt.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;위와 같이 load_prompt 함수의 파라미터로는 파일의 경로(path)와 encoding 타입을 받을 수 있도록 구성되어있습니다. 실제 비즈니스 로직에서 호출할 때도 마찬가지로 해당 인자값들을 채워주시면 됩니다. 아래 예시 코드를 참고하시면 좋을 것 같습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1742051276682&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from pathlib import Path
from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import ChatPromptTemplate, load_prompt
from langchain_openai import ChatOpenAI

llm = ChatOpenAI()

# 프롬프트 파일 경로 지정
PROMPT_DIR = Path(&quot;app/utils/prompts&quot;)

# YAML 파일로부터 프롬프트 템플릿 로드
prompt_template = load_prompt(PROMPT_DIR / &quot;template.yaml&quot;, encoding=&quot;utf-8&quot;)

# 프롬프트에 사용될 메시지 구성
chat_prompt = ChatPromptTemplate.from_messages([
    (&quot;placeholder&quot;, &quot;{messages}&quot;),
    (&quot;human&quot;, prompt_template.format(input=&quot;{input}&quot;)) # 프롬프트 템플릿에 input이라는 변수가 있을 경우
])

# 체인 생성 및 실행
chain = chat_prompt | llm | StrOutputParser()
response = chain.invoke({
    &quot;messages&quot;: messages[:-1],
    &quot;input&quot;: messages[-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;프롬프트 템플릿은 yaml 파일뿐만 아니라, json 형식도 가능합니다. 어떤 확장자들을 지원하는지는 아래와 같이 _load_prompt_from_file 함수에서 확인해 보실 수 있습니다. 해당 함수도 마찬가지로 langchain_core/prompts/loading.py 내에 존재합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;image (1).png&quot; data-origin-width=&quot;1907&quot; data-origin-height=&quot;938&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cKpXxB/btsMNeJNOrX/bRxFwZGSfvj4LHtlT4QyZ0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cKpXxB/btsMNeJNOrX/bRxFwZGSfvj4LHtlT4QyZ0/img.png&quot; data-alt=&quot;https://api.python.langchain.com/en/latest/_modules/langchain_core/prompts/loading.html#load_prompt&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cKpXxB/btsMNeJNOrX/bRxFwZGSfvj4LHtlT4QyZ0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcKpXxB%2FbtsMNeJNOrX%2FbRxFwZGSfvj4LHtlT4QyZ0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/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;938&quot; data-filename=&quot;image (1).png&quot; data-origin-width=&quot;1907&quot; data-origin-height=&quot;938&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://api.python.langchain.com/en/latest/_modules/langchain_core/prompts/loading.html#load_prompt&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;json, yaml 이외에도 만약 원하는 확장자를 추가하고 싶다면 직접 langchain 오픈소스 커밋터가 되어보시는 것도 추천드립니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1742051327559&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;langchain/libs/core/langchain_core/prompts/loading.py at master &amp;middot; langchain-ai/langchain&quot; data-og-description=&quot;  Build context-aware reasoning applications. Contribute to langchain-ai/langchain development by creating an account on GitHub.&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/langchain-ai/langchain/blob/master/libs/core/langchain_core/prompts/loading.py&quot; data-og-url=&quot;https://github.com/langchain-ai/langchain/blob/master/libs/core/langchain_core/prompts/loading.py&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/RJJzg/hyYqcHZxNS/BdGTUO1iql3ArKdkPMMkk0/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/MBexy/hyYr2Y6j0x/82gCWG02BvLPGfeLKzf3ck/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/langchain-ai/langchain/blob/master/libs/core/langchain_core/prompts/loading.py&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/langchain-ai/langchain/blob/master/libs/core/langchain_core/prompts/loading.py&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/RJJzg/hyYqcHZxNS/BdGTUO1iql3ArKdkPMMkk0/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/MBexy/hyYr2Y6j0x/82gCWG02BvLPGfeLKzf3ck/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;langchain/libs/core/langchain_core/prompts/loading.py at master &amp;middot; langchain-ai/langchain&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;  Build context-aware reasoning applications. Contribute to langchain-ai/langchain 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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style7&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span&gt; &lt;/span&gt; 프롬프트 YAML 파일의 템플릿 구조&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음은 랭체인의 load_prompt 함수를 사용하기 위한 YAML 파일 템플릿 구조를 살펴보도록 하겠습니다. 프롬프트 템플릿으로 사용하기 위한 YAML 파일 구조는 다음과 같습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1742051379361&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 예시1
_type: prompt
template: &quot;다음 주제에 대해 설명해주세요: {topic}&quot;
input_variables: [&quot;topic&quot;]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1742051389513&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 예시2
_type: prompt
template: |
  당신은 조선의 왕 입니다. 사용자 질문에 대해 조선의 왕 처럼 답변하시오.
   
  사용자 질문 : {input}
input_variables: [&quot;input&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;table id=&quot;1b7fd64d-44a0-80e5-9676-d670698d5b6c&quot; style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style8&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;옵션&lt;/td&gt;
&lt;td&gt;설명&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;1b7fd64d-44a0-8010-96d1-e485e9a1d4ca&quot;&gt;
&lt;td id=&quot;Tw_j&quot;&gt;_type&lt;/td&gt;
&lt;td id=&quot;NScD&quot;&gt;prompt | few_shot | chat 중에서 선택하여 사용가능&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;1b7fd64d-44a0-802c-887e-cf25a762be8a&quot;&gt;
&lt;td id=&quot;Tw_j&quot;&gt;template&lt;/td&gt;
&lt;td id=&quot;NScD&quot;&gt;프롬프트 내용 작성 (변수가 있는 경우 중괄호 사용)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;1b7fd64d-44a0-809d-a2a3-d0b9a49be1ea&quot;&gt;
&lt;td id=&quot;Tw_j&quot;&gt;input_variables&lt;/td&gt;
&lt;td id=&quot;NScD&quot;&gt;template 변수에 매핑할 변수명 지정. 비즈니스 로직에서 아래와 같이 사용 가능&lt;br /&gt;&lt;br /&gt;template = load_prompt(&amp;rdquo;template.yaml&quot;, encoding=&quot;utf-8&quot;)&lt;br /&gt;template.format(topic=&quot;{topic}&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;p data-ke-size=&quot;size16&quot;&gt;위의 예제로 사용한 기본 prompt 타입을 이외에도 few_shot과 chat을 사용할 수도 있는데요. 각 프롬프트의 성격에 맞게 선택하여 사용하실 수 있습니다. 템플릿 구조는 다음과 같습니다.&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;Few Shot 프롬프트(_type:few_shot)&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;예제를 포함한 프롬프트&lt;/li&gt;
&lt;li&gt;학습 예제를 통해 모델에게 패턴을 보여줄 때 사용&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;python&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;_type: few_shot
prefix: &quot;다음은 문장을 긍정/부정으로 분류하는 예제입니다:&quot;
examples:
  - input: &quot;오늘은 정말 좋은 날이에요!&quot;
    output: &quot;긍정&quot;
  - input: &quot;날씨가 너무 안 좋네요.&quot;
    output: &quot;부정&quot;
suffix: &quot;입력: {input}\\n출력:&quot;
example_prompt:
  _type: prompt
  template: &quot;입력: {input}\\n출력: {output}&quot;
input_variables: [&quot;question&quot;]&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;채팅 프롬프트(_type: chat)&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;대화형 상호작용을 위한 프롬프트&lt;/li&gt;
&lt;li&gt;system/user/assistant 역할 구분 가능&lt;/li&gt;
&lt;li&gt;대화 맥락 유지 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;_type: chat
messages:
  - role: system
    content: &quot;당신은 수학 선생님입니다.&quot;
  - role: user
    content: &quot;{question}&quot;
  - role: assistant
    content: &quot;이 문제를 단계별로 풀어보겠습니다.&quot;
input_variables: [&quot;question&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;정리해 보자면 load_prompt 함수를 사용할 때 yaml 파일에서 사용할 수 있는 타입은 prompt, few_shot, chat 3가지 타입입니다. 주의하실 점은 채팅 프롬프트에는 사용자와 주고받은 채팅 히스토리를 집어넣는 게 아니라 위와 같이 일부 지정된 프롬프트를 기입하는 용도입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서, 실제 사용자와의 대화 히스토리 자체는 위의 프롬프트 앞뒤로 붙여서 사용해야 합니다. 즉, messages를 통째로 매핑할 수 있는 구조는 아니기 때문에 실제 멀티턴(사용자와의 대화 히스토리 맥락을 파악) 사용 시에는 few_shot과 chat 타입의 프롬프트를 적용하기 어려운 점들이 있어서 제가 프로젝트에 사용할 때는 prompt 템플릿 하나만을 사용하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약, yaml 파일에 대화 히스토리를 넣고 싶다면 MS에서 개발한 프롬프트 관리 도구 및 라이브러리인 prompty를 사용하는 것도 방법입니다. 프롬프티는 yaml 파일 확장자 대신 .prompty라는 확장자를 사용하지만 yaml 파일과 비슷한 형식으로 사용 가능합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 때는 프롬프티가 루프변수를 지원하기 때문에 대화 히스토리도 변수로 사용할 수 있는 장점이 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;.prompty 예시 코드&lt;/p&gt;
&lt;pre id=&quot;code_1742051514051&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;---
name: Basic Prompt
description: A basic prompt that uses the GPT-3 chat API to answer questions
authors:
  - author_1
  - author_2
model:
  api: chat
  configuration:
    azure_deployment: gpt-35-turbo
sample:
  firstName: Jane
  lastName: Doe
  question: What is the meaning of life?
  chat_history: []
---
system:
You are an AI assistant who helps people find information.
As the assistant, you answer questions briefly, succinctly, 
and in a personable manner using markdown and even add some personal flair with appropriate emojis.

{% for item in chat_history %}
{{item.role}}:
{{item.content}}
{% endfor %}


user:
{{input}}&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;프롬프티 역시 랭체인에서 지원이 되기 때문에 필요에 따라서 사용하시면 됩니다. 프롬프티의 장점은 vscode에서 extension으로 플레이그라운드를 지원하기 때문에 개발자들의 테스트가 용이할 수 있다는 것이 장점 중에 하나 입니다.&lt;/p&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;MS 프롬프티&lt;/b&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1742051540888&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 - microsoft/prompty: Prompty makes it easy to create, manage, debug, and evaluate LLM prompts for your AI applications.  &quot; data-og-description=&quot;Prompty makes it easy to create, manage, debug, and evaluate LLM prompts for your AI applications. Prompty is an asset class and format for LLM prompts designed to enhance observability, understan...&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/Microsoft/prompty&quot; data-og-url=&quot;https://github.com/microsoft/prompty&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bgVYCe/hyYupZ0gmv/rTkwgPP5cookqkau3GMWH1/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/7RAIa/hyYqXqAHij/UOP4yXkpk4HrN3zY2BMw2k/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/Microsoft/prompty&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/Microsoft/prompty&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bgVYCe/hyYupZ0gmv/rTkwgPP5cookqkau3GMWH1/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/7RAIa/hyYqXqAHij/UOP4yXkpk4HrN3zY2BMw2k/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 - microsoft/prompty: Prompty makes it easy to create, manage, debug, and evaluate LLM prompts for your AI applications.&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Prompty makes it easy to create, manage, debug, and evaluate LLM prompts for your AI applications. Prompty is an asset class and format for LLM prompts designed to enhance observability, understan...&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;MS 프롬프티 랭체인 연동 라이브러리&lt;/b&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1742051542791&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;langchain/libs/partners/prompty at master &amp;middot; langchain-ai/langchain&quot; data-og-description=&quot;  Build context-aware reasoning applications. Contribute to langchain-ai/langchain development by creating an account on GitHub.&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/langchain-ai/langchain/tree/master/libs/partners/prompty&quot; data-og-url=&quot;https://github.com/langchain-ai/langchain/tree/master/libs/partners/prompty&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/hAfHA/hyYp9LiuMY/y4hKkdGi2mQ3yWJyHRbKg0/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/GA4yB/hyYrUtfnFV/rMdJhlyyeyf1tkmlkYwS2k/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/langchain-ai/langchain/tree/master/libs/partners/prompty&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/langchain-ai/langchain/tree/master/libs/partners/prompty&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/hAfHA/hyYp9LiuMY/y4hKkdGi2mQ3yWJyHRbKg0/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/GA4yB/hyYrUtfnFV/rMdJhlyyeyf1tkmlkYwS2k/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;langchain/libs/partners/prompty at master &amp;middot; langchain-ai/langchain&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;  Build context-aware reasoning applications. Contribute to langchain-ai/langchain 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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style7&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span&gt; &lt;/span&gt; 프롬프트 버전 관리 방법&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞서 프롬프트를 파일로 관리하는 경우 보안뿐만 아니라 버전 관리도 문제가 될 수 있다고 말씀드렸는데요. lakefs 또는 S3 등 별도 스토리지를 통해 관리하는 방법들이 있겠지만, 그 마저도 여력이 없다면 레포지토리 내 폴더로 버전관리를 해야 할 수도 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저의 경우에는 다음과 같이 prompts 폴더를 하나 만들어서 버전별로 프롬프트들을 관리하였습니다. 버전 폴더에는 yaml 파일들이 존재합니다. 그리고 팀원들과는 그라운드 룰을 설정하여 배포할 때는 폴더를 복사하여 v7, v8 이런 식으로 늘려나가는 방식을 택하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;image (2).png&quot; data-origin-width=&quot;257&quot; data-origin-height=&quot;587&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ICJHD/btsMMCdk3Zq/9SBxGbzxVoZesfokEfalkK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ICJHD/btsMMCdk3Zq/9SBxGbzxVoZesfokEfalkK/img.png&quot; data-alt=&quot;레포지토리 기반 프롬프트 버전 관리&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ICJHD/btsMMCdk3Zq/9SBxGbzxVoZesfokEfalkK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FICJHD%2FbtsMMCdk3Zq%2F9SBxGbzxVoZesfokEfalkK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;257&quot; height=&quot;587&quot; data-filename=&quot;image (2).png&quot; data-origin-width=&quot;257&quot; data-origin-height=&quot;587&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;레포지토리 기반 프롬프트 버전 관리&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 폴더 안에는 버전별로 프롬프트가 10개 이상 존재하기 때문에 사실, 버전이 변하지 않은 프롬프트들도 많이 존재하는데요. 그럼에도 변하지 않는 프롬프트까지 복사하여 버전 업하는 이유는 직관적인 관리를 위해서입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약, 프롬프트 별로 버전관리를 하게 된다면 10개나 되는 프롬프트의 버전을 전부 기억하고 있어야 하기 때문에 랩원 분들의 인지 부하가 발생할 수 있습니다. 따라서, 위와 같은 구조로 프롬프트를 관리하는 방법을 택하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;추가로, 프롬프트 버전관리에 대한 커뮤니케이션 방법으로는 노션을 활용하였습니다. 프롬프트가 중요한 프로젝트였기 때문에 모든 직군이 전부 관심을 갖고 모니터링할 수 있어야 해서 가장 접근이 쉬운 노션을 활용하여 중앙 관리식으로 워크플로우를 설계하고 각 직군별 소통문제를 해결하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;제목 없음 (1).png&quot; data-origin-width=&quot;1448&quot; data-origin-height=&quot;837&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/RPdn8/btsMLrRyIJg/FQR094nEMhdnLi2TmD4ep0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/RPdn8/btsMLrRyIJg/FQR094nEMhdnLi2TmD4ep0/img.png&quot; data-alt=&quot;프롬프트 버전 관리 노트&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/RPdn8/btsMLrRyIJg/FQR094nEMhdnLi2TmD4ep0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FRPdn8%2FbtsMLrRyIJg%2FFQR094nEMhdnLi2TmD4ep0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1448&quot; height=&quot;837&quot; data-filename=&quot;제목 없음 (1).png&quot; data-origin-width=&quot;1448&quot; data-origin-height=&quot;837&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;프롬프트 버전 관리 노트&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;table id=&quot;1b7fd64d-44a0-8095-96fd-cdefef8c26b7&quot; style=&quot;border-collapse: collapse; width: 100%; height: 120px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style8&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 20px;&quot;&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 id=&quot;1b7fd64d-44a0-8027-8ab3-c6248d6bf2ad&quot; style=&quot;height: 20px;&quot;&gt;
&lt;td id=&quot;SG&amp;lt;&amp;gt;&quot; style=&quot;height: 20px;&quot;&gt;버전&lt;/td&gt;
&lt;td id=&quot;\qpo&quot; style=&quot;height: 20px;&quot;&gt;프롬프트 버전 기입(클릭시 각 프롬프트 내용 및 수정사항 확인 가능)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;1b7fd64d-44a0-807e-8820-fcbfe9625605&quot; style=&quot;height: 20px;&quot;&gt;
&lt;td id=&quot;SG&amp;lt;&amp;gt;&quot; style=&quot;height: 20px;&quot;&gt;날짜&lt;/td&gt;
&lt;td id=&quot;\qpo&quot; style=&quot;height: 20px;&quot;&gt;프롬프트 배포 날짜&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;1b7fd64d-44a0-8095-b359-dfd7db383551&quot; style=&quot;height: 20px;&quot;&gt;
&lt;td id=&quot;SG&amp;lt;&amp;gt;&quot; style=&quot;height: 20px;&quot;&gt;설명&lt;/td&gt;
&lt;td id=&quot;\qpo&quot; style=&quot;height: 20px;&quot;&gt;한 줄 요약 설명&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;1b7fd64d-44a0-8041-8acf-e20efa36345b&quot; style=&quot;height: 20px;&quot;&gt;
&lt;td id=&quot;SG&amp;lt;&amp;gt;&quot; style=&quot;height: 20px;&quot;&gt;작업자&lt;/td&gt;
&lt;td id=&quot;\qpo&quot; style=&quot;height: 20px;&quot;&gt;프롬프트 배포한 사람&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;1b7fd64d-44a0-8067-a104-d805c54d5bb8&quot; style=&quot;height: 20px;&quot;&gt;
&lt;td id=&quot;SG&amp;lt;&amp;gt;&quot; style=&quot;height: 20px;&quot;&gt;플레이그라운드&lt;/td&gt;
&lt;td id=&quot;\qpo&quot; style=&quot;height: 20px;&quot;&gt;프롬프트 테스트 환경 링크 (Langserve 플레이그라운드 사용)&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;&lt;b&gt;  버전 번호가 갖는 의미&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;상호 간 의사소통에서 헷갈리지 않도록 버전은 다음과 같이 표기합니다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;v{메이저버전}.{마이너버전}&lt;/li&gt;
&lt;li&gt;한 주가 지나면 메이저버전을 올려서 배포하고 그 주에 여러 번 배포하는 경우 마이너 버전을 배포합니다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;ex) 02.03 ~ 02.09   v3, v3.1, v3.2 &amp;hellip;.&lt;/li&gt;
&lt;li&gt;ex) 02.10 ~ 02.16   v4 &amp;hellip;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같은 식으로 메이저버전과 마이너버전으로 나눠서 관리를 진행했던 이유는 hotfix 등 빠르게 변경사항 조치가 일어나거나 간단한 수정을 하는 경우에도 버전이 계속해서 늘려야 하는 상황을 방지하였고 메이저 버전이 수정될 때는 프롬프트 폴더를 통째로 복사해서 버전업을 하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 도미노를 쌓을 때 도미노 사이사이에 Safety Gap을 두는 것과 비슷합니다. 도미노 전체가 무너지는 것을 방지하기 위하여 사이사이에 도미노 하나를 눕혀 놓는데, 이처럼 메이저버전이 배포될 때는 기존 프롬프트를 업데이트하는 것이 아닌 폴더 채로 복사해서 버전업을 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Risk-Assessment-pic.webp&quot; data-origin-width=&quot;524&quot; data-origin-height=&quot;328&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b6Segb/btsMMEB764r/boPmeMUcW0Or31554urlV1/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b6Segb/btsMMEB764r/boPmeMUcW0Or31554urlV1/img.webp&quot; data-alt=&quot;이미지 출처 : https://eranyona.com/the-pivotal-role-of-risk-assessment-in-medical-device-drug-and-medical-software-development-and-regulation/&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b6Segb/btsMMEB764r/boPmeMUcW0Or31554urlV1/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb6Segb%2FbtsMMEB764r%2FboPmeMUcW0Or31554urlV1%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;524&quot; height=&quot;328&quot; data-filename=&quot;Risk-Assessment-pic.webp&quot; data-origin-width=&quot;524&quot; data-origin-height=&quot;328&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;이미지 출처 : https://eranyona.com/the-pivotal-role-of-risk-assessment-in-medical-device-drug-and-medical-software-development-and-regulation/&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제로 프로젝트 도중에 배포된 새 버전에서 일부 기능들이 동작 안 하는 이슈가 있었는데 다행히 이전 버전에서는 잘 동작하여서 새로 배포될 때까지 기다리지 않고도 다른 분들께서 이전 버전으로 테스트나 다른 작업들을 진행할 수가 있었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;테스트는 Langserve 프레임워크를 사용하여 메이저 버전별로 플레이그라운드에 접속하여 테스트가 가능하도록 링크를 노션에 공유해 놓았습니다. 실제로 메인 코드에는 다음과 같이 버전별로 라우트를 생성하여 관리합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1742051948310&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from langserve import add_routes

# v5
add_routes(app, create_sql_chain(&quot;v5&quot;), path=&quot;/api/v5/sql&quot;)
add_routes(app, create_chat_chain(&quot;v5&quot;), path=&quot;/api/v5/chat&quot;, playground_type=&quot;chat&quot;)

# v6
add_routes(app, create_sql_chain(&quot;v6&quot;), path=&quot;/api/v6/sql&quot;)
add_routes(app, create_chat_chain(&quot;v6&quot;), path=&quot;/api/v6/chat&quot;, playground_type=&quot;chat&quot;)
add_routes(app, trend_graph.compile(&quot;v6&quot;), path=&quot;/api/v6/trend&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;Langserve를 사용할 경우 단점 중 하나가 PathVariable 지원이 안된다는 점이어서 버전이 많아질수록 메인 코드가 지저분해질 수 있기 때문에 고려해서 사용하시면 좋을 것 같습니다. Langserve는 지원이 더 이상 되지 않기 때문에 새로 프로젝트를 시작하는 경우라면 LangGraph 플랫폼을 사용해 주시면 좋습니다. 아래 블로그를 참고하시면 좋을 것 같습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1742051962380&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 프로토타입 개발을 위한 랭서브(Langserve) 도입기&quot; data-og-description=&quot;최근에 5개월짜리 짧은 기간의 AI 프로젝트 리드를 맡게 되면서 어떤 프레임워크와 환경 셋팅을 가져가야 구현 속도면에서 이점이 있을까 고민하던 찰나에 우아한형제들의 기술블로그 중 AI 데&quot; data-og-host=&quot;chucoding.tistory.com&quot; data-og-source-url=&quot;https://chucoding.tistory.com/155&quot; data-og-url=&quot;https://chucoding.tistory.com/155&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/b6nEwj/hyYuqdxXUl/o13gCYxLMdFnW5LRC40U40/img.jpg?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/expEyf/hyYr2SjIOh/BcgJIFbz3mVJqd4LBioYQK/img.jpg?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/bpZsfC/hyYubUZe8o/bru2KB5UQD95I6qOJufnrk/img.png?width=1904&amp;amp;height=1117&amp;amp;face=0_0_1904_1117&quot;&gt;&lt;a href=&quot;https://chucoding.tistory.com/155&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://chucoding.tistory.com/155&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/b6nEwj/hyYuqdxXUl/o13gCYxLMdFnW5LRC40U40/img.jpg?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/expEyf/hyYr2SjIOh/BcgJIFbz3mVJqd4LBioYQK/img.jpg?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/bpZsfC/hyYubUZe8o/bru2KB5UQD95I6qOJufnrk/img.png?width=1904&amp;amp;height=1117&amp;amp;face=0_0_1904_1117');&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 프로토타입 개발을 위한 랭서브(Langserve) 도입기&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;최근에 5개월짜리 짧은 기간의 AI 프로젝트 리드를 맡게 되면서 어떤 프레임워크와 환경 셋팅을 가져가야 구현 속도면에서 이점이 있을까 고민하던 찰나에 우아한형제들의 기술블로그 중 AI 데&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;chucoding.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;위와 같이 Langserve 라우트에서 프롬프트 버전을 파라미터로 받은 후에는 다음과 같이 사용가능합니다. 랭그래프에서 사용하는 방법을 예시로 들도록 하겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1742051972494&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class TrendGraph():
    &quot;&quot;&quot;
    추세 분석 워크플로우 정의
    &quot;&quot;&quot;
    def __init__(self):
        self.version = &quot;v6&quot;
        self.llm = LLMFactory.create(provider=&quot;openai&quot;, model=&quot;gpt-4o-mini&quot;, temperature=0.0)
        self.prompt_dir = Path(&quot;app/utils/prompts&quot;) / f&quot;{self.version}&quot;
        self.workflow = StateGraph(GraphState)
        ...

    def compile(self, version: str = &quot;v6&quot;) -&amp;gt; CompiledStateGraph:
    	&quot;&quot;&quot; 랭그래프 컴파일 &quot;&quot;&quot;
    	self.version = version
    	self.prompt_dir = Path(&quot;app/utils/prompts&quot;) / f&quot;{self.version}&quot; # 버전
    	self.workflow.add_node(&quot;text_to_sql&quot;, self._text_to_sql)
    	...

    def _text_to_sql(self, state: GraphState) -&amp;gt; GraphState:
        &quot;&quot;&quot; 테이블 분류 + SQL 변환 &quot;&quot;&quot;
    	trend_sql_prompt = load_prompt(f&quot;{self.prompt_dir}/trend_sql.yaml&quot;, encoding=&quot;utf-8&quot;)
    	...&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style7&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span&gt; &lt;/span&gt; 정리하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금까지 제가 적용했던 프롬프트 파일 관리 방법에 대해 정리해 보았습니다. 사실 프로젝트를 진행하면서 팀원들 중에 AI 프로젝트가 처음인 분들이 많았음에도 불구하고 5개월 동안 빠르게 성공시켜야 하는 단기간 AI 프로젝트였기 때문에 저는 무조건 진행속도 위주로 방법들을 채택하였는데요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼에도 많은 시행착오를 반복하며, 실제로 프로젝트 진행 속도를 증진시키는 데 있어 좋은 변화로 이어졌던 부분들 위주로 작성해 보았습니다. 물론, 모든 환경에서 적용되는 최적화된 관리방법이라는 생각은 안 들지만 AI 프로젝트를 운영하는 경우 해당 문서가 참고가 되어 조금이라도 도움이 된다면 좋을 것 같다는 생각이 듭니다.&lt;/p&gt;</description>
      <category>AI</category>
      <author>외계공룡</author>
      <guid isPermaLink="true">https://chucoding.tistory.com/157</guid>
      <comments>https://chucoding.tistory.com/157#entry157comment</comments>
      <pubDate>Sun, 16 Mar 2025 00:38:06 +0900</pubDate>
    </item>
    <item>
      <title>BigQuery와 하이퍼클로바X로 Text-To-SQL(NL2SQL) 구현하기</title>
      <link>https://chucoding.tistory.com/156</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-03-02 161634.png&quot; data-origin-width=&quot;1038&quot; data-origin-height=&quot;460&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cYzgAZ/btsMyB1e7JZ/kQyHVDbriztXZLqo9PgoYk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cYzgAZ/btsMyB1e7JZ/kQyHVDbriztXZLqo9PgoYk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cYzgAZ/btsMyB1e7JZ/kQyHVDbriztXZLqo9PgoYk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcYzgAZ%2FbtsMyB1e7JZ%2FkQyHVDbriztXZLqo9PgoYk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1038&quot; height=&quot;460&quot; data-filename=&quot;스크린샷 2025-03-02 161634.png&quot; data-origin-width=&quot;1038&quot; data-origin-height=&quot;460&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;BigQuery는 Google Cloud에서 제공하는 데이터 웨어하우스로, 대량의 데이터를 빠르게 분석할 수 있다는 장점이 있습니다. BigQuery를 사용할 경우 별도의 DB 설치 필요 없이 클라우드 내에서 데이터베이스를 조작할 수 있고 언어별 SDK도 지원이 잘 되어 있기 때문에 구글 계정 인증만 한다면 코드단에서도 손쉽게 사용이 가능합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 포스팅에서는 랭체인을 활용하여 BigQuery와 하이퍼클로바X를 연동해 보고 Text-to-SQL 프롬프트를 작성하는 방법에 대해 알아보도록 하겠습니다.&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;a href=&quot;#1&quot;&gt;1. BigQuery 사용방법 알아보기&lt;/a&gt;&lt;br /&gt;&lt;a href=&quot;#2&quot;&gt;2. BigQuery 연동하기&lt;/a&gt;&lt;br /&gt;&lt;a href=&quot;#3&quot;&gt;3. 하이퍼클로바X 연동하기&lt;/a&gt;&lt;br /&gt;&lt;a href=&quot;#4&quot;&gt;4. Text-to-SQL(NL2SQL) 프롬프트 작성하기&lt;/a&gt;&lt;br /&gt;&lt;a href=&quot;#5&quot;&gt;5. 정리하기&lt;/a&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style7&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 id=&quot;1&quot; data-ke-size=&quot;size26&quot;&gt;  BigQuery 사용방법 알아보기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 BigQuery를 사용하기 위해서는 다음과 같은 절차를 거치게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;Google Cloud 프로젝트 생성&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;데이터셋 및 테이블 생성&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;데이터 조회 및 확인&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;BigQuery API 활성화&lt;/b&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1. Google Cloud 프로젝트 생성하기&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 Google Cloud Platform으로 이동하여 BigQuery에 사용할 프로젝트를 하나 생성합니다. Google Cloud 계정이 없으시다면 계정을 먼저 생성해주셔야 합니다. 첫 회원가입 시 3달간 $300달러(약 43만 원) 무료 크레딧이 지급됩니다. 프로젝트를 생성하면 Google Cloud Console 접속 시 다음과 같은 화면이 나타납니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;제목 없음 (1) (1).png&quot; data-origin-width=&quot;1911&quot; data-origin-height=&quot;943&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/uyMOY/btsMAFnGpnh/s1aWdiFkL9GfF0cvpcudQK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/uyMOY/btsMAFnGpnh/s1aWdiFkL9GfF0cvpcudQK/img.png&quot; data-alt=&quot;https://console.cloud.google.com/&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/uyMOY/btsMAFnGpnh/s1aWdiFkL9GfF0cvpcudQK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FuyMOY%2FbtsMAFnGpnh%2Fs1aWdiFkL9GfF0cvpcudQK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/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;943&quot; data-filename=&quot;제목 없음 (1) (1).png&quot; data-origin-width=&quot;1911&quot; data-origin-height=&quot;943&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://console.cloud.google.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;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span data-token-index=&quot;0&quot;&gt;2. 데이터셋 및 테이블 생성&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음은 BigQuery 스튜디오에 접속하여 새로운 데이터셋과 테이블을 생성하도록 하겠습니다. 구글 클라우드 콘솔의 왼쪽 네비게이션 바를 열어서 BigQuery 메뉴를 클릭합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;image.png&quot; data-origin-width=&quot;1908&quot; data-origin-height=&quot;955&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/XdGcb/btsMy00AcDi/hgkNDk4Z3WiPbdl3ShLUyk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/XdGcb/btsMy00AcDi/hgkNDk4Z3WiPbdl3ShLUyk/img.png&quot; data-alt=&quot;BigQuery 서비스 선택&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/XdGcb/btsMy00AcDi/hgkNDk4Z3WiPbdl3ShLUyk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FXdGcb%2FbtsMy00AcDi%2FhgkNDk4Z3WiPbdl3ShLUyk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/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;955&quot; data-filename=&quot;image.png&quot; data-origin-width=&quot;1908&quot; data-origin-height=&quot;955&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;BigQuery 서비스 선택&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;BigQuery 메뉴를 클릭하면 다음과 같이 BigQuery Studio에 접속할 수 있습니다. 먼저 화면 왼쪽의 탐색기에서 프로젝트id 오른쪽의 [더보기 아이콘(:)]을 클릭하여 [데이터 세트 만들기] 메뉴를 클릭합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;image (1).png&quot; data-origin-width=&quot;1910&quot; data-origin-height=&quot;933&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/YGXkt/btsMySItkg8/J9Gpdz0muPqhuS83Gb840K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/YGXkt/btsMySItkg8/J9Gpdz0muPqhuS83Gb840K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/YGXkt/btsMySItkg8/J9Gpdz0muPqhuS83Gb840K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FYGXkt%2FbtsMySItkg8%2FJ9Gpdz0muPqhuS83Gb840K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1910&quot; height=&quot;933&quot; data-filename=&quot;image (1).png&quot; data-origin-width=&quot;1910&quot; data-origin-height=&quot;933&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[데이터 세트 만들기] 메뉴 클릭시 오른쪽에 메타 정보를 입력할 수 있는 Drawer가 열립니다. 원하는 데이터세트ID로 입력해 주신 뒤에 하단의 [데이터 세트 만들기] 버튼을 클릭합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;제목 없음 (1).png&quot; data-origin-width=&quot;1917&quot; data-origin-height=&quot;938&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b25Swh/btsMBeJRuuo/GUP2OHFkYjRutI8AlloXJK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b25Swh/btsMBeJRuuo/GUP2OHFkYjRutI8AlloXJK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b25Swh/btsMBeJRuuo/GUP2OHFkYjRutI8AlloXJK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb25Swh%2FbtsMBeJRuuo%2FGUP2OHFkYjRutI8AlloXJK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/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;938&quot; data-filename=&quot;제목 없음 (1).png&quot; data-origin-width=&quot;1917&quot; data-origin-height=&quot;938&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터셋이 정상적으로 만들어진다면 다음과 같이 프로젝트 내부에 새로 만든 데이터셋 ID가 표시됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;제목 없음 (1) (2).png&quot; data-origin-width=&quot;1910&quot; data-origin-height=&quot;934&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b2IyDc/btsMz7rlpG4/lG60YRUbdOIi4OlKRmCDK1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b2IyDc/btsMz7rlpG4/lG60YRUbdOIi4OlKRmCDK1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b2IyDc/btsMz7rlpG4/lG60YRUbdOIi4OlKRmCDK1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb2IyDc%2FbtsMz7rlpG4%2FlG60YRUbdOIi4OlKRmCDK1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1910&quot; height=&quot;934&quot; data-filename=&quot;제목 없음 (1) (2).png&quot; data-origin-width=&quot;1910&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;다음은 테이블을 생성해보도록 하겠습니다. 아래의 예제 스키마는 간단한 사용자 이벤트를 추적할 수 있는 테이블 구조로 이루어져 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1740900297427&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;CREATE TABLE datasets.events (
    event_name STRING,
    event_timestamp TIMESTAMP,
    user_id STRING,
    session_id STRING,
    event_params STRING
);&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;다음과 같이 상단의 [+] 버튼을 통해 쿼리 입력창을 열어서 위에서 정의한 DDL(Data Definition Language) 문을 복사 붙여넣기한 후 [실행] 버튼을 클릭합니다. 쿼리가 잘 실행된다면 하단 쿼리 결과창에 테이블이 생성되었다는 문구가 나타납니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;image (2).png&quot; data-origin-width=&quot;1916&quot; data-origin-height=&quot;941&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/wt4Qh/btsMyj0R82h/6q9OitHyqPhgJBPmmPUWl0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/wt4Qh/btsMyj0R82h/6q9OitHyqPhgJBPmmPUWl0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/wt4Qh/btsMyj0R82h/6q9OitHyqPhgJBPmmPUWl0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fwt4Qh%2FbtsMyj0R82h%2F6q9OitHyqPhgJBPmmPUWl0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/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;941&quot; data-filename=&quot;image (2).png&quot; data-origin-width=&quot;1916&quot; data-origin-height=&quot;941&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음으로는 데이터를 저장하도록 하겠습니다. 아래 DML(Data Manipulation Language) 쿼리를 복사해서 쿼리 입력창에 추가합니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1740900344963&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;INSERT INTO datasets.events (event_name, event_timestamp, user_id, session_id, event_params)
VALUES
    (&quot;page_view&quot;, TIMESTAMP(&quot;2025-02-23 12:00:00&quot;), &quot;user_001&quot;, &quot;session_001&quot;, '{&quot;page&quot;:&quot;home&quot;,&quot;referrer&quot;:&quot;google&quot;}'),
    (&quot;page_view&quot;, TIMESTAMP(&quot;2025-02-23 12:10:00&quot;), &quot;user_002&quot;, &quot;session_002&quot;, '{&quot;page&quot;:&quot;donation&quot;,&quot;referrer&quot;:&quot;facebook&quot;}'),
    (&quot;page_view&quot;, TIMESTAMP(&quot;2025-02-23 12:20:00&quot;), &quot;user_003&quot;, &quot;session_003&quot;, '{&quot;page&quot;:&quot;home&quot;,&quot;referrer&quot;:&quot;direct&quot;}'),
    (&quot;donation&quot;, TIMESTAMP(&quot;2025-02-23 12:25:00&quot;), &quot;user_004&quot;, &quot;session_004&quot;, '{&quot;campaign&quot;:&quot;SaveTheEarth&quot;,&quot;donation_amount&quot;:50000}'),
    (&quot;donation&quot;, TIMESTAMP(&quot;2025-02-23 13:00:00&quot;), &quot;user_005&quot;, &quot;session_005&quot;, '{&quot;campaign&quot;:&quot;SaveTheEarth&quot;,&quot;donation_amount&quot;:75000}'),
    (&quot;donation&quot;, TIMESTAMP(&quot;2025-02-23 14:30:00&quot;), &quot;user_006&quot;, &quot;session_006&quot;, '{&quot;campaign&quot;:&quot;HelpTheChildren&quot;,&quot;donation_amount&quot;:100000}');&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;복사 붙여넣기 후 다음과 같이 [실행] 버튼을 클릭하여 데이터를 저장합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;image (3).png&quot; data-origin-width=&quot;1907&quot; data-origin-height=&quot;937&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dE5NOB/btsMyl5vtM6/qZ6LVmOFKC5tZW45g6At61/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dE5NOB/btsMyl5vtM6/qZ6LVmOFKC5tZW45g6At61/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dE5NOB/btsMyl5vtM6/qZ6LVmOFKC5tZW45g6At61/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdE5NOB%2FbtsMyl5vtM6%2FqZ6LVmOFKC5tZW45g6At61%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/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;937&quot; data-filename=&quot;image (3).png&quot; data-origin-width=&quot;1907&quot; data-origin-height=&quot;937&quot;/&gt;&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;&lt;b&gt;3. 데이터 조회 및 확인&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음은 BigQuery 콘솔에서 SQL을 통해 데이터를 조회해보도록 하겠습니다. 저는 앞서 Mockup 데이터를 실제 비영리단체에서 사용할 법한 데이터를 기반으로 만들어보았습니다. 따라서 사용하실 수 있는 쿼리문 예제는 다음과 같습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span data-token-index=&quot;0&quot;&gt;예제 쿼리: 일별 후원 발생 건수 조회&lt;/span&gt; &lt;span data-token-index=&quot;0&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1740900475523&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;SELECT
    DATE(event_timestamp) AS donation_date,
    COUNT(*) AS donation_count
FROM datasets.events
WHERE event_name = 'donation'
GROUP BY donation_date
ORDER BY donation_date;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 쿼리를 복사해서 쿼리 입력창에서 실행해보고 결괏값을 확인해 보도록 하겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;제목 없음 (1) (3).png&quot; data-origin-width=&quot;1910&quot; data-origin-height=&quot;939&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kUjaz/btsMxG9saDg/a0JGO7bjo7d0lcFYXGP0s1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kUjaz/btsMxG9saDg/a0JGO7bjo7d0lcFYXGP0s1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kUjaz/btsMxG9saDg/a0JGO7bjo7d0lcFYXGP0s1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkUjaz%2FbtsMxG9saDg%2Fa0JGO7bjo7d0lcFYXGP0s1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1910&quot; height=&quot;939&quot; data-filename=&quot;제목 없음 (1) (3).png&quot; data-origin-width=&quot;1910&quot; data-origin-height=&quot;939&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마찬가지로 다음 예제도 실행해보도록 하겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span data-token-index=&quot;0&quot;&gt;예제 쿼리: 특정 캠페인 후원 금액 합산&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1740900510187&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;SELECT
    JSON_VALUE(event_params, '$.campaign') AS campaign_name,
    SUM(CAST(JSON_VALUE(event_params, '$.donation_amount') AS FLOAT)) AS total_donation
FROM datasets.events
WHERE event_name = 'donation'
GROUP BY campaign_name
ORDER BY total_donation DESC;&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-filename=&quot;제목 없음 (1) (4).png&quot; data-origin-width=&quot;1914&quot; data-origin-height=&quot;939&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bQOpCg/btsMBjqRwee/JU5gbzpyi8Grnxik5noX91/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bQOpCg/btsMBjqRwee/JU5gbzpyi8Grnxik5noX91/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bQOpCg/btsMBjqRwee/JU5gbzpyi8Grnxik5noX91/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbQOpCg%2FbtsMBjqRwee%2FJU5gbzpyi8Grnxik5noX91%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/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;939&quot; data-filename=&quot;제목 없음 (1) (4).png&quot; data-origin-width=&quot;1914&quot; data-origin-height=&quot;939&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 예시들과 같이 간단한 SQL을 활용하여 데이터가 정상적으로 저장되었는지 검토할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span data-token-index=&quot;0&quot;&gt;4. BigQuery API 활성화&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음으로는 LLM을 활용하기 위해 Python 코드 단에서 데이터를 저장하고 분석할 수 있도록 BigQuery API를 활성화하도록 하겠습니다. 왼쪽 상단의 탐색 메뉴를 클릭합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;제목 없음 (1).png&quot; data-origin-width=&quot;1916&quot; data-origin-height=&quot;932&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b6tdE5/btsMyAOL5sN/bdn5N0zewmn71Yc1uoDA61/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b6tdE5/btsMyAOL5sN/bdn5N0zewmn71Yc1uoDA61/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b6tdE5/btsMyAOL5sN/bdn5N0zewmn71Yc1uoDA61/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb6tdE5%2FbtsMyAOL5sN%2Fbdn5N0zewmn71Yc1uoDA61%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/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;932&quot; data-filename=&quot;제목 없음 (1).png&quot; data-origin-width=&quot;1916&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;다음으로는 [API 및 서비스] 메뉴를 클릭합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;제목 없음 (2).png&quot; data-origin-width=&quot;1912&quot; data-origin-height=&quot;921&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/EhhrZ/btsMzYnNWTP/nFOJ9hVBncyWQlKr7n3u81/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/EhhrZ/btsMzYnNWTP/nFOJ9hVBncyWQlKr7n3u81/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/EhhrZ/btsMzYnNWTP/nFOJ9hVBncyWQlKr7n3u81/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FEhhrZ%2FbtsMzYnNWTP%2FnFOJ9hVBncyWQlKr7n3u81%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/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;921&quot; data-filename=&quot;제목 없음 (2).png&quot; data-origin-width=&quot;1912&quot; data-origin-height=&quot;921&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음으로는 BigQuery API 항목을 클릭합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;image.png&quot; data-origin-width=&quot;1916&quot; data-origin-height=&quot;923&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bQKycj/btsMA6ywUb3/IuROQtFkpDGLVgqmnBf6NK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bQKycj/btsMA6ywUb3/IuROQtFkpDGLVgqmnBf6NK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bQKycj/btsMA6ywUb3/IuROQtFkpDGLVgqmnBf6NK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbQKycj%2FbtsMA6ywUb3%2FIuROQtFkpDGLVgqmnBf6NK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/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;923&quot; data-filename=&quot;image.png&quot; data-origin-width=&quot;1916&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;오른쪽 상단의 [사용자 인증 정보 만들기] 버튼을 클릭합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;image (1).png&quot; data-origin-width=&quot;1912&quot; data-origin-height=&quot;943&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dlqXhy/btsMzZ7YeVM/sfnxLWliC9Qj6IqocGUeY0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dlqXhy/btsMzZ7YeVM/sfnxLWliC9Qj6IqocGUeY0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dlqXhy/btsMzZ7YeVM/sfnxLWliC9Qj6IqocGUeY0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdlqXhy%2FbtsMzZ7YeVM%2FsfnxLWliC9Qj6IqocGUeY0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/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;943&quot; data-filename=&quot;image (1).png&quot; data-origin-width=&quot;1912&quot; data-origin-height=&quot;943&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용할 API와 애플리케이션 데이터를 선택한 후에 [다음] 버튼을 클릭합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;image (2).png&quot; data-origin-width=&quot;1916&quot; data-origin-height=&quot;934&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/UxEoP/btsMBggFDeR/GeCk1N4ZkrkyRwH24eUpKk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/UxEoP/btsMBggFDeR/GeCk1N4ZkrkyRwH24eUpKk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/UxEoP/btsMBggFDeR/GeCk1N4ZkrkyRwH24eUpKk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FUxEoP%2FbtsMBggFDeR%2FGeCk1N4ZkrkyRwH24eUpKk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/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;934&quot; data-filename=&quot;image (2).png&quot; data-origin-width=&quot;1916&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;다음으로는 서비스 계정 이름 입력 후 [완료] 버튼을 클릭합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;제목 없음 (1) (1).png&quot; data-origin-width=&quot;1914&quot; data-origin-height=&quot;935&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/p9Bub/btsMAjypi6H/AefNK873V3EErUzhlfD2M0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/p9Bub/btsMAjypi6H/AefNK873V3EErUzhlfD2M0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/p9Bub/btsMAjypi6H/AefNK873V3EErUzhlfD2M0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fp9Bub%2FbtsMAjypi6H%2FAefNK873V3EErUzhlfD2M0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/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;935&quot; data-filename=&quot;제목 없음 (1) (1).png&quot; data-origin-width=&quot;1914&quot; data-origin-height=&quot;935&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서비스 계정이 만들어지면 [사용자 인증 정보] 탭을 클릭하여 하단에 생성된 서비스 계정을 확인 후 계정을 클릭합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;image (3).png&quot; data-origin-width=&quot;1912&quot; data-origin-height=&quot;940&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/yan4Q/btsMA6FieDv/4zwUcT5NM38y5IOgmpbgjk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/yan4Q/btsMA6FieDv/4zwUcT5NM38y5IOgmpbgjk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/yan4Q/btsMA6FieDv/4zwUcT5NM38y5IOgmpbgjk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fyan4Q%2FbtsMA6FieDv%2F4zwUcT5NM38y5IOgmpbgjk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/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;940&quot; data-filename=&quot;image (3).png&quot; data-origin-width=&quot;1912&quot; data-origin-height=&quot;940&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서비스 계정 내에서는 다음과 같이 [키] 탭으로 이동하여 [키 추가] 버튼을 클릭한 후 JSON 비공개 키를 생성합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;image (4).png&quot; data-origin-width=&quot;1912&quot; data-origin-height=&quot;940&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ol6vt/btsMyabJij1/qXsX3sFrhmWTadlsRpNOZK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ol6vt/btsMyabJij1/qXsX3sFrhmWTadlsRpNOZK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ol6vt/btsMyabJij1/qXsX3sFrhmWTadlsRpNOZK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fol6vt%2FbtsMyabJij1%2FqXsX3sFrhmWTadlsRpNOZK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/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;940&quot; data-filename=&quot;image (4).png&quot; data-origin-width=&quot;1912&quot; data-origin-height=&quot;940&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JSON 파일이 다운로드 되면서 다음과 같이 Key가 활성화되면 API를 사용할 수 있게 됩니다. 다운로드된 JSON파일은 안전한 곳에 잘 보관해 놓습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;image (5).png&quot; data-origin-width=&quot;1912&quot; data-origin-height=&quot;937&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b55FeO/btsMA7xpBAt/6uFkpnKKBgK3UY24COrXw0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b55FeO/btsMA7xpBAt/6uFkpnKKBgK3UY24COrXw0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b55FeO/btsMA7xpBAt/6uFkpnKKBgK3UY24COrXw0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb55FeO%2FbtsMA7xpBAt%2F6uFkpnKKBgK3UY24COrXw0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/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;937&quot; data-filename=&quot;image (5).png&quot; data-origin-width=&quot;1912&quot; data-origin-height=&quot;937&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음은 서비스 계정에 빅쿼리 액세스 권한을 부여하도록 하겠습니다. 왼쪽 내비게이션바의 IAM 메뉴(사람 모양의 아이콘)로 접속하여 [액세스 권한 부여] 버튼을 클릭합니다. 다음으로는 주 구성원으로 새로 생성한 서비스 계정을 추가한 뒤에 역할에 BigQuery 관리자를 지정한 후 [저장] 버튼을 클릭합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;image (6).png&quot; data-origin-width=&quot;1912&quot; data-origin-height=&quot;938&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/CX1Vf/btsMyBfTPFF/7Pgx30dwEnnYWDvNqsX701/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/CX1Vf/btsMyBfTPFF/7Pgx30dwEnnYWDvNqsX701/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/CX1Vf/btsMyBfTPFF/7Pgx30dwEnnYWDvNqsX701/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FCX1Vf%2FbtsMyBfTPFF%2F7Pgx30dwEnnYWDvNqsX701%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/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;938&quot; data-filename=&quot;image (6).png&quot; data-origin-width=&quot;1912&quot; data-origin-height=&quot;938&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음과 같이 서비스 계정에 BigQuery 관리자 역할이 생성된 것이 확인되었다면 이제 빅쿼리 클라이언트 SDK를 활용하여 코드단에서도 API 호출이 가능합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;image (7).png&quot; data-origin-width=&quot;1916&quot; data-origin-height=&quot;939&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c6GvP5/btsMyk6ukOL/IkHBnIxRyvhCBNiKxqFcx1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c6GvP5/btsMyk6ukOL/IkHBnIxRyvhCBNiKxqFcx1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c6GvP5/btsMyk6ukOL/IkHBnIxRyvhCBNiKxqFcx1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc6GvP5%2FbtsMyk6ukOL%2FIkHBnIxRyvhCBNiKxqFcx1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/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;939&quot; data-filename=&quot;image (7).png&quot; data-origin-width=&quot;1916&quot; data-origin-height=&quot;939&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style7&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 id=&quot;2&quot; data-ke-size=&quot;size26&quot;&gt;  BigQuery 연동하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에는 앞서 Google BigQuery 스튜디오에서 만들어놓은 데이터셋을 실제 파이썬 코드에서 호출하는 방법에 대해 알아보도록 하겠습니다. 저는 Google에서 제공하고 있는 Colab 환경에서 가이드드리도록 하겠습니다. 먼저 환경변수 셋팅을 위한 dotenv 라이브러리를 설치합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1740903372704&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# colab에서는 pip앞에 느낌표(!)를 붙여서 모듈을 설치합니다.
!pip install dotenv&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;.env 파일에는 다음과 같이 하이퍼클로바X 호출에 필요한 API 키 값과 BigQuery에서 생성한 테이블 ID를 저장해놓으면 좋습니다. colab에서는 다음과 같이 왼쪽 네비게이션 바에서 폴더( ) 선택 후 마우스 우 클릭하여 .env 파일을 생성하시면 됩니다.&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;1372&quot; data-origin-height=&quot;664&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/r5X4P/btsMyDELodg/Ko1m5AEHJz7j9Uyo1aA6RK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/r5X4P/btsMyDELodg/Ko1m5AEHJz7j9Uyo1aA6RK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/r5X4P/btsMyDELodg/Ko1m5AEHJz7j9Uyo1aA6RK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fr5X4P%2FbtsMyDELodg%2FKo1m5AEHJz7j9Uyo1aA6RK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/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;664&quot; data-origin-width=&quot;1372&quot; data-origin-height=&quot;664&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;.env 파일에는 발급받은 빅쿼리에서 발급받은 프로젝트 아이디를 다음과 같이 입력해 주시면 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1740904632257&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;BIGQUERY_PROJECT_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;pre id=&quot;code_1740904908729&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;load_dotenv()
project_id = os.getenv('BIGQUERY_PROJECT_ID')
table_id = f&quot;{project_id}.datasets.events&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;다음은 아래와 같이 필요한 라이브러리들을 import 합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1740903427366&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import os
from dotenv import load_dotenv
from google.cloud import bigquery&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;다음으로는 BigQueryClient 클래스 파일을 다음과 같이 생성합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1740905236556&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class BigQueryClient:
    &quot;&quot;&quot;Google BigQuery 클라이언트

    BigQuery API를 사용하여 쿼리를 실행하고 결과를 반환하는 클라이언트 클래스입니다.
    &quot;&quot;&quot;

    def __init__(self):
        try:
            # Colab에서 인증된 계정의 기본 자격 증명 가져오기
            credentials, project_id = google.auth.default()

            # BigQuery 클라이언트 초기화
            self.client = bigquery.Client(
                credentials=credentials,
                project=os.getenv('BIGQUERY_PROJECT_ID')
            )
            print(f&quot;BigQuery 클라이언트 초기화 성공&quot;)

        except Exception as e:
            print(f&quot;BigQuery 클라이언트 초기화 실패: {str(e)}&quot;)
            raise

    async def execute_query(self, sql: str) -&amp;gt; Dict[str, Any]:
        &quot;&quot;&quot;SQL 쿼리를 실행하고 결과를 반환합니다.

        Args:
            sql (str): 실행할 SQL 쿼리문.

        Returns:
            Dict[str, Any]: 쿼리 실행 결과를 포함하는 딕셔너리

        Raises:
            Exception: 쿼리 실행 중 오류 발생시
        &quot;&quot;&quot;
        try:
            print(f&quot;Executing query : {sql}&quot;)

            # 쿼리 작업 실행
            query_job = self.client.query(sql)

            # 결과 대기 및 가져오기
            results = query_job.result()

            # 결과를 딕셔너리 리스트로 변환
            rows = [dict(row.items()) for row in results]

            response = {
                &quot;results&quot;: rows,
                &quot;total_rows&quot;: results.total_rows,
                &quot;job_id&quot;: query_job.job_id
            }

            print(f&quot;Query executed successfully. Total rows: {results.total_rows}&quot;)
            return response

        except Exception as e:
            error_msg = f&quot;BigQuery 쿼리 실행 실패: {str(e)}&quot;
            print(error_msg)
            raise Exception(error_msg)
            

# ✅ Colab 환경에서 사용하기 위해 Google 계정 인증 먼저 실행
from google.colab import auth
auth.authenticate_user()

# BigQueryClient 인스턴스 생성
bigquery_client = BigQueryClient()&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;colab에서는 Google의 SSO (Single Sign On)로 인해 colab의 계정 인증을 사용하시면 BigQuery 인증까지 가능합니다. 만약 Colab이 아니라 로컬 PC에서 연동할 때는 별도로 auth 라이브러리를 사용해주셔야 합니다. 로컬 PC에서는 다음과 같이 연동하실 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1740906474227&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;pip install google-cloud-bigquery&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1740906574772&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from google.cloud import bigquery
from google.oauth2 import service_account

from app.core.settings import settings
from app.utils.logger import make_logger

logger = make_logger(__name__)

...

class BigQueryClient:
    def __init__(self):
        try:
            # 서비스 계정 키 파일 경로로부터 인증 정보 생성
            credentials = service_account.Credentials.from_service_account_file(
                settings.GOOGLE_APPLICATION_CREDENTIALS
            )

            # BigQuery 클라이언트 초기화
            self.client = bigquery.Client(
                credentials=credentials,
                project=settings.BIGQUERY_PROJECT_ID
            )
            logger.info(&quot;BigQuery 클라이언트 초기화 성공&quot;)

        except Exception as e:
            logger.error(f&quot;BigQuery 클라이언트 초기화 실패: {str(e)}&quot;)
            raise&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;주의하실 점은 GOOGLE_APPLICATION_CREDENTIALS 환경변수가 추가되었는데요. 이 환경변수에는 이전에 서비스 계정을 만들면서 발급받은 JSON 키 파일이 저장된 경로를 입력해주시면 됩니다. 위 예시 코드에서는 pydantic_settings 모듈을 사용했는데 해당 부분은 os.getenv('GOOGLE_APPLICATION_CREDENTIALS')를 사용해 주셔도 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음은 빅쿼리가 실제로 실행되는지 아래와 같이 테스트해보실 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1740907789867&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;query = f&quot;&quot;&quot;
SELECT
    DATE(event_timestamp) AS donation_date,
    COUNT(*) AS donation_count
FROM {table_id}
WHERE event_name = 'donation'
GROUP BY donation_date
ORDER BY donation_date;
&quot;&quot;&quot;

result = await bigquery_client.execute_query(query)
print(result)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정상적으로 수행된다면 다음과 같이 결과값이 조회됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;22 (1).png&quot; data-origin-width=&quot;1386&quot; data-origin-height=&quot;732&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/0UhNC/btsMyBNJej8/sv14DGSHKAu4qPKxdjyHN1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/0UhNC/btsMyBNJej8/sv14DGSHKAu4qPKxdjyHN1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/0UhNC/btsMyBNJej8/sv14DGSHKAu4qPKxdjyHN1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F0UhNC%2FbtsMyBNJej8%2Fsv14DGSHKAu4qPKxdjyHN1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1386&quot; height=&quot;732&quot; data-filename=&quot;22 (1).png&quot; data-origin-width=&quot;1386&quot; data-origin-height=&quot;732&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style7&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 id=&quot;3&quot; data-ke-size=&quot;size26&quot;&gt;  하이퍼클로바X 연동하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음은 하이퍼클로바X 연동방법에 대해 가이드 드리도록 하겠습니다. 하이퍼클로바X의 API 키는 클로바 스튜디오에서 발급받을 수 있는데요. 2025년 1월 부로 API 연동방식의 변화가 생겼습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1910&quot; data-origin-height=&quot;1122&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/K14wc/btsMyClyL8B/qWVRk28k9vf9iyR0KjkPf1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/K14wc/btsMyClyL8B/qWVRk28k9vf9iyR0KjkPf1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/K14wc/btsMyClyL8B/qWVRk28k9vf9iyR0KjkPf1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FK14wc%2FbtsMyClyL8B%2FqWVRk28k9vf9iyR0KjkPf1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1910&quot; height=&quot;1122&quot; data-origin-width=&quot;1910&quot; data-origin-height=&quot;1122&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존에는 클로바 스튜디오 플레이그라운드에 접속하여 테스트 앱을 발행한 후 API 키를 발급받았다면 이제는 Bearer 인증 방식으로 조금 더 안전하게 관리가 가능해졌고 불 필요하게 API KEY를 두 개씩이나 입력할 필요가 없어졌습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bcNlPN/btsMy4IHxpN/UY49EFfzuhOkPKBtgZPg10/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bcNlPN/btsMy4IHxpN/UY49EFfzuhOkPKBtgZPg10/img.png&quot; data-origin-width=&quot;1896&quot; data-origin-height=&quot;981&quot; data-is-animation=&quot;false&quot; style=&quot;width: 49.1464%; margin-right: 10px;&quot; data-widthpercent=&quot;49.72&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bcNlPN/btsMy4IHxpN/UY49EFfzuhOkPKBtgZPg10/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbcNlPN%2FbtsMy4IHxpN%2FUY49EFfzuhOkPKBtgZPg10%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/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;981&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/obmib/btsMAkqxML7/efsmzlGBFu1NHGqFQV2TC1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/obmib/btsMAkqxML7/efsmzlGBFu1NHGqFQV2TC1/img.png&quot; data-origin-width=&quot;1917&quot; data-origin-height=&quot;981&quot; data-is-animation=&quot;false&quot; style=&quot;width: 49.6908%;&quot; data-widthpercent=&quot;50.28&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/obmib/btsMAkqxML7/efsmzlGBFu1NHGqFQV2TC1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fobmib%2FbtsMAkqxML7%2FefsmzlGBFu1NHGqFQV2TC1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/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;981&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물론 기존에 쓰고 있던 방식도 아직까지는 지원이 되고 있습니다. 언제 중단될지 모르는 상황이므로 안전하게 최신 방식으로 변경하시는 게 좋습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러면 우선, 먼저 새로 변경된 방식으로 API KEY를 발급받아보도록 하겠습니다. 클로바 스튜디오에 접속하여 오른쪽 상단의 계정 메뉴에서 API 키를 선택합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1910&quot; data-origin-height=&quot;985&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cvZKi7/btsMyt98i9R/9j8ZKXGUHnkqzDBnvEXF5K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cvZKi7/btsMyt98i9R/9j8ZKXGUHnkqzDBnvEXF5K/img.png&quot; data-alt=&quot;clovastudio.ncloud.com&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cvZKi7/btsMyt98i9R/9j8ZKXGUHnkqzDBnvEXF5K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcvZKi7%2FbtsMyt98i9R%2F9j8ZKXGUHnkqzDBnvEXF5K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1910&quot; height=&quot;985&quot; data-origin-width=&quot;1910&quot; data-origin-height=&quot;985&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;clovastudio.ncloud.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;p data-ke-size=&quot;size16&quot;&gt;오른쪽 상단의 [테스트 API 키 발급] 버튼을 클릭한 후 API 키를 발급하실 수 있습니다. 발급받은 API키는 &lt;span style=&quot;background-color: #ffffff; color: #212121; text-align: start;&quot;&gt;향후 재확인이 불가능하다고 하니 &lt;span style=&quot;background-color: #ffffff; color: #212121; text-align: start;&quot;&gt;안전한 곳에 저장해 놓도록 합니다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cAHOTj/btsMBildMTL/2lL8q47ASzHqFon6ulxN3k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cAHOTj/btsMBildMTL/2lL8q47ASzHqFon6ulxN3k/img.png&quot; data-origin-width=&quot;1916&quot; data-origin-height=&quot;984&quot; data-is-animation=&quot;false&quot; style=&quot;width: 49.573%; margin-right: 10px;&quot; data-widthpercent=&quot;50.16&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cAHOTj/btsMBildMTL/2lL8q47ASzHqFon6ulxN3k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcAHOTj%2FbtsMBildMTL%2F2lL8q47ASzHqFon6ulxN3k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/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;984&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cc8X9Y/btsMzRoMHqD/nuMjrc9l5hMhQes0ifFIu0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cc8X9Y/btsMzRoMHqD/nuMjrc9l5hMhQes0ifFIu0/img.png&quot; data-origin-width=&quot;1906&quot; data-origin-height=&quot;985&quot; data-is-animation=&quot;false&quot; style=&quot;width: 49.2642%;&quot; data-widthpercent=&quot;49.84&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cc8X9Y/btsMzRoMHqD/nuMjrc9l5hMhQes0ifFIu0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcc8X9Y%2FbtsMzRoMHqD%2FnuMjrc9l5hMhQes0ifFIu0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/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;985&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;CLOVA Studio API 발급 방법&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;발급 후에는 다음과 같이 API 발급 이력이 남게 됩니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1898&quot; data-origin-height=&quot;642&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cNDeef/btsMBh0VNki/cV9PiOJqJxM9TAmm4iPJ00/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cNDeef/btsMBh0VNki/cV9PiOJqJxM9TAmm4iPJ00/img.png&quot; data-alt=&quot;API키 발급 이력 조회&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cNDeef/btsMBh0VNki/cV9PiOJqJxM9TAmm4iPJ00/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcNDeef%2FbtsMBh0VNki%2FcV9PiOJqJxM9TAmm4iPJ00%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/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;642&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1898&quot; data-origin-height=&quot;642&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;API키 발급 이력 조회&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;새로 발급받은 키는 다시 .env 파일에 다음과 같이 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1741102135575&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;NCP_CLOVASTUDIO_API_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;pre id=&quot;code_1741102247921&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;load_dotenv()&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;다음은 랭체인을 활용하여 하이퍼클로바 X를 호출할 수 있도록 코드를 구현해보도록 하겠습니다. 먼저 필요한 라이브러리를 install 합니다. 참고로 langchain-community 0.3.15 버전부터 새로운 발급방식으로 발행한 키를 적용하실 수 있습니다. 따라서 0.3.15이상 버전을 설치합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1741101946334&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# colab에서는 pip앞에 느낌표(!)를 붙여서 모듈을 설치합니다.
!pip install langchain-core==0.3.37
!pip install langchain-community==0.3.18&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;랭체인에서 하이퍼클로바X를 호출할 때는 다음과 같이 간단하게 사용하실 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1378&quot; data-origin-height=&quot;281&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bcKYcV/btsMCs2uNW5/ASaEpMX13OBKBDPaSN9G81/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bcKYcV/btsMCs2uNW5/ASaEpMX13OBKBDPaSN9G81/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bcKYcV/btsMCs2uNW5/ASaEpMX13OBKBDPaSN9G81/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbcKYcV%2FbtsMCs2uNW5%2FASaEpMX13OBKBDPaSN9G81%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/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;281&quot; data-origin-width=&quot;1378&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;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style7&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 id=&quot;4&quot; data-ke-size=&quot;size26&quot;&gt;  Text-to-SQL(NL2SQL) 프롬프트 작성하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 BigQuery와 하이퍼클로바X 준비를 모두 마쳤으니 실제로 Text-to-SQL 구동을 위한 프롬프트를 작성해 보도록 하겠습니다. 먼저, Text-to-SQL이라는 것은 자연어를 SQL로 변환하여 SQL 지식 없이도 원하는 데이터를 출력해서 사용하는 기법을 말하는데요. 이것이 가능하려면 다음과 같은 템플릿 구조로 프롬프트를 작성해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1740918118505&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;prompt = &quot;&quot;&quot;
다음은 데이터베이스 스키마 정보입니다:
{schema}

위 스키마에 따라 SQL쿼리를 작성해주세요. 주의:
{filter}

다음은 몇 가지 예시 SQL 쿼리입니다.
{example}

요청 : {query}
SQL 쿼리:
&quot;&quot;&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 프롬프트 템플릿에서 각 변수들이 의미하는 것은 다음과 같습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;table id=&quot;2fc1e8f5-dafb-4687-8a8e-89d250d39a5c&quot; style=&quot;border-collapse: collapse; width: 100%; height: 99px;&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: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;템플릿 변수명&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;설명&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;0d84745d-187a-4c81-9d8c-447916063174&quot; style=&quot;height: 20px;&quot;&gt;
&lt;td id=&quot;&amp;gt;zr?&quot; style=&quot;height: 20px;&quot;&gt;schema&lt;/td&gt;
&lt;td id=&quot;noMQ&quot; style=&quot;height: 20px;&quot;&gt;데이터베이스 스키마 설명&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;55001fd0-6301-4952-8571-02ed3aab17fc&quot; style=&quot;height: 20px;&quot;&gt;
&lt;td id=&quot;&amp;gt;zr?&quot; style=&quot;height: 20px;&quot;&gt;filter&lt;/td&gt;
&lt;td id=&quot;noMQ&quot; style=&quot;height: 20px;&quot;&gt;BigQuery 사용시 주의사항, 제한사항&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;edcf501e-28b1-45dd-b883-d0f24db6ca1e&quot; style=&quot;height: 20px;&quot;&gt;
&lt;td id=&quot;&amp;gt;zr?&quot; style=&quot;height: 20px;&quot;&gt;example&lt;/td&gt;
&lt;td id=&quot;noMQ&quot; style=&quot;height: 20px;&quot;&gt;AI가 답변해줬으면 하는 예시 쿼리 (few-shot)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr id=&quot;128fff7b-4591-4021-83ee-f15b68e35d9b&quot; style=&quot;height: 20px;&quot;&gt;
&lt;td id=&quot;&amp;gt;zr?&quot; style=&quot;height: 20px;&quot;&gt;query&lt;/td&gt;
&lt;td id=&quot;noMQ&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;반드시 위의 템플릿 구조를 따를 필요는 없지만 통상적으로 필요한 정보들이 위와 같은 형태라고 보시면 좋을 것 같습니다. 따라서 위의 템플릿 구조를 바탕으로 코드를 구현하면 다음과 같은 형태가 나타납니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1740918386938&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from langchain_core.runnables import Runnable
from langchain_core.prompts import PromptTemplate
from langchain_core.output_parsers import StrOutputParser

def sql_agent() -&amp;gt; Runnable:
    &quot;&quot;&quot;SQL 생성을 위한 LangChain 체인 생성&quot;&quot;&quot;

    sql_prompt = &quot;&quot;&quot;
    당신은 비영리단체에 속한 수준급의 BigQuery 및 SQL 전문가 겸 데이터 분석가 입니다.
    아래 스키마를 참고하여 사용자의 질문에 대해 적절한 SQL문을 생성해주세요.
    당신이 사용할 테이블명은 {table_id} 입니다.

    ** 스키마 **
    | 필드명 | 데이터 유형 | 설명 |
    | ------ | ----------- | ---- |
    | event_name | STRING | 이벤트 이름 (예: page_view, donation) |
    | event_timestamp | TIMESTAMP | 이벤트 발생 시간 |
    | user_id | STRING | 사용자 고유 ID |
    | session_id | STRING | 세션 ID |
    | event_params | STRING | 추가적인 이벤트 정보 (JSON 형식) |

    사용자 질문 : {input}

    ** 주의사항 **
    - 답변은 SQL 쿼리문으로만 대답합니다.
    - 마크다운 형식으로 답변하지 마세요.
    &quot;&quot;&quot;

    llm = ChatClovaX()
    sql_chain = (lambda x: {&quot;input&quot;: x, &quot;table_id&quot;: f&quot;{table_id}&quot;}) | PromptTemplate.from_template(sql_prompt) | llm | StrOutputParser()

    return sql_chain

def report_agent() -&amp;gt; Runnable:
    &quot;&quot;&quot;보고서 생성을 위한 LangChain 체인 생성&quot;&quot;&quot;

    report_prompt = &quot;&quot;&quot;
    사용자 질문 : {input}
    반환된 SQL : {sql}
    쿼리 실행 결과 : {result}

    위 데이터를 바탕으로 보고서를 작성하세요.
    &quot;&quot;&quot;

    llm = ChatClovaX()
    report_chain = PromptTemplate.from_template(report_prompt) | llm | StrOutputParser()

    return report_chain

async def execute_full_chain(question: str) -&amp;gt; str:
    &quot;&quot;&quot;SQL 생성부터 보고서 생성까지 전체 실행&quot;&quot;&quot;

    # SQL 생성
    sql = await sql_agent().ainvoke(question)

    # 쿼리 실행
    sql_result = await bigquery_client.execute_query(sql)
    print(sql_result)
    result = {
        &quot;input&quot;:question,
        &quot;sql&quot;:sql,
        &quot;result&quot;: str(sql_result.get(&quot;results&quot;, []))
    }

    # 보고서 생성
    report = await report_agent().ainvoke(result)

    return report&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;위 코드는 사용자 질문이 들어오면 쿼리로 반환하여 BigQuery에서 데이터를 조회해 오고 분석하여 보고서 생성까지 이어지는 간단한 Text-to-SQL 에이전트입니다. 이 코드를 토대로 아래와 같이 실제로 테스트해보면 다음과 같은 결과값이 나타나는 것을 확인하실 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;제목 없음2 (1).png&quot; data-origin-width=&quot;1380&quot; data-origin-height=&quot;540&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bGg3TR/btsMylK7PZ1/twa54fJZjjopJsyR3uD1EK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bGg3TR/btsMylK7PZ1/twa54fJZjjopJsyR3uD1EK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bGg3TR/btsMylK7PZ1/twa54fJZjjopJsyR3uD1EK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbGg3TR%2FbtsMylK7PZ1%2Ftwa54fJZjjopJsyR3uD1EK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/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;540&quot; data-filename=&quot;제목 없음2 (1).png&quot; data-origin-width=&quot;1380&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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style7&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 id=&quot;5&quot; style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;  정리하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금까지 간단하게 BigQuery와 하이퍼클로바X를 활용하여 Text-to-SQL 구현하는 방법에 대해 알아보았는데요. 쓰다 보니 조금 긴 글이 되었지만, 최대한 핸즈온하면서 따라 해볼 수 있도록 자세하게 작성해 보았습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실, 조금 더 복잡한 테이블 구조이면 테이블을 나눠야 할 수도 있고 그에 따른 테이블 분류 및 서치 하는 과정도 필요합니다. 이전 포스팅에서 작성하였지만, 이 때는 Vector Database를 활용하여 적절한 테이블과 스키마, few-shot 예제들을 선정해 오는 것이 중요합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1740919399915&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;Text-to-SQL을 위한 랭체인 Vector DBLess 환경 구축하기(ClovaXEmbeddings 활용)&quot; data-og-description=&quot;AI를 활용하여 자연어를 SQL로 변환하는 기술을 Text-to-SQL 또는 NL2SQL이라고 부릅니다. 이 기술을 적용하기 위한 프롬프트로는 일반적으로 테이블 스키마 정보, 쿼리 생성 시 주의사항, 예시 등이 &quot; data-og-host=&quot;chucoding.tistory.com&quot; data-og-source-url=&quot;https://chucoding.tistory.com/153&quot; data-og-url=&quot;https://chucoding.tistory.com/153&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/ghRfq/hyYju9F0Zc/yZ8AV1BHLYhjmhtLaOUAcK/img.png?width=800&amp;amp;height=596&amp;amp;face=0_0_800_596,https://scrap.kakaocdn.net/dn/4H46g/hyYjuvLvG4/LGULVbduZjnm6kKGU2TZU0/img.png?width=800&amp;amp;height=596&amp;amp;face=0_0_800_596,https://scrap.kakaocdn.net/dn/bdJX1C/hyYmKjnOZ2/3K7HTFHNP2mnuITTVd0fO1/img.png?width=1910&amp;amp;height=941&amp;amp;face=0_0_1910_941&quot;&gt;&lt;a href=&quot;https://chucoding.tistory.com/153&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://chucoding.tistory.com/153&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/ghRfq/hyYju9F0Zc/yZ8AV1BHLYhjmhtLaOUAcK/img.png?width=800&amp;amp;height=596&amp;amp;face=0_0_800_596,https://scrap.kakaocdn.net/dn/4H46g/hyYjuvLvG4/LGULVbduZjnm6kKGU2TZU0/img.png?width=800&amp;amp;height=596&amp;amp;face=0_0_800_596,https://scrap.kakaocdn.net/dn/bdJX1C/hyYmKjnOZ2/3K7HTFHNP2mnuITTVd0fO1/img.png?width=1910&amp;amp;height=941&amp;amp;face=0_0_1910_941');&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;Text-to-SQL을 위한 랭체인 Vector DBLess 환경 구축하기(ClovaXEmbeddings 활용)&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;AI를 활용하여 자연어를 SQL로 변환하는 기술을 Text-to-SQL 또는 NL2SQL이라고 부릅니다. 이 기술을 적용하기 위한 프롬프트로는 일반적으로 테이블 스키마 정보, 쿼리 생성 시 주의사항, 예시 등이&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;chucoding.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;Text-to-SQL 프롬프트 엔지니어링에는 여러 가지 기법들이 있겠지만, 가장 중요한 것은 결국 스키마와 설명, 예제들을 어떻게 작성하느냐에 따라 답변 품질이 달라지기 때문에 보유하고 있는 테이블의 상황에 맞게 적절한 프롬프트를 작성하는 것이 중요합니다.&lt;/p&gt;</description>
      <category>AI/HyperCLOVAX</category>
      <author>외계공룡</author>
      <guid isPermaLink="true">https://chucoding.tistory.com/156</guid>
      <comments>https://chucoding.tistory.com/156#entry156comment</comments>
      <pubDate>Sun, 2 Mar 2025 21:55:38 +0900</pubDate>
    </item>
  </channel>
</rss>