Skip to content

블로그에 AI 기능 붙이기 (2편) - MCP 서버로 Claude 교육시키기

블로그에 AI 기능 붙이기 (2편) - MCP 서버로 Claude 교육시키기




TL;DR

  • 문제: 50개 블로그 포스팅에서 특정 주제 찾으려면 파일 탐색기 + 검색 5~10분 소요
  • 원인: Claude가 내 블로그 내용을 모름, 매번 수동으로 복사/붙여넣기 필요
  • 해결: MCP 서버 (Python FastAPI) + Private Git 웹훅 + GCP Cloud Run 배포
  • 효과: “Spring Batch 글 있어?” 한 마디로 즉시 검색, 5~10분 → 5초, 블로그가 AI의 개인 아카이브
  • 한계: Python 필수, GCP 배포 복잡도, 웹훅 설정, 초기 구축 시간 (1주), 무료 플랜 제한




글 머리말

“Spring Batch 성능 최적화 글… 예전에 썼던 것 같은데?”

블로그를 운영하면서 가장 답답한 순간입니다. 50개가 넘는 포스팅 중에서 특정 주제를 찾으려면 파일 탐색기를 열고, Cmd+F로 검색하고, 파일을 하나씩 열어봐야 합니다. 매번 5~10분씩 허비하는 게 일상이었거든요.


1편에서는 블로그에 AI 코드 설명 기능을 붙이면서 Supabase 캐싱으로 API 비용을 95% 줄인 경험을 공유했습니다. 그런데 글을 쓰다 보니 새로운 니즈가 생겼습니다. “Claude가 내 블로그 전체를 알고 있으면 얼마나 편할까?”


그래서 MCP(Model Context Protocol) 서버를 만들어봤습니다. “Spring Batch 글 있어?”라고 물으면 Claude가 즉시 검색해서 답해줍니다. 50개 파일을 뒤질 필요 없이, 제가 쓴 모든 포스팅이 AI가 직접 접근할 수 있는 개인 아카이브가 된 거죠.

이번 글에서는 Python으로 MCP 서버를 구현하고, GCP Cloud Run에 배포한 뒤, Private Git 저장소와 웹훅으로 연동한 전체 과정을 정리했습니다.





MCP란 무엇인가

MCP는 Model Context Protocol의 약자입니다. Anthropic에서 만든 표준 프로토콜인데, 쉽게 말하면 AI가 외부 데이터를 읽을 수 있게 해주는 다리 역할을 합니다.


일반 Claude vs MCP 연결된 Claude

💡비교: Claude의 능력 차이

일반 Claude:

나: "내 블로그에 Spring Batch 글이 있어?"
Claude: "죄송하지만 외부 데이터에는 접근할 수 없습니다."

MCP 연결된 Claude:

나: "내 블로그에 Spring Batch 글이 있어?"
Claude: [MCP 서버로 검색 중...]
        "네, 2개 있습니다.
         1. Spring Batch 성능 최적화 (1편) - 2023-10-14
         2. Spring Batch 성능 최적화 (2편) - 2023-10-15"

결국 MCP 서버는 AI의 눈과 귀가 되어주는 겁니다. 제 블로그의 컨텐츠, 파일 구조, 카테고리, 태그 정보를 Claude가 직접 읽고 분석할 수 있게 만들어줍니다.


MCP Tools 개념

MCP는 Tools라는 단위로 기능을 제공합니다. 각 Tool은 특정 작업을 수행하는 함수라고 생각하면 됩니다.

flowchart TB
    AI[AI Claude] --> Tools

    subgraph Tools["MCP Tools (5개)"]
        T1[list_categories<br/>카테고리 목록]
        T2[search_posts<br/>포스팅 검색]
        T3[get_post_content<br/>전체 내용 읽기]
        T4[get_stats<br/>블로그 통계]
        T5[generate_template<br/>템플릿 생성]
    end

    Tools --> Data[Markdown Files<br/>content/blog/*.md]

    style AI fill:#E3F2FD
    style T1 fill:#FFF3E0
    style T2 fill:#FFF3E0
    style T3 fill:#FFF3E0
    style T4 fill:#FFF3E0
    style T5 fill:#FFF3E0
    style Data fill:#E8F5E9

제가 구현한 Tools는 5가지입니다:

  1. list_categories: 블로그의 모든 카테고리 목록 조회
  2. search_posts: 키워드로 포스팅 검색 (제목, 태그, 본문)
  3. get_post_content: 특정 포스팅의 전체 내용 읽기
  4. get_stats: 블로그 통계 (카테고리별 분포, 연도별 추이 등)
  5. generate_template: 기존 포스팅 구조를 참고한 빈 템플릿 생성




왜 MCP 서버가 필요했나

블로그 관리의 불편함

블로그를 3년 넘게 운영하면서 쌓인 불편함이 있었습니다.

중복 주제 체크의 번거로움

새 글을 쓰려고 할 때마다 “이 주제로 이미 쓴 적 있나?” 확인이 필요했습니다. 50개 넘는 파일을 훑어보는 건 생각보다 귀찮더라고요. 그래서 종종 비슷한 내용을 다시 쓰게 되는 경우도 있었습니다.

예전 글 참고의 어려움

“JPA N+1 문제 해결 글에서 어떤 해결책 제시했더라?” 이런 걸 확인하려면 직접 파일을 찾아서 열어봐야 했습니다. 제가 쓴 글인데 검색이 안 되니 답답했어요.

블로그 통계 확인의 번거로움

카테고리별 포스팅 개수, 연도별 분포, 자주 쓰는 태그 같은 통계를 보려면 코드를 직접 돌려야 했습니다. 간단한 건데 매번 스크립트 짜기는 귀찮더라고요.


시도했던 방법들

처음엔 단순하게 접근해봤습니다.

graph TD
    Problem[문제: 50개 포스팅 관리] --> Try1[방법 1: 전체 복붙]
    Problem --> Try2[방법 2: 매번 수동 검색]
    Problem --> Try3[방법 3: RAG + 벡터 DB]
    Problem --> Solution[방법 4: MCP 서버 ✅]

    Try1 --> Fail1[❌ 토큰 제한 초과<br/>Claude 200K<br/>포스팅 500K]
    Try2 --> Fail2[❌ 검색 5-10분 소요<br/>비효율적]
    Try3 --> Fail3[❌ 과도한 비용<br/>Pinecone $70/월]
    Solution --> Success[✅ 월 100원 이하<br/>자동 동기화<br/>어디서든 접근]

    style Problem fill:#ffebee
    style Fail1 fill:#ffcdd2
    style Fail2 fill:#ffcdd2
    style Fail3 fill:#ffcdd2
    style Success fill:#c8e6c9

⚠️각 방법이 안 됐던 이유

방법 1: 블로그 전체를 Claude에 복붙 50개 포스팅을 합치면 대략 500K 토큰 정도 됩니다. Claude의 컨텍스트 윈도우가 200K인데, 절반도 못 넣어요.

방법 2: 매번 필요한 글만 복붙 이건 결국 수동 작업이잖아요. 매번 5~10분씩 검색하는 건 비효율적입니다.

방법 3: RAG (Retrieval-Augmented Generation) 벡터 DB에 블로그 글을 임베딩하는 방식인데, Pinecone 월 $70에 임베딩 API 비용까지… 개인 블로그 하나 관리하는데 좀 과하다 싶었습니다.


그러다 Anthropic의 MCP 문서를 보게 됐습니다. 가볍고, 표준화되어 있고, 확장 가능했습니다. 딱 제가 원하던 거였어요.





설계 논의: 어떤 방식으로 구현할까

MCP 서버를 만들기로 결정하고 두 가지 방식을 고민했습니다.

항목로컬 MCP (Stdio)REST API MCP (SSE)
통신 방식표준 입출력Server-Sent Events
배포로컬에서만 실행Cloud Run 배포
사용 환경Claude Desktop만Cursor, 웹 Claude 등 어디서든
파일 접근로컬 파일 직접 접근 (빠름)Git Clone 필요 (느림)
공유불가능가능
비용무료월 100원 이하
자동 동기화불가능Webhook으로 가능

최종 선택: 둘 다 만들기

고민하다가 결국 두 가지 다 만들었습니다. 각자 역할이 다르더라고요.

역할 분담
  • 로컬 MCP 서버: 글쓰기 작업에 사용
    • Claude Desktop에서 실행
    • 로컬 파일을 직접 생성/수정 가능
  • REST API MCP 서버: 검색/조회 용도로 어디서든 접근
    • Cursor IDE, 웹 Claude에서 사용
    • GCP Cloud Run에 배포
    • GitHub Webhook으로 Push 시 자동 동기화

이번 글에서는 REST API MCP 서버 (Cloud Run 배포) 구현에 집중합니다.





전체 아키텍처

먼저 큰 그림을 보겠습니다.

flowchart TB
    subgraph clients["AI 클라이언트"]
        A1["Claude Desktop"]
        A2["Cursor IDE"]
        A3["웹 Claude"]
    end

    subgraph server["MCP API Server (Cloud Run)"]
        B["Tools API"]
        S["Local Storage<br/>/tmp/blog-content/"]
        W["Webhook Handler"]
    end

    subgraph github["Private GitHub"]
        G["content/blog/"]
    end

    A1 & A2 & A3 -->|① SSE 연결| B
    B -->|② 파일 읽기| S
    S -->|③ Git Clone/Pull| G
    G -->|④ Push Event| W
    W -->|⑤ 동기화| S

    style A1 fill:#BBDEFB
    style A2 fill:#BBDEFB
    style A3 fill:#BBDEFB
    style B fill:#FFE0B2
    style S fill:#E1BEE7
    style W fill:#FFCDD2
    style G fill:#C8E6C9

핵심은 3개 레이어입니다:

  1. MCP API 레이어: AI가 사용하는 Tools 제공
  2. Git Sync 레이어: Private 저장소에서 컨텐츠 가져오기
  3. Webhook 레이어: Push 시 자동으로 최신 내용 동기화




단계별 구현

1단계: Git 동기화 구현

왜 이게 필요한가

가장 먼저 해결해야 할 문제는 Private Git 저장소 접근입니다. 블로그 저장소가 Private이라서 GitHub Personal Access Token(PAT)으로 인증해야 합니다. 서버 시작할 때 Git Clone하고, Webhook 받으면 Git Pull하는 구조로 만들었습니다.

Git 동기화 상태 흐름

stateDiagram-v2
    [*] --> Uninitialized: 서버 시작

    Uninitialized --> Cloning: initialize() 호출
    Cloning --> Ready: Clone 성공
    Cloning --> Failed: 인증 실패

    Failed --> Cloning: 재시도

    Ready --> Pulling: Webhook 수신
    Pulling --> Ready: Pull 성공
    Pulling --> Failed: Pull 실패

    Ready --> Serving: AI 요청
    Serving --> Ready: 응답 완료

핵심 코드

# mcp-server/src/git_sync.py
import subprocess
from pathlib import Path
import os

class GitContentSync:
    def __init__(self, repo_url, content_dir="/tmp/blog-content"):
        # PAT로 인증 URL 생성 (https://token@github.com/... 형태)
        gh_token = os.getenv("GH_PAT", "")
        if gh_token:
            self.repo_url = repo_url.replace("https://", f"https://{gh_token}@")

        self.content_dir = Path(content_dir)
        self.is_initialized = False

    def initialize(self) -> bool:
        """서버 시작 시 Git Clone"""
        env = os.environ.copy()
        env['GIT_TERMINAL_PROMPT'] = '0'  # 컨테이너에서 프롬프트 비활성화

        result = subprocess.run([
            "git", "clone", "--depth", "1",  # 최근 커밋만 (shallow clone)
            self.repo_url, str(self.content_dir)
        ], env=env, timeout=60)

        return result.returncode == 0

    def pull(self) -> bool:
        """Webhook 수신 시 Git Pull"""
        result = subprocess.run([
            "git", "-C", str(self.content_dir),
            "pull", "origin", "master"
        ], timeout=30)

        return result.returncode == 0

핵심 포인트

  • --depth 1: 전체 히스토리 대신 최근 커밋만 가져옵니다. 덕분에 3~5초면 Clone이 끝나요.
  • GIT_TERMINAL_PROMPT=0: Docker 컨테이너에서 Git이 비밀번호 묻지 않게 합니다. 안 하면 멈춰버려요.
  • 환경변수로 PAT 전달: 코드에 토큰 하드코딩하면 안 됩니다. Cloud Run 환경변수로 관리하세요.

2단계: FastAPI REST API 구현

왜 FastAPI인가

MCP Tools를 REST API로 제공하고 Webhook 엔드포인트도 만들어야 하는데, FastAPI가 가볍고 빠르고 문서 자동 생성도 해줘서 선택했습니다. Flask도 고려했는데 async 지원이 FastAPI가 더 낫더라고요.

서버 시작 시 Git Clone

# mcp-server/src/rest_api.py
from fastapi import FastAPI, Request, Header, HTTPException
import hmac
import hashlib

app = FastAPI(title="MCP REST API")

@app.on_event("startup")
async def startup_event():
    """서버 시작할 때 Git Clone"""
    success = initialize_git_sync()
    if success:
        logger.info("✅ Git sync 초기화 완료")
    else:
        logger.error("❌ Git sync 초기화 실패")

Webhook 엔드포인트

@app.post("/webhook/github")
async def github_webhook(
    request: Request,
    x_hub_signature_256: str = Header(None)
):
    """GitHub Webhook 수신 - Push 이벤트 처리"""
    payload_bytes = await request.body()
    payload = await request.json()

    # HMAC 서명 검증 (GitHub에서 온 요청인지 확인)
    expected_signature = "sha256=" + hmac.new(
        WEBHOOK_SECRET.encode(),
        payload_bytes,
        hashlib.sha256
    ).hexdigest()

    if not hmac.compare_digest(expected_signature, x_hub_signature_256):
        raise HTTPException(status_code=401, detail="Invalid signature")

    # Push 이벤트만 처리
    event_type = request.headers.get("X-GitHub-Event", "")
    if event_type == "push":
        ref = payload.get("ref", "")

        # master/main 브랜치만 동기화
        if ref in ["refs/heads/master", "refs/heads/main"]:
            git_sync = get_git_sync()
            success = git_sync.pull()

            return {"success": success, "message": "Content synced"}

    return {"success": True, "message": "Ignored"}

핵심 포인트

  • HMAC 서명 검증: GitHub에서 보낸 요청인지 확인합니다. 이거 안 하면 아무나 Webhook 호출할 수 있어요.
  • master/main 브랜치만 처리: 다른 브랜치는 무시합니다. feature 브랜치까지 동기화할 필요 없잖아요.
  • 자동 Git Pull: Push하면 보통 5초 안에 최신 컨텐츠가 동기화됩니다.

3단계: MCP Tools 구현

포스팅 검색 Tool

AI가 실제로 사용할 검색 기능입니다. 제목, 카테고리, 태그, 본문 전체에서 키워드를 찾아줍니다.

# mcp-server/src/tools/search_posts.py
from typing import List, Dict
from ..blog_parser.markdown_parser import get_all_posts

def search_posts(keyword: str) -> List[Dict]:
    """키워드로 포스팅 검색"""
    posts = get_all_posts()
    results = []

    keyword_lower = keyword.lower()

    for post in posts:
        # 제목, 카테고리, 태그, 본문에서 검색
        if (keyword_lower in post.get('title', '').lower() or
            keyword_lower in post.get('category', '').lower() or
            any(keyword_lower in tag.lower() for tag in post.get('tags', [])) or
            keyword_lower in post.get('content', '').lower()):

            results.append({
                'title': post.get('title'),
                'date': post.get('date'),
                'category': post.get('category'),
                'tags': post.get('tags', []),
                'path': post.get('path'),
                'excerpt': post.get('content', '')[:200] + '...'
            })

    return results

블로그 통계 Tool

전체 현황을 한눈에 볼 수 있는 통계 기능입니다.

# mcp-server/src/tools/get_stats.py
from typing import Dict
from ..blog_parser.markdown_parser import get_all_posts
from collections import defaultdict

def get_stats() -> Dict:
    """블로그 전체 통계"""
    posts = get_all_posts()

    categories = defaultdict(int)
    tags = defaultdict(int)
    years = defaultdict(int)

    for post in posts:
        # 카테고리별 집계
        category = post.get('category', 'Uncategorized')
        categories[category] += 1

        # 태그별 집계
        for tag in post.get('tags', []):
            tags[tag] += 1

        # 연도별 집계
        date = post.get('date', '')
        if date:
            year = date[:4]
            years[year] += 1

    return {
        "total_posts": len(posts),
        "categories": dict(categories),
        "top_tags": dict(sorted(tags.items(), key=lambda x: x[1], reverse=True)[:10]),
        "posts_by_year": dict(years)
    }

핵심 포인트

  • 다양한 검색 필드: 제목만 검색하면 놓치는 게 많아서, 카테고리, 태그, 본문까지 전부 검색합니다.
  • excerpt 제공: 전체 내용 대신 앞 200자만 보여줍니다. 컨텍스트 창 절약도 되고 미리보기도 되고요.
  • Top 10 태그: 태그가 너무 많으면 보기 힘들어서 상위 10개만 보여줍니다.

4단계: GCP Cloud Run 배포

Dockerfile

FROM python:3.11-slim

WORKDIR /app

# Git 설치 (필수! 이거 없으면 Clone이 안 됩니다)
RUN apt-get update && apt-get install -y \
    git \
    && rm -rf /var/lib/apt/lists/*

# Python 의존성
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

# 소스 코드
COPY src/ ./src/

# Git Clone할 임시 디렉토리
RUN mkdir -p /tmp/blog-content

EXPOSE 8001

CMD ["python", "-m", "uvicorn", "src.rest_api:app", "--host", "0.0.0.0", "--port", "8001"]

GitHub Actions 배포 설정

# .github/workflows/deploy-mcp-server.yml (핵심 부분)
gcloud run deploy mcp-api-server \
  --image $IMAGE_NAME \
  --memory 512Mi \
  --min-instances 0 \  # 사용 안 할 때 자동 종료 (비용 절감)
  --max-instances 1 \  # 개인용이라 1개면 충분
  --set-env-vars "WEBHOOK_SECRET=${{ secrets.WEBHOOK_SECRET }},GH_PAT=${{ secrets.GH_PAT }}"

핵심 포인트

  • Git 설치: Docker 이미지에 Git이 없으면 Clone/Pull이 안 됩니다. apt-get install git 필수예요.
  • /tmp/blog-content: Cloud Run은 읽기 전용이라서 /tmp에 저장해야 합니다.
  • min-instances 0: 사용하지 않을 때 인스턴스가 꺼지니까 비용이 거의 안 나와요.

5단계: GitHub Webhook 설정

Webhook 동작 흐름

sequenceDiagram
    participant Dev as 개발자
    participant Git as GitHub
    participant Hook as Webhook
    participant MCP as MCP Server
    participant Store as Local Storage

    Dev->>Git: git push origin master

    Note over Git: Push 이벤트 발생

    Git->>Hook: POST /webhook/github<br/>+ HMAC 서명

    Note over Hook: 서명 검증

    Hook->>MCP: 검증 성공

    MCP->>Store: git pull origin master

    Note over Store: 최신 컨텐츠로 업데이트

    Store-->>MCP: Pull 완료
    MCP-->>Hook: 200 OK
    Hook-->>Git: Webhook 처리 완료

    Note over MCP: 새로운 포스팅<br/>즉시 검색 가능!

설정 방법

  1. Secret 생성: openssl rand -hex 32로 32바이트 랜덤 문자열 생성
  2. GitHub Secret 추가: Repository → Settings → Secrets → WEBHOOK_SECRET 등록
  3. Webhook 등록: Repository → Settings → Webhooks → Add webhook
    • Payload URL: Cloud Run URL + /webhook/github
    • Content type: application/json
    • Secret: 위에서 생성한 값
    • Events: Just the push event 선택

핵심 포인트

실제로 테스트해보니 블로그 글을 Push하면 5초 안에 MCP 서버가 최신 컨텐츠를 동기화합니다. 거의 실시간이에요.





실전 활용 사례

실제로 어떻게 쓰고 있는지 몇 가지 예를 들어볼게요.

중복 주제 체크

나: "Spring Batch 성능 최적화라는 주제로 글을 쓴 적 있어?"

Claude: [search_posts("Spring Batch") 호출]
"네, 2개의 관련 포스팅이 있습니다:
1. Spring Batch 성능 최적화 (1편) - 2023-10-14
2. Spring Batch 성능 최적화 (2편) - 2023-10-15

두 글 모두 병목 분석과 20배 성능 개선 내용을 다루고 있네요."

예전 같았으면 파일 탐색기로 10분 걸렸을 작업인데, 이제 5초면 됩니다.


이전 글 참고

나: "JPA N+1 문제 해결 글에서 어떤 방법을 추천했지?"

Claude: [get_post_content("jpa-n-plus-one") 호출]
"3가지 해결 방법을 제시하셨습니다:
1. fetch join 사용
2. @BatchSize 어노테이션
3. @EntityGraph 활용

특히 fetch join을 우선 추천하셨고, 페이징과 함께 쓸 때
주의사항도 언급하셨습니다."

제가 쓴 글인데 기억이 안 날 때가 있거든요. 이제 Claude한테 물어보면 됩니다.


블로그 통계 확인

나: "내 블로그 통계 보여줘"

Claude: [get_stats() 호출]
"블로그 통계입니다:
- 총 포스팅: 54개
- 카테고리별:
  * Spring: 15개
  * Java: 10개
  * AI: 6개
  * Architecture: 4개
- 연도별:
  * 2025: 12개
  * 2024: 20개
  * 2023: 22개
- 자주 쓰는 태그: Spring, JPA, 성능 최적화, AI..."

매번 스크립트 돌릴 필요 없이 물어보면 바로 나옵니다.





비용 및 성능

실제 운영 비용

Cloud Run 콘솔에서 직접 확인한 수치입니다.

항목월 비용
GCP Cloud Run약 100원 ($0.08)
GitHub Webhook무료
GitHub PAT무료
총 비용월 100원 이하
자판기 음료 1/10 가격

월 100원도 안 되는 비용으로:

  • 50개 이상의 포스팅을 즉시 검색
  • Push할 때마다 자동 동기화
  • 어디서든 접근 가능 (Cursor, 웹 Claude)

시간으로 따지면 주당 1시간 정도 절약되는 것 같습니다.


성능 측정 결과

Chrome DevTools로 응답 시간을 측정해봤습니다.

작업응답 시간
포스팅 검색 (search_posts)약 200ms
전체 내용 읽기 (get_post_content)약 300ms
블로그 통계 (get_stats)약 150ms
Git Pull (Webhook)3-5초

50개 포스팅을 검색해도 200ms면 끝납니다. 로컬 파일 시스템에서 읽는 거라 빠르더라고요.





내 프로젝트에 바로 적용하기

체크리스트

  • Python 3.11 이상 설치되어 있는가?
  • GCP 계정과 프로젝트가 준비되었는가?
  • GitHub Personal Access Token을 발급받았는가?
  • FastAPI 기본 개념을 이해하고 있는가?
  • Docker 이미지 빌드 경험이 있는가?

주의사항

🚨절대 하지 말 것

❌ GitHub PAT를 코드에 하드코딩

  • 절대 금지입니다. 환경변수로만 관리하세요.
  • 실수로 커밋하면 GitHub이 자동으로 토큰을 무효화합니다.

❌ Webhook Secret 없이 배포

  • 누구나 Webhook을 호출할 수 있게 됩니다.
  • HMAC 서명 검증은 필수예요.

❌ 모든 브랜치 동기화

  • master/main만 동기화하세요.
  • feature 브랜치까지 동기화하면 불필요한 트래픽만 늘어납니다.

❌ Git Clone에서 —depth 옵션 빼기

  • 전체 히스토리를 받으면 시간도 오래 걸리고 용량도 커집니다.
  • --depth 1로 최근 커밋만 받으세요.

추천 설정

검증된 설정값

Cloud Run 설정:

memory: 512Mi # 50개 포스팅 기준 충분
min-instances: 0 # 비용 절감 (안 쓰면 자동 종료)
max-instances: 1 # 개인용이면 1개로 충분
timeout: 60s # Git 작업 고려해서 넉넉하게

Git 설정:

git clone --depth 1  # shallow clone으로 빠르게
git pull origin master  # 메인 브랜치만

Webhook 설정:

  • Events: push만 선택
  • Secret: 32자 랜덤 문자열 (openssl rand -hex 32)

트러블슈팅

Q. “Git Clone이 실패해요”

  • GitHub PAT 권한 확인하세요. repo 전체 권한이 필요합니다.
  • Private 저장소라면 PAT가 필수입니다.
  • GIT_TERMINAL_PROMPT=0 환경변수 설정됐는지 확인해보세요.

Q. “Webhook이 작동 안 해요”

  • Secret이 Cloud Run 환경변수와 GitHub Webhook 설정에서 동일한지 확인하세요.
  • Cloud Run 로그에서 에러 메시지 확인해보세요.
  • HMAC 서명 검증 코드가 제대로 동작하는지 디버깅해보세요.

Q. “배포가 너무 느려요”

  • Docker 이미지 크기 확인하세요. 500MB 이하가 좋습니다.
  • --depth 1 옵션으로 Clone 시간 단축하세요.
  • Cloud Run 메모리가 부족하면 느려질 수 있어요. 512Mi로 설정해보세요.




시스템 점검 체크리스트

저도 배포 전에 이 항목들을 꼭 확인합니다. MCP + Git Webhook를 구축한다면 참고하시면 좋을 것 같습니다.

  • GitHub PAT 보안: Personal Access Token이 환경변수에만 있고, 코드에 하드코딩하지 않았는가?
  • Webhook Secret: HMAC 서명 검증으로 GitHub에서 온 요청만 처리하는가?
  • Git Clone 최적화: —depth 1 옵션으로 shallow clone을 사용하는가? (빠른 동기화)
  • 브랜치 필터링: master/main 브랜치만 동기화하고, feature 브랜치는 무시하는가?
  • Cloud Run 설정: 메모리(512Mi), 타임아웃(60s)이 적절하게 설정되어 있는가?



마무리

MCP 서버를 만들면서 가장 놀라웠던 건 생각보다 어렵지 않다는 점이었습니다.

처음엔 “AI 연동”이라고 하면 뭔가 거창한 인프라가 필요할 줄 알았어요. 벡터 DB에 임베딩 API에… 근데 실제로 필요한 건 FastAPI로 REST API 만들고, Git Clone/Pull 구현하는 게 전부였습니다.


월 100원도 안 되는 비용으로 제 블로그 전체가 AI의 지식이 됐습니다.

“이 주제로 글 썼어?”, “예전에 어떤 설정 썼더라?”, “블로그 통계 보여줘” — 이런 질문에 3초 안에 답이 옵니다. 예전에는 파일 탐색기 열어서 10분씩 뒤졌는데, 이제는 Claude한테 물어보면 끝입니다.


사실 이 글을 쓸 때도 MCP 서버를 활용했습니다. “1편에서 뭘 다뤘더라?” 물어보니까 바로 요약해주더라고요. 시리즈 글 쓸 때 앞 편 내용 참고하기가 정말 편해졌습니다.

블로그를 운영하시는 분들, 특히 포스팅이 50개 이상 쌓인 분들께 강력 추천합니다. 투자 대비 효과가 확실합니다.





참고 :

Anthropic MCP 공식 문서: https://docs.anthropic.com/mcp
FastAPI 공식 가이드: https://fastapi.tiangolo.com
GCP Cloud Run 문서: https://cloud.google.com/run/docs
GitHub Webhook 가이드: https://docs.github.com/webhooks




읽어주셔서 감사합니다.🖐


Ramsbaby
Written byRamsbaby
이 블로그는 직접 개발/운영하는 블로그이므로 당신을 불쾌하게 만드는 불필요한 광고가 없습니다.

#My Github#소개 페이지#Blog OpenSource Github#Blog OpenSource Demo Site