기존 방식의 문제점
전통적인 MVC 모델에서 비즈니스 로직은 컨트롤러 액션(동기)과 백그라운드 잡(비동기)으로 나뉘어 구현됩니다. 예를 들어, 사용자 가입 워크플로우는 SignupsController에서 사용자 레코드를 생성하고, UserActivationJob에서 계정을 생성하고 활성화 이메일을 보내는 식으로 분리됩니다. 이는 다음과 같은 문제를 야기합니다.
-
도메인 로직 분산: 동일한 워크플로우의 여러 단계가 서로 다른 파일과 실행 컨텍스트에 흩어져 있어 전체 흐름을 한눈에 파악하기 어렵습니다.
-
코드 이해도 저해: 개발자는 여러 파일을 탐색하며 워크플로우 조각들을 머릿속으로 조합해야 합니다.
-
인프라 종속성: 도메인 로직이 ‘빠른 것’은 컨트롤러, ‘느리고 취약한 것’은 백그라운드 잡이라는 인프라적 경계에 맞춰 모델링됩니다.
응집력 있는 워크플로우 객체 제안
저자는 이러한 문제에 대한 개선책으로 워크플로우를 단일 객체(예: Signup 클래스)에 통합하여 도메인 중심의 응집력을 높이는 방식을 제안합니다. 이 객체는 워크플로우의 모든 단계를 포함하며, 컨트롤러는 웹 컨텍스트에서 필요한 부분을 호출하고, 경량의 일반적인 백그라운드 잡은 비동기적으로 실행할 단계를 위임합니다.
```ruby # Signup 워크플로우를 나타내는 클래스 class Signup < ApplicationRecord # … def create_account # … end # … end
컨트롤러는 웹 컨텍스트에서 워크플로우의 일부를 실행
class SignupsController < ApplicationController def create signup = Signup.start(user_params) SignupJob.perform_later(signup, :create_account) end # … end
잡은 백그라운드에서 실행할 부분을 담당
class SignupJob < ApplicationJob def perform(signup, step) case step when :create_account signup.create_account # … end end end ```
이 방식은 실행 인프라와 분리된 단일 장소에서 전체 워크플로우를 읽을 수 있게 하여 가독성을 높입니다.
여전히 남는 과제
이러한 개선에도 불구하고, 다음과 같은 문제들이 여전히 남아있습니다.
-
복구 및 재시도 전략: 특정 단계가 실패하거나 재시도될 때의 동작, 계정 생성 후 이메일 전송 실패 시의 복구 전략 등이 명확히 정의되기 어렵습니다.
-
단계 간 의존성: 단계 간의 의존성을 어떻게 선언하고 관리할 것인지에 대한 해답이 부족합니다.
-
혼합 실행 워크플로우: 이메일 전송 성공 여부에 따라 사용자에게 다른 페이지를 보여주는 것과 같이, 포그라운드와 백그라운드, 사용자 입력이 혼합된 복합적인 워크플로우를 모델링하는 데 한계가 있습니다.
프레임워크가 제공하는 내구성 있는(durable) 백그라운드 워크플로우 지원은 도움이 되지만, 여전히 웹의 다단계 워크플로우를 총체적으로 표현하기에는 부족합니다.