Skip to content

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는 이 패턴을 내장하고 있어, 개발자가 쉽게 트랜잭션 관리를 수행할 수 있도록 지원함.

Reference⚑