<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>SQLite on Yarang's Tech Lair</title><link>https://blog.agentthread.dev/ko/tags/sqlite/</link><description>Recent content in SQLite on Yarang's Tech Lair</description><generator>Hugo -- gohugo.io</generator><language>ko</language><lastBuildDate>Sat, 30 May 2026 09:00:30 +0900</lastBuildDate><atom:link href="https://blog.agentthread.dev/ko/tags/sqlite/index.xml" rel="self" type="application/rss+xml"/><item><title>SQLite 기반 영구적 워크플로우 설계: 'SQLite is all you need' 실전 가이드</title><link>https://blog.agentthread.dev/ko/post/sqlite-%EA%B8%B0%EB%B0%98-%EC%98%81%EA%B5%AC%EC%A0%81-%EC%9B%8C%ED%81%AC%ED%94%8C%EB%A1%9C%EC%9A%B0-%EC%84%A4%EA%B3%84-sqlite-is-all-you-need-%EC%8B%A4%EC%A0%84-%EA%B0%80%EC%9D%B4%EB%93%9C/</link><pubDate>Sat, 30 May 2026 09:00:30 +0900</pubDate><guid>https://blog.agentthread.dev/ko/post/sqlite-%EA%B8%B0%EB%B0%98-%EC%98%81%EA%B5%AC%EC%A0%81-%EC%9B%8C%ED%81%AC%ED%94%8C%EB%A1%9C%EC%9A%B0-%EC%84%A4%EA%B3%84-sqlite-is-all-you-need-%EC%8B%A4%EC%A0%84-%EA%B0%80%EC%9D%B4%EB%93%9C/</guid><description>&lt;h1 id="sqlite로-워크플로우를-지속-가능하게-만드는-방법"&gt;SQLite로 워크플로우를 지속 가능하게 만드는 방법
&lt;/h1&gt;&lt;p&gt;최근 Hacker News에서 &amp;ldquo;SQLite is all you need for durable workflows&amp;quot;라는 글이 큰 화제가 되었습니다. 분산 시스템을 설계할 때 Kafka, Redis, 복잡한 클러스터링을 떠올리기 쉽지만, 정작 우리가 해결하려는 핵심 문제인 &amp;lsquo;상태 유지(State Management)&amp;lsquo;와 &amp;lsquo;재시작 내성(Resiliency)&amp;lsquo;은 단일 파일 데이터베이스인 SQLite만으로도 충분히 해결할 수 있다는 주장입니다.&lt;/p&gt;
&lt;p&gt;저희 팀의 &amp;lsquo;ZeroClaw&amp;rsquo;나 &amp;lsquo;Discord Decision MCP&amp;rsquo;와 같은 에이전트 시스템을 개발하면서, 복잡한 메시지 큐를 도입하기 전에 SQLite의 가벼우면서도 강력한 트랜잭션 기능을 활용해 워크플로우 엔진을 직접 구현해 보았습니다. 이 글에서는 그 핵심 아키텍처와 실제 코드 예제를 공유합니다.&lt;/p&gt;
&lt;h2 id="1-영구적-워크플로우durable-workflow란"&gt;1. 영구적 워크플로우(Durable Workflow)란?
&lt;/h2&gt;&lt;p&gt;워크플로우가 &amp;lsquo;영구적&amp;rsquo;이다 한다는 것은, 프로그램이 죽거나 서버가 재시작되더라도 중단된 지점부터 다시 시작할 수 있음을 의미합니다. 이를 위해 우리는 다음 두 가지를 보장해야 합니다.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;상태 저장(Snapshotting):&lt;/strong&gt; 현재 진행 상황을 지속적으로 저장해야 합니다.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;정확히 한 번 실행(Exactly-Once Semantics):&lt;/strong&gt; 재시작 시 중복 실행을 방지해야 합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="2-아키텍처-설계-sqlite를-큐와-상태-저장소로-사용하기"&gt;2. 아키텍처 설계: SQLite를 큐와 상태 저장소로 사용하기
&lt;/h2&gt;&lt;p&gt;SQLite는 &lt;code&gt;WAL(Write-Ahead Logging)&lt;/code&gt; 모드와 고급 잠금 메커니즘 덕분에 동시성 환경에서도 놀라운 성능을 냅니다. 워크플로우 엔진은 다음과 같은 테이블 구조를 기반으로 동작합니다.&lt;/p&gt;
&lt;h3 id="데이터베이스-스키마"&gt;데이터베이스 스키마
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sql" data-lang="sql"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;-- 작업의 현재 상태를 저장하는 테이블
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;CREATE&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;TABLE&lt;/span&gt; workflows (
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; id TEXT &lt;span style="color:#66d9ef"&gt;PRIMARY&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;KEY&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; status TEXT &lt;span style="color:#66d9ef"&gt;NOT&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;NULL&lt;/span&gt;, &lt;span style="color:#75715e"&gt;-- pending, running, completed, failed
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; current_step INTEGER &lt;span style="color:#66d9ef"&gt;NOT&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;NULL&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;DEFAULT&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; payload TEXT, &lt;span style="color:#75715e"&gt;-- JSON 형태의 입력 데이터
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; updated_at INTEGER &lt;span style="color:#66d9ef"&gt;NOT&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;NULL&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;-- 실행해야 할 작업 목록 (큐)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;CREATE&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;TABLE&lt;/span&gt; jobs (
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; id INTEGER &lt;span style="color:#66d9ef"&gt;PRIMARY&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;KEY&lt;/span&gt; AUTOINCREMENT,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; workflow_id TEXT &lt;span style="color:#66d9ef"&gt;NOT&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;NULL&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; target_step INTEGER &lt;span style="color:#66d9ef"&gt;NOT&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;NULL&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; run_at INTEGER, &lt;span style="color:#75715e"&gt;-- 예약 실행 시간 (NULL 즉시 실행)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;FOREIGN&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;KEY&lt;/span&gt;(workflow_id) &lt;span style="color:#66d9ef"&gt;REFERENCES&lt;/span&gt; workflows(id)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;CREATE&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;INDEX&lt;/span&gt; idx_jobs_run_at &lt;span style="color:#66d9ef"&gt;ON&lt;/span&gt; jobs(run_at);
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="rust-구현-예제"&gt;Rust 구현 예제
&lt;/h3&gt;&lt;p&gt;Rust의 &lt;code&gt;rusqlite&lt;/code&gt; 라이브러리를 사용하여 간단한 워크플로우 스케줄러를 작성해 보겠습니다. 이 코드는 &amp;lsquo;ZeroClaw&amp;rsquo; 런타임의 가벼운 작업 스케줄링 로직에서 영감을 받았습니다.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-rust" data-lang="rust"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;use&lt;/span&gt; rusqlite::{Connection, Result, params};
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;use&lt;/span&gt; std::time::{Duration, SystemTime};
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;fn&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;main&lt;/span&gt;() -&amp;gt; Result&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;()&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// 1. 메모리 또는 파일 DB 연결 (WAL 모드 활성화 권장)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;let&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;mut&lt;/span&gt; conn &lt;span style="color:#f92672"&gt;=&lt;/span&gt; Connection::open(&lt;span style="color:#e6db74"&gt;&amp;#34;workflow.db&amp;#34;&lt;/span&gt;)&lt;span style="color:#f92672"&gt;?&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; conn.execute_batch(&lt;span style="color:#e6db74"&gt;&amp;#34;PRAGMA journal_mode=WAL;&amp;#34;&lt;/span&gt;)&lt;span style="color:#f92672"&gt;?&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// 2. 워크플로우 생성 (초기 상태)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;let&lt;/span&gt; workflow_id &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;order-12345&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; insert_workflow(&lt;span style="color:#f92672"&gt;&amp;amp;&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;mut&lt;/span&gt; conn, workflow_id, &lt;span style="color:#e6db74"&gt;&amp;#34;{&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;\&amp;#34;&lt;/span&gt;&lt;span style="color:#e6db74"&gt;item&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;\&amp;#34;&lt;/span&gt;&lt;span style="color:#e6db74"&gt;: &lt;/span&gt;&lt;span style="color:#ae81ff"&gt;\&amp;#34;&lt;/span&gt;&lt;span style="color:#e6db74"&gt;keyboard&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;\&amp;#34;&lt;/span&gt;&lt;span style="color:#e6db74"&gt;}&amp;#34;&lt;/span&gt;)&lt;span style="color:#f92672"&gt;?&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// 3. 작업 예약 (결제 단계)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; enqueue_job(&lt;span style="color:#f92672"&gt;&amp;amp;&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;mut&lt;/span&gt; conn, workflow_id, &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;, None)&lt;span style="color:#f92672"&gt;?&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// 4. 워커 루프 (실무에서는 데몬화)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;loop&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;match&lt;/span&gt; fetch_next_job(&lt;span style="color:#f92672"&gt;&amp;amp;&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;mut&lt;/span&gt; conn)&lt;span style="color:#f92672"&gt;?&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Some(job) &lt;span style="color:#f92672"&gt;=&amp;gt;&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;println!&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;Processing job &lt;/span&gt;&lt;span style="color:#e6db74"&gt;{}&lt;/span&gt;&lt;span style="color:#e6db74"&gt; for workflow &lt;/span&gt;&lt;span style="color:#e6db74"&gt;{}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;, job.target_step, job.workflow_id);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// 비즈니스 로직 실행 (예: LLM 호출, 외부 API 요청)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// simulate_work();
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// 성공 시 다음 스텝 예약 또는 완료 처리
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; complete_step(&lt;span style="color:#f92672"&gt;&amp;amp;&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;mut&lt;/span&gt; conn, &lt;span style="color:#f92672"&gt;&amp;amp;&lt;/span&gt;job.workflow_id, job.target_step)&lt;span style="color:#f92672"&gt;?&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; None &lt;span style="color:#f92672"&gt;=&amp;gt;&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;println!&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;No jobs, sleeping...&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; std::thread::sleep(Duration::from_secs(&lt;span style="color:#ae81ff"&gt;5&lt;/span&gt;));
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// 작업을 큐에 넣고, 워크플로우 상태를 &amp;#39;pending&amp;#39;으로 업데이트하는 트랜잭션
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;fn&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;enqueue_job&lt;/span&gt;(conn: &lt;span style="color:#66d9ef"&gt;&amp;amp;&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;mut&lt;/span&gt; Connection, workflow_id: &lt;span style="color:#66d9ef"&gt;&amp;amp;&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;str&lt;/span&gt;, step: &lt;span style="color:#66d9ef"&gt;i32&lt;/span&gt;, delay: Option&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;SystemTime&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt;) -&amp;gt; Result&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;()&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;let&lt;/span&gt; tx &lt;span style="color:#f92672"&gt;=&lt;/span&gt; conn.transaction()&lt;span style="color:#f92672"&gt;?&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; tx.execute(
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;INSERT INTO jobs (workflow_id, target_step, run_at) VALUES (?1, ?2, ?3)&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;params!&lt;/span&gt;[workflow_id, step, delay.and_then(&lt;span style="color:#f92672"&gt;|&lt;/span&gt;t&lt;span style="color:#f92672"&gt;|&lt;/span&gt; Some(t.duration_since(SystemTime::&lt;span style="color:#66d9ef"&gt;UNIX_EPOCH&lt;/span&gt;).unwrap().as_secs()))]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; )&lt;span style="color:#f92672"&gt;?&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; tx.execute(
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;UPDATE workflows SET status = &amp;#39;pending&amp;#39;, updated_at = ?1 WHERE id = ?2&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;params!&lt;/span&gt;[SystemTime::now().duration_since(SystemTime::&lt;span style="color:#66d9ef"&gt;UNIX_EPOCH&lt;/span&gt;).unwrap().as_secs(), workflow_id]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; )&lt;span style="color:#f92672"&gt;?&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; tx.commit()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// 다음 처리할 작업을 가져오는 함수 (Locking Read)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;fn&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;fetch_next_job&lt;/span&gt;(conn: &lt;span style="color:#66d9ef"&gt;&amp;amp;&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;mut&lt;/span&gt; Connection) -&amp;gt; Result&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;Option&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;Job&lt;span style="color:#f92672"&gt;&amp;gt;&amp;gt;&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// UPDATE 문을 통해 행을 잠그고 반환하는 기법 (UPDATE RETURNING)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// SQLite 3.35.0 이상 지원
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;let&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;mut&lt;/span&gt; stmt &lt;span style="color:#f92672"&gt;=&lt;/span&gt; conn.prepare(
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;UPDATE jobs 
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; SET workflow_id = workflow_id || &amp;#39;:processing&amp;#39; -- 임시 상태 표시 (실제로는 status 컬럼 추천)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; WHERE rowid IN (
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; SELECT rowid FROM jobs 
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; WHERE run_at IS NULL OR run_at &amp;lt;= strftime(&amp;#39;%s&amp;#39;, &amp;#39;now&amp;#39;)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; LIMIT 1
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; )
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; RETURNING id, workflow_id, target_step;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; )&lt;span style="color:#f92672"&gt;?&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;let&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;mut&lt;/span&gt; rows &lt;span style="color:#f92672"&gt;=&lt;/span&gt; stmt.query(&lt;span style="color:#a6e22e"&gt;params!&lt;/span&gt;[])&lt;span style="color:#f92672"&gt;?&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;match&lt;/span&gt; rows.next()&lt;span style="color:#f92672"&gt;?&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Some(row) &lt;span style="color:#f92672"&gt;=&amp;gt;&lt;/span&gt; Ok(Some(Job {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; id: &lt;span style="color:#a6e22e"&gt;row&lt;/span&gt;.get(&lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;)&lt;span style="color:#f92672"&gt;?&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; workflow_id: &lt;span style="color:#a6e22e"&gt;row&lt;/span&gt;.get(&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;)&lt;span style="color:#f92672"&gt;?&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; target_step: &lt;span style="color:#a6e22e"&gt;row&lt;/span&gt;.get(&lt;span style="color:#ae81ff"&gt;2&lt;/span&gt;)&lt;span style="color:#f92672"&gt;?&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; })),
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; None &lt;span style="color:#f92672"&gt;=&amp;gt;&lt;/span&gt; Ok(None),
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// 스템 완료 처리
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;fn&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;complete_step&lt;/span&gt;(conn: &lt;span style="color:#66d9ef"&gt;&amp;amp;&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;mut&lt;/span&gt; Connection, workflow_id: &lt;span style="color:#66d9ef"&gt;&amp;amp;&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;str&lt;/span&gt;, step: &lt;span style="color:#66d9ef"&gt;i32&lt;/span&gt;) -&amp;gt; Result&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;()&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;let&lt;/span&gt; tx &lt;span style="color:#f92672"&gt;=&lt;/span&gt; conn.transaction()&lt;span style="color:#f92672"&gt;?&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// 완료된 작업 삭제
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; tx.execute(&lt;span style="color:#e6db74"&gt;&amp;#34;DELETE FROM jobs WHERE workflow_id = ?1 AND target_step = ?2&amp;#34;&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;params!&lt;/span&gt;[workflow_id, step])&lt;span style="color:#f92672"&gt;?&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// 워크플로우 상태 업데이트 (다음 단계로)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; tx.execute(
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;UPDATE workflows SET current_step = ?1, updated_at = ?2 WHERE id = ?3&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;params!&lt;/span&gt;[step &lt;span style="color:#f92672"&gt;+&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;, SystemTime::now().duration_since(SystemTime::&lt;span style="color:#66d9ef"&gt;UNIX_EPOCH&lt;/span&gt;).unwrap().as_secs(), workflow_id]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; )&lt;span style="color:#f92672"&gt;?&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; tx.commit()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;struct&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Job&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; id: &lt;span style="color:#66d9ef"&gt;i64&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; workflow_id: String,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; target_step: &lt;span style="color:#66d9ef"&gt;i64&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="3-왜-이-구조가-강력한가"&gt;3. 왜 이 구조가 강력한가?
&lt;/h2&gt;&lt;p&gt;이 접근 방식의 가장 큰 장점은 &lt;strong&gt;관리 부하(Ops Overhead)가 거의 없다는 점&lt;/strong&gt;입니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;ZeroClaw와 같은 에이전트 시스템&lt;/strong&gt;에서는 각 에이전트가 자신의 SQLite 파일을 가지고 동작하므로, 중앙 집중식 DB의 병목 현상을 피할 수 있습니다.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Discord MCP&lt;/strong&gt; 같은 봇을 만들 때, 복잡한 Redis 컨테이너를 띄우는 대신 단순히 &lt;code&gt;discord.db&lt;/code&gt; 파일만 백업해두면 모든 대화 기록과 처리 중인 작업을 완벽하게 보호할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="4-결론-필요한-만큼만"&gt;4. 결론: &amp;ldquo;필요한 만큼만&amp;rdquo;
&lt;/h2&gt;&lt;p&gt;&amp;ldquo;SQLite is all you need&amp;quot;라는 제목은 SQLite가 모든 것을 대체할 수 있다는 뜻이 아니라, &lt;strong&gt;대부분의 문제는 SQLite면 충분하다&lt;/strong&gt;는 뜻입니다. 복잡한 분산 시스템을 도입하기 전에, 가장 단순하고 안정적인 파일 기반 데이터베이스로 워크플로우의 신뢰성을 확보해 보는 것은 어떨까요?&lt;/p&gt;
&lt;p&gt;다음 포스트에서는 이 SQLite 워크플로우 엔진을 Kubernetes 환경에서 안정적으로 운영하기 위한 Persisntent Volume 전략에 대해 다루겠습니다.&lt;/p&gt;</description></item><item><title>[Claude Code] 팀 에이전트 통신 아키텍처</title><link>https://blog.agentthread.dev/ko/post/2026-02-28-002-claude-code-team-agent-communication-architecture/</link><pubDate>Sat, 28 Feb 2026 14:07:38 +0900</pubDate><guid>https://blog.agentthread.dev/ko/post/2026-02-28-002-claude-code-team-agent-communication-architecture/</guid><description>&lt;h1 id="claude-code-팀-에이전트-간-통신-아키텍처-설계"&gt;Claude Code 팀 에이전트 간 통신 아키텍처 설계
&lt;/h1&gt;&lt;h2 id="개요"&gt;개요
&lt;/h2&gt;&lt;p&gt;Claude Code에서 여러 AI 에이전트가 협업할 때 효율적인 통신 방식이 필요합니다. 이 글에서는 Discord와 연동된 서버 모니터링 팀을 구축하며 설계한 통신 아키텍처를 공유합니다.&lt;/p&gt;
&lt;h2 id="문제-정의"&gt;문제 정의
&lt;/h2&gt;&lt;p&gt;Claude Code에는 두 가지 유형의 에이전트가 있습니다:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Python 프로세스 (Daemon)&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Discord Gateway, user_comm_daemon 등&lt;/li&gt;
&lt;li&gt;항상 실행 중, 파일 시스템 접근 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;LLM Agent (Task로 생성)&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;gcp-monitor, oci-monitor, alert-manager 등&lt;/li&gt;
&lt;li&gt;SendMessage 도구 사용, Claude Code 네이티브 통신&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;이 두 유형 간의 통신이 필요했습니다.&lt;/p&gt;
&lt;h2 id="고려한-방식들"&gt;고려한 방식들
&lt;/h2&gt;&lt;h3 id="1-파일-기반-메시지-큐-기존"&gt;1. 파일 기반 메시지 큐 (기존)
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;장점: 구현 단순, 의존성 없음
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;단점: 지연 (~1초), 파일 관리 필요
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="2-websocket"&gt;2. WebSocket
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;장점: 실시간 양방향 통신
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;단점: 서버/클라이언트 구현 필요
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="3-grpc"&gt;3. gRPC
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;장점: 강력한 타입 체크, 스트리밍
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;단점: proto 파일 관리, 높은 복잡도
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="4-sqlite-메시지-허브-채택"&gt;4. SQLite 메시지 허브 (채택)
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;장점: 빠름 (~10ms), 트랜잭션, 추가 의존성 없음
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;단점: 단일 머신에서만 동작
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="최종-아키텍처"&gt;최종 아키텍처
&lt;/h2&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;┌─────────────────────────────────────────────────────────────────┐
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;│ SQLite Message Hub (messages.db) │
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;│ ~/.claude/teams/{team}/messages.db │
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;└───────────────────────────┬─────────────────────────────────────┘
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; │
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ┌───────────────────┼───────────────────┐
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ↓ ↓ ↓
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;┌──────────────┐ ┌──────────────┐ ┌──────────────┐
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;│ Discord │ │ Python │ │ LLM │
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;│ Gateway │ │ Daemon │ │ Agent │
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;│ (직접 접근) │ │ (직접 접근) │ │ (MCP Tool) │
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;└──────────────┘ └──────────────┘ └──────────────┘
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="sqlite-message-hub-구조"&gt;SQLite Message Hub 구조
&lt;/h2&gt;&lt;h3 id="메시지-테이블"&gt;메시지 테이블
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sql" data-lang="sql"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;CREATE&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;TABLE&lt;/span&gt; messages (
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; id INTEGER &lt;span style="color:#66d9ef"&gt;PRIMARY&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;KEY&lt;/span&gt; AUTOINCREMENT,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; message_id TEXT &lt;span style="color:#66d9ef"&gt;UNIQUE&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;NOT&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;NULL&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; sender TEXT &lt;span style="color:#66d9ef"&gt;NOT&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;NULL&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; recipient TEXT, &lt;span style="color:#75715e"&gt;-- NULL = broadcast
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; message_type TEXT &lt;span style="color:#66d9ef"&gt;DEFAULT&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;message&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; content TEXT &lt;span style="color:#66d9ef"&gt;NOT&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;NULL&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; priority TEXT &lt;span style="color:#66d9ef"&gt;DEFAULT&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;normal&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; metadata TEXT &lt;span style="color:#66d9ef"&gt;DEFAULT&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;{}&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; status TEXT &lt;span style="color:#66d9ef"&gt;DEFAULT&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;pending&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; created_at &lt;span style="color:#66d9ef"&gt;TIMESTAMP&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;DEFAULT&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;CURRENT_TIMESTAMP&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; processed_at &lt;span style="color:#66d9ef"&gt;TIMESTAMP&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; expires_at &lt;span style="color:#66d9ef"&gt;TIMESTAMP&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="에이전트-상태-테이블"&gt;에이전트 상태 테이블
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sql" data-lang="sql"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;CREATE&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;TABLE&lt;/span&gt; agent_status (
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; agent_name TEXT &lt;span style="color:#66d9ef"&gt;PRIMARY&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;KEY&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; status TEXT &lt;span style="color:#66d9ef"&gt;DEFAULT&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;offline&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; last_heartbeat &lt;span style="color:#66d9ef"&gt;TIMESTAMP&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; metadata TEXT &lt;span style="color:#66d9ef"&gt;DEFAULT&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;{}&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="통신-방식"&gt;통신 방식
&lt;/h2&gt;&lt;h3 id="llm-agent--llm-agent"&gt;LLM Agent ↔ LLM Agent
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;SendMessage&lt;/strong&gt; 사용 (Claude Code 네이티브)&lt;/li&gt;
&lt;li&gt;자동 관리, idle notification 제공&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="python--llm-agent"&gt;Python ↔ LLM Agent
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;SQLite Hub&lt;/strong&gt; + &lt;strong&gt;MCP Tool&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Python은 직접 SQLite 접근&lt;/li&gt;
&lt;li&gt;LLM Agent는 MCP Tool로 접근&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="mcp-server-구현"&gt;MCP Server 구현
&lt;/h2&gt;&lt;p&gt;LLM Agent를 위한 MCP 도구들:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@server.list_tools&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;async&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;list_tools&lt;/span&gt;():
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; [
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Tool(name&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;team_send_message&amp;#34;&lt;/span&gt;, &lt;span style="color:#f92672"&gt;...&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Tool(name&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;team_read_messages&amp;#34;&lt;/span&gt;, &lt;span style="color:#f92672"&gt;...&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Tool(name&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;team_broadcast&amp;#34;&lt;/span&gt;, &lt;span style="color:#f92672"&gt;...&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Tool(name&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;team_get_status&amp;#34;&lt;/span&gt;, &lt;span style="color:#f92672"&gt;...&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Tool(name&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;team_update_status&amp;#34;&lt;/span&gt;, &lt;span style="color:#f92672"&gt;...&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ]
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="메시지-플로우-예시"&gt;메시지 플로우 예시
&lt;/h2&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;1. Discord 사용자: &amp;#34;안녕&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ↓
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;2. Discord Gateway → SQLite: INSERT message
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ↓
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;3. user_comm Daemon → SQLite: SELECT pending
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ↓
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;4. user_comm → SQLite: INSERT response
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ↓
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;5. Discord Gateway → SQLite: SELECT response
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ↓
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;6. Discord 채널: &amp;#34;안녕하세요! 👋&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="성능-비교"&gt;성능 비교
&lt;/h2&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;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;~1000ms&lt;/td&gt;
					&lt;td&gt;보통&lt;/td&gt;
					&lt;td&gt;낮음&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;WebSocket&lt;/td&gt;
					&lt;td&gt;~5ms&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;strong&gt;SQLite&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;~10ms&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;h2 id="결론"&gt;결론
&lt;/h2&gt;&lt;p&gt;SQLite Message Hub + MCP Server 조합이 Claude Code 팀 에이전트 통신에 가장 적합했습니다:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;낮은 지연&lt;/strong&gt;: 파일 기반보다 100배 빠름&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;안정성&lt;/strong&gt;: 트랜잭션으로 데이터 무결성 보장&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;단순함&lt;/strong&gt;: 추가 의존성 없이 Python 내장 sqlite3 사용&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;유연성&lt;/strong&gt;: Python과 LLM Agent 모두 동일한 데이터 소스 사용&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2 id="코드"&gt;코드
&lt;/h2&gt;&lt;p&gt;핵심 컴포넌트:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;message_hub.py&lt;/code&gt;: SQLite Message Hub&lt;/li&gt;
&lt;li&gt;&lt;code&gt;team_comm_mcp_server.py&lt;/code&gt;: MCP Server&lt;/li&gt;
&lt;li&gt;&lt;code&gt;discord_gateway.py&lt;/code&gt;: Discord 연동 게이트웨이&lt;/li&gt;
&lt;li&gt;&lt;code&gt;user_comm_daemon.py&lt;/code&gt;: 메시지 처리 데몬&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;strong&gt;영어 버전:&lt;/strong&gt; &lt;a class="link" href="https://blog.agentthread.dev/post/2026-02-28-002-claude-code-team-agent-communication-architecture/" &gt;English Version&lt;/a&gt;&lt;/p&gt;</description></item></channel></rss>