시작하며
SQLAlchemy는 파이썬에서 가장 널리 사용되는 ORM(Object-Relational Mapping) 프레임워크로서, 데이터베이스 작업을 효율적이고 유연하게 처리할 수 있게 해줍니다. 이 가이드에서는 SQLAlchemy ORM을 사용하여 데이터베이스를 다루는 핵심 방법들을 살펴보겠습니다.
목차
- SQLAlchemy 설치
- 핵심 개념 이해
- 데이터베이스 연결
- 데이터 모델 정의
- 테이블 생성
- 기본 CRUD 연산
- 데이터 조회
- 관계형 데이터 처리
- 트랜잭션 관리
- 최적화 기법
1. SQLAlchemy 설치
가장 먼저 pip를 사용하여 SQLAlchemy를 설치합니다.
pip install sqlalchemy
연결하려는 데이터베이스 종류에 따라 적절한 드라이버를 추가로 설치해야 합니다.
# PostgreSQL 연결 시
pip install psycopg2-binary
# MySQL 연결 시
pip install mysql-connector-python
# SQLite의 경우 파이썬 표준 라이브러리에 포함되어 있음
2. 핵심 개념 이해
SQLAlchemy의 아키텍처를 이해하기 위해 핵심 구성요소를 살펴보겠습니다:
- Engine: 데이터베이스와 통신을 담당하는 연결 엔진
- Session: 영속성 연산을 관리하는 데이터베이스 세션
- Model: 데이터베이스 테이블에 대응하는 파이썬 클래스
- Query: 데이터베이스 查询을 구성하고 실행하는 객체
3. 데이터베이스 연결
데이터베이스 연결을 설정하는 방법을 알아봅시다.
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
# SQLite 연결 엔진 생성
engine = create_engine('sqlite:///example.db', echo=True)
# PostgreSQL 연결 예시
# engine = create_engine('postgresql://username:password@localhost:5432/mydatabase')
# MySQL 연결 예시
# engine = create_engine('mysql+mysqlconnector://username:password@localhost:3306/mydatabase')
# 세션 팩토리 생성
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
# 세션 인스턴스 생성
session = SessionLocal()
4. 데이터 모델 정의
테이블 구조를 파이썬 클래스로 정의하는 방법을 보여줍니다.
from sqlalchemy import Column, Integer, String, ForeignKey
from sqlalchemy.orm import relationship, declarative_base
# 베이스 클래스 생성
Base = declarative_base()
class User(Base):
__tablename__ = 'users'
id = Column(Integer, primary_key=True, index=True)
name = Column(String(50), nullable=False)
email = Column(String(100), unique=True, index=True)
# 일대다 관계 정의
posts = relationship("Post", back_populates="author")
class Post(Base):
__tablename__ = 'posts'
id = Column(Integer, primary_key=True, index=True)
title = Column(String(100), nullable=False)
content = Column(String(500))
author_id = Column(Integer, ForeignKey('users.id'))
# 다대일 관계 정의
author = relationship("User", back_populates="posts")
# 다대다 관계 정의 (중개 테이블 사용)
tags = relationship("Tag", secondary="post_tags", back_populates="posts")
class Tag(Base):
__tablename__ = 'tags'
id = Column(Integer, primary_key=True, index=True)
name = Column(String(30), unique=True, nullable=False)
posts = relationship("Post", secondary="post_tags", back_populates="tags")
# 다대다 관계를 위한 중개 테이블
class PostTag(Base):
__tablename__ = 'post_tags'
post_id = Column(Integer, ForeignKey('posts.id'), primary_key=True)
tag_id = Column(Integer, ForeignKey('tags.id'), primary_key=True)
5. 테이블 생성
정의된 모델을 바탕으로 실제 데이터베이스 테이블을 생성합니다.
# 모든 테이블 생성
Base.metadata.create_all(bind=engine)
# 모든 테이블 삭제
# Base.metadata.drop_all(bind=engine)
6. 기본 CRUD 연산
데이터 생성
# 단일 사용자 생성
new_user = User(name="홍길동", email="hong@example.com")
session.add(new_user)
session.commit()
# 여러 사용자批量 생성
session.add_all([
User(name="김철수", email="kim@example.com"),
User(name="이영희", email="lee@example.com")
])
session.commit()
데이터 조회
# 모든 사용자 조회
users = session.query(User).all()
# 첫 번째 사용자 조회
first_user = session.query(User).first()
# ID로 특정 사용자 조회
user = session.query(User).get(1)
데이터 수정
# 조회 후 수정
user = session.query(User).get(1)
user.name = "홍길동수정"
session.commit()
#批量 수정
session.query(User).filter(User.name.like("홍%")).update({"name": "홍氏"}, synchronize_session=False)
session.commit()
데이터 삭제
# 조회 후 삭제
user = session.query(User).get(1)
session.delete(user)
session.commit()
#批量 삭제
session.query(User).filter(User.name == "김철수").delete(synchronize_session=False)
session.commit()
7. 데이터 조회 심화
기본 조회
# 전체 레코드 조회
users = session.query(User).all()
# 특정 필드만 조회
names = session.query(User.name).all()
# 정렬
users = session.query(User).order_by(User.name.desc()).all()
# 결과 수 제한
users = session.query(User).limit(10).all()
# 오프셋 지정
users = session.query(User).offset(5).limit(10).all()
필터 조회
from sqlalchemy import or_
# 정확히 일치하는 조건
user = session.query(User).filter(User.name == "홍길동").first()
# 유사 검색
users = session.query(User).filter(User.name.like("홍%")).all()
# IN 조건
users = session.query(User).filter(User.name.in_(["홍길동", "김철수"])).all()
#复合 조건
users = session.query(User).filter(
User.name == "홍길동",
User.email.like("%@example.com")
).all()
# OR 조건
users = session.query(User).filter(
or_(User.name == "홍길동", User.name == "김철수")
).all()
# 같지 않음
users = session.query(User).filter(User.name != "홍길동").all()
집계 조회
from sqlalchemy import func
# 레코드 수 계산
count = session.query(User).count()
# 그룹별 카운트
user_post_count = session.query(
User.name,
func.count(Post.id)
).join(Post).group_by(User.name).all()
# 평균값 조회
avg_id = session.query(func.avg(User.id)).scalar()
조인 조회
# 내부 조인
results = session.query(User, Post).join(Post).filter(Post.title.like("%Python%")).all()
# 왼쪽 외부 조인
results = session.query(User, Post).outerjoin(Post).all()
# 조인 조건 명시
results = session.query(User, Post).join(Post, User.id == Post.author_id).all()
8. 관계형 데이터 처리
# 관계를 가진 객체 생성
user = User(name="박지민", email="park@example.com")
post = Post(title="내 첫 번째 글", content="Hello World!", author=user)
session.add(post)
session.commit()
# 관계를 통해 데이터 접근
print(f"글 '{post.title}'의 작성자: {post.author.name}")
print(f"사용자 {user.name}의 모든 글:")
for p in user.posts:
print(f" - {p.title}")
# 다대다 관계 조작
python_tag = Tag(name="Python")
sqlalchemy_tag = Tag(name="SQLAlchemy")
post.tags.append(python_tag)
post.tags.append(sqlalchemy_tag)
session.commit()
print(f"글 '{post.title}'의 태그:")
for tag in post.tags:
print(f" - {tag.name}")
9. 트랜잭션 관리
# 자동 커밋 트랜잭션
try:
user = User(name="테스트사용자", email="test@example.com")
session.add(user)
session.commit()
except Exception as e:
session.rollback()
print(f"오류 발생: {e}")
# 컨텍스트 매니저를 사용한 트랜잭션
from sqlalchemy.orm import Session
def create_user(session: Session, name: str, email: str):
try:
user = User(name=name, email=email)
session.add(user)
session.commit()
return user
except:
session.rollback()
raise
# 중첩 트랜잭션
with session.begin_nested():
user = User(name="트랜잭션사용자", email="transaction@example.com")
session.add(user)
# 세이브포인트
savepoint = session.begin_nested()
try:
user = User(name="세이브포인트사용자", email="savepoint@example.com")
session.add(user)
savepoint.commit()
except:
savepoint.rollback()
10. 최적화 기법
효율적인 데이터베이스 연산을 위한 모범 사례를 알아봅시다:
- 세션 관리: 각 요청마다 새 세션을 생성하고 완료 후 반드시 닫아야 합니다
- 예외 처리: 예외 상황을 적절히 처리하고 트랜잭션을 롤백해야 합니다
- 지연 로딩 주의: N+1 查询 문제를 인식하고 eager loading으로 최적화해야 합니다
- 연결 풀: 연결 풀 크기와超时 시간을 적절히 설정해야 합니다
- 데이터 검증: 모델 계층 또는 애플리케이션 계층에서 데이터 무결성을 검증해야 합니다
# 컨텍스트 매니저로 세션 관리
from contextlib import contextmanager
@contextmanager
def get_db():
db = SessionLocal()
try:
yield db
db.commit()
except Exception:
db.rollback()
raise
finally:
db.close()
# 사용 예시
with get_db() as db:
user = User(name="컨텍스트사용자", email="context@example.com")
db.add(user)
마치며
SQLAlchemy ORM은 강력하고 유연한 데이터베이스 操作 수단을 제공합니다. 이 가이드의 내용을 통해 다음을 수행할 수 있게 되었습니다:
- SQLAlchemy 설치 및 설정
- 데이터 모델과 관계 정의
- 기본 CRUD 연산 수행
- 복잡한 查询 구성
- 데이터베이스 트랜잭션 관리
- 최적화 기법 적용
SQLAlchemy에는 혼합 속성, 이벤트 리스너, 사용자 정의 查询 등 더 많은 고급 기능이 있으며, 이를 더욱 탐구해 보시기 바랍니다.