[FastAPI] SQLAlchemy
SQLAlchemy는 파이썬의 ORM 기술으로 자바 진영의 JPA와 유사한 부분이 많다.
JPA의 영속성 컨텍스트와 1차 캐시 : SQLAlchemy의 Session객체
N+1 문제를 해결하기 위한 JPA의 fetch 속성 : SQLAlchemy의 joinedload(), subqueryload()
JPA의 @Entity : SQLAlchemy의 declarative_base()
@ManyToOne, @OneToMany : relationship()
이렇듯 같은 ORM 기술이다 보니 비슷한 개념을 공유하고 있어 JPA에 익숙하다면 SQLAlchemy도 어렵지 않게 적응할 수 있다.
Core
SQLAlchemy의 Core는 데이터베이스별 SQL과 스키마 정의를 위한 시스템으로, SQL 대신 Python코드로 데이터베이스를 조작하는 도구를 포함한다.
Engine : Dialect와 Connection Pool을 포함한 데이터베이스와의 연결을 관리한다.
Dialect : MySQL, PostgreSQL 등 여러 데이터베이스별로 특화된 Dialect가 있으며 각 데이터베이스와 통신하는 방법을 정의한다.
Connection Pool : 데이터베이스 연결을 재사용 할 수 있게 해 연결 관련 오버헤드를 줄인다.
Metadata : 데이터베이스 스키마를 정의하고 DDL 명령어를 생성한다.
ORM
ORM은 파이썬 객체와 데이터베이스 테이블을 매핑하는 시스템으로 SQL 작업을 추상화한다.
Session : 변화 추적, 트랜잭션 관리, 객체 로딩 등 데이터베이스와의 전반적인 통신을 제어한다.
Declarative Base : 테이블과 클래스 매핑을 선언적으로 정의한다.
Query : 데이터베이스 쿼리를 구성할 때 사용되는 객체로 Session을 통해 생성되며 여러 필터링 메서드를 제공해 복잡한 쿼리를 간단하게 작성할 수 있다. (JPA의 QueryDSL과 유사하다)
SQLALCHEMY_DATABASE_URL = 'sqlite:///./blog_api.db'
engine = create_engine(SQLALCHEMY_DATABASE_URL, connect_args={"check_same_thread": False})
SessionLocal = sessionmaker(bind=engine, autocommit=False, autoflush=False)
Base = declarative_base()
def get_db():
db = SessionLocal()
try:
yield db
finally:
db.close()
SQLite 데이터베이스를 FastAPI에서 사용하는 예시이다.
create_engine 함수로 데이터베이스와의 연결을 관리하는 엔진 객체를 생성한다.
sessionmaker 함수는 데이터베이스 세션을 생성하는 팩토리로 여기서는 자동 커밋과 플러시를 사용하지 않아 트랜잭션을 더 세밀하게 조정한다.
declarative_base는 모든 데이터베이스 테이블의 기본 클래스를 생성해 Base클래스는 ORM을 사용해 테이블과 파이썬 클래스를 매핑하는 구조를 제공한다
get_db 함수는 FastAPI의 의존성 주입 시스템에 사용돼 요청에 대해 새로운 데이터베이스 세션을 생성하고 처리가 완료된 후 세션을 닫는다.
class DbPost(Base):
__tablename__ = "post"
id = Column(Integer, primary_key=True, index=True)
image_url = Column(String)
title = Column(String)
content = Column(String)
creator = Column(String)
timestamp = Column(DateTime)
Base를 상속받은 클래스는 ORM 모델 기반 클래스로 작동한다.
__tablename__ 부분으로 클래스가 매핑될 데이터베이스 테이블의 이름을 알려주고, Column 객체로 데이터베이스 테이블의 각 열을 나타낸다.
class PostBase(BaseModel):
image_url: str
title: str
creator: str
class PostDisplay(BaseModel):
id: int
image_url: str
title: str
content: str
creator: str
timestamp: datetime
class Config():
orm_mode = True
Pydantic 모델으로 데이터 검증과 설정 관리를 수행한다.
PostBase는 클라이언트로부터 데이터를 받아 새로운 게시물을 데이터베이스에 생성할 때 본문을 검증할 때 사용되고,
PostDisplay는 클라이언트에게 데이터를 반환할 때 응답 본문의 스키마를 정의한다.
orm_mode 설정으로 SQLAlchemy 객체의 데이터를 직접 읽어 Pydnatic 모델에 할당한다.
DbPost 객체는 JPA에서 @Entity로 정의하는 객체라고 할 수 있고, PostBase와 PostDisplay는 DTO라고 할 수 있다.
SRP원칙, 보안, 데이터 유효성 검증, 성능 최적화를 위해 이렇게 엔티티와 DTO를 나눠서 분리한다.
@router.post('')
def create(request: PostBase, db: Session = Depends(get_db)):
return db_post.create(db, request)
def create(db: Session, request: PostBase):
new_post = DbPost(
image_url=request.image_url,
title=request.title,
content=request.content,
creator=request.creator,
timestamp=datetime.datetime.now()
)
db.add(new_post)
db.commit()
db.refresh(new_post)
return new_post
엔드포인트에서 데이터베이스를 사용하는 부분이다.
FastAPI에서는 Depends로 의존성 주입을 구현한다.
재사용 가능한 데이터베이스 연결 로직을 함수로 분리해 Depends를 통해 의존성으로 주입받아 사용한다.
스프링은 ApplicationContext가 스프링 빈들을 직접 싱글톤으로 관리하고, @Autowired 같은 애너테이션으로 컨테이너에서 싱글톤으로 관리되는 빈들을 주입한다.
FastAPI에서의 의존성 주입은 좀 다르다.
Depends를 사용해 의존성 주입을 구현하면 코드를 읽는 사람에게 해당 매개변수가 외부 의존성임을 명확히 알려줘 가독성을 향상시키고, 의존성 내에서 또 다른 의존성을 선언하는 것 처럼 복잡한 의존성 체인을 구성할 수 있다.
이 외에도 특정 기능을 모듈로 분리해 모듈성과 유지보수성을 향상시키는 등 여러 이점을 제공하니... Depends를 쓰던 안 쓰던 런타임에 호출되는 동작의 차이는 없지만 되도록이면 Depends를 명시적으로 사용하자.
'FastAPI > FastAPI' 카테고리의 다른 글
FastAPI에서의 동시성 처리 (0) | 2023.11.29 |
---|---|
[FastAPI] 요청과 응답 (0) | 2023.11.02 |
댓글
이 글 공유하기
다른 글
-
FastAPI에서의 동시성 처리
FastAPI에서의 동시성 처리
2023.11.29 -
[FastAPI] 요청과 응답
[FastAPI] 요청과 응답
2023.11.02