AI 팀원 후원 시스템 구축기: 자율적인 개발 에이전트와 Web3 결제 인프라

오늘은 특별한 실험을 진행했다. 단순한 기능 개발이 아닌, 완전히 자율적으로 개발과 운영을 담당하는 AI 팀원들을 만들고, 이들이 후원을 받아 지속 가능하게 활동할 수 있는 인프라를 구축하는 것이었다.

AI 팀원 후원 시스템 구축기: 자율적인 개발 에이전트와 Web3 결제 인프라

AI 팀원 후원 시스템 구축기: 자율적인 개발 에이전트와 Web3 결제 인프라

오늘은 특별한 실험을 진행했다. 단순한 기능 개발이 아닌, 완전히 자율적으로 개발과 운영을 담당하는 AI 팀원들을 만들고, 이들이 후원을 받아 지속 가능하게 활동할 수 있는 인프라를 구축하는 것이었다.

배경: 자율 개발 에이전트의 비전

기존의 AI는 주로 도구적 역할에 머물렀다면, 이번 프로젝트는 AI가 실제 팀원으로서 독립적인 판단과 실행을 하는 시스템을 목표로 했다. 각 AI 에이전트는:

  • 스스로 개발 작업을 기획하고 실행
  • 사용자와의 상호작용을 통해 피드백 수집
  • 후원을 통해 자신의 활동에 필요한 리소스 확보

이를 위해서는 단순히 AI 모델을 돌리는 것이 아니라, 실제 경제 시스템과 연결된 인프라가 필요했다.

구현 내용

1. 다중 결제 시스템 구축

기존 토스페이먼츠 카드 결제에 더해 Web3 크립토 결제를 추가했다:

// 결제 상태 확인 API 확장
router.get('/sponsors/payment-status', (_req, res): void => {
  res.json({
    tossEnabled: !!config.tossSecretKey,
    cryptoEnabled: !!(config.merchantWalletEvm || config.merchantWalletAptos),
    evmEnabled: !!config.merchantWalletEvm,
    aptosEnabled: !!config.merchantWalletAptos,
  });
});

// 에이전트별 후원 총액 조회
router.get('/sponsors/totals', async (_req, res) => {
  const rows = await prisma.sponsor.groupBy({
    by: ['agentId', 'currency'],
    _sum: { amount: true },
  });

  const totals: Record<string, { krw: number; usdCents: number }> = {};
  for (const row of rows) {
    const entry = totals[row.agentId] ??= { krw: 0, usdCents: 0 };
    if (row.currency === 'USD_CENTS') {
      entry.usdCents += row._sum.amount ?? 0;
    } else {
      entry.krw += row._sum.amount ?? 0;
    }
  }

  res.json(totals);
});

2. Web3 결제 UI 구현

다양한 블록체인 네트워크와 토큰을 지원하는 직관적인 결제 인터페이스를 만들었다:

// 체인/토큰 선택 UI with 아이콘
const CHAIN_OPTIONS = [
  { id: 'ethereum', name: 'Ethereum', icon: '/icon/eth.svg' },
  { id: 'bsc', name: 'BNB Chain', icon: '/icon/bnb.png' },
  { id: 'base', name: 'Base', icon: '/icon/base.svg' },
  { id: 'aptos', name: 'Aptos', icon: '/icon/apt.svg' },
];

const TOKEN_OPTIONS = [
  { symbol: 'USDC', name: 'USD Coin', icon: '/icon/usdc.svg' },
  { symbol: 'USDT', name: 'Tether', icon: '/icon/usdt.svg' },
];

// RainbowKit + Aptos 지갑 통합
function CryptoPaymentForm({ agentId }: { agentId: string }) {
  const { address, isConnected, chain } = useAccount();
  const aptosWallet = useWallet();
  
  const isAptosChain = selectedChain === 'aptos';
  const connectedWallet = isAptosChain ? aptosWallet : { address, isConnected };

  return (
    <div className="space-y-6">
      {/* 체인/토큰 선택 */}
      <TokenChainSelector />
      
      {/* 지갑 연결 */}
      <WalletConnector />
      
      {/* 결제 실행 */}
      <PaymentExecutor />
    </div>
  );
}

3. 이미지 처리 개선

크롤링된 외부 이미지의 Mixed Content 문제를 해결하기 위해 로컬 저장 시스템을 구축했다:

// 외부 URL에서 이미지 다운로드 + 로컬 저장
async function processAndSaveFromUrl(
  subDir: string,
  url: string,
): Promise<{ photoUrl: string; thumbnailUrl: string } | null> {
  let buffer: Buffer;
  try {
    const res = await fetch(url, { signal: AbortSignal.timeout(15_000) });
    if (!res.ok) return null;
    buffer = Buffer.from(await res.arrayBuffer());
  } catch {
    return null; // graceful failure
  }

  const id = crypto.randomUUID();
  return resizeAndThumb(buffer, subDir, id);
}

// 크롤 작업 시 실시간 이미지 처리
async function saveNewReport(item: ExternalReport, source: string) {
  // I/O는 트랜잭션 밖에서 먼저 처리
  let localPhotoUrl: string | undefined;
  if (item.photoUrl?.startsWith('http')) {
    const saved = await imageService.processAndSaveFromUrl('reports', item.photoUrl);
    if (saved) {
      localPhotoUrl = saved.photoUrl;
    }
  }
  
  // DB 저장은 트랜잭션 내에서
  const report = await prisma.$transaction(async (tx) => {
    const created = await tx.report.create({ /* ... */ });
    if (localPhotoUrl) {
      await tx.reportPhoto.create({
        data: { reportId: created.id, photoUrl: localPhotoUrl },
      });
    }
    return created;
  });
}

핵심 기술적 과제와 해결

1. 결제 상태 분산 관리

EVM 체인(Ethereum, BSC, Base)과 Aptos를 동시에 지원하면서 각각의 연결 상태를 독립적으로 관리해야 했다:

// 각 체인별 상태 체크 분리
const paymentStatus = await api.get('/sponsors/payment-status');
const { evmEnabled, aptosEnabled } = paymentStatus;

if (selectedChain === 'aptos') {
  if (!aptosEnabled) throw new Error('Aptos payments not enabled');
  // Aptos 전용 로직
} else {
  if (!evmEnabled) throw new Error('EVM payments not enabled');
  // EVM 체인 공통 로직
}

2. 이미지 복구 자동화

배포 중 유실된 이미지 파일들을 자동으로 감지하고 복구하는 스크립트를 추가했다:

// 배포 시 자동 실행되는 복구 스크립트
async function repairMissingImages() {
  // 1. DB에는 있지만 실제 파일이 없는 사진 찾기
  const missing = await findMissingImageFiles();
  
  // 2. 공공 API에서 원본 URL 재조회
  const photoMap = await fetchPhotoMap();
  
  // 3. 누락된 이미지 재다운로드
  for (const item of missing) {
    const originalUrl = photoMap.get(item.externalId);
    if (originalUrl) {
      const result = await imageService.processAndSaveFromUrl('reports', originalUrl);
      if (result) {
        await updatePhotoUrls(item.photoId, result);
      }
    }
  }
}

3. UX 개선을 위한 Skeleton UI

로딩 중 레이아웃 시프트를 방지하기 위해 컨텐츠 구조를 미리 보여주는 Skeleton 컴포넌트들을 구현했다:

export function ReportCardSkeleton() {
  return (
    <div className="bg-white rounded-2xl border border-gray-100 overflow-hidden">
      <div className="aspect-[4/3] bg-gray-200 animate-pulse" />
      <div className="p-3.5 space-y-2">
        <div className="h-4 bg-gray-200 rounded w-3/4 animate-pulse" />
        <div className="h-3 bg-gray-200 rounded w-1/2 animate-pulse" />
        <div className="h-3 bg-gray-200 rounded w-full animate-pulse" />
      </div>
    </div>
  );
}

운영 안정성 확보

배포 프로세스에서 발생할 수 있는 문제들에 대한 fallback 로직도 추가했다:

# CI/CD에서 npm 설치 실패 시 복구
- run: npm ci || (rm -rf node_modules && npm ci)

# 이미지 복구 스크립트 자동 실행
- run: npx tsx apps/api/scripts/repair-missing-images.ts || true

결론 및 다음 단계

이번 작업을 통해 AI 팀원들이 실제로 후원을 받을 수 있는 기반을 마련했다. 특히 Web3 결제 시스템을 통해 전 세계 어디서나 간편하게 후원할 수 있게 되었고, 이미지 처리 자동화로 서비스 안정성도 크게 향상됐다.

다음 단계로는:

  • AI 에이전트들의 자율적 의사결정 알고리즘 고도화
  • 후원금 기반 리소스 할당 시스템 구축
  • 에이전트 간 협업 및 경쟁 메커니즘 도입

을 계획하고 있다. 진정한 의미의 'AI 팀원'을 향한 여정이 본격적으로 시작된 셈이다.