본문 바로가기

[Python] db.flush() Service/CRUD 계층 분리 구조에서의 실전 예제

@Jeeqong 2025. 4. 11. 13:15
반응형

서론

FastAPI와 SQLAlchemy를 함께 사용할 때, 코드를 다음처럼 계층 분리하는 경우가 많습니다.

  • router: 요청/응답 처리
  • service: 비즈니스 로직 처리
  • crud: DB 직접 접근

이 구조는 유지보수성과 테스트 편의성을 높여주지만, 실제 DB와의 상호작용이 나뉘어 있기 때문에 데이터 저장 흐름을 정확히 이해하는 것이 중요합니다.

그 중 하나가 바로 db.flush()입니다.

db.flush()는 데이터를 실제 DB에 저장하되 트랜잭션은 유지하는 명령어입니다.

db.commit()과는 달리, 다른 연산(예: insert → select)을 하기 위한 "중간 저장" 용도로 사용됩니다.


본론

상황

예를 들어 브랜드를 생성하고, 생성된 브랜드의 ID로 브랜드-카테고리 연관 데이터를 추가하고 싶다고 해보죠.

1. 브랜드 추가 (brand 테이블)
2. 생성된 brand.id 를 기반으로 brand_category 테이블에 연관 등록

이때 brand.id를 얻기 위해 db.commit()을 먼저 실행하면,

브랜드만 저장되고 카테고리 등록 중 실패하면 트랜잭션이 쪼개져버리는 문제가 발생합니다.

→ 이럴 때 사용하는 게 바로 db.flush()입니다.


🔄 계층별 구조 설명

📁 api/brand.py (라우터)

@router.post("/brands", response_model=BrandResponse)
def create_brand(data: BrandCreate, db: Session = Depends(get_db)):
    return brand_service.create_brand_with_category(db, data)
  • 라우터는 단순히 서비스 계층으로 요청을 전달합니다.
  • 비즈니스 흐름은 service에서, DB 조작은 crud에서 수행합니다.

📁 services/brand_service.py (서비스 계층)

def create_brand_with_category(db: Session, data: BrandCreate):
    # 1. 브랜드 먼저 생성 (이때 flush로 ID 확보)
    brand = brand_crud.create(db, data)
    db.flush()  # 트랜잭션 유지 + brand.id 생성

    # 2. 연관 브랜드-카테고리 저장 (브릿지 테이블)
    bridge = BrandCategory(
        brand_id=brand.id,
        category_id=data.category_id,
    )
    db.add(bridge)

    db.commit()
    return brand
  • db.flush()를 통해 brand.id 값은 생성되지만 아직 commit은 하지 않음
  • 이후 브릿지 테이블에 brand.id를 활용해 등록
  • 최종적으로 db.commit()으로 한번에 확정

💡 flush는 중간 작업, commit은 확정 저장

같은 트랜잭션 안에서 여러 DB 작업을 연결할 때 꼭 필요한 과정


📁 crud/brand_crud.py (CRUD 계층)

def create(db: Session, data: BrandCreate) -> Brand:
    brand = Brand(**data.dict())
    db.add(brand)
    return brand  # flush 또는 commit은 service에서
  • DB에 brand를 추가하지만 실제 commit은 하지 않습니다.
  • commit은 service 계층에서 모든 작업이 끝난 후에 처리합니다.

 요약 정리

항목 설명

db.flush() INSERT 쿼리를 실행해 ID 값을 생성함 (하지만 트랜잭션은 종료되지 않음)
db.commit() 트랜잭션을 종료하고 모든 변경사항을 확정
언제 사용? INSERT 직후 해당 객체의 ID가 필요할 때 (ex: 관계 테이블 등록)
위치 Service 계층: 비즈니스 흐름을 조율하고 flush/commit 판단

✍️ 정리 문장 (Obsidian 메모 예시)

## db.flush() 사용 위치와 이유

- brand 생성 직후 brand.id를 사용해야 할 때 `db.flush()`를 통해 ID 확보
- flush는 중간 저장, commit은 확정
- 계층 구조에 따라 flush/commit 위치를 신중히 분리해야 한다
- service 계층에서 flush 후 추가 연산, commit은 마지막에 1회만 수행

결론

📌 마무리

FastAPI에서 계층 분리를 할수록 flush()의 위치는 더욱 중요해집니다.

모든 작업을 한 트랜잭션에서 처리하고 싶다면,

"commit 대신 flush → 필요한 연산 → 마지막에 commit" 흐름을 기억하세요.

반응형
Jeeqong
@Jeeqong :: JQVAULT

Jeeqong's vault : 정보/기록을 쌓아두는 공간 웹개발 포스팅 일상 리뷰를 기록하는 공간입니다.

공감하셨다면 ❤️ 구독도 환영합니다! 🤗

목차