pytest-capquery๋ก N+1 ์ฟผ๋ฆฌ ํ๊ท๋ฅผ CI ๋จ๊ณ์์ ์ฆ์ ๊ฐ์งํจ
๐ Stop Testing Your Code and Ignoring Your Database (Catching N+1 in Pytest)
AI ์์ฝ
Context
Python ๊ฐ๋ฐ์๋ค ๋๋ถ๋ถ CI ํ์ดํ๋ผ์ธ์์ ์ ํ๋ฆฌ์ผ์ด์ ์ต์ข ์ํ๋ง ๊ฒ์ฆํจ. SQLAlchemy ๋ฐ์ดํฐ๋ฒ ์ด์ค ๋ ์ด์ด๋ฅผ ๋ธ๋๋ฐ์ค๋ก ์ทจ๊ธํ์ฌ N+1 ์ฟผ๋ฆฌ ๋ฌธ์ ๊ฐ ํ๋ก๋์ ๋ฐฐํฌ ์ง์ ๊น์ง ๋ฐ๊ฒฌ๋์ง ์์. lazy-load์ผ๋ก ์ธํ ๋นํจ์จ์ ์ฟผ๋ฆฌ๊ฐ ๋ฉ์ธ ๋ธ๋์น์ ์นจํฌํ๋ฉด ํด๋ผ์ฐ๋ ๋น์ฉ์ด ์ฆ๊ฐํ๊ณ ์ฌ์ฉ์ ๊ฒฝํ์ด ์ ํ๋จ.
Technical Solution
- pytest-capquery: Pytest suite์์ SQL ์ฟผ๋ฆฌ๋ฅผ 1๋ฑ ์๋ฏผ์ผ๋ก ์ทจ๊ธํ๋ ์คํ์์ค ๋๊ตฌ์
- SQLAlchemy engine ๊ฐ๋ก์ฑ๊ธฐ: ๋๋ผ์ด๋ฒ ๋ ๋ฒจ์์ SQL์ ์ธํฐ์ ํธํจ
- Chronological timeline enforcement: ์คํ ํ์ ์ ์๊ฒฉํ ์๊ฐ์ ๊ฐ์๋ฅผ ๋ณด์ฅํจ
- assert_executed_queries(): ๊ฒฐ์ ๋ก ์ I/O๋ฅผ ์๊ฒฉํ๊ฒ ๊ฒ์ฆํจ
- N+1 regression detection: ๋นํจ์จ ์ฟผ๋ฆฌ ๊ฐ์ง ์ ๋น๋๋ฅผ ์ฆ์ ์คํจ์ํด
Impact
ํ ์คํธ ์คํ ์ N+1 ํจํด์ด ๋ฐ๊ฒฌ๋๋ฉด ๋น๋๊ฐ ์คํจํ๋ฏ๋ก ํ๋ก๋์ ํ๊ฒฝ์์ ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ฑ๋ฅ ์ ํ๋ฅผ ์ฌ์ ์ฐจ๋จํจ.
Key Takeaway
ORM ์ฑ๋ฅ ๋ณ๋ชฉ์ ์์ธ์ ORM์ด ์๋๋ผ ํ ์คํธ ์ ๋ต์์ ์ฐพ์์ผ ํจ. ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ฟผ๋ฆฌ๋ฅผ ํ ์คํธ ์ ํฌํจํ๋ฉด ์ํํธ์จ์ด ํ๋ณต ํ๋ ฅ์ฑ์ด ํฌ๊ฒ ํฅ์๋จ.
์ค์ฒ ํฌ์ธํธ
SQLAlchemy ๊ธฐ๋ฐ Python ํ๋ก์ ํธ์์ Pytest ํ ์คํธ ์์ฑ ์ pytest-capquery๋ฅผ ํ์ฉํ ๊ฒ. db_session๊ณผ capquery fixture๋ฅผ ํจ๊ป ์ฌ์ฉํ์ฌ ์ฟผ๋ฆฌ ์คํ ํ assert_executed_queries()๋ก ์ ํํ SQL ํจํด์ ๊ฒ์ฆํ๋ฉด N+1 ํ๊ท๊ฐ ๋น๋ ๋จ๊ณ์์ ์๋์ผ๋ก ํ์ง๋จ.