Ruby 모델 클래스, 서비스 객체 및 인터랙터 사용

발행: (2026년 1월 16일 오전 06:47 GMT+9)
6 min read
원문: Dev.to

Source: Dev.to

번역할 텍스트가 제공되지 않았습니다. 번역을 원하는 본문을 입력해 주시면 한국어로 번역해 드리겠습니다.

Source:

왜 기능에 모델 클래스를 사용할까?

Rails에서 모델 클래스(예: app/models에 있는 간단한 PORO)를 사용하면 여러 가지 장점이 있습니다.
개발자들은 로직을 컨트롤러, 헬퍼, 초기화 파일 등에 흩어놓기보다 “모델 클래스”에 넣는 것을 선호합니다.

아래는 핵심 장점입니다:

1. 중앙 집중식, 재사용 가능한 로직

기능 로직이 모델 클래스(FeatureFlag, BetaAccess, Onboarding 등)에 있으면 다음 곳에서 재사용할 수 있습니다:

  • 컨트롤러
  • 백그라운드 잡
  • 서비스
  • Pundit 정책

…여러 곳에 로직을 중복해서 넣는 대신.

2. 컨트롤러와 뷰를 깔끔하게 유지

Rails 컨트롤러와 뷰는 얇게 유지해야 합니다.
도메인 로직을 모델에 넣으면 설계가 깔끔해집니다(Fat Model, Skinny Controller).

다음과 같이:

if user.admin? && SomeConfig.beta_enabled?
  # …
end

대신:

if BetaAccess.allowed_for?(user)
  # …
end

3. 테스트 용이성 향상

모델은 가장 테스트하기 쉬운 대상입니다:

RSpec.describe BetaAccess do
  describe ".allowed_for?" do
    # …
  end
end

컨트롤러를 띄우거나 웹 요청을 시뮬레이션할 필요가 없습니다.

4. 규칙 캡슐화

기능 로직이 복잡해질 경우, 모델 하나에 모두 모아두면 관리가 쉽습니다.

class Onboarding
  def completed?(user)
    user.profile_filled? && user.verified? && user.tutorial_done?
  end
end

새로운 온보딩 규칙을 추가하고 싶을 때는 클래스만 업데이트하면 됩니다.

5. 명확한 네이밍 + 가독성 향상

전용 모델은 의도를 명확히 전달합니다:

if FeatureFlag.enabled?(:new_ui)
  # …
end

…은 다음보다 읽기 쉽습니다:

if Rails.configuration.x.new_ui_enabled
  # …
end

6. 나중에 영속성 지원이 쉬움

처음에는 간단한 PORO로 시작할 수 있습니다:

class FeatureFlag
  FLAGS = { new_ui: false }
end

나중에 인터페이스를 바꾸지 않고 ActiveRecord 모델로 전환할 수 있습니다:

class FeatureFlag  e
    context.fail!(error: e.message)
  end
end

모델 vs. 서비스 vs. 인터랙터

개념책임예시
모델도메인 상태와 규칙을 나타내며; 속성과 동작을 캡슐화한다FeatureFlag, User, Subscription
서비스개별 작업을 수행하며; 여러 모델을 사용할 수 있다PaymentProcessor, EmailSender
인터랙터모델 및 서비스를 사용하여 워크플로/트랜잭션을 조정하고; 성공/실패를 처리한다CreateOrder, SendWeeklyReport, EnrollUserInCourse

핵심 차이점

  • 서비스 = 한 가지 일을 수행한다.
  • 인터랙터 = 여러 작업을 하나의 비즈니스 운영으로 조정한다.

Source:

인터랙터가 모델과 서비스 사이에 어떻게 위치하는가

  • Models → 상태와 도메인 로직을 보유

    FeatureFlag.enabled?(:new_ui)
  • Services → 하나의 모델 또는 도메인과 관련된 작업을 수행

    PaymentProcessor.charge(order)
  • Interactors → 여러 모델과 서비스를 하나의 트랜잭션 워크플로우로 조정

    CreateOrder.call(params: order_params)

비유

  • Model = 레고 블록
  • Service = 단일 레고 작품 (예: 문이나 바퀴)
  • Interactor = 전체 레고 세트 (여러 작품을 결합해 작동하는 시스템을 만듦)

Interactor, Service, Model을 언제 사용해야 할까

사용 사례권장 패턴
상태, 규칙, 계산, 또는 쿼리Model
모델에 작용하는 단일 액션Service
실패할 수 있고 깔끔한 오케스트레이션이 필요한 다단계 워크플로Interactor

예시 워크플로

# Model
class User; end
class FeatureFlag; end

# Service
class WelcomeEmailSender; end

# Interactor
class OnboardNewUser
  include Interactor

  def call
    user = User.create!(context.params)
    WelcomeEmailSender.send(user)
    context.success_message = "Welcome #{user.name}!"
  rescue => e
    context.fail!(error: e.message)
  end
end

Source: Originally posted at DevBlog.

요약

패턴적합한 경우부적합한 경우
Model (PORO or ActiveRecord)도메인 개념, 규칙, 상태일회성 작업
Service실행 가능한 동작 (“X 수행”)도메인 객체를 나타내는 것
Initializer / config정적 규칙확장될 수 있거나 의존성이 필요한 규칙
Interactor다단계 워크플로/트랜잭션 조정단일 목적 상태 또는 간단한 규칙
Back to Blog

관련 글

더 보기 »

Gon v7.0.0 출시

Release v7.0.0 Gon v7.0.0 릴리스 https://github.com/gazay/gon/releases/tag/v7.0.0 – 이번 메이저 버전 업데이트는 breaking changes를 도입합니다. Breaking change: reque...