[FastAPI] 9. Persistence Layer ๊ตฌ๊ฐ์ ๋น๋๊ธฐ ์ฒ๋ฆฌ ํ๋ ๋ฐฉ๋ฒ
์ฒซ ํฌ์คํธ์์ ์ฐ๋ฆฌ๋ FastAPI๊ฐ ASGI ๊ธฐ๋ฐ์ uvicorn์ ์ด์ฉํ์ฌ uvloop์ ๊ธฐ๋ฐํ ๋น๋๊ธฐ ์ฒ๋ฆฌ๋ก API ์์ฒญ๊ณผ ์๋ต์ ๋น๋๊ธฐ๋ก ์ฒ๋ฆฌํ ์ ์๋ค๋ ์ด์ผ๊ธฐ๋ฅผ ํ์์ต๋๋ค. ํ์ง๋ง ๊ณต๊ต๋กญ๊ฒ๋ Database Connection์์ ์ด๋ฌํ ๊ธฐ๋ฅ์ ์ง์ํด์ฃผ์ง ์์ API ์์ฒญ ๋จ์๋ ๋น๋๊ธฐ ์ฒ๋ฆฌ๊ฐ ๊ฐ๋ฅํ์ฌ๋ DB์ ์ก์ธ์ค ํ๋ ๊ตฌ๊ฐ์ ๋น๋๊ธฐ ์ฒ๋ฆฌ๊ฐ ๋์ง ์๊ธฐ ๋๋ฌธ์ Blocking์ด ๋ฐ์ํ๊ณ , ๋ค์ ์์ฒญ์ด ๊ณ์ ๋๊ธฐ ๋๋ ๋ฌธ์ ์ ์ ๊ฐ์ง๊ณ ์์์ต๋๋ค.
Python DB API
๊ทธ๋ ๋ค๋ฉด ํ์ด์ฌ์ ์ด๋ค์์ผ๋ก Database์ ์ฐ๊ฒฐํ ์ ์๋ ๊ฒ์ผ๊น์? Java์ ๊ฒฝ์ฐ๋ JDBC๋ผ๋ Database Connector๋ผ๋ ๊ฒ์ด ์กด์ฌํ์ฌ ์ ํ๋ฆฌ์ผ์ด์ ๊ณผ Database ์ฌ์ด๋ฅผ ์ฐ๊ฒฐํด์ฃผ๋ ๊ฒ ๊ฐ๋ฅํ๋ฐ์.
Python์๋ ์ด์ ๋น์ทํ ๊ฒ์ผ๋ก DB API๋ผ๋ ๊ฒ์ด ์กด์ฌํฉ๋๋ค.
ํ์ด์ฌ ๋ฌธ์ PEP 249์๋ Python DB API 2.0์ด ๋ด๊ฒจ์ ธ ์์ต๋๋ค. ํ์ด์ฌ์์ DB์ ์ก์ธ์คํ ์ ์๋ ํ์ค ์ธํฐํ์ด์ค๋ก ์ฐ๋ฆฌ๊ฐ ์ด์ ์ ๋ค๋ค๋ FastAPI CRUD ๋ง๋ค๊ธฐ์์๋ ์ด๋ฌํ ํ์ค API ๊ธฐ๋ฐ์ ๋ชจ๋์ ์ฌ์ฉํ์ฌ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ก์ธ์ค ํ๋ ๊ฒ์ ๋๋ค.
์ข ๋ ์ดํดํ๊ธฐ ์ฝ๊ฒ ๊ทธ๋ฆผ์ผ๋ก ๋ณด์ฌ๋๋ฆฌ์๋ฉด, asyncpg๋ผ๋ PostgreSQL์ ์ํ DB ๋ผ์ด๋ธ๋ฌ๋ฆฌ๊ฐ ์์ต๋๋ค. ์ด ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ Python DB API๋ฅผ ๊ธฐ๋ณธ ๊ณจ๊ฒฉ์ผ๋ก ํ์ฌ PostgreSQL์ ๋ง์ถฐ์ง ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ด๋ฉฐ ์ปค๋ฅ์ ์ ๋ง๋ค๊ธฐ ์ํด Connection ๊ฐ์ฒด๋ฅผ ์ฌ์ฉํ๊ณ , Query๋ฅผ ์์ฑํ๊ธฐ ์ํด Cursor ๊ฐ์ฒด๋ฅผ ๋ง๋ค๊ณ , connection ์์ execute ๋ฉ์๋๋ฅผ ์ด์ฉํ์ฌ Query๋ฅผ ์คํํ ์ ์๋๋ก ๋์ด ์์ต๋๋ค.
์ด๋ฌํ ๊ตฌ์กฐ๋ asyncpg ๋ฟ๋ง ์๋๋ผ Python์์ ์ง์ํ๋ ๋๋ถ๋ถ์ DB ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ค์ด ์ด๋ ๊ฒ ๊ตฌํ์ด ๋์ด์ง๊ณ ์๋ ๊ฒ์ ๋๋ค. ์ด๊ฒ์ด ๋ฐ๋ก ํ์ค Python DB API ์ ๋๋ค.
(Old) Databases + asyncpg + SQLAlchemy 1.3
asyncpg๋ PosgreSQL์ฉ ๋น๋๊ธฐ DB ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ก FastAPI์์๋ Databases์ asyncpg ๋ชจ๋์ ์กฐํฉํ์ฌ SQLAlchemy Core์ ๊ธฐ๋ฐํ ๋ฌธ๋ฒ์ ํตํด ๋น๋๊ธฐ ํธ๋์ญ์ ์ ์งํํ ์ ์๋ ๋ฐฉ๋ฒ์ด ์์ต๋๋ค.
asyncpg๋ ๊ธฐ๋ณธ์ ์ผ๋ก Python์ asyncio๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ๋์ํ๋ฉฐ Python 3.5 ๋ฒ์ ๋ถํฐ ๋์ํ๊ณ , PostgreSQL 9.2 ~ 12 ๋ฒ์ ๊น์ง๋ฅผ ์ง์ํฉ๋๋ค.
$ poetry add databases[asyncpg]
Databases ๋ํ๋์๋ฅผ ์ฌ์ฉํ ๋๋ ํจํค์ง ์ข ๋ฅ์ ์์ ์ด ์ฌ์ฉํ Database ์ด๋ฆ์ ๋ฃ๊ณ ๋ํ๋์์ ์ถ๊ฐํด์ผ ํฉ๋๋ค. ์ด๋ ๊ฒ ๋ํ๋์๋ฅผ ์ถ๊ฐํ๊ฒ ๋๋ฉด SQLAlchemy 1.3 ๋ฒ์ ์ ๋ํ๋์๊ฐ ๊ฐ์ด ์ถ๊ฐ๋ ๊ฒ์ ๋๋ค.
๊ณต์์ ์ผ๋ก SQLAlchemy์ 1.3 ๋ฒ์ ์์๋ asyncpg dialect๋ฅผ ์ง์ํ์ง ์์ต๋๋ค. ๋ฐ๋ผ์ FastAPI๋ Sanic ๊ฐ์ ๋น๋๊ธฐ ํ๋ ์์ํฌ์์ Persistence Layer์ ๋น๋๊ธฐ ์ฒ๋ฆฌ๋ฅผ ๋ฃ๊ธฐ ์ํด์๋ asyncpgsa๋ ์ฐ๋ฆฌ๊ฐ ํ์ฌ ๋ค๋ฃจ๊ณ ์๋ databases์ ๊ฐ์ ํ์ฌ์ ์ด๋ํฐ์ ๊ฐ์ด ์ฌ์ฉํด์ผ ํฉ๋๋ค.
Databases ์ด๋ํฐ์ ์ฐ๊ฒฐํ๊ณ ์ ํ๋ Database์ URI๋ฅผ ๋ฃ๊ณ , ๋๋ถ์ด SQLAlchemy์ create_engine์๋ ๊ฐ์ด URI๋ฅผ ๋ฃ์ด์ค๋๋ค.
์ด๋ ๊ฒ 2๊ฐ์ง ์ ๋ถ์ URI๋ฅผ ๋ฃ์ด์ฃผ๋ ์ด์ ๋ Databases ์ด๋ํฐ์์ ์ค์ ํธ๋์ญ์ ์ ๋งก๊ณ , SQLAlchemy์ Core๋ฅผ ์ด์ฉํ์ฌ Query๋ฅผ ์์ฑํด์ฃผ๊ธฐ ์ํจ์ ๋๋ค. ๋ฐ๋ผ์ ์ด๋ฌํ ๋น๋๊ธฐ ์ฒ๋ฆฌ๋ ์๋์ ๊ฐ์ด ๋์ํฉ๋๋ค.
SQLAlchemy์์ DB ์๋ฒ์ ์ ์ํ์ฌ ํ๋ ์ผ์ ํ ์ด๋ธ์ ๋ฉํ๋ฐ์ดํฐ๋ฅผ ์ ์ํ๋ ๊ฒ์ ๋๋ค. ์ฌ๊ธฐ์ ๋ฉํ๋ฐ์ดํฐ๋ ํ ์ด๋ธ์ ์ฌ์ฉํ๊ธฐ ์ํ DDL ์ ์๋ฅผ ์ด์ผ๊ธฐ ํ๋ฉฐ ์ฐจํ ์ ์ํ ๋ฉํ๋ฐ์ดํฐ๋ฅผ DB ์๋ฒ์ ๋ณด๋ด์ด DDL์ ์ํํด ํ ์ด๋ธ์ ์์ฑํ๋ ์ญํ ์ ํด์ค๋๋ค.
์ฌ๊ธฐ์ QueryDSL๋ฐฉ์์ SQLAlchemy Core๋ฅผ ์ฌ์ฉํ์ฌ DB ์ฟผ๋ฆฌ๋ฅผ ์ง์ํ๊ณ , ์ด ์ฟผ๋ฆฌ๋ฅผ Databases ๋ชจ๋์ fetch_all์ด๋ execute ์ ๊ฐ์ Python DB API ํจ์์ ๋ฃ์ด์ฃผ๋ฉด ํธ๋์ญ์ ์ด ๋์ํ๋ฉฐ ์ ํ๋ฆฌ์ผ์ด์ ์ด ๋ฐ์์น๋ ํ์์ ๋๋ค.
์ด๋ ๊ฒ ๋ณต์กํ ํ๋ก์์ ๋ฅผ ๊ฑฐ์น๋ ์ด์ ๋ SQLAlchemy๊ฐ ์์ฒด์ ์ผ๋ก asyncpg๋ aiopg์ ๊ฐ์ ๋น๋๊ธฐ ํธ๋์ญ์ ๋ชจ๋์ ์ง์ํ์ง ์๋ ์ ์ ์๊ณ , ์ด๋ฅผ ์ง์ํด์ฃผ๋ ๋ณ๋์ ๋ชจ๋์ ์ฌ์ฉํ์ฌ ์ด์ฉํ๋ ๋ฐฉ๋ฒ์ธ๋ฐ, ๋ง์ฝ ์์ ์ด ์๋์ผ๋ก Query๋ฅผ ์์ฑํ์ฌ ์ฌ์ฉํ๊ณ ์ ํ๋ ๊ฒฝ์ฐ์๋ SQLAlchemy Core ์์ด๋ Databases ๋ชจ๋๋ก๋ง์ผ๋ก๋ ๋น๋๊ธฐ ์ฒ๋ฆฌ๋ฅผ ์ถฉ๋ถํ ์งํํ ์ ์์ต๋๋ค.
(New) SQLAlchemy 1.4.x + asyncpg
์ง๋ 3์ 29์ผ๋ถํฐ SQLAlchemy 1.4.x ๋ฒ์ ์ด ์ ์ ๋ฒ์ ์ผ๋ก ์ถ์๋๋ฉด์ asyncpg๋ฅผ ์ด์ SQLAlchemy์์๋ ์ฌ์ฉํ ์ ์๊ฒ ๋์์ต๋๋ค. ๋ฐ๋ผ์ ์์ ๋ฐฉ๋ฒ์ฒ๋ผ ๋น๋๊ธฐ ์ฒ๋ฆฌ๋ฅผ ์ํด ๋ณ๋์ ์ด๋ํฐ๋ฅผ ์ฌ์ฉํ์ง ์์๋ SQLAlchemy์ Connection Pool๋ง ์๋ค๋ฉด ๋น๋๊ธฐ ์ฒ๋ฆฌ๊ฐ ๊ฐ๋ฅํ ๊ฒ์ด์ฃ .
SQLAlchemy์์ asyncpg๋ฅผ ์ง์ํ๊ธฐ ๋๋ฌธ์ ๋ณ๋๋ก ์ ํ๋ฆฌ์ผ์ด์ ์์ asyncpg ๋ด ํจ์๋ ๊ฐ์ฒด๋ฅผ ์ ์ธํ ํ์ ์์ด ๋ฐ๋ก SQLALchemy์ create_async_engine ํจ์๋ฅผ ์ฌ์ฉํ์ฌ ์ปค๋ฅ์ ์ ๋ง๋ค๊ณ , DB ์ฒ๋ฆฌ ํจ์ ๋ํ Alchemy์์ ์ ๊ณตํ๊ธฐ ๋๋ฌธ์ ์ด๋ฅผ ์ฌ์ฉํด์ ์งํํ ์ ์์ต๋๋ค.
๊ธฐ์กด์ด๋ ๋ค๋ฅธ์ ์ create_engine์ด ์๋ create_async_engine์ ์ฌ์ฉํ๋ค๋ ์ ์ด๊ณ , PostgreSQL์ ์ฌ์ฉํ์ ๋ค๋ฉด ์ฐ๊ฒฐ ํ์์ ๊ตฌ๋ถํ๊ธฐ ์ํด postgresql ๋ค์ + asyncpg๋ฅผ ๋ถ์ธ๋ค๋ ์ ์ด ์์ต๋๋ค. ๋ง์ง๋ง์ผ๋ก Session ๋ํ AsyncSession์ด ๋ณ๋๋ก ์กด์ฌํ์ฌ ORM์ ์ด์ฉํ DB ์ฒ๋ฆฌ์ ๋น๋๊ธฐ ์ฒ๋ฆฌ๋ฅผ ํ์ธต ๊ฐํ์ํจ ์ ์ด ๋์ ๋ณด์ ๋๋ค.
ORM์ผ๋ก ์ ์ํ ํด๋์ค๋ฅผ DDL ์ฒ๋ฆฌํ ๋์๋ ๋๊ธฐ ์ฒ๋ฆฌ๋ก ์งํํด์ผ ํ๋ ์ ์ด ๋์ ๋ณด์ด๋๋ฐ์. ์ผ๋ฐ์ ์ผ๋ก ์ฌ์ฉํ๋ create_all ํจ์๋ฅผ ์ฌ์ฉํ๋ฉด ๋๊ธฐ ํจ์์ด๊ธฐ ๋๋ฌธ์ asyncio์ ํธํ๋์ง ์์ผ๋ฏ๋ก run_sync ํจ์๋ฅผ ์ด์ฉํ์ฌ asyncio์ ํธํ๋๋ ๋๊ธฐ ๋ชจ๋ธ๋ก ์คํํ๋๋ก ์ ๋ํด์ค์ผ ํฉ๋๋ค.
DB์์ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ค๋ ๋ฐฉ๋ฒ๋ ์กฐ๊ธ์ ๋ค๋ฆ ๋๋ค. ๊ธฐ์กด์๋ sqlalchemy ์์์ ์๋ select ํจ์๋ฅผ ๊ฐ์ง๊ณ ์ฟผ๋ฆฌ๋ฅผ ์ง์ํ๋ ๋ฐฉ๋ฒ์ด๋ ORM session์์ query ํจ์๋ฅผ ์ด์ฉํด ์ฟผ๋ฆฌ๋ฅผ ์ง์ํ๋ ๋ฐฉ๋ฒ์ด ์์์ต๋๋ค. ํ์ง๋ง ์ด๋ค ๋ชจ๋ ๋๊ธฐ ๋ฐฉ์์ผ๋ก ๋น๋๊ธฐ๋ก ์ฒ๋ฆฌํ๊ธฐ ์ํด์๋ ์ด ๋ฐฉ๋ฒ์ ์ฌ์ฉํ ์ ์์ต๋๋ค.
SQLAlchemy 1.4 ๋ฒ์ ์์ ์๋ก์ด ์ถ๊ฐ๋ sqlalchemy.ext.asyncio.select ํจ์๋ฅผ ์ฌ์ฉํ์ฌ select ์ฟผ๋ฆฌ๋ฅผ ์์ฑํ๋๋ก ํ๊ณ , ์ด๋ฅผ ์ด์ฉํด ์ฟผ๋ฆฌ๋ฅผ ์์ฑ์ด ๋๋ฌ์ผ๋ฉด AsyncSession์ execute ๋น๋๊ธฐ ๋ฉ์๋๋ฅผ ์ด์ฉํ์ฌ ์ฟผ๋ฆฌ ์คํํ ๊ฒฐ๊ณผ๋ฅผ ๊ฐ์ ธ์ค๋๋ก ํ๋ฉด ๊น๋ํ๊ฒ ๋น๋๊ธฐ ์ฒ๋ฆฌ๊ฐ ์งํ๋ฉ๋๋ค.
์ด ๋ค์ ์ฌ์ฉํ scalars์ ๊ฒฝ์ฐ๋ ๋ชจ๋ธ์ ๋ฉํ๋ฐ์ดํฐ๋ฅผ ๊ฑท๊ณ ์์ ๋ฐ์ดํฐ๋ง์ ๊ฐ์ ธ์ค๊ฒ ๋ค๋ ํจ์์ด๋ฉฐ, fetchall์ ํตํด ๊ฐ์ ธ์ค๋ฉด ๊ธฐ์กด์ ์ฌ์ฉํ๋ ์ฝ๋๋ฅผ ๊ทธ๋๋ก ๋ฆฌํฉํ ๋ง ํ ์ ์์ต๋๋ค.
ํน์ SQLAlchemy์์ select, where ๋ฑ์ ๋นORM ํจ์๋ฅผ ์ด์ฉํ์ฌ ์ฌ์ฉํ์ ๋ถ๋ค์ด๋ผ๋ฉด session.refresh ํจ์๋ฅผ ์ฌ์ฉํด๋ณด์ จ๊ฒ ์ง๋ง ORM ํจ์์์๋ refresh ํจ์๊ฐ ์ฌ์ค์ ๊ฑฐ์ ์ฌ์ฉํ์ง ์๊ธฐ ๋๋ฌธ์ ๋ชจ๋ฅด์ค ์ ์์ต๋๋ค.
SQLAlchemy 1.4์์๋ ์๋ก์ด ๋ฐ์ดํฐ์ ์์ฑ์ด๋, ์ ๋ฐ์ดํธ ๋ฐ์์ ๋ชจ๋ธ ๊ฒฐ๊ณผ๋ฅผ ์ ๋ฐ์ดํธํด์ฃผ๊ธฐ ์ํด refresh ๋ฉ์๋๋ฅผ ์ด์ฉํ์ฌ์ผ ํฉ๋๋ค. AsyncSession์ด ๊ธฐ๋ณธ์ ์ผ๋ก Session์ ๊ธฐ๋ฐํ์ฌ AsyncEngine์ ๋ถ์ธ ๊ฒ์ด๊ธฐ ๋๋ฌธ์ ์ฌ์ค์ Session ๊ฐ์ฒด๋ ๊ฑฐ์ ๋์ผํ๊ฒ ๋์ํฉ๋๋ค.
๊ทธ๋ฐ๋ฐ, commit ๋ฑ๊ณผ ๊ฐ์ด transaction์ ํจ์๋ค์ AsyncSession์์ greenlet_spawn ํจ์๋ฅผ ํตํด ๋น๋๊ธฐ ์ฒ๋ฆฌ๋ฅผ ์งํํ๋๋ก ์ฌ๊ตฌํ๋์ด ์์ง๋ง add, expire ๋ฑ์ ํจ์๋ค์ ์ฌ๊ตฌํ๋์ด ์์ง ์์, ์ฌ์ค์ AsyncSession์ ๋ฉค๋ฒ ๋ณ์์ ํฌํจ๋์ด ์๋ Session ํด๋์ค๋ก ๋์ํฉ๋๋ค.
์ฝ๋๋ฅผ ๋ณด๋ฉด, ํ๋ก์ ํจํด์ ์ด์ฉํด์ Session ํด๋์ค์ ๋ฉ์๋๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ ๋ณผ ์ ์๋๋ฐ, ์ด ๋๋ฌธ์ add ํจ์๋ ๋ชจ๋ธ์ ์ ๋ฐ์ดํธ๋ฅผ ์งํํ ๋๋ ๋๊ธฐ ํด๋์ค๋ฅผ ์ฌ์ฉํ๋ฏ๋ก refresh ๋ฉ์๋๋ฅผ ์ด์ฉํด์ ๋น๋๊ธฐ ์ฒ๋ฆฌ ํ ๋ชจ๋ธ ๋ฐํ์ ์งํํ์ ์ผ ํฉ๋๋ค.
๋ง์น๋ฉฐ...
DB ๊ตฌ๊ฐ์ ๋น๋๊ธฐ ์ฒ๋ฆฌํ๋ ๋ฐฉ๋ฒ์ asyncpg ์ธ์๋ aiopg๋ผ๋ ๋ค๋ฅธ ๋ชจ๋๋ ์กด์ฌํฉ๋๋ค. ๋ณธ์ธ์ด ๊ฐ๋ฐํ์๋ ํ๋ก์ ํธ์์ ์ด๋ค ๋ชจ๋์ ์ฌ์ฉํ๊ณ ๊ทธ์ ๋ํ ์ฑ๋ฅ์ ํ์ ํ๋ ๊ฒ์ด ๋น๋๊ธฐ ์ฒ๋ฆฌ ๊ตฌํ์ ์งํํ๋ ๋ฐ ์ ์์ ์ด ๋์ด์ผ ํ ๊ฒ์ ๋๋ค.
SQLAlchemy 1.4 ๋ฒ์ ์ ์์์ผ๋ก ์์ผ๋ก๋ JPQL ๋ฐฉ์์ด ์๋ 2.x์ QueryDSL ๋ฐฉ์์ผ๋ก ๋ฐ๋ ๊ฒ์ผ๋ก ๋ณด์ ๋๋ค. ๋ฐ๋ผ์ ํ์ด์ฌ์ ๊ณ์ ๋ฐฑ์๋ ๊ฐ๋ฐ๋ก ์ด์ด๋๊ฐ ๋ถ์ด์๋ผ๋ฉด ORM์ SQLAlchemy๋ก ์ฌ์ฉํ์ค ๋ ์ด ๋ถ๋ถ์ ์ผ๋ํด๋๋ ๊ฒ์ด ์ข์ ๋ณด์ ๋๋ค.
๋น๋ก ์ด๋ฒ ๊ธ์์๋ asyncpg๋ผ๋ PostgreSQL์ ์ฃผ์ ๋ก ๋ค๋ฃจ์์ง๋ง ํ์ด์ฌ DB API์์ ๋น๋๊ธฐ๋ฅผ ์ง์ํ๋ ๋ชจ๋์๋ ์๋์ ๊ฐ์ด ์กด์ฌํฉ๋๋ค.
- MySQL (aiomysql)
- PostgreSQL (aiopg, asyncpg)
- SQLite (aiosqlite)
ํ Dialect๋ฅผ ๊ฐ๋ ฅํ๊ฒ ์ง์ํ๋ ๋ชจ๋์๋ ์์ 3๊ฐ์ง๊ฐ ์์ง๋ง MSSQL๊ณผ ๊ฐ์ ๋ค๋ฅธ Dialect์์๋ aioodbc๋ฅผ ์ด์ฉํ์ฌ ODBC ๋๋ผ์ด๋ฒ๋ฅผ ๋ก๋ํ ๋ค์ ์ฌ์ฉํ ์ ์์ต๋๋ค.