<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>rhfksl</title>
    <link>https://elly1108.tistory.com/</link>
    <description></description>
    <language>ko</language>
    <pubDate>Fri, 7 Aug 2026 19:32:29 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>ranygo12</managingEditor>
    <item>
      <title>  개인정보보호법</title>
      <link>https://elly1108.tistory.com/25</link>
      <description>&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;blockquote data-ke-style=&quot;style2&quot;&gt;한 줄 요약: 개인정보 유출 사고는 한 번의 실수로 기업 신뢰와 개인의 권리를 동시에 무너뜨릴 수 있어, 정확한 법적 이해가 무엇보다 중요합니다.&lt;/blockquote&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  개인정보보호법의 등장 배경&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개인정보보호법은 정보통신 기술의 발달로 개인정보 처리가 보편화되면서, 이를 체계적으로 보호하기 위해 2011년 제정되었습니다. 이전에는 공공기관과 정보통신서비스 분야에 각각 다른 법률이 적용되어 사각지대가 존재했는데, 이 법을 통해 공공&amp;middot;민간을 포괄하는 통합적 보호 체계가 마련되었습니다. 이후 데이터 3법 개정(2020년)을 거치며 가명정보 개념이 도입되고, 개인정보보호위원회가 중앙행정기관으로 격상되는 등 지속적으로 보완되어 왔습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  핵심 개념 정리&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;개인정보의 정의&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개인정보보호법상 개인정보란&lt;b&gt; 살아있는 개인&lt;/b&gt;에 관한 정보로서, 성명&amp;middot;주민등록번호&amp;middot;영상 등을 통해 개인을 알아볼 수 있는 정보를 말합니다. 여기에는 해당 정보만으로 식별이 어렵더라도 다른 &lt;b&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;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;주민등록번호와 고유식별정보의 차이&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 둘은 실무에서 자주 혼동되는 개념입니다. 고유식별정보는 개인정보보호법 시행령에서 정한 항목으로, 주민등록번호&amp;middot;여권번호&amp;middot;운전면허번호&amp;middot;외국인등록번호 네 가지를 의미합니다. 즉 주민등록번호는 고유식별정보에 포함되는 하위 항목 중 하나입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처리 근거 측면에서도 차이가 있습니다. 일반 고유식별정보는 정보주체의 별도 동의가 있거나 법령에 근거가 있으면 처리할 수 있는 반면, 주민등록번호는 동의만으로는 처리할 수 없고 법률&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;암호화 기준에서도 미묘한 차이가 존재합니다. 개인정보의 안전성 확보조치 기준에 따르면 고유식별정보와 비밀번호, 바이오정보는 원칙적으로 안전한 알고리즘으로 암호화하여 저장해야 합니다.개인을 고유하게 구별하기 위해 부여된 식별정보로서, 주민등록번호, 여권번호, 운전면허의 면허번호, 외국인등록번호가 이에 해당합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;다만 내부망과 외부망(인터넷 구간) 사이에는 적용 기준에 차이가 있습니다. 외부망에 저장하거나 인터넷 구간을 통해 송수신되는 고유식별정보는 예외 없이 암호화 대상이지만, 내부망 내에서 송, 수신되는 고유식별정보는 업무상 필요할 경우 암호화 대상에서 제외할 수 있다는 점에서 차이가 있습니다. 단, 이 내부망 예외도 모든 기관에 무조건 적용되는 것은 아니며, 개인정보 영향평가의 대상이 되는 공공기관의 경우 해당 영향평가 결과나 암호화 미적용 시 위험도 분석 결과에 따라 적용 여부와 범위를 별도로 정해야 한다는 제한이 있습니다. 반면 비밀번호와 바이오정보는 내부망 여부와 관계없이 반드시 암호화하여야 합니다.&lt;span data-state=&quot;closed&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;⚠️&lt;b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;주의해야 할 점&lt;/b&gt;&lt;/p&gt;
&lt;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;주민등록번호 처리 근거를 단순히 &quot;이용자가 동의했으니 괜찮다&quot;고 판단하면 위법 소지가 있으므로, 법령상 근거를 별도로 검토하는 것이 안전합니다.&lt;/li&gt;
&lt;li&gt;내부망 암호화 예외를 적용할 때는 단순히 &quot;내부망이니 괜찮다&quot;고 판단하지 않고 영향평가나 위험도 분석 결과를 함께 검토하는 것이 바람직합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;위수탁 계약 시 유의사항&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개인정보 처리업무를 외부에 위탁하는 경우(예: 클라우드 운영, CS 업무, 데이터 분석 등), 위탁자와 수탁자 간 책임 소재를 명확히 하는 것이 중요합니다.&lt;/p&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;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;기술적&amp;middot;관리적 보호조치에 관한 사항&lt;/li&gt;
&lt;li&gt;재위탁 제한에 관한 사항&lt;/li&gt;
&lt;li&gt;수탁자에 대한 관리&amp;middot;감독 사항&lt;/li&gt;
&lt;li&gt;손해배상 책임에 관한 사항&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;⚠️ 특히&lt;b&gt; 주의할 점&lt;/b&gt;은 수탁자가 위탁받은 업무 범위를 벗어나 개인정보를 처리하거나 유출 사고를 일으킨 경우에도, 위탁자가 관리&amp;middot;감독 책임에서 완전히 자유로워지지 않는다는 부분입니다. 법적으로는 수탁자의 업무 처리 시 발생한 손해에 대해 위탁자도 사용자 책임 법리에 따라 함께 책임을 질 수 있다는 점을 고려할 필요가 있습니다.&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;개인정보 유출 사고, 신고와 통지 절차 정리&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개인정보보호법상 유출 사고 대응은 &quot;통지&quot;와 &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;&lt;b&gt;정보주체에 대한 통지 의무&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개인정보처리자는 개인정보가 유출등이 되었음을 알게 되었을 때에는 지체 없이 서면, 전자우편, 팩스, 전화, 문자전송 등의 방법으로 72시간 이내에 해당 정보주체에게 알려야 합니다. 인터넷 홈페이지가 없는 경우에는 사업장 등 보기 쉬운 장소에 30일 이상 게시하는 방법으로 통지할 수 있습니다.&lt;span data-state=&quot;closed&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;통지해야 할 항목은 다음과 같습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;유출된 개인정보의 항목&lt;/li&gt;
&lt;li&gt;유출 시점과 경위&lt;/li&gt;
&lt;li&gt;분실&amp;middot;도난&amp;middot;유출로 인하여 발생할 수 있는 피해를 최소화하기 위하여 정보주체가 할 수 있는 방법&amp;nbsp;&lt;/li&gt;
&lt;li&gt;개인정보처리자의 대응조치 및 피해 구제절차&amp;nbsp;&lt;/li&gt;
&lt;li&gt;피해가 발생한 경우 신고를 접수할 수 있는 담당부서 및 연락처&lt;span data-state=&quot;closed&quot;&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;⚠️ 이를 위반하면 3천만원 이하의 과태료가 부과됩니다.&amp;nbsp;&lt;/p&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;신고는 모든 유출 사고에 적용되는 것이 아니라, 일정 조건을 충족할 때 발생합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;신고 대상 조건은 다음과 같습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;1천 명 이상의 개인정보가 유출된 경우&amp;nbsp;&lt;/li&gt;
&lt;li&gt;민감정보 또는 고유식별정보가 유출된 경우&lt;span data-state=&quot;closed&quot;&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;개인정보처리시스템 또는 개인정보취급자의 정보기기에 대한 외부로부터의 불법적인 접근에 의해 유출된 경우&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;신고처는 개인정보보호위원회 또는 한국인터넷진흥원(KISA)이며, 72시간 이내 신고하지 않으면 3천만원 이하의 과태료가 부과됩니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  이 글을 정리하며&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;개인정보보호법을 들여다보면, 결국 핵심은 &quot;어디까지가 식별 가능한 정보인가&quot;와 &quot;누가 책임을 지는가&quot;라는 두 질문으로 모인다는 생각이 듭니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 위수탁 구조가 복잡해지는 요즘 환경에서는 계약서 문구 하나가, 그리고 암호화처럼 기술적인 영역에서는 내부망&amp;middot;외부망 구분 하나가 실제 사고 발생 시 책임 소재를 가르는 경우가 많아 실무자 입장에서는 더욱 꼼꼼히 들여다볼 필요가 있어 보입니다.&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;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  참고 자료&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;개인정보보호법 (국가법령정보센터)&lt;/li&gt;
&lt;li&gt;개인정보의 안전성 확보조치 기준, 개인정보보호위원회&lt;/li&gt;
&lt;li&gt;KISA, 개인정보의 암호화 조치 안내서&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Security &amp;amp; Compliance</category>
      <author>ranygo12</author>
      <guid isPermaLink="true">https://elly1108.tistory.com/25</guid>
      <comments>https://elly1108.tistory.com/25#entry25comment</comments>
      <pubDate>Tue, 16 Jun 2026 22:07:41 +0900</pubDate>
    </item>
    <item>
      <title>Amazon Bedrock Agent를 활용한 AI 에이전트 구축</title>
      <link>https://elly1108.tistory.com/24</link>
      <description>&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;Agent = LLM + 도구(API) + 계획 능력. 질문에 &quot;답&quot;하는 것이 아니라 목표를 &quot;달성&quot;합니다.&lt;/b&gt;&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AWS Bedrock에서 Agent를 직접 설계하고 GitHub PR 자동 리뷰 파이프라인을 구축하는 과정을 처음부터 끝까지 다룹니다. 단순히 질문에 답하는 LLM을 넘어, 스스로 판단하고 외부 API를 호출하며 Slack 알림까지 보내는 에이전트를 만들 수 있습니다. 이 글을 읽고 나면 Agent가 어떻게 동작하는지, 왜 단순 LLM 호출과 다른지, 실무에서 어떻게 써야 하는지 감이 잡힐 것입니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  AI Agent란 무엇인가&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;일반 LLM과 Agent의 결정적 차이&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;생성형 AI를 처음 접하면 대부분 이런 경험을 합니다. &quot;오늘 서울 날씨 알려줘&quot;라고 물으면 LLM은 &quot;저는 실시간 정보에 접근할 수 없습니다&quot;라고 정중하게 거절합니다. 학습 데이터 안에만 갇혀 있기 때문입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Agent는 다릅니다. 같은 질문을 받으면 &quot;날씨 API를 호출해야겠다&quot;고 스스로 판단하고, API를 실제로 호출하고, 결과를 받아 &quot;서울은 맑음, 22도입니다&quot;라고 답합니다. 이 차이가 전부입니다. 말만 하는 시스템과, 행동까지 하는 시스템의 차이.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Agent의 핵심 구성 요소를 팀에 비유하면 이렇습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구성 요소 역할 팀 비유&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Planner (LLM)&lt;/td&gt;
&lt;td&gt;전체 계획 수립, 어떤 도구를 쓸지 결정&lt;/td&gt;
&lt;td&gt;팀장 (판단과 지시)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Tool Invoker&lt;/td&gt;
&lt;td&gt;API 호출, 코드 실행 등 실제 도구 작동&lt;/td&gt;
&lt;td&gt;실행자 (손과 발)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Memory&lt;/td&gt;
&lt;td&gt;과거 대화 내용 및 작업 맥락 유지&lt;/td&gt;
&lt;td&gt;메모장 (단기/장기 기억)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Knowledge Base&lt;/td&gt;
&lt;td&gt;학습되지 않은 외부 전문 지식 참조&lt;/td&gt;
&lt;td&gt;사전/백과사전 (외부 지식)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Agent의 내부 동작 흐름&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자가 &quot;PR #123 리뷰하고, 결과를 Slack에 알려줘&quot;라고 요청하면 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;&lt;b&gt;1단계 &amp;mdash; Planner (LLM) 추론:&lt;/b&gt; &quot;사용자가 PR 리뷰와 Slack 알림을 요청했다. 먼저 PR 내용을 가져오고, 리뷰한 후, 결과를 Slack으로 전송해야 한다.&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;&lt;b&gt;2단계 &amp;mdash; Tool Invoker 실행:&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;Step 1: get_github_pr(owner, repo, pr_number=123) &amp;rarr; 코드 가져오기&lt;/li&gt;
&lt;li&gt;Step 2: review_code(code) &amp;rarr; 리뷰 결과 생성&lt;/li&gt;
&lt;li&gt;Step 3: send_slack(message=리뷰결과) &amp;rarr; Slack 전송&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3단계 &amp;mdash; 응답 생성:&lt;/b&gt; &quot;PR #123 리뷰를 완료하고 Slack으로 결과를 전송했습니다.&quot;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 일련의 과정을 ReAct(Reasoning + Acting) 패턴이라고 부릅니다. 생각(Reasoning)과 행동(Acting)을 번갈아 가며 목표를 향해 나아가는 방식으로, 현재 대부분의 상용 Agent 시스템이 이 패턴을 기반으로 동작합니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt; ️ Amazon Bedrock Agent 아키텍처&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;우리가 만들 것: Chandler Agent&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 글에서 구현할 에이전트는 GitHub PR을 자동으로 리뷰하는 Chandler 입니다. 전체 아키텍처는 이렇습니다.&lt;/p&gt;
&lt;pre class=&quot;shell&quot; data-ke-language=&quot;shell&quot;&gt;&lt;code&gt;Chandler Agent
├── Instructions (지침)
│   └── &quot;너는 코드 리뷰 전문가야. 스타일, 버그, 보안을 검사해.&quot;
├── Knowledge Base (지식)
│   ├── PEP8 스타일 가이드
│   ├── OWASP 보안 규칙
│   └── Google Style Guide
└── Action Group (도구)
    ├── get_github_pr       &amp;larr; PR 가져오기
    ├── post_pr_comment     &amp;larr; 댓글 등록
    └── send_slack_message  &amp;larr; Slack 알림&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Bedrock Agent의 핵심 구성 요소&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Bedrock에서 Agent를 구성할 때 알아야 할 5가지 개념이 있습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Instructions&lt;/b&gt; &amp;mdash; Agent의 역할, 행동 규칙, 출력 형식을 정의합니다. Agent의 &quot;업무 매뉴얼&quot;입니다. 구체적일수록 좋습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Knowledge Base &lt;/b&gt;&amp;mdash; RAG(Retrieval Augmented Generation)를 통해 참조할 외부 문서 저장소입니다. Agent에 연결하면 질문에 답할 때 자동으로 KB를 검색합니다. 코드 스타일 가이드, 보안 문서 같은 전문 지식을 여기에 넣어두면 됩니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Action Group&lt;/b&gt; &amp;mdash; 외부 시스템과 통신하기 위한 도구 모음입니다. Lambda 함수와 OpenAPI 스키마로 구성됩니다. Agent의 &quot;손과 발&quot;이 되는 부분입니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Model &lt;/b&gt;&amp;mdash; Agent의 두뇌 역할을 하는 LLM입니다. 이 글에서는 Claude Sonnet 4.6(global.anthropic.claude-sonnet-4-6)을 사용합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Alias&lt;/b&gt; &amp;mdash; 배포 버전을 관리하는 식별자입니다. dev, prod처럼 환경을 분리할 수 있습니다. 실무에서는 반드시 사용하는 것이 좋습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Agent를 만들고 사용하는 흐름은 이렇습니다.&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;# 1. Agent 생성
agent = create_agent(name, instruction, model, role_arn)

# 2. (선택) Knowledge Base 연결
associate_agent_knowledge_base(agent_id, kb_id)

# 3. Prepare (배포 준비 &amp;mdash; 설정 컴파일)
prepare_agent(agent_id)

# 4. Alias 생성 (호출용)
create_agent_alias(agent_id, alias_name)
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt; ️ 기본 Agent 생성 &amp;mdash; Boto3로 처음부터 만들기&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Agent 생성 코드&lt;/h3&gt;
&lt;pre class=&quot;shell&quot; data-ke-language=&quot;shell&quot;&gt;&lt;code&gt;import boto3

# Bedrock Agent 클라이언트 초기화
bedrock_agent = boto3.client('bedrock-agent', region_name='ap-northeast-2')
bedrock_agent_runtime = boto3.client('bedrock-agent-runtime', region_name='ap-northeast-2')

agent_name = &quot;Chandler-Reviewer&quot;
agent_resource_role_arn = &quot;arn:aws:iam::YOUR_ACCOUNT_ID:role/AmazonBedrockAgentServiceRole&quot;

# Instructions 정의
instruction_content = &quot;&quot;&quot;당신은 시니어 개발자이자 코드 리뷰 전문가입니다.

## 역할
- Python, JavaScript, Java 코드 리뷰를 수행합니다.
- 버그, 보안 취약점, 스타일 위반을 찾습니다.

## 행동 규칙
1. 코드를 받으면 반드시 Knowledge Base를 참고하여 검사합니다.
2. 발견된 문제는 심각도(높음/중간/낮음)와 함께 보고합니다.
3. 수정 제안은 구체적인 코드 예시를 포함합니다.

## 출력 형식
  높은 심각도
[라인번호] 문제: 설명
수정 제안: 코드 예시

  중간 심각도
...

  낮은 심각도
...

## 제약사항
- 모르는 내용은 &quot;정보 부족&quot;이라고 답변합니다.
- 보안 취약점은 반드시 보고합니다.&quot;&quot;&quot;

foundation_model = &quot;global.anthropic.claude-sonnet-4-6&quot;

# Agent 생성 또는 업데이트
agent_id = None

try:
    # 기존 Agent 확인
    list_agents_response = bedrock_agent.list_agents(maxResults=100)
    for agent_summary in list_agents_response['agentSummaries']:
        if agent_summary['agentName'] == agent_name:
            agent_id = agent_summary['agentId']
            print(f&quot;기존 Agent 발견: {agent_id}&quot;)
            break

    if agent_id:
        # 기존 Agent 업데이트
        bedrock_agent.update_agent(
            agentId=agent_id,
            agentName=agent_name,
            agentResourceRoleArn=agent_resource_role_arn,
            instruction=instruction_content,
            foundationModel=foundation_model,
            description=&quot;코드 리뷰 및 보안 분석 에이전트&quot;
        )
        print(f&quot;✅ Agent 업데이트 완료: {agent_id}&quot;)
    else:
        # 새 Agent 생성
        response = bedrock_agent.create_agent(
            agentName=agent_name,
            agentResourceRoleArn=agent_resource_role_arn,
            instruction=instruction_content,
            foundationModel=foundation_model,
            description=&quot;코드 리뷰 및 보안 분석 에이전트&quot;
        )
        agent_id = response['agent']['agentId']
        print(f&quot;✅ Agent 생성 완료: {agent_id}&quot;)

except Exception as e:
    print(f&quot;오류 발생: {e}&quot;)
    if &quot;AccessDeniedException&quot; in str(e):
        print(&quot;HINT: IAM 역할에 bedrock:InvokeModel 권한이 있는지 확인하세요.&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;좋은 Instructions를 작성하는 법&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Instructions는 Agent의 업무 매뉴얼입니다. 모호하게 쓰면 Agent도 모호하게 행동합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;항목 나쁜 예 좋은 예&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;역할 명확화&lt;/td&gt;
&lt;td&gt;&quot;코드 리뷰해줘&quot;&lt;/td&gt;
&lt;td&gt;&quot;당신은 10년차 시니어 개발자입니다&quot;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;구체적 작업&lt;/td&gt;
&lt;td&gt;&quot;버그 찾아줘&quot;&lt;/td&gt;
&lt;td&gt;&quot;논리적 오류, 예외 처리 누락, 무한 루프를 찾아주세요&quot;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;출력 형식&lt;/td&gt;
&lt;td&gt;(형식 없음)&lt;/td&gt;
&lt;td&gt;&quot;Markdown 테이블로 정리해주세요&quot;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;참고 자료&lt;/td&gt;
&lt;td&gt;(없음)&lt;/td&gt;
&lt;td&gt;&quot;Knowledge Base의 PEP8 문서를 먼저 검색하세요&quot;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;제약 조건&lt;/td&gt;
&lt;td&gt;(없음)&lt;/td&gt;
&lt;td&gt;&quot;모르면 '모르겠습니다'라고 답변&quot;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Instructions 분량도 중요합니다. 단순 요약기나 번역기라면 200~500 토큰으로도 충분하지만, 코드 리뷰처럼 여러 판단이 필요한 에이전트라면 500~1500 토큰 수준의 상세한 지침이 더 잘 동작합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Knowledge Base 연결&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3장에서 미리 구축해둔 Knowledge Base(코드 스타일 가이드, 보안 문서 등)를 Agent에 연결합니다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;# 이미 생성된 Knowledge Base ID
kb_id = &quot;YOUR_KNOWLEDGE_BASE_ID&quot;

association = bedrock_agent.associate_agent_knowledge_base(
    agentId=agent_id,
    agentVersion=&quot;DRAFT&quot;,
    knowledgeBaseId=kb_id,
    description=&quot;코드 스타일 및 보안 규칙&quot;,
    knowledgeBaseState=&quot;ENABLED&quot;
)
print(f&quot;✅ Knowledge Base 연결 완료: {kb_id}&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;KB를 연결하면 Agent가 질문에 답할 때 자동으로 관련 문서를 검색해 활용합니다. Instructions에 KB 활용 지침을 명시해두면 더욱 일관성 있게 동작합니다.&lt;/p&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;## Knowledge Base 활용 방법
- 코드 스타일 검사 시 반드시 Knowledge Base의 PEP8 문서를 참고하세요.
- 보안 취약점 탐지 시 Knowledge Base의 OWASP 문서를 참고하세요.
- 검색 결과가 없으면 &quot;관련 문서를 찾을 수 없습니다&quot;라고 알려주세요.
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Agent Prepare와 Alias&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설정을 마쳤으면 Prepare를 실행해야 합니다. Prepare는 Instructions, KB, Action Group 설정을 실제 배포 가능한 상태로 컴파일하는 과정입니다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;import time

# Prepare 실행
prepare_response = bedrock_agent.prepare_agent(agentId=agent_id)
print(f&quot;Prepare 요청 완료. 상태: {prepare_response['agentStatus']}&quot;)

# Prepare 완료 대기 (최대 60초)
for i in range(60):
    agent_info = bedrock_agent.get_agent(agentId=agent_id)
    status = agent_info['agent']['agentStatus']
    if status == 'PREPARED':
        print(f&quot;✅ PREPARED 완료 (소요: {i+1}초)&quot;)
        break
    elif status == 'FAILED':
        print(f&quot;❌ Prepare 실패: {agent_info['agent']['failureReasons']}&quot;)
        break
    time.sleep(1)

# Alias 생성 (호출용)
alias_response = bedrock_agent.create_agent_alias(
    agentId=agent_id,
    agentAliasName=&quot;dev&quot;,
    description=&quot;개발 환경용 Alias&quot;
)
alias_id = alias_response['agentAlias']['agentAliasId']
print(f&quot;✅ Alias 생성 완료: {alias_id}&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Alias가 필요한 이유는 세 가지입니다. 개발(dev)과 운영(prod) 환경을 물리적으로 분리해 코드 수정이 실 서비스에 바로 영향을 미치지 않도록 방지할 수 있고, 문제 발생 시 이전 버전을 가리키는 Alias로 즉시 교체해 롤백할 수 있습니다. 두 버전을 각각 다른 Alias로 운영해 A/B 테스트도 할 수 있습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Agent 호출&lt;/h3&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;def invoke_agent(prompt, session_id=&quot;test-session&quot;):
    &quot;&quot;&quot;Agent를 호출하고 스트리밍 응답을 하나의 문자열로 반환&quot;&quot;&quot;
    response = bedrock_agent_runtime.invoke_agent(
        agentId=agent_id,
        agentAliasId=alias_id,
        sessionId=session_id,      # 사용자별 고유 세션 ID
        inputText=prompt,
        enableTrace=False
    )

    # 스트리밍 응답 처리
    full_response = &quot;&quot;
    for event in response['completion']:
        if 'chunk' in event:
            chunk_bytes = event['chunk']['bytes']
            full_response += chunk_bytes.decode('utf-8')

    return full_response

# 코드 리뷰 요청 테스트
test_code = &quot;&quot;&quot;
def divide(a, b):
    return a / b

def get_user(id):
    query = f&quot;SELECT * FROM users WHERE id = {id}&quot;
    return execute(query)
&quot;&quot;&quot;

prompt = f&quot;&quot;&quot;
다음 코드를 리뷰해주세요:

```python
{test_code}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;버그, 보안 취약점, 스타일 문제를 찾아주세요. &quot;&quot;&quot;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;response = invoke_agent(prompt, session_id=&quot;code-review-session&quot;) print(&quot;Agent 응답:&quot;) print(response)&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;`sessionId`는 대화 컨텍스트를 유지하는 핵심입니다. 동일한 세션 ID로 여러 번 호출하면 이전 대화를 기억합니다. &quot;이 함수 봐줘&quot; 이후 &quot;방금 본 코드에서 버그 수정해줘&quot; 같은 멀티턴 대화가 가능한 이유입니다. 기본 세션 만료 시간은 마지막 대화로부터 1시간입니다.

### Trace로 Agent의 추론 과정 들여다보기

`enableTrace=True`를 설정하면 Agent가 어떻게 생각하는지 볼 수 있습니다.

```python
def inspect_agent_thinking(prompt):
    response = bedrock_agent_runtime.invoke_agent(
        agentId=agent_id,
        agentAliasId=alias_id,
        sessionId=&quot;trace-demo&quot;,
        inputText=prompt,
        enableTrace=True   # Trace 활성화
    )

    for event in response['completion']:
        if 'trace' in event:
            trace_info = event['trace']['trace']

            # 전처리 단계 &amp;mdash; 사용자 입력 해석
            if 'preProcessingTrace' in trace_info:
                pre = trace_info['preProcessingTrace']
                print(f&quot;[전처리] {pre.get('rationale', 'N/A')}&quot;)

            # 계획 수립 단계 &amp;mdash; Agent의 생각
            if 'orchestrationTrace' in trace_info:
                orch = trace_info['orchestrationTrace']
                print(f&quot;\n[계획 수립] {orch.get('rationale', 'N/A')}&quot;)

        elif 'chunk' in event:
            print(f&quot;\n[최종 응답]:&quot;)
            print(event['chunk']['bytes'].decode('utf-8'))
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Trace를 통해 Agent가 문제를 어떻게 이해했는지, KB에서 무엇을 검색했는지, 어떤 도구를 호출하려고 하는지, 최종 응답을 어떻게 생성했는지 전 과정을 확인할 수 있습니다. 디버깅할 때 필수입니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  Tool Use &amp;mdash; Agent에 손발을 달아주다&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Tool Use(Function Calling)란&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단순 LLM과 Agent의 가장 큰 차이는 Tool Use입니다. Tool Use가 없으면 Agent는 말만 합니다. Tool Use가 있으면 행동합니다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;❌ Tool Use 없을 때:
사용자: &quot;내 GitHub PR 가져와줘&quot;
Agent: &quot;죄송합니다. 저는 GitHub에 접근할 수 없어요.&quot;

✅ Tool Use 있을 때:
사용자: &quot;내 GitHub PR 가져와줘&quot;
Agent: (내부 추론) &quot;get_github_pr 도구를 써야겠다!&quot;
Agent: Lambda 호출 &amp;rarr; GitHub API &amp;rarr; PR 정보 반환
Agent: &quot;PR #123: Fix login bug (작성자: devkim, 상태: open, 변경 파일: 3개)&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Action Group의 구조&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Bedrock Agent에서 Tool은 Action Group 단위로 관리됩니다. Action Group은 세 가지 요소로 구성됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1. OpenAPI Schema (도구 설명서)&lt;/b&gt; Agent가 이 문서를 읽고 &quot;언제, 어떻게&quot; 도구를 사용할지 결정합니다. description 필드가 핵심입니다. 구체적으로 쓸수록 Agent가 정확한 상황에서 도구를 선택합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2. Lambda Function (실제 실행 코드)&lt;/b&gt; GitHub API 호출, 데이터 처리, 결과 반환 등 실제 로직이 담깁니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3. IAM Role (권한)&lt;/b&gt; Agent가 Lambda를 호출할 수 있도록 허용하는 권한 설정입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;첫 번째 Tool: GitHub PR 가져오기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Tool 스펙:&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;항목 내용&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Tool 이름&lt;/td&gt;
&lt;td&gt;get_github_pr&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;설명&lt;/td&gt;
&lt;td&gt;GitHub Pull Request의 상세 정보를 가져옵니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;파라미터&lt;/td&gt;
&lt;td&gt;owner, repo, pr_number&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;반환값&lt;/td&gt;
&lt;td&gt;PR 제목, 설명, 변경 파일 목록 등&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Agent가 이 Tool을 사용하는 흐름:&lt;/p&gt;
&lt;pre class=&quot;avrasm&quot;&gt;&lt;code&gt;사용자: &quot;my-org/my-repo의 PR #123 가져와줘&quot;

Agent 내부 추론:
  1. &quot;사용자가 PR 정보를 요청했네&quot;
  2. &quot;get_github_pr 도구를 사용해야겠다&quot;
  3. &quot;파라미터: owner='my-org', repo='my-repo', pr_number=123&quot;
  4. &amp;rarr; Lambda 호출!

Lambda: GitHub API 호출 &amp;rarr; PR 정보 가져오기 &amp;rarr; 결과 반환

Agent: &quot;PR #123: Fix login bug (작성자: devkim, 상태: open, 변경 파일: 3개)&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Lambda 함수 구현:&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;python&quot;&gt;&lt;code&gt;import json
import os
import logging
from github import Github, GithubException

logger = logging.getLogger()
logger.setLevel(logging.INFO)

def handler(event, context):
    logger.info(f&quot;Received event: {json.dumps(event)}&quot;)

    try:
        # Bedrock Agent가 전달한 파라미터 추출
        parameters = {p[&quot;name&quot;]: p[&quot;value&quot;] for p in event.get(&quot;parameters&quot;, [])}

        owner = parameters.get(&quot;owner&quot;)
        repo_name = parameters.get(&quot;repo&quot;)
        pr_number = parameters.get(&quot;pr_number&quot;)

        # 필수 파라미터 검증
        if not all([owner, repo_name, pr_number]):
            raise ValueError(&quot;Missing required parameters: owner, repo, pr_number&quot;)

        # GitHub API 호출
        g = Github(os.environ['GITHUB_TOKEN'])
        repo = g.get_repo(f&quot;{owner}/{repo_name}&quot;)
        pr = repo.get_pull(int(pr_number))

        # 응답 생성
        response_body = {
            &quot;title&quot;: pr.title,
            &quot;body&quot;: pr.body or &quot;&quot;,
            &quot;state&quot;: pr.state,
            &quot;author&quot;: pr.user.login,
            &quot;created_at&quot;: pr.created_at.isoformat(),
            &quot;updated_at&quot;: pr.updated_at.isoformat(),
            &quot;changed_files&quot;: pr.changed_files,
            &quot;additions&quot;: pr.additions,
            &quot;deletions&quot;: pr.deletions,
            &quot;diff_url&quot;: pr.diff_url
        }

        # Bedrock Agent가 이해하는 형식으로 반환
        return {
            &quot;messageVersion&quot;: &quot;1.0&quot;,
            &quot;response&quot;: {
                &quot;actionGroup&quot;: event[&quot;actionGroup&quot;],
                &quot;apiPath&quot;: event[&quot;apiPath&quot;],
                &quot;httpMethod&quot;: event[&quot;httpMethod&quot;],
                &quot;httpStatusCode&quot;: 200,
                &quot;responseBody&quot;: {
                    &quot;application/json&quot;: {&quot;body&quot;: response_body}
                }
            }
        }

    except GithubException as e:
        logger.error(f&quot;GitHub API error: {e}&quot;)
        return build_error_response(event, f&quot;GitHub API error: {e.data.get('message', str(e))}&quot;, 404)

    except Exception as e:
        logger.error(f&quot;Unexpected error: {e}&quot;)
        return build_error_response(event, str(e), 500)


def build_error_response(event, error_msg, status_code):
    return {
        &quot;messageVersion&quot;: &quot;1.0&quot;,
        &quot;response&quot;: {
            &quot;actionGroup&quot;: event.get(&quot;actionGroup&quot;),
            &quot;apiPath&quot;: event.get(&quot;apiPath&quot;),
            &quot;httpMethod&quot;: event.get(&quot;httpMethod&quot;),
            &quot;httpStatusCode&quot;: status_code,
            &quot;responseBody&quot;: {
                &quot;application/json&quot;: {&quot;body&quot;: {&quot;error&quot;: error_msg}}
            }
        }
    }
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Lambda 응답은 반드시 messageVersion, response 구조를 따라야 합니다. 이 형식이 맞지 않으면 Agent가 결과를 이해하지 못합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;OpenAPI Schema 작성:&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;{
  &quot;openapi&quot;: &quot;3.0.0&quot;,
  &quot;info&quot;: {
    &quot;title&quot;: &quot;GitHub PR Tools&quot;,
    &quot;version&quot;: &quot;1.0.0&quot;
  },
  &quot;paths&quot;: {
    &quot;/pr&quot;: {
      &quot;get&quot;: {
        &quot;operationId&quot;: &quot;get_github_pr&quot;,
        &quot;summary&quot;: &quot;Get GitHub Pull Request details&quot;,
        &quot;description&quot;: &quot;Retrieves detailed information about a specific GitHub Pull Request. Use this when the user asks about PR details, wants to review a PR, or needs to analyze code changes in a pull request.&quot;,
        &quot;parameters&quot;: [
          {
            &quot;name&quot;: &quot;owner&quot;,
            &quot;in&quot;: &quot;query&quot;,
            &quot;required&quot;: true,
            &quot;schema&quot;: {&quot;type&quot;: &quot;string&quot;},
            &quot;description&quot;: &quot;The GitHub repository owner/organization name (e.g., 'aws-samples', 'facebook')&quot;
          },
          {
            &quot;name&quot;: &quot;repo&quot;,
            &quot;in&quot;: &quot;query&quot;,
            &quot;required&quot;: true,
            &quot;schema&quot;: {&quot;type&quot;: &quot;string&quot;},
            &quot;description&quot;: &quot;The GitHub repository name&quot;
          },
          {
            &quot;name&quot;: &quot;pr_number&quot;,
            &quot;in&quot;: &quot;query&quot;,
            &quot;required&quot;: true,
            &quot;schema&quot;: {&quot;type&quot;: &quot;integer&quot;},
            &quot;description&quot;: &quot;The Pull Request number to retrieve&quot;
          }
        ],
        &quot;responses&quot;: {
          &quot;200&quot;: {
            &quot;description&quot;: &quot;PR details retrieved successfully&quot;,
            &quot;content&quot;: {&quot;application/json&quot;: {&quot;schema&quot;: {&quot;type&quot;: &quot;object&quot;}}}
          }
        }
      }
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;description 필드는 Agent가 읽는 &quot;사용 설명서&quot;입니다. &quot;Use this when the user asks about PR details&quot;처럼 언제 써야 하는지 명확히 적어주면 Agent가 올바른 상황에서 도구를 선택합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Action Group 생성:&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;import json

def create_action_group(agent_id, lambda_arn, api_schema):
    response = bedrock_agent.create_agent_action_group(
        agentId=agent_id,
        agentVersion=&quot;DRAFT&quot;,
        actionGroupName=&quot;GitHubPRTools&quot;,
        actionGroupExecutor={
            &quot;lambda&quot;: lambda_arn  # Lambda 함수 ARN
        },
        apiSchema={
            &quot;payload&quot;: json.dumps(api_schema)  # OpenAPI 스키마
        },
        actionGroupState=&quot;ENABLED&quot;,
        description=&quot;Tools for fetching GitHub Pull Request information&quot;
    )

    action_group_id = response['agentActionGroup']['actionGroupId']
    print(f&quot;✅ Action Group 생성 완료: {action_group_id}&quot;)
    return action_group_id
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;⚠️ Action Group 이름에는 하이픈(-)을 사용하지 않는 것이 좋습니다. GitHubPRTools처럼 카멜케이스나 밑줄을 사용하세요. 또한 Action Group을 추가하거나 변경하면 반드시 Agent를 다시 Prepare해야 합니다. 그리고 Lambda 함수에 Bedrock Agent가 호출할 수 있도록 리소스 기반 정책도 추가해야 합니다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;lambda_client = boto3.client('lambda')

lambda_client.add_permission(
    FunctionName=lambda_function_name,
    StatementId='AllowBedrockAgentInvoke',
    Action='lambda:InvokeFunction',
    Principal='bedrock.amazonaws.com',
    SourceArn=f'arn:aws:bedrock:{region}:{account_id}:agent/{agent_id}'
)
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 단계를 빠뜨리면 Agent가 Lambda를 호출할 수 없습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Token 보안 관리&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;GitHub Personal Access Token은 절대로 코드에 직접 입력하지 않는 것이 좋습니다. AWS Secrets Manager나 Lambda 환경 변수에 저장하는 방법을 권장합니다. Colab에서 실습할 때는 Colab Secrets를 활용하세요.&lt;/p&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;from google.colab import userdata
GITHUB_TOKEN = userdata.get('GITHUB_TOKEN')
&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  Tool 확장 &amp;mdash; PR 댓글, Slack 알림까지&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 Agent가 정보를 가져오는 것을 넘어, 실제로 뭔가를 &quot;하는&quot; 단계입니다. PR에 댓글을 남기고, Slack으로 알림을 보내는 Tool을 추가합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;두 번째 Tool: PR에 댓글 남기기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Tool 스펙:&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;항목 내용&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Tool 이름&lt;/td&gt;
&lt;td&gt;post_pr_comment&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;설명&lt;/td&gt;
&lt;td&gt;GitHub Pull Request에 댓글을 추가합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;파라미터&lt;/td&gt;
&lt;td&gt;owner, repo, pr_number, comment&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Lambda 역할&lt;/td&gt;
&lt;td&gt;GitHub API (POST /issues/{pr_number}/comments) 호출&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;# Lambda 함수: post_pr_comment
def handler(event, context):
    try:
        parameters = {p[&quot;name&quot;]: p[&quot;value&quot;] for p in event.get(&quot;parameters&quot;, [])}

        owner = parameters.get(&quot;owner&quot;)
        repo_name = parameters.get(&quot;repo&quot;)
        pr_number = int(parameters.get(&quot;pr_number&quot;))
        comment = parameters.get(&quot;comment&quot;)

        if not all([owner, repo_name, pr_number, comment]):
            raise ValueError(&quot;Missing required parameters&quot;)

        # GitHub API 호출 &amp;mdash; 댓글 등록
        g = Github(os.environ['GITHUB_TOKEN'])
        repo = g.get_repo(f&quot;{owner}/{repo_name}&quot;)
        pr = repo.get_pull(pr_number)
        pr.create_issue_comment(comment)

        response_body = {
            &quot;success&quot;: True,
            &quot;message&quot;: f&quot;Comment added to PR #{pr_number}&quot;,
            &quot;pr_url&quot;: pr.html_url
        }

        return build_success_response(event, response_body)

    except GithubException as e:
        return build_error_response(event, str(e), 400)
    except Exception as e:
        return build_error_response(event, str(e), 500)
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;GitHub API를 직접 호출할 때는 PyGithub 라이브러리와 requests 라이브러리 두 가지 선택지가 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;방식 장점&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;PyGithub&lt;/td&gt;
&lt;td&gt;코드가 직관적이고 간결, 속도 제한 처리 자동화&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;직접 requests&lt;/td&gt;
&lt;td&gt;외부 라이브러리 없이 가볍게 처리, Lambda 패키지 크기 절감&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PyGithub는 약 2MB, requests는 약 0.5MB입니다. Lambda 제한(250MB)을 감안하면 둘 다 사용 가능하지만, 의존성을 줄이고 싶다면 requests를 선택할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;OpenAPI Schema에 새 경로를 추가합니다.&lt;/p&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;&quot;/pr/comment&quot;: {
  &quot;post&quot;: {
    &quot;operationId&quot;: &quot;post_pr_comment&quot;,
    &quot;summary&quot;: &quot;Add a comment to a Pull Request&quot;,
    &quot;description&quot;: &quot;Adds a comment to a specific GitHub Pull Request. Use this when the user wants to leave feedback, approval, or questions on a PR.&quot;,
    &quot;parameters&quot;: [
      {&quot;name&quot;: &quot;owner&quot;, &quot;in&quot;: &quot;query&quot;, &quot;required&quot;: true, &quot;schema&quot;: {&quot;type&quot;: &quot;string&quot;}, &quot;description&quot;: &quot;Repository owner/organization&quot;},
      {&quot;name&quot;: &quot;repo&quot;, &quot;in&quot;: &quot;query&quot;, &quot;required&quot;: true, &quot;schema&quot;: {&quot;type&quot;: &quot;string&quot;}, &quot;description&quot;: &quot;Repository name&quot;},
      {&quot;name&quot;: &quot;pr_number&quot;, &quot;in&quot;: &quot;query&quot;, &quot;required&quot;: true, &quot;schema&quot;: {&quot;type&quot;: &quot;integer&quot;}, &quot;description&quot;: &quot;Pull Request number&quot;},
      {&quot;name&quot;: &quot;comment&quot;, &quot;in&quot;: &quot;query&quot;, &quot;required&quot;: true, &quot;schema&quot;: {&quot;type&quot;: &quot;string&quot;}, &quot;description&quot;: &quot;The comment text to post&quot;}
    ],
    &quot;responses&quot;: {&quot;200&quot;: {&quot;description&quot;: &quot;Comment added successfully&quot;}}
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;세 번째 Tool: Slack 알림 보내기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Tool 스펙:&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;항목 내용&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Tool 이름&lt;/td&gt;
&lt;td&gt;send_slack_message&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;설명&lt;/td&gt;
&lt;td&gt;Slack 채널로 메시지를 전송합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;파라미터&lt;/td&gt;
&lt;td&gt;channel, message&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Lambda 역할&lt;/td&gt;
&lt;td&gt;Slack Incoming Webhook URL로 POST 요청 발송&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Slack 연동 방법은 두 가지입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;방식 특징&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Incoming Webhook&lt;/td&gt;
&lt;td&gt;설정이 간단, 특정 채널에 고정&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Chat.postMessage API&lt;/td&gt;
&lt;td&gt;유연성이 높음, OAuth 토큰 필요, 채널을 동적으로 지정 가능&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음 시작하는 경우에는 Incoming Webhook이 간단하고 좋습니다.&lt;/p&gt;
&lt;pre class=&quot;cs&quot;&gt;&lt;code&gt;# Lambda 함수: send_slack_message
import requests

def handler(event, context):
    try:
        params = {p[&quot;name&quot;]: p[&quot;value&quot;] for p in event.get(&quot;parameters&quot;, [])}
        channel = params.get(&quot;channel&quot;)
        message = params.get(&quot;message&quot;)

        if not channel or not message:
            raise ValueError(&quot;Missing channel or message&quot;)

        webhook_url = os.environ['SLACK_WEBHOOK_URL']

        # Slack 메시지 전송
        payload = {&quot;text&quot;: message}
        if channel:
            payload[&quot;channel&quot;] = channel

        response = requests.post(webhook_url, json=payload)
        response.raise_for_status()

        result = {&quot;success&quot;: True, &quot;message&quot;: &quot;Slack message sent&quot;}
        return build_success_response(event, result)

    except Exception as e:
        return build_error_response(event, str(e), 500)
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;⚠️ Slack Webhook URL은 비밀번호와 같습니다. 절대 코드에 직접 입력하지 말고, Lambda 환경 변수나 AWS Secrets Manager에 저장하세요.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;세 가지 Tool을 하나의 Action Group으로 통합&lt;/h3&gt;
&lt;pre class=&quot;nimrod&quot;&gt;&lt;code&gt;# 최종 통합 API 스키마 &amp;mdash; 세 가지 Tool 모두 포함
final_api_schema = {
    &quot;openapi&quot;: &quot;3.0.0&quot;,
    &quot;info&quot;: {&quot;title&quot;: &quot;GitHub PR Tools&quot;, &quot;version&quot;: &quot;1.0.0&quot;},
    &quot;paths&quot;: {
        &quot;/pr&quot;: {&quot;get&quot;: {...}},            # get_github_pr
        &quot;/pr/comment&quot;: {&quot;post&quot;: {...}},   # post_pr_comment
        &quot;/slack/send&quot;: {&quot;post&quot;: {...}}    # send_slack_message
    }
}

# Action Group 업데이트
bedrock_agent.update_agent_action_group(
    agentId=agent_id,
    agentVersion=&quot;DRAFT&quot;,
    actionGroupId=action_group_id,
    actionGroupName=&quot;GitHubPRTools&quot;,
    actionGroupExecutor={&quot;lambda&quot;: lambda_arn},
    apiSchema={&quot;payload&quot;: json.dumps(final_api_schema)},
    actionGroupState=&quot;ENABLED&quot;
)

# 변경 후 반드시 Prepare 재실행
bedrock_agent.prepare_agent(agentId=agent_id)
print(&quot;✅ Agent Prepare 완료 &amp;mdash; 세 가지 Tool 사용 가능&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;복합 시나리오 테스트: 한 번의 요청으로 세 가지 작업 자동 실행&lt;/h3&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;def test_multi_tool_scenario(agent_id, alias_id):
    prompt = &quot;&quot;&quot;
    다음 작업을 순서대로 수행해주세요:
    1. my-org/my-repo의 PR #123 정보를 가져오고
    2. 코드 리뷰 결과를 '코드가 깔끔합니다! 수고하셨어요' 댓글로 남겨주세요
    3. 완료되면 #code-review 채널에 'PR #123 리뷰 완료' 메시지를 보내주세요
    &quot;&quot;&quot;

    response = bedrock_agent_runtime.invoke_agent(
        agentId=agent_id,
        agentAliasId=alias_id,
        sessionId=&quot;multi-tool-session&quot;,
        inputText=prompt,
        enableTrace=True
    )

    for event in response['completion']:
        if 'chunk' in event:
            print(event['chunk']['bytes'].decode('utf-8'))
        elif 'trace' in event:
            trace = event['trace']['trace']
            if 'orchestrationTrace' in trace:
                orch = trace['orchestrationTrace']
                rationale = orch.get('rationale', '')
                if rationale:
                    print(f&quot;\n  Agent 생각: {rationale[:200]}...&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Agent는 이 요청을 받아 스스로 3단계로 계획을 세웁니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;get_github_pr 실행 &amp;rarr; PR 정보 획득&lt;/li&gt;
&lt;li&gt;코드를 내부적으로 분석한 뒤 post_pr_comment 실행&lt;/li&gt;
&lt;li&gt;send_slack_message 실행&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최종 응답: &quot;모든 작업이 완료되었습니다.&quot;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;순차 호출 vs 병렬 호출&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여러 Tool을 조합할 때 두 가지 전략이 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;순차 호출(Sequential)&lt;/b&gt;: 앞선 도구의 결과가 뒤따르는 도구의 입력이 되는 경우. PR 내용을 먼저 가져온 뒤, 그 내용을 분석해서 댓글을 다는 것이 대표적입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;병렬 호출(Parallel)&lt;/b&gt;: 서로 독립적인 도구를 동시에 호출하는 경우. 리뷰가 끝난 시점에 GitHub 댓글 등록과 Slack 알림을 동시에 실행하면 전체 지연 시간을 줄일 수 있습니다. Bedrock Agent는 기본적으로 순차 호출하지만, 프롬프팅을 통해 병렬 호출도 유도할 수 있습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Tool 호출 횟수 제한: max_iterations&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Agent가 무한 루프에 빠지거나 불필요한 비용이 발생하는 것을 막기 위한 설정입니다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;bedrock_agent.update_agent(
    agentId=agent_id,
    ...
    maxIterations=5  # 최대 5번의 Tool 호출 허용
)
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용 사례 추천 max_iterations&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;단순 정보 조회 (1단계)&lt;/td&gt;
&lt;td&gt;1 ~ 2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2~3단계 워크플로우&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;복잡한 다단계 자동화&lt;/td&gt;
&lt;td&gt;10 ~ 15&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;권장 공식: 예상 Tool 호출 수 + 2 (여유분). max_iterations에 도달하면 Agent는 더 이상 Tool을 호출하지 않고 현재까지의 결과를 바탕으로 응답을 생성합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Tool 결과의 자연어 변환&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Lambda가 반환한 원시 JSON을 Agent가 자동으로 자연어로 재구성합니다. Instructions에 포맷 지침을 추가하면 더 일관성 있는 응답을 얻을 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;haml&quot;&gt;&lt;code&gt;## Tool 결과 처리 규칙
- get_github_pr 결과: &quot;PR #{number}: {title} (작성자: {author}, 상태: {state})&quot;
- post_pr_comment 결과: &quot;✅ 댓글이 성공적으로 등록되었습니다.&quot;
- send_slack_message 결과: &quot;  Slack 알림이 전송되었습니다.&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Lambda 반환 (Raw) Agent가 재구성 (자연어)&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;{&quot;title&quot;:&quot;Fix bug&quot;, &quot;state&quot;:&quot;open&quot;}&lt;/td&gt;
&lt;td&gt;&quot;PR 제목은 'Fix bug'이고, 현재 상태는 열려있습니다.&quot;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;⚠️ 주의사항&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1. Action Group 변경 후 반드시 Prepare를 재실행하세요.&lt;/b&gt; Instructions, Action Group, Knowledge Base 설정을 바꾸면 반드시 prepare_agent()를 다시 실행해야 합니다. Prepare를 빠뜨리면 이전 설정 그대로 동작합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2. GitHub Token, Slack Webhook URL을 코드에 하드코딩하지 마세요.&lt;/b&gt; API 키나 Webhook URL을 코드에 직접 입력하면 Git에 올라가는 순간 유출될 수 있습니다. Lambda 환경 변수나 AWS Secrets Manager에 저장하는 것이 좋습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3. Lambda 응답 형식을 정확히 지키세요.&lt;/b&gt; Lambda가 Bedrock Agent에게 응답할 때는 messageVersion, response, responseBody 구조를 반드시 따라야 합니다. 형식이 맞지 않으면 Agent가 결과를 이해하지 못합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;4. Action Group 이름에 하이픈(-)을 사용하지 마세요.&lt;/b&gt; GitHub-PR-Tools 대신 GitHubPRTools나 GitHub_PR_Tools를 사용하세요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;5. Bedrock Agent IAM 역할과 Lambda 리소스 기반 정책을 모두 설정하세요.&lt;/b&gt; Agent가 Lambda를 호출하려면 두 가지 권한이 모두 필요합니다. Lambda 리소스 기반 정책에 Bedrock Agent를 Principal로 추가하는 단계를 빠뜨리기 쉬우니 주의가 필요합니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  직접 구현하면서 느낀 것들&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Bedrock Agent를 직접 구현해보면 몇 가지 인상적인 지점이 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Instructions의 품질이 전부에 가깝습니다.&lt;/b&gt; 같은 도구를 연결해도 Instructions를 어떻게 쓰느냐에 따라 Agent 동작이 완전히 달라집니다. &quot;코드 리뷰해줘&quot;와 &quot;10년차 시니어 개발자로서 논리적 오류, 예외 처리 누락, SQL Injection 취약점을 심각도와 함께 보고해줘&quot;는 결과가 다릅니다. Instructions 작성이 사실상 엔지니어링의 핵심입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;OpenAPI Schema의 description이 도구 선택 정확도를 결정합니다.&lt;/b&gt; Agent는 사용자의 자연어 요청과 Schema의 description을 비교해서 어떤 도구를 쓸지 결정합니다. &quot;언제 이 도구를 써야 하는지&quot; 명확히 적어둘수록 오발동이 줄어듭니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Trace는 디버깅의 핵심입니다.&lt;/b&gt; Agent가 예상과 다르게 동작할 때 enableTrace=True로 추론 과정을 보면 원인을 빠르게 파악할 수 있습니다. &quot;왜 이 도구를 선택했지?&quot;, &quot;왜 이 파라미터를 넣었지?&quot;를 직접 확인할 수 있어서 실제로 많이 씁니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Agent와 직접 LLM 호출의 경계를 명확히 해두는 것이 좋습니다.&lt;/b&gt; 단순 Q&amp;amp;A나 예산/성능 최적화가 우선이라면 직접 호출이 낫습니다. RAG가 필요하거나, 외부 API를 호출해야 하거나, 멀티턴 복잡한 작업이라면 Agent가 적합합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;앞으로의 방향: Multi-Agent Collaboration&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2025년 3월, Amazon Bedrock에서 Multi-Agent Collaboration이 정식 출시되었습니다. 단일 에이전트로 감당하기 어려운 복잡한 워크플로우를 여러 전문화된 에이전트가 협력해서 처리하는 방식입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Supervisor 에이전트가 전체 요청을 분석하고, 하위 전문 에이전트들에게 작업을 위임하는 구조입니다. 예를 들어 금융 분석 시스템이라면 데이터 수집 에이전트, 트렌드 분석 에이전트, 투자 추천 에이전트가 각자의 역할을 맡아 병렬로 처리하고 결과를 통합하는 방식입니다. 단일 에이전트 대비 복잡한 다단계 작업에서 정확도와 처리 속도가 개선된다고 알려져 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 Amazon Bedrock AgentCore라는 플랫폼도 출시되어, 에이전트의 메모리(에피소딕 메모리 포함), 보안(Identity, Policy), 관찰 가능성(Observability) 등 프로덕션 운영에 필요한 기능들이 점점 정교해지고 있습니다. Agent를 단순히 만드는 것을 넘어 안전하게 운영하는 방향으로 생태계가 빠르게 발전하고 있습니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  참고 자료&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.aws.amazon.com/bedrock/latest/userguide/agents.html&quot;&gt;Amazon Bedrock Agents 공식 문서&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://aws.amazon.com/about-aws/whats-new/2025/03/amazon-bedrock-multi-agent-collaboration/&quot;&gt;Amazon Bedrock Multi-Agent Collaboration GA 발표&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://aws.amazon.com/bedrock/agentcore/&quot;&gt;Amazon Bedrock AgentCore&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.aws.amazon.com/bedrock/latest/APIReference/API_agent_CreateAgent.html&quot;&gt;Bedrock Agent API 레퍼런스&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Concepts/Cloud Infra</category>
      <author>ranygo12</author>
      <guid isPermaLink="true">https://elly1108.tistory.com/24</guid>
      <comments>https://elly1108.tistory.com/24#entry24comment</comments>
      <pubDate>Mon, 25 May 2026 16:37:29 +0900</pubDate>
    </item>
    <item>
      <title>Amazon Bedrock으로 AI 코드 리뷰어 만들기: LLM 호출부터 RAG까지 완전 정복</title>
      <link>https://elly1108.tistory.com/23</link>
      <description>&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt; LLM은 프롬프트라는 언어로 대화하고, RAG는 그 대화에 '참고 자료'를 제공한다.&amp;nbsp;&lt;/span&gt;&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AWS의 생성형 AI 플랫폼인 Amazon Bedrock을 이용하면 Claude와 같은 강력한 대형 언어 모델을 직접 인프라 구축 없이 API로 호출할 수 있습니다. 이 글에서는 Bedrock의 기본 API 구조부터 프롬프트 엔지니어링, 그리고 RAG(검색 증강 생성) 시스템 구축까지, 코드 스타일 검사와 보안 취약점 탐지를 직접 구현하는 흐름을 중심으로 핵심 개념을 정리합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단순한 API 호출에서 시작해 지식 기반을 갖춘 AI 리뷰어를 만들기까지의 여정을 따라가면 생성형 AI를 실무에 적용하는 데 필요한 개념적 기반을 갖출 수 있을 것입니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt; ️ 전체 아키텍처 개요&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 시리즈는 &quot;CodeBuddy&quot;라는 이름의 AI 코드 리뷰 에이전트를 단계적으로 완성해 나가는 프로젝트입니다. 전체 구조를 먼저 조감하면 각 장의 개념이 어디에 위치하는지 이해하기 쉽습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;1장&lt;/b&gt;: AWS 계정 설정, Bedrock 접근 권한, 개발 환경 준비&lt;/li&gt;
&lt;li&gt;&lt;b&gt;2장&lt;/b&gt;: Boto3로 Claude 호출, 프롬프트 엔지니어링, 코드 분석 기초&lt;/li&gt;
&lt;li&gt;&lt;b&gt;3장&lt;/b&gt;: RAG 개념, 임베딩, Knowledge Base 구축&lt;/li&gt;
&lt;li&gt;&lt;b&gt;4장&lt;/b&gt;: RAG 기반 코드 스타일/보안 검사 자동화, 검색 품질 최적화&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 네 단계를 거치면 코드를 입력했을 때 버그 가능성, 스타일 위반, 보안 취약점을 자동으로 리포트해주는 시스템이 완성됩니다. 여기서 주목할 점은, 처음에는 LLM 자체의 일반 지식만 활용하지만 후반부에서 PEP8, OWASP 같은 외부 문서를 '지식 창고'로 연결하면서 시스템이 훨씬 정확하고 맥락에 맞는 답변을 제공하게 된다는 점입니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  Bedrock API 구조 이해하기&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Foundation Model이란 무엇인가&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Amazon Bedrock은 Anthropic, Meta, Amazon 등 여러 회사의 대형 언어 모델을 단일 플랫폼에서 관리하고 호출할 수 있게 해주는 완전 관리형 서비스입니다. 각 모델은 '기반 모델(Foundation Model)'이라고 불리며, 수천억 개의 파라미터로 훈련된 범용 AI 모델입니다. 기반 모델의 핵심 특징은 단 하나의 모델이 번역, 요약, 코드 생성, 질의응답, 창작 등 매우 다양한 작업을 수행할 수 있다는 점입니다. 이는 이전 세대 AI가 특정 작업(예: 이미지 분류, 감성 분석)에 특화된 별도의 모델을 요구했던 것과 근본적으로 다른 패러다임입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Bedrock이 편리한 이유 중 하나는 인프라 관리가 필요 없다는 점입니다. GPU 서버를 직접 구축하거나 모델 가중치를 직접 다운로드해 운영할 필요 없이, API 호출만으로 세계 최고 수준의 모델을 즉시 사용할 수 있습니다. 이 과정은 AWS IAM 권한 설정과 Boto3 클라이언트 생성만으로 시작됩니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;InvokeModel vs Converse API&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Bedrock에서 모델을 호출하는 방법은 크게 두 가지입니다. 기존의 InvokeModel API와 더 최신의 Converse API입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;InvokeModel은 모델별로 다른 요청 형식을 사용합니다. Claude를 호출할 때와 Meta의 Llama를 호출할 때 요청 본문(body)의 구조가 완전히 다르기 때문에, 개발자가 각 모델의 스펙을 개별적으로 파악하고 코드를 다르게 작성해야 했습니다. 새로운 모델로 교체하면 코드 전체를 다시 작성해야 하는 번거로움이 있었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반면 Converse API는 Claude, Llama, Titan 등 다양한 모델을 동일한 인터페이스로 호출할 수 있는 통합 API입니다. 메시지 구조, 토큰 계산, 대화 기록 관리 방식이 모델과 무관하게 표준화되어 있습니다. 따라서 모델을 교체하더라도 modelId 파라미터 값만 바꾸면 됩니다. 이 과정에서 배운 내용들은 이 표준화된 인터페이스를 기반으로 하기 때문에 특정 모델에 종속되지 않는 코드 작성이 가능합니다.&lt;/p&gt;
&lt;pre class=&quot;nix&quot;&gt;&lt;code&gt;# Converse API의 기본 호출 구조
response = bedrock.converse(
    modelId='global.anthropic.claude-sonnet-4-6',
    messages=[
        {
            &quot;role&quot;: &quot;user&quot;,
            &quot;content&quot;: [{&quot;text&quot;: &quot;코드를 분석해줘&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;응답 구조 이해하기&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Converse API의 응답은 일관된 JSON 구조를 가집니다. 핵심 응답 텍스트는 response['output']['message']['content'][0]['text']에 담겨 있습니다. 그 외에 usage 필드에는 입력 토큰 수, 출력 토큰 수, 총 토큰 수가 기록됩니다. 이 토큰 정보는 비용 계산과 성능 최적화에 중요하게 활용됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;stopReason 필드는 모델이 왜 응답을 멈췄는지를 나타냅니다. 정상적인 완료는 end_turn이고, 토큰 한도를 초과해서 멈추면 max_tokens가 됩니다. max_tokens로 응답이 끊겼다면 리포트가 중간에 잘린 것이므로, 이를 감지해서 토큰 한도를 늘리거나 입력 코드를 분할하는 처리가 필요합니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  토큰이란 무엇인가&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LLM을 다루다 보면 &quot;토큰&quot;이라는 단위가 계속 등장합니다. 토큰은 LLM이 텍스트를 처리하는 기본 단위로, 단어보다 작거나 단어 자체이거나 여러 단어의 조합일 수 있습니다. 영어의 경우 &quot;tokenization&quot;이라는 단어는 하나의 토큰이 아니라 [&quot;token&quot;, &quot;ization&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;토큰이 중요한 이유는 세 가지입니다.&lt;/p&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 호출 비용은 입력 토큰 수와 출력 토큰 수를 기준으로 책정됩니다. Claude Sonnet 4.6 기준으로 입력은 약 $0.003/1K 토큰, 출력은 약 $0.015/1K 토큰으로, 출력 토큰이 입력 토큰보다 약 5배 비쌉니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;둘째, 모델의 컨텍스트 윈도우 크기가 토큰 수로 제한됩니다. 한 번의 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;셋째, 응답 품질에도 영향을 미칩니다. maxTokens 설정이 너무 작으면 모델이 답변을 완성하기 전에 강제로 종료됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실용적인 가이드라인으로, 단순한 질문에는 500토큰, 함수 하나 분석에는 2000토큰, 여러 파일 분석이나 긴 리포트에는 4000~8000토큰이 적합합니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;✍️ 프롬프트 엔지니어링 기초&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;프롬프트가 왜 중요한가&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;코드 리뷰해줘&quot;라고 입력하면 모델은 아마 &quot;좋아요, 잘 짜여진 코드네요&quot;처럼 막연한 답변을 할 것입니다. 반면 &quot;당신은 시니어 개발자입니다. 다음 코드를 버그 가능성, 성능, 스타일 측면에서 분석하고 Markdown 형식으로 정리해주세요.&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;이처럼 LLM에게 '일하는 방식'을 알려주는 기술이 프롬프트 엔지니어링입니다. 모델 자체를 재훈련하지 않고도 입력 텍스트를 잘 설계하는 것만으로 완전히 다른 결과를 얻을 수 있습니다. 프롬프트 엔지니어링은 AI의 잠재 능력을 최대한 끌어내는 기술이자, 비용 효율적으로 고품질 결과를 얻는 핵심 방법입니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;프롬프트의 구성 요소&lt;/h3&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;역할(Role)&lt;/b&gt;은 AI에게 특정 페르소나를 부여합니다. &quot;당신은 실무 경험 10년차 시니어 개발자입니다&quot;라고 역할을 지정하면 모델은 그 역할에 맞는 어조, 관점, 전문성 수준으로 답변하게 됩니다. 단순히 &quot;전문가처럼 답해줘&quot;보다 구체적인 역할 부여가 훨씬 효과적입니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;작업(Task)&lt;/b&gt;은 구체적으로 해야 할 일을 명시합니다. &quot;코드를 분석해줘&quot;보다 &quot;다음 코드에서 SQL Injection 취약점이 있는지 확인하고, 발견 시 위치와 수정 방법을 알려줘&quot;처럼 구체적일수록 원하는 답변을 얻기 쉽습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;형식(Format)&lt;/b&gt;은 출력 구조를 지정합니다. &quot;버그: [위치] - [설명]&quot; 형식으로 작성해달라거나, JSON으로 결과를 반환해달라거나, Markdown 테이블로 정리해달라는 형식 지시는 결과를 자동화 파이프라인에 연결할 때 특히 중요합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;맥락(Context)&lt;/b&gt;은 배경 정보를 제공합니다. 이 코드가 Django로 개발된 웹 서비스의 일부라는 것, 사용 중인 Python 버전이 3.11이라는 것, 또는 회사의 특정 코딩 컨벤션을 따른다는 것을 알려주면 모델이 더 맥락에 맞는 조언을 제공할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Temperature: 창의성의 조절 다이얼&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Temperature는 LLM이 답변을 생성할 때 단어를 선택하는 방식의 '무작위성'을 제어하는 파라미터입니다. 기술적으로는 소프트맥스 함수를 통해 계산된 다음 토큰의 확률 분포를 얼마나 평탄하게(균일하게) 만들지를 결정합니다.&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;1436&quot; data-origin-height=&quot;720&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/2Gf9G/dJMcaftaLSq/rro4klXaxhma6bgh4XEkN0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/2Gf9G/dJMcaftaLSq/rro4klXaxhma6bgh4XEkN0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/2Gf9G/dJMcaftaLSq/rro4klXaxhma6bgh4XEkN0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F2Gf9G%2FdJMcaftaLSq%2Frro4klXaxhma6bgh4XEkN0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/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;249&quot; data-origin-width=&quot;1436&quot; data-origin-height=&quot;720&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Temperature 값이 0에 가까울수록 모델은 항상 가장 확률이 높은 토큰을 선택합니다. 이렇게 되면 같은 질문에 항상 거의 동일한 답변이 나오는 결정론적인 동작이 됩니다. Temperature 값이 높아질수록 낮은 확률의 토큰도 선택될 기회를 얻어 더 다양하고 창의적인 답변이 나옵니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드 분석처럼 정확성이 중요한 작업에서는 낮은 Temperature(0.1~0.3)를 사용하는 것이 좋습니다. 같은 코드를 여러 번 분석해도 일관된 결과를 얻을 수 있기 때문입니다. 반면 새로운 아이디어를 브레인스토밍하거나 창의적인 글쓰기를 할 때는 높은 Temperature(0.7~1.0)가 더 유용합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;table style=&quot;letter-spacing: 0px; border-collapse: collapse; width: 100%; height: 73px;&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;Temperature 범위&amp;nbsp;&lt;/td&gt;
&lt;td&gt;특징&amp;nbsp;&lt;/td&gt;
&lt;td&gt;적합한 용도&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;0.0 ~ 0.3&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;일관적, 보수적, 예측 가능&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;코드 분석, 사실 기반 질답&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;0.4 ~ 0.7&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;균형 잡힌 답변&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;일반 대화, 요약&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;0.8 ~ 1.0&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;창의적, 다양한 답변&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;아이디어 생성, 시나리오 작성&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Guardrails: AI의 안전장치&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Guardrails는 모델이 부적절한 응답을 생성하지 못하도록 막는 필터 레이어입니다. &quot;해킹 코드 알려줘&quot;라는 요청에 Guardrails 없이는 실제 악성 코드가 반환될 수 있지만, Guardrails를 설정하면 &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;Guardrails가 차단하는 대상에는 해킹, 불법 복제 같은 불법 콘텐츠, 차별적 혐오 발언, 주민번호나 계좌번호 같은 개인정보, 악성코드나 바이러스 같은 유해 코드 등이 포함됩니다. Bedrock 콘솔에서 Guardrail을 생성하면 고유 ID를 받게 되고, API 호출 시 이 ID를 파라미터로 전달하면 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드 리뷰 에이전트처럼 내부 도구로 사용한다면 Guardrails가 반드시 필요하지 않을 수 있습니다. 그러나 외부 사용자가 임의의 코드를 입력할 수 있는 환경이라면, 보안 강화와 책임 관리 측면에서 Guardrails 도입을 고려하는 것이 좋습니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  RAG: LLM에게 참고 자료를 주다&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;왜 RAG가 필요한가&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;순수하게 LLM의 사전 학습 지식만으로 &quot;이 코드가 우리 회사 코딩 컨벤션에 맞나요?&quot;라고 물으면 모델은 &quot;죄송합니다, 회사 코딩 컨벤션을 모릅니다&quot;라고 대답할 수밖에 없습니다. LLM은 훈련 시점까지의 공개된 정보를 학습했을 뿐, 특정 조직의 내부 규칙이나 훈련 이후에 등장한 새로운 보안 취약점 데이터베이스는 알지 못합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 Hallucination(환각) 문제도 중요합니다. 모델이 모르는 내용을 물어봤을 때, 정직하게 &quot;모른다&quot;고 답하는 대신 그럴듯하게 들리지만 사실이 아닌 내용을 자신 있게 생성하는 현상입니다. 실제로 구글의 Bard가 제임스 웹 우주망원경에 대한 틀린 정보를 자신 있게 제공해 구글 주가가 1000억 달러 하락하는 사건이 있었습니다. 코드 보안 검사에서 모델이 없는 취약점을 있다고 하거나, 실제 취약점을 간과하는 것은 매우 위험할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RAG(Retrieval-Augmented Generation, 검색 증강 생성)는 이 문제를 해결합니다. 질문에 답하기 전에 먼저 외부 문서 데이터베이스에서 관련 내용을 검색해 그 내용을 LLM에게 '참고 자료'로 제공하는 방식입니다. 모델이 처음부터 모든 것을 알고 있을 필요 없이, 답변할 때 관련 최신 문서를 참조하도록 만드는 것입니다. 마치 '오픈북 시험'과 같습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;RAG의 동작 원리&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RAG는 크게 오프라인 단계와 실시간 단계 두 부분으로 나뉩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;601&quot; data-origin-height=&quot;464&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cI9AQu/dJMcabxwJ6x/c7qk8f1Exvnk2PsFRYyAe1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cI9AQu/dJMcabxwJ6x/c7qk8f1Exvnk2PsFRYyAe1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cI9AQu/dJMcabxwJ6x/c7qk8f1Exvnk2PsFRYyAe1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcI9AQu%2FdJMcabxwJ6x%2Fc7qk8f1Exvnk2PsFRYyAe1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;569&quot; height=&quot;439&quot; data-origin-width=&quot;601&quot; data-origin-height=&quot;464&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오프라인 단계(문서 색인)에서는 PEP8, OWASP, 회사 내부 코딩 가이드 등의 문서를 임베딩 모델을 통해 벡터로 변환하고 벡터 데이터베이스에 저장합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실시간 단계(질의 응답)에서는 사용자의 질문을 임베딩 벡터로 변환하고, 벡터 데이터베이스에서 질문과 의미적으로 유사한 문서 조각들을 검색합니다. 검색된 문서 조각들을 LLM에게 컨텍스트로 제공하면, LLM은 그 내용을 참조해 답변을 생성합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 방식의 핵심 장점은 세 가지입니다. 첫째, 모델을 재훈련하지 않고도 새로운 정보를 반영할 수 있습니다. 새로운 보안 취약점이 발견되면 문서를 업데이트하고 재색인하면 됩니다. 둘째, 답변에 출처를 포함할 수 있어 검증이 가능합니다. &quot;PEP8 문서 3번째 섹션에 따르면...&quot;처럼 근거를 제시할 수 있습니다. 셋째, 할루시네이션을 크게 줄일 수 있습니다. 모델이 주어진 참고 자료에 기반해 답변하도록 유도하기 때문입니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  임베딩과 벡터 검색의 원리&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;텍스트를 숫자로 변환하다&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;임베딩(Embedding)은 텍스트를 고차원 공간의 숫자 벡터로 변환하는 기술입니다. Amazon Titan Embeddings v2를 사용하면 어떤 텍스트든 1024개의 숫자로 이루어진 벡터로 변환됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;임베딩의 핵심 특성은 의미적으로 유사한 텍스트가 벡터 공간에서 서로 가까운 위치에 배치된다는 점입니다. &quot;Python 변수명은 snake_case를 사용합니다&quot;와 &quot;함수명은 소문자로 작성합니다&quot;는 모두 파이썬 네이밍 규칙에 관한 내용이므로 벡터 공간에서 가까이 위치합니다. 반면 &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;벡터 = 문장의 '수학적 지문'이라고 이해하면 됩니다. 지문이 사람을 고유하게 식별하듯, 벡터는 텍스트의 의미를 고유하게 표현합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;코사인 유사도&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 벡터가 얼마나 유사한지 측정하는 방법 중 가장 널리 쓰이는 것이 코사인 유사도입니다. 코사인 유사도는 두 벡터 사이의 각도를 측정하는데, 각도가 작을수록(방향이 비슷할수록) 유사도가 높습니다. 값의 범위는 -1에서 1 사이이며, 1에 가까울수록 매우 유사, 0에 가까울수록 관련 없음, -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;수식으로는 cos(&amp;theta;) = (A &amp;middot; B) / (|A| &amp;times; |B|)로 표현됩니다. 두 벡터의 내적을 각 벡터의 크기의 곱으로 나눈 값입니다. 실제로 &quot;calculate_total&quot;과 &quot;get_sum&quot;처럼 기능은 비슷하지만 이름이 다른 두 함수의 임베딩 벡터는 코사인 유사도가 0.35 수준으로 나타나는 반면, 전혀 다른 도메인의 함수는 0.15 수준으로 낮게 나타납니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Amazon Titan Embeddings v2&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Amazon Bedrock에서 기본 임베딩 모델로 제공하는 Titan Embeddings v2는 1024차원 벡터를 출력하며, 한국어를 포함한 100개 이상의 언어를 지원합니다. 벡터 차원(256, 512, 1024)을 선택할 수 있는데, 차원이 높을수록 의미를 더 정밀하게 표현할 수 있지만 저장 공간과 검색 속도에서 트레이드오프가 있습니다. 일반적으로 1024차원이 적정 균형을 제공합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt; ️ Amazon Bedrock Knowledge Base 아키텍처&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;구성 요소와 그 역할&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;645&quot; data-origin-height=&quot;382&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cnHn7v/dJMcaaZHxgd/T7NKkF5Zh4TbKPiGRnHtYk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cnHn7v/dJMcaaZHxgd/T7NKkF5Zh4TbKPiGRnHtYk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cnHn7v/dJMcaaZHxgd/T7NKkF5Zh4TbKPiGRnHtYk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcnHn7v%2FdJMcaaZHxgd%2FT7NKkF5Zh4TbKPiGRnHtYk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;523&quot; height=&quot;310&quot; data-origin-width=&quot;645&quot; data-origin-height=&quot;382&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Bedrock Knowledge Base는 RAG 시스템을 구축하기 위해 S3, 임베딩 모델, 벡터 데이터베이스를 연결해주는 완전 관리형 오케스트레이터입니다. 세 가지 주요 구성 요소가 있습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;S3(Simple Storage Service&lt;/b&gt;)는 원본 문서를 저장하는 '서류 창고' 역할을 합니다. PDF, TXT, Markdown 형식의 문서를 S3 버킷에 업로드하면 Bedrock이 자동으로 읽어서 처리합니다. S3의 버전 관리 기능 덕분에 문서를 업데이트해도 이전 버전을 보존할 수 있습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Bedrock Knowledge Base&lt;/b&gt; 자체는 전체 RAG 파이프라인을 관리하는 오케스트레이터입니다. S3에서 문서를 읽어오고, 텍스트를 청크(Chunk)로 분할하고, 임베딩 모델을 호출해 벡터를 생성하고, 그 벡터를 벡터 데이터베이스에 저장하는 전 과정을 자동으로 처리합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;OpenSearch Serverless&lt;/b&gt;는 벡터를 저장하고 빠르게 검색하는 벡터 데이터베이스 역할을 합니다. 수백만 개의 벡터 중에서 유사한 것을 밀리초 단위로 찾아낼 수 있습니다. 'Serverless'라는 이름처럼 클러스터를 직접 관리할 필요 없이 사용한 만큼만 비용을 지불합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Knowledge Base ID는 생성 후 AWS가 자동으로 할당하는 고유 식별자입니다(예: ABC123DEFG). API 호출 시 어떤 지식 기반을 참조해야 하는지 지정하는 '주소'로 사용되며, IAM 권한 제어에도 활용됩니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;청킹(Chunking) 전략&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;50페이지짜리 PEP8 문서 전체를 하나의 벡터로 만들면 검색의 정밀도가 떨어집니다. &quot;변수명 규칙&quot;에 대해 물었을 때, 인덱싱 규칙이나 들여쓰기 규칙까지 포함된 거대한 문서 전체가 검색 결과로 나올 수 있기 때문입니다. 이를 해결하기 위해 &lt;b&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 data-ke-size=&quot;size16&quot;&gt;청킹 크기는 검색 품질에 직접적인 영향을 미칩니다. 청크가 너무 작으면 검색 정밀도는 높아지지만 앞뒤 맥락이 부족해집니다. 반면 너무 크면 맥락은 풍부하지만 관련 없는 내용도 함께 검색될 수 있습니다. Amazon Bedrock은 기본 청킹(300토큰), 고정 크기 청킹(원하는 토큰 수 지정), 계층적 청킹(작은 청크와 큰 청크를 함께 사용) 등 다양한 전략을 제공합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Overlap(겹침)도 중요한 개념입니다. 청크와 청크 사이에 일정 비율의 내용이 겹치도록 설정하면, 경계에 걸친 정보가 어느 청크에도 포함됩니다. 예를 들어 &quot;함수 X는 Y를 반환합니다&quot;라는 문장이 두 청크 경계에 걸쳐 있더라도, 20%의 Overlap을 설정하면 양쪽 청크 모두에 이 문장이 포함되어 어떤 청크가 검색되더라도 해당 정보를 얻을 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드 문서의 경우 함수나 클래스 단위로 청크를 나누는 것이 의미적으로 가장 자연스럽습니다. 함수 정의의 일부가 한 청크에, 나머지가 다른 청크에 나뉘는 상황을 방지할 수 있기 때문입니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;메타데이터 활용&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;메타데이터는 문서에 추가로 붙이는 태그 정보입니다. S3에 파일을 업로드할 때 &quot;언어: Python&quot;, &quot;규칙 유형: 스타일&quot;과 같은 메타데이터를 함께 저장하면, 검색 시 필터링에 활용할 수 있습니다. JavaScript 코드를 분석할 때 Python 스타일 가이드 대신 JavaScript Airbnb 스타일 가이드만 검색하도록 제한하는 방식입니다. 이렇게 하면 검색 결과의 정확도가 크게 올라갑니다.&lt;/p&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;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;S3에 새 문서를 추가하거나 기존 문서를 수정했다고 해서 자동으로 Knowledge Base가 업데이트되지는 않습니다. Bedrock의 Ingestion Job을 실행해야 S3의 변경 사항이 벡터 데이터베이스에 반영됩니다. 동기화 상태는 STARTED &amp;rarr; IN_PROGRESS &amp;rarr; COMPLETE 또는 FAILED로 변화하며, 일반적으로 수십 초에서 수 분이 소요됩니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt; ️ RetrieveAndGenerate API: 검색과 생성의 통합&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;수동 방식의 복잡함&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RAG를 직접 구현하면 네 단계가 필요합니다. 먼저 사용자 질문을 임베딩 벡터로 변환하고, 그 벡터로 벡터 데이터베이스에서 유사한 문서를 검색한 다음, 검색된 문서들을 LLM이 이해할 수 있는 컨텍스트로 조합하고, 마지막으로 LLM에게 컨텍스트와 함께 질문을 전달해 답변을 받아야 합니다. 각 단계마다 별도의 API 호출이 필요하고 오류 처리도 복잡합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;RetrieveAndGenerate의 단순함&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RetrieveAndGenerate는 이 모든 단계를 단일 API 호출로 처리합니다. 질문을 입력하면 Bedrock이 알아서 임베딩 변환, 문서 검색, 컨텍스트 조합, LLM 호출의 모든 과정을 처리하고 최종 답변과 참조한 문서 목록을 반환합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 API의 중요한 장점 중 하나는 retrievedResults 필드를 통해 어떤 문서가 답변 생성에 활용됐는지 확인할 수 있다는 점입니다. 각 결과에는 관련성 점수(Relevance Score)도 함께 제공되어, 검색 품질을 평가하고 조정하는 데 활용할 수 있습니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  검색 품질 최적화&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Relevance Score 해석&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;벡터 검색의 결과는 단순히 문서 목록만 반환하는 것이 아니라 각 문서의 관련성 점수도 함께 제공합니다. 이 점수를 기준으로 품질이 낮은 검색 결과를 필터링할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;점수 범위 의미 권장 조치&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;0.8 ~ 1.0&lt;/td&gt;
&lt;td&gt;매우 관련 높음&lt;/td&gt;
&lt;td&gt;컨텍스트로 그대로 사용&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;0.5 ~ 0.8&lt;/td&gt;
&lt;td&gt;중간 관련&lt;/td&gt;
&lt;td&gt;추가 검토 또는 프롬프트 보강&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;0.3 ~ 0.5&lt;/td&gt;
&lt;td&gt;낮은 관련&lt;/td&gt;
&lt;td&gt;청크 크기 조정 또는 임베딩 모델 재검토&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;0.0 ~ 0.3&lt;/td&gt;
&lt;td&gt;거의 무관&lt;/td&gt;
&lt;td&gt;관련 문서 추가 및 재색인 필요&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;관련성 점수가 낮은 결과를 필터링하는 방법도 있습니다. 먼저 retrieve API로 상위 10개 문서와 점수를 받아온 뒤, 점수가 0.7 이상인 문서만 선별해 LLM에게 컨텍스트로 제공하는 방식입니다. 이렇게 하면 관련 없는 정보가 섞여 들어가는 것을 방지할 수 있습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;TopK 튜닝&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Knowledge Base에서 가져올 검색 결과 수를 결정하는 파라미터를 TopK라고 합니다. 3개만 가져오면 빠르고 비용이 낮지만 관련 문서를 누락할 수 있습니다. 10개를 가져오면 더 많은 정보를 얻지만 관련 없는 내용이 섞일 가능성이 높아지고 비용도 올라갑니다. 일반적으로 5~7개가 품질과 비용의 균형을 맞추기에 적당합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;하이브리드 검색&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;벡터 검색만 사용할 경우의 약점이 있습니다. &quot;SQL Injection&quot;이라는 정확한 단어가 포함된 문서를 찾을 때 벡터 검색은 의미적으로 유사한 &quot;query injection&quot;, &quot;parameterized query&quot; 같은 문서를 잘 찾아내지만, 정확한 단어 매칭에서는 상대적으로 약할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하이브리드 검색은 키워드 기반의 BM25 검색과 벡터 기반의 시맨틱 검색을 결합해 두 방식의 장점을 모두 취합니다. &quot;SQL Injection&quot;처럼 정확한 기술 용어가 있는 질문에는 키워드 검색이, &quot;사용자 입력을 쿼리에 직접 넣으면 어떤 문제가 있나요?&quot;처럼 의미 기반 질문에는 시맨틱 검색이 더 효과적으로 동작하도록 두 결과를 결합합니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  보안 취약점 검사: RAG가 빛나는 순간&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;OWASP Top 10과 RAG&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;OWASP(Open Worldwide Application Security Project)는 매년 가장 위험한 웹 애플리케이션 보안 취약점 10가지를 발표합니다. 이 문서는 정기적으로 업데이트되는데, LLM의 훈련 데이터에는 특정 시점 이후의 최신 OWASP 업데이트가 포함되지 않을 수 있습니다. RAG를 사용하면 최신 OWASP 문서를 Knowledge Base에 추가함으로써 모델이 항상 최신 보안 기준에 따라 코드를 검사하도록 만들 수 있습니다.&lt;/p&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 Injection은 사용자 입력을 SQL 쿼리에 직접 삽입할 때 발생하는 취약점으로, OWASP A03:2021(Injection)에 해당합니다. query = f&quot;SELECT * FROM users WHERE id = {user_id}&quot; 같은 코드는 user_id에 1 OR '1'='1' 같은 값이 들어오면 인증을 우회할 수 있는 매우 위험한 패턴입니다. RAG를 통해 OWASP 문서를 참조하는 LLM은 이 패턴을 식별하고 &quot;Prepared Statement(파라미터 바인딩)를 사용하라&quot;는 구체적인 수정 제안까지 제시할 수 있습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;코드 리뷰에 RAG가 필요한 이유&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;회사 내부 코딩 컨벤션은 외부에 공개되지 않으므로 LLM이 알 수 없습니다. 회사의 아키텍처 규칙, 특정 라이브러리 사용 지침, 팀 고유의 네이밍 규칙 등을 Knowledge Base에 담아두면 LLM이 이를 참조해 회사 맞춤형 코드 리뷰를 수행할 수 있습니다. 새로운 팀원이 입사했을 때 방대한 사내 문서를 모두 읽지 않아도 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;프레임워크 가이드도 마찬가지입니다. React, Django, Spring 등의 공식 문서와 베스트 프랙티스를 Knowledge Base에 추가하면, LLM이 &quot;이 방식은 React 18에서 deprecated되었으며 대신 이것을 사용하세요&quot;처럼 프레임워크 버전에 맞는 조언을 제공할 수 있습니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  비용과 성능 최적화&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;캐싱으로 중복 비용 방지&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PR(Pull Request)을 리뷰할 때마다 같은 코드에 대해 반복적으로 RAG 검사를 실행하면 비용이 낭비됩니다. 코드의 MD5 해시를 캐시 키로 사용하고, Redis 같은 캐시 시스템에 결과를 저장해두면 동일한 코드는 두 번째부터 캐시에서 즉시 반환할 수 있습니다. TTL(Time to Live)을 1시간으로 설정하면 코드가 변경됐을 때 자동으로 캐시가 만료되어 새로운 검사가 실행됩니다. 이 방식으로 비용을 약 70% 절감할 수 있다고 알려져 있습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;작은 모델의 전략적 활용&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단순한 스타일 검사(공백, 들여쓰기 등)는 Claude Haiku 같은 더 작고 저렴한 모델로도 충분히 처리할 수 있습니다. 반면 복잡한 보안 취약점 분석이나 리팩토링 제안에는 Claude Sonnet이나 Claude Opus 같은 강력한 모델이 필요할 수 있습니다. 작업의 복잡도에 따라 모델을 다르게 선택하면 비용을 약 80%까지 절감할 수 있습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;월간 비용 예측&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;500줄 코드 한 번 리뷰에 드는 예상 비용을 분해하면, 임베딩 비용은 약 $0.001, OpenSearch 검색 비용은 약 $0.0005, LLM 입력 비용은 약 $0.005, LLM 출력 비용은 약 $0.01으로 합계 약 $0.0165입니다. 일 100회 리뷰 기준으로 월간 약 $36 정도가 됩니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  코드 분석용 프롬프트 설계 패턴&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;효과적인 코드 분석 프롬프트의 구성&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&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;버그 가능성 분석에서는 논리적 오류(예: 0으로 나누기)와 예외 처리 누락을 중점적으로 검토하도록 지시합니다. 보안 취약점 분석에서는 SQL Injection, XSS, 하드코딩된 비밀번호 등 특정 취약점 유형을 명시합니다. 코드 스타일 검사에서는 PEP8 같은 특정 스타일 가이드를 기준으로 삼도록 지시하고, Cyclomatic Complexity(순환 복잡도) 분석을 추가하면 코드의 유지보수 가능성도 평가할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;출력 형식을 &quot;### 버그 - [위치] 문제 설명&quot; 같은 구조로 고정하면 결과를 파싱해 다른 시스템(JIRA 티켓 생성, Slack 알림 등)과 연동하기 쉬워집니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Cyclomatic Complexity 이해&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Cyclomatic Complexity(순환 복잡도)는 코드의 복잡성을 수치로 측정하는 지표입니다. 조건문(if), 반복문(for, while), 예외 처리(try/except) 등 분기가 생기는 지점마다 복잡도가 1씩 올라갑니다. 값이 낮을수록 이해하고 테스트하기 쉬운 코드입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;복잡도 평가 의미&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1 ~ 10&lt;/td&gt;
&lt;td&gt;좋음&lt;/td&gt;
&lt;td&gt;구조가 단순하며 쉬운 이해&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;11 ~ 20&lt;/td&gt;
&lt;td&gt;주의&lt;/td&gt;
&lt;td&gt;복잡도가 높아지고 있어 리팩토링 고려&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;21 ~ 50&lt;/td&gt;
&lt;td&gt;복잡&lt;/td&gt;
&lt;td&gt;로직이 얽혀 있어 함수 분할 필요&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;50+&lt;/td&gt;
&lt;td&gt;위험&lt;/td&gt;
&lt;td&gt;완전한 재작성 권장&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  다중 언어 지원 아키텍처&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Knowledge Base에 PEP8(Python), Airbnb Style Guide(JavaScript), Google Java Style Guide(Java), Effective Go(Go) 등의 언어별 스타일 가이드를 모두 저장해두면 단일 시스템으로 여러 언어를 지원할 수 있습니다. 프롬프트에 언어를 명시하면 Knowledge Base가 해당 언어의 가이드라인을 우선적으로 검색해 반환합니다. 메타데이터 필터링을 활용하면 언어별로 다른 Knowledge Base를 쓰는 것보다 효율적으로 관리할 수 있습니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  이 과정을 통해 배운 것들&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 4장의 여정을 돌아보면 몇 가지 중요한 관찰이 눈에 띕니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LLM은 만능이 아닙니다. 훈련 데이터에 없는 최신 정보, 회사 내부 문서, 특정 도메인 지식에서는 한계를 보입니다. RAG는 이 한계를 코드 변경 없이 보완하는 우아한 방법입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프롬프트 엔지니어링은 기술이자 투자입니다. 프롬프트를 잘 설계하는 데 드는 시간은 코드 품질 향상과 일관된 결과라는 형태로 돌아옵니다. &quot;코드 리뷰해줘&quot;와 &quot;실무 경험 10년차 시니어 개발자로서 SQL Injection, XSS, 하드코딩된 비밀번호를 중심으로 다음 코드의 보안 취약점을 찾아주세요&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;비용과 품질은 트레이드오프입니다. 더 강력한 모델, 더 많은 검색 결과, 더 긴 응답은 더 좋은 품질을 제공하지만 비용도 함께 올라갑니다. 캐싱, 적절한 TopK 설정, 작업 복잡도에 따른 모델 선택 등의 최적화를 통해 품질을 유지하면서 비용을 관리하는 것이 실무에서 매우 중요합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RAG는 단순한 검색이 아닙니다. 문서를 어떻게 청킹하고, 어떤 임베딩 모델을 사용하고, 메타데이터를 어떻게 설계하고, 검색 결과를 어떻게 필터링하는지에 따라 시스템의 품질이 크게 달라집니다. RAG는 구현보다 튜닝이 더 중요한 기술입니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  참고 자료&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.aws.amazon.com/bedrock/&quot;&gt;Amazon Bedrock 공식 문서&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.aws.amazon.com/bedrock/latest/userguide/knowledge-base.html&quot;&gt;Bedrock Knowledge Bases 개발자 안내서&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://owasp.org/www-project-top-ten/&quot;&gt;OWASP Top 10:2021&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://peps.python.org/pep-0008/&quot;&gt;PEP 8 &amp;ndash; Python Code Style Guide&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/airbnb/javascript&quot;&gt;Airbnb JavaScript Style Guide&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://aws.amazon.com/bedrock/titan/&quot;&gt;Amazon Titan Embeddings 모델 카드&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Concepts/Cloud Infra</category>
      <category>AI</category>
      <category>AWS</category>
      <category>Bedrock</category>
      <category>Rag</category>
      <author>ranygo12</author>
      <guid isPermaLink="true">https://elly1108.tistory.com/23</guid>
      <comments>https://elly1108.tistory.com/23#entry23comment</comments>
      <pubDate>Sun, 24 May 2026 15:54:59 +0900</pubDate>
    </item>
    <item>
      <title>ESO(External Secrets Operator) 입문</title>
      <link>https://elly1108.tistory.com/22</link>
      <description>&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt; ESO는 외부 시크릿 저장소(Vault 등)에서 값을 읽어, 자동으로 Kubernetes Secret을 생성&amp;middot;동기화해주는 오퍼레이터입니다. &lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쿠버네티스에서 민감한 정보를 다루다 보면 &quot;이걸 정말 여기에 저장해도 될까?&quot;라는 의문이 생기는 순간이 옵니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본 Kubernetes Secret은 단순히 Base64로 인코딩된 값일 뿐이며, Git에 올라가거나 클러스터가 침해되면 그대로 노출됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;External Secrets Operator(ESO)는 HashiCorp Vault, AWS Secrets Manager 등 외부 비밀 관리 시스템과 쿠버네티스를 연결해 이 문제를 구조적으로 해결해 줍니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt; ️ 등장 배경과 기본 개념&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Kubernetes의 기본 Secret 리소스는 편리하지만 보안 측면에서 한계가 있습니다. etcd에 저장된 Secret은 기본적으로 암호화되지 않으며, RBAC 설정이 느슨하면 클러스터 내 누구나 조회할 수 있습니다. 또한 GitOps 워크플로에서 YAML 파일에 시크릿을 직접 넣으면 Git 히스토리에 민감 정보가 남게 됩니다.&lt;/p&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;과의 연동 패턴이며, ESO는 그 다리 역할을 합니다. ESO는 HashiCorp Vault, AWS Secrets Manager, Google Secret Manager, Azure Key Vault, CyberArk Conjur 등 다양한 외부 저장소를 지원합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ESO는 설치 시 세 가지 핵심 CRD(Custom Resource Definition)를 클러스터에 등록합니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style8&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;CRD&lt;/td&gt;
&lt;td&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;범위&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;역할&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SecretStore&lt;/td&gt;
&lt;td&gt;네임스페이스&lt;/td&gt;
&lt;td&gt;특정 네임스페이스에서만 사용 가능한 외부 저장소 연결 정보 정의&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ClusterSecretStore&lt;/td&gt;
&lt;td&gt;클러스터 전체&lt;/td&gt;
&lt;td&gt;모든 네임스페이스에서 참조 가능한 전역 연결 정보 정의&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ExternalSecret&lt;/td&gt;
&lt;td&gt;네임스페이스&lt;/td&gt;
&lt;td&gt;어떤 시크릿을 가져와 어떤 Kubernetes Secret으로 생성할지 정의&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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  핵심 동작 원리와 리소스 구조&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ESO의 동작은 크게 네 단계로 구분할 수 있습니다.&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;선언&lt;/b&gt; &amp;mdash; 사용자가 SecretStore(연결 정보)와 ExternalSecret(가져올 시크릿 정의)을 클러스터에 적용합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;인증&lt;/b&gt; &amp;mdash; ESO 컨트롤러가 SecretStore에 정의된 방법으로 외부 저장소(Vault 등)에 인증합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;동기화&lt;/b&gt; &amp;mdash; ESO가 외부 저장소에서 값을 읽어 Kubernetes Secret을 생성하고, refreshInterval마다 주기적으로 갱신합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;사용&lt;/b&gt; &amp;mdash; 애플리케이션 Pod가 생성된 Kubernetes Secret을 환경변수나 볼륨으로 일반적인 방식 그대로 마운트합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기준점은 &lt;b&gt;외부 저장소(Vault)&lt;/b&gt;가 아니라 &lt;b&gt;쿠버네티스 클러스터&lt;/b&gt;입니다. ESO가 능동적으로 외부 저장소에 요청해서 값을 가져오는 Pull 방식입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;731&quot; data-origin-height=&quot;605&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/vjK0q/dJMcaiDqUIu/Q5jw86OHUgN1PzBXV8XJq0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/vjK0q/dJMcaiDqUIu/Q5jw86OHUgN1PzBXV8XJq0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/vjK0q/dJMcaiDqUIu/Q5jw86OHUgN1PzBXV8XJq0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FvjK0q%2FdJMcaiDqUIu%2FQ5jw86OHUgN1PzBXV8XJq0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;557&quot; height=&quot;461&quot; data-origin-width=&quot;731&quot; data-origin-height=&quot;605&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;0단계 &amp;mdash; ESO 설치&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실습을 시작하기 전에 ESO를 클러스터에 먼저 설치해야 합니다. Helm을 사용하는 것이 가장 간편합니다&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;oxygene&quot; style=&quot;color: #14181f;&quot;&gt;&lt;code&gt;# ESO Helm 저장소 추가
helm repo add external-secrets https://charts.external-secrets.io
helm repo update

# external-secrets 네임스페이스에 ESO 설치
helm install external-secrets \
   external-secrets/external-secrets \
   -n external-secrets \
   --create-namespace \
   --set installCRDs=true

# 설치 확인 (CRD 등록 여부)
kubectl api-resources | grep -i externalsecret
kubectl api-resources | grep -i secretstore&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설치가 완료되면 SecretStore, ClusterSecretStore, ExternalSecret 등의 CRD가 클러스터에 등록됩니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1단계 &amp;mdash; Vault 토큰을 Kubernetes Secret으로 저장&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ESO가 Vault에 인증하려면 인증 정보가 필요합니다. 가장 간단한 방식은 Vault 토큰을 Kubernetes Secret으로 미리 저장해 두는 것입니다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;# vault-token.yaml
# ESO가 Vault에 접속할 때 사용할 토큰을 저장
apiVersion: v1
kind: Secret
metadata:
  name: vault-token
data:
  # &quot;dev-only-token&quot;을 Base64 인코딩한 값
  token: ZGV2LW9ubHktdG9rZW4=
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 운영 환경에서는 토큰 방식보다 Kubernetes 네이티브 인증(AppRole, Kubernetes Auth Method) 사용을 권장합니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2단계 &amp;mdash; SecretStore: Vault 연결 정보 정의&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SecretStore는 &quot;어떤 Vault 서버에, 어떻게 인증해서 접속할 것인가&quot;를 정의합니다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;# secret-store.yaml
# Vault 서버 접속 정보 및 인증 방식 설정
apiVersion: external-secrets.io/v1
kind: SecretStore
metadata:
  name: vault-backend
  namespace: default
spec:
  provider:
    vault:
      server: &quot;http://my.vault.server:8200&quot;
      path: &quot;secret&quot;          # Vault KV 엔진의 마운트 경로
      version: &quot;v2&quot;           # KV 시크릿 엔진 버전 (v1 또는 v2)
      auth:
        tokenSecretRef:
          name: &quot;vault-token&quot; # 위에서 생성한 K8s Secret 이름
          key: &quot;token&quot;        # Secret 내 키 이름
&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;3단계 &amp;mdash; Vault에 데이터 저장&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ESO가 가져올 데이터를 Vault에 미리 저장합니다.&lt;/p&gt;
&lt;pre class=&quot;applescript&quot;&gt;&lt;code&gt;# KV v2 방식으로 시크릿 저장
vault kv put secret/foo my-value=s3cr3t

# 저장 확인
vault kv get secret/foo

# 현재 활성화된 시크릿 엔진 목록 확인
vault secrets list -detailed
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;4단계 &amp;mdash; ExternalSecret: 가져올 시크릿 정의&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ExternalSecret은 &quot;어느 SecretStore에서, 어떤 키를, Kubernetes의 어떤 이름으로 만들 것인가&quot;를 선언합니다.&lt;/p&gt;
&lt;pre class=&quot;dts&quot;&gt;&lt;code&gt;# external-secret.yaml
apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata:
  name: vault-example
  namespace: default
spec:
  refreshInterval: &quot;1m&quot;        # 동기화 주기 (60초마다 Vault에서 최신 값 갱신)
  secretStoreRef:
    name: vault-backend         # 위에서 만든 SecretStore 이름
    kind: SecretStore
  target:
    name: example-sync          # 생성될 Kubernetes Secret의 이름
  data:
    - secretKey: foobar         # K8s Secret에서 사용할 키 이름
      remoteRef:
        key: foo                # Vault의 시크릿 경로
        property: my-value      # Vault 시크릿 내 특정 필드
&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;# 적용
kubectl apply -f vault-token.yaml
kubectl apply -f secret-store.yaml
kubectl apply -f external-secret.yaml

# 생성된 Secret 확인
kubectl get secret example-sync

# Base64 디코딩으로 실제 값 확인
kubectl get secret example-sync -o jsonpath='{.data.foobar}' | base64 -d
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;적용 후 잠시 기다리면 ESO가 자동으로 example-sync라는 이름의 Kubernetes Secret을 생성합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;⚠️ 주의사항&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt; 토큰 방식 인증은 운영 환경에 적합하지 않을 수 있습니다.&lt;/b&gt; Dev 모드의 Vault 루트 토큰이나 정적 토큰은 편의를 위한 설정입니다. 운영 환경에서는 Vault의 &lt;b&gt;Kubernetes Auth Method&lt;/b&gt;(서비스 어카운트 토큰 기반)나 &lt;b&gt;AppRole&lt;/b&gt; 방식을 통해 단기 토큰을 자동 갱신하는 방식이 더 안전합니다. Kubernetes Auth Method는 클러스터 내 SA 토큰으로 Vault에 인증하는 네이티브 방식이며, AppRole은 Vault 자체의 인증 방식으로 CI/CD 파이프라인 등 외부 환경에서 주로 활용됩니다&lt;/p&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;ClusterSecretStore는 범위를 신중하게 설정하는 것이 좋습니다.&lt;/b&gt; ClusterSecretStore는 클러스터의 모든 네임스페이스에서 참조 가능하므로, 의도치 않은 네임스페이스가 시크릿에 접근할 수 있습니다. namespaceSelector나 명시적 namespaces 목록으로 접근 가능한 네임스페이스를 제한하는 것이 좋습니다. 꼭 필요하지 않다면 네임스페이스 범위의 SecretStore를 우선 사용하는 방향을 고려해 볼 수 있습니다.&lt;/p&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;metadataPolicy: Fetch는 KV v2에서만 동작합니다.&lt;/b&gt; ExternalSecret에서 레이블 등 메타데이터를 함께 가져오는 metadataPolicy: Fetch 기능은 Vault KV 시크릿 엔진 &lt;b&gt;v2에서만&lt;/b&gt; 지원됩니다. v1 엔진을 사용하는 경우 해당 옵션은 무시되거나 오류가 발생할 수 있습니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  SecretStore vs ClusterSecretStore 비교&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style8&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;항목&lt;/td&gt;
&lt;td&gt;SecretStore&lt;/td&gt;
&lt;td&gt;ClusterSecretStore&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;범위&lt;/td&gt;
&lt;td&gt;특정 네임스페이스&lt;/td&gt;
&lt;td&gt;클러스터 전체&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;참조 가능 주체&lt;/td&gt;
&lt;td&gt;같은 네임스페이스의 ExternalSecret&lt;/td&gt;
&lt;td&gt;모든 네임스페이스의 ExternalSecret&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;보안 경계&lt;/td&gt;
&lt;td&gt;네임스페이스 단위로 격리&lt;/td&gt;
&lt;td&gt;중앙 집중 관리 가능, 권한 범위 주의 필요&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;권장 상황&lt;/td&gt;
&lt;td&gt;팀별/서비스별 독립적 Vault 접근&lt;/td&gt;
&lt;td&gt;플랫폼 팀이 전사 시크릿을 중앙 관리할 때&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  ESO를 도입하면서 생각해볼 것들&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ESO는 &quot;시크릿을 Git에 넣지 않는다&quot;는 원칙을 지키면서도 GitOps 워크플로를 유지할 수 있는 실용적인 방법입니다. ExternalSecret 리소스 자체는 민감 정보를 담지 않으므로 Git에 커밋해도 무방하고 Vault에서 값이 바뀌면 refreshInterval에 따라 자동으로 Kubernetes Secret이 갱신됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 ESO가 모든 것을 해결해주지는 않습니다. ESO가 최종적으로 만드는 것은 결국 일반 Kubernetes Secret이기 때문에, etcd 암호화나 RBAC 설정이 함께 갖춰져 있지 않으면 보안 효과가 반감될 수 있습니다. ESO는 &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;또한 Vault를 처음 도입하는 팀이라면 운영 부담도 고려 대상입니다. Vault 자체가 HA 구성, 초기화, unseal 관리 등 학습 곡선이 있는 도구입니다. AWS나 GCP 환경을 이미 사용하고 있다면 각 클라우드의 네이티브 시크릿 관리 서비스(AWS Secrets Manager, GCP Secret Manager)를 ESO와 연동하는 방식이 운영 복잡도를 낮추는 대안이 될 수 있습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  참고 자료&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://external-secrets.io/main/introduction/getting-started/&quot;&gt;External Secrets Operator 공식 문서&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://external-secrets.io/latest/provider/hashicorp-vault/&quot;&gt;ESO + HashiCorp Vault 공식 가이드&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://external-secrets.io/latest/guides/security-best-practices/&quot;&gt;ESO Security Best Practices&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://earthly.dev/blog/eso-with-hashicorp-vault/&quot;&gt;ESO with HashiCorp Vault &amp;mdash; Earthly Blog&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Concepts/security</category>
      <category>ESO</category>
      <category>ExternalSecretsOperator</category>
      <category>Hashicrop</category>
      <category>Secrets</category>
      <category>SecretStore</category>
      <category>security</category>
      <category>vault</category>
      <author>ranygo12</author>
      <guid isPermaLink="true">https://elly1108.tistory.com/22</guid>
      <comments>https://elly1108.tistory.com/22#entry22comment</comments>
      <pubDate>Sat, 23 May 2026 17:33:08 +0900</pubDate>
    </item>
    <item>
      <title>HashiCorp Vault: 시크릿 관리의 표준, 개념부터 Kubernetes 연동까지</title>
      <link>https://elly1108.tistory.com/21</link>
      <description>&lt;h1&gt;&amp;nbsp;&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;애플리케이션을 운영하다 보면 데이터베이스 비밀번호, API 키, TLS 인증서 같은 민감한 정보를 어디에, 어떻게 저장할지 고민하게 됩니다. 환경변수나 설정 파일에 평문으로 저장하는 방식은 유출 시 치명적인 보안 사고로 이어질 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HashiCorp Vault는 이러한 시크릿(비밀 정보)을 안전하게 중앙화하여 관리하고, 다양한 애플리케이션과 유연하게 연동할 수 있도록 설계된 시크릿 관리 플랫폼입니다. 특히 Kubernetes 기반의 현대적인 클라우드 네이티브 환경에서 ESO(External Secrets Operator), Kyverno, Dex와 함께 쓰이며 그 진가를 발휘합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt; ️ HashiCorp Vault란 무엇인가&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt; HashiCorp Vault는 시크릿을 암호화하여 저장하고, 인증된 클라이언트에게만 정책 기반으로 접근을 허용하는 시크릿 관리 플랫폼입니다.&lt;br /&gt;&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HashiCorp Vault는 암호화된 형태로 민감한 데이터를 저장하는 도구입니다. 핵심 철학은 단순합니다. 설령 스토리지가 공격자에게 탈취되더라도, Vault가 복호화하기 전까지는 데이터가 암호화된 상태로 유지되므로 내용을 확인할 수 없습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Vault의 기본 데이터 저장 단위는 Key/Value(키/값) 형태이며, 이 구조 위에 다양한 Secrets Engine을 탑재하여 데이터베이스 자격증명, PKI 인증서, 클라우드 자격증명 등을 동적으로 생성하고 관리할 수 있습니다.&lt;/p&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;유사한 목적의 도구로는 AWS Secrets Manager가 있습니다. 보안 정보당 월 USD 0.40의 비용이 발생하며, AWS 생태계에 종속된다는 특징이 있습니다. 반면 Vault는 오픈소스 기반으로 클라우드 벤더에 종속되지 않으며, 멀티 클라우드 및 온프레미스 환경 모두에서 유연하게 활용할 수 있다는 점에서 차별점을 가집니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  코어 볼트 워크플로우: 인증부터 접근까지&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Vault가 동작하는 핵심 흐름은 네 단계로 구성됩니다.&lt;/p&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;1단계 &amp;mdash; 인증 (Authenticate)&lt;/b&gt; 클라이언트가 자신의 신원을 증명합니다. Vault는 이를 확인하고, 해당 신원에 맞는 정책(Policy)이 연결된 토큰을 생성합니다.&lt;/p&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;2단계 &amp;mdash; 유효성 검사 (Validate)&lt;/b&gt; GitHub, LDAP, OIDC 등 외부 신뢰 소스를 통해 클라이언트의 신원을 실제로 검증합니다. Vault 자체가 인증 기관이 되는 것이 아니라, 신뢰할 수 있는 외부 시스템에 위임하는 방식입니다.&lt;/p&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;3단계 &amp;mdash; 권한 부여 (Authorize)&lt;/b&gt; 정책(Policy)을 통해 클라이언트가 접근 가능한 경로(path)와 수행 가능한 작업의 범위(read, write, delete 등)를 결정합니다.&lt;/p&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;4단계 &amp;mdash; 접근 권한 부여 (Access)&lt;/b&gt; 검증된 신원에 맞는 토큰을 최종 발급하여, 허용된 비밀 정보 및 기능에 접근할 수 있게 합니다. 이 토큰은 유효 기간(TTL)을 가지며 만료되면 자동으로 폐기됩니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  Vault의 핵심 기능&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2.1 데이터 암호화/복호화: Barrier 계층&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Vault 서버가 시작되면, 데이터는 스토리지 백엔드에 기록됩니다. 이 스토리지 백엔드는 Vault의 신뢰 경계 밖에 위치하는 &quot;신뢰할 수 없는 영역&quot;입니다. 공격자가 스토리지에 접근하더라도 데이터는 Vault가 복호화하기 전까지 암호화된 상태를 유지합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 암호화를 담당하는 것이 바로 **Barrier(암호화 계층)**입니다. Vault는 데이터를 스토리지 백엔드로 전송하기 전에 반드시 Barrier를 통해 암호화합니다. Barrier 내부에는 Token Store, Policy Store, Core, Path Routing, Secret Engine, Auth Method, Audit Device 등이 포함되어 있으며, 이 모든 컴포넌트가 암호화된 경계 안에서 동작합니다.&lt;/p&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;봉인(Sealed) 및 봉인 해제(Unseal) 메커니즘&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Vault 서버가 처음 시작되면 sealed state(봉인된 상태)로 진입합니다. 봉인된 상태에서는 어떠한 작업도 수행할 수 없으며, 운영을 시작하려면 반드시 봉인을 해제해야 합니다. 이 봉인 해제 과정은 샤미르(Shamir) 비밀 분산 기법을 기반으로 합니다.&lt;/p&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개의 Unseal Key가 생성되며, 그 중 최소 3개를 모아 입력해야 Root Key가 완성됩니다. Root Key는 실제 데이터를 암호화하는 Encryption Key를 해독하는 데 사용되며, 이 과정이 완료되면 Vault는 Unsealed state로 전환됩니다.&lt;/p&gt;
&lt;pre class=&quot;cs&quot;&gt;&lt;code&gt;# 금고 초기화 (최초 1회만 실행 &amp;mdash; Unseal Key와 Root Token이 출력됩니다)
vault operator init

# Unseal Key를 3회 입력하여 봉인 해제
vault operator unseal  # Key 1 입력
vault operator unseal  # Key 2 입력
vault operator unseal  # Key 3 입력

# Sealed: false 가 출력되면 봉인 해제 성공
vault status
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구분 봉인 해제 키 (Unseal Keys) 복구 키 (Recovery Keys)&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;운영 방식&lt;/td&gt;
&lt;td&gt;Shamir Seal (수동)&lt;/td&gt;
&lt;td&gt;Auto Unseal (자동)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;서버 기동 시&lt;/td&gt;
&lt;td&gt;관리자가 직접 입력해야 함&lt;/td&gt;
&lt;td&gt;KMS가 자동으로 처리&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;관리 작업 승인&lt;/td&gt;
&lt;td&gt;이 키로 승인&lt;/td&gt;
&lt;td&gt;이 키로 승인&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;보안적 의미&lt;/td&gt;
&lt;td&gt;마스터 키를 직접 복구&lt;/td&gt;
&lt;td&gt;마스터 키 복구 권한은 없으나 권한 증명용&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보안을 강화하려면 AWS KMS, GCP KMS, Azure Key Vault 또는 하드웨어 보안 모듈(HSM)을 통해 자동 봉인 해제(Auto Unseal)를 구성할 수 있습니다. 단, 이 경우 해당 클라우드 서비스 의존성이 생긴다는 점을 고려하는 것이 좋습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2.2 API 제공: 모든 것은 HTTP로&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Vault는 HTTP/S API를 통해 클라이언트에게 데이터 조회, 생성, 수정, 삭제 기능을 제공합니다. Token, SSO 등 다양한 방식으로 인증을 처리하고, Vault Policy를 통해 인가(Authorization) 기능을 수행합니다.&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;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;클라이언트가 인증 수단(토큰, OIDC 토큰 등)과 함께 Vault API를 호출합니다.&lt;/li&gt;
&lt;li&gt;Vault는 Policy Store에서 해당 토큰의 정책을 확인합니다.&lt;/li&gt;
&lt;li&gt;권한이 있으면 요청을 처리하고, 없으면 permission denied를 반환합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 구조 덕분에 Vault는 단순한 비밀 저장소를 넘어, 암호화-as-a-service (Transit Secret Engine)나 동적 데이터베이스 자격증명 발급 등 다양한 용도로 활용될 수 있습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2.3 OIDC와 Vault 연동: Dex를 활용한 SSO&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Vault는 OIDC(OpenID Connect) 프로토콜을 통해 Dex 같은 외부 ID 공급자와 연동하여 SSO(Single Sign-On)를 구현할 수 있습니다. 이를 통해 개발자는 별도의 Vault 계정 없이, 기존 GitHub&amp;middot;Google&amp;middot;LDAP 계정으로 Vault에 로그인할 수 있습니다.&lt;/p&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;1단계: Dex 설정 (ID 공급자 쪽 작업)&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;1c&quot;&gt;&lt;code&gt;# Dex staticClients 설정 예시
staticClients:
  - id: vault-client-id          # Vault 설정과 반드시 일치해야 함
    name: 'HashiCorp Vault'
    secret: vault-client-secret  # Vault 설정과 반드시 일치해야 함
    redirectURIs:
      # [UI용] Vault 서버의 실제 도메인 주소
      - 'https://vault.example.com:8200/ui/vault/auth/oidc/oidc/callback'
      # [CLI용] 로컬 주소 (포트 8250은 기본값)
      - 'http://localhost:8250/oidc/callback'
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2&lt;/b&gt;&lt;b&gt;단계: Vault 설정 (금고 쪽 작업)&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;# OIDC 인증 방식 활성화
vault auth enable oidc

# Dex와 연결 설정
vault write auth/oidc/config \
    oidc_discovery_url=&quot;https://dex.example.com&quot; \
    oidc_client_id=&quot;vault-client-id&quot; \
    oidc_client_secret=&quot;vault-client-secret&quot;

# Role 생성 (UI와 CLI 리다이렉트 URI 모두 등록)
vault write auth/oidc/role/test-role \
    user_claim=&quot;email&quot; \
    allowed_redirect_uris=&quot;https://vault.example.com:8200/ui/vault/auth/oidc/oidc/callback&quot; \
    allowed_redirect_uris=&quot;http://localhost:8250/oidc/callback&quot; \
    policies=&quot;default,reader-policy&quot; \
    role_type=&quot;oidc&quot; \
    oidc_scopes=&quot;openid,email,profile&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3단계: 실제 로그인&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;UI를 통한 로그인은 브라우저에서 Vault 주소에 접속 후 Method로 OIDC를 선택하고 Role을 입력한 뒤, Dex 화면에서 소셜 로그인을 완료하면 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CLI를 사용하는 경우 아래 명령어 한 줄로 동일하게 진행할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;oxygene&quot;&gt;&lt;code&gt;# CLI를 통한 OIDC 로그인 (브라우저가 자동으로 열립니다)
vault login -method=oidc role=test-role
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2.4 시크릿 인증 흐름도에서의 역할: ESO, Kyverno, etcd와의 협력&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Vault는 Kubernetes 환경에서 단독으로 동작하지 않습니다. ESO(External Secrets Operator), Kyverno, etcd Encryption과 함께 다층적인 시크릿 보호 체계를 구성합니다.&lt;/p&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;table style=&quot;border-collapse: collapse; width: 100%; height: 104px;&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;단계&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;도구&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;역할&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;저장 단계&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;&lt;b&gt;Vault&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;시크릿을 가장 안전하게 암호화하여 보관&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;전달 단계&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;&lt;b&gt;ESO&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;Vault에서 K8s Secret으로 안전한 통로를 통해 복사&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;감시 단계&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;&lt;b&gt;Kyverno&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;잘못된 시크릿 접근이나 배포 설정을 실시간 차단&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;물리 단계&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;&lt;b&gt;etcd Encryption&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;서버 디스크가 탈취되어도 시크릿을 볼 수 없게 암호화&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 주목할 점이 있습니다. ESO가 Vault의 암호화된 데이터를 자동으로 인식하여 K8s Secret을 생성할 때, 해당 Secret은 base64로 인코딩된 평문이기 때문에 그 자체로는 보안이 완전하지 않습니다. 이 취약 지점을 보완하기 위해 Kyverno 정책을 통해 잘못된 접근을 실시간으로 차단하고, 전달 단계에서 RBAC 정책을 통해 접근을 최소화하는 것이 중요합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&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;정적 관리 vs. 동적 연동&lt;/b&gt;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 118px;&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: 17px;&quot;&gt;
&lt;td style=&quot;height: 17px;&quot;&gt;구분&lt;/td&gt;
&lt;td style=&quot;height: 17px;&quot;&gt;정적 관리 (Static)&lt;/td&gt;
&lt;td style=&quot;height: 17px;&quot;&gt;동적 연동 (Dynamic)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;주요 도구&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;RBAC, Namespace 격리&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;Dex Info + Kyverno + Generate&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;장점&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;구조가 단순하고 장애 포인트가 적음&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;관리가 거의 필요 없는 완전 자동화&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;단점&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;팀이 늘어날 때마다 반복 설정 필요&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;정책 코드가 복잡하고 Webhook 의존도 높음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;실무 비중&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;약 80% (일반적인 기업)&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;약 20% (플랫폼 엔지니어링 지향)&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;동적 연동이 실질적으로 필요한 상황은 셀프 서비스 프로비저닝(개발자가 로그인하면 자동으로 네임스페이스와 Vault 접근 권한이 생성되는 경우), 긴급 조치 및 감사(접속자 신분에 따라 클러스터 수정 권한을 실시간으로 제어해야 할 때), 그리고 멀티 테넌시 환경에서 팀별로 접근 가능한 경로를 동적으로 필터링해야 할 때입니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt; ️ HashiCorp Vault 설치 및 시작하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로컬 설치&lt;/p&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;# macOS
brew tap hashicorp/tap
brew install hashicorp/tap/vault

# Linux (Ubuntu/Debian 계열)
wget -O - https://apt.releases.hashicorp.com/gpg | \
  sudo gpg --dearmor -o /usr/share/keyrings/hashicorp-archive-keyring.gpg

echo &quot;deb [arch=$(dpkg --print-architecture) \
  signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] \
  https://apt.releases.hashicorp.com \
  $(grep -oP '(?&amp;lt;=UBUNTU_CODENAME=).*' /etc/os-release || lsb_release -cs) main&quot; | \
  sudo tee /etc/apt/sources.list.d/hashicorp.list

sudo apt update &amp;amp;&amp;amp; sudo apt install vault

# 설치 확인
vault --version
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Kubernetes 위에 Helm으로 설치 (HA 모드)&lt;/p&gt;
&lt;pre class=&quot;vbnet&quot;&gt;&lt;code&gt;# 네임스페이스 생성
kubectl create namespace vault

# Helm을 이용한 HA 모드 설치 (워커 노드 3개 활용)
helm install vault hashicorp/vault \
  --namespace vault \
  --set='server.ha.enabled=true' \
  --set='server.ha.raft.enabled=true' \
  --set='server.replicas=3'
&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;crystal&quot;&gt;&lt;code&gt;# 1. 설정 및 데이터 디렉토리 생성
sudo mkdir -p /etc/vault.d
sudo mkdir -p /var/lib/vault/data

# 2. vault 전용 유저 생성을 권장합니다 (보안상)
# sudo chown -R vault:vault /etc/vault.d
# sudo chown -R vault:vault /var/lib/vault/data
&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;# /etc/vault.d/vault-config.hcl 설정 파일 생성
cat &amp;lt;&amp;lt;EOF &amp;gt; /etc/vault.d/vault-config.hcl
listener &quot;tcp&quot; {
  address     = &quot;0.0.0.0:8200&quot;
  tls_disable = &quot;true&quot;   # 운영 환경에서는 TLS 활성화를 권장합니다
}

storage &quot;raft&quot; {
  path    = &quot;/var/lib/vault/data&quot;  # 실제 데이터가 저장될 디스크 경로
  node_id = &quot;node1&quot;
}

# cluster_addr: Vault 서버끼리 통신할 때 쓰는 주소
cluster_addr = &quot;http://&amp;lt;마스터-노드-IP&amp;gt;:8201&quot;

# api_addr: 클라이언트(ESO, 애플리케이션, 사용자)가 접속할 때 쓰는 주소
api_addr = &quot;http://&amp;lt;마스터-노드-IP&amp;gt;:8200&quot;
EOF

# 서버 실행
vault server -config=/etc/vault.d/vault-config.hcl
&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;# 터미널에 Vault 서버 주소 등록
export VAULT_ADDR='http://&amp;lt;마스터-노드-IP&amp;gt;:8200'

# 금고 초기화 (최초 1회만 &amp;mdash; Unseal Key와 Root Token을 반드시 안전하게 보관하세요)
vault operator init

# Unseal Key 3개를 순서대로 입력
vault operator unseal  # Key 1
vault operator unseal  # Key 2
vault operator unseal  # Key 3

# 토큰 환경 변수 설정
export VAULT_TOKEN=&quot;초기화_때_받은_진짜_토큰&quot;

# KV v2 시크릿 엔진 활성화
vault secrets enable -path=secret kv-v2
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Python 클라이언트로 시크릿 저장 및 조회&lt;/p&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;# Python 및 hvac 라이브러리 설치
sudo apt update
sudo apt install python3 python3-pip python3-venv -y
pip install --upgrade pip
pip install hvac
&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;import hvac
import sys

# ---------------------------------------------------
# 1. Vault 클라이언트 초기화 (인증 준비)
# ---------------------------------------------------
# 주의: 실제 마스터 노드의 IP와 초기화 때 받은 토큰으로 교체하세요.
client = hvac.Client(
    url='http://&amp;lt;마스터-노드-IP&amp;gt;:8200',
    token='hvs.XXXXXXX...',  # 초기화 때 받은 Root Token 입력
)

# ---------------------------------------------------
# 2. 비밀 정보 저장 (쓰기)
# ---------------------------------------------------
create_response = client.secrets.kv.v2.create_or_update_secret(
    path='my-secret-password',
    secret=dict(password='Hashi123'),  # 저장할 key-value 쌍
)
print('Secret written successfully.')

# ---------------------------------------------------
# 3. 비밀 정보 읽기 (조회)
# ---------------------------------------------------
read_response = client.secrets.kv.v2.read_secret_version(
    path='my-secret-password'
)
password = read_response['data']['data']['password']

# 4. 결과 검증
if password != 'Hashi123':
    sys.exit('unexpected password')

print('Access granted!')
&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;vala&quot;&gt;&lt;code&gt;# 예상 실행 결과
# Secret written successfully.
# Access granted!
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;⚠️ 주의사항&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Unseal Key와 Root Token은 절대 분실하거나 단일 장소에 보관하지 않는 것이 좋습니다.&lt;/b&gt; vault operator init 실행 시 출력되는 Unseal Key 5개와 Initial Root Token은 단 한 번만 표시됩니다. 이를 분실하면 Vault에 저장된 모든 데이터를 영구적으로 복구할 수 없게 됩니다. 안전한 오프라인 저장소 또는 신뢰할 수 있는 키 관리 시스템에 나누어 보관하는 것이 좋습니다.&lt;/p&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;운영 환경에서는 TLS를 반드시 활성화하는 것이 좋습니다.&lt;/b&gt; 위 예시 설정에서 tls_disable = &quot;true&quot;로 설정된 부분은 학습 및 내부 테스트 용도입니다. 실제 운영 환경에서는 TLS 인증서를 적용하여 네트워크 구간에서의 데이터 도청을 방지해야 합니다. TLS 없이 운영할 경우, API 토큰이 네트워크에 평문으로 노출될 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;K8s Secret은 base64 인코딩이지, 암호화가 아닙니다.&lt;/b&gt; ESO가 Vault의 시크릿을 K8s Secret으로 변환할 때, K8s Secret은 base64로 인코딩된 평문으로 저장됩니다. 이는 암호화가 아니므로 etcd Encryption과 Kyverno 정책을 함께 적용하여 다층적으로 보호하는 구성을 고려하는 것이 좋습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;nbsp;Root Token은 운영 중 상시 사용하지 않는 것이 좋습니다.&lt;/b&gt; Root Token은 Vault의 모든 권한을 가진 슈퍼 토큰입니다. 초기 설정 이후에는 최소 권한 원칙에 따라 별도의 정책(Policy)이 부여된 일반 토큰이나 AppRole 인증 방식을 사용하는 것이 권장됩니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  Vault vs AWS Secrets Manager 비교&lt;/h2&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: 168px;&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: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;항목&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;HashiCorp Vault&amp;nbsp;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;AWS Secrets Manager&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;라이선스&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;오픈소스 (BSL 1.1)&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;상용 (관리형 SaaS)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;비용&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;인프라 비용만 발생&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;시크릿당 월 USD 0.40&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;클라우드 종속성&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;없음 (멀티 클라우드 지원)&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;AWS 종속&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;인증 방식&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;Token, OIDC, LDAP, AppRole 등 다양&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;IAM 기반&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;운영 부담&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;직접 운영 필요&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;완전 관리형&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;Kubernetes 연동&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;ESO, 공식 Sidecar 등 다양한 방법&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;ESO, AWS Pod Identity&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;동적 시크릿&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;지원 (DB, PKI, Cloud 등)&lt;/td&gt;
&lt;td style=&quot;height: 21px;&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;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  Vault를 공부하며 새롭게 이해한 것들&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Vault의 진짜 가치는 단순히 비밀번호를 안전하게 보관하는 것을 넘어, &lt;b&gt;시크릿의 생애주기 전체를 자동화&lt;/b&gt;한다는 점에 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 데이터베이스 자격증명을 Vault의 Dynamic Secrets 기능으로 관리하면, 애플리케이션이 필요할 때마다 유효 기간이 짧은 임시 자격증명을 자동으로 발급받고 만료 후 자동 폐기할 수 있습니다. 이는 정적인 비밀번호를 공유하는 방식보다 훨씬 강력한 보안 모델입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Kubernetes 환경에서는 Vault + ESO + Kyverno 조합이 사실상 표준 패턴으로 자리잡고 있습니다. 단, 모든 조직에 동적 연동(Dynamic)이 필요한 것은 아닙니다. 실무 비중을 보면 정적 RBAC 기반 관리가 80%, 완전 동적 연동이 20% 수준입니다. 팀의 규모와 운영 성숙도에 따라 정적 관리부터 시작하여 점진적으로 자동화를 확장하는 전략이 현실적입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보안은 도구 하나로 완성되지 않습니다. Vault가 저장 단계를 책임지고, ESO가 전달을 맡고, Kyverno가 감시하고, etcd Encryption이 물리적 보호를 담당하는 구조처럼, 각 계층에서 역할을 분담하는 심층 방어(Defense in Depth) 전략이 클라우드 네이티브 보안의 핵심입니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  참고 자료&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;HashiCorp Vault 공식 사이트: &lt;a href=&quot;https://www.hashicorp.com/en/products/vault&quot;&gt;https://www.hashicorp.com/en/products/vault&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Vault 시작 가이드: &lt;a href=&quot;https://developer.hashicorp.com/vault/tutorials/get-started/why-use-vault&quot;&gt;https://developer.hashicorp.com/vault/tutorials/get-started/why-use-vault&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Vault 공식 문서: &lt;a href=&quot;https://developer.hashicorp.com/vault/docs&quot;&gt;https://developer.hashicorp.com/vault/docs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Vault OIDC/JWT 인증: &lt;a href=&quot;https://developer.hashicorp.com/vault/docs/auth/jwt&quot;&gt;https://developer.hashicorp.com/vault/docs/auth/jwt&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;ESO + Vault 연동: &lt;a href=&quot;https://earthly.dev/blog/eso-with-hashicorp-vault/&quot;&gt;https://earthly.dev/blog/eso-with-hashicorp-vault/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Concepts/security</category>
      <category>AWS</category>
      <category>Hashicrop</category>
      <category>Kuvernetes</category>
      <category>Secrets</category>
      <category>vault</category>
      <author>ranygo12</author>
      <guid isPermaLink="true">https://elly1108.tistory.com/21</guid>
      <comments>https://elly1108.tistory.com/21#entry21comment</comments>
      <pubDate>Sat, 23 May 2026 17:07:49 +0900</pubDate>
    </item>
    <item>
      <title>Grafana GitHub 토큰 탈취 사건: 소스코드 유출과 협박 시도</title>
      <link>https://elly1108.tistory.com/20</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;GitHub 액세스 토큰 하나가 어떻게 오픈소스 생태계의 핵심 기업을 위협할 수 있는지를 보여주는 사건이 발생하였습니다. Grafana를 대상으로 한 이번 침해 사고는 토큰 관리 미흡이 가져오는 실질적인 위험을 다시 한번 상기시켜 줍니다. 소스코드 유출에서 협박 시도까지 이어진 이 흐름은 현대 공급망 보안에서 자격증명 보호가 얼마나 중요한지를 잘 보여줍니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Grafana의 GitHub 액세스 토큰이 탈취되어 소스코드가 유출되었으며, 공격자는 이를 빌미로 금전을 요구하였으나 Grafana는 FBI 지침에 따라 협박에 응하지 않았습니다.&lt;/span&gt;&lt;/blockquote&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;  사건 개요 및 타임라인&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Grafana는 인가되지 않은 외부 당사자가 자사의 GitHub 환경에 접근하여 소스코드를 내려받는 사건이 발생하였다고 공식 발표하였습니다. 회사 측은 포렌식 조사를 즉각 착수하였으며, 유출된 자격증명은 이미 무효화 처리하였고 추가적인 보안 조치를 적용하였다고 밝혔습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사건의 주요 경과는 다음과 같습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;침해 발생&lt;/b&gt;: 정확한 일시는 미공개, Grafana는 &quot;최근에 인지&quot;하였다고만 발표&lt;/li&gt;
&lt;li&gt;&lt;b&gt;초기 대응&lt;/b&gt;: 포렌식 분석 착수, 탈취 토큰 즉시 무효화&lt;/li&gt;
&lt;li&gt;&lt;b&gt;협박 시도&lt;/b&gt;: 공격자가 탈취한 데이터베이스를 공개하지 않는 조건으로 금전 지불을 요구하는 협박을 시도하였습니다.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;협박 거부&lt;/b&gt;: Grafana는 FBI 지침을 근거로 몸값 지불을 거부하였습니다.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;책임 주장&lt;/b&gt;: CoinbaseCartel이라는 사이버범죄 그룹이 이번 사건에 대한 책임을 주장하였다는 보고가 있습니다.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;  원인 분석 및 공격 그룹 특성&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;침해 경로&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공격자는 Grafana의 GitHub 환경에 접근할 수 있는 토큰을 획득하여 소스코드 전체를 내려받았습니다. GitHub 토큰은 코드 저장소에 대한 광범위한 접근 권한을 포함하는 경우가 많아, 단 하나의 토큰 탈취만으로도 전체 코드베이스 유출이 가능해집니다.&lt;/p&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 data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;공격 그룹: CoinbaseCartel&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CoinbaseCartel은 2025년 9월에 등장한 데이터 탈취&amp;middot;협박 전문 그룹으로, ShinyHunters, Scattered Spider, LAPSUS$ 생태계에서 파생된 조직으로 평가받고 있습니다. 전통적인 랜섬웨어 그룹과 달리 파일 암호화 없이 데이터 탈취 및 협박에만 집중하며, 헬스케어&amp;middot;기술&amp;middot;운송&amp;middot;제조&amp;middot;비즈니스 서비스 분야를 아울러 170개 피해 조직을 확보한 것으로 알려져 있습니다.&amp;nbsp;&lt;/p&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;이번 사건은 미국 교육 기술 기업 Instructure가 ShinyHunters 협박 그룹과 합의에 이른 사건이 알려진 지 불과 며칠 만에 발생한 것입니다. ShinyHunters는 미국 수천 개 학교와 대학에 속한 테라바이트 규모의 데이터를 유출하겠다고 위협한 바 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;⚠️ 주의사항&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;GitHub 토큰의 권한 범위를 최소화하는 것이 좋습니다.&lt;/b&gt; 개발 편의를 위해 광범위한 권한의 Personal Access Token(PAT)을 장기간 사용하는 경우가 많습니다. 그러나 토큰이 탈취되는 순간 해당 권한 전체가 공격자의 손에 넘어갑니다. Fine-grained token을 활용하여 저장소별, 권한별 최소 접근 원칙(Principle of Least Privilege)을 적용하는 것이 바람직합니다.&lt;/p&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; FBI는 몸값 협상에 응하지 않도록 권고하고 있으며, 지불한다고 해서 데이터를 되돌려받는다는 보장이 없을 뿐 아니라 더 많은 피해자를 대상으로 삼도록 공격자를 독려하는 결과를 낳을 수 있다고 경고하고 있습니다.&amp;nbsp;&lt;/p&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;소스코드 유출은 2차 공격의 발판이 됩니다.&lt;/b&gt; 코드베이스가 공격자의 손에 들어가면, 숨겨진 취약점이나 하드코딩된 비밀 값(API 키, 내부 엔드포인트 등)이 분석 대상이 될 수 있습니다. 유출 이후에는 코드 내 민감 정보 전수 점검이 필요합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;  실무 관점에서의 교훈&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 사건은 단순한 해킹 피해 공시를 넘어, 여러 실질적인 시사점을 남깁니다.&lt;/p&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; GitHub, AWS, GCP 등 주요 플랫폼은 비정상적인 토큰 사용을 감지하는 기능을 제공하고 있습니다. Secret scanning, token rotation 정책을 CI/CD 파이프라인에 통합하면, 침해 발생 전 또는 초기 단계에 탐지할 가능성이 높아집니다.&lt;/p&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; CoinbaseCartel처럼 랜섬웨어 없이 데이터 탈취만으로 협박하는 그룹이 증가하고 있습니다. 시스템 암호화가 없으므로 백업 복구로는 대응이 불가능하며, 데이터 유출 자체를 방지하는 예방 중심 보안 전략이 더욱 중요해지고 있습니다.&lt;/p&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; Grafana는 이번 사건을 비교적 신속하게 공개하고, 고객 데이터 미접근 사실을 명시하였습니다. 사고 대응에서의 투명성은 법적 의무를 넘어 고객 신뢰를 지키는 핵심 수단임을 보여줍니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;  참고 자료&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Grafana 공식 발표: &lt;a href=&quot;https://x.com/grafana/status/2055827123236171827&quot;&gt;https://x.com/grafana/status/2055827123236171827&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;FBI 랜섬웨어 대응 지침: &lt;a href=&quot;https://www.fbi.gov/how-we-can-help-you/scams-and-safety/common-frauds-and-scams/ransomware&quot;&gt;https://www.fbi.gov/how-we-can-help-you/scams-and-safety/common-frauds-and-scams/ransomware&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;CoinbaseCartel 위협 그룹 분석 (Halcyon): &lt;a href=&quot;https://www.halcyon.ai/jp/threat-group/coinbasecartel&quot;&gt;https://www.halcyon.ai/jp/threat-group/coinbasecartel&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;원문 기사: &lt;a href=&quot;https://thehackernews.com/2026/05/grafana-github-token-breach-led-to.html&quot;&gt;The Hacker News&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Security &amp;amp; Compliance</category>
      <author>ranygo12</author>
      <guid isPermaLink="true">https://elly1108.tistory.com/20</guid>
      <comments>https://elly1108.tistory.com/20#entry20comment</comments>
      <pubDate>Fri, 22 May 2026 22:31:16 +0900</pubDate>
    </item>
    <item>
      <title>AWS Bedrock RAG로 코드 스타일 &amp;amp; 보안 검사 자동화 구현하기</title>
      <link>https://elly1108.tistory.com/19</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;195&quot; data-origin-height=&quot;155&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bf4KuZ/dJMcaglh6xz/j7G3YtSsLkkTD9CDhrFlvk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bf4KuZ/dJMcaglh6xz/j7G3YtSsLkkTD9CDhrFlvk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bf4KuZ/dJMcaglh6xz/j7G3YtSsLkkTD9CDhrFlvk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbf4KuZ%2FdJMcaglh6xz%2Fj7G3YtSsLkkTD9CDhrFlvk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;195&quot; height=&quot;155&quot; data-origin-width=&quot;195&quot; data-origin-height=&quot;155&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;안녕하세요! 오늘은 AWS Amazon Bedrock의 Knowledge Base와 RAG(Retrieval-Augmented Generation) 기술을 활용해서 코드 스타일 검사와 보안 취약점 탐지를 자동화하는 시스템을 구축해보겠습니다. GitHub PR 자동 리뷰 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;전체 아키텍처를 간단히 설명하면 이렇습니다. S3에 PEP8 스타일 가이드와 OWASP 보안 문서를 저장해두고, Bedrock Knowledge Base가 이 문서들을 벡터로 인덱싱합니다. 코드를 검사할 때 RetrieveAndGenerate API가 관련 문서를 검색해서 Claude에게 컨텍스트로 제공하고, Claude가 그 기준에 맞춰 코드를 리뷰하는 구조입니다.&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;855&quot; data-origin-height=&quot;203&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cG0tAY/dJMcagex5Ae/wwoQw77JzNX7KXCz3sTbk1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cG0tAY/dJMcagex5Ae/wwoQw77JzNX7KXCz3sTbk1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cG0tAY/dJMcagex5Ae/wwoQw77JzNX7KXCz3sTbk1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcG0tAY%2FdJMcagex5Ae%2FwwoQw77JzNX7KXCz3sTbk1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/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;203&quot; data-origin-width=&quot;855&quot; data-origin-height=&quot;203&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Bedrock Knowledge Base&lt;/b&gt;는 RAG 파이프라인 전체를 완전 관리형으로 제공하는 AWS 서비스입니다. 데이터 수집, 청킹, 임베딩 변환, 벡터 저장, 검색, 응답 생성까지의 과정을 직접 구현할 필요 없이 데이터 소스 연결만으로 구성할 수 있습니다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;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;/li&gt;
&lt;li&gt;정확한 응답 생성: 검색된 정보를 바탕으로 관련성 높은 답변 생성&lt;/li&gt;
&lt;li&gt;프롬프트 보강: 검색된 정보를 프롬프트에 자동으로 추가&lt;/li&gt;
&lt;li&gt;출처 인용 포함: 응답에 원본 소스 참조를 포함해 할루시네이션 최소화&lt;/li&gt;
&lt;li&gt;이미지 검색: 문서 내 이미지도 쿼리 결과로 추출 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;동작 방식&lt;/b&gt;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;데이터 위치를 지정 (S3 등)&lt;/li&gt;
&lt;li&gt;임베딩 모델 선택&lt;/li&gt;
&lt;li&gt;Bedrock이 데이터를 &lt;b&gt;벡터 임베딩&lt;/b&gt;으로 변환하여 벡터 저장소에 저장&lt;/li&gt;
&lt;li&gt;이후 쿼리가 들어오면 벡터 검색으로 관련 정보를 찾아 LLM에 전달&amp;nbsp;&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Bedrock Agents와의 연동&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Knowledge Base는 &lt;span style=&quot;background-color: #f6e199; color: #000000;&quot;&gt;Bedrock Agents에 연결&lt;/span&gt;할 수 있어, 에이전트가 사용자 입력에 따라 관련 정보를 자동으로 식별하고 검색하도록 만들 수 있습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: none;&quot;&gt;&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;RAG (Retrieval Augmented Generation)&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div style=&quot;color: #333333; text-align: start;&quot;&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;RAG는 LLM이 응답을 생성할 때, 외부 데이터 소스에서 관련 정보를 검색해 컨텍스트로 함께 제공하는 아키텍처 패턴입니다.&lt;/li&gt;
&lt;li&gt;LLM은 학습 시점의 데이터만 알고 있기 때문에, 학습 이후에 생성된 정보나 학습 데이터에 포함되지 않은 도메인 특화 정보(사내 문서, 제품 DB 등)는 답변할 수 없습니다. RAG는 이 한계를 모델 재학습 없이 해결합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote style=&quot;background-color: #fcfcfc; color: #666666; text-align: left;&quot; data-ke-style=&quot;style3&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;동작 순서&amp;nbsp;&lt;br /&gt;1. 사용자 질문을 벡터로 변환&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;2. 벡터 DB에서 유사도가 높은 문서 청크를 검색&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;3. 검색된 문서를 원래 질문과 함께 LLM에 전달&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;4. LLM이 제공된 컨텍스트를 바탕으로 응답 생성&lt;/span&gt;&lt;/blockquote&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;파인튜닝과의 차이점은, 파인튜닝은 모델 가중치 자체를 변경하는 반면 RAG는 모델을 그대로 두고 추론 시점에 외부 지식을 주입한다는 점입니다. 데이터가 자주 변경되거나 출처 추적이 필요한 경우 RAG가 더 적합합니다.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style8&quot; /&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;1단계. IAM 유저 생성 및 액세스 키 발급&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;먼저 AWS 콘솔에서 Bedrock과 S3에 접근할 수 있는 IAM 유저를 만들어야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AWS 콘솔에 루트 계정으로 로그인한 후, 검색창에 IAM을 입력해서 이동합니다. 왼쪽 사이드바에서 사용자를 클릭하고 사용자 생성 버튼을 누릅니다.&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;720&quot; data-origin-height=&quot;567&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/8uyRp/dJMb997wXMz/yr1wzQ2GE5j0GT4FwZkKT0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/8uyRp/dJMb997wXMz/yr1wzQ2GE5j0GT4FwZkKT0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/8uyRp/dJMb997wXMz/yr1wzQ2GE5j0GT4FwZkKT0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F8uyRp%2FdJMb997wXMz%2Fyr1wzQ2GE5j0GT4FwZkKT0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;417&quot; height=&quot;328&quot; data-origin-width=&quot;720&quot; data-origin-height=&quot;567&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자 이름은 bedrocktry 처럼 알아보기 쉽게 설정합니다. AWS Management Console 액세스는 이번 실습에서는 체크하지 않아도 됩니다. 프로그래밍 방식 액세스만 사용할 것이기 때문입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&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;AmazonBedrockFullAccess&lt;/li&gt;
&lt;li&gt;AmazonS3FullAccess&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;705&quot; data-origin-height=&quot;511&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c5kTlj/dJMcagS8w91/s5TeoJkVjKmCXGOXMlc9mk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c5kTlj/dJMcagS8w91/s5TeoJkVjKmCXGOXMlc9mk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c5kTlj/dJMcagS8w91/s5TeoJkVjKmCXGOXMlc9mk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc5kTlj%2FdJMcagS8w91%2Fs5TeoJkVjKmCXGOXMlc9mk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;690&quot; height=&quot;500&quot; data-origin-width=&quot;705&quot; data-origin-height=&quot;511&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 운영 환경이라면 최소 권한 원칙에 따라 더 세밀하게 설정해야 하지만, 실습 목적이므로 위 두 정책으로 진행합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자 생성이 완료되면 해당 유저를 클릭하고 보안 자격 증명 탭으로 이동합니다. 액세스 키 만들기를 클릭하고, 사용 사례는 로컬 코드로 선택합니다. 생성된 액세스 키 ID와 시크릿 액세스 키를 반드시 안전한 곳에 저장해두세요. 시크릿 키는 이 화면을 벗어나면 다시 확인할 수 없습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;발급받은 키는 로컬 환경에서 아래와 같이 설정합니다.&lt;/p&gt;
&lt;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/wAmRj/dJMcadBZ7z3/FJaflOXqKOHUHugeKNO4iK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/wAmRj/dJMcadBZ7z3/FJaflOXqKOHUHugeKNO4iK/img.png&quot; width=&quot;410&quot; data-widthpercent=&quot;37.84&quot; data-is-animation=&quot;false&quot; data-origin-height=&quot;658&quot; data-origin-width=&quot;770&quot; style=&quot;width: 37.4044%; margin-right: 10px;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/wAmRj/dJMcadBZ7z3/FJaflOXqKOHUHugeKNO4iK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FwAmRj%2FdJMcadBZ7z3%2FFJaflOXqKOHUHugeKNO4iK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;770&quot; height=&quot;658&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dUv1qY/dJMb990OOkH/zVZNoUCwRT6RlZFzKhWSZ0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dUv1qY/dJMb990OOkH/zVZNoUCwRT6RlZFzKhWSZ0/img.png&quot; width=&quot;323&quot; height=&quot;168&quot; data-filename=&quot;blob&quot; data-widthpercent=&quot;62.16&quot; data-is-animation=&quot;false&quot; data-origin-height=&quot;410&quot; data-origin-width=&quot;788&quot; style=&quot;width: 61.4328%;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dUv1qY/dJMb990OOkH/zVZNoUCwRT6RlZFzKhWSZ0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdUv1qY%2FdJMb990OOkH%2FzVZNoUCwRT6RlZFzKhWSZ0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;788&quot; height=&quot;410&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;발급받은 키는 실습 환경에서 아래와 같이 설정합니다.&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/6B7b3/dJMcacJZKRb/QqCHMEO4nN3dXUak2mkMDk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/6B7b3/dJMcacJZKRb/QqCHMEO4nN3dXUak2mkMDk/img.png&quot; width=&quot;418&quot; height=&quot;334&quot; data-is-animation=&quot;false&quot; data-origin-height=&quot;578&quot; data-origin-width=&quot;724&quot; data-widthpercent=&quot;33.66&quot; style=&quot;width: 33.2672%; margin-right: 10px;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/6B7b3/dJMcacJZKRb/QqCHMEO4nN3dXUak2mkMDk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F6B7b3%2FdJMcacJZKRb%2FQqCHMEO4nN3dXUak2mkMDk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;724&quot; height=&quot;578&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cFG52t/dJMcahEsuZP/MebmD0NBwox3TUr7Ymj7zK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cFG52t/dJMcahEsuZP/MebmD0NBwox3TUr7Ymj7zK/img.png&quot; data-is-animation=&quot;false&quot; data-origin-height=&quot;241&quot; data-origin-width=&quot;595&quot; style=&quot;width: 65.57%;&quot; data-widthpercent=&quot;66.34&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cFG52t/dJMcahEsuZP/MebmD0NBwox3TUr7Ymj7zK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcFG52t%2FdJMcahEsuZP%2FMebmD0NBwox3TUr7Ymj7zK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;595&quot; height=&quot;241&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style8&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;2단계. Amazon Bedrock에서 Claude Sonnet 4.6 모델 연동 &lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AWS 콘솔에서 Amazon Bedrock으로 이동합니다. 리전이 아시아 태평양(서울), ap-northeast-2로 설정되어 있는지 꼭 확인하세요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1527&quot; data-origin-height=&quot;696&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ofzlF/dJMcaglgFdz/xQA9JdRYLb8Bky4LNbyiek/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ofzlF/dJMcaglgFdz/xQA9JdRYLb8Bky4LNbyiek/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ofzlF/dJMcaglgFdz/xQA9JdRYLb8Bky4LNbyiek/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FofzlF%2FdJMcaglgFdz%2FxQA9JdRYLb8Bky4LNbyiek%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;621&quot; height=&quot;283&quot; data-origin-width=&quot;1527&quot; data-origin-height=&quot;696&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;왼쪽 메뉴에서&amp;nbsp; Model Catalog를 클릭합니다. 검색창에 Claude를 입력하면 Anthropic의 모델 목록이 나타납니다. Claude Sonnet 4.6을 찾아서 액세스 요청을 클릭합니다.&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/cdkpH9/dJMcajoGRgU/MVn8Sz92w7sKwqFhwS04YK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cdkpH9/dJMcajoGRgU/MVn8Sz92w7sKwqFhwS04YK/img.png&quot; data-origin-width=&quot;1213&quot; data-origin-height=&quot;728&quot; data-is-animation=&quot;false&quot; width=&quot;392&quot; height=&quot;235&quot; style=&quot;width: 58.4065%; margin-right: 10px;&quot; data-widthpercent=&quot;59.09&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cdkpH9/dJMcajoGRgU/MVn8Sz92w7sKwqFhwS04YK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcdkpH9%2FdJMcajoGRgU%2FMVn8Sz92w7sKwqFhwS04YK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1213&quot; height=&quot;728&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bh6oLS/dJMcaaL7kaB/FFPe4VZNs0BmgE79kHTOD1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bh6oLS/dJMcaaL7kaB/FFPe4VZNs0BmgE79kHTOD1/img.png&quot; width=&quot;284&quot; data-origin-width=&quot;797&quot; data-origin-height=&quot;691&quot; data-is-animation=&quot;false&quot; style=&quot;width: 40.4307%;&quot; data-widthpercent=&quot;40.91&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bh6oLS/dJMcaaL7kaB/FFPe4VZNs0BmgE79kHTOD1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbh6oLS%2FdJMcaaL7kaB%2FFFPe4VZNs0BmgE79kHTOD1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;797&quot; height=&quot;691&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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style8&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;3단계. S3 버킷 생성 및 가이드 문서 업로드 &lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Knowledge Base가 참고할 문서들을 S3에 저장할 버킷을 만들겠습니다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1779330997449&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# AWS CLI로 생성 (또는 콘솔에서)
!aws s3 mb s3://kb-docs-bucket-2026 --region ap-northeast-2

# 폴더 구조 생성
!aws s3api put-object --bucket kb-docs-bucket-2026 --key style-guides/
!aws s3api put-object --bucket kb-docs-bucket-2026 --key security/
!aws s3api put-object --bucket kb-docs-bucket-2026 --key frameworks/&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;colab 에서 아래의 코드를 이용해서 S3 버킷을 생성합니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;950&quot; data-origin-height=&quot;308&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c8KPuY/dJMcaaL7lMI/96SpckQQ9OSKTehPjFxYP1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c8KPuY/dJMcaaL7lMI/96SpckQQ9OSKTehPjFxYP1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c8KPuY/dJMcaaL7lMI/96SpckQQ9OSKTehPjFxYP1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc8KPuY%2FdJMcaaL7lMI%2F96SpckQQ9OSKTehPjFxYP1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;950&quot; height=&quot;308&quot; data-origin-width=&quot;950&quot; data-origin-height=&quot;308&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;버킷 생성 후 폴더 구조를 만듭니다. 폴더 만들기 버튼으로 style-guides와 security 폴더를 생성합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;pre id=&quot;code_1779435414249&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;%%bash
cat &amp;lt;&amp;lt; EOF &amp;gt; pep8.txt
PEP 8 - Python Code Style Guide

## Naming Conventions
- 변수명: snake_case 사용 (예: user_name, total_count)
- 함수명: snake_case 사용 (예: calculate_total, get_user_data)
- 클래스명: PascalCase 사용 (예: UserProfile, DataProcessor)
- 상수명: UPPER_SNAKE_CASE 사용 (예: MAX_RETRIES, DEFAULT_TIMEOUT)

## Indentation
- 4 spaces per indentation level (tab 사용 금지)

## Line Length
- 최대 79 characters
- 주석/문서열은 72 characters
EOF&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_1779435434066&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;%%bash
cat &amp;lt;&amp;lt; EOF &amp;gt; owasp-top10.txt
OWASP Top 10 - Web Application Security Risks

## A01:2021 - Broken Access Control
- 사용자의 권한이 적절하게 관리되지 않아 비인가된 접근이 허용되는 문제.

## A02:2021 - Cryptographic Failures
- 민감한 데이터의 암호화가 부적절하거나 누락되어 데이터 유출로 이어지는 문제.

## A03:2021 - Injection
- SQL, NoSQL, OS 명령어 등 신뢰할 수 없는 데이터가 쿼리나 명령의 일부로 해석되어 실행되는 문제.

## A04:2021 - Insecure Design
- 보안 통제를 적절하게 구현하지 못하여 설계 단계에서부터 보안 취약점이 발생하는 문제.

## A05:2021 - Security Misconfiguration
- 보안 설정이 미흡하거나 기본 설정이 그대로 사용되어 취약점이 노출되는 문제.

## A06:2021 - Vulnerable and Outdated Components
- 알려진 취약점을 가진 오래된 라이브러리나 컴포넌트를 사용하여 발생하는 문제.

## A07:2021 - Identification and Authentication Failures
- 사용자 인증 또는 세션 관리 기능이 제대로 구현되지 않아 계정 탈취가 가능한 문제.

## A08:2021 - Software and Data Integrity Failures
- 소프트웨어 업데이트, 중요한 데이터, CI/CD 파이프라인 등의 무결성 검증이 부족하여 발생하는 문제.

## A09:2021 - Security Logging and Monitoring Failures
- 보안 이벤트 로깅 및 모니터링이 부적절하여 침해 사고 발생 시 탐지 및 대응이 어려운 문제.

## A10:2021 - Server-Side Request Forgery (SSRF)
- 공격자가 서버 측에서 임의의 URL로 요청을 보내 내부 시스템에 접근하거나 정보를 탈취하는 문제.
EOF&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;pre id=&quot;code_1779331182250&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 문서 업로드
!aws s3 cp pep8.txt s3://kb-docs-bucket-2026/style-guides/
!aws s3 cp owasp-top10.txt s3://kb-docs-bucket-2026/security/&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;샘플 문서를 작성한 후에 생성해둔 폴더에 맞게 업로드합니다.&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style8&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;4단계. Bedrock Knowledge Base 생성&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Bedrock Knowledge Base&lt;/b&gt;는 RAG 파이프라인 전체를 완전 관리형으로 제공하는 AWS 서비스입니다. 데이터 수집, 청킹, 임베딩 변환, 벡터 저장, 검색, 응답 생성까지의 과정을 직접 구현할 필요 없이 데이터 소스 연결만으로 구성할 수 있습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;지원하는&lt;/b&gt; &lt;b&gt;데이터 소스&lt;/b&gt;로는 Amazon S3, Confluence, Salesforce, SharePoint, OneDrive, Web Crawler 등이 있습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;지원하는 &lt;/b&gt;&lt;b&gt;벡터 DB&lt;/b&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;로는 Amazon OpenSearch Serverless, Aurora, Neptune Analytics, MongoDB, Pinecone, Redis Enterprise Cloud, 그리고 2025년 말에 추가된 Amazon S3 Vectors가 있습니다. S3 Vectors는 기존 벡터 DB 대비 최대 90% 비용 절감이 가능한 옵션입니다.&lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;제공하는 주&lt;/b&gt;&lt;b&gt;요 API&lt;/b&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;는 두 가지입니다.&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;RetrieveAndGenerate: 검색과 응답 생성을 한 번에 처리&lt;/li&gt;
&lt;li&gt;Retrieve: 관련 문서 검색만 수행, 응답 생성은 별도로 처&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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style8&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Amazon Bedrock 콘솔로 돌아와서 왼쪽 메뉴의 지식 기반(Knowledge Bases)을 클릭하고 지식 기반 생성을 선택합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지식 기반 이름은 자동으로 생성한 이름으로 하고, IAM 권한은 새 서비스 역할 생성으로 두면 Bedrock이 필요한 권한을 자동으로 설정해줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bBkjGW/dJMcag6EA3F/RLBuSlQE0YD9OyAUkHHKyK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bBkjGW/dJMcag6EA3F/RLBuSlQE0YD9OyAUkHHKyK/img.png&quot; data-origin-width=&quot;1220&quot; data-origin-height=&quot;638&quot; data-is-animation=&quot;false&quot; style=&quot;width: 60.1054%; margin-right: 10px;&quot; data-widthpercent=&quot;60.81&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bBkjGW/dJMcag6EA3F/RLBuSlQE0YD9OyAUkHHKyK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbBkjGW%2FdJMcag6EA3F%2FRLBuSlQE0YD9OyAUkHHKyK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1220&quot; height=&quot;638&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pfyvX/dJMcaf7IB4j/AIsfXc6HTLpZLIFQH5CJy0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pfyvX/dJMcaf7IB4j/AIsfXc6HTLpZLIFQH5CJy0/img.png&quot; data-origin-width=&quot;971&quot; data-origin-height=&quot;788&quot; data-is-animation=&quot;false&quot; width=&quot;768&quot; height=&quot;623&quot; style=&quot;width: 38.7318%;&quot; data-widthpercent=&quot;39.19&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pfyvX/dJMcaf7IB4j/AIsfXc6HTLpZLIFQH5CJy0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpfyvX%2FdJMcaf7IB4j%2FAIsfXc6HTLpZLIFQH5CJy0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;971&quot; height=&quot;788&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1054&quot; data-origin-height=&quot;599&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cbPWb6/dJMcajbamTc/jp0485UMa2wInajR4G07W0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cbPWb6/dJMcajbamTc/jp0485UMa2wInajR4G07W0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cbPWb6/dJMcajbamTc/jp0485UMa2wInajR4G07W0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcbPWb6%2FdJMcajbamTc%2Fjp0485UMa2wInajR4G07W0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/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;351&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1054&quot; data-origin-height=&quot;599&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;데이터 소스 설정에서는 Amazon S3를 선택하고, '&lt;span style=&quot;background-color: #99cefa;&quot;&gt;S3 찾아보기'&lt;/span&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;S3 URI: s3://kb-docs-bucket-2026&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;997&quot; data-origin-height=&quot;585&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lQ3uD/dJMcajhY5mp/doOKkNvfXw2rSTbpujL7kK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lQ3uD/dJMcajhY5mp/doOKkNvfXw2rSTbpujL7kK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lQ3uD/dJMcajhY5mp/doOKkNvfXw2rSTbpujL7kK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlQ3uD%2FdJMcajhY5mp%2FdoOKkNvfXw2rSTbpujL7kK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;748&quot; height=&quot;439&quot; data-origin-width=&quot;997&quot; data-origin-height=&quot;585&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;임베딩 모델은 Titan Embeddings V2를 선택합니다.&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Titan Embedding&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Amazon Titan Embedding은 AWS에서 제공하는 텍스트 임베딩 모델입니다. 텍스트를 고차원 벡터 공간의 숫자 배열로 변환하며, 의미적으로 유사한 텍스트는 벡터 공간에서 가까운 위치에 매핑됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;임베딩 모델의 역할은 두 가지입니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;인덱싱 시점&lt;/b&gt;: 문서를 청크 단위로 나눠 각 청크를 벡터로 변환 후 벡터 DB에 저장&lt;/li&gt;
&lt;li&gt;&lt;b&gt;검색 시점&lt;/b&gt;: 사용자 질문을 벡터로 변환 후, 저장된 벡터들과 코사인 유사도 등으로 비교&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Titan Embedding은 다국어를 지원하며, Bedrock Knowledge Base와 네이티브로 통합되어 별도 설정 없이 사용할 수 있습니다. 현재 amazon.titan-embed-text-v2 버전은 최대 8,192 토큰 입력을 지원하고, 256 / 512 / 1,024 차원의 벡터를 출력할 수 있습니다.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;벡터 스토어는 S3 vector bucket을 선택합니다. Knowledge Base 생성 시 벡터 스토어로 S3 vector bucket을 선택하면 두 가지 방식 중 하나를 고를 수 있습니다. Quick create a new vector store를 선택하면 Amazon Bedrock이 S3 vector bucket과 vector index를 자동으로 생성하고 필요한 설정을 구성해줍니다. 실습에서는 Quick create a new vector store로 진행하면 가장 간편합니다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Amazon S3 Vectors는 비용 효율적인 벡터 스토리지를 제공하며, 대규모 벡터 데이터셋을 내구성 있고 탄력적으로 저장하면서 서브세컨드 쿼리 성능을 지원합니다. OpenSearch Serverless 대비 스토리지 비용을 크게 줄일 수 있어 실습이나 소규모 프로젝트에 적합한 선택입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;생성 완료 후 Knowledge Base ID를 꼭 복사해두세요.&lt;/span&gt; 10자리 영숫자 조합(예: ABC123DEFG)으로 생성되며, 이후 코드에서 반드시 필요합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;생성이 완료되면 데이터 소스 동기화를 해줘야 합니다. Knowledge Base 상세 페이지에서 데이터 소스를 선택하고 동기화 버튼을 클릭합니다. Amazon Bedrock이 S3 데이터 소스에서 데이터를 자동으로 가져오고, 콘텐츠를 텍스트 블록으로 변환한 뒤 임베딩을 생성해 S3 vector index에 저장합니다. 완료까지 1~2분 정도 소요됩니다. &lt;span data-state=&quot;delayed-open&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style8&quot; /&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;4단계. AWS 지식 기반(Knowledge Base) 연동 스타일 및 보안 검사 코드 작성&amp;nbsp;&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드 스타일 검사 (PEP8 가이드라인 기반)&lt;/p&gt;
&lt;pre id=&quot;code_1779436344162&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import boto3

def style_check(code, kb_id):
    &quot;&quot;&quot;코드 스타일 검사 후 위반 사항 리스트 반환&quot;&quot;&quot;

    # Ensure bedrock-agent-runtime client is used
    bedrock_agent_client_for_rag = boto3.client('bedrock-agent-runtime', region_name='ap-northeast-2')

    prompt = f&quot;&quot;&quot;
    당신은 코드 스타일 검사기입니다. 다음 코드에서 PEP8 또는 일반적인 스타일 규칙을 위반한 부분을 찾아주세요.

    형식:
    - [라인번호] 위반 유형: 설명

    코드:
    {code}
    &quot;&quot;&quot;

    response = bedrock_agent_client_for_rag.retrieve_and_generate(
        input={'text': prompt},
        retrieveAndGenerateConfiguration={
            'type': 'KNOWLEDGE_BASE',
            'knowledgeBaseConfiguration': {
                'knowledgeBaseId': kb_id,
                'modelArn': 'global.anthropic.claude-sonnet-4-6'
            }
        }
    )

    # 1. 여기서 return을 하면서 함수가 깔끔하게 끝납니다.
    return response['output']['text']


# ========================================================
# 2. 실행 테스트 영역 (들여쓰기를 모두 맨 앞으로 당겼습니다!)
# ========================================================

# 단일 코드 테스트
bad_code = &quot;&quot;&quot;
def Sum(a,b):
    return a+b
&quot;&quot;&quot;
print(&quot;=== 단일 bad_code 테스트 결과 ===&quot;)
print(style_check(bad_code, kb_id))
print(&quot;-&quot; * 40)


# 샘플 리스트 다중 테스트 수행
samples = [
    &quot;&quot;&quot;def add(a,b): return a+b&quot;&quot;&quot;,   # 한 줄에 여러 구문
    &quot;&quot;&quot;def GetUserData(): pass&quot;&quot;&quot;,    # 함수명 대문자 시작
    &quot;&quot;&quot;import os,sys&quot;&quot;&quot;,              # 여러 import 한 줄에
    &quot;&quot;&quot;x = 1\ny = 2\nz=x+y&quot;&quot;&quot;,        # 공백 부족
    &quot;&quot;&quot;def long_function_name_very_long_parameter_list(param1, param2, param3, param4, param5): pass&quot;&quot;&quot;
]

print(&quot;=== 여러 샘플 코드 순회 검사 시작 ===&quot;)
for i, sample_code in enumerate(samples, 1):
    print(f&quot;\n[샘플 {i}] 검사할 코드: {sample_code}&quot;)

    try:
        result = style_check(sample_code, kb_id)
        print(f&quot;결과:\n{result}&quot;)
    except Exception as e:
        print(f&quot;❌ 샘플 {i} 검사 중 에러 발생: {e}&quot;)
    print(&quot;=&quot; * 30)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보안 취약점 검사 코드 (OWASP Top 10 기반)&lt;/p&gt;
&lt;pre id=&quot;code_1779436404949&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import boto3

def check_security(code, kb_id):
    &quot;&quot;&quot;보안 취약점 검사&quot;&quot;&quot;

    # Explicitly create a bedrock-agent-runtime client for retrieve_and_generate
    bedrock_agent_client = boto3.client('bedrock-agent-runtime', region_name='ap-northeast-2')

    response = bedrock_agent_client.retrieve_and_generate(
        input={'text': f&quot;&quot;&quot;
                        다음 코드에서 보안 취약점을 찾아주세요. 특히 SQL Injection, XSS, 하드코딩된 비밀번호를 중점적으로 검사해주세요.

                        코드:
                        {code}
                        취약점이 있으면 위치, 유형, 심각도, 수정 제안을 포함해 주세요.
                        &quot;&quot;&quot;},
        retrieveAndGenerateConfiguration={
            'type': 'KNOWLEDGE_BASE',
            'knowledgeBaseConfiguration': {
                'knowledgeBaseId': kb_id,
                'modelArn': 'global.anthropic.claude-sonnet-4-6'
            }
        }
    )

    return response['output']['text']

# ========================================================
# 수정 구간: 들여쓰기를 맨 앞으로 맞추고 끊긴 문자열을 닫아줬습니다.
# ========================================================

# 취약한 코드 테스트
vulnerable_code = &quot;&quot;&quot;
def get_user_by_name(username):
    query = &quot;SELECT * FROM users WHERE name = '&quot; + username + &quot;'&quot;
&quot;&quot;&quot;  #   닫는 따옴표 추가

# 결과 출력 추가
print(check_security(vulnerable_code, kb_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;div style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;
&lt;div&gt;&lt;span style=&quot;color: #000000;&quot;&gt;취약점 리포트 생성&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;pre id=&quot;code_1779436489947&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import boto3

def generate_security_report(code, kb_id):
    &quot;&quot;&quot;보안 취약점 리포트 (JSON 형식) 생성&quot;&quot;&quot;

    # Explicitly create a bedrock-agent-runtime client for retrieve_and_generate
    bedrock_agent_client = boto3.client('bedrock-agent-runtime', region_name='ap-northeast-2')

    response = bedrock_agent_client.retrieve_and_generate(
        input={'text': f&quot;&quot;&quot;
        다음 코드의 보안 취약점을 분석하고 JSON 형식으로 보고서를 작성해주세요.

        형식:
        {{
            &quot;vulnerabilities&quot;: [
                {{
                    &quot;line&quot;: 라인번호,
                    &quot;type&quot;: &quot;취약점 유형&quot;,
                    &quot;severity&quot;: &quot;CRITICAL/HIGH/MEDIUM/LOW&quot;,
                    &quot;description&quot;: &quot;설명&quot;,
                    &quot;suggestion&quot;: &quot;수정 제안&quot;
                }}
            ],
            &quot;summary&quot;: &quot;전체 평가&quot;
        }}

        코드:
        {code}
        &quot;&quot;&quot;},
        retrieveAndGenerateConfiguration={
            'type': 'KNOWLEDGE_BASE',
            'knowledgeBaseConfiguration': {
                'knowledgeBaseId': kb_id,
                'modelArn': 'global.anthropic.claude-sonnet-4-6'
            }
        }
    )

    return response['output']['text']


# ========================================================
# 수정 및 추가 구간: 들여쓰기를 맞추고, vulnerable_code를 정의했습니다.
# ========================================================

# 테스트용 취약한 샘플 코드 정의 (앞 단계에서 썼던 코드 활용)
vulnerable_code = &quot;&quot;&quot;
def get_user_by_name(username):
    query = &quot;SELECT * FROM users WHERE name = '&quot; + username + &quot;'&quot;
    return query
&quot;&quot;&quot;

# 결과를 파일로 저장
report = generate_security_report(vulnerable_code, kb_id)
with open('security_report.json', 'w', encoding='utf-8') as f:
    f.write(report)

print(&quot;✅ 리포트 저장 완료: security_report.json&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a title=&quot;github&quot; href=&quot;https://github.com/ranygo12-pixel/AWS/tree/54f4a12b5d3f6d5c8811a1f2ebb3a1b7036900fe/Bedrock/Bedrock04&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt; https://github.com/ranygo12-pixel/AWS/tree/54f4a12b5d3f6d5c8811a1f2ebb3a1b7036900fe/Bedrock/Bedrock04 &lt;/a&gt;&lt;/p&gt;</description>
      <category>Concepts/Cloud Infra</category>
      <category>AI</category>
      <category>AWS</category>
      <category>AWS Bedrock</category>
      <category>Bedrock</category>
      <author>ranygo12</author>
      <guid isPermaLink="true">https://elly1108.tistory.com/19</guid>
      <comments>https://elly1108.tistory.com/19#entry19comment</comments>
      <pubDate>Fri, 22 May 2026 16:55:45 +0900</pubDate>
    </item>
    <item>
      <title>AWS 기초개념 : 가상화&amp;amp;EC2</title>
      <link>https://elly1108.tistory.com/18</link>
      <description>&lt;h4 data-ke-size=&quot;size20&quot;&gt;가상화 (virtualization)&amp;nbsp;&lt;/h4&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;단일 컴퓨터의 하드웨워 요소를 일반적으로 가상 머신(VM)이 라고 하는 &lt;span style=&quot;background-color: #f6e199;&quot;&gt;다수의 가상 컴퓨터&lt;/span&gt;로 분할할 수 있도록 해주는 기술&amp;nbsp;&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;762&quot; data-origin-height=&quot;290&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/AlNq3/dJMcaja7fty/gEfCyHstMASE4SdEPKi010/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/AlNq3/dJMcaja7fty/gEfCyHstMASE4SdEPKi010/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/AlNq3/dJMcaja7fty/gEfCyHstMASE4SdEPKi010/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FAlNq3%2FdJMcaja7fty%2FgEfCyHstMASE4SdEPKi010%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;399&quot; height=&quot;152&quot; data-origin-width=&quot;762&quot; data-origin-height=&quot;290&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&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; (privilesged instruction) : 시스템 요소들과 소통할 수 있는 명령 - OS 만 가능&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;OS 는 특권 명령 때문에 하나의 하드웨어 시스템 당 하나밖에 돌아갈 수 없음&lt;/li&gt;
&lt;li&gt;일반 프로그램은 특권 명령이 필요 없기 때문에 많은 프로그램을 동시에 수행 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가상화가 나타나기 전까지는 하나의 하드웨어 시스템은 하나의 OS 만 실행 가능했음 &amp;rarr; 일반적인 컴퓨터처럼 직접 OS 가 하드웨어에 설치 상태로만 운영 가능&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style8&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock floatRight&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;219&quot; data-origin-height=&quot;259&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c9U6i5/dJMcaciOvIE/wKpoyorMcbEhpIamRKu3lK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c9U6i5/dJMcaciOvIE/wKpoyorMcbEhpIamRKu3lK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c9U6i5/dJMcaciOvIE/wKpoyorMcbEhpIamRKu3lK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc9U6i5%2FdJMcaciOvIE%2FwKpoyorMcbEhpIamRKu3lK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;151&quot; height=&quot;179&quot; data-origin-width=&quot;219&quot; data-origin-height=&quot;259&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock floatRight&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;330&quot; data-origin-height=&quot;308&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/9olu3/dJMcabjZIgX/KDwk1dKel8KnM6DC1sEi31/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/9olu3/dJMcabjZIgX/KDwk1dKel8KnM6DC1sEi31/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/9olu3/dJMcabjZIgX/KDwk1dKel8KnM6DC1sEi31/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F9olu3%2FdJMcabjZIgX%2FKDwk1dKel8KnM6DC1sEi31%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;159&quot; height=&quot;148&quot; data-origin-width=&quot;330&quot; data-origin-height=&quot;308&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock floatRight&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;331&quot; data-origin-height=&quot;233&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Ni7dv/dJMcafGGojP/nAYgvngg42FJtY69Px10pK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Ni7dv/dJMcafGGojP/nAYgvngg42FJtY69Px10pK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Ni7dv/dJMcafGGojP/nAYgvngg42FJtY69Px10pK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FNi7dv%2FdJMcafGGojP%2FnAYgvngg42FJtY69Px10pK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;180&quot; height=&quot;233&quot; data-origin-width=&quot;331&quot; data-origin-height=&quot;233&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1세대 : 완전 가상화 (Fully Emulated)&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;즉 CPU, 하드디스크, 마더보드 등 모든 요소를 에뮬레이터로 구현하여 OS와 연동&lt;/li&gt;
&lt;li&gt;엄청 느림&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2세대 : Paravirtualization&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;Guest OS 는 하이퍼바이저와 통신&lt;/li&gt;
&lt;li&gt;Hypervisor : OS 와 하드웨어 사이에 존재하는 일종의 가상화 매니저&lt;/li&gt;
&lt;li&gt;속도의 향상&amp;nbsp;&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;&lt;b&gt;3세대 : Haedware Virtual Machun(HVM)&amp;nbsp;&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;&lt;u&gt;직접 Guest-OS 가 하드웨어와 통신&lt;/u&gt; =&amp;gt; 빠른 속도 (near bare-metal)&amp;nbsp;&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;style8&quot; /&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;AWS 클라우드 환경에서 리소스를 작은 단위로 빠르게 구성할 수 있는 원동력 -&amp;gt; 가상화&lt;/li&gt;
&lt;li&gt;즉 AWS에서 사용자마다 컴퓨터를 할당해주는 것이 아닌 &lt;span style=&quot;background-color: #f6e199;&quot;&gt;이미 구축된 가상화 가능한 서버의 한 부분을 할당해주는 것!&lt;/span&gt;&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;style5&quot; /&gt;
&lt;h4 style=&quot;background-color: #ffffff; color: #0f0f0f; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;EC2&lt;/h4&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR'; color: #000000;&quot;&gt;Amazon Elastic Compute Cloud (EC2)는 안전하고 크기 조정이 가능한 컴퓨팅 파워를 클라우드에서 제공하는 웹서비스입니다. 개발자가 더 쉽게 웹 규모의 클라우드 컴퓨팅 작업을 할 수 있도록 설계되었습니다. Amazon EC2 의 간단한 웹서비스 인터페이스를 통해 간편하게 필요한 용량을 얻고 구성할 수 있습니다. 컴퓨팅 리소스에 대한 포괄적인 제어권을 제공하며, Amazon의 검증된 컴퓨팅 인프라에서 실행할 수 있습니다.&lt;/span&gt;&lt;/blockquote&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;초 단위 온디맨드 가격 모델
&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;/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;몇 분이면 전 세계에 인스턴스 수백여대를 구축 가능&lt;/li&gt;
&lt;/ul&gt;
&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;머신 러닝, 웹서버, 게임서버,이미지 처리 등 다양한 용도에 최적화된 서버 구성 가능&lt;/li&gt;
&lt;li&gt;다양한 과금 모델 사용 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;여러 AWS 서비스와 연동
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;오토스케일링, Elastic Load Banlancer(ELB), CloudWatch&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style8&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;EC2 구성&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;인스턴스 : 클라우드에서 사용하는 가상 서버로 CPU,메모리,그래픽카드 등 연산을 위한 하드웨어를 담당&lt;/li&gt;
&lt;li&gt;EBS : Elastic BLock Storage 의 줄임말로 클라우드에서 사용하는 가상 하드 디스크&lt;/li&gt;
&lt;li&gt;AMI : EC2 인스턴스를 실행하기 위한 정보를 담고 있는 이미지&lt;/li&gt;
&lt;li&gt;보안 그룹 : 가상의 방화벽'&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style8&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;EC2의 가격 정책&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;b&gt;On-Demand&lt;/b&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;li&gt;장기적인 수요 예측이 힘들거나 유연하게 EC2를 사용하고 싶을 때&lt;/li&gt;
&lt;li&gt;한번 써보고 싶을때&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Spot Instance&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;AWS가 자동으로 가격 책정&lt;/li&gt;
&lt;li&gt;최대 90% 정도 저렴&lt;/li&gt;
&lt;li&gt;회수 2분 전 사전 경고 있음&lt;/li&gt;
&lt;li&gt;시작 종료가 자유롭거나 추가적인 컴퓨팅 파워가 필요한 경우&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;예약 인스턴스(Reserved Instance-RI)&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;미리 일정 기간(1년~3년) 약정해서 쓰는 방식&amp;nbsp;&lt;/li&gt;
&lt;li&gt;초대 70%정도 저렴&lt;/li&gt;
&lt;li&gt;수요 예측이 확실할 때&lt;/li&gt;
&lt;li&gt;총 비용을 절감하기 위해 어느 정도 기간의 약정이 가능한 사용자&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Savings Plans&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;시간당 일정 금액($\hour)을 1년 또는 3년간 약정하는 방식으로, RI보다 유연한 현대적 약정 할인 모델&lt;/li&gt;
&lt;li&gt;Compute Savings Plans와 EC2 Instance Savings Plans 두 가지 유형이 EC2에 적용됨
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Compute Savings Plans&lt;/b&gt;: 최대 66% 할인. 인스턴스 패밀리, 사이즈, 리전, OS 무관하게 자동 적용되며 Fargate, Lambda까지 커버&lt;/li&gt;
&lt;li&gt;&lt;b&gt;EC2 Instance Savings Plans&lt;/b&gt;: 최대 72% 할인. 특정 리전과 인스턴스 패밀리를 지정해야 하지만 그 안에서 사이즈&amp;middot;OS는 자유롭게 변경 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;RI와의 차이점: 특정 인스턴스를 예약하는 방식이 아닌 시간당 지출 금액을 약정하는 방식이라 인프라 변경에 유연하게 대응 가능&lt;/li&gt;
&lt;li&gt;아키텍처가 자주 바뀌거나 EC2 외에 Fargate&amp;middot;Lambda도 함께 사용하는 경우&lt;/li&gt;
&lt;li&gt;비용 절감은 원하지만 RI처럼 특정 인스턴스에 고정되고 싶지 않은 경우&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;전용호스트(Dedicated)&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;가상화된 서버에서 EC2를 빌리는 것이 아닌 지정된 실제 물리적인 서버를 임대하는 방식&amp;nbsp;&lt;/li&gt;
&lt;li&gt;라이선스 이슈(Windows server등)&lt;/li&gt;
&lt;li&gt;규정에 따라 필요한 경우&lt;/li&gt;
&lt;li&gt;퍼포먼스 이슈 (CPU Steal 등)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style8&quot; /&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;EC2 의 가격 모델은 EBS 와 별도&lt;/li&gt;
&lt;li&gt;기타 데이터 통신 등의 비용은 별도로 청구 - AWS 는 AWS 바깥으로 나가는 트래픽에 대해서만 요금을 부과&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;인스턴스 유형&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div id=&quot;code_1779195720337&quot; data-ke-type=&quot;html&quot; data-source=&quot;
&amp;lt;style&amp;gt;
  * { box-sizing: border-box; margin: 0; padding: 0; }
  body { background: transparent; }
  .wrapper { font-family: var(--font-sans); padding: 1rem 0; }
  .header { display: flex; align-items: center; gap: 10px; margin-bottom: 20px; }
  .aws-badge { background: #FF9900; color: #000; font-size: 11px; font-weight: 500; padding: 3px 8px; border-radius: 4px; letter-spacing: 0.5px; }
  h2 { font-size: 16px; font-weight: 500; color: var(--color-text-primary); }
  .subtitle { font-size: 12px; color: var(--color-text-secondary); margin-bottom: 20px; }

  .filter-tabs { display: flex; gap: 6px; flex-wrap: wrap; margin-bottom: 18px; }
  .tab { font-size: 12px; padding: 5px 12px; border-radius: 20px; border: 0.5px solid var(--color-border-secondary); background: transparent; color: var(--color-text-secondary); cursor: pointer; transition: all .15s; }
  .tab:hover { background: var(--color-background-secondary); color: var(--color-text-primary); }
  .tab.active { background: var(--color-text-primary); color: var(--color-background-primary); border-color: var(--color-text-primary); }

  .grid { display: flex; flex-direction: column; gap: 10px; }
  .card { background: var(--color-background-secondary); border: 0.5px solid var(--color-border-tertiary); border-radius: 12px; overflow: hidden; transition: border-color .15s; }
  .card:hover { border-color: var(--color-border-primary); }
  .card-header { display: flex; align-items: center; gap: 12px; padding: 14px 16px; cursor: pointer; }
  .icon { width: 32px; height: 32px; border-radius: 8px; display: flex; align-items: center; justify-content: center; font-size: 16px; flex-shrink: 0; }
  .card-title { flex: 1; }
  .cat-name { font-size: 14px; font-weight: 500; color: var(--color-text-primary); }
  .cat-desc { font-size: 12px; color: var(--color-text-secondary); margin-top: 2px; }
  .chevron { font-size: 12px; color: var(--color-text-tertiary); transition: transform .2s; }
  .chevron.open { transform: rotate(180deg); }
  .instances-table { border-top: 0.5px solid var(--color-border-tertiary); }
  .table-head { display: grid; grid-template-columns: 80px 1fr 90px 100px 80px; gap: 8px; padding: 8px 16px; background: var(--color-background-tertiary); }
  .th { font-size: 11px; color: var(--color-text-tertiary); font-weight: 500; letter-spacing: 0.3px; }
  .row { display: grid; grid-template-columns: 80px 1fr 90px 100px 80px; gap: 8px; padding: 10px 16px; border-top: 0.5px solid var(--color-border-tertiary); align-items: center; transition: background .1s; }
  .row:hover { background: var(--color-background-primary); cursor: pointer; }
  .family-tag { font-size: 12px; font-weight: 500; padding: 3px 8px; border-radius: 6px; text-align: center; display: inline-block; }
  .td { font-size: 12px; color: var(--color-text-secondary); }
  .badge-new { font-size: 10px; padding: 2px 6px; background: var(--color-background-success); color: var(--color-text-success); border-radius: 4px; margin-left: 6px; vertical-align: middle; }
  .badge-arm { font-size: 10px; padding: 2px 6px; background: var(--color-background-info); color: var(--color-text-info); border-radius: 4px; margin-left: 4px; }
  .use-chip { font-size: 11px; color: var(--color-text-secondary); background: var(--color-background-tertiary); padding: 2px 7px; border-radius: 10px; display: inline-block; }
  .hidden { display: none; }
&amp;lt;/style&amp;gt;

&amp;lt;div class=&amp;quot;wrapper&amp;quot;&amp;gt;
  &amp;lt;div class=&amp;quot;header&amp;quot;&amp;gt;
    &amp;lt;span class=&amp;quot;aws-badge&amp;quot;&amp;gt;AWS&amp;lt;/span&amp;gt;
    &amp;lt;h2&amp;gt;EC2 인스턴스 타입 가이드 2025&amp;lt;/h2&amp;gt;
  &amp;lt;/div&amp;gt;
  &amp;lt;p class=&amp;quot;subtitle&amp;quot;&amp;gt;최신 세대 기준 &amp;middot; 클릭하면 상세 인스턴스 목록 확인&amp;lt;/p&amp;gt;

  &amp;lt;div class=&amp;quot;filter-tabs&amp;quot;&amp;gt;
    &amp;lt;button class=&amp;quot;tab active&amp;quot; onclick=&amp;quot;filter('all')&amp;quot;&amp;gt;전체&amp;lt;/button&amp;gt;
    &amp;lt;button class=&amp;quot;tab&amp;quot; onclick=&amp;quot;filter('general')&amp;quot;&amp;gt;범용&amp;lt;/button&amp;gt;
    &amp;lt;button class=&amp;quot;tab&amp;quot; onclick=&amp;quot;filter('compute')&amp;quot;&amp;gt;컴퓨팅&amp;lt;/button&amp;gt;
    &amp;lt;button class=&amp;quot;tab&amp;quot; onclick=&amp;quot;filter('memory')&amp;quot;&amp;gt;메모리&amp;lt;/button&amp;gt;
    &amp;lt;button class=&amp;quot;tab&amp;quot; onclick=&amp;quot;filter('accelerated')&amp;quot;&amp;gt;가속 컴퓨팅&amp;lt;/button&amp;gt;
    &amp;lt;button class=&amp;quot;tab&amp;quot; onclick=&amp;quot;filter('storage')&amp;quot;&amp;gt;스토리지&amp;lt;/button&amp;gt;
    &amp;lt;button class=&amp;quot;tab&amp;quot; onclick=&amp;quot;filter('hpc')&amp;quot;&amp;gt;HPC&amp;lt;/button&amp;gt;
  &amp;lt;/div&amp;gt;

  &amp;lt;div class=&amp;quot;grid&amp;quot; id=&amp;quot;grid&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;

&amp;lt;script&amp;gt;
const categories = [
  {
    id: 'general',
    icon: '⚖️',
    color: '#3B8BD4',
    bg: 'rgba(59,139,212,0.15)',
    name: '범용 (General Purpose)',
    desc: '컴퓨팅&amp;middot;메모리&amp;middot;네트워크 균형 &amp;mdash; 웹서버, 소규모 DB, 개발 환경',
    instances: [
      { family: 'M8i', tag: '#2563eb', desc: '4세대 Intel Xeon (Sapphire Rapids)', use: '엔터프라이즈 앱', gen: 'new' },
      { family: 'M8g', tag: '#1d4ed8', desc: 'AWS Graviton4 (ARM)', use: '컨테이너, 마이크로서비스', gen: 'new', arm: true },
      { family: 'M8a', tag: '#1e40af', desc: '4세대 AMD EPYC', use: '웹 앱, 소규모 DB', gen: 'new' },
      { family: 'M7i', tag: '#3b82f6', desc: '3세대 Intel Xeon Scalable', use: '중간 규모 DB', gen: '' },
      { family: 'M7g', tag: '#60a5fa', desc: 'AWS Graviton3 (ARM)', use: '컨테이너, K8s', gen: '', arm: true },
      { family: 'T4g', tag: '#93c5fd', desc: 'AWS Graviton2 &amp;mdash; 버스터블', use: '개발환경, 소규모 앱', gen: '', arm: true },
      { family: 'T3', tag: '#bfdbfe', desc: 'Intel &amp;mdash; 버스터블 성능', use: '저트래픽 웹, 테스트', gen: '' },
    ]
  },
  {
    id: 'compute',
    icon: '⚡',
    color: '#EF9F27',
    bg: 'rgba(239,159,39,0.15)',
    name: '컴퓨팅 최적화 (Compute Optimized)',
    desc: '고성능 CPU &amp;mdash; 배치 처리, 미디어 트랜스코딩, 게임 서버, ML 추론',
    instances: [
      { family: 'C8g', tag: '#d97706', desc: 'AWS Graviton4 (ARM)', use: '배치 처리, HPC', gen: 'new', arm: true },
      { family: 'C8i', tag: '#b45309', desc: '4세대 Intel Xeon', use: '미디어 트랜스코딩', gen: 'new' },
      { family: 'C8a', tag: '#92400e', desc: '4세대 AMD EPYC', use: '고성능 웹서버', gen: 'new' },
      { family: 'C7g', tag: '#f59e0b', desc: 'AWS Graviton3 (ARM)', use: '게임 서버, 과학 모델링', gen: '', arm: true },
      { family: 'C7i', tag: '#fbbf24', desc: '3세대 Intel Xeon', use: '광고 서버, ML 추론', gen: '' },
      { family: 'C7a', tag: '#fcd34d', desc: '4세대 AMD EPYC', use: 'CI/CD, 배치 처리', gen: '' },
    ]
  },
  {
    id: 'memory',
    icon: ' ',
    color: '#8B5CF6',
    bg: 'rgba(139,92,246,0.15)',
    name: '메모리 최적화 (Memory Optimized)',
    desc: '대용량 인메모리 처리 &amp;mdash; SAP HANA, Redis, 실시간 분석, 대형 DB',
    instances: [
      { family: 'R8g', tag: '#7c3aed', desc: 'AWS Graviton4 (ARM)', use: '인메모리 DB, 실시간 분석', gen: 'new', arm: true },
      { family: 'R8i', tag: '#6d28d9', desc: '4세대 Intel Xeon', use: 'SAP HANA, 엔터프라이즈 DB', gen: 'new' },
      { family: 'R7g', tag: '#5b21b6', desc: 'AWS Graviton3 (ARM)', use: 'Redis, Memcached', gen: '', arm: true },
      { family: 'R7i', tag: '#8b5cf6', desc: '3세대 Intel Xeon', use: 'Oracle DB, SQL', gen: '' },
      { family: 'X2gd', tag: '#a78bfa', desc: 'Graviton2 + NVMe SSD', use: '인메모리 캐시, NoSQL', gen: '' },
      { family: 'U7i', tag: '#c4b5fd', desc: 'Intel &amp;mdash; 고용량 메모리 (3~32TB)', use: 'SAP HANA 스케일업', gen: 'new' },
    ]
  },
  {
    id: 'accelerated',
    icon: ' ',
    color: '#EC4899',
    bg: 'rgba(236,72,153,0.15)',
    name: '가속 컴퓨팅 (Accelerated Computing)',
    desc: 'GPU/NPU &amp;mdash; AI/ML 훈련&amp;middot;추론, 그래픽 렌더링, HPC',
    instances: [
      { family: 'P6e-GB200', tag: '#be185d', desc: 'NVIDIA GB200 Superchip', use: 'AI 대규모 훈련', gen: 'new' },
      { family: 'P6-B300', tag: '#9d174d', desc: 'NVIDIA B300 GPU', use: 'ML 훈련, 대형 LLM', gen: 'new' },
      { family: 'P5e', tag: '#831843', desc: 'NVIDIA H200 GPU', use: '딥러닝, 생성형 AI', gen: 'new' },
      { family: 'P4d', tag: '#ec4899', desc: 'NVIDIA A100 GPU', use: 'ML 훈련, HPC', gen: '' },
      { family: 'G6e', tag: '#f472b6', desc: 'NVIDIA L40S GPU', use: '그래픽, 영상 렌더링', gen: 'new' },
      { family: 'Inf2', tag: '#f9a8d4', desc: 'AWS Inferentia2', use: 'ML 추론 (저비용)', gen: '' },
      { family: 'Trn2', tag: '#fbcfe8', desc: 'AWS Trainium2', use: 'LLM 훈련 최적화', gen: 'new' },
    ]
  },
  {
    id: 'storage',
    icon: ' ',
    color: '#10B981',
    bg: 'rgba(16,185,129,0.15)',
    name: '스토리지 최적화 (Storage Optimized)',
    desc: '고속 로컬 NVMe SSD &amp;mdash; MongoDB, Cassandra, Elasticsearch, NoSQL',
    instances: [
      { family: 'I8g', tag: '#065f46', desc: 'Graviton4 + 3세대 Nitro SSD', use: 'NoSQL, 트랜잭션 DB', gen: 'new', arm: true },
      { family: 'I7i', tag: '#047857', desc: 'Intel Emerald Rapids + Nitro SSD', use: 'Elasticsearch, OLTP', gen: 'new' },
      { family: 'I7ie', tag: '#059669', desc: 'Intel + 고용량 NVMe', use: '데이터 레이크, 로그', gen: 'new' },
      { family: 'I4g', tag: '#10b981', desc: 'Graviton2 + NVMe', use: 'Cassandra, Redis', gen: '', arm: true },
      { family: 'D3en', tag: '#34d399', desc: 'HDD 스토리지 특화', use: '분산 파일시스템, HDFS', gen: '' },
    ]
  },
  {
    id: 'hpc',
    icon: ' ',
    color: '#F43F5E',
    bg: 'rgba(244,63,94,0.15)',
    name: 'HPC &amp;amp; 특수 목적',
    desc: '고성능 컴퓨팅, macOS 빌드, 딥러닝 전용',
    instances: [
      { family: 'Hpc7g', tag: '#9f1239', desc: 'Graviton3E &amp;mdash; EFA 네트워크', use: '분자 동역학, 유체 시뮬레이션', gen: '' },
      { family: 'Hpc7a', tag: '#be123c', desc: 'AMD EPYC 9004 &amp;mdash; EFA', use: '기상 모델링, 고성능 시뮬레이션', gen: '' },
      { family: 'Mac-m4', tag: '#e11d48', desc: 'Apple M4 프로세서', use: 'iOS/macOS 빌드, Xcode CI', gen: 'new' },
      { family: 'Mac2-m2', tag: '#f43f5e', desc: 'Apple M2 프로세서', use: 'macOS 앱 빌드', gen: '' },
      { family: 'DL2q', tag: '#fb7185', desc: 'Qualcomm AI 100 가속기', use: '딥러닝 모델 서빙', gen: '' },
    ]
  },
];

function renderRow(inst) {
  return &amp;#96;&amp;lt;div class=&amp;quot;row&amp;quot; onclick=&amp;quot;sendPrompt('AWS EC2 ${inst.family} 인스턴스에 대해 자세히 설명해줘')&amp;quot;&amp;gt;
    &amp;lt;div&amp;gt;&amp;lt;span class=&amp;quot;family-tag&amp;quot; style=&amp;quot;background:${inst.tag}22;color:${inst.tag};border:0.5px solid ${inst.tag}44&amp;quot;&amp;gt;${inst.family}&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;
    &amp;lt;div class=&amp;quot;td&amp;quot;&amp;gt;${inst.desc}${inst.gen==='new' ? '&amp;lt;span class=&amp;quot;badge-new&amp;quot;&amp;gt;NEW&amp;lt;/span&amp;gt;' : ''}${inst.arm ? '&amp;lt;span class=&amp;quot;badge-arm&amp;quot;&amp;gt;ARM&amp;lt;/span&amp;gt;' : ''}&amp;lt;/div&amp;gt;
    &amp;lt;div&amp;gt;&amp;lt;span class=&amp;quot;use-chip&amp;quot;&amp;gt;${inst.use.split(',')[0]}&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;
    &amp;lt;div class=&amp;quot;td&amp;quot; style=&amp;quot;font-size:11px&amp;quot;&amp;gt;${inst.use}&amp;lt;/div&amp;gt;
    &amp;lt;div class=&amp;quot;td&amp;quot; style=&amp;quot;font-size:11px;color:var(--color-text-tertiary)&amp;quot;&amp;gt;클릭 &amp;rarr;&amp;lt;/div&amp;gt;
  &amp;lt;/div&amp;gt;&amp;#96;;
}

function renderCard(cat) {
  return &amp;#96;&amp;lt;div class=&amp;quot;card&amp;quot; data-cat=&amp;quot;${cat.id}&amp;quot;&amp;gt;
    &amp;lt;div class=&amp;quot;card-header&amp;quot; onclick=&amp;quot;toggle(this)&amp;quot;&amp;gt;
      &amp;lt;div class=&amp;quot;icon&amp;quot; style=&amp;quot;background:${cat.bg}&amp;quot;&amp;gt;${cat.icon}&amp;lt;/div&amp;gt;
      &amp;lt;div class=&amp;quot;card-title&amp;quot;&amp;gt;
        &amp;lt;div class=&amp;quot;cat-name&amp;quot;&amp;gt;${cat.name}&amp;lt;/div&amp;gt;
        &amp;lt;div class=&amp;quot;cat-desc&amp;quot;&amp;gt;${cat.desc}&amp;lt;/div&amp;gt;
      &amp;lt;/div&amp;gt;
      &amp;lt;span class=&amp;quot;chevron&amp;quot;&amp;gt;▼&amp;lt;/span&amp;gt;
    &amp;lt;/div&amp;gt;
    &amp;lt;div class=&amp;quot;instances-table hidden&amp;quot;&amp;gt;
      &amp;lt;div class=&amp;quot;table-head&amp;quot;&amp;gt;
        &amp;lt;span class=&amp;quot;th&amp;quot;&amp;gt;패밀리&amp;lt;/span&amp;gt;
        &amp;lt;span class=&amp;quot;th&amp;quot;&amp;gt;프로세서&amp;lt;/span&amp;gt;
        &amp;lt;span class=&amp;quot;th&amp;quot;&amp;gt;주요 용도&amp;lt;/span&amp;gt;
        &amp;lt;span class=&amp;quot;th&amp;quot;&amp;gt;세부 활용&amp;lt;/span&amp;gt;
        &amp;lt;span class=&amp;quot;th&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;
      &amp;lt;/div&amp;gt;
      ${cat.instances.map(renderRow).join('')}
    &amp;lt;/div&amp;gt;
  &amp;lt;/div&amp;gt;&amp;#96;;
}

const grid = document.getElementById('grid');
grid.innerHTML = categories.map(renderCard).join('');

function toggle(header) {
  const table = header.nextElementSibling;
  const ch = header.querySelector('.chevron');
  table.classList.toggle('hidden');
  ch.classList.toggle('open');
}

function filter(id) {
  document.querySelectorAll('.tab').forEach(t =&amp;gt; t.classList.remove('active'));
  event.target.classList.add('active');
  document.querySelectorAll('.card').forEach(card =&amp;gt; {
    if (id === 'all' || card.dataset.cat === id) {
      card.style.display = '';
    } else {
      card.style.display = 'none';
    }
  });
}
&amp;lt;/script&amp;gt;
&quot;&gt;
&lt;style&gt;
  * { box-sizing: border-box; margin: 0; padding: 0; }
  body { background: transparent; }
  .wrapper { font-family: var(--font-sans); padding: 1rem 0; }
  .header { display: flex; align-items: center; gap: 10px; margin-bottom: 20px; }
  .aws-badge { background: #FF9900; color: #000; font-size: 11px; font-weight: 500; padding: 3px 8px; border-radius: 4px; letter-spacing: 0.5px; }
  h2 { font-size: 16px; font-weight: 500; color: var(--color-text-primary); }
  .subtitle { font-size: 12px; color: var(--color-text-secondary); margin-bottom: 20px; }

  .filter-tabs { display: flex; gap: 6px; flex-wrap: wrap; margin-bottom: 18px; }
  .tab { font-size: 12px; padding: 5px 12px; border-radius: 20px; border: 0.5px solid var(--color-border-secondary); background: transparent; color: var(--color-text-secondary); cursor: pointer; transition: all .15s; }
  .tab:hover { background: var(--color-background-secondary); color: var(--color-text-primary); }
  .tab.active { background: var(--color-text-primary); color: var(--color-background-primary); border-color: var(--color-text-primary); }

  .grid { display: flex; flex-direction: column; gap: 10px; }
  .card { background: var(--color-background-secondary); border: 0.5px solid var(--color-border-tertiary); border-radius: 12px; overflow: hidden; transition: border-color .15s; }
  .card:hover { border-color: var(--color-border-primary); }
  .card-header { display: flex; align-items: center; gap: 12px; padding: 14px 16px; cursor: pointer; }
  .icon { width: 32px; height: 32px; border-radius: 8px; display: flex; align-items: center; justify-content: center; font-size: 16px; flex-shrink: 0; }
  .card-title { flex: 1; }
  .cat-name { font-size: 14px; font-weight: 500; color: var(--color-text-primary); }
  .cat-desc { font-size: 12px; color: var(--color-text-secondary); margin-top: 2px; }
  .chevron { font-size: 12px; color: var(--color-text-tertiary); transition: transform .2s; }
  .chevron.open { transform: rotate(180deg); }
  .instances-table { border-top: 0.5px solid var(--color-border-tertiary); }
  .table-head { display: grid; grid-template-columns: 80px 1fr 90px 100px 80px; gap: 8px; padding: 8px 16px; background: var(--color-background-tertiary); }
  .th { font-size: 11px; color: var(--color-text-tertiary); font-weight: 500; letter-spacing: 0.3px; }
  .row { display: grid; grid-template-columns: 80px 1fr 90px 100px 80px; gap: 8px; padding: 10px 16px; border-top: 0.5px solid var(--color-border-tertiary); align-items: center; transition: background .1s; }
  .row:hover { background: var(--color-background-primary); cursor: pointer; }
  .family-tag { font-size: 12px; font-weight: 500; padding: 3px 8px; border-radius: 6px; text-align: center; display: inline-block; }
  .td { font-size: 12px; color: var(--color-text-secondary); }
  .badge-new { font-size: 10px; padding: 2px 6px; background: var(--color-background-success); color: var(--color-text-success); border-radius: 4px; margin-left: 6px; vertical-align: middle; }
  .badge-arm { font-size: 10px; padding: 2px 6px; background: var(--color-background-info); color: var(--color-text-info); border-radius: 4px; margin-left: 4px; }
  .use-chip { font-size: 11px; color: var(--color-text-secondary); background: var(--color-background-tertiary); padding: 2px 7px; border-radius: 10px; display: inline-block; }
  .hidden { display: none; }
&lt;/style&gt;
&lt;div class=&quot;wrapper&quot;&gt;
&lt;div class=&quot;header&quot;&gt;&lt;span class=&quot;aws-badge&quot;&gt;AWS&lt;/span&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;EC2 인스턴스 타입 가이드 2025&lt;/h2&gt;
&lt;/div&gt;
&lt;p class=&quot;subtitle&quot; data-ke-size=&quot;size16&quot;&gt;최신 세대 기준 &amp;middot; 클릭하면 상세 인스턴스 목록 확인&lt;/p&gt;
&lt;div class=&quot;filter-tabs&quot;&gt;&lt;button class=&quot;tab active&quot;&gt;전체&lt;/button&gt; &lt;button class=&quot;tab&quot;&gt;범용&lt;/button&gt; &lt;button class=&quot;tab&quot;&gt;컴퓨팅&lt;/button&gt; &lt;button class=&quot;tab&quot;&gt;메모리&lt;/button&gt; &lt;button class=&quot;tab&quot;&gt;가속 컴퓨팅&lt;/button&gt; &lt;button class=&quot;tab&quot;&gt;스토리지&lt;/button&gt; &lt;button class=&quot;tab&quot;&gt;HPC&lt;/button&gt;&lt;/div&gt;
&lt;div id=&quot;grid&quot; class=&quot;grid&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;script&gt;
const categories = [
  {
    id: 'general',
    icon: '⚖️',
    color: '#3B8BD4',
    bg: 'rgba(59,139,212,0.15)',
    name: '범용 (General Purpose)',
    desc: '컴퓨팅·메모리·네트워크 균형 — 웹서버, 소규모 DB, 개발 환경',
    instances: [
      { family: 'M8i', tag: '#2563eb', desc: '4세대 Intel Xeon (Sapphire Rapids)', use: '엔터프라이즈 앱', gen: 'new' },
      { family: 'M8g', tag: '#1d4ed8', desc: 'AWS Graviton4 (ARM)', use: '컨테이너, 마이크로서비스', gen: 'new', arm: true },
      { family: 'M8a', tag: '#1e40af', desc: '4세대 AMD EPYC', use: '웹 앱, 소규모 DB', gen: 'new' },
      { family: 'M7i', tag: '#3b82f6', desc: '3세대 Intel Xeon Scalable', use: '중간 규모 DB', gen: '' },
      { family: 'M7g', tag: '#60a5fa', desc: 'AWS Graviton3 (ARM)', use: '컨테이너, K8s', gen: '', arm: true },
      { family: 'T4g', tag: '#93c5fd', desc: 'AWS Graviton2 — 버스터블', use: '개발환경, 소규모 앱', gen: '', arm: true },
      { family: 'T3', tag: '#bfdbfe', desc: 'Intel — 버스터블 성능', use: '저트래픽 웹, 테스트', gen: '' },
    ]
  },
  {
    id: 'compute',
    icon: '⚡',
    color: '#EF9F27',
    bg: 'rgba(239,159,39,0.15)',
    name: '컴퓨팅 최적화 (Compute Optimized)',
    desc: '고성능 CPU — 배치 처리, 미디어 트랜스코딩, 게임 서버, ML 추론',
    instances: [
      { family: 'C8g', tag: '#d97706', desc: 'AWS Graviton4 (ARM)', use: '배치 처리, HPC', gen: 'new', arm: true },
      { family: 'C8i', tag: '#b45309', desc: '4세대 Intel Xeon', use: '미디어 트랜스코딩', gen: 'new' },
      { family: 'C8a', tag: '#92400e', desc: '4세대 AMD EPYC', use: '고성능 웹서버', gen: 'new' },
      { family: 'C7g', tag: '#f59e0b', desc: 'AWS Graviton3 (ARM)', use: '게임 서버, 과학 모델링', gen: '', arm: true },
      { family: 'C7i', tag: '#fbbf24', desc: '3세대 Intel Xeon', use: '광고 서버, ML 추론', gen: '' },
      { family: 'C7a', tag: '#fcd34d', desc: '4세대 AMD EPYC', use: 'CI/CD, 배치 처리', gen: '' },
    ]
  },
  {
    id: 'memory',
    icon: ' ',
    color: '#8B5CF6',
    bg: 'rgba(139,92,246,0.15)',
    name: '메모리 최적화 (Memory Optimized)',
    desc: '대용량 인메모리 처리 — SAP HANA, Redis, 실시간 분석, 대형 DB',
    instances: [
      { family: 'R8g', tag: '#7c3aed', desc: 'AWS Graviton4 (ARM)', use: '인메모리 DB, 실시간 분석', gen: 'new', arm: true },
      { family: 'R8i', tag: '#6d28d9', desc: '4세대 Intel Xeon', use: 'SAP HANA, 엔터프라이즈 DB', gen: 'new' },
      { family: 'R7g', tag: '#5b21b6', desc: 'AWS Graviton3 (ARM)', use: 'Redis, Memcached', gen: '', arm: true },
      { family: 'R7i', tag: '#8b5cf6', desc: '3세대 Intel Xeon', use: 'Oracle DB, SQL', gen: '' },
      { family: 'X2gd', tag: '#a78bfa', desc: 'Graviton2 + NVMe SSD', use: '인메모리 캐시, NoSQL', gen: '' },
      { family: 'U7i', tag: '#c4b5fd', desc: 'Intel — 고용량 메모리 (3~32TB)', use: 'SAP HANA 스케일업', gen: 'new' },
    ]
  },
  {
    id: 'accelerated',
    icon: ' ',
    color: '#EC4899',
    bg: 'rgba(236,72,153,0.15)',
    name: '가속 컴퓨팅 (Accelerated Computing)',
    desc: 'GPU/NPU — AI/ML 훈련·추론, 그래픽 렌더링, HPC',
    instances: [
      { family: 'P6e-GB200', tag: '#be185d', desc: 'NVIDIA GB200 Superchip', use: 'AI 대규모 훈련', gen: 'new' },
      { family: 'P6-B300', tag: '#9d174d', desc: 'NVIDIA B300 GPU', use: 'ML 훈련, 대형 LLM', gen: 'new' },
      { family: 'P5e', tag: '#831843', desc: 'NVIDIA H200 GPU', use: '딥러닝, 생성형 AI', gen: 'new' },
      { family: 'P4d', tag: '#ec4899', desc: 'NVIDIA A100 GPU', use: 'ML 훈련, HPC', gen: '' },
      { family: 'G6e', tag: '#f472b6', desc: 'NVIDIA L40S GPU', use: '그래픽, 영상 렌더링', gen: 'new' },
      { family: 'Inf2', tag: '#f9a8d4', desc: 'AWS Inferentia2', use: 'ML 추론 (저비용)', gen: '' },
      { family: 'Trn2', tag: '#fbcfe8', desc: 'AWS Trainium2', use: 'LLM 훈련 최적화', gen: 'new' },
    ]
  },
  {
    id: 'storage',
    icon: ' ',
    color: '#10B981',
    bg: 'rgba(16,185,129,0.15)',
    name: '스토리지 최적화 (Storage Optimized)',
    desc: '고속 로컬 NVMe SSD — MongoDB, Cassandra, Elasticsearch, NoSQL',
    instances: [
      { family: 'I8g', tag: '#065f46', desc: 'Graviton4 + 3세대 Nitro SSD', use: 'NoSQL, 트랜잭션 DB', gen: 'new', arm: true },
      { family: 'I7i', tag: '#047857', desc: 'Intel Emerald Rapids + Nitro SSD', use: 'Elasticsearch, OLTP', gen: 'new' },
      { family: 'I7ie', tag: '#059669', desc: 'Intel + 고용량 NVMe', use: '데이터 레이크, 로그', gen: 'new' },
      { family: 'I4g', tag: '#10b981', desc: 'Graviton2 + NVMe', use: 'Cassandra, Redis', gen: '', arm: true },
      { family: 'D3en', tag: '#34d399', desc: 'HDD 스토리지 특화', use: '분산 파일시스템, HDFS', gen: '' },
    ]
  },
  {
    id: 'hpc',
    icon: ' ',
    color: '#F43F5E',
    bg: 'rgba(244,63,94,0.15)',
    name: 'HPC &amp; 특수 목적',
    desc: '고성능 컴퓨팅, macOS 빌드, 딥러닝 전용',
    instances: [
      { family: 'Hpc7g', tag: '#9f1239', desc: 'Graviton3E — EFA 네트워크', use: '분자 동역학, 유체 시뮬레이션', gen: '' },
      { family: 'Hpc7a', tag: '#be123c', desc: 'AMD EPYC 9004 — EFA', use: '기상 모델링, 고성능 시뮬레이션', gen: '' },
      { family: 'Mac-m4', tag: '#e11d48', desc: 'Apple M4 프로세서', use: 'iOS/macOS 빌드, Xcode CI', gen: 'new' },
      { family: 'Mac2-m2', tag: '#f43f5e', desc: 'Apple M2 프로세서', use: 'macOS 앱 빌드', gen: '' },
      { family: 'DL2q', tag: '#fb7185', desc: 'Qualcomm AI 100 가속기', use: '딥러닝 모델 서빙', gen: '' },
    ]
  },
];

function renderRow(inst) {
  return `&lt;div class=&quot;row&quot; onclick=&quot;sendPrompt('AWS EC2 ${inst.family} 인스턴스에 대해 자세히 설명해줘')&quot;&gt;
    &lt;div&gt;&lt;span class=&quot;family-tag&quot; style=&quot;background:${inst.tag}22;color:${inst.tag};border:0.5px solid ${inst.tag}44&quot;&gt;${inst.family}&lt;/span&gt;&lt;/div&gt;
    &lt;div class=&quot;td&quot;&gt;${inst.desc}${inst.gen==='new' ? '&lt;span class=&quot;badge-new&quot;&gt;NEW&lt;/span&gt;' : ''}${inst.arm ? '&lt;span class=&quot;badge-arm&quot;&gt;ARM&lt;/span&gt;' : ''}&lt;/div&gt;
    &lt;div&gt;&lt;span class=&quot;use-chip&quot;&gt;${inst.use.split(',')[0]}&lt;/span&gt;&lt;/div&gt;
    &lt;div class=&quot;td&quot; style=&quot;font-size:11px&quot;&gt;${inst.use}&lt;/div&gt;
    &lt;div class=&quot;td&quot; style=&quot;font-size:11px;color:var(--color-text-tertiary)&quot;&gt;클릭 →&lt;/div&gt;
  &lt;/div&gt;`;
}

function renderCard(cat) {
  return `&lt;div class=&quot;card&quot; data-cat=&quot;${cat.id}&quot;&gt;
    &lt;div class=&quot;card-header&quot; onclick=&quot;toggle(this)&quot;&gt;
      &lt;div class=&quot;icon&quot; style=&quot;background:${cat.bg}&quot;&gt;${cat.icon}&lt;/div&gt;
      &lt;div class=&quot;card-title&quot;&gt;
        &lt;div class=&quot;cat-name&quot;&gt;${cat.name}&lt;/div&gt;
        &lt;div class=&quot;cat-desc&quot;&gt;${cat.desc}&lt;/div&gt;
      &lt;/div&gt;
      &lt;span class=&quot;chevron&quot;&gt;▼&lt;/span&gt;
    &lt;/div&gt;
    &lt;div class=&quot;instances-table hidden&quot;&gt;
      &lt;div class=&quot;table-head&quot;&gt;
        &lt;span class=&quot;th&quot;&gt;패밀리&lt;/span&gt;
        &lt;span class=&quot;th&quot;&gt;프로세서&lt;/span&gt;
        &lt;span class=&quot;th&quot;&gt;주요 용도&lt;/span&gt;
        &lt;span class=&quot;th&quot;&gt;세부 활용&lt;/span&gt;
        &lt;span class=&quot;th&quot;&gt;&lt;/span&gt;
      &lt;/div&gt;
      ${cat.instances.map(renderRow).join('')}
    &lt;/div&gt;
  &lt;/div&gt;`;
}

const grid = document.getElementById('grid');
grid.innerHTML = categories.map(renderCard).join('');

function toggle(header) {
  const table = header.nextElementSibling;
  const ch = header.querySelector('.chevron');
  table.classList.toggle('hidden');
  ch.classList.toggle('open');
}

function filter(id) {
  document.querySelectorAll('.tab').forEach(t =&gt; t.classList.remove('active'));
  event.target.classList.add('active');
  document.querySelectorAll('.card').forEach(card =&gt; {
    if (id === 'all' || card.dataset.cat === id) {
      card.style.display = '';
    } else {
      card.style.display = 'none';
    }
  });
}
&lt;/script&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;408&quot; data-origin-height=&quot;206&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/wYA8A/dJMcafUdAVt/oh0tRKbH0UXAbAnpBGkFFk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/wYA8A/dJMcafUdAVt/oh0tRKbH0UXAbAnpBGkFFk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/wYA8A/dJMcafUdAVt/oh0tRKbH0UXAbAnpBGkFFk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FwYA8A%2FdJMcafUdAVt%2Foh0tRKbH0UXAbAnpBGkFFk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;408&quot; height=&quot;206&quot; data-origin-width=&quot;408&quot; data-origin-height=&quot;206&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>Concepts/Cloud Infra</category>
      <author>ranygo12</author>
      <guid isPermaLink="true">https://elly1108.tistory.com/18</guid>
      <comments>https://elly1108.tistory.com/18#entry18comment</comments>
      <pubDate>Tue, 19 May 2026 22:20:15 +0900</pubDate>
    </item>
    <item>
      <title>AWS 구성 및 IAM</title>
      <link>https://elly1108.tistory.com/17</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #9d9d9d; text-align: start;&quot;&gt;*이 글은 유튜브 채널 &amp;lt;AWS 강의실&amp;gt;의 영상을 보고 정리한 글입니다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a title=&quot; 쉽게 설명하는 AWS 기초 강좌 3&quot; href=&quot;https://www.youtube.com/watch?v=tvwDDM-Y-qE&amp;amp;t=367s&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://www.youtube.com/watch?v=tvwDDM-Y-qE&amp;amp;t=367s&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt; Amazon Web Services(AWS)는 전 세계적으로 분포한 데이터 센터에서 200개가 넘는 완벽한 기능의 서비스를 제공하는, 세계적으로 가장 포괄적이며, 널리 채택되고 있는 클라우드입니다.&lt;/span&gt;&lt;/blockquote&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;962&quot; data-origin-height=&quot;484&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/tezXF/dJMcaii3Rso/bt7v570oYdMJ1DZIK9CK21/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/tezXF/dJMcaii3Rso/bt7v570oYdMJ1DZIK9CK21/img.png&quot; data-alt=&quot;https://docs.aws.amazon.com/whitepapers/latest/aws-fault-isolation-boundaries/regions.html&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/tezXF/dJMcaii3Rso/bt7v570oYdMJ1DZIK9CK21/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FtezXF%2FdJMcaii3Rso%2Fbt7v570oYdMJ1DZIK9CK21%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;569&quot; height=&quot;286&quot; data-origin-width=&quot;962&quot; data-origin-height=&quot;484&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://docs.aws.amazon.com/whitepapers/latest/aws-fault-isolation-boundaries/regions.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;&lt;b&gt;&lt;span style=&quot;color: #232b37; text-align: start;&quot;&gt;리전&lt;/span&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;AWS의 서비스가 제공되는 서버의 물리적 위치&lt;/li&gt;
&lt;li&gt;전 세계에 흩어져 있으며 큰 구분으로 묶여 있음&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;서울 리전 : ap-northeast-2&lt;/li&gt;
&lt;li&gt;도쿄 리전 :&lt;/li&gt;
&lt;li&gt;리전 별 가능한 서비스가 다름&lt;/li&gt;
&lt;/ul&gt;
&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;지연 속도&lt;/li&gt;
&lt;li&gt;법률 (데이터, 서비스 제공 관련)&lt;/li&gt;
&lt;li&gt;사용 가능한 AWS 서비스&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;가용 영역(Availability Zone)&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;하나의 리전은 반드시 2개 이상의 가용영역으로 구성&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock floatRight&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;610&quot; data-origin-height=&quot;433&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dE6EvM/dJMcaaL2HyO/5jck31NuRk6SznXmbXtn71/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dE6EvM/dJMcaaL2HyO/5jck31NuRk6SznXmbXtn71/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dE6EvM/dJMcaaL2HyO/5jck31NuRk6SznXmbXtn71/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdE6EvM%2FdJMcaaL2HyO%2F5jck31NuRk6SznXmbXtn71%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;293&quot; height=&quot;208&quot; data-origin-width=&quot;610&quot; data-origin-height=&quot;433&quot;/&gt;&lt;/span&gt;&lt;/figure&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;리전 간의 연결은 매우 빠른 전용 네트워크로 연결&lt;/li&gt;
&lt;li&gt;반드시 물리적으로 일정 거리(몇 KM 이상) 떨어져 있음
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;다만 모든 AZ는 서로 100km 이내의 거리에 위치&lt;/li&gt;
&lt;li&gt;여러 재해에 대한 대비 및 보안&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;각 계정별로 AZ의 코드와 실제 위치는 다름
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;예: 계정 Test1의 AZ-A 는 계정 Test2의 AZ-A와 다른 위치 (랜덤)&lt;/li&gt;
&lt;li&gt;보안 및 한 AZ 로 몰림을 방지&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;엣지 로케이션&amp;nbsp;&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;AWS의 CloudFront (CDN) 등의 여러 서비스들을 가장 빠른 속도로 제공(캐싱)하기 위한 거점&amp;nbsp;&lt;/li&gt;
&lt;li&gt;전 세계에 여러 장소에 흩어져 있음&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;901&quot; data-origin-height=&quot;561&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/AAGSX/dJMcaa6qWPI/ONnsNgGNxNKedN9q0ERhFK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/AAGSX/dJMcaa6qWPI/ONnsNgGNxNKedN9q0ERhFK/img.png&quot; data-alt=&quot;https://awsfundamentals.com/blog/aws-edge-locations&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/AAGSX/dJMcaa6qWPI/ONnsNgGNxNKedN9q0ERhFK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FAAGSX%2FdJMcaa6qWPI%2FONnsNgGNxNKedN9q0ERhFK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;445&quot; height=&quot;277&quot; data-origin-width=&quot;901&quot; data-origin-height=&quot;561&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://awsfundamentals.com/blog/aws-edge-locations&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;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;글로벌 서비스와 리전 서비스&amp;nbsp;&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;AWS 에는 서비스가 제공되는 지역의 기반에 따라 글로벌 서비스와 리전 서비스로 분류&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333; background-color: #c0d1e7;&quot;&gt;&lt;b&gt;글로벌 서비스&lt;/b&gt;&lt;/span&gt; : 데이터 및 서비스를 전 세계의 모든 인프라가 공유&amp;nbsp;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;CloudFront : AWS의 CDN(Content Delivery Network) 서비스&amp;nbsp;
&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;/li&gt;
&lt;li&gt;IAM : AWS 리소스에 대한 사용자/권한 관리 서비스
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;누가 어떤 서비스에 접근 가능한지 정책(Policy)으로 제어&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Route53 : AWS의 DNS 서비스
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;도메인(example.com)을 서버 IP나 CloudFront 같은 서비스로 연결&amp;nbsp;&lt;/li&gt;
&lt;li&gt;헬스 체크 및 로드밸런&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;WAF : 웹 애플리케이션 방화벽
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;SQL Injection, XSS 같은 웹 공격을 차단하고 특정 IP/국가/패턴 기반 필터링&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #c0d1e7;&quot;&gt;&lt;b&gt;리전 서비스&lt;/b&gt;&lt;/span&gt; : 특정 리전을 기반으로 데이터 및 서비스를 제공&lt;br /&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;S3 : 전세계에서 동일하게 사용할 수 있으나 데이터 자테는 리전에 종속됨&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ARN&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;: Amazon Resource Name : AWS 으 모든 리소스의 고유 아이디&amp;nbsp;&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;형식 : arn : [partitoin]: [serviece]:[region]:[account_id]:[resource_type]/resourece_name/(qualifier)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;AWS 계정&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock floatRight&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;346&quot; data-origin-height=&quot;563&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kCBwa/dJMcaciNGBH/eB1wb4wK1Y8UUs02y1tJek/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kCBwa/dJMcaciNGBH/eB1wb4wK1Y8UUs02y1tJek/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kCBwa/dJMcaciNGBH/eB1wb4wK1Y8UUs02y1tJek/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkCBwa%2FdJMcaciNGBH%2FeB1wb4wK1Y8UUs02y1tJek%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;157&quot; height=&quot;255&quot; data-origin-width=&quot;346&quot; data-origin-height=&quot;563&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Root User&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;생성 시 만든 이메일 주소로 로그인&lt;/li&gt;
&lt;li&gt;탈취 당했을 때 복구가 매우 힘듬 : 사용을 자제하고 MFA(&lt;span&gt;&amp;nbsp;&lt;/span&gt;Multi-Factor Authentication/일회용 패스워드 생성하여 로그 ) 설정 필요&amp;nbsp;&lt;/li&gt;
&lt;li&gt;루트 유저는 관리용으로만 이용 : 계정 설정 변경, 빌링 등&lt;/li&gt;
&lt;li&gt;AWS API 호출 불가 (Access key. Secret AccessKey 부여 불가)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock floatRight&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;322&quot; data-origin-height=&quot;517&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Zd3EK/dJMcacDa6RQ/0trfsW4kbiOIdKC54fYvJk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Zd3EK/dJMcacDa6RQ/0trfsW4kbiOIdKC54fYvJk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Zd3EK/dJMcacDa6RQ/0trfsW4kbiOIdKC54fYvJk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FZd3EK%2FdJMcacDa6RQ%2F0trfsW4kbiOIdKC54fYvJk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;150&quot; height=&quot;241&quot; data-origin-width=&quot;322&quot; data-origin-height=&quot;517&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&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;IAM User&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;IAM (Identity and Access Management) 을 통해 생성한 유저&lt;/li&gt;
&lt;li&gt;만들 때 주어진 아이디로 로그인&lt;/li&gt;
&lt;li&gt;기본 권한 없음 : 따로 권한을 부여해야 함.&amp;nbsp;
&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;li&gt;회계팀 - 요금&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;꼭 사람이 아닌 어플리케이션 등의 가상 주체를 대표할 수도 있음 (내가 만든 어플리케이션에 부여하여 소통할 수 있음)&lt;/li&gt;
&lt;li&gt;AWS API 호출 가능
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;AccessKey : 아이디 개념&lt;/li&gt;
&lt;li&gt;Secret AccessKey : 패스워드 개념&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;AWS 의 관리를 제오한 모든 작업은 관리용 IAM User 를 만들어 사용&amp;nbsp;&lt;/li&gt;
&lt;li&gt;권한 부여시 루트 유저와 같이 모든 권한을 가질 수 있지만 (Admin Access 를 부여하더라도) 빌링 관련 권한은 루트 유저가 허용해야 함&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #dddddd;&quot;&gt;&amp;rarr; 계정 생성은 이미 실습해봐서 따로 정리하지 않음&lt;/span&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;IAM 기초&lt;/span&gt;&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;AWS Identity and Access Management(IAM) 를 사용하면 AWS 서비스와 리소스에 대한 액세스를 안전하게 관리할 수 있습다. 또한, AWS 사용자 및 그룹을 만들고 관리하며 AWS 리소스에 대한 액세스를 허용 및 거부할 수 있습니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;AWS 어카운트 관리 및 리소스/사용자/서비스의 권한 제어 - 서비스 사용을 위한 인증 정보 부여&lt;/li&gt;
&lt;li&gt;사용자의 생성&amp;middot;관리&amp;middot;계정의 보안 - 사용자의 패스워드 정책 관리 (패스워드 만료 기한 설정 등)&lt;/li&gt;
&lt;li&gt;다른 계정의 리소스 공유 - Identity Feeration (Facebook 로그인, 구글 로그인 등)&lt;/li&gt;
&lt;li&gt;계정에 별명 부여 가능 &amp;rarr; 로그인 주소 생성 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;IAM 구성&lt;/h4&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&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;실제 AWS 를 사용하는 사람 혹은 어플리케이션을 의미&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&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;사용자의 집합&lt;/li&gt;
&lt;li&gt;그룹에 속한 사용자는 그룹에 부여된 권한을 행사&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;정책 (Policy)
&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;JSON 형식으로 정의&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;역할(Role)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;AWS 리소스에 부여하여 AWS 리소스가 무엇을 할 수 있는지를 정의&lt;/li&gt;
&lt;li&gt;혹은 다른 사용자가 역할을 부여 받아 사용&lt;/li&gt;
&lt;li&gt;다른 자격에 대해서 신뢰 관계를 구축 가능&lt;/li&gt;
&lt;li&gt;역할을 바꾸어 가며 서비스를 사용 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&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/bfAuHy/dJMcacQFQks/xT8N8ljDONECJHVDAb8Kc0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bfAuHy/dJMcacQFQks/xT8N8ljDONECJHVDAb8Kc0/img.png&quot; data-origin-width=&quot;740&quot; data-origin-height=&quot;428&quot; data-is-animation=&quot;false&quot; width=&quot;410&quot; height=&quot;237&quot; style=&quot;width: 47.2647%; margin-right: 10px;&quot; data-widthpercent=&quot;47.82&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bfAuHy/dJMcacQFQks/xT8N8ljDONECJHVDAb8Kc0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbfAuHy%2FdJMcacQFQks%2FxT8N8ljDONECJHVDAb8Kc0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;740&quot; height=&quot;428&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bncB0C/dJMcahEqshw/ZgmjnPw41kvFeVqx4TjLxK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bncB0C/dJMcahEqshw/ZgmjnPw41kvFeVqx4TjLxK/img.png&quot; data-origin-width=&quot;898&quot; data-origin-height=&quot;476&quot; data-is-animation=&quot;false&quot; width=&quot;337&quot; height=&quot;179&quot; style=&quot;width: 51.5725%;&quot; data-widthpercent=&quot;52.18&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bncB0C/dJMcahEqshw/ZgmjnPw41kvFeVqx4TjLxK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbncB0C%2FdJMcahEqshw%2FZgmjnPw41kvFeVqx4TjLxK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/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;476&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;h4 data-ke-size=&quot;size20&quot;&gt;IAM 자격 증명보고서&amp;nbsp;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;계정의 모든 사용자와 암호, 액세스 키, MFA 장치 등의 증명 상태를 나열하는 보고서를 생성하고 다운로드 가능&lt;/li&gt;
&lt;li&gt;4시간에 한번씩 생성 가능&amp;nbsp;&lt;/li&gt;
&lt;li&gt;AWS 콘솔, CLI, API 에서 생성 요청 및 다운로드 가능&lt;/li&gt;
&lt;li&gt;포함되는 정보
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&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;암호 활성화 여부&lt;/li&gt;
&lt;li&gt;마지막으로 사용된 시간, 마지막으로 변경된 시간&lt;/li&gt;
&lt;li&gt;언제 변경되어야 하는지&lt;/li&gt;
&lt;/ul&gt;
&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;엑세스 키 활성화 여부&lt;/li&gt;
&lt;li&gt;마지막으로 사용된 시간 및 서비스&lt;/li&gt;
&lt;li&gt;마지막으로 변경된 시간&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;기타
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;MFA 사용 여부&lt;/li&gt;
&lt;li&gt;사용자&amp;nbsp;&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;&lt;span style=&quot;background-color: #ffc9af;&quot;&gt;&lt;b&gt;IAM 주의 사항&amp;nbsp;&lt;/b&gt;&lt;/span&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;불필요한 사용자는 만들지 않기&lt;/li&gt;
&lt;li&gt;가능하면 그룹과 정책을 사용하기&lt;/li&gt;
&lt;li&gt;최소한의 권한만을 허용하는 습관들이기(principle of least privilege)&lt;/li&gt;
&lt;li&gt;MFA를 활성화하기&amp;nbsp;&lt;/li&gt;
&lt;li&gt;AccessKey 대신 역할을 활용하기&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Concepts/Cloud Infra</category>
      <author>ranygo12</author>
      <guid isPermaLink="true">https://elly1108.tistory.com/17</guid>
      <comments>https://elly1108.tistory.com/17#entry17comment</comments>
      <pubDate>Mon, 18 May 2026 17:56:12 +0900</pubDate>
    </item>
    <item>
      <title>클라우드 컴퓨팅</title>
      <link>https://elly1108.tistory.com/16</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;*이 글은 유튜브 채널 &amp;lt;AWS 강의실&amp;gt;의 영상을 보고 정리한 글입니다.&amp;nbsp;&lt;/span&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 style=&quot;color: #000000;&quot;&gt;1. 클라우드 컴퓨팅의 등장 배경&lt;/span&gt;&lt;/h4&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 style=&quot;color: #333333; text-align: start;&quot;&gt;&amp;rArr;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;이걸 해결하는 방법이 서버를 두는 것임.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버- 클라이언트 아키텍쳐를 구성하였고 서버를 여러개 두는 데이터 센터가 생김&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #99cefa;&quot;&gt;&lt;b&gt;&lt;span style=&quot;background-color: #e8f3f8;&quot;&gt;데이터 센터&lt;/span&gt;&lt;/b&gt;&lt;/span&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;/li&gt;
&lt;li&gt;네트워킹&lt;/li&gt;
&lt;li&gt;전원공급장치&lt;/li&gt;
&lt;li&gt;전기 시스템&lt;/li&gt;
&lt;li&gt;백업 발전기&lt;/li&gt;
&lt;li&gt;환경 제어장치&lt;/li&gt;
&lt;li&gt;운영 인력&lt;/li&gt;
&lt;li&gt;기타 인프라 (보안 장치, 휴게 공간 등등)&lt;/li&gt;
&lt;/ul&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;건물 유지 비용, 서버 구매 비용, 셋업, 유지 보수&lt;/li&gt;
&lt;li&gt;한번 구매하면 수용에 상관없이 계속 보유해야 함&lt;/li&gt;
&lt;/ul&gt;
&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;유저의 수요에 빠르게 대처하기 힘듦&lt;/li&gt;
&lt;li&gt;장애 기기를 교체하는 시간 역시 느림&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;rArr; 클라우드가 등장함.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR'; color: #000000;&quot;&gt;클라우드 컴퓨팅은 IT 리소스를 인터넷을 통해 온디맨드로 제공하고 사용한 만큼만 비용을 지불하는 것을 말한다&lt;/span&gt;&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;&amp;nbsp;&lt;/p&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 style=&quot;color: #000000;&quot;&gt;클라우드의 장점&lt;/span&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;nbsp;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #666666;&quot;&gt;데이터 센터 구축 비용, 서버 구매 비용 =&amp;gt; 운영비&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #666666;&quot;&gt;막대한 초기 비용을 소비하는 대신 쓰는 만큼 비용 지불&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333;&quot;&gt;규모의 경제로 얻게 되는 이점&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #666666;&quot;&gt;한 개를 사는 것보다 100개를 사는 게 단가가 낮음 =&amp;gt; 규모의 경제&amp;nbsp;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #666666;&quot;&gt;AWS의 규모의 경제로 인한 이득을 누릴 수 있음&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #666666;&quot;&gt;AWS의 모든 고객과 공동구매 하는 효과&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #666666;&quot;&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;속도 및 민첩성 개선&amp;nbsp;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #666666;&quot;&gt;몇 번의 클릭으로 바로 리소를 확보 가능&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #666666;&quot;&gt;개발비용 절감&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&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;&lt;span style=&quot;color: #666666;&quot;&gt;인프라 관리가 아닌 비즈니스에 자원 집중 가능&lt;/span&gt;&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&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;&lt;span style=&quot;color: #666666;&quot;&gt;몇 번의 클릭으로 전 세계에 서비스 가능&lt;/span&gt;&lt;span style=&quot;color: #666666;&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color: #666666;&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color: #666666;&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color: #666666;&quot;&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;2. 클라우드의 컴퓨팅의 유형&lt;/span&gt;&lt;/h4&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&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;312&quot; data-origin-height=&quot;363&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bDiWTy/dJMcai4j2vm/vm3hhN3ESGkEWiz1P2KcN0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bDiWTy/dJMcai4j2vm/vm3hhN3ESGkEWiz1P2KcN0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bDiWTy/dJMcai4j2vm/vm3hhN3ESGkEWiz1P2KcN0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbDiWTy%2FdJMcai4j2vm%2Fvm3hhN3ESGkEWiz1P2KcN0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;214&quot; height=&quot;249&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;312&quot; data-origin-height=&quot;363&quot;/&gt;&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;style8&quot; /&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;클라우드 컴퓨팅 모델&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;698&quot; data-origin-height=&quot;446&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/d6osRK/dJMcaiJ6EZs/p3NC5Pooluw6rCvMwk22e1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/d6osRK/dJMcaiJ6EZs/p3NC5Pooluw6rCvMwk22e1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/d6osRK/dJMcaiJ6EZs/p3NC5Pooluw6rCvMwk22e1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fd6osRK%2FdJMcaiJ6EZs%2Fp3NC5Pooluw6rCvMwk22e1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;526&quot; height=&quot;336&quot; data-origin-width=&quot;698&quot; data-origin-height=&quot;446&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;IaaS : Infrastructure as a Service&lt;/b&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;OS 를 직접 설치하고 필요한 소프트웨어를 개발해서 사용&lt;/li&gt;
&lt;li&gt;즉 가상의 컴퓨터를 하나 임대하는 것과 비슷함&lt;/li&gt;
&lt;li&gt;예 : AWS EC2&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;PaaS : Platform as a Servie&amp;nbsp;&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;인프라 + OS + 기타프로그램 실행에 필요한 부분(런타임)&lt;/li&gt;
&lt;li&gt;바로 코드만 올려서 돌릴 수 있도록 구성&lt;/li&gt;
&lt;li&gt;예시 : Google App Engine 등&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&amp;nbsp;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;
&lt;div style=&quot;color: #3d3d3a; text-align: start;&quot;&gt;인프라 + OS + 컨테이너 런타임 제공&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;div style=&quot;color: #3d3d3a; text-align: start;&quot;&gt;컨테이너(Docker 등) 단위로 앱 배포&amp;middot;관리&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;div style=&quot;color: #3d3d3a; text-align: start;&quot;&gt;IaaS보다 편리하지만 PaaS보다 유연함&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;예 : &lt;span style=&quot;color: #3d3d3a; text-align: start;&quot;&gt;AWS ECS&lt;/span&gt;&lt;span style=&quot;color: #3d3d3a; text-align: start;&quot;&gt;Google GKE&lt;/span&gt;&lt;span style=&quot;color: #3d3d3a; text-align: start;&quot;&gt;Azure AKS&lt;/span&gt; &lt;span style=&quot;color: #3d3d3a; text-align: start;&quot;&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li style=&quot;background-color: #ffffff; color: #141413; text-align: start;&quot;&gt;&lt;b&gt;CaaS : Container as a Service&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Function as a Service (Serverless)&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/b&gt;:&lt;span&gt;&amp;nbsp;&lt;/span&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;IaaS/PaaS보다 더 높은 추상화 수준&lt;/li&gt;
&lt;li&gt;예 : AWS Lambda, Azure Functions&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;SaaS : Software as a Service&lt;/b&gt; : 인프라 + OS + 필요한 소프트웨어가 제공됨
&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;li&gt;예: gmail, DropBox, Slack, Google Docs&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style8&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; 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;모든 부분이 클라우드에서 실행&lt;/li&gt;
&lt;li&gt;낮은 비용&lt;/li&gt;
&lt;li&gt;높은 확장성&lt;/li&gt;
&lt;/ul&gt;
&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;퍼블릭 클라우드와 프라이빗 클라우드(또는 온-프레미스)를 혼합한 형태&lt;/li&gt;
&lt;li&gt;퍼블릭으로 전환하는 과도기에 사용&lt;/li&gt;
&lt;li&gt;규제&amp;middot;보안 이유로 민감한 데이터는 온-프레미스/프라이빗에, 나머지는 퍼블릭 클라우드에 두는 항구적인 운영 형태로도 사용&lt;/li&gt;
&lt;/ul&gt;
&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;클라우드 기술(가상화 등)을 사용하지만 외부에 공개하지 않고 내부에서만 운영&lt;/li&gt;
&lt;li&gt;높은 보안 및 커스터마이징 가능&lt;/li&gt;
&lt;li&gt;구축&amp;middot;유지보수 비용이 높음&lt;/li&gt;
&lt;/ul&gt;
&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;자체 데이터 센터에 직접 서버를 구축&amp;middot;운영하는 전통적인 방식&lt;/li&gt;
&lt;li&gt;클라우드 기술 없이 물리 서버를 직접 소유 및 관리&lt;/li&gt;
&lt;li&gt;높은 수준의 커스터마이징 가능&lt;/li&gt;
&lt;li&gt;초기 비용 및 유지보수 비용이 매우 높음&lt;/li&gt;
&lt;li&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;&amp;nbsp;&lt;/p&gt;</description>
      <category>Concepts/Cloud Infra</category>
      <author>ranygo12</author>
      <guid isPermaLink="true">https://elly1108.tistory.com/16</guid>
      <comments>https://elly1108.tistory.com/16#entry16comment</comments>
      <pubDate>Wed, 13 May 2026 22:40:15 +0900</pubDate>
    </item>
  </channel>
</rss>