← All guides

Claude API 루비 가이드: Rails 앱에 Claude 통합하기

Claude API를 Ruby on Rails에서 사용하는 방법 — gem 설치, 메시지 생성, 스트리밍, ActiveJob 백그라운드 처리, Rails API 엔드포인트까지 한국어로 설명합니다.

🇺🇸 Read in English →

Claude API 루비 가이드: Rails 앱에 Claude 통합하기

Claude API를 Ruby on Rails에서 사용하려면 anthropic-sdk-ruby gem을 Gemfile에 추가하고, ANTHROPIC_API_KEY 환경변수를 설정한 뒤, Anthropic::Client를 통해 메시지를 전송하면 됩니다. 서비스 객체 패턴으로 Claude 호출을 캡슐화하면 컨트롤러를 깔끔하게 유지할 수 있습니다. ActiveJob과 연동하면 긴 응답을 백그라운드에서 처리하여 요청 타임아웃을 방지할 수 있습니다. 이 가이드에서는 gem 설치부터 프로덕션 Rails API 엔드포인트 구현, 에러 처리, 재시도 전략까지 단계별로 설명합니다.


gem 설치

Gemfile에 Claude Ruby SDK를 추가합니다:

# Gemfile
gem "anthropic-sdk-ruby", "~> 0.3"
gem "dotenv-rails", groups: [:development, :test]
bundle install

환경변수 설정:

# .env (개발 환경)
ANTHROPIC_API_KEY=sk-ant-...

Rails credentials를 사용하는 경우:

rails credentials:edit
# config/credentials.yml.enc
anthropic:
  api_key: sk-ant-...

기본 메시지 생성

Claude 클라이언트를 이니셜라이저로 설정하고 첫 번째 메시지를 전송합니다:

# config/initializers/anthropic.rb
require "anthropic"

ANTHROPIC_CLIENT = Anthropic::Client.new(
  api_key: ENV.fetch("ANTHROPIC_API_KEY") {
    Rails.application.credentials.dig(:anthropic, :api_key)
  }
)
# 기본 메시지 전송 예시 (Rails 콘솔 또는 서비스에서 사용)
response = ANTHROPIC_CLIENT.messages(
  model: "claude-sonnet-4-5",
  max_tokens: 1024,
  messages: [
    { role: "user", content: "Rails 앱의 N+1 쿼리 문제를 해결하는 방법을 알려주세요." }
  ]
)

puts response.content.first.text
puts "사용 토큰 — 입력: #{response.usage.input_tokens}, 출력: #{response.usage.output_tokens}"

시스템 프롬프트를 추가하면 Claude의 역할을 명확히 지정할 수 있습니다:

response = ANTHROPIC_CLIENT.messages(
  model: "claude-sonnet-4-5",
  max_tokens: 2048,
  system: "당신은 Ruby on Rails 전문 개발자입니다. 간결한 코드 예제와 함께 답변해 주세요.",
  messages: [
    { role: "user", content: "ActiveRecord에서 복잡한 조인 쿼리를 최적화하는 패턴을 알려주세요." }
  ]
)

스트리밍 처리

긴 응답을 실시간으로 스트리밍하면 사용자 경험을 크게 향상시킬 수 있습니다:

# app/services/claude_streaming_service.rb
class ClaudeStreamingService
  def initialize
    @client = ANTHROPIC_CLIENT
  end

  def stream(prompt, &block)
    @client.messages(
      model: "claude-sonnet-4-5",
      max_tokens: 2048,
      messages: [{ role: "user", content: prompt }],
      stream: true
    ) do |event|
      case event.type
      when "content_block_delta"
        block.call(event.delta.text) if event.delta.type == "text_delta"
      when "message_stop"
        block.call(nil) # 스트리밍 완료 신호
      end
    end
  end
end

Server-Sent Events(SSE)로 프론트엔드에 스트리밍을 전달합니다:

# app/controllers/api/chat_controller.rb
class Api::ChatController < ApplicationController
  include ActionController::Live

  def stream
    response.headers["Content-Type"] = "text/event-stream"
    response.headers["Cache-Control"] = "no-cache"
    response.headers["X-Accel-Buffering"] = "no"

    service = ClaudeStreamingService.new
    service.stream(params[:message]) do |chunk|
      if chunk
        response.stream.write("data: #{chunk.to_json}\n\n")
      else
        response.stream.write("data: [DONE]\n\n")
      end
    end
  rescue ActionController::Live::ClientDisconnected
    # 클라이언트 연결 종료 — 정상 처리
  ensure
    response.stream.close
  end
end

Ruby, Python, Node.js로 Claude API를 활용하는 30개 이상의 프로덕션 레시피

Agent SDK Cookbook ($49 / ₩64,000)은 스트리밍 파이프라인, Rails 통합 패턴, 멀티에이전트 조율, 에러 처리, 비용 최적화 예제를 모두 포함합니다.

Agent SDK Cookbook 구매 — $49 (₩64,000)


ActiveJob 백그라운드 처리

시간이 걸리는 Claude 호출은 ActiveJob으로 백그라운드 처리하여 웹 요청 타임아웃을 방지합니다:

# app/jobs/claude_analysis_job.rb
class ClaudeAnalysisJob < ApplicationJob
  queue_as :default

  # 재시도 전략 설정
  retry_on Anthropic::RateLimitError, wait: :exponentially_longer, attempts: 5
  retry_on Anthropic::APIConnectionError, wait: 3.seconds, attempts: 3
  discard_on Anthropic::AuthenticationError

  def perform(document_id, prompt)
    document = Document.find(document_id)

    response = ANTHROPIC_CLIENT.messages(
      model: "claude-sonnet-4-5",
      max_tokens: 4096,
      system: "문서 분석 전문가입니다. 한국어로 핵심 내용을 요약해 주세요.",
      messages: [
        {
          role: "user",
          content: "다음 문서를 분석해 주세요:\n\n#{document.content}\n\n#{prompt}"
        }
      ]
    )

    document.update!(
      analysis: response.content.first.text,
      analyzed_at: Time.current,
      token_count: response.usage.input_tokens + response.usage.output_tokens
    )

    # 분석 완료 알림 (선택)
    AnalysisMailer.completed(document).deliver_later
  end
end

컨트롤러에서 Job 큐에 등록합니다:

# app/controllers/documents_controller.rb
def analyze
  @document = Document.find(params[:id])

  ClaudeAnalysisJob.perform_later(
    @document.id,
    "이 문서의 핵심 인사이트 3가지를 추출해 주세요."
  )

  render json: { status: "queued", document_id: @document.id }
end

Rails API 엔드포인트

프로덕션 수준의 Claude API 엔드포인트를 구성합니다:

# app/services/claude_service.rb
class ClaudeService
  MODEL = "claude-sonnet-4-5"
  MAX_TOKENS = 2048

  def initialize
    @client = ANTHROPIC_CLIENT
  end

  def chat(messages, system: nil)
    params = {
      model: MODEL,
      max_tokens: MAX_TOKENS,
      messages: messages
    }
    params[:system] = system if system.present?

    response = @client.messages(**params)
    {
      text: response.content.first.text,
      usage: {
        input_tokens: response.usage.input_tokens,
        output_tokens: response.usage.output_tokens
      }
    }
  end
end
# app/controllers/api/v1/messages_controller.rb
class Api::V1::MessagesController < ApplicationController
  before_action :authenticate_api_key!

  def create
    messages = build_messages(params[:messages])
    system_prompt = params[:system]

    result = ClaudeService.new.chat(messages, system: system_prompt)

    render json: {
      content: result[:text],
      usage: result[:usage]
    }, status: :ok
  rescue Anthropic::RateLimitError => e
    render json: { error: "요청 한도 초과. 잠시 후 다시 시도해 주세요.", code: "rate_limit" },
           status: :too_many_requests
  rescue Anthropic::AuthenticationError
    render json: { error: "API 인증 오류.", code: "auth_error" },
           status: :unauthorized
  rescue Anthropic::APIError => e
    Rails.logger.error("Claude API Error: #{e.message}")
    render json: { error: "AI 서비스 오류가 발생했습니다.", code: "api_error" },
           status: :service_unavailable
  end

  private

  def build_messages(raw_messages)
    raw_messages.map do |msg|
      { role: msg[:role], content: msg[:content] }
    end
  end

  def authenticate_api_key!
    token = request.headers["Authorization"]&.split(" ")&.last
    render json: { error: "인증 필요" }, status: :unauthorized unless valid_token?(token)
  end

  def valid_token?(token)
    ApiKey.active.exists?(token: token)
  end
end

라우트를 설정합니다:

# config/routes.rb
namespace :api do
  namespace :v1 do
    resources :messages, only: [:create]
  end

  namespace :chat do
    post :stream, to: "chat#stream"
  end
end

에러 처리 및 재시도

견고한 에러 처리와 재시도 로직을 구현합니다:

# app/services/claude_with_retry_service.rb
class ClaudeWithRetryService
  MAX_RETRIES = 3
  BASE_DELAY = 1.0 # 초

  def call(messages, model: "claude-haiku-4-5", max_tokens: 1024)
    attempt = 0

    begin
      attempt += 1
      ANTHROPIC_CLIENT.messages(
        model: model,
        max_tokens: max_tokens,
        messages: messages
      )
    rescue Anthropic::RateLimitError => e
      raise if attempt >= MAX_RETRIES

      # 지수 백오프: 1초, 2초, 4초
      delay = BASE_DELAY * (2 ** (attempt - 1))
      Rails.logger.warn("Rate limit hit. Retry #{attempt}/#{MAX_RETRIES} in #{delay}s")
      sleep(delay)
      retry

    rescue Anthropic::APIConnectionError => e
      raise if attempt >= MAX_RETRIES

      Rails.logger.warn("Connection error. Retry #{attempt}/#{MAX_RETRIES}")
      sleep(BASE_DELAY)
      retry

    rescue Anthropic::BadRequestError => e
      # 재시도 불가 에러 — 즉시 실패
      Rails.logger.error("Bad request: #{e.message}")
      raise
    end
  end
end

에러 코드별 대응 전략은 Claude API 에러 코드 레퍼런스 가이드를 참고하세요.

모델 선택에 따라 비용과 성능이 크게 달라집니다. 단순 분류 작업에는 Haiku, 복잡한 코드 분석에는 Sonnet을 사용하는 전략은 Claude Haiku vs Sonnet vs Opus: 어떤 모델을 선택해야 할까 가이드에서 자세히 확인할 수 있습니다.


Rails 프로덕션 환경에서 Claude API를 다루는 실전 예제 모음

Agent SDK Cookbook ($49 / ₩64,000)에는 ActiveJob 통합, 스트리밍 SSE 패턴, 비용 모니터링, 프롬프트 캐싱 적용, 멀티에이전트 파이프라인 레시피가 포함되어 있습니다.

Agent SDK Cookbook 구매 — $49 (₩64,000)


Frequently Asked Questions

Claude API Ruby gem을 어떻게 설치하나요?

Gemfilegem "anthropic-sdk-ruby" 를 추가하고 bundle install을 실행합니다. 그런 다음 ANTHROPIC_API_KEY 환경변수를 설정합니다. Rails에서는 config/initializers/anthropic.rb 파일에 Anthropic::Client.new로 전역 클라이언트 인스턴스를 만들어 사용하는 것이 일반적입니다.

Rails에서 Claude API 호출이 타임아웃되는 경우 어떻게 처리하나요?

Claude API 응답은 수 초에서 수십 초가 걸릴 수 있어 웹 요청 타임아웃(일반적으로 30초)에 걸릴 수 있습니다. 긴 응답이 필요한 경우 ActiveJob을 사용해 백그라운드에서 처리하거나, 스트리밍과 SSE를 조합해 응답을 청크 단위로 전달하는 방식을 사용하세요. Heroku 같은 플랫폼은 30초 타임아웃이 있으므로 스트리밍이 필수입니다.

Rate Limit 에러(429)를 어떻게 처리해야 하나요?

지수 백오프(exponential backoff)를 구현하거나, ActiveJob의 retry_on Anthropic::RateLimitError, wait: :exponentially_longer 옵션을 사용하세요. 동시 요청이 많은 경우 Sidekiq의 concurrency 설정을 낮추거나, Redis 기반의 토큰 버킷 알고리즘으로 요청 속도를 제한하세요. 자세한 에러 코드 대응 방법은 Claude API 에러 코드 레퍼런스를 참고하세요.

Rails에서 Claude API 비용을 어떻게 추적하나요?

각 API 응답의 response.usage.input_tokensresponse.usage.output_tokens를 데이터베이스에 기록하세요. UsageLog 모델을 만들어 사용자, 엔드포인트, 토큰 수, 비용을 저장하면 월별 비용 분석이 가능합니다. 모델별 단가는 Haiku가 가장 저렴하므로, 단순 작업은 Haiku로 라우팅하면 비용을 크게 줄일 수 있습니다.

멀티턴 대화를 Rails에서 어떻게 구현하나요?

대화 히스토리를 세션이나 데이터베이스에 저장하고, 매 요청 시 전체 메시지 배열을 Claude API에 전달하면 됩니다. Conversation 모델과 Message 모델을 만들어 has_many :messages 관계로 관리하는 것이 일반적입니다. Claude는 자체 메모리가 없으므로 클라이언트에서 컨텍스트를 전달해야 합니다. 대화가 길어지면 오래된 메시지를 요약하여 토큰을 절약하세요.

도구와 자료