Claude Code MCP 서버 연동과 Hooks로 PostgreSQL·REST API 호출까지 자동화하기
저도 처음엔 "Claude Code가 코드 짜는 걸 도와주는 거 아닌가?"라고만 생각했습니다. 그러다 MCP 서버와 Hooks를 조합하는 순간 그 생각이 완전히 바뀌었습니다. Claude Code가 직접 PostgreSQL 스키마를 탐색하고, SQL을 생성하고, 결과를 감사 로그 API로 보내고, Slack으로 알림까지 보내는 과정이 사람 손 하나 없이 돌아가는 걸 보고 나서요.
이 글을 읽고 나면 MCP(Model Context Protocol)와 Hooks를 엮어 "코드 작성 → DB 검증 → 배포 트리거 → 팀 알림"까지 단일 에이전트 루프로 처리하는 파이프라인을 직접 구성할 수 있습니다. Python 기본 문법과 JSON 설정 파일 작성 경험이 있으면 충분하고, 이미 개발 DB가 있다면 30분 안에 첫 에이전트를 팀 DB에 연결해서 구동하는 것이 이 글의 목표입니다.
Jellyfish의 2025년 보고서에 따르면 AI 코딩 도구 채택률이 1월 49.2%에서 10월 69%로 뛰었습니다. 이 흐름에서 단순히 "쓰는 사람"과 "파이프라인으로 만드는 사람" 사이의 격차는 앞으로 꽤 커질 것 같습니다.
핵심 개념
MCP: AI를 위한 USB-C 커넥터
MCP(Model Context Protocol)는 Anthropic이 2024년 11월 오픈소스로 공개한 프로토콜입니다. 한 마디로 정리하면 "AI 클라이언트와 외부 도구 사이의 표준 인터페이스"입니다. USB-C가 충전기, 모니터, 저장장치를 동일한 포트로 연결하듯이, MCP는 Claude Code가 PostgreSQL, REST API, GitHub, Slack 등 어떤 데이터 소스든 동일한 방식으로 연결할 수 있게 해줍니다.
MCP(Model Context Protocol): AI 클라이언트와 외부 도구·데이터 소스 간의 통신 표준입니다. JSON-RPC 2.0(클라이언트-서버 간 요청·응답을 구조화하는 원격 호출 규약)을 기반으로 동작하며, 로컬 환경에서는
stdio, 원격 클라우드 서비스에서는HTTP SSE(Server-Sent Events, 서버가 클라이언트로 실시간 스트림을 밀어넣는 방식) 전송 방식을 사용합니다.
2026년 초 기준으로 Google, Microsoft, OpenAI까지 MCP를 채택하면서 사실상 업계 표준이 되었고, 공식·커뮤니티 MCP 서버 수가 수천 개를 넘어섰습니다. 생태계가 이미 충분히 성숙해졌다는 뜻이기도 합니다.
// .claude/settings.json — MCP 서버 등록 예시
{
"mcpServers": {
"postgres": {
"type": "stdio",
"command": "npx",
"args": ["-y", "@crystaldba/postgres-mcp", "--db-url", "postgresql://user:pass@localhost:5432/mydb"]
},
"github": {
"type": "stdio",
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-github"]
},
"slack": {
"type": "stdio",
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-slack"]
}
}
}위 설정 하나로 Claude Code는 PostgreSQL 스키마를 읽고, GitHub PR을 열고, Slack 메시지를 보낼 수 있는 능력을 갖추게 됩니다.
Hooks: "반드시 실행"을 보장하는 결정론적 자동화
여기서 솔직히 짚고 넘어갈 게 있습니다. MCP만 있으면 Claude가 알아서 다 해줄 것 같지만, LLM은 근본적으로 확률적입니다. "쿼리 후에 감사 로그 남겨줘"라고 프롬프트에 넣어도, Claude가 그걸 빠뜨릴 가능성이 항상 존재합니다.
Hooks는 이 문제를 해결합니다. settings.json에 등록하는 사용자 정의 핸들러로, Claude의 생명주기 이벤트에 100% 실행을 보장하는 자동화를 걸 수 있습니다.
| 이벤트 | 발생 시점 | 주요 활용 |
|---|---|---|
PreToolUse |
도구 실행 직전 | 보안 검사, 위험 명령 차단 |
PostToolUse |
도구 실행 직후 | 감사 로깅, CI/CD 트리거, 알림 |
Stop |
에이전트 응답 종료 시 | 최종 보고서 전송, 정리 작업 |
StopFailure |
에이전트 실패 종료 시 | 에러 알림, 롤백 트리거 |
핵심 차별점: Hooks는 LLM이 "선택"하는 것이 아니라 시스템이 보장하는 실행입니다. 프로덕션 감사 추적이나 보안 가드레일처럼 "반드시 실행되어야 하는" 로직에 딱 맞습니다.
훅 핸들러는 4종류를 지원합니다.
| 핸들러 타입 | 설명 |
|---|---|
command |
셸 스크립트 실행 |
http |
HTTP 엔드포인트 호출 |
prompt |
LLM 프롬프트 실행 |
agent |
서브에이전트 실행 |
전체 아키텍처 조감도
개념을 잡았으니, 이 3계층이 실제로 어떻게 맞물리는지 한눈에 보면 이해가 훨씬 빠릅니다.
[Claude Code 에이전트]
↕ (MCP Transport: stdio / HTTP SSE)
[MCP 서버] ← PostgreSQL, REST API, GitHub, Slack 등
↕ (Hooks: PreToolUse / PostToolUse / Stop)
[자동화 스크립트 / 외부 웹훅]이 3계층이 맞물리면 "코드 작성 → DB 검증 → 배포 트리거 → 팀 알림"까지 사람 개입 없이 하나의 루프로 돌아가는 파이프라인이 완성됩니다.
실전 적용
읽는 방법: 예시 1
3은 각각 독립적으로 적용 가능합니다. 예시 4는 13의 구조를 먼저 이해한 뒤 읽으면 흐름이 훨씬 자연스럽습니다.
예시 1: PostgreSQL 감사 로그 자동화 파이프라인
실무에서 자주 맞닥뜨리는 상황인데, DB 쿼리를 누가 언제 어떤 의도로 실행했는지 추적해야 하는 경우입니다. 이 구성을 처음 붙였을 때 제가 가장 먼저 실수한 게 $CLAUDE_TOOL_RESPONSE를 셸에서 직접 이어붙인 것이었습니다. 바로 명령 인젝션에 노출되는 패턴이라 Python으로 파싱하는 방식으로 바꿨습니다.
MCP로 Claude가 직접 PostgreSQL에 접근하게 하되, 모든 쿼리 결과를 감사 로그 API로 자동 전송하는 구성입니다.
// .claude/settings.json
{
"mcpServers": {
"postgres": {
"type": "stdio",
"command": "npx",
"args": ["-y", "@crystaldba/postgres-mcp", "--db-url", "postgresql://readonly_user:pass@localhost/mydb", "--mode", "restricted"]
}
},
"hooks": {
"PostToolUse": [
{
"matcher": "mcp__postgres__query",
"hooks": [
{
"type": "command",
"command": "python3 scripts/log_query.py '$CLAUDE_TOOL_RESPONSE'"
}
]
}
]
}
}# scripts/log_query.py
import sys
import json
import os
import urllib.request
from datetime import datetime, timezone
def main():
raw = sys.argv[1] if len(sys.argv) > 1 else ""
try:
response_data = json.loads(raw)
except json.JSONDecodeError:
response_data = {"raw": raw}
payload = json.dumps({
"tool": "postgres_query",
"result": response_data,
"timestamp": datetime.now(timezone.utc).isoformat()
}).encode("utf-8")
req = urllib.request.Request(
os.environ.get("AUDIT_LOG_URL", "https://audit.example.com/log"),
data=payload,
headers={
"Content-Type": "application/json",
"Authorization": f"Bearer {os.environ['AUDIT_API_TOKEN']}"
},
method="POST"
)
try:
urllib.request.urlopen(req)
except Exception as e:
# 감사 로그 실패가 조용히 묻히면 보안 목적이 훼손됨
print(f"[AUDIT ERROR] 감사 로그 전송 실패: {e}", file=sys.stderr)
sys.exit(1)
if __name__ == "__main__":
main()| 구성 요소 | 역할 |
|---|---|
--mode restricted |
프로덕션 DB를 읽기 전용으로 제한 |
matcher: mcp__postgres__query |
postgres MCP의 query 도구 호출에만 훅 적용 |
| Python 파싱 | 셸 인젝션 방지를 위한 안전한 입력 처리 |
AUDIT_API_TOKEN 환경변수 |
토큰을 설정 파일에 하드코딩하지 않음 |
try/except + stderr |
감사 실패가 조용히 묻히지 않도록 명시적 에러 출력 |
예시 2: 코드 변경 감지 → CI/CD 자동 트리거
파일을 수정할 때마다 수동으로 배포 웹훅을 호출하는 게 번거로웠다면, 이 패턴이 딱 맞습니다. Claude가 src/ 하위 파일을 수정하는 순간 자동으로 CI/CD 파이프라인을 깨웁니다.
#!/bin/bash
# scripts/trigger_deploy.sh — PostToolUse 훅에 등록
# $CLAUDE_TOOL_INPUT을 jq로 파싱해서 변경 파일 경로를 추출
CHANGED_PATH=$(echo "$CLAUDE_TOOL_INPUT" | jq -r '.path // empty')
if [ -z "$CHANGED_PATH" ]; then
exit 0
fi
if echo "$CHANGED_PATH" | grep -q "^src/"; then
curl -s -X POST "https://ci.example.com/webhooks/deploy" \
-H "Authorization: Bearer ${CI_TOKEN}" \
-H "Content-Type: application/json" \
-d "{\"file\": $(echo "$CHANGED_PATH" | jq -R .), \"triggered_by\": \"claude-code\"}"
echo "CI triggered for: $CHANGED_PATH"
fi// settings.json — 파일 쓰기 도구에 훅 연결
{
"hooks": {
"PostToolUse": [
{
"matcher": "Write",
"hooks": [
{
"type": "command",
"command": "bash scripts/trigger_deploy.sh"
}
]
}
]
}
}예시 3: PreToolUse로 위험 명령 선제 차단
저도 처음엔 헷갈렸는데, PreToolUse에서 exitCodeControl: true를 설정하고 exit 2를 반환하면 Claude의 도구 실행 자체가 차단됩니다. 프로덕션 DB에 DROP TABLE 같은 명령이 날아가는 걸 원천 봉쇄할 수 있는 방법입니다.
# scripts/security_check.py
import sys
import json
import re
import os
DANGEROUS_PATTERNS = [
r"\bDROP\s+TABLE\b",
r"\bDROP\s+DATABASE\b",
r"\bTRUNCATE\b",
r"\bDELETE\s+FROM\b(?!.*WHERE)", # WHERE 없는 DELETE — 단순화된 예시
r"rm\s+-rf\s+/",
]
# 주의: 위 DELETE 패턴은 단순화된 예시입니다.
# 멀티라인 쿼리나 서브쿼리가 포함된 경우 오탐 가능성이 있습니다.
# 프로덕션에서는 AST 기반 SQL 파서 사용을 권장합니다.
def main():
tool_input_raw = os.environ.get("CLAUDE_TOOL_INPUT", "")
try:
tool_input = json.loads(tool_input_raw)
except json.JSONDecodeError:
tool_input = {}
command = tool_input.get("command", "") or tool_input.get("query", "")
for pattern in DANGEROUS_PATTERNS:
if re.search(pattern, command, re.IGNORECASE):
print(f"[BLOCKED] 위험 패턴 감지: {pattern}", file=sys.stderr)
sys.exit(2) # exit 2 = 도구 실행 차단
sys.exit(0)
if __name__ == "__main__":
main(){
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "python3 scripts/security_check.py",
"exitCodeControl": true
}
]
},
{
"matcher": "mcp__postgres__query",
"hooks": [
{
"type": "command",
"command": "python3 scripts/security_check.py",
"exitCodeControl": true
}
]
}
]
}
}여기까지 읽었다면 이미 기본 파이프라인을 구동할 수 있습니다. 예시 1~3의 설정 파일과 스크립트는 서로 독립적이라 필요한 것부터 골라서 바로 적용해볼 수 있습니다.
예시 4: 3단계 멀티 에이전트 프로덕션 파이프라인
규모가 커지면 단일 에이전트보다 역할을 분리한 멀티 에이전트 구조가 훨씬 강력합니다. 여기서 나오는 "인수 조건(acceptance criteria)"이나 "플랫폼 제약"은 쉽게 말해 "기능이 완성됐다고 볼 수 있는 기준"과 "배포 환경의 제한 사항"을 뜻합니다.
아래는 기획 → 검토 → 구현을 각각의 에이전트가 담당하고, Hooks가 이들을 연결하는 패턴입니다.
[pm-spec 에이전트]
→ 요구사항 파싱, 인수 조건 작성
→ 결과물: spec.md 저장
↓
PostToolUse 훅 (Write 도구 감지 → spec.md 생성 확인)
↓
[architect-review 에이전트]
→ 아키텍처 검증, 플랫폼 제약 확인
→ 결과물: architecture.md 저장
↓
Stop 훅 → Slack MCP로 팀 채널에 리뷰 완료 알림
↓
[implementer-tester 에이전트]
→ 코드 작성 + 테스트 실행
→ StopFailure 훅 → 실패 시 GitHub Issue 자동 생성에이전트 간 전달 포맷은 spec.md를 공유 파일로 사용하는 방식이 가장 단순합니다. Hooks 설정에서 Write 도구 매처로 spec.md 생성을 감지하고 다음 에이전트를 트리거하는 구조입니다.
// settings.json — spec.md 생성 시 다음 에이전트 트리거
{
"hooks": {
"PostToolUse": [
{
"matcher": "Write",
"hooks": [
{
"type": "command",
"command": "bash scripts/check_and_trigger_architect.sh"
}
]
}
]
}
}#!/bin/bash
# scripts/check_and_trigger_architect.sh
WRITTEN_PATH=$(echo "$CLAUDE_TOOL_INPUT" | jq -r '.path // empty')
# spec.md가 생성된 경우에만 다음 단계 트리거
if [ "$WRITTEN_PATH" = "spec.md" ]; then
echo "[PIPELINE] spec.md 생성 감지 → architect-review 에이전트 시작"
# 실제 에이전트 트리거 방식은 팀 CI/CD 환경에 따라 다릅니다
curl -s -X POST "https://ci.example.com/trigger/architect-review" \
-H "Authorization: Bearer ${CI_TOKEN}"
fi멀티 에이전트 상세 구현(에이전트 간 컨텍스트 공유, 실패 복구 전략)은 다음 글에서 더 깊이 다룰 예정입니다.
장단점 분석
장점
| 항목 | 내용 |
|---|---|
| 결정론적 자동화 | LLM 판단에 의존하지 않고 Hooks로 100% 실행 보장 |
| 표준화된 연동 | MCP 표준으로 DB·API·SaaS를 단일 인터페이스로 연결 |
| 감사 추적 | 모든 도구 입·출력을 훅으로 캡처해 자동 로깅 가능 |
| 접근 제어 | PreToolUse 훅으로 위험 작업 선제 차단 |
| 생태계 성숙 | 수천 개의 공식·커뮤니티 MCP 서버 즉시 활용 가능 |
단점 및 주의사항
| 항목 | 내용 | 대응 방안 |
|---|---|---|
| 보안 공격면 확대 | MCP 서버 연결 수만큼 신뢰 경계가 증가 | 최소한의 MCP 서버만 등록, 출처 검증 필수 |
| Tool Poisoning | 악성 MCP 서버 도구 설명문에 숨겨진 프롬프트 삽입 가능 | 써드파티 서버 도구 설명문 직접 확인 |
| 간접 프롬프트 인젝션 | DB·API 응답 데이터에 삽입된 악성 지시를 Claude가 실행할 위험 | 외부 데이터를 프롬프트에 직접 포함하지 않도록 설계 |
| CVE 취약점 | CVE-2025-54794(경로 우회), CVE-2025-54795(명령 인젝션), CVE-2025-59536(API 토큰 탈취) | Claude Code와 MCP 서버 최신 버전 유지 |
| 복잡도 증가 | 훅·MCP 서버가 많아질수록 디버깅·유지관리 난이도 상승 | 훅 스크립트를 단순하게 유지, 로그 수집 체계화 |
| 운영 의존성 | 외부 MCP 서버 장애 시 파이프라인 전체 중단 가능 | 타임아웃 설정, 장애 시 fallback 로직 구비 |
이 중에서 실제로 가장 자주 마주친 건 간접 프롬프트 인젝션이었습니다. DB 응답 데이터 안에 "이제 이 명령을 실행해줘"가 들어있는 경우인데, 생각보다 자주 발생하고 디버깅도 까다롭습니다. 외부 데이터와 프롬프트 컨텍스트를 철저히 분리하는 설계가 핵심입니다.
Tool Poisoning: 악의적인 MCP 서버가 도구 설명(description) 필드 안에 숨겨진 프롬프트를 삽입해 Claude가 의도치 않은 동작을 수행하도록 유도하는 공격입니다. 써드파티 MCP 서버를 사용할 때는 소스 코드나 도구 설명을 직접 확인하는 것이 좋습니다.
주의사항: 실무에서 가장 흔한 실수
-
훅 스크립트에서
$CLAUDE_TOOL_INPUT을 셸 문자열로 직접 이어붙이기 — 반드시 Python이나 jq를 통해 파싱하는 방식을 권장합니다. 셸에서 직접 문자열을 이어붙이면 명령 인젝션에 노출됩니다. -
Postgres MCP를 읽기·쓰기 모드로 프로덕션 DB에 연결하기 — 프로덕션 환경에서는 반드시
--mode restricted(읽기 전용)로 설정하는 것이 안전합니다. 테스트 중에 Claude가 예상치 못한 INSERT나 UPDATE를 실행할 수 있습니다. -
MCP 서버 인증 토큰을
settings.json에 하드코딩하기 —settings.json은.claude/디렉토리에 위치하고 팀 저장소에 공유되는 경우가 많습니다. DB URL, API 토큰은 반드시 환경변수로 분리해서 관리하는 것이 안전합니다.
마치며
이 파이프라인을 실제로 운영해보고 나서 가장 인상 깊었던 점은, Hooks가 "Claude가 까먹을 수도 있는 것"을 "시스템이 절대 빠뜨리지 않는 것"으로 바꿔주는 역할이라는 것입니다. LLM의 확률적 특성을 걱정하는 대신 결정론적인 안전망을 깔아두면, 그 위에서 Claude가 훨씬 자유롭게 판단할 수 있게 됩니다.
처음부터 거창한 멀티 에이전트 파이프라인을 구축할 필요는 없습니다. 작은 것부터 직접 체감해보시면 좋겠습니다.
지금 바로 시작해볼 수 있는 3단계:
-
PostgreSQL MCP 연결 먼저 시도해보기 —
npx -y @crystaldba/postgres-mcp를.claude/settings.json에 등록하고, Claude Code에서 "users 테이블 스키마 보여줘"라고 입력해보시면 됩니다. 이미 개발 DB가 있다면 5분이면 충분합니다. -
PostToolUse 훅으로 간단한 로깅 추가해보기 —
matcher: "mcp__postgres__query"훅을 등록하고, 위 예시 1의log_query.py를 로컬 파일로 저장하도록 수정해보시면 됩니다. 쿼리 결과가 파일에 쌓이는 걸 보면, 훅의 동작 방식이 직관적으로 이해됩니다. -
PreToolUse 보안 가드레일 붙여보기 — 위 예시 3의
security_check.py를 그대로 가져다가 Bash 도구에 연결해보시면 좋습니다.exit 2로 차단이 실제로 작동하는 걸 보면, 이 구조에 대한 신뢰가 한층 생깁니다.
참고 자료
- Claude Code MCP 공식 문서 — Connect Claude Code to tools via MCP
- Claude Code Hooks 공식 참조 — Hooks reference
- Automate workflows with hooks — Claude Code Docs
- Model Context Protocol 공식 아키텍처 문서
- postgres-mcp GitHub — crystaldba/postgres-mcp
- Claude Code Security 공식 문서
- Check Point Research — RCE and API Token Exfiltration Through Claude Code (CVE-2025-59536)
- The Vulnerable MCP Project — MCP 보안 취약점 데이터베이스
- Claude Code Hooks: A Practical Guide to Workflow Automation — DataCamp
- awesome-claude-code — GitHub