WeniVooks

검색

FastAPI 베이스캠프

SQLite와 SQLAlchemy

1. SQLite와 SQLAlchemy

앞서 우리는 리스트나 딕셔너리로 데이터를 관리했습니다. 실제 애플리케이션에서는 데이터를 영구적으로 저장하고 사용하기 위해 데이터베이스를 사용합니다. 이 챕터에서는 간단하게 사용할 수 있는 SQLite를 SQLAlchemy를 통하여 연동하는 방법을 알아보겠습니다. SQLite는 파일 기반의 경량 데이터베이스이며, SQL이라는 언어를 통해 데이터를 저장하고 조회할 수 있습니다. 아래 서비스를 통해 SQL 언어를 간단하게 실습해볼 수 있습니다.

weniv SQL

SQL언어는 데이터베이스를 다룬다면 필수로 알아야 하는 언어입니다. PostgreSQL, MySQL 등의 데이터베이스를 사용하더라도 마찬가지입니다.

그런데 SQLAlchemy를 사용하면 데이터베이스를 파이썬 코드로 쉽게 다룰 수 있게 해줍니다. 이렇게 프로그래밍 언어로 데이터베이스를 다루는 것을 ORM(Object-Relational Mapping)이라고 합니다. 이렇게 데이터베이스를 파이썬 코드로 다루는 것이 편리하기 때문에 많은 프레임워크에서 사용합니다. 그렇다 하더라도 프로그래머에게 데이터베이스를 다루는 것은 필수이므로 SQL을 꼭 공부해야 합니다. 우리 수업에서는 SQL 구문까지 다루지는 않습니다. 더 자세한 내용은 아래 무료 강의를 참고해주세요.

SQL 베이스캠프

앞서 말한 SQLite와 SQLAlchemy는 모두 FastAPI와 독립적입니다. FastAPI가 아니더라도 이 라이브러리를 사용하는 곳은 많습니다. 그렇기에 이 라이브러리들을 좀 더 쉽게 이해하기 위해 FastAPI와 독립적으로 살펴보도록 하겠습니다. 복잡도를 낮출 수 있기 때문이죠. 추후 이 내용들을 FastAPI와 연동하는 방법을 알아보겠습니다.

2. SQLite Colab 실습

간단하게 Colab에서 실습할 수 있는 코드를 준비했습니다. 이 코드로 Python이 어떻게 SQLite를 사용하여 데이터를 저장하고 조회하는지 확인할 수 있습니다. 여기서 SQL 구문이나 sqlite3 라이브러리를 상세하게 이해하실 필요는 없습니다. 어떤 식으로 사용하는지 이해하는 것이 중요합니다.

import sqlite3
 
# 데이터 베이스와 연동이 필요합니다.
conn = sqlite3.connect('example.db')
cursor = conn.cursor() # 여기서 얻어온 커서는 지금 깜빡이고 있는 커서라고 생각해주시면 됩니다.
 
# 테이블 생성
# TABLE IF NOT EXISTS users: 유저가 있지 않으면
# 컬럼명은 id, name, age로 편성하고
# id는 자동으로 증가되는 숫자값
# name은 비울수 없는 값
# age는 숫자값
cursor.execute(
    '''
    CREATE TABLE IF NOT EXISTS users (
        id INTEGER PRIMARY KEY,
        name TEXT NOT NULL,
        age INTEGER
    )
    '''
)
 
# 데이터 삽입
cursor.execute('INSERT INTO users (name, age) VALUES ("홍길동", 20)')
cursor.execute('INSERT INTO users (name, age) VALUES ("김철수", 30)')
# 이렇게 삽입도 가능합니다.
cursor.execute('INSERT INTO users (name, age) VALUES (?, ?)', ('김숙희', 30))
conn.commit()
 
# 삽입된 데이터 확인
# cursor.execute는 SQL 구문을 실행하는 것이고
# 구문을 실행한 것을 보여주진 않습니다.
cursor.execute('SELECT * FROM users')
# 결과를 보기위해서는 fetch를 해야 합니다.
# 다 가져오는 것은 fetchall이고 하나만 가져오는 것은 fetchone 입니다.
print(cursor.fetchall())
 
# 데이터 수정
cursor.execute('UPDATE users SET age = ? WHERE name = ?', (25, '홍길동'))
conn.commit()
 
# 데이터 삭제
cursor.execute('DELETE FROM users WHERE name = ?', ('김철수',))
conn.commit()
 
# 수정된 데이터 확인
cursor.execute('SELECT * FROM users')
print(cursor.fetchall())
 
conn.close()
import sqlite3
 
# 데이터 베이스와 연동이 필요합니다.
conn = sqlite3.connect('example.db')
cursor = conn.cursor() # 여기서 얻어온 커서는 지금 깜빡이고 있는 커서라고 생각해주시면 됩니다.
 
# 테이블 생성
# TABLE IF NOT EXISTS users: 유저가 있지 않으면
# 컬럼명은 id, name, age로 편성하고
# id는 자동으로 증가되는 숫자값
# name은 비울수 없는 값
# age는 숫자값
cursor.execute(
    '''
    CREATE TABLE IF NOT EXISTS users (
        id INTEGER PRIMARY KEY,
        name TEXT NOT NULL,
        age INTEGER
    )
    '''
)
 
# 데이터 삽입
cursor.execute('INSERT INTO users (name, age) VALUES ("홍길동", 20)')
cursor.execute('INSERT INTO users (name, age) VALUES ("김철수", 30)')
# 이렇게 삽입도 가능합니다.
cursor.execute('INSERT INTO users (name, age) VALUES (?, ?)', ('김숙희', 30))
conn.commit()
 
# 삽입된 데이터 확인
# cursor.execute는 SQL 구문을 실행하는 것이고
# 구문을 실행한 것을 보여주진 않습니다.
cursor.execute('SELECT * FROM users')
# 결과를 보기위해서는 fetch를 해야 합니다.
# 다 가져오는 것은 fetchall이고 하나만 가져오는 것은 fetchone 입니다.
print(cursor.fetchall())
 
# 데이터 수정
cursor.execute('UPDATE users SET age = ? WHERE name = ?', (25, '홍길동'))
conn.commit()
 
# 데이터 삭제
cursor.execute('DELETE FROM users WHERE name = ?', ('김철수',))
conn.commit()
 
# 수정된 데이터 확인
cursor.execute('SELECT * FROM users')
print(cursor.fetchall())
 
conn.close()

여기서 commit()은 데이터베이스에 변경사항을 저장하는 것입니다. 이 코드를 실행하지 않으면 데이터베이스에 변경사항이 저장되지 않습니다.

3. SQLAlchemy colab 실습

이번에는 SQLAlchemy를 사용하여 데이터베이스를 다루는 방법을 알아보겠습니다. 앞서 소개한 것과 같이 SQLAlchemy는 데이터베이스를 파이썬 코드로 쉽게 다룰 수 있게 해줍니다. 이렇게 프로그래밍 언어로 데이터베이스를 다루는 것을 ORM(Object-Relational Mapping)이라고 합니다. SQLAlchemy는 sqlite3 뿐만 아니라 MySQL, PostgreSQL, Oracle 등의 데이터베이스를 지원합니다. 여기서는 sqlite3를 사용하여 실습해보겠습니다. Colab에서 실습할 수 있는 간단한 코드를 준비했습니다.

구분선으로 구분된 코드는 각각 별도의 코드 블럭입니다. 코드를 복사하여 실행해보세요.

from sqlalchemy import create_engine, Column, Integer, String
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
 
# 데이터베이스 연결 설정
# 'sqlite:///a/b/c/sample.db'와 'sqlite:///./a/b/c/sample.db'는 같습니다.
# echo=True를 하게 되면 SQL문이 실행이 될 때마다 출력창에서 
# 반복해 출력해줍니다. 공부할 때 좋습니다.
engine = create_engine('sqlite:///sample.db', echo=True)
# DB와 연동되는 모델을 생성하기 위한 기본 모델
Base = declarative_base()
 
class User(Base):
    __tablename__ = 'users' # 테이블 이름
 
    id = Column(Integer, primary_key=True) # 기본키
    name = Column(String(50)) # 이름 컬럼, 50자 제한 
    age = Column(Integer) # 나이 컬럼
 
    def __repr__(self):
        return f'<{self.id}, {self.name}, {self.age}>'
 
# 테이블 생성
Base.metadata.create_all(engine)
 
# 세션 생성(DB와 연결하는 코드)
Session = sessionmaker(bind=engine)
session = Session()
 
###############################
 
new_user = User(name='licat', age=10)
session.add(new_user)
session.commit()
 
###############################
 
new_user = User(name='mura', age=20)
# add, update, delete 등이 있습니다.
session.add(new_user)
session.commit()
 
###############################
 
session.query(User).all()
 
###############################
 
# 이렇게 하면 쿼리만 생성합니다.
session.query(User).filter(User.name == 'mura')
# 이렇게 해야 쿼리를 실행합니다.
session.query(User).filter(User.name == 'mura').first()
 
###############################
 
mura = session.query(User).filter(User.name == 'mura').first()
if mura:
    mura.age = 100
    session.commit()
 
session.query(User).all()
 
###############################
 
if mura:
    session.delete(mura)
    session.commit()
 
session.query(User).all()
 
###############################
 
session.close()
from sqlalchemy import create_engine, Column, Integer, String
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
 
# 데이터베이스 연결 설정
# 'sqlite:///a/b/c/sample.db'와 'sqlite:///./a/b/c/sample.db'는 같습니다.
# echo=True를 하게 되면 SQL문이 실행이 될 때마다 출력창에서 
# 반복해 출력해줍니다. 공부할 때 좋습니다.
engine = create_engine('sqlite:///sample.db', echo=True)
# DB와 연동되는 모델을 생성하기 위한 기본 모델
Base = declarative_base()
 
class User(Base):
    __tablename__ = 'users' # 테이블 이름
 
    id = Column(Integer, primary_key=True) # 기본키
    name = Column(String(50)) # 이름 컬럼, 50자 제한 
    age = Column(Integer) # 나이 컬럼
 
    def __repr__(self):
        return f'<{self.id}, {self.name}, {self.age}>'
 
# 테이블 생성
Base.metadata.create_all(engine)
 
# 세션 생성(DB와 연결하는 코드)
Session = sessionmaker(bind=engine)
session = Session()
 
###############################
 
new_user = User(name='licat', age=10)
session.add(new_user)
session.commit()
 
###############################
 
new_user = User(name='mura', age=20)
# add, update, delete 등이 있습니다.
session.add(new_user)
session.commit()
 
###############################
 
session.query(User).all()
 
###############################
 
# 이렇게 하면 쿼리만 생성합니다.
session.query(User).filter(User.name == 'mura')
# 이렇게 해야 쿼리를 실행합니다.
session.query(User).filter(User.name == 'mura').first()
 
###############################
 
mura = session.query(User).filter(User.name == 'mura').first()
if mura:
    mura.age = 100
    session.commit()
 
session.query(User).all()
 
###############################
 
if mura:
    session.delete(mura)
    session.commit()
 
session.query(User).all()
 
###############################
 
session.close()
4장 데이터베이스와 인증4.2 FastAPI에 DB적용하기