<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" xml:lang="ko"><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://goodongwoo.github.io/feed.xml" rel="self" type="application/atom+xml" /><link href="https://goodongwoo.github.io/" rel="alternate" type="text/html" hreflang="ko" /><updated>2026-10-01T16:24:25+00:00</updated><id>https://goodongwoo.github.io/feed.xml</id><title type="html">GooDongWoo</title><subtitle>개발자 GooDongWoo의 포트폴리오 &amp; 기술 블로그</subtitle><author><name>GooDongWoo</name><email>wendy1301@naver.com</email></author><entry><title type="html">일은 안 하고 잔머리만 굴리는데 대단한 녀석: AI 에이전트 ‘포니테일(Ponytail)’ 심층 해부</title><link href="https://goodongwoo.github.io/tech/ai/2026/10/01/dietrichgebertponytail-%EA%B2%8C%EC%9C%BC%EB%A5%B8-%EC%8B%9C%EB%8B%88%EC%96%B4-%EA%B0%9C%EB%B0%9C%EC%9E%90%EC%B2%98%EB%9F%BC-%EC%83%9D%EA%B0%81%ED%95%98%EB%8A%94-ai-%EC%97%90%EC%9D%B4%EC%A0%84%ED%8A%B8/" rel="alternate" type="text/html" title="일은 안 하고 잔머리만 굴리는데 대단한 녀석: AI 에이전트 ‘포니테일(Ponytail)’ 심층 해부" /><published>2026-10-01T16:06:39+00:00</published><updated>2026-10-01T16:06:39+00:00</updated><id>https://goodongwoo.github.io/tech/ai/2026/10/01/dietrichgebertponytail-%EA%B2%8C%EC%9C%BC%EB%A5%B8-%EC%8B%9C%EB%8B%88%EC%96%B4-%EA%B0%9C%EB%B0%9C%EC%9E%90%EC%B2%98%EB%9F%BC-%EC%83%9D%EA%B0%81%ED%95%98%EB%8A%94-ai-%EC%97%90%EC%9D%B4%EC%A0%84%ED%8A%B8</id><content type="html" xml:base="https://goodongwoo.github.io/tech/ai/2026/10/01/dietrichgebertponytail-%EA%B2%8C%EC%9C%BC%EB%A5%B8-%EC%8B%9C%EB%8B%88%EC%96%B4-%EA%B0%9C%EB%B0%9C%EC%9E%90%EC%B2%98%EB%9F%BC-%EC%83%9D%EA%B0%81%ED%95%98%EB%8A%94-ai-%EC%97%90%EC%9D%B4%EC%A0%84%ED%8A%B8/"><![CDATA[<p>또 새로운 도구가 나왔다. 솔직히 처음엔 ‘또 뭔 신기술이야, 챗gpt 껍데기 하나 또 바꿨겠지’ 싶었다. 깃허브 트렌딩을 무심코 내리다가 <code class="language-plaintext highlighter-rouge">DietrichGebert/ponytail</code>이라는 레포를 발견하기 전까지는 말이다.</p>

<p>슬로건을 보는 순간 심장이 쿵 내려앉았다.</p>
<blockquote>
  <p><em>“He says nothing. He writes one line. It works.”</em>
(말은 없지. 한 줄만 쓰지. 근데 작동하지.)</p>
</blockquote>

<p>이거 완전 우리 팀에서 제일 일 안 하는 것 같은데 핵심만 쏙쏙 골라 처리하고 칼퇴하는 그 전설의 10년 차 시니어 사내 모습이잖아? AI가 이제는 인간의 ‘성실함’을 모방하는 걸 넘어, ‘전략적인 게으름’까지 학습하기 시작했다는 뜻이다. 도대체 이 녀석이 뭔지 파헤쳐 보자.</p>

<hr />

<h3 id="1-왜-ai는-항상-굳이-없어도-될-코드를-오백-줄씩-짜는가">1. 왜 AI는 항상 굳이 없어도 될 코드를 오백 줄씩 짜는가?</h3>

<p>우리가 평소에 쓰는 AI 코딩 에이전트(Claude Code라든가 Cursor라든가)를 생각해 보자. 뭐 하나 고쳐달라고 하면 자상하기가 아주 조상님 수준이다.</p>

<p>“사용자님의 요구사항을 반영하여, 예외 처리와 확장성을 고려한 450줄짜리 팩토리 패턴 구조를 생성했습니다! 테스트 코드도 3개 추가했고요!”</p>

<p><img src="/assets/images/memes/this-is-fine.gif" alt="기술 스택을 새로 도입하기 전과 도입한 후의 모습" />
<em>▲ 기술 스택을 새로 도입하기 전과 도입한 후의 모습</em></p>

<blockquote>
  <p><em>개발자: “아니 버튼 색깔 하나 바꾸랬잖아. 왜 컴포넌트를 싹 다 갈아엎는데?”</em></p>
</blockquote>

<p>AI는 본질적으로 ‘열정 과다’ 상태다. 프롬프트에 아무 말 안 하면 자기가 가진 토큰을 풀로 활용해서 세상 화려하고 복잡한 엔터프라이즈급(사실은 오버엔지니어링의 극치인) 코드를 뱉어낸다. 그리고 우리는 그 코드를 보며 한숨을 쉬고, <code class="language-plaintext highlighter-rouge">git checkout .</code>을 누른 뒤 “한 줄만 고쳐달라고!”를 외치며 다시 프롬프트를 치곤 한다.</p>

<p><code class="language-plaintext highlighter-rouge">ponytail</code>은 정확히 이 지점을 저격한다. AI에게 “성실한 모범생” 대신 <strong>“할 일만 하고 짱박히는 노련한 시니어”</strong>의 페르소나를 장착시키는 것이다.</p>

<hr />

<h3 id="2-포니테일의-아키텍처-어떻게-게으름을-시스템화했는가">2. 포니테일의 아키텍처: 어떻게 게으름을 시스템화했는가?</h3>

<p>이 녀석이 단순한 농담조의 프롬프트 쪼가리라고 생각하면 오산이다. 공식 벤치마크 데이터를 보면 숫자가 꽤 진지하다.</p>

<ul>
  <li><strong>코드 양</strong>: 평균 <strong>54% 감소</strong> (최대 94%까지 줄어듦. 예를 들어 달력 컴포넌트 같은 걸 구현할 때 오버빌딩하는 걸 칼같이 차단함)</li>
  <li><strong>비용</strong>: 약 <strong>20% 절감</strong> (토큰을 적게 쓰니까 당연함)</li>
  <li><strong>속도</strong>: 약 <strong>27% 향상</strong></li>
  <li><strong>안전성</strong>: 보안 가드레일은 100% 유지 (“한 줄만 써라”고 했다가 보안 취약점까지 대충 짜는 대참사는 막음)</li>
</ul>

<p>이게 가능한 이유는 에이전트 레이어에서 불필요한 사족을 제거하고, “최소한의 변경으로 목적을 달성하라(Minimal Viable Change)”는 철학을 시스템 프롬프트와 컨텍스트 제어로 강제하기 때문이다.</p>

<p>구조를 대략 그려보면 이렇다.</p>

<pre><code class="language-mermaid">graph TD
    A[유저 요구사항: 버튼 색 바꿔줘] --&gt; B{기존 AI 에이전트}
    A --&gt; C{Ponytail 에이전트}
    
    B --&gt; B1[오버엔지니어링 발동]
    B1 --&gt; B2[450줄 리팩토링 및 팩토리 패턴 도입]
    B2 --&gt; B3[토큰 폭발 및 야근 확정]

    C --&gt; C1[게으른 시니어 모드 활성화]
    C1 --&gt; C2[CSS 클래스명 하나 틱 변경]
    C2 --&gt; C3[1초 만에 퇴근]
</code></pre>

<p>원리는 단순하지만 개발 현장에서 피부로 와닿는 임팩트는 어마어마하다. 복잡하게 꼬인 레거시 코드베이스에서 AI가 파일을 20개씩 건드려가며 버그를 양산하는 꼴을, 이 녀석은 원천 차단한다. 건드릴 필요 없는 코드는 아예 쳐다보지도 않는다. 진정한 ‘고수의 무심함’이다.</p>

<hr />

<h3 id="3-실무에서-써본-느낌과-팩폭">3. 실무에서 써본 느낌과 팩폭</h3>

<p>이 툴을 Claude Code 세션에 연동해서 FastAPI + React 프로젝트에 굴려봤다.</p>

<p>확실히 다르다. 평소 같으면 날새며 수정사항을 반영하느라 토큰이 쫙쫙 닳고 제3자의 눈으로 봤을 때 “이게 왜 들어가 있지?” 싶은 템플릿 코드들이 생기기 마련인데, ponytail을 적용한 에이전트는 딱 diff가 깔끔하게 떨어진다.</p>

<p><img src="/assets/images/memes/works-on-my-machine.gif" alt="분명 로컬에선 잘 돌아갔는데...?" />
<em>▲ 분명 로컬에선 잘 돌아갔는데…?</em></p>

<blockquote>
  <p><em>PR 리뷰어: “이번 PR 왜 이렇게 깔끔함? 너 오늘 컨디션 좋냐?”</em>
<em>나: “아니, AI가 일 안 하려고 잔머리 좀 굴렸어.”</em></p>
</blockquote>

<p>물론 단점이 아예 없는 건 아니다. 가끔은 너무 요약해서 짜는 바람에 “잠깐, 여기 예외 처리는 안 넣냐?” 하고 인간 시니어가 다시 코드를 들여다봐야 할 때가 있다. 하지만 생각해 보자. AI가 싸놓은 똥을 치우는 것보다, 차라리 최소한의 코드를 보고 필요한 부분만 덧붙이는 게 정신 건강에 백배 이롭다.</p>

<hr />

<h3 id="결론">결론</h3>

<p>오픈소스 세상은 참 재미있다. 기술이 발전할수록 우리는 더 똑똑하고, 더 거대하고, 더 복잡한 에이전트를 만들려고 혈안이 되어 있었는데, 문득 어떤 개발자가 <strong>“AI한테 그냥 일하기 싫어하는 시니어 영혼을 불어넣으면 어떨까?”</strong>라는 미친 생각을 해냈고, 그것이 대박이 났다.</p>

<p>그래서 내 프로젝트에 쓸 거냐고? 솔직히 말하면, 이미 내 로컬 환경에 깔아뒀다. 팀원들 몰래 나만 써서 칼퇴하는 비밀 무기로 삼기에 이보다 더 완벽할 순 없다.</p>

<p><strong>한 줄 총평:</strong>
말은 없지만, 잔머리 하나는 예술이라 결국 코드를 살리는 최고의 게으름뱅이 비서.</p>]]></content><author><name>GooDongWoo</name></author><category term="Tech" /><category term="AI" /><category term="트렌드" /><category term="개발" /><category term="오픈소스" /><summary type="html"><![CDATA[또 새로운 도구가 나왔다. 솔직히 처음엔 ‘또 뭔 신기술이야, 챗gpt 껍데기 하나 또 바꿨겠지’ 싶었다. 깃허브 트렌딩을 무심코 내리다가 DietrichGebert/ponytail이라는 레포를 발견하기 전까지는 말이다.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://goodongwoo.github.io/avatar.jpeg" /><media:content medium="image" url="https://goodongwoo.github.io/avatar.jpeg" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">내 AI 에이전트가 내 카드로 긁고 다닌다면? OpenID Foundation의 ‘에이전트 신분증’ 백서 톺아보기</title><link href="https://goodongwoo.github.io/tech/ai/2026/10/01/openid-foundation-%EC%97%90%EC%9D%B4%EC%A0%84%ED%8A%B8-ai%EB%A5%BC-%EC%9C%84%ED%95%9C-%EC%8B%A0%EC%9B%90-%EA%B4%80%EB%A6%AC-identity-manag/" rel="alternate" type="text/html" title="내 AI 에이전트가 내 카드로 긁고 다닌다면? OpenID Foundation의 ‘에이전트 신분증’ 백서 톺아보기" /><published>2026-10-01T16:06:13+00:00</published><updated>2026-10-01T16:06:13+00:00</updated><id>https://goodongwoo.github.io/tech/ai/2026/10/01/openid-foundation-%EC%97%90%EC%9D%B4%EC%A0%84%ED%8A%B8-ai%EB%A5%BC-%EC%9C%84%ED%95%9C-%EC%8B%A0%EC%9B%90-%EA%B4%80%EB%A6%AC-identity-manag</id><content type="html" xml:base="https://goodongwoo.github.io/tech/ai/2026/10/01/openid-foundation-%EC%97%90%EC%9D%B4%EC%A0%84%ED%8A%B8-ai%EB%A5%BC-%EC%9C%84%ED%95%9C-%EC%8B%A0%EC%9B%90-%EA%B4%80%EB%A6%AC-identity-manag/"><![CDATA[<p>AI 에이전트가 유행이란다. 이제는 단순히 “오늘 날씨 어때?”라고 묻는 수준을 넘어서, “내 다음 여행 계획 짜고 비행기랑 호텔까지 다 결제해 줘”라고 시키는 시대다. 그런데 여기서 잠깐, 소름 돋는 생각 하나 안 드나?</p>

<p>이 AI 녀석이 도대체 ‘나’라는 걸 어떻게 증명하고 내 지갑을 연다는 걸까? 내가 준 API 키 하나로 온 사방을 들쑤시고 다니게 내버려 둬도 괜찮은 걸까?</p>

<p>최근 OpenID Foundation에서 발표한 <strong>[Identity Management for Agentic AI]</strong> 백서를 읽다가 무릎을 탁 쳤다. 아니, 사실은 뒷목을 좀 잡았다. 우리가 그동안 OAuth 2.0이니 OIDC니 하면서 쌓아 올린 신원 인증의 탑을 에이전트라는 녀석들이 아주 가볍게 무너뜨리려 하고 있거든.</p>

<h2 id="1-지금-우리-방식이-왜-문제냐고">1. 지금 우리 방식이 왜 문제냐고?</h2>

<p>지금까지의 인증(Authentication)은 철저히 ‘인간’ 중심이었다. 브라우저에 팝업이 뜨면 우리가 아이디, 비번을 치거나 지문을 찍었다. 하지만 자율형 에이전트(Agentic AI)는 다르다. 얘네는 우리가 잘 때도 돌아가고, 지들끼리 통신하며 의사결정을 내린다.</p>

<p>기존 방식대로 에이전트한테 내 <code class="language-plaintext highlighter-rouge">Access Token</code>을 통째로 넘겨주는 건, 마치 낯선 심부름꾼한테 내 집 도어락 번호랑 인감도장을 통째로 맡기는 거나 다름없다. 에이전트가 해킹당하거나, 코드가 꼬여서 내 클라우드 인프라를 다 날려 먹으면? 책임은 고스란히 내 몫이다.</p>

<p><img src="/assets/images/memes/works-on-my-machine.gif" alt="분명 로컬에선 잘 돌아갔는데...?" />
<em>▲ 분명 로컬에선 잘 돌아갔는데…?</em></p>

<p><em>(설명: “Just give it the Admin API Key”라고 말하는 개발자와 경악하는 보안 담당자 짤)</em></p>

<h2 id="2-openid-foundation이-제시하는-해결책-에이전트에게-신분증을">2. OpenID Foundation이 제시하는 해결책: 에이전트에게 ‘신분증’을</h2>

<p>백서의 핵심은 간단하다. <strong>“에이전트 자체도 하나의 신원(Identity)으로 정의하자”</strong>는 거다. 단순히 사용자의 권한을 대리 수행하는 ‘도구’가 아니라, 독립적인 주체로서 인증을 받아야 한다는 뜻이다.</p>

<p>이들이 제안하는 구조를 대략적으로 그려보면 이렇다.</p>

<pre><code class="language-mermaid">sequenceDiagram
    participant User as 사용자 (Subject)
    participant Agent as AI 에이전트 (Relying Party)
    participant IdP as 신원 제공자 (Issuer)
    participant RS as 리소스 서버 (API)

    User-&gt;&gt;Agent: 작업 위임 (Task Delegation)
    Agent-&gt;&gt;IdP: 에이전트 인증 및 토큰 요청
    IdP--&gt;&gt;Agent: Agent-Bound Access Token 발급
    Agent-&gt;&gt;RS: 요청 + 토큰 전송
    RS-&gt;&gt;IdP: 토큰 및 정책(Policy) 검증
    RS--&gt;&gt;Agent: 데이터 제공/작업 수행
</code></pre>

<p>여기서 중요한 포인트는 <strong>‘Dynamic Client Registration’</strong>과 <strong>‘Short-lived Tokens’</strong>다.</p>

<ol>
  <li><strong>동적 등록</strong>: 에이전트가 생성될 때마다 실시간으로 신분증을 발급받는다.</li>
  <li><strong>용도 제한</strong>: “이 토큰은 30분 동안 ‘항공권 예약’에만 쓸 수 있음” 같은 아주 좁은 범위(Scope)의 권한만 부여한다.</li>
  <li><strong>검증 가능성</strong>: 이 에이전트가 어떤 LLM을 쓰는지, 어떤 보안 수준을 갖췄는지 증명서(Attestation)를 포함한다.</li>
</ol>

<h2 id="3-기술적으로-깊게-들어가면-나만-쓸-수-있는-토큰">3. 기술적으로 깊게 들어가면: “나만 쓸 수 있는 토큰”</h2>

<p>백서에서 강조하는 기술 중 하나가 <strong>DPoP (Demonstrating Proof-of-Possession)</strong>다. 기존 Bearer 토큰은 훔치기만 하면 누구나 쓸 수 있었지만, DPoP을 쓰면 토큰과 특정 개인키를 묶어버린다. 에이전트가 토큰을 탈취당해도, 그 에이전트의 ‘열쇠’가 없으면 쓸모없는 쓰레기 데이터가 된다는 소리다.</p>

<p>또한, <strong>‘Federated Identity for Agents’</strong> 개념도 흥미롭다. 예를 들어, 내가 ‘A 서비스’에서 만든 에이전트가 ‘B 서비스’의 데이터를 가져올 때, A와 B가 서로 신뢰할 수 있는 표준 프로토콜(OIDC 확장판 등)을 통해 에이전트의 신원을 보증해 주는 방식이다.</p>

<h2 id="4-그래서-이게-왜-중요한데">4. 그래서 이게 왜 중요한데?</h2>

<p>솔직히 말해보자. 우리 개발자들, 귀찮으면 <code class="language-plaintext highlighter-rouge">chmod 777</code> 때리고 <code class="language-plaintext highlighter-rouge">sudo</code> 남발하잖아? AI 에이전트 개발할 때도 똑같은 짓을 할 가능성이 99%다.</p>

<p>하지만 에이전트가 ‘자율성’을 갖는 순간, 보안 사고의 스케일이 달라진다. 내가 시키지도 않은 람보르기니를 에이전트가 결제했다고 생각해보라고(물론 그럴 잔고는 없겠지만).</p>

<p>OpenID Foundation의 이 백서는 단순히 기술 표준을 만들자는 게 아니라, <strong>“AI에게 어디까지 권한을 줄 것인가?”</strong>에 대한 거버넌스를 코드로 구현하자는 선언이다.</p>

<p><img src="/assets/images/memes/hotfix-in-production.gif" alt="릴리즈 5분 전 긴급 핫픽스 상황" />
<em>▲ 릴리즈 5분 전 긴급 핫픽스 상황</em></p>

<p><em>(설명: 복잡한 OAuth 흐름도를 보며 “I just wanted to automate a spreadsheet”라고 울먹이는 개발자 짤)</em></p>

<h2 id="마무리-내-프로젝트에-쓸-거냐고">마무리: 내 프로젝트에 쓸 거냐고?</h2>

<p>솔직히 지금 당장 실무에 적용하기엔 아직 프로토콜이 구체화되어야 할 부분이 많다. 하지만 조만간 엔터프라이즈 급 AI 솔루션을 만든다면 이 가이드라인은 선택이 아닌 필수(Must-have)가 될 거다.</p>

<p><strong>한 줄 총평:</strong>
에이전트한테 내 계정 비번 알려주던 ‘야만적 시대’는 끝났다. 이제 AI도 주민등록증 들고 다녀야 하는 시대다.</p>

<p>혹시 지금 에이전트 만들면서 <code class="language-plaintext highlighter-rouge">OPENAI_API_KEY</code>랑 서비스 계정 키를 하드코딩하거나 통째로 넘겨주고 있다면? 축하한다. 당신은 미래의 보안 사고 주인공 후보 1순위다. 얼른 이 백서 한 번 쓱 훑어보길 바란다.</p>

<hr />
<p><em>참고: 더 자세한 내용이 궁금하면 <a href="https://openid.net/wp-content/uploads/2025/10/Identity-Management-for-Agentic-AI.pdf">OpenID Foundation 백서 원문</a>을 직접 파보자. 영어지만 개발자라면 그림만 봐도 대충 감이 올 거다.</em></p>]]></content><author><name>GooDongWoo</name></author><category term="Tech" /><category term="AI" /><category term="트렌드" /><category term="개발" /><category term="오픈소스" /><summary type="html"><![CDATA[AI 에이전트가 유행이란다. 이제는 단순히 “오늘 날씨 어때?”라고 묻는 수준을 넘어서, “내 다음 여행 계획 짜고 비행기랑 호텔까지 다 결제해 줘”라고 시키는 시대다. 그런데 여기서 잠깐, 소름 돋는 생각 하나 안 드나?]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://goodongwoo.github.io/avatar.jpeg" /><media:content medium="image" url="https://goodongwoo.github.io/avatar.jpeg" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">실시간 통신? 제발 WebSocket부터 박지 마라 (Polling, SSE, WebSocket 생존 가이드)</title><link href="https://goodongwoo.github.io/tech/ai/2026/10/01/%EB%B8%8C%EB%9D%BC%EC%9A%B0%EC%A0%80-%EC%8B%A4%EC%8B%9C%EA%B0%84-%ED%86%B5%EC%8B%A0-%EC%84%A4%EA%B3%84-%ED%8F%B4%EB%A7%81ssewebsocket%EA%B3%BC-%EC%83%81%ED%83%9C-%EB%B3%B5%EA%B5%AC/" rel="alternate" type="text/html" title="실시간 통신? 제발 WebSocket부터 박지 마라 (Polling, SSE, WebSocket 생존 가이드)" /><published>2026-10-01T16:05:39+00:00</published><updated>2026-10-01T16:05:39+00:00</updated><id>https://goodongwoo.github.io/tech/ai/2026/10/01/%EB%B8%8C%EB%9D%BC%EC%9A%B0%EC%A0%80-%EC%8B%A4%EC%8B%9C%EA%B0%84-%ED%86%B5%EC%8B%A0-%EC%84%A4%EA%B3%84-%ED%8F%B4%EB%A7%81ssewebsocket%EA%B3%BC-%EC%83%81%ED%83%9C-%EB%B3%B5%EA%B5%AC</id><content type="html" xml:base="https://goodongwoo.github.io/tech/ai/2026/10/01/%EB%B8%8C%EB%9D%BC%EC%9A%B0%EC%A0%80-%EC%8B%A4%EC%8B%9C%EA%B0%84-%ED%86%B5%EC%8B%A0-%EC%84%A4%EA%B3%84-%ED%8F%B4%EB%A7%81ssewebsocket%EA%B3%BC-%EC%83%81%ED%83%9C-%EB%B3%B5%EA%B5%AC/"><![CDATA[<p>어제 신입 개발자가 올린 PR을 보다가 뒷목을 잡았다. 단순한 파일 업로드 완료 알림 기능을 만드는데 <code class="language-plaintext highlighter-rouge">Socket.io</code> 라이브러리부터 임포트하고 있더라고. “왜 소켓을 썼어?”라고 물으니 돌아온 대답이 가관이다. “실시간이니까요.”</p>

<p>이봐, 친구. 실시간이라는 단어에 낚여서 무조건 소켓부터 박는 건, 파리 잡겠다고 에프킬라 대신 K2 소총을 들고 오는 격이다. 오늘은 우리가 ‘실시간’이라는 달콤한 단어 뒤에 숨겨진 통신 방식들을 어떻게 골라 써야 하는지, 시니어의 짬바를 담아 탈탈 털어보겠다.</p>

<h3 id="실시간-얼마나-빨라야-하는데">실시간? ‘얼마나’ 빨라야 하는데?</h3>

<p>공학에서 말하는 <strong>실시간(Real-time)</strong>은 단순히 “존나 빠름”을 의미하지 않는다. 핵심은 <strong>마감 시간(Deadline)</strong> 안에 결과가 도착하느냐다.</p>

<ul>
  <li><strong>Hard Real-time</strong>: 1ms라도 늦으면 시스템이 터지는 상황 (자율주행, 미사일 제어). 브라우저에선 거의 볼 일 없다.</li>
  <li><strong>Soft Real-time</strong>: 좀 늦으면 사용자 기분은 잡치겠지만, 시스템은 돌아가는 상황 (채팅, 알림). 우리가 다루는 대부분이 이거다.</li>
</ul>

<p>여기서 중요한 건 ‘지연 시간(Latency)’과 ‘최신성(Freshness)’의 구분이다. 0.1초 만에 데이터가 왔어도 그게 1분 전 데이터면 아무 소용 없잖아?</p>

<p><img src="/assets/images/memes/rage-computer-throw.gif" alt="코드가 왜 돌아가는지 아무도 모를 때" />
<em>▲ 코드가 왜 돌아가는지 아무도 모를 때</em></p>

<p><em>(짤 설명: “Why are you using WebSockets for a static notification?” 템플릿에 당황하는 개발자 짤)</em></p>

<hr />

<h3 id="1-폴링polling--롱-폴링long-polling---갔어-아니-갔어-아니">1. 폴링(Polling) &amp; 롱 폴링(Long Polling) - “갔어? 아니? 갔어? 아니?”</h3>

<p>가장 원시적이지만, 의외로 생명력이 끈질기다.</p>

<ul>
  <li><strong>Polling</strong>: 5초마다 서버에 “야, 뭐 바뀐 거 있냐?”라고 묻는 거다. 서버가 “없어”라고 해도 5초 뒤에 또 묻는다. HTTP 오버헤드가 크고 리소스 낭비가 심하다.</li>
  <li><strong>Long Polling</strong>: 질문을 던지고 서버가 “잠깐만, 바뀔 때까지 기다려”라며 연결을 잡고 있는다. 뭔가 바뀌면 그때 응답을 주고 연결을 끊는다.</li>
</ul>

<p><strong>언제 쓰나?</strong> 
상태 변경이 아주 가끔 일어나고, 인프라 세팅하기 귀찮을 때. 혹은 구닥다리 브라우저까지 지원해야 하는 눈물 나는 상황일 때 쓴다.</p>

<hr />

<h3 id="2-sse-server-sent-events---나는-말할-테니-너는-듣기만-해">2. SSE (Server-Sent Events) - “나는 말할 테니 너는 듣기만 해”</h3>

<p>내 최애 기술 중 하나다. 서버에서 클라이언트로 단방향 스트리밍을 쏘는 방식이다.</p>

<ul>
  <li><strong>특징</strong>: HTTP 프로토콜을 그대로 쓴다. 재연결(Reconnection) 로직이 브라우저에 내장되어 있다. 가볍다.</li>
  <li><strong>치명적 단점</strong>: 단방향이다. 클라이언트가 서버에 뭘 보내려면 별도의 HTTP 요청을 날려야 한다.</li>
</ul>

<pre><code class="language-mermaid">sequenceDiagram
    participant Client
    participant Server
    Client-&gt;&gt;Server: GET /stream (Accept: text/event-stream)
    Note right of Server: 연결 유지
    Server--&gt;&gt;Client: data: { "status": "processing" }
    Server--&gt;&gt;Client: data: { "status": "completed" }
    Note over Client, Server: 연결 끊기면 브라우저가 자동 재접속 시도
</code></pre>

<hr />

<h3 id="3-websocket---풀-듀플렉스-양방향-아우토반">3. WebSocket - “풀-듀플렉스 양방향 아우토반”</h3>

<p>모두가 사랑하지만, 관리하기는 지랄맞은 녀석이다.</p>

<ul>
  <li><strong>특징</strong>: 한 번 연결되면(Handshake) 양방향으로 데이터를 막 던질 수 있다. HTTP가 아니라 Binary 프로토콜이라 헤더도 가볍다.</li>
  <li><strong>현실</strong>: 로드밸런서 설정(Sticky Session), 좀비 커넥션 관리, 하트비트(Heartbeat) 체크 등 신경 쓸 게 한두 개가 아니다. 서버 리소스를 꽤나 잡아먹는다.</li>
</ul>

<hr />

<h3 id="연결-끊기면-상태-복구의-미학">연결 끊기면? 상태 복구의 미학</h3>

<p>실시간 통신에서 가장 간과하는 게 <strong>“재연결 시 누락된 데이터”</strong>다. 터널 통과하다가 끊겼을 때, 그 사이 발생한 이벤트를 어떻게 채울 건가?</p>

<ol>
  <li><strong>Sequence ID (Last-Event-ID)</strong>: SSE는 이걸 기본으로 지원한다. “나 100번까지 받았어!”라고 말하면 서버가 101번부터 쏴주는 식이다.</li>
  <li><strong>Snapshot + Delta</strong>: 재연결되면 일단 현재 전체 상태(Snapshot)를 한 번 긁어오고, 그다음부터 바뀐 것(Delta)만 다시 받는다.</li>
</ol>

<p><img src="/assets/images/memes/rage-computer-throw.gif" alt="코드가 왜 돌아가는지 아무도 모를 때" />
<em>▲ 코드가 왜 돌아가는지 아무도 모를 때</em></p>

<p><em>(짤 설명: “Reconnecting…” 무한 루프 돌면서 눈물 흘리는 개발자)</em></p>

<h3 id="그래서-뭘-써야-하냐고-시니어의-한-줄-가이드">그래서 뭘 써야 하냐고? (시니어의 한 줄 가이드)</h3>

<p>솔직히 말해서, 90%의 서비스는 WebSocket이 필요 없다. 내 기준은 이렇다.</p>

<ol>
  <li><strong>채팅이나 실시간 협업 툴(피그마 같은 거) 만드나?</strong> -&gt; <strong>WebSocket</strong></li>
  <li><strong>주식 차트나 대시보드, 알림 기능인가?</strong> -&gt; <strong>SSE</strong> 가 답이다. 훨씬 싸고 안정적이다.</li>
  <li><strong>그냥 ‘작업 완료’ 알림 하나 띄우는 건가?</strong> -&gt; <strong>Polling</strong>으로도 충분하다. 유저가 5초 늦게 안다고 세상 안 망한다.</li>
</ol>

<p><strong>한 줄 총평:</strong>
신기술이나 ‘간지’나는 기술에 매몰되지 마라. 가장 좋은 아키텍처는 목적에 맞는 최소한의 기술로 최대의 안정성을 뽑아내는 거다. 소켓 서버 터져서 새벽에 전화 받기 싫으면 SSE부터 검토해라. 알겠냐?</p>]]></content><author><name>GooDongWoo</name></author><category term="Tech" /><category term="AI" /><category term="트렌드" /><category term="개발" /><category term="오픈소스" /><summary type="html"><![CDATA[어제 신입 개발자가 올린 PR을 보다가 뒷목을 잡았다. 단순한 파일 업로드 완료 알림 기능을 만드는데 Socket.io 라이브러리부터 임포트하고 있더라고. “왜 소켓을 썼어?”라고 물으니 돌아온 대답이 가관이다. “실시간이니까요.”]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://goodongwoo.github.io/avatar.jpeg" /><media:content medium="image" url="https://goodongwoo.github.io/avatar.jpeg" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">벡터 DB 따위 개나 줘버려? PageIndex가 던진 RAG의 도발</title><link href="https://goodongwoo.github.io/tech/ai/2026/10/01/pageindex-vectorless-reasoning-based-rag-%EB%AC%B8%EC%84%9C-%EC%9D%B8%EB%8D%B1%EC%8A%A4/" rel="alternate" type="text/html" title="벡터 DB 따위 개나 줘버려? PageIndex가 던진 RAG의 도발" /><published>2026-10-01T16:05:17+00:00</published><updated>2026-10-01T16:05:17+00:00</updated><id>https://goodongwoo.github.io/tech/ai/2026/10/01/pageindex-vectorless-reasoning-based-rag-%EB%AC%B8%EC%84%9C-%EC%9D%B8%EB%8D%B1%EC%8A%A4</id><content type="html" xml:base="https://goodongwoo.github.io/tech/ai/2026/10/01/pageindex-vectorless-reasoning-based-rag-%EB%AC%B8%EC%84%9C-%EC%9D%B8%EB%8D%B1%EC%8A%A4/"><![CDATA[<p>어제도 새벽까지 ‘청크 사이즈를 512로 할까 1024로 할까’ 고민하다가 현타가 왔다. 임베딩 모델 바꾸고, 오버랩 구간 조정하고… 솔직히 말해보자. 우리 다들 RAG(Retrieval-Augmented Generation) 성능 안 나와서 삽질하는 거, 일종의 ‘기우제’ 지내는 마음 아니었나? “제발 유사도 높은 놈이 정답이길!” 하면서 말이다.</p>

<p>그런데 오늘 깃허브 트렌딩에서 아주 발칙한 놈을 발견했다. 이름은 <strong>PageIndex</strong>. 이 녀석의 슬로건이 아주 가관이다. <strong>“Vectorless(벡터 없음), No Chunking(청킹 안 함)”.</strong></p>

<p>아니, RAG에서 벡터랑 청킹을 빼면 뭐가 남지? 앙꼬 없는 찐빵 아닌가? 궁금해서 뜯어봤더니, 이건 찐빵이 아니라 아예 스테이크를 구워왔더라.</p>

<hr />

<h3 id="유사도similarity는-정답relevance이-아니다">유사도(Similarity)는 정답(Relevance)이 아니다</h3>

<p>우리가 그동안 믿어 의심치 않았던 벡터 기반 RAG의 치명적인 약점은 바로 ‘유사도’에만 집착한다는 거다. 예를 들어, “작년 대비 매출이 얼마나 늘었어?”라고 물으면, 기존 RAG는 ‘매출’, ‘증가’ 같은 단어가 많이 포함된 문단을 가져온다.</p>

<p>하지만 진짜 정답은 “매출은 10억인데, 작년엔 8억이었으니 25% 늘었다”는 식의 맥락 파악이 필요한 데이터일 확률이 높다. 벡터 검색은 이 맥락을 모른다. 그냥 숫자랑 단어가 비슷하면 냅다 던져줄 뿐이다.</p>

<p><img src="/assets/images/memes/mind-blown.gif" alt="메모리 누수 잡으려다가 OS까지 날려먹을 뻔한 개발자" />
<em>▲ 메모리 누수 잡으려다가 OS까지 날려먹을 뻔한 개발자</em></p>

<p><em>(짤 설명: “Why are you searching like this?”라고 묻는 상사와 “Because the vector says so”라고 답하며 울고 있는 개발자)</em></p>

<p>PageIndex는 여기서 발상의 전환을 한다. <strong>“인간은 문서를 벡터로 안 읽잖아? 목차 보고, 훑어보고, 필요한 부분을 찾아 읽지.”</strong></p>

<h3 id="pageindex의-핵심-alphago-스타일의-트리-검색">PageIndex의 핵심: AlphaGo 스타일의 트리 검색</h3>

<p>PageIndex는 문서를 벡터 공간에 뿌리는 대신, 문서 전체를 <strong>계층형 트리(Hierarchical Tree)</strong> 구조로 인덱싱한다. 그리고 여기에 LLM을 태워 보낸다.</p>

<p>원리는 단순하지만 강력하다.</p>
<ol>
  <li><strong>Index</strong>: 문서를 읽고 의미 단위로 계층 구조를 만든다. (PageIndex Flash라는 기술로 겁나 빠르게 만든단다.)</li>
  <li><strong>Retrieve</strong>: 질문이 들어오면 LLM 에이전트가 트리를 타고 내려간다. “이 질문은 3장 재무제표 쪽에 답이 있겠군? 3.2절로 가보자.” 하는 식이다.</li>
</ol>

<p>이 과정을 Mermaid 다이어그램으로 그려보면 대략 이렇다.</p>

<pre><code class="language-mermaid">graph TD
    A[사용자 질문] --&gt; B{LLM 리즈닝 에이전트}
    B --&gt; C[트리 인덱스 Root]
    C --&gt; D[섹션 1: 개요]
    C --&gt; E[섹션 2: 상세 분석]
    C --&gt; F[섹션 3: 결론]
    E --&gt; G[2.1 데이터]
    E --&gt; H[2.2 결과]
    B -- "2.2절에 답이 있겠는데?" --&gt; H
    H --&gt; I[최종 답변 생성]
</code></pre>

<p>이게 왜 대단하냐면, <strong>‘맥락(Context)’</strong>을 유지한 채로 정보를 찾는다는 거다. 벡터 RAG처럼 문장을 조각조각(Chunking) 내서 의미를 파편화하지 않는다.</p>

<hr />

<h3 id="벡터-db-없으면-진짜-편할까">벡터 DB 없으면 진짜 편할까?</h3>

<p>PageIndex의 기술 스택을 보면 개발자 입장에서 무릎을 탁 치게 되는 포인트가 몇 가지 있다.</p>

<ul>
  <li><strong>No Vector DB</strong>: 파인콘(Pinecone)이니 밀버스(Milvus)니 설정하고 관리할 필요가 없다. 인프라 복잡도가 수직 하락한다.</li>
  <li><strong>Reasoning-based</strong>: 단순히 ‘비슷한’ 문장이 아니라, 질문의 의도를 파악해서 ‘관련 있는’ 노드를 찾아간다.</li>
  <li><strong>Local &amp; Cloud</strong>: <code class="language-plaintext highlighter-rouge">pip install pageindex</code> 한 줄로 로컬에서도 돌릴 수 있고, 클라우드 API로 확장도 된다.</li>
</ul>

<p>물론 세상에 공짜는 없다. 모든 노드를 LLM이 판단하며 내려가야 하니, 단순 벡터 검색보다는 <strong>레이턴시(Latency)</strong>와 <strong>비용</strong>이 더 들 수밖에 없다. 하지만 100번 틀린 대답을 0.1초 만에 내놓는 것보다, 2초 걸려도 정확한 정답을 내놓는 게 중요한 비즈니스 환경이라면? 이건 게임 체인저다.</p>

<p><img src="/assets/images/memes/this-is-fine.gif" alt="기술 스택을 새로 도입하기 전과 도입한 후의 모습" />
<em>▲ 기술 스택을 새로 도입하기 전과 도입한 후의 모습</em></p>

<p><em>(짤 설명: 고속도로에서 ‘Fast but Wrong’ 출구와 ‘Slow but Right’ 출구 중 ‘Slow but Right’로 급커브 트는 자동차)</em></p>

<hr />

<h3 id="직접-써보니-어떤가-technical-deep-dive">직접 써보니 어떤가? (Technical Deep Dive)</h3>

<p>최근 업데이트된 <code class="language-plaintext highlighter-rouge">PageIndex SDK</code>를 써보니, 로컬 모드에서 자기들만의 <code class="language-plaintext highlighter-rouge">PageIndex Flash</code>라는 엔진을 쓰는데 이게 물건이다. 텍스트 기반 PDF를 읽어서 트리 구조로 만드는 속도가 꽤나 빠릿하다.</p>

<p>특히 ‘PageIndex File System’이라는 개념을 도입해서 문서 한 권이 아니라 수백, 수천 권의 문서 더미 위에서도 트리 구조로 리즈닝을 수행한다. 이건 마치 기업 내부 위키나 문서 저장소 전체를 하나의 거대한 뇌처럼 쓰겠다는 야심이다.</p>

<hr />

<h3 id="한-줄-총평">한 줄 총평</h3>

<p><strong>“RAG 성능 안 나와서 샷건 치던 시절은 이제 끝났다. 벡터의 시대가 가고 리즈닝의 시대가 오고 있다.”</strong></p>

<p>솔직히 말하면, 모든 서비스에 PageIndex를 쓸 필요는 없다. 단순한 FAQ 봇이라면 기존 벡터 RAG로 충분하다. 하지만 전문적인 리포트 분석, 복잡한 계약서 검토, 혹은 롱폼(Long-form) 문서를 다루는 프로젝트를 하고 있다면?</p>

<p>당장 <code class="language-plaintext highlighter-rouge">pip install pageindex</code> 해봐라. 어쩌면 그동안 당신을 괴롭히던 ‘청크 사이즈 최적화’의 지옥에서 탈출할 유일한 비상구일지도 모르니까. 나? 나는 이미 내 개인 프로젝트 문서 인덱스를 이걸로 갈아치우는 중이다. 끝.</p>]]></content><author><name>GooDongWoo</name></author><category term="Tech" /><category term="AI" /><category term="트렌드" /><category term="개발" /><category term="오픈소스" /><summary type="html"><![CDATA[어제도 새벽까지 ‘청크 사이즈를 512로 할까 1024로 할까’ 고민하다가 현타가 왔다. 임베딩 모델 바꾸고, 오버랩 구간 조정하고… 솔직히 말해보자. 우리 다들 RAG(Retrieval-Augmented Generation) 성능 안 나와서 삽질하는 거, 일종의 ‘기우제’ 지내는 마음 아니었나? “제발 유사도 높은 놈이 정답이길!” 하면서 말이다.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://goodongwoo.github.io/avatar.jpeg" /><media:content medium="image" url="https://goodongwoo.github.io/avatar.jpeg" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">AI 에이전트들의 단기 기억 상실증을 치료하는 법: ai-memory 찍먹기</title><link href="https://goodongwoo.github.io/tech/ai/2026/09/21/akitaonrailsai-memory-%EC%97%90%EC%9D%B4%EC%A0%84%ED%8A%B8-%EC%BD%94%EB%94%A9-cli%EB%A5%BC-%EC%9C%84%ED%95%9C-%EC%9E%A5%EA%B8%B0-%EA%B8%B0%EC%96%B5-%EB%B0%8F-%EB%B2%A4%EB%8D%94-%EA%B0%84/" rel="alternate" type="text/html" title="AI 에이전트들의 단기 기억 상실증을 치료하는 법: ai-memory 찍먹기" /><published>2026-09-21T15:10:43+00:00</published><updated>2026-09-21T15:10:43+00:00</updated><id>https://goodongwoo.github.io/tech/ai/2026/09/21/akitaonrailsai-memory-%EC%97%90%EC%9D%B4%EC%A0%84%ED%8A%B8-%EC%BD%94%EB%94%A9-cli%EB%A5%BC-%EC%9C%84%ED%95%9C-%EC%9E%A5%EA%B8%B0-%EA%B8%B0%EC%96%B5-%EB%B0%8F-%EB%B2%A4%EB%8D%94-%EA%B0%84</id><content type="html" xml:base="https://goodongwoo.github.io/tech/ai/2026/09/21/akitaonrailsai-memory-%EC%97%90%EC%9D%B4%EC%A0%84%ED%8A%B8-%EC%BD%94%EB%94%A9-cli%EB%A5%BC-%EC%9C%84%ED%95%9C-%EC%9E%A5%EA%B8%B0-%EA%B8%B0%EC%96%B5-%EB%B0%8F-%EB%B2%A4%EB%8D%94-%EA%B0%84/"><![CDATA[<p>어제는 Claude Code로 리팩토링을 하다가 퇴근했고, 오늘은 집에서 Cursor를 켰다. 그런데 이 자식(AI)이 어제 내가 삽질했던 기록을 전혀 모른다. “야, 어제 그 라이브러리 버전 문제 때문에 안 된다고 했잖아!”라고 백날 소리쳐봐야 소용없다. 다시 처음부터 상황 설명하고, 로그 복붙하고… 이 짓을 반복하다 보면 ‘내가 에이전트를 모시는 건지, 에이전트가 나를 도와주는 건지’ 현타가 세게 온다.</p>

<p>솔직히 요즘 AI 코딩 툴들, 다들 자기네가 최고라고 우기지만 치명적인 단점이 하나 있다. 바로 ‘도구 간의 담장’이다. Claude에서 배운 걸 Cursor는 모르고, 회사 데스크탑에서 하던 맥락은 내 노트북으로 이어지지 않는다. 이 ‘컨텍스트 파편화’ 문제를 해결하겠다고 나온 게 바로 <code class="language-plaintext highlighter-rouge">akitaonrails/ai-memory</code>다.</p>

<p><img src="/assets/images/memes/this-is-fine.gif" alt="기술 스택을 새로 도입하기 전과 도입한 후의 모습" />
<em>▲ 기술 스택을 새로 도입하기 전과 도입한 후의 모습</em></p>

<p><em>(짤 설명: ‘기억해줘…‘라고 애원하는 개발자와 ‘그게 뭔데 씹덕아’라는 표정의 AI 에이전트)</em></p>

<h3 id="1-그래서-이게-정확히-뭔데">1. 그래서 이게 정확히 뭔데?</h3>

<p>한마디로 정의하자면 <strong>“AI 에이전트 전용 외장하드이자 공용 위키”</strong>다.</p>

<p>기존의 방식은 각 에이전트가 자기만의 DB나 로컬 파일에 기억을 저장했다. 하지만 <code class="language-plaintext highlighter-rouge">ai-memory</code>는 중간에서 서버 역할을 하며, 다양한 에이전트(Claude Code, Codex, Cursor, Devin 등 20여 종)의 활동 기록을 하나로 통합한다.</p>

<p>가장 소름 돋는 지점은 <strong>‘벤더 간 핸드오프(Handoff)’</strong> 기능이다. Claude Code를 끄면서 “나 여기까지 했음”이라고 기록하면, 다음에 킨 Codex가 그 기록을 이어받아 “아, 아까 그 리팩토링 하다가 DB 커넥션 오류 나서 멈춘 거군요? 제가 이어서 할게요”라고 말할 수 있게 해준다는 거다.</p>

<h3 id="2-아키텍처-왜-이게-똑똑한가">2. 아키텍처: 왜 이게 똑똑한가?</h3>

<p>이 프로젝트의 구조를 뜯어보면 개발자 취향 저격 포인트가 몇 가지 있다.</p>

<pre><code class="language-mermaid">graph LR
    A[Claude Code] --&gt;|API / Lifecycle Hook| M(ai-memory Server)
    B[Cursor] --&gt;|API / Lifecycle Hook| M
    C[OpenAI Codex] --&gt;|API / Lifecycle Hook| M
    
    subgraph Storage Layer
        M --&gt; D[Markdown Files]
        D --&gt; E[Git-backed Wiki]
        M --&gt; F[SQLite / Search Index]
    end
    
    E -.-&gt; G[Obsidian / Grep]
</code></pre>

<ol>
  <li><strong>Markdown이 진실의 원천(Source of Truth):</strong> 기억을 이상한 바이너리나 벡터 DB에만 꽁꽁 숨겨두지 않는다. 모든 기록은 일반 <code class="language-plaintext highlighter-rouge">.md</code> 파일로 저장된다. 즉, 서버가 터져도 내 기억은 <code class="language-plaintext highlighter-rouge">grep</code>으로 찾을 수 있고, 평소 쓰던 Obsidian으로 열어봐도 된다.</li>
  <li><strong>Zero-LLM 전략:</strong> 메모리를 캡처하고, 검색하고, 인계하는 기본 프로세스에서 LLM 호출을 0으로 줄였다. 쓸데없이 API 비용 태우지 않고 텍스트 매칭과 구조화된 데이터를 활용한다는 뜻이다. 가성비에 미친 시니어 개발자라면 여기서 무릎을 탁 칠 수밖에 없다.</li>
  <li><strong>Rust 기반의 단일 바이너리:</strong> 인프라 설정하느라 반나절 보내는 건 딱 질색인데, 이건 Rust로 짜여 있어서 그냥 바이너리 하나 실행하면 끝이다.</li>
</ol>

<h3 id="3-실무-관점에서의-팩트-체크">3. 실무 관점에서의 팩트 체크</h3>

<p>이게 단순히 “기억력이 좋다” 수준에서 끝날 일일까? 아니다. 협업 관점에서 보면 꽤 파괴적이다.</p>

<ul>
  <li><strong>팀 단위 지식 공유:</strong> 팀원 전체가 하나의 <code class="language-plaintext highlighter-rouge">ai-memory</code> 서버를 바라보게 설정하면, 동료가 AI랑 삽질하면서 얻은 교훈을 내 AI 에이전트도 실시간으로 습득한다.</li>
  <li><strong>프라이버시 경계:</strong> 무지성으로 다 저장하는 게 아니라, 타입 정의된 경계(Typed privacy boundary)에서 민감한 정보를 거르고 저장한다. 회사 보안 팀이랑 싸울 일 하나 줄여주는 디테일이다.</li>
  <li><strong>감사 로그(Audit Log):</strong> AI가 내 코드를 어떻게 주물럭거렸는지, 어떤 프롬프트가 먹혔는지 히스토리가 남는다. “누가 코드를 이따위로 짰어?”라고 물었을 때 “아, 그건 어제 Claude가…“라고 확실히 덤터기를 씌울 수(아니, 원인을 파악할 수) 있다.</li>
</ul>

<p><img src="/assets/images/memes/this-is-fine.gif" alt="기술 스택을 새로 도입하기 전과 도입한 후의 모습" />
<em>▲ 기술 스택을 새로 도입하기 전과 도입한 후의 모습</em></p>

<p><em>(짤 설명: ‘Memory Found!’라며 환호하는 로봇과 그 뒤에서 안도의 한숨을 내쉬는 개발자)</em></p>

<h3 id="4-기존-기술과-뭐가-다른데">4. 기존 기술과 뭐가 다른데?</h3>

<p>혹자는 말할 거다. “그냥 RAG(Retrieval-Augmented Generation) 쓰면 되는 거 아님?”</p>

<p>틀린 말은 아니지만, 결이 다르다. RAG는 보통 ‘문서’를 찾아주는 데 집중한다면, <code class="language-plaintext highlighter-rouge">ai-memory</code>는 <strong>‘작업의 흐름(Workflow Flow)’</strong>을 유지하는 데 집중한다.</p>
<ul>
  <li>시도했던 접근법 중 실패한 것들</li>
  <li>현재 해결되지 않은 의문점(Open Questions)</li>
  <li>다음 스텝에 대한 명시적 가이드</li>
</ul>

<p>이걸 에이전트가 알아서 정리하고 다음 녀석에게 넘겨준다는 게 핵심이다. 컨벤션(Convention)이 아니라 프로토콜(Protocol)로 풀었다는 점이 이 프로젝트의 진짜 가치다.</p>

<h3 id="5-한-줄-총평">5. 한 줄 총평</h3>

<p><strong>“AI 에이전트를 ‘도구’에서 ‘동료’로 업그레이드하고 싶다면 반드시 깔아야 할 미들웨어.”</strong></p>

<p>그래서 내 프로젝트에 쓸 거냐고? 
솔직히 말하면, 아직은 설정하는 게 조금 귀찮긴 하다. 하지만 AI 툴을 3개 이상 스왑해가며 쓰는 ‘헤비 에이전트 유저’라면, 이건 선택이 아니라 생존의 문제다. 기억 못 하는 AI랑 매일 아침 통성명하기 싫으면 일단 찍먹부터 해보길 권한다.</p>

<p>아, 참고로 Rust 1.95 이상 버전 필수다. 설치하다 에러 나면 환경 변수부터 체크해라. 이상!</p>]]></content><author><name>GooDongWoo</name></author><category term="Tech" /><category term="AI" /><category term="트렌드" /><category term="개발" /><category term="오픈소스" /><summary type="html"><![CDATA[어제는 Claude Code로 리팩토링을 하다가 퇴근했고, 오늘은 집에서 Cursor를 켰다. 그런데 이 자식(AI)이 어제 내가 삽질했던 기록을 전혀 모른다. “야, 어제 그 라이브러리 버전 문제 때문에 안 된다고 했잖아!”라고 백날 소리쳐봐야 소용없다. 다시 처음부터 상황 설명하고, 로그 복붙하고… 이 짓을 반복하다 보면 ‘내가 에이전트를 모시는 건지, 에이전트가 나를 도와주는 건지’ 현타가 세게 온다.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://goodongwoo.github.io/avatar.jpeg" /><media:content medium="image" url="https://goodongwoo.github.io/avatar.jpeg" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">LLM 에이전트의 뇌절을 막는 브레이크, GAVEL 픝어보기</title><link href="https://goodongwoo.github.io/tech/ai/2026/09/21/gavel-%EA%B2%80%EC%A6%9D%EB%90%98%EA%B3%A0-%ED%9A%A8%EC%9C%A8%EC%A0%81%EC%9D%B8-%EC%9E%A5%EA%B8%B0-llm-%ED%83%9C%EC%8A%A4%ED%81%AC-%EA%B3%84%ED%9A%8D%EC%9D%84-%EC%9C%84%ED%95%9C-%EA%B7%B8%EB%9E%98%ED%94%84-%EC%84%B8%EA%B3%84-%EB%AA%A8%EB%8D%B8/" rel="alternate" type="text/html" title="LLM 에이전트의 뇌절을 막는 브레이크, GAVEL 픝어보기" /><published>2026-09-21T15:10:07+00:00</published><updated>2026-09-21T15:10:07+00:00</updated><id>https://goodongwoo.github.io/tech/ai/2026/09/21/gavel-%EA%B2%80%EC%A6%9D%EB%90%98%EA%B3%A0-%ED%9A%A8%EC%9C%A8%EC%A0%81%EC%9D%B8-%EC%9E%A5%EA%B8%B0-llm-%ED%83%9C%EC%8A%A4%ED%81%AC-%EA%B3%84%ED%9A%8D%EC%9D%84-%EC%9C%84%ED%95%9C-%EA%B7%B8%EB%9E%98%ED%94%84-%EC%84%B8%EA%B3%84-%EB%AA%A8%EB%8D%B8</id><content type="html" xml:base="https://goodongwoo.github.io/tech/ai/2026/09/21/gavel-%EA%B2%80%EC%A6%9D%EB%90%98%EA%B3%A0-%ED%9A%A8%EC%9C%A8%EC%A0%81%EC%9D%B8-%EC%9E%A5%EA%B8%B0-llm-%ED%83%9C%EC%8A%A4%ED%81%AC-%EA%B3%84%ED%9A%8D%EC%9D%84-%EC%9C%84%ED%95%9C-%EA%B7%B8%EB%9E%98%ED%94%84-%EC%84%B8%EA%B3%84-%EB%AA%A8%EB%8D%B8/"><![CDATA[<p>커피 한 잔 타오라고 시켰더니 주방을 난장판으로 만들어놓는 에이전트를 보고 있으면 뒷목이 당긴다. 분명 “컵을 잡고, 물을 붓고, 섞어라”라고 단계별로 알려줬는데, 이 녀석은 컵도 안 집어 들고 허공에 물을 붓는 ‘할루시네이션 퍼포먼스’를 선보인다. LLM이 똑똑해졌다고는 하지만, 물리적인 제약이 있는 ‘현실 세계’나 ‘복잡한 롱 호라이즌(Long-horizon) 태스크’ 앞에선 여전히 나사 빠진 소리를 한다.</p>

<p>오늘 소개할 <strong>GAVEL(Graph World Models for Verified and Efficient Long-Horizon LLM Task Planning)</strong>은 바로 이런 에이전트의 ‘능지’ 문제를 해결하겠다고 나온 물건이다. 겉만 번지르르한 프롬프트 엔지니어링이 아니라, 그래프 구조로 세계관을 박아버리는 접근법이다.</p>

<h3 id="llm은-왜-자꾸-헛스윙을-할까">LLM은 왜 자꾸 헛스윙을 할까?</h3>

<p>전통적인 LLM 기반 에이전트의 문제는 ‘계획’과 ‘검증’이 따로 논다는 거다. “이거 해봐”라고 하면 “네!” 하고 일단 뱉고 보는데, 그 과정에서 물리적인 인과관계(Affordance)를 무시하기 일쑤다. 예를 들어, 문을 열지도 않고 “방에 들어간다”는 계획을 세우는 식이다.</p>

<p><img src="/assets/images/memes/this-is-fine.gif" alt="기술 스택을 새로 도입하기 전과 도입한 후의 모습" />
<em>▲ 기술 스택을 새로 도입하기 전과 도입한 후의 모습</em></p>

<p><em>(설명: 계획은 완벽하지만 현실은 시궁창인 개발자/로봇 짤)</em></p>

<p>기존에는 이걸 해결하려고 ReAct나 Reflexion 같은 방식을 썼다. 하지만 이건 실행해보고 “어? 안 되네?” 하고 다시 시도하는 방식이라 너무 느리고 비효율적이다. API 비용은 누가 낼 건데? 우리 사장님이? 아니면 내 지갑이?</p>

<h3 id="gavel-그래프로-지도를-그려줄게-헛소리-금지야">GAVEL: “그래프로 지도를 그려줄게, 헛소리 금지야”</h3>

<p>GAVEL의 핵심 아이디어는 <strong>‘그래프 세계 모델(Graph World Model)’</strong>이다. 환경의 상태와 가능한 행동을 그래프 노드와 엣지로 정의한다.</p>

<ol>
  <li><strong>그래프 기반 상태 추상화</strong>: 현재 환경에서 가능한 모든 상태를 그래프로 구조화한다.</li>
  <li><strong>검증된 계획(Verified Planning)</strong>: LLM이 다음 행동을 제안하면, 그래프 모델이 “잠깐, 지금 상태에서 그 행동이 가능해?”라고 즉시 검증한다.</li>
  <li><strong>효율적 탐색</strong>: 단순히 텍스트로만 생각하는 게 아니라, 그래프 경로를 따라가며 최적의 루트를 찾는다.</li>
</ol>

<p>작동 아키텍처를 대략 그려보면 이렇다.</p>

<pre><code class="language-mermaid">graph TD
    A[사용자 요청: 커피 타와] --&gt; B{LLM Planner}
    B --&gt; C[행동 제안: 물 붓기]
    C --&gt; D{Graph World Model}
    D -- "검증 실패: 컵이 없음!" --&gt; B
    D -- "검증 성공: 진행시켜!" --&gt; E[Executor]
    E --&gt; F[환경 변화 반영 및 그래프 업데이트]
    F --&gt; B
</code></pre>

<h3 id="왜-이-방식이-물건인가">왜 이 방식이 ‘물건’인가?</h3>

<p>GAVEL이 기존 방식과 차별화되는 지점은 <strong>‘검증(Verification)’의 시점</strong>이다.</p>

<p>대부분의 에이전트는 일단 사고를 치고(Action) 나서 환경으로부터 피드백을 받는다. 하지만 GAVEL은 사고를 치기 직전에 <strong>세계 모델(Graph)</strong> 내에서 시뮬레이션을 돌려본다.</p>

<ul>
  <li><strong>정확도</strong>: 물리적 제약 조건을 그래프 엣지로 박아놨기 때문에, ‘열리지 않은 문 통과하기’ 같은 멍청한 실수를 원천 봉쇄한다.</li>
  <li><strong>속도</strong>: LLM한테 “이게 말이 되니?”라고 물어보는 대신, 그래프 탐색(BFS/DFS 혹은 휴리스틱)으로 처리하니 훨씬 빠르다.</li>
  <li><strong>장기 계획(Long-horizon)</strong>: 단계가 20단계, 30단계가 넘어가면 LLM은 앞 내용을 까먹기 시작하는데, 그래프는 망각이 없다.</li>
</ul>

<p><img src="/assets/images/memes/hotfix-in-production.gif" alt="릴리즈 5분 전 긴급 핫픽스 상황" />
<em>▲ 릴리즈 5분 전 긴급 핫픽스 상황</em></p>

<p><em>(설명: 100줄짜리 if-else 문을 쓰다가 그래프 DB를 도입하고 평화를 찾은 개발자 짤)</em></p>

<h3 id="벤치마크-결과가-말해주는-팩트">벤치마크 결과가 말해주는 팩트</h3>

<p>논문에 따르면 GAVEL은 기존의 ReAct나 단순 Zero-shot 프롬프팅보다 성공률(Success Rate) 면에서 압도적인 성능을 보였다. 특히 태스크의 단계가 길어질수록(Long-horizon) 격차는 더 벌어진다.</p>

<p>단순히 성공만 하는 게 아니라, 목표에 도달하기까지의 ‘스텝 수’도 훨씬 적다. 즉, 최적의 경로를 찾아낸다는 소리다. 토큰 아껴서 부자 되겠다는 의지가 엿보인다.</p>

<h3 id="실무자-입장에서-본-한계와-가능성">실무자 입장에서 본 한계와 가능성</h3>

<p>물론 장밋빛 미래만 있는 건 아니다.</p>
<ul>
  <li><strong>그래프 구축 비용</strong>: 환경을 그래프로 추상화하는 작업 자체가 또 다른 일이다. 동적인 환경에서 그래프를 실시간으로 업데이트하는 오버헤드를 어떻게 감당할지가 관건이다.</li>
  <li><strong>도메인 의존성</strong>: 서비스마다 세계 모델을 새로 정의해야 할 수도 있다. 범용 ‘세계 모델’이 나오기 전까지는 특정 도메인(물류 창고 로봇, 특정 소프트웨어 자동화 등)에서 먼저 빛을 발할 것 같다.</li>
</ul>

<p>그럼에도 불구하고, GAVEL은 “LLM은 통계적인 앵무새일 뿐이다”라는 비판을 정면으로 돌파한다. LLM의 창의적인 추론 능력과 그래프의 엄격한 논리를 결합했다는 점에서 아주 영리한 접근이다.</p>

<h3 id="한-줄-총평">한 줄 총평</h3>

<p><strong>“에이전트한테 제발 ‘생각’ 좀 하고 움직이라고 빌 시간에, 그냥 GAVEL 같은 족쇄(그래프)를 채우는 게 정신 건강에 이롭다.”</strong></p>

<p>조만간 내 사이드 프로젝트 에이전트에도 이 구조를 얹어봐야겠다. 자꾸 DB 커넥션도 안 맺고 쿼리 날리겠다는 소리 좀 안 듣게 말이다. 끝.</p>]]></content><author><name>GooDongWoo</name></author><category term="Tech" /><category term="AI" /><category term="트렌드" /><category term="개발" /><category term="오픈소스" /><summary type="html"><![CDATA[커피 한 잔 타오라고 시켰더니 주방을 난장판으로 만들어놓는 에이전트를 보고 있으면 뒷목이 당긴다. 분명 “컵을 잡고, 물을 붓고, 섞어라”라고 단계별로 알려줬는데, 이 녀석은 컵도 안 집어 들고 허공에 물을 붓는 ‘할루시네이션 퍼포먼스’를 선보인다. LLM이 똑똑해졌다고는 하지만, 물리적인 제약이 있는 ‘현실 세계’나 ‘복잡한 롱 호라이즌(Long-horizon) 태스크’ 앞에선 여전히 나사 빠진 소리를 한다.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://goodongwoo.github.io/avatar.jpeg" /><media:content medium="image" url="https://goodongwoo.github.io/avatar.jpeg" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">AI한테 내 마우스를 맡겨도 될까? cua(trycua)가 보여주는 Computer-Use 2.0의 실체</title><link href="https://goodongwoo.github.io/tech/ai/2026/09/21/trycuacua-%EC%98%A4%ED%94%88%EC%86%8C%EC%8A%A4-%EB%93%9C%EB%9D%BC%EC%9D%B4%EB%B2%84%EC%99%80-%ED%81%AC%EB%A1%9C%EC%8A%A4-os-%ED%94%8C%EB%A6%BF%EC%9D%84-%ED%99%9C%EC%9A%A9%ED%95%9C-computer-use-2/" rel="alternate" type="text/html" title="AI한테 내 마우스를 맡겨도 될까? cua(trycua)가 보여주는 Computer-Use 2.0의 실체" /><published>2026-09-21T15:09:12+00:00</published><updated>2026-09-21T15:09:12+00:00</updated><id>https://goodongwoo.github.io/tech/ai/2026/09/21/trycuacua-%EC%98%A4%ED%94%88%EC%86%8C%EC%8A%A4-%EB%93%9C%EB%9D%BC%EC%9D%B4%EB%B2%84%EC%99%80-%ED%81%AC%EB%A1%9C%EC%8A%A4-os-%ED%94%8C%EB%A6%BF%EC%9D%84-%ED%99%9C%EC%9A%A9%ED%95%9C-computer-use-2</id><content type="html" xml:base="https://goodongwoo.github.io/tech/ai/2026/09/21/trycuacua-%EC%98%A4%ED%94%88%EC%86%8C%EC%8A%A4-%EB%93%9C%EB%9D%BC%EC%9D%B4%EB%B2%84%EC%99%80-%ED%81%AC%EB%A1%9C%EC%8A%A4-os-%ED%94%8C%EB%A6%BF%EC%9D%84-%ED%99%9C%EC%9A%A9%ED%95%9C-computer-use-2/"><![CDATA[<p>GitHub Trending을 보다가 간만에 뒷목 잡을 뻔했다. 또 ‘Computer Use’ 타령이다. 솔직히 말해보자. 클로드(Claude)가 처음 이 기능을 내놨을 때, 우리 다들 “오, 이제 메일 보내고 지라(Jira) 티켓 끊는 거 AI가 해주겠네?”라며 설레지 않았나? 근데 막상 써보면 어때. 스크린샷 하나 찍고 10초 멍 때리다가 엉뚱한 버튼 클릭하고… 속 터져서 내가 직접 마우스 잡는 게 일상이다.</p>

<p>그런데 이번에 등장한 <strong>trycua/cua</strong>는 결이 좀 다르다. 단순히 “AI가 화면을 보고 클릭한다” 수준을 넘어서, 아예 OS 레벨에서 에이전트가 뛰어놀 수 있는 ‘인프라’를 통째로 깔아버리겠다는 야심이 느껴진다. 오늘은 이 녀석이 왜 단순한 래퍼(Wrapper)가 아닌지, 그리고 우리 같은 시니어 개발자들이 왜 이걸 주목해야 하는지 뼛속까지 파헤쳐 보겠다.</p>

<h2 id="1-껍데기만-ai가-아니다-cua의-아키텍처">1. 껍데기만 AI가 아니다: Cua의 아키텍처</h2>

<p>기존의 Computer Use가 ‘브라우저 안에서 꼼지락거리는 수준’이었다면, Cua는 OS라는 거대한 생태계를 AI가 지배할 수 있도록 다리를 놓는다. 핵심은 크게 세 가지다: <strong>Cua Driver</strong>, <strong>Cua Fleets</strong>, 그리고 <strong>Lume</strong>.</p>

<p>이게 왜 중요하냐면, AI 에이전트가 네이티브 앱(Slack, VS Code, Excel 등)을 제어하려면 단순히 ‘이미지 인식’만으로는 한계가 명확하기 때문이다. 버튼이 살짝만 가려져도 바보가 되거든. Cua는 접근성(Accessibility) 트리와 드라이버 레벨의 제어를 결합해서 AI가 “아, 이게 클릭 가능한 확인 버튼이구나”라는 걸 픽셀이 아니라 데이터로 이해하게 만든다.</p>

<pre><code class="language-mermaid">graph TD
    A[AI Agent / LLM] --&gt;|Action Request| B(Cua Driver)
    B --&gt; C{Platform Check}
    C --&gt;|macOS| D[Native Accessibility API / Lume VM]
    C --&gt;|Windows/Linux| E[Win32 API / X11 / Wayland]
    D --&gt; F[App Control &amp; Screenshot]
    E --&gt; F
    F --&gt;|Observation Data| A
</code></pre>

<p><img src="/assets/images/memes/works-on-my-machine.gif" alt="분명 로컬에선 잘 돌아갔는데...?" />
<em>▲ 분명 로컬에선 잘 돌아갔는데…?</em></p>

<p><em>(짤 설명: “This is fine” 강아지가 불타는 집 대신, AI 에이전트가 내 운영체제 설정을 멋대로 바꾸고 있는 모니터를 바라보며 흐뭇해하는 짤)</em></p>

<h2 id="2-왜-굳이-cua-fleets라는-격리-공간이-필요한가">2. 왜 굳이 ‘Cua Fleets’라는 격리 공간이 필요한가?</h2>

<p>개발자라면 본능적으로 이런 생각이 들 거다. “내 메인 맥북의 마우스 제어권을 이름도 모르는 오픈소스 에이전트한테 준다고? 미쳤어?”</p>

<p>Cua 형들은 이 지점을 정확히 짚었다. 그래서 <strong>Cua Fleets</strong>와 <strong>Lume</strong>이 나온 거다.</p>
<ul>
  <li><strong>Lume</strong>: Apple Silicon 기반에서 로컬 macOS VM을 띄워버린다. 내 본체랑 격리된 가상 환경에서 AI가 마음껏 삽질하게 두는 거다.</li>
  <li><strong>Cua Fleets</strong>: 클라우드 기반의 격리된 데스크톱 환경이다. API 호출 한 번으로 에이전트 전용 PC를 대량으로 뽑아낼 수 있다.</li>
</ul>

<p>이건 확장성 측면에서 괴물 같은 장점이다. 내가 짠 에이전트 100개가 동시에 서로 다른 PC 환경에서 엑셀을 돌리고 이메일을 보낸다고 상상해 봐라. 이건 더 이상 장난감이 아니라 ‘디지털 노동력’의 플릿(Fleet)을 구축하는 셈이다.</p>

<h2 id="3-cua-s1-생각은-짧게-행동은-정확하게">3. CUA-S1: “생각은 짧게, 행동은 정확하게”</h2>

<p>Cua가 영리한 건 모델 전략에서도 드러난다. 얘네는 모든 걸 GPT-4나 Claude 3.5 Sonnet 같은 무거운 모델에 맡기지 않는다. <strong>CUA-S1</strong>이라는 특화된 소형 모델을 들이밀었다.</p>

<ul>
  <li><strong>문제</strong>: 거대 모델은 화면 전체를 이해하는 데 너무 많은 토큰을 쓰고 느리다.</li>
  <li><strong>해결</strong>: S1은 ‘Computer Use’라는 특정 태스크, 즉 좌표 계산과 요소 인식에 최적화되어 있다.</li>
</ul>

<p>이게 실무에서 왜 중요하냐면, 마우스 클릭 한 번에 5초씩 걸리면 서비스로 못 쓴다. Cua는 특화 모델을 통해 레이턴시를 줄이고 정확도를 높이는 정공법을 택했다. 벤치마크 결과? 뭐, 자기들이 만든 거니까 당연히 좋게 나오겠지만, 구조적으로 ‘전문가 모델’을 따로 둔 건 확실히 박수 쳐줄 만한 선택이다.</p>

<h2 id="4-기존-기술과-뭐가-다른데-playwright-vs-cua">4. 기존 기술과 뭐가 다른데? (Playwright vs Cua)</h2>

<p>혹자는 “그거 그냥 Playwright나 Selenium으로 브라우저 자동화하면 되는 거 아님?”이라고 묻는다. 미안하지만 그건 유치원생이랑 대학생을 비교하는 수준이다.</p>

<ol>
  <li><strong>범위</strong>: Playwright는 브라우저라는 샌드박스에 갇혀 있다. Cua는 OS 전체다. 슬랙 알림을 읽고, 터미널을 열어 빌드를 돌리고, 결과를 엑셀에 붙여넣는 게 가능하다.</li>
  <li><strong>유연성</strong>: 셀레늄은 돔(DOM) 구조가 조금만 바뀌어도 터진다. Cua의 드라이버 기반 접근 방식은 시각 정보와 접근성 데이터를 버무려서 훨씬 유연하게 대응한다.</li>
</ol>

<p><img src="/assets/images/memes/rage-computer-throw.gif" alt="코드가 왜 돌아가는지 아무도 모를 때" />
<em>▲ 코드가 왜 돌아가는지 아무도 모를 때</em></p>

<p><em>(짤 설명: 고전적인 셀레늄 코드가 0.1초 만에 깨지는 모습과, 여유롭게 OS를 활보하는 Cua 에이전트의 대비)</em></p>

<h2 id="5-그래서-내-프로젝트에-쓸-거냐고">5. 그래서 내 프로젝트에 쓸 거냐고?</h2>

<p>솔직히 말하면, <strong>“내일 당장 프로덕션에 박겠다”는 오버다.</strong> 아직 ‘Computer Use’ 자체의 불확실성이 크고, AI가 내 개인 정보를 다루는 환경에서 보안 이슈는 여전히 뜨거운 감자니까.</p>

<p>하지만 <strong>“로컬 개발 도구로는 당장 써보겠다”</strong>는 게 내 결론이다. 특히 반복적인 UI 테스트나, 여러 앱을 넘나드는 귀찮은 워크플로우를 자동화하는 데 이만한 물건이 없다. Lume을 이용해 로컬 VM에서 돌리면 보안 걱정도 덜 수 있고 말이다.</p>

<h3 id="한-줄-총평">한 줄 총평:</h3>
<p><strong>“AI에게 마우스 제어권을 주는 게 공포 영화의 시작일지, 개발자의 낙원일지는 cua의 격리 기술이 얼마나 완벽하냐에 달렸다. 일단 아키텍처는 합격이다.”</strong></p>

<p>자, 이제 <code class="language-plaintext highlighter-rouge">brew install</code>은 아니고 깃허브 가서 <code class="language-plaintext highlighter-rouge">git clone</code>부터 때려보자. AI가 내 대신 지라 티켓을 처리해주는 그날까지, 우리네 삽질은 계속될 테니까.</p>]]></content><author><name>GooDongWoo</name></author><category term="Tech" /><category term="AI" /><category term="트렌드" /><category term="개발" /><category term="오픈소스" /><summary type="html"><![CDATA[GitHub Trending을 보다가 간만에 뒷목 잡을 뻔했다. 또 ‘Computer Use’ 타령이다. 솔직히 말해보자. 클로드(Claude)가 처음 이 기능을 내놨을 때, 우리 다들 “오, 이제 메일 보내고 지라(Jira) 티켓 끊는 거 AI가 해주겠네?”라며 설레지 않았나? 근데 막상 써보면 어때. 스크린샷 하나 찍고 10초 멍 때리다가 엉뚱한 버튼 클릭하고… 속 터져서 내가 직접 마우스 잡는 게 일상이다.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://goodongwoo.github.io/avatar.jpeg" /><media:content medium="image" url="https://goodongwoo.github.io/avatar.jpeg" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Playwright로 AI 에이전트 만들다 통장 깨진 썰: Stagehand v4 뜯어보기</title><link href="https://goodongwoo.github.io/tech/ai/2026/09/20/stagehand-v4-playwright-%EB%8C%80%EB%B9%84-2%EB%B0%B0-%EB%B9%A0%EB%A5%B8-%EC%86%8D%EB%8F%84%EC%99%80-80-%ED%86%A0%ED%81%B0-%EC%A0%88%EA%B0%90%EC%9D%84-%EB%8B%AC%EC%84%B1%ED%95%9C/" rel="alternate" type="text/html" title="Playwright로 AI 에이전트 만들다 통장 깨진 썰: Stagehand v4 뜯어보기" /><published>2026-09-20T06:17:25+00:00</published><updated>2026-09-20T06:17:25+00:00</updated><id>https://goodongwoo.github.io/tech/ai/2026/09/20/stagehand-v4-playwright-%EB%8C%80%EB%B9%84-2%EB%B0%B0-%EB%B9%A0%EB%A5%B8-%EC%86%8D%EB%8F%84%EC%99%80-80-%ED%86%A0%ED%81%B0-%EC%A0%88%EA%B0%90%EC%9D%84-%EB%8B%AC%EC%84%B1%ED%95%9C</id><content type="html" xml:base="https://goodongwoo.github.io/tech/ai/2026/09/20/stagehand-v4-playwright-%EB%8C%80%EB%B9%84-2%EB%B0%B0-%EB%B9%A0%EB%A5%B8-%EC%86%8D%EB%8F%84%EC%99%80-80-%ED%86%A0%ED%81%B0-%EC%A0%88%EA%B0%90%EC%9D%84-%EB%8B%AC%EC%84%B1%ED%95%9C/"><![CDATA[<p>지난달 OpenAI API 영수증을 보고 내 눈을 의심했다.</p>

<p>겨우 웹사이트 몇 개 돌아다니면서 데이터 좀 긁어오라고 에이전트 만들어놨더니, 내 한 달 치 치킨 값이 API 비용으로 증발했더라. 이유? 간단하다. Playwright에 LLM 붙여서 브라우저 제어할 때 무지성으로 <code class="language-plaintext highlighter-rouge">page.content()</code> 떠서 DOM 전체를 LLM에 던졌기 때문이다. <code class="language-plaintext highlighter-rouge">&lt;div&gt;</code> 지옥과 온갖 쓰레기 스크립트태그가 섞인 몇만 줄짜리 HTML을 매 행동마다 프롬프트로 들이부었으니 토큰이 안 터지고 배기겠나.</p>

<p>그러다 깃허브를 떠돌다 발견한 녀석이 바로 <strong>Stagehand v4</strong>다.</p>

<p>“Playwright는 테스트용이고, Stagehand는 에이전트용이다”라는 당돌한 슬로건을 걸고 나왔다. 심지어 <strong>속도 2배, 토큰 80% 절감</strong>이란다. 약 파는 건지 진짜 물건인지 궁금해서 바로 뜯어봤다.</p>

<hr />

<h2 id="기존-브라우저-에이전트가-토큰을-처먹던-방식">기존 브라우저 에이전트가 토큰을 처먹던 방식</h2>

<p>기존에 LLM으로 브라우저를 조종하려면 대충 두 가지 방법을 썼다.</p>

<ol>
  <li><strong>스크린샷 방식</strong>: 매 프레임 스크린샷 찍어서 VLM(Vision LLM)에 넘기기 ➔ <em>비용 폭탄 + 속도 답 없음</em></li>
  <li><strong>Raw HTML / DOM Dump 방식</strong>: 현재 페이지 HTML 전체를 텍스트로 넘기기 ➔ <em>토큰 폭탄 + LLM이 환각 일으켜서 이상한 버튼 클릭함</em></li>
</ol>

<p><img src="/assets/images/memes/works-on-my-machine.gif" alt="분명 로컬에선 잘 돌아갔는데...?" />
<em>▲ 분명 로컬에선 잘 돌아갔는데…?</em></p>

<p>결국 문제는 <strong>“LLM한테 브라우저 상태를 어떻게 효율적으로 보여줄 것인가”</strong>다. Playwright는 원래 E2E 테스트용으로 만들어진 도구라, LLM이 이해하기 좋은 형태로 DOM을 정제해 주는 기능 따윈 없다. 실무 개발자가 직접 DOM 트리 깎는 장인이 되어야 했다.</p>

<p>Stagehand는 바로 이 지점을 파고들었다.</p>

<hr />

<h2 id="stagehand-v4의-핵심-아키텍처-세-가지-기둥-observe-act-extract">Stagehand v4의 핵심 아키텍처: 세 가지 기둥 (<code class="language-plaintext highlighter-rouge">observe</code>, <code class="language-plaintext highlighter-rouge">act</code>, <code class="language-plaintext highlighter-rouge">extract</code>)</h2>

<p>Stagehand의 동작 원리는 생각보다 명쾌하다. LLM에게 웹페이지 전체를 다 보여주지 않는다. 대신 <strong>“경량화된 DOM 추상화”</strong> 엔진을 중간에 둔다.</p>

<pre><code class="language-mermaid">flowchart TD
    A[웹페이지 DOM] --&gt; B[Stagehand DOM Pruning Engine]
    B --&gt;|인터랙티브 요소를 유효한 Selector로 압축| C[경량화된 Context]
    C --&gt; D[LLM - gpt-5.4-mini 등]
    D --&gt;|액션 제안 / Selector 반환| E[Stagehand SDK]
    E --&gt;|로컬 Playwright 드라이버 실행| F[브라우저 액션 수행]
</code></pre>

<p>Stagehand는 개발자에게 세 가지 핵심 메서드를 제공한다.</p>

<h3 id="1-observe--토큰-절감과-보안의-핵심">1. <code class="language-plaintext highlighter-rouge">observe()</code> : 토큰 절감과 보안의 핵심</h3>
<p>“로그인 버튼 찾아줘”라고 하면, LLM이 직접 클릭까지 하는 게 아니라 <strong>해당 요소의 실제 Playwright Selector만 찾아준다.</strong></p>

<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// observe()는 진짜 CSS/Xpath Selector를 반환함</span>
<span class="kd">const</span> <span class="p">{</span> <span class="na">data</span><span class="p">:</span> <span class="nx">email</span> <span class="p">}</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">stagehand</span><span class="p">.</span><span class="nx">observe</span><span class="p">(</span><span class="dl">"</span><span class="s2">find the email input</span><span class="dl">"</span><span class="p">);</span>
<span class="kd">const</span> <span class="p">{</span> <span class="na">data</span><span class="p">:</span> <span class="nx">password</span> <span class="p">}</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">stagehand</span><span class="p">.</span><span class="nx">observe</span><span class="p">(</span><span class="dl">"</span><span class="s2">find the password input</span><span class="dl">"</span><span class="p">);</span>

<span class="c1">// 비밀번호 입력은 로컬 브라우저 드라이버에서 직접 처리!</span>
<span class="c1">// 즉, 내 비밀번호가 LLM API로 전송될 일이 전혀 없다.</span>
<span class="k">await</span> <span class="nx">page</span><span class="p">.</span><span class="nx">locator</span><span class="p">(</span><span class="nx">email</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="nx">selector</span><span class="p">).</span><span class="nx">fill</span><span class="p">(</span><span class="nx">process</span><span class="p">.</span><span class="nx">env</span><span class="p">.</span><span class="nx">APP_EMAIL</span><span class="o">!</span><span class="p">);</span>
<span class="k">await</span> <span class="nx">page</span><span class="p">.</span><span class="nx">locator</span><span class="p">(</span><span class="nx">password</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="nx">selector</span><span class="p">).</span><span class="nx">fill</span><span class="p">(</span><span class="nx">process</span><span class="p">.</span><span class="nx">env</span><span class="p">.</span><span class="nx">APP_PASSWORD</span><span class="o">!</span><span class="p">);</span>
</code></pre></div></div>
<p>이 방식의 미친 점은 두 가지다.</p>
<ul>
  <li><strong>보안</strong>: 비밀번호나 개인정보를 LLM 프롬프트에 실어 보낼 필요가 없다.</li>
  <li><strong>토큰 절감</strong>: DOM에서 클릭 가능한 핵심 요소를 미리 필터링(DOM Pruning)해서 LLM에 전달하므로 입력 토큰이 80% 이상 줄어든다.</li>
</ul>

<h3 id="2-act--자가-치유self-healing-ui-액션">2. <code class="language-plaintext highlighter-rouge">act()</code> : 자가 치유(Self-Healing) UI 액션</h3>
<p>웹사이트 디자인이 바뀌어서 클래스명이 <code class="language-plaintext highlighter-rouge">btn-primary-v2</code>에서 <code class="language-plaintext highlighter-rouge">button-submit-new</code>로 개편되었다고 치자. 기존 맘대로 짜둔 스크립트는 터진다.하지만 Stagehand의 <code class="language-plaintext highlighter-rouge">act()</code>는 자연어로 명령을 받아서 알아서 재시도하고 찾아낸다.</p>

<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// 사이트 UI가 다 엎어져도 자연어 기반으로 알아서 치유해서 클릭함</span>
<span class="k">await</span> <span class="nx">stagehand</span><span class="p">.</span><span class="nx">act</span><span class="p">(</span><span class="dl">"</span><span class="s2">click the sign in button</span><span class="dl">"</span><span class="p">);</span>
<span class="k">await</span> <span class="nx">stagehand</span><span class="p">.</span><span class="nx">act</span><span class="p">(</span><span class="dl">"</span><span class="s2">open the billing page</span><span class="dl">"</span><span class="p">);</span>
</code></pre></div></div>

<h3 id="3-extract--zod-스키마-기반-타입-안전-추출">3. <code class="language-plaintext highlighter-rouge">extract()</code> : Zod 스키마 기반 타입 안전 추출</h3>
<p>크롤링할 때 가장 귀찮은 게 RegEx나 Cheerio 붙여서 텍스트 파싱하는 거다. <code class="language-plaintext highlighter-rouge">extract()</code>는 Zod 스키마만 던져주면 알아서 JSON 구조로 딱 맞춰서 뽑아준다.</p>

<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">const</span> <span class="p">{</span> <span class="nx">data</span> <span class="p">}</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">stagehand</span><span class="p">.</span><span class="nx">extract</span><span class="p">(</span>
  <span class="dl">"</span><span class="s2">extract every invoice in the table</span><span class="dl">"</span><span class="p">,</span>
  <span class="nx">z</span><span class="p">.</span><span class="nx">object</span><span class="p">({</span>
    <span class="na">invoices</span><span class="p">:</span> <span class="nx">z</span><span class="p">.</span><span class="nx">array</span><span class="p">(</span><span class="nx">z</span><span class="p">.</span><span class="nx">object</span><span class="p">({</span> 
      <span class="na">number</span><span class="p">:</span> <span class="nx">z</span><span class="p">.</span><span class="kr">string</span><span class="p">(),</span> 
      <span class="na">amount</span><span class="p">:</span> <span class="nx">z</span><span class="p">.</span><span class="kr">number</span><span class="p">(),</span> 
      <span class="na">paid</span><span class="p">:</span> <span class="nx">z</span><span class="p">.</span><span class="nx">boolean</span><span class="p">()</span> 
    <span class="p">})),</span>
  <span class="p">}),</span>
<span class="p">);</span>

<span class="c1">// data.invoices는 완벽히 타입이 추론된 배열이다.</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">data</span><span class="p">.</span><span class="nx">invoices</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="nx">amount</span><span class="p">);</span>
</code></pre></div></div>

<hr />

<h2 id="2배-빠른-속도와-80-토큰-절감이-진짜인가">2배 빠른 속도와 80% 토큰 절감이 진짜인가?</h2>

<p>결론부터 말하면 <strong>“조건부 진짜”</strong>다.</p>

<p>기존에 LangChain + Playwright 조합이나 AutoGPT 계열 도구들이 페이지 하나 분석할 때마다 통째로 50k~100k 토큰씩 잡아먹던 것에 비하면, Stagehand의 DOM 경량화 알고리즘은 혁명 수준이다.</p>

<p><img src="/assets/images/memes/github-star.gif" alt="새로운 오픈소스 라이브러리 스타 찍는 손가락" />
<em>▲ 새로운 오픈소스 라이브러리 스타 찍는 손가락</em></p>

<ol>
  <li><strong>입력 토큰 감축</strong>: DOM 트리를 분석해서 불필요한 <code class="language-plaintext highlighter-rouge">div</code>, <code class="language-plaintext highlighter-rouge">span</code>, CSS 스타일 정보, 메타태그를 전부 쳐내고 인터랙션 가능한 노드(AOM, Accessibility Object Model 기반)만 뽑아 프롬프트로 만든다. 여기서 토큰 80% 절감이 나온다.</li>
  <li><strong>응답 속도 향상</strong>: 프롬프트를 적게 먹으니 LLM의 첫 토큰 생성 시간(TTFT)이 비약적으로 줄어든다. 게다가 <code class="language-plaintext highlighter-rouge">observe()</code>를 통해 로컬 Playwright 코드로 실행을 위임하니까 네트워크 왕복 횟수가 최소화된다. 속도가 2배 빨라지는 건 어찌 보면 당연한 수학적 결과다.</li>
</ol>

<p>게다가 TypeScript뿐만 아니라 <strong>Python과 Go까지 지원</strong>한다. 백엔드 파이프라인에 붙이기 매우 쾌적해졌다.</p>

<hr />

<h2 id="실무-개발자-입장에서-본-아쉬운-점--한계">실무 개발자 입장에서 본 아쉬운 점 &amp; 한계</h2>

<p>찬양만 할 순 없다. 내 통장을 지켜줄 은인이긴 하지만, 여전히 한계는 존재한다.</p>

<ul>
  <li><strong>복잡한 Canvas / Shadow DOM</strong>: 캔버스 기반으로 그려진 차트나 complex한 Shadow DOM으로 꽁꽁 묶인 Enterprise ERP 시스템에서는 <code class="language-plaintext highlighter-rouge">observe()</code>가 요소를 놓치는 경우가 더러 있다.</li>
  <li><strong>LLM 의존성</strong>: 아무리 DOM을 깎아도 결국 판단은 LLM(GPT-5.4-mini나 Claude 3.5 Sonnet 등)이 한다. 모델이 멍청한 날엔 여전히 엄한 곳을 클릭하려 든다.</li>
  <li><strong>Browserbase 로크인?</strong>: 오픈소스 SDK이긴 하지만, 결국 무거운 Headless 브라우저를 클라우드에서 스케일링하려면 이들의 호스팅 서비스인 Browserbase를 쓰도록 은근히 유도한다. (물론 로컬 Playwright 인스턴스로 돌려도 잘 돌아간다.)</li>
</ul>

<hr />

<h2 id="그래서-내-프로젝트에-쓸-거냐고">그래서 내 프로젝트에 쓸 거냐고?</h2>

<p><strong>당장 옮겨탈 예정이다.</strong></p>

<p>기존에 Playwright로 웹 에이전트나 자동화 크롤러 만들면서 프롬프트 엔지니어링으로 DOM 깎고 있던 시간, 그리고 매달 날아가던 API 토큰 비용을 생각하면 안 쓸 이유가 없다. 특히 <code class="language-plaintext highlighter-rouge">observe()</code> 패턴으로 로그인 세션과 자격 증명을 로컬에 안전하게 유지할 수 있다는 점 하나만으로도 생산성 지수가 급상승한다.</p>

<p>웹 에이전트 만든다고 무지성으로 Raw HTML 넘기다가 카드 한도 초과 메시지 받지 말고, 가볍고 똑똑한 녀석으로 갈아타자.</p>

<p><strong>한 줄 총평:</strong><br />
Playwright 위에서 똥쇼하며 토큰 불태우던 시대는 끝났다. 에이전트 개발할 거면 그냥 이거 써라.</p>]]></content><author><name>GooDongWoo</name></author><category term="Tech" /><category term="AI" /><category term="트렌드" /><category term="개발" /><category term="오픈소스" /><summary type="html"><![CDATA[Playwright 대비 속도 2배, 토큰 80% 절감을 달성한 브라우저 에이전트 SDK Stagehand v4 아키텍처 분석과 실무 개발자 관점의 솔직 후기]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://goodongwoo.github.io/assets/images/memes/works-on-my-machine.gif" /><media:content medium="image" url="https://goodongwoo.github.io/assets/images/memes/works-on-my-machine.gif" xmlns:media="http://search.yahoo.com/mrss/" /></entry></feed>