Claude(또는 Cursor)에게 Rails 앱의 활동 로그 접근 권한을 부여하는 방법
요약
Anthropic의 MCP(Model Context Protocol)를 활용하여 Claude나 Cursor 같은 AI 도구가 Rails 앱의 활동 로그에 안전하고 정확하게 접근하는 방법을 설명합니다. 모델의 환각(Hallucination)을 방지하기 위해 원시 DB 대신 정제된 활동 로그를 MCP 서버로 노출하는 전략을 다룹니다.
핵심 포인트
- MCP는 AI 클라이언트가 외부 데이터 소스에 접근할 수 있게 하는 개방형 프로토콜입니다.
- 모델이 데이터 없이 추측하여 발생하는 환각 문제를 MCP로 해결할 수 있습니다.
- 원시 DB 직접 연결보다 정제된 활동 로그(Activity Log)를 제공하는 것이 모델의 정확도를 높입니다.
- MCP 서버는 도구(Tools), 리소스(Resources), 프롬프트(Prompts) 세 가지를 노출합니다.
오늘 아무런 설정 없이 Claude에게 이렇게 물어보세요:
"어제 우리 앱에서 사용자 4421이 무엇을 했나요?"
당신은 답변을 받게 될 것입니다. 그 답변은 확신에 차 있고 구체적이며, 완전히 지어낸 내용일 것입니다. Claude는 당신의 운영(production) 데이터베이스에 접근할 권한이 없습니다. 따라서 Claude는 사실 관계가 없을 때 언어 모델(Language Model)이 하는 행동을 수행합니다. 즉, 답변이 어떠해야 하는지 패턴을 매칭하여 차분한 목소리로 허구를 전달하는 것입니다.
이것이 문제의 핵심입니다. 모델이 멍청해서가 아닙니다. 데이터 없이 당신의 데이터에 관한 질문에 답하고 있다는 점이 문제입니다.
Model Context Protocol (MCP)은 적절한 소스를 지정하기만 하면 이 문제를 해결해 줍니다. 이 포스트는 아직 MCP를 연결해 보지 않은 Rails 개발자를 위해 MCP가 무엇인지, 왜 앱의 활동 로그(activity log)를 사용하는 것이 좋은지, 그리고 어떻게 제가 EZLogs MCP 서버를 구축하여 확신에 찬 가짜 답변 대신 지루할 정도로 정확한 진짜 답변을 얻을 수 있게 했는지에 대해 다룹니다.
MCP의 실제 정체
MCP는 Anthropic에서 만든 작은 개방형 프로토콜(open protocol)입니다. 이를 통해 AI 클라이언트(Claude Desktop, Cursor, 내부 코파일럿 등)는 대화 도중에 당신이 제어하는 서버를 호출하여, 답변을 하기 전에 데이터를 요청할 수 있습니다.
그게 전부입니다. 이것은 프레임워크(framework)가 아닙니다. 모델(model)도 아닙니다. 모델이 추측하는 대신 "잠시만 기다려 주세요, 확인해 보겠습니다"라고 말할 수 있게 하는 방법입니다.
서버는 세 가지를 노출합니다:
- 도구 (Tools): AI가 호출할 수 있는 기능, 예: "이 사용자 사이의 특정 날짜 간의 활동 찾기"
- 리소스 (Resources): AI가 읽을 수 있는 데이터, 예: "가장 최근의 중요한 활동 50개"
- 프롬프트 (Prompts): AI가 사용할 수 있는 지침, 예: "이 활동에 대한 사고 보고서 초안 작성"
AI는 언제 이들을 호출할지 결정합니다. 당신은 그것들이 무엇을 반환할지 결정합니다. 프로토콜은 단일 엔드포인트(endpoint)에서의 HTTP 기반 JSON-RPC일 뿐입니다.
왜 활동 로그(activity log)를 노출하는 것이 적절한가
MCP 서버를 원시 데이터베이스(raw database)에 직접 연결할 수도 있습니다. 실제로 그렇게 하는 사람들도 있습니다. 하지만 이는 원시 로그(raw logs)가 인간에게 좋지 않은 것과 동일한 이유로 결과가 좋지 않습니다. 모델이 gid://app/Order/4421과 같은 값, 수많은 외래 키(foreign keys), 그리고 Sidekiq 작업 클래스(job class)를 받게 되면, 이제 모델이 직접 번역을 수행해야 하기 때문입니다. 결국 모델은 조인(join)을 추측하고, 컬럼(column)의 의미를 추측하게 되며, 당신은 단지 한 단계 더 깊어진 수준의 '확신에 찬 가짜 답변'을 다시 마주하게 될 뿐입니다.
활동 로그(activity log)는 이미 번역된 레이어입니다. 사용자 작업당 하나의 행(row)이 존재하며, HTTP, 백그라운드 작업(background jobs), 데이터베이스 변경 사항이 상호 연관(correlated)되어 있고, 사람이 읽을 수 있는 이름이 붙어 있습니다. "사용자 4421이 주문 #4421을 배송하려고 시도했으나 주소 검증에 실패했습니다. 재시도 작업(retry job)이 세 번 실행된 후 중단되었습니다." 모델이 이것을 읽을 때, 모델이 새로 만들어내야 할 것은 아무것도 남지 않습니다. 사실은 이미 사실인 상태로 존재하기 때문입니다.
따라서 활동 로그는 가장 어려운 부분인 상관관계 분석(correlation)과 명명(naming)이 모델이 등장하기 전에 이미 완료되었기 때문에, 훨씬 더 나은 MCP 소스가 됩니다.
중요한 부분: 생성된 것이 아닌 결정론적인(deterministic) 것
이것은 제가 가장 중요하게 생각하는 지점이자, 만약 당신이 이 분야의 어떤 도구를 평가하고 있다면 제가 가장 강력하게 반론을 제기할 부분입니다.
EZLogs의 번역에는 모델이 포함되어 있지 않습니다.
당신의 팀이 읽는 카드와 MCP 서버가 반환하는 데이터는 상관관계 분석(correlation)과 템플릿(templates)에 의해 생성됩니다. 동일한 이벤트가 입력되면 매번 동일한 문장이 출력됩니다. 액션 스트림(action stream)은 LLM을 호출하지 않고 데이터베이스에 쓰지도 않는 순수 함수(pure functions)를 통해 상태(state)로 접히며(folded), 모든 값은 그것이 유래한 특정 이벤트의 ID를 포함하고 있습니다. 당신은 어떤 숫자든 클릭하여 그 근거(evidence)로 되돌아갈 수 있습니다.
즉, 당신의 AI가 EZLogs MCP 서버에 "주문 4421에 무슨 일이 일어났나요?"라고 물을 때, 서버가 반환하는 행들은 두 번째 모델의 의견이 아닌 결정론적인(deterministic) 사실입니다. 당신의 AI는 그 사실을 서술할 뿐입니다. 저희 쪽에서는 절대 서술하지 않습니다. 저희는 인용(citations)이 포함된 구조화된 데이터(structured data)를 전달하며, 모델이 이를 문장으로 바꾸는 과정에서 자신의 크레딧(credits)을 소모하게 합니다.
이것이 "AI가 답변을 지어낸 것"과 "AI가 답변을 읽은 것"의 차이입니다. AI 기능을 출시한 지 1년이 지난 지금, CTO들이 실제로 묻고 있는 질문은 바로 이 차이점입니다. 사실(facts)이 어디에서 왔는지, 그리고 이를 추적할 수 있는지 말입니다.
EZLogs MCP 서버가 노출하는 것
6개의 쿼리 전용(query-only) 도구가 있습니다. 이 도구들은 모두 읽기(read)를 수행합니다. 그 어떤 것도 귀하의 앱에 다시 쓰기(write)를 수행하지 않습니다. 이는 현재의 제한 사항이 아니라 영구적인 설계 규칙입니다. EZLogs는 귀하의 시스템에 대한 쓰기 권한(write credentials)을 보유하지 않으므로, 전체 시스템의 영향 범위(blast radius)는 읽기 전용(read-only)으로 제한됩니다.
- find_actions: 행위자(actor), 엔티티(entity), 결과(outcome), 날짜, 중요도(significance)별로 활동 로그 검색
- get_action: ID를 통한 단일 작업(action) 조회 및 해당 작업의 전체 하부 이벤트 스트림(event stream) 확인
- actor_timeline: 특정 사용자 또는 에이전트의 최근 작업 및 현재 상태 확인
- entity_timeline: 특정 주문, 계정, 문서 등의 엔티티 및 현재 상태 확인
- compare_actions: 2개에서 5개의 작업을 나열하여 서로 갈라지는 지점 확인
- top_lists: 특정 기간 내 활동량에 따른 행위자 및 엔티티 순위 산정
find_actions와 top_lists는 무료 티어(free tier)에서 제공됩니다. 나머지는 유료입니다. MCP 사용 자체는 모든 유료 티어에서 무제한입니다. AI가 이야기를 서술하기 위해 자신의 크레딧(credits)을 소모하기 때문이며, 저희는 단지 행(rows)을 전달할 뿐입니다.
또한 읽기 전용 리소스(ezlogs://recent, ezlogs://actor/{id} 등)와 일반적인 질문을 위한 사전 구축된 프롬프트(summarize_day, investigate_failure, incident_report)도 제공됩니다.
연결하기 (Wiring it up)
두 단계로 진행됩니다. 이벤트를 흐르게 만든 다음, AI를 연결하세요.
Rails 앱 (이 젬(gem)은 수동 미들웨어 없이 HTTP, Sidekiq, ActiveRecord를 캡처합니다):
# Gemfile
gem 'ez_logs_agent'
bundle install
rails generate ez_logs_agent:install
# config/initializers/ez_logs_agent.rb
EzLogsAgent.configure do |config|
config.server_url = "https://app.ezlogs.io"
...
Next.js app을 스택으로 사용하거나 함께 사용 중이라면, npm install ezlogs-nextjs를 설치하고 작은 instrumentation.ts 파일을 추가하면 됩니다. 두 에이전트(agent) 모두 동일한 와이어 포맷(wire format)을 방출하므로, 서버는 이를 하나의 소스로 취급합니다.
그 다음 Claude 또는 Cursor를 연결하세요. Cursor의 경우, ~/.cursor/mcp.json에 다음을 추가합니다:
{
"mcpServers": {
"ezlogs": {
...
macOS용 Claude Desktop의 경우, 기존 설정을 백업하고 EZLogs 항목을 병합하는 단 한 줄의 명령어로 설치할 수 있습니다:
curl -fsSL https://app.ezlogs.io/install/claude-desktop.sh | sh
Claude를 종료하고 다시 연 다음, "어제 무슨 일이 있었어?"라고 물어보세요. 이번에는 Claude가 직접 가서 확인합니다.
이것이 아닌 것
이것은 메트릭 플랫폼(metrics platform)도 아니고 APM(Application Performance Monitoring)도 아닙니다. CPU를 감시하거나 새벽 3시에 페이지(page)를 보내지 않습니다. Datadog이 인프라를 감시한다면, 이것은 여러분의 앱이 수행한 작업을 지원 담당자가 읽을 수 있는 언어로 설명해 줍니다.
또한 로그를 번역해 주는 AI 도구도 아닙니다. AI는 연결의 여러분의 측면, 즉 질문을 던지는 쪽에 있습니다. 하부의 번역은 결정론적(deterministic)이며 AI를 껐을 때도 동일합니다. 만약 경로에서 모든 모델을 제거했을 때 답변이 깨진다면 설계가 잘못된 것이겠지만, 이 서비스는 그렇지 않습니다.
시도해 보세요
카드 등록이 필요 없는 무료 티어(free tier)가 있으며, MCP 서버도 이에 포함됩니다. Rails 또는 Next.js 앱과 프로덕션(production)에 대해 계속 질문하고 싶은 AI 클라이언트를 가지고 있다면, 하나를 다른 하나에 연결하여 답변이 유효한지 확인해 보세요.
연결을 마쳤는데 답변이 틀리거나, 더 나쁘게는 확신에 찬 거짓 답변(confidently fake)이 돌아온다면 저에게 알려주세요. 그것이 바로 이 모든 시스템이 존재함으로써 방지하고자 하는 유일한 버그입니다.
Razvan
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기