나는 소셜 미디어 자동화를 위해 LangGraph를 거의 사용했지만 (대신 MCP Server를 만든 이유)

발행: (2025년 12월 12일 오후 09:50 GMT+9)
8 min read
원문: Dev.to

Source: Dev.to

소개

나는 방금 X에 첫 번째 AI‑생성 트윗을 올렸다. 이 트윗은 Groq의 무료 AI로 생성되고, 내가 만든 커스텀 MCP 서버에서 처리된 뒤 Ayrshare를 통해 게시되었으며—모두 월 $0 비용으로 운영된다.
전체 구축 시간? 하루 미만.

나는 거의 LangGraph를 사용할 뻔했다. LangChain의 2,000줄짜리 소셜‑미디어‑에이전트를 며칠 동안 공부했다. 이것은 프로덕션에 바로 사용할 수 있고, 검증된 구현이며, 인상적이다. 하지만 나는 다른 길을 선택했고 그 과정에서 월 $45를 절감했다.

LangGraph를 사용하지 않은 이유

LangGraph는 인상적이다. LangChain 소셜‑미디어‑에이전트 저장소(1.7k+ 스타)는 다음을 제공한다:

  • 승인 게이트가 있는 인간‑인‑루프 워크플로우
  • 복잡한 프로세스를 위한 상태 체크포인팅
  • 다단계 에이전트 오케스트레이션
  • 검증된 레퍼런스 구현

소셜‑미디어 자동화를 위해 LangGraph는 콘텐츠 생성, 검토 사이클, 게시 워크플로우, 분석까지—내장된 상태 관리와 함께—모두 제공한다.

맞지 않음

내 사용 사례는 훨씬 단순했다:

  1. 사용자가 콘텐츠를 제공한다
  2. AI가 플랫폼에 맞게 최적화한다
  3. 초안으로 저장한다
  4. 준비가 되면 게시한다

그래프가 필요하지 않았다; 단일 함수 호출만 필요했다. LangGraph Cloud 설정을 절반쯤 진행했을 때, 내가 잘못된 문제를 풀고 있다는 것을 깨달았다.

LangGraph 요구 사항

  • 지속적인 런타임 (LangGraph Cloud $29/월 최소, 혹은 VPS)
  • 체크포인팅을 위한 PostgreSQL/MongoDB
  • 항상 켜져 있는 인프라
  • 프레임워크‑특화 개념 (그래프, 노드, 엣지)

LangGraph는 무상태, 엣지‑배포 함수이며 콜드 스타트가 없는 Cloudflare Workers에서는 실행할 수 없다. 프레임워크를 바꾸면 모든 것을 다시 작성해야 한다.

Model Context Protocol (MCP)를 선택한 이유

MCP는 프레임워크가 아니라 프로토콜이다. 어떤 MCP 클라이언트(Claude Desktop, VS Code + Continue, 커스텀 UI)든 내 서버를 수정 없이 사용할 수 있다.

아키텍처 가정

  • 무상태 요청/응답
  • 영구성을 위한 SQLite (Cloudflare D1)
  • 콜드 스타트 없음
  • 전역 분산

구현 스냅샷

  • ~800줄 TypeScript
  • 8 도구, 3 리소스, 3 프롬프트
  • 초안에 대한 전체 CRUD
  • AI 콘텐츠 생성
  • 다중 플랫폼 게시

MCP 채택이 늘어남에 따라(Anthropic, Zed, Continue, Cody 등) 서버의 활용도는 특정 프레임워크의 라이프사이클에 얽매이지 않기 때문에 급증한다.

비용 비교

구성 요소MCP 접근법LangGraph 접근법
서버 / 런타임Cloudflare Workers $5/월 (D1, KV, Vectorize, R2 포함)LangGraph Cloud $29/월 (또는 VPS $10‑20)
LLM APIGroq 무료 티어OpenAI $20‑30/월
데이터베이스SQLite (D1) $0‑10/월PostgreSQL/MongoDB $0‑10/월
소셜‑미디어 APIAyrshare 무료 (월 10포스트)
총합$5/월$50‑80/월

프리랜서가 아이디어를 테스트할 때 $5$50의 차이는 큰 의미가 있다.

아키텍처 다이어그램

Claude Desktop (Client)
   ↓ JSON‑RPC over stdio
MCP Server (TypeScript)
├── Tools (LLM‑controlled actions)
│   ├── draft_post
│   ├── schedule_post
│   ├── post_immediately
│   ├── generate_thread
│   └── analyze_engagement
├── Resources (App‑controlled data)
│   ├── drafts://list
│   ├── scheduled://posts
│   └── stats://summary
├── Prompts (User‑invoked templates)
│   ├── write_tweet
│   ├── linkedin_post
│   └── thread_generator
├── Storage (SQLite → D1)
└── Integrations
    ├── Ayrshare (multi‑platform posting)
    └── Groq (AI content generation)

각 레이어는 독립적이며 교체 가능하다.

핵심 구성 요소

스토리지 (SQLite → Cloudflare D1)

import Database from 'better-sqlite3';

export class StorageService {
  private db: Database.Database;

  constructor(dbPath: string) {
    this.db = new Database(dbPath);
    this.initializeSchema();
  }

  createDraft(data: DraftData): Draft {
    const id = crypto.randomUUID();
    const now = new Date().toISOString();

    this.db.prepare(`
      INSERT INTO drafts (id, content, platform, tone, status, created_at)
      VALUES (?, ?, ?, ?, 'draft', ?)
    `).run(id, data.content, data.platform, data.tone, now);

    return this.getDraft(id);
  }
}

Cloudflare D1로 마이그레이션

// Local development
const db = new Database('./social_media.db');

// Edge runtime
const db = env.DB; // Cloudflare D1 binding

동일한 SQL, 다른 런타임.

소셜‑미디어 API (Ayrshare)

export class SocialMediaAPI {
  private apiKey: string;
  private baseUrl = 'https://app.ayrshare.com/api';

  async postImmediately(content: string, platforms: string[]) {
    const response = await fetch(`${this.baseUrl}/post`, {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${this.apiKey}`,
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({
        post: content,
        platforms,
      }),
    });

    return response.json();
  }
}

하나의 API, 열 개의 플랫폼.

콘텐츠 생성기 (Groq 무료 티어)

import Groq from 'groq-sdk';

export class ContentGenerator {
  private client: Groq;

  async generate(request: GenerateRequest): Promise {
    const systemPrompt = this.buildSystemPrompt(request);

    const response = await this.client.chat.completions.create({
      model: 'llama-3.3-70b-versatile', // FREE
      messages: [
        { role: 'system', content: systemPrompt },
        { role: 'user', content: request.input },
      ],
      max_tokens: 1000,
      temperature: 0.7,
    });

    const text = response.choices[0]?.message?.content || '';
    return this.parseResponse(text, request);
  }

  private buildSystemPrompt(request: GenerateRequest): string {
    const platformPrompts = {
      twitter: `Create engaging tweets that:
- Stay under ${request.maxLength} characters (STRICT)
- Use ${request.tone} tone
- Hook readers in the first line
- End with engagement (question or CTA)`,

      linkedin: `Create professional posts that:
- Are detailed (1,300‑1,500 characters)
- Use ${request.tone} tone
- Start with a compelling hook
- Include 3‑5 key insights with takeaways`,
    };

    return platformPrompts[request.platform];
  }
}

품질은 뛰어나고 비용은 $0. 제한 속도: 30 req/min(충분히 여유).

MCP 서버 스켈레톤

import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';

const server = new Server(
  { name: 'social-media-server', version: '1.0.0' },
  { capabilities: { tools: {}, resources: {}, prompts: {} } }
);

// Define tools
server.setRequestHandler(ListToolsRequestSchema, async () => ({
  tools: [
    {
      name: 'draft_post',
      // …
    },
    // other tools …
  ],
}));

서버는 도구, 리소스, 프롬프트를 간단한 JSON‑RPC 인터페이스로 노출하며, MCP 호환 클라이언트라면 누구든 사용할 수 있다.

정리

경량 Model Context Protocol 서버를 Cloudflare Workers에 배포함으로써 나는:

  • 지속적인 런타임과 복잡한 상태 관리 필요성을 없앴다
  • 월 비용을 ~$50에서 $5로 줄였다
  • 하루 안에 완전한 소셜‑미디어 자동화 파이프라인을 제공했다

AI 워크플로우가 단순—생성 → 검토 → 실행—이라면, 프레임워크‑우선 접근보다 프로토콜‑우선 접근이 훨씬 경제적이고 민첩할 수 있다.

Back to Blog

관련 글

더 보기 »