개발/그 외

SQLite에 대해 알아보자!

paice 2025. 3. 26. 01:19

 

회사에서 진행 중이던 기존 프로젝트 TF팀에 합류하면서 인수인계를 받게 되었다.


해당 프로젝트는 백엔드는 Python의 FastAPI, 프론트엔드는 React로 구성되어 있었고, 전체적인 구조나 워크플로우는 내가 기존에 경험해왔던 방식과는 조금 달랐다. 그래서 하나씩 구조를 뜯어보며 이해하는 데 집중했다.

 

나는 프론트엔드 개발자로 참여하게 되었지만, 기존 시스템의 흐름을 제대로 이해하려면 백엔드와 데이터베이스까지 포함한 전체 구조를 파악하는 게 먼저라는 생각이 들었다.


그 과정에서 자연스럽게 SQLite라는 데이터베이스를 접하게 되었고, FastAPI와 함께 어떻게 연동되는지를 알아보게 되었다.

 

SQLite는 서버 없이도 작동하는 파일 기반 데이터베이스로, 설치나 복잡한 설정 없이도 빠르게 사용할 수 있다는 장점이 있다.

특히 FastAPI 같은 프레임워크와의 연동도 간단해서 소규모 프로젝트에서 사용하기 적합해 보였다.

 

이번 글에서는 SQLite가 어떤 DB인지 간단히 정리하고, FastAPI와의 연동 방식까지 함께 정리해보려고 한다.


1. SQLite란?

SQLite는 서버리스(serverless) SQL 데이터베이스다.

말 그대로 서버 설치 없이, 하나의 .db 파일로 작동한다.

굳이 데이터베이스 서버를 띄우지 않아도 되니, 작은 프로젝트나 로컬 개발 환경에서 빠르게 쓰기에 딱 좋다.

 

하기는 특징과 단점을 요약한 내용이다.

  • 서버 없이 동작 → 설치 필요 없음
  • 파일 기반 저장 → .db 파일 하나만 있으면 끝
  • 트랜잭션(ACID) 지원 → 데이터 안정성 보장
  • 가볍고 빠름 → 작은 앱, 테스트용으로 최적
  • 동시성 약함 → 여러 사용자 동시 쓰기엔 부적합
  • 쓰기 작업이 많으면 부담 → 읽기 위주로 추천
  • 스케일 아웃 불가 → 분산 처리나 고가용성 요구 시 부적합

즉, 초기 단계나 소규모 앱엔 강력하지만, 사용자 수가 많아질 경우엔 풀사이즈 DB로 전환을 고려해야 한다.


2. FastAPI와 연동하기

SQLite는 FastAPI와도 자연스럽게 연동할 수 있고, 파이썬 내장 모듈이라 별도 설치 없이 바로 사용할 수 있다는 점도 큰 장점이다.

실제로 FastAPI에서 SQLite를 사용할 때는 보통 SQLAlchemy ORM을 함께 활용하면 모델 정의나 세션 관리가 훨씬 편리해진다.


하기는 SQLite를 FastAPI와 연동하는 기본적인 예시 코드다.

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

# SQLite 데이터베이스 파일 경로 설정
DATABASE_URL = "sqlite:///./test.db"

# DB 엔진 생성 (SQLite는 멀티스레드 환경에서 check_same_thread=False 필요)
engine = create_engine(DATABASE_URL, connect_args={"check_same_thread": False})

# 요청마다 사용할 세션 클래스 생성
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)

# 모델 생성을 위한 베이스 클래스
Base = declarative_base()

# User 테이블 정의
class User(Base):
    __tablename__ = "users"
    id = Column(Integer, primary_key=True, index=True)
    name = Column(String, index=True)

# 테이블 생성 (정의한 모델 기반으로 DB에 실제 테이블 생성)
Base.metadata.create_all(bind=engine)

# FastAPI 인스턴스 생성
app = FastAPI()

# 요청마다 DB 세션을 생성하고 자동으로 close되도록 설정
def get_db():
    db = SessionLocal()
    try:
        yield db
    finally:
        db.close()

# GET 요청 시 전체 사용자 목록 반환
@app.get("/users/")
def read_users(db: Session = Depends(get_db)):  # Depends(get_db)로 DB 세션 주입
    return db.query(User).all()  # Session을 통해 User 테이블의 모든 데이터 조회

 

간단한 모델이라면 이 정도만 해도 CRUD 작업은 무리 없이 돌아간다.


3. SQLite의 내부 구조

SQLite는 앱 내부에서 같이 돌아가는 내장형 DB다.
데이터는 B-트리 구조로 정리되고, 기본적으로 롤백 저널 방식으로 트랜잭션을 보장한다.

좀 더 성능이 필요한 경우엔 WAL (Write-Ahead Logging) 모드로 설정하면

읽기/쓰기를 병렬로 처리할 수 있어서 성능 개선도 가능하다.

추가 설명을 하자면,

 

🧱 B-트리 구조란?

파일 캐비닛처럼 정리된 데이터 저장 방식이라고 보면 된다.

데이터를 찾을 때 무작정 처음부터 끝까지 뒤지지 않고,
중간 기준을 잡고 이분 탐색하듯 빠르게 원하는 데이터를 찾아가는 구조다.
이 덕분에 데이터가 많아져도 검색 속도가 느려지지 않고, 정렬된 상태로 저장되기 때문에 삽입/삭제도 효율적으로 처리된다.

 

🔄 롤백 저널 방식이란?

작업 중 문제가 생겼을 때 이전 상태로 되돌릴 수 있게 해주는 보호막이다.

SQLite는 기본적으로 데이터를 바꾸기 전에, 변경 전 내용을 "저널 파일"에 따로 저장해둔다.
작업 도중 오류가 나면 이 저널 파일을 이용해 변경 전 상태로 되돌리는(rollback) 게 가능하다.
이런 방식 덕분에 전원이 꺼지거나 에러가 나도 데이터가 손상되지 않는다.
단점은 이 과정이 다소 무겁고, 특히 동시 다중 작업에서는 속도가 느려질 수 있다.

 

🚀 WAL (Write-Ahead Logging) 모드란?

좀 더 성능을 높이기 위한 대체 트랜잭션 처리 방식이다.

롤백 저널은 매번 원본 파일을 수정하기 때문에 속도에 한계가 있다.
WAL 모드는 데이터를 바로 DB에 쓰는 게 아니라, 먼저 로그 파일에 임시로 기록해두고,
나중에 원본 DB에 한꺼번에 반영하는 방식이다.

이렇게 하면:

  • 읽기 작업은 계속 가능하고,
  • 쓰기 작업은 한쪽에서 따로 처리되므로,
    → 읽기와 쓰기를 동시에 처리할 수 있어 성능이 개선된다.

FastAPI처럼 비동기 요청이 많은 웹 API 환경에서도 WAL 모드는 꽤 유용할 수 있다.


4. SQLite vs 다른 DB 간단 비교

항목 SQLite PostgreSQL / MySQL
설치 필요 없음 (파일 기반) 서버 설치 필요
성능 소규모 앱엔 빠름 대규모 트래픽에도 견고
설정 거의 없음 세밀한 설정 가능
동시성 약함 (단일 프로세스에 적합) 강함 (멀티 클라이언트 지원)
트랜잭션 기본 ACID 보장 고급 기능 포함

5. 결론

SQLite는 작고 빠르고, 무엇보다 쉽다.
FastAPI 프로젝트에서도 초기에 붙이기 좋은 DB 중 하나라고 생각한다.

나중에 서비스가 커지면 PostgreSQL 같은 DB로 자연스럽게 옮겨갈 수 있으니,
처음부터 너무 무거운 설정을 고민하기보단, 지금 필요한 수준에 맞춰 가볍게 시작해보는 것도 나쁘지 않다고 생각했다.