uow-pattern
TL;DR⚑
- Unit of Work 패턴은 데이터베이스 작업을 하나의 작업 단위로 묶어 일관성을 보장하고, 커밋과 롤백을 통해 데이터의 무결성을 유지하는 패턴임.
- 이 패턴은 주로 트랜잭션 관리를 위해 사용되며, 여러 작업을 한 번에 처리하고, 문제가 발생했을 때 모두 롤백할 수 있는 장점이 있음.
- Repository 패턴과 함께 사용되어 데이터 작업의 분리를 더욱 명확히 할 수 있음.
Unit of Work 패턴 개요⚑
- 단일 작업 단위: 여러 데이터베이스 작업을 하나의 작업 단위로 묶어 관리함. 이를 통해 트랜잭션 내에서 일관된 상태를 유지할 수 있음.
- 트랜잭션 관리: Unit of Work는 트랜잭션의 시작과 끝을 관리함. 작업이 성공하면 커밋하고, 오류가 발생하면 롤백하여 데이터의 무결성을 보장함.
- 변경 추적: 객체의 상태 변화를 추적하여, 필요한 경우 데이터베이스에 반영함. 이를 통해 불필요한 작업을 줄이고 효율성을 높일 수 있음.
Unit of Work 패턴의 구현⚑
SQLAlchemy를 사용한 구현 예시⚑
SQLAlchemy에서 Unit of Work 패턴을 구현할 때, with
문을 이용해 트랜잭션의 범위를 지정하고, Repository 객체가 Unit of Work 객체를 받아서 데이터베이스 작업을 처리함.
from sqlalchemy import create_engine, Column, Integer, String
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
Base = declarative_base()
class User(Base):
__tablename__ = 'users'
id = Column(Integer, primary_key=True)
name = Column(String)
# Unit of Work 클래스 정의
class UnitOfWork:
def __init__(self, session_factory):
self.session_factory = session_factory
self.session = None
def __enter__(self):
self.session = self.session_factory()
return self
def __exit__(self, exc_type, exc_val, exc_tb):
if exc_type:
self.session.rollback()
else:
self.session.commit()
self.session.close()
# Repository 클래스 정의
class UserRepository:
def __init__(self, uow: UnitOfWork):
self.uow = uow
def add(self, user):
self.uow.session.add(user)
def get(self, user_id):
return self.uow.session.query(User).filter_by(id=user_id).first()
# 데이터베이스 연결 및 세션 생성
engine = create_engine('sqlite:///example.db')
Session = sessionmaker(bind=engine)
# Unit of Work 사용 예시
with UnitOfWork(Session) as uow:
user_repo = UserRepository(uow)
user1 = User(name='Alice')
user2 = User(name='Bob')
user_repo.add(user1)
user_repo.add(user2)
# Unit of Work는 with 블록의 끝에서 자동으로 커밋됨
- with 문을 통한 트랜잭션 관리:
UnitOfWork
클래스가__enter__
와__exit__
메서드를 통해 트랜잭션을 관리함. with 블록을 벗어나면 트랜잭션이 커밋되거나 롤백됨. - Repository와의 통합:
UserRepository
클래스가 Unit of Work 객체를 받아서 데이터베이스 작업을 수행함. 이를 통해 데이터 작업의 책임을 분리하고 코드의 가독성을 높임.
Django를 사용한 구현 예시⚑
Django에서는 transaction.atomic()
을 사용하여 Unit of Work 패턴을 구현할 수 있으며, Repository 객체가 Unit of Work 객체를 받아서 with 문을 통해 작업을 처리할 수 있음.
from django.db import transaction
from myapp.models import User
# Unit of Work 클래스 정의
class UnitOfWork:
def __enter__(self):
self.transaction = transaction.atomic()
self.transaction.__enter__()
def __exit__(self, exc_type, exc_val, exc_tb):
if exc_type:
self.transaction.__exit__(exc_type, exc_val, exc_tb)
else:
self.transaction.__exit__(None, None, None)
# Repository 클래스 정의
class UserRepository:
def __init__(self, uow):
self.uow = uow
def add(self, user):
user.save()
def get(self, user_id):
return User.objects.get(id=user_id)
# Unit of Work 사용 예시
with UnitOfWork() as uow:
user_repo = UserRepository(uow)
user1 = User(name='Alice')
user2 = User(name='Bob')
user_repo.add(user1)
user_repo.add(user2)
# Unit of Work는 with 블록의 끝에서 자동으로 커밋됨
- transaction.atomic()을 통한 트랜잭션 관리: Django의
transaction.atomic()
을 사용하여 Unit of Work를 구현함.with
문 내에서 작업이 성공하면 자동으로 커밋되고, 예외가 발생하면 롤백됨. - Repository와의 통합:
UserRepository
클래스가 Unit of Work 객체를 받아 Django ORM을 통해 데이터베이스 작업을 처리함.
Python에서의 Unit of Work 패턴 사용⚑
- 상황에 따른 적용: 웹 애플리케이션, 특히 데이터베이스 작업이 복잡하고 다수의 엔터티가 연관된 경우 Unit of Work 패턴을 활용하는 것이 유용함.
- 라이브러리 사용: SQLAlchemy나 Django ORM에서 Unit of Work 패턴을 기본적으로 지원함. 이를 통해 트랜잭션 관리를 자동화할 수 있음.
- 직접 구현: 특수한 요구사항이 있는 경우 Unit of Work 패턴을 직접 구현하여 트랜잭션 관리, 변경 추적, 커밋 및 롤백 기능을 커스터마이징할 수 있음.
이와 같은 방법으로 Python에서 Unit of Work 패턴을 사용하면 데이터베이스 작업의 일관성과 무결성을 유지할 수 있음. SQLAlchemy와 Django는 이 패턴을 내장하고 있어, 개발자가 쉽게 트랜잭션 관리를 수행할 수 있도록 지원함.