DRF 필터링 및 정렬 동작 원리 분석
Django REST Framework(DRF)에서 GenericAPIView와 ListModelMixin을 상속받는 뷰 클래스는 filter_backends 속성만 적절히 설정하면 내장된 필터링 및 정렬 기능을 즉시 활용할 수 있습니다. 여기에는 DRF 기본 제공 클래스인 SearchFilter, OrderingFilter뿐만 아니라 django-filter 라이브러리나 BaseFilterBackend를 상속받아 직접 구현한 커스텀 필터 클래스도 포함됩니다.
데이터 목록을 조회하는 list 메서드 내부에서는 정렬과 필터링이 어떻게 적용되는지 소스 코드 수준에서 살펴보겠습니다.
def list(self, request, *args, **kwargs):
# 1. 기본 쿼리셋을 가져온 후, 필터링 및 정렬 파이프라인을 통과시킵니다.
base_queryset = self.get_queryset()
filtered_queryset = self.filter_queryset(base_queryset)
# 2. 페이지네이션 설정이 되어 있다면 페이지 단위로 데이터를 분할합니다.
paginated_data = self.paginate_queryset(filtered_queryset)
if paginated_data is not None:
response_serializer = self.get_serializer(paginated_data, many=True)
return self.get_paginated_response(response_serializer.data)
# 3. 페이지네이션이 없다면 전체 쿼리셋을 직렬화하여 응답합니다.
response_serializer = self.get_serializer(filtered_queryset, many=True)
return Response(response_serializer.data)
위 코드에서 핵심은 self.filter_queryset()입니다. 이 메서드는 현재 뷰 클래스에서 정의되지 않았다면 상위 클래스인 GenericAPIView의 로직을 따르게 됩니다.
def filter_queryset(self, queryset):
# 등록된 모든 백엔드를 순회하며 쿼리셋을 점진적으로 필터링합니다.
for filter_backend in list(self.filter_backends):
queryset = filter_backend().filter_queryset(self.request, queryset, self)
return queryset
핵심 요약: 커스텀 필터 클래스를 작성할 때는 반드시 filter_queryset 메서드를 오버라이딩하여 처리된 쿼리셋을 반환해야 합니다. 만약 별도의 필터 클래스를 만들기 부담스럽다면, 뷰 클래스 내에서 직접 filter_queryset을 오버라이딩하여 필터링 로직을 구현하는 것도 유효한 접근 방식입니다.
RBAC 및 다양한 접근 제어 모델
RBAC(Role-Based Access Control)의 개념과 구조
RBAC는 권한을 개별 사용자에게 직접 부여하는 대신 '역할(Role)'이라는 중간 계층을 도입하여 권한을 관리하는 모델입니다. 사용자는 특정 역할의 구성원이 됨으로써 해당 역할에 할당된 권한을 상속받습니다. 이는 계층적이고 종속적인 관리 구조를 형성하여 권한 부여 및 회수를 훨씬 직관적이고 효율적으로 만듭니다.
RBAC는 주로 사내 내부 관리 시스템에 적합하며, 불특정 다수를 대상으로 하는 대외 인터넷 서비스에는 적합하지 않을 수 있습니다. 표준 RBAC는 다음과 같은 5개의 테이블 구조를 가집니다.
- 사용자 테이블 (User): 시스템에 접근하는 개체
- 역할 테이블 (Role): 권한의 집합 (예: 개발자, 인사팀, 주주)
- 권한 테이블 (Permission): 구체적인 작업 단위 (예: 급여 지급, 코드 커밋, 신규 채용)
- 사용자-역할 매핑 테이블: 다대다(N:M) 관계를 위한 중간 테이블
- 역할-권한 매핑 테이블: 다대다(N:M) 관계를 위한 중간 테이블
Django Admin의 확장된 RBAC
Django의 auth 모듈은 기본 RBAC를 넘어선 6개의 테이블 구조를 제공합니다. 여기에는 사용자가 역할(그룹)을 거치지 않고 직접 권한을 할당받을 수 있는 '사용자-권한 매핑 테이블'이 추가되어 있습니다.
auth_user: 사용자 정보auth_group: 역할 및 그룹 정보auth_permission: 권한 정의auth_user_groups: 사용자-그룹 중간 테이블auth_group_permissions: 그룹-권한 중간 테이블auth_user_user_permissions: 사용자-권한 직접 매핑 테이블
이러한 구조 덕분에 Django Admin을 기반으로 사내 백오피스를 구축할 때, 복잡한 권한 로직을 처음부터 구현할 필요 없이 내장된 기능을 활용하여 빠르게 비즈니스 로직에 집중할 수 있습니다.
ACL, ABAC와의 비교 및 Casbin 활용
ACL (Access Control List): 사용자와 객체 간의 권한을 직접 매핑하는 가장 단순한 방식입니다.
ABAC (Attribute-Based Access Control): PBAC(Policy-Based) 또는 CBAC(Claims-Based)라고도 불리며, 주체(Subject), 행위(Action), 객체(Object)에 맥락(Contextual)과 속성(Parameter)을 추가한 고도화된 모델입니다. 예를 들어, 사용자의 부서, 시간대, IP 주소 등의 환경 속성에 따라 동적으로 권한을 평가합니다.
다양한 접근 제어 모델을 코드 수준에서 쉽게 구현하고 싶다면 Casbin 라이브러리를 활용할 수 있습니다.
import casbin
# 모델 정의 파일과 정책 CSV 파일을 로드하여 Enforcer 초기화
access_enforcer = casbin.Enforcer("access_model.conf", "access_policy.csv")
request_user = "developer_kim" # 리소스 접근을 시도하는 주체
target_resource = "production_db" # 접근하려는 대상 리소스
requested_action = "write" # 수행하려는 작업
# Casbin 엔진을 통한 권한 검증
if access_enforcer.enforce(request_user, target_resource, requested_action):
print(f"[허용] {request_user}의 {target_resource}에 대한 {requested_action} 작업이 승인되었습니다.")
else:
print(f"[거부] {request_user}의 접근 요청이 정책에 의해 차단되었습니다.")
SimpleUI를 활용한 Django Admin 커스터마이징
simpleui는 Django Admin의 노후화된 UI를 현대적인 Vue.js 및 Element UI 기반으로 탈바꿈시켜주는 서드파티 패키지입니다. INSTALLED_APPS에 추가하는 것만으로도 즉시 적용되며, settings.py에서 상세한 메뉴 구성과 동작을 제어할 수 있습니다.
SIMPLEUI_CONFIG = {
'system_keep': False,
# 메뉴 표시 순서 및 필터링 (빈 리스트는 모두 숨김, 미정의 시 기본 정렬)
'menu_display': ['대시보드', '콘텐츠 관리', '시스템 설정', '다중 메뉴 테스트'],
'dynamic': True, # 로그인 시마다 메뉴를 동적으로 렌더링할지 여부
'menus': [
{
'name': '대시보드',
'icon': 'fas fa-tachometer-alt',
'url': '/dashboard/'
},
{
'app': 'content',
'name': '콘텐츠 관리',
'icon': 'fas fa-book',
'models': [
{'name': '게시글', 'icon': 'fa fa-newspaper', 'url': 'content/article/'},
{'name': '카테고리', 'icon': 'fa fa-tags', 'url': 'content/category/'}
]
},
{
'app': 'auth',
'name': '시스템 설정',
'icon': 'fas fa-cogs',
'models': [
{'name': '사용자 관리', 'icon': 'fa fa-users', 'url': 'auth/user/'},
{'name': '그룹 관리', 'icon': 'fa fa-user-shield', 'url': 'auth/group/'}
]
},
{
'name': '다중 메뉴 테스트',
'icon': 'fa fa-sitemap',
'models': [
{
'name': '외부 링크',
'icon': 'far fa-folder-open',
'models': [
{'name': '공식 문서', 'url': 'https://docs.djangoproject.com/'},
{'name': 'GitHub 저장소', 'icon': 'fab fa-github', 'url': 'https://github.com/'}
]
}
]
}
]
}
# 로그인 페이지 파티클 효과 및 홈 화면 정보 표시 여부 설정
SIMPLEUI_LOGIN_PARTICLES = False
SIMPLEUI_HOME_INFO = False
또한, admin.py 파일에서 모델 어드민 클래스를 확장하여 목록 화면에 커스텀 액션 버튼을 추가하고, 클릭 시 확인 다이얼로그나 특정 스타일을 적용할 수 있습니다.
from django.contrib import admin
from .models import Article, Category
@admin.register(Article)
class ArticleAdmin(admin.ModelAdmin):
list_display = ('id', 'title', 'author', 'created_at')
# 커스텀 일괄 처리 액션 정의
actions = ['export_as_csv', 'toggle_publish_status']
def export_as_csv(self, request, queryset):
# CSV 내보내기 로직 구현
pass
export_as_csv.short_description = '선택된 게시글 CSV 내보내기'
export_as_csv.icon = 'fas fa-file-csv'
export_as_csv.type = 'success'
def toggle_publish_status(self, request, queryset):
# 게시 상태 토글 로직 구현
pass
toggle_publish_status.short_description = '게시 상태 일괄 변경'
toggle_publish_status.icon = 'fas fa-sync-alt'
toggle_publish_status.type = 'warning'
toggle_publish_status.confirm = '선택한 게시글의 상태를 변경하시겠습니까?'
admin.site.register(Category)