🔄 Django에서 Transaction 관리 실수 사례와 해결법
Django에서는 트랜잭션(transaction)이 ORM에 기본적으로 녹아있지만, 실무에서는 예상치 못한 버그나 데이터 꼬임 현상이 자주 발생합니다. 오늘은 백엔드 개발자가 흔히 겪는 트랜잭션 실수 사례들과 그 해결 방법을 정리해봅니다.
❗ 실수 사례 1: 예외 발생 시 데이터 일부만 저장됨
아래와 같은 코드에서 중간에 예외가 발생하면 어떻게 될까요?
def create_user_and_profile():
user = User.objects.create(username="john")
Profile.objects.create(user=user)
raise ValueError("중간 에러!") # 이 에러 발생 시 User는 저장되지만 Profile은 안 됨
문제: Django는 기본적으로 각 요청 단위로 트랜잭션을 묶지만, 뷰 함수 밖에서 트랜잭션 처리를 하지 않으면 일부만 커밋될 수 있습니다.
✅ 해결법: atomic 사용
from django.db import transaction
def create_user_and_profile():
with transaction.atomic():
user = User.objects.create(username="john")
Profile.objects.create(user=user)
raise ValueError("중간 에러!") # 전체 롤백됨
❗ 실수 사례 2: nested atomic에서 내부 오류 무시
아래 코드는 내부 atomic에서 에러가 발생했는데, 외부에 영향을 주지 않을 것처럼 보입니다.
from django.db import transaction
def outer():
with transaction.atomic():
do_something()
try:
with transaction.atomic():
raise Exception("내부 에러")
except:
pass
문제: 내부 트랜잭션은 savepoint를 만들고 관리하지만, 예외가 발생해도 rollback
처리를 명시하지 않으면 커밋할 때 TransactionManagementError
가 날 수 있습니다.
✅ 해결법: set_rollback(True) 명시
from django.db import transaction
def outer():
with transaction.atomic():
do_something()
try:
with transaction.atomic():
raise Exception("내부 에러")
except:
transaction.set_rollback(True)
❗ 실수 사례 3: 뷰 함수 밖에서 ORM 호출 후 예외 발생
의외로 많은 실수가 비동기 작업(Celery 등)이나 서비스 함수에서 ORM을 사용한 후 예외가 나는 경우입니다.
문제: 트랜잭션이 묶이지 않기 때문에, 예외가 발생해도 커밋이 완료되어 데이터 무결성이 깨질 수 있습니다.
✅ 해결법: Celery 내부나 서비스 레이어에도 atomic 적용
@shared_task
def register_user(data):
with transaction.atomic():
user = User.objects.create(username=data["username"])
send_email_to_user(user) # 예외 발생 가능
💡 정리
- atomic은 트랜잭션의 최소 단위로, 예외가 발생하면 전체 롤백됩니다.
- 예외 처리 시 반드시 set_rollback(True)을 고려하세요.
- Celery나 서비스 함수에서도 atomic으로 직접 감싸는 습관이 중요합니다.
Django에서 트랜잭션을 다룰 땐, atomic
을 적극적으로 사용하는 것이 데이터 일관성과 버그 예방의 핵심입니다.
다음 글에서는 Django에서 트랜잭션과 select_for_update()를 함께 쓰는 실전 예제도 다뤄보겠습니다!
댓글
댓글 쓰기