Django-rest-framework: PUT ํ˜ธ์ถœ์ด "๋Œ€์ƒ ๋ฆฌ์†Œ์Šค์˜ ์ƒํƒœ๋ฅผ ์™„์ „ํžˆ ๋ฐ”๊พธ์ง€" ์•Š์Šต๋‹ˆ๋‹ค.

์— ๋งŒ๋“  2016๋…„ 06์›” 30์ผ  ยท  68์ฝ”๋ฉ˜ํŠธ  ยท  ์ถœ์ฒ˜: encode/django-rest-framework

ํŽธ์ง‘: ๋ฌธ์ œ์˜ ํ˜„์žฌ ์ƒํƒœ๋Š” https://github.com/encode/django-rest-framework/issues/4231#issuecomment -332935943์œผ๋กœ ๊ฑด๋„ˆ๋œ๋‹ˆ๋‹ค.

===

DRF๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์™€ ์ƒํ˜ธ ์ž‘์šฉํ•˜๋Š” ์‘์šฉ ํ”„๋กœ๊ทธ๋žจ์—์„œ ๋‚™๊ด€์  ๋™์‹œ์„ฑ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ๋ฅผ ๊ตฌํ˜„ํ•˜๋Š” ๋ฐ ๋ฌธ์ œ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ๋‚˜๋Š” ๋…ธ๋ ฅํ•˜๊ณ ์žˆ์–ด :

  • ๋‚ด๊ฐ€ ๋ณด๊ณ  ์žˆ๋Š” ๋™์ž‘์ด DRF์— ๊ธฐ์ธํ•œ ๊ฒƒ์ธ์ง€ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค.
  • ์ด๊ฒƒ์ด ์˜๋„ํ•œ ๋™์ž‘์ธ์ง€ ํ™•์ธ
  • ์ด ๋™์ž‘์„ ๊ทน๋ณตํ•  ์‹ค์šฉ์ ์ธ ๋ฐฉ๋ฒ•์ด ์žˆ๋Š”์ง€ ํ™•์ธ

์ตœ๊ทผ์— Django ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์— ๋‚™๊ด€์  ๋™์‹œ์„ฑ์„ ์ถ”๊ฐ€ํ–ˆ์Šต๋‹ˆ๋‹ค. Wiki ์กฐํšŒ๋ฅผ ์ €์žฅํ•˜๋ ค๋ฉด:

  • ๋ชจ๋“  ๋ชจ๋ธ์—๋Š” ๋ฒ„์ „ ํ•„๋“œ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ํŽธ์ง‘์ž๊ฐ€ ๊ฐœ์ฒด๋ฅผ ํŽธ์ง‘ํ•  ๋•Œ ํŽธ์ง‘ ์ค‘์ธ ๊ฐœ์ฒด์˜ ๋ฒ„์ „์„ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค.
  • ํŽธ์ง‘๊ธฐ๊ฐ€ ๊ฐœ์ฒด๋ฅผ ์ €์žฅํ•  ๋•Œ ํฌํ•จ๋œ ๋ฒ„์ „ ๋ฒˆํ˜ธ๊ฐ€ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์™€ ๋น„๊ต๋ฉ๋‹ˆ๋‹ค.
  • ๋ฒ„์ „์ด ์ผ์น˜ํ•˜๋ฉด ํŽธ์ง‘๊ธฐ๊ฐ€ ์ตœ์‹  ๋ฌธ์„œ๋ฅผ ์—…๋ฐ์ดํŠธํ•˜๊ณ  ์ €์žฅ์ด ์ง„ํ–‰๋ฉ๋‹ˆ๋‹ค.
  • ๋ฒ„์ „์ด ์ผ์น˜ํ•˜์ง€ ์•Š์œผ๋ฉด ํŽธ์ง‘๊ธฐ๊ฐ€ ๋กœ๋“œ๋˜๊ณ  ์ €์žฅ๋˜๋Š” ์‹œ๊ฐ„ ์‚ฌ์ด์— "์ถฉ๋Œ" ํŽธ์ง‘์ด ์ œ์ถœ๋œ ๊ฒƒ์œผ๋กœ ๊ฐ„์ฃผํ•˜์—ฌ ํŽธ์ง‘์„ ๊ฑฐ๋ถ€ํ•ฉ๋‹ˆ๋‹ค.
  • ๋ฒ„์ „์ด ์—†์œผ๋ฉด ํ…Œ์ŠคํŠธ๋ฅผ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์—†์œผ๋ฉฐ ํŽธ์ง‘์„ ๊ฑฐ๋ถ€ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

DRF๋ฅผ ํ†ตํ•ด ๋งํ•˜๋Š” ๋ ˆ๊ฑฐ์‹œ UI๊ฐ€ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค. ๋ ˆ๊ฑฐ์‹œ UI๋Š” ๋ฒ„์ „ ๋ฒˆํ˜ธ๋ฅผ ์ฒ˜๋ฆฌํ•˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค. ์ด๊ฒƒ์ด ๋™์‹œ์„ฑ ์˜ค๋ฅ˜๋ฅผ ์ผ์œผํ‚ฌ ๊ฒƒ์œผ๋กœ ์˜ˆ์ƒํ–ˆ์ง€๋งŒ ๊ทธ๋ ‡์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค. #3648์˜ ๋…ผ์˜๋ฅผ ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ์ดํ•ดํ–ˆ๋‹ค๋ฉด:

  • DRF๋Š” PUT์„ ๊ธฐ์กด ๋ ˆ์ฝ”๋“œ์™€ ๋ณ‘ํ•ฉํ•ฉ๋‹ˆ๋‹ค. ์ด๋กœ ์ธํ•ด ๋ˆ„๋ฝ๋œ ๋ฒ„์ „ ID๊ฐ€ ํ˜„์žฌ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ID๋กœ ์ฑ„์›Œ์ง‘๋‹ˆ๋‹ค.
  • ์ด๊ฒƒ์€ ํ•ญ์ƒ ์ผ์น˜๋ฅผ ์ œ๊ณตํ•˜๋ฏ€๋กœ ์ด ๋ณ€์ˆ˜๋ฅผ ์ƒ๋žตํ•˜๋ฉด ํ•ญ์ƒ DRF๋ฅผ ํ†ตํ•ด ํ†ต์‹ ํ•˜๋Š” ๋‚™๊ด€์  ๋™์‹œ์„ฑ ์‹œ์Šคํ…œ์ด ์ค‘๋‹จ๋ฉ๋‹ˆ๋‹ค.
  • ~๋ฐ์ดํ„ฐ๊ฐ€ ๋งค๋ฒˆ ์ œ์ถœ๋˜๋„๋ก ํ•˜๋Š” ์‰ฌ์šด ์˜ต์…˜(ํ•„๋“œ๋ฅผ "ํ•„์ˆ˜"๋กœ ๋งŒ๋“œ๋Š” ๊ฒƒ๊ณผ ๊ฐ™์€)์€ ์—†์Šต๋‹ˆ๋‹ค.~ (ํŽธ์ง‘: ์ด ์ฃผ์„ ์— ์„ค๋ช…๋œ ๋Œ€๋กœ ํ•„์ˆ˜ ํ•ญ๋ชฉ์œผ๋กœ ๋งŒ๋“ค์–ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค)

์žฌํ˜„ ๋‹จ๊ณ„

  1. ๋ชจ๋ธ์— ๋‚™๊ด€์  ๋™์‹œ์„ฑ ํ•„๋“œ ์„ค์ •
  2. ์ƒˆ ์ธ์Šคํ„ด์Šค๋ฅผ ๋งŒ๋“ค๊ณ  ์—ฌ๋Ÿฌ ๋ฒˆ ์—…๋ฐ์ดํŠธํ•ฉ๋‹ˆ๋‹ค(๋” ์ด์ƒ ๊ธฐ๋ณธ ๋ฒ„์ „ ๋ฒˆํ˜ธ๊ฐ€ ์—†๋„๋ก ํ•˜๊ธฐ ์œ„ํ•ด).
  3. ๋ฒ„์ „ ID๋ฅผ ์ œ์™ธํ•œ DRF๋ฅผ ํ†ตํ•ด ์—…๋ฐ์ดํŠธ(PUT) ์ œ์ถœ

    ์˜ˆ์ƒ๋˜๋Š” ํ–‰๋™

๋ˆ„๋ฝ๋œ ๋ฒ„์ „ ID๋Š” ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์™€ ์ผ์น˜ํ•˜์ง€ ์•Š์•„์•ผ ํ•˜๋ฉฐ ๋™์‹œ์„ฑ ๋ฌธ์ œ๋ฅผ ์•ผ๊ธฐํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

์‹ค์ œ ํ–‰๋™

๋ˆ„๋ฝ๋œ ๋ฒ„์ „ ID๋Š” ๋™์‹œ์„ฑ ๊ฒ€์‚ฌ๋ฅผ ํ†ต๊ณผํ•˜๋„๋ก DRF์— ์˜ํ•ด ํ˜„์žฌ ID๋กœ ์ฑ„์›Œ์ง‘๋‹ˆ๋‹ค.

Enhancement

๋ชจ๋“  68 ๋Œ“๊ธ€

์ข‹์•„, ๊ณง ์žˆ์„ 3.4 ๋ฆด๋ฆฌ์Šค๊ฐ€ ์šฐ์„  ์ˆœ์œ„์ด๋ฏ€๋กœ ์ด ๋งค์šฐ ์‹ฌ์ธต์ ์ธ ํ‹ฐ์ผ“์„ ์ฆ‰์‹œ ๊ฒ€ํ† ํ•  ์ˆ˜ ์žˆ์„ ๊ฒƒ์ด๋ผ๊ณ  ์•ฝ์†ํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ๋ฌธ์ œ๋ฅผ ํ†ตํ•ด ์ƒ์„ธํ•˜๊ณ  ์ž˜ ์ƒ๊ฐํ•ด ์ฃผ์…”์„œ ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ ๋ฉฐ์น ์ด๋‚˜ ๋ช‡ ๋‹ฌ์ด ์•„๋‹ˆ๋ผ ๋ช‡ ์ฃผ ๋‹จ์œ„๋กœ ๋ณด๋Š” ๊ฒƒ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค. ์ง„ํ–‰ ์ƒํ™ฉ์ด ๋” ์ด์ƒ ์ƒ๊ฐ๋‚˜๋ฉด ํ‹ฐ์ผ“์„ ์—…๋ฐ์ดํŠธํ•˜๊ณ  ๊ณ„์† ์•Œ๋ ค์ฃผ์‹œ๊ธฐ ๋ฐ”๋ž๋‹ˆ๋‹ค.

ํ™•์ธ. ๋‚ด ๋ฌธ์ œ๋Š” ๋‘ ๊ฐ€์ง€ ์š”์†Œ์˜ ์กฐํ•ฉ์ด๋ผ๊ณ  ํ™•์‹ ํ•ฉ๋‹ˆ๋‹ค.

  1. DRF ์—๋Š” ๊ธฐ๋ณธ๊ฐ’(๋ฒ„์ „=0)์ด ์žˆ๊ธฐ ๋•Œ๋ฌธ์— PUT์˜ ํ•„๋“œ๊ฐ€ ํ•„์š”ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค(๋ชจ๋ธ์— ํ•„์š”ํ•˜๋”๋ผ๋„).
  2. DRF ๋Š” PUT ํ•„๋“œ๋ฅผ ํ˜„์žฌ ๊ฐœ์ฒด์™€ ๋ณ‘ํ•ฉํ•ฉ๋‹ˆ๋‹ค(๊ธฐ๋ณธ๊ฐ’์„ ์ฃผ์ž…ํ•˜์ง€ ์•Š์Œ).

๊ฒฐ๊ณผ์ ์œผ๋กœ DRF๋Š” ํ˜„์žฌ(๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค) ๊ฐ’์„ ์‚ฌ์šฉํ•˜๊ณ  ๋™์‹œ์„ฑ ์ œ์–ด๋ฅผ ์ค‘๋‹จํ•ฉ๋‹ˆ๋‹ค. ๋ฌธ์ œ์˜ ํ›„๋ฐ˜๋ถ€๋Š” #3648(์œ„์—์„œ๋„ ์ธ์šฉ๋จ)์˜ ํ† ๋ก ๊ณผ ๊ด€๋ จ์ด ์žˆ์œผ๋ฉฐ #1445์—๋Š” ์—ฌ์ „ํžˆ ๊ด€๋ จ์ด ์žˆ๋Š” ๊ฒƒ์œผ๋กœ ๋ณด์ด๋Š” (3.x ์ด์ „) ํ† ๋ก ์ด ์žˆ์Šต๋‹ˆ๋‹ค.

๊ธฐ๋ณธ ๋™์ž‘์ด ๋น„๋šค์–ด์ง„ ๊ตฌ์ฒด์ ์ธ(๊ทธ๋ฆฌ๊ณ  ์ ์  ๋” ์ผ๋ฐ˜์ ์ธ) ๊ฒฝ์šฐ๊ฐ€ ModelSerializer์˜ "์ด์ƒ์ ์ธ" ๋™์ž‘์— ๋Œ€ํ•œ ๋…ผ์˜๋ฅผ ์žฌ๊ฐœํ•˜๊ธฐ์— ์ถฉ๋ถ„ํ•˜๊ธฐ๋ฅผ ๋ฐ”๋ž๋‹ˆ๋‹ค. ๋ถ„๋ช…ํžˆ, ๋‚˜๋Š” DRF์— ๋Œ€ํ•ด 1์ธ์น˜๋ฐ–์— ์•ˆ๋˜์ง€๋งŒ ๋‚ด ์ง๊ด€์€ ๋‹ค์Œ ๋™์ž‘์ด ํ•„์ˆ˜ ํ•„๋“œ ๋ฐ PUT์— ์ ํ•ฉํ•˜๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

  • ๋ถ€๋ถ„์ ์ด์ง€ ์•Š์€ ์ง๋ ฌ ๋ณ€ํ™˜๊ธฐ๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ ๊ฐ’์„ ๋ฐ›๊ฑฐ๋‚˜ ๊ธฐ๋ณธ๊ฐ’์„ ์‚ฌ์šฉํ•˜๊ฑฐ๋‚˜(๊ธฐ๋ณธ๊ฐ’์ด ์—†๋Š” ๊ฒฝ์šฐ) ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ ์˜ค๋ฅ˜๋ฅผ ๋ฐœ์ƒ์‹œ์ผœ์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๋ชจ๋ธ ์ „์ฒด ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ๋Š” ์ž…๋ ฅ/๊ธฐ๋ณธ๊ฐ’์—๋งŒ ์ ์šฉํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
  • ๋ถ€๋ถ„ ์ง๋ ฌ ๋ณ€ํ™˜๊ธฐ๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ ๊ฐ’์„ ๋ฐ›๊ฑฐ๋‚˜ ํ˜„์žฌ ๊ฐ’์— ๋Œ€ํ•œ ํด๋ฐฑ(fallback)์„ ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๋ชจ๋ธ ์ „์ฒด์˜ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ๋Š” ๊ฒฐํ•ฉ๋œ ๋ฐ์ดํ„ฐ์— ์ ์šฉ๋˜์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
  • ๋‚˜๋Š” ํ˜„์žฌ์˜ "non-partial" ์ง๋ ฌ ๋ณ€ํ™˜๊ธฐ๊ฐ€ ์‹ค์ œ๋กœ ์ค€ ๋ถ€๋ถ„์ ์ด๋ผ๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค.

    • ํ•„์ˆ˜์ด๋ฉฐ ๊ธฐ๋ณธ๊ฐ’์ด ์—†๋Š” ํ•„๋“œ์˜ ๊ฒฝ์šฐ ๋ถ€๋ถ„์ ์ด์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

    • ํ•„์ˆ˜์ด๋ฉฐ ๊ธฐ๋ณธ๊ฐ’์ด ์žˆ๋Š” ํ•„๋“œ์˜ ๊ฒฝ์šฐ ๋ถ€๋ถ„์ ์ž…๋‹ˆ๋‹ค(๊ธฐ๋ณธ๊ฐ’์ด ์‚ฌ์šฉ๋˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์—).

    • ํ•„์š”ํ•˜์ง€ ์•Š์€ ํ•„๋“œ์˜ ๊ฒฝ์šฐ ๋ถ€๋ถ„์ ์ž…๋‹ˆ๋‹ค.

์œ„์˜ ๊ธ€๋จธ๋ฆฌ ๊ธฐํ˜ธ(1)๋ฅผ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์—†๊ฑฐ๋‚˜ ๊ธฐ๋ณธ๊ฐ’์ด ๋ฌด์šฉ์ง€๋ฌผ์ด ๋ฉ๋‹ˆ๋‹ค(๊ธฐ๋ณธ๊ฐ’์„ ์•Œ๊ณ  ์žˆ์ง€๋งŒ ์ž…๋ ฅ์ด ํ•„์š”ํ•จ). ์ฆ‰, ์œ„์˜ #2๋ฅผ ๋ณ€๊ฒฝํ•˜์—ฌ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๋‚˜๋Š” #2683์—์„œ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๋‹น์‹ ์˜ ์ฃผ์žฅ์— ๋™์˜ํ•ฉ๋‹ˆ๋‹ค.

๋ชจ๋ธ ๊ธฐ๋ณธ๊ฐ’์€ ๋ชจ๋ธ ๊ธฐ๋ณธ๊ฐ’์ž…๋‹ˆ๋‹ค. ์ง๋ ฌ ๋ณ€ํ™˜๊ธฐ๋Š” ๊ฐ’์„ ์ƒ๋žตํ•˜๊ณ  ์ด๋ฅผ ์ฒ˜๋ฆฌํ•˜๊ธฐ ์œ„ํ•ด Model.object.create() ์— ๋Œ€ํ•œ ์ฑ…์ž„์„ ์—ฐ๊ธฐํ•ด์•ผ โ€‹โ€‹ํ•ฉ๋‹ˆ๋‹ค.

์ด๋Ÿฌํ•œ ๋ฌธ์ œ ๋ถ„๋ฆฌ์™€ ์ผ๊ด€์„ฑ์„ ์œ ์ง€ํ•˜๊ธฐ ์œ„ํ•ด ์—…๋ฐ์ดํŠธ ๋Š” ์ƒˆ ์ธ์Šคํ„ด์Šค๋ฅผ ์ƒ์„ฑํ•˜๊ณ (๋ชจ๋“  ๊ธฐ๋ณธ๊ฐ’์„ ๋ชจ๋ธ์— ์œ„์ž„) ์ œ์ถœ๋œ ๊ฐ’์„ ํ•ด๋‹น ์ƒˆ ์ธ์Šคํ„ด์Šค์— ์ ์šฉํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ ๊ฒฐ๊ณผ #3648์—์„œ ์š”์ฒญํ•œ ๋™์ž‘์ด ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.

๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ๊ฒฝ๋กœ๋ฅผ ์„ค๋ช…ํ•˜๋ฉด ํ˜„์žฌ ๋™์ž‘์ด ์–ผ๋งˆ๋‚˜ ์ด์ƒํ•œ์ง€ ๊ฐ•์กฐํ•˜๋Š” ๋ฐ ๋„์›€์ด ๋ฉ๋‹ˆ๋‹ค. ์ตœ์ข… ๋ชฉํ‘œ๋Š”

  1. ModelSerializer ์ˆ˜์ •,
  2. ์ด ์ค€-๋ถ€๋ถ„ ์ƒํƒœ์— ๋Œ€ํ•œ ํ”Œ๋ž˜๊ทธ๋ฅผ ์ถ”๊ฐ€ํ•˜๊ณ ,
  3. ํ•ด๋‹น ํ”Œ๋ž˜๊ทธ๋ฅผ ๊ธฐ๋ณธ๊ฐ’์œผ๋กœ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค(์ด์ „ ๋ฒ„์ „๊ณผ์˜ ํ˜ธํ™˜์„ฑ์„ ์œ„ํ•ด)

๊ทธ ๊นƒ๋ฐœ์˜ ์ด๋ฆ„์€ ๋ฌด์—‡์ž…๋‹ˆ๊นŒ? ํ˜„์žฌ ๋ชจ๋ธ ์ง๋ ฌ ๋ณ€ํ™˜๊ธฐ๋Š” ์‹ค์ œ๋กœ required==True and default==None ์กฐ๊ฑด์„ ์ถฉ์กฑํ•˜๋Š” ํ•„๋“œ๊ฐ€ ํ•„์š”ํ•œ ๋ถ€๋ถ„ ์ง๋ ฌ ๋ณ€ํ™˜๊ธฐ์ž…๋‹ˆ๋‹ค. ์ด์ „ ๋ฒ„์ „๊ณผ์˜ ํ˜ธํ™˜์„ฑ์„ ๊นจ๋œจ๋ฆฌ์ง€ ์•Š๊ณ ๋Š” partial ํ”Œ๋ž˜๊ทธ๋ฅผ ๋ช…์‹œ์ ์œผ๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†์œผ๋ฏ€๋กœ ์ƒˆ๋กœ์šด(์ž„์‹œ์ ์ธ) ํ”Œ๋ž˜๊ทธ๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. quasi_partial ๋‚จ์•˜์ง€๋งŒ ์ž„์˜์ ์ธ ์š”๊ตฌ ์‚ฌํ•ญ required==True and default==None ์„ ํ‘œํ˜„ํ•  ์ˆ˜ ์—†๊ธฐ ๋•Œ๋ฌธ์— ์ด ๋™์ž‘์ด ์‹œ๊ธ‰ํžˆ ์ค‘๋‹จ๋˜์–ด์•ผ ํ•œ๋‹ค๋Š” ๊ฒƒ์ด ๋„ˆ๋ฌด๋‚˜ ๋ถ„๋ช…ํ•ฉ๋‹ˆ๋‹ค.

์ง๋ ฌ ๋ณ€ํ™˜๊ธฐ์˜ ๋ฉ”ํƒ€์— extra_kwargs ๋ฅผ ์ถ”๊ฐ€ํ•˜์—ฌ version ๋ฅผ ํ•„์ˆ˜ ํ•„๋“œ๋กœ ๋งŒ๋“ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

class ConcurrentModelSerializer(serializers.ModelSerializer):
    class Meta:
        model = ConcurrentModel
        extra_kwargs = {'version': {'required': True}}

@anoopmalev๋‹˜ ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๊ฒƒ์€ ๋‚˜๋ฅผ ์ƒ์‚ฐ ์ง€์ ์— ๋จธ๋ฌผ๊ฒŒ ํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค.

"์ž๊ณ " ๋‚œ ํ›„์— ์ฃผ๋ฆ„์ด ํ•˜๋‚˜ ๋” ์žˆ๋‹ค๋Š” ๊ฒƒ์„ ๊นจ๋‹ฌ์•˜์Šต๋‹ˆ๋‹ค. ๋‚ด๊ฐ€ ๋งํ•œ ๋ชจ๋“  ๊ฒƒ์€ ์ง๋ ฌ ๋ณ€ํ™˜๊ธฐ์˜ ํ•„๋“œ์— ์ ์šฉ๋˜์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ํ•„๋“œ๊ฐ€ ์ง๋ ฌ ๋ณ€ํ™˜๊ธฐ์— ํฌํ•จ๋˜์ง€ ์•Š์€ ๊ฒฝ์šฐ ์ˆ˜์ •ํ•˜๋ฉด ์•ˆ ๋ฉ๋‹ˆ๋‹ค. ์ด๋Ÿฐ ์‹์œผ๋กœ ๋ชจ๋“  ์ง๋ ฌ ๋ณ€ํ™˜๊ธฐ๋Š” ํฌํ•จ๋˜์ง€ ์•Š์€ ํ•„๋“œ์— ๋Œ€ํ•ด ๋ถ€๋ถ„์ ์ž…๋‹ˆ๋‹ค(๊ทธ๋ฆฌ๊ณ  ๊ทธ๋ž˜์•ผ๋งŒ ํ•ฉ๋‹ˆ๋‹ค). ์ด๊ฒƒ์€ ์œ„์˜ "์ƒˆ ์ธ์Šคํ„ด์Šค ๋งŒ๋“ค๊ธฐ"๋ณด๋‹ค ์กฐ๊ธˆ ๋” ๋ณต์žกํ•ฉ๋‹ˆ๋‹ค.

๋‚˜๋Š” ์ด ๋ฌธ์ œ๊ฐ€ ์•ž์œผ๋กœ ๋‚˜์•„๊ฐ€๊ธฐ ์œ„ํ•ด ๋” ์ œํ•œ์ ์ธ ์ œ์•ˆ์œผ๋กœ ์ถ•์†Œ๋˜์–ด์•ผ ํ•œ๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค.
ํ˜„์žฌ ์ƒํƒœ์—์„œ ์‹คํ–‰ ๊ฐ€๋Šฅํ•œ ๊ฒƒ์œผ๋กœ ๋ณด์ž…๋‹ˆ๋‹ค.
์ง€๊ธˆ์€ ์ด๊ฒƒ์„ ๋‹ซ๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ๋ˆ„๊ตฐ๊ฐ€๊ฐ€ ์›ํ•˜๋Š” ํ–‰๋™์— ๋Œ€ํ•œ ๊ฐ„๊ฒฐํ•˜๊ณ  ์‹คํ–‰ ๊ฐ€๋Šฅํ•œ ์„ค๋ช…์œผ๋กœ ์ค„์ผ ์ˆ˜ ์žˆ๋‹ค๋ฉด ์žฌ๊ณ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋•Œ๊นŒ์ง€๋Š” ๋‹จ์ˆœํžˆ ๊ด‘๋ฒ”์œ„ํ•˜๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค.

๋‹ค์Œ์€ non-partial serializer์— ๋Œ€ํ•œ ๊ฐ„๊ฒฐํ•œ ์ œ์•ˆ์ž…๋‹ˆ๋‹ค.

  1. ์ง๋ ฌ ๋ณ€ํ™˜๊ธฐ์— ๋‚˜์—ด๋˜์ง€ ์•Š๊ฑฐ๋‚˜(์•”์‹œ์  ๋˜๋Š” ๋ช…์‹œ์ ์œผ๋กœ) ์ฝ๊ธฐ ์ „์šฉ์œผ๋กœ ํ‘œ์‹œ๋œ ํ•„๋“œ์˜ ๊ฒฝ์šฐ ๊ธฐ์กด ๊ฐ’์„ ์œ ์ง€ํ•ฉ๋‹ˆ๋‹ค.
  2. ๋‹ค๋ฅธ ๋ชจ๋“  ํ•„๋“œ์˜ ๊ฒฝ์šฐ ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ์ฒซ ๋ฒˆ์งธ ์˜ต์…˜์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

    1. ์ œ์ถœ๋œ ๊ฐ’์œผ๋กœ ์ฑ„์šฐ๊ธฐ

    2. blank ๋ฐ/๋˜๋Š” null ์— ์˜ํ•ด ์•”์‹œ๋œ ๊ฐ’์„ ํฌํ•จํ•˜์—ฌ ๊ธฐ๋ณธ๊ฐ’์œผ๋กœ ์ฑ„์šฐ๊ธฐ

    3. ์˜ˆ์™ธ ๋ฐœ์ƒ

๋ช…ํ™•์„ฑ์„ ์œ„ํ•ด ์ด ํ”„๋กœ์„ธ์Šค์˜ ์ตœ์ข… ์ œํ’ˆ์— ๋Œ€ํ•ด ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ๊ฐ€ ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค.

์ฆ‰, ์—…๋ฐ์ดํŠธ๋ฅผ ์œ„ํ•ด ๋ชจ๋ธ ๊ธฐ๋ณธ๊ฐ’์ด ์—†๋Š” ์ง๋ ฌ ๋ณ€ํ™˜๊ธฐ ํ•„๋“œ์— required=True ๋ฅผ ์„ค์ •ํ•˜์‹œ๊ฒ ์Šต๋‹ˆ๊นŒ?

๋‚ด๊ฐ€ ๋งž์•˜์–ด?

์˜ˆ(๋ฐ ๊ทธ ์ด์ƒ). ์ด๊ฒƒ์ด ๋‚ด๊ฐ€ partial (๋ชจ๋“  ํ•„๋“œ๋Š” ์„ ํƒ ์‚ฌํ•ญ)์™€ non-partial (๋ชจ๋“  ํ•„๋“œ๋Š” ํ•„์ˆ˜)์˜ ๊ตฌ๋ถ„์„ ์ดํ•ดํ•˜๋Š” ๋ฐฉ๋ฒ•์ž…๋‹ˆ๋‹ค. non-partial ์ง๋ ฌ ๋ณ€ํ™˜๊ธฐ๊ฐ€ ํ•„๋“œ๋ฅผ ํ•„์š”๋กœ ํ•˜์ง€ ์•Š๋Š” ์œ ์ผํ•œ ๊ฒฝ์šฐ๋Š” ๊ธฐ๋ณธ๊ฐ’(์ข๊ฑฐ๋‚˜ ๊ด‘๋ฒ”์œ„ํ•˜๊ฒŒ ์ •์˜๋จ)์ด ์žˆ๋Š” ๊ฒฝ์šฐ์ž…๋‹ˆ๋‹ค. ๊ฐ’์ด ์ œ๊ณต๋˜์ง€ ์•Š์œผ๋ฉด ์ง๋ ฌ ๋ณ€ํ™˜๊ธฐ๊ฐ€ ํ•ด๋‹น ๊ธฐ๋ณธ๊ฐ’์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค._

๊ธฐ์šธ์ž„๊ผด ์„น์…˜์€ DRF๊ฐ€ ํ˜„์žฌ ์ˆ˜ํ–‰ํ•˜์ง€ ์•Š๋Š” ๊ฒƒ์ด๋ฉฐ ๋‚ด ์ œ์•ˆ์—์„œ ๋” ์ค‘์š”ํ•œ ๋ณ€๊ฒฝ ์‚ฌํ•ญ์ž…๋‹ˆ๋‹ค. ํ˜„์žฌ ๊ตฌํ˜„์€ ํ•„๋“œ๋ฅผ ๊ฑด๋„ˆ๋œ๋‹ˆ๋‹ค.

๋‚˜๋Š” ๋‘ ๋ฒˆ์งธ ์ œ์•ˆ์ด ์„ž์—ฌ ์žˆ์—ˆ์ง€๋งŒ ์‹ค์ œ๋กœ "๊ธฐ๋ณธ๊ฐ’"์ด๋ผ๋Š” ์•„์ด๋””์–ด์— ์–ผ๋งˆ๋‚˜ ๊ด€๋Œ€ํ•˜๊ณ  ์‹ถ์€์ง€์— ๋Œ€ํ•œ ๋ณ„๋„์˜ ์งˆ๋ฌธ์ž…๋‹ˆ๋‹ค. ํ˜„์žฌ ๋™์ž‘์€ default ๊ทธ๋ ‡๊ฒŒ ์ทจ๊ธ‰๋œ๋‹ค๋Š” ์ ์—์„œ "์—„๊ฒฉ"ํ•ฉ๋‹ˆ๋‹ค. ํ•„์š”ํ•œ ๋ฐ์ดํ„ฐ์˜ ์–‘์„ _์ •๋ง__ ์ค„์ด๊ณ  ์‹ถ๋‹ค๋ฉด blank=True ํ•„๋“œ๋„ ์„ ํƒ ์‚ฌํ•ญ์œผ๋กœ ๋งŒ๋“ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค... ๋ถ€์žฌ ๊ฐ’์ด ๊ณต๋ฐฑ ๊ฐ’์ด๋ผ๊ณ  ๊ฐ€์ •ํ•ฉ๋‹ˆ๋‹ค.

@claytondaley ์ด ๋ฐฉ๋ฒ•์œผ๋กœ 2๋ฐฐ ์ดํ›„๋กœ DRF์™€ ํ•จ๊ป˜ OOL์„ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

class VersionModelSerializer(serializers.ModelSerializer, BaseSerializer):
    _initial_version = 0

    _version = VersionField()

    def __init__(self, *args, **kwargs):
        super(VersionModelSerializer, self).__init__(*args, **kwargs)

        # version field should not be required if there is no object
        if self.instance is None and '_version' in self.fields and\
                getattr(self, 'parent', None) is None:
            self.fields['_version'].read_only = True
            self.fields['_version'].required = False

        # version field is required while updating instance
        if self.instance is not None and '_version' in self.fields:
            self.fields['_version'].required = True

        if self.instance is not None and hasattr(self.instance, '_version'):
            self._initial_version = self.instance._version

    def validate__version(self, value):
        if self.instance is not None:
            if not value and not isinstance(value, int):
                raise serializers.ValidationError(_(u"This field is required"))

        return value
   # more code & helpers

๊ทธ๊ฒƒ์€ ๋ชจ๋“  ์ข…๋ฅ˜์˜ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง๊ณผ ์ž˜ ์ž‘๋™ํ•˜๋ฉฐ ์–ด๋–ค ๋ฌธ์ œ๋„ ์ผ์œผํ‚ค์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค.

์ด ์™ผ์ชฝ์ด ์‚ฌ๊ณ ๋กœ ๋‹ซํ˜”์Šต๋‹ˆ๊นŒ? ๋‚˜๋Š” ๊ตฌ์ฒด์ ์ธ ์งˆ๋ฌธ์— ๋Œ€๋‹ตํ–ˆ๊ณ  ์ œ์•ˆ์— ๋ฌด์—‡์ด ์ž˜๋ชป๋˜์—ˆ๋Š”์ง€ ์ด์œ ๋ฅผ ๋“ฃ์ง€ ๋ชปํ–ˆ์Šต๋‹ˆ๋‹ค.

@claytondaley OOL์ด DRF์˜ ์ผ๋ถ€์—ฌ์•ผ ํ•˜๋Š” ์ด์œ ๋Š” ๋ฌด์—‡์ž…๋‹ˆ๊นŒ? ๋‚ด ์ฝ”๋“œ๋ฅผ ํ™•์ธํ•˜์‹ญ์‹œ์˜ค โ€“ ํฐ ์•ฑ์—์„œ ์ฐพ๊ธฐ๋งŒ ํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค(1400๊ฐœ์˜ ํ…Œ์ŠคํŠธ). VersionField ๋Š” IntegerField ์ž…๋‹ˆ๋‹ค.

์ง๋ ฌ ๋ณ€ํ™˜๊ธฐ์— OOL์„ ํ•˜๋“œ ์ฝ”๋”ฉํ–ˆ์Šต๋‹ˆ๋‹ค. ๊ฒฝ์Ÿ ์กฐ๊ฑด์ด ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ์ด๊ฒƒ์€ ์ž˜๋ชป๋œ ์žฅ์†Œ์ž…๋‹ˆ๋‹ค. ๋ณ‘๋ ฌ ์—…๋ฐ์ดํŠธ(๋™์ผํ•œ ์ด์ „ ๋ฒ„์ „ ์‚ฌ์šฉ)๋Š” ๋ชจ๋‘ ์ง๋ ฌ ๋ณ€ํ™˜๊ธฐ์—์„œ ์ „๋‹ฌ๋˜์ง€๋งŒ... ์ €์žฅ ์ž‘์—…์—์„œ๋Š” ํ•˜๋‚˜๋งŒ ์Šน๋ฆฌํ•ฉ๋‹ˆ๋‹ค.

์ €๋Š” django-concurrency ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ OOL ๋…ผ๋ฆฌ๋ฅผ ์ €์žฅ ์ž‘์—…(์†ํ•ด ์žˆ๋Š” ์œ„์น˜)์— ๋„ฃ์Šต๋‹ˆ๋‹ค. ๊ธฐ๋ณธ์ ์œผ๋กœ UPDATE... WHERE version = submitted_version . ์ด๊ฒƒ์€ ์›์ž์ ์ด๋ฏ€๋กœ ๊ฒฝ์Ÿ ์กฐ๊ฑด์ด ์—†์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ์ง๋ ฌํ™” ๋…ผ๋ฆฌ์— ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๊ฒฐํ•จ์ด ์žˆ์Šต๋‹ˆ๋‹ค.

  • ๊ธฐ๋ณธ๊ฐ’์ด ๋ชจ๋ธ์˜ ํ•„๋“œ์— ์„ค์ •๋œ ๊ฒฝ์šฐ DRF๋Š” required=False ๋ฅผ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค. (์œ ํšจํ•œ) ์•„์ด๋””์–ด๋Š” ์ œ์ถœ๋œ ๊ฐ’์ด ์—†๋Š” ๊ฒฝ์šฐ DRF๊ฐ€ ํ•ด๋‹น ๊ธฐ๋ณธ๊ฐ’์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.
  • ๊ทธ๋Ÿฌ๋‚˜ ํ•ด๋‹น ํ•„๋“œ๊ฐ€ ์—†์œผ๋ฉด DRF๋Š” ๊ธฐ๋ณธ๊ฐ’์„ ์‚ฌ์šฉํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๋Œ€์‹  ์ œ์ถœ๋œ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ์ฒด์˜ ํ˜„์žฌ ๋ฒ„์ „๊ณผ ๋ณ‘ํ•ฉํ•ฉ๋‹ˆ๋‹ค.

ํ•„๋“œ๊ฐ€ ํ•„์š”ํ•˜์ง€ ์•Š์€ ๊ฒฝ์šฐ ์‚ฌ์šฉํ•  ๊ธฐ๋ณธ๊ฐ’์ด ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ๊ทธ๋ ‡๊ฒŒ ํ•ฉ๋‹ˆ๋‹ค. DRF๋Š” ๊ธฐ๋ณธ๊ฐ’์„ ์‚ฌ์šฉํ•˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— ํ•ด๋‹น ๊ณ„์•ฝ์„ ์ดํ–‰ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๊ธฐ์กด ๊ฐ’์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

๊ทผ๋ณธ์ ์ธ ๋ฌธ์ œ๋Š” ์ด์ „์— ๋…ผ์˜๋˜์—ˆ์ง€๋งŒ ๋ฉ‹์ง€๊ณ  ๊ตฌ์ฒด์ ์ธ ์‚ฌ๋ก€๊ฐ€ ์—†์—ˆ์Šต๋‹ˆ๋‹ค. OOL์ด ๊ทธ ์ด์ƒ์ ์ธ ๊ฒฝ์šฐ์ž…๋‹ˆ๋‹ค. ๋ฒ„์ „ ํ•„๋“œ์˜ ๊ธฐ์กด ๊ฐ’์€ ํ•ญ์ƒ OOL์„ ์ „๋‹ฌํ•˜๋ฏ€๋กœ ๋ฒ„์ „์„ ์ƒ๋žตํ•˜์—ฌ ์ „์ฒด OOL ์‹œ์Šคํ…œ์„ ์šฐํšŒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๊ฒƒ์€ (๋ถ„๋ช…ํžˆ) OOL ์‹œ์Šคํ…œ์˜ ์›ํ•˜๋Š” ๋™์ž‘์ด ์•„๋‹™๋‹ˆ๋‹ค.

@claytondaley

์ง๋ ฌ ๋ณ€ํ™˜๊ธฐ์— OOL์„ ํ•˜๋“œ ์ฝ”๋”ฉํ–ˆ์Šต๋‹ˆ๋‹ค.

๋‚ด๊ฐ€ ๊ทธ๋žฌ์–ด? ํ•„๋“œ ์š”๊ตฌ ์‚ฌํ•ญ ์™ธ์— ์ง๋ ฌ ๋ณ€ํ™˜๊ธฐ์—์„œ OOL ๋…ผ๋ฆฌ๋ฅผ ์ฐพ์•˜์Šต๋‹ˆ๊นŒ?

๊ฒฝ์Ÿ ์กฐ๊ฑด์ด ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ์ด๊ฒƒ์€ ์ž˜๋ชป๋œ ์žฅ์†Œ์ž…๋‹ˆ๋‹ค.

Sry, ์—ฌ๊ธฐ ๊ฒฝ์Ÿ ์กฐ๊ฑด์ด ์–ด๋””์— ์žˆ๋Š”์ง€ ์•Œ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.

OOL ๋…ผ๋ฆฌ๋ฅผ ์ €์žฅ ์ž‘์—…(์†ํ•œ ์œ„์น˜)์— ๋„ฃ๋Š” django-concurrency๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

๋‚˜๋Š” ๋˜ํ•œ django-concurrency ๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค :) ํ•˜์ง€๋งŒ ์ง๋ ฌ ๋ณ€ํ™˜๊ธฐ๊ฐ€ ์•„๋‹Œ ๋ชจ๋ธ ์ˆ˜์ค€์ž…๋‹ˆ๋‹ค. ์ง๋ ฌ ๋ณ€ํ™˜๊ธฐ ์ˆ˜์ค€์—์„œ ๋‹ค์Œ์„ ์ˆ˜ํ–‰ํ•˜๊ธฐ๋งŒ ํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค.

  • _version ํ•„๋“œ๊ฐ€ ํ•ญ์ƒ ํ•„์ˆ˜์ธ์ง€ ํ™•์ธํ•˜์‹ญ์‹œ์˜ค(ํ•„์š”ํ•œ ๊ฒฝ์šฐ).
  • ์ง๋ ฌ ๋ณ€ํ™˜๊ธฐ๊ฐ€ OOL ์˜ค๋ฅ˜๋ฅผ ์ฒ˜๋ฆฌํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์•Œ๊ณ  ์žˆ๋Š”์ง€ ํ™•์ธํ•˜์‹ญ์‹œ์˜ค(์ด ๋ถ€๋ถ„์€ ์ƒ๋žตํ–ˆ์Šต๋‹ˆ๋‹ค).
  • apiview๊ฐ€ OOL ์˜ค๋ฅ˜๋ฅผ ์ฒ˜๋ฆฌํ•˜๊ณ  ๊ฐ€๋Šฅํ•œ diff ์ปจํ…์ŠคํŠธ๋กœ HTTP 409๋ฅผ ๋ฐœ์ƒ์‹œํ‚ค๋Š” ๋ฐฉ๋ฒ•์„ ์•Œ๊ณ  ์žˆ๋Š”์ง€ ํ™•์ธํ•˜์‹ญ์‹œ์˜ค.

์‹ค์ œ๋กœ autor๊ฐ€ "์ˆ˜์ •ํ•˜์ง€ ์•Š์Œ"์œผ๋กœ ํ‘œ์‹œ๋œ ๋ฌธ์ œ๋กœ ์ธํ•ด django-concurrency ๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. obj.save(update_fields=['one', 'two', 'tree']) ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์ž˜๋ชป๋œ ๊ด€ํ–‰์„ ๋ฐœ๊ฒฌํ•˜์—ฌ OOL์„ ์šฐํšŒํ•˜๋ฏ€๋กœ ํŒจํ‚ค์ง€๋ฅผ ๋ถ„๊ธฐํ–ˆ์Šต๋‹ˆ๋‹ค.

์—ฌ๊ธฐ์— ์•ž์„œ ์–ธ๊ธ‰ํ•œ serializer์˜ ๋ˆ„๋ฝ๋œ save ๋ฉ”์„œ๋“œ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ๋ชจ๋“  ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

    def save(self, **kwargs):
        try:
            self.instance = super(VersionModelSerializer, self).save(**kwargs)
            return self.instance
        except VersionException:
            # Use select_for_update so we have some level of guarantee
            # that object won't be modified at least here at the same time
            # (but it may be modified somewhere else, where select_for_update
            # is not used!)
            with transaction.atomic():
                db_instance = self.instance.__class__.objects.\
                    select_for_update().get(pk=self.instance.pk)
                diff = self._get_serializer_diff(db_instance)

                # re-raise exception, so api client will receive friendly
                # printed diff with writable fields of current serializer
                if diff:
                    raise VersionException(diff)

                # otherwise re-try saving using db_instance
                self.instance = db_instance
                if self.is_valid():
                    return super(VersionModelSerializer, self).save(**kwargs)
                else:
                    # there are errors that could not be displayed to a user
                    # so api client should refresh & retry by itself
                    raise VersionException

        # instance.save() was interrupted by application error
        except ApplicationException as logic_exc:
            if self._initial_version != self.instance._version:
                raise VersionException

            raise logic_exc

์ฃ„์†กํ•ฉ๋‹ˆ๋‹ค. ๋‚˜๋Š” ๋‹น์‹ ์ดํ•˜๊ณ ์žˆ๋Š” ์ผ์„ ํŒŒ์•…ํ•˜๊ธฐ ์œ„ํ•ด ๋‹น์‹ ์˜ ์ฝ”๋“œ๋ฅผ ์ฝ์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค. ์‹œ๋ฆฌ์–ผ๋ผ์ด์ €๋ฅผ ๋ดค์Šต๋‹ˆ๋‹ค. ๋ถ„๋ช…ํžˆ ์ง๋ ฌ ๋ณ€ํ™˜๊ธฐ๋ฅผ ํ•ดํ‚นํ•˜์—ฌ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ์ง€๋งŒ ๊ทธ๋Ÿด ํ•„์š”๋Š” ์—†์Šต๋‹ˆ๋‹ค. DRF ๋…ผ๋ฆฌ์˜ ๊ฒฐํ•จ์€ ๊ทธ ์ž์ฒด๋กœ ์กด์žฌํ•˜๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. ๋‚˜๋Š” ๋‹จ์ง€ ์š”์ ์„ ์„ค๋ช…ํ•˜๊ธฐ ์œ„ํ•ด OOL์„ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

๊ทธ๋ฆฌ๊ณ  ์ตœ์‹  ๋ฒ„์ „์˜ django-concurrency์— ๋Œ€ํ•ด ํ•ด๋‹น ์ฝ”๋“œ๋ฅผ ์‹œ๋„ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค( IGNORE_DEFAULT=False ์‚ฌ์šฉ). django-concurrency๋„ ๊ธฐ๋ณธ๊ฐ’์„ ๋ฌด์‹œํ–ˆ๋Š”๋ฐ ํŒจ์น˜๋ฅผ ์ œ์ถœํ–ˆ์Šต๋‹ˆ๋‹ค. ์ •์ƒ์ ์ธ ๊ฒฝ์šฐ์— ์ž‘๋™ํ•˜๋„๋ก ํ•˜๊ธฐ ์œ„ํ•ด ์ถ”์ ํ•ด์•ผ ํ•˜๋Š” ์ด์ƒํ•œ ์ฝ”๋„ˆ ์ผ€์ด์Šค๊ฐ€ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.

ํ•ดํ‚น์ด ์•„๋‹ˆ๋ผ ๊ธฐ๋ณธ ๊ธฐ๋Šฅ ํ™•์žฅ์ด๋ผ๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค. ์ด๋Ÿฌํ•œ ๊ธฐ๋Šฅ ์ง€์›์„ ์œ„ํ•œ ์ตœ์ ์˜ ์žฅ์†Œ๋Š” django-concurrency ํŒจํ‚ค์ง€๋ผ๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค.

๋‚˜๋Š” ์ „์ฒด ๋ฌธ์ œ ํ† ๋ก ์„ ๋‹ค์‹œ ์ฝ์—ˆ๊ณ  ๋‹น์‹ ์˜ ์ œ์•ˆ์ด ๋„ˆ๋ฌด ๊ด‘๋ฒ”์œ„ํ•˜๊ณ  ๋งŽ์€ ๊ณณ์—์„œ ์‹คํŒจํ•  ๊ฒƒ์ด๋ผ๋Š” ๊ฒƒ์„ ๋ฐœ๊ฒฌํ–ˆ์Šต๋‹ˆ๋‹ค(๋‹ค๋ฅธ ์กฐ๊ฑด์—์„œ ๋‹ค๋ฅธ ์†Œ์Šค์˜ ๊ธฐ๋ณธ๊ฐ’์„ ๋งˆ์ˆ ์ฒ˜๋Ÿผ ์‚ฌ์šฉํ•˜๊ธฐ ๋•Œ๋ฌธ์—). DRF 3.x๋Š” 2.x๋ณด๋‹ค ํ›จ์”ฌ ์‰ฝ๊ณ  ์˜ˆ์ธก ๊ฐ€๋Šฅํ•˜๋ฏ€๋กœ ๊ทธ๋Œ€๋กœ ์œ ์ง€ํ•ฉ๋‹ˆ๋‹ค. :)

์ง๋ ฌ ๋ณ€ํ™˜๊ธฐ(๋ชจ๋ธ์— ๋„๋‹ฌํ•˜๊ธฐ ์ „์—)์—์„œ ๊นจ์กŒ๊ธฐ ๋•Œ๋ฌธ์— ๋ชจ๋ธ ๊ณ„์ธต์—์„œ ์ด๊ฒƒ์„ ๊ณ ์น  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. OOL์„ ์ œ์ณ๋‘๊ณ ... default ๊ฐ€ ์„ค์ •๋œ ๊ฒฝ์šฐ ํ•„๋“œ๊ฐ€ ํ•„์š”ํ•˜์ง€ ์•Š์€ ์ด์œ ๋Š” ๋ฌด์—‡์ž…๋‹ˆ๊นŒ?

๋ถ€๋ถ„์ ์ด์ง€ ์•Š์€ ์ง๋ ฌ ๋ณ€ํ™˜๊ธฐ๋Š” (๊ธฐ๋ณธ์ ์œผ๋กœ) ๋ชจ๋“  ํ•„๋“œ๋ฅผ "์š”๊ตฌ"ํ•˜์ง€๋งŒ ์šฐ๋ฆฌ๋Š” ์ด๊ฒƒ์„ ์ƒ๋žตํ•ฉ๋‹ˆ๋‹ค. ๋ฒ„๊ทธ์ธ๊ฐ€์š”? ์•„๋‹ˆ๋ฉด ๋…ผ๋ฆฌ์ ์ธ ์ด์œ ๊ฐ€ ์žˆ์Šต๋‹ˆ๊นŒ?

๋‚ด ์ฝ”๋“œ ์˜ˆ์ œ์—์„œ ๋ณผ ์ˆ˜ ์žˆ๋“ฏ์ด _version ํ•„๋“œ๋Š” ๊ฐ€๋Šฅํ•œ ๋ชจ๋“  ๊ฒฝ์šฐ์— ํ•ญ์ƒ ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

btw, ๋‚ด๊ฐ€ https://github.com/gavinwahl/django-optimistic-lock ์—์„œ ๋ชจ๋ธ lvl ์ฝ”๋“œ๋ฅผ ๋นŒ๋ ธ๊ณ  django-concurrency ์—์„œ ๋นŒ๋ฆฐ ๊ฒƒ์ด ์•„๋‹ˆ๋ผ ๊ฑฐ์˜ ์ด์œ  ์—†์ด ๋ณต์žกํ•˜๋‹ค๋Š” ๊ฒƒ์ด ๋ฐํ˜€์กŒ์Šต๋‹ˆ๋‹ค.

... ๊ทธ๋ž˜์„œ ๋ฒ„๊ทธ๋Š” "๋น„๋ถ€๋ถ„ ์ง๋ ฌ ๋ณ€ํ™˜๊ธฐ๊ฐ€ ์ผ๋ถ€ ํ•„๋“œ๋ฅผ ํ•„์ˆ˜๊ฐ€ ์•„๋‹Œ ๊ฒƒ์œผ๋กœ ์ž˜๋ชป ์„ค์ •ํ–ˆ์Šต๋‹ˆ๋‹ค"์ž…๋‹ˆ๋‹ค. ๊ทธ๊ฒŒ ๋Œ€์•ˆ์ž…๋‹ˆ๋‹ค. ๊ทธ๊ฒƒ์ด ๋ถ€๋ถ„์ ์ด์ง€ ์•Š์€ ์ง๋ ฌ ๋ณ€ํ™˜๊ธฐ๊ฐ€ ๋งŒ๋“œ๋Š” (์•”์‹œ์ ) ์•ฝ์†์ด๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.

๋‚˜๋Š” ๊ทธ๊ฒƒ์„ ์ธ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค:

๊ธฐ๋ณธ์ ์œผ๋กœ ์ง๋ ฌ ๋ณ€ํ™˜๊ธฐ๋Š” ๋ชจ๋“  ํ•„์ˆ˜ ํ•„๋“œ์— ๋Œ€ํ•œ ๊ฐ’์„ ์ „๋‹ฌํ•ด์•ผ ํ•˜๋ฉฐ ๊ทธ๋ ‡์ง€ ์•Š์œผ๋ฉด ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.

์ด๊ฒƒ์€ ํ•„์ˆ˜ ์‚ฌํ•ญ์— ๋Œ€ํ•ด ์•„๋ฌด ๊ฒƒ๋„ ๋งํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค(๊ธฐ๋ณธ๊ฐ’์ด ์ œ๊ณต๋˜๋Š” ๊ฒฝ์šฐ ์ œ์™ธ).

(๊ทธ๋ฆฌ๊ณ  ๋‘ ๊ฐ€์ง€ ๋‹ค๋ฅธ ์ˆ˜์ค€์— ๋Œ€ํ•ด ์ด์•ผ๊ธฐํ•˜๊ณ  ์žˆ๋‹ค๋Š” ๊ฒƒ์„ ์•Œ์ง€๋งŒ ModelSerializer๋Š” ํ•ด๋‹น ๊ฒฐ์ •์— ๋Œ€ํ•œ ์ฑ…์ž„์„ ์ง€์ง€ ์•Š๋Š” ๊ฒฝ์šฐ ํ•„๋“œ๋ฅผ ์š”๊ตฌํ•˜์ง€ ์•Š์•„์•ผ ํ•ฉ๋‹ˆ๋‹ค.)

์š”์ ์„ ์žƒ์€ ๊ฒƒ ๊ฐ™์•„์š”..

(๊ทธ๋ฆฌ๊ณ  ๋‘ ๊ฐ€์ง€ ๋‹ค๋ฅธ ์ˆ˜์ค€์— ๋Œ€ํ•ด ์ด์•ผ๊ธฐํ•˜๊ณ  ์žˆ๋‹ค๋Š” ๊ฒƒ์„ ์•Œ์ง€๋งŒ ModelSerializer๋Š” ํ•ด๋‹น ๊ฒฐ์ •์— ๋Œ€ํ•œ ์ฑ…์ž„์„ ์ง€์ง€ ์•Š๋Š” ๊ฒฝ์šฐ ํ•„๋“œ๋ฅผ ์š”๊ตฌํ•˜์ง€ ์•Š์•„์•ผ ํ•ฉ๋‹ˆ๋‹ค.)

๊ทธ๊ฒŒ ๋ฌด์Šจ ๋ฌธ์ œ์•ผ?

์•Œ๊ฒ ์Šต๋‹ˆ๋‹ค. ๋‹ค๋ฅธ ๊ฐ๋„๋กœ ์‹œ๋„ํ•ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

  • ๋‚ด ๋ชจ๋ธ์˜ ๋ชจ๋“  ํ•„๋“œ๋ฅผ ๋‹ค๋ฃจ๋Š” ๋ถ€๋ถ„์ ์ด์ง€ ์•Š์€ ๋ชจ๋ธ ์ง๋ ฌ ๋ณ€ํ™˜๊ธฐ(ํŽธ์ง‘: ๋ชจ๋“  ๊ธฐ๋ณธ๊ฐ’)๊ฐ€ ์žˆ๋‹ค๊ณ  ๊ฐ€์ •ํ•ฉ๋‹ˆ๋‹ค.

๋™์ผํ•œ ๋ฐ์ดํ„ฐ๋กœ CREATE ๋˜๋Š” UPDATE๊ฐ€ ๋‹ค๋ฅธ ๊ฐœ์ฒด๋ฅผ ์ƒ์„ฑํ•ด์•ผ ํ•ฉ๋‹ˆ๊นŒ(ID ์ œ์™ธ)

์ •๋ง ๊ฐ„๋‹จํ•œ ๋ชจ๋ธ ๋ฐ ์ง๋ ฌ ๋ณ€ํ™˜๊ธฐ์™€ ์‹คํŒจ/์˜ˆ์ƒ ๋™์ž‘์„ ๋ณด์—ฌ์ฃผ๋Š” ๋ช‡ ์ค„์„ ์‚ฌ์šฉํ•˜์—ฌ ์•„์ด๋””์–ด๋ฅผ ์„ค๋ช…ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๊นŒ?

์˜ค๋Š˜์€ ์‹œ๊ฐ„์ด ๋Šฆ์–ด์ ธ์„œ ๋‚ด์ผ ์ •๋ฆฌํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค... ํ•˜์ง€๋งŒ ๊นŠ์ด ๋“ค์–ด๊ฐˆ์ˆ˜๋ก #3648์ด non-partial serializer์— ๋” ์ ํ•ฉํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋™์•ˆ ModelSerializer๊ฐ€ ๋ชจ๋ธ์˜ ๋ชจ๋“  ํ•„๋“œ๋ฅผ ์š”๊ตฌํ•˜์ง€ ์•Š๋Š” ์ด์œ ๋Š” ๋ฌด์—‡์ž…๋‹ˆ๊นŒ? ๋‹น์‹ ์˜ ๋…ผ๋ฆฌ๊ฐ€ ๋‚˜์™€ ๋‹ค๋ฅผ ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค.

ModelSerializer๋Š” ๊ฒฝ๊ณ„ ๋ชจ๋ธ์„ ๊ฒ€์‚ฌํ•˜๊ณ  ํ•„์š”ํ•œ์ง€ ์—ฌ๋ถ€๋ฅผ ๊ฒฐ์ •ํ•˜์ง€ ์•Š์Šต๋‹ˆ๊นŒ?

๋‚˜๋Š” ๊ธฐ๊ณ„์ ์œผ๋กœ ๋ฐฉ๋ฒ• ์„ ์˜๋ฏธํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. non-partial Serializer์˜ ๊ธฐ๋ณธ ๊ฐ€์ •์€ ๋ชจ๋“  ๊ฒƒ์„ ์š”๊ตฌํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค(์œ„์—์„œ ์ธ์šฉ). get_field_kwargs ๊ฐ€ ์ด ๊ฐ€์ •(ํŠนํžˆ ์—ฌ๊ธฐ )์—์„œ ๋ฒ—์–ด๋‚  ๊ฒฝ์šฐ์—๋Š” ํƒ€๋‹นํ•œ ์ด์œ ๊ฐ€ ์žˆ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ ์ด์œ ๋Š” ๋ฌด์—‡์ž…๋‹ˆ๊นŒ?

๋‚ด๊ฐ€ ์„ ํ˜ธํ•˜๋Š” ๋Œ€๋‹ต์€ "์ œ์ถœ๋œ ๊ฐ’์ด ์—†์œผ๋ฉด ํ•ด๋‹น ๊ธฐ๋ณธ๊ฐ’์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์—" ๊ณ„์† ์ œ๊ณตํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค(๊ทธ๋Ÿฌ๋‚˜ DRF๋Š” ์‹ค์ œ๋กœ ๊ธฐ๋ณธ๊ฐ’์„ ์‚ฌ์šฉํ•ด์•ผ ํ•จ). ๋‚ด๊ฐ€ ๋†“์นœ ๋˜ ๋‹ค๋ฅธ ๋Œ€๋‹ต์ด ์žˆ์Šต๋‹ˆ๊นŒ? ๊ธฐ๋ณธ๊ฐ’์ด ์žˆ๋Š” ํ•„๋“œ๊ฐ€ ํ•„์š”ํ•˜์ง€ ์•Š์€ ์ด์œ ๋Š” ๋ฌด์—‡์ž…๋‹ˆ๊นŒ?

๋ถ„๋ช…ํžˆ ์ €๋Š” "์™„์ „ํ•œ" ์†”๋ฃจ์…˜์„ ์„ ํ˜ธํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ๋‘ ๋ฒˆ์งธ ๋‹ต๋ณ€์ด ์žˆ์Œ์„ ์ธ์ •ํ•ฉ๋‹ˆ๋‹ค. ๊ธฐ๋ณธ์ ์œผ๋กœ ์ด๋Ÿฌํ•œ ํ•„๋“œ๋ฅผ ์š”๊ตฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๊ฒƒ์€ (ํ˜„์žฌ ์ž„์˜์ ์ธ) ํŠน๋ณ„ํ•œ ๊ฒฝ์šฐ๋ฅผ ์ œ๊ฑฐํ•ฉ๋‹ˆ๋‹ค. ์ฝ”๋“œ๋ฅผ ๋‹จ์ˆœํ™”/์ถ•์†Œํ•ฉ๋‹ˆ๋‹ค. ๋‚ด๋ถ€์ ์œผ๋กœ ์ผ๊ด€์„ฑ์ด ์žˆ์Šต๋‹ˆ๋‹ค. ๋‚ด ์šฐ๋ ค๋ฅผ ํ•ด๊ฒฐํ•ฉ๋‹ˆ๋‹ค.

๊ธฐ๋ณธ์ ์œผ๋กœ non-partial serializer๋ฅผ non-partial๋กœ ๋งŒ๋“ญ๋‹ˆ๋‹ค.

์ด์ œ ๋‚˜๋Š” ์ ์–ด๋„ ๋‹น์‹ ์ด ์˜๋ฏธํ•˜๋Š” ๋ฐ”๋ฅผ ์•Œ๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋Ÿฌํ•œ ๊ฒฝ์šฐ ModelForm ๋™์ž‘์ด ๋ฌด์—‡์ธ์ง€ ํ™•์ธํ–ˆ์Šต๋‹ˆ๊นŒ? (๋ชจ๋ฐ”์ผ์—์„œ๋Š” ์ง์ ‘ ํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค)

Django ๋ฌธ์„œ์— ๋”ฐ๋ฅด๋ฉด '๊ณต๋ฐฑ'์€ ํ•„๋“œ๊ฐ€ ํ•„์ˆ˜์ธ์ง€ ์—ฌ๋ถ€๋ฅผ ์ œ์–ดํ•ฉ๋‹ˆ๋‹ค. ์ด ๋ฌธ์ œ์—๋Š” ๊ด€๋ จ ์—†๋Š” ์˜๊ฒฌ์ด ๋งŽ์ด ํฌํ•จ๋˜์–ด ์žˆ์œผ๋ฏ€๋กœ ์ด ๋ฌธ์ œ์— ๋Œ€ํ•ด ๋ณ„๋„์˜ ํ‹ฐ์ผ“์„ ์—ฌ๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค. ๋‚ด ์ƒ๊ฐ์— modelserializer๋Š” modelform: ๋นˆ ์˜ต์…˜ ์ œ์–ด๊ฐ€ ํ•„์š”ํ•˜๊ณ  'null'์€ None์ด ํ—ˆ์šฉ ๊ฐ€๋Šฅํ•œ ์ž…๋ ฅ์ด๊ณ  'default'๊ฐ€ ํ•ด๋‹น ๋…ผ๋ฆฌ์— ์˜ํ–ฅ์„ ๋ฏธ์น˜์ง€ ์•Š๋Š”์ง€ ์•Œ๋ ค์ค๋‹ˆ๋‹ค.

๋‘ ๋ฒˆ์งธ ํ‹ฐ์ผ“์„ ์—ด ์˜ํ–ฅ์ด ์žˆ์ง€๋งŒ ๊ณต๋ฐฑ์— ๋น„์Šทํ•œ ์ฝ”๋“œ๊ฐ€ ํ•„์š”ํ•œ์ง€ ๊ฑฑ์ •๋ฉ๋‹ˆ๋‹ค. django ํ† ๋ก  ๊ทธ๋ฃน ์—์„œ :

๊ธฐ์กด ๋ชจ๋ธ ์–‘์‹๊ณผ ์ž‘๋™ํ•˜๋Š” ํ…œํ”Œ๋ฆฟ์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ ๋ชจ๋ธ์— ์„ ํƒ์  ๋ฌธ์ž ํ•„๋“œ๋ฅผ ์ถ”๊ฐ€ํ•˜์ง€๋งŒ HTML ํ…œํ”Œ๋ฆฟ์— ํ•ด๋‹น ํ•„๋“œ๋ฅผ ์ถ”๊ฐ€ํ•˜์ง€ ๋ชปํ•ฉ๋‹ˆ๋‹ค(์˜ˆ: ์ธ์  ์˜ค๋ฅ˜, ํ…œํ”Œ๋ฆฟ์„ ์žŠ์–ด๋ฒ„๋ ธ๊ฑฐ๋‚˜ ํ…œํ”Œ๋ฆฟ ์ž‘์„ฑ์ž์—๊ฒŒ ๋ณ€๊ฒฝ, ํ…œํ”Œ๋ฆฟ์— ๋ณ€๊ฒฝ์ด ํ•„์š”ํ•œ์ง€ ๊นจ๋‹ซ์ง€ ๋ชปํ•จ), ํ•ด๋‹น ์–‘์‹์ด ์ œ์ถœ๋  ๋•Œ Django๋Š” ์‚ฌ์šฉ์ž๊ฐ€ ๋ˆ„๋ฝ๋œ ํ•„๋“œ์— ๋Œ€ํ•ด ๋นˆ ๋ฌธ์ž์—ด ๊ฐ’์„ ์ œ๊ณตํ–ˆ๋‹ค๊ณ  ๊ฐ€์ •ํ•˜๊ณ  ์ด๋ฅผ ๋ชจ๋ธ์— ์ €์žฅํ•˜์—ฌ ๊ธฐ์กด ๊ฐ’์„ ์ง€์›๋‹ˆ๋‹ค. .

์ผ๊ด€์„ฑ์„ ์œ„ํ•ด ์šฐ๋ฆฌ๋Š” ๊ณ„์•ฝ์˜ ํ›„๋ฐ˜๋ถ€๋ฅผ ์ดํ–‰ํ•  ์˜๋ฌด๊ฐ€ ์žˆ์œผ๋ฉฐ ๋ถ€์žฌ ๊ฐ’์„ ๊ณต๋ฐฑ์œผ๋กœ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ ๋ชจ๋ธ์— ๋Œ€ํ•œ ์ฐธ์กฐ ์—†์ด ๊ณต๋ฐฑ์„ ์ฑ„์šธ ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ๋ฌธ์ œ๊ฐ€ ์•ฝ๊ฐ„ ์ ์ง€๋งŒ ๋งค์šฐ ์œ ์‚ฌํ•ฉ๋‹ˆ๋‹ค(๋‚ด ์ƒ๊ฐ์— #3648๊ณผ ์ผ์น˜ํ•จ).

@tomchristie ์— ๋Œ€ํ•ด ๊ฐ„๋žตํ•œ ์ •๋ณด๋ฅผ ์ œ๊ณตํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. required ์ƒํƒœ๊ฐ€ ๋ชจ๋ธ ํ•„๋“œ defaults ์†์„ฑ์— ์˜์กดํ•˜๋Š” ์ด์œ ๋Š” ๋ฌด์—‡์ž…๋‹ˆ๊นŒ?

ํ•„์ˆ˜ ์ƒํƒœ๊ฐ€ ๋ชจ๋ธ ํ•„๋“œ ๊ธฐ๋ณธ ์†์„ฑ์— ์˜์กดํ•˜๋Š” ์ด์œ ๋Š” ๋ฌด์—‡์ž…๋‹ˆ๊นŒ?

๊ฐ„๋‹จํžˆ ๋งํ•ด์„œ: ๋ชจ๋ธ ํ•„๋“œ์— ๊ธฐ๋ณธ๊ฐ’์ด ์žˆ๋Š” ๊ฒฝ์šฐ ์ž…๋ ฅ์œผ๋กœ ์ œ๊ณตํ•˜๋Š” ๊ฒƒ์„ ์ƒ๋žตํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์‚ฌ์‹ค ๋‚˜๋Š” ์ด ํ–‰๋™์— ๋™์˜ํ•œ๋‹ค. ์ฝ”๋“œ์—๋„ ๋ถˆ๊ตฌํ•˜๊ณ  ModelForm์€ ๋™์ผํ•œ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค(์ƒ์„ฑ๋œ html์ด ๊ธฐ๋ณธ๊ฐ’์„ ์ œ๊ณตํ•จ). DRF๊ฐ€ ๋‹ค๋ฅธ ๋…ผ๋ฆฌ๋ผ๋ฉด '๊ธฐ๋ณธ๊ฐ’'์€ ์ ˆ๋Œ€ ์ ์šฉ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ–ˆ์Šต๋‹ˆ๋‹ค.

@pySilver ์‹ค์ œ๋กœ ๋‹ค์Œ์€ ModelForm ๋™์ž‘์ž…๋‹ˆ๋‹ค.

# models.py

from django.db import models

class MyModel(models.Model):
    no_default = models.CharField(max_length=100)
    has_default = models.CharField(max_length=100, default="iAmTheDefault")

๋ช…ํ™•์„ฑ์„ ์œ„ํ•ด _update_๊ฐ€ ๋ถ€๋ถ„์ ์ด๊ธฐ ๋•Œ๋ฌธ์— ๋ฌผ๊ฑด์˜ ์ด๋ฆ„์€ ์—ฌ์ „ํžˆ โ€‹โ€‹"๋ถ€๋ถ„์ "์ž…๋‹ˆ๋‹ค. ๋˜ํ•œ ์™„์ „ํ•œ("์ „์ฒด") ์—…๋ฐ์ดํŠธ๋ฅผ ํ…Œ์ŠคํŠธํ•˜๊ณ  ์žˆ์—ˆ์ง€๋งŒ ๋™์ž‘์„ ํ‘œ์‹œํ•˜๋Š” ๋ฐ ์ฝ”๋“œ๊ฐ€ ํ•„์š”ํ•˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค.

# in manage.py shell
>>> from django import forms
>>> from django.conf import settings
>>> from form_serializer.models import MyModel
>>>
>>> class MyModelForm(forms.ModelForm):
...     class Meta:
...         model = MyModel
...         fields = ['no_default', 'has_default']
...
>>>
>>> partial = MyModel.objects.create()
>>> partial.id = 2
>>> partial.no_default = "Must replace me"
>>> partial.has_default = "I should be replaced"
>>> partial.save()
>>>
>>>
>>> POST_PARTIAL = {
...     "id": 2,
...     "no_default": "must change me",
... }
>>>
>>>
>>> form_partial = MyModelForm(POST_PARTIAL)
>>> form_partial.is_valid()
False
>>> form_partial._errors
{'has_default': [u'This field is required.']}

ModelForm์€ ๊ธฐ๋ณธ๊ฐ’์ด ์žˆ๋‹ค๊ณ  ์ƒ๊ฐํ•˜๋”๋ผ๋„ ์ด ์ž…๋ ฅ์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค . ์ด๊ฒƒ์€ ๋‚ด๋ถ€์ ์œผ๋กœ ์ผ๊ด€๋œ ๋‘ ๊ฐ€์ง€ ๋™์ž‘ ์ค‘ ํ•˜๋‚˜์ž…๋‹ˆ๋‹ค.

ํ•„์ˆ˜ ์ƒํƒœ๊ฐ€ ๋ชจ๋ธ ํ•„๋“œ ๊ธฐ๋ณธ ์†์„ฑ์— ์˜์กดํ•˜๋Š” ์ด์œ ๋Š” ๋ฌด์—‡์ž…๋‹ˆ๊นŒ?

๊ฐ„๋‹จํžˆ ๋งํ•ด์„œ: ๋ชจ๋ธ ํ•„๋“œ์— ๊ธฐ๋ณธ๊ฐ’์ด ์žˆ๋Š” ๊ฒฝ์šฐ ์ž…๋ ฅ์œผ๋กœ ์ œ๊ณตํ•˜๋Š” ๊ฒƒ์„ ์ƒ๋žตํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

@tomchristie ๋Š” ์›์น™์ ์œผ๋กœ ๋™์˜ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ์˜ˆ์ƒ๋˜๋Š” ๋™์ž‘์€ ๋ฌด์—‡์ž…๋‹ˆ๊นŒ?

  • ์ƒ์„ฑํ•  ๋•Œ ๊ธฐ๋ณธ๊ฐ’์„ ์–ป์Šต๋‹ˆ๋‹ค(์‚ฌ์†Œํ•˜์ง€๋งŒ ๋ชจ๋‘๊ฐ€ ์ด๊ฒƒ์ด ์˜ณ๋‹ค๋Š” ๋ฐ ๋™์˜ํ•ฉ๋‹ˆ๋‹ค).
  • ์—…๋ฐ์ดํŠธ ์‹œ ๋ฌด์—‡์„ ๋ฐ›์•„์•ผ ํ•ฉ๋‹ˆ๊นŒ?

์—…๋ฐ์ดํŠธ์‹œ์—๋„ ๊ธฐ๋ณธ๊ฐ’์„ ๊ฐ€์ ธ์™€์•ผ ํ•  ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค. ๋ถ€๋ถ„์ ์ด์ง€ ์•Š์€ ์ง๋ ฌ ๋ณ€ํ™˜๊ธฐ๊ฐ€ ๋‘ ๊ฒฝ์šฐ์— ๋‹ค๋ฅด๊ฒŒ ์ž‘๋™ํ•ด์•ผ ํ•˜๋Š” ์ด์œ ๋ฅผ ๋ชจ๋ฅด๊ฒ ์Šต๋‹ˆ๋‹ค. ๋ถ€๋ถ„์ ์ด์ง€ ์•Š๋‹ค๋Š” ๊ฒƒ์€ "์™„์ „ํ•œ" ๋ ˆ์ฝ”๋“œ๋ฅผ ๋ณด๋‚ด๊ณ  ์žˆ์Œ์„ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ ์™„์ „ํ•œ ๊ธฐ๋ก์„ ๊ต์ฒดํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

์—…๋ฐ์ดํŠธ ์‹œ ๊ฐ’์ด ์ œ๊ณต๋˜์ง€ ์•Š์œผ๋ฉด ๊ฐ’์ด ๋ณ€๊ฒฝ๋˜์ง€ ์•Š์„ ๊ฒƒ์œผ๋กœ ์˜ˆ์ƒํ•ฉ๋‹ˆ๋‹ค. ์š”์ ์€ ์•Œ์ง€๋งŒ ๊ธฐ๋ณธ๊ฐ’์œผ๋กœ ํˆฌ๋ช…ํ•˜๊ฒŒ ๋ฎ์–ด์“ฐ๋Š” ๊ฒƒ์€ ๋‚ด POV์—์„œ ์ง๊ด€์ ์ด์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

(์ œ๊ฐ€ ์ƒ๊ฐํ•˜๊ธฐ์— ๋ชจ๋“  ์—…๋ฐ์ดํŠธ๊ฐ€ ๋ชจ๋“  ํ•„๋“œ์— ๋Œ€ํ•ด ๋ถ€๋ถ„์  ์˜๋ฏธ๋ก ์„ ๊ฐ–๋Š” ๊ฒƒ์ด ์‹ค์ œ๋กœ ๋” ๋‚˜์„ ๊ฒƒ์ด๋ผ๊ณ  ์ƒ๊ฐํ•˜๋Š” ๊ฒฝ์šฐ - PUT์€ ์—ฌ์ „ํžˆ โ€‹โ€‹๋ฉฑ๋“ฑ์„ฑ์ด๊ณ  ์ด๋Š” ์ค‘์š”ํ•œ ์ธก๋ฉด์ด๋ฉฐ ์ฃผ์–ด์ง„ ํ˜„์žฌ ๋™์ž‘์„ ๋ณ€๊ฒฝํ•˜๋Š” ๊ฒƒ์ด ์–ด์ƒ‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค)

๋‚˜๋Š” ํ™•์‹คํžˆ ๋‹น์‹ ์—๊ฒŒ ์„ ํ˜ธ๋„๋ฅผ ๊ณต์œ ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์˜๋„์ ์œผ๋กœ ๋‹ค๋ฅด๊ฒŒ ๋งŒ๋“ค์ง€ ์•Š๋Š” ํ•œ ๋ชจ๋“  ์ธํ„ฐํŽ˜์ด์Šค๊ฐ€ ์—„๊ฒฉํ•˜๊ธฐ๋ฅผ ์›ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ PARTIAL ๋Œ€ NON-PARTIAL ๊ตฌ๋ถ„์€ ์ด๋ฏธ (์ด๋ก ์ ์œผ๋กœ) ์šฐ๋ฆฌ ๋‘˜ ๋‹ค ์›ํ•˜๋Š” ๊ฒƒ์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

๋‚˜๋Š” ๋ถ€๋ถ„์ด ๋‹น์‹ ์ด ์›ํ•˜๋Š”๋Œ€๋กœ ์ •ํ™•ํ•˜๊ฒŒ ํ–‰๋™ํ•œ๋‹ค๊ณ  โ€‹โ€‹๋ฏฟ์Šต๋‹ˆ๋‹ค.

  • ์—…๋ฐ์ดํŠธ๋Š” 100% ๋ถ€๋ถ„์ ์ž…๋‹ˆ๋‹ค.
  • CREATEs(๋‚ด ์ƒ๊ฐ์—)๋Š” default ๋ฐ blank (๋…ผ๋ฆฌ์  ์˜ˆ์™ธ)์™€ ๊ด€๋ จํ•˜์—ฌ ๋ถ€๋ถ„์ ์ž…๋‹ˆ๋‹ค. ๋‹ค๋ฅธ ๋ชจ๋“  ๊ฒฝ์šฐ์—๋Š” ๋ชจ๋ธ/๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ œ์•ฝ ์กฐ๊ฑด์ด ๋ฐ”์ธ๋”ฉ๋ฉ๋‹ˆ๋‹ค.

๋น„ ๋ถ€๋ถ„ ์ง๋ ฌ ๋ณ€ํ™˜๊ธฐ์—์„œ ์ผ๊ด€์„ฑ์„ ์–ป์œผ๋ ค๊ณ ํ•ฉ๋‹ˆ๋‹ค. default ์— ๋Œ€ํ•œ ํŠน์ˆ˜ ์‚ฌ๋ก€๋ฅผ ์ œ๊ฑฐํ•˜๋ฉด ๊ธฐ์กด์˜ ๋ถ€๋ถ„์ ์ด์ง€ ์•Š์€ ์ง๋ ฌ ๋ณ€ํ™˜๊ธฐ๊ฐ€ ๋‚ด๊ฐ€ ์›ํ•˜๋Š” ์—„๊ฒฉํ•œ ์ง๋ ฌ ๋ณ€ํ™˜๊ธฐ๊ฐ€ ๋ฉ๋‹ˆ๋‹ค. ModelForm๊ณผ๋„ ๋™๋“ฑํ•ฉ๋‹ˆ๋‹ค.

๋‚˜๋Š” ์ด๊ฒƒ์ด ํ”„๋กœ์ ํŠธ ๋‚ด์—์„œ ์ž‘์€ ๋ถˆ์—ฐ์†์„ฑ์„ ๋งŒ๋“ ๋‹ค๋Š” ๊ฒƒ์„ ์•Œ๊ณ  ์žˆ์ง€๋งŒ ๋ˆ„๊ตฐ๊ฐ€๊ฐ€ ์ด์™€ ๊ฐ™์€ ๋ณ€๊ฒฝ์„ ํ•œ ๊ฒƒ์€ ์ด๋ฒˆ์ด ์ฒ˜์Œ์ด ์•„๋‹™๋‹ˆ๋‹ค. ํ˜„์žฌ ๋™์ž‘์„ ๊ธฐ๋ณธ๊ฐ’์œผ๋กœ ํ•˜๋Š” "๋ ˆ๊ฑฐ์‹œ" ํ”Œ๋ž˜๊ทธ๋ฅผ ์ถ”๊ฐ€ํ•˜๊ณ , ๊ฒฝ๊ณ (๊ธฐ๋ณธ ๋™์ž‘์ด ๋ณ€๊ฒฝ๋จ)๋ฅผ ์ถ”๊ฐ€ํ•˜๊ณ , ํ›„์† ์ฃผ์š” ๋ฆด๋ฆฌ์Šค์—์„œ ๊ธฐ๋ณธ๊ฐ’์„ ๋ณ€๊ฒฝํ•ฉ๋‹ˆ๋‹ค.

๋” ์ค‘์š”ํ•œ ๊ฒƒ์€ ์ง๋ ฌ ๋ณ€ํ™˜๊ธฐ๊ฐ€ Django์˜ ์ƒˆ๋กœ์šด ์‚ฌ์‹ค์ƒ์˜ ์—ญํ• ์„ ํ•˜๋„๋ก ํ•˜๋ ค๋ฉด ๊ฒฐ๊ตญ ์ด ๋ณ€๊ฒฝ์„ ์ˆ˜ํ–‰ํ•ด์•ผ ํ•œ๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ModelForm์—์„œ ๋ณ€ํ™˜ํ•˜๋Š” ์‚ฌ๋žŒ๋“ค์˜ ์ˆ˜๋Š” ๊ธฐ์กด ์‚ฌ์šฉ์ž ๊ธฐ๋ฐ˜์„ ํฌ๊ฒŒ ์ดˆ๊ณผํ•  ๊ฒƒ์ด๋ฉฐ ์ตœ์†Œํ•œ ์ด๋Ÿฌํ•œ ๋ณ€ํ™”๋ฅผ ์˜ˆ์ƒํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค.

๋‚ด ๋‘ ์„ผํŠธ ์‚ฝ์ž…:
@claytondaley์— ๋™์˜ํ•˜๋Š” ๊ฒฝํ–ฅ์ด ์žˆ์Šต๋‹ˆ๋‹ค. PUT์€ ๋ฉฑ๋“ฑ์› ๋ฆฌ์†Œ์Šค ๊ต์ฒด์ด๊ณ  PATCH๋Š” ๊ธฐ์กด ๋ฆฌ์†Œ์Šค์— ๋Œ€ํ•œ ์—…๋ฐ์ดํŠธ์ž…๋‹ˆ๋‹ค. ๋‹ค์Œ ์˜ˆ๋ฅผ ๋“ค์–ด๋ณด์„ธ์š”.

class Profiles(models.Model):
    username = models.CharField()
    role = models.CharField(default='member', choices=(
        ('member', 'Member'), 
        ('moderator', 'Moderator'),
        ('admin', 'Admin'), 
    ))

์ƒˆ ํ”„๋กœํ•„์—๋Š” ๊ธฐ๋ณธ ๊ตฌ์„ฑ์› ์—ญํ• ์ด ์žˆ์Šต๋‹ˆ๋‹ค. ๋‹ค์Œ ์š”์ฒญ์„ ๋ฐ›์•„๋ด…์‹œ๋‹ค.

POST /profiles username=moe
PUT /profiles/1 username=curly
PATCH /profiles/1 username=larry&role=admin
PUT /profiles/1 username=curly

ํ˜„์žฌ ์ƒํƒœ๋กœ ์ฒซ ๋ฒˆ์งธ PUT ์ดํ›„ ํ”„๋กœํ•„ ๋ฐ์ดํ„ฐ์—๋Š” {'username': 'curly', 'role': 'member'} ๊ฐ€ ํฌํ•จ๋ฉ๋‹ˆ๋‹ค. ๋‘ ๋ฒˆ์งธ PUT ํ›„์— {'username': 'curly', 'role': 'admin'} ๊ฐ€ ๋ฉ๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ ๋ฉฑ๋“ฑ์„ฑ์„ ๊นจ๋œจ๋ฆฌ์ง€ ์•Š์Šต๋‹ˆ๊นŒ? (์™„์ „ํžˆ ํ™•์‹คํ•˜์ง€ ์•Š์Œ - ํ•ฉ๋ฒ•์ ์œผ๋กœ ์š”์ฒญํ•จ)

ํŽธ์ง‘ํ•˜๋‹ค:
๋‚˜๋Š” ๋ชจ๋‘๊ฐ€ PATCH์˜ ์˜๋ฏธ์— ๋Œ€ํ•ด ๊ฐ™์€ ํŽ˜์ด์ง€์— ์žˆ๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค.

๋‘ ๋ฒˆ์งธ PUT ํ›„์—๋Š” {'username': 'curly', 'role': 'admin'}์ด ๋ฉ๋‹ˆ๋‹ค.

์ €๋Š” ๊ฐœ์ธ์ ์œผ๋กœ ์—ญํ• ์ด ๊ธฐ๋ณธ๊ฐ’์œผ๋กœ ๋‹ค์‹œ ์ „ํ™˜๋œ๋‹ค๋ฉด ๋†€๋ž„ ๊ฒƒ์ž…๋‹ˆ๋‹ค replace

์•„์ง ์‹ค์ œ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•œ ์ ์ด ์—†์Šต๋‹ˆ๋‹ค.

์—ฌ๊ธฐ๋„ ๋งˆ์ฐฌ๊ฐ€์ง€์ง€๋งŒ ์ง€๊ธˆ๊นŒ์ง€ ์šฐ๋ฆฌ ํ”„๋กœ์ ํŠธ๋Š” PATCH์— ์˜์กดํ–ˆ์Šต๋‹ˆ๋‹ค. :)
์ฆ‰, ๋™์‹œ์„ฑ์„ ์ฒ˜๋ฆฌํ•˜๊ธฐ ์œ„ํ•œ ๋ชจ๋ธ ๋ฒ„์ „ ๊ด€๋ฆฌ๊ฐ€ ํฌํ•จ๋œ OP์˜ ์‚ฌ์šฉ ์‚ฌ๋ก€๋Š” ๋‚˜์—๊ฒŒ ์˜๋ฏธ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. PUT์ด ๊ธฐ๋ณธ๊ฐ’(๊ฐ’์ด ์ƒ๋žต๋œ ๊ฒฝ์šฐ)์„ ์‚ฌ์šฉํ•˜์—ฌ ๋™์‹œ์„ฑ ์˜ˆ์™ธ๋ฅผ ๋ฐœ์ƒ์‹œํ‚ฌ ๊ฒƒ์œผ๋กœ ์˜ˆ์ƒํ•ฉ๋‹ˆ๋‹ค.

์ง๋ ฌ ๋ณ€ํ™˜๊ธฐ๊ฐ€ ๋ฐ˜๋“œ์‹œ RESTful RFC๋ฅผ ๋”ฐ๋ฅผ ํ•„์š”๋Š” ์—†๋‹ค๋Š” ์ ์„ ์ธ์ •ํ•˜๋Š” ๊ฒƒ์œผ๋กœ ์‹œ์ž‘ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ๊ทธ๋“ค์€ ์ตœ์†Œํ•œ ํ˜ธํ™˜ ๊ฐ€๋Šฅํ•œ ๋ชจ๋“œ๋ฅผ ์ œ๊ณตํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ํŠนํžˆ REST ์ง€์›์„ ์ œ๊ณตํ•˜๋Š” ํŒจํ‚ค์ง€์—์„œ๋Š” ๋”์šฑ ๊ทธ๋ ‡์Šต๋‹ˆ๋‹ค.

๋‚ด ์›๋ž˜ ์ฃผ์žฅ์€ ์ฒซ ๋ฒˆ์งธ ์›์น™์—์„œ ๋‚˜์˜จ ๊ฒƒ์ด์ง€๋งŒ RFC (์„น์…˜ 4.3.4)๋Š” ๊ตฌ์ฒด์ ์œผ๋กœ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๋งํ•ฉ๋‹ˆ๋‹ค(๊ฐ•์กฐ ์ถ”๊ฐ€๋จ).

POST์™€ PUT ๋ฉ”์†Œ๋“œ์˜ ๊ทผ๋ณธ์ ์ธ ์ฐจ์ด์ ์€ ๋™๋ด‰๋œ ํ‘œํ˜„์— ๋Œ€ํ•œ ๋‹ค๋ฅธ ์˜๋„๋กœ ๊ฐ•์กฐ๋ฉ๋‹ˆ๋‹ค. POST ์š”์ฒญ์˜ ๋Œ€์ƒ ๋ฆฌ์†Œ์Šค๋Š” ๋ฆฌ์†Œ์Šค ์ž์ฒด์˜ ์˜๋ฏธ์— ๋”ฐ๋ผ ํฌํ•จ๋œ ํ‘œํ˜„์„ ์ฒ˜๋ฆฌํ•˜๊ธฐ ์œ„ํ•œ ๋ฐ˜๋ฉด PUT ์š”์ฒญ์˜ ํฌํ•จ๋œ ํ‘œํ˜„ ์€ ๋Œ€์ƒ ๋ฆฌ์†Œ์Šค์˜ ์ƒํƒœ๋ฅผ ๊ต์ฒดํ•˜๋Š” ๊ฒƒ์œผ๋กœ ์ •์˜๋ฉ๋‹ˆ๋‹ค.
...
ํŽ˜์ด๋กœ๋“œ๊ฐ€ ๋ถ€๋ถ„์ ์ธ ์ฝ˜ํ…์ธ ์ผ ๊ฐ€๋Šฅ์„ฑ์ด ์žˆ์œผ๋ฏ€๋กœ ์ฃผ์–ด์ง„ ๋Œ€์ƒ ๋ฆฌ์†Œ์Šค์— ๋Œ€ํ•œ PUT์„ ํ—ˆ์šฉํ•˜๋Š” ์› ์„œ๋ฒ„๋Š” Content-Range ํ—ค๋” ํ•„๋“œ([RFC7233]์˜ ์„น์…˜ 4.2)๋ฅผ ํฌํ•จํ•˜๋Š” PUT ์š”์ฒญ์— ๋Œ€ํ•ด 400(์ž˜๋ชป๋œ ์š”์ฒญ) ์‘๋‹ต์„ ๋ณด๋‚ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

๋”ฐ๋ผ์„œ PUT์€ ๋ถ€๋ถ„์ ์ด์–ด์„œ๋Š” ์•ˆ ๋ฉ๋‹ˆ๋‹ค( ์—ฌ๊ธฐ๋„ ์ฐธ์กฐ). ๊ทธ๋Ÿฌ๋‚˜ PUT์˜ ์„น์…˜๋„ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์„ค๋ช…ํ•ฉ๋‹ˆ๋‹ค.

PUT ๋ฉ”์„œ๋“œ๋Š” ๋Œ€์ƒ ๋ฆฌ์†Œ์Šค์˜ ์ƒํƒœ๊ฐ€ ์ƒ์„ฑ๋˜๊ฑฐ๋‚˜ ์š”์ฒญ ๋ฉ”์‹œ์ง€ ํŽ˜์ด๋กœ๋“œ์— ํฌํ•จ๋œ ํ‘œํ˜„์— ์˜ํ•ด ์ •์˜๋œ ์ƒํƒœ๋กœ ๋Œ€์ฒด๋˜๋„๋ก ์š”์ฒญํ•ฉ๋‹ˆ๋‹ค. ์ฃผ์–ด์ง„ ํ‘œํ˜„์˜ ์„ฑ๊ณต์ ์ธ PUT์€ ๋™์ผํ•œ ๋Œ€์ƒ ๋ฆฌ์†Œ์Šค์— ๋Œ€ํ•œ ํ›„์† GET์ด 200(OK) ์‘๋‹ต์œผ๋กœ ์ „์†ก๋˜๋Š” ๋™๋“ฑํ•œ ํ‘œํ˜„์„ ์ดˆ๋ž˜ํ•  ๊ฒƒ์ž„์„ ์ œ์•ˆํ•ฉ๋‹ˆ๋‹ค.

GET์— ๋Œ€ํ•œ ์š”์ (ํ•„์ˆ˜๋Š” ์•„๋‹ˆ์ง€๋งŒ)์€ ๋‚ด "ํƒ€ํ˜‘" ์†”๋ฃจ์…˜์„ ์ฃผ์žฅํ•ฉ๋‹ˆ๋‹ค. ๊ณต๋ฐฑ/๊ธฐ๋ณธ๊ฐ’์„ ์‚ฝ์ž…ํ•˜๋Š” ๊ฒƒ์€ ํŽธ๋ฆฌํ•˜์ง€๋งŒ ์ด ๋™์ž‘์„ ์ œ๊ณตํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๊ด€์˜ ๋ชป์€ ์•„๋งˆ๋„ ์ด ์†”๋ฃจ์…˜์ด ์˜์‹ฌ์„ ๋ถˆ๋Ÿฌ์ผ์œผํ‚ฌ ๋ˆ„๋ฝ๋œ ํ•„๋“œ๊ฐ€ ์—†๊ธฐ ๋•Œ๋ฌธ์— ํ˜ผ๋ž€์„ ์ตœ์†Œํ™”ํ•œ๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

๋ถ„๋ช…ํžˆ PATCH๋Š” ๋ถ€๋ถ„ ์—…๋ฐ์ดํŠธ๋ฅผ ์œ„ํ•œ ์ง€์ • ์˜ต์…˜์ด์ง€๋งŒ ๋ถ€๋ถ„ PUT์ด ์•„๋‹Œ "์ง€์นจ ์„ธํŠธ"๋กœ ์„ค๋ช…๋˜์–ด ํ•ญ์ƒ ์•ฝ๊ฐ„ ์งœ์ฆ์ด ๋‚ฉ๋‹ˆ๋‹ค. POST(4.3.3)์˜ ์„น์…˜์€ ์‹ค์ œ๋กœ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๋งํ•ฉ๋‹ˆ๋‹ค.

POST ๋ฉ”์„œ๋“œ๋Š” ๋Œ€์ƒ ๋ฆฌ์†Œ์Šค๊ฐ€ ๋ฆฌ์†Œ์Šค ๊ณ ์œ ์˜ ํŠน์ • ์˜๋ฏธ์— ๋”ฐ๋ผ ์š”์ฒญ์— ํฌํ•จ๋œ ํ‘œํ˜„์„ ์ฒ˜๋ฆฌํ•˜๋„๋ก ์š”์ฒญํ•ฉ๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด POST๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๊ธฐ๋Šฅ์— ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค.

  • HTML ์–‘์‹์— ์ž…๋ ฅ๋œ ํ•„๋“œ์™€ ๊ฐ™์€ ๋ฐ์ดํ„ฐ ๋ธ”๋ก์„ ๋ฐ์ดํ„ฐ ์ฒ˜๋ฆฌ ํ”„๋กœ์„ธ์Šค์— ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

...

  • ๋ฆฌ์†Œ์Šค์˜ ๊ธฐ์กด ํ‘œํ˜„์— ๋ฐ์ดํ„ฐ ์ถ”๊ฐ€.

๋‹ค์Œ๊ณผ ๊ฐ™์€ ์ด์œ ๋กœ ๋ถ€๋ถ„ ์—…๋ฐ์ดํŠธ์— POST๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์— ๋Œ€ํ•œ ์ฃผ์žฅ์ด ์žˆ๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค.

  • ๊ฐœ๋…์ ์œผ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ์ˆ˜์ •ํ•˜๋Š” ๊ฒƒ์€ ์ถ”๊ฐ€ํ•˜๋Š” ๊ฒƒ๊ณผ ๋‹ค๋ฅด์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
  • POST๋Š” ์ž์ฒด ๊ทœ์น™์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ ํ•ด๋‹น ๊ทœ์น™์€ ๋ถ€๋ถ„ ์—…๋ฐ์ดํŠธ๊ฐ€ ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ์ด ์ž‘์—…์€ ID๊ฐ€ ์žˆ์œผ๋ฉด CREATE์™€ ์‰ฝ๊ฒŒ ๊ตฌ๋ณ„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

DRF๊ฐ€ ์™„์ „ํ•œ ์ค€์ˆ˜๋ฅผ ์—ด๋งํ•˜์ง€ ์•Š๋”๋ผ๋„ ์‚ฌ์–‘ PUT ์ž‘์—…(์ฆ‰, ์ „์ฒด ๊ฐœ์ฒด ๊ต์ฒด)๊ณผ ํ˜ธํ™˜๋˜๋Š” ์ง๋ ฌ ๋ณ€ํ™˜๊ธฐ๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. ๊ฐ€์žฅ ๊ฐ„๋‹จํ•œ(๊ทธ๋ฆฌ๊ณ  ๊ฐ€์žฅ ํ˜ผ๋ž€์Šค๋Ÿฌ์šด) ๋Œ€๋‹ต์€ ๋ชจ๋“  ํ•„๋“œ๋ฅผ ์š”๊ตฌํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๋˜ํ•œ PUT์€ ๊ธฐ๋ณธ์ ์œผ๋กœ ๋ถ€๋ถ„์ ์ด์ง€ ์•Š์•„์•ผ ํ•˜๊ณ  ๋ถ€๋ถ„ ์—…๋ฐ์ดํŠธ๋Š” ๋‹ค๋ฅธ ํ‚ค์›Œ๋“œ(PATCH ๋˜๋Š” POST)๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ•œ๋‹ค๊ณ  ์ œ์•ˆํ•ฉ๋‹ˆ๋‹ค.

์šฐ๋ฆฌ ์•ฑ์„ drf3.4.x๋กœ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ํ•˜๋Š” ๋™์•ˆ ์ฒซ ๋ฒˆ์งธ PUT ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•œ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค. :)

<strong i="6">@cached_property</strong>
    def _writable_fields(self):
        return [
            field for field in self.fields.values()
            if (not field.read_only) or (field.default is not empty)
        ]

์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ๋‚ด .validated_data์— PUT ์š”์ฒญ์—์„œ ์ œ๊ณตํ•˜์ง€ ์•Š๊ณ  ์ง๋ ฌ ๋ณ€ํ™˜๊ธฐ ๋‚ด์—์„œ ์ˆ˜๋™์œผ๋กœ ์ œ๊ณตํ•˜์ง€ ์•Š์€ ๋ฐ์ดํ„ฐ๊ฐ€ ํฌํ•จ๋ฉ๋‹ˆ๋‹ค. ์ง๋ ฌ ๋ณ€ํ™˜๊ธฐ ์ˆ˜์ค€์˜ default= ์—์„œ ๊ฐ’์„ ๊ฒ€์ƒ‰ํ–ˆ์Šต๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ ๊ธฐ๋ณธ์ ์œผ๋กœ ํŠน์ • ํ•„๋“œ๋ฅผ ์—…๋ฐ์ดํŠธํ•˜๋ ค๊ณ  ํ•˜๋Š” ๋™์•ˆ ํ•ด๋‹น ํ•„๋“œ ์ค‘ ์ผ๋ถ€๋ฅผ ํŒŒ๋ž€์ƒ‰ ๊ธฐ๋ณธ๊ฐ’์œผ๋กœ ๋ฎ์–ด์”๋‹ˆ๋‹ค.

์‚ฌ์šฉ์ž ์ง€์ • ModelSerializer๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ์œผ๋ฏ€๋กœ ๋ฌธ์ œ๋ฅผ ์‰ฝ๊ฒŒ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

@pySilver ์ตœ๊ทผ ๋Œ“๊ธ€ ๋‚ด์šฉ์ด ์ดํ•ด๊ฐ€ ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

@rpkilby "๋‹ค์Œ ์š”์ฒญ์„ ๋ฐ›์ž... ๋ฉฑ๋“ฑ์„ฑ์ด ๊นจ์ง€์ง€ ์•Š๋Š”๊ฐ€?"

์•„๋‹ˆ์š”, ๊ฐ PUT ์š”์ฒญ์€ ์—ฌ๋Ÿฌ ๋ฒˆ ๋ฐ˜๋ณต๋˜์–ด ๋™์ผํ•œ ์ƒํƒœ๊ฐ€ ๋  ์ˆ˜ ์žˆ๋‹ค๋Š” ์ ์—์„œ ๋ฉฑ๋“ฑ์›์ž…๋‹ˆ๋‹ค. ๊ทธ๋ ‡๋‹ค๊ณ  ํ•ด์„œ ์ƒํƒœ์˜ ๋‹ค๋ฅธ ๋ถ€๋ถ„์ด ๊ทธ ๋™์•ˆ ์ˆ˜์ •๋œ ๊ฒฝ์šฐ ์–ด๋–ป๊ฒŒ๋“  ์žฌ์„ค์ •๋œ๋‹ค๋Š” ์˜๋ฏธ๋Š” ์•„๋‹™๋‹ˆ๋‹ค.

๋‹ค์Œ์€ PUT ๋™์ž‘์— ๋Œ€ํ•œ ๋ช‡ ๊ฐ€์ง€ ๋‹ค๋ฅธ ์˜ต์…˜์ž…๋‹ˆ๋‹ค.

  • required=False ๋˜๋Š” default ๊ฐ€ ์—†๋Š” ํ•„๋“œ๋Š” ํ•„์ˆ˜์ž…๋‹ˆ๋‹ค. (๊ธฐ์กด์˜)
  • ๋ชจ๋“  ๋ถ„์•ผ๊ฐ€ ์š”๊ตฌ๋ฉ๋‹ˆ๋‹ค. (์™„์ „ ์—…๋ฐ์ดํŠธ์˜ ๋” ์—„๊ฒฉํ•˜๊ณ  ๋ฐ€์ ‘ํ•˜๊ฒŒ ์ •๋ ฌ๋œ ์˜๋ฏธ ์ฒด๊ณ„๋Š” POST์— ๋Œ€ํ•œ ์ดˆ๊ธฐ ์ƒ์„ฑ ์˜๋ฏธ ์ฒด๊ณ„๋ณด๋‹ค ์‹ค์ œ๋กœ ๋” ์—„๊ฒฉํ•˜๊ธฐ ๋•Œ๋ฌธ์— _ํ•˜์ง€๋งŒ_ ์–ด์ƒ‰ํ•ฉ๋‹ˆ๋‹ค.)
  • ํ•„๋“œ๊ฐ€ ํ•„์š”ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค(์ฆ‰, PATCH ๋™์ž‘์„ ๋ฏธ๋Ÿฌ๋งํ•˜๊ธฐ๋งŒ ํ•˜๋ฉด ๋จ)

_absolute_ ์ •๋‹ต์€ ์—†๋‹ค๋Š” ์ ์„ ๋ถ„๋ช…ํžˆ ํ•˜์ง€๋งŒ ํ˜„์žฌ๋กœ์„œ๋Š” ๊ฐ€์žฅ ์‹ค์šฉ์ ์ธ ํ–‰๋™์„ ์ทจํ–ˆ๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค.

POST ์š”์ฒญ์— ๋Œ€ํ•ด ์ œ๊ณตํ•  ํ•„์š”๊ฐ€ ์—†์ง€๋งŒ ์ดํ›„์— PUT ์š”์ฒญ์— ๋Œ€ํ•ด ์ œ๊ณตํ•ด์•ผ ํ•˜๋Š” ํ•„๋“œ๊ฐ€ ์žˆ๋Š” ๊ฒฝ์šฐ ์ผ๋ถ€ ์‚ฌ์šฉ ์‚ฌ๋ก€์—์„œ ๋ฌธ์ œ๊ฐ€ ๋  ์ˆ˜ ์žˆ๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค. ๋˜ํ•œ PUT-as-create ์ž์ฒด๊ฐ€ ์œ ํšจํ•œ ์ž‘์—…์ด๋ฏ€๋กœ POST์™€ ๋‹ค๋ฅธ "์š”๊ตฌ ์‚ฌํ•ญ" ์˜๋ฏธ ์ฒด๊ณ„๊ฐ€ ์žˆ๋Š” ๊ฒฝ์šฐ์—๋„ ์ด์ƒํ•ฉ๋‹ˆ๋‹ค.

๋ˆ„๊ตฐ๊ฐ€๊ฐ€ ์ด๊ฒƒ์„ ๋ฐœ์ „์‹œํ‚ค๊ณ  ์‹ถ๋‹ค๋ฉด ๋‚˜๋Š” _๊ฐ•๋ ฅํ•˜๊ฒŒ_ ๋‹ค์–‘ํ•œ ๊ธฐ๋ณธ ์ง๋ ฌ ๋ณ€ํ™˜๊ธฐ ํด๋ž˜์Šค๋ฅผ ๊ตฌํ˜„ํ•˜๋Š” ํƒ€์‚ฌ ํŒจํ‚ค์ง€๋กœ ์‹œ์ž‘ํ•˜๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฐ ๋‹ค์Œ ์ง๋ ฌ ๋ณ€ํ™˜๊ธฐ ์„ค๋ช…์„œ์—์„œ ํ•ด๋‹น ํ•ญ๋ชฉ์— ์—ฐ๊ฒฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์‚ฌ๋ก€๊ฐ€ ์ž˜ ๋งŒ๋“ค์–ด์ง€๋ฉด ๋ฏธ๋ž˜์˜ ์–ด๋Š ์‹œ์ ์—์„œ ๊ธฐ๋ณธ ๋™์ž‘์„ ์กฐ์ •ํ•˜๋Š” ๊ฒƒ์„ ๊ณ ๋ คํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

@tomchristie ๋‚ด๊ฐ€ ๋งํ•˜๊ณ  ์‹ถ์€ ๊ฒƒ:

์ฝ๊ธฐ ์ „์šฉ language ํ•„๋“œ์™€ ๋ชจ๋ธ์ด ์žˆ๋Š” ์ง๋ ฌ ๋ณ€ํ™˜๊ธฐ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

class Book(models.Model):
      title = models.CharField(max_length=100)
      language = models.ChoiceField(default='en', choices=(('pl', 'Polish'), ('en', 'English'))

class BookUpdateSerialzier(serializers.ModelSerializer):
      # language is readonly, I dont want to let users update that field using this serializer
      language = serializers.ChoiceField(default='en', choices=(('pl', 'Polish'), ('en', 'English'), read_only=True)
      class Meta:
          model = MyModel
          fields = ('title', 'language', )

book = Book(title="To be or 42", language="pl")
book.save()

s = BookUpdateSerialzier(book, data={'title': 'Foobar'}, partial=True)
s.is_valid()
assert 'language' in s.validated_data # !!! 
assert 'pl' == s.validated_data # AssertionError... here :(
  • ์š”์ฒญ์—์„œ language ๋ฅผ ์ „๋‹ฌํ•˜์ง€ ์•Š์•˜๊ณ  ๊ฒ€์ฆ๋œ ๋ฐ์ดํ„ฐ์—์„œ ์ด๊ฒƒ์„ ๋ณผ ๊ฒƒ์œผ๋กœ ๊ธฐ๋Œ€ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. update ์— ์ „๋‹ฌ๋˜๋ฉด ๊ฐ์ฒด์— ์ด๋ฏธ ๊ธฐ๋ณธ๊ฐ’์ด ์•„๋‹Œ ๊ฐ’์ด ํ• ๋‹น๋˜์–ด ์žˆ์Œ์—๋„ ๋ถˆ๊ตฌํ•˜๊ณ  ๊ธฐ๋ณธ๊ฐ’์œผ๋กœ ๋‚ด ์ธ์Šคํ„ด์Šค๋ฅผ ๋ฎ์–ด์”๋‹ˆ๋‹ค.
  • ์ด ๊ฒฝ์šฐ validated_data['language'] ๊ฐ€ book.language ์ด๋ฉด ๋ฌธ์ œ๊ฐ€ ๋œํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค.

@pySilver - ์˜ˆ, ์˜ค๋Š˜ ๋ฐ”๋กœ https://github.com/tomchristie/django-rest-framework/pull/4346 ์—์„œ ํ•ด๊ฒฐ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

$ ModelField ์— ๊ธฐ๋ณธ๊ฐ’์ด ์žˆ์œผ๋ฏ€๋กœ ๊ฐ€์ง€๊ณ  ์žˆ๋Š” ์˜ˆ์ œ์˜ ์ง๋ ฌ ๋ณ€ํ™˜๊ธฐ ํ•„๋“œ์— default= ์ด ํ•„์š”ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

@tomchristie ์ตœ์†Œํ•œ ํ˜„์žฌ PUT ๋™์ž‘์ด RFC ์‚ฌ์–‘์ด ์•„๋‹ˆ๋ผ๋Š” ๋ฐ ๋™์˜ํ•˜์‹ญ๋‹ˆ๊นŒ? ๊ทธ๋ฆฌ๊ณ  ๋‚ด ์ œ์•ˆ(๋ชจ๋‘ ์š”๊ตฌ ๋˜๋Š” ๊ธฐ๋ณธ๊ฐ’ ์‚ฝ์ž…)์ด ๋ชจ๋‘ ๊ทธ๋ ‡๊ฒŒ ํ•  ๊ฒƒ์ด๋ผ๊ณ ์š”?

@tomchristie ์ข‹์€ ์†Œ์‹์ž…๋‹ˆ๋‹ค!

ModelField์— ๊ธฐ๋ณธ๊ฐ’์ด ์žˆ์œผ๋ฏ€๋กœ ๊ฐ€์ง€๊ณ  ์žˆ๋Š” ์˜ˆ์ œ์˜ ์ง๋ ฌ ๋ณ€ํ™˜๊ธฐ ํ•„๋“œ์— default=๊ฐ€ ํ•„์š”ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

์˜ˆ, ๋ฐ๋ชจ์šฉ์œผ๋กœ ๋งค์šฐ ๋ช…์‹œ์ ์œผ๋กœ ๋งŒ๋“ค๊ณ  ์‹ถ์—ˆ์Šต๋‹ˆ๋‹ค.

@tomchristie ๊ฐ€ ์ง๋ ฌ ๋ณ€ํ™˜๊ธฐ ๋™์ž‘์— ๋Œ€ํ•ด ๋”ฐ๋กœ ๋…ผ์Ÿํ•˜์ง€ ์•Š๋Š”๋‹ค๋Š” ์ ์—์„œ ๋งˆ์นจ๋‚ด ๊ฐ€๋ผ์•‰๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ๋‚˜๋Š” ๊ทธ์˜ ๋ฐ˜๋Œ€๊ฐ€ (์•”์‹œ์ ์œผ๋กœ) ๋‹จ์ผ ์ง๋ ฌ ๋ณ€ํ™˜๊ธฐ๊ฐ€ ๋ชจ๋“  REST ๋ชจ๋“œ๋ฅผ ์ง€์›ํ•ด์•ผ ํ•œ๋‹ค๋Š” ์š”๊ตฌ ์‚ฌํ•ญ์—์„œ ๋น„๋กฏ๋œ ๊ฒƒ์ด๋ผ๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ ์—„๊ฒฉํ•œ ์ง๋ ฌ ๋ณ€ํ™˜๊ธฐ๊ฐ€ POST์— ์˜ํ–ฅ์„ ๋ฏธ์น˜๋Š” ๋ฐฉ์‹์— ๋Œ€ํ•œ ๊ทธ์˜ ๋ถˆ๋งŒ์—์„œ ๋‚˜ํƒ€๋‚ฉ๋‹ˆ๋‹ค. REST ๋ชจ๋“œ๋Š” ํ˜ธํ™˜๋˜์ง€ ์•Š์œผ๋ฏ€๋กœ ํ˜„์žฌ ์†”๋ฃจ์…˜์€ ๋‹จ์ผ ๋ชจ๋“œ์— ๋Œ€ํ•œ ์‚ฌ์–‘์ด ์•„๋‹Œ ์ง๋ ฌ ๋ณ€ํ™˜๊ธฐ์ž…๋‹ˆ๋‹ค.

๊ทธ๊ฒƒ์ด ์ด์˜ ์ œ๊ธฐ์˜ ์ง„์ •ํ•œ ๊ทผ์›์ด๋ผ๋ฉด ์ •๋ฉด์œผ๋กœ ๋ฐ›์•„๋“ค์ด๋„๋ก ํ•ฉ์‹œ๋‹ค. ๋‹จ์ผ ์ง๋ ฌ ๋ณ€ํ™˜๊ธฐ๊ฐ€ ์–ด๋–ป๊ฒŒ ๋ชจ๋“  REST ๋ชจ๋“œ์— ๋Œ€ํ•œ ์‚ฌ์–‘ ๋™์ž‘์„ ์ œ๊ณตํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๊นŒ? ๋‚ด ๋Œ€๋‹ต์€ PARTIAL ๋Œ€ NON-PARTIAL์ด ์ž˜๋ชป๋œ ์ˆ˜์ค€์—์„œ ๊ตฌํ˜„๋œ๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

  • ๋ถ€๋ถ„ ์ง๋ ฌ ๋ณ€ํ™˜๊ธฐ์™€ ๋ถ€๋ถ„ ์ง๋ ฌ ๋ณ€ํ™˜๊ธฐ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ์ ‘๊ทผ ๋ฐฉ์‹์€ ๋ชจ๋“  ๋ชจ๋“œ์— ๋Œ€ํ•œ ์‚ฌ์–‘ ๋™์ž‘์„ ์ง€์›ํ•˜๊ธฐ ์œ„ํ•ด ์—ฌ๋Ÿฌ ์ง๋ ฌ ๋ณ€ํ™˜๊ธฐ๊ฐ€ ํ•„์š”ํ•จ์„ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค.
  • ์šฐ๋ฆฌ๋Š” ์‹ค์ œ๋กœ ๋ถ€๋ถ„์  ๋Œ€ ๋น„-๋ถ€๋ถ„์  ๊ฒ€์ฆ(๋˜๋Š” ์ด์™€ ๊ฐ™์€ ๋งฅ๋ฝ์—์„œ)์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. ๋‹ค๋ฅธ REST ๋ชจ๋“œ๋Š” ์ง๋ ฌ ๋ณ€ํ™˜๊ธฐ์—์„œ ๋‹ค๋ฅธ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ ๋ชจ๋“œ๋ฅผ ์š”์ฒญํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

๋ฌธ์ œ๋ฅผ ๋ถ„๋ฆฌํ•˜๊ธฐ ์œ„ํ•ด ์ง๋ ฌ ๋ณ€ํ™˜๊ธฐ๋Š” REST ๋ชจ๋“œ๋ฅผ ์•Œ์ง€ ๋ชปํ•˜๋ฏ€๋กœ ํƒ€์‚ฌ ์ง๋ ฌ ๋ณ€ํ™˜๊ธฐ๋กœ ๊ตฌํ˜„ํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ๋Œ€์‹  DRF๋Š” ์ถ”๊ฐ€ ์ •๋ณด๋ฅผ ์ง๋ ฌ ๋ณ€ํ™˜๊ธฐ์— ์ „๋‹ฌํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค(๋Œ€๋žต replace=True PUT ). ์ง๋ ฌ ๋ณ€ํ™˜๊ธฐ๋Š” ์ด๊ฒƒ์„ ๊ตฌํ˜„ํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ๊ฒฐ์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค(๋ชจ๋“  ํ•„๋“œ๊ฐ€ ํ•„์š”ํ•˜๊ฑฐ๋‚˜ ๊ธฐ๋ณธ๊ฐ’์„ ์‚ฝ์ž…ํ•ด์•ผ ํ•จ).

๋ถ„๋ช…ํžˆ ์ด๊ฒƒ์€ ๋Œ€๋žต์ ์ธ ์ œ์•ˆ์ด์ง€๋งŒ ๊ต์ฐฉ ์ƒํƒœ๋ฅผ ๊นจ๋œจ๋ฆด ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค.

๋”์šฑ์ด, PUT-as-create๋Š” ๊ทธ ์ž์ฒด๋กœ ์œ ํšจํ•œ ์ž‘์—…์ด๋ฏ€๋กœ POST์™€ ๋‹ค๋ฅธ "ํ•„์ˆ˜์„ฑ" ์˜๋ฏธ ์ฒด๊ณ„๋ฅผ ๊ฐ–๋Š”๋‹ค๋ฉด ์ด์ƒํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค.

PUT์œผ๋กœ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ์ ์—๋Š” ๋™์˜ํ•˜์ง€๋งŒ ์˜๋ฏธ๋ก ์ด ๋™์ผํ•˜๋‹ค๋Š” ์ ์—๋Š” ๋™์˜ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. PUT ๋Š” ํŠน์ • ๋ฆฌ์†Œ์Šค์—์„œ ์ž‘๋™ํ•ฉ๋‹ˆ๋‹ค.

PUT ๋ฉ”์„œ๋“œ๋Š” ๋Œ€์ƒ ๋ฆฌ์†Œ์Šค์˜ ์ƒํƒœ๊ฐ€ ์ƒ์„ฑ๋˜๊ฑฐ๋‚˜ ์š”์ฒญ ๋ฉ”์‹œ์ง€ ํŽ˜์ด๋กœ๋“œ์— ํฌํ•จ๋œ ํ‘œํ˜„์— ์˜ํ•ด ์ •์˜๋œ ์ƒํƒœ๋กœ ๋Œ€์ฒด๋˜๋„๋ก ์š”์ฒญํ•ฉ๋‹ˆ๋‹ค.

๋”ฐ๋ผ์„œ ์˜๋ฏธ ์ƒ์„ฑ์ด ์‹ค์ œ๋กœ ๋‹ค๋ฅด๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค.

  • ๊ฒŒ์‹œํ•˜๋‹ค/citizen/ ์€ SSN(์‚ฌํšŒ ๋ณด์žฅ ๋ฒˆํ˜ธ)์ด ์ƒ์„ฑ๋  ๊ฒƒ์œผ๋กœ ์˜ˆ์ƒํ•ฉ๋‹ˆ๋‹ค.
  • ๋†“๋‹ค/citizen/<SSN> ๋กœ ํŠน์ • SSN์— ๋Œ€ํ•œ ๋ฐ์ดํ„ฐ๋ฅผ ์—…๋ฐ์ดํŠธํ•ฉ๋‹ˆ๋‹ค. ํ•ด๋‹น SSN์— ๋ฐ์ดํ„ฐ๊ฐ€ ์—†์œผ๋ฉด ์ƒ์„ฑ๋ฉ๋‹ˆ๋‹ค.

"id"๋Š” PUT์˜ URI์— ํฌํ•จ๋˜์–ด์•ผ ํ•˜๋ฏ€๋กœ ํ•„์š”์— ๋”ฐ๋ผ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋Œ€์กฐ์ ์œผ๋กœ "id"๋Š” POST์—์„œ ์„ ํƒ ์‚ฌํ•ญ์ž…๋‹ˆ๋‹ค.

"id"๋Š” PUT์˜ URI์— ํฌํ•จ๋˜์–ด์•ผ ํ•˜๋ฏ€๋กœ ํ•„์š”์— ๋”ฐ๋ผ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋Œ€์กฐ์ ์œผ๋กœ "id"๋Š” POST์—์„œ ์„ ํƒ ์‚ฌํ•ญ์ž…๋‹ˆ๋‹ค.

๋ฌผ๋ก . ๋‚˜๋Š” "PUT์ด _all_ ํ•„๋“œ๋ฅผ ์—„๊ฒฉํžˆ ์š”๊ตฌํ•˜๋„๋ก ํ•˜๋ผ"์˜ ์ œ์•ˆ๋œ ๋ณ€๊ฒฝ์ด PUT-as-create๊ฐ€ POST-as-create wrt์™€ ๋‹ค๋ฅธ ๋™์ž‘์„ ๊ฐ€์ง์„ ์˜๋ฏธํ•œ๋‹ค๋Š” ์‚ฌ์‹ค์„ ๊ตฌ์ฒด์ ์œผ๋กœ ์–ธ๊ธ‰ํ•˜๊ณ  ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค. ํ•„๋“œ๊ฐ€ ํ•„์ˆ˜์ธ์ง€ ์—ฌ๋ถ€.

PUT-is-strict ํ–‰๋™์˜ ์˜ต์…˜์„ ๊ฐ–๋Š” ๊ฐ€์น˜์— ๋Œ€ํ•ด ์ด์•ผ๊ธฐํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

(์ด ๊ฒฝ์šฐ _all_ ํ•„๋“œ๊ฐ€ ์—„๊ฒฉํ•˜๊ฒŒ ํ•„์ˆ˜์ž„์„ ์ ์šฉํ•˜๊ณ , PATCH์—์„œ _no_ ํ•„๋“œ๊ฐ€ ํ•„์ˆ˜์ž„์„ ์ ์šฉํ•˜๊ณ , POST์— required= ํ”Œ๋ž˜๊ทธ๋ฅผ ์‚ฌ์šฉํ•˜์‹ญ์‹œ์˜ค.)

๋‹จ์ผ ์ง๋ ฌ ๋ณ€ํ™˜๊ธฐ๊ฐ€ ์–ด๋–ป๊ฒŒ ๋ชจ๋“  REST ๋ชจ๋“œ์— ๋Œ€ํ•œ ์‚ฌ์–‘ ๋™์ž‘์„ ์ œ๊ณตํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๊นŒ?

์ง๋ ฌ ๋ณ€ํ™˜๊ธฐ๊ฐ€ ์ธ์Šคํ„ด์Šคํ™”๋˜๋Š” ๋ฐฉ์‹์— ๋”ฐ๋ผ ์ƒ์„ฑ, ์—…๋ฐ์ดํŠธ ๋ฐ ๋ถ€๋ถ„ ์—…๋ฐ์ดํŠธ๋ฅผ ๊ตฌ๋ณ„ํ•  ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ ๋ฌธ์ œ๊ฐ€ ๋˜์ง€ ์•Š๋Š”๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค.

PUT ๋˜๋Š” POST $ ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ create ํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ์ ์„ ์ด๋ฏธ ์ง€์ ํ–ˆ์Šต๋‹ˆ๋‹ค. ์˜๋ฏธ ์ฒด๊ณ„์™€ ์š”๊ตฌ ์‚ฌํ•ญ์ด ๋‹ค๋ฅด๋ฏ€๋กœ create ๋Š” REST ๋ชจ๋“œ์— ๋Œ€ํ•ด ๋ถˆ๊ฐ€์ง€๋ก ์ ์ด์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๋‚˜๋Š” ๊ตฌ๋ณ„์ด ์‹ค์ œ๋กœ is_valid ์˜ ์ผ๋ถ€๋กœ ๋ฐœ์ƒํ•œ๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค. ํŠน์ • ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ ๋ชจ๋“œ๋ฅผ ์š”์ฒญํ•ฉ๋‹ˆ๋‹ค.

  • ํ˜„์žฅ ์กด์žฌ ๊ฒ€์ฆ(PATCH) ์—†์Œ
  • required ํ”Œ๋ž˜๊ทธ ๊ธฐ๋ฐ˜ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ(POST)
  • ์—„๊ฒฉํ•œ ํ˜„์žฅ ์กด์žฌ ๊ฒ€์ฆ(PUT)

ํ‚ค์›Œ๋“œ๋ณ„ ๋…ผ๋ฆฌ๋ฅผ CRUD ์ž‘์—…์—์„œ ์ œ์™ธํ•จ์œผ๋กœ์จ ์ง๋ ฌ ๋ณ€ํ™˜๊ธฐ์™€ DRF ๊ฐ„์˜ ๊ฒฐํ•ฉ๋„ ์ค„์ž…๋‹ˆ๋‹ค. ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ ๋ชจ๋“œ๋ฅผ ๊ตฌ์„ฑํ•  ์ˆ˜ ์žˆ๋Š” ๊ฒฝ์šฐ ์™„์ „ํžˆ ๋ฒ”์šฉ์ด ๋ฉ๋‹ˆ๋‹ค(3๊ฐœ์˜ ํ‚ค์›Œ๋“œ์— ๋Œ€ํ•ด 3๊ฐœ์˜ ํŠน์ • ๊ฒฝ์šฐ๋งŒ ๊ตฌํ˜„ํ•˜๋”๋ผ๋„).

์ด ๊ธฐ๋Šฅ์— ๋Œ€ํ•ด ์ž˜ ์„ค๋ช…ํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. :)

.is_valid()๋ฅผ ํ˜ธ์ถœํ•  ๋•Œ "์œ ํšจ์„ฑ ๊ฒ€์‚ฌ ๋ชจ๋“œ"๊ฐ€ ๋‹ค๋ฅด๋‹ค๋Š” ๊ฒƒ์€ ์ผ์–ด๋‚˜์ง€ ์•Š์„ ๊ฒฉ๋ณ€์ž…๋‹ˆ๋‹ค.

์šฐ๋ฆฌ๋Š” ์•„๋งˆ๋„ ๊ธฐ์กด์˜ 'partial=True' ๋‹จ์œ„ kwarg์— ๋Œ€์‘ํ•˜๋Š” 'complete=True'๋ฅผ ๊ณ ๋ คํ•  _์ˆ˜_ ์žˆ์Šต๋‹ˆ๋‹ค_. ๊ทธ๊ฒƒ์€ ํ˜„์žฌ ์ž‘๋™ํ•˜๋Š” ๋ฐฉ์‹์— ์ถฉ๋ถ„ํžˆ ์‰ฝ๊ฒŒ ๋งž๊ณ  "์—„๊ฒฉํ•œ ํ•„๋“œ"์˜ ๊ฒฝ์šฐ๋ฅผ ๊ณ„์† ์ง€์›ํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค.

์ง๋ ฌ ๋ณ€ํ™˜๊ธฐ๊ฐ€ ์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๋Š” ๋ฐ ์ ํ•ฉํ•œ ์žฅ์†Œ์ž…๋‹ˆ๊นŒ? ์ด ์š”๊ตฌ ์‚ฌํ•ญ์€ REST ํ‚ค์›Œ๋“œ์™€ ๋ฐ€์ ‘ํ•˜๊ฒŒ ์—ฐ๊ฒฐ๋˜์–ด ์žˆ์œผ๋ฏ€๋กœ ์ ์šฉํ•˜๊ธฐ์— ์ ํ•ฉํ•œ ์œ„์น˜์ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ์ ‘๊ทผ ๋ฐฉ์‹์„ ์ง€์›ํ•˜๋ ค๋ฉด ์ง๋ ฌ ๋ณ€ํ™˜๊ธฐ๊ฐ€ ์ž…๋ ฅ์œผ๋กœ ํ—ˆ์šฉํ•˜๋Š” ํ•„๋“œ ๋ชฉ๋ก๋งŒ ๋…ธ์ถœํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค.

์ œ์ณ๋‘๊ณ  ... Django์˜ ๊ด€์‹ฌ์‚ฌ ๋ถ„๋ฆฌ (ํ• ๋‹น)์— ๋Œ€ํ•œ ์ข‹์€ ๋…ผ์˜๊ฐ€ ์–ด๋”˜๊ฐ€์— ์žˆ์Šต๋‹ˆ๊นŒ? "์™œ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ๊ฐ€ ์ง๋ ฌํ™”์˜ ์ผ๋ถ€์ธ์ง€"์™€ ๊ฐ™์€ ์งˆ๋ฌธ์— ๋Œ€ํ•œ ๋‹ต๋ณ€์„ ๋ชจ๋ฅด๊ธฐ ๋•Œ๋ฌธ์— Django ์นœํ™”์ ์ธ ๋‹ต๋ณ€์œผ๋กœ ์ž์‹ ์„ ์ œํ•œํ•˜๋Š” ๋ฐ ๋ฌธ์ œ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. 1.9์˜ ์ง๋ ฌํ™” ๋ฌธ์„œ์—๋Š” ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ์— ๋Œ€ํ•œ ์–ธ๊ธ‰์กฐ์ฐจ ์—†์Šต๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ์—„๊ฒฉํ•˜๊ฒŒ ์ฒซ ๋ฒˆ์งธ ์›์น™์—์„œ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๋ณด์ž…๋‹ˆ๋‹ค.

  1. ๋ชจ๋ธ์€ ๋‚ด๋ถ€ ์ผ๊ด€์„ฑ๊ณผ
  2. "๋ณด๊ธฐ"(์ด ๊ฒฝ์šฐ REST ๋ชจ๋“œ ํ”„๋กœ์„ธ์„œ)๋Š” ํ•ด๋‹น ๋ณด๊ธฐ์™€ ๊ด€๋ จ๋œ ๋น„์ฆˆ๋‹ˆ์Šค ๊ทœ์น™(์˜ˆ: RFC)์„ ์ ์šฉํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

์œ ํšจ์„ฑ ๊ฒ€์‚ฌ์— ๋Œ€ํ•œ ์ฑ…์ž„์ด ์‚ฌ๋ผ์ง€๋ฉด ์ง๋ ฌ ๋ณ€ํ™˜๊ธฐ๋Š” 100% ๋ถ€๋ถ„์ (๊ธฐ๋ณธ์ ์œผ๋กœ)์ด ๋  ์ˆ˜ ์žˆ์œผ๋ฉฐ "์ฝ๊ธฐ ์ „์šฉ"๊ณผ ๊ฐ™์€ I/O ๊ทœ์น™์— ํŠนํ™”๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ๋ฐฉ๋ฒ•์œผ๋กœ ๋นŒ๋“œ๋œ ModelSerializer๋Š” ๋‹ค์–‘ํ•œ ๋ณด๊ธฐ๋ฅผ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค.

์ง๋ ฌ ๋ณ€ํ™˜๊ธฐ๊ฐ€ ์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๋Š” ๋ฐ ์ ํ•ฉํ•œ ์žฅ์†Œ์ž…๋‹ˆ๊นŒ?

์˜ˆ.

1.9์˜ ์ง๋ ฌํ™” ๋ฌธ์„œ์—๋Š” ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ์— ๋Œ€ํ•œ ์–ธ๊ธ‰์กฐ์ฐจ ์—†์Šต๋‹ˆ๋‹ค.

Django์˜ ๊ธฐ๋ณธ ์ œ๊ณต ์ง๋ ฌํ™”๋Š” Web APIS์— ์œ ์šฉํ•˜์ง€ ์•Š์œผ๋ฉฐ ์‹ค์ œ๋กœ ๊ณ ์ • ์žฅ์น˜๋ฅผ ๋คํ”„ํ•˜๊ณ  ๋กœ๋“œํ•˜๋Š” ๋ฐ ์ œํ•œ๋ฉ๋‹ˆ๋‹ค.

๋‹น์‹ ์€ Django์™€ DRF์˜ ์•„ํ‚คํ…์ฒ˜ ๊ฐ€์ •์„ ๋‚˜๋ณด๋‹ค ๋” ์ž˜ ์•Œ๊ณ  ์žˆ์œผ๋ฏ€๋กœ ๋ฐฉ๋ฒ•์— ๋Œ€ํ•ด์„œ๋Š” ๋‹น์‹ ์—๊ฒŒ ๋งํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ํ™•์‹คํžˆ init kwarg๋Š” ๊ทธ๊ฒƒ์— ๋Œ€ํ•œ ์˜ฌ๋ฐ”๋ฅธ ๋Š๋‚Œ์„ ๊ฐ€์ง€๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค... ์ง๋ ฌ ๋ณ€ํ™˜๊ธฐ๋ฅผ "์ฃผ๋ฌธํ˜•"์œผ๋กœ ์žฌ๊ตฌ์„ฑํ•ฉ๋‹ˆ๋‹ค. ์œ ์ผํ•œ ์ œํ•œ ์‚ฌํ•ญ์€ "์ฆ‰์‹œ" ์žฌ๊ตฌ์„ฑํ•  ์ˆ˜ ์—†๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ์ธ์Šคํ„ด์Šค๊ฐ€ ์ผํšŒ์šฉ์ด๋ผ๊ณ  ๊ฐ€์ •ํ•˜๋ฏ€๋กœ ์ด๊ฒƒ์€ ์ค‘์š”ํ•œ ๋ฌธ์ œ๊ฐ€ ์•„๋‹™๋‹ˆ๋‹ค.

๋‚˜๋Š” ์ง€๊ธˆ ์ด๊ฒƒ์„ ๋””๋งˆ์ผ์Šคํ†ค(de-milestone)์œผ๋กœ ํ•  ๊ฒƒ์ด๋‹ค. v3.7 ์ดํ›„์— ์žฌํ‰๊ฐ€ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์—ฌ๋Ÿฌ๋ถ„์—๊ฒŒ ๋‹ฌ๋ ค ์žˆ์ง€๋งŒ ์ด๊ฒƒ์ด ๋™์‹œ์„ฑ ์ง€์›์„ ์ถ”๊ฐ€ํ•˜๊ธฐ ์œ„ํ•œ ํ‹ฐ์ผ“์ด ์•„๋‹ˆ๋ผ๋Š” ์ ์„ ๋ถ„๋ช…ํžˆ ํ•˜๊ณ  ์‹ถ์Šต๋‹ˆ๋‹ค. ์‹ค์ œ ๋ฌธ์ œ๋Š” ๋‹จ์ผ ์ง๋ ฌ ๋ณ€ํ™˜๊ธฐ๊ฐ€ ํ˜„์žฌ ์•„ํ‚คํ…์ฒ˜์—์„œ PUT๊ณผ POST๋ฅผ ๋ชจ๋‘ ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ๊ฒ€์ฆํ•  ์ˆ˜ ์—†๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๋™์‹œ์„ฑ์€ "์‹คํŒจํ•œ ํ…Œ์ŠคํŠธ"๋ฅผ ์ œ๊ณตํ–ˆ์Šต๋‹ˆ๋‹ค.

TL;DR Tom์ด ์ œ์•ˆํ•œ ์ˆ˜์ • ์‚ฌํ•ญ ์—์„œ ์‹œ์ž‘ํ•˜์—ฌ ์ด ๋ฌธ์ œ๊ฐ€ ์ฐจ๋‹จ๋œ ์ด์œ ๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์š”์•ฝํ•˜๋ฉด ์ œ์•ˆ๋œ ์†”๋ฃจ์…˜์€ PUT ์š”์ฒญ์— ํ•„์š”ํ•œ ๋ชจ๋“  ํ•„๋“œ๋ฅผ ๋งŒ๋“œ๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ด ์ ‘๊ทผ ๋ฐฉ์‹์—๋Š” (์ ์–ด๋„) ๋‘ ๊ฐ€์ง€ ๋ฌธ์ œ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

  1. ์ง๋ ฌ ๋ณ€ํ™˜๊ธฐ๋Š” HTTP ๋ฉ”์„œ๋“œ๊ฐ€ ์•„๋‹Œ ์ž‘์—…์œผ๋กœ ์ƒ๊ฐํ•˜๋ฏ€๋กœ ์ผ๋Œ€์ผ ๋งคํ•‘์ด ์—†์Šต๋‹ˆ๋‹ค. ๋ช…๋ฐฑํ•œ ์˜ˆ๋Š” create PUT ๋ฐ POST ๊ฐ€ ๊ณต์œ ํ•˜๊ธฐ ๋•Œ๋ฌธ์— create ์ž…๋‹ˆ๋‹ค. PUT ์— ์˜ํ•œ ์ƒ์„ฑ์€ ๊ธฐ๋ณธ์ ์œผ๋กœ ๋น„ํ™œ์„ฑํ™”๋˜์–ด ์žˆ์œผ๋ฏ€๋กœ ์ œ์•ˆ๋œ ์ˆ˜์ • ์‚ฌํ•ญ์ด ์—†๋Š” ๊ฒƒ๋ณด๋‹ค ๋‚˜์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  2. PUT ์˜ ๋ชจ๋“  ํ•„๋“œ๋ฅผ ์š”๊ตฌํ•  ํ•„์š”๋Š” ์—†์Šต๋‹ˆ๋‹ค(#3648, #4703์—์„œ ๊ณต์œ ํ•˜๋Š” ๊ฐ์ •). nillable ํ•„๋“œ๊ฐ€ ์—†์œผ๋ฉด None์ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ธฐ๋ณธ๊ฐ’์ด ์žˆ๋Š” ํ•„๋“œ๊ฐ€ ์—†์œผ๋ฉด ๊ธฐ๋ณธ๊ฐ’์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. PUT ๋Š” ์‹ค์ œ๋กœ POST ์™€ ๋™์ผํ•œ (๋ชจ๋ธ ํŒŒ์ƒ) ํ•„๋“œ ์š”๊ตฌ ์‚ฌํ•ญ์„ ๊ฐ–์Šต๋‹ˆ๋‹ค.

์ง„์งœ ๋ฌธ์ œ๋Š” #3648, #4703์—์„œ ๋ˆ„๋ฝ๋œ ๋ฐ์ดํ„ฐ์™€ ๊ธฐ๋ณธ ์ œ์•ˆ์„ ์ฒ˜๋ฆฌํ•˜๋Š” ๋ฐฉ๋ฒ•์ด๋ฉฐ ์—ฌ๊ธฐ ์— ์˜ฌ๋ฐ”๋ฅธ ์†”๋ฃจ์…˜์ด ๋‚จ์•„ ์žˆ์Šต๋‹ˆ๋‹ค. if_missing_use_default ์™€ ๊ฐ™์€ ๊ฐœ๋…์„ ๋„์ž…ํ•˜๋ฉด ๋ชจ๋“  HTTP ๋ชจ๋“œ( PUT ํฌํ•จ)๋ฅผ ์ง€์›ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋‚ด ์›๋ž˜ ์ œ์•ˆ ์€ partial ์˜ ๋Œ€์ฒดํ’ˆ์œผ๋กœ ์ œ์‹œํ–ˆ์ง€๋งŒ ์ง๊ต ๊ฐœ๋…์œผ๋กœ ์ƒ๊ฐํ•˜๋Š” ๊ฒƒ์ด ๋” ์‰ฝ๊ณ  ํ•„์š”ํ•  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค.

if_missing_use_default์™€ ๊ฐ™์€ ๊ฐœ๋…์„ ๋„์ž…ํ•œ๋‹ค๋ฉด.

๋ˆ„๊ตฐ๊ฐ€๊ฐ€ ์ด๊ฒƒ์„ ๊ตฌํ˜„ํ•˜๊ฑฐ๋‚˜ ์—„๊ฒฉํ•œ "๋ชจ๋“  ํ•„๋“œ ํ•„์š”"๋ฅผ ๊ธฐ๋ณธ ์ง๋ ฌ ๋ณ€ํ™˜๊ธฐ ํด๋ž˜์Šค๋กœ ๊ตฌํ˜„ํ•˜๊ณ ์ด๋ฅผ ํƒ€์‚ฌ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋กœ ๋ž˜ํ•‘ํ•˜๋Š” ๊ฒƒ์„ ๋ฐฉํ•ดํ•˜๋Š” ๊ฒƒ์€ ์—†์Šต๋‹ˆ๋‹ค.

๋‚ด ์ƒ๊ฐ์—๋Š” ์—„๊ฒฉํ•œ "๋ชจ๋“  ํ•„๋“œ ํ•„์š”" ๋ชจ๋“œ๋„ ํ•ต์‹ฌ์œผ๋กœ ๋งŒ๋“ค ์ˆ˜ ์žˆ์œผ๋ฉฐ, ์ด๋Š” ๋งค์šฐ ๋ถ„๋ช…ํ•˜๊ณ  ๋ช…๋ฐฑํ•œ ๋™์ž‘์ด๋ฉฐ ์ด๊ฒƒ์ด ์™œ ์œ ์šฉํ•œ์ง€ ์•Œ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋‚˜๋Š” "ํ•„๋“œ๋ฅผ ์„ ํƒ ์‚ฌํ•ญ์œผ๋กœ ํ—ˆ์šฉํ•˜์ง€๋งŒ ์กด์žฌํ•˜๋Š” ๊ฒฝ์šฐ ๋ชจ๋ธ ๊ธฐ๋ณธ๊ฐ’์„ ์‚ฌ์šฉํ•˜์—ฌ ๋ชจ๋“  ๊ฒƒ์„ ๊ต์ฒดํ•ฉ๋‹ˆ๋‹ค"๋ผ๊ณ  ํ™•์‹ ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์ด๋Š” ๋งค์šฐ ์ง๊ด€์ ์ด์ง€ ์•Š์€ ๋™์ž‘(์˜ˆ: "created_at" ํ•„๋“œ, ์ž๋™์œผ๋กœ ์ข…๋ฃŒ๋˜๋Š” ์Šค์Šค๋กœ ์—…๋ฐ์ดํŠธ). ์šฐ๋ฆฌ๊ฐ€ ๋” ์—„๊ฒฉํ•œ ํ–‰๋™์„ ์›ํ•œ๋‹ค๋ฉด ๋” ์—„๊ฒฉํ•œ ํ–‰๋™์„ ์ทจํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

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

๋˜๋Š” ์‚ฌ์šฉ์ž์—๊ฒŒ ์ •๋ง ํ•„์š”ํ•œ ๋™์ž‘์ด ์ฝ”์–ด์—์„œ ๋ˆ„๋ฝ๋˜์—ˆ๋‹ค๊ณ  ํ™•์‹ ํ•˜๋Š” ๊ฒฝ์šฐ ํ’€ ์š”์ฒญ์„ ํ•˜๊ณ  ๋™์ž‘ ๋ฐ ๋ฌธ์„œ๋ฅผ ์—…๋ฐ์ดํŠธํ•˜์—ฌ ๋งค์šฐ ์‹ ์†ํ•˜๊ฒŒ ์žฅ์ ์„ ํ‰๊ฐ€ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ตฌ์ฒด์ ์ธ ๋ฐฉ๋ฒ•.

ํ’€ ๋ฆฌํ€˜์ŠคํŠธ๋ฅผ ์ด๋ฅผ ์œ„ํ•œ ์ถœ๋ฐœ์ ์œผ๋กœ ์‚ผ๊ฒŒ ๋˜์–ด ๊ธฐ์ฉ๋‹ˆ๋‹ค. ์ด ๋™์ž‘์„ ์‹œ์—ฐํ•˜๋Š” ํƒ€์‚ฌ ํŒจํ‚ค์ง€๋ฅผ ํฌํ•จํ•˜๊ฒŒ ๋˜์–ด ๋”์šฑ ๊ธฐ์ฉ๋‹ˆ๋‹ค.

PUT-is-strict ํ–‰๋™์˜ ์˜ต์…˜์„ ๊ฐ–๋Š” ๊ฐ€์น˜์— ์ ‘๊ทผํ•ฉ๋‹ˆ๋‹ค.

์ด๊ฒƒ์€ ์—ฌ์ „ํžˆ โ€‹โ€‹์œ ์ง€๋ฉ๋‹ˆ๋‹ค. ๋ˆ„๊ตฐ๊ฐ€๊ฐ€ ๊ทธ ๋ผ์ธ์„ ๋”ฐ๋ผ ํ’€ ๋ฆฌํ€˜์ŠคํŠธ๋ฅผ ํ•  ๋งŒํผ ์ถฉ๋ถ„ํžˆ ์‹ ๊ฒฝ์„ ์“ด ๋‹ค๋ฉด ์šฐ๋ฆฌ๋Š” ๊ทธ ์ธก๋ฉด์„ ํ•ต์‹ฌ์ ์œผ๋กœ ๊ณ ๋ คํ•  ์ˆ˜ ์žˆ๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค. ์„ ํƒ์  ๋™์ž‘์ด์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

๊ทธ๊ฒƒ์€ ๋งค์šฐ ์ง๊ด€์ ์ด์ง€ ์•Š์€ ๋™์ž‘์„ ๋‚˜ํƒ€๋‚ด๋Š” ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค(์˜ˆ: ์ž๋™์œผ๋กœ ์—…๋ฐ์ดํŠธ๋˜๋Š” "created_at" ํ•„๋“œ).

created_at ํ•„๋“œ๋Š” read_only ์ด์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค(๋˜๋Š” ์ง๋ ฌ ๋ณ€ํ™˜๊ธฐ์—์„œ ์ œ์™ธ). ์ด ๋‘ ๊ฒฝ์šฐ ๋ชจ๋‘ ๋ณ€๊ฒฝ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค(์ผ๋ฐ˜ ์ง๋ ฌ ๋ณ€ํ™˜๊ธฐ ๋™์ž‘). ์ง๋ ฌ ๋ณ€ํ™˜๊ธฐ์—์„œ ํ•„๋“œ๊ฐ€ ์ฝ๊ธฐ ์ „์šฉ์ด ์•„๋‹Œ ์ง๊ด€์ ์ด์ง€ ์•Š์€ ๊ฒฝ์šฐ ์ž๋™์œผ๋กœ ํ•„๋“œ๋ฅผ ๋ณ€๊ฒฝํ•˜๋Š” ์ง๊ด€์ ์ด์ง€ ์•Š์€ ๋™์ž‘์„ ์–ป๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

ํ’€ ๋ฆฌํ€˜์ŠคํŠธ๋ฅผ ์ด๋ฅผ ์œ„ํ•œ ์ถœ๋ฐœ์ ์œผ๋กœ ์‚ผ๊ฒŒ ๋˜์–ด ๊ธฐ์ฉ๋‹ˆ๋‹ค. ์ด ๋™์ž‘์„ ์‹œ์—ฐํ•˜๋Š” ํƒ€์‚ฌ ํŒจํ‚ค์ง€๋ฅผ ํฌํ•จํ•˜๊ฒŒ ๋˜์–ด ๋”์šฑ ๊ธฐ์ฉ๋‹ˆ๋‹ค.

์ „์ ์œผ๋กœ. "๊ธฐ๋ณธ๊ฐ’ ์‚ฌ์šฉ" ๋ณ€ํ˜•์€ ํƒ€์‚ฌ ํŒจํ‚ค์ง€์— ์ด์ƒ์ ์ธ ๊ฒฝ์šฐ์ž…๋‹ˆ๋‹ค. ๋ณ€๊ฒฝ ์‚ฌํ•ญ์€ ๊ธฐ์กด ๋™์ž‘์— ๋Œ€ํ•œ ๊ฐ„๋‹จํ•œ ๋ž˜ํผ(ํ•œ ๊ฐ€์ง€ ๋ฐฉ๋ฒ•)์ด๊ณ  (๊ธฐ๋ณธ ์ธ์ˆ˜๋ฅผ ๊ตฌ๋งคํ•˜๋Š” ๊ฒฝ์šฐ) ๋ชจ๋“  ๋น„-๋ถ€๋ถ„ ์ง๋ ฌ ๋ณ€ํ™˜๊ธฐ์— ๋Œ€ํ•ด ์ž‘๋™ํ•˜๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.

tomchristie๊ฐ€ 4์‹œ๊ฐ„ ์ „์— ๋‹ซ์•˜์Šต๋‹ˆ๋‹ค.

์•„๋งˆ๋„ "PR Welcome" ๋˜๋Š” "3rd Party Plugin"๊ณผ ๊ฐ™์€ ๋ ˆ์ด๋ธ”์„ ์ถ”๊ฐ€ํ•˜๊ณ  ์ด์™€ ๊ฐ™์ด ์œ ํšจํ•œ/ํ™•์ธ๋œ ๋ฌธ์ œ๋ฅผ ์—ด์–ด ๋‘๋Š” ๊ฒƒ์„ ๊ณ ๋ คํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋‚˜๋Š” ์ข…์ข… ๋ฌธ์ œ๊ฐ€ ์ด๋ฏธ ๋ณด๊ณ ๋˜์—ˆ๊ณ  ํ•ด๊ฒฐ ๊ณผ์ •์ด ์ง„ํ–‰ ์ค‘์ธ์ง€ ํ™•์ธํ•˜๊ธฐ ์œ„ํ•ด ๋ฏธํ•ด๊ฒฐ ๋ฌธ์ œ๋ฅผ ๊ฒ€์ƒ‰ํ•ฉ๋‹ˆ๋‹ค. ๋‹ซํžŒ ๋ฌธ์ œ๋ฅผ "์ž˜๋ชป๋œ" ๋˜๋Š” "๊ณ ์ •๋œ" ๊ฒƒ์œผ๋กœ ์ธ์‹ํ•ฉ๋‹ˆ๋‹ค. ๋ช‡ ๊ฐ€์ง€ "์œ ํšจํ•˜์ง€๋งŒ ๋งˆ๊ฐ๋œ" ๋ฌธ์ œ๋ฅผ ์ˆ˜์ฒœ ๊ฐœ์˜ ์œ ํšจํ•˜์ง€ ์•Š๊ฑฐ๋‚˜ ์ˆ˜์ •๋œ โ€‹โ€‹๋ฌธ์ œ์— ํ˜ผํ•ฉํ•˜๋Š” ๊ฒƒ์€ ํšจ์œจ์ ์ธ ๊ฒ€์ƒ‰์„ ์œ ๋„ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

"PR Welcome" ๋˜๋Š” "3rd Party Plugin"๊ณผ ๊ฐ™์€ ๋ ˆ์ด๋ธ”์„ ์ถ”๊ฐ€ํ•˜๋Š” ๊ฒƒ์„ ๊ณ ๋ คํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ถฉ๋ถ„ํžˆ ํ•ฉ๋ฆฌ์ ์ด์ง€๋งŒ ๋ฌธ์ œ ์ถ”์ ๊ธฐ๊ฐ€ ํ”„๋กœ์ ํŠธ ์ž์ฒด์— ๋Œ€ํ•œ ํ™œ์„ฑ ๋˜๋Š” ์‹คํ–‰ ๊ฐ€๋Šฅํ•œ ์ž‘์—…์„ ๋ฐ˜์˜ํ•˜๊ธฐ๋ฅผ ๋ฐ”๋ž๋‹ˆ๋‹ค.

๋ฌธ์ œ์˜ ๋ฒ”์œ„๋ฅผ ์—„๊ฒฉํ•˜๊ฒŒ ์œ ์ง€ํ•˜๋ ค๊ณ  ๋…ธ๋ ฅํ•˜๋Š” ๊ฒƒ์ด ์ •๋ง ์ค‘์š”ํ•ฉ๋‹ˆ๋‹ค. ์šฐ์„  ์ˆœ์œ„๋ฅผ ๋ณ€๊ฒฝํ•œ๋‹ค๋Š” ๊ฒƒ์€ ์ด์ „์— ๋‹ซ์•˜๋˜ ๋ฌธ์ œ๋ฅผ ๋‹ค์‹œ ์—ด๊ฒ ๋‹ค๊ณ  ์„ ํƒํ•˜๋Š” ๊ฒƒ์„ ์˜๋ฏธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค . ์ง€๊ธˆ ๋‹น์žฅ์€ ์ด๊ฒƒ์ด "ํ•ต์‹ฌ ํŒ€์ด ๊ฐ€๊นŒ์šด ์žฅ๋ž˜์— ์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ณ ์ž ํ•˜๋Š” ๊ฒƒ"์—์„œ ๋ฒ—์–ด๋‚ฌ๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค.

๊ทธ๊ฒƒ์ด ๋ฐ˜๋ณต์ ์œผ๋กœ ๋‚˜ํƒ€๋‚˜๊ณ  ์ œ3์ž ์†”๋ฃจ์…˜์ด ๊ณ„์† ์—†๋‹ค๋ฉด ์•„๋งˆ๋„ ์šฐ๋ฆฌ๋Š” ๊ทธ๊ฒƒ์„ ์žฌํ‰๊ฐ€ํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค.

์ด์™€ ๊ฐ™์ด ์œ ํšจํ•œ/ํ™•์ธ๋œ ๋ฌธ์ œ๋ฅผ ์—ด์–ด ๋‘ก๋‹ˆ๋‹ค.

๋ฌธ์ œ ๊ด€๋ฆฌ ์Šคํƒ€์ผ์— ๋Œ€ํ•œ ์ถ”๊ฐ€ ์ •๋ณด - https://www.dabapps.com/blog/sustainable-open-source-management/

์ด ํŽ˜์ด์ง€๊ฐ€ ๋„์›€์ด ๋˜์—ˆ๋‚˜์š”?
0 / 5 - 0 ๋“ฑ๊ธ‰