3단계
3단계 — PostgreSQL 연결
30 분
3단계 — PostgreSQL 연결
API 가 데이터를 기억 하려면 DB 가 필요해요. 가장 합의된 선택이 PostgreSQL.
연결 풀 — 매번 새 연결은 비싸다
DB 연결 한 번에 수십 ms. 매 요청마다 새로 만들면 느려요. 풀(pool) 이 미리 N개 연결을 만들어 두고 빌려 줍니다.
# db/connection.py
import os
from psycopg2 import pool
_pg_pool: pool.SimpleConnectionPool | None = None
def get_pool() -> pool.SimpleConnectionPool:
global _pg_pool
if _pg_pool is None:
_pg_pool = pool.SimpleConnectionPool(
minconn=1,
maxconn=10,
host=os.getenv("DB_HOST", "localhost"),
port=int(os.getenv("DB_PORT", "5432")),
database=os.getenv("DB_NAME", "mydb"),
user=os.getenv("DB_USER", "user"),
password=os.getenv("DB_PASSWORD", "secret"),
)
return _pg_pool
컨텍스트 매니저로 안전하게
# db/connection.py
from contextlib import contextmanager
@contextmanager
def get_conn():
p = get_pool()
conn = p.getconn()
try:
yield conn
conn.commit()
except Exception:
conn.rollback()
raise
finally:
p.putconn(conn)
with get_conn() as conn: 로 쓰면 예외 시 자동 rollback, 성공 시 commit, 항상 풀 반환.
첫 쿼리
# services/post_service.py
from db.connection import get_conn
def find_recent(limit: int = 20):
with get_conn() as conn, conn.cursor() as cur:
cur.execute(
"SELECT id, title, body, created_at FROM posts ORDER BY created_at DESC LIMIT %s",
(limit,),
)
return [
{"id": r[0], "title": r[1], "body": r[2], "created_at": r[3]}
for r in cur.fetchall()
]
%s 자리표시자 — 절대 f-string 으로 SQL 만들지 마세요 (SQL Injection 위험).
.env 로 비밀 관리
DB_HOST=localhost
DB_PORT=5432
DB_NAME=mydb
DB_USER=user
DB_PASSWORD=secret
from dotenv import load_dotenv
load_dotenv() # .env 자동 로드
.env 는 gitignore — 운영 비밀번호가 git 에 들어가면 큰일.
직접 해 보기
docker run -d -p 5432:5432 -e POSTGRES_PASSWORD=secret postgres:17 로 로컬 DB 를 띄우고 posts 테이블 (id BIGSERIAL, title TEXT, body TEXT, created_at TIMESTAMPTZ) 을 만든 뒤 위 find_recent 를 호출해 보세요.
더 깊이
다음 단계
4단계에서는 정해진 시간에 자동으로 도는 작업 (스케줄러) 을 배워요.