서비스 클래스의 등장과 초기 정의
서비스 클래스는 Rails 3.1~3.2 시대에 Fat Model(비대해진 모델) 문제에 대한 해결책으로 부상했습니다. 당시 발표자는 ‘퍼펙트 레일즈’에서 서비스 클래스를 컨트롤러와 모델 사이의 인터페이스로 정의하며, 비즈니스 로직을 캡슐화하고 유비쿼터스 언어(Ubiquitous Language)에 기반한 명명의 중요성을 강조했습니다. 이는 도메인 주도 설계(DDD)의 영향을 받은 것으로, 소프트웨어 개발에서 비즈니스 도메인 이해의 중요성을 인지하고 있었습니다.
서비스 클래스 활용의 어려움과 한계
그러나 발표자는 시간이 지나면서 서비스 클래스 사용의 어려움을 깨달았습니다. “신중하게 사용하라”는 기준이 사람마다 다르고, 비즈니스 도메인에 대한 이해도 차이로 인해 ‘단순히 복잡한 것’에 쉽게 이름을 붙이는 경향이 있었습니다. 특히 Active Record의 데이터 조작과 기능의 행위를 적절히 분리하는 것이 매우 어려웠으며, 이는 팀 개발 시 공통 인식 부족으로 이어져 코드의 일관성을 해치는 주된 원인이 되었습니다. Rails의 기본 레이어(Controller-Model-View)는 어디에 무엇이 있을지 예측하기 쉬운 강력한 공통 인식을 제공하는데, 서비스 클래스의 무분별한 도입은 이러한 프레임워크의 장점을 훼손할 수 있습니다.
폼 클래스의 역할과 근본적인 문제
최근 논의되는 폼 클래스는 서비스 클래스와 유사하게 단일 Active Record 클래스의 책임으로 처리하기 어려운 문제의 해결책으로 제시됩니다. 폼 클래스는 화면/액션과 연동되어 명명하기 쉽고, Active Model 및 Active Record 인터페이스와의 친화성이 높다는 장점이 있습니다. 그러나 폼 클래스 역시 Active Record에 기인한 진정한 의존성 문제(예: 여러 폼/서비스에서 Active Record 메서드를 호출할 때 발생하는 수정의 파급 효과)를 해결하지 못합니다. 결국 Active Record를 효과적으로 정리하는 것에서 벗어날 수 없으며, 이는 Rails의 큰 과제 중 하나입니다.
도메인 컨텍스트 이해와 모듈러 모놀리스
근본적인 해결책은 도메인 컨텍스트를 깊이 이해하고 컨텍스트 맵을 만드는 것입니다. 복잡한 시스템에서는 바운디드 컨텍스트(Bounded Context)를 통해 시스템의 각 컴포넌트와 그 상호작용을 명확히 정의하고, 의존성을 한 방향으로 유지하여 경계를 넘는 영향이 없도록 제어해야 합니다. 루비(Ruby) 세계에서는 모듈(Module)을 이용한 이름 공간과 디렉터리 구성으로 이를 시도했으나, 현대에는 모듈러 모놀리스(Modular Monolith)가 현실적인 대안으로 부상하고 있습니다. 모듈러 모놀리스는 분산 시스템의 복잡성을 피하면서도 컨텍스트 경계를 명확히 하고 강제하는 것을 목표로 합니다. Shopify의 Packwerk나 개발 중인 Ruby Boxes(네임스페이스)는 이러한 가시성 제어와 컴포넌트 간 의존성 검증을 위한 도구로 활용될 수 있습니다.
지속적인 판단과 개선의 중요성
컨텍스트 매핑과 컴포넌트 분할은 한 번에 완벽하게 이루어지는 것이 아니라, 코드와 멘탈 모델을 오가며 지속적으로 판단하고 개선해야 하는 과정입니다. 이는 ‘상류(upstream)’ 작업으로 불리며, 개발팀 전체가 꾸준히 노력해야 합니다.