<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>Data Makes Our Future</title>
    <link>https://data-make.tistory.com/</link>
    <description>Data Makes Our Future</description>
    <language>ko</language>
    <pubDate>Wed, 27 May 2026 00:29:59 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>Aaron</managingEditor>
    <image>
      <title>Data Makes Our Future</title>
      <url>https://tistory1.daumcdn.net/tistory/2994519/attach/87022a50bae94c9aa8c38d5bc4481c23</url>
      <link>https://data-make.tistory.com</link>
    </image>
    <item>
      <title>opencode &amp;amp; oh-my-opencode 알아가기</title>
      <link>https://data-make.tistory.com/809</link>
      <description>&lt;div class=&quot;markdown-body&quot;&gt;
&lt;h1&gt;  opencode&lt;/h1&gt;
&lt;p&gt;오픈 소스 AI 코딩 에이전트&lt;/p&gt;
&lt;h2&gt;Key Features&lt;/h2&gt;
&lt;p&gt;✅ 모델 자유도:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Claude 뿐만 아니라 ChatGPT, OpenAI, GitHub Copilot, Google Gemini, 로컬 모델까지 75개 이상의 LLM 프로바이더를 지원&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;✅ LSP(Language Server Protocol) 기본 내장:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;LSP 덕분에 에이전트가 IDE 수준으로 코드를 이해&lt;/li&gt;
&lt;li&gt;프로젝트를 열면 OpenCode가 알아서 적절한 Language Server를 찾아 실행&lt;/li&gt;
&lt;li&gt;타입 정보, 함수 시그니처, 참조 관계까지 정확하게 파악&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;✅ 클라이언트/서버 아키텍처:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;PC에서 OpenCode 서버를 돌리고, 모바일에서 원격으로 조작 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;✅ 멀티 세션과 에이전트 시스템:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;같은 프로젝트에서 여러 에이전트를 동시에 돌릴 수 있음&lt;/li&gt;
&lt;li&gt;Build 모드와 Plan 모드&lt;ul&gt;
&lt;li&gt;Build는 전체 접근 권한을 가진 실행 에이전트&lt;/li&gt;
&lt;li&gt;Plan은 읽기 전용으로 분석과 탐색을 담당&lt;/li&gt;
&lt;li&gt;복잡한 작업을 할 때 Plan 모드에서 먼저 전략을 세우고, Build 모드로 넘어가 실행하는 워크플로우가 유용&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;✅ 기능:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;/share&lt;/code&gt; 명령으로 대화를 링크로 만들어 공유&lt;/li&gt;
&lt;li&gt;에이전트가 만든 변경 사항을 &lt;code&gt;/undo&lt;/code&gt;로 되돌리고, 마음이 바뀌면 &lt;code&gt;/redo&lt;/code&gt;로 다시 적용 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;✅ 프라이버시:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;사용자 코드나 컨텍스트를 저장하지 않음&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Install&lt;/h2&gt;
&lt;p&gt;설치 스크립트를 사용한 설치 &amp;gt; 최신 릴리스는 OpenCode tap 사용을 권장&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;$ curl -fsSL https://opencode.ai/install | bash


...

Installing opencode version: 1.2.10
■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■ 100%
Successfully added opencode to $PATH in /Users/aaron/.zshrc

                                 ▄
█▀▀█ █▀▀█ █▀▀█ █▀▀▄ █▀▀▀ █▀▀█ █▀▀█ █▀▀█
█░░█ █░░█ █▀▀▀ █░░█ █░░░ █░░█ █░░█ █▀▀▀
▀▀▀▀ █▀▀▀ ▀▀▀▀ ▀  ▀ ▀▀▀▀ ▀▀▀▀ ▀▀▀▀ ▀▀▀▀


OpenCode includes free models, to start:

cd &amp;lt;project&amp;gt;  # Open directory
opencode      # Run command

For more information visit https://opencode.ai/docs&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;다운로드가 완료되었다면 &lt;code&gt;~/.zshrc&lt;/code&gt; 파일 적용을 해주어야 opencode 설치 경로가 정상적으로 등록됩니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;$ source ~/.zshrc

...
export PATH=/Users/{id}/.opencode/bin:$PATH&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Config&lt;/h2&gt;
&lt;p&gt;opencode TUI 모드 실행&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;$ opencode&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;API 키를 설정하면 원하는 LLM 제공자를 사용 가능&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;TUI에서 &lt;code&gt;/connect&lt;/code&gt; 명령을 실행한 뒤 구독하고 있는 LLM Provider 선택 및 API 적용&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Init&lt;/h2&gt;
&lt;p&gt;프로젝트 경로로 이동해서 OpenCode 초기화를 통해 프로젝트를 분석하고 프로젝트 루트에 &lt;code&gt;AGENTS.md&lt;/code&gt; 파일을 생성&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;이렇게 하면 OpenCode가 프로젝트 구조와 사용 중인 코딩 패턴을 더 잘 이해&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;$ cd /path/to/project
$ opencode&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이제, TUI에서 &lt;code&gt;/init&lt;/code&gt; 명령을 실행하여 초기화&lt;/p&gt;
&lt;h2&gt;Reference&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://opencode.ai/&quot;&gt;opencode&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/anomalyco/opencode&quot;&gt;opencode repo.&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://opencode.ai/docs/ko&quot;&gt;opencode docs.&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt; ️ oh-my-opencode&lt;/h1&gt;
&lt;p&gt;oh-my-opencode(OmO)는 OpenCode의 플러그인으로, &lt;strong&gt;AI 코딩 에이전트를 오케스트레이션&lt;/strong&gt;하는 도구&lt;/p&gt;
&lt;p&gt;  프롬프트로 LLM 에이전트에서 설치시키기&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;Install and configure oh-my-opencode by following the instructions here:
https://raw.githubusercontent.com/code-yeongyu/oh-my-opencode/refs/heads/master/docs/guide/installation.md&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Key Features&lt;/h2&gt;
&lt;h3&gt;  Discipline Agents&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;모델의 강점을 극대화하는 역할 기반 시스템&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;AI 코딩 에이전트를 쓰다 보면 이런 경험이 있을 것이다. 간단한 작업인데 에이전트가 쓸데없이 코드베이스를 뒤지거나, 반대로 복잡한 작업인데 계획 없이 덤벼들거나. 문제는 모든 작업을 하나의 모델이 처리하려 한다는 데 있다.&lt;/p&gt;
&lt;p&gt;OmO는 작업 종류에 따라 세 가지 핵심 에이전트를 자동으로 배치한다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;Sisyphus&lt;/code&gt;&lt;/strong&gt;(claude-opus-4-6 / kimi-k2.5 / glm-5)는 &lt;strong&gt;&lt;code&gt;메인 오케스트레이터&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;계획을 세우고, 하위 에이전트에 위임하고, 병렬로 실행&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;사용자가 ultrawork 명령을 내리면 Sisyphus는 &lt;strong&gt;작업이 완료될 때까지 스스로를 재호출&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;Hephaestus&lt;/code&gt;&lt;/strong&gt;(gpt-5.3-codex)는 &lt;strong&gt;&lt;code&gt;자율적 딥 워커&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&amp;quot;이 버그 고쳐줘&amp;quot; 같은 &lt;strong&gt;목표만 던져주면 스스로 코드베이스를 탐색하고, 연구하고, 실행&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;GPT-5.3-codex의 코드 이해 능력이 특히 강력해서 복잡한 버그 추적, 리팩토링 같은 작업에 적합&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;Prometheus&lt;/code&gt;&lt;/strong&gt;(claude-opus-4-6 / kimi-k2.5 / glm-5)는 &lt;strong&gt;&lt;code&gt;전략 플래너&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;복잡한 작업을 받으면 인터뷰 모드로 전환해서 &lt;strong&gt;사용자에게 질문&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&amp;quot;이 기능 어디까지 구현할까요?&amp;quot;, &amp;quot;기존 코드 중 건드리지 말아야 할 부분 있나요?&amp;quot; 같은 식&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;범위를 파악하고, 모호성을 제거하고, 검증된 계획을 수립&lt;/strong&gt;한 뒤 Sisyphus에게 넘김&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;핵심은 이 세 에이전트가 각자의 모델 강점에 맞게 튜닝되어 있다는 점이다. 사용자는 모델을 수동으로 선택할 필요가 없다. 작업 카테고리만 지정하면(visual-engineering, deep, quick, ultrabrain 등) 하네스가 적절한 모델을 자동으로 선택한다. Claude는 대화와 계획에 강하고, GPT-5.3-codex는 코드 이해에 강하고, Kimi나 GLM은 긴 컨텍스트 처리에 강하다. 이걸 시스템이 알아서 매칭한다.&lt;/p&gt;
&lt;h3&gt;⚡ ultrawork&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;한 단어로 끝까지 가는 루프&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;AI 에이전트가 작업 중간에 멈추는 이유는 대부분 두 가지&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;컨텍스트 윈도우가 부족하거나&lt;/li&gt;
&lt;li&gt;다음 단계를 판단하지 못해서&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;결국, 사용자는 &amp;quot;계속해&amp;quot;를 반복 입력하게 된다.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;ultrawork&lt;/code&gt;(축약: ulw)는 이 문제를 근본적으로 해결한다. 한 단어를 입력하면 모든 에이전트가 활성화되고, 작업이 100% 완료될 때까지 멈추지 않는다.&lt;/p&gt;
&lt;p&gt;내부적으로 Ralph Loop와 ulw-loop가 작동한다. &lt;strong&gt;&lt;code&gt;에이전트가 스스로를 계속 호출하면서 todo 리스트를 소진&lt;/code&gt;&lt;/strong&gt; 한다. Todo Enforcer는 에이전트가 유휴 상태에 빠지면 시스템이 개입해서 다시 작업하게 만든다. &amp;quot;이거 끝났으니 다음 뭐 하지?&amp;quot;라고 물어보는 대신, 시스템이 &amp;quot;다음은 이거야&amp;quot;라고 알려준다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Tip. OmO 제작자는 ChatGPT($20) + Kimi($0.99) + GLM($10) 조합을 추천
- 월 31달러로 거의 모든 작업을 커버
- Claude API를 직접 쓰는 것보다 저렴하고, 모델 조합도 더 유연&lt;/code&gt;&lt;/pre&gt;&lt;h3&gt;  IntentGate&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;리터럴한 오해석을 막는 의도 분석&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;AI 에이전트는 종종 사용자의 말을 너무 문자 그대로 받아들인다. &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&amp;quot;이 코드 살펴봐&amp;quot;라고 하면 정말 코드만 읽고 끝낸다. &lt;/li&gt;
&lt;li&gt;사용자가 원했던 건 버그를 찾거나 개선점을 제안하는 것이었는데 말이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;code&gt;IntentGate&lt;/code&gt;는 &lt;strong&gt;사용자의 진짜 의도를 먼저 분석한 뒤 분류하고 행동&lt;/strong&gt;한다. &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&amp;quot;이거 살펴봐&amp;quot;가 단순 조사인지, 구현까지 필요한지, 아니면 설명만 원하는 건지 먼저 판단한다. &lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;이게 중요한 이유는, 잘못된 분류가 전체 작업 흐름을 망치기 때문이다. &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;구현이 필요한데 조사만 하고 끝나면, 사용자는 다시 프롬프트를 작성해야 한다. &lt;/li&gt;
&lt;li&gt;컨텍스트는 낭비되고, 시간도 낭비된다. IntentGate는 이 첫 단계를 확실히 잡는다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;  Hashline&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;stale-line 에러를 제로로 만드는 편집 도구&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;AI 코딩 에이전트의 가장 치명적인 문제 중 하나는 편집 도구다. &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;기존 에이전트들은 대부분 줄 번호 기반 편집을 쓴다. &amp;quot;12번째 줄을 이렇게 바꿔&amp;quot;라는 식. &lt;/li&gt;
&lt;li&gt;문제는 파일이 동시에 변경되면 줄 번호가 어긋난다는 것이다. 결과는 stale-line 에러, 코드 손상, 작업 실패다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Hash-Anchored Edit Tool(&lt;code&gt;Hashline&lt;/code&gt;)은 이 문제를 &lt;strong&gt;&lt;code&gt;콘텐츠 해시로 해결&lt;/code&gt;&lt;/strong&gt; 한다. (oh-my-pi에서 영감을 받아 구현)&lt;/p&gt;
&lt;p&gt;동작 방식: &lt;strong&gt;&lt;code&gt;에이전트가 파일을 읽으면 각 줄에 해시 태그&lt;/code&gt;&lt;/strong&gt; 가 붙는다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;11#VK| function hello() {
12#PM|   console.log(&amp;quot;world&amp;quot;);
13#QR| }&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;에이전트가 편집을 시도할 때, 시스템은 해시를 검증&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;11번째 줄의 해시가 여전히 VK인지 확인&lt;/li&gt;
&lt;li&gt;만약 다른 에이전트가 이미 그 줄을 수정했다면 해시가 달라지고, 편집은 거부&lt;/li&gt;
&lt;li&gt;에이전트는 파일을 다시 읽고, 최신 해시를 가져와서 재시도&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Grok Code Fast 1 벤치마크에서 Hashline 적용 전 성공률 6.7% -&amp;gt; 적용 후 68.3% 10배 이상 향상&lt;/p&gt;
&lt;h3&gt; ️ LSP + AST-Grep&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;IDE 수준 정밀도&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;대부분의 AI 에이전트는 코드를 텍스트로 취급&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&amp;quot;이 함수 이름 바꿔&amp;quot;라고 하면 정규식으로 검색해서 치환&lt;/li&gt;
&lt;li&gt;문제는 같은 이름의 다른 함수까지 바뀐다는 것&lt;/li&gt;
&lt;li&gt;주석 안의 함수 이름도 바뀐다. 재앙이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;OmO는 &lt;code&gt;LSP&lt;/code&gt;(Language Server Protocol)를 내장&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;lsp_rename은 IDE처럼 &lt;strong&gt;&lt;code&gt;심볼의 모든 참조를 추적해서 정확히&lt;/code&gt;&lt;/strong&gt; 바꾼다. &lt;/li&gt;
&lt;li&gt;lsp_goto_definition은 정의로 점프하고, lsp_find_references는 모든 사용처를 찾고, lsp_diagnostics는 에러와 경고를 미리 체크한다. &lt;/li&gt;
&lt;li&gt;빌드 전에 문제를 잡는다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;code&gt;AST-Grep&lt;/code&gt;은 25개 언어를 지원하는 &lt;strong&gt;패턴 인식 코드 검색/리라이팅 도구&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&amp;quot;모든 console.log를 logger.info로 바꿔&amp;quot;라는 작업을 AST 수준에서 처리&lt;/li&gt;
&lt;li&gt;문자열 안의 console.log는 건드리지 않는다. 주석 안의 것도 건드리지 않고 코드만 정확히 바꾼다.&lt;/li&gt;
&lt;li&gt;이 두 도구의 조합은 에이전트가 코드를 텍스트가 아니라 구조로 이해하게 만드므로 리팩토링이 안전해진다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;  Background Agents&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;실제 팀처럼 분업하는 시스템&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;하나의 에이전트가 모든 작업을 순차적으로 처리하면 느리다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;OmO는 &lt;strong&gt;&lt;code&gt;5개 이상의 전문 에이전트를 병렬로 동시 실행&lt;/code&gt;&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;메인 컨텍스트 윈도우는 가볍게 유지하고, 백그라운드 에이전트들이 각자 작업을 처리&lt;/li&gt;
&lt;li&gt;결과는 준비되면 수집&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;예를 들어, 프론트엔드 기능을 추가하는 작업이라면,&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;에이전트 A는 API 문서를 검색&lt;/li&gt;
&lt;li&gt;에이전트 B는 기존 컴포넌트를 분석&lt;/li&gt;
&lt;li&gt;에이전트 C는 테스트 코드를 작성&lt;/li&gt;
&lt;li&gt;메인 에이전트는 이 결과들을 종합해서 구현&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;ultrawork와 결합되면, 백그라운드 에이전트들이 병렬로 작업하고, 메인 에이전트는 완료될 때까지 루프를 돌린다. &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;실제 개발팀이 스프린트를 돌리는 것과 비슷한 느낌&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;  Built-in MCPs&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;항상 켜져 있는 정보 파이프라인&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;보통 AI 에이전트가 인터넷에서 검색하거나 공식 문서를 참조하려면 별도 도구를 설정해야 한다. 그렇다 보니 대부분의 사용자는 귀찮아서 안 하게 되는데, 이로 인해 에이전트는 학습 데이터에만 의존하게 되고, 오래된 정보로 코드를 작성한다.&lt;/p&gt;
&lt;p&gt;OmO는 &lt;strong&gt;&lt;code&gt;세 가지 MCP&lt;/code&gt;(Model Context Protocol) &lt;code&gt;서버를 기본 탑재&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Exa&lt;/code&gt;: 웹 검색. 최신 정보, 블로그 포스트, 기술 문서를 실시간으로 조회&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Context7&lt;/code&gt;: 공식 문서. Next.js, React, MongoDB 같은 라이브러리의 최신 공식 문서를 참조&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Grep.app&lt;/code&gt;: GitHub 코드 검색. 100만 개 이상의 오픈소스 프로젝트에서 실제 사용 예시 조회&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;이 세 개는 에이전트가 바로 쓸 수 있도록 항상 켜져 있다. &lt;/p&gt;
&lt;p&gt;예를 들어, &amp;quot;Next.js 15에서 Server Actions 어떻게 써?&amp;quot;라고 물으면, 에이전트는 수동 설정 없이도 &lt;code&gt;Context7&lt;/code&gt;에서 공식 문서를 가져오고, &lt;code&gt;Grep.app&lt;/code&gt;에서 실제 코드 예시를 찾고, &lt;code&gt;Exa&lt;/code&gt;로 최신 블로그 포스트를 참조&lt;/p&gt;
&lt;h3&gt;Skill-Embedded MCPs&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;컨텍스트 윈도우를 절약하는 설계&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;MCP 서버는 강력하지만 서버가 제공하는 모든 도구와 스키마가 프롬프트에 포함되기 때문에 컨텍스트 윈도우를 잡아먹는다. 10개 MCP 서버를 켜두면 컨텍스트의 절반이 도구 설명으로 채워진다.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Skill-Embedded MCP&lt;/code&gt;는 각 스킬(예: Playwright, Git Master)이 자체 MCP 서버를 탑재하여 &lt;strong&gt;&lt;code&gt;필요할 때만 로드되고, 작업이 끝나면 해제&lt;/code&gt;&lt;/strong&gt;된다. 따라서, &lt;strong&gt;&lt;code&gt;컨텍스트 윈도우는 실제 작업에만&lt;/code&gt;&lt;/strong&gt; 쓰인다.&lt;/p&gt;
&lt;p&gt;예를 들어, 브라우저 자동화 작업이 필요하면 Playwright 스킬이 로드되고, Playwright MCP 서버가 활성화된다. 작업이 끝나면 서버는 해제되고, 컨텍스트에서 사라진다. 다음 작업이 Git 관련이면 Git Master 스킬이 로드되고, Git MCP 서버가 활성화된다.&lt;/p&gt;
&lt;p&gt;이 방식은 토큰 효율성과 에이전트 성능을 동시에 높인다. 필요한 도구만 로드되니 컨텍스트가 깨끗하고, 에이전트는 혼란스러워하지 않는다.&lt;/p&gt;
&lt;h3&gt;  Claude Code Compatible&lt;/h3&gt;
&lt;p&gt;기존에 Claude Code에서 설정해 둔 훅, 커맨드, 스킬, MCP, 플러그인을 모두 그대로 쓸 수 있다. 설정 파일도 그대로다. OmO는 &lt;strong&gt;&lt;code&gt;Claude Code의 확장이지, 대체가 아니다&lt;/code&gt;&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;이 덕분에 마이그레이션 비용이 없다. 오늘 OmO를 설치하고 ultrawork 명령어만 익히면, 내일부터 바로 생산성이 올라간다.&lt;/p&gt;
&lt;h3&gt;  Prometheus Planner&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;&amp;quot;&lt;strong&gt;프롬프트 앤 프레이&lt;/strong&gt;&amp;quot;를 넘어서&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;복잡한 작업을 AI 에이전트에게 던지는 건 도박이다. 사용자가 모든 걸 명확히 설명했다고 생각해도, 에이전트는 예상 밖의 방향으로 간다. 범위를 오해하거나, 제약 사항을 무시하거나, 아예 다른 걸 구현한다.&lt;/p&gt;
&lt;p&gt;Prometheus Planner(/start-work)는 &lt;strong&gt;&lt;code&gt;복잡한 작업을 받으면 인터뷰 모드로 전환&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&amp;quot;이 기능 어디까지 구현할까요?&amp;quot;  &lt;/li&gt;
&lt;li&gt;&amp;quot;기존 코드 중 건드리지 말아야 할 부분 있나요?&amp;quot;  &lt;/li&gt;
&lt;li&gt;&amp;quot;테스트는 어느 수준까지 작성할까요?&amp;quot;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;사용자가 답변하면, Prometheus는 범위를 파악하고 모호성을 제거하고 상세 계획을 수립한다. 이 계획은 검증되고, Sisyphus에게 넘어가서 실행된다.&lt;br&gt;&amp;quot;프롬프트 앤 프레이&amp;quot; 방식은 이제 옛날 얘기다. Prometheus는 작업 전에 정렬(alignment)을 먼저 한다.&lt;/p&gt;
&lt;h3&gt;  /init-deep&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;에이전트에게 프로젝트를 설명하는 자동화&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;대규모 프로젝트에서 AI 에이전트를 쓰려면 컨텍스트를 제공해야 한다. &amp;quot;이 프로젝트는 이런 구조고, 이 모듈은 이런 역할을 하고...&amp;quot; 하지만 이걸 매번 프롬프트에 넣으면 토큰이 낭비된다. 프로젝트가 변경되면 문서도 수동으로 업데이트해야 한다.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;/init-deep&lt;/code&gt; 명령은 &lt;strong&gt;&lt;code&gt;프로젝트 전체에 계층적 AGENTS.md를 자동 생성&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;프로젝트 루트에 하나, &amp;#39;src&amp;#39;에 하나, &amp;#39;src/components&amp;#39;에 하나 ..&lt;/li&gt;
&lt;li&gt;각 AGENTS.md는 해당 디렉토리와 하위 모듈의 컨텍스트를 담는다.&lt;/li&gt;
&lt;li&gt;에이전트가 작업할 때 관련 AGENTS.md를 자동으로 읽는다. &lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;예를 들어 &amp;#39;src/components/Button.tsx&amp;#39;를 수정하려면 &amp;#39;src/components/AGENTS.md&amp;#39;를 읽고, 프로젝트 전체 구조가 필요하면 루트의 &amp;#39;AGENTS.md&amp;#39;를 읽는다. 수동 관리가 필요 없다.&lt;/p&gt;
&lt;p&gt;이 시스템은 토큰 효율성과 에이전트 성능을 모두 높인다. 필요한 컨텍스트만 로드되니 윈도우가 절약되고, 에이전트는 정확한 정보를 바탕으로 작업한다.&lt;/p&gt;
&lt;h3&gt;  Comment Checker&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;AI slop을 막는 마지막 방어선&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;AI가 생성한 코드에는 특유의 냄새가 있다. 의미 없는 주석, 과도한 설명, 장황한 문서화. &amp;quot;이 함수는 사용자 입력을 검증합니다&amp;quot; 같은 코멘트는 코드만 봐도 알 수 있는 내용이다.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Comment Checker&lt;/code&gt;는 &lt;strong&gt;&lt;code&gt;에이전트가 생성한 주석을 검토하고, 의미 없는 것들을 제거&lt;/code&gt;&lt;/strong&gt;한다. 결과는 시니어 엔지니어가 작성한 것처럼 읽힌다.&lt;/p&gt;
&lt;h3&gt; ️ Tmux Integration&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;REPL과 디버거도 라이브로&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;AI 에이전트가 터미널에서 할 수 있는 건 제한적이다. 명령을 실행하고, 출력을 받고, 끝이다. 세션이 유지되지 않기 때문에 Python REPL에서 변수를 확인하거나, 디버거를 붙여서 스텝 실행을 하거나, TUI 앱을 실행하는 건 불가능하다.&lt;/p&gt;
&lt;p&gt;OmO는 &lt;code&gt;Tmux&lt;/code&gt;를 &lt;strong&gt;&lt;code&gt;모든 것을 통합&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;에이전트가 Tmux 세션을 열고, 그 안에서 인터랙티브 작업&lt;/li&gt;
&lt;li&gt;Python REPL을 켜서 변수를 확인하고, 디버거를 붙여서 브레이크포인트를 걸고, htop이나 vim 같은 TUI 앱을 실행&lt;/li&gt;
&lt;li&gt;세션은 유지&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;이건 단순한 편의 기능이 아니다. 디버깅 워크플로우가 완전히 달라진다. 에이전트가 코드를 수정하고, 바로 REPL에서 테스트하고, 문제가 있으면 디버거로 추적한다. 사람처럼 일한다.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;OmO는 도구가 아니라 시스템으로, 에이전트가 실제로 일할 수 있는 환경을 만드는 것&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;oh-my-opencode의 각 기능은 독립적으로도 강력하지만, 결합되면 시너지가 생긴다.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Discipline Agents&lt;/code&gt;는 작업을 올바른 모델에 배치하고, &lt;code&gt;ultrawork&lt;/code&gt;는 끝까지 작업을 완료시키고, &lt;code&gt;Background Agents&lt;/code&gt;는 병렬로 분업한다. &lt;/p&gt;
&lt;p&gt;&lt;code&gt;IntentGate&lt;/code&gt;는 의도를 정확히 파악하고, &lt;code&gt;Hashline&lt;/code&gt;은 편집을 안전하게 만들고, &lt;code&gt;LSP&lt;/code&gt;는 코드를 구조로 이해한다. &lt;/p&gt;
&lt;p&gt;&lt;code&gt;Built-in MCPs&lt;/code&gt;는 최신 정보를 제공하고, &lt;code&gt;Skill-Embedded MCPs&lt;/code&gt;는 컨텍스트를 절약하고, &lt;code&gt;/init-deep&lt;/code&gt;는 프로젝트를 자동 문서화한다. &lt;/p&gt;
&lt;p&gt;&lt;code&gt;Prometheus&lt;/code&gt;는 계획을 세우고, &lt;code&gt;Comment Checker&lt;/code&gt;는 AI 냄새를 지우고, &lt;code&gt;Tmux&lt;/code&gt;는 인터랙티브 워크플로우를 가능하게 한다.&lt;/p&gt;
&lt;h2&gt;Example&lt;/h2&gt;
&lt;p&gt;✅ 개인 프로젝트에 리펙토링 요청을 했을 때, 에이전트별 전략 분석 답변&lt;/p&gt;
&lt;p&gt;1️⃣. Sisyphus (메인 오케스트레이터) — 전체 설계 &amp;amp; 판단&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;역할&lt;/th&gt;
&lt;th&gt;전략&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;코드베이스 평가&lt;/td&gt;
&lt;td&gt;먼저 ESLint, 빌드 상태, 프로젝트 구조를 파악하여 &amp;quot;규율 있는(Disciplined) 코드베이스인가?&amp;quot;를 판단&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;리팩토링 범위 결정&lt;/td&gt;
&lt;td&gt;유저에게 &amp;quot;부분 리팩토링 vs 전면 리팩토링&amp;quot; 선택지를 제시하여 스코프 확정&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;작업 분해 &amp;amp; 우선순위&lt;/td&gt;
&lt;td&gt;10개 작업을 의존성 순서로 배치 — ESLint 에러(blocking) → 구조 변경 → 스타일 통일(cosmetic)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;직접 수행 vs 위임 판단&lt;/td&gt;
&lt;td&gt;복잡한 로직 변경(hook 추출, 유틸 생성, 데이터 매니저 중복제거)은 직접 수행, 단순 스타일 통일은 위임&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;도구 선택&lt;/td&gt;
&lt;td&gt;ast_grep_replace로 JSON.parse(JSON.stringify()) → structuredClone 7건을 한 번에 치환 (수동 편집 대신 AST 기반 자동화)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;검증 루프&lt;/td&gt;
&lt;td&gt;매 작업 완료 시 npm run lint 실행하여 회귀 확인&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;핵심 전략&lt;/strong&gt;: &amp;quot;가장 영향도 높은 것부터, 안전한 방법으로&amp;quot; — ESLint 에러를 먼저 잡아 CI를 녹색으로 만든 뒤, 구조적 개선을 진행.&lt;/p&gt;
&lt;p&gt;.&lt;/p&gt;
&lt;p&gt;2️⃣. Explore Agent (탐색 에이전트) — 코드베이스 패턴 발견&lt;br/&gt;&lt;/p&gt;
&lt;p&gt;리팩토링 초기에 병렬 탐색으로 사용됨:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;주간 네비게이션 패턴 탐색: &lt;ul&gt;
&lt;li&gt;5개 컴포넌트에서 currentDate, goToPrevWeek, goToNextWeek가 반복되는 것을 발견 → useWeekNavigation 훅 추출의 근거&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;데이터 변환 중복 탐색: &lt;ul&gt;
&lt;li&gt;dataManager.js에서 transformDailyData/transformTeacherDailyData, updateAttendance/updateTeacherAttendance 등이 거의 동일한 로직인 것을 발견 → generic 함수 추출의 근거&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;낙관적 업데이트 패턴 탐색: &lt;ul&gt;
&lt;li&gt;StudentList.jsx와 TeacherList.jsx에서 deep clone → ensure structure → apply update 패턴이 ~20줄씩 반복 → applyOptimisticUpdate 유틸 추출의 근거&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;AdminPanel 래퍼 함수 탐색: &lt;ul&gt;
&lt;li&gt;handleDataUpdate = (newData) =&amp;gt; setData(newData) 같은 불필요한 passthrough 래퍼 발견 → 직접 setData 전달로 단순화&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;핵심 전략&lt;/strong&gt;: &amp;quot;중복을 찾아라&amp;quot; — Explore는 grep 역할로, 여러 파일에 걸친 패턴 반복을 빠르게 식별. Sisyphus가 &amp;quot;이건 추출할 만한가?&amp;quot;를 판단하는 데이터를 제공.&lt;/p&gt;
&lt;p&gt;.&lt;/p&gt;
&lt;p&gt;3️⃣. Sisyphus-Junior (Quick 카테고리) — 단순 반복 작업 위임&lt;br/&gt;&lt;/p&gt;
&lt;p&gt;총 4번 위임됨 (이전 세션 2회 + 이번 세션 시도 5회 → 취소):&lt;/p&gt;
&lt;p&gt;이전 세션에서 성공한 2건:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;위임 대상&lt;/th&gt;
&lt;th&gt;작업 내용&lt;/th&gt;
&lt;th&gt;전략&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;ClassManagement.jsx&lt;/td&gt;
&lt;td&gt;React import 제거, export 스타일 변경, 세미콜론 제거&lt;/td&gt;
&lt;td&gt;&amp;quot;파일 하나, 3가지 기계적 변환&amp;quot; — 명확한 6-section 프롬프트로 지시&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;TeacherManagement.jsx&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&gt;프롬프트 전략: &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;TASK / EXPECTED OUTCOME / REQUIRED TOOLS / MUST DO / MUST NOT DO / CONTEXT 6개 섹션을 빠짐없이 명시하여 에이전트가 범위를 벗어나지 않도록 제한.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;이번 세션에서 시도 → 취소한 5건:&lt;br&gt;세미콜론 제거를 5개 파일에 병렬 위임했으나, 에이전트 응답이 느려 직접 sed 한 줄로 처리하는 것이 더 효율적이라고 판단하여 전원 취소.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;교훈&lt;/strong&gt;: 작업이 &amp;quot;에이전트 부팅 시간 &amp;gt; 실제 작업 시간&amp;quot;인 경우, 위임보다 직접 수행이 낫다. sed &amp;#39;s/;$//&amp;#39;로 3개 파일을 1초에 처리.&lt;/p&gt;
&lt;p&gt;.&lt;/p&gt;
&lt;p&gt;4️⃣. Sisyphus (직접 수행) — 핵심 구조 변경&lt;br/&gt;&lt;/p&gt;
&lt;p&gt;가장 많은 작업을 직접 수행. 위임하지 않은 이유는 컨텍스트 의존성이 높은 작업이었기 때문:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;작업&lt;/th&gt;
&lt;th&gt;직접 수행 이유&lt;/th&gt;
&lt;th&gt;전략&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;useWeekNavigation 훅 생성 + 5개 컴포넌트 적용&lt;/td&gt;
&lt;td&gt;5개 파일의 기존 로직을 정확히 이해한 상태에서만 안전한 추출 가능&lt;/td&gt;
&lt;td&gt;공통 패턴을 훅으로 추출 → 각 컴포넌트에서 기존 코드를 훅 호출로 교체&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;dataManager.js 중복제거&lt;/td&gt;
&lt;td&gt;6개 export 함수의 시그니처를 유지하면서 내부만 변경해야 함&lt;/td&gt;
&lt;td&gt;transformWeeklyData, upsertRecord, upsertPrayerRequest 3개 제네릭 함수 생성 → 기존 6개 함수를 thin wrapper로 변환 (하위호환 유지)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;applyOptimisticUpdate 유틸&lt;/td&gt;
&lt;td&gt;StudentList/TeacherList 두 파일의 ~20줄 보일러플레이트를 정확히 이해해야 추출 가능&lt;/td&gt;
&lt;td&gt;순수 함수로 설계 (React hook이 아님) — deep clone + ensure structure + apply updater&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;WeeklyListView 공통 컴포넌트&lt;/td&gt;
&lt;td&gt;PrayerView(배열)와 NotesView(단일 문자열)의 미묘한 차이를 extractItems 콜백으로 추상화&lt;/td&gt;
&lt;td&gt;공통 렌더링 로직을 공유 컴포넌트로 추출, 데이터 추출 로직만 각 래퍼에 남김&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AdminPanel 래퍼 제거&lt;/td&gt;
&lt;td&gt;App.jsx ↔ AdminPanel ↔ 하위 컴포넌트 3층 간의 prop 흐름을 정확히 추적해야 함&lt;/td&gt;
&lt;td&gt;passthrough 핸들러 3개 제거 → setData 직접 전달, 불필요한 prop 2개 제거&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;전체 워크플로우 요약&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Phase 1: 진단&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Sisyphus → npm run lint, npm run build로 현재 상태 파악&lt;/li&gt;
&lt;li&gt;Explore → 병렬로 코드베이스 패턴/중복 탐색&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Phase 2: 긴급 수정 (blocking issues)&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Sisyphus 직접 → ESLint 에러 3건 수정 (CI 녹색 확보)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Phase 3: 구조적 개선 (high-impact)&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Sisyphus 직접 → 훅 추출, 유틸 생성, 중복 제거&lt;/li&gt;
&lt;li&gt;AST-grep → JSON.parse 패턴 일괄 치환 (7건)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Phase 4: 스타일 통일 (cosmetic)&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Junior 위임 → ClassManagement, TeacherManagement 스타일 변경&lt;/li&gt;
&lt;li&gt;Sisyphus 직접 (sed) → 나머지 파일 세미콜론 제거&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Phase 5: 검증&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;npm run lint → 0 errors&lt;/li&gt;
&lt;li&gt;npm run build → 성공 (749ms)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;핵심 원칙&lt;/strong&gt;: 복잡한 판단이 필요한 작업은 직접, 기계적 반복은 위임(또는 도구). 매 단계마다 lint/build로 회귀 검증.&lt;/p&gt;
&lt;h2&gt;Reference&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;oh my opencode&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/code-yeongyu/oh-my-opencode&quot;&gt;oh-my-opencode repo.&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;</description>
      <category>AI</category>
      <category>AI</category>
      <category>LLM</category>
      <category>oh-my-opencode</category>
      <category>opencode</category>
      <author>Aaron</author>
      <guid isPermaLink="true">https://data-make.tistory.com/809</guid>
      <comments>https://data-make.tistory.com/809#entry809comment</comments>
      <pubDate>Sun, 1 Mar 2026 14:59:36 +0900</pubDate>
    </item>
    <item>
      <title>[Spring Boot] Graceful Shutdown, 우아하게 종료하기</title>
      <link>https://data-make.tistory.com/808</link>
      <description>&lt;h1&gt;Spring Graceful Shutdown&lt;/h1&gt;
&lt;p&gt;&lt;strong&gt;서비스의 품격 있는 퇴장, Spring Graceful Shutdown&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;갑작스러운 서버 종료는 처리 중이던 요청의 연결 끊김이나 데이터 유실을 초래할 수 있습니다. 이를 방지하기 위해 Spring Boot는 실행 중인 작업을 안전하게 마무리하고 종료하는 &lt;strong&gt;Graceful Shutdown&lt;/strong&gt; 기능을 제공합니다.&lt;/p&gt;
&lt;h1&gt;Web Server&lt;/h1&gt;
&lt;p&gt;&lt;strong&gt;Web Server의 Graceful Shutdown&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-2.3-Release-Notes#graceful-shutdown&quot;&gt;Spring Boot 2.3&lt;/a&gt;부터 주요 4대 임베디드 웹 서버(Tomcat, Jetty, Undertow, Netty) 모두에서 &lt;strong&gt;Graceful Shutdown&lt;/strong&gt;을 지원합니다.&lt;/p&gt;
&lt;p&gt;이 옵션을 활성화하면 서버는 종료 신호(SIGTERM)를 받았을 때 다음과 같이 동작합니다.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;신규 요청 차단&lt;/strong&gt;: 새로운 네트워크 연결을 더 이상 받지 않습니다.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;기존 요청 처리&lt;/strong&gt;: 이미 들어와서 처리 중인 요청들이 완료될 때까지 지정된 시간 동안 기다립니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;✅ 설정 방법&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;application.yml&lt;/code&gt;에 아래 설정을 추가하여 활성화할 수 있습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-yml&quot;&gt;server:
  shutdown: graceful&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;  &lt;strong&gt;참고&lt;/strong&gt;: &lt;a href=&quot;https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-3.4-Release-Notes#graceful-shutdown&quot;&gt;Spring Boot 3.4&lt;/a&gt;부터는 기본값이 &lt;code&gt;immediate&lt;/code&gt;(즉시 종료)에서 &lt;code&gt;graceful&lt;/code&gt;로 변경되었습니다. 최신 버전을 사용 중이라면 별도 설정 없이도 기본적인 우아한 종료가 작동합니다.&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;h3&gt;✅ 종료 유예 기간(Timeout) 설정&lt;/h3&gt;
&lt;p&gt;서버가 모든 요청을 무한정 기다릴 수는 없습니다. 따라서 특정 시간이 지나면 강제로 프로세스를 종료하도록 유예 기간을 설정해야 합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-yml&quot;&gt;spring:
  lifecycle:
    timeout-per-shutdown-phase: 1m # 기본값은 30초&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이 설정은 웹 서버뿐만 아니라 Spring Context 내의 다른 Bean들이 종료되는 &lt;strong&gt;단계별(Phase) 타임아웃&lt;/strong&gt;을 의미합니다. 웹 요청 처리뿐만 아니라 백그라운드 작업 등 애플리케이션의 전반적인 정리 시간을 고려하여 적절한 값을 설정하는 것이 중요합니다.&lt;/p&gt;
&lt;h3&gt;⚠️ 주의사항&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;SIGTERM vs SIGKILL&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Graceful Shutdown은 OS의 &lt;code&gt;SIGTERM&lt;/code&gt; 신호를 받았을 때 작동.&lt;/li&gt;
&lt;li&gt;만약 &lt;code&gt;kill -9(SIGKILL)&lt;/code&gt; 명령어로 프로세스를 즉시 종료하면, Spring이 손쓸 새도 없이 꺼지게 되어 Graceful Shutdown이 작동하지 않음.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Kubernetes 환경&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;쿠버네티스에서 운영 중이라면 &lt;code&gt;terminationGracePeriodSeconds&lt;/code&gt; 설정이 Spring의 타임아웃 설정보다 충분해야 함&lt;/li&gt;
&lt;li&gt;컨테이너가 먼저 죽어버리면 Spring의 Graceful Shutdown 설정이 무의미&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;Thread Pool (TaskExecutor)&lt;/h1&gt;
&lt;p&gt;웹 서버의 Graceful Shutdown 설정은 &amp;#39;HTTP 요청&amp;#39;을 처리하는 스레드에 집중되어 있습니다. 따라서 &lt;code&gt;@Async&lt;/code&gt;나 별도의 비동기 처리를 위해 커스텀하게 생성한 &lt;code&gt;ThreadPoolTaskExecutor&lt;/code&gt;는 별도의 설정 없이는 작업 도중 즉시 종료될 위험이 있습니다.&lt;/p&gt;
&lt;p&gt;비동기 작업까지 안전하게 마무리하려면 아래와 같이 명시적인 설정이 필요합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-kotlin&quot;&gt;@Bean
fun taskExecutor(): TaskExecutor {
    val executor = ThreadPoolTaskExecutor()
    executor.corePoolSize = 5
    executor.maxPoolSize = 10

    // 애플리케이션 종료 시 큐에 대기 중인 작업들을 모두 완료할 때까지 대기
    executor.setWaitForTasksToCompleteOnShutdown(true)

    // 종료를 기다릴 최대 시간 설정
    // spring.lifecycle.timeout-per-shutdown-phase 값보다 작게 설정하는 것을 권장
    executor.setAwaitTerminationSeconds(30)

    executor.initialize()
    return executor
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;✅ 주요 설정&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;setWaitForTasksToCompleteOnShutdown(true)&lt;/strong&gt;: 프로세스 종료 시 해당 Executor가 즉시 멈추지 않고, 현재 실행 중인 작업과 큐에 쌓인 작업들을 처리할 때까지 기다리도록 합니다.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;setAwaitTerminationSeconds(30)&lt;/strong&gt;: 대기 중인 작업들을 처리하기 위해 최대 몇 초간 기다릴지 결정합니다. 이 시간이 지나면 남은 작업과 상관없이 스레드 풀을 강제로 종료합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;Shutdown Callbacks&lt;/h1&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;종료 콜백을 사용하면 우아하게 애플리케이션 종료를 제어할 수 있습니다.&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;  &lt;strong&gt;종료 콜백 접근 방식&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Spring은 컴포넌트 수준과 컨텍스트 수준의 종료 콜백을 모두 지원합니다. 다음과 같이 콜백을 생성할 수 있습니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;@PreDestroy&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;DisposableBean interface&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Bean-destroy method&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Global ServletContextListener&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;@PreDestroy&lt;/h3&gt;
&lt;p&gt;빈 초기화 중에 스프링은 &lt;code&gt;@PreDestroy&lt;/code&gt;로 주석이 달린 모든 빈 메서드를 등록하고 애플리케이션이 종료되면 이를 호출합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-kotlin&quot;&gt;@Component
class Bean1 {
    @PreDestroy
    fun destroy() {
        System.out.println(&amp;quot;Callback triggered - @PreDestroy.&amp;quot;)
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;DisposableBean Interface&lt;/h3&gt;
&lt;p&gt;종료 콜백을 등록하기 위해 &lt;strong&gt;DisposableBean&lt;/strong&gt; 인터페이스 구현&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-kotlin&quot;&gt;@Component
class Bean2 : DisposableBean {
    @Throws(Exception::class)
    override fun destroy() {
        println(
            &amp;quot;Callback triggered - DisposableBean.&amp;quot;
        )
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Bean Destroy Method&lt;/h3&gt;
&lt;p&gt;커스텀 destroy 메드를 가진 클래스 생성&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-kotlin&quot;&gt;class Bean3 {
    fun destroy() {
        println(
            &amp;quot;Callback triggered - bean destroy method.&amp;quot;
        )
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;빈을 초기화하고 destroy() 메서드를 종료 콜백으로 표시하는 configuration 클래스 생성&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-kotlin&quot;&gt;@Configuration
class ShutdownHookConfiguration {
    @Bean(destroyMethod = &amp;quot;destroy&amp;quot;)
    fun initializeBean3(): Bean3? {
        return Bean3()
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Global ServletContextListener&lt;/h3&gt;
&lt;p&gt;다른 세 가지 접근 방식과 달리, 빈 레벨에서 콜백을 등록하는 &lt;code&gt;ServletContextListener&lt;/code&gt;는 컨텍스트 레벨에서 콜백을 등록.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-kotlin&quot;&gt;// custom context listener
class ExampleServletContextListener : ServletContextListener {
    public override fun contextDestroyed(event: ServletContextEvent) {
        println(&amp;quot;Callback triggered - ContextListener.&amp;quot;)
    }

    public override fun contextInitialized(event: ServletContextEvent) {
        // Triggers when context initializes
    }
}

// ServletListenerRegistrationBean 등록
@Bean
fun servletListener(): ServletListenerRegistrationBean&amp;lt;ServletContextListener&amp;gt; {
    val srb = ServletListenerRegistrationBean&amp;lt;ServletContextListener&amp;gt;()
    srb.setListener(ExampleServletContextListener())
    return srb
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Web/Spring</category>
      <category>graceful</category>
      <category>graceful shutdown</category>
      <category>java</category>
      <category>Kotlin</category>
      <category>shutdown</category>
      <category>spring</category>
      <author>Aaron</author>
      <guid isPermaLink="true">https://data-make.tistory.com/808</guid>
      <comments>https://data-make.tistory.com/808#entry808comment</comments>
      <pubDate>Sun, 15 Feb 2026 08:55:04 +0900</pubDate>
    </item>
    <item>
      <title>[Stablecoin] 스테이블코인과 간편 결제 서비스</title>
      <link>https://data-make.tistory.com/807</link>
      <description>&lt;div class=&quot;markdown-body&quot;&gt;
&lt;h1&gt;Pay for stablecoins&lt;/h1&gt;
&lt;h2&gt;스테이블 코인 결제 시스템&lt;/h2&gt;
&lt;p&gt;스테이블 코인은 단순히 &amp;#39;가격이 안정적인 코인&amp;#39;을 넘어, 블록체인 기반의 &lt;strong&gt;효율적인 가치 이전 수단&lt;/strong&gt;으로싸 금융 시스템에 새로운 지평을 열고 있습니다. 특히, 기존의 복잡하고 느린 국경 간 결제 방식을 혁신할 잠재력을 가지고 있습니다.&lt;/p&gt;
&lt;h3&gt;1. 스테이블 코인 &lt;strong&gt;구매&lt;/strong&gt; 단계&lt;/h3&gt;
&lt;p&gt;스테이블 코인을 얻는 과정은 일반적인 암호화폐 거래소 또는 분산형 거래소(DEX)를 통해 이루어집니다.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th align=&quot;left&quot;&gt;구매 방식&lt;/th&gt;
&lt;th align=&quot;left&quot;&gt;설명&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;&lt;strong&gt;중앙화 거래소 (CEX)&lt;/strong&gt;&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;사용자가 &lt;strong&gt;법정화폐(KRW, USD 등)를 입금&lt;/strong&gt;하고, 이를 이용해 USDT, USDC와 같은 스테이블 코인을 시장 가격으로 매수. 이 과정은 &lt;strong&gt;KYC(신원 확인)&lt;/strong&gt; 및 &lt;strong&gt;AML(자금세탁 방지)&lt;/strong&gt; 규제를 준수&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;&lt;strong&gt;분산형 거래소 (DEX)&lt;/strong&gt;&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;이미 다른 암호화폐(ETH, BTC 등)를 보유한 경우, 이더리움 기반의 Uniswap과 같은 DEX에서 해당 암호화폐를 스테이블 코인으로 &lt;strong&gt;Swap&lt;/strong&gt; 하여 교환&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;&lt;strong&gt;발행사 직접 구매&lt;/strong&gt;&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;USDC의 경우, 서클(Circle)과 같은 발행사를 통해 법정화폐를 직접 송금하고 1:1 비율로 스테이블 코인을 발행&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;h3&gt;2. 스테이블 코인 &lt;strong&gt;관리&lt;/strong&gt; 단계&lt;/h3&gt;
&lt;p&gt;구매한 스테이블 코인은 블록체인 네트워크 상의 특정 주소에 저장되며, 이 주소에 대한 접근 권한(Private Key)은 &lt;strong&gt;암호화폐 지갑&lt;/strong&gt; 을 통해 관리&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;하드웨어 지갑 (콜드 스토리지):&lt;/strong&gt; &lt;ul&gt;
&lt;li&gt;물리적 장치에 프라이빗 키를 오프라인으로 보관하여 &lt;strong&gt;보안성이 가장 높음&lt;/strong&gt; &lt;/li&gt;
&lt;li&gt;대규모 자산 관리에 적합&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;소프트웨어 지갑 (핫 스토리지):&lt;/strong&gt; &lt;ul&gt;
&lt;li&gt;모바일 앱이나 웹 기반으로 제공되며, &lt;strong&gt;접근성이 높아&lt;/strong&gt; 일상적인 결제나 거래에 편리&lt;/li&gt;
&lt;li&gt;예: MetaMask, Trust Wallet&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;거래소 지갑:&lt;/strong&gt; &lt;ul&gt;
&lt;li&gt;거래소 플랫폼 내에 보관되는 형태로, 사용자가 프라이빗 키를 직접 소유하지 않고 거래소에 &lt;strong&gt;보관을 위임&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;⭐️ 스테이블 코인 자체는 지갑에 들어있는 것이 아니라 &lt;strong&gt;블록체인 장부&lt;/strong&gt;에 기록되어 있으며, 지갑은 그 기록에 접근하고 거래를 승인할 수 있는 &lt;strong&gt;열쇠(프라이빗 키)&lt;/strong&gt; 역할&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;h3&gt;3. 스테이블 코인 &lt;strong&gt;결제 (Payment)&lt;/strong&gt; 단계&lt;/h3&gt;
&lt;p&gt;스테이블 코인 결제는 기존의 &lt;strong&gt;현금 결제&lt;/strong&gt; 및 &lt;strong&gt;카드 결제&lt;/strong&gt; 방식과는 근본적으로 다른 메커니즘&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th align=&quot;left&quot;&gt;구분&lt;/th&gt;
&lt;th align=&quot;left&quot;&gt;스테이블 코인 결제 (블록체인)&lt;/th&gt;
&lt;th align=&quot;left&quot;&gt;신용/체크카드 결제 (전통 금융)&lt;/th&gt;
&lt;th align=&quot;left&quot;&gt;현금 결제 (물리적)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;&lt;strong&gt;거래 방식&lt;/strong&gt;&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;&lt;strong&gt;P2P (Peer-to-Peer)&lt;/strong&gt; 송금&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;&lt;strong&gt;중앙화 중개&lt;/strong&gt; (카드사, 은행, VAN사)&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;&lt;strong&gt;P2P (Peer-to-Peer)&lt;/strong&gt; 교환&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;&lt;strong&gt;거래 매개&lt;/strong&gt;&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;블록체인 상의 &lt;strong&gt;스마트 컨트랙트&lt;/strong&gt;&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;은행 간 &lt;strong&gt;결제 네트워크 (SWIFT, ACH 등)&lt;/strong&gt;&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;&lt;strong&gt;물리적 화폐&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;&lt;strong&gt;중개자&lt;/strong&gt;&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;&lt;strong&gt;거의 없음&lt;/strong&gt; (네트워크 노드)&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;&lt;strong&gt;필수적&lt;/strong&gt; (카드사, 은행, VAN사, PG사)&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;없음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;&lt;strong&gt;수수료&lt;/strong&gt;&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;&lt;strong&gt;네트워크 수수료 (Gas Fee)&lt;/strong&gt;&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;&lt;strong&gt;가맹점 수수료&lt;/strong&gt; (매출액의 0.5~3%)&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;없음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;&lt;strong&gt;정산 시간&lt;/strong&gt;&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;&lt;strong&gt;수 초 ~ 분&lt;/strong&gt; (블록 생성 속도)&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;&lt;strong&gt;수 시간 ~ 일&lt;/strong&gt; (결제일 기준)&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;&lt;strong&gt;즉시&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;&lt;strong&gt;국경&lt;/strong&gt;&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;&lt;strong&gt;국경 제한 없음&lt;/strong&gt;&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;&lt;strong&gt;국경 간 복잡하고 느림&lt;/strong&gt;&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;국경 간 사용 불가&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;figure&gt;&lt;img src=&quot;https://github.com/jihunparkme/blog/blob/main/img/stablecoin/commission.jpg?raw=true&quot; alt=&quot;&quot;&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;/figure&gt;

&lt;p&gt;ref. &lt;a href=&quot;https://www.hankyung.com/article/2025091678061&quot;&gt;https://www.hankyung.com/article/2025091678061&lt;/a&gt;&lt;/p&gt;
&lt;h4&gt;  스테이블 코인 결제의 상세 과정&lt;/h4&gt;
&lt;p&gt;1️⃣. &lt;strong&gt;결제 요청 및 주소 확인:&lt;/strong&gt; &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;소비자가 판매자에게 스테이블 코인으로 결제하겠다고 요청하면, 판매자는 자신의 블록체인 &lt;strong&gt;지갑 주소(Public Key)&lt;/strong&gt; 및 결제 금액을 소비자에게 (QR 코드, NFC 태그 등을 통해) 제시&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;2️⃣. &lt;strong&gt;트랜잭션 생성:&lt;/strong&gt; &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;소비자는 자신의 지갑 앱에서 해당 주소와 금액을 입력하고, &lt;strong&gt;개인 키(Private Key)&lt;/strong&gt; 로 트랜잭션에 &lt;strong&gt;디지털 서명&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;3️⃣. &lt;strong&gt;네트워크 전송:&lt;/strong&gt; &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;서명된 트랜잭션은 해당 블록체인 네트워크(예: 이더리움, 솔라나)의 &lt;strong&gt;노드(Node)&lt;/strong&gt;들에게 전파&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;4️⃣. &lt;strong&gt;검증 및 기록:&lt;/strong&gt; &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;노드들은 트랜잭션의 유효성을 검증하고, 이를 &lt;strong&gt;블록&lt;/strong&gt;에 담아 체인에 기록&lt;/li&gt;
&lt;li&gt;이 과정이 완료되면 결제가 최종적으로 확정&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;5️⃣. &lt;strong&gt;판매자 확인:&lt;/strong&gt; &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;판매자의 지갑에 스테이블 코인이 수 초~분 내에 입금된 것을 확인하고 상품/서비스를 제공합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;4. 판매자 &lt;strong&gt;정산 및 환전&lt;/strong&gt; 단계&lt;/h3&gt;
&lt;p&gt;판매자 입장에서 스테이블 코인으로 받은 대금을 최종적으로 법정화폐(KRW/USD)로 정산하는 과정&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;즉시 사용:&lt;/strong&gt; &lt;ul&gt;
&lt;li&gt;판매자가 스테이블 코인을 그대로 원자재 구매, 직원 급여 지급, 또는 국경 간 송금에 &lt;strong&gt;재사용&lt;/strong&gt;할 경우, 정산 과정 없이 즉시 유동성을 활용&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;법정화폐로 환전:&lt;/strong&gt;&lt;ol&gt;
&lt;li&gt;판매자는 받은 스테이블 코인을 &lt;strong&gt;중앙화 거래소(CEX)의 지갑&lt;/strong&gt;으로 송금&lt;/li&gt;
&lt;li&gt;거래소에서 스테이블 코인을 법정화폐(KRW/USD)로 &lt;strong&gt;매도&lt;/strong&gt;합니다.&lt;/li&gt;
&lt;li&gt;거래소의 &lt;strong&gt;은행 계좌를 통해&lt;/strong&gt; 판매자의 법정화폐 계좌로 금액을 인출(출금)&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;⚠️ &lt;strong&gt;중요한 차이:&lt;/strong&gt; 신용카드 결제의 경우, 은행/카드사가 개입하여 &lt;strong&gt;결제일&lt;/strong&gt;에 맞춰 정산이 이루어지며, 판매자는 매출액의 일정 부분을 &lt;strong&gt;수수료&lt;/strong&gt;로 공제당합니다. 반면, 스테이블 코인 결제는 P2P 기반으로 &lt;strong&gt;정산 시간이 사실상 수 초~분으로 단축&lt;/strong&gt;되며, 지불하는 비용은 블록체인 네트워크에 지불하는 &lt;strong&gt;Gas Fee&lt;/strong&gt;만 발생&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;h3&gt;스테이블 코인의 잠재력&lt;/h3&gt;
&lt;p&gt;스테이블 코인 결제 방식은 &lt;strong&gt;저렴한 수수료, 신속한 정산(거의 실시간), 국경 없는 거래&lt;/strong&gt;라는 점에서 기존의 카드나 은행 기반 결제 시스템의 단점을 보완&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;특히, 판매자 입장에서 수수료 절감은 물론, &lt;strong&gt;정산 유동성 확보&lt;/strong&gt;에 큰 이점을 제공&lt;/li&gt;
&lt;li&gt;이러한 효율성은 앞으로 글로벌 상거래, 특히 국경 간 거래 분야에서 스테이블 코인의 채택을 가속화하는 핵심 동력&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;간편 결제 서비스와 스테이블 코인&lt;/h2&gt;
&lt;p&gt;순수한 스테이블 코인 결제는 &lt;strong&gt;P2P 온체인 송금&lt;/strong&gt;을 통해 중간 단계를 최소화&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;하지만 이는 사용자 편의성, 법정화폐로의 환전 용이성, 그리고 광범위한 가맹점 확보에 어려움&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;간편 결제 서비스 기업은 이러한 간극을 메워 &lt;strong&gt;스테이블 코인의 대중화&lt;/strong&gt;를 가속화하는 핵심적인 &lt;strong&gt;미들웨어(Middleware)&lt;/strong&gt; 역할을 수행&lt;/p&gt;
&lt;h3&gt;페이 서비스의 주요 역할&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th align=&quot;left&quot;&gt;역할 구분&lt;/th&gt;
&lt;th align=&quot;left&quot;&gt;상세 내용&lt;/th&gt;
&lt;th align=&quot;left&quot;&gt;가치 제안&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;&lt;strong&gt;UX/UI 간소화&lt;/strong&gt;&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;복잡한 지갑 주소 입력, 가스비(Gas Fee) 설정 등을 &lt;strong&gt;앱 내 QR, 바코드, NFC&lt;/strong&gt; 등의 간편한 방식으로 대체하여 일반 소비자에게 익숙한 결제 경험을 제공&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;&lt;strong&gt;사용자 편의성 극대화&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;&lt;strong&gt;On/Off-ramp 중개&lt;/strong&gt;&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;사용자의 &lt;strong&gt;법정화폐 계좌와 스테이블 코인 간의 실시간 전환(발행/상환)&lt;/strong&gt; 을 중개하여, 법정화폐를 스테이블 코인으로 쉽게 구매하고, 받은 스테이블 코인을 쉽게 현금화(정산)&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;&lt;strong&gt;유동성 및 접근성 제공&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;&lt;strong&gt;가맹점 결제망 연동&lt;/strong&gt;&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;이미 확보된 &lt;strong&gt;대규모 가맹점 인프라&lt;/strong&gt;에 스테이블 코인 결제 기능을 연동하여, 판매자 측의 추가적인 시스템 도입 부담 감소&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;&lt;strong&gt;채택률 및 범용성 확대&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;&lt;strong&gt;보안 및 규제 준수&lt;/strong&gt;&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;결제 과정의 &lt;strong&gt;보안&lt;/strong&gt;을 책임지고, &lt;strong&gt;KYC/AML&lt;/strong&gt; 등 각국 금융당국의 규제를 준수하는 시스템을 제공하여 거래의 신뢰도를 향상&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;&lt;strong&gt;신뢰성 및 법적 안정성 확보&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;h2&gt;간편 결제 서비스의 스테이블코인 결제 과정&lt;/h2&gt;
&lt;p&gt;간편 결제 서비스의 스테이블 코인 결제는 순수한 온체인 방식보다는 &lt;strong&gt;&amp;#39;앱 연동을 통한 결제&amp;#39;&lt;/strong&gt; 또는 &lt;strong&gt;&amp;#39;카드를 통한 결제&amp;#39;&lt;/strong&gt; 형태의 하이브리드 모델을 따르게 됩니다.&lt;/p&gt;
&lt;h3&gt;1단계: 사용자 준비&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;코인 확보:&lt;/strong&gt; &lt;ul&gt;
&lt;li&gt;사용자는 간편 결제 앱에 연결된 &lt;strong&gt;은행 계좌&lt;/strong&gt;를 통해 법정화폐를 입금하고, 앱 내에서 이를 원화 스테이블 코인이나 글로벌 스테이블 코인(USDC 등)으로 &lt;strong&gt;전환(발행 요청)&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;잔액 관리:&lt;/strong&gt; &lt;ul&gt;
&lt;li&gt;전환된 스테이블 코인 잔액은 간편 결제 기업이 관리하는 &lt;strong&gt;custodial wallet (수탁 지갑)&lt;/strong&gt; 또는 사용자 연동 지갑에 기록&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;2단계: 결제 요청 및 트랜잭션 생성&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;결제 요청:&lt;/strong&gt; &lt;ul&gt;
&lt;li&gt;사용자는 간편 결제 앱을 실행하고, 판매처의 &lt;strong&gt;QR 코드 또는 바코드&lt;/strong&gt;를 스캔하거나 &lt;strong&gt;실물 연동 카드&lt;/strong&gt;를 제시&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;앱 내 서명 (오프체인 승인):&lt;/strong&gt; &lt;ul&gt;
&lt;li&gt;앱은 결제 금액을 인식하고, 사용자에게 결제 &lt;strong&gt;승인(PIN, 생체 인증)&lt;/strong&gt;을 요청&lt;/li&gt;
&lt;li&gt;이 승인은 블록체인 상의 서명(Private Key) 대신, &lt;strong&gt;간편 결제 기업 서버 내의 인증&lt;/strong&gt;으로 처리 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;3단계: 결제 중개 및 온/오프체인 처리&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;트랜잭션 전달:&lt;/strong&gt; &lt;ul&gt;
&lt;li&gt;간편 결제 기업의 서버는 사용자의 &lt;strong&gt;스테이블 코인 잔액&lt;/strong&gt;을 확인하고, 판매자에게 대금을 지급해야 함을 시스템적으로 기록&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;하이브리드 처리:&lt;/strong&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;온체인 처리(대규모 거래/정산):&lt;/strong&gt; 만약 결제가 대규모이거나, 판매자가 직접 스테이블 코인을 받기를 원한다면, &lt;strong&gt;간편 결제 기업의 서버&lt;/strong&gt;가 사용자를 대신하여 &lt;strong&gt;블록체인에 실제 송금 트랜잭션&lt;/strong&gt;을 생성하고 가스비와 함께 네트워크에 전송&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;4단계: 판매자 정산 (Seller Settlement)&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;정산 확정:&lt;/strong&gt; &lt;ul&gt;
&lt;li&gt;판매자는 간편 결제 기업의 가맹점 관리 시스템을 통해 스테이블 코인 대금을 실시간으로 확인&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;선택적 환전 (Off-ramp):&lt;/strong&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;즉시 현금화:&lt;/strong&gt; 판매자가 정산을 요청하면, 간편 결제 기업은 내부적으로 스테이블 코인을 &lt;strong&gt;법정화폐로 환전&lt;/strong&gt;하고, 은행 시스템을 통해 판매자의 계좌로 &lt;strong&gt;법정화폐&lt;/strong&gt;를 입금. 이 과정에서 간편 결제 기업이 환전 및 PG 수수료를 수취&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;스테이블 코인 유지:&lt;/strong&gt; 판매자가 대금을 스테이블 코인 상태로 유지하고, 이를 다른 스테이블 코인 생태계 내 거래(B2B 송금 등)에 재활용 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description>
      <category>eTc</category>
      <category>stablecoin</category>
      <category>간편결제</category>
      <category>블록체인</category>
      <category>스테이블코인</category>
      <author>Aaron</author>
      <guid isPermaLink="true">https://data-make.tistory.com/807</guid>
      <comments>https://data-make.tistory.com/807#entry807comment</comments>
      <pubDate>Tue, 4 Nov 2025 21:51:14 +0900</pubDate>
    </item>
    <item>
      <title>[Stablecoin] 스테이블코인 기본 개념과 궁금증들</title>
      <link>https://data-make.tistory.com/806</link>
      <description>&lt;div class=&quot;markdown-body&quot;&gt;
&lt;h1&gt;Stablecoin&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스테이블 코인에 대한 관심이 높아지고 있는 만큼, 스테이블 코인의 개념을 잡아보려고 합니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Stablecoin?  &lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스테이블 코인은 말 그대로 &lt;b&gt;'가치가 안정적인(&lt;code&gt;Stable&lt;/code&gt;) 암호화폐(&lt;code&gt;Coin&lt;/code&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;b&gt;가격 변동이 매우 크기 때문&lt;/b&gt;에, 실생활에서 결제 수단으로 사용하거나 가치를 저장하기엔 불안정 ➜ 이러한 문제를 해결하기 위해 스테이블 코인이 등장&lt;/li&gt;
&lt;li&gt;미국 달러나 대한민국 원화같은 &lt;b&gt;실제 화폐에 그 가치를 고정(pegging)&lt;/b&gt; 하여, 1코인이 항상 1달러 또는 1000원의 가치를 갖도록 설계 ➜ 쉽게 말해, &lt;b&gt;'디지털 화폐'&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;br /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt; &amp;zwj;♂️ 스테이블 코인은 왜 필요할까?&lt;/h2&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;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;ex) 스위스로 송금 시 6개의 은행을 거치는데, 스테이블 코인을 사용하면 은행망을 거치지 않고 송금 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;안전한 가치 저장:&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;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;디파이(DeFi)의 핵심:&lt;/b&gt; 탈중앙화된 금융(Decentralized Finance).
&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;모든 금융 거래가 블록체인 위에서 스마트 계약(Smart Contract)이라는 기술을 통해 자동으로 실행되는 프로그램 ➜ 미리 정해진 조건이 충족되면 자동으로 실행.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;ex. &quot;A가 100달러를 B에게 보내면, B는 A에게 5개의 코인을 보낸다&quot;와 같은 계약을 코드&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;은행 없이 예금, 대출 등의 금융 서비스를 이용
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;구분&lt;/th&gt;
&lt;th&gt;기존 금융 (은행)&lt;/th&gt;
&lt;th&gt;탈중앙화 금융 (DeFi)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;중개자&lt;/td&gt;
&lt;td&gt;은행, 증권사, 카드사&lt;/td&gt;
&lt;td&gt;없음 (Smart Contract)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;운영&lt;/td&gt;
&lt;td&gt;시간 은행 영업 시간 (일반적으로 9시-16시)&lt;/td&gt;
&lt;td&gt;24시간 365일&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;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;br /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt; &amp;zwj;♂️ 스테이블 코인의 사용 사례?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해외 송금, 해외 결제 등 디지털 환경에서 실시간 송금이 필요한 모든 곳에서 사용&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1️⃣ 페이팔 스테이블 코인(PYUSD)&lt;/h3&gt;
&lt;figure&gt;&lt;img src=&quot;https://github.com/jihunparkme/blog/blob/main/img/stablecoin/pyusd.png?raw=true&quot; alt=&quot;&quot; /&gt;
&lt;figcaption&gt;&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.paypal.com/us/digital-wallet/manage-money/crypto/pyusd&quot;&gt;PayPal USD (PYUSD) Stablecoin&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;페이팔은 결제 시스템의 디지털 혁신을 목표로 &lt;code&gt;PYUSD&lt;/code&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;를 해결하고, &lt;b&gt;암호화폐의 장점을 결제에 접목&lt;/b&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;b&gt;빠르고 저렴한 송금&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;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;암호화폐 거래&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;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;일상 결제&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;/ul&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;b&gt;1:1 달러 연동&lt;/b&gt;:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;PYUSD는 미국 달러와 1:1의 비율로 가치가 연동&lt;/li&gt;
&lt;li&gt;이 연동을 보장하기 위해, 페이팔은 PYUSD 발행량에 해당하는 미국 달러 예금, 단기 미국 국채, 현금 등가물로 구성된 준비금을 보유&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;투명성&lt;/b&gt;:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;PYUSD의 준비금은 &lt;a href=&quot;https://www.paxos.com/&quot;&gt;Paxos Trust Company&lt;/a&gt;가 매달 투명하게 공개하고, 제3자 회계법인의 감사를 받음
&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;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;이더리움 기반&lt;/b&gt;:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;PYUSD는 이더리움 블록체인 위에서 발행된 &lt;a href=&quot;https://www.investopedia.com/news/what-erc20-and-what-does-it-mean-ethereum/&quot;&gt;ERC-20&lt;/a&gt; 토큰&lt;/li&gt;
&lt;li&gt;이더리움 기반의 다른 암호화폐나 탈중앙화 금융(DeFi) 생태계와 쉽게 상호 운용될 수 있다는 장점&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;페이팔 플랫폼 통합&lt;/b&gt;:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;PYUSD는 페이팔 플랫폼 내에서 손쉽게 구매, 판매, 전송 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2️⃣ 싱가포르의 스테이블 코인&lt;/h3&gt;
&lt;figure&gt;&lt;img src=&quot;https://github.com/jihunparkme/blog/blob/main/img/stablecoin/mas.png?raw=true&quot; alt=&quot;&quot; /&gt;
&lt;figcaption&gt;&lt;/figcaption&gt;
&lt;/figure&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;싱가포르 통화청(MAS)의 명확한 규제와 민간 부문의 적극적인 참여 덕분에 스테이블 코인 생태계가 빠르게 성장&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;b&gt;해외 송금 및 국경 간 결제&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;국제 금융 거래가 활발한 싱가포르의 특성과 잘 맞음&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;일상생활 결제&lt;/b&gt;:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;QR 코드를 이용한 결제가 널리 보급되어 있고, 일부 가맹점에서는 스테이블 코인을 통해 결제가 가능
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이때 코인은 결제 시점에 싱가포르 달러(SGD)로 자동 환전되어 거래&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;사용자는 복잡한 환전 절차 없이도 스테이블 코인을 현금처럼 사용 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;관광 및 상업 분야&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;/ul&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;b&gt;싱가포르 통화청(MAS)의 규제 프레임워크&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;발행량에 해당하는 준비금을 보유하고, 이를 발행사의 자산과 분리하며, 요청 시 5영업일 이내에 액면가로 상환할 의무를 부과&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;'MAS 스테이블 코인' 라벨&lt;/b&gt;:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;MAS의 요건을 충족하는 스테이블 코인에는 특별한 라벨이 부여&lt;/li&gt;
&lt;li&gt;코인의 안정성과 신뢰성을 증명하며, 사용자에게 안전한 선택지를 제공하는 역할&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;프로젝트 가디언 (Project Guardian)&lt;/b&gt;:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;싱가포르는 MAS가 주도하는 &lt;b&gt;'프로젝트 가디언'&lt;/b&gt; 을 통해 금융 시스템의 혁신을 추진&lt;/li&gt;
&lt;li&gt;&lt;b&gt;자산 토큰화(RWA)&lt;/b&gt; 를 통해 주식, 채권, 부동산 등 실물 자산을 디지털 토큰으로 변환하고, 스테이블 코인을 토큰화된 자산을 거래하는 데 필요한 디지털 화폐로 활용하는 것을 목표&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3️⃣ 커머스의 스테이블 코인&lt;/h3&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;b&gt;수수료를 없애거나 대폭 절감&lt;/b&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;b&gt;특별 할인이나 포인트를 제공&lt;/b&gt;하여 사용을 장려함으로써, 고객 충성도를 높이고, 궁극적으로 더 많은 거래를 유도할 수 있는 수단&lt;/li&gt;
&lt;li&gt;결제 정산이 신용카드보다 훨씬 빠르게 이루어져 판매자들이 더 &lt;b&gt;빨리 정산 대금&lt;/b&gt;을 받을 수 있다는 장점&lt;/li&gt;
&lt;/ul&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;a href=&quot;https://it.chosun.com/news/articleView.html?idxno=2023092142561&quot;&gt;월마트&amp;middot;아마존, 자체 스테이블 코인 발행 검토&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://v.daum.net/v/20250910200751413&quot;&gt;쿠팡, 블록체인 실험 돌입&amp;hellip;'스테이블코인 결제'로 시장 뒤흔들까&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;br /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt; &amp;zwj;♂️ 스테이블 코인은 누가 발행할까?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스테이블 코인 발행사인 &lt;b&gt;테더(Tether)&lt;/b&gt; 와 &lt;b&gt;서클(Circle)&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;code&gt;USDT&lt;/code&gt;, &lt;code&gt;USDC&lt;/code&gt;)을 발행&lt;/li&gt;
&lt;li&gt;유사한 사업 모델을 가지고 있지만 자산 운용 방식과 전략에서 차이를 보임&lt;/li&gt;
&lt;/ul&gt;
&lt;img src=&quot;https://github.com/jihunparkme/blog/blob/main/img/stablecoin/tether.png?raw=true&quot; width=&quot;40%&quot; /&gt;&lt;img src=&quot;https://github.com/jihunparkme/blog/blob/main/img/stablecoin/circle.png?raw=true&quot; width=&quot;47.5%&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✅ &lt;b&gt;자산 운용 사례:&lt;/b&gt;&lt;/p&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;-&lt;/th&gt;
&lt;th&gt;테더 (Tether)&lt;/th&gt;
&lt;th&gt;서클 (Circle)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;전략&lt;/td&gt;
&lt;td&gt;USDC에 비해 &lt;b&gt;상대적으로 더 공격적인 자산 운용 전략&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;안전성&lt;/b&gt;과 &lt;b&gt;투명성&lt;/b&gt;을 가장 중요하게 생각하는 전략&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;준비금&lt;/td&gt;
&lt;td&gt;준비금의 대부분은 미국 국채, 환매 조건부 채권, 머니 마켓 펀드 등 &lt;b&gt;안전 자산&lt;/b&gt;으로 구성되지만,&lt;br /&gt;일부를 &lt;b&gt;비트코인(BTC)&lt;/b&gt; 과 &lt;b&gt;금(Gold)&lt;/b&gt; 같은 변동성 자산에도 투자하여 수익을 극대화&lt;/td&gt;
&lt;td&gt;USDC의 준비금은 100% &lt;b&gt;현금 및 현금 등가물&lt;/b&gt;로 구성되어 있으며,&lt;br /&gt;대부분은 &lt;b&gt;미국 재무부 증권(Treasury Bills)&lt;/b&gt; 과 SEC(미국 증권거래위원회)에 등록된 &lt;b&gt;머니 마켓 펀드&lt;/b&gt;에 보관&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;특징&lt;/td&gt;
&lt;td&gt;이러한 자산 배분은 높은 수익을 가져올 수 있지만, &lt;b&gt;시장 변동성에 노출될 위험&lt;/b&gt;이 더 크다는 평가&lt;/td&gt;
&lt;td&gt;매달 회계법인의 감사 보고서를 통해 준비금 내역을 투명하게 공개하여 투자자들에게 높은 신뢰 제공.&lt;br /&gt;이처럼 보수적인 자산 운용은 &lt;b&gt;안정적인 수익과 함께 규제 친화적인 이미지를 구축&lt;/b&gt;하는 데 기여&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✅ &lt;b&gt;수익 모델:&lt;/b&gt;&lt;/p&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;-&lt;/th&gt;
&lt;th&gt;테더 (Tether)&lt;/th&gt;
&lt;th&gt;서클 (Circle)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;공통점&lt;/td&gt;
&lt;td&gt;- &lt;b&gt;스테이블 코인 준비금&lt;/b&gt;에서 발생하는 이자 수익을 주된 수익원으로 삼음&lt;br /&gt;- 사용자에게 받은 법정 화폐(달러)를 &lt;b&gt;안전자산에 투자&lt;/b&gt;하여 이자를 벌어들이는 방식&lt;br /&gt;- 최근의 고금리 환경에서 이 수익은 더욱 커졌으며, 테더는 2023년에 세계 최대 자산 운용 회사인 블랙록보다 더 많은 이익을 기록&lt;/td&gt;
&lt;td&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;추가 수익 모델&lt;/td&gt;
&lt;td&gt;- 준비금을 &lt;b&gt;비트코인&lt;/b&gt;이나 &lt;b&gt;금&lt;/b&gt;에 투자하여 발생하는 평가 이익을 추가적인 수익원으로 활용&lt;br /&gt;- 자체 블록체인 기반의 &lt;b&gt;금융 서비스&lt;/b&gt;를 제공하거나 &lt;b&gt;전략적 투자&lt;/b&gt;를 통해 다양한 수익을 창출&lt;/td&gt;
&lt;td&gt;- 스테이블 코인 &lt;b&gt;발행 및 상환 수수료&lt;/b&gt;를 통해 수익&lt;br /&gt;- 기업 고객을 대상으로 USDC를 활용한 &lt;b&gt;결제 솔루션&lt;/b&gt;, &lt;b&gt;송금 서비스&lt;/b&gt; 등을 제공하여 수수료를 수익화&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;br /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt; &amp;zwj;♂️ USDT, USDC?&lt;/h2&gt;
&lt;figure&gt;&lt;img src=&quot;https://github.com/jihunparkme/blog/blob/main/img/stablecoin/usdc-usdt.png?raw=true&quot; alt=&quot;&quot; /&gt;
&lt;figcaption&gt;&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;USDT&lt;/code&gt;와 &lt;code&gt;USDC&lt;/code&gt;는 모두 미국 달러에 가치를 고정하는 스테이블 코인이지만, 발행 주체, 자산 운용 방식, 투명성, 그리고 규제 환경에서 중요한 차이를 보임&lt;/p&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;-&lt;/th&gt;
&lt;th&gt;USDT (Tether)&lt;/th&gt;
&lt;th&gt;USDC (USD Coin)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;발행 주체 및 운영 방식&lt;/td&gt;
&lt;td&gt;- &lt;b&gt;발행사&lt;/b&gt;: &lt;b&gt;테더&lt;/b&gt;(Tether)사. 암호화폐 시장 초창기인 2014년에 출시되어 &lt;b&gt;가장 오래된 스테이블 코인&lt;/b&gt;&lt;br /&gt;- &lt;b&gt;특징&lt;/b&gt;: &lt;b&gt;암호화폐 거래소 간의 자금 이동&lt;/b&gt;을 위해 주로 사용되며, 비제도권 금융 시장에서 압도적인 유동성을 자랑. 주로 아시아 시장에서 강세&lt;/td&gt;
&lt;td&gt;- &lt;b&gt;발행사&lt;/b&gt;: &lt;b&gt;서클&lt;/b&gt;(Circle)과 &lt;b&gt;코인베이스&lt;/b&gt;(Coinbase)가 공동으로 설립한 '&lt;b&gt;센터&lt;/b&gt;(Centre)' 컨소시엄이 발행&lt;br /&gt;- &lt;b&gt;특징&lt;/b&gt;: 2018년에 출시되었으며, 처음부터 투명성과 규제 준수를 강조하며 제도권 금융과의 협력을 목표. 미국 규제 환경에 맞춰 설계된 것이 가장 큰 특징&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;준비금 운용 방식 및 투명성&lt;/td&gt;
&lt;td&gt;- &lt;b&gt;자산 운용&lt;/b&gt;: 준비금은 미국 국채, 환매 조건부 채권, 머니 마켓 펀드 등 &lt;b&gt;안전 자산 위주로 구성&lt;/b&gt;되지만, 일부를 비트코인과 금 같은 &lt;b&gt;변동성 자산&lt;/b&gt;에도 투자. 높은 수익을 추구하지만, 자산 가치가 급변할 경우 위험에 노출될 수 있다는 비판.&lt;br /&gt;- &lt;b&gt;투명성&lt;/b&gt;: 과거에는 준비금 내역을 명확히 공개하지 않아 '테더 논란'을 낳기도 했음. 최근에는 분기별 감사 보고서를 공개하며 투명성을 강화하려 노력하고 있지만, 여전히 서클에 비해 불투명하다는 평가 존재&lt;/td&gt;
&lt;td&gt;- &lt;b&gt;자산 운용&lt;/b&gt;: 준비금은 100% 현금 또는 미국 단기 국채로만 구성. 서클은 철저히 &lt;b&gt;안전자산만을 담보&lt;/b&gt;로 삼아 안정성을 최우선&lt;br /&gt;- &lt;b&gt;투명성&lt;/b&gt;: 매달 독립적인 회계법인의 감사를 받아 준비금 내역을 상세히 공개. 높은 투명성은 금융 기관과 기업들로부터 신뢰를 얻는 데 핵심적인 역할&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;규제 및 제도권과의 관계&lt;/td&gt;
&lt;td&gt;- &lt;b&gt;규제&lt;/b&gt;: 미국 규제 당국의 감시를 받는다는 점에서 자유롭지 않음. 그동안 불투명한 준비금 운영으로 인해 규제 압박을 받아왔으며, 제도권 금융과의 협력에는 제한적인 모습을 보임.&lt;br /&gt;- &lt;b&gt;특징&lt;/b&gt;: 규제 당국의 영향이 덜한 시장에서 강력한 유동성을 바탕으로 성장&lt;/td&gt;
&lt;td&gt;- &lt;b&gt;규제&lt;/b&gt;: 미국 금융 당국과 협력하며 규제 친화적인 모델을 구축. 비자(Visa), 마스터카드(Mastercard) 등 전통적인 결제 네트워크와 통합되는 데 유리하게 작용&lt;br /&gt;- &lt;b&gt;특징&lt;/b&gt;: 미국 금융 시스템 안에서 '디지털 달러'로 인정받으며, 페이팔, 코인베이스 등 제도권 기업과 파트너십을 맺고 있음.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;시가총액 및 유동성&lt;/td&gt;
&lt;td&gt;과거부터 현재까지 스테이블 코인 시장에서 압도적인 시가총액 1위를 유지. 암호화폐 거래소에서 가장 많이 사용되는 코인이라는 점에서 막강한 유동성&lt;/td&gt;
&lt;td&gt;한때 시가총액 2위였지만, 2023년 실리콘밸리 은행 파산 사태로 인해 일시적인 '페그 이탈(달러 가치 연동 실패)'을 겪으면서 시가총액이 감소한 바 있음. 하지만 여전히 USDT 다음으로 큰 스테이블 코인이며, 투명성 덕분에 기관 투자자들에게 선호&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;br /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt; &amp;zwj;♂️ 스테이블 코인은 어떻게 가치를 안정적으로 유지할까?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스테이블 코인이 가치를 일정하게 유지하는 방법은 크게 세 가지가 존재하고, 담보로 무엇을 잡느냐에 따라 구분&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 법정화폐 담보 스테이블 코인 (Fiat-Collateralized)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 직관적이고 안정적인 방식&lt;br /&gt;스테이블 코인을 발행하는 회사가 &lt;b&gt;은행에 실제 돈(달러, 원 등)을 예치&lt;/b&gt;해 두고, 그 금액만큼만 코인을 발행하는 방식&lt;/p&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&amp;nbsp;&lt;/th&gt;
&lt;th&gt;설명&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;원리&lt;/td&gt;
&lt;td&gt;1개의 코인을 발행할 때마다, 은행 계좌에 1달러를 실제로 넣어둔다.&lt;br /&gt;사용자가 1코인을 가져오면, 은행에 있는 1달러를 내주는 방식&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;비유&lt;/td&gt;
&lt;td&gt;&lt;b&gt;백화점 상품권&lt;/b&gt;과 비슷.&lt;br /&gt;1만 원짜리 상품권을 가져가면 언제든 1만 원어치 물건을 살 수 있는 것과 유사&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;대표 코인&lt;/td&gt;
&lt;td&gt;&lt;b&gt;테더(USDT)&lt;/b&gt;, &lt;b&gt;서클(USDC)&lt;/b&gt; 등이 여기에 해당하며, 가장 널리 사용&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. 암호화폐 담보 스테이블 코인 (Crypto-Collateralized)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다른 암호화폐(비트코인, 이더리움 등)를 담보로 잡고 스테이블 코인을 발행하는 방식&lt;/p&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&amp;nbsp;&lt;/th&gt;
&lt;th&gt;설명&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;원리&lt;/td&gt;
&lt;td&gt;담보로 잡는 암호화폐의 가격 변동성이 크기 때문에, 보통 &lt;b&gt;필요한 금액보다 더 많은 양의 암호화폐를 담보로&lt;/b&gt; 맡기는 방식&lt;br /&gt;담보 가치가 떨어지면 추가 담보를 요구하거나 강제로 청산하여 코인의 가치를 안정&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;비유&lt;/td&gt;
&lt;td&gt;&lt;b&gt;전당포&lt;/b&gt;와 비슷.&lt;br /&gt;10만 원짜리 금반지를 맡기고 5만 원을 빌리는 것처럼, 가치가 변동될 수 있는 자산을 맡기고 안정적인 가치의 돈을 빌리는 것과 유사&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;대표 코인&lt;/td&gt;
&lt;td&gt;&lt;b&gt;다이(DAI)&lt;/b&gt; 가 대표적&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. 알고리즘 스테이블 코인 (Algorithmic)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어떠한 담보도 없이, &lt;b&gt;알고리즘이 코인의 공급량을 조절&lt;/b&gt;하여 가치를 유지하는 방식(가장 혁신적이지만, 그만큼 위험도 큰 방식)&lt;/p&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&amp;nbsp;&lt;/th&gt;
&lt;th&gt;설명&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;원리&lt;/td&gt;
&lt;td&gt;코인 가격이 1달러보다 비싸지면, 알고리즘이 자동으로 코인을 더 많이 발행해서(공급량 증가) 가격을 떨어뜨리고&lt;br /&gt;반대로 1달러보다 싸지면, 코인을 사들여 소각해서(공급량 감소) 가격을 올리는 방식&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;비유&lt;/td&gt;
&lt;td&gt;&lt;b&gt;중앙은행의 통화 정책&lt;/b&gt;과 유사. 시중에 돈이 너무 많이 풀려 물가가 오르면(화폐 가치 하락), 중앙은행이 금리를 올리거나 돈을 거둬들여 통화량을 조절하는 것과 같은 원리&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;대표 코인&lt;/td&gt;
&lt;td&gt;과거 &lt;b&gt;테라USD(UST)&lt;/b&gt; 가 있었지만, 알고리즘의 실패로 가치가 폭락하는 사태를 겪으며 위험성이 크게 부각&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;br /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt; &amp;zwj;♂️ 스테이블코인 구매 방식?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스테이블 코인을 구매하는 방법은 주로 두 가지 주요 경로. 어떤 방법을 선택하느냐에 따라 편리성과 보안성 측면에서 차이가 존재&lt;/p&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;-&lt;/th&gt;
&lt;th&gt;중앙화된 암호화폐 거래소(CEX)를 통한 구매&lt;/th&gt;
&lt;th&gt;탈중앙화된 거래소(DEX) 또는 지갑 내 구매&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;설명&lt;/td&gt;
&lt;td&gt;가장 일반적이고 대중적인 방식&lt;br /&gt;Binance, Coinbase 같은 대형 해외 거래소나, Upbit 등 국내 거래소에서 스테이블 코인을 구매&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;br /&gt;- 원화/달러 입금&lt;br /&gt;- 스테이블 코인 구매&lt;/td&gt;
&lt;td&gt;- 개인용 암호화폐 지갑 설치: MetaMask, Trust Wallet ..&lt;br /&gt;- 다른 코인 구매: DEX에서는 보통 법정 화폐로 직접 스테이블 코인을 살 수 없으므로, 먼저 비트코인(BTC)이나 이더리움(ETH)을 구매&lt;br /&gt;-스테이블 코인으로 교환(Swap): 보유하고 있는 다른 암호화폐를 DEX에서 원하는 스테이블 코인으로 교환(Swap)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;장점&lt;/td&gt;
&lt;td&gt;- 편리성: 매우 간단하고 직관적인 인터페이스를 제공하여 초보자도 쉽게 구매&lt;br /&gt;- 유동성: 거래량이 많아 원하는 가격에 빠르게 거래&lt;/td&gt;
&lt;td&gt;- 탈중앙화: 중앙화된 주체가 없으므로 해킹 및 파산 위험에서 비교적 자유&lt;br /&gt;- 완전한 소유권: 코인의 개인 키를 직접 관리하므로 자산에 대한 통제권이 온전히 사용자에게&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;단점&lt;/td&gt;
&lt;td&gt;- 거래소 의존: 스테이블 코인을 거래소에 보관할 경우, 거래소가 해킹을 당하거나 파산할 경우 자산을 잃을 위험&lt;br /&gt;- 수수료: 거래소마다 거래 수수료나 입출금 수수료가 발생&lt;/td&gt;
&lt;td&gt;- 높은 수수료(Gas Fee): 이더리움 등 특정 블록체인 네트워크의 혼잡도에 따라 가스비가 높게 발생&lt;br /&gt;- 복잡성: 중앙화 거래소에 비해 사용 방법이 복잡하고, 오류가 발생했을 때 도움을 받기 어려움&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;br /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt; &amp;zwj;♂️ 스테이블 코인의 주의할 점?&lt;/h2&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;/li&gt;
&lt;li&gt;&lt;b&gt;알고리즘 실패 위험:&lt;/b&gt; 알고리즘 스테이블 코인은 설계가 잘못되거나 예측 불가능한 시장 상황이 발생하면 가치가 0에 가까워질 수 있음. (테라-루나 사태)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;규제 위험:&lt;/b&gt; 각국 정부와 금융 당국이 스테이블 코인을 어떻게 규제할지에 따라 시장에 큰 영향을 미칠 수 있음&lt;/li&gt;
&lt;/ul&gt;
&lt;br /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt; &amp;zwj;♂️ 스테이블코인은 투자 대상이 될 수 있을까?&lt;/h2&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;/ul&gt;
&lt;br /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt; &amp;zwj;♂️ 한국은행이 만드는 스테이블 코인?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;CBDC&lt;/code&gt;(Central Bank Digital Currency)는 '&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;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✅ &lt;b&gt;CBDC의 정의와 성격&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;즉, 실물 원화가 디지털화된 형태로, 국가가 가치를 100% 보장&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;안정성: 민간이 발행하는 스테이블 코인(예: USDT, USDC, PYUSD)은 발행사의 준비금에 의해 가치가 보장되지만, CBDC는 국가의 신뢰를 바탕으로 하기 때문에 파산, 디페깅(가치 연동 실패) 등으로부터 훨씬 안전&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✅ &lt;b&gt;한국은행의 CBDC 추진 현황&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;한국은행은 2020년대 초부터 CBDC 도입 가능성을 연구해 왔으며, 현재는 &lt;b&gt;'프로젝트 한강'&lt;/b&gt; 이라는 이름으로 실제 테스트를 진행&lt;/li&gt;
&lt;li&gt;1단계 모의실험:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;2021년~2022년 동안 CBDC의 기본 기능(발행, 유통, 회수)을 테스트&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;2단계 활용성 테스트 (프로젝트 한강):
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;2023년부터는 금융기관과 협력하여 실제 금융 거래에서의 활용 가능성을 실험&lt;/li&gt;
&lt;li&gt;일반 국민이 시중은행의 '예금토큰'을 통해 CBDC를 간접적으로 체험하고, 이를 통해 상품을 구매하는 등 실거래 테스트를 진행&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✅ &lt;b&gt;CBDC의 주요 특징 및 목적&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;민간 스테이블 코인과의 차이점:&lt;/b&gt;&lt;/p&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;발행 주체&lt;/th&gt;
&lt;th&gt;민간 기업(테더, 서클, 페이팔)&lt;/th&gt;
&lt;th&gt;중앙은행(한국은행)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&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;&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;/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;은행 계좌가 없는 사람도 스마트폰만 있으면 CBDC로 금융 거래를 할 수 있게 되어, 금융 소외 계층의 접근성을 높임&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;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✅ &lt;b&gt;CBDC 도입의 과제와 논란&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;CBDC는 모든 거래 내역이 중앙은행에 기록될 가능성이 있어, 개인 금융 정보가 투명하게 노출될 수 있다는 우려가&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;CBDC가 은행 예금을 대체할 경우, 시중은행의 예금 규모가 줄어들어 은행의 자금 중개 기능이 약화&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;민간에서 원화 기반 스테이블 코인 발행을 시도하는 움직임이 있어, 한국은행의 CBDC와 민간 스테이블 코인이 어떻게 공존할지에 대한 논의가 필요&lt;/li&gt;
&lt;li&gt;한국은행은 민간의 무분별한 스테이블 코인 발행에는 신중한 입장&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;br /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt; &amp;zwj;♂️ 원화 스테이블 코인과 달러 스테이블 코인과의 거래?&lt;/h2&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;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✅ &lt;b&gt;거래 방식&lt;/b&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 거래는 주로 두 가지 경로로 이루어짐&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;중앙화된 암호화폐 거래소(CEX):&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;b&gt;거래 쌍(Trading Pair)&lt;/b&gt;을 이용해 매수/매도하는 방식&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;탈중앙화된 거래소(DEX):&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;유동성 풀(Liquidity Pool)&lt;/b&gt;을 통해 두 스테이블코인을 교환(Swap)&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;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✅ &lt;b&gt;환율 적용&lt;/b&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스테이블코인 간의 거래에서 중요한 요소는 실시간 환율&lt;/p&gt;
&lt;/blockquote&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;원화 스테이블코인(가상적으로 1KRW = 1 코인)과 달러 스테이블코인(1USD = 1 코인)의 교환 비율은 실제 원화-달러 환율에 따라 결정&lt;/li&gt;
&lt;/ul&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,300원이라면, 1달러 스테이블코인($1)을 1,300원 스테이블코인($1,300)으로 교환하는 것이 기본적인 원리&lt;/li&gt;
&lt;li&gt;거래소나 DEX는 이 환율에 약간의 &lt;b&gt;스프레드(Spread)&lt;/b&gt;나 거래 수수료를 더해 최종 교환 비율을 제시&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;차익 거래(Arbitrage):&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;/ul&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;b&gt;수수료 절감&lt;/b&gt;&lt;/p&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;은행 수수료&lt;/th&gt;
&lt;th&gt;환전 스프레드&lt;/th&gt;
&lt;th&gt;스테이블 코인 수수료&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&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;&lt;b&gt;시간 단축 (신속성)&lt;/b&gt;&lt;/p&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;은행 송금&lt;/th&gt;
&lt;th&gt;스테이블 코인&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;전통적인 해외 송금은 여러 중개 은행을 거치기 때문에 보통 2~5 영업일이 소요&lt;br /&gt;주말이나 공휴일에는 송금이 불가능&lt;/td&gt;
&lt;td&gt;블록체인 네트워크를 통해 전송되므로 거의 실시간으로 송금&lt;br /&gt;24시간 365일 언제든 송금이 가능하여 긴급한 자금 이체에 매우 유리&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;금융 포용성 (접근성)&lt;/b&gt;&lt;/p&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;은행 계좌&lt;/th&gt;
&lt;th&gt;스테이블 코인&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;해외 송금은 은행 계좌가 필수&lt;br /&gt;전 세계적으로 은행 계좌가 없는 사람들이 많아 금융 서비스에서 소외되는 경우가 존재&lt;/td&gt;
&lt;td&gt;인터넷과 스마트폰만 있으면 누구나 암호화폐 지갑을 만들어 스테이블 코인을 주고받을 수 있음&lt;br /&gt;특히 은행 인프라가 취약한 개발도상국에서 큰 장점&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;투명성과 효율성&lt;/b&gt;&lt;/p&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;추적 가능성&lt;/th&gt;
&lt;th&gt;스마트 컨트랙트 활용&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;블록체인 기반의 스테이블 코인 거래는 모든 거래 내역이 투명하게 기록되어 누구나 추적 가능&lt;br /&gt;자금 이동의 투명성을 높이고, 불필요한 서류 작업이나 확인 절차 감소&lt;/td&gt;
&lt;td&gt;스테이블 코인은 스마트 컨트랙트와 결합하여 특정 조건이 충족되면 자동으로 결제가 이루어지도록 프로그래밍 가능&lt;br /&gt;무역 거래 등에서 효율성을 극대화&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, '어떤 환율을 적용할 것인가'보다 '어떻게 빠르고 저렴하게 환전하고 송금할 것인가'에 대한 해답을 제시&lt;br /&gt;은행 송금의 비효율성을 해소함으로써 사용자에게 비용, 시간, 편리성 측면에서 훨씬 뛰어난 경험을 제공하는 것이 스테이블 코인의 핵심적인 장점&lt;/p&gt;
&lt;br /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt; &amp;zwj;♂️ 결제에 필요한 코인 개수는 어떻게 변화되는지?&lt;/h2&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;만약 대한민국 원화(KRW)에 가치가 고정된 스테이블코인을 사용한다면, 1원당 1개의 스테이블코인이 필요&lt;br /&gt;따라서 10,000원짜리 물건을 구매할 때는 10,000개의 원화 스테이블코인이 필요&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;10,000원짜리 물건 구매&lt;/li&gt;
&lt;li&gt;1KRW = 1 스테이블코인&lt;/li&gt;
&lt;li&gt;10,000 스테이블코인 결제&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;p data-ke-size=&quot;size16&quot;&gt;가장 흔한 달러 스테이블코인(USDT, USDC 등)을 사용한다면, 실시간 원/달러 환율에 따라 결제에 필요한 코인 개수가 변화&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;10,000원짜리 물건 구매&lt;/li&gt;
&lt;li&gt;현재 환율: 1 USD = 1,300원&lt;/li&gt;
&lt;li&gt;필요한 달러: 10,000원 / 1,300원/USD = 약 7.69 USD&lt;/li&gt;
&lt;li&gt;7.69개의 달러 스테이블코인(USDT 또는 USDC) 결제&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;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;사용자: 10,000원짜리 물건을 구매하고 결제 수단으로 '스테이블코인'을 선택&lt;/li&gt;
&lt;li&gt;시스템: 결제 시점의 실시간 환율을 조회합니다. (예: 1 USD = 1,300원)&lt;/li&gt;
&lt;li&gt;시스템: 10,000원에 해당하는 스테이블코인 개수(7.69 USDC)를 계산하여 사용자에게 노출&lt;/li&gt;
&lt;li&gt;사용자: 해당 개수의 스테이블코인을 전송하여 결제를 완료&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;</description>
      <category>eTc</category>
      <category>blockchain</category>
      <category>cbdc</category>
      <category>coin</category>
      <category>stablecoin</category>
      <category>usdc</category>
      <category>usdt</category>
      <category>블록채인</category>
      <category>스테이블코인</category>
      <author>Aaron</author>
      <guid isPermaLink="true">https://data-make.tistory.com/806</guid>
      <comments>https://data-make.tistory.com/806#entry806comment</comments>
      <pubDate>Thu, 16 Oct 2025 20:34:04 +0900</pubDate>
    </item>
    <item>
      <title>[AI] Gemini CLI + Atlassian MCP 조합으로 SR 생성하기</title>
      <link>https://data-make.tistory.com/805</link>
      <description>&lt;div class=&quot;markdown-body&quot;&gt;
&lt;h1&gt;Gemini CLI + Atlassian MCP 조합으로 SR 생성하기&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사내 배포 프로세스에서는 별도의 Service Request(SR) 지라 티켓이 필요합니다. 기존에는 작업 티켓의 내용을 수동으로 복사하여 SR 티켓을 생성하는 방식을 사용했는데, 이는 시간 소모적이고 실수가 발생할 수 있는 비효율적인 프로세스였습니다.&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 문제를 해결하기 위해 기존 작업 티켓을 기반으로 SR 티켓을 자동으로 생성하는 방법을 고민하게 되었습니다. 여러 도구들을 서칭한 결과, Atlassian에서 제공하는 &lt;code&gt;MCP&lt;/code&gt;와 &lt;code&gt;Gemini CLI&lt;/code&gt;를 조합하여 SR을 생성하는 방법을 공유하려고 합니다.&lt;/p&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://github.com/sooperset/mcp-atlassian&quot;&gt;mcp-atlassian&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/google-gemini/gemini-cli&quot;&gt;gemini-cli&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Gemini CLI 설정&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Gemini CLI 설정 파일(&lt;code&gt;~/.gemini/settings.json&lt;/code&gt;)에 Atlassian MCP 설정을 추가합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;MCP-Atlassian 실행을 위해 &lt;code&gt;Docker&lt;/code&gt;가 필요합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;{
  ..
  mcpServers: {
      mcp-atlassian: {
        command: docker,
        args: [
          run,
          -i,
          --rm,
          -e, CONFLUENCE_URL,
          -e, CONFLUENCE_PERSONAL_TOKEN,
          -e, CONFLUENCE_SPACES_FILTER,
          -e, JIRA_URL,
          -e, JIRA_PERSONAL_TOKEN,
          -e, JIRA_PROJECTS_FILTER,
          ghcr.io/sooperset/mcp-atlassian:latest
        ],
        env: {
          CONFLUENCE_URL: https://wiki.company.com,
          CONFLUENCE_PERSONAL_TOKEN: &quot;your_confluence_pat&quot;,
          CONFLUENCE_SPACES_FILTER: project,
          JIRA_URL: https://jira.company.com,
          JIRA_PERSONAL_TOKEN: &quot;your_jira_pat&quot;,
          JIRA_PROJECTS_FILTER: PROJECT
        }
      }
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;프로세스 정의&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;~/.gemini/GEMINI.md&lt;/code&gt; 파일에 SR 생성 프로세스를 정의합니다.&lt;br /&gt;특히 사용자 지정 필드를 사용하는 경우, 필드명과 데이터 타입을 명확히 지정하는 것이 중요합니다.&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;pre class=&quot;markdown&quot;&gt;&lt;code&gt; ---

 ## Custom Command: Create SR from Ticket

 아래 프로세스를 &quot;티켓으로 SR 생성&quot;이라는 이름으로 기억해 줘.

 내가 &quot;PROJECT-XXXXX로 SR 생성해 줘&quot;라고 요청하면, 다음 단계를 수행해야 해:

 1.  **데이터 가져오기**: `PROJECT-XXXXX`로 지정된 원본 Jira 티켓의 세부 정보를 가져온다.
 2.  **새 이슈 준비**: 다음 속성을 가진 새 Jira 이슈를 준비한다:
     *   **프로젝트**: `PROJECT`
     *   **이슈 타입**: `SR`
     *   **요약**: 원본 티켓의 요약을 그대로 사용한다.
     *   **설명**: 원본 티켓의 설명을 그대로 사용하되, 모든 '\r\n'을 '\n'으로 바꾼다.
     *   **담당자**: 원본 티켓의 담당자와 동일하게 지정한다.
     *   **마감일**: 현재 날짜로부터 정확히 한 달 뒤로 설정한다.
     *   **사용자 지정 필드 `customfield_12345` (SR 승인자)**: 문자열 `&quot;hello.world&quot;`으로 설정한다.
     *   **사용자 지정 필드 `customfield_54321` (서비스 분류)**: `[&quot;[플랫폼] 프로젝트플랫폼&quot;]` 문자열을 포함하는 배열로 설정한다.
 3.  **이슈 생성**: 이전 단계에서 준비된 데이터로 새 SR 이슈를 생성한다.
 4.  **이슈 연결**: 새 이슈가 생성되면, 새 이슈와 원본 `PROJECT-XXXXX` 티켓 사이에 'Relates' 관계의 링크를 생성한다.&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;사용 방법&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SR 티켓 생성이 필요할 때 다음과 같이 명령어를 실행합니다.&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;&amp;gt; PROJECT-12254 으로 SR 생성해 줘&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;실행 결과 예시&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;pre class=&quot;prolog&quot;&gt;&lt;code&gt;알겠습니다. PROJECT-12254 티켓을 기반으로 SR을 생성하겠습니다. 먼저 원본 티켓의 정보를 가져오겠습니다.

...

네, 원본 티켓 정보를 확인했습니다. 이제 해당 정보로 새로운 SR 이슈를 생성하겠습니다. 마감일은 한 달 뒤인 2025년 9월 25일로 설정됩니다.

...

SR 이슈(PROJECT-12267)가 성공적으로 생성되었습니다. 이제 원본 티켓과 'Relates' 관계로 연결하겠습니다.

...

SR 생성이 완료되었습니다. 새로운 SR 티켓 번호는 PROJECT-12267이며, 원본 티켓 PROJECT-12254와 연결되었습니다.&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;</description>
      <category>AI</category>
      <category>AI</category>
      <category>Atlassian</category>
      <category>CLI</category>
      <category>Gemini</category>
      <category>Gemini-CLI</category>
      <category>JIRA</category>
      <category>MCP</category>
      <category>sr</category>
      <author>Aaron</author>
      <guid isPermaLink="true">https://data-make.tistory.com/805</guid>
      <comments>https://data-make.tistory.com/805#entry805comment</comments>
      <pubDate>Fri, 29 Aug 2025 20:52:38 +0900</pubDate>
    </item>
    <item>
      <title>[AI] genai로 지라 이슈 리포트 생성하기</title>
      <link>https://data-make.tistory.com/804</link>
      <description>&lt;div class=&quot;markdown-body&quot;&gt;
&lt;h1&gt;genai로 지라 이슈 리포트 생성하기&lt;/h1&gt;
&lt;p&gt;최근 AI를 활용한 업무 자동화가 주목받고 있습니다. 특히 일상적인 리포트 작성이나 데이터 분석 작업에서 AI의 도움을 받으면 많은 시간을 절약할 수 있습니다.&lt;br&gt;&lt;br/&gt;&lt;/p&gt;
&lt;p&gt;사내에서 n8n을 활용해서 여러 AI Agent를 만들어 보고 싶었지만, 보안 이슈로 아쉽게 꿈을 펼치지 못 했습니다..ㅎㅎ&lt;br&gt;&lt;br/&gt;&lt;/p&gt;
&lt;p&gt;그래서 Python과 Gemini AI를 활용하여 AI Agent 대체품이라도 만들어 보면서 과정을 기록해 보려고 합니다.&lt;br&gt;&lt;br/&gt;&lt;/p&gt;
&lt;h2&gt;Intro&lt;/h2&gt;
&lt;p&gt;  &lt;strong&gt;Gemini API용 Python SDK는 &lt;code&gt;google-generativeai&lt;/code&gt; 패키지 설치&lt;/strong&gt;&lt;br&gt;&lt;br/&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://pypi.org/project/google-generativeai/&quot;&gt;google-generativeai&lt;/a&gt;&lt;br&gt;&lt;br/&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;pip install -q -U google-generativeai&lt;/code&gt;&lt;/pre&gt;
&lt;br/&gt;

&lt;p&gt;  &lt;strong&gt;프로젝트 루트 디렉토리에 &lt;code&gt;.env&lt;/code&gt; 파일을 만들고 API KEY 저장&lt;/strong&gt; &lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;GEMINI_API_KEY=&amp;quot;...&amp;quot;
JIRA_API_KEY=&amp;quot;...&amp;quot;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Python Code&lt;/h2&gt;
&lt;p&gt;Gemini AI API와 Jira API를 사용하여 최근 Jira 이슈들을 분석하고 리포트를 생성합니다.&lt;br&gt;&lt;br/&gt;&lt;/p&gt;
&lt;p&gt;설명은 보기 쉽게 주석으로 남기게 되었습니다.&lt;br&gt;&lt;br/&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;######################################################
# 필요 라이브러리 임포트
######################################################
import textwrap # 텍스트 포매팅을 위한 라이브러리
import google.generativeai as genai # Gemini AI API 사용을 위한 SDK
from dotenv import load_dotenv # 환경변수 관리를 위한 라이브러리
import os
import requests # HTTP 요청을 위한 라이브러리

######################################################
# 유틸리티 함수
######################################################
# 사용 가능한 Gemini AI 모델 목록을 반환하는 함수
def get_genai_models():
  result = []
  for m in genai.list_models():
    if &amp;#39;generateContent&amp;#39; in m.supported_generation_methods:
      result.append(m.name)
  return result

# Gemini AI API를 초기화하고 모델 인스턴스를 반환하는 함수
def init_genai():
  global gemini_api_key
  genai.configure(api_key=gemini_api_key)
  print(&amp;#39;\n&amp;#39;.join(get_genai_models())) # 사용 가능한 모델 확인
  return genai.GenerativeModel(&amp;#39;gemini-2.5-pro&amp;#39;)

# 최근 7일간 업데이트된 Jira 이슈들을 가져오는 함수
# - JQL을 사용하여 이슈 검색
# - 각 이슈의 key, summary, description 정보를 수집
# - 최대 100개의 이슈를 조회
def get_jira_issues():
  global jira_api_key
  jql = &amp;quot;project=SETTLEMENT+and+type!=SR+and+updatedDate&amp;gt;=-7d+order+by+updatedDate+desc&amp;quot;
  fields = &amp;quot;key,summary,updated,description&amp;quot;
  url = f&amp;quot;https://jira.company.com/rest/api/latest/search?jql={jql}&amp;amp;fields={fields}&amp;amp;maxResults=100&amp;quot;
  headers = {
    &amp;#39;Authorization&amp;#39;: f&amp;#39;Bearer {jira_api_key}&amp;#39;,
  }

  jira_issues_response = requests.request(&amp;quot;GET&amp;quot;, url, headers=headers, data={})
  data = jira_issues_response.json()

  result = []
  for issue in data[&amp;#39;issues&amp;#39;]:
    result.append({
      &amp;#39;key&amp;#39;: issue[&amp;#39;key&amp;#39;],
      &amp;#39;summary&amp;#39;: issue[&amp;#39;fields&amp;#39;][&amp;#39;summary&amp;#39;],
      &amp;#39;description&amp;#39;: issue[&amp;#39;fields&amp;#39;].get(&amp;#39;description&amp;#39;, &amp;#39;No description provided&amp;#39;)
    })
  return result

######################################################
# 메인 실행 로직
######################################################

load_dotenv() # .env 파일의 환경변수를 로드
gemini_api_key = os.getenv(&amp;#39;GEMINI_API_KEY&amp;#39;)
jira_api_key = os.getenv(&amp;#39;JIRA_API_KEY&amp;#39;)

if __name__ == &amp;#39;__main__&amp;#39;:
  model = init_genai()
  jira_issues = get_jira_issues()
  question = f&amp;#39;&amp;#39;&amp;#39;
    최근 업데이트된 지라 이슈 목록을 바탕으로 리포트를 작성해줘.
    목록에 제공된 정보에 대한 설명을 해주자면, key는 이슈 번호, summary는 이슈 제목, description은 이슈 설명이야.

    지라 이슈 목록:
    {jira_issues}

    이 내용들을 바탕으로
    1) 핵심 주제를 분류해서 요약해줘,
    2) 가장 많이 언급된 버그 유형을 분석해줘,
    3) 요청사항의 주요 패턴을 알려줘.
    &amp;#39;&amp;#39;&amp;#39;

  response = model.generate_content(question)
  print(response.text)&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Execution Results&lt;/h2&gt;
&lt;p&gt;  &lt;strong&gt;실행&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;python3 weekly-report.py&lt;/code&gt;&lt;/pre&gt;
&lt;br/&gt;

&lt;p&gt;아래 결과는 실제 실행 결과입니다.&lt;br&gt;&lt;br/&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;최근 지라 이슈 기반 업무 현황 리포트&lt;/h3&gt;
&lt;p&gt;제공해주신 최근 업데이트된 지라 이슈 목록을 바탕으로 업무 현황을 분석하고 요약한 리포트입니다.&lt;/p&gt;
&lt;h3&gt;1. 핵심 주제별 업무 분류 및 요약&lt;/h3&gt;
&lt;p&gt;최근 진행된 이슈들은 크게 &lt;strong&gt;5가지 주제&lt;/strong&gt;로 분류할 수 있습니다.&lt;/p&gt;
&lt;h4&gt;가. 신규 기능 개발 및 시스템 고도화&lt;/h4&gt;
&lt;p&gt;새로운 비즈니스 요구사항을 반영하고 시스템의 근본적인 성능과 안정성을 개선하는 작업이 활발히 진행 중입니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;주요 내용&lt;/strong&gt;:&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;...&lt;/p&gt;
&lt;h4&gt;나. 외부 시스템 연동 및 정책 변경 대응&lt;/h4&gt;
&lt;p&gt;외부 서비스 연동, 제휴사 정책 변경, 그리고 정부 정책에 대응하기 위한 개발이 다수 진행되었습니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;주요 내용&lt;/strong&gt;:&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;...&lt;/p&gt;
&lt;h4&gt;다. 장애 대응 및 안정성 강화&lt;/h4&gt;
&lt;p&gt;정산 시스템의 안정적인 운영을 저해하는 장애에 신속하게 대응하고, 재발 방지를 위한 개선 작업을 진행했습니다.&lt;/p&gt;
&lt;p&gt;...&lt;/p&gt;
&lt;h4&gt;라. 데이터 보정 및 재처리&lt;/h4&gt;
&lt;p&gt;과거 데이터의 오류를 바로잡거나 누락된 데이터를 반영하기 위한 데이터 보정 및 배치 재수행 요청이 꾸준히 발생하고 있습니다.&lt;/p&gt;
&lt;p&gt;...&lt;/p&gt;
&lt;h4&gt;마. 운영 효율화 및 사용자 편의성 개선&lt;/h4&gt;
&lt;p&gt;운영 업무의 자동화 및 어드민 기능 개선을 통해 수기 작업을 줄이고 업무 효율성을 높이는 작업이 진행 중입니다.&lt;/p&gt;
&lt;p&gt;...&lt;/p&gt;
&lt;hr&gt;
&lt;h4&gt;2. 가장 많이 언급된 버그/장애 유형 분석&lt;/h4&gt;
&lt;p&gt;지라 이슈 목록에서 &amp;#39;장애&amp;#39;로 명시된 티켓과 그 원인을 분석한 결과, 가장 빈번하게 발생한 장애 유형은 다음과 같습니다.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;데이터 및 설정 누락 (Configuration &amp;amp; Data Integrity Issues)&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;...&lt;/p&gt;
&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;&lt;strong&gt;외부 시스템 연동 실패 (External Dependency Failures)&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;...&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;3. 요청사항의 주요 패턴 분석&lt;/h3&gt;
&lt;p&gt;이슈들의 &amp;#39;요청사항&amp;#39; 섹션을 분석한 결과, 공통적으로 나타나는 주요 패턴은 다음과 같습니다.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;필드 추가 및 데이터 스키마 변경&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;...&lt;/p&gt;
&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;&lt;strong&gt;데이터 재처리 및 수동 보정&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;...&lt;/p&gt;
&lt;ol start=&quot;3&quot;&gt;
&lt;li&gt;&lt;strong&gt;기존 로직 및 기준 변경&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;...&lt;/p&gt;
&lt;ol start=&quot;4&quot;&gt;
&lt;li&gt;&lt;strong&gt;신규 프로세스 및 기능 도입&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;...&lt;/p&gt;
&lt;h2&gt;Finish&lt;/h2&gt;
&lt;p&gt;실행 결과는 슬랙 알림이나 메일로도 전송시킬 수가 있습니다.&lt;br&gt;&lt;br/&gt;&lt;/p&gt;
&lt;p&gt;이렇게 AI를 다양한 업무에 적용하여 생산성을 향상시킬 수 있을 것이라고 기대합니다.&lt;br&gt;&lt;br/&gt;&lt;/p&gt;
&lt;p&gt;AI를 활용한 다양한 업무 자동화를 시도하며, 그 경험을 공유해 보려고 합니다. &lt;/p&gt;
&lt;h2&gt;Reference&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://ai.google.dev/gemini-api/docs/get-started/tutorial?hl=ko&amp;amp;lang=python&quot;&gt;Gemini API 시작하기&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;</description>
      <category>AI</category>
      <category>AI</category>
      <category>confluence</category>
      <category>Gemini</category>
      <category>GenAI</category>
      <category>JIRA</category>
      <category>LLM</category>
      <category>PYTHON</category>
      <author>Aaron</author>
      <guid isPermaLink="true">https://data-make.tistory.com/804</guid>
      <comments>https://data-make.tistory.com/804#entry804comment</comments>
      <pubDate>Wed, 27 Aug 2025 23:22:01 +0900</pubDate>
    </item>
    <item>
      <title>[Kafka Streams] 정산 데이터, 이제 스트리밍으로 즐기세요! (feat. Kafka streams) 2편</title>
      <link>https://data-make.tistory.com/803</link>
      <description>&lt;div class=&quot;markdown-body&quot;&gt;
&lt;p&gt;&lt;a href=&quot;https://data-make.tistory.com/802&quot;&gt;정산 데이터, 이제 스트리밍으로 즐기세요! (feat. Kafka streams) 1편&lt;/a&gt;에 이어 2편을 진행하겠습니다.&lt;br&gt;&lt;br/&gt;&lt;/p&gt;
&lt;h3&gt;5단계. 지급룰 조회 및 세팅&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://github.com/jihunparkme/blog/blob/main/img/kafka-streams/example-processValues.png?raw=true&quot; alt=&quot;지급룰 조회 및 세팅&quot;&gt;&lt;/p&gt;
&lt;p&gt;이제 필터링된 정산 대상 데이터에 지급룰 정보를 설정할 차례입니다. 지급룰은 API 호출을 통해 조회하는데, 동일한 규칙을 사용하는 데이터에 대해 중복 API 호출을 방지하고 네트워크 통신 비용을 절감하기 위해 지급룰을 별도로 관리하고자 합니다.&lt;br&gt;&lt;br/&gt;&lt;/p&gt;
&lt;p&gt;이러한 요구사항을 해결하기 위해 &lt;code&gt;Redis&lt;/code&gt;를 사용할 수도 있지만, 여기서는 Kafka Streams의 &lt;code&gt;상태 저장소&lt;/code&gt;를 활용해 보겠습니다. 상태 저장소는 &lt;code&gt;RocksDB&lt;/code&gt;와 같은 로컬 저장소를 사용하여 &lt;code&gt;KTable&lt;/code&gt; 형태로 키-값 데이터를 관리하며, &lt;code&gt;변경 로그 토픽&lt;/code&gt;을 통해 상태를 복원하여 내결함성을 보장합니다. 이렇게 구성된 상태 저장소와 연동하여 레코드를 개별적으로 처리하기 위해&lt;br&gt;&lt;a href=&quot;https://docs.confluent.io/platform/7.9/streams/javadocs/javadoc/org/apache/kafka/streams/kstream/KStream.html#processValues-org.apache.kafka.streams.processor.api.FixedKeyProcessorSupplier-java.lang.String...-&quot;&gt;processValues()&lt;/a&gt; 메서드를 사용합니다.&lt;br&gt;&lt;br/&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;processValues()&lt;/code&gt; 메서드는 스트림의 각 레코드에 대해 키는 그대로 유지하면서 값만을 대상으로 사용자 정의 로직을 실행할 때 유용합니다. 이 사용자 정의 로직은 &lt;code&gt;FixedKeyProcessorSupplier&lt;/code&gt; 인터페이스를 구현한 객체를 processValues() 메서드의 인자로 전달하여 정의합니다&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-kotlin&quot;&gt;// SettlementKafkaStreamsApp.kt
@Bean
fun settlementStreams(): KafkaStreams {
    // ...
    builder.globalTable( // 토폴로지에 GlobalKTable 정의
        kafkaProperties.paymentRulesGlobalTopic, //  GlobalKTable이 데이터를 읽어올 토픽 이름
        // 상태 저장소 설정
        Materialized.`as`&amp;lt;String, Rule, KeyValueStore&amp;lt;Bytes, ByteArray&amp;gt;&amp;gt;( // GlobalKTable이 String 키와 Rule 값을 가지며, 내부적으로 KeyValueStore 타입의 상태 저장소를 사용할 것임을 명시
            kafkaProperties.globalPayoutRuleStateStoreName // 내부 상태 저장소에 부여하는 고유한 이름
        )
            .withKeySerde(Serdes.String()) // GlobalKTable의 소스 토픽에서 레코드를 읽을 때 키를 역직렬화하고, 내부 상태 저장소에 키를 직렬화/역직렬화할 때 사용
            .withValueSerde(serdeFactory.ruleSerde()) // GlobalKTable의 소스 토픽에서 레코드를 읽을 때 값을 역직렬화하고, 내부 상태 저장소에 값을 직렬화/역직렬화할 때 사용
    )

    // ...
    paymentStream
        .processValues(
            // 사용자 정의 상태 기반 값 처리 로직을 적용
            PayoutRuleProcessValues(
                rulesGlobalTopic = kafkaProperties.paymentRulesGlobalTopic,
                stateStoreName = kafkaProperties.globalPayoutRuleStateStoreName,
                payoutRuleClient = payoutRuleClient,
                ruleKafkaTemplate = ruleKafkaTemplate,
            ),
        )
    // ...
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;FixedKeyProcessorSupplier&lt;/code&gt; 인터페이스 구현체&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-kotlin&quot;&gt;class PayoutRuleProcessValues(
  private val rulesGlobalTopic: String, // GlobalKTable의 소스 토픽 이름
  private val stateStoreName: String, // GlobalKTable의 로컬 상태 저장소 이름
  private val payoutRuleClient: PayoutRuleClient, // 외부 API 호출을 위한 클라이언트
  private val ruleKafkaTemplate: KafkaTemplate&amp;lt;String, Rule&amp;gt;, // 지급룰 정보를 토픽으로 보내기 위한 카프카 템플릿
) : FixedKeyProcessorSupplier&amp;lt;String, Base, Base&amp;gt; {
  override fun get(): FixedKeyProcessor&amp;lt;String, Base, Base&amp;gt; {
    return PayoutRuleProcessor(rulesGlobalTopic, stateStoreName, payoutRuleClient, ruleKafkaTemplate)
  }
}

class PayoutRuleProcessor(
  private val rulesGlobalTopic: String,
  private val stateStoreName: String,
  private val payoutRuleClient: PayoutRuleClient,
  private val ruleKafkaTemplate: KafkaTemplate&amp;lt;String, Rule&amp;gt;,
) : FixedKeyProcessor&amp;lt;String, Base, Base&amp;gt; {
  private var context: FixedKeyProcessorContext&amp;lt;String, Base&amp;gt;? = null
  private var payoutRuleStore: ReadOnlyKeyValueStore&amp;lt;String, ValueAndTimestamp&amp;lt;Rule&amp;gt;&amp;gt;? = null

  // 초기화 메서드
  override fun init(context: FixedKeyProcessorContext&amp;lt;String, Base&amp;gt;) {
    this.context = context
    this.payoutRuleStore = this.context?.getStateStore(stateStoreName) // 상태 저장소 이름을 통해 GlobalKTable의 로컬 복제본에 접근
  }

  // 레코드 처리 메서드
  override fun process(record: FixedKeyRecord&amp;lt;String, Base&amp;gt;) {
    val key = record.key()
    val base = record.value()

    // 결제 데이터가 없을 경우 스킵
    if (base == null) {
      log.info(&amp;quot;&amp;gt;&amp;gt;&amp;gt; [결제 데이터 누락] Payment data is null, skipping processing for key: $key&amp;quot;)
      return
    }

    // 지급룰 조회를 위한 키 생성 (가맹점번호/결제일/결제액션타입/결제수단타입)
    val ruleKey = &amp;quot;${base.merchantNumber}/${base.paymentDate.toLocalDate()}/${base.paymentActionType}/${base.paymentMethodType}&amp;quot;
    // 상태 저장소(GlobalKTable)에서 ruleKey로 지급룰 조회
    val valueAndTimestamp = payoutRuleStore?.get(ruleKey)
    var rule = valueAndTimestamp?.value()
    // 상태 저장소에 지급룰이 없을 경우
    if (rule == null) {
      // 외부 API를 통해 지급룰 조회
      val findRule = payoutRuleClient.getPayoutDate(
        PayoutDateRequest(
          merchantNumber = base.merchantNumber ?: throw IllegalArgumentException(),
          paymentDate = base.paymentDate,
          paymentActionType = base.paymentActionType ?: throw IllegalArgumentException(),
          paymentMethodType = base.paymentMethodType ?: throw IllegalArgumentException(),
        )
      )
      // 조회된 지급룰을 GlobalKTable의 소스 토픽으로 전송하여 GlobalKTable이 업데이트되고, 다른 인스턴스에서도 이 지급룰을 사용
      ruleKafkaTemplate.send(rulesGlobalTopic, ruleKey, findRule)
      rule = findRule
    }

    // 가맹점에 대한 지급룰이 없을 경우
    if (rule == null) {
      log.info(&amp;quot;&amp;gt;&amp;gt;&amp;gt; [지급룰 없음] Not found payment payout rule. key: $ruleKey&amp;quot;)
      base.updateDefaultPayoutDate()
    }

    // 지급룰 업데이트 대상일 경우
    if (rule != null &amp;amp;&amp;amp; (rule.payoutDate != base.payoutDate || rule.confirmDate != base.confirmDate)) {
      log.info(&amp;quot;&amp;gt;&amp;gt;&amp;gt; [지급룰 업데이트] Update payout date.. $ruleKey&amp;quot;)
      base.updatePayoutDate(rule)
    }

    // 처리된 Base 객체를 다음 스트림 단계로 전달
    context?.forward(record.withValue(base))
  }

  companion object {
    private val log by logger()
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;상태 저장소에 저장된 데이터는 &lt;code&gt;변경 로그 토픽&lt;/code&gt;을 통해 복원됩니다. &lt;code&gt;GlobalKTable&lt;/code&gt;의 경우, 정의 시 지정했던 소스 토픽 자체가 변경 로그 토픽 역할을 합니다. 해당 토픽에서 지급룰 정보의 변경 내역을 확인할 수 있습니다&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;[key]
merchant-4436/2025-05-26/PAYMENT/MONEY

[value]
{
   &amp;quot;ruleId&amp;quot;: &amp;quot;16858a4e-d08c-4ba5-ae44-54ff4d4b219b&amp;quot;,
   &amp;quot;payoutDate&amp;quot;: &amp;quot;2025-06-10&amp;quot;,
   &amp;quot;confirmDate&amp;quot;: &amp;quot;2025-06-09&amp;quot;,
   &amp;quot;merchantNumber&amp;quot;: &amp;quot;merchant-4436&amp;quot;,
   &amp;quot;paymentDate&amp;quot;: &amp;quot;2025-05-26T00:00:00&amp;quot;,
   &amp;quot;paymentActionType&amp;quot;: &amp;quot;PAYMENT&amp;quot;,
   &amp;quot;paymentMethodType&amp;quot;: &amp;quot;MONEY&amp;quot;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt; &lt;strong&gt;상태 저장소를 사용하는 방법&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Kafka Streams에서 상태를 관리하는 주요 방법으로 &lt;code&gt;KTable&lt;/code&gt;과 &lt;code&gt;GlobalKTable&lt;/code&gt;이 있습니다. 두 방식의 주요 차이점을 살펴보겠습니다.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;KTable&lt;/th&gt;
&lt;th&gt;GlobalKTable&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&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;tr&gt;
&lt;td&gt;변경 로그&lt;/td&gt;
&lt;td&gt;소스 토픽의 파티션과 1:1로 매핑되는 별도의 내부 변경 로그 토픽을 생성하고 사용&lt;/td&gt;
&lt;td&gt;소스 토픽 자체가 변경 로그 역할을 하며, 모든 인스턴스가 이 토픽을 구독하여 로컬 상태를 최신으로 유지&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;br/&gt;

&lt;p&gt;이 글에서는 지급룰을 상태 저장소에 저장하기 위해 &lt;code&gt;GlobalKTable&lt;/code&gt;을 사용했습니다. &lt;code&gt;GlobalKTable&lt;/code&gt;은 &lt;code&gt;KTable&lt;/code&gt;과 달리 모든 인스턴스가 동일한 소스 토픽을 변경 로그로 사용합니다. 따라서 각 인스턴스는 소스 토픽에서 데이터를 읽어와 자신의 로컬 상태 저장소를 업데이트하고, 이를 통해 모든 인스턴스가 전체 데이터셋의 완전한 복제본을 로컬에 유지하게 됩니다. 그 결과, 파티션에 관계없이 로컬에서 신속한 조회가 가능해집니다.&lt;br&gt;&lt;br/&gt;&lt;/p&gt;
&lt;p&gt;반면, &lt;code&gt;KTable&lt;/code&gt;은 각 인스턴스가 자신이 담당하는 파티션의 변경 로그만을 소비하여 로컬 상태를 관리합니다. 이로 인해 데이터가 파티션별로 분산 저장되어, 특정 키를 조회할 때 해당 키가 어떤 파티션에 있는지 확인해야 하는 번거로움이 있을 수 있습니다. &lt;code&gt;Interactive Queries&lt;/code&gt;를 사용하면 특정 키를 담당하는 인스턴스의 정보를 얻어 HTTP 요청을 통해 데이터를 가져올 수 있지만, 지급룰 조회와 같이 빈번한 읽기 작업에는 불필요한 네트워크 통신이 발생할 수 있습니다. 이러한 이유로 지급룰 데이터를 캐시처럼 활용하기 위해 &lt;code&gt;GlobalKTable&lt;/code&gt;을 선택했습니다.&lt;/p&gt;
&lt;h3&gt;6단계. 정산 베이스 저장&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://github.com/jihunparkme/blog/blob/main/img/kafka-streams/example-peek-2.png?raw=true&quot; alt=&quot;정산 베이스 저장&quot;&gt;&lt;/p&gt;
&lt;p&gt;&amp;#39;2단계. 결제 메시지 저장&amp;#39; 단계에서 사용되었던 &lt;a href=&quot;https://docs.confluent.io/platform/7.9/streams/javadocs/javadoc/org/apache/kafka/streams/kstream/KStream.html#peek-org.apache.kafka.streams.kstream.ForeachAction-&quot;&gt;peek()&lt;/a&gt; 메서드를 활용해서 정산 베이스를 데이터베이스에 저장합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-kotlin&quot;&gt;// SettlementKafkaStreamsApp.kt
@Bean
fun settlementStreams(): KafkaStreams {
    // ...
    paymentStream
        .peek({ _, message -&amp;gt; settlementService.saveBase(message) })
    // ...
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;7단계. 집계&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://github.com/jihunparkme/blog/blob/main/img/kafka-streams/example-aggregate.png?raw=true&quot; alt=&quot;집계&quot;&gt;&lt;/p&gt;
&lt;p&gt;정산 베이스 데이터에 대한 통계를 생성하기 위해 스트림 레코드를 집계합니다. 먼저, &lt;a href=&quot;https://docs.confluent.io/platform/7.9/streams/javadocs/javadoc/org/apache/kafka/streams/kstream/KTable.html#groupBy-org.apache.kafka.streams.kstream.KeyValueMapper-&quot;&gt;groupBy()&lt;/a&gt; 메서드를 사용하여 집계 기준이 될 키와 값을 스트림 레코드에서 추출하여 &lt;code&gt;KGroupedStream&lt;/code&gt;을 생성합니다. 그런 다음, 이 &lt;code&gt;KGroupedStream&lt;/code&gt;에 &lt;a href=&quot;https://docs.confluent.io/platform/7.9/streams/javadocs/javadoc/org/apache/kafka/streams/kstream/KGroupedStream.html#aggregate-org.apache.kafka.streams.kstream.Initializer-org.apache.kafka.streams.kstream.Aggregator-org.apache.kafka.streams.kstream.Materialized-&quot;&gt;aggregate()&lt;/a&gt; 메서드를 적용하여 그룹화된 키를 기준으로 레코드 값을 집계합니다. 집계 결과는 &lt;code&gt;KTable&lt;/code&gt; 형태로 저장되며, 이는 &amp;#39;5단계. 지급룰 조회 및 세팅&amp;#39;에서 언급된 것처럼 &lt;code&gt;Interactive Queries&lt;/code&gt;를 통해 조회할 수 있습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-kotlin&quot;&gt;// SettlementKafkaStreamsApp.kt
@Bean
fun settlementStreams(): KafkaStreams {
    // ...
    val aggregatedTable: KTable&amp;lt;BaseAggregationKey, BaseAggregateValue&amp;gt; = baseStream.groupBy( // baseStream을 그룹화하고 집계하여 KTable을 생성
        { _, base -&amp;gt; base.toAggregationKey() }, // 각 Base 객체에서 집계에 사용할 키 추출
        Grouped.with( // 그룹화 연산 시 사용할 키와 값의 직렬화/역직렬화기(Serde) 설정
            serdeFactory.baseAggregationKeySerde(), // 그룹화 키를 위한 Serde
            serdeFactory.baseSerde() // 그룹화될 값을 위한 Serde
        )
    )
        .aggregate( // 그룹화된 스트림(KGroupedStream)에 대해 집계 연산을 수행
            { BaseAggregateValue() }, // 새로운 그룹이 생성될 때 사용할 초기 집계값 정의
            { _aggKey, newBaseValue, currentAggregate -&amp;gt; // (그룹 키, 새로운 값, 현재 집계값) -&amp;gt; 새로운 집계값
                currentAggregate.updateWith(newBaseValue.amount) // 각 그룹에 새로운 레코드가 도착할 때마다 현재 집계값을 업데이트
            },
            // 집계 결과를 상태 저장소에 저장하기 위한 설정 정의
            Materialized.`as`&amp;lt;BaseAggregationKey, BaseAggregateValue, KeyValueStore&amp;lt;Bytes, ByteArray&amp;gt;&amp;gt;(
                kafkaProperties.statisticsStoreName // 상태 저장소 이름 정의
            )
                .withKeySerde(serdeFactory.baseAggregationKeySerde()) // 상태 저장소 및 집계 결과 KTable의 키를 위한 Serde
                .withValueSerde(serdeFactory.baseAggregateValueSerde()) // 상태 저장소 및 집계 결과 KTable의 값을 위한 Serde
        )
    // ...
}

// Base.kt (집계에 사용할 키 정의)
fun toAggregationKey() = 
  BaseAggregationKey(
    merchantNumber = this.merchantNumber,
    paymentDateDaily = this.paymentDate.toLocalDate(),
    paymentActionType = this.paymentActionType,
    paymentMethodType = this.paymentMethodType
  )

// BaseAggregateValue.kt (초기 집계값 및 집계 계산식 정의)
data class BaseAggregateValue(
  val totalAmount: Long = 0L,
  val count: Long = 0L
) {
  fun updateWith(amount: Long): BaseAggregateValue {
    return this.copy(
      totalAmount = this.totalAmount + amount,
      count = this.count + 1
    )
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;8단계. 집계 결과 전송&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://github.com/jihunparkme/blog/blob/main/img/kafka-streams/example-to.png?raw=true&quot; alt=&quot;집계 결과 전송&quot;&gt;&lt;/p&gt;
&lt;p&gt;집계 결과인 &lt;code&gt;KTable&lt;/code&gt;은 &lt;a href=&quot;https://docs.confluent.io/platform/7.9/streams/javadocs/javadoc/org/apache/kafka/streams/kstream/KTable.html#toStream--&quot;&gt;toStream()&lt;/a&gt; 메서드를 통해 &lt;code&gt;KStream&lt;/code&gt;으로 변환한 후, 싱크 프로세서인 &lt;a href=&quot;https://docs.confluent.io/platform/7.9/streams/javadocs/javadoc/org/apache/kafka/streams/kstream/KStream.html#to-java.lang.String-org.apache.kafka.streams.kstream.Produced-&quot;&gt;to()&lt;/a&gt; 메서드를 사용하여 다른 Kafka 토픽으로 전송할 수 있습니다. 물론 &lt;code&gt;Interactive Queries&lt;/code&gt;를 활용하여 집계 결과를 직접 조회하는 것으로 마무리할 수도 있지만, 결과를 토픽으로 전송하면 데이터 변경 이력을 남기거나 집계 결과를 검증하는 데 유용하게 활용할 수 있습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-kotlin&quot;&gt;@Bean
fun settlementStreams(): KafkaStreams {
    // ...
    aggregatedTable.toStream() // 집계 결과인 KTable을 KStream으로 변환
        .to( // KStream의 데이터를 특정 토픽으로 전송
            kafkaProperties.paymentStatisticsTopic, // 데이터를 전송할 대상 토픽 이름
            Produced.with(
                serdeFactory.baseAggregationKeySerde(),
                serdeFactory.baseAggregateValueSerde()
            )
        )
    // ...
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;kafka streams 인스턴스 생성&lt;/h2&gt;
&lt;p&gt;KafkaStreams 인스턴스는 &lt;a href=&quot;https://docs.confluent.io/platform/7.9/streams/javadocs/javadoc/org/apache/kafka/streams/KafkaStreams.html#start--&quot;&gt;start()&lt;/a&gt; 메서드를 호출하여 시작할 수 있습니다. 이 글에서는 KafkaStreams를 Spring Bean으로 등록하고, 별도의 CommandLineRunner를 통해 애플리케이션 시작 시 자동으로 실행되도록 구현했습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-kotlin&quot;&gt;// SettlementKafkaStreamsApp.kt
@Bean
fun settlementStreams(): KafkaStreams {
    // ...
    return KafkaStreams(builder.build(), streamsConfig)
}

// KafkaStreamsRunner.kt
@Component
class KafkaStreamsRunner(
  private val settlementKafkaStreamsApp: SettlementKafkaStreamsApp,
) : CommandLineRunner {

    private lateinit var settlementStreams: KafkaStreams

    override fun run(vararg args: String?) {
        // ...
        settlementStreams.start()
        return
    }
    // ...
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;마치며&lt;/h2&gt;
&lt;p&gt;이 글을 통해 Kafka Streams를 활용하여 기존의 비실시간 정산 데이터 처리 과정을 실시간 스트리밍 방식으로 전환하는 여정을 함께 살펴보았습니다. Kafka를 이미 사용하고 계시다면, 별도의 복잡한 클러스터 구축 없이도 강력한 스트림 처리 기능을 애플리케이션에 통합할 수 있다는 점이 Kafka Streams의 큰 매력입니다. 단순한 데이터 필터링부터 상태 기반의 복잡한 연산, 그리고 데이터 집계에 이르기까지, Kafka Streams는 다양한 기능을 직관적인 DSL로 제공하여 개발 과정을 한결 수월하게 만들어 줍니다. 특히, GlobalKTable과 같은 상태 저장소 활용은 외부 시스템 의존도를 낮추고 실시간 조회 성능을 극대화하는 데 큰 도움이 되었습니다. 만약 여러분의 시스템에서도 대용량 데이터를 실시간으로 처리해야 하는 도전 과제가 있다면, 혹은 기존 Kafka 인프라를 더욱 효과적으로 활용하고 싶다면, Kafka Streams 도입을 적극적으로 고려해 보시길 권합니다.&lt;br&gt;&lt;br/&gt;&lt;/p&gt;
&lt;p&gt;이 글에서 다룬 예제가 여러분의 스트리밍 여정에 작은 영감이 되기를 바랍니다. 지금 바로 Kafka Streams의 강력함을 경험해 보세요!&lt;/p&gt;
&lt;h2&gt;참고&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.confluent.io/platform/current/streams/developer-guide/dsl-api.html&quot;&gt;Kafka Streams Domain Specific Language for Confluent Platform&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.confluent.io/platform/current/streams/architecture.html&quot;&gt;Kafka Streams Architecture for Confluent Platform&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.confluent.io/platform/current/streams/developer-guide/interactive-queries.html&quot;&gt;Kafka Streams Interactive Queries for Confluent Platform&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div/&gt;</description>
      <category>Web/Infra</category>
      <category>kafka</category>
      <category>Kafka Streams</category>
      <category>Kotlin</category>
      <category>Processor</category>
      <category>Streaming</category>
      <category>Streams</category>
      <category>스트림</category>
      <category>스트림즈</category>
      <category>카프카</category>
      <author>Aaron</author>
      <guid isPermaLink="true">https://data-make.tistory.com/803</guid>
      <comments>https://data-make.tistory.com/803#entry803comment</comments>
      <pubDate>Thu, 7 Aug 2025 23:14:32 +0900</pubDate>
    </item>
    <item>
      <title>[Kafka Streams] 정산 데이터, 이제 스트리밍으로 즐기세요! (feat. Kafka streams) 1편</title>
      <link>https://data-make.tistory.com/802</link>
      <description>&lt;div class=&quot;markdown-body&quot;&gt;
&lt;h1&gt;정산 데이터, 이제 스트리밍으로 즐기세요! (feat. Kafka streams)&lt;/h1&gt;
&lt;p&gt;정산 데이터는 실시간으로 처리되지 않고, 매일 새벽 채널별 데이터 양에 따라 비실시간으로 생성되어 처리되고 있는데, 최근 파이프라인 방식으로 데이터를 처리하기 위해 Kafka Streams를 적용하면서, 기존에 비실시간으로 생성되던 정산 데이터를 실시간 스트림으로 생성하는 방안을 고민하게 되었습니다.&lt;br&gt;&lt;br/&gt;&lt;/p&gt;
&lt;p&gt;이 글에서는 기존 Kafka 인프라에서 별도의 클러스터 없이 스트림 처리 애플리케이션을 구축할 수 있는 Kafka Streams의 장점을 소개하고, 정산 데이터 생성 과정에 적용하는 과정을 공유하고자 합니다.&lt;/p&gt;
&lt;h1&gt;Kafka Streams?&lt;/h1&gt;
&lt;p&gt;kafka streams는 kafka 위에서 동작하는 클라이언트 라이브러리로, 실시간 데이터를 스트림 형태로 처리하고 연속적으로 필터링(filtering), 변환(transform), 결합(joining), 집계(aggregating) 등의 작업을 연속적으로 수행할 수 있도록 지원합니다. 특히 로컬 상태 저장소(RocksDB)를 통해 복잡한 데이터의 상태를 효율적으로 관리할 수 있으며, 분산 처리 및 고가용성을 내장하여 복잡한 스트림 처리 애플리케이션을 간편하게 구축할 수 있습니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;분산 처리: 여러 인스턴스가 작업을 분담하여 병렬로 처리함으로써 성능과 처리량을 향상시킵니다.&lt;/li&gt;
&lt;li&gt;고가용성: 일부 인스턴스에 장애가 발생하더라도 다른 인스턴스가 작업을 이어받아 서비스 중단을 최소화합니다.&lt;br/&gt;

&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;이제 kafka streams 적용에 앞서 가장 기본적이면서 핵심적인 개념인 &lt;code&gt;토폴로지&lt;/code&gt;를 간략하게 살펴보겠습니다.&lt;br&gt;&lt;br/&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;kafka streams의 토폴로지는 &lt;strong&gt;데이터 처리 흐름과 변환 과정을 정의하는 구조&lt;/strong&gt;입니다.&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;br/&gt;

&lt;p&gt;토폴로지는 다음과 같은 주요 구성 요소로 이루어져 있습니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;프로세서(Processor)&lt;/strong&gt;: 토폴로지를 구성하는 노드로서, &lt;strong&gt;데이터를 처리하는 역할&lt;/strong&gt;을 담당합니다.&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;소스 프로세서&lt;/strong&gt;: kafka 토픽에서 데이터를 읽어와 처리의 &lt;strong&gt;시작점&lt;/strong&gt;이 되는 노드입니다.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;스트림 프로세서&lt;/strong&gt;: 이전 프로세서로부터 전달받은 데이터를 필터링, 변환, 결합, 집계하는 등 실제 &lt;strong&gt;데이터 처리 로직을 수행&lt;/strong&gt;하는 노드입니다.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;싱크 프로세서&lt;/strong&gt;: 처리된 데이터를 특정 kafka 토픽에 저장하는 &lt;strong&gt;최종 노드&lt;/strong&gt;입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;스트림(Stream)&lt;/strong&gt;: 프로세서 노드들을 연결하는 선으로, 프로듀서/컨슈머의 레코드와 동일한 &lt;strong&gt;토픽의 데이터를 의미&lt;/strong&gt;합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://github.com/jihunparkme/blog/blob/main/img/kafka-streams/topology-2.png?raw=true&quot; alt=&quot;프로세서와 스트림&quot;&gt;&lt;/p&gt;
&lt;h2&gt;정산 데이터 생성 단계&lt;/h2&gt;
&lt;p&gt;실제 정산 데이터 생성 과정은 복잡하지만, 이해를 돕기 위해 kafka streams 적용 단계를 다음과 같이 간략하게 정리했습니다.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;결제 데이터 수신: kafka를 통해 결제 데이터를 받아옵니다.&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;/ol&gt;
&lt;h1&gt;Kafka Streams 적용&lt;/h1&gt;
&lt;p&gt;일반적으로 kafka streams 애플리케이션은 다음 단계를 따라 개발합니다.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;StreamsConfig&lt;/code&gt; 인스턴스 생성&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Serde&lt;/code&gt; 객체 생성&lt;/li&gt;
&lt;li&gt;처리 토폴로지 구성&lt;/li&gt;
&lt;li&gt;kafka streams 프로그램 시작&lt;br/&gt;

&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;kafka streams를 적용하는 방법으로는 &lt;code&gt;Streams DSL&lt;/code&gt;과 &lt;code&gt;Processor API&lt;/code&gt; 두 가지가 있으며, 본 글에서는 &lt;code&gt;Streams DSL&lt;/code&gt;(Domain Specific Language)을 사용하여 구현합니다.&lt;br&gt;&lt;br/&gt;&lt;/p&gt;
&lt;p&gt;두 방식의 차이점은 다음과 같습니다.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Streams DSL&lt;/th&gt;
&lt;th&gt;processor API&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;일반적인 스트림 처리 작업을 위한 &lt;strong&gt;고수준 추상화&lt;/strong&gt; 제공&lt;/td&gt;
&lt;td&gt;스트림 처리 로직을 직접 정의하고 제어할 수 있는 &lt;strong&gt;저수준 추상화&lt;/strong&gt; 제공&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;필터링, 변환, 결합, 집계 등 &lt;strong&gt;일반적인 스트림 처리 작업을 간단하고 선언적인 방식&lt;/strong&gt;으로 수행&lt;/td&gt;
&lt;td&gt;스트림 프로세서, 상태 저장소, 토폴로지 등을 &lt;strong&gt;직접 정의하고 관리&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;Streams DSL 에서 제공하는 추상화된 모든 메서드는 &lt;a href=&quot;https://docs.confluent.io/platform/current/streams/developer-guide/dsl-api.html#&quot;&gt;Kafka Streams Domain Specific Language for Confluent Platform&lt;/a&gt;에서 확인할 수 있습니다.&lt;br&gt;&lt;br/&gt;&lt;/p&gt;
&lt;p&gt;이제 본격적으로 Kafka Streams를 적용해 보러 가볼까요~? ️&lt;/p&gt;
&lt;h2&gt;StreamsConfig 인스턴스 생성&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;StreamsConfig&lt;/code&gt;에는 kafka 스트림즈 애플리케이션의 동작 방식을 정의하는 다양한 설정들이 들어갑니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;애플리케이션의 기본 동작, kafka 클러스터 연결, 데이터 직렬화/역직렬화, 상태 관리, 장애 처리, 성능 튜닝 등&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-kotlin&quot;&gt;// SettlementKafkaStreamsApp.kt
@Bean
fun settlementStreams(): KafkaStreams {
    val streamsConfig = streamsConfig()
    // ..
}

// KafkaStreamsConfig.kt
@Bean
fun streamsConfig(): StreamsConfig =
    StreamsConfig(Properties().apply {
      put(StreamsConfig.APPLICATION_ID_CONFIG, kafkaProperties.paymentApplicationName)
      put(StreamsConfig.BOOTSTRAP_SERVERS_CONFIG, kafkaProperties.servers)
      put(StreamsConfig.DEFAULT_KEY_SERDE_CLASS_CONFIG, Serdes.String().javaClass)
      put(StreamsConfig.DEFAULT_VALUE_SERDE_CLASS_CONFIG, serdeFactory.messagePaymentSerde().javaClass)
      put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, &amp;quot;earliest&amp;quot;)
    })&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;여기서 사용할 kafka 스트림즈 애플리케이션의 설정을 살펴보겠습니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;application.id&lt;/code&gt;: kafka 스트림즈 애플리케이션의 &lt;strong&gt;고유 식별자&lt;/strong&gt;입니다.&lt;ul&gt;
&lt;li&gt;kafka 클러스터 내에서 유일해야 하며, &lt;strong&gt;Kafka Consumer Group ID&lt;/strong&gt;로 사용됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;bootstrap.servers&lt;/code&gt;: kafka 브로커 서버의 주소 목록을 지정합니다.&lt;ul&gt;
&lt;li&gt;초기 연결을 위해 사용되며, &lt;code&gt;host:port&lt;/code&gt; 형태로 쉼표로 구분하여 여러 개 지정이 가능합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;default.key.serde&lt;/code&gt;: kafka 토픽에서 메시지를 읽거나 쓸 때 키(Key)의 기본 직렬화/역직렬화(Serde) 방식을 지정합니다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;default.value.serde&lt;/code&gt;: kafka 토픽에서 메시지를 읽거나 쓸 때 값(Value)의 기본 직렬화/역직렬화(Serde) 방식을 지정합니다.&lt;ul&gt;
&lt;li&gt;메시지 키/값의 Serde 객체는 기본값 설정이 되어 있지 않으므로 명시적으로 설정해 주어야 합니다.&lt;/li&gt;
&lt;li&gt;커스텀한 Serde 객체를 사용할 수도 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;consumer.auto.offset.reset&lt;/code&gt;: kafka 컨슈머의 오프셋을 설정합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;레코드 역직렬화를 위한 Serde 객체 생성&lt;/h2&gt;
&lt;p&gt;kafka에서 기본적으로 제공하는 &lt;a href=&quot;https://docs.confluent.io/platform/current/streams/developer-guide/datatypes.html#available-serdes&quot;&gt;Serde&lt;/a&gt;를 사용하거나, 필요한 형태의 레코드를 사용하기 위해서 커스텀한 객체 생성이 필요합니다.&lt;br&gt;&lt;br/&gt;&lt;/p&gt;
&lt;p&gt;여기서는 Json 형태의 &lt;code&gt;StreamMessage&amp;lt;Payment&amp;gt;&lt;/code&gt; 객체로 메시지 값을 역직렬화화기 위해 커스텀한 Serde 객체를 생성해 보겠습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-kotlin&quot;&gt;// SerdeFactory.kt
fun messagePaymentSerde(): JsonSerde&amp;lt;StreamMessage&amp;lt;Payment&amp;gt;&amp;gt; {
    // JsonDeserializer 생성
    val streamMessagePaymentDeserializer = JsonDeserializer(
        object : TypeReference&amp;lt;StreamMessage&amp;lt;Payment&amp;gt;&amp;gt;() {}, // 역직렬화 대상 타입 지정
        objectMapper, // JSON 처리를 위한 ObjectMapper
        false // failOnUnknownProperties flag
    )
    // 신뢰할 수 있는 패키지 설정
    streamMessagePaymentDeserializer.addTrustedPackages(
        &amp;quot;kafkastreams.study.sample.settlement.common.*&amp;quot;,
        &amp;quot;kafkastreams.study.sample.settlement.domain.*&amp;quot;,
    )

    // JsonSerde 객체 생성 및 반환
    return JsonSerde(
        JsonSerializer(objectMapper),
        streamMessagePaymentDeserializer
    )
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;JsonDeserializer&lt;/code&gt; 생성&lt;/strong&gt;&lt;ul&gt;
&lt;li&gt;역직렬화 대상 타입 지정&lt;ul&gt;
&lt;li&gt;&lt;code&gt;JsonDeserializer&lt;/code&gt;는 JSON 문자열을 어떤 객체로 변환해야 하는지 알아야 하므로 역직렬화 대상 타입을 지정해야 합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;JSON 처리를 위한 &lt;code&gt;ObjectMapper&lt;/code&gt;&lt;ul&gt;
&lt;li&gt;날짜 형식, 특정 필드 무시, null 값 처리 등 다양한 JSON 처리 관련 설정이 적용된 &lt;code&gt;objectMapper&lt;/code&gt; 인스턴스를 주입받아 일관된 방식으로 JSON을 처리할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;failOnUnknownProperties&lt;/code&gt; 플래그&lt;ul&gt;
&lt;li&gt;JsonDeserializer가 알 수 없는 JSON 속성(ex. 대상 객체에 매핑될 필드가 없는 속성)을 만났을 때 어떻게 동작할지를 결정합니다.&lt;ul&gt;
&lt;li&gt;false: JSON 데이터에 역직렬화 대상 타입 객체에 정의되지 않은 속성이 있더라도 오류를 발생시키지 않고 해당 속성을 무시합니다.&lt;/li&gt;
&lt;li&gt;true: 역직렬화 대상 타입 객체에 알 수 없는 속성이 있을 경우 역직렬화 과정에서 예외가 발생합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;신뢰할 수 있는 패키지 설정&lt;/strong&gt;&lt;ul&gt;
&lt;li&gt;Jackson이 역직렬화를 수행할 때, 아무 클래스나 역직렬화하지 않도록 제한하는 기능입니다.&lt;/li&gt;
&lt;li&gt;addTrustedPackages() 메서드를 사용하여 역직렬화가 허용되는 패키지 경로를 명시적으로 지정합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;JsonSerde&lt;/code&gt; 객체 생성 및 반환&lt;/strong&gt;&lt;ul&gt;
&lt;li&gt;&lt;code&gt;JsonSerde&lt;/code&gt;는 kafka 스트림즈에서 사용할 수 있도록 Serializer와 Deserializer를 하나로 묶은 클래스입니다.&lt;/li&gt;
&lt;li&gt;이렇게 생성된 &lt;code&gt;JsonSerde&amp;lt;StreamMessage&amp;lt;Payment&amp;gt;&amp;gt;&lt;/code&gt; 객체는 kafka 스트림즈 토폴로지에서 &lt;code&gt;StreamMessage&amp;lt;Payment&amp;gt;&lt;/code&gt; 타입의 데이터를 읽고 쓸 때 사용됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;  &lt;a href=&quot;https://docs.confluent.io/platform/current/streams/developer-guide/datatypes.html#kstreams-data-types-and-serialization-for-cp&quot;&gt;Kafka Streams Data Types and Serialization for Confluent Platform&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;처리 토폴로지 구성&lt;/h2&gt;
&lt;p&gt;kafka 스트림즈 적용을 위한 기본적인 준비는 되었습니다. 이제 생성하게 될 토폴로지의 구성을 살펴보겠습니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://github.com/jihunparkme/blog/blob/main/img/kafka-streams/topology-example.png?raw=true&quot; alt=&quot;처리 토폴로지 구성&quot;&gt;&lt;/p&gt;
&lt;p&gt;토폴로지를 정의하기 위해 먼저 &lt;code&gt;StreamsBuilder&lt;/code&gt;라는 빌더 생성이 필요합니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;StreamsBuilder&lt;/code&gt;를 사용해서 여러 프로세서를 연결하고, 데이터 처리 파이프라인을 구축할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-kotlin&quot;&gt;// SettlementKafkaStreamsApp.kt
@Bean
fun settlementStreams(): KafkaStreams {
    // ...
    val builder = StreamsBuilder()
    // ...
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이제 정산 데이터 생성을 위해 여섯 단계의 토폴로지를 한 개씩 만들어 보겠습니다.&lt;/p&gt;
&lt;h3&gt;1단계. 토픽으로부터 결제 데이터 받기&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://github.com/jihunparkme/blog/blob/main/img/kafka-streams/source-processor.png?raw=true&quot; alt=&quot;토픽으로부터 결제 데이터 받기&quot;&gt;&lt;/p&gt;
&lt;p&gt;싱크 프로세서에 해당하는 &lt;code&gt;stream()&lt;/code&gt; 메서드(input topics → KStream)는 토픽으로부터 소비한 메시지를 명시한 Serde 객체 형태에 맞게 매핑하고 레코드 스트림 &lt;a href=&quot;https://docs.confluent.io/platform/current/streams/concepts.html#kstream&quot;&gt;KStream&lt;/a&gt;을 생성합니다. &lt;code&gt;Serde&lt;/code&gt;를 명시적으로 지정하지 않으면 streamsConfig 구성의 기본 Serde가 사용되고, Kafka 입력 토픽에 있는 레코드의 키/값 유형이 구성된 기본 Serde와 일치하지 않는 경우 Serde를 명시적으로 지정해야 합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-kotlin&quot;&gt;// SettlementKafkaStreamsApp.kt
@Bean
fun settlementStreams(): KafkaStreams {
    // ...
    val paymentStream: KStream&amp;lt;String, StreamMessage&amp;lt;Payment&amp;gt;&amp;gt; = builder.stream( // 입력 스트림(소스 프로세서)을 토폴로지에 추가
        kafkaProperties.paymentTopic, // 데이터를 읽어올 Kafka 토픽 이름
        Consumed.with( // 키와 값의 직렬화/역직렬화기(Serde) 지정
            Serdes.String(),
            serdeFactory.messagePaymentSerde() // Serde 객체 생성 단계에서 생성한 Serde를 사용
        )
    )
    // ...
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;토폴로지에 추가된 스트림이 정상적으로 동작하는지 확인하고 싶다면, 디버깅/테스트 환경에서 &lt;a href=&quot;https://docs.confluent.io/platform/7.9/streams/javadocs/javadoc/org/apache/kafka/streams/kstream/KStream.html#print-org.apache.kafka.streams.kstream.Printed-&quot;&gt;print()&lt;/a&gt; 메서드를 활용해서 단계별로 레코드의 상태를 확인할 수도 있습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-kotlin&quot;&gt;// SettlementKafkaStreamsApp.kt
@Bean
fun settlementStreams(): KafkaStreams {
    // ...
    paymentStream 
        .print(Printed.toSysOut&amp;lt;String, StreamMessage&amp;lt;Payment&amp;gt;&amp;gt;().withLabel(&amp;quot;payment-stream&amp;quot;))
        // [payment-stream]: 5a54041d-2cce-43f5-8194-299acb8e8766, StreamMessage(channel=OFFLINE, action=PAYMENT, data=Payment(paymentType=OFFLINE, amount=65218, payoutDate=2025-05-21, confirmDate=2025-05-21, merchantNumber=merchant-1881, paymentDate=2025-05-19T21:48:15.989609, paymentActionType=PAYMENT, paymentMethodType=CARD))
  // ...
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;2단계. 결제 메시지 저장&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://github.com/jihunparkme/blog/blob/main/img/kafka-streams/example-peek.png?raw=true&quot; alt=&quot;결제 메시지 저장&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;stream&lt;/code&gt; 메서드를 통해 수신되는 결제 데이터를 &lt;a href=&quot;https://docs.confluent.io/platform/7.9/streams/javadocs/javadoc/org/apache/kafka/streams/kstream/KStream.html#peek-org.apache.kafka.streams.kstream.ForeachAction-&quot;&gt;peek()&lt;/a&gt; 연산에 적용된 람다 함수를 통해 로그에 저장합니다. &lt;code&gt;peek&lt;/code&gt; 메서드는 각 레코드에 대해 작업을 수행하고 변경되지 않은 스트림을 반환합니다. peek는 로깅이나 메트릭 추적, 디버깅 및 트러블슈팅과 같은 상황에 유용하게 사용할 수 있습니다. 만일 스트림 데이터에 대한 수정 작업이 필요할 경우 &lt;code&gt;map&lt;/code&gt;, &lt;code&gt;mapValues&lt;/code&gt; 같은 메서드를 사용할 수 있습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-kotlin&quot;&gt;// SettlementKafkaStreamsApp.kt
@Bean
fun settlementStreams(): KafkaStreams {
    // ...
    paymentStream // 스트림을 통해 들어오는 모든 결제 메시지를 로그로 저장
        .peek({ _, message -&amp;gt; settlementService.savePaymentMessageLog(message) })
    // ...
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;3단계. 결제 데이터로 정산 베이스 생성&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://github.com/jihunparkme/blog/blob/main/img/kafka-streams/example-mapValue.png?raw=true&quot; alt=&quot;결제 데이터로 정산 베이스 생성&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://docs.confluent.io/platform/7.9/streams/javadocs/javadoc/org/apache/kafka/streams/kstream/KStream.html#map-org.apache.kafka.streams.kstream.KeyValueMapper-&quot;&gt;mapValues()&lt;/a&gt; 메서드를 통해 스트림의 각 레코드에 대해 키는 그대로 유지하면서, 값만을 새로운 타입(&lt;code&gt;Base&lt;/code&gt;)으로 변환합니다. 변환 로직은 인자로 전달된 &lt;code&gt;ValueMapper&lt;/code&gt; 인터페이스의 구현체에 의해 정의됩니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-kotlin&quot;&gt;// SettlementKafkaStreamsApp.kt
@Bean
fun settlementStreams(): KafkaStreams {
    // ...
    paymentStream
        .mapValues(BaseMapper())
    // ...
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;mapValues&lt;/code&gt; 메서드에 전달하기 위한 &lt;code&gt;ValueMapper&lt;/code&gt; 구현체를 정의해 보겠습니다. &lt;code&gt;ValueMapper&amp;lt;V, VR&amp;gt;&lt;/code&gt; 인터페이스는 입력 값 타입 &lt;code&gt;V&lt;/code&gt;를 출력 값 타입 &lt;code&gt;VR&lt;/code&gt;로 변환하는 역할을 합니다. 여기서 입력 값 타입 &lt;code&gt;V&lt;/code&gt;는 &lt;code&gt;StreamMessage&amp;lt;Payment&amp;gt;&lt;/code&gt;, 출력 값 타입 &lt;code&gt;VR&lt;/code&gt;은 &lt;code&gt;Base&lt;/code&gt;에 해당하고, 기존 스트림의 값을 어떻게 변환할지에 대한 구체적인 로직을 정의합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-kotlin&quot;&gt;class BaseMapper() : ValueMapper&amp;lt;StreamMessage&amp;lt;Payment&amp;gt;, Base&amp;gt; { // ValueMapper 인터페이스 구현
  // 스트림의 각 메시지에 대해 apply 메서드를 호출하며, 메시지의 값을 인자로 전달
  override fun apply(payment: StreamMessage&amp;lt;Payment&amp;gt;): Base {
    return Base( // 입력으로 받은 객체의 데이터를 사용하여 새로운 객체를 생성하고 반환
      paymentType = payment.data?.paymentType,
      amount = payment.data.amount,
      payoutDate = payment.data.payoutDate,
      confirmDate = payment.data.confirmDate,
      merchantNumber = payment.data.merchantNumber,
      paymentDate = payment.data.paymentDate,
      paymentActionType = payment.data.paymentActionType,
      paymentMethodType = payment.data.paymentMethodType,
    )
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;4단계. 비정산 또는 중복 결제 건 필터링&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://github.com/jihunparkme/blog/blob/main/img/kafka-streams/example-filter.png?raw=true&quot; alt=&quot;비정산 또는 중복 결제 건 필터링&quot;&gt;&lt;/p&gt;
&lt;p&gt;베이스가 생성된 후, 결제 데이터 중에서 비정산(테스트 결제, 비정산 가맹점, 망 취소, 미확인 등) 또는 중복된 건들은 UnSettlement, Duplicated로 분류합니다. 이렇게 분류된 데이터 중 정산 대상에 해당하는 데이터만 다음 파이프라인으로 이어지도록 &lt;a href=&quot;https://docs.confluent.io/platform/7.9/streams/javadocs/javadoc/org/apache/kafka/streams/kstream/KStream.html#filter-org.apache.kafka.streams.kstream.Predicate-&quot;&gt;filter()&lt;/a&gt; 메서드를 사용합니다.&lt;br&gt;&lt;br/&gt;&lt;/p&gt;
&lt;p&gt;원칙적으로 비정산 결제 건과 중복 결제 건 필터링은 각각 별도의 프로세서로 구현하는 것이 더 명확하겠지만, 이 글에서는 설명의 간결함을 위해 하나의 단계로 합쳤습니다. filter 메서드는 주어진 조건을 만족하는 레코드만으로 구성된 새로운 KStream을 반환하며, 조건을 만족하지 않는 레코드는 스트림에서 제외됩니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-kotlin&quot;&gt;// SettlementKafkaStreamsApp.kt
@Bean
fun settlementStreams(): KafkaStreams {
    // ...
    paymentStream
        .filter { _, base -&amp;gt; settlementService.isSettlement(base) }
    // ...
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;.&lt;br&gt;.&lt;br&gt;.&lt;br&gt;&lt;br/&gt;&lt;/p&gt;
&lt;p&gt;5단계부터는 2편에서 찾아뵙겠습니다~!  &lt;br&gt;&lt;br/&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://data-make.tistory.com/803&quot;&gt;정산 데이터, 이제 스트리밍으로 즐기세요! (feat. Kafka streams) 2편&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;</description>
      <category>Web/Infra</category>
      <category>kafka</category>
      <category>Kafka Streams</category>
      <category>Kotlin</category>
      <category>Processor</category>
      <category>spring</category>
      <category>Streaming</category>
      <category>Streams</category>
      <category>스트림</category>
      <category>스트림즈</category>
      <category>카프카</category>
      <author>Aaron</author>
      <guid isPermaLink="true">https://data-make.tistory.com/802</guid>
      <comments>https://data-make.tistory.com/802#entry802comment</comments>
      <pubDate>Wed, 6 Aug 2025 21:01:37 +0900</pubDate>
    </item>
    <item>
      <title>[MongoDB] MongoDB Sharding</title>
      <link>https://data-make.tistory.com/801</link>
      <description>&lt;div class=&quot;markdown-body&quot;&gt;
&lt;h1&gt;MongoDB Sharding&lt;/h1&gt;
&lt;p&gt;&lt;code&gt;MongoDB Sharding&lt;/code&gt;에 대한 빠른 이해를 위해 &lt;a href=&quot;https://www.mongodb.com/docs/manual/sharding/&quot;&gt;공식 문서&lt;/a&gt;를 간략하게 정리하고자 합니다.&lt;/p&gt;
&lt;h2&gt;Sharding?&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;데이터를 여러 머신에 분산하는 방법&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;MongoDB는 샤딩을 통한 수평적 확장을 지원&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;수평적 확장&lt;/strong&gt;: 시스템 데이터 세트와 로드를 여러 서버로 나누고 필요에 따라 서버를 추가하여 용량을 늘리는 것&lt;/li&gt;
&lt;/ul&gt;
&lt;br/&gt;

&lt;h2&gt;Sharded Cluster&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;컬렉션 수준에서 데이터를 샤딩하여 클러스터의 샤드 전체에 컬렉션 데이터를 분산&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;구성 컴포넌트&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;shard&lt;/code&gt;: 각 샤드에는 샤드 데이터의 하위 집합이 포함.&lt;ul&gt;
&lt;li&gt;각 샤드는 복제본 세트로 배포되어야 함&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Routing with mongos&lt;/code&gt;: mongos는 클라이언트 애플리케이션과 샤딩된 클러스터 간의 인터페이스를 제공하는 쿼리 라우터 역할&lt;/li&gt;
&lt;li&gt;&lt;code&gt;config servers&lt;/code&gt;: config 서버는 클러스터에 대한 메타데이터와 구성 설정을 저장&lt;ul&gt;
&lt;li&gt;config 서버는 복제본 세트(CSRS)로 배포되어야 함&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;br/&gt;

&lt;h2&gt;Shard Keys&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;MongoDB는 샤드 키를 사용하여 샤드 전반에서 컬렉션 문서를 분산&lt;/p&gt;
&lt;p&gt;샤드 키는 문서의 필드 하나 또는 여러 필드로 구성&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;샤딩된 컬렉션의 문서에는 샤드 키 필드가 누락될 수 있습니다. &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;누락된 샤드 키 필드는 문서를 여러 샤드에 분산할 때 null 값이 있는 것으로 처리&lt;/li&gt;
&lt;li&gt;null이 아닌 값으로 채워지도록 설정도 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;샤드 데이터를 청크로 분할하는데, 각 청크에는 샤드 키에 따라 포괄적인 하위 범위와 배타적인 상위 범위가 있습니다.&lt;/p&gt;
&lt;br/&gt;

&lt;p&gt;&lt;strong&gt;Shard Key Index&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;채워진 컬렉션을 샤딩하려면 컬렉션에 샤드 키로 시작하는 인덱스가 있어야 합니다. &lt;/li&gt;
&lt;li&gt;빈 컬렉션을 샤딩할 때, 컬렉션에 지정된 샤드 키에 대한 적절한 인덱스가 아직 없는 경우 MongoDB는 지원 인덱스를 생성힙니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Shard Key Strategy&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;샤드 키 선택은 샤딩된 클러스터의 성능, 효율성 및 확장성에 영향을 줄 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;  나중에 컬렉션을 다시 샤딩할 수는 있지만, 확장성 및 성능 관련 문제를 피하려면 샤드 키 선택을 신중하게 고려하는 것이 중요합니다.&lt;/p&gt;
&lt;br/&gt;

&lt;h2&gt;Advantages of Sharding&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;읽기/쓰기&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;샤딩된 클러스터의 샤드 전반에 읽기 및 쓰기 워크로드를 분산시켜 각 샤드가 클러스터 작업의 하위 집합을 처리할 수 있도록 합니다.&lt;/li&gt;
&lt;li&gt;읽기 및 쓰기 워크로드는 더 많은 샤드를 추가하여 클러스터 전반에서 수평으로 확장 가능합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;저장 용량&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;샤딩은 클러스터의 샤드 전반에 데이터를 분산시켜 각 샤드가 전체 클러스터 데이터의 하위 집합을 포함하도록 합니다. &lt;/li&gt;
&lt;li&gt;데이터가 증가함에 따라 추가 샤드로 클러스터의 저장 용량을 늘릴 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;고가용성&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;config 서버 및 샤드를 복제본 세트로 배포하면 가용성이 향상됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;br/&gt;

&lt;h2&gt;Sharding Strategy&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;MongoDB는 샤딩된 클러스터 전반에 데이터를 분산하기 위한 두 가지 샤딩 전략을 지원&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;&lt;strong&gt;Hashed Sharding&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;샤드 키 필드 값의 해시를 계산하는 작업이 포함(해시를 자동으로 계산)&lt;ul&gt;
&lt;li&gt;그런 다음 각 청크에는 해시 샤드 키 값에 따라 범위가 할당&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;해시 값이 같은 청크에 있을 가능성이 낮음&lt;/li&gt;
&lt;li&gt;해시된 값을 기반으로 하는 데이터 분산은 특히 샤드 키가 단조롭게 변경되는 데이터 세트에서 더욱 균일한 데이터 분산 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;center&gt;&lt;img src=&quot;https://www.mongodb.com/ko-kr/docs/manual/images/sharding-hash-based.bakedsvg.svg&quot; width=&quot;100%&quot;&gt;&lt;/center&gt;

&lt;p&gt;&lt;strong&gt;Ranged Sharding&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;샤드 키 값을 기준으로 데이터를 범위로 나누는 작업이 포함&lt;ul&gt;
&lt;li&gt;샤드 키 값을 기반으로 각 청크에 범위가 할당&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;샤드 키는 동일한 청크에 존재할 가능성이 높음&lt;/li&gt;
&lt;/ul&gt;
&lt;center&gt;&lt;img src=&quot;https://www.mongodb.com/ko-kr/docs/manual/images/sharding-range-based.bakedsvg.svg&quot; width=&quot;100%&quot;&gt;&lt;/center&gt;

&lt;p&gt;  범위가 지정된 샤딩의 효율성은 선택한 샤드 키에 따라 달라집니다. &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;샤드 키를 제대로 고려하지 않으면 데이터가 고르지 않게 분산되어 샤딩의 일부 이점을 무효화하거나 성능 병목 현상을 초래할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;</description>
      <category>Web/Infra</category>
      <category>database</category>
      <category>mongodb</category>
      <category>shard</category>
      <category>sharding</category>
      <author>Aaron</author>
      <guid isPermaLink="true">https://data-make.tistory.com/801</guid>
      <comments>https://data-make.tistory.com/801#entry801comment</comments>
      <pubDate>Fri, 27 Jun 2025 18:48:08 +0900</pubDate>
    </item>
    <item>
      <title>[MongoDB MCP] MongoDB MCP Server로 데이터 추출해 보기</title>
      <link>https://data-make.tistory.com/800</link>
      <description>&lt;div class=&quot;markdown-body&quot;&gt;
&lt;h1&gt;MongoDB MCP Server로 데이터 추출해보기&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지난 시간에 다룬 &lt;a href=&quot;https://data-make.tistory.com/799&quot;&gt;5분 만에 GitHub MCP Server 구축해서 GitHub 작업 시키기&lt;/a&gt;에 이어, 이번에는 &lt;code&gt;MongoDB MCP Server&lt;/code&gt; 구축 방법을 소개하려고 합니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;MongoDB MCP Server&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/mongodb-js/mongodb-mcp-server&quot;&gt;mongodb-mcp-server&lt;/a&gt; 설정 역시 지난번에 다룬 GitHub MCP Server와 유사합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다른 MCP 서버와 동일하게 &lt;code&gt;mcp.json&lt;/code&gt; 파일에서 MongoDB MCP Server 설정만 추가하면 준비가 완료됩니다.&lt;/p&gt;
&lt;br /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;실제 MongoDB 클러스터에 연동&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;{
    &quot;mcp&quot;: { // MCP 서버의 전체 설정을 담는 객체
        &quot;inputs&quot;: [], // 입력 소스(예: 파일, 폴더 등)를 지정하는 배열
        &quot;servers&quot;: { // 사용할 MCP 서버들을 정의하는 객체
            &quot;mongodb&quot;: { // 'mongodb'라는 이름의 MCP 서버 설정
                &quot;command&quot;: &quot;npx&quot;, // 서버 실행에 사용할 명령어
                &quot;args&quot;: [ // npx에 전달할 인자(Arguments) 목록
                    &quot;-y&quot;, // npx 실행 시 자동으로 yes를 선택
                    &quot;mongodb-mcp-server&quot;, // 실행할 패키지 이름
                    &quot;--connectionString&quot;, // MongoDB 연결 문자열 옵션
                    &quot;mongodb+srv://&amp;lt;yourcluster&amp;gt;&quot;, // 실제 MongoDB 클러스터의 연결 정보
                    &quot;--readOnly&quot; // 서버를 읽기 전용 모드로 실행
                ],
            }
        }
    },
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;MongoDB MCP Server가 사용할 포트를 명시할 수도 있습니다.&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;1c&quot;&gt;&lt;code&gt;&quot;mongodb&quot;: {
    &quot;command&quot;: &quot;npx&quot;,
    &quot;args&quot;: [
        &quot;-y&quot;, 
        &quot;mongodb-mcp-server&quot;, 
        &quot;--connectionString&quot;, 
        &quot;mongodb://localhost:27017&quot;,
        &quot;--port&quot;, // MCP 서버 포트             
        &quot;27018&quot;, // MCP 서버가 사용할 포트
        &quot;--readOnly&quot;
    ],
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설정이 완료되면 &lt;code&gt;mcp.json&lt;/code&gt; 파일에서 Start 버튼을 누르거나,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;Command Palette(cmd + shift + p) &amp;gt; MCP: List Servers &amp;gt; mongodb&lt;/code&gt;를 선택하여 MongoDB MCP Server를 실행할 수 있습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Agent mode&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 본격적으로 MongoDB MCP Server로 요청을 보내보겠습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Claude 3.5 사용. &lt;del&gt;(Claude 4는 데이터를 조회한다고 해놓고 도망가는 행동을 자주 보였습니다.)&lt;/del&gt;&lt;/li&gt;
&lt;li&gt;Claude 3.5를 계속 사용하다 보니 결과는 주지 않고 조회만 반복하는 현상이 나타나, 다시 Claude 4를 사용하게 되었는데, Claude 3.5와의 대화 내용이 학습되었는지 이후에는 대답을 잘 해주었습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;간단한 질문으로 시작해 보겠습니다.&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;  &lt;b&gt;시작 질문&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;데이터베이스에 있는 컬렉션 목록을 보여줘&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과&lt;/p&gt;
&lt;pre class=&quot;markdown&quot;&gt;&lt;code&gt;먼저 데이터베이스 목록을 조회하겠습니다:
&amp;gt; mongodb (MCP Server)...

다음으로 각 데이터베이스의 컬렉션을 조회하겠습니다. 먼저 'aaa' 데이터베이스의 컬렉션을 확인해보겠습니다:
&amp;gt; mongodb (MCP Server)...

'bbb' 데이터베이스의 컬렉션도 확인해보겠습니다:
&amp;gt; mongodb (MCP Server)...

데이터베이스 별 컬렉션 목록은 다음과 같습니다:

1. aaa 데이터베이스:
- abcd123
1. bbb 데이터베이스:
- apple 
- banana
- cherry

나머지 admin, config, local은 MongoDB의 시스템 데이터베이스이고, ccc는 비어 있는 테스트용 데이터베이스로 보입니다.

특정 데이터베이스나 컬렉션에 대해 더 자세한 정보가 필요하시다면 말씀해 주세요.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로컬 MongoDB에 연동 후 테스트한 결과, &lt;b&gt;데이터베이스 목록&lt;/b&gt;과 &lt;b&gt;컬렉션 목록&lt;/b&gt;을 정확하게 조회하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;테스트 데이터베이스와 연동하여 요청해 보았을 때에도 &lt;b&gt;컬렉션의 성격에 따라 정확하게 분류하고 설명&lt;/b&gt;하며, &lt;b&gt;존재하지 않는 컬렉션을 표시&lt;/b&gt;하거나 &lt;b&gt;존재하는 컬렉션을 누락&lt;/b&gt;하는 경우는 없었습니다.&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 질문부터는 데이터가 어느 정도 쌓여 있는 테스트 데이터베이스에 연동해서 요청을 보내보려고 합니다.&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1️⃣ &lt;b&gt;첫 번째 질문&lt;/b&gt; (간단한 조회 1)&lt;/p&gt;
&lt;pre class=&quot;yaml&quot;&gt;&lt;code&gt;xxx_collection 데이터를 추출해 주세요.
xxx_field 기준으로 상위 10개만 보여주세요.

조건은 아래와 같습니다.
- merchant_id : ZXC1234
- created_date : 2025-06-01 이후
- 추출 데이터 : merchant_id, total_amount&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;MongoDB에서 xxx_collection 컬렉션에서 데이터를 추출해드리겠습니다. 먼저 어떤 데이터베이스와 컬렉션이 있는지 확인해보겠습니다.
&amp;gt; mongodb (MCP Server)...

이제 aaa 데이터베이스의 컬렉션을 확인해보겠습니다.
&amp;gt; mongodb (MCP Server)...

이제 xxx_collection 컬렉션의 스키마를 확인해보겠습니다.
&amp;gt; mongodb (MCP Server)...

이제 요청하신 조건에 맞는 데이터를 추출해보겠습니다. merchant_id가 &quot;ZXC1234&quot;이고 created_date가 2025-06-01 이후인 데이터에서 total_amount와 merchant_id를 가져와서 total_amount 기준으로 상위 10개를 조회하겠습니다.

검색 결과를 확인해보겠습니다. 현재 2건의 데이터가 조회되었습니다. 표로 정리해드리겠습니다:

(표 생략)

조회 결과 요약:
- 조건: merchant_id = &quot;ZXC1234&quot;, created_date &amp;gt;= 2025-06-01
- 조회된 레코드 수: 2건
- 정렬: total_amount 내림차순
- 상위 10개를 요청했지만 조건에 맞는 데이터가 2건만 존재합니다.

두 레코드 모두 total_amount가 3,000원으로 동일하며, 2025년 6월 12일에 생성된 데이터입니다.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;간단한 조회 요청은 MCP가 충분히 처리할 수 있을 것이라고 생각했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;표 정리를 포함하여 조회 결과도 요약해 주는 것을 볼 수 있습니다.&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2️⃣ 두 번째 질문 (간단한 조회 2)&lt;/p&gt;
&lt;pre class=&quot;haml&quot;&gt;&lt;code&gt;aaa 데이터베이스의 log 컬렉션에서 아래 조건에 맞게 모든 결과를 조회해서 보여줘

조회 조건
- channel: OFFLINE
- created_date: {$gte: ISODate('2025-06-19T15:00:00'), $lt: ISODate('2025-06-20T15:00:00')} 조건을 그대로 사용
- source: PAYMENT
- status: FAILED

조회 필드
- action
- group_id
- serial
- status
- body&lt;/code&gt;&lt;/pre&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;b&gt;실제 결과를 보여주지 않는 현상&lt;/b&gt;이 있었습니다.&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;b&gt;'보여줘'와 같은 명확한 요청 동사를 사용&lt;/b&gt;하는 것이 좋을 것 같습니다.&lt;/p&gt;
&lt;br /&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;MongoDB는 특성상 &lt;b&gt;한국 시간(KST)&lt;/b&gt; 이 아닌 &lt;b&gt;UTC(협정 세계시)&lt;/b&gt; 를 사용합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 때문에 KST와 UTC를 모두 고려하여 조회 조건을 요청하면, MCP 서버가 &lt;b&gt;임의로 타임존을 변경하여 날짜를 잘못 변환하는 현상&lt;/b&gt;이 나타났습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 문제를 해결하기 위해서는 &lt;b&gt;날짜 조건을 쿼리 그대로 명확하게 전달&lt;/b&gt;하는 것이 가장 정확했습니다.&lt;/p&gt;
&lt;br /&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;조회할 필드를 요청에 포함시키지 않으면 MCP 서버가 &lt;b&gt;임의의 필드만 추출하여 결과를 제공&lt;/b&gt;하는 경우가 있었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;원하는 필드만 정확하게 조회하기 위해서는 &lt;b&gt;요청 시 조회할 필드명을 명확하게 지정&lt;/b&gt;해주는 것이 좋습니다.&lt;/p&gt;
&lt;br /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;결과&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;erlang&quot;&gt;&lt;code&gt;MongoDB에서 지정하신 조건에 맞는 데이터를 조회해드리겠습니다.
&amp;gt; mongodb (MCP Server)...

조회 결과 설명
...
조회 조건
...
조회된 주요 필드
...
주요 특징
...&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이어서 조회 결과를 csv 파일로 추출할 수도 있었습니다.&lt;/p&gt;
&lt;br /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과적으로, 조회 필드를 명시적으로 요청했음에도 불구하고 MCP 서버가 json 타입의 body 필드의 경우 하위 필드의 일부만 임의로 추출해서 보여주는 현상이 있었습니다. 하위 필드를 가지고 있다면 하위 필드에 대한 명확한 지정을 해주는 것이 필요해 보입니다.&lt;/p&gt;
&lt;br /&gt;&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3️⃣ 세 번째 질문 (조인이 포함된 복잡한 조회)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 간단한 조회가 아닌 조인이 포함된 요청을 해보겠습니다.&lt;/p&gt;
&lt;br /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;요청&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;haml&quot;&gt;&lt;code&gt;apple 컬렉션과 banana 컬렉션을 조인해서 데이터를 추출해 주는데 표 형태로 보여주세요

---

apple 컬렉션 기준 조회 조건 (and 조건)
- p.p_date: {$gte: ISODate('2024-06-02T15:00:00')},
- e.origin_init_p_date: {$gte: ISODate('2024-06-02T15:00:00')},

apple 컬렉션 기준 그룹 조건
- p.p_rule_id
- channel
- cid
- payment_method

조인 조건
왼쪽은 banana 컬렉션 기준, 오른쪽은 apple 컬렉션 기준이고, apple 컬렉션의 left join으로 banana 컬렉션에 조인되는 데이터가 없다면 공백으로 치환해 주세요.
- p_rule.$id : p.p_rule_id
- channel : channel
- cid : cid
- payment_method : payment_method

결과 필드
- &quot;01_채널타입&quot;: &quot;apple.channel&quot;,
- &quot;02_상호명&quot;: &quot;banana.description&quot;
- &quot;03_CID&quot;: &quot;apple.cid&quot;,
- &quot;04_정대상일&quot;: &quot;apple.target_date&quot;,
- &quot;05_지급일(As is)&quot;: &quot;apple.e.origin_init_p_date&quot;,
- &quot;06_지급일(To be)&quot;: &quot;apple.p.p_date&quot;,
- &quot;07_정산주기&quot;: banana.cycle_type,
- &quot;08_지급그룹ID&quot;: &quot;p.p_rule_id&quot;,
- &quot;09_YID&quot;: &quot;apple.yid&quot;,
- &quot;10_건별내역건수&quot;: &quot;count&quot;, // 그룹으로 묶인 수량
- &quot;11_지급예정금액&quot;: &quot;apple.p.p_amount&quot;,&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;결과&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;lasso&quot;&gt;&lt;code&gt;MongoDB에서 apple과 banana 컬렉션을 LEFT JOIN하여 데이터를 추출해드리겠습니다. LEFT JOIN으로 설정하여 banana에 매칭되는 데이터가 없어도 apple 데이터는 표시되도록 하겠습니다.
&amp;gt; mongodb (MCP Server)...

LEFT JOIN 결과가 조회되었습니다. 총 3개의 그룹이 조회되었으며, banana와 매칭되지 않는 데이터는 공백으로 표시되었습니다. 결과를 표로 정리해드리겠습니다:

조회 결과
...

조회 결과 요약
...

주요 특징
...&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 요청에서는 조회 결과, 조회 결과 요약, 주요 특징이 포함되었지만, 다른 요청에서는 적용된 조회 조건, 그룹핑 기준, 조인 조건, 결과 분석이 포함되었는데, 매번 요청 때마다 템플릿이 달라지는 것으로 보입니다. 템플릿에 맞는 결과를 받길 원한다면 템플릿을 명시하여 요청하면 좋을 것 같습니다.&lt;/p&gt;
&lt;br /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;생각보다 복잡한 쿼리가 필요한 조회였는데, 놀랍게도 결과가 정확하게 나왔습니다. 그렇다면 의도한 쿼리대로 조회가 잘 되었는지, 조회에 사용된 쿼리도 확인해 보겠습니다.&lt;/p&gt;
&lt;br /&gt;&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;질문&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;위 추출 결과에 사용된 몽고디비 쿼리를 작성해 주세요&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;결과&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;erlang&quot;&gt;&lt;code&gt;위에서 사용된 MongoDB 쿼리를 작성해드리겠습니다. 이 쿼리는 apple과 banana 컬렉션을 LEFT JOIN하여 데이터를 추출하는 aggregation pipeline입니다.
&amp;gt; mongodb (MCP Server)...

MongoDB 쿼리를 mongodb_query.js 파일로 작성했습니다. 이 쿼리는 다음과 같은 특징을 가지고 있습니다:

쿼리 구조
...

실행 방법
...&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;마치며&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;직접 작성한 쿼리로 먼저 결과를 확인한 후, MCP 서버가 원하는 결과를 도출할 때까지 프롬프트를 계속 수정하며 테스트를 진행했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과적으로는 원하던 값을 얻을 수 있었지만, 정확한 결과를 모르는 상태에서 프롬프트만으로 얻어낸 데이터를 과연 신뢰할 수 있을지는 의문입니다.&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;높은 확률(사실상 100%)로 정확한 결과를 도출하는 템플릿이 존재하고, 그 템플릿에서 크게 벗어나지 않는 반복적인 요청이 있다면 운영 환경에서 유용하게 활용할 수 있을 것 같습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만, 매번 요구사항이 달라지는 현 상황에서는 대응하기 어려울 수도 있다는 생각이 듭니다.&lt;/p&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://github.com/mongodb-js/mongodb-mcp-server&quot;&gt;https://github.com/mongodb-js/mongodb-mcp-server&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.mongodb.com/blog/post/announcing-mongodb-mcp-server&quot;&gt;https://www.mongodb.com/blog/post/announcing-mongodb-mcp-server&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.mongodb.com/ko-kr/docs/manual/mcp/&quot;&gt;https://www.mongodb.com/ko-kr/docs/manual/mcp/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;</description>
      <category>AI</category>
      <category>AI</category>
      <category>LLM</category>
      <category>MCP</category>
      <category>mongodb</category>
      <category>mongodb mcp server</category>
      <category>VS Code</category>
      <author>Aaron</author>
      <guid isPermaLink="true">https://data-make.tistory.com/800</guid>
      <comments>https://data-make.tistory.com/800#entry800comment</comments>
      <pubDate>Sun, 22 Jun 2025 21:43:14 +0900</pubDate>
    </item>
  </channel>
</rss>