🚦 Django에서의 Race Condition 해결 및 삭제 기능 구현 튜토리얼
Django 웹 개발 시 자주 마주하는 엑세스 카운트 처리 문제와 삭제 요청 처리 방식, 그리고 좋은 코드 구조 설계 원칙까지 단계별로 정리합니다.
1. ⚙️ 장고에서의 엑세스 카운트 처리와 레이스 컨디션
1.1 🧨 문제 상황 설명
- 엑세스 카운트를 Python 코드에서 +1 하는 방식은 아래와 같은 흐름을 가집니다:
- short_url.access_count += 1 short_url.save()
- 문제점:
- DB에서 값을 먼저 읽어옴 → 파이썬에서 +1 연산 → 다시 저장
- 동시에 요청이 들어올 경우, 둘 다 이전 값을 기반으로 연산하여 카운트가 덜 증가하는 문제가 발생함
1.2 🎯 Race Condition(경쟁 상태)
- 동일한 자원에 대해 동시 접근 시 결과가 엉킬 수 있는 상황을 의미
- Django 개발 서버는 요청을 순차 처리하지만, 실제 배포 환경(예: gunicorn 등 WSGI 서버)은 동시 요청을 처리함 → 실제 문제 발생 가능
1.3 ✅ 해결 방법: F 표현식 사용
from django.db.models import F
short_url.access_count = F("access_count") + 1
short_url.save()
- Django의 F() 표현식은 DB에서 직접 연산을 수행함
- Python에서 값을 가져오는 것과 달리, 현재 DB 상태를 기준으로 덧셈 연산이 실행됨
- 결과적으로 Race Condition 방지 가능
📌 실제 적용 예시는 아래와 같음:
from django.db.models import F
from django.shortcuts import get_object_or_404
short_url = get_object_or_404(ShortURL, code=code)
short_url.access_count = F('access_count') + 1
short_url.save()
2. 🗑️ 삭제 기능 구현: HTTP 메서드 분리 처리
2.1 🔄 GET vs DELETE
- REST 원칙에 따라, 특정 자원 삭제 시 DELETE 요청을 사용하는 것이 적절함
- 하지만 HTML form은 GET과 POST 메서드만 지원
2.2 ✨ 해결 방법: POST 방식으로 삭제 요청 처리
- 뷰 코드에서 POST 요청을 감지하여 삭제 수행
# views.py
from django.shortcuts import redirect
if request.method == 'POST':
short_url.delete()
return redirect('home')
- HTML 폼:
<form method="POST" action="{% url 'shortener:detail' short_url.code %}">
{% csrf_token %}
<button type="submit" class="btn btn-danger btn-sm">삭제</button>
</form>
- URL 패턴은 그대로 유지:
path('<str:code>/', views.short_url_detail, name='detail')
3. 🗂️ 삭제 버튼 추가: HTML 구현
3.1 🔧 테이블 내 삭제 버튼 구성
<thead>
<tr>
<th>URL</th>
<th>생성일</th>
<th>액션</th>
</tr>
</thead>
<tbody>
{% for short_url in url_list %}
<tr>
<td>{{ short_url.original_url }}</td>
<td>{{ short_url.created_at }}</td>
<td>
<form method="POST" action="{% url 'shortener:detail' short_url.code %}">
{% csrf_token %}
<button class="btn btn-danger btn-sm" type="submit">삭제</button>
</form>
</td>
</tr>
{% endfor %}
</tbody>
📌 주의 사항
- action에는 URL을 정확히 지정해야 함 → short_url.code 등 포함
- CSRF 토큰 반드시 포함 필요
4. ✏️ HTTP 메서드와 CSRF 토큰 처리
4.1 POST 방식에서의 보안 처리
- HTML 폼은 GET, POST만 지원
- POST 방식 사용 시 csrf_token을 반드시 포함해야 보안상 안전함
<form method="POST" action="...">
{% csrf_token %}
<button type="submit">삭제</button>
</form>
- CSRF 미포함 시, Django는 요청을 거부함 (403 Forbidden)
5. 🧑💻 좋은 코드 설계: 뷰 분리와 함수 단일 책임 원칙
문제 상황:
if request.method == 'GET':
return redirect(short_url.original_url)
elif request.method == 'POST':
short_url.delete()
return redirect('home')
- GET 요청은 리디렉션, POST 요청은 삭제 처리
- 두 가지 기능이 하나의 뷰에서 처리되며, 함수의 책임이 모호함
🛠 개선 방법: 기능별 함수 분리
# redirect view
path('<str:code>/go/', views.redirect_view, name='redirect')
# delete view
path('<str:code>/delete/', views.delete_view, name='delete')
- 각각의 기능을 별도의 함수/클래스 기반 뷰로 분리하여 가독성과 유지보수성 향상
✅ 정리
항목 설명
Race Condition | 동시에 요청 시 DB 데이터 충돌 가능성 |
해결책 | F 표현식 활용해 DB에서 직접 연산 수행 |
삭제 요청 | HTML 폼은 POST만 지원하므로 POST로 구현 |
CSRF | POST 요청에는 csrf_token 필수 |
좋은 코드 | 한 뷰에서 여러 기능을 처리하지 않고 기능별로 분리 구성 |
이 구조를 통해 Django에서의 동시성 이슈와 RESTful 방식 삭제 처리, 그리고 코드 유지보수성을 한층 높일 수
'특강' 카테고리의 다른 글
[Django 4일차] Django를 백엔드 프레임워크로 활용하기 - DRF 입문 가이드 1/4 (short_url 프로젝트)(250515) (0) | 2025.05.15 |
---|---|
[Django 3일차] Django 함수형 뷰 vs 클래스형 뷰 + ORM 4/4 (short_url 프로젝트)(250514) (0) | 2025.05.14 |
[Django 2일차] Django로 만드는 숏 URL 생성기 4/4 (250513) (0) | 2025.05.13 |
[Django 2일차] Django로 숏 URL 생성 서비스 만들기 3/4 (250513) (0) | 2025.05.13 |
[Django 2일차] Django 마이그레이션과 ORM 실전 이해 가이드2/4 (250513) (0) | 2025.05.13 |