Django ORM 최적화, 폼 검증 및 인증 시스템 심화 가이드

ORM 심화 및 쿼리 최적화

Q 객체를 활용한 동적 쿼리 구성

Q 객체는 복잡한 조건을 동적으로 조합할 때 유용합니다. 커넥터를 변경하거나 리스트에 조건을 추가하여 유연한 필터링이 가능합니다.

from django.db.models import Q

# 동적 조건 객체 생성
condition = Q()
condition.connector = 'OR'
condition.children.append(('is_active', True))

# 다른 Q 객체와 결합하여 필터링 수행
active_or_cheap_items = Item.objects.filter(condition, Q(stock__gt=0))

다대다(N:N) 관계의 두 가지 설계 방식

방식 1: ManyToManyField 직접 사용
Django가 자동으로 중간 테이블을 생성하며, add, remove, clear 등의 ORM 메서드를 즉시 사용할 수 있습니다. 단, 중간 테이블에 추가 필드를 정의할 수 없어 확장성이 떨어집니다.

방식 2: 중간 테이블 직접 정의 후 연결
중간 테이블에 생성일시나 추가 상태값이 필요할 때 사용하며, throughthrough_fields 인자를 명시해야 합니다.

class Course(models.Model):
    name = models.CharField(max_length=50)
    students = models.ManyToManyField(
        to='Student',
        through='Enrollment',
        through_fields=('course', 'student')
    )

class Student(models.Model):
    full_name = models.CharField(max_length=50)

class Enrollment(models.Model):
    course = models.ForeignKey(Course, on_delete=models.CASCADE)
    student = models.ForeignKey(Student, on_delete=models.CASCADE)
    enrolled_at = models.DateTimeField(auto_now_add=True)
    grade = models.CharField(max_length=2, null=True)

쿼리셋 성능 최적화 기법

Django ORM은 지연 평가(Lazy Evaluation) 특성을 가지므로, 실제 데이터가 필요한 시점(반복, 슬라이싱, 평가)에 SQL이 실행됩니다.

  • 필드 선택적 로딩 (only / defer): values()는 딕셔너리를 반환하여 가볍지만 모델 메서드를 사용할 수 없습니다. only()defer()는 모델 인스턴스를 반환하되 특정 필드의 로딩을 지연시킵니다. 단, defer()로 제외된 필드에 접근하면 추가 쿼리가 발생(N+1 문제)하므로 주의해야 합니다.
  • 관계형 데이터 최적화 (select_related / prefetch_related): 외래키 접근 시 발생하는 추가 쿼리를 방지합니다. select_related는 SQL JOIN을 사용하고, prefetch_related는 별도의 쿼리를 실행 후 Python 메모리에서 조인합니다.
  • 대량 데이터 삽입 (bulk_create): 반복문을 통해 save()를 호출하는 대신, 인스턴스 리스트를 bulk_create()에 전달하여 단일 SQL로 삽입 성능을 극대화합니다.

데이터 무결성을 위한 트랜잭션 처리

  • 전역 트랜잭션: ATOMIC_REQUESTS를 True로 설정하면 뷰의 모든 DB 작업이 하나의 트랜잭션으로 묶입니다.
  • 함수 단위 트랜잭션: @transaction.atomic 데코레이터를 사용하여 특정 함수 내부의 작업을 원자적으로 만듭니다.
  • 블록 단위 트랜잭션: with transaction.atomic(): 컨텍스트 매니저를 사용하여 코드 블록 단위로 세밀하게 트랜잭션을 제어합니다.

주요 필드 및 외래키 삭제 옵션

  • AutoField: 자동 증가 정수형 (주로 기본키).
  • CharField: 문자열 (max_length 필수).
  • DecimalField: 고정 소수점 (max_digits, decimal_places 필수).
  • DateTimeField: auto_now(수정일), auto_now_add(생성일) 옵션 제공.
  • FileField: 파일 업로드 (upload_to 경로 지정, DB에는 경로 문자열만 저장).

외래키 on_delete 옵션:

  • CASCADE: 참조된 객체 삭제 시 함께 삭제.
  • PROTECT: 참조된 객체가 존재하면 삭제 방지 (ProtectedError 발생).
  • SET_NULL: 참조된 객체 삭제 시 NULL로 변경 (null=True 필요).
  • SET_DEFAULT: 참조된 객체 삭제 시 기본값으로 설정.
  • DO_NOTHING: 아무 작업도 수행하지 않음 (DB 레벨 제약 조건에 위임).

비동기 통신(AJAX)과 데이터 직렬화

Fetch API를 활용한 비동기 요청

현대적인 프론트엔드 환경에서는 jQuery 대신 Fetch API를 주로 사용합니다. JSON 데이터를 전송할 때는 CSRF 토큰을 헤더에 포함해야 합니다.

const payload = { title: 'New Post', content: 'Hello World' };
const csrfToken = document.querySelector('[name=csrfmiddlewaretoken]').value;

fetch('/api/posts/', {
    method: 'POST',
    headers: {
        'Content-Type': 'application/json',
        'X-CSRFToken': csrfToken
    },
    body: JSON.stringify(payload)
})
.then(response => response.json())
.then(data => console.log('Success:', data))
.catch(error => console.error('Error:', error));

Django REST Framework(DRF) 직렬화

쿼리셋을 JSON 형태로 변환할 때 DRF의 ModelSerializer를 사용하면 필드 제어와 중첩 관계 처리가 용이합니다.

from rest_framework import serializers
from rest_framework.response import Response
from .models import Article

class ArticleSerializer(serializers.ModelSerializer):
    class Meta:
        model = Article
        fields = ['id', 'title', 'content', 'created_at']

# 뷰에서의 활용
def article_list_api(request):
    queryset = Article.objects.all()
    serializer = ArticleSerializer(queryset, many=True)
    return Response(serializer.data)

폼(Form) 컴포넌트를 활용한 데이터 검증

폼 검증 로직 및 세 가지 방식

Django Form은 HTML 태그 생성, 데이터 바인딩, 유효성 검사를 통합 관리합니다. is_valid() 메서드 호출 시 다음 세 가지 단계를 거칩니다.

  1. 필드 기본 제약 조건: max_length, required 등 필드 자체의 속성 검증.
  2. 정규 표현식 검증: validators 인자에 RegexValidator를 전달하여 커스텀 패턴 검증.
  3. 후크(Hook) 함수:
    • 로컬 후크 (clean_필드명): 특정 필드에 대한 교차 검증. 반드시 검증된 값을 반환해야 하며, 실패 시 ValidationError를 발생시킵니다.
    • 전역 후크 (clean): 여러 필드 간의 의존성 검증. self.add_error()를 사용하여 오류를 등록하고 self.cleaned_data를 반환합니다.

ModelForm을 통한 DB 연동

ModelForm은 모델의 필드를 자동으로 폼 필드로 매핑하며, save() 메서드를 통해 DB 삽입 및 수정을 간소화합니다.

from django import forms
from .models import Article

class ArticleForm(forms.ModelForm):
    class Meta:
        model = Article
        fields = '__all__'
        labels = {
            'title': '게시글 제목',
            'content': '본문 내용',
        }

# 신규 생성
form = ArticleForm(request.POST)
if form.is_valid():
    form.save()

# 기존 데이터 수정 (instance 전달)
target_article = Article.objects.get(pk=1)
update_form = ArticleForm(request.POST, instance=target_article)
if update_form.is_valid():
    update_form.save()

요청/응답 생명주기 제어를 위한 미들웨어

커스텀 미들웨어 작성

미들웨어는 전역적인 요청/응답 처리를 담당합니다. MiddlewareMixin을 상속받아 process_requestprocess_response를 오버라이딩합니다.

  • process_request: 뷰 함수 실행 전 호출되며, HttpResponse를 반환하면 즉시 응답 단계로 단축 이동합니다.
  • process_response: 뷰 함수 실행 후 호출되며, 반드시 HttpResponse 객체를 반환해야 합니다.

동적 모듈 로딩 원리: settings.py에 문자열로 등록된 미들웨어 경로(예: 'myapp.middleware.CustomAuth')는 importlib.import_module로 모듈을 동적 임포트하고, getattr을 통해 클래스를 추출하여 인스턴스화합니다.

상태 유지를 위한 쿠키와 세션

  • 쿠키 설정 및 조회: 응답 객체의 set_cookie(key, value, max_age)를 사용하며, 요청 시 request.COOKIES.get(key)로 읽습니다.
  • 세션 설정 및 조회: 서버 측 저장소를 사용하므로 더 안전합니다. request.session['user_id'] = 123으로 저장하고, request.session.get('user_id')로 조회합니다.

CSRF 토큰 검증 및 보안 처리

Cross-Site Request Forgery를 방지하기 위해 Django는 POST 요청에 대해 CSRF 토큰을 검증합니다.

  • 템플릿: 폼 내부에 {% csrf_token %} 태그를 삽입합니다.
  • AJAX: 요청 헤더에 X-CSRFToken을 추가하거나, 공식 csrf.js 스크립트를 로드하여 자동 처리합니다.
  • CBV 데코레이터 적용: 클래스 기반 뷰에서는 method_decorator를 사용하여 dispatch 메서드나 특정 HTTP 메서드(get, post)에 csrf_exempt 또는 csrf_protect를 적용합니다.

Auth 모듈과 커스텀 유저 모델 확장

기본 Auth 기능 요약

메서드 / 속성 설명
User.objects.create_user() 일반 사용자 생성 (비밀번호 해싱 적용)
authenticate(request, ...) 자격 증명 검증 및 유저 객체 반환
login(request, user) 세션에 로그인 상태 저장
@login_required 비로그인 사용자의 접근 차단 데코레이터
request.user.is_authenticated 현재 요청자의 인증 여부 확인

AbstractUser를 상속받은 커스텀 유저 모델

기본 auth_user 테이블의 필드는 고정되어 있으므로, 비즈니스 로직에 맞춰 확장해야 합니다. AbstractUser를 상속하여 새 모델을 정의하고 settings.py에 등록합니다.

from django.contrib.auth.models import AbstractUser
from django.db import models

class CustomUser(AbstractUser):
    # AbstractUser에 없는 추가 필드 정의
    phone_number = models.CharField(max_length=15, blank=True)
    bio = models.TextField(blank=True)
    birth_date = models.DateField(null=True, blank=True)

settings.py에 다음 설정을 추가하여 Django가 기본 User 모델 대신 커스텀 모델을 사용하도록 지정합니다.

AUTH_USER_MODEL = 'accounts.CustomUser'

설정 완료 후 makemigrationsmigrate 명령어를 실행하면 새로운 스키마의 인증 테이블이 데이터베이스에 생성됩니다.

태그: Django ORM ModelForm CSRF DjangoAuth

8월 2일 03:46에 게시됨