๐ŸŒŠ 210919 WIL

์ƒˆ๋ฒฝ์ˆ˜์˜ยท2021๋…„ 9์›” 19์ผ
0

๐Ÿ“ ํ•ญํ•ด ์ผ์ง€

๋ชฉ๋ก ๋ณด๊ธฐ
2/23

์ฒซ์งธ ๋‚  TIL์„ ์ ์€ ํ›„ 1์ฃผ์ฐจ ํ”„๋กœ์ ํŠธ ๋งˆ๋ฌด๋ฆฌ์™€ ์›น๊ฐœ๋ฐœ ๊ฐ•์˜ ์ˆ˜๊ฐ•์— ์น˜์—ฌ ๊ฒฐ๊ตญ WIL๋กœ ๋ฐ”๋กœ ๊ฑด๋„ˆ์˜ค๊ฒŒ ๋˜์—ˆ๋‹ค..
ํ”„๋กœ์ ํŠธ ์ œ์ถœ์ผ์—” ํ™”์ด์ž 2์ฐจ ์ ‘์ข… ํ›„์œ ์ฆ์œผ๋กœ ์˜จ์ข…์ผ ๋‘ํ†ต์— ์‹œ๋‹ฌ๋ ธ์ง€๋งŒ ํŒ€์›๋“ค์˜ ๋„์›€์œผ๋กœ ๋ฌด์‚ฌํžˆ ์ œ์ถœํ•  ์ˆ˜ ์žˆ์—ˆ๋‹ค..
์„œ๋ก ์ด ํ•‘๊ณ„๋กœ ๊ธธ์–ด์กŒ๋Š”๋ฐ.. ์šฐ์„  1์ฃผ์ฐจ ๋™์•ˆ ๋‚ด๊ฐ€ ๋ฐฐ์› ๋˜ ๊ฒƒ๋“ค์„ ์ •๋ฆฌํ•ด๋ณด๊ณ ์ž ํ•œ๋‹ค!

์›น๊ฐœ๋ฐœ ํ”Œ๋Ÿฌ์Šค ๊ฐ•์˜

1์ฃผ์ฐจ

์™•์ดˆ๋ณด๋ฐ˜์˜ ๊ณผ์ •๋“ค์„ ๋น ๋ฅด๊ฒŒ ํ›‘์–ด๋ณด๋Š” ์‹œ๊ฐ„์ด์—ˆ๋‹ค. ๊ธฐ๋ณธ ํ‹€์„ ์งœ๋Š” ๊ฒƒ์—์„œ ์‹œ์ž‘ํ•ด, ์‚ฌ์ง„ ์—…๋กœ๋“œ ๊ฐ™์€ ๋””ํ…Œ์ผ์„ ์ถ”๊ฐ€ํ•˜๊ณ  ์„œ๋ฒ„์— ๋ฐฐํฌํ•˜๋Š” ๊ณผ์ •๊นŒ์ง€ ํ”„๋กœ์ ํŠธ๋ฅผ ์œ„ํ•œ ๊ธฐ๋ณธ์ ์ธ ์ค€๋น„๋ฅผ ํ•˜๋Š” ๊ณผ์ •์ด์—ˆ๋˜ ๊ฒƒ ๊ฐ™๋‹ค. ํŠนํžˆ ์„œ๋ฒ„์™€ ํด๋ผ์ด์–ธํŠธ ๊ฐ„์˜ ํ•‘ํ์„ ์‹ ๊ฒฝ์“ฐ๋Š” ๊ฒƒ์ด ์ค‘์š”ํ•˜๋‹ค๊ณ  ๋А๊ปด์กŒ๋‹ค.

2์ฃผ์ฐจ

1์ฃผ์ฐจ ๋ฏธ๋‹ˆ ํ”„๋กœ์ ํŠธ์˜ ์ฃผ์š” ํฌ์ธํŠธ ์ค‘ ํ•˜๋‚˜์ธ Jinja2๋ฅผ ์ฒ˜์Œ ๋งˆ์ฃผํ–ˆ๋˜ ์‹œ๊ฐ„์ด์—ˆ๋‹ค. ajax๋ฅผ ์ด์šฉํ•ด ๊ฐ’์„ ๋ถˆ๋Ÿฌ์˜ค๋Š” ๊ฒƒ์— ์ต์ˆ™ํ–ˆ๋˜์ง€๋ผ ์ดˆ๋ฐ˜์—๋Š” ์ดํ•ดํ•˜๊ธฐ ์‰ฝ์ง€ ์•Š์•˜๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ๊ฐ•์˜๋ฅผ ๋”ฐ๋ผ ์ง์ ‘ ์ฝ”๋”ฉ์„ ํ•ด๋ณด๋ฉฐ ์›นํŽ˜์ด์ง€์˜ ์„ฑ๊ฒฉ์— ๋”ฐ๋ผ ajax์™€ Jinja2๊ฐ€ ์ ์ ˆํžˆ ์“ฐ์ผ ์ˆ˜ ์žˆ๋‹ค๋Š” ์‚ฌ์‹ค์„ ์•Œ๊ฒŒ ๋˜์—ˆ๊ณ , ๋น„๋กœ์†Œ ํ”„๋กœ์ ํŠธ์— ์ง์ ‘ ์ ์šฉํ•  ์ˆ˜ ์žˆ์—ˆ๋‹ค.

๋„์›Œ์ง€๊ณ  ๋‚œ ๋’ค ๋ฐ”๋€” ์ผ์ด ์—†๋Š” ํŽ˜์ด์ง€ ๊ฐ™์€ ๊ฒฝ์šฐ์—๋Š” html์„ ์™„์„ฑํ•ด์„œ ๋ณด๋‚ด์ฃผ๋Š” ์„œ๋ฒ„์‚ฌ์ด๋“œ๋ Œ๋”๋ง(SSR) ๋ฐฉ์‹์˜ Jinja2 ํ…œํ”Œ๋ฆฟ์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ์ ์ ˆํ•˜๋‹ค.

3์ฃผ์ฐจ

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

4์ฃผ์ฐจ

๋‚ด๊ฐ€ ์ด๋ฒˆ ํŒ€ ํ”„๋กœ์ ํŠธ์—์„œ ๋งก์€ ๋ถ€๋ถ„์ธ ํšŒ์›๊ฐ€์ž…๊ณผ ๋กœ๊ทธ์ธ ๊ธฐ๋Šฅ ์ œ์ž‘์„ ๋ฐฐ์šธ ์ˆ˜ ์žˆ์—ˆ๋˜ ๊ฐ•์˜์˜€๋‹ค. Jinja2์™€ ํ•จ๊ป˜ 1์ฃผ์ฐจ ํ”„๋กœ์ ํŠธ์˜ ์ฃผ์š” ํฌ์ธํŠธ์˜€๋˜ JWT๋ฅผ ๋‹ค๋ฃจ๋Š” ๊ฐ•์˜์ด๊ธฐ๋„ ํ–ˆ๊ธฐ์— ๋” ์—ด์‹ฌํžˆ ๊ณต๋ถ€ํ–ˆ๋‹ค. ๋ณด์•ˆ์— ์ทจ์•ฝํ•˜์ง€ ์•Š์œผ๋ฉด์„œ ๊ฐ„ํŽธํ•˜๊ณ  ๋น ๋ฅธ ์ธ์ฆ ๋•๋ถ„์— ์„œ๋ฒ„์˜ ๋ถ€ํ•˜๋„ ์ค„์ผ ์ˆ˜ ์žˆ๋‹ค๋Š” ๊ฒƒ์ด JWT์˜ ํฐ ์žฅ์ ์œผ๋กœ ๋А๊ปด์กŒ๋‹ค.

ํšŒ์› ๋กœ๊ทธ์ธ ์‹œ์Šคํ…œ์„ ๋‹ค๋ฃฐ ๋•Œ, ๊ธฐ์กด์˜ ์„œ๋ฒ„ ์‚ฌ์ด๋“œ ์„ธ์…˜์˜ ๋ฐฉ์‹์„ ์ด์šฉํ•œ๋‹ค๋ฉด ๋กœ๊ทธ์ธ ์„ธ์…˜ ์ •๋ณด๋ฅผ ์„œ๋ฒ„๋‚˜ DB์— ์ €์žฅํ•œ๋‹ค. ๋‹จ์ผ ์„œ๋ฒ„์ด๊ฑฐ๋‚˜ ๋™์‹œ ์ ‘์†์ž ์ˆ˜๊ฐ€ ์ ์€ ๊ฒฝ์šฐ์—๋Š” ๋ฌธ์ œ๊ฐ€ ๋˜์ง€ ์•Š์„ ์ˆ˜ ์žˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ๋™์‹œ ์ ‘์†์ž ์ˆ˜๊ฐ€ ๋Š˜์–ด๋‚˜๊ณ  ์„œ๋ฒ„์˜ ๊ฐฏ์ˆ˜๊ฐ€ ๋Š˜์–ด๋‚œ๋‹ค๋ฉด DB๊ฐ€ ๋ถ€๋‹ดํ•˜๋Š” ํšŒ์› ๋ฐ์ดํ„ฐ์˜ ์–‘๋„ ๋Š˜์–ด๋‚  ๋ฟ๋”๋Ÿฌ, ๋กœ๊ทธ์ธ ์ดํ›„ ๋กœ๊ทธ์ธ ์ •๋ณด๊ฐ€ ์ €์žฅ๋˜์ง€ ์•Š์€ ์„œ๋ฒ„๋กœ ์ ‘์†ํ•  ์‹œ ์žฌ์ ‘์†์„ ํ•ด์•ผํ•˜๋Š” ๋ถˆํŽธํ•จ์„ ๊ฐ์ˆ˜ํ•ด์•ผํ•œ๋‹ค. ์ถ”๊ฐ€์ ์œผ๋กœ ๋งค ์š”์ฒญ ๋งˆ๋‹ค ์•„์ด๋””๋‚˜ ๋น„๋ฐ€๋ฒˆํ˜ธ์™€ ๊ฐ™์€ ์ •๋ณด๋“ค์ด ์˜ค๊ฐ€๋Š” ๊ฒƒ์€ ๋ณด์•ˆ์ ์ธ ์ธก๋ฉด์—์„œ๋„ ์ทจ์•ฝํ•˜๋‹ค.

JWT(JSON Web Token)์€ header . payload . signature ์„ธ ๋ถ€๋ถ„์œผ๋กœ ์ด๋ฃจ์–ด์ ธ์žˆ๋‹ค. ์šฐ์„  payload ์•ˆ์—๋Š” ์ด ํ† ํฐ์˜ ๋ฐœ๊ธ‰ ์ •๋ณด, ์œ ํšจ ๊ธฐ๊ฐ„, ์‚ฌ์šฉ์ž์˜ ์„œ๋น„์Šค ์ด์šฉ ๊ถŒํ•œ๊ณผ ๊ฐ™์€ ๋ฐ์ดํ„ฐ๊ฐ€ JSON ํŒŒ์ผ ํ˜•์‹์œผ๋กœ base64 ์ธ์ฝ”๋”ฉ๋˜์–ด ๋‹ด๊ฒจ์žˆ๋‹ค. payload ๋ถ€๋ถ„๋งŒ ๋ณด์•˜์„ ๋•Œ๋Š” ๋ˆ„๊ตฌ๋‚˜ ๋””์ฝ”๋”ฉ๊ณผ ์ธ์ฝ”๋”ฉ ๊ณผ์ •์„ ํ†ตํ•ด ๊ฐ’์„ ์กฐ์ž‘ํ•  ์ˆ˜ ์žˆ์–ด๋ณด์ด๊ธฐ ๋•Œ๋ฌธ์— ๋ณด์•ˆ์— ์ทจ์•ฝํ•  ๊ฒƒ์ด๋ผ๊ณ  ์ƒ๊ฐํ•  ์ˆ˜ ์žˆ์ง€๋งŒ ๊ทธ๋ ‡์ง€ ์•Š๋‹ค. ๋‚˜๋จธ์ง€ ๋‘ ๋ถ€๋ถ„์ด ๋‚จ์•„์žˆ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. header ๋ถ€๋ถ„์„ ์‚ดํŽด๋ณด๋ฉด, ์—ฌ๊ธฐ์—” JWT๋ฅผ ์ง€์ •ํ•˜๋Š” ๊ณ ์ •๊ฐ’์ธ type๊ณผ signature ๊ฐ’์„ ๋งŒ๋“œ๋Š”๋ฐ์— ์‚ฌ์šฉ๋˜๋Š” ์•Œ๊ณ ๋ฆฌ์ฆ˜ ์ง€์ •๊ฐ’์ด ๋“ค์–ด์žˆ๋Š”๋ฐ, ์„ธ๋ฒˆ์งธ ๋ถ€๋ถ„์ธ signature๊ฐ’์€ ๋ฐ”๋กœ ์ด header์˜ ์•Œ๊ณ ๋ฆฌ์ฆ˜ ์ง€์ •๊ฐ’์„ ๋ฐ”ํƒ•์œผ๋กœ payload ๊ฐ’๊ณผ ์„œ๋ฒ„๋งŒ์ด ์•Œ๊ณ ์žˆ๋Š” SECRET KEY ๋ฐ์ดํ„ฐ๊ฐ€ ์•”ํ˜ธํ™”๋˜์–ด ๋งŒ๋“ค์–ด์ง„๋‹ค. ๊ทธ๋ ‡๊ธฐ ๋•Œ๋ฌธ์— ๋งŒ์•ฝ ์ž„์˜์˜ ์‚ฌ์šฉ์ž๊ฐ€ payload์˜ ๊ฐ’์ด ์กฐ์ž‘๋œ ํ† ํฐ์œผ๋กœ ์„œ๋น„์Šค์— ์ ‘๊ทผ์„ ์‹œ๋„ํ• ์ง€๋ผ๋„ ๊ณ ์œ ์˜ SECRET KEY ๊ฐ’์„ ๋Œ€์ž…ํ•ด ๋‚˜์˜จ signature ๊ฐ’์ด ๋‹ค๋ฅด๋‹ค๋ฉด ์„œ๋ฒ„๋Š” ์กฐ์ž‘๋œ ํ† ํฐ์˜ ์ ‘๊ทผ์„ ๊ฑฐ๋ถ€ํ•  ์ˆ˜ ์žˆ๋‹ค.

์—ฌ๊ธฐ์„œ JWT์˜ ์žฅ์ ์ด ๋ณด์ด๊ธฐ ์‹œ์ž‘ํ•œ๋‹ค. ์˜ˆ์ „๋ถ€ํ„ฐ ์“ฐ์ด๋˜ ์„ธ์…˜์ด๋‚˜ ์ธ๊ฐ€(authorization) ํ† ํฐ์˜ ๊ฒฝ์šฐ์—๋Š” ์‚ฌ์šฉ์ž์˜ ๊ถŒํ•œ ์ •๋ณด๋ฅผ DB์— ์ €์žฅํ•ด๋†“๊ณ  ๋กœ๊ทธ์ธ ์ •๋ณด๋ฅผ ๋ฐ›์„ ๋•Œ ๋งˆ๋‹ค DB๋ฅผ ํ™•์ธํ•˜๋Š” ๊ณผ์ •์„ ๊ฑฐ์ณ์•ผํ–ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ JWT๋Š” ๊ฐ ์‚ฌ์šฉ์ž์˜ ๋ธŒ๋ผ์šฐ์ €์— cookie์˜ ํ˜•ํƒœ๋กœ ์ €์žฅ๋˜๊ณ  ์ด๋ฅผ ์„œ๋ฒ„๋กœ ๋ณด๋‚ธ๋‹ค. ์„œ๋ฒ„๋Š” ๋ฐ›์€ ํ† ํฐ์˜ ์ •๋ณด๋ฅผ ๊ณ ์œ  SECRET KEY ๊ฐ’์„ ์ด์šฉํ•ด ๊ฒ€์ฆํ•˜๊ณ , signature ๊ฐ’์ด ๋ณ€ํ–ˆ๋Š”์ง€์˜ ์—ฌ๋ถ€๋ฅผ ํŒ๋‹จํ•˜๋Š” ๊ฒƒ ๋งŒ์œผ๋กœ ์ธ์ฆ ๊ณผ์ •์„ ์ˆ˜ํ–‰ํ•  ์ˆ˜๊ฐ€ ์žˆ๋‹ค. DB๋ฅผ ๊ฑฐ์น˜๋Š” ์ˆ˜๊ณ ์—†์ด ์ธ์ฆ๊ณผ ๊ถŒํ•œํ™•์ธ ๊ณผ์ •์„ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ๋‹ค. ๋ฌผ๋ก  ์–ธ์  ๊ฐ€๋Š” ๋†’์€ ์ˆ˜์ค€์˜ ์•”ํ˜ธํ™” ํ•ด๋…๋ฒ•์„ ์ด์šฉํ•˜์—ฌ JWT์˜ ๊ณ ์œ  ํ‚ค๊ฐ’์„ ์•Œ์•„๋‚ผ ์ˆ˜๋„ ์žˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ์•„๋ฌด๋ฆฌ ๊ณ ๋„ํ™”๋œ ์•”ํ˜ธ ํ•ด๋… ๊ธฐ์ˆ ์ผ์ง€๋ผ๋„ ์ ˆ๋Œ€์ ์ธ ์‹œ๊ฐ„์„ ํ•„์š”๋กœ ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์ด๋Š” payload ๊ฐ’ ์•ˆ์˜ ํ† ํฐ ์œ ํšจ ๊ธฐ๊ฐ„์„ ์งง๊ฒŒ ์„ค์ •ํ•˜์—ฌ ํ•ด๋…๋˜๊ธฐ ์ „์— ํ† ํฐ์ด ๋ฌดํšจํ™”๋˜๋„๋ก ํ•˜๋Š” ๋ฐฉ๋ฒ•์œผ๋กœ ํ•ด๊ฒฐ์ด ๊ฐ€๋Šฅํ•˜๋‹ค๊ณ  ์ƒ๊ฐํ•œ๋‹ค.

๋ฏธ๋‹ˆ ํ”„๋กœ์ ํŠธ SCRUM!

๋กœ๊ทธ์ธ ํšŒ์›๊ฐ€์ž… ๊ธฐ๋Šฅ ๊ตฌํ˜„

๊ฐ•์˜๋ฅผ ๋ฐ”ํƒ•์œผ๋กœ ๊ตฌํ˜„ํ–ˆ์—ˆ๋Š”๋ฐ, ๊ธฐ์กด ๊ฐ•์˜์—์„œ ๋งŒ๋“ค์—ˆ๋˜ SWEETER ์„œ๋น„์Šค๋Š” ํšŒ์›๊ฐ€์ž… ์ •๋ณด๊ฐ€ ์•„์ด๋””์™€ ๋น„๋ฐ€๋ฒˆํ˜ธ์— ๊ทธ์ณค๋˜ ๋ฐ˜๋ฉด, ์šฐ๋ฆฌ์˜ SCRUM ์„œ๋น„์Šค์—์„œ๋Š” ๋‹‰๋„ค์ž„๊ณผ ์ด๋ฉ”์ผ, ๊ทธ๋ฆฌ๊ณ  ๊ฐ„๋‹จํ•œ ์†Œ๊ฐœ๊ธ€์ด ์ถ”๊ฐ€ ๋๋‹ค. ๋‹‰๋„ค์ž„ ์ž…๋ ฅ์ฐฝ์—์„œ๋Š” ์•„์ด๋””์™€ ๊ฐ™์ด ์ค‘๋ณตํ™•์ธ์„ ํ•„์š”๋กœ ํ–ˆ๊ณ , ํ•œ๊ธ€๋กœ๋งŒ ๋‹‰๋„ค์ž„์„ ๋งŒ๋“ค ์ˆ˜ ์žˆ๊ฒŒ ํ•˜๊ธฐ ์œ„ํ•ด ํ•œ๊ธ€์ธ์ง€ ํŒ๋‹จํ•˜๋Š” ์ •๊ทœ์‹๋„ ๋งŒ๋“ค์–ด์•ผ ํ–ˆ๋‹ค. ์ด๋ฉ”์ผ๋„ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ ์ด๋ฉ”์ผ ํ˜•์‹์ด ๋งž๋Š”์ง€๋ฅผ ํŒ๋‹จํ•˜๋Š” ์ •๊ทœ์‹์„ ๋งŒ๋“ค์–ด์•ผํ–ˆ๋‹ค. 1์ฃผ์ฐจ ๊ฐ•์˜์—์„œ ๊ฐ•์กฐํ•˜์…จ๋˜ ์„œ๋ฒ„์™€ ํด๋ผ์ด์–ธํŠธ ๊ฐ„์˜ ํ•‘ํ์„ ์ฃผ์˜ํ•˜๋ฉฐ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ–ˆ๊ณ  ๋ฌด๋ฆฌ์—†์ด ๊ธฐ๋Šฅ์„ ๋งŒ๋“ค ์ˆ˜ ์žˆ์—ˆ๋‹ค.

์„œ๋ฒ„ ์ฝ”๋“œ

@app.route('/sign_in', methods=['POST'])
def sign_in():
    # ๋กœ๊ทธ์ธ
    # ํด๋ผ์ด์–ธํŠธ์—์„œ ๋ฐ›์•„์˜จ ๊ฐ’์„ ๊ฐ ๋ณ€์ˆ˜์— ๋‹ด๊ธฐ
    username_receive = request.form['username_give']
    password_receive = request.form['password_give']
    # ํŒจ์Šค์›Œ๋“œ๋Š” ํ•ด์‰ฌํ•จ์ˆ˜๋ฅผ ์ด์šฉํ•˜์—ฌ ์•”ํ˜ธํ™” ํ•˜๊ณ  ๊ทธ ๊ฐ’์„ pw_hash ๋ณ€์ˆ˜์— ๋‹ด๋Š”๋‹ค
    pw_hash = hashlib.sha256(password_receive.encode('utf-8')).hexdigest()
    # DB์˜ ํšŒ์› ์ •๋ณด์™€ ๋งž๋Š”์ง€ ํ™•์ธ
    result = db.users.find_one({'username': username_receive, 'password': pw_hash})

    if check_today(username_receive):
        status = 'posted' # ๋‹น์ผ ๊ฐ์˜ค ์ด๋ฏธ ์ž‘์„ฑํ–ˆ์„ ์‹œ์—๋Š” ๋ฉ”์ธ ํŽ˜์ด์ง€
    else:
        status = 'not_yet' # ์•„๋‹ˆ๋ผ๋ฉด ์ž‘์„ฑ ํŽ˜์ด์ง€๋กœ
    if result is not None:
        payload = {
            'id': username_receive,
            'exp': datetime.utcnow() + timedelta(seconds=60 * 60 * 3)  # ๋กœ๊ทธ์ธ 3์‹œ๊ฐ„ ์œ ์ง€
        }
        token = jwt.encode(payload, SECRET_KEY, algorithm='HS256').decode('utf-8')
        # ๋กœ๊ทธ์ธ ์ •๋ณด๋ฅผ ์•”ํ˜ธํ™”ํ•œ JWT

        # DB์˜ ์ •๋ณด์™€ ๋งž๋‹ค๋ฉด ๋กœ๊ทธ์ธ ์„ฑ๊ณต
        return jsonify({'result': 'success', 'token': token, 'post_state': status})
    # ์ฐพ์ง€ ๋ชปํ•˜๋ฉด
    else:
        return jsonify({'result': 'fail', 'msg': '์•„์ด๋””/๋น„๋ฐ€๋ฒˆํ˜ธ๊ฐ€ ์ผ์น˜ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.'})


@app.route('/sign_up/save', methods=['POST'])
def sign_up():
    # ํšŒ์›๊ฐ€์ž…
    # ํด๋ผ์ด์–ธํŠธ์—์„œ ๋ฐ›์•„์˜จ ๊ฐ’์„ ๊ฐ ๋ณ€์ˆ˜์— ๋‹ด๊ธฐ
    username_receive = request.form['username_give']
    password_receive = request.form['password_give']
    # ํŒจ์Šค์›Œ๋“œ๋Š” DB์— ๊ทธ๋Œ€๋กœ ๋‹ด์ง€ ์•Š๊ณ  ์•„๋ž˜์˜ ํ•ด์‹œํ•จ์ˆ˜๋ฅผ ์ด์šฉํ•˜์—ฌ ์•”ํ˜ธํ™”ํ•œ ๊ฐ’์„ ๋Œ€์‹  ๋‹ด๋Š”๋‹ค
    password_hash = hashlib.sha256(password_receive.encode('utf-8')).hexdigest()
    email_receive = request.form['email_give']
    nickname_receive = request.form['nickname_give']
    profile_info_receive = request.form['profile_info_give']

    # ๋ฐ›์•„์˜จ ๊ฐ’์„ DB์— ์ €์žฅ
    doc = {
        "username": username_receive,                               # ์•„์ด๋””
        "password": password_hash,                                  # ๋น„๋ฐ€๋ฒˆํ˜ธ
        "nickname": nickname_receive,                               # ๋‹‰๋„ค์ž„
        "profile_pic": "",                                          # ํ”„๋กœํ•„ ์‚ฌ์ง„ ํŒŒ์ผ ์ด๋ฆ„
        "profile_pic_real": "profile_pics/profile_placeholder.png", # ํ”„๋กœํ•„ ์‚ฌ์ง„ ๊ธฐ๋ณธ ์ด๋ฏธ์ง€
        "profile_info": profile_info_receive,                       # ํ”„๋กœํ•„ ์†Œ๊ฐœ๊ธ€
        "email": email_receive                                      # ์ด๋ฉ”์ผ
    }
    db.users.insert_one(doc)
    return jsonify({'result': 'success'})

# ์•„์ด๋”” ์ค‘๋ณตํ™•์ธ
@app.route('/sign_up/check_dup', methods=['POST'])
def check_dup():
    username_receive = request.form['username_give']
    # ํด๋ผ์ด์–ธํŠธ๋กœ๋ถ€ํ„ฐ ๋ฐ›์•„์˜จ ๊ฐ’์ด DB์— ์กด์žฌํ•˜๋Š”์ง€ ์กด์žฌํ•˜์ง€ ์•Š๋Š”์ง€ ํŒŒ์•…
    exists = bool(db.users.find_one({"username": username_receive}))
    return jsonify({'result': 'success', 'exists': exists})

# ๋‹‰๋„ค์ž„ ์ค‘๋ณตํ™•์ธ
@app.route('/sign_up/check_dup_nick', methods=['POST'])
def check_dup_nick():
    nickname_receive = request.form['nickname_give']
    # ํด๋ผ์ด์–ธํŠธ๋กœ๋ถ€ํ„ฐ ๋ฐ›์•„์˜จ ๊ฐ’์ด DB์— ์กด์žฌํ•˜๋Š”์ง€ ์กด์žฌํ•˜์ง€ ์•Š๋Š”์ง€ ํŒŒ์•…
    exists = bool(db.users.find_one({"nickname": nickname_receive}))
    return jsonify({'result': 'success', 'exists': exists})
์ฝ”๋“œ๋ฅผ ์ž…๋ ฅํ•˜์„ธ์š”

ํด๋ผ์ด์–ธํŠธ script

<script>
        {#๋กœ๊ทธ์ธ ๊ธฐ๋Šฅ ํ•จ์ˆ˜#}
        function sign_in() {
            let username = $("#input-username").val() {#์•„์ด๋”” ๊ฐ’์„ username์ด๋ผ๋Š” ๋ณ€์ˆ˜์— ๋‹ด๊ธฐ#}
            let password = $("#input-password").val() {#ํŒจ์Šค์›Œ๋“œ ๊ฐ’์„ password๋ผ๋Š” ๋ณ€์ˆ˜์— ๋‹ด๊ธฐ#}

            {#์•„์ด๋””, ํŒจ์Šค์›Œ๋“œ ๋ฏธ์ž…๋ ฅ ์‹œ ๊ฒฝ๊ณ  ๋ฌธ๊ตฌ ๋…ธ์ถœ, input ๋ฐ•์Šค์— ๊ฐ’์ด ์žˆ๋‹ค๋ฉด ๊ฒฝ๊ณ  ๋ฌธ๊ตฌ ์ˆจ๊น€#}
            if (username == "") {
                $("#help-id-login").text("์•„์ด๋””๋ฅผ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”.").removeClass("is-safe").addClass("is-danger")
                $("#input-username").focus()
                return;
            } else {
                $("#help-id-login").text("")
            }

            if (password == "") {
                $("#help-password-login").text("๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”.").removeClass("is-safe").addClass("is-danger")
                $("#input-password").focus()
                return;
            } else {
                $("#help-password-login").text("")
            }

            $.ajax({
                type: "POST",
                url: "/sign_in",
                data: {
                    username_give: username,
                    password_give: password
                },
                {#๊ฐ์˜ค ์ž‘์„ฑ์ด ๋œ ์•„์ด๋””๋ผ๋ฉด ๋ฉ”์ธ๋ฆฌ์ŠคํŠธ ํŽ˜์ด์ง€๋กœ, ์ž‘์„ฑ๋˜์ง€ ์•Š์€ ์ƒํƒœ๋ผ๋ฉด ๊ฐ์˜ค ์ž‘์„ฑ ํŽ˜์ด์ง€๋กœ#}
                success: function (response) {
                    if (response['result'] == 'success') {
                        {# ๋ธŒ๋ผ์šฐ์ €์˜ ์ฟ ํ‚ค์— ํ† ํฐ์„ ํ‚ค๊ฐ’ ํ˜•ํƒœ๋กœ ์ €์žฅ #}
                        $.cookie('mytoken', response['token'], {path: '/'});
                        let post_state = response['post_state']
                        window.localStorage.setItem('post_state', post_state)
                        if (post_state == 'posted') {
                            window.location.replace("/")
                        } else {
                            window.location.href = "/boards/form"
                        }
                    } else {
                        alert(response['msg'])
                    }
                }
            });
        }

        {# ํšŒ์›๊ฐ€์ž… ๊ธฐ๋Šฅ ํ•จ์ˆ˜ #}
        function sign_up() {
            {# ๊ฐ ํšŒ์›์ •๋ณด ๊ฐ’์„ ๋ณ€์ˆ˜์— ๋‹ด๊ธฐ #}
            let username = $("#input-username").val()
            let password = $("#input-password").val()
            let password2 = $("#input-password2").val()
            let nickname = $("#input-nickname").val()
            let email = $("#input-email").val()
            let profile_info = $("#input-profile_info").val()

            console.log(username, password, password2, nickname, email, profile_info)

            {# ์•„์ด๋””๊ฐ’์ด ์—†๋Š” ์ƒํƒœ์—์„œ ๋กœ๊ทธ์ธ์„ ์‹œ๋„ํ•˜๋ฉด ๊ฒฝ๊ณ  ๋ฌธ๊ตฌ ํ‘œ์‹œ#}
            if ($("#help-id").hasClass("is-danger")) {
                alert("์•„์ด๋””๋ฅผ ๋‹ค์‹œ ํ™•์ธํ•ด์ฃผ์„ธ์š”.")
                return;
            {# ์ค‘๋ณตํ™•์ธ์„ ํ•˜์ง€์•Š๊ณ  ํšŒ์›๊ฐ€์ž…์„ ์ง„ํ–‰ํ•  ์‹œ ๊ฒฝ๊ณ  ๋ฌธ๊ตฌ ํ‘œ์‹œ#}
            } else if (!$("#help-id").hasClass("is-success")) {
                alert("์•„์ด๋”” ์ค‘๋ณตํ™•์ธ์„ ํ•ด์ฃผ์„ธ์š”.")
                return;
            }

            if (password == "") { {# ๋น„๋ฐ€๋ฒˆํ˜ธ ๊ฐ’์„ ์ž…๋ ฅํ•˜์ง€ ์•Š์œผ๋ฉด ๊ฒฝ๊ณ ๋ฌธ๊ตฌ ํ‘œ์‹œ#}
                $("#help-password").text("๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”.").removeClass("is-safe").addClass("is-danger")
                $("#input-password").focus()
                return;
            } else if (!is_password(password)) { {# ๊ฐ’์„ ์ž…๋ ฅํ–ˆ์ง€๋งŒ ํšŒ์›๊ฐ€์ž…์„ ์œ„ํ•œ ๋น„๋ฐ€๋ฒˆํ˜ธ ํ˜•์‹์— ๋งž์ง€ ์•Š์œผ๋ฉด ๊ฒฝ๊ณ  ๋ฌธ๊ตฌ ํ‘œ์‹œ#}
                $("#help-password").text("๋น„๋ฐ€๋ฒˆํ˜ธ์˜ ํ˜•์‹์„ ํ™•์ธํ•ด์ฃผ์„ธ์š”. ์˜๋ฌธ๊ณผ ์ˆซ์ž ํ•„์ˆ˜ ํฌํ•จ, ํŠน์ˆ˜๋ฌธ์ž(!@#$%^&*) ์‚ฌ์šฉ๊ฐ€๋Šฅ 8-20์ž").removeClass("is-safe").addClass("is-danger")
                $("#input-password").focus()
                return
            } else { {#๊ฐ’๋„ ์žˆ๊ณ  ํ˜•์‹๋„ ๋งž๋‹ค๋ฉด ํšŒ์›๊ฐ€์ž… ๋ฒ„ํŠผ์„ ๋ˆ„๋ฆ„๊ณผ ๋™์‹œ์— ์•„๋ž˜ ๋ฌธ๊ตฌ ์ถœ๋ ฅ#}
                $("#help-password").text("์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ๋น„๋ฐ€๋ฒˆํ˜ธ์ž…๋‹ˆ๋‹ค.").removeClass("is-danger").addClass("is-success")
            }
            if (password2 == "") {
                $("#help-password2").text("๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”.").removeClass("is-safe").addClass("is-danger")
                $("#input-password2").focus()
                return;
            } else if (password2 != password) { {# ์ฒซ ์นธ์— ์ ์€ ๋น„๋ฐ€๋ฒˆํ˜ธ์™€ ์ผ์น˜ํ•˜์ง€ ์•Š์„ ์‹œ์— ๊ฒฝ๊ณ ๋ฌธ๊ตฌ ์ถœ๋ ฅ#}
                $("#help-password2").text("๋น„๋ฐ€๋ฒˆํ˜ธ๊ฐ€ ์ผ์น˜ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.").removeClass("is-safe").addClass("is-danger")
                $("#input-password2").focus()
                return;
            } else {
                $("#help-password2").text("๋น„๋ฐ€๋ฒˆํ˜ธ๊ฐ€ ์ผ์น˜ํ•ฉ๋‹ˆ๋‹ค.").removeClass("is-danger").addClass("is-success")
            }

            if ($("#help-nickname").hasClass("is-danger")) {
                alert("๋‹‰๋„ค์ž„์„ ๋‹ค์‹œ ํ™•์ธํ•ด์ฃผ์„ธ์š”.")
                return;
            {# ์ค‘๋ณตํ™•์ธ์„ ํ•˜์ง€์•Š๊ณ  ํšŒ์›๊ฐ€์ž…์„ ์ง„ํ–‰ํ•  ์‹œ ๊ฒฝ๊ณ  ๋ฌธ๊ตฌ ํ‘œ์‹œ#}
            } else if (!$("#help-nickname").hasClass("is-success")) {
                alert("๋‹‰๋„ค์ž„ ์ค‘๋ณตํ™•์ธ์„ ํ•ด์ฃผ์„ธ์š”.")
                return;
            }

            if (email == "") {
                $("#help-email").text("์ด๋ฉ”์ผ์„ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”.").removeClass("is-safe").addClass("is-danger")
                $("#input-email").focus()
                return;
            } else if (!is_email(email)) {
                $("#help-email").text("์ด๋ฉ”์ผ ํ˜•์‹(scrum@email.com)์ด ๋งž๋Š”์ง€ ํ™•์ธํ•ด์ฃผ์„ธ์š”.").removeClass("is-safe").addClass("is-danger")
                $("#input-email").focus()
                return
            } else {
                $("#help-email").text("์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ์ด๋ฉ”์ผ์ž…๋‹ˆ๋‹ค.").removeClass("is-danger").addClass("is-success")
            }
            {# ์ž…๋ ฅ๋ฐ›์€ ๊ฐ’๋“ค์„ ์„œ๋ฒ„์˜ sign_up()์œผ๋กœ ๋ณด๋ƒ„ #}
            $.ajax({
                type: "POST",
                url: "/sign_up/save",
                data: {
                    username_give: username,
                    password_give: password,
                    nickname_give: nickname,
                    email_give: email,
                    profile_info_give: profile_info
                },
                {# ์ •์ƒ ์ž‘๋™๋˜์—ˆ์„ ์‹œ ์•„๋ž˜ ๋ฌธ๊ตฌ ์ถœ๋ ฅ #}
                success: function (response) {
                    alert("ํšŒ์›๊ฐ€์ž…์„ ์ถ•ํ•˜๋“œ๋ฆฝ๋‹ˆ๋‹ค!")
                    window.location.replace("/login")
                }
            });

        }
        {# ๋กœ๊ทธ์ธ ์ฐฝ๊ณผ ํšŒ์›๊ฐ€์ž… ์ฐฝ์€ "ํšŒ์›๊ฐ€์ž…ํ•˜๋Ÿฌ๊ฐ€๊ธฐ"์™€ "์ทจ์†Œ" ๋ฒ„ํŠผ ํด๋ฆญ์œผ๋กœ ์ „ํ™˜๋˜๋Š”๋ฐ, toggleClass๊ธฐ๋Šฅ์„ ํ†ตํ•ด
            ๋กœ๊ทธ์ธ ํ™”๋ฉด์—์„œ ๋ณด์—ฌ์•ผํ•˜๋Š” ์ž…๋ ฅ์นธ๊ณผ ํšŒ์›๊ฐ€์ž…์—์„œ ๋ณด์—ฌ์•ผํ•˜๋Š” ์ž…๋ ฅ์นธ์„ ๋•Œ์— ๋”ฐ๋ผ ๋…ธ์ถœ/์ˆจ๊น€#}
        function toggle_sign_up() {
            $("#sign-up-box").toggleClass("is-hidden")
            $("#div-sign-in-or-up").toggleClass("is-hidden")
            $("#btn-check-dup").toggleClass("is-hidden")
            $("#btn-check-dup_nick").toggleClass("is-hidden")
            $("#help-id").toggleClass("is-hidden")
            $("#help-password").toggleClass("is-hidden")
            $("#help-password2").toggleClass("is-hidden")
            $("#help-email").toggleClass("is-hidden")
            $("#help-profile_info").toggleClass("is-hidden")
            $("#help-nickname").toggleClass("is-hidden")
            $("#help-id-login").toggleClass("is-hidden")
            $("#help-password-login").toggleClass("is-hidden")
        }

            {# ์•„์ด๋”” ํ˜•์‹์„ ๊ฒ€์‚ฌํ•˜๋Š” ์ •๊ทœ์‹ #}
            function is_username(asValue) {
                var regExp = /^(?=.*[a-zA-Z])[-a-zA-Z0-9_.]{2,10}$/;
                return regExp.test(asValue);
            }
            {# ํŒจ์Šค์›Œ๋“œ ํ˜•์‹์„ ๊ฒ€์‚ฌํ•˜๋Š” ์ •๊ทœ์‹ #}
            function is_password(asValue) {
                var regExp = /^(?=.*\d)(?=.*[a-zA-Z])[0-9a-zA-Z!@#$%^&*]{8,20}$/;
                return regExp.test(asValue);
            }
            {# ์ด๋ฉ”์ผ ํ˜•์‹์„ ๊ฒ€์‚ฌํ•˜๋Š” ์ •๊ทœ์‹ #}
            function is_email(asValue) {
                var regExp = /^[a-zA-Z0-9+-_.]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$/;
                return regExp.test(asValue);
            }
            {# ๋‹‰๋„ค์ž„ ํ˜•์‹์„ ๊ฒ€์‚ฌํ•˜๋Š” ์ •๊ทœ์‹ #}
            function is_nickname(asValue) {
                var regExp = /^[๊ฐ€-ํžฃ]{2,6}$/;
                return regExp.test(asValue);
            }

            {# ์•„์ด๋”” ํ˜•์‹ ๊ฒ€์‚ฌ, ์•„์ด๋”” ์ค‘๋ณตํ™•์ธ ๊ธฐ๋Šฅ์„ ๋‹ด๋‹นํ•˜๋Š” ํ•จ์ˆ˜ #}
            function check_dup() {
                let username = $("#input-username").val()
                console.log(username)
                if (username == "") {
                    $("#help-id").text("์•„์ด๋””๋ฅผ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”.").removeClass("is-safe").addClass("is-danger")
                    $("#input-username").focus()
                    return;
                }
                if (!is_username(username)) {
                    $("#help-id").text("์•„์ด๋””์˜ ํ˜•์‹์„ ํ™•์ธํ•ด์ฃผ์„ธ์š”. ์˜๋ฌธ๊ณผ ์ˆซ์ž, ์ผ๋ถ€ ํŠน์ˆ˜๋ฌธ์ž(._-) ์‚ฌ์šฉ ๊ฐ€๋Šฅ. 2-10์ž ๊ธธ์ด").removeClass("is-safe").addClass("is-danger")
                    $("#input-username").focus()
                    return;
                }
                $("#help-id").addClass("is-loading")
                $.ajax({
                    type: "POST",
                    url: "/sign_up/check_dup",
                    data: {
                        username_give: username
                    },
                    success: function (response) {
                        {# ์„œ๋ฒ„๋กœ๋ถ€ํ„ฐ์˜ ์‘๋‹ต์ด exists ๋ผ๋ฉด ์ด๋ฏธ ์กด์žฌํ•˜๋Š” ์•„์ด๋””๋ผ๋Š” ๊ฒฝ๊ณ ๋ฌธ๊ตฌ ๋…ธ์ถœ๊ณผ ํ•จ๊ป˜ ์ž…๋ ฅ์นธ ์ด๋™ #}
                        if (respose["exists"]) {
                            $("#help-id").text("์ด๋ฏธ ์กด์žฌํ•˜๋Š” ์•„์ด๋””์ž…๋‹ˆ๋‹ค.").removeClass("is-safe").addClass("is-danger")
                            $("#input-username").focus()
                        } else { {# ์„œ๋ฒ„๋กœ๋ถ€ํ„ฐ์˜ ์‘๋‹ต์ด exists๊ฐ€ ์•„๋‹ˆ๋ฉด ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•˜๋‹ค๋Š” ๋ฌธ๊ตฌ ๋…ธ์ถœ #}
                            $("#help-id").text("์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ์•„์ด๋””์ž…๋‹ˆ๋‹ค.").removeClass("is-danger").addClass("is-success")
                        }
                        $("#help-id").removeClass("is-loading")

                    }
                });
            }
            {# ๋‹‰๋„ค์ž„ ํ˜•์‹ ๊ฒ€์‚ฌ, ์•„์ด๋”” ์ค‘๋ณตํ™•์ธ ๊ธฐ๋Šฅ์„ ๋‹ด๋‹นํ•˜๋Š” ํ•จ์ˆ˜ #}
            function check_dup_nick() {
                let nickname = $("#input-nickname").val()
                console.log(nickname)
                if (nickname == "") {
                    $("#help-nickname").text("๋‹‰๋„ค์ž„์„ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”.").removeClass("is-safe").addClass("is-danger")
                    $("#input-nickname").focus()
                    return;
                }
                if (!is_nickname(nickname)) {
                    $("#help-nickname").text("๋‹‰๋„ค์ž„์˜ ํ˜•์‹์„ ํ™•์ธํ•ด์ฃผ์„ธ์š”. ํ•œ๊ธ€๋งŒ ์‚ฌ์šฉ ๊ฐ€๋Šฅ. 2-6์ž ๊ธธ์ด").removeClass("is-safe").addClass("is-danger")
                    $("#input-nickname").focus()
                    return;
                }
                $("#help-nickname").addClass("is-loading")
                $.ajax({
                    type: "POST",
                    url: "/sign_up/check_dup_nick",
                    data: {
                        nickname_give: nickname
                    },
                    success: function (response) {

                        if (response["exists"]) {
                            $("#help-nickname").text("์ด๋ฏธ ์กด์žฌํ•˜๋Š” ๋‹‰๋„ค์ž„์ž…๋‹ˆ๋‹ค.").removeClass("is-safe").addClass("is-danger")
                            $("#input-nickname").focus()
                        } else {
                            $("#help-nickname").text("์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ๋‹‰๋„ค์ž„์ž…๋‹ˆ๋‹ค.").removeClass("is-danger").addClass("is-success")
                        }
                        $("#help-nickname").removeClass("is-loading")

                    }
                });
            }
    </script>์ฝ”๋“œ๋ฅผ ์ž…๋ ฅํ•˜์„ธ์š”

์•„์‰ฌ์› ๋˜ ์ 

ํ”„๋กœ์ ํŠธ ์ดˆ๊ธฐ ์„ค๊ณ„์˜ ์•„์‰ฌ์›€ (Git ์ถฉ๋Œ)

๊ฐ์ž ํŽ˜์ด์ง€ ๋ณ„๋กœ ๊ธฐ๋Šฅ์„ ๋งก์•˜๋˜ ๊ฒƒ์€ ๊ณผ์ •์ ์œผ๋กœ ํ’€์Šคํƒ์œผ๋กœ ์ฝ”๋“œ๋ฅผ ์งœ๋ฉด์„œ ์„œ๋ฒ„์™€ ํด๋ผ์ด์–ธํŠธ์˜ ๊ด€๊ณ„๋ฅผ ์ดํ•ดํ•˜๋Š”๋ฐ์— ๋„์›€์ด ๋˜์—ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ์•„์‰ฌ์› ๋˜ ์ ์€ ๊ฐ์ž์˜ ๊ธฐ๋Šฅ์„ ํ•ฉ์น˜๋Š” ๊ณผ์ •์—์„œ Git์„ ์‚ฌ์šฉํ–ˆ๋Š”๋ฐ, ๊ฒน์น˜๋Š” ๋ถ€๋ถ„์ด ๋งŽ๋‹ค๋ณด๋‹ˆ ์ฝ”๋“œ๋ฅผ commitํ•˜๊ณ  pushํ•˜๋Š” ๊ณผ์ •, pull์„ ๋ฐ›์•„์˜ค๋Š” ๊ณผ์ • ๋ชจ๋‘์—์„œ ์ฝ”๋“œ ๊ฐ„์˜ ์ถฉ๋Œ์ด ์žฆ์•˜๊ณ , ํŒ€ ์ „์ฒด์ ์œผ๋กœ workflow๋ฅผ ํ•ด์น˜๊ฒŒ ๋˜์—ˆ๋‹ค. ํŒ€์›๋“ค ๋ชจ๋‘ ์ด ์ ์— ์•„์‰ฌ์›€์„ ๋А๊ผˆ๊ณ , ๋‹ค์Œ ํ”„๋กœ์ ํŠธ์—์„œ๋Š” ๊ธฐ์ดˆ ์„ค๊ณ„ ๋‹จ๊ณ„์—์„œ๋ถ€ํ„ฐ ๊ผผ๊ผผํ•˜๊ฒŒ ์‹œ์ž‘ํ•˜์ž๋Š” ๋‹ค์ง์„ ํ•˜๊ฒŒ ๋˜์—ˆ๋‹ค.

์ฒซ WIL์„ ๋งˆ๋ฌด๋ฆฌ ํ•˜๋ฉด์„œ...

์ฒซ ๋‚ ๋ถ€ํ„ฐ TIL์„ ๊พธ์ค€ํžˆ ์ž‘์„ฑํ•ด๋ณด๋ฆฌ๋ž€ ๊ฐ์˜ค๋ฅผ ์ง€ํ‚ค์ง€ ๋ชปํ•œ ๊ฒƒ์ด ๋ถ€๋„๋Ÿฝ๋‹ค.. ์˜ค๋Š˜ WIL๋„ ์ž˜ ์ž‘์„ฑํ–ˆ๋Š”์ง€๋Š” ๋ชจ๋ฅด๊ฒ ์ง€๋งŒ.. ์ด๋ฏธ 1์ฃผ์ฐจ๋Š” ์ง€๋‚˜๊ฐ”๋‹ค! ๋‹ค์‹œ๊ธˆ ๊พธ์ค€ํ•จ์„ ์ด์–ด๋‚˜๊ฐ€๊ฒ ๋‹ค๋Š” ๋‹ค์ง์„ ํ•˜๋ฉฐ ์ฒซ WIL์„ ๋งˆ๋ฌด๋ฆฌํ•œ๋‹ค. ๋‚ด์ผ ๋ถ€ํ„ฐ๋Š” ๊ผญ! 1์ผ 1TIL! (์ดˆ๋”ฉ์ผ๊ธฐ ๊ฐ™๋”๋ผ๋„ ๊ดœ์ฐฎ์•„๐Ÿ˜…)

profile
๊ทธ๋Š” ํŠนํžˆ ์š”๋ฆฌ ์‹ค๋ ฅ์„ ๊ฐ–์ถ˜ ์ƒํƒœ์—์„œ ๋‹ค๋ฅธ ์‚ฌ๋žŒ์˜ ๋„์›€์„ ๋ฐ›์•„

0๊ฐœ์˜ ๋Œ“๊ธ€