모듈식 Rails SaaS 애플리케이션을 구조화하는 방법
Source: Dev.to

이전 글에서 저는 5 년 전만큼 가지고 싶었던 Rails SaaS 아키텍처에 대해 썼습니다.
핵심 아이디어는 간단했습니다: Rails 애플리케이션이 실제 SaaS 제품으로 성장하면, 문제는 더 이상 기능을 빠르게 작성하는 것만이 아니게 됩니다.
진정한 도전은 시스템을 이해하기 쉽게 유지하는 것이 됩니다.
그것은 자연스럽게 다음 질문으로 이어집니다:
그 구조가 실제로 어떻게 보이는가?
이 글은 그 질문에 대한 제 시도입니다. 이것이 Rails 앱을 조직하는 유일한 방법은 아닙니다; 다만 현재 여러 비즈니스 기능, 내부 도구, 장기적인 성장을 가진 제품에 가장 적합하다고 생각되는 구조일 뿐입니다.
기본 성장 경로의 문제점
대부분의 Rails 애플리케이션은 매우 합리적인 구조로 시작합니다:
app/
config/
db/
lib/
시작 단계에서는 잘 작동합니다.
하지만 제품이 성장함에 따라 많은 비즈니스 기능들이 동일한 애플리케이션 레이어에 나란히 존재하게 됩니다:
- 인증
- 역할 및 권한
- 알림
- 대시보드
- 감사
- 지원 티켓
- 파일 관리
- 청구 로직
- 관리자 도구
이 시점에서 문제는 Rails가 나쁘다는 것이 아니라, 모든 것이 동일한 앱 경계 안에서 공간을 차지하려고 경쟁하기 시작한다는 것입니다.
- 모델이 너무 많은 것을 인식하게 됩니다.
- 컨트롤러가 관련 없는 관심사를 조정하기 시작합니다.
- 헬퍼가 이상한 방향으로 성장합니다.
- 컨선(Concern)이 늘어납니다.
결국 개별 기능이 그리 복잡하지 않음에도 불구하고 애플리케이션이 크게 느껴지게 됩니다.
전환: 역량에 따른 구조화
저에게 더 잘 맞았던 방법은 비즈니스 역량 별로 시스템을 구조화하는 것이었습니다. 단순히 기술 계층만으로 나누는 것이 아니라 말이죠.
따라서 다음과 같이만 생각하는 대신에:
- 모델
- 컨트롤러
- 뷰
다음과 같이 생각합니다:
- 지원
- 감사
- 관리
- 계정
- 사용자
- 대시보드
- 청구
각각의 역량마다 자체 경계를 갖게 됩니다. Rails에서는 이를 가장 깔끔하게 구현할 수 있는 방법이 엔진(engines) 을 사용하는 것입니다.
간단한 고수준 구조
모듈식 Rails SaaS 애플리케이션은 다음과 같이 보일 수 있습니다:
my_app/
├── app/
├── config/
├── db/
├── lib/
├── engines/
│ ├── lesli_core/
│ ├── lesli_admin/
│ ├── lesli_audit/
│ ├── lesli_billing/
│ ├── lesli_dashboard/
│ ├── lesli_shield/
│ └── lesli_support/
└── Gemfile
메인 애플리케이션은 여전히 존재하지만, 이제 모든 기능이 쏟아지는 장소는 아닙니다.
대신, 메인 앱은 통합 계층 역할을 하며, 엔진들은 실제 비즈니스 기능을 포함합니다.
메인 앱에 포함되어야 할 것?
이 부분이 가장 중요합니다. 모듈식 구조는 메인 앱이 규칙을 지킬 때만 제대로 작동합니다. 제 경우 메인 Rails 앱은 보통 다음을 담당합니다:
- 환경 설정
- 배포 설정
- 부팅 프로세스
- 엔진 마운팅
- 앱‑특화 브랜딩 및 오버라이드
- 제품‑특화 커스텀 로직
- 시스템 최종 구성
따라서 앱 자체는 여전히 중요하지만, 이제 모든 도메인을 직접 소유한다고 가장하지는 않습니다.
Source:
엔진에 무엇이 포함되어야 할까?
각 엔진은 명확한 기능을 가집니다. 예를 들어, support 엔진은 다음과 같은 구조를 가질 수 있습니다:
engines/lesli_support/
├── app/
│ ├── controllers/
│ ├── models/
│ ├── views/
│ └── components/
├── config/
│ └── routes.rb
├── db/
│ └── migrate/
├── lib/
│ └── lesli_support/
└── lesli_support.gemspec
그 엔진은 다음을 포함할 수 있습니다:
- 티켓
- 댓글 또는 토론
- 상태
- 우선순위
- 할당 흐름
- 지원 전용 대시보드
- 지원과 관련된 알림
이렇게 하면 매우 가치 있는 지역적 추론이 가능해집니다. 지원 작업을 해야 할 때, 다른 레이어에 섞여 있는 티켓 로직을 찾기 위해 거대한 애플리케이션을 뒤지지 않고 바로 지원 엔진으로 이동할 수 있습니다.
코어 레이어의 역할
나는 여전히 공유 코어 레이어를 선호하지만, 작고 의도적으로 유지해야 한다. 내게 코어 엔진은 보통 다음과 같은 것들을 포함한다:
- 진정으로 횡단되는 공유 관심사
- 공유 UI 프리미티브
- 기본 클래스
- 공통 헬퍼
- 플랫폼 구성 헬퍼
- 엔진 간 공통 인터페이스
내가 피하려는 것은 코어 레이어를 두 번째 모놀리스로 만드는 것이다. core가 모든 엔진이 공유 단축키를 버리는 장소가 되면, 아키텍처는 서서히 같은 문제로 되돌아간다. 그래서 나는 계속 스스로에게 묻는다:
이것이 정말 횡단적인가, 아니면 더 깔끔한 경계를 피하고 있는 것인가?
라우팅 및 구성
이 접근 방식에서 내가 좋아하는 점 중 하나는 구성이 명시적으로 유지된다는 것이다. 메인 애플리케이션이 무엇을 마운트할지 결정한다. 간소화된 예시:
# config/routes.rb
Rails.application.routes.draw do
mount LesliAdmin::Engine , at: "/admin"
mount LesliSupport::Engine , at: "/support"
mount LesliAudit::Engine , at: "/audit"
end
그것은 최종 애플리케이션 형태를 이해하기 쉽게 만든다. 제품은 미스터리가 아니라; 기능들의 조합이다.
경계는 재사용보다 더 중요합니다
엔진의 좋은 부수 효과 중 하나는 재사용이지만, 그것이 내가 엔진을 좋아하는 주된 이유는 아닙니다. 더 큰 이점은 강제된 경계입니다.
경계가 없으면 모든 기능이 결국 다른 모든 것에 침투하게 됩니다.
경계가 있으면 통합은 의도적으로 이루어져야 합니다.
이것은 코드베이스가 성장하는 방식을 바꿉니다. 더 나은 질문을 스스로에게 하게 됩니다:
- 이 로직 조각이 기존 엔진에 속해야 할까요, 아니면 자체 기능을 가져야 할까요?
- 엔진들은 어떻게 긴밀하게 결합되지 않으면서 소통할 수 있을까요?
- 무엇이 핵심 레이어에 속하고, 무엇이 특정 엔진에 속해야 할까요?
이러한 질문들을 앞에 두고 고민함으로써, 아키텍처는 모듈화되고 유지보수가 쉬우며 장기적인 성장에 대비할 수 있습니다.
이런 의존성이 존재할 수 있을까?
- 지원팀이 실제로 청구 내부 구조를 알아야 할까요?
- 이 관심사가 공유된 것인가, 아니면 잘못 배치된 것인가?
- 이 로직은 앱, 엔진, 혹은 코어 레이어 중 어디에 있어야 할까요?
그러한 질문들은 어떤 명명 규칙보다도 아키텍처를 개선합니다.
이것은 무료가 아니다
공정하게 말하자면, 이러한 구조는 어느 정도 오버헤드를 추가합니다.
다음과 같은 사항들을 더 많이 고민해야 합니다:
- 엔진 명명
- 도메인 경계
- 의존성 방향
- 엔진 간 마이그레이션
- 공유 컨벤션
- 로컬 개발 워크플로우
따라서 모든 프로젝트에 사용하지 않을 것입니다.
작은 내부 도구, MVP, 혹은 매우 빠르게 배포해야 하는 무언가를 만들고 있다면, 이 구조는 너무 이른 시점에 과도하게 느껴질 수 있습니다.
하지만 제품이 여러 기능에 걸쳐 성장하기 시작하면, 그 추가 구조는 무겁게 느껴지는 것이 아니라 유용하게 느껴지게 됩니다.
이 접근 방식에서 가장 마음에 드는 점
내가 가장 마음에 드는 것은 그것이 더 “고급”처럼 느껴진다는 것이 아니다.
시스템을 더 쉽게 다룰 수 있게 만든다는 점이다.
- 기능을 추가할 때, 그것이 어디에 들어가야 할지 더 명확하게 파악할 수 있다.
- 디버깅을 할 때, 관련 없는 앱의 다른 부분을 뛰어다니지 않는다.
- 리팩터링을 할 때, 주변을 모두 깨뜨리지 않을 것이라는 약간의 자신감이 생긴다.
내게 있어 좋은 아키텍처가 해야 할 일이다.
- 추상화 점수를 얻기 위해서가 아니다.
- 다이어그램에서 인상적으로 보이기 위해서가 아니다.
- 단지 앱이 성장함에 따라 이해하기 쉽게 만드는 것이다.
이것이 Lesli와 연결되는 방식
이는 Lesli를 구축하면서 탐구해 온 전반적인 방향과 동일합니다.
Rails를 더 복잡하게 만들려는 것이 아닙니다.
주로 더 큰 SaaS‑스타일 애플리케이션에 더 깔끔한 성장 공간을 제공하려는 것입니다.
Lesli는 아직 진화 중이지만, 이 모듈식 접근 방식은 그 배경에 있는 아이디어 중 하나입니다.
