Rails GraphQL API 2026: graphql-ruby, 구독, 면접 질문 가이드
graphql-ruby를 사용한 Rails GraphQL API 구축 방법을 다룹니다. DataLoader를 활용한 N+1 문제 해결, ActionCable 구독, RSpec 테스트, 실무 면접 질문을 포괄적으로 설명합니다.

GraphQL은 유연한 데이터 페칭이 필요한 API에서 표준적인 선택이 되었으며, graphql-ruby는 ActiveRecord와의 긴밀한 통합을 통해 이 기능을 Rails에 제공합니다. 2024년 말에 출시된 Rails 8은 개선된 구독 처리와 N+1 방지를 위한 지연 실행 기능을 도입한 graphql-ruby 2.4와 잘 어울립니다.
graphql-ruby gem은 완전한 GraphQL 구현을 제공합니다. Ruby DSL을 사용한 스키마 정의, ActiveRecord 모델에서의 자동 타입 추론, 배치 처리를 위한 내장 DataLoader, 실시간 구독을 위한 ActionCable 통합이 포함됩니다.
Rails 8 애플리케이션에서 graphql-ruby 설정하기
gem generator는 초기 스키마 구조를 생성하고 GraphQL 엔드포인트를 마운트합니다. gem을 추가하고 install generator를 실행하여 기본 파일들을 스캐폴드합니다.
# Gemfile
gem 'graphql', '~> 2.4'# Terminal
bundle install
rails generate graphql:installgenerator는 app/graphql/에 스키마 파일, 기본 타입, mutations 디렉토리를 생성합니다. 또한 /graphql에 라우트를 추가하고 개발 환경에서는 선택적으로 GraphiQL을 마운트합니다.
# app/graphql/sharpskill_schema.rb
class SharpskillSchema < GraphQL::Schema
mutation(Types::MutationType)
query(Types::QueryType)
subscription(Types::SubscriptionType)
# N+1 방지를 위한 내장 DataLoader 사용
use GraphQL::Dataloader
# 구독에 필요
use GraphQL::Subscriptions::ActionCableSubscriptions
end스키마 파일은 진입점 역할을 합니다. 쿼리, 뮤테이션, 구독을 처리하는 타입을 선언하고 DataLoader와 같은 미들웨어를 등록합니다.
Rails 모델용 타입과 리졸버 정의하기
API에 등장하는 각 ActiveRecord 모델에는 해당하는 GraphQL 타입이 필요합니다. 타입은 어떤 필드가 노출되고 어떻게 해결되는지를 정의합니다.
# app/graphql/types/user_type.rb
module Types
class UserType < Types::BaseObject
field :id, ID, null: false
field :email, String, null: false
field :created_at, GraphQL::Types::ISO8601DateTime, null: false
# DataLoader를 통한 자동 배치 처리 관계
field :posts, [Types::PostType], null: false
def posts
dataloader.with(Sources::ActiveRecordCollection, Post, :user_id).load(object.id)
end
end
enddataloader.with 호출은 여러 posts 페치를 단일 SQL 쿼리로 배치 처리합니다. DataLoader가 없다면 50명의 사용자와 게시물을 요청하는 쿼리는 51개의 쿼리를 실행합니다. DataLoader를 사용하면 2개로 줄어듭니다.
# app/graphql/sources/active_record_collection.rb
class Sources::ActiveRecordCollection < GraphQL::Dataloader::Source
def initialize(model, foreign_key)
@model = model
@foreign_key = foreign_key
end
def fetch(ids)
records = @model.where(@foreign_key => ids).group_by(&@foreign_key)
ids.map { |id| records[id] || [] }
end
end이 패턴은 모든 본격적인 graphql-ruby 프로젝트에서 볼 수 있습니다. 공식 DataLoader 문서에서는 캐싱과 중첩 소스 등 추가 사용 사례를 다룹니다.
인자와 필터링을 사용한 쿼리 구축하기
QueryType은 클라이언트가 사용할 수 있는 루트 필드를 정의합니다. 인자를 통해 필터링과 페이지네이션이 가능합니다.
# app/graphql/types/query_type.rb
module Types
class QueryType < Types::BaseObject
field :users, [Types::UserType], null: false do
argument :email_contains, String, required: false
argument :limit, Integer, required: false, default_value: 20
end
def users(email_contains: nil, limit:)
scope = User.all
scope = scope.where('email ILIKE ?', "%#{email_contains}%") if email_contains
scope.limit(limit)
end
field :user, Types::UserType, null: true do
argument :id, ID, required: true
end
def user(id:)
User.find_by(id: id)
end
end
end리졸버 메서드는 필드 정의와 일치하는 키워드 인자를 받습니다. nullable 필드에서 nil을 반환하는 것은 유효합니다. non-null 필드에서 에러를 발생시키면 GraphQL 에러 응답이 트리거됩니다.
Ruby on Rails 면접 준비가 되셨나요?
인터랙티브 시뮬레이터, flashcards, 기술 테스트로 연습하세요.
뮤테이션: 레코드 생성과 수정
뮤테이션은 쿼리보다 더 엄격한 규약을 따릅니다. 각 뮤테이션은 자체 클래스에 존재하며 결과와 에러를 모두 포함하는 페이로드 타입을 반환합니다.
# app/graphql/mutations/create_post.rb
module Mutations
class CreatePost < Mutations::BaseMutation
argument :title, String, required: true
argument :body, String, required: true
field :post, Types::PostType, null: true
field :errors, [String], null: false
def resolve(title:, body:)
post = context[:current_user].posts.build(title: title, body: body)
if post.save
{ post: post, errors: [] }
else
{ post: nil, errors: post.errors.full_messages }
end
end
end
endcontext[:current_user]는 컨트롤러에서 가져옵니다. 인증은 GraphQL 레이어 전에 발생하며, 일반적으로 Devise나 JWT 라이브러리를 사용합니다.
# app/controllers/graphql_controller.rb
class GraphqlController < ApplicationController
def execute
context = {
current_user: current_user,
request: request
}
result = SharpskillSchema.execute(
params[:query],
variables: params[:variables],
context: context,
operation_name: params[:operationName]
)
render json: result
end
end리졸버 내의 접근 제어는 context[:current_user]를 확인하고 권한이 없을 때 GraphQL::ExecutionError를 발생시킵니다. graphql-ruby 인가 가이드에서 필드 레벨과 타입 레벨 인가 패턴을 자세히 설명합니다.
GraphQL 구독과 ActionCable을 통한 실시간 업데이트
구독을 통해 클라이언트는 서버 측 이벤트 발생 시 업데이트를 받을 수 있습니다. Rails ActionCable이 WebSocket 연결을 처리하고, graphql-ruby는 GraphQL::Subscriptions::ActionCableSubscriptions를 통해 통합됩니다.
# app/graphql/types/subscription_type.rb
module Types
class SubscriptionType < Types::BaseObject
field :post_created, Types::PostType, null: false do
argument :user_id, ID, required: false
end
def post_created(user_id: nil)
object # trigger에서 전달된 객체
end
end
end구독 트리거는 애플리케이션의 어디에서나 발생할 수 있습니다. 일반적으로 모델 콜백이나 서비스 객체 내에서 실행됩니다.
# app/models/post.rb
class Post < ApplicationRecord
belongs_to :user
after_create_commit :notify_subscribers
private
def notify_subscribers
SharpskillSchema.subscriptions.trigger(
:post_created,
{ user_id: user_id },
self
)
end
endGraphQL 구독을 처리하는 ActionCable 채널은 gem에 포함되어 있습니다.
# app/channels/graphql_channel.rb
class GraphqlChannel < ApplicationCable::Channel
def subscribed
@subscription_ids = []
end
def execute(data)
result = SharpskillSchema.execute(
data['query'],
variables: data['variables'],
context: { current_user: current_user, channel: self },
operation_name: data['operationName']
)
payload = { result: result.to_h, more: result.subscription? }
@subscription_ids << result.context[:subscription_id] if result.subscription?
transmit(payload)
end
def unsubscribed
@subscription_ids.each do |sid|
SharpskillSchema.subscriptions.delete_subscription(sid)
end
end
end클라이언트 측에서는 Apollo Client나 urql 같은 라이브러리가 ActionCable WebSocket에 연결하고 구독 상태를 관리합니다. ActionCable 메커니즘에 대한 면접 준비는 ActionCable & WebSockets 모듈을 참조하십시오.
RSpec을 사용한 GraphQL API 테스트
GraphQL API 테스트는 스키마에 대해 쿼리를 실행하고 응답 구조와 데이터에 대해 어설션을 수행합니다.
# spec/graphql/queries/users_spec.rb
RSpec.describe 'Users query' do
let!(:user) { create(:user, email: 'test@example.com') }
let(:query) do
<<~GRAPHQL
query {
users(emailContains: "test") {
id
email
}
}
GRAPHQL
end
it 'returns users matching the filter' do
result = SharpskillSchema.execute(query)
users = result.dig('data', 'users')
expect(users.length).to eq(1)
expect(users.first['email']).to eq('test@example.com')
end
end뮤테이션 테스트는 성공과 유효성 검사 에러 경로를 모두 검증합니다.
# spec/graphql/mutations/create_post_spec.rb
RSpec.describe Mutations::CreatePost do
let(:user) { create(:user) }
let(:context) { { current_user: user } }
let(:mutation) do
<<~GRAPHQL
mutation($title: String!, $body: String!) {
createPost(input: { title: $title, body: $body }) {
post { id title }
errors
}
}
GRAPHQL
end
it 'creates a post when valid' do
result = SharpskillSchema.execute(
mutation,
variables: { title: 'Hello', body: 'World' },
context: context
)
data = result.dig('data', 'createPost')
expect(data['errors']).to be_empty
expect(data['post']['title']).to eq('Hello')
end
it 'returns errors when invalid' do
result = SharpskillSchema.execute(
mutation,
variables: { title: '', body: 'World' },
context: context
)
data = result.dig('data', 'createPost')
expect(data['errors']).to include("Title can't be blank")
end
endRails 테스트 패턴에 대한 자세한 내용은 RSpec 테스트 모듈을 참조하십시오.
Rails 개발자를 위한 GraphQL 면접 빈출 질문
Rails 환경에서 GraphQL 지식을 테스트하는 면접관은 추상적인 타입 이론보다 실용적인 구현 관련 사항에 초점을 맞추는 경우가 많습니다.
graphql-ruby에서 N+1 쿼리를 어떻게 방지합니까?
커스텀 소스를 가진 DataLoader를 사용합니다. 데이터베이스 호출을 배치 처리하는 소스 클래스를 정의하고, 리졸버에서 dataloader.with(SourceClass, args).load(id)를 호출합니다. DataLoader는 단일 GraphQL 실행에서 요청된 모든 ID를 수집하고 한 번의 쿼리로 페치합니다.
쿼리와 뮤테이션의 차이점은 무엇입니까?
의미론적 차이입니다. 쿼리는 데이터를 읽고 부작용이 없어야 합니다. 뮤테이션은 데이터를 수정하고 부작용을 가질 수 있습니다. GraphQL은 쿼리 필드의 병렬 실행과 뮤테이션 필드의 순차 실행을 보장하므로, 같은 요청의 두 뮤테이션은 순서대로 실행됩니다.
Rails에서 구독은 어떻게 작동합니까?
구독은 WebSocket 연결을 위해 ActionCable을 사용합니다. 클라이언트는 WebSocket을 통해 구독 쿼리를 전송합니다. 서버는 구독을 저장하고, trigger가 호출되면 일치하는 모든 클라이언트에 결과를 푸시합니다. 구독 ID를 통해 클라이언트는 구독을 취소할 수 있습니다.
Rails API에서 REST 대신 GraphQL을 선택하는 경우는 언제입니까?
GraphQL은 클라이언트가 다양한 데이터 서브셋을 필요로 할 때(다양한 화면 크기의 모바일 앱에서 흔함) 오버페칭을 줄입니다. 또한 단일 뷰가 여러 모델의 데이터를 필요로 할 때 여러 REST 엔드포인트의 필요성을 제거합니다. REST는 일관된 데이터 형태를 가진 CRUD 중심 API와 캐싱이 중요한 공개 API에서 여전히 더 간단합니다.
graphql-ruby에서 인증과 인가를 어떻게 처리합니까?
인증은 GraphQL 실행 전에 컨트롤러에서 발생하며, 일반적으로 Devise, Warden 또는 JWT 검증을 사용합니다. 인증된 사용자는 context로 전달됩니다. 인가는 리졸버 내에서 또는 graphql-ruby의 타입과 뮤테이션에 대한 authorized? 훅을 통해 발생하며, 접근이 거부되면 GraphQL::ExecutionError를 발생시킵니다.
Ruby on Rails 면접 준비에 대한 자세한 내용은 GraphQL 지식을 보완하는 RESTful API 디자인 패턴을 다루는 Rails API 모드 모듈을 참조하십시오.
연습을 시작하세요!
면접 시뮬레이터와 기술 테스트로 지식을 테스트하세요.
Rails에서 프로덕션 GraphQL API 구축하기: 핵심 요점
rails generate graphql:install로 graphql-ruby를 설치하여 스키마, 타입, 라우팅을 스캐폴드합니다- 모든 관계에 DataLoader 소스를 사용하여 데이터베이스 쿼리를 배치 처리하고 N+1 성능 문제를 방지합니다
- 뮤테이션은 반환 타입에 명시적인 에러 필드를 가진 별도 클래스로 정의합니다
- ActionCable을 통해 구독을 통합하고, 모델 콜백이나 서비스 객체에서 트리거하여 실시간 업데이트를 구현합니다
- RSpec에서 스키마에 대해 직접 쿼리와 뮤테이션을 실행하여 테스트합니다
- 인증은 컨트롤러에서 처리하고, 인가 검사는
context[:current_user]를 사용하여 리졸버 내에서 수행합니다 - 클라이언트가 유연한 데이터 페칭을 필요로 할 때 GraphQL을 선택하고, 균일한 응답을 가진 간단한 CRUD API에서는 REST를 유지합니다
Ruby on Rails 코드의 버그를 찾을 수 있나요
실제 코드 한 조각, 숨은 버그 하나, 하루 한 번. 계정 없이 바로 도전할 수 있습니다.

작성자
Anthony Fillion-MailletSharpSkill 창업자
10년 이상 풀스택 개발을 해왔습니다. SharpSkill을 운영하며 이곳에 게시되는 모든 내용에 책임을 집니다.
2026년 9월 19일 업데이트
태그
공유
관련 기사

Rails 8의 Solid Queue와 Solid Cache: 2026년 기술 면접 완벽 가이드
Rails 8에서 기본 탑재된 Solid Queue와 Solid Cache가 Redis를 대체하는 방식을 설명합니다. 아키텍처, 설정, 동시성 제어, 2026년 면접 핵심 질문까지 총정리합니다.

Ruby on Rails 면접 질문: 2026년 Top 25
가장 자주 묻는 Ruby on Rails 면접 질문 25선. MVC 아키텍처, Active Record, 마이그레이션, RSpec 테스트, REST API에 대한 자세한 답변과 코드 예제.

Rails 백그라운드 작업 2026: Sidekiq vs Good Job 비교 및 면접 질문
2026년 Rails에서 백그라운드 작업 처리를 상세히 분석합니다. Sidekiq, Good Job, Solid Queue의 특징을 비교하고 기술 면접에서 자주 출제되는 질문과 답변을 다룹니다.