๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ
Programming/Django

๐Ÿงพ Django REST Framework์—์„œ API ๋ฌธ์„œ ์ž๋™ํ™”ํ•˜๊ธฐ (DRF Spectacular + Swagger)

by Mandy's 2025. 8. 6.

๐Ÿ’ก ์™œ API ๋ฌธ์„œ๊ฐ€ ์ค‘์š”ํ• ๊นŒ?

API๋Š” ๊ฐœ๋ฐœ์ž๋“ค์ด ์‚ฌ์šฉํ•˜๋Š” ์ œํ’ˆ์ž…๋‹ˆ๋‹ค.
์ข‹์€ API๋ฅผ ๋งŒ๋“ ๋‹ค๊ณ  ํ•ด๋„, ์‚ฌ์šฉ๋ฒ•์„ ๋ชจ๋ฅด๋ฉด ์“ธ ์ˆ˜ ์—†๊ฒ ์ฃ ?

“API๋Š” ๋ฌธ์„œํ™”๋œ ๋งŒํผ๋งŒ ๊ฐ€์น˜๊ฐ€ ์žˆ๋‹ค.”

  • ์–ด๋–ค endpoint๊ฐ€ ์žˆ๋Š”์ง€
  • ์–ด๋–ค method(GET, POST ๋“ฑ)๊ฐ€ ๊ฐ€๋Šฅํ•œ์ง€
  • ์–ด๋–ค ๋ฐ์ดํ„ฐ๋ฅผ ์ฃผ๊ณ ๋ฐ›๋Š”์ง€
  • ์ธ์ฆ์€ ์–ด๋–ป๊ฒŒ ํ•˜๋Š”์ง€

์ด๋Ÿฐ ๋‚ด์šฉ์ด ์—†๋‹ค๋ฉด, ๊ทธ API๋Š” ์‚ฌ์šฉ์ž๊ฐ€ ์—†๋Š” ์•ฑ๊ณผ ๋‹ค๋ฅผ ๋ฐ” ์—†์Šต๋‹ˆ๋‹ค.


๐Ÿงช TDD๋กœ Django ํ”„๋กœ์ ํŠธ๋ฅผ ๊ฐœ๋ฐœ ์ค‘

์ด๋ฒˆ ๊ฐ•์˜๋Š” Django ์‹ฌํ™” ๊ณผ์ •์ด์—ˆ๊ณ , TDD(Test-Driven Development) ๋ฐฉ์‹์œผ๋กœ API๋ฅผ ํ•˜๋‚˜์”ฉ ๋งŒ๋“ค๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.
์˜ˆ๋ฅผ ๋“ค์–ด POST /recipes/๋‚˜ GET /tags/์™€ ๊ฐ™์€ API๋ฅผ ๋งŒ๋“ค๊ณ  ํ…Œ์ŠคํŠธ๋กœ ๊ฒ€์ฆํ•˜์ฃ .

๊ทธ๋Ÿฐ๋ฐ ๊ธฐ๋Šฅ์ด ๋งŽ์•„์ง€๋ฉด์„œ, ๋‚ด๊ฐ€ ๋งŒ๋“  API๋ฅผ ์™ธ๋ถ€ ๊ฐœ๋ฐœ์ž๋‚˜ ํŒ€์›๋“ค์ด ์–ด๋–ป๊ฒŒ ์‚ฌ์šฉํ• ์ง€ ๊ณ ๋ฏผ์ด ์ƒ๊ฒผ์Šต๋‹ˆ๋‹ค.
๊ทธ๋ž˜์„œ ์ด๋ฒˆ์—” API ๋ฌธ์„œ๋ฅผ ์ž๋™์œผ๋กœ ์ƒ์„ฑํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ๋ฐฐ์› ์Šต๋‹ˆ๋‹ค.


๐Ÿ“š ๋ฌธ์„œํ™” ๋ฐฉ์‹ ๋‘ ๊ฐ€์ง€

  1. ์ˆ˜๋™ ๋ฌธ์„œํ™” (์ถ”์ฒœ โŒ)
    • Markdown ๋ฌธ์„œ๋‚˜ Word ๋ฌธ์„œ๋กœ ์ž‘์„ฑ
    • ๊ธฐ๋Šฅ ์ถ”๊ฐ€ ์‹œ ์ˆ˜๋™์œผ๋กœ ์ˆ˜์ •ํ•ด์•ผ ํ•จ
    • ์‹œ๊ฐ„์ด ์˜ค๋ž˜ ๊ฑธ๋ฆฌ๊ณ , ์ตœ์‹  ์ƒํƒœ ์œ ์ง€๊ฐ€ ์–ด๋ ค์›€
  2. ์ž๋™ ๋ฌธ์„œํ™” (์ถ”์ฒœ โœ…)
    • ์ฝ”๋“œ์— ์ž‘์„ฑํ•œ ์ •๋ณด๋กœ๋ถ€ํ„ฐ ๋ฌธ์„œ๋ฅผ ์ž๋™ ์ƒ์„ฑ
    • API ๋ณ€๊ฒฝ์‚ฌํ•ญ์ด ๋ฌธ์„œ์—๋„ ์ž๋™ ๋ฐ˜์˜
    • ํ…Œ์ŠคํŠธ๋„ ๊ฐ€๋Šฅํ•œ UI ์ œ๊ณต

๐Ÿงฐ ์šฐ๋ฆฌ๊ฐ€ ์‚ฌ์šฉํ•  ๋„๊ตฌ: DRF Spectacular + Swagger UI

  • DRF Spectacular
    Django REST Framework์šฉ ๋ฌธ์„œ ์ž๋™ ์ƒ์„ฑ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ
    → OpenAPI 3.0 ์ŠคํŽ™ ์ง€์› (์—…๊ณ„ ํ‘œ์ค€)
  • Swagger UI
    OpenAPI ๋ฌธ์„œ๋ฅผ ์ฝ์–ด์„œ, ๋ธŒ๋ผ์šฐ์ € ๊ธฐ๋ฐ˜์˜ ์ธํ„ฐํŽ˜์ด์Šค๋กœ ์‹œ๊ฐํ™”
    → API๋ฅผ ์ง์ ‘ ํ…Œ์ŠคํŠธํ•˜๊ณ  ์‘๋‹ต๋„ ๋ณผ ์ˆ˜ ์žˆ์Œ

๐Ÿ“„ Schema๋ž€?

DRF Spectacular์€ ๋‚ด๋ถ€์ ์œผ๋กœ schema๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.
์ด ์Šคํ‚ค๋งˆ๋Š” .yaml ๋˜๋Š” .json ํ˜•์‹์œผ๋กœ ์ž‘์„ฑ๋˜๋ฉฐ, API์˜ ๋ชจ๋“  ์ •๋ณด๋ฅผ ๋‹ด๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

์˜ˆ:

paths:
  /tags/:
    get:
      description: "List all tags"
      parameters:
        - name: "assigned_only"
          in: "query"
          required: false
          schema:
            type: "integer"
            enum: [0, 1]
      responses:
        "200":
          description: "Successful Response"

์Šคํ‚ค๋งˆ๋Š” Swagger์™€ ๊ฐ™์€ ๋„๊ตฌ์—์„œ ์ž๋™์œผ๋กœ ์ฝ์–ด์„œ ์‚ฌ์šฉ์ž ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๋งŒ๋“ค์–ด์ค๋‹ˆ๋‹ค.


๐Ÿง‘‍๐Ÿ’ป Swagger UI๋กœ ๋ฌธ์„œํ™”๋œ API ํ…Œ์ŠคํŠธํ•˜๊ธฐ

Swagger UI๋ฅผ ๋„์šฐ๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค:

  • ๋ชจ๋“  API ์—”๋“œํฌ์ธํŠธ ๋ชฉ๋ก ํ™•์ธ
  • ๊ฐ ์š”์ฒญ์˜ method(GET/POST ๋“ฑ), ํŒŒ๋ผ๋ฏธํ„ฐ, ์‘๋‹ต ์˜ˆ์‹œ ํ™•์ธ
  • Try it out → ์ง์ ‘ API์— ์š”์ฒญํ•ด๋ณด๊ธฐ
  • ์ธ์ฆ ํ† ํฐ ์ž…๋ ฅ ๊ธฐ๋Šฅ๋„ ์ง€์› (JWT, Token ๋“ฑ)

์ด๋ ‡๊ฒŒ ๋ฌธ์„œ์™€ ํ…Œ์ŠคํŠธ ๋„๊ตฌ๊ฐ€ ํ•˜๋‚˜์˜ ํŽ˜์ด์ง€๋กœ ํ†ตํ•ฉ๋ฉ๋‹ˆ๋‹ค.


โœ… TDD + ์ž๋™ ๋ฌธ์„œํ™”์˜ ์‹œ๋„ˆ์ง€

  • TDD๋กœ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ถ€ํ„ฐ ์ž‘์„ฑํ•˜๋ฉด, API ๊ธฐ๋Šฅ์ด ๋ช…ํ™•ํ•ด์ง€๊ณ 
  • docstring์„ ์ž˜ ์ž‘์„ฑํ•˜๋ฉด, ๋ฌธ์„œ๋„ ์ž๋™์œผ๋กœ ๊ตฌ์„ฑ
  • Swagger๋ฅผ ๋„์›Œ์„œ ๋ฐ”๋กœ ํ…Œ์ŠคํŠธ ๊ฐ€๋Šฅ

ํ…Œ์ŠคํŠธ + ๋ฌธ์„œ + ๊ธฐ๋Šฅ ๊ตฌํ˜„
์ด ์„ธ ๊ฐ€์ง€๋ฅผ ๋™์‹œ์— ์ฑ™๊ธธ ์ˆ˜ ์žˆ๋‹ค๋Š” ๊ฒƒ์ด ์ด๋ฒˆ ๊ฐ•์˜์—์„œ ๊ฐ€์žฅ ์ธ์ƒ ๊นŠ์—ˆ๋˜ ๋ถ€๋ถ„์ด์—ˆ์Šต๋‹ˆ๋‹ค.


๐Ÿ“Œ ๋งˆ๋ฌด๋ฆฌํ•˜๋ฉฐ

์ˆ˜๋™ ๋ฌธ์„œํ™”๋ฅผ ๋ฐ˜๋ณตํ•˜๋Š” ๋Œ€์‹ , ์ฝ”๋“œ๋ฅผ ๊ธฐ์ค€์œผ๋กœ ์ž๋™์œผ๋กœ ๋ฌธ์„œ๋ฅผ ์ƒ์„ฑํ•˜๊ณ ,
๊ทธ ๋ฌธ์„œ ์•ˆ์—์„œ ์ง์ ‘ API๋ฅผ ํ…Œ์ŠคํŠธํ•  ์ˆ˜ ์žˆ๋‹ค๋ฉด ์–ผ๋งˆ๋‚˜ ํšจ์œจ์ ์ผ๊นŒ์š”?

DRF Spectacular + Swagger UI ์กฐํ•ฉ์€ Django REST Framework์—์„œ API ๊ฐœ๋ฐœ๊ณผ ๋ฌธ์„œํ™”๋ฅผ ์ •๋ง ๊ฐ„ํŽธํ•˜๊ฒŒ ๋งŒ๋“ค์–ด์ค๋‹ˆ๋‹ค.

API๋ฅผ ํŒ€๊ณผ ๊ณต์œ ํ•˜๊ฑฐ๋‚˜ ์˜คํ”ˆ API๋กœ ๋งŒ๋“ค๊ณ  ์‹ถ๋‹ค๋ฉด, ๊ผญ ์ž๋™ ๋ฌธ์„œํ™”๋ฅผ ๋„์ž…ํ•ด๋ณด์„ธ์š”!


ํ•„์š”ํ•˜๋‹ค๋ฉด ๋‹ค์Œ ๊ธ€์—์„œ๋Š” DRF Spectacular ์„ค์น˜ ๋ฐ ์„ค์ •๋ฒ•๋„ ๋‹จ๊ณ„๋ณ„๋กœ ์ •๋ฆฌํ•ด๋“œ๋ฆด๊ฒŒ์š”.
์ด ๊ธ€์ด ๋„์›€์ด ๋˜์…จ๋‹ค๋ฉด ๊ณต๊ฐ ♥๏ธ ๋˜๋Š” ๋Œ“๊ธ€๋กœ ์•Œ๋ ค์ฃผ์„ธ์š”!