SQLAlchemy

[SQLALchemy] 세션(Session), 비동기 세션(Async Session) 그리고 scoped_session의 정리

bluebamus 2025. 2. 1.

1. SQLAlchemy의 세션(Session) 개념

   - SQLAlchemy에서 세션은 데이터베이스와의 트랜잭션을 관리하는 객체이다.
   - 세션은 데이터베이스 연결(Connection)을 유지하고, 쿼리를 실행하며, 변경된 데이터를 커밋(commit)하거나 롤백(rollback) 하는 역할을 한다.

 

   2) 주요 역할

      - 데이터 변경 추적 : add(), delete() 등을 통해 변경된 데이터를 추적
      - 트랜잭션 관리 : commit() 또는 rollback() 을 사용해 데이터 변경 사항을 저장 또는 취소
      - DB 연결 풀 관리 : 세션이 닫히면 내부적으로 커넥션 풀을 관리하여 성능 최적화

 

2. FastAPI에서 SQLAlchemy 세션 설정 방법

   - FastAPI에서 SQLAlchemy를 사용할 때 세션을 안전하게 관리하는 방식은 다음과 같다.

 

   1) 데이터베이스 설정 및 세션 팩토리 생성

      - FastAPI에서 SQLAlchemy의 세션을 생성하려면 SessionLocal 을 사용하여 세션 팩토리를 설정한다.

 

      1. database.py (데이터베이스 설정)

from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker, declarative_base

# 데이터베이스 URL 설정
DATABASE_URL = "postgresql+psycopg2://user:password@localhost:5432/mydatabase"

# SQLAlchemy 엔진 생성 (연결 풀 포함)
engine = create_engine(DATABASE_URL, pool_size=10, max_overflow=20)

# 세션 팩토리 생성
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)

# Base 클래스 생성 (모든 모델이 상속받음)
Base = declarative_base()

 

   2) 의존성 주입을 통한 세션 관리

      - FastAPI에서는 의존성 주입(Dependency Injection) 을 통해 요청(Request)별 세션을 관리한다.

 

         1. dependencies.py (세션 종속성 설정)

            - yield db 를 사용하면 FastAPI가 요청이 끝난 후 자동으로 세션을 닫음
            - db.close() 는 세션을 닫고 커넥션 풀에 반환하여 성능 최적화

from fastapi import Depends
from sqlalchemy.orm import Session
from .database import SessionLocal

# 요청마다 새로운 세션을 생성하고, 요청 종료 시 닫음
def get_db():
    db = SessionLocal()
    try:
        yield db  # 호출한 곳에서 사용 가능
    finally:
        db.close()  # 요청 종료 후 세션 닫기

 

   3) 엔드포인트에서 세션 사용

      - 이제 FastAPI 엔드포인트에서 get_db 의존성을 사용하여 SQLAlchemy 세션을 주입할 수 있다.

 

      1. main.py (FastAPI 라우터에서 DB 사용)

         - db : Session = Depends(get_db) : get_db() 함수를 통해 요청마다 새로운 세션을 주입받음
         - 요청이 끝나면 세션이 자동으로 닫힘 (finally: db.close())

from fastapi import FastAPI, Depends
from sqlalchemy.orm import Session
from .dependencies import get_db
from .models import User

app = FastAPI()

@app.get("/users/{user_id}")
def read_user(user_id: int, db: Session = Depends(get_db)):
    user = db.query(User).filter(User.id == user_id).first()
    if user is None:
        return {"message": "User not found"}
    return user

 

3. FastAPI에서 세션 사용 시 주의할 점

   1) autocommit=False, autoflush=False 의 의미

      - 위에서 SessionLocal 을 설정할 때 autocommit=False, autoflush=False 옵션을 사용했다.

         - autocommit=False → 명시적으로 commit() 해야 데이터가 저장된다. (트랜잭션 관리 용이)
         - autoflush=False → flush() 가 자동으로 실행되지 않는다. (불필요한 쿼리 방지)

      - 즉, commit() 을 직접 호출하여 트랜잭션을 명확하게 관리하는 것이 안전하다.

SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)

 

   2) 비동기 지원 (async 세션 사용)

      - FastAPI는 비동기(Async) 프레임워크이므로, async 방식의 SQLAlchemy 세션을 사용할 수도 있다.

 

      1. database.py (비동기 세션 설정)

from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession
from sqlalchemy.orm import sessionmaker, declarative_base

DATABASE_URL = "postgresql+asyncpg://user:password@localhost:5432/mydatabase"

async_engine = create_async_engine(DATABASE_URL, pool_size=10, max_overflow=20)

AsyncSessionLocal = sessionmaker(bind=async_engine, class_=AsyncSession, expire_on_commit=False)

Base = declarative_base()

 

      2. dependencies.py (비동기 세션 의존성 주입)

         - dependencies.py (비동기 세션 의존성 주입)async with 을 사용하면 세션이 자동으로 정리됨
         - 비동기 함수에서 await db.execute() 형식으로 쿼리를 실행해야 함

from fastapi import Depends
from sqlalchemy.ext.asyncio import AsyncSession
from .database import AsyncSessionLocal

async def get_db():
    async with AsyncSessionLocal() as db:
        yield db

 

      3. main.py (비동기 라우터)

         - await db.execute(select(User).filter(User.id == user_id))
         - 비동기 AsyncSession 은 await 키워드와 함께 사용해야 함

from fastapi import FastAPI, Depends
from sqlalchemy.ext.asyncio import AsyncSession
from .dependencies import get_db
from .models import User
from sqlalchemy.future import select

app = FastAPI()

@app.get("/users/{user_id}")
async def read_user(user_id: int, db: AsyncSession = Depends(get_db)):
    result = await db.execute(select(User).filter(User.id == user_id))
    user = result.scalar_one_or_none()
    if user is None:
        return {"message": "User not found"}
    return user

 

4. SQLAlchemy Session과 AsyncSession 개요

   - SQLAlchemy에서 데이터베이스와의 상호작용을 위해 Session과 AsyncSession을 사용한다.
      - Session: 동기 방식으로 데이터베이스 작업을 처리한다.
      - AsyncSession: 비동기 방식으로 데이터베이스 작업을 처리하여 FastAPI와 같은 비동기 웹 프레임워크에서 효율적으로 사용할 수 있다.

 

5. 데이터베이스 설정 및 엔진 생성

(1) 동기 방식 엔진 및 세션 설정

from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker

DATABASE_URL = "postgresql://user:password@localhost/dbname"

engine = create_engine(DATABASE_URL, echo=True)
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)

 

(2) 비동기 방식 엔진 및 세션 설정

from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession
from sqlalchemy.orm import sessionmaker

ASYNC_DATABASE_URL = "postgresql+asyncpg://user:password@localhost/dbname"

async_engine = create_async_engine(ASYNC_DATABASE_URL, echo=True)
AsyncSessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=async_engine, class_=AsyncSession)

 

6. Session과 sessionmaker의 차이

   1) Session 직접 사용

      - 특징 : Session(bind=engine)을 통해 직접 세션을 생성하고 사용

         - Session을 직접 사용하면 명시적으로 엔진을 바인딩해야 한다.
         - 개별적인 세션을 직접 관리해야 하며, 여러 개의 세션을 생성할 경우 별도의 코드 처리가 필요하다.

 

      - 주요 함수 및 옵션 :

         - add(instance): 객체를 추가
         - delete(instance): 객체를 삭제
         - commit(): 트랜잭션 커밋
         - rollback(): 트랜잭션 롤백
         - close(): 세션 종료
         - flush(): 변경 사항을 DB에 반영 (트랜잭션 커밋 없이 반영)


      - 단점 : 세션이 여러 요청에서 공유될 수 있어 Thread-Safety 문제가 발생할 가능성이 있음

 

      - 이 방식의 문제점 :

         - session이 전역 변수로 사용되므로, 여러 요청이 동시에 발생하면 Thread-Safety 문제가 발생할 가능성이 있음
         - 매 요청마다 세션을 새로 생성하지 않으므로 동시 요청 시 데이터 정합성 문제가 생길 수 있음
         - FastAPI의 종속성 주입(Depends)을 활용할 수 없음

from fastapi import FastAPI, Depends
from sqlalchemy import create_engine, Column, Integer, String
from sqlalchemy.orm import Session
from sqlalchemy.ext.declarative import declarative_base

# FastAPI 앱 생성
app = FastAPI()

# 데이터베이스 설정
DATABASE_URL = "sqlite:///./test.db"
engine = create_engine(DATABASE_URL, connect_args={"check_same_thread": False})

# ORM 모델 정의
Base = declarative_base()

class User(Base):
    __tablename__ = "users"
    id = Column(Integer, primary_key=True, index=True)
    name = Column(String, index=True)

# 데이터베이스 테이블 생성
Base.metadata.create_all(bind=engine)

# 🚨 직접 세션을 생성하여 사용하는 방식
session = Session(bind=engine)

@app.get("/users_direct")
def get_users():
    try:
        users = session.query(User).all()
        return users
    finally:
        session.close()  # 수동으로 세션을 닫아야 함

 

   2) sessionmaker를 사용하는 코드

      - 특징: sessionmaker를 사용하여 요청마다 새로운 세션을 생성하고 Depends를 활용

         - sessionmaker를 사용하면 세션을 더 쉽게 생성하고 관리할 수 있다.
         - autocommit, autoflush 등의 기본 옵션을 설정할 수 있다.

 

      - 주요 함수 및 옵션 :

         - autocommit: 자동 커밋 활성화 여부 (기본값: False)
         - autoflush: 자동 플러시 활성화 여부 (기본값: True)
         - expire_on_commit: 커밋 후 객체 만료 여부 (기본값: True)


      - 장점: 요청 단위로 세션을 관리할 수 있어 Thread-Safety가 보장됨

from fastapi import FastAPI, Depends
from sqlalchemy.orm import sessionmaker, Session

# FastAPI 앱 생성
app = FastAPI()

# 🚀 sessionmaker 사용하여 세션 팩토리 생성
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)

# 요청마다 새로운 세션을 제공하는 의존성 함수
def get_db():
    db = SessionLocal()
    try:
        yield db  # 요청 처리 중에 사용할 세션을 반환
    finally:
        db.close()  # 요청 완료 후 세션 닫기

@app.get("/users_sessionmaker")
def get_users(db: Session = Depends(get_db)):
    return db.query(User).all()

 

   3) 두 방식의 차이점

비교 항목 Session 직접 사용 sessionmaker 사용
세션 생성 방식 session = Session(bind=engine) SessionLocal = sessionmaker(bind=engine) 후 SessionLocal() 사용
세션 관리 전역 세션을 사용하므로 요청 간 공유될 가능성 있음 요청마다 독립적인 세션을 생성
Thread-Safety ❌ 여러 요청이 동시에 들어올 경우 데이터 정합성 문제 발생 가능 ✅ 요청마다 새로운 세션을 생성하여 Thread-Safety 보장
종속성 주입 (Depends) ❌ 직접 session을 호출해야 함 ✅ get_db()를 이용해 FastAPI의 Depends 활용 가능
세션 닫기 (.close()) 직접 .close()를 호출해야 함 get_db()에서 자동으로 관리

 

7. 동기식과 비동기식의 get_db 구현 방법

   1) 동기(Sync) 방식의 get_db

      - try...yield...finally 패턴을 사용해 세션을 안전하게 관리
      - 요청이 성공적으로 끝나면 db.commit()을 호출하여 변경사항을 저장
      - 예외 발생 시 db.rollback()을 수행하여 트랜잭션 안전성 유지
      - finally:에서 db.close()를 호출하여 커넥션 누수 방지

from fastapi import Depends, HTTPException
from sqlalchemy.orm import sessionmaker, Session
from sqlalchemy.exc import SQLAlchemyError
from starlette.status import HTTP_500_INTERNAL_SERVER_ERROR

# SQLAlchemy 엔진 및 세션 설정
DATABASE_URL = "sqlite:///./test.db"
engine = create_engine(DATABASE_URL, connect_args={"check_same_thread": False})
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)

# 🚀 동기 방식의 의존성 주입
def get_db():
    db: Session = SessionLocal()
    try:
        yield db  # 요청 처리 중에 사용할 세션 반환
        db.commit()  # 정상 요청이라면 커밋 수행
    except SQLAlchemyError as e:
        db.rollback()  # 예외 발생 시 롤백 수행
        raise HTTPException(status_code=HTTP_500_INTERNAL_SERVER_ERROR, detail=f"Database error: {str(e)}")
    finally:
        db.close()  # 요청 완료 후 반드시 세션 닫기

 

   2) 그렇다면 동기 방식에서 with를 사용할 방법이 전혀 없을까? - scoped_session

      - 동기 방식에서도 with 문을 사용할 수는 있지만, 일반적인 SessionLocal() 방식에서는 불가능하다.
      - 만약 동기 방식에서도 with 문을 사용하고 싶다면 scoped_session을 활용해야 하다.

from sqlalchemy.orm import scoped_session

# Scoped Session 설정
SessionLocal = scoped_session(sessionmaker(bind=engine, autocommit=False, autoflush=False))

def get_db():
    with SessionLocal() as db:  # scoped_session을 사용하면 가능
        try:
            yield db
            db.commit()
        except SQLAlchemyError as e:
            db.rollback()
            raise HTTPException(status_code=500, detail=f"Database error: {str(e)}")

 

      1. scoped_session이란?

         - scoped_session은 스레드 또는 특정 컨텍스트(예: 요청 단위)마다 고유한 세션을 제공하는 SQLAlchemy의 세션 관리 도구이다.

            - sessionmaker()는 호출될 때마다 새로운 Session 인스턴스를 반환하지만, scoped_session()은 각 스레드(또는 컨텍스트)마다 하나의 Session을 유지하고 필요할 때 재사용한다.

 

         - 즉, scoped_session을 사용하면 같은 스레드에서 여러 번 Session()을 호출해도 동일한 인스턴스를 반환한다.

         - 주로 Flask, Celery, FastAPI 등의 웹 프레임워크에서 요청 단위의 세션 관리에 활용된다.

 

      2. 기본적인 사용법 :

         - SessionLocal = scoped_session(SessionFactory)
            - 요청마다 새로운 세션을 제공하면서 같은 스레드에서는 동일한 세션을 유지
         - SessionLocal()을 여러 번 호출해도 같은 요청 내에서는 같은 Session을 반환
         - finally:에서 db.close()를 호출해 세션 누수를 방지

from sqlalchemy.orm import sessionmaker, scoped_session
from sqlalchemy import create_engine

# 데이터베이스 엔진 생성
DATABASE_URL = "sqlite:///./test.db"
engine = create_engine(DATABASE_URL, connect_args={"check_same_thread": False})

# sessionmaker 생성
SessionFactory = sessionmaker(bind=engine, autocommit=False, autoflush=False)

# scoped_session 설정 (스레드별 세션 관리)
SessionLocal = scoped_session(SessionFactory)

# 요청 단위로 사용되는 DB 세션 함수
def get_db():
    db = SessionLocal()
    try:
        yield db
        db.commit()  # 정상적으로 처리되면 커밋
    except Exception as e:
        db.rollback()  # 오류 발생 시 롤백
        raise
    finally:
        db.close()  # 요청이 끝나면 세션 닫기

 

      3. scoped_session을 사용해야 하는 경우 :

         - 멀티스레딩 환경 :

            - scoped_session은 멀티스레딩 환경에서 각 스레드마다 독립적인 세션을 관리할 수 있도록 도와준다.

            - 각 스레드는 서로 다른 세션을 가지므로 안전하게 병렬 처리 가능하다.

from threading import Thread

def db_task():
    db = SessionLocal()  # 각 스레드에서 동일한 SessionLocal()을 호출
    print(f"Session ID: {id(db)}")  # 각 스레드마다 다른 세션 ID 반환
    db.close()

threads = [Thread(target=db_task) for _ in range(3)]
for t in threads:
    t.start()
for t in threads:
    t.join()

 

      4. FastAPI와 같은 웹 프레임워크에서 요청 단위 세션 관리 :

         - FastAPI의 Depends(get_db)를 활용하면 각 요청마다 고유한 세션을 제공할 수 있다.
         - 요청이 끝나면 세션이 닫히므로 커넥션 누수를 방지할 수 있다.

from fastapi import FastAPI, Depends
from sqlalchemy.orm import Session

app = FastAPI()

@app.get("/")
def read_root(db: Session = Depends(get_db)):
    result = db.execute("SELECT 'Hello, world!'").fetchone()
    return {"message": result[0]}

 

      5. scoped_session의 장점과 단점 :

         - 장점 :

            - 멀티스레딩에서 안전하게 세션 관리 (각 스레드마다 독립적인 세션 유지)
            - FastAPI, Flask 등에서 요청 단위의 세션 유지 (요청이 끝나면 자동 정리 가능)
            - 코드의 일관성 유지 (SessionLocal()을 여러 번 호출해도 같은 요청 내에서는 동일한 세션 반환`)


         - 단점 :

            - 비동기 환경에서는 적절하지 않음 (scoped_session은 동기 전용, 비동기 AsyncSession과 함께 사용할 수 없음)
            - 세션 범위를 정확하게 설정해야 함 (clear()를 사용하지 않으면 스레드 간 세션이 꼬일 수도 있음)

 

         - 비동기 환경에서는 scoped_session 대신 async with AsyncSessionLocal() as db:를 사용하는 것이 안전하다.

 

      6. scoped_session에서 세션 수동 정리 (remove())

            - scoped_session은 스레드나 요청이 종료될 때 remove()를 호출해야 안전하게 세션이 정리됨   

from fastapi import Request

@app.middleware("http")
async def db_session_middleware(request: Request, call_next):
    response = await call_next(request)
    SessionLocal.remove()  # 요청이 끝나면 세션 제거
    return response

 

      7. SessionLocal.remove()

         - 사용 대상 : scoped_session 객체
         - 기능: 현재 스레드 또는 요청과 연결된 세션을 제거하고, 이후 새로운 세션 요청이 오면 새로운 세션을 생성하도록 한다.

         - 작동 방식: SessionLocal이 scoped_session을 사용할 경우, 내부적으로 Session.close()를 호출하고, 세션을 제거하여 메모리 누수를 방지한다.

 

         - 예제 코드

from sqlalchemy.orm import sessionmaker, scoped_session

SessionLocal = scoped_session(sessionmaker(bind=engine))

# 요청이 끝난 후 세션 정리
SessionLocal.remove()

 

      8.  db.close()

         - 사용 대상: 개별 세션 인스턴스 (Session 객체)
         - 기능: 해당 db 세션을 직접 종료하여 연결을 정리함
         - 작동 방식: 세션을 닫아 더 이상 사용할 수 없게 만듦 (해당 객체는 다시 사용할 수 없음)

 

         - 예제 코드

from sqlalchemy.orm import sessionmaker

SessionLocal = sessionmaker(bind=engine)
db = SessionLocal()

# 세션 종료
db.close()

 

      9. 차이점 요약

구분 SessionLocal.remove() db.close()
대상 scoped_session 객체 개별 Session 객체
효과 현재 스레드/요청과 연결된 세션을 제거하고 새 세션을 할당 가능 특정 세션 인스턴스를 닫음 (재사용 불가)
내부 동작 Session.close()를 호출하고 세션을 제거 Session.close()만 호출

 

      10. 언제 사용해야 할까?

         - FastAPI 등 웹 애플리케이션에서 요청마다 새로운 세션을 생성하고 정리해야 할 경우:
            - SessionLocal.remove()
         - 특정한 세션을 직접 다룰 경우:
            - db.close()

 

         즉, 일반적으로 scoped_session을 사용할 때는 remove()를, 개별 세션을 사용할 때는 close()를 사용하면 된다.

 

      11. scoped_session vs sessionmaker

비교 항목 sessionmaker() scoped_session()
멀티스레드 지원 ❌ (각 스레드가 직접 Session()을 생성해야 함) ✅ (스레드별 세션 자동 관리)
FastAPI 요청 단위 세션 관리 가능 (get_db()에서 직접 생성) 가능 (get_db()에서 동일한 세션 재사용)
동기/비동기 지원 ✅ 동기 지원 (Session) ❌ 동기 전용 (AsyncSession 지원 안 됨)
세션 자동 정리 (remove()) ❌ (close()를 직접 호출해야 함) ✅ (remove()로 자동 정리 가능)

 

      12. 결론

         - scoped_session은 멀티스레드 환경에서 안전하게 세션을 관리하는 도구
         - FastAPI와 같은 웹 프레임워크에서 요청 단위의 세션 관리를 할 때 유용
         - 같은 스레드에서 SessionLocal()을 여러 번 호출해도 동일한 세션을 반환하므로 관리가 편리
         - 하지만, 비동기(AsyncSession) 환경에서는 scoped_session을 사용하면 안 됨! (async with 사용해야 함)

         - FastAPI에서 동기 방식의 DB 관리가 필요하다면 scoped_session을 활용하는 것이 효과적입니다! 

 

   3) 비동기(Async) 방식의 get_db

      - AsyncSessionLocal()을 async with 문으로 감싸 자동으로 커넥션을 닫음
      - await db.commit() 및 await db.rollback()을 통해 비동기 방식으로 트랜잭션 관리
      - 비동기 데이터베이스 드라이버 필요 (asyncpg, aiomysql 등)

from sqlalchemy.ext.asyncio import AsyncSession, create_async_engine
from sqlalchemy.orm import sessionmaker
from fastapi import Depends, HTTPException
from sqlalchemy.exc import SQLAlchemyError
from starlette.status import HTTP_500_INTERNAL_SERVER_ERROR

# 비동기 데이터베이스 설정 (예: PostgreSQL + asyncpg)
ASYNC_DATABASE_URL = "postgresql+asyncpg://user:password@localhost/dbname"
async_engine = create_async_engine(ASYNC_DATABASE_URL, echo=True)

# 비동기 세션 팩토리
AsyncSessionLocal = sessionmaker(
    bind=async_engine, class_=AsyncSession, expire_on_commit=False
)

# 🚀 비동기 방식의 의존성 주입
async def get_db():
    async with AsyncSessionLocal() as db:
        try:
            yield db  # 요청 처리 중에 사용할 세션 반환
            await db.commit()  # 정상 요청이라면 커밋 수행
        except SQLAlchemyError as e:
            await db.rollback()  # 예외 발생 시 롤백 수행
            raise HTTPException(status_code=HTTP_500_INTERNAL_SERVER_ERROR, detail=f"Database error: {str(e)}")

 

   4) 비교: 동기 vs. 비동기

      1. 동기 방식 (Session)

         - Session은 컨텍스트 매니저(with 지원)가 아니라서 with 문을 사용할 수 없음
         - 따라서 try...finally 블록을 사용해 .close()를 명시적으로 호출해야 함
         - scoped_session을 사용하면 with 문을 사용할 수 있으나 일반적이지 않음

 

      2. 비동기 방식 (AsyncSession)

         - AsyncSession은 컨텍스트 매니저를 지원하므로 async with 문을 사용할 수 있음
         - async with를 사용하면 세션이 자동으로 닫히므로 .close()가 필요 없음

      - 결론적으로, 동기 방식에서는 try...finally로 명시적으로 db.close()를 호출하고, 비동기 방식에서는 async with로 자동으로 세션을 닫는 것이 가장 안전한 방법이다.

비교 항목 동기 (SessionLocal) 비동기 (AsyncSessionLocal)
세션 클래스 Session AsyncSession
커넥션 유지 방식 session.close() 필요 async with 사용하여 자동 종료
커밋/롤백 db.commit(), db.rollback() await db.commit(), await db.rollback()
예외 처리 try...except SQLAlchemyError: try...except SQLAlchemyError:
트랜잭션 관리 autocommit=False, autoflush=False로 관리 expire_on_commit=False로 관리
비동기 지원

 

   5) 동기 방식에서 with를 사용하지 않는 이유

      - 동기 방식에서 with 문을 사용하지 않는 것은 SQLAlchemy의 Session 객체가 컨텍스트 매니저(__enter__() 및 __exit__())를 직접 구현하지 않았기 때문이다.
      - 즉, Session은 with 문을 사용할 수 없는 구조이므로 finally:에서 .close()를 명시적으로 호출해야 한다.

 

   6)  비동기 방식에서 close()가 필요 없는 이유

      - 비동기 방식(AsyncSession)에서는 async with AsyncSessionLocal() as db: 구문을 사용했기 때문에 세션이 자동으로 닫힌다.
      - async with 문은 비동기 컨텍스트 매니저로 동작하며, __aenter__()에서 세션을 생성하고, __aexit__()에서 세션을 닫아준다.
      - 즉, async with를 사용하면 명시적으로 db.close()를 호출할 필요가 없다.
      - 동기 방식에서는 finally: 블록에서 .close()를 명시적으로 호출해야 하지만, 비동기 방식은 이를 자동으로 처리합니다.

 

 - reference : 

https://docs.sqlalchemy.org/en/20/orm/session.html

 

Using the Session — SQLAlchemy 2.0 Documentation

 

docs.sqlalchemy.org

https://miintto.github.io/docs/python-sqlalchemy-session

 

SQLAlchemy의 세션 관리 - miintto.log

보통 Django를 벗어나서 독자적인 ORM이 없는 파이썬 프레임워크를 사용한다면 아마 거의 대부분은 SQLAlchemy를 채택하여 사용할 겁니다. 최근 FastAPI를 이용해 개발하면서 SQLAlchemy를 다루어 보았는

miintto.github.io

https://miintto.github.io/docs/python-sqlalchemy-asyncsession

 

Async 환경에서 SQLAlchemy 세션 관리 - miintto.log

이전에 SQLAlchemy의 세션 관리 포스트에서 SQLAlchemy 라이브러리 내부에서 세션의 작동 방식을 알아보았습니다. 하지만 해당 내용은 sync 환경에서만 유효하며, asyncio 기반으로 실행되는 경우에는 여

miintto.github.io

 

댓글