반응형
서론
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" 흐름을 기억하세요.
반응형
'Dev > Python' 카테고리의 다른 글
[Python] print만 쓰다가 logging으로 바꾼 이유 (파이썬 로깅 입문기) (2) | 2025.04.14 |
---|---|
[Python] db.flush()는 언제, 왜 사용하는가? (0) | 2025.04.12 |
튜플 (Tuple) - 여러 값을 묶는 간단하고 효율적인 방법 (2) | 2025.04.10 |
[FastAPI x PostgreSQL] 프로세스 정리 (with SQLAlchemy, Pydantic) (0) | 2025.04.08 |
[FastAPI] dict vs Pydantic 스키마 사용 비교 (0) | 2025.04.07 |