Werkzeug: werkzeug๋Š” ASGI๋ฅผ ์ง€์›ํ•  ๊ณ„ํš์ด ์žˆ์Šต๋‹ˆ๊นŒ?

์— ๋งŒ๋“  2018๋…„ 06์›” 07์ผ  ยท  21์ฝ”๋ฉ˜ํŠธ  ยท  ์ถœ์ฒ˜: pallets/werkzeug

Werkzeug๋Š” ๋งŽ์€ ์œ ์šฉํ•œ ๋ฐฉ๋ฒ•์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. ์ฒ˜์Œ๋ถ€ํ„ฐ ์‹œ์ž‘ํ•˜๋Š” ๊ฒƒ๋ณด๋‹ค ASGI๋ฅผ ์ง€์›ํ•˜๋Š” ๊ฒƒ์ด ํ›จ์”ฌ ์‰ฌ์šธ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

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

์˜ˆ, Werkzeug์™€ Flask๋Š” ๊ฒฐ๊ตญ ASGI๋ฅผ ์ง€์›ํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ด์— ๋Œ€ํ•œ ์ผ์ •์€ ์—†์ง€๋งŒ ๋ˆ„๊ตฐ๊ฐ€ PR์„ ์‹œ์ž‘ํ–ˆ๋‹ค๋ฉด ๊ธฐ๊บผ์ด ๊ฒ€ํ† ํ•˜๋Š” ๋ฐ ๋„์›€์„ ๋“œ๋ฆฌ๊ฒ ์Šต๋‹ˆ๋‹ค.

๊ทธ๋Ÿฌ๋‚˜ ๋‚˜๋Š” ๊ทธ๊ฒƒ์„ ๊ตฌํ˜„ํ•˜๋Š” ์‚ฌ๋žŒ์ด ์•„๋‹ˆ๋ฏ€๋กœ ์ปค๋ฎค๋‹ˆํ‹ฐ์˜ ๋„์›€์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. ์•„๋ž˜์˜ ์ตœ์‹  ์—…๋ฐ์ดํŠธ๋ฅผ ์ฐธ์กฐํ•˜์‹ญ์‹œ์˜ค. https://github.com/pallets/werkzeug/issues/1322#issuecomment -600926145

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

์˜ˆ, Werkzeug์™€ Flask๋Š” ๊ฒฐ๊ตญ ASGI๋ฅผ ์ง€์›ํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ด์— ๋Œ€ํ•œ ์ผ์ •์€ ์—†์ง€๋งŒ ๋ˆ„๊ตฐ๊ฐ€ PR์„ ์‹œ์ž‘ํ–ˆ๋‹ค๋ฉด ๊ธฐ๊บผ์ด ๊ฒ€ํ† ํ•˜๋Š” ๋ฐ ๋„์›€์„ ๋“œ๋ฆฌ๊ฒ ์Šต๋‹ˆ๋‹ค.

๊ทธ๋Ÿฌ๋‚˜ ๋‚˜๋Š” ๊ทธ๊ฒƒ์„ ๊ตฌํ˜„ํ•˜๋Š” ์‚ฌ๋žŒ์ด ์•„๋‹ˆ๋ฏ€๋กœ ์ปค๋ฎค๋‹ˆํ‹ฐ์˜ ๋„์›€์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. ์•„๋ž˜์˜ ์ตœ์‹  ์—…๋ฐ์ดํŠธ๋ฅผ ์ฐธ์กฐํ•˜์‹ญ์‹œ์˜ค. https://github.com/pallets/werkzeug/issues/1322#issuecomment -600926145

์ด ์ž‘์—…์— ๊ด€์‹ฌ์ด ์žˆ์Šต๋‹ˆ๋‹ค.

์ผ๋ถ€ ์ž‘๋™ํ•˜์ง€๋งŒ ํ•ดํ‚น๋œ ASGI ์ง€์›์ด ์ง„ํ–‰ ์ค‘์ž…๋‹ˆ๋‹ค: werkzeug , flask . ๋” ์ง„ํ–‰ํ•˜๊ธฐ ์ „์— ๊ฐ€์ง€๊ณ  ๊ณ„์‹  ๊ณ„ํš์— ๋Œ€ํ•ด ๋” ์•Œ๊ณ  ์‹ถ์Šต๋‹ˆ๋‹ค.

๋‚ด๊ฐ€ ๊ฑฐ๊ธฐ์—์„œ ํ•œ ๋ชจ๋“  ๊ฒƒ์ด ์ผ๋ฐ˜์ ์œผ๋กœ ๋” ์ข‹์„ ์ˆ˜ ์žˆ๋‹ค๋Š” ์‚ฌ์‹ค์„ ๋„˜์–ด์„œ ์ด๋ฏธ ๋‚˜์—๊ฒŒ ์ผ์–ด๋‚˜๋Š” ์ผ:

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

๋‹น์‹ ์˜ ์ƒ๊ฐ์„ ์ €์—๊ฒŒ ๋ง ํ•ด์ฃผ์„ธ์š”.

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

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

์ปจํ…์ŠคํŠธ ๋กœ์ปฌ ์ง€์›์€ ๋งค์šฐ ์ทจ์•ฝํ•ฉ๋‹ˆ๋‹ค.

์ปจํ…์ŠคํŠธ ๋กœ์ปฌ(asyncio์šฉ)์€ 3.7 stdlib์— ์žˆ์Šต๋‹ˆ๋‹ค. https://docs.python.org/3.7/library/contextvars.html
์ด์ „ ๋ฒ„์ „์˜ Python์„ ์ง€์›ํ•˜๊ธฐ ์œ„ํ•ด ์„ ํƒ์  compat ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์™€ ํ•จ๊ป˜ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค. (๋˜๋Š” Flask์˜ ASGI๊ฐ€ 3.7 ์ด์ƒ์ด ๋  ๊ฒƒ์ด๋ผ๊ณ  ๊ฐ€์ •ํ•˜์‹ญ์‹œ์˜ค)

werkzeug๋Š” ์Šค๋ ˆ๋“œ ๋กœ์ปฌ์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๊นŒ? (๋‚˜๋Š” Flask๊ฐ€ ํ•œ๋‹ค๋Š” ๊ฒƒ์„ ์•Œ๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค)

ASGI์—์„œ ์‹คํ–‰ํ•  ๋•Œ ๋™๊ธฐ ํ•จ์ˆ˜์— ๋Œ€ํ•œ ์š”์ฒญ ์‹œ ์–‘์‹ ๋ฐ์ดํ„ฐ๋ฅผ ๊ตฌ๋ฌธ ๋ถ„์„ํ•˜๋Š” ํ™•์‹คํ•œ ๋ฐฉ๋ฒ•์ด ์—†์Šต๋‹ˆ๋‹ค.

์–‘์‹ ๋ฐ์ดํ„ฐ๋ฅผ ๊ตฌ๋ฌธ ๋ถ„์„ํ•˜๊ธฐ ์œ„ํ•œ ๋™๊ธฐ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์ œ๊ณตํ•˜์ง€ ์•Š๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค. (๋˜๋Š” ์–ด๋–ค ์‹์œผ๋กœ๋“  ์š”์ฒญ ๋ณธ๋ฌธ์— ์•ก์„ธ์Šคํ•˜๊ธฐ ์œ„ํ•ด) ๋Œ€์‹  ๋น„๋™๊ธฐ API๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. ์—ฌ๊ธฐ์—์„œ ๋ช‡ ๊ฐ€์ง€ ์˜ˆ๋ฅผ ๋ณด๋ ค๋ฉด Starlette์˜ API๋ฅผ ์ฐธ์กฐํ•˜์„ธ์š”. https://github.com/encode/starlette#body

Python 3.7์ด ๋‚˜์™”๊ธฐ ๋•Œ๋ฌธ์— ๋‹ค๋ฅธ ์ฝ”๋“œ๊ฐ€ ์˜ํ–ฅ์„ ๋ฐ›์ง€ ์•Š๋Š” ํ•œ Python 3.7์ด ํ•„์š”ํ•œ ๋น„๋™๊ธฐ ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์„ ๋ฐ˜๋Œ€ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ๊ธฐ๋ณธ 3.7 ์†”๋ฃจ์…˜๋งŒํผ ์ข‹์€ ๋ฐฑํฌํŠธ๊ฐ€ ์žˆ๋‹ค๋ฉด ๋” ์ข‹์Šต๋‹ˆ๋‹ค!

๋น„๋™๊ธฐ ์–‘์‹ ๋ฐ์ดํ„ฐ ๊ตฌ๋ฌธ ๋ถ„์„์— ๊ด€ํ•ด์„œ๋Š”... ๋น„๋™๊ธฐ ํ•จ์ˆ˜์—์„œ await request.parse() ์™€ ๊ฐ™์€ ๊ฒƒ์œผ๋กœ ์ถฉ๋ถ„ํ•  ๊ฒƒ ๊ฐ™์€๋ฐ ๊ตฌ๋ฌธ ๋ถ„์„๋˜์ง€ ์•Š์€ ์–‘์‹ ๋ฐ์ดํ„ฐ์— ์•ก์„ธ์Šคํ•˜๋ ค๊ณ  ํ•  ๋•Œ ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๊นŒ?

๋ฌผ๋ก ์ž…๋‹ˆ๋‹ค. ๋˜๋Š” $ values = await request.values() values = await request.values values ๋ฐ ์นœ๊ตฌ๋ฅผ ๋น„๋™๊ธฐ์‹์œผ๋กœ ๋งŒ๋“ญ๋‹ˆ๋‹ค.

์‚ฌ์ด์— ํ• ๋‹นํ•˜์ง€ ์•Š๊ณ  dict ์š”์†Œ๋ฅผ ์ง์ ‘ ๊ฐ€์ ธ์˜ค๋Š” ๋™์•ˆ ๋น„๋™๊ธฐ ํ˜ธ์ถœ์„ ์ˆ˜ํ–‰ํ•˜๋ ค๋ฉด (await request.form)['foo'] ์™€ ๊ฐ™์€ ๋‹ค์†Œ ์ถ”ํ•œ ๊ฒƒ์ด ํ•„์š”ํ•˜์ง€ ์•Š์Šต๋‹ˆ๊นŒ?

๋„ค :(

๊ทธ๋Ÿฌ๋‚˜ ๊ทธ๊ฒƒ์ด ํŠนํžˆ ํ”ผํ•  ์ˆ˜ ์žˆ๋Š”์ง€ ํ™•์‹คํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๋‚˜๋Š” ์ƒ๊ฐํ•˜์ง€ ์•Š๋Š”๋‹ค

form = await request.form
form['foo']

์ •๋ง ๊ทธ ์ด์ƒ๋„ ์ดํ•˜๋„ ์•„๋‹ˆ๋‹ค.

await request.parse()
request.form['foo']

๊ทธ๊ฒƒ์€ ๋ถ„๋ช…ํžˆ ์ทจํ–ฅ์— ๋”ฐ๋ผ ๋‹ฌ๋ผ์งˆ ์ˆ˜ ์žˆ์ง€๋งŒ.

๋‚˜๋Š” ์šฐ๋ฆฌ๊ฐ€ _members_๊ฐ€ ๋ชจ๋‘ ๋น„๋™๊ธฐํ™”๋˜๋Š” "๋น„๋™๊ธฐ ์‚ฌ์ „"์„ ๋Œ€์‹  ๋ฐœ๋ช…ํ•  ์ˆ˜๋„ ์žˆ๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค.

๋ฌผ๋ก , ์•„๋‹ˆ๋ฉด ๊ฐ’๊ณผ ์นœ๊ตฌ๋ฅผ ๋น„๋™๊ธฐ์‹์œผ๋กœ ๋งŒ๋“œ์‹ญ์‹œ์˜ค. values โ€‹โ€‹= await request.values โ€‹โ€‹๋˜๋Š” values โ€‹โ€‹= await request.values(), ์ฃผ๋กœ ์–ด๋Š ๊ฒƒ์ด ๋” ์ข‹์•„ ๋ณด์ด๋Š”์ง€์— ๋”ฐ๋ผ ๋‹ค๋ฆ…๋‹ˆ๋‹ค.

์†์„ฑ๋ณด๋‹ค๋Š” I/O ์ˆ˜ํ–‰ ์ž‘์—…์— ๋Œ€ํ•œ ํ•จ์ˆ˜ ํ˜ธ์ถœ์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค.

(await request.form)['foo'] ์™€ ๊ฐ™์€ ๋‹ค์†Œ ์ถ”ํ•œ ๊ฒƒ์ด ํ•„์š”ํ•˜์ง€ ์•Š์Šต๋‹ˆ๊นŒ? ์‚ฌ์ด์— ํ• ๋‹นํ•˜์ง€ ์•Š๊ณ  dict ์š”์†Œ๋ฅผ ์ง์ ‘ ๊ฐ€์ ธ์˜ค๋Š” ๋™์•ˆ ๋น„๋™๊ธฐ ํ˜ธ์ถœ์„ ์ˆ˜ํ–‰ํ•˜๋ ค๋ฉด?

์–ด๊นจ๋ฅผ ์œผ์“ฑ - ํ•˜์ง€ ๋งˆ์„ธ์š”.

asyncio๋Š” ์ฝ”๋“œ๋ฒ ์ด์Šค์˜ ์–ด๋Š ๋ถ€๋ถ„์ด I/O๋ฅผ ์ˆ˜ํ–‰ํ•˜๋Š”์ง€์— ๋Œ€ํ•ด ๋ฐ˜๋“œ์‹œ ๋” ๋ช…์‹œ์ ์ด๋ฏ€๋กœ ์ด๋ฅผ ๋ณ„๋„์˜ ์ค„๋กœ ๋‚˜๋ˆ„๋Š” ๊ฒฝํ–ฅ์ด ์žˆ์Šต๋‹ˆ๋‹ค.

form = await request.form()
form['foo']

๋น„๋™๊ธฐ ์ง€์›์„ ์ถ”๊ฐ€ํ•˜๋Š” ๋ฐ ์ฐฌ์„ฑํ•˜์ง€๋งŒ ์—ฌ์ „ํžˆ Python 2 ๋ฐ ๋™๊ธฐํ™” ๋ฒ„์ „์„ ์ค‘๋‹จํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. @njsmith ์—๊ฒŒ ๋“ค์€ ํ•œ ๊ฐ€์ง€ ์ ‘๊ทผ ๋ฐฉ์‹์€ ๋ชจ๋“  ๊ฒƒ์„ ๋น„๋™๊ธฐ์‹์œผ๋กœ ์ž‘์„ฑํ•œ ๋‹ค์Œ 2to3๊ณผ ์œ ์‚ฌํ•œ ๋„๊ตฌ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋™๊ธฐํ™” ๋ฒ„์ „์„ ์ƒ์„ฑํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๋ถ„๋ช…ํžˆ ๊ทธ๊ฒƒ์€ urllib3์—์„œ ์‹œ๋„๋˜๊ณ  ์žˆ์ง€๋งŒ ๊ทธ๊ฒƒ์— ๋Œ€ํ•ด ์ถฉ๋ถ„ํžˆ ๋ชจ๋ฆ…๋‹ˆ๋‹ค.

request.form ๋“ฑ ๋’ค์— ์žˆ๋Š” ๊ฐœ์ฒด์— ๋งˆ๋ฒ•์„ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ๋Š”์ง€ ๊ถ๊ธˆํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ ํ˜ธ์ถœํ•˜๋ฉด ๋น„๋™๊ธฐ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•˜๋Š” ๋ฐ˜๋ฉด ์ผ๋ฐ˜์ ์ธ dict-like ๋ฉ”์„œ๋“œ๋Š” ๋™๊ธฐํ™”๋˜๊ณ  ๋น„๋™๊ธฐ ๋ชจ๋“œ์—์„œ๋Š” ์‹คํŒจํ•ฉ๋‹ˆ๋‹ค. ๋˜๋Š” ๋น„๋™๊ธฐ ๋ชจ๋“œ์—์„œ request.form ๋“ฑ์— ๋Œ€ํ•œ ์•ก์„ธ์Šค์— ์‹คํŒจํ•˜๊ณ  ๋น„๋™๊ธฐ ๋ฒ„์ „์— ๋Œ€ํ•ด ๋ณ„๋„์˜ ์ด๋ฆ„์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค(์˜ˆ: request.parse_form() ).

๋˜๋Š”... request_class = AsyncRequest ๋น„๋™๊ธฐ๋ฅผ ์›ํ•˜๋Š” ๊ฒฝ์šฐ; ์ด๊ฒƒ์€ ์‹ค์ œ๋กœ AsyncFlask ํด๋ž˜์Šค์˜ ๊ธฐ๋ณธ๊ฐ’์ด ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋˜๋Š”... request_class = ๋ˆ„๊ตฐ๊ฐ€๊ฐ€ ๋น„๋™๊ธฐ๋ฅผ ์›ํ•˜๋Š” ๊ฒฝ์šฐ AsyncRequest; ์ด๊ฒƒ์€ ์‹ค์ œ๋กœ AsyncFlask ํด๋ž˜์Šค์˜ ๊ธฐ๋ณธ๊ฐ’์ด ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๊ทธ๊ฒƒ์€ ๋‚˜์—๊ฒŒ ์˜๋ฏธ๊ฐ€ ์žˆ๋Š” ์ ‘๊ทผ ๋ฐฉ์‹์ž…๋‹ˆ๋‹ค. ๋„ค.

ํผ ํŒŒ์„œ์— ๊ด€ํ•ด์„œ๋Š” #1330์—์„œ sansio-ing์„ ์‹œ๋„ํ–ˆ์Šต๋‹ˆ๋‹ค.

https://github.com/andrew-d/python-multipart ์— 100% ์ ์šฉ ๋ฒ”์œ„์˜ ์ŠคํŠธ๋ฆฌ๋ฐ ์–‘์‹ ํŒŒ์„œ ๊ตฌํ˜„๋„ ์žˆ์Šต๋‹ˆ๋‹ค. (๋‹ค๋ฅธ ํ•˜๋‚˜๋ฅผ ์ฐพ์•˜์ง€๋งŒ "ํ”ผ๋“œ ๋ฐ์ดํ„ฐ, ์ด๋ฒคํŠธ ์ฒ˜๋ฆฌ" ํ๋ฆ„์— ์‰ฝ๊ฒŒ ์ ์‘ํ•  ์ˆ˜ ์žˆ๋Š”์ง€ ๋ช…ํ™•ํ•˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค.)

python-multipart ๋Š” ํ˜„์žฌ Starlette์— ์‚ฌ์šฉ ์ค‘์ธ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ž…๋‹ˆ๋‹ค. ์—ฌ๊ธฐ์—์„œ ๋น„๋™๊ธฐ ์ŠคํŠธ๋ฆผ๊ณผ์˜ ํ†ตํ•ฉ์„ ์‚ดํŽด๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. https://github.com/encode/starlette/blob/master/starlette/formparsers.py#L207

๋‚˜๋Š” ๋˜ํ•œ ๋™๊ธฐํ™” ๋ฐ ๋น„๋™๊ธฐ ํ˜ธํ™˜ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์ œ๊ณตํ•˜๋Š” ๊ฐ€์žฅ ์ข‹์€ ๋ฐฉ๋ฒ•์— ๋Œ€ํ•ด ์ƒ๊ฐํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. Starlette์— ๋Œ€ํ•ด์„œ๋„ ๊ทธ๋ ‡๊ฒŒ ํ•˜๊ธฐ๋ฅผ ์›ํ•˜๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค(Werkzeug์™€ ๋‹ค๋ฅธ ๋ฐฉํ–ฅ์œผ๋กœ ๊ฐ€๊ณ  ์žˆ์ง€๋งŒ ์ €์—๊ฒŒ๋Š” "๊ธฐ์กด ๋น„๋™๊ธฐ ์ธํ„ฐํŽ˜์ด์Šค๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ์–ด๋–ป๊ฒŒ ํ•ด์•ผ ์ด์ œ ๋™๊ธฐํ™” ํ•ญ๋ชฉ๋„ ํ‘œ์‹œํ•˜์‹ญ์‹œ์˜ค.")

Starlette์˜ ๊ฒฝ์šฐ ์‹ค์ œ ๊ตฌ๋ฌธ ๋ถ„์„์„ async parse(self) ๋ฉ”์„œ๋“œ๋กœ ํ‘ธ์‹œํ•˜๊ณ  ์ผ๋ฐ˜์ ์œผ๋กœ ์š”์ฒญ ๋””์ŠคํŒจ์น˜ ์ค‘ ์–ธ์  ๊ฐ€๋Š” ํ˜ธ์ถœํ•˜์ง€๋งŒ ์ผ๋ฐ˜ ์ผ๋ฐ˜ ์†์„ฑ form , files ๋“ฑ์„ ๋…ธ์ถœํ•ฉ๋‹ˆ๋‹ค. ... ์‚ฌ์šฉ์ž ์ฝ”๋“œ์—์„œ ๊ฒฐ๊ณผ์— ์•ก์„ธ์Šคํ•ฉ๋‹ˆ๋‹ค.

์ฃผ์ œ์—์„œ ๋ฒ—์–ด๋‚˜์ง€๋งŒ ์ธ๊ธฐ ์žˆ๋Š” ASGI ๋ฐ werkzeug ์‚ฌ๋ก€์™€ ๊ด€๋ จ์ด ์žˆ์Šต๋‹ˆ๋‹ค(์ด ๋ฌธ์ œ๋ฅผ ํƒˆ์„ ์‹œํ‚ค๊ณ  ์‹ถ์ง€ ์•Š์ง€๋งŒ ์ถ”๊ฐ€ํ•  ๋‚ด์šฉ ์—†์ด ์ค‘๋ณต ๋ฌธ์ œ๋ฅผ ๋งŒ๋“ค๊ณ  ์‹ถ์ง€๋Š” ์•Š์Œ).

https://github.com/django-extensions/django-extensions with ./manage.py runserver_plus ๋ฐ https://github.com/django/channels(ASGI ์‚ฌ์šฉ)๋ฅผ ์‹คํ–‰ ์ค‘์ธ ๊ฒฝ์šฐ Opcode -1 ์ธ์ŠคํŽ™ํ„ฐ์—์„œ ./manage.py runserver ๋ฅผ ์‹คํ–‰ํ•ด ๋ณด์„ธ์š”.

(Werkzeug๋Š” django-extensions๊ฐ€ ์žˆ๋Š” Django์˜ ํ›„๋“œ ์•„๋ž˜์—์„œ ์‚ฌ์šฉ๋˜๋ฉฐ ๋””๋ฒ„๊น…์„ ์œ„ํ•ด runserver_plus ๋กœ ๊ฐœ๋ฐœํ•˜๋Š” ๋ฐ ๋„ˆ๋ฌด ์ต์ˆ™ํ•ด์„œ ๋ช‡ ์‹œ๊ฐ„ ๋™์•ˆ ์ด์œ ๋ฅผ ์•Œ์•„๋ƒˆ์Šต๋‹ˆ๋‹ค.)

๊ฐœ๋ฐœ์—์„œ https๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก runserver_plus๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.
์ด์ œ Django ์ฑ„๋„์„ ์‚ฌ์šฉํ•˜์—ฌ Websockets ์ง€์›์„ ์ถ”๊ฐ€ํ•˜๋ ค๊ณ  ํ•˜๋Š”๋ฐ ์ฑ„๋„์ด runserver๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  runserver_plus๋ฅผ ์ง€์›ํ•˜์ง€ ์•Š๋Š”๋‹ค๋Š” ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ ์ฑ„๋„์„ ์‚ฌ์šฉํ•  ์ˆ˜๋„ ์žˆ๊ณ  https๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค. ๋‘˜ ๋‹ค ์‚ฌ์šฉํ•  ์ˆ˜๋Š” ์—†์Šต๋‹ˆ๋‹ค!

@davidism ์ด ์ฃผ์ œ์— ๋Œ€ํ•œ ๋งˆ์ง€๋ง‰ ๋‹ต๋ณ€ ์ดํ›„ Flask์— ๋Œ€ํ•œ ASGI ์ง€์› ๊ตฌํ˜„์— ๋ณ€๊ฒฝ ์‚ฌํ•ญ์ด ์žˆ๋Š”์ง€, Python 2์— ๋Œ€ํ•œ ์ง€์›์„ ์ข…๋ฃŒํ•˜๊ธฐ ์œ„ํ•ด ์ด ์ž‘์—…๊ณผ ๊ด€๋ จํ•˜์—ฌ ๊ณ„ํš์ด ๋ณ€๊ฒฝ๋˜์—ˆ๋Š”์ง€ ์—ฌ๋ถ€๋ฅผ ์•Œ๋ ค์ฃผ์‹ญ์‹œ์˜ค.

2018๋…„ 7์›” 2์ผ @davidism ์€ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์”๋‹ˆ๋‹ค.

๋ถ„๋ช…ํžˆ ๊ทธ๊ฒƒ์€ urllib3์—์„œ ์‹œ๋„๋˜๊ณ  ์žˆ์ง€๋งŒ ๊ทธ๊ฒƒ์— ๋Œ€ํ•ด ์ถฉ๋ถ„ํžˆ ๋ชจ๋ฆ…๋‹ˆ๋‹ค.

์–ผ๋งˆ ์ „์— ์ด๊ฒƒ์„ ๋ณด์•˜์Šต๋‹ˆ๋‹ค. ๋ˆ„๊ตฐ๊ฐ€ ๋” ๋ฐฐ์šฐ๊ณ  ์‹ถ๋‹ค๋ฉด python-trio/urllib3#1์— ๋งŽ์€ ์„ธ๋ถ€ ์ •๋ณด๊ฐ€ ์žˆ๋Š” ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค. ๋‹ค์Œ์„ ํฌํ•จํ•˜๋Š” urllib3/urllib3#1323์— ๋Œ€ํ•œ ๋งํฌ๋ฅผ ํ™•์ธํ•˜์„ธ์š”.

์†”๋ฃจ์…˜: ์šฐ๋ฆฌ๋Š” async/await ์ฃผ์„์ด ์žˆ๋Š” ๋ฒ„์ „์˜ ์ฝ”๋“œ ๋ณต์‚ฌ๋ณธ์„ ์œ ์ง€ํ•˜๊ณ  ์•ฝ๊ฐ„์˜ ์Šคํฌ๋ฆฝํŠธ๊ฐ€ ๋™๊ธฐ ๋ณต์‚ฌ๋ณธ์„ ์ž๋™์œผ๋กœ ๋‹ค์‹œ ์ œ๊ฑฐํ•˜์—ฌ ์œ ์ง€ํ•ฉ๋‹ˆ๋‹ค. ์•„๋ฆ„๋‹ต์ง€๋Š” ์•Š์ง€๋งŒ ๋ชจ๋“  ๋Œ€์•ˆ์ด ๋” ๋‚˜์˜๋‹ค๊ณ  ๋งํ•  ์ˆ˜ ์žˆ๋Š” ํ•œ...

(๊ด€์‹ฌ ์žˆ์œผ๋ฉด ๊ฑฐ๊ธฐ์—์„œ ๊ณ„์† ์ฝ์œผ์‹ญ์‹œ์˜ค.)

https://github.com/python-trio/urllib3/commits/bleach-spike์—์„œ ๊พธ์ค€ํ•œ ์ง„ํ–‰์„ ๋ฐ”ํƒ•์œผ๋กœ ์ด๊ฒƒ์ด ๊ณ„์†ํ•ด์„œ ์ž˜ ์ž‘๋™ํ•˜๊ณ  ์žˆ๋Š” ๊ฒƒ์„ ๋ณด๋‹ˆ ๋ฐ˜๊ฐ‘์Šต๋‹ˆ๋‹ค.

์ด ๋ฌธ์ œ๋ฅผ ๋‹ค์‹œ ๋ ˆ์ด๋”์— ์˜ฌ๋ฆฌ๊ธฐ ์œ„ํ•œ ์ž‘์€ ๋ฒ”ํ”„. ์˜ฌํ•ด 3.5๊ฐ€ EOL์— ๋„๋‹ฌํ•˜๋ฉด ๋น„๋™๊ธฐ ์ง€์›์— ๋Œ€ํ•ด ์ƒ๊ฐํ•˜๊ธฐ ์‹œ์ž‘ํ•˜๊ธฐ์— ์ข‹์€ ์‹œ๊ธฐ๋ผ๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค.

์ด ๊ธ€์ด ๊ฒŒ์‹œ๋œ ์ง€ 1๋…„ ๋ฐ˜์ด ์ง€๋‚ฌ์ง€๋งŒ ์ด๋ฅผ ๊ตฌํ˜„ํ•˜๊ธฐ ์œ„ํ•œ ํ™œ๋™์ด ๋งŽ์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค. ๋‚˜๋Š” ๊ฐœ์ธ์ ์œผ๋กœ asyncio์— ๋Œ€ํ•œ ๊ฒฝํ—˜์ด๋‚˜ ํ•„์š”์„ฑ์ด ์—†์œผ๋ฉฐ ASGI๋ฅผ ์ข‹์•„ํ•˜์ง€๋งŒ ๊ฒฐ์ฝ” ๋‚˜ ์ž์‹ ์„ ๋งก์„ ์ผ์ด ์•„๋‹™๋‹ˆ๋‹ค.

๊ทธ ์‚ฌ์ด Quart์˜ ์ €์ž์ธ @pgjones ๋Š” Werkzeug์— ๋” ๋งŽ์ด ๊ด€์—ฌํ•˜๊ฒŒ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. Quart๋Š” ์ด์ œ ๊ฐ€๋Šฅํ•œ ํ•œ ๋ฐฐํ›„์—์„œ Werkzeug๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ์œผ๋ฉฐ ๊ณ„์†ํ•ด์„œ ๊ฐœ๋ฐœํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ASGI๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์— ๋Œ€ํ•ด Flask ์œ ์ง€ ๊ด€๋ฆฌ์ž๋กœ๋ถ€ํ„ฐ ์•ฝ๊ฐ„์˜ ๋ฐ˜๋ฐœ์ด ์žˆ์—ˆ๊ธฐ ๋•Œ๋ฌธ์— Phil์€ ๋˜ํ•œ ์ตœ์†Œํ•œ async def ๊ธฐ๋Šฅ์— ๋Œ€ํ•œ ๋ผ์šฐํŒ…์„ ํ—ˆ์šฉํ•˜๋Š” ํŒ”๋ ˆํŠธ/ํ”Œ๋ผ์Šคํฌ#3412๋ฅผ ๋งŒ๋“ค์—ˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ์ด๋Š” ์ž ์‹œ ๋™์•ˆ ์ค‘๋‹จ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ์ด ์‹œ์ ์—์„œ ๋‚˜๋Š” ๊ทธ๊ฒƒ์— ์•ˆ์ฃผํ•˜๊ธฐ๋ณด๋‹ค ASGI๋กœ ๊ฐ€๊ณ  ์‹ถ์Šต๋‹ˆ๋‹ค. @edk0 ์€ sans-io ํ˜•์‹์„ ๊ตฌ๋ฌธ ๋ถ„์„ํ•˜๊ธฐ ์œ„ํ•ด #1330์„ ๋งŒ๋“ค์—ˆ์Šต๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ์ด ์—ญ์‹œ ์•„์ง ์ง„ํ–‰ ์ค‘์ด๋ฉฐ ๋จผ์ € ๋” ๋งŽ์€ ๋””์ž์ธ๊ณผ ๊ฒ€ํ† ๋ฅผ ๊ฑฐ์ณ์•ผ ํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค.

"์™œ Flask๋Š” Django๊ฐ€ ํ•˜๋Š” ์ผ์„ ํ•  ์ˆ˜ ์—†๋‚˜์š”?"๋ผ๊ณ  ๋ฌผ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ €๋Š” Django ๋‚ด๋ถ€ ์ „๋ฌธ๊ฐ€๋Š” ์•„๋‹ˆ์ง€๋งŒ @andrewgodwin ์€ Django๊ฐ€ ์›๋ž˜ WSGI์— ์ ์‘ํ•œ ๋ฐฉ์‹ ๋•Œ๋ฌธ์— Django๊ฐ€ "๋” ์‰ฌ์šด"(์ฝ๊ธฐ: ์—ฌ์ „ํžˆ ๋งค์šฐ ๋ณต์žกํ•œ) ์‹œ๊ฐ„์„ ๊ฐ–๋Š”๋‹ค๊ณ  ์„ค๋ช…ํ–ˆ์Šต๋‹ˆ๋‹ค. Werkzeug์™€ Flask๊ฐ€ ์‹œ์ž‘ํ•œ ๋งค์šฐ WSGI ์ค‘์‹ฌ API์ž…๋‹ˆ๋‹ค. ๋˜ํ•œ Django๋Š” Pallets๋ณด๋‹ค ํ›จ์”ฌ ๋” ๋งŽ์€ ํ’€ํƒ€์ž„ ๊ด€์‹ฌ๊ณผ ๋ฆฌ์†Œ์Šค๋ฅผ ๋ฐ›์Šต๋‹ˆ๋‹ค.

๊ทธ๋ ‡๋‹ค๋ฉด ์ด ๋ฌธ์ œ๋Š” ์–ด๋””์— ์žˆ์Šต๋‹ˆ๊นŒ? Werkzeug๋ฅผ ์‚ฌ์šฉํ•˜๋Š” Flask ํ˜ธํ™˜ ํ”„๋ ˆ์ž„์›Œํฌ๋ฅผ ์›ํ•œ๋‹ค๋ฉด Quart๋ฅผ ์‚ฌ์šฉํ•˜์„ธ์š”. Quart(๋˜๋Š” Flask)์— ๊ธฐ์—ฌํ•˜์—ฌ ๋ˆ„๋ฝ๋œ ๋ถ€๋ถ„์—์„œ API ํ˜ธํ™˜์„ฑ์„ ๋†’์ž…๋‹ˆ๋‹ค. Werkzeug์™€ Flask๊ฐ€ ASGI๋ฅผ ์ง€์›ํ•˜๋„๋ก ํ•˜๋ ค๋ฉด ํ•œ ๋‹จ๊ณ„ ๋” ์˜ฌ๋ผ์•ผ ํ•ฉ๋‹ˆ๋‹ค. ASGI์— ๋Œ€ํ•ด ๋ฐฐ์šฐ๊ธฐ ์‹œ์ž‘ํ•ฉ๋‹ˆ๋‹ค. Werkzeug API์˜ WSGI ๊ด€๋ จ ๋ฐ ์ฐจ๋‹จ ๋ถ€๋ถ„์„ ์‹๋ณ„ํ•˜๊ธฐ ์‹œ์ž‘ํ•ฉ๋‹ˆ๋‹ค. WSGI์™€ ASGI ๋ชจ๋‘์— ๋Œ€ํ•œ ๊ตฌํ˜„์„ ๊ฐ€๋Šฅํ•˜๊ฒŒ ํ•˜๊ธฐ ์œ„ํ•ด ๋งŒ๋“ค ์ˆ˜ ์žˆ๋Š” ์ถ”์ƒํ™”์— ๋Œ€ํ•ด ์ƒ๊ฐํ•˜๊ธฐ ์‹œ์ž‘ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฐ ๋‹ค์Œ ํ•ด๋‹น ์—ฐ๊ตฌ๋ฅผ ์ด ํ† ๋ก ์œผ๋กœ ๊ฐ€์ ธ์™€์„œ PR ๋””์ž์ธ ๋ฐ ์ž‘์„ฑ์„ ์‹œ์ž‘ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

Quart ์ œ์•ˆ์— ๊ฐ์‚ฌ๋“œ๋ฆฌ๋ฉฐ, ์ด์— ๋Œ€ํ•œ ๊ธฐ์—ฌ๋ฅผ ์ˆ˜๋ฝํ•˜๊ฒŒ ๋˜์–ด ๋งค์šฐ ๊ธฐ์ฉ๋‹ˆ๋‹ค.

๋‚˜๋Š” ์™œ Flask๊ฐ€ Django๊ฐ€ ์ด ๊ธฐ์‚ฌ ์—์„œ ํ•œ ์ผ์„ ํ•  ์ˆ˜ ์—†๋‹ค๊ณ  ์ƒ๊ฐํ•˜๋Š”์ง€ ๋Œ€๋‹ตํ•˜๋ ค๊ณ  ๋…ธ๋ ฅํ–ˆ์Šต๋‹ˆ๋‹ค. ๊ถ๊ทน์ ์œผ๋กœ ๋‚˜๋Š” palettes/flask#3412๊ฐ€ Flask๋ฅผ ์œ„ํ•œ ์ตœ๊ณ ์˜ ์†”๋ฃจ์…˜์ด๋ผ๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค.

Werkzeug์˜ ๊ด€์ ์—์„œ ASGI๊ฐ€ ๊ฐ€๋Šฅํ•˜์ง€๋งŒ ์•ฝ๊ฐ„์˜ ๊ณ ํ†ต์ด ์žˆ๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค. ๊ณ ํ†ต์˜ ์ฃผ๋ชฉํ• ๋งŒํ•œ ์˜ˆ๋Š” Werkzeug์˜ ๋งŽ์€ ๊ฒƒ๋“ค์ด WSGI ํ˜ธ์ถœ ๊ฐ€๋Šฅ(์˜ˆ: ์˜ˆ์™ธ)์ด๋ผ๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ASGI์—์„œ๋Š” ์ด ๊ธฐ๋Šฅ์„ ์–ด๋–ป๊ฒŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š”์ง€/์‚ฌ์šฉํ•ด์•ผ ํ•˜๋Š”์ง€ ๋ช…ํ™•ํ•˜์ง€ ์•Š์œผ๋ฏ€๋กœ ์ œ๊ฑฐํ•˜๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค.

๋‚ด ๊ณ„ํš์€ Werkzeug๋ฅผ Quart์— ๊ณ„์† ํ†ตํ•ฉํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. Werkzeug๋ฅผ ASGI(sans-io)์— ๋งž๊ฒŒ ์กฐ์ •ํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

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

๊ด€๋ จ ๋ฌธ์ œ

miki725 picture miki725  ยท  10์ฝ”๋ฉ˜ํŠธ

ngaya-ll picture ngaya-ll  ยท  8์ฝ”๋ฉ˜ํŠธ

SimonSapin picture SimonSapin  ยท  12์ฝ”๋ฉ˜ํŠธ

abathur picture abathur  ยท  13์ฝ”๋ฉ˜ํŠธ

masklinn picture masklinn  ยท  11์ฝ”๋ฉ˜ํŠธ