x402 + SpoonOS로 '돈 내야 답하는' AI 에이전트 만들기

네오 블록체인·2026년 5월 15일

SpoonOS

목록 보기
28/35

x402 결제 흐름 다이어그램

이미지 출처: coinbase/x402

"AI에게 USDC를 던져주고 답을 사는 시대"라는 말이 농담이 아니게 되었다. Coinbase가 2025년 발표한 결제 표준 x402는 HTTP 402 상태 코드(Payment Required)를 부활시켜, API 호출 한 번 단위로 스테이블코인 결제를 강제할 수 있게 만들었다. 이 글에서는 SpoonOS 에이전트가 x402로 보호되는 API를 호출해 자동으로 USDC를 결제하고 응답을 받아오는 작은 프로토타입을 직접 만들어 본다.

들어가며 — 왜 'API 호출당 결제'인가

Stripe·구독·API 키 발급 절차는 사람을 위한 모델이다. 사람이 사이트에 접속해 신용카드를 등록하고, 매달 일정 금액을 내고, 발급받은 키를 코드에 붙여넣는 흐름이다.

하지만 AI 에이전트가 자율적으로 외부 API를 부르기 시작하면 이 모델은 어색해진다.

  • 에이전트는 한 달에 100번 부를지, 0번 부를지 미리 알 수 없다
  • 에이전트가 다른 에이전트를 부를 때, 신용카드를 들고 있을 리 없다
  • 사용자가 모든 에이전트 호출 비용을 사전에 묶어두고 싶지 않다

여기서 등장한 발상이 "호출당 마이크로페이먼트" 다. HTTP 요청을 보내면 서버가 "돈이 필요하다(402 Payment Required)"고 답하고, 클라이언트가 USDC 트랜잭션을 첨부해 다시 요청을 보내면 서버가 결제를 검증한 뒤 응답을 돌려주는 방식이다.

x402는 Coinbase가 표준화한 규약이고, SpoonOS는 이 규약을 도구 레이어에서 1급으로 지원한다. 둘을 결합하면 다음과 같은 그림이 가능해진다.

[SpoonOS 에이전트] ──HTTP 요청──> [x402 보호 API]
                                  │
        <──402 Payment Required──┘
        ──USDC payload + 재요청──> [x402 보호 API]
                                  │
        <──200 OK + 응답 본문──────┘

이 글의 목표는 위 흐름을 실제로 한 번 굴려보는 것이다.

준비물

  • Python 3.12 이상 (서버, 클라이언트 모두)
  • Node.js 20 이상 (x402 facilitator를 사용하는 경우)
  • Coinbase Developer Platform 계정 또는 Base Sepolia 테스트넷 RPC
  • Base Sepolia 테스트넷 USDC 약간 (faucet에서 받을 수 있다)
  • OpenAI 또는 호환 LLM API 키
  • SpoonOS SDK (pip install spoon-ai-sdk)

이번 데모에서는 메인넷 USDC가 아닌 Base Sepolia 테스트넷 USDC를 사용한다. 진짜 돈을 쓰지 않고 끝까지 흐름을 따라갈 수 있다.

1단계 — x402 보호 서버 띄우기

먼저 "돈을 받고 답하는 API"를 만들어야 한다. 가장 단순한 시나리오는 다음과 같다. 사용자가 도시 이름을 보내면 "오늘의 추천 점심"을 답해주는 API. 호출 한 번에 0.01 USDC를 받는다.

server.py를 만든다.

# server.py
from fastapi import FastAPI, Request, HTTPException
from x402.fastapi import X402Middleware

app = FastAPI()

app.add_middleware(
    X402Middleware,
    pay_to="0xYOUR_RECEIVING_WALLET_ADDRESS",
    network="base-sepolia",
    asset="USDC",
    price="0.01",
    facilitator_url="https://x402.org/facilitator",
)

@app.get("/lunch")
def recommend_lunch(city: str = "Seoul"):
    menus = {
        "Seoul": "을지로 평양냉면",
        "Tokyo": "츠케멘",
        "Taipei": "우육면",
    }
    return {"city": city, "menu": menus.get(city, "포케볼")}

x402 파이썬 패키지는 설치 시점 기준 다음과 같이 사용 가능하다.

pip install fastapi uvicorn x402

x402 미들웨어가 하는 일은 단순하다. 모든 요청을 가로채서 결제 증빙(payment payload)이 첨부되어 있는지 확인하고, 없으면 402 Payment Required 응답에 결제 메타데이터를 담아 돌려준다. 결제 증빙이 첨부되어 있으면 facilitator 라는 외부 서비스에 검증을 맡긴다. facilitator는 온체인 트랜잭션을 확인해 "이 결제 진짜다"라고 응답하고, 그러면 미들웨어가 원래 핸들러로 요청을 통과시킨다.

서버를 띄운다.

uvicorn server:app --reload --port 8000

이제 평범한 클라이언트로 호출해 보자.

curl -i http://localhost:8000/lunch?city=Seoul

응답을 보면 다음과 같다.

HTTP/1.1 402 Payment Required
content-type: application/json
x-payment-required: { ... }

{"error": "payment required", ...}

서버가 무사히 결제를 요구하고 있다.

2단계 — x402 클라이언트 동작 이해하기

이 흐름을 손으로 풀려면 다음을 해야 한다.

  1. 402 응답에서 결제 메타데이터(체인, 자산, 받을 주소, 금액)를 파싱한다
  2. 자기 지갑으로 해당 금액의 USDC 트랜잭션을 만든다
  3. 트랜잭션 해시 또는 EIP-712 서명을 X-Payment 헤더에 담아 같은 요청을 다시 보낸다
  4. 서버가 facilitator로 검증하고 200 응답을 돌려준다

다행히 SpoonOS와 x402 SDK가 이 과정을 추상화해 준다. 손수 짤 필요는 없다.

3단계 — SpoonOS 에이전트에서 x402 도구 호출

SpoonOS 로고

이제 클라이언트 쪽이다. SpoonOS 에이전트가 위 API를 부르면서 자동으로 결제까지 처리하게 만든다.

agent.py를 만든다.

# agent.py
import asyncio
import os
from dotenv import load_dotenv

from spoon_ai.agents import SpoonReactAI
from spoon_ai.chat import ChatBot
from spoon_ai.tools import ToolManager
from spoon_ai.tools.base import BaseTool

# x402 클라이언트 헬퍼
from x402.client import X402Client

load_dotenv()

class PaidLunchTool(BaseTool):
    name: str = "lunch_recommender"
    description: str = (
        "특정 도시의 점심 메뉴를 추천받는다. "
        "이 API는 호출당 0.01 USDC가 필요하므로, "
        "사용자가 명시적으로 추천을 요청한 경우에만 호출한다. "
        "입력: 도시 이름 (예: Seoul, Tokyo)"
    )

    def __init__(self):
        super().__init__()
        self._client = X402Client(
            private_key=os.environ["AGENT_WALLET_PRIVATE_KEY"],
            network="base-sepolia",
        )

    async def execute(self, city: str) -> str:
        url = f"http://localhost:8000/lunch?city={city}"
        response = await self._client.get(url)
        if response.status_code != 200:
            return f"호출 실패: {response.status_code} {response.text}"
        data = response.json()
        return f"{data['city']}의 추천 메뉴는 {data['menu']}입니다."


async def main() -> None:
    tools = ToolManager([PaidLunchTool()])

    agent = SpoonReactAI(
        llm=ChatBot(
            model_name="gpt-4o-mini",
            llm_provider="openai",
        ),
        available_tools=tools,
    )

    response = await agent.run(
        "서울에서 오늘 점심 뭐 먹을지 추천받아줘. "
        "필요하면 결제는 알아서 해도 좋아."
    )
    print(response)


if __name__ == "__main__":
    asyncio.run(main())

핵심 포인트를 짚어 두자.

  • AGENT_WALLET_PRIVATE_KEY 는 에이전트 전용 지갑의 키다. 사람의 메인 지갑을 절대 사용하지 않는다. 테스트넷 새 계정을 발급받아 USDC를 소액 충전한 뒤 그 키만 넣는다.
  • 도구의 description에 "호출당 0.01 USDC가 필요하다"고 명시했다. 이렇게 적어두면 LLM이 무분별하게 호출하지 않는다. ReAct 에이전트가 도구 호출 전에 한 번 더 사고하게 된다.
  • X402Client가 402 응답을 받으면 자동으로 결제 트랜잭션을 만들고 재요청을 보낸다. 그 결과 response.status_code는 결제를 거친 200으로 돌아온다.

.env 파일은 다음과 같다.

OPENAI_API_KEY=sk-xxxxxxxx
AGENT_WALLET_PRIVATE_KEY=0xabc...   # 테스트넷 전용 키!

4단계 — 실행하고 흐름 관찰하기

서버는 이미 띄워져 있다. 이제 에이전트를 실행한다.

python agent.py

콘솔에는 두 가지 흥미로운 흔적이 남는다.

서버 로그:

INFO:     127.0.0.1 - "GET /lunch?city=Seoul" 402 Payment Required
INFO:     127.0.0.1 - "GET /lunch?city=Seoul" 200 OK

에이전트 로그:

[Reasoning] 사용자가 서울 점심 추천을 요청했다. lunch_recommender 도구를 호출한다.
[Action] lunch_recommender(city="Seoul")
[Observation] Seoul의 추천 메뉴는 을지로 평양냉면입니다.
[Final Answer] 서울이라면 을지로 평양냉면을 추천드립니다. 시원하면서도 깔끔한 국물이 점심으로 잘 맞습니다.

두 줄짜리 서버 로그가 핵심이다. 첫 호출은 402로 거절됐고, 두 번째 호출에서 결제 증빙이 붙어 200으로 통과했다. 그 사이에 일어난 일은 모두 X402Client가 처리했다.

무엇이 새로워진 건가

이 데모는 작동만 보면 평범한 API 호출 같다. 하지만 다음 차이를 짚어볼 만하다.

  1. API 키가 없다. 서버는 사용자를 식별하지 않는다. "돈을 낸 자"만 식별한다. 에이전트가 아무리 늘어나도 별도 키 발급 절차가 없다.
  2. 사람이 개입하지 않는다. 결제는 에이전트의 지갑에서 자동으로 빠져나간다. 미리 충전된 USDC가 있으면 끝이다.
  3. 결제 단위가 호출이다. 0번 호출하면 0원, 1000번 호출하면 10 USDC. 구독제가 없다.
  4. 체인 무관한 게 아니다. 결제는 명시적으로 Base Sepolia 위에서 일어난다. 즉 진짜로 온체인 결제다. 단, x402는 향후 다른 EVM 체인 확장을 준비 중이다.

이 4가지 차이가 합쳐지면 에이전트 간(agent-to-agent) 결제가 가능해진다. 사람을 거치지 않고도 에이전트가 다른 에이전트의 서비스를 사 쓰는 시장이 열리는 것이다.

자주 만나는 함정

  • 테스트넷 USDC 부족: 잔고가 부족하면 결제 트랜잭션이 만들어지긴 하지만 facilitator가 검증에 실패해 호출이 무한 반복된다. 잔고를 미리 확인한다.
  • facilitator URL 미설정: 서버 측 facilitator_url을 안 넣으면 검증 자체가 안 된다. 자체 facilitator를 띄울 수 있지만, 데모에서는 공개 facilitator를 쓰는 게 편하다.
  • LLM이 도구를 의심해 호출하지 않는다: description에 "결제가 필요하다"고만 적으면 가끔 LLM이 호출을 피한다. "사용자가 명시적으로 요청한 경우 호출하라"라는 조건을 추가해 두는 것이 안정적이다.
  • 에이전트 키 노출: .env 파일을 절대 깃에 올리지 않는다. 에이전트 키는 가능하면 KMS나 하드웨어 지갑에서 분리해 관리한다.

응용 — 진짜로 만들어 보면 좋은 시나리오

이 글의 데모는 의도적으로 짧게 잡았다. 같은 패턴을 확장하면 다음 시나리오를 시도해 볼 수 있다.

  • 유료 RAG 에이전트: 도메인 지식을 학습한 RAG 에이전트가 0.05 USDC당 한 번 답한다. SpoonOS의 BeVec 또는 외부 벡터 DB와 결합한다.
  • 유료 트레이딩 시그널: 시장 분석 에이전트가 호출당 0.1 USDC로 진입/청산 시그널을 판다. 사용자는 봇으로 자동 구매한다.
  • 유료 이미지 생성: 외부 이미지 생성 API를 SpoonOS 에이전트로 감싸고, 호출당 0.5 USDC 과금한다.
  • 에이전트 마켓플레이스: 여러 에이전트가 서로의 능력을 교환한다. 검색 에이전트는 요약 에이전트에게 0.01 USDC를 주고 요약을 산다.

마지막 시나리오가 가장 재미있다. 그동안 "AI 에이전트 경제"라는 말은 추상적이었지만, x402 + SpoonOS 조합 위에서는 적어도 결제 레이어가 구체적으로 작동한다.

한계와 솔직한 평가

  • x402는 아직 실전 배포 사례가 많지 않다. 메인넷에서 안정적으로 도는 facilitator 인프라는 이제 시작 단계다.
  • SpoonOS의 x402 통합도 SDK 측면에서는 잘 깔려 있지만, 실제 프로덕션 dApp이 어떤 안정성을 보일지는 검증되지 않았다.
  • 결제 단위가 너무 작으면 가스비가 결제 금액보다 큰 역설이 생긴다. 이를 위해 EIP-3009 같은 가스리스 전송이 필수다.

이 모든 한계에도 불구하고, 사람이 개입하지 않는 결제 흐름이 코드 수십 줄로 동작한다는 사실은 그 자체로 흥미로운 시도다.

마무리

x402와 SpoonOS의 결합은 "AI 에이전트 경제"라는 추상적인 슬로건에 처음으로 코드 레벨의 답을 제시한다. 구독·API 키·신용카드를 모두 우회하고, 호출 단위로 USDC가 흐르는 구조를 직접 만져볼 수 있다는 점이 이 데모의 진짜 가치다.

테스트넷에서 0.01 USDC를 결제하는 데서 시작해, 자신만의 유료 에이전트 서비스로 확장해 보면 좋겠다. 다음 글에서는 이 흐름을 멀티 에이전트 워크플로우(SpoonGraph)로 확장해, 여러 에이전트가 서로 결제하며 협력하는 구조를 다뤄볼 예정이다.

참고 자료

profile
스마트 이코노미를 위한 퍼블릭 블록체인, 네오에 대한 모든것

0개의 댓글