# connection.py
def get_db():
# 세션 팩토리를 이용해서 세션 객체를 만들어준다.
session = SessionFactory()
try:
yield session
finally:
session.close()
- generator를 만들어준다.
- FastAPI를 사용해서 데이버베이스에 접근할 수 있게 해준다.
- request가 들어왔을 때 session이 생성이 돼서 yield 문으로 return이 된 후에 사용이 되다가 response 한 이후에 session을 close() 해서 삭제하는 식으로 FastAPI가 session을 관리해주게 된다.
# repository.py
from typing import List
from sqlalchemy import select
from sqlalchemy.orm import Session
from database.orm import ToDo
def get_todos(session: Session) -> List[ToDo]:
return list(session.scalars(select(ToDo)))
- repository 패턴
- 함수를 통해서 데이터베이스를 조회하는 부분을 생성하고 그 함수를 main.py에서 사용
- ToDo를 전체 select(전체 조회)해서 return
-> List[ToDo] 는 반환 타입 힌트
한 문장으로 표현하면:
"이 함수는 실행하고 나면 이런 타입의 값을 돌려줄 거야" 라고 알려주는 문법
기본 문법 구조
반환 타입 힌트 구조
def 함수이름(파라미터) -> 반환타입:
# ↑
# 이 화살표가 "반환값은 이거야" 라는 표시
파라미터 타입 힌트랑 비교해보기
타입 힌트 위치 비교
def get_todos(session: Session) -> List[ToDo]:
# ↑ ↑
# 파라미터 타입 힌트 반환값 타입 힌트
# "session은 Session "이 함수는 List[ToDo]를
# 타입으로 받을 거야" 돌려줄 거야"
str | None = None 이랑 같은 개념
파라미터에 타입 힌트를 붙이듯, 반환값에도 타입 힌트를 붙이는 것
List[ToDo] 가 뭔지
List[ToDo] 분해
List[ToDo]
↑ ↑
│ └── 리스트 안에 들어있는 요소 타입 (ToDo 객체)
└─────── 리스트 타입
타입 힌트 예시 비교
| 타입 힌트 | 의미 |
| -> str | 문자열 하나 반환 |
| -> int | 정수 하나 반환 |
| -> List[str] | 문자열들의 리스트 반환 |
| -> List[ToDo] | ToDo 객체들의 리스트 반환 |
| -> None | 반환값 없음 |
실제로 어떤 값이 나오냐면
반환값 시각화
def get_todos(session: Session) -> List[ToDo]:
return list(session.scalars(select(ToDo)))
# 실제로 이런 값이 반환됨
[
ToDo(id=1, contents="FastAPI 섹션 0 수강", is_done=True),
ToDo(id=2, contents="FastAPI 섹션 1 수강", is_done=False),
ToDo(id=3, contents="FastAPI 섹션 2 수강", is_done=False),
]
# ↑ ToDo 객체들이 담긴 List!
타입 힌트를 안 써도 동작은 한다
타입 힌트 유무 비교
# 타입 힌트 없는 버전 (동작은 같음)
def get_todos(session):
return list(session.scalars(select(ToDo)))
# 타입 힌트 있는 버전
def get_todos(session: Session) -> List[ToDo]:
return list(session.scalars(select(ToDo)))
그럼 왜 쓰냐면:
타입 힌트를 쓰는 이유
| 이유 | 설명 |
| 가독성 | 코드만 봐도 뭘 받고 뭘 반환하는지 바로 알 수 있음 |
| IDE 자동완성 | PyCharm이 타입을 알고 있어서 자동완성 도와줌 |
| 에러 조기 발견 | 잘못된 타입 쓰면 실행 전에 경고 띄워줌 |
| FastAPI 필수 | FastAPI는 타입 힌트 보고 자동으로 검증/직렬화 처리 |
전체 함수 한눈에 읽기
함수 전체 의미 시각화
def get_todos(session: Session) -> List[ToDo]:
↑ ↑ ↑ ↑
함수이름 파라미터 Session ToDo 객체들의
타입으로 리스트를
받아 반환
💡 핵심 요약:
-> = "이 함수가 반환하는 값의 타입은"
List[ToDo] = "ToDo 객체들이 담긴 리스트"
타입 힌트는 안 써도 동작하지만, 쓰면 코드 이해와 자동완성에 훨씬 도움이 돼요.
# main.py
from typing import List
from database.connection import get_db
from database.orm import ToDo
from database.repository import get_todos
from fastapi import FastAPI, Body, HTTPException, Depends
from sqlalchemy.orm import Session
# status_code만 적어주면 =200을 안 적어도 자동으로 200으로 매핑된다.
@app.get("/todos", status_code=200)
def get_todos_handler(
order: str | None = None,
session: Session = Depends(get_db),
):
# 타입 힌트
todos: List[ToDo] = get_todos(session=session)
if order and order == "DESC":
return todos[::-1]
return todos
swagger ui에서 확인해보기

제너레이터란?
한 문장으로 표현하면:
"중간에 잠깐 멈췄다가, 나중에 다시 이어서 실행할 수 있는 함수"
일반 함수 vs 제너레이터 함수 비교
일반 함수
def normal_func():
print("시작")
return "결과" # ← 여기서 완전히 끝남, 다시 돌아올 수 없음
print("이건 실행 안 됨")
제너레이터 함수
def generator_func():
print("시작")
yield "결과" # ← 여기서 잠깐 멈추고 값을 넘겨줌
print("나중에 다시 여기서 이어서 실행됨")
yield가 핵심. return은 끝내는 거고, yield는 잠깐 멈추는 것
제너레이터 동작 흐름
yield 동작 시각화
generator_func() 호출
↓
"시작" 출력
↓
yield "결과" →→→→→→→ 호출한 쪽이 "결과"를 받음
⏸ 멈춤! (여기서 다른 작업 가능)
↓ ↓
(나중에 next() 호출) ←←←←←← 작업 끝
↓
"나중에 다시 여기서 이어서 실행됨" 출력
↓
함수 완전 종료
직접 눈으로 확인해보기
제너레이터 직접 실행
def generator_func():
print("1. 시작")
yield "잠깐 멈춤"
print("3. 다시 실행")
gen = generator_func() # 아직 실행 안 됨!
print("next() 호출 전")
value = next(gen) # 여기서 실행 시작 → yield에서 멈춤
print(f"2. yield가 넘겨준 값: {value}")
next(gen) # yield 이후부터 다시 실행
# 출력 순서:
# next() 호출 전
# 1. 시작
# 2. yield가 넘겨준 값: 잠깐 멈춤
# 3. 다시 실행
next()가 뭔지는 밑에서 다시 다뤄보자.
이제 get_db()를 이해해보기
get_db 제너레이터
def get_db():
session = SessionFactory() # 1. DB 세션 생성
try:
yield session # 2. 세션을 넘겨주고 잠깐 멈춤
finally:
session.close() # 3. API 처리 끝나면 여기서 세션 닫음
FastAPI에서 get_db 동작 흐름
GET /todos 요청 들어옴
↓
get_db() 실행 시작
↓
session = SessionFactory() → DB 연결 생성
↓
yield session →→→→→→→→→→ get_todos_handler에게 session 전달
⏸ 멈춤! ↓
todos 조회 로직 실행
↓
응답 반환
↓ ↓
finally 실행 ←←←←←←←←←← 요청 처리 완료
↓
session.close() → DB 연결 닫음
Depends(get_db)가 하는 역할
Depends 동작
@app.get("/todos")
def get_todos_handler(
session: Session = Depends(get_db),
# ↑
# "get_db 실행해서 yield한 값을 session에 넣어줘"
):
todos = get_todos(session=session) # session 바로 사용 가능
FastAPI가 Depends를 보고 자동으로 해줘요:
Depends가 자동으로 처리하는 것
| 순서 | 동작 |
| 요청 시작 | get_db() 실행 → session 생성 |
| 핸들러 실행 | session을 파라미터로 주입 |
| 요청 완료 | finally 블록 실행 → session.close() |
Depends도 밑에서 더 알아보자
왜 일반 함수 대신 제너레이터를 쓰냐면
만약 일반 함수로 했다면?
# 일반 함수로 하면 세션을 닫을 타이밍을 알 수 없음
def get_db():
session = SessionFactory()
return session
# 언제 session.close() 해야 하지...?
# 제너레이터로 하면 요청 전후를 하나의 함수로 관리
def get_db():
session = SessionFactory()
try:
yield session # 요청 처리 중
finally:
session.close() # 요청 끝나면 자동 정리
💡 핵심 요약:
yield 앞 = 요청 전 준비 (세션 생성)
yield = 핸들러에게 세션 전달
yield 뒤 = 요청 후 정리 (세션 닫기)
이 3단계를 하나의 함수로 깔끔하게 관리할 수 있어서 제너레이터를 써요.
next()란?
next()는 Python 내장 함수
"제너레이터한테 다음 단계로 진행해" 라고 명령하는 함수
제너레이터는 기본적으로 가만히 있는다
제너레이터는 호출해도 바로 실행 안 됨
def generator_func():
print("1. 시작")
yield "잠깐 멈춤"
print("3. 다시 실행")
gen = generator_func() # 실행 X, 그냥 제너레이터 객체만 생성
print(gen) # <generator object generator_func at 0x...>
제너레이터는 누군가 "실행해" 라고 명령해야 움직인다.
그 명령이 바로 next()
next() 호출할 때마다 어떻게 되는지
next() 단계별 동작
gen = generator_func()
# 1번째 next() → yield까지 실행하고 멈춤
value = next(gen)
# "1. 시작" 출력
# yield에서 멈추고 "잠깐 멈춤" 반환
# 2번째 next() → yield 이후부터 끝까지 실행
next(gen)
# "3. 다시 실행" 출력
# 함수 끝
# 3번째 next() → 더 이상 실행할 게 없음
next(gen)
# StopIteration 에러 발생!
시각화로 보기
next() 호출 흐름
gen = generator_func()
│
│ next(gen) 1번째 호출
▼
print("1. 시작") ← 실행
yield "잠깐 멈춤" ← 여기서 멈추고 "잠깐 멈춤" 반환
⏸
│ next(gen) 2번째 호출
▼
print("3. 다시 실행") ← 실행
함수 끝 ← StopIteration
next()가 내장 함수인 이유
Python 주요 내장 함수들
| 함수 | 역할 |
| print() | 출력 |
| len() | 길이 반환 |
| range() | 숫자 범위 생성 |
| next() | 이터레이터/제너레이터 다음 값 꺼냄 |
| list() | 리스트로 변환 |
next()는 import 없이 바로 쓸 수 있어요.
사실 for문이 next()를 자동으로 호출
for문 = next() 자동 반복
def countdown():
yield 3
yield 2
yield 1
# 직접 next() 호출
gen = countdown()
next(gen) # 3
next(gen) # 2
next(gen) # 1
# for문이 내부적으로 next()를 자동 호출
for num in countdown():
print(num)
# 3
# 2
# 1
💡 핵심 요약:
next() = 제너레이터한테 "다음 yield까지 실행해" 명령
for문 = next()를 자동으로 반복 호출하는 것
get_db()에서는 FastAPI가 내부적으로 next()를 대신 호출.
Depends란?
한 문장으로 표현하면:
"이 함수를 실행하기 전에, 먼저 저 함수를 실행해서 결과를 가져다줘" 라고 FastAPI한테 시키는 도구
왜 필요한지 - 문제부터 이해하기
Depends 없이 쓴다면?
@app.get("/todos")
def get_todos_handler(order: str | None = None):
# 매번 여기서 직접 세션을 만들고
session = SessionFactory()
try:
todos = get_todos(session=session)
return todos
finally:
session.close() # 매번 여기서 직접 닫고
@app.post("/todos")
def create_todo_handler():
# 여기서도 또 똑같이 반복...
session = SessionFactory()
try:
...
finally:
session.close() # 또 반복...
@app.delete("/todos/{todo_id}")
def delete_todo_handler():
# 또 반복...
session = SessionFactory()
try:
...
finally:
session.close() # 또또 반복...
모든 핸들러마다 똑같은 코드가 반복
Depends가 이걸 어떻게 해결하냐면
Depends 사용 후
# 세션 관련 코드는 여기 한 곳에만!
def get_db():
session = SessionFactory()
try:
yield session
finally:
session.close()
# 각 핸들러는 그냥 받아서 쓰기만 하면 됨
@app.get("/todos")
def get_todos_handler(session: Session = Depends(get_db)):
return get_todos(session=session)
@app.post("/todos")
def create_todo_handler(session: Session = Depends(get_db)):
... # session 그냥 쓰면 됨
@app.delete("/todos/{todo_id}")
def delete_todo_handler(session: Session = Depends(get_db)):
... # session 그냥 쓰면 됨
Depends 동작 흐름
Depends 동작 시각화
GET /todos 요청
↓
FastAPI가 get_todos_handler 실행 전에
Depends(get_db) 발견!
↓
"get_db 먼저 실행해야겠다"
↓
get_db() 실행 → session 생성 → yield session
↓
session을 get_todos_handler에 자동으로 주입
↓
get_todos_handler 실행 (session 사용)
↓
응답 반환
↓
get_db()의 finally 실행 → session.close()
자연어로 읽어보기
코드를 자연어로 해석
def get_todos_handler(
order: str | None = None, # 쿼리스트링에서 order 받아와
session: Session = Depends(get_db),
# ↑ ↑
# 이 타입으로 get_db 실행한 결과를
# 받을 거야 여기에 넣어줘
):
Depends는 세션 말고도 다양하게 쓰인다
Depends 활용 예시들
# 1. 로그인 체크
def get_current_user(token: str):
# 토큰 검증 로직
return user
@app.get("/my-profile")
def my_profile(user = Depends(get_current_user)):
return user # 로그인한 유저 정보 바로 사용
# 2. 권한 체크
def check_admin(user = Depends(get_current_user)):
if not user.is_admin:
raise HTTPException(403)
return user
@app.delete("/users/{user_id}")
def delete_user(admin = Depends(check_admin)):
... # 관리자만 여기 들어올 수 있음
핵심 정리
Depends 한눈에 정리
| 항목 | 설명 |
| 역할 | 핸들러 실행 전에 먼저 특정 함수 실행 |
| 장점 | 반복 코드 제거, 재사용 가능 |
| 주입 타이밍 | 핸들러 실행 직전 자동으로 주입 |
| 주로 쓰는 곳 | DB 세션, 로그인 체크, 권한 체크 |
💡 핵심 요약:
Depends(get_db) = "get_db 먼저 실행해서 결과를 session에 넣어줘"
FastAPI가 알아서 실행 순서를 관리해주기 때문에
개발자는 그냥 session 받아서 쓰기만 하면 된다.
'FastAPI > 데이터베이스' 카테고리의 다른 글
| ORM 적용 - GET 단일 조회 API (0) | 2026.06.16 |
|---|---|
| ORM 적용 - HTTP Response 처리 (1) | 2026.06.14 |
| ORM 모델링 (1) | 2026.06.09 |
| 데이터베이스 연결 (0) | 2026.06.04 |
| DDL - Create Table (0) | 2026.06.04 |