Django-rest-framework: etag๋ฅผ ์‰ฝ๊ฒŒ ์ถ”๊ฐ€/๊ฒ€์ฆํ•ฉ๋‹ˆ๋‹ค.

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

Django์—๋Š” ๋ฉ‹์ง„ etag/condition/last_modified ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. 'get'์„ ์žฅ์‹ํ•  ์ˆ˜ ์—†๊ธฐ ๋•Œ๋ฌธ์— drf ํด๋ž˜์Šค ๊ธฐ๋ฐ˜ ๋ณด๊ธฐ์—์„œ๋Š” ์ž‘๋™ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. get์€ http ์‘๋‹ต์ด ์•„๋‹Œ ๊ฐ์ฒด๋ฅผ ๋ฐ˜ํ™˜ํ•˜๊ธฐ ๋•Œ๋ฌธ์— etag ํ—ค๋”๋ฅผ ์‘๋‹ต์— ์ถ”๊ฐ€ํ•  ๋ฐฉ๋ฒ•์ด ์—†์Šต๋‹ˆ๋‹ค.

drf ๋‚ด์—์„œ ์ด ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ๋ณด๊ณ  ์‹ถ์Šต๋‹ˆ๋‹ค. ๋ฆฌ์†Œ์Šค์— ๋Œ€ํ•œ ์žฌ์ •์˜ ๊ฐ€๋Šฅํ•œ ๋ฉ”์„œ๋“œ ๋˜๋Š” etag๋ฅผ ์ƒ์„ฑํ•˜๋Š” ๋ฐ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ๋ณด๊ธฐ(๋˜๋Š” ๋ฏน์Šค์ธ)์˜ ๋ผ์ธ์„ ๋”ฐ๋ผ ์ƒ๊ฐํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

django์—์„œ ์ˆ˜ํ–‰ํ•˜๋Š” ๋‹ค๋ฅธ ๋ฐฉ๋ฒ•์€ ๋ฏธ๋“ค์›จ์–ด๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด์ง€๋งŒ ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ์ฒ˜๋Ÿผ ๋ทฐ์˜ ๋ณธ๋ฌธ ์‹คํ–‰์„ ๋‹จ์ถ•ํ•  ์ˆ˜๋Š” ์—†์Šต๋‹ˆ๋‹ค.

Enhancement

๊ฐ€์žฅ ์œ ์šฉํ•œ ๋Œ“๊ธ€

๋ถˆํ–‰ํžˆ๋„ drf-extensions์˜ Etag ๊ธฐ๋Šฅ์— ๋Œ€ํ•œ ๊ธฐ๋ณธ ๊ตฌํ˜„ ๋ฐ ๋ฌธ์„œ๋Š” ๋‹จ์ˆœํžˆ ์ž˜๋ชป๋˜์—ˆ์œผ๋ฉฐ ์œ„ํ—˜ํ•œ ๋ฒ„๊ทธ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. _response_๊ฐ€ ๋ณ€๊ฒฝ๋˜๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ _request_๊ฐ€ ๋ณ€๊ฒฝ๋˜๋ฉด Etag๋ฅผ ๋ณ€๊ฒฝํ•ฉ๋‹ˆ๋‹ค. ์„œ๋ฒ„ ์ธก ์บ์‹ฑ์— ๋Œ€ํ•ด ์›ํ•˜๋Š” ๊ฒƒ์ด ์ •ํ™•ํžˆ ๋ฌด์—‡์ด๋ฉฐ Etag์— ๋Œ€ํ•ด์„œ๋Š” ์›ํ•˜์ง€ ์•Š๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

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

์ž, ์˜ˆ๋น„ ํŒจ์น˜๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค: https://github.com/schinckel/django-rest-framework/commit/cc3a88edc6be21347a9b35929d158b8831ba9bd3

๊ทธ๋Ÿฌ๋‚˜ ์ด๊ฒƒ์— ๋Œ€ํ•œ ํ”ผ๋“œ๋ฐฑ์— ๋Œ€ํ•ด ๋งค์šฐ ๊ธฐ์˜๊ฒŒ ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค.

์ข‹์•„, ์ฒ˜์Œ์— ๋‚ด๊ฐ€ ์“ด ๊ฒƒ์€ ์ด๊ฒƒ์ด์—ˆ๋‹ค....

๋ฉ‹์ง€๋„ค์š”. ์ •๋ง ๋ณด๊ณ  ์‹ถ์Šต๋‹ˆ๋‹ค.

Coupla ์ƒ๊ฐ - ํ˜„์žฌ ๊ฐ€์ง€๊ณ  ์žˆ๋Š” View._ETAG๊ฐ€ ์•„๋‹Œ View.add_header๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
(๊ทธ๋ฆฌ๊ณ  .add_header๊ฐ€ ResponseMixin ํด๋ž˜์Šค๋กœ ์˜ฎ๊ฒจ์ ธ์•ผ ํ•  ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.)

๋‘˜์งธ, @condition , @etag ๋ฐ @last_modified ์žฅ์‹์ด https://github.com/django/django/blob/master/django/views/decorators ์˜ ์ง์ ‘์ ์ธ ๋ณต์ œ๊ฐ€ ๋  ์ˆ˜ ์žˆ๊ธฐ๋ฅผ

ํ•˜์ง€๋งŒ ์กฐ๊ธˆ ๋” ์ž์„ธํžˆ ์‚ดํŽด๋ณด๋‹ˆ...

๊ทธ๋ฆฌ๊ณ  ์•„๋งˆ๋„ ์ด๊ฒƒ์€ ๊ฒฐ๊ตญ ์˜ฌ๋ฐ”๋ฅธ ๋ฐฉ๋ฒ•์ด ์•„๋‹ ๊ฒƒ์ž…๋‹ˆ๋‹ค...

REST ํ”„๋ ˆ์ž„์›Œํฌ ๋ณด๊ธฐ์—์„œ ์‹ค์ œ๋กœ HttpResponses๋ฅผ ๋ฐ˜ํ™˜ํ•  ์ˆ˜ ์žˆ์ง€๋งŒ ์ผ๋ฐ˜์ ์ธ ์ฝ˜ํ…์ธ  ํ˜‘์ƒ/์ง๋ ฌํ™” ํ•ญ๋ชฉ์ด ๋ชจ๋‘ ์ ์šฉ๋˜์ง€๋Š” ์•Š์Šต๋‹ˆ๋‹ค. @last_modified , @etag ๋ฐ @condition ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๋Š” ๋นˆ HttpResponses๋งŒ ๋ฐ˜ํ™˜ํ•˜๋ฏ€๋กœ ์‹ค์ œ๋กœ ๋ฌธ์ œ๊ฐ€ ๋˜์ง€๋Š” ์•Š์Šต๋‹ˆ๋‹ค.

๊ทธ๋ž˜์„œ ๋‚ด๊ฐ€ ์ƒ๊ฐํ•˜๋Š” ๊ฒƒ์€ ๋‹จ์ˆœํžˆ __setitem__ __getitem__ ๋ฐ has_header ๋ฅผ Response ํด๋ž˜์Šค์— ์ถ”๊ฐ€ํ•˜๋ฉด Django์˜ ๊ธฐ์กด @last_modified ๋ผ๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค. @etag ๋ฐ @condition ๋ทฐ๊ฐ€ ์‚ฌ์šฉ as_ ์žฅ์‹์ด _so ์˜ค๋ž˜ REST ํ”„๋ ˆ์ž„ ์›Œํฌ๋ณด๊ธฐ์— ์ž˜ ์ž‘๋™ํ•ฉ๋‹ˆ๋‹ค return Response(status, data) ์˜คํžˆ๋ ค๋ณด๋‹ค๋Š” ์Šคํƒ€์ผ return data ์Šคํƒ€์ผ์„.

Obv ๊ทธ๊ฒƒ์„ ๋ฌธ์„œํ™”ํ•˜๋ฉด ๋„์›€์ด ๋  ๊ฒƒ์ด์ง€๋งŒ Django๊ฐ€ ์ด๋ฏธ ์ˆ˜ํ–‰ํ•˜๋Š” ๊ฒƒ์„ ๋ณต์ œํ•˜๋Š” ๊ฒƒ๋ณด๋‹ค ๋” ํ•ฉ๋ฆฌ์ ์ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์–ด๋–ป๊ฒŒ ์ƒ๊ฐํ•ด?

django ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๋ฐ ํ•œ ๊ฐ€์ง€ ๋ฌธ์ œ๊ฐ€ ์žˆ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋ฉ”์„œ๋“œ์™€ ํ•จ๊ป˜ ์ž‘๋™ํ•˜๋Š”์ง€ ํ™•์‹ ํ•  ์ˆ˜ ์—†๊ณ  ๋ฒ ์–ด ๊ธฐ๋Šฅ๋งŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋‚ด๊ฐ€ ์ž‘์„ฑํ•œ ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๋Š” ์ด ๊ฒฝ์šฐ์—๋งŒ StackOverflow๋ฅผ ํ†ตํ•ด ์ฐพ์€ ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ํ–ˆ์Šต๋‹ˆ๋‹ค.

๊ทธ๋ ‡์ง€ ์•Š์„ ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ๊ฒฝ์šฐ ์ด ์†”๋ฃจ์…˜์ด ๋” ๋‚˜์€ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.

๊ทธ๋ ‡๊ธด ํ•˜์ง€๋งŒ, ๋œ ์ƒ์šฉ๊ตฌ์ด๊ธฐ ๋•Œ๋ฌธ์— ๋ฐ˜ํ™˜ ๋ฐ์ดํ„ฐ ์Šคํƒ€์ผ์„ ๋ฐ˜ํ™˜ํ•˜๊ณ  ์žˆ์œผ๋ฉฐ ์ผ๋ฐ˜์ ์œผ๋กœ json์œผ๋กœ ์ง๋ ฌํ™”ํ•˜๋ ค๋Š” ๊ฐœ์ฒด๋งŒ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค. ์šฐ๋ฆฌ๋Š” ๋‘ ๊ฐ€์ง€ ๋ฐฉ๋ฒ•์œผ๋กœ ์ž‘๋™ํ•˜๋„๋ก ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋˜ ๋‹ค๋ฅธ ์˜ต์…˜์€ ์ด๋ฅผ View ํด๋ž˜์Šค์— ์ถ”๊ฐ€ํ•˜๋Š” ๋ฏน์Šค์ธ์ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋˜ํ•œ django ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๊ฐ€ ์กฐ๊ฑด๋ถ€ PUT, POST ๋ฐ DELETE ์š”์ฒญ๊ณผ ๊ด€๋ จํ•˜์—ฌ ์˜ฌ๋ฐ”๋ฅธ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•˜์ง€ ์•Š์„ ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด sinatra์— ํŒจ์น˜๋ฅผ ์ œ์ถœํ–ˆ์Šต๋‹ˆ๋‹ค.

๋งˆ์ง€๋ง‰ ๋น„ํŠธ๋ฅผ ๋ฌด์‹œํ•˜์‹ญ์‹œ์˜ค. ๋ถ„๋ช…ํžˆ ์ €๋Š” ์ฝ”๋“œ๋ฅผ ์ œ๋Œ€๋กœ ์ฝ์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค.

์‹ค์ œ๋กœ ์š”์ ์ด ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋Ÿฌํ•œ ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๋Š” _methods_์—์„œ ATM์„ ์ž‘๋™ํ•˜์ง€ ์•Š์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ถ”๊ฐ€ 'self' ์ธ์ˆ˜๊ฐ€ ์žˆ๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. ๋‚˜๋Š” ๊ทธ๊ฒƒ์„ ์กฐ์‚ฌํ•˜๊ณ  poss๊ฐ€ Django์— ํ‹ฐ์ผ“์„ ์ œ์ถœํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค. Django๋„ CBV์™€ ํ•จ๊ป˜ ์ž‘์—…ํ•ด์•ผ ํ•˜๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค...

์•„, ์•Œ๊ฒ ์Šต๋‹ˆ๋‹ค. ์ด์ œ https://docs.djangoproject.com/en/dev/topics/class-based-views/#decorating -class-based-views
๊ทธ๋ž˜์„œ ์ด๊ฒƒ์„ ๋‹ซ์œผ๋ ค๋ฉด ๋‹ค์Œ์ด ํ•„์š”ํ•˜๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค.

  1. ์‘๋‹ต์— ๋Œ€ํ•œ ์•ฝ๊ฐ„์˜ ์กฐ์ •
  2. ์•ฝ๊ฐ„์˜ ๊ฐ€๋ฒผ์šด ๋ฌธ์„œ

๋ถ„๋ช…ํžˆ ๊ทธ ๋ฌธ์„œ๋ฅผ ๋ณด์•˜์ง€๋งŒ ๊ทธ ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๋ฅผ ๋ณด์ง€ ๋ชปํ–ˆ์Šต๋‹ˆ๋‹ค!

๋‚˜๋Š” django ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๋ฅผ ์‚ฌ์šฉํ•˜๋ ค๊ณ  ํ–ˆ๊ณ  ์ดˆ๊ธฐ ๊ฒฐ๊ณผ๋Š” ์ •๋ง ์ด์ƒํ–ˆ๊ณ , ๊ด€๋ จ ์—†๋Š” etag ํ•จ์ˆ˜์— ๋‚˜ํƒ€๋‚˜๋Š” ๋ทฐ์—๋งŒ ์†ํ•ด์•ผ ํ•˜๋Š” ์ •๋ณด๊ฐ€ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.

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

https://bitbucket.org/vitormazzi/django-rest-framework/changeset/6f8de4500c6f

2.x ๋ฆด๋ฆฌ์Šค๊ฐ€ ๋‚˜์˜จ ์ง€๊ธˆ ์ด ๋ฌธ์ œ์— ์ƒˆ๋กœ์šด ์ƒ๋ช…์„ ๋ถˆ์–ด๋„ฃ๊ธฐ๋ฅผ ๋ฐ”๋ž๋‹ˆ๋‹ค.

์—ฌ๊ธฐ์—์„œ ๋‚ด ์ƒ๊ฐ์€ ๋Œ€๋ถ€๋ถ„ ํ”„๋กœ์ ํŠธ์— ๊ธฐ๋Šฅ์„ ์ถ”๊ฐ€ํ•˜๋ ค๊ณ  ์‹œ๋„ํ•˜๊ณ  ์ด ๊ฒŒ์‹œ๋ฌผ ์„ ์ฝ๋Š” ๊ฒƒ์—์„œ ๋น„๋กฏ๋œ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

DRF๊ฐ€ ETag๋ฅผ ๊ณ ๋ คํ•ด์•ผ ํ•˜๋Š” ๋‘ ๊ฐ€์ง€ ์˜์—ญ, ์ฆ‰ ๋ทฐ์—์„œ์˜ ์‚ฌ์šฉ๊ณผ ์ธ์Šคํ„ด์Šค ๋ฒ„์ „์˜ ๊ณ ์œ ํ•œ ํ‘œํ˜„์„ ์–ป๋Š” ๋ฐฉ๋ฒ•์ด ์žˆ์Šต๋‹ˆ๋‹ค.

๊ฒฌํ•ด

๊ฐ€์ ธ ์˜ค๊ธฐ
GET ์š”์ฒญ์€ ์ ์ ˆํ•œ ํ—ค๋”์—์„œ ๊ฐœ์ฒด ETag๋ฅผ ์ œ๊ณตํ•˜๊ธฐ๋งŒ ํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค. RetrieveModelMixin ๋Œ€ํ•œ ํ•œ ์ค„ ๋ณ€๊ฒฝ์€ ๋‹ค์Œ์„ ์‰ฝ๊ฒŒ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

def retrieve(self, request, *args, **kwargs):
    self.object = self.get_object()
    serializer = self.get_serializer(self.object)
    headers = {'ETag': self.object.etag}
    return Response(serializer.data, headers=headers)

๋„ฃ๊ธฐ, ํŒจ์น˜, ์‚ญ์ œ
HTTP ๋™์‚ฌ ์—…๋ฐ์ดํŠธ์— ๋Œ€ํ•œ ์ผ๋ฐ˜์ ์ธ ํ™•์ธ์€ ๋ณด๊ธฐ์˜ dispatch ํ•˜๊ฑฐ๋‚˜ ETag๊ฐ€ ์ผœ์ ธ ์žˆ๋Š”์ง€ ํ™•์ธํ•ด์•ผ ํ•˜๋ฏ€๋กœ ๋‹ค๋ฅธ ๋ฉ”์„œ๋“œ๋กœ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค(์•„๋ž˜ ์˜ต์…˜ ์„น์…˜ ์ฐธ์กฐ).

    header_etag = request.META.get('HTTP_IF_MATCH')
    if header_etag is None:
        return Response({'error': 'IF_MATCH header is required'}, status=400)

๊ทธ๋Ÿฐ ๋‹ค์Œ ๊ฐœ์ฒด๋ฅผ ๊ฒ€์ƒ‰ํ•œ ํ›„ ์š”์ฒญ์ด ์˜ฌ๋ฐ”๋ฅธ ๊ฐœ์ฒด๋ฅผ ๋ณด๊ณ  ์žˆ๋‹ค๊ณ  ์ƒ๊ฐํ•˜๋Š”์ง€ ํ™•์ธํ•˜๊ธฐ ์œ„ํ•ด ๋” ์ž์„ธํžˆ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค.

    if self.object.etag != header_etag:
        return Response({'error': 'object has been updated since you last saw it'}, status=412)

์ธ์Šคํ„ด์Šค ๋ฒ„์ „์˜ ๊ณ ์œ ํ•œ ํ‘œํ˜„

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

์ €๋Š” DRF๊ฐ€ ๊ธฐ๋ณธ์ ์œผ๋กœ obj.etag ๋ฅผ ์กฐํšŒํ•˜์ง€๋งŒ get_etag() ๋ฐ etag_var = 'get_my_objects_etag' ์™€ ๊ฐ™์€ ์ผ๋ฐ˜ CBV ํ๋ฆ„์„ ์‚ฌ์šฉํ•˜์—ฌ ๊ตฌ์„ฑํ•  ์ˆ˜ ์žˆ๋‹ค๊ณ  ์ œ์•ˆํ•ฉ๋‹ˆ๋‹ค.

ํ—ค๋”์™€ ๋น„๊ตํ•˜๊ณ  ์œ ํ˜•์„ ํ•ด์„ํ•˜๋ ค๋Š” ์‹œ๋„๋Š” ๊ธฐ๊ปํ•ด์•ผ ๊ณ ํ†ต์Šค๋Ÿฌ์šธ ๊ฒƒ์ด๊ธฐ ๋•Œ๋ฌธ์— ETag๊ฐ€ ๊ฐ์ฒด์—์„œ ๋ฌธ์ž์—ด๋กœ ๊ฒ€์ƒ‰๋˜๋„๋ก ๊ฐ•์ œํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

์˜ต์…˜

  • ETag ์‚ฌ์šฉ์„ ์ผœ๊ฑฐ๋‚˜ ๋„๊ธฐ ์œ„ํ•œ ์ „์—ญ ์„ค์ •(์ง๋ ฌ ๋ณ€ํ™˜๊ธฐ ๋“ฑ).
  • ๋ณด๊ธฐ์— ๋Œ€ํ•œ ๋‘ ๊ฐ€์ง€ ์„ค์ •:

    • use_etags (๋˜๋Š” ์ด์™€ ์œ ์‚ฌํ•œ ๊ฒƒ) - ๋ถ€์šธ

    • etag_var - ํ•ด๋‹น ๊ฐ์ฒด์— ๋Œ€ํ•ด getattr ํ•  ์ˆ˜ ์žˆ๋Š” ํ•จ์ˆ˜ ์ด๋ฆ„ ๋ฌธ์ž์—ด

@ghickman - ETag ๋ฐ LastModified๋ฅผ ๊ฒฐ์ •ํ•˜๋Š” ๋™์ž‘์ด ๋‹ค๋ฅธ ํ”Œ๋Ÿฌ๊ทธํ˜• ํด๋ž˜์Šค์™€ ์œ ์‚ฌํ•˜๊ฒŒ ๋ณด์ด๋Š”์ง€ ํ™•์ธํ•˜๊ณ  ์‹ถ์Šต๋‹ˆ๋‹ค. ์ฆ‰. ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๊ฒƒ์ด ์žˆ์Šต๋‹ˆ๋‹ค.

class MyView(views.APIView):
    cache_lookup_classes = []

์บ์‹ฑ ์„œ๋ช…์€ ETag์™€ LastModified๋ฅผ ๋ชจ๋‘ ์ฒ˜๋ฆฌํ•ด์•ผ ํ•˜๋ฉฐ ์šฐ๋ฆฌ๊ฐ€ ์ œ๊ณตํ•˜๊ณ ์ž ํ•˜๋Š” ๋‘ ๊ฐ€์ง€๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

  • ์ง€์ •๋œ ๊ฐ์ฒด ์ธ์Šคํ„ด์Šค์—์„œ etag ๋ฐ/๋˜๋Š” ๋งˆ์ง€๋ง‰์œผ๋กœ ์ˆ˜์ •๋œ ์‚ฌํ•ญ์„ ๊ฒฐ์ •ํ•ฉ๋‹ˆ๋‹ค.
  • ๋“ค์–ด์˜ค๋Š” ์š”์ฒญ์— ๋”ฐ๋ผ etag ๋ฐ/๋˜๋Š” ๋งˆ์ง€๋ง‰ ์ˆ˜์ •์„ ์„ ์ œ์ ์œผ๋กœ ๊ฒฐ์ •ํ•ฉ๋‹ˆ๋‹ค.

BaseCacheLookup ์žˆ๊ณ  ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๋‘ ๊ฐ€์ง€ ๋ฉ”์„œ๋“œ ์„œ๋ช…์ด ์žˆ์Šต๋‹ˆ๋‹ค.

.object_etag_and_last_modified(self, view, obj)
.preemptive_etag_and_last_modified(self, view, request, *view_kwargs, **view_kwargs)

๊ฐ์ฒด๊ฐ€ (etag, last modified) ์˜ 2-ํŠœํ”Œ์„ ๋ฐ˜ํ™˜ํ•˜๋ฉด ๋‘˜ ์ค‘ ํ•˜๋‚˜๋Š” ๋‹จ์ˆœํžˆ ์—†์Œ์ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
๋“ค์–ด์˜ค๋Š” ์š”์ฒญ์— ์ผ์น˜ํ•˜๋Š” If-Modified-Since ๋˜๋Š” If-None-Match ํ—ค๋”๊ฐ€ ํฌํ•จ๋œ ๊ฒฝ์šฐ 304 Not Modified ์‘๋‹ต์ด ๋ฐ˜ํ™˜๋ฉ๋‹ˆ๋‹ค. ๋“ค์–ด์˜ค๋Š” ์‘๋‹ต์— ์ผ์น˜ํ•˜๋Š” If-Match ๋˜๋Š” If-Unmodified-Since๊ฐ€ ํฌํ•จ๋˜์–ด ์žˆ์œผ๋ฉด 412 Precondition Failed ์‘๋‹ต์ด ๋ฐ˜ํ™˜๋ฉ๋‹ˆ๋‹ค.

์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ์„ค๋ช…ํ•œ ๊ตฌํ˜„๊ณผ ์ผ์น˜ํ•˜๋Š” CacheLookupClass๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ๋‹ค๋ฅธ ๋ณ€ํ˜•๋„ ํ—ˆ์šฉ๋ฉ๋‹ˆ๋‹ค.

์˜ˆ๋ฅผ ๋“ค์–ด, ๋‹ค๋ฅธ ๋งˆ์ง€๋ง‰ ์ˆ˜์ • ์„ธ๋ถ„์„ฑ์—์„œ ์—ฌ๋Ÿฌ ์บ์‹œ ์กฐํšŒ ํด๋ž˜์Šค๋ฅผ ์ ์šฉํ•  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค.
GlobalLastModifiedLookup ์™ธ์— ObjectETagLookup ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋ฉด ์บ์‹œ๋œ ๋ณต์‚ฌ๋ณธ ์ดํ›„์— ์“ฐ๊ธฐ๊ฐ€ ์ˆ˜ํ–‰๋˜์ง€ ์•Š์€ ๊ฒฝ์šฐ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ํ˜ธ์ถœ์„ ์ˆ˜ํ–‰ํ•˜๊ธฐ ์ „์— ๋ทฐ๊ฐ€ ์„ ์ œ์ ์œผ๋กœ ๋ฐ˜ํ™˜๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. (Varnish์™€ ํ•จ๊ป˜ ์„œ๋ฒ„ ์ธก ์บ์‹ฑ์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ ์ด์™€ ๊ฐ™์€ ์ •๋ง ๊ธฐ๋ณธ์ ์ธ ์ •์ฑ…์ด๋ผ๋„ ์—„์ฒญ๋‚œ ์ฐจ์ด๋ฅผ ๋งŒ๋“ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค)

์ด ํ”Œ๋Ÿฌ๊ทธ์ธ์˜ ํด๋ž˜์Šค ์ธก๋ฉด์ด ํ•ฉ๋ฆฌ์ ์œผ๋กœ ๋“ค๋ฆฝ๋‹ˆ๊นŒ?

ํ˜„์žฌ ๊ตฌํ˜„์—์„œ ์‚ฌ์šฉํ•˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— LastModified์— ๋Œ€ํ•ด ์ƒ๊ฐํ•˜์ง€ ์•Š์•˜์ง€๋งŒ ์ฃผ์–ด์ง„ ๋ชฉ์ ์„ ๊ฐ์•ˆํ•  ๋•Œ ํฌํ•จํ•˜๋Š” ๊ฒƒ์ด ํ•ฉ๋ฆฌ์ ์ž…๋‹ˆ๋‹ค.

Pluggable ํด๋ž˜์Šค๋Š” ํŠนํžˆ LastModified ๋ฐ ETag ๊ตฌํ˜„์„ ๊ธฐ๋ณธ ์˜ˆ์ œ๋กœ ํฌํ•จํ•˜๋Š” ๊ฒฝ์šฐ ์ข‹์€ ์•„์ด๋””์–ด์ฒ˜๋Ÿผ ๋“ค๋ฆฝ๋‹ˆ๋‹ค. ๋‚˜๋Š” GET ์บ์‹ฑ์ด ํ”„๋กœ์ ํŠธ์— ๋Œ€ํ•œ ์ตœ์†Œํ•œ์˜ ๋ณ€๊ฒฝ์œผ๋กœ ๋งค์šฐ ์‰ฝ๊ฒŒ ์ผค ์ˆ˜ ์žˆ๋‹ค๋Š” ์•„์ด๋””์–ด๋ฅผ ์ข‹์•„ํ•ฉ๋‹ˆ๋‹ค.

๋‚˜๋Š” etag์™€ last_modified ์ƒ์„ฑ์„ ๋‹น์‹ ์ด ์ œ์•ˆํ•œ ๋Œ€๋กœ ๊ตฌํ˜„๋˜์ง€ ์•Š์„ ๋•Œ None ๋ฐ˜ํ™˜ํ•˜๋Š” ๋‘ ๊ฐ€์ง€ ๋ฐฉ๋ฒ•(๊ทธ ์ด๋ฆ„ ์ค‘)์œผ๋กœ ๋ถ„ํ• ํ•˜๋Š” ๊ฒƒ์„ ์„ ํ˜ธํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฐ ๋‹ค์Œ CacheLookup ๋ฐฑ์—”๋“œ๋Š” ํ•˜๋‚˜ ๋ฐ/๋˜๋Š” ๋‹ค๋ฅธ ํ•˜๋‚˜๋ฅผ ๊ตฌํ˜„ํ•˜๋„๋ก ์„ ํƒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์œ ์šฉํ•˜๋‹ค๊ณ  ์ƒ๊ฐ๋˜๋ฉด ๋‘ ๊ฐ€์ง€๋ฅผ ๊ฒฐํ•ฉํ•œ ํŽธ๋ฆฌํ•œ ์œ ํ‹ธ๋ฆฌํ‹ฐ ๋ฉ”์„œ๋“œ( cachable_obj_repr ๋˜๋Š” unique_obj_repr ์•„๋งˆ๋„?)๋ฅผ ํ•ญ์ƒ ์ œ๊ณตํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

tl;dr ์˜ˆ, ํ”Œ๋Ÿฌ๊ทธํ˜• ํด๋ž˜์Šค ์ธก๋ฉด์€ ํ•ฉ๋ฆฌ์ ์œผ๋กœ ๋“ค๋ฆฌ๊ณ  ํ›จ์”ฌ ๋” ํฐ ์œ ์—ฐ์„ฑ์„ ์ œ๊ณตํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ด์— ๋Œ€ํ•œ ํŒจ์น˜ ์ž‘์„ฑ์„ ์‹œ์ž‘ํ•˜๊ฒŒ ๋˜์–ด ๊ธฐ์ฉ๋‹ˆ๋‹ค.

์—ฌ๋Ÿฌ๋ถ„, ์•ˆ๋…•ํ•˜์„ธ์š”. ๊ด€์‹ฌ์ด ์žˆ์œผ์‹œ๋ฉด ๋‚ด ํ™•์žฅ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ http://chibisov.github.io/drf-extensions/docs/ ์—์„œ etag ์ง€์›์— ๋Œ€ํ•œ ๋‹ค๋ฅธ ์ ‘๊ทผ ๋ฐฉ์‹์„ ๊ตฌํ˜„ํ–ˆ์Šต๋‹ˆ๋‹ค

@chibisov Neato. #1019๋ฅผ ๋๋‚ด์•ผ ํ•˜๋ฏ€๋กœ ๋ฌธ์„œ ์–ด๋”˜๊ฐ€์— ์ด์™€ ๊ฐ™์€ ํŒจํ‚ค์ง€์— ์—ฐ๊ฒฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

#1019๋กœ ๋‹ซ๊ณ  @chibisov ์˜ ํŒจํ‚ค์ง€๊ฐ€ ๋‚˜์—ด๋ฉ๋‹ˆ๋‹ค.

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

๋ถˆํ–‰ํžˆ๋„ drf-extensions์˜ Etag ๊ธฐ๋Šฅ์— ๋Œ€ํ•œ ๊ธฐ๋ณธ ๊ตฌํ˜„ ๋ฐ ๋ฌธ์„œ๋Š” ๋‹จ์ˆœํžˆ ์ž˜๋ชป๋˜์—ˆ์œผ๋ฉฐ ์œ„ํ—˜ํ•œ ๋ฒ„๊ทธ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. _response_๊ฐ€ ๋ณ€๊ฒฝ๋˜๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ _request_๊ฐ€ ๋ณ€๊ฒฝ๋˜๋ฉด Etag๋ฅผ ๋ณ€๊ฒฝํ•ฉ๋‹ˆ๋‹ค. ์„œ๋ฒ„ ์ธก ์บ์‹ฑ์— ๋Œ€ํ•ด ์›ํ•˜๋Š” ๊ฒƒ์ด ์ •ํ™•ํžˆ ๋ฌด์—‡์ด๋ฉฐ Etag์— ๋Œ€ํ•ด์„œ๋Š” ์›ํ•˜์ง€ ์•Š๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

@mbox ๊ฐ€์žฅ ์ข‹์€ ๋ฐฉ๋ฒ•์€ drf-extensions์—์„œ ์ด์— ๋Œ€ํ•œ ๋ฌธ์ œ๋ฅผ ์—ฌ๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๋˜๋Š” ์—ฌ๊ธฐ์—์„œ DRF "ํ•ต์‹ฌ" ๋ฌธ์ œ๊ฐ€ ์—ด๋ ค ์žˆ๋‹ค๊ณ  ์ƒ๊ฐํ•˜๋Š” ๊ฒฝ์šฐ์ž…๋‹ˆ๋‹ค. ํ…Œ์ŠคํŠธ์— ์‹คํŒจํ•˜๋ฉด ๋ฌธ์ œ๋ฅผ ์‚ดํŽด๋ณด๋Š” ๋ฐ ์ข‹์€ ์ถœ๋ฐœ์ ์ด ๋  ๊ฒƒ์ž…๋‹ˆ๋‹ค.

@mbox @xordoquy
๋ฐฉ๊ธˆ DRF API๋ฅผ ํ†ตํ•ด ๋ฆฌ์†Œ์Šค๋ฅผ ์กฐ์ž‘ํ•˜๊ธฐ ์œ„ํ•œ ๋‚™๊ด€์  ๋™์‹œ์„ฑ ์ œ์–ด๋ฅผ ํ—ˆ์šฉํ•˜๋Š” drf-extensions(https://github.com/chibisov/drf-extensions/pull/171)์— PR์„ ์ œ์ถœํ–ˆ์Šต๋‹ˆ๋‹ค. ๋ชจ๋“  ๊ฐœ์ฒด ํ•„๋“œ์˜ ์‹œ๋งจํ‹ฑ ํ•ด์‹œ๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ์œผ๋ฉฐ ๋ฐ๋ชจ ๋ชฉ์ ์œผ๋กœ ํ…Œ์ŠคํŠธ ์•ฑ์„ ํฌํ•จํ–ˆ์Šต๋‹ˆ๋‹ค. Python 2.7, 3.4, 3.5์—์„œ DRF>=3.3.1 ๋ฐ django>=1.8์— ๋Œ€ํ•ด ํ…Œ์ŠคํŠธ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

๊ฐ์‚ฌ

๋ฏธ๋ž˜์˜ ๋…์ž๋ฅผ ์œ„ํ•œ ์ฐธ๊ณ  ์‚ฌํ•ญ - DRF์™€ ํ•จ๊ป˜ Django์˜ ์กฐ๊ฑด๋ถ€ ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด ์ž‘์€ ํŒจํ‚ค์ง€๋ฅผ ๋งŒ๋“ค์—ˆ์Šต๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ ๊ด€์‹ฌ์ด ์žˆ๋Š” ๊ฒฝ์šฐ:
https://github.com/jozo/django-rest-framework-condition

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