본문으로 건너뛰기

JavaScript 개발자를 위한 Rails 가이드: Pundit을 활용한 권한 부여 시스템 이해하기

A JavaScript developer's guide to Rails: Authorization with Pundit

작성자
jeff
발행일
2026년 02월 18일

핵심 요약

  • 1 Pundit은 "사용자가 특정 리소스에 대해 특정 작업을 수행할 수 있는가"라는 질문에 답하는 정책(Policy) 클래스를 통해 권한 로직을 중앙 집중화합니다.
  • 2 개별 레코드에 대한 접근을 제어하는 authorize 메서드와 컬렉션 전체를 필터링하는 policy_scope를 구분하여 사용하는 것이 핵심입니다.
  • 3 after_action을 통한 강제 검증 설정을 활용하면 개발 단계에서 권한 확인 누락을 방지하고 애플리케이션의 보안성을 획기적으로 높일 수 있습니다.

도입

JavaScript 환경에서 프론트엔드 중심의 조건문이나 단순한 역할 기반 접근 제어에 익숙했던 개발자들에게 Rails의 Pundit은 다소 생소할 수 있습니다. Pundit은 권한 부여를 단순한 코드 조각이 아닌 하나의 체계적인 시스템으로 다루며, 비즈니스 로직과 권한 로직을 명확히 분리합니다. 이 글은 Pundit의 핵심 동작 원리인 Policy와 Scope의 차이점을 설명하고, 요청 흐름에 따라 권한이 어떻게 검증되는지 상세히 다룹니다.

1. Pundit의 핵심 개념: Policy와 Scope

Pundit은 크게 두 가지 레이어로 구성됩니다. * Policy (정책): “이 사용자가 이 리소스에 대해 이 동작을 수행할 수 있는가?”라는 질문에 답합니다. 예를 들어, 특정 사용자가 다른 팀의 연락처를 수정할 수 있는지, 관리자가 아닌 사용자가 리소스를 삭제할 수 있는지 등을 결정합니다. * Scope (범위): “이 사용자가 접근할 수 있는 리소스 목록은 무엇인가?”라는 질문에 답합니다. 관리자는 모든 레코드를 볼 수 있지만, 일반 상담원은 자신이 담당하는 레코드만 볼 수 있도록 데이터를 필터링합니다.

중요한 점은 Pundit에 정렬(sorting), 비즈니스 필터(status=’active’), 페이지네이션, Eager Loading과 같은 쿼리 관련 로직을 넣지 않는 것입니다. Pundit은 오직 ‘권한’에만 집중해야 합니다.

2. 요청 흐름과 동작 원리

사용자가 ‘연락처 수정’ 버튼을 클릭했을 때의 흐름은 다음과 같습니다. 1. Controller: authorize @contact 메서드를 호출합니다. 2. Pundit 탐색: @contact 객체의 클래스 이름(Contact)을 확인하고 대응하는 ContactPolicy를 찾습니다. 3. 메서드 실행: 현재 컨트롤러 액션(edit)에 대응하는 정책 메서드(edit?)를 호출합니다. 4. 결과 처리: 메서드가 true를 반환하면 액션을 계속 진행하고, false를 반환하면 Pundit::NotAuthorizedError 예외를 발생시킵니다.

이 과정에서 Pundit은 내부적으로 ContactPolicy.new(current_user, @contact)를 실행하여 정책 인스턴스를 생성합니다. 이는 Pundit이 단순히 루비 메서드를 호출하는 세련된 방식임을 보여줍니다.

3. Policy 클래스의 구조와 내부 변수

모든 정책 클래스는 기본적으로 ApplicationPolicy를 상속받습니다. 이 베이스 클래스는 초기화 시 userrecord를 인자로 받아 내부 변수로 저장합니다. * user: 현재 로그인한 사용자 (current_user 반환값) * record: 권한을 확인하려는 대상 객체 (모델 인스턴스 또는 클래스)

ruby class ContactPolicy < ApplicationPolicy def edit? user.admin? || record.user_id == user.id end end 위와 같이 작성하면, 관리자이거나 해당 레코드의 소유자인 경우에만 수정 권한을 부여하게 됩니다. attr_reader를 통해 userrecord에 쉽게 접근할 수 있습니다.

4. 컬렉션 필터링을 위한 Scope 활용

인덱스(index) 액션처럼 여러 레코드를 보여줄 때는 policy_scope를 사용합니다. 이는 단순히 데이터를 가져오는 것이 아니라, 권한에 따라 데이터 범위를 제한합니다. ruby def index @contacts = policy_scope(Contact) end 이 코드는 ContactPolicy::Scope 클래스의 resolve 메서드를 호출합니다. 관리자는 scope.all을, 일반 사용자는 scope.where(user_id: user.id)를 반환받도록 설계하여 보안 사고를 방지합니다. Scope는 백그라운드 작업이나 서비스 객체에서도 독립적으로 호출하여 재사용할 수 있습니다.

5. 보안 강화를 위한 강제 검증 설정

Pundit의 가장 강력한 기능 중 하나는 권한 검증 누락을 방지하는 것입니다. ApplicationControllerafter_action 설정을 추가하여 모든 액션에서 권한 검사가 수행되었는지 강제할 수 있습니다. 만약 개발자가 authorizepolicy_scope 호출을 잊었다면, Pundit은 에러를 발생시켜 이를 즉시 알립니다. 예외적으로 권한 검사가 필요 없는 공공 페이지의 경우 skip_authorization을 명시적으로 호출하여 의도를 분명히 할 수 있습니다.

결론

Pundit을 도입하면 권한 로직이 컨트롤러나 뷰에 흩어지지 않고 독립적인 정책 클래스에 모이게 되어 코드의 가독성과 테스트 용이성이 크게 향상됩니다. 특히 Rails의 강력한 콜백 시스템과 결합하여 권한 검증을 강제함으로써 보안 실수를 사전에 방지할 수 있다는 점이 큰 장점입니다. 처음에는 구조가 복잡해 보일 수 있으나, 객체 지향적인 접근 방식을 이해하면 대규모 애플리케이션에서도 유지보수가 쉬운 견고한 보안 계층을 구축할 수 있습니다.

댓글0

댓글 작성

댓글 삭제 시 비밀번호가 필요합니다.

이미 계정이 있으신가요? 로그인 후 댓글을 작성하세요.

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