SQLAlchemy 설치 및 환경 구성
SQLAlchemy는 파이썬에서 데이터베이스 작업을 간편하게 수행할 수 있도록 도와주는 대표적인 객체 관계 매핑(ORM) 프레임워크입니다. 기본 설치를 위해 다음 명령어를 실행하세요.
pip install sqlalchemy
특정 데이터베이스 시스템과 연결하기 위해서는 추가 드라이버 설치가 필요합니다.
# PostgreSQL
pip install psycopg2-binary
# MySQL
pip install mysql-connector-python
# SQLite (Python 표준 라이브러리 포함, 별도 설치 불필요)
핵심 구성 요소 이해
- Engine: 데이터베이스 연결의 중앙 제어자로, 실제 통신을 담당합니다.
- Session: 데이터베이스 작업의 일관성을 보장하는 세션 단위 처리 흐름입니다.
- Model: 데이터베이스 테이블에 대응하는 파이썬 클래스입니다.
- Query: SQL 쿼리를 생성하고 실행하는 객체입니다.
데이터베이스 연결 설정
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
# SQLite 사용 예시
engine = create_engine('sqlite:///app_database.db', echo=True)
# PostgreSQL 연결 예시
# engine = create_engine('postgresql://user:pass@localhost:5432/myapp')
# MySQL 연결 예시
# engine = create_engine('mysql+mysqlconnector://user:pass@localhost:3306/myapp')
# 세션 팩토리 생성
SessionLocal = sessionmaker(
autocommit=False,
autoflush=False,
bind=engine
)
# 세션 인스턴스 확보
db_session = SessionLocal()
모델 정의 및 관계 설정
from sqlalchemy import Column, Integer, String, ForeignKey
from sqlalchemy.orm import relationship, declarative_base
Base = declarative_base()
class Author(Base):
__tablename__ = 'authors'
id = Column(Integer, primary_key=True)
full_name = Column(String(80), nullable=False)
email = Column(String(120), unique=True, index=True)
# 1:N 관계
articles = relationship("Article", back_populates="writer")
class Article(Base):
__tablename__ = 'articles'
id = Column(Integer, primary_key=True)
title = Column(String(150), nullable=False)
body = Column(String(1000))
writer_id = Column(Integer, ForeignKey('authors.id'))
# N:1 관계
writer = relationship("Author", back_populates="articles")
# M:N 관계를 위한 중간 테이블
tags = relationship("Tag", secondary="article_tags", back_populates="articles)
class Tag(Base):
__tablename__ = 'tags'
id = Column(Integer, primary_key=True)
label = Column(String(50), unique=True, nullable=False)
articles = relationship("Article", secondary="article_tags", back_populates="tags")
# 다대다 관계를 위한 연결 테이블
class ArticleTag(Base):
__tablename__ = 'article_tags'
article_id = Column(Integer, ForeignKey('articles.id'), primary_key=True)
tag_id = Column(Integer, ForeignKey('tags.id'), primary_key=True)
테이블 생성 및 삭제
# 모든 모델 기반 테이블 생성
Base.metadata.create_all(bind=engine)
# 모든 테이블 제거 (주의: 데이터 손실 가능)
# Base.metadata.drop_all(bind=engine)
기본적인 데이터 조작 (CRUD)
생성
# 개별 삽입
new_author = Author(full_name="김철수", email="kim@example.com")
db_session.add(new_author)
db_session.commit()
# 여러 항목 일괄 삽입
db_session.add_all([
Author(full_name="박영희", email="park@example.com"),
Author(full_name="최민호", email="choi@example.com")
])
db_session.commit()
조회
# 전체 조회
all_authors = db_session.query(Author).all()
# 첫 번째 항목
first_author = db_session.query(Author).first()
# ID 기반 조회
author_by_id = db_session.query(Author).get(1)
수정
# 특정 항목 수정
target = db_session.query(Author).get(1)
if target:
target.full_name = "김철수 변경"
db_session.commit()
삭제
# 항목 삭제
item_to_delete = db_session.query(Author).get(1)
if item_to_delete:
db_session.delete(item_to_delete)
db_session.commit()
복잡한 쿼리 작성
조건 필터링
from sqlalchemy import or_
# 정확한 값 검색
result = db_session.query(Author).filter(Author.email == "kim@example.com").first()
# 와일드카드 검색
results = db_session.query(Author).filter(Author.full_name.like("김%")).all()
# 리스트 내 포함 여부
filtered = db_session.query(Author).filter(Author.full_name.in_(['김철수', '박영희'])).all()
# 조건 결합
complex_query = db_session.query(Author).filter(
Author.full_name == "김철수",
Author.email.like("%@example.com")
).all()
# OR 조건
or_result = db_session.query(Author).filter(
or_(Author.full_name == "김철수", Author.full_name == "박영희")
).all()
집계 및 그룹화
from sqlalchemy import func
# 전체 수량
total_count = db_session.query(Author).count()
# 그룹별 카운트
grouped_stats = db_session.query(
Author.full_name,
func.count(Article.id)
).join(Article).group_by(Author.full_name).all()
# 평균값 계산
avg_id = db_session.query(func.avg(Author.id)).scalar()
조인 연산
# 내부 조인
joined_data = db_session.query(Author, Article).join(Article).filter(
Article.title.like("%파이썬%")
).all()
# 외부 조인
outer_data = db_session.query(Author, Article).outerjoin(Article).all()
# 조건 기반 조인
conditional_join = db_session.query(Author, Article).join(
Article, Author.id == Article.writer_id
).all()
관계형 데이터 처리
# 관계 객체 생성
author = Author(full_name="이지은", email="lee@example.com")
article = Article(title="첫 글", body="안녕하세요!", writer=author)
db_session.add(article)
db_session.commit()
# 관계 참조
print(f"글 '{article.title}'의 저자는 {article.writer.full_name}")
# 관련 글 목록 출력
for a in author.articles:
print(f" - {a.title}")
# 다대다 관계 추가
py_tag = Tag(label="Python")
sql_tag = Tag(label="SQLAlchemy")
article.tags.append(py_tag)
article.tags.append(sql_tag)
db_session.commit()
# 태그 출력
for t in article.tags:
print(f" - {t.label}")
트랜잭션 제어 및 안정성 보장
try:
new_user = Author(full_name="테스트 유저", email="test@test.com")
db_session.add(new_user)
db_session.commit()
except Exception as e:
db_session.rollback()
raise e
# 트랜잭션 컨텍스트 사용
def register_author(session, name, email):
try:
user = Author(full_name=name, email=email)
session.add(user)
session.commit()
return user
except:
session.rollback()
raise
# 중첩 트랜잭션
with db_session.begin_nested():
temp_user = Author(full_name="내부 트랜잭션", email="nested@test.com")
db_session.add(temp_user)
# 저장점 사용
savepoint = db_session.begin_nested()
try:
save_user = Author(full_name="저장점 테스트", email="save@test.com")
db_session.add(save_user)
savepoint.commit()
except:
savepoint.rollback()
권장되는 실무 팁
- 세션 생명주기 관리: 각 요청마다 새 세션 생성 후 종료해야 합니다.
- 예외 처리: 실패 시 자동 롤백을 보장하세요.
- 지연 로딩 최적화: N+1 문제를 피하기 위해 지연 로딩을 적절히 설정하세요.
- 커넥션 풀 설정: 동시성과 성능을 고려해 적절한 풀 크기 및 타임아웃을 구성하세요.
- 데이터 유효성 검사: 모델 또는 비즈니스 계층에서 입력 검증을 수행하세요.
from contextlib import contextmanager
@contextmanager
def get_db_session():
session = SessionLocal()
try:
yield session
session.commit()
except Exception:
session.rollback()
raise
finally:
session.close()
# 사용 예시
with get_db_session() as db:
user = Author(full_name="컨텍스트 사용자", email="context@test.com")
db.add(user)
결론
SQLAlchemy ORM은 데이터베이스 접근을 매우 직관적이고 안정적으로 만들어줍니다. 본 문서를 통해 다음과 같은 능력을 갖출 수 있습니다:
- SQLAlchemy 설치 및 초기 설정
- 모델 정의 및 관계 구현
- 기본 데이터 조작(생성, 조회, 수정, 삭제)
- 복잡한 쿼리 및 조인 문법 사용
- 트랜잭션 및 에러 처리 전략 적용
- 실제 서비스 환경에서의 최적화 방법 이해
추가로, 하이브리드 속성, 이벤트 리스너, 사용자 정의 쿼리 등 고급 기능은 심화 학습을 통해 더욱 깊이 있게 탐구할 수 있습니다.