Rails 앱에서 CurrentAttributes를 사용하여 멀티테넌시 구현하기

Achieving Multitenancy in a Rails App Using CurrentAttributes | Hashrocket

작성자
Ruby Weekly
발행일
2025년 08월 05일

핵심 요약

  • 1 Rails 애플리케이션에서 ActiveSupport::CurrentAttributes를 활용하여 멀티테넌시를 성공적으로 구현한 사례를 다룹니다.
  • 2 레거시 PRO/5 데이터베이스 연동 시 company_id 기반의 데이터 필터링 문제를 효율적으로 해결했습니다.
  • 3 CurrentAttributes는 요청별 전역 속성을 안전하게 관리하여 코드 복잡성을 줄이는 데 기여했습니다.

도입

이 글은 레거시 BBj PRO/5 데이터베이스와 연동되는 Rails 애플리케이션에서 멀티테넌시(Multitenancy)를 구현한 사례를 다룹니다. 클라이언트는 여러 자회사를 관리하며 CMS 사용자가 회사 간 데이터를 손쉽게 전환할 수 있도록 요구했으며, ActiveSupport::CurrentAttributes를 활용하여 이 문제를 해결한 과정을 상세히 설명합니다.

클라이언트의 CMS는 다양한 규칙을 가진 여러 자회사를 관리해야 했으며, 사용자들은 회사 간 데이터를 쉽게 볼 수 있고 전환할 수 있어야 했습니다. 데이터는 외래 테이블에 company_id 컬럼과 함께 존재했지만, 레거시 시스템의 제약으로 테이블 변경은 어려웠습니다. 회사 간 강력한 보안 분리가 불필요했기에, 별도 스키마나 데이터베이스 분리 대신 모든 외래 테이블을 퍼블릭 스키마에 두기로 결정했습니다. 핵심 과제는 요청별로 현재 회사를 식별하고 그에 따라 데이터를 필터링하는 것이었고, Rails의 ActiveSupport::CurrentAttributes가 이러한 전역적인, 요청별 속성 관리에 적합하다고 판단되었습니다.

구현은 ActiveSupport::CurrentAttributes를 상속받는 Current 클래스에 company_id 속성을 정의하는 것으로 시작했습니다. 다음으로, ApplicationRecord를 상속하는 MultitenantRecord 클래스에 default_scope를 추가하여 Current.company_id에 따라 레코드를 자동으로 필터링하도록 했습니다. 이때 company_id가 설정되지 않으면 오류를 발생시키는 가드 절을 포함했습니다. 마지막으로, ApplicationControllerbefore_action에서 인증된 사용자의 company_idCurrent.company_id에 할당하여 각 요청에 대한 현재 회사를 지정했습니다. 이로써 대부분의 멀티테넌시 기능이 구현되었고, Current 객체는 뷰의 기능 플래그나 외부 API 연동 등 다양한 곳에서 활용되었습니다.

그러나 두 가지 주요 난관이 있었습니다. 첫째, 백그라운드 작업은 별도의 프로세스에서 실행되므로 CurrentAttributes 값이 자동으로 전달되지 않아 company_id를 각 작업에 명시적으로 전달해야 했습니다. 둘째, 요청 스펙(request specs)에서 컨트롤러 처리 후 CurrentAttributes 값이 초기화되어 테스트 실패가 발생했습니다. 이 문제는 Current 클래스에 with_company 헬퍼 메서드를 추가하여 set 블록 내에서 company_id 컨텍스트를 유지하도록 함으로써 해결했습니다.

결론

`CurrentAttributes`는 Rails 애플리케이션에서 전역 변수를 효과적으로 다루는 강력한 도구임을 보여줍니다. 이 도구를 통해 CMS에 멀티테넌시 기능을 신속하게 구현할 수 있었고, 백그라운드 작업을 제외한 애플리케이션 코드의 거의 모든 부분에서 `company_id` 변수를 개별적으로 전달할 필요성을 없앴습니다. 이는 코드의 복잡성을 줄이고 유지보수성을 향상시키는 데 기여했으며, 특정 조건 하에서 `CurrentAttributes`가 매우 유용하게 활용될 수 있음을 시사합니다.

댓글 0

댓글 작성

0/1000
정중하고 건설적인 댓글을 작성해 주세요.

아직 댓글이 없습니다

첫 번째 댓글을 작성해보세요!