๐Ÿค” โ€œJWT ํƒˆ์ทจ๋˜๋ฉด ์–ด๋–ป๊ฒŒ ํ•˜์„ธ์š”?โ€

CodeByHanยท2025๋…„ 11์›” 16์ผ

์Šคํ”„๋ง

๋ชฉ๋ก ๋ณด๊ธฐ
31/33

์š”์ฆ˜ ๋ฉด์ ‘์—์„œ โ€œJWT ํƒˆ์ทจ ์‹œ ๋Œ€์ฒ˜ ๋ฐฉ์•ˆ?โ€ ๊ฐ™์€ ์งˆ๋ฌธ์„ ์ •๋ง ๋งŽ์ด ๋ฐ›๋Š”๋‹ค. ๋ณด์•ˆ ์ด์Šˆ๊ฐ€ ์ค‘์š”ํ•ด์ง€๋‹ค ๋ณด๋‹ˆ, ์ธ์ฆ/์ธ๊ฐ€ ๊ด€๋ จ ์งˆ๋ฌธ ๋น„์ค‘๋„ ์ ์  ์ปค์ง€๋Š” ๋А๋‚Œ์ด๋‹ค. ๋‚˜๋„ ์ฒ˜์Œ์—๋Š” ์ด ์งˆ๋ฌธ์ด ๋„ˆ๋ฌด ๋ง‰๋ง‰ํ•ด์„œ ํž˜๋“ค์—ˆ๋˜ ๊ฒฝํ—˜์ด ์žˆ๋‹ค. ์ •๋‹ต์ด ๋”ฑ ์ •ํ•ด์ ธ ์žˆ๋Š” ์ฃผ์ œ๋„ ์•„๋‹ˆ๊ณ , ์ƒํ™ฉ๋งˆ๋‹ค ๋Œ€์‘์ด ๋‹ฌ๋ผ์ง€๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.
๊ทธ๋ž˜์„œ ์ด๋ฒˆ ๊ธฐํšŒ์— ๋‚ด๊ฐ€ ์ƒ๊ฐํ•˜๊ณ  ์ •๋ฆฌํ•œ ๋‚ด์šฉ์„ ํ•œ๋ฒˆ ๊ธฐ๋กํ•ด๋ณด๋ ค๊ณ  ํ•œ๋‹ค.

๐Ÿ“Œ JWT

์šฐ๋ฆฌ๋Š” ํ”„๋กœ์ ํŠธ๋ฅผ ํ•  ๋•Œ JWT(JSON Web Token)์„ ์ •๋ง ๋งŽ์ด ์‚ฌ์šฉํ•œ๋‹ค. ์ฟ ํ‚ค ๊ธฐ๋ฐ˜ ์ธ์ฆ์€ ์†๋„๋Š” ๋น ๋ฅด์ง€๋งŒ ๋ณด์•ˆ์— ์ทจ์•ฝํ•˜๊ณ , ์„ธ์…˜ ๊ธฐ๋ฐ˜ ์ธ์ฆ์€ ์„œ๋ฒ„์— ์„ธ์…˜์„ ์ €์žฅํ•ด์•ผ ํ•ด์„œ ์ž์›์„ ๋งŽ์ด ์‚ฌ์šฉํ•œ๋‹ค. ๊ทธ๋Ÿฐ ์ด์œ ๋“ค ๋•Œ๋ฌธ์— ์š”์ฆ˜์—” JWT๋ฅผ ์„ ํ˜ธํ•˜๋Š” ๋ถ„์œ„๊ธฐ๋‹ค. ์†”์งํžˆ ๋งํ•˜๋ฉด, ๋งŽ์€ ๊ฐœ๋ฐœ์ž๋“ค์ด โ€œ์š”์ฆ˜ ํŠธ๋ Œ๋“œ๊ฐ€ JWT๋‹ˆ๊นŒ!โ€ ํ•˜๋ฉด์„œ ์ผ๋‹จ ์“ฐ๊ณ  ๋ณธ๋‹ค. ๋‚˜ ์—ญ์‹œ๋„ ์ฒ˜์Œ์—๋Š” ์ œ๋Œ€๋กœ ์ดํ•ดํ•˜์ง€ ์•Š์€ ์ฑ„, ๊ทธ๋ƒฅ ๋‹ค๋“ค ์“ฐ๋‹ˆ๊นŒ ๋”ฐ๋ผ์„œ ์‚ฌ์šฉํ–ˆ๋˜ ๊ฒŒ ์‚ฌ์‹ค์ด๋‹ค. ๊ทธ๋Ÿฐ๋ฐ ๋ฌธ๋“ ์ด๋Ÿฐ ์ƒ๊ฐ์ด ๋“ค์—ˆ๋‹ค. ๋„๋Œ€์ฒด JWT๊ฐ€ ๋ญ๊ธธ๋ž˜ ์ด๋ ‡๊ฒŒ ๋ชจ๋‘๊ฐ€ ์‚ฌ์šฉํ•˜๋Š” ๊ฑธ๊นŒ? ๊ทธ๊ฒŒ ๋ณด์•ˆ์ ์œผ๋กœ ์™„๋ฒฝํ•œ ์กด์žฌ์ธ๊ฐ€? ์•„๋‹ˆ๋ฉด ๋‹จ์ˆœํžˆ ์„œ๋ฒ„ ์ž์›์„ ์•„๋ผ๊ธฐ ์œ„ํ•œ ์ˆ˜๋‹จ์ผ ๋ฟ์ธ๊ฐ€? ๊ทธ๋ž˜์„œ ์ด๋ฒˆ์—๋Š” ๊ทธ๋™์•ˆ ๊ฐ€๋ณ๊ฒŒ๋งŒ ์•Œ๊ณ  ๋„˜์–ด๊ฐ”๋˜ JWT์— ๋Œ€ํ•ด ์ง์ ‘ ์ •๋ฆฌํ•ด๋ณด๊ณ  ์ œ๋Œ€๋กœ ์ดํ•ดํ•ด๋ณด๋ ค๊ณ  ํ•œ๋‹ค.

์ผ๋‹จ HTTP์˜ ํŠน์„ฑ๋ถ€ํ„ฐ ์งš๊ณ  ๋„˜์–ด๊ฐ€์ž. ์šฐ๋ฆฌ๊ฐ€ ํ”ํžˆ ์•Œ๊ณ  ์žˆ๋“ฏ์ด HTTP๋Š” Statelessํ•˜๊ณ  Connectionlessํ•œ ํŠน์ง•์„ ๊ฐ€์ง€๊ณ  ์žˆ๋‹ค.

ํ•œ๋งˆ๋””๋กœ ๋งํ•˜๋ฉด, ์„œ๋ฒ„๋Š” ์š”์ฒญ(Request)์ด ๋“ค์–ด์˜ฌ ๋•Œ๋งˆ๋‹ค ๊ทธ๋•Œ๊ทธ๋•Œ ์‘๋‹ต(Response)์„ ๋ณด๋‚ด์ค„ ๋ฟ, ํด๋ผ์ด์–ธํŠธ๊ฐ€ ๋ˆ„๊ตฌ์˜€๊ณ  ์–ด๋–ค ์ƒํƒœ์˜€๋Š”์ง€๋ฅผ ๊ธฐ์–ตํ•˜์ง€ ์•Š๋Š”๋‹ค๋Š” ๊ฒƒ์ด๋‹ค. ๋˜ํ•œ ์š”์ฒญ์ด ๋๋‚˜๋ฉด ์—ฐ๊ฒฐ์„ ์œ ์ง€ํ•˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— ์ง€์†์ ์œผ๋กœ ์—ฐ๊ฒฐ๋˜์–ด ์žˆ๋Š” ์ƒํƒœ๋„ ์•„๋‹ˆ๋‹ค.

๋”ฐ๋ผ์„œ ์ด๊ฑธ ํ•ด๊ฒฐํ•ด์ฃผ๋Š” ๊ฒƒ์ด ๋ฌด์—‡์ด๋ƒ? ๋ฐ”๋กœ ์ฟ ํ‚ค(Cookie)์™€ ์„ธ์…˜(Session)์ด๋‹ค.

์ฟ ํ‚ค(Cookie)

์ฟ ํ‚ค(Cookie)๋ž€, ์›น์‚ฌ์ดํŠธ๊ฐ€ ์‚ฌ์šฉ์ž์˜ ๋ธŒ๋ผ์šฐ์ €์— ์ €์žฅํ•˜๋Š” ์ž‘์€ ๋ฐ์ดํ„ฐ ํŒŒ์ผ์„ ๋งํ•œ๋‹ค.

Set-Cookie: userName=kevin; password=abc123

๋กœ๊ทธ์ธ ์š”์ฒญ์— ๋Œ€ํ•ด ์„œ๋ฒ„๋Š” ์‘๋‹ต ํ—ค๋”์— Set-Cookie๋ฅผ ๋‹ด์•„ ์‚ฌ์šฉ์ž ์ •๋ณด๋ฅผ ํด๋ผ์ด์–ธํŠธ์— ์ „๋‹ฌํ•œ๋‹ค.

๊ทธ ์ดํ›„ ์‚ฌ์šฉ์ž๋Š” ๋ชจ๋“  ์š”์ฒญ๋งˆ๋‹ค ์ฟ ํ‚ค๋ฅผ ํ•จ๊ป˜ ์ „์†กํ•˜๊ณ , ์„œ๋ฒ„๋Š” ํ•ด๋‹น ์ฟ ํ‚ค ๊ฐ’์œผ๋กœ ์‚ฌ์šฉ์ž๋ฅผ ์‹๋ณ„ํ•  ์ˆ˜ ์žˆ๋‹ค.

ํ•˜์ง€๋งŒ ์ฟ ํ‚ค ๋ฐฉ์‹์—๋Š” ๋ถ„๋ช…ํ•œ ๋‹จ์ ์ด ์žˆ๋‹ค.

๋ณด์•ˆ์— ์ทจ์•ฝ

  • ์ฟ ํ‚ค ๊ฐ’์ด ํƒˆ์ทจ๋˜๊ฑฐ๋‚˜ ์กฐ์ž‘๋  ์œ„ํ—˜์ด ์žˆ๋‹ค. (ํŠนํžˆ ๋น„๋ฐ€๋ฒˆํ˜ธ ๊ฐ™์€ ๋ฏผ๊ฐ ์ •๋ณด๋Š” ์ ˆ๋Œ€ ๋‹ด์•„์„œ๋Š” ์•ˆ ๋œ๋‹ค)

  • ์ €์žฅ ์šฉ๋Ÿ‰ ์ œํ•œ ์กด์žฌ

  • ๋ธŒ๋ผ์šฐ์ € ๊ฐ„ ๊ณต์œ  ๋ถˆ๊ฐ€

  • ์ฟ ํ‚ค ํฌ๊ธฐ๊ฐ€ ์ปค์ง€๋ฉด ๋„คํŠธ์›Œํฌ ๋ถ€ํ•˜ ์ฆ๊ฐ€

์ฆ‰, ํด๋ผ์ด์–ธํŠธ์— ์ค‘์š”ํ•œ ์ •๋ณด๋ฅผ ์ €์žฅํ•œ๋‹ค๋Š” ๊ฒƒ ์ž์ฒด๊ฐ€ ์œ„ํ—˜ํ•˜๋‹ค๋Š” ๊ทผ๋ณธ์  ํ•œ๊ณ„๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ๋‹ค.

์„ธ์…˜(Session)

์ฟ ํ‚ค๋งŒ์œผ๋กœ ํด๋ผ์ด์–ธํŠธ์˜ ๋กœ๊ทธ์ธ ์ƒํƒœ๋ฅผ ์œ ์ง€ํ•  ์ˆ˜ ์žˆ์—ˆ์ง€๋งŒ, ๊ฐ€์žฅ ํฐ ๋‹จ์ ์€ ์ฟ ํ‚ค๊ฐ€ ํƒˆ์ทจ๋˜๊ฑฐ๋‚˜ ์กฐ์ž‘๋  ์œ„ํ—˜์ด ์žˆ๋‹ค๋Š” ์ ์ด๋‹ค. ํŠนํžˆ, ๋ฏผ๊ฐํ•œ ๊ฐœ์ธ์ •๋ณด๋ฅผ HTTP๋กœ ์ฃผ๊ณ ๋ฐ›๋Š” ๊ฒƒ์€ ๋งค์šฐ ์œ„ํ—˜ํ•˜๋‹ค.

์ด๋ฅผ ๋ณด์™„ํ•˜๊ธฐ ์œ„ํ•ด ์„ธ์…˜(Session) ๋ฐฉ์‹์ด ๋“ฑ์žฅํ–ˆ๋‹ค. ์„ธ์…˜์€ ๋น„๋ฐ€๋ฒˆํ˜ธ ๋“ฑ ์ค‘์š”ํ•œ ์ธ์ฆ ์ •๋ณด๋ฅผ ์„œ๋ฒ„ ์ธก์— ์ €์žฅํ•˜๊ณ , ํด๋ผ์ด์–ธํŠธ์—๋Š” ์„ธ์…˜ ID๋งŒ ์ฟ ํ‚ค๋กœ ์ „๋‹ฌํ•œ๋‹ค.

HTTP/1.1 200
Set-Cookie: JSESSIONID=FDB5E30BF20045E8A9AAFC788383680C;

์„œ๋ฒ„๋Š” ๋กœ๊ทธ์ธ ์š”์ฒญ ์‹œ ์ธ์ฆ ์ •๋ณด๋ฅผ ์„œ๋ฒ„์— ์ €์žฅํ•˜๊ณ , ํด๋ผ์ด์–ธํŠธ์—๋Š” ์‹๋ณ„์šฉ ์„ธ์…˜ ID(JSESSIONID)๋ฅผ ์ฟ ํ‚ค์— ๋‹ด์•„ ์ „๋‹ฌํ•œ๋‹ค.

์ดํ›„ ํด๋ผ์ด์–ธํŠธ๋Š” ์š”์ฒญํ•  ๋•Œ๋งˆ๋‹ค JSESSIONID ์ฟ ํ‚ค๋ฅผ ํ•จ๊ป˜ ์ „์†กํ•˜๊ณ , ์„œ๋ฒ„๋Š” ์ด๋ฅผ ํ™•์ธํ•ด ํด๋ผ์ด์–ธํŠธ๋ฅผ ์‹๋ณ„ํ•œ๋‹ค.

์žฅ์ 

  • ์„ธ์…˜ ID๋งŒ ์ „๋‹ฌ๋˜๋ฏ€๋กœ, ์ฟ ํ‚ค๊ฐ€ ๋…ธ์ถœ๋˜์–ด๋„ ์ค‘์š”ํ•œ ๊ฐœ์ธ์ •๋ณด๋Š” ์•ˆ์ „ํ•˜๋‹ค.

  • ๊ฐ ์‚ฌ์šฉ์ž๋งˆ๋‹ค ๊ณ ์œ  ์„ธ์…˜ ID๊ฐ€ ๋ฐœ๊ธ‰๋˜๋ฏ€๋กœ, ์š”์ฒญ ์‹œ ํšŒ์›์ •๋ณด๋ฅผ ์‰ฝ๊ฒŒ ํ™•์ธ ๊ฐ€๋Šฅํ•˜๋‹ค.

๋‹จ์ 

  • ์„ธ์…˜ ID๊ฐ€ ํƒˆ์ทจ๋˜๋ฉด, ๊ณต๊ฒฉ์ž๊ฐ€ ํด๋ผ์ด์–ธํŠธ์ธ ์ฒ™ ์œ„์žฅํ•  ์ˆ˜ ์žˆ๋‹ค.

  • ์„œ๋ฒ„์— ์„ธ์…˜ ์ •๋ณด๋ฅผ ์ €์žฅํ•ด์•ผ ํ•˜๋ฏ€๋กœ, ์‚ฌ์šฉ์ž ์š”์ฒญ์ด ๋งŽ์•„์ง€๋ฉด ์„œ๋ฒ„ ๋ถ€ํ•˜์™€ ํ™•์žฅ์„ฑ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.

JWT ๊ธฐ๋ฐ˜ ์ธ์ฆ

์•ž์„œ ์‚ดํŽด๋ณธ ์ฟ ํ‚ค/์„ธ์…˜ ๊ธฐ๋ฐ˜ ์ธ์ฆ์€ ๊ฐ๊ฐ ์žฅ์ ์ด ์žˆ์ง€๋งŒ, ๋™์‹œ์— ํ•œ๊ณ„๋„ ์กด์žฌํ•œ๋‹ค.

  • ์ฟ ํ‚ค: ํด๋ผ์ด์–ธํŠธ์— ๋ฏผ๊ฐ ์ •๋ณด๋ฅผ ๋‹ด์„ ๊ฒฝ์šฐ ๋ณด์•ˆ์— ์ทจ์•ฝ

  • ์„ธ์…˜: ์„œ๋ฒ„์— ์ธ์ฆ ์ •๋ณด๋ฅผ ์ €์žฅํ•ด์•ผ ํ•˜๋ฏ€๋กœ ์„œ๋ฒ„ ๋ถ€ํ•˜ ๋ฐ ํ™•์žฅ์„ฑ ๋ฌธ์ œ ๋ฐœ์ƒ

์ด๋Ÿฌํ•œ ๋ฌธ์ œ๋ฅผ ๊ฐœ์„ ํ•˜๊ณ ์ž ๋“ฑ์žฅํ•œ ๊ฒƒ์ด JWT(Json Web Token, JWT) ๊ธฐ๋ฐ˜ ์ธ์ฆ์ด๋‹ค.

JWT(JSON Web Token)๋ž€ ์ธ์ฆ์— ํ•„์š”ํ•œ ์ •๋ณด๋ฅผ ์•”ํ˜ธํ™”์‹œ์ผœ ํ† ํฐ ํ˜•ํƒœ๋กœ ์ „๋‹ฌํ•˜๋Š” ๋ฐฉ์‹์ด๋‹ค. JWT ๊ธฐ๋ฐ˜ ์ธ์ฆ์€ ์ฟ ํ‚ค/์„ธ์…˜ ๋ฐฉ์‹๊ณผ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ JWT ํ† ํฐ(Access Token)์„ HTTP ํ—ค๋”์— ์‹ค์–ด ์„œ๋ฒ„๊ฐ€ ํด๋ผ์ด์–ธํŠธ๋ฅผ ์‹๋ณ„ํ•œ๋‹ค.

ํŠนํžˆ JWT๋Š” ์„œ๋ฒ„์— ์ƒํƒœ ์ •๋ณด๋ฅผ ์ €์žฅํ•˜์ง€ ์•Š์•„๋„ ๋˜๋Š” Stateless ๊ตฌ์กฐ์ด๋ฏ€๋กœ, ๋ถ„์‚ฐ ํ™˜๊ฒฝ(์˜ˆ: ์—ฌ๋Ÿฌ ๋Œ€์˜ ์„œ๋ฒ„, ํด๋ผ์šฐ๋“œ ํ™˜๊ฒฝ)์—์„œ ๋›ฐ์–ด๋‚œ ํ™•์žฅ์„ฑ์„ ์ œ๊ณตํ•œ๋‹ค.

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

์ฆ‰, JWT๋Š” Stateless + ๋ถ„์‚ฐ ํ™˜๊ฒฝ ์ ํ•ฉ์„ฑ ๋•Œ๋ฌธ์—, ํ˜„๋Œ€ ์›น ์„œ๋น„์Šค์—์„œ ํ™•์žฅ์„ฑ๊ณผ ์„ฑ๋Šฅ์„ ๋™์‹œ์— ์žก์„ ์ˆ˜ ์žˆ๋Š” ์ธ์ฆ ๋ฐฉ์‹์œผ๋กœ ๊ฐ๊ด‘๋ฐ›๋Š”๋‹ค.

JWT ๊ตฌ์กฐ

JWT๋Š” . (dot) ๊ตฌ๋ถ„์ž๋กœ ์ด๋ฃจ์–ด์ง„ ์„ธ ๊ฐ€์ง€ ๋ฌธ์ž์—ด๋กœ ๊ตฌ์„ฑ๋˜๋ฉฐ, ์‹ค์ œ ๊ตฌ์กฐ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

1. Header

  • JWT์˜ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ๋ฅผ ๋‹ด๋Š” ๋ถ€๋ถ„
  • ์ฃผ๋กœ ํ† ํฐ ํƒ€์ž…๊ณผ ์„œ๋ช… ์•Œ๊ณ ๋ฆฌ์ฆ˜ ์ •๋ณด๋ฅผ ํฌํ•จํ•œ๋‹ค
  • alg: ํ† ํฐ์˜ ์„œ๋ช…(Signature)์„ ์ƒ์„ฑํ•  ๋•Œ ์‚ฌ์šฉํ•˜๋Š” ํ•ด์‹ฑ ์•Œ๊ณ ๋ฆฌ์ฆ˜ (์˜ˆ: HS256, RS256 ๋“ฑ)
  • typ: ํ† ํฐ์˜ ํƒ€์ž…. ํ•ญ์ƒ "JWT"๋กœ ์„ค์ •

Header๋Š” ๋‹จ์ˆœํžˆ ์ •๋ณด๋ฅผ ๋‹ด๋Š” ๋ถ€๋ถ„์ด๋ฉฐ, ๋ณด์•ˆ๊ณผ ๊ด€๋ จ๋œ ๋‚ด์šฉ์€ Signature์—์„œ ์ฒ˜๋ฆฌ๋œ๋‹ค.

2. Payload

  • JWT์˜ ์‹ค์ œ ๋ฐ์ดํ„ฐ ์˜์—ญ

  • ํด๋ผ์ด์–ธํŠธ์™€ ์„œ๋ฒ„๊ฐ€ ์ธ์ฆ์„ ์œ„ํ•ด ํ•„์š”๋กœ ํ•˜๋Š” ์ •๋ณด๋ฅผ ๋‹ด๋Š”๋‹ค

  • ๊ฐ key-value ์Œ์„ Claim์ด๋ผ๊ณ  ๋ถ€๋ฅธ๋‹ค.

๐Ÿšจ ์ฃผ์˜: Payload๋Š” ์ธ์ฝ”๋”ฉ๋งŒ ๋˜์–ด ์žˆ์„ ๋ฟ ์•”ํ˜ธํ™”๋˜์ง€ ์•Š์Œ โ‡’ ๋ˆ„๊ตฌ๋‚˜ Base64๋กœ ๋””์ฝ”๋”ฉ ๊ฐ€๋Šฅํ•˜๋ฏ€๋กœ ๋ฏผ๊ฐํ•œ ์ •๋ณด๋Š” ์ ˆ๋Œ€ ๋„ฃ์œผ๋ฉด ์•ˆ ๋œ๋‹ค.

3. Signature

  • Header์™€ Payload๋ฅผ ๋น„๋ฐ€ํ‚ค(secret key)๋กœ ์กฐํ•ฉํ•˜์—ฌ ํ•ด์‹ฑ(Signature)ํ•œ ๊ฐ’
  • ์„œ๋ฒ„๋Š” ์ด๋ฅผ ํ†ตํ•ด ํ† ํฐ์ด ๋ณ€์กฐ๋˜์ง€ ์•Š์•˜๋Š”์ง€ ํ™•์ธ
  • Signature ๊ฒ€์ฆ์„ ํ†ตํ•ด ํ† ํฐ ์œ„๋ณ€์กฐ ์—ฌ๋ถ€ ํ™•์ธ ๊ฐ€๋Šฅ
  • Header์™€ Payload๋Š” ๋‹จ์ˆœ ์ธ์ฝ”๋”ฉ๋งŒ ๋˜์–ด ์žˆ์–ด ๋ˆ„๊ตฌ๋‚˜ ์ฝ์„ ์ˆ˜ ์žˆ์ง€๋งŒ,
  • Signature๋Š” ์„œ๋ฒ„๊ฐ€ ๊ด€๋ฆฌํ•˜๋Š” ๋น„๋ฐ€ํ‚ค๊ฐ€ ์—†์œผ๋ฉด ์œ„์กฐํ•  ์ˆ˜ ์—†๋‹ค.

์ฆ‰, JWT์˜ ๋ณด์•ˆ ํ•ต์‹ฌ์€ Signature์— ์žˆ์œผ๋ฉฐ, ์„œ๋ฒ„๋Š” ํ† ํฐ ์ž์ฒด๋ฅผ ์ €์žฅํ•˜์ง€ ์•Š๊ณ ๋„ ์œ ํšจ์„ฑ์„ ๊ฒ€์ฆํ•  ์ˆ˜ ์žˆ๋‹ค.

์–ด๋–ป๊ฒŒ ์ธ์ฆํ• ๊นŒ?

1. ๋กœ๊ทธ์ธ ์š”์ฒญ

  • ํด๋ผ์ด์–ธํŠธ๊ฐ€ ์‚ฌ์šฉ์ž ์ด๋ฆ„/๋น„๋ฐ€๋ฒˆํ˜ธ ๋“ฑ์œผ๋กœ ๋กœ๊ทธ์ธ ์š”์ฒญ์„ ๋ณด๋‚ธ๋‹ค.

2. ์„œ๋ฒ„ ๊ฒ€์ฆ ๋ฐ ํ† ํฐ ๋ฐœ๊ธ‰

  • ์„œ๋ฒ„๋Š” ์‚ฌ์šฉ์ž๋ฅผ ๊ฒ€์ฆํ•œ ํ›„, ๊ณ ์œ  ID, ๊ถŒํ•œ, ๋งŒ๋ฃŒ ์‹œ๊ฐ„ ๋“ฑ ํ•„์š”ํ•œ ์ •๋ณด๋ฅผ Payload์— ๋‹ด๋Š”๋‹ค.

  • ๋น„๋ฐ€ํ‚ค(secret key)๋ฅผ ์‚ฌ์šฉํ•ด Header์™€ Payload๋ฅผ ์กฐํ•ฉํ•˜๊ณ  ์„œ๋ช…(Signature)์„ ๋งŒ๋“ค์–ด JWT ํ† ํฐ(Access Token)์„ ๋ฐœ๊ธ‰ํ•œ๋‹ค.

3. ํด๋ผ์ด์–ธํŠธ ํ† ํฐ ์ €์žฅ

  • ํด๋ผ์ด์–ธํŠธ๋Š” ๋ฐœ๊ธ‰๋ฐ›์€ JWT๋ฅผ ๋กœ์ปฌ ์Šคํ† ๋ฆฌ์ง€, ์„ธ์…˜ ์Šคํ† ๋ฆฌ์ง€, ๋˜๋Š” ์ฟ ํ‚ค ๋“ฑ์— ์•ˆ์ „ํ•˜๊ฒŒ ์ €์žฅํ•œ๋‹ค.

4. ํ† ํฐ ํฌํ•จ ์š”์ฒญ

  • ํด๋ผ์ด์–ธํŠธ๋Š” ์„œ๋ฒ„์— ์š”์ฒญํ•  ๋•Œ๋งˆ๋‹ค, JWT๋ฅผ HTTP ์š”์ฒญ ํ—ค๋” Authorization์— ํฌํ•จ์‹œ์ผœ ๋ณด๋‚ธ๋‹ค.
Authorization: Bearer <JWT>

5. ์„œ๋ฒ„ ๊ฒ€์ฆ

  • ์„œ๋ฒ„๋Š” ์ „๋‹ฌ๋ฐ›์€ JWT์˜ Signature๋ฅผ ๋น„๋ฐ€ํ‚ค๋กœ ๊ฒ€์ฆํ•œ๋‹ค.
  • ์œ„๋ณ€์กฐ ์—ฌ๋ถ€, ๋งŒ๋ฃŒ ์—ฌ๋ถ€ ๋“ฑ์„ ํ™•์ธํ•˜๊ณ , ์œ ํšจํ•˜๋ฉด Payload์˜ ์ •๋ณด๋ฅผ ์‚ฌ์šฉํ•ด ์š”์ฒญ์„ ์ฒ˜๋ฆฌํ•œ๋‹ค.

6. ์š”์ฒญ ์ฒ˜๋ฆฌ

  • ํ† ํฐ์ด ์œ ํšจํ•˜๋‹ค๋ฉด, ์„œ๋ฒ„๋Š” ์š”์ฒญ์— ์‘๋‹ตํ•œ๋‹ค.

  • ํ† ํฐ์ด ์œ ํšจํ•˜์ง€ ์•Š๊ฑฐ๋‚˜ ๋ณ€์กฐ๋˜์—ˆ๋‹ค๋ฉด, ์ธ์ฆ ์˜ค๋ฅ˜๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค.

โšก ํ•ต์‹ฌ ํฌ์ธํŠธ: ์„œ๋ฒ„๋Š” JWT ์ž์ฒด๋ฅผ ์ €์žฅํ•˜์ง€ ์•Š์•„๋„ ์œ ํšจ์„ฑ์„ ๊ฒ€์ฆํ•  ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ Stateless ์ธ์ฆ์ด ๊ฐ€๋Šฅํ•˜๋‹ค.
์ด๋Š” ์„œ๋ฒ„ ํ™•์žฅ์„ฑ ์ธก๋ฉด์—์„œ ํฐ ์žฅ์ ์ด๋‹ค.

JWT ํ† ํฐ์€ ์–ด๋””์— ์ €์žฅํ•ด์•ผํ• ๊นŒ?

์‚ฌ์‹ค ์ด ๋ถ€๋ถ„์— ๋Œ€ํ•ด์„œ๋Š” ์ •๋‹ต์ด ์—†๋‹ค. ์ €์žฅ ์œ„์น˜๋งˆ๋‹ค ์žฅ๋‹จ์ ์ด ์žˆ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

JWT๋ฅผ ์ €์žฅํ•˜๋Š” ๋Œ€ํ‘œ์ ์ธ 4๊ฐ€์ง€ ๋ฐฉ์‹์ด ์กด์žฌํ•œ๋‹ค. LocalStorage, SessionStorage, Cookie, ๋ฉ”๋ชจ๋ฆฌ(In-Memory) ์— ์ €์žฅํ•˜๋Š” ๋ฐฉ์‹์ด ์žˆ๋‹ค.

LocalStorage

LocalStorage๋Š” ๋ธŒ๋ผ์šฐ์ €์— ๋ฐ์ดํ„ฐ๋ฅผ ์˜๊ตฌ์ ์œผ๋กœ ์ €์žฅํ•  ์ˆ˜ ์žˆ๋Š” ๊ณต๊ฐ„์ด๋‹ค. JWT๋ฅผ LocalStorage์— ์ €์žฅํ•˜๋ฉด ์ ‘๊ทผ์ด ๋งค์šฐ ์‰ฝ๊ณ  ๊ตฌํ˜„์ด ๊ฐ„๋‹จํ•˜๋‹ค๋Š” ์žฅ์ ์ด ์žˆ๋‹ค.

ํ•˜์ง€๋งŒ ๋ณด์•ˆ ์ธก๋ฉด์—์„œ๋Š” ์ทจ์•ฝํ•˜๋‹ค. ์›น ํŽ˜์ด์ง€์— ์•…์„ฑ ์Šคํฌ๋ฆฝํŠธ๊ฐ€ ์‚ฝ์ž…๋  ๊ฒฝ์šฐ(XSS ๊ณต๊ฒฉ), ์ €์žฅ๋œ ํ† ํฐ์„ ์‰ฝ๊ฒŒ ํƒˆ์ทจํ•  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. ๋”ฐ๋ผ์„œ ๋ฏผ๊ฐํ•œ ์ •๋ณด๋ฅผ ๋‹ด๊ฑฐ๋‚˜ ๋ณด์•ˆ์ด ์ค‘์š”ํ•œ ์„œ๋น„์Šค์—์„œ๋Š” ์ฃผ์˜๊ฐ€ ํ•„์š”ํ•˜๋‹ค.

SessionStorage

SessionStorage๋Š” ๋ธŒ๋ผ์šฐ์ € ํƒญ ๋‹จ์œ„๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ์ €์žฅํ•˜๋ฉฐ, ๋ธŒ๋ผ์šฐ์ €๋‚˜ ํƒญ์„ ๋‹ซ์œผ๋ฉด ์ž๋™์œผ๋กœ ์‚ญ์ œ๋œ๋‹ค. LocalStorage๋ณด๋‹ค ๋น„๊ต์  ์•ˆ์ „ํ•˜๊ณ , SPA(Single Page Application) ํ™˜๊ฒฝ์—์„œ ํŽธ๋ฆฌํ•˜๊ฒŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

ํ•˜์ง€๋งŒ ์ƒˆ๋กœ๊ณ ์นจ ์‹œ ๋ฐ์ดํ„ฐ๊ฐ€ ์‚ฌ๋ผ์ง„๋‹ค๋Š” ๋‹จ์ ์ด ์žˆ์–ด, ์‚ฌ์šฉ์„ฑ ์ธก๋ฉด์—์„œ๋Š” ๋‹ค์†Œ ๋ถˆํŽธํ•  ์ˆ˜ ์žˆ๋‹ค. ๋ณด์•ˆ ์ธก๋ฉด์—์„œ๋Š” LocalStorage์™€ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ XSS ๊ณต๊ฒฉ์— ์ทจ์•ฝํ•˜๋‹ค.

์ฟ ํ‚ค๋Š” ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ์ž๋™์œผ๋กœ ์š”์ฒญ ์‹œ ์ „์†กํ•  ์ˆ˜ ์žˆ๋Š” ์ €์žฅ ๋ฐฉ์‹์ด๋‹ค. HttpOnly์™€ Secure ์˜ต์…˜์„ ์‚ฌ์šฉํ•˜๋ฉด ์Šคํฌ๋ฆฝํŠธ์—์„œ ์ ‘๊ทผํ•  ์ˆ˜ ์—†์œผ๋ฏ€๋กœ XSS ๊ณต๊ฒฉ์„ ๋ฐฉ์ง€ํ•  ์ˆ˜ ์žˆ๋‹ค. ๋˜ํ•œ, ์„œ๋ฒ„์™€ ์ž๋™์œผ๋กœ ํ† ํฐ์„ ์ฃผ๊ณ ๋ฐ›์„ ์ˆ˜ ์žˆ์–ด ๊ตฌํ˜„์ด ํŽธ๋ฆฌํ•˜๋‹ค.

ํ•˜์ง€๋งŒ ์ฟ ํ‚ค๋Š” CSRF ๊ณต๊ฒฉ์— ์ทจ์•ฝํ•˜๋ฉฐ, ํฌ๊ธฐ ์ œํ•œ์ด ์žˆ๊ณ  ์„œ๋ฒ„ ์„ค์ •์ด ํ•„์š”ํ•˜๋‹ค๋Š” ๋‹จ์ ์ด ์žˆ๋‹ค. ๊ทธ๋ž˜์„œ ๋ฏผ๊ฐํ•œ ์„œ๋น„์Šค์—์„œ๋Š” CSRF ๋Œ€์‘ ์ „๋žต์„ ํ•จ๊ป˜ ์‚ฌ์šฉํ•ด์•ผ ํ•œ๋‹ค.

๋ฉ”๋ชจ๋ฆฌ(In-Memory)

๋ฉ”๋ชจ๋ฆฌ ์ €์žฅ ๋ฐฉ์‹์€ ๋ธŒ๋ผ์šฐ์ €์˜ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ๋ณ€์ˆ˜์— JWT๋ฅผ ์ €์žฅํ•˜๋Š” ๋ฐฉ๋ฒ•์ด๋‹ค. ๋ธŒ๋ผ์šฐ์ € ์ƒˆ๋กœ๊ณ ์นจ ์‹œ ํ† ํฐ์ด ์‚ฌ๋ผ์ง€๊ธฐ ๋•Œ๋ฌธ์—, ํƒˆ์ทจ ์œ„ํ—˜์„ ์ตœ์†Œํ™”ํ•  ์ˆ˜ ์žˆ๋‹ค. SPA ํ™˜๊ฒฝ์—์„œ๋Š” ํŽ˜์ด์ง€ ์ด๋™ ์‹œ์—๋„ ์œ ์ง€ํ•  ์ˆ˜ ์žˆ์–ด UX ์ธก๋ฉด์—์„œ ๋น„๊ต์  ํŽธ๋ฆฌํ•˜๋‹ค.

ํ•˜์ง€๋งŒ ์ƒˆ๋กœ๊ณ ์นจ ์‹œ ํ† ํฐ์ด ์ดˆ๊ธฐํ™”๋˜๋ฏ€๋กœ ์‚ฌ์šฉ์ž ๊ฒฝํ—˜์ด ๋ถˆํŽธํ•ด์งˆ ์ˆ˜ ์žˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ๋ธŒ๋ผ์šฐ์ €๋ฅผ ์ข…๋ฃŒํ•˜๋ฉด ํ† ํฐ์ด ์‚ฌ๋ผ์ง€๋ฏ€๋กœ ์žฅ๊ธฐ ์ธ์ฆ์—๋Š” ์ ํ•ฉํ•˜์ง€ ์•Š๋‹ค.

๊ทธ๋ž˜์„œ ์–ด๋””์— ์ €์žฅํ•˜๋ผ๊ณ ?

์•„์ง ๋‚˜๋„ ์ทจ์—…์ค€๋น„์ƒ์ด๊ณ , ํ˜„์—…์—์„œ๋Š” JWT๋ฅผ ์–ด๋–ป๊ฒŒ ์‚ฌ์šฉํ•˜๋Š”์ง€ ์ง์ ‘ ๊ฒฝํ—˜ํ•ด๋ณด์ง„ ๋ชปํ–ˆ์ง€๋งŒ, ๊ฐœ์ธ ํ”„๋กœ์ ํŠธ๋‚˜ ํ•™์Šต์„ ํ•˜๋ฉด์„œ๋Š” ๋ณดํ†ต ์ด๋ ‡๊ฒŒ ๋‚˜๋ˆ„์–ด ์‚ฌ์šฉํ•œ๋‹ค:

  • Access Token โ†’ ๋ฉ”๋ชจ๋ฆฌ(In-Memory)
    Access Token์€ ์ฃผ๋กœ ์งง์€ ์‹œ๊ฐ„ ๋™์•ˆ๋งŒ ์‚ฌ์šฉ๋˜๊ธฐ ๋•Œ๋ฌธ์—, ๋ธŒ๋ผ์šฐ์ € ๋ฉ”๋ชจ๋ฆฌ์— ์ €์žฅํ•ด๋„ ์ถฉ๋ถ„ํ•˜๋‹ค. ๋ฉ”๋ชจ๋ฆฌ์—๋งŒ ์ €์žฅํ•˜๋ฉด XSS ๊ณต๊ฒฉ ์‹œ ํ† ํฐ ํƒˆ์ทจ ์œ„ํ—˜์„ ์ค„์ผ ์ˆ˜ ์žˆ์œผ๋ฉฐ, ํŽ˜์ด์ง€๋ฅผ ์ƒˆ๋กœ๊ณ ์นจํ•˜๋ฉด ์ž๋™์œผ๋กœ ์ดˆ๊ธฐํ™”๋˜์–ด ๋ณด์•ˆ์„ฑ์ด ๋†’๋‹ค.

  • Refresh Token โ†’ HttpOnly Cookie
    Refresh Token์€ Access Token์„ ์žฌ๋ฐœ๊ธ‰๋ฐ›๋Š” ๋ฐ ์‚ฌ์šฉ๋˜๋ฏ€๋กœ ์ƒ๋Œ€์ ์œผ๋กœ ์žฅ๊ธฐ๊ฐ„ ์ €์žฅ๋œ๋‹ค. HttpOnly Cookie์— ์ €์žฅํ•˜๋ฉด ์Šคํฌ๋ฆฝํŠธ์—์„œ ์ ‘๊ทผํ•  ์ˆ˜ ์—†์–ด XSS ๊ณต๊ฒฉ์„ ๋ฐฉ์ง€ํ•  ์ˆ˜ ์žˆ๊ณ , Secure ์˜ต์…˜๊ณผ HTTPS๋ฅผ ํ•จ๊ป˜ ์‚ฌ์šฉํ•˜๋ฉด ์•ˆ์ „ํ•˜๊ฒŒ ์„œ๋ฒ„์™€ ํ†ต์‹ ํ•  ์ˆ˜ ์žˆ๋‹ค. ๋˜ํ•œ, ์„œ๋ฒ„์—์„œ DB๋‚˜ Redis ๋“ฑ ์ €์žฅ์†Œ์— ํ† ํฐ ์ƒํƒœ๋ฅผ ๊ด€๋ฆฌํ•˜๋ฉฐ ๊ฒ€์ฆํ•˜๊ฑฐ๋‚˜ ๋ธ”๋ž™๋ฆฌ์ŠคํŠธ ์ฒ˜๋ฆฌ๋ฅผ ํ†ตํ•ด ํƒˆ์ทจ ์‹œ ๋Œ€์‘ํ•  ์ˆ˜๋„ ์žˆ๋‹ค.

Refresh Token ์ „๋žต

JWT ๊ธฐ๋ฐ˜ ์ธ์ฆ์—์„œ Access Token์€ ๋น„๊ต์  ์งง์€ ๋งŒ๋ฃŒ ์‹œ๊ฐ„์„ ๊ฐ€์ง€์ง€๋งŒ, Refresh Token์€ ์ƒ๋Œ€์ ์œผ๋กœ ์žฅ๊ธฐ๊ฐ„ ์œ ํšจํ•˜๋ฉฐ Access Token์„ ์žฌ๋ฐœ๊ธ‰ ๋ฐ›๋Š” ์šฉ๋„๋กœ ์‚ฌ์šฉ๋œ๋‹ค. ๋”ฐ๋ผ์„œ Refresh Token์„ ์•ˆ์ „ํ•˜๊ฒŒ ๊ด€๋ฆฌํ•˜๋Š” ์ „๋žต์ด ๋งค์šฐ ์ค‘์š”ํ•˜๋‹ค.

Refresh Token Rotation(RTR)

RTR๋Š” โ€œRefresh Token์„ ์žฌ์‚ฌ์šฉํ•  ๋•Œ๋งˆ๋‹ค ์ƒˆ๋กœ์šด Refresh Token์„ ๋ฐœ๊ธ‰ํ•˜๊ณ , ์ด์ „ ํ† ํฐ์€ ๋ฌดํšจํ™”ํ•˜๋Š” ์ „๋žตโ€์„ ๋งํ•œ๋‹ค.

์ด ๋ฐฉ์‹์€ ํŠนํžˆ Refresh Token ํƒˆ์ทจ ๊ณต๊ฒฉ(Replay Attack)์— ๋Œ€๋น„ํ•˜๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉ๋œ๋‹ค.

โš ๏ธ ๊ณต๊ฒฉ ์‹œ๋‚˜๋ฆฌ์˜ค(์™œ RTR๊ฐ€ ํ•„์š”?)

  • ๊ณต๊ฒฉ์ž๊ฐ€ ์‚ฌ์šฉ์ž์˜ Refresh Token์„ ํƒˆ์ทจํ–ˆ๋‹ค๊ณ  ๊ฐ€์ •

  • ์‚ฌ์šฉ์ž๋Š” ์ •์ƒ์ ์œผ๋กœ Access Token ์žฌ๋ฐœ๊ธ‰ ์š”์ฒญ

  • ์„œ๋ฒ„๋Š” ์ƒˆ๋กœ์šด Refresh Token์„ ๋ฐœ๊ธ‰ํ•˜๊ณ , ๊ธฐ์กด Token์„ ๋ฌดํšจํ™”

  • ๊ณต๊ฒฉ์ž๊ฐ€ ํƒˆ์ทจํ•œ Token์„ ์‚ฌ์šฉํ•˜๋ ค ํ•˜๋ฉด?
    โ†’ ์„œ๋ฒ„ DB/Redis์—์„œ ์ด๋ฏธ ์‚ฌ์šฉ๋œ ํ† ํฐ์œผ๋กœ ํŒ์ • โ†’ ์ฆ‰์‹œ ์ฐจ๋‹จ
    โ†’ ๊ณ„์ • ํƒˆ์ทจ๋กœ ์ด์–ด์ง€๋Š” ๊ฒƒ์„ ๋ฐฉ์ง€

์ฆ‰, ํ† ํฐ์ด ํ•œ ๋ฒˆ์ด๋ผ๋„ ์‚ฌ์šฉ๋˜๋ฉด ์žฌ์‚ฌ์šฉ ๋ถˆ๊ฐ€ํ•˜๋ฏ€๋กœ
ํ† ํฐ ํƒˆ์ทจ ํ›„ ์žฌ์‚ฌ์šฉ(Replay)์„ ์›์ฒœ ๋ด‰์‡„ํ•  ์ˆ˜ ์žˆ๋‹ค.

๋ธ”๋ž™๋ฆฌ์ŠคํŠธ(Blacklist) ์ „๋žต

๋ธ”๋ž™๋ฆฌ์ŠคํŠธ ์ „๋žต์€ ๋” ์ด์ƒ ์œ ํšจํ•˜์ง€ ์•Š์•„์•ผ ํ•˜๋Š” Refresh Token ๋˜๋Š” Access Token์„ ์„œ๋ฒ„์— ๊ธฐ๋กํ•ด๋‘๊ณ , ํ•ด๋‹น ํ† ํฐ์˜ ์‚ฌ์šฉ์„ ์ฐจ๋‹จํ•˜๋Š” ๋ฐฉ๋ฒ•์ด๋‹ค.

JWT๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ Stateless๋ผ ํ† ํฐ ์ž์ฒด๋งŒ ์œ ํšจํ•˜๋ฉด ์„œ๋ฒ„ ๊ฒ€์ฆ์„ ํ†ต๊ณผํ•œ๋‹ค. ํ•˜์ง€๋งŒ ๋กœ๊ทธ์•„์›ƒ, ํ† ํฐ ํšŒ์ „(RTR), ํƒˆ์ทจ ๋“ฑ์˜ ์ƒํ™ฉ์—์„œ๋Š” ํŠน์ • ํ† ํฐ์„ ์ฆ‰์‹œ ๋ฌดํšจํ™”ํ•ด์•ผ ํ•œ๋‹ค.

์ด๋•Œ ์„œ๋ฒ„ ์ €์žฅ์†Œ(DB/Redis)์— ๋ฌดํšจ ์ฒ˜๋ฆฌ๋œ ํ† ํฐ ๋ชฉ๋ก์„ ๊ธฐ๋กํ•ด๋‘๋Š” ๊ฒƒ์ด ๋ธ”๋ž™๋ฆฌ์ŠคํŠธ ์ „๋žต์ด๋‹ค.

๐Ÿšซ ๊ณต๊ฒฉ ์‹œ๋‚˜๋ฆฌ์˜ค(์™œ ๋ธ”๋ž™๋ฆฌ์ŠคํŠธ๊ฐ€ ํ•„์š”?)

  • ๊ณต๊ฒฉ์ž๊ฐ€ ์‚ฌ์šฉ์ž์˜ ํ† ํฐ(Access/Refresh Token)์„ ํƒˆ์ทจํ–ˆ๋‹ค๊ณ  ๊ฐ€์ •

  • ์‚ฌ์šฉ์ž๊ฐ€ ๋กœ๊ทธ์•„์›ƒ์„ ํ•˜๊ฑฐ๋‚˜, ๋น„์ •์ƒ ์ ‘๊ทผ์ด ๊ฐ์ง€๋˜์–ด ํ•ด๋‹น ํ† ํฐ์„ ๋ฌดํšจํ™” ์ฒ˜๋ฆฌ

  • ๋ฌดํšจํ™”๋œ ํ† ํฐ์ด DB/Redis์˜ ๋ธ”๋ž™๋ฆฌ์ŠคํŠธ์— ๋“ฑ๋ก

  • ๊ณต๊ฒฉ์ž๊ฐ€ ํƒˆ์ทจํ•œ ํ† ํฐ์œผ๋กœ ์š”์ฒญ์„ ์‹œ๋„ํ•˜๋ฉด?
    โ†’ ์„œ๋ฒ„๊ฐ€ JWT ๊ฒ€์ฆ ๊ณผ์ •์—์„œ ๋ธ”๋ž™๋ฆฌ์ŠคํŠธ๋ฅผ ์กฐํšŒ
    โ†’ ์ด๋ฏธ ๋ฌดํšจ ์ฒ˜๋ฆฌ๋œ ํ† ํฐ์œผ๋กœ ํŒ์ • โ†’ ์ฆ‰์‹œ ์ธ์ฆ ์‹คํŒจ
    โ†’ ์ถ”๊ฐ€์ ์ธ ๊ถŒํ•œ ์ƒ์Šน ๋˜๋Š” ๊ณ„์ • ํƒˆ์ทจ ๋ฐฉ์ง€

์ฆ‰, ๋งŒ๋ฃŒ ์ „ ํ† ํฐ์ด๋ผ๋„ ๋ธ”๋ž™๋ฆฌ์ŠคํŠธ์— ๋“ฑ๋ก๋˜๋ฉด ์ ˆ๋Œ€ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†๋‹ค. ์ด๋Š” ๋กœ๊ทธ์•„์›ƒ / ํƒˆ์ทจ / ํšŒ์ „ ์‹œ ์ฆ‰์‹œ ์‚ฌ์šฉ์ž ๋ณดํ˜ธ๊ฐ€ ๊ฐ€๋Šฅํ•˜๋‹ค๋Š” ์˜๋ฏธ๋‹ค.

Redis๋ฅผ ๋งŽ์ด ์“ฐ๋Š” ์ด์œ 

JWT ๊ธฐ๋ฐ˜ ์ธ์ฆ์„ ์“ฐ๋Š” ํ”„๋กœ์ ํŠธ๊ฐ€ ๋งŽ์•„์ง€๋ฉด์„œ, ๋งŽ์€ ์‚ฌ๋žŒ๋“ค์ด Redis๋ฅผ ๋งŽ์ด ์‚ฌ์šฉํ•œ๋‹ค. ๊ทธ ์ด์œ ๊ฐ€ ๋ญ˜๊นŒ? ๐Ÿค”

๋งค ์š”์ฒญ ๊ฒ€์ฆ ์†๋„๊ฐ€ ๋น ๋ฅด๋‹ค
๋ธ”๋ž™๋ฆฌ์ŠคํŠธ๋‚˜ Refresh Token ์ƒํƒœ๋Š” ๋ชจ๋“  ์ธ์ฆ ์š”์ฒญ์—์„œ ํ™•์ธํ•ด์•ผ ํ•œ๋‹ค. DB๋ฅผ ๋งค๋ฒˆ ์กฐํšŒํ•˜๋ฉด ์ง€์—ฐ์ด ์ƒ๊ธฐ์ง€๋งŒ, Redis๋Š” ๋ฉ”๋ชจ๋ฆฌ ๊ธฐ๋ฐ˜์ด๋ผ ์ดˆ๊ณ ์† ์กฐํšŒ๊ฐ€ ๊ฐ€๋Šฅํ•˜๋‹ค.

TTL(๋งŒ๋ฃŒ ์‹œ๊ฐ„) ๊ด€๋ฆฌ๊ฐ€ ์‰ฝ๋‹ค
ํ† ํฐ์€ ๋งŒ๋ฃŒ ์‹œ๊ฐ„์ด ์žˆ๊ธฐ ๋•Œ๋ฌธ์—, Redis์— TTL์„ ์„ค์ •ํ•˜๋ฉด ์ž๋™์œผ๋กœ ๋งŒ๋ฃŒ ์ฒ˜๋ฆฌ๋œ๋‹ค. ๋ณ„๋„์˜ ์ •๋ฆฌ ์ž‘์—…์ด ํ•„์š” ์—†์–ด ์‹ค๋ฌด์—์„œ ํŽธ๋ฆฌํ•˜๋‹ค.

Refresh Token Rotation(RTR)๊ณผ ๊ถํ•ฉ์ด ์ข‹๋‹ค
ํ† ํฐ ์žฌ๋ฐœ๊ธ‰ ์‹œ ์ด์ „ ํ† ํฐ์„ ์ฆ‰์‹œ ๋ฌดํšจํ™”ํ•ด์•ผ ํ•˜๋Š”๋ฐ, Redis๋Š” ์ฝ๊ธฐ/์“ฐ๊ธฐ ์†๋„๊ฐ€ ๋น ๋ฅด๊ธฐ ๋•Œ๋ฌธ์— ์ฆ‰์‹œ ๋ฐ˜์˜ ๊ฐ€๋Šฅํ•˜๋‹ค.

๋ถ„์‚ฐ ์„œ๋ฒ„ ํ™˜๊ฒฝ์—์„œ๋„ ์•ˆ์ •์ ์ด๋‹ค
์„œ๋ฒ„๋ฅผ ์—ฌ๋Ÿฌ ๋Œ€ ์šด์˜ํ•˜๋”๋ผ๋„ Redis๋ฅผ ์ค‘์•™ ์บ์‹œ์ฒ˜๋Ÿผ ์“ฐ๋ฉด ๋ชจ๋“  ์„œ๋ฒ„๊ฐ€ ํ† ํฐ ์ƒํƒœ๋ฅผ ๊ณต์œ ํ•  ์ˆ˜ ์žˆ์–ด, ์ˆ˜ํ‰ ํ™•์žฅ ํ™˜๊ฒฝ์—์„œ๋„ ๋ฌธ์ œ ์—†๋‹ค.

๋‹ค๋ฅธ ์บ์‹œ๋„ ์‚ฌ์šฉ ๊ฐ€๋Šฅํ• ๊นŒ?

Redis๊ฐ€ ๋งŽ์ด ์“ฐ์ด๊ธด ํ•˜์ง€๋งŒ, ์กฐ๊ฑด๋งŒ ๋งž์œผ๋ฉด ๋‹ค๋ฅธ ์บ์‹œ ์†”๋ฃจ์…˜๋„ ์“ธ ์ˆ˜ ์žˆ๋‹ค.

1. Caffeine Cache

  • JVM ๋‚ด๋ถ€ ๋ฉ”๋ชจ๋ฆฌ ๊ธฐ๋ฐ˜ ์บ์‹œ

  • ์กฐํšŒ ์†๋„๋Š” ๋งค์šฐ ๋น ๋ฅด์ง€๋งŒ, ๋ถ„์‚ฐ ํ™˜๊ฒฝ์—์„œ ์ƒํƒœ ๊ณต์œ  ๋ถˆ๊ฐ€

  • ๋‹จ์ผ ์„œ๋ฒ„ ํ™˜๊ฒฝ์—์„œ๋Š” ์ถฉ๋ถ„ํžˆ ์‚ฌ์šฉ ๊ฐ€๋Šฅ

2. Memcached ๋“ฑ ๋„คํŠธ์›Œํฌ ๊ธฐ๋ฐ˜ ์บ์‹œ

  • Redis์™€ ์œ ์‚ฌํ•˜๊ฒŒ ๋ถ„์‚ฐ ํ™˜๊ฒฝ์—์„œ ์ƒํƒœ ๊ณต์œ  ๊ฐ€๋Šฅ

  • TTL ๊ด€๋ฆฌ, ๋™์‹œ ์ ‘๊ทผ ์„ฑ๋Šฅ ๊ณ ๋ ค ๊ฐ€๋Šฅ

๐Ÿ’ก ์ •๋ฆฌ

  • ๋‹จ์ผ ์„œ๋ฒ„ + ๋‹จ์ˆœ ํ† ํฐ ๊ด€๋ฆฌ โ†’ Caffeine Cache ๊ฐ€๋Šฅ
  • ๋ถ„์‚ฐ ์„œ๋ฒ„ + ์‹ค์‹œ๊ฐ„ ๋ธ”๋ž™๋ฆฌ์ŠคํŠธ/Refresh Token ์ƒํƒœ ๊ณต์œ  ํ•„์š” โ†’ Redis ์ถ”์ฒœ

๐Ÿงน ์ •๋ฆฌ

JWT ํƒˆ์ทจ ์‹œ ๋Œ€์‘ ๋ฐฉ์•ˆ

JWT๋Š” Stateless๋ผ ํ† ํฐ์„ ์„œ๋ฒ„์—์„œ ๊ฐ•์ œ๋กœ ๋ฌดํšจํ™”ํ•  ์ˆ˜ ์—†๊ธฐ ๋•Œ๋ฌธ์—, ํƒˆ์ทจ๋ฅผ ์™„์ „ํžˆ ์ฐจ๋‹จํ•˜๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ ํ”ผํ•ด๋ฅผ ์ตœ์†Œํ™”ํ•˜๋Š” ๋ฐฉ์‹์œผ๋กœ ์„ค๊ณ„ํ•˜๋Š” ๊ฒƒ์ด ํ•ต์‹ฌ์ด๋‹ค.

Access Token๊ณผ Refresh Token์˜ ์—ญํ• ์ด ๋‹ค๋ฅด๊ธฐ ๋•Œ๋ฌธ์— ๋Œ€์‘ ๋ฐฉ๋ฒ•๋„ ๋‹ค๋ฅด๊ฒŒ ๊ฐ€์ ธ๊ฐ„๋‹ค.

1) Access Token ํƒˆ์ทจ ์‹œ ๋Œ€์‘

Access Token์€ ์งง์€ ๋งŒ๋ฃŒ ์‹œ๊ฐ„์„ ๊ฐ€์ง€๊ธฐ ๋•Œ๋ฌธ์—
ํ”ผํ•ด๋ฅผ ์ตœ์†Œํ™”ํ•˜๋Š” ์ „๋žต์„ ์‚ฌ์šฉํ•œ๋‹ค.

  • ๋งŒ๋ฃŒ ์‹œ๊ฐ„์„ ์งง๊ฒŒ ์„ค์ • (5~15๋ถ„)
    โ†’ ํƒˆ์ทจ๋˜์–ด๋„ ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ์‹œ๊ฐ„์ด ์ œํ•œ๋จ.

  • ๋น„์ •์ƒ ์‚ฌ์šฉ ํƒ์ง€(IP / UA / ์ง€์—ญ ๋ณ€ํ™” ๊ฐ์ง€)
    โ†’ ๋™์ผ ํ† ํฐ์ด ๊ฐ‘์ž๊ธฐ ๋‹ค๋ฅธ ํ™˜๊ฒฝ์—์„œ ์‚ฌ์šฉ๋˜๋ฉด ์˜์‹ฌ ์š”์ฒญ์œผ๋กœ ํŒ๋‹จํ•ด Redis ๋ธ”๋ž™๋ฆฌ์ŠคํŠธ์— ๋“ฑ๋กํ•˜์—ฌ ์ฆ‰์‹œ ์ฐจ๋‹จํ•œ๋‹ค.

  • Refresh Token ์žฌ๋ฐœ๊ธ‰ ์‹œ ์—ฐ๊ด€๋œ Access Token ํ๊ธฐ
    โ†’ RTR ๋„์ค‘ ์‚ฌ์šฉ๋œ Access Token์„ ๋ธ”๋ž™๋ฆฌ์ŠคํŠธ์— ๋„ฃ์–ด ํ™•์‚ฐ์„ ๋ง‰๋Š”๋‹ค.

๊ฒฐ๋ก ์ ์œผ๋กœ Access Token์€ ์ฆ‰์‹œ ๋ฌดํšจํ™”๋Š” ์–ด๋ ต์ง€๋งŒ,
โ€œ์งง์€ ๋งŒ๋ฃŒ + ์ด์ƒํ–‰์œ„ ์ฐจ๋‹จโ€์œผ๋กœ ํ”ผํ•ด ๊ตฌ๊ฐ„์„ ์ตœ์†Œํ™”ํ•œ๋‹ค.

2) Refresh Token ํƒˆ์ทจ ์‹œ ๋Œ€์‘

  • Refresh Token์€ ์žฅ๊ธฐ ๊ถŒํ•œ์„ ๊ฐ€์ง€๊ธฐ ๋•Œ๋ฌธ์— ๋ณด์•ˆ์ด ํ›จ์”ฌ ์ค‘์š”ํ•˜๊ณ , ๋ฌด์กฐ๊ฑด ์„œ๋ฒ„ ์ €์žฅ์†Œ(DB/Redis)๋กœ ๊ด€๋ฆฌํ•œ๋‹ค.

  • ๊ทธ๋ฆฌ๊ณ  Refresh Token Rotation(RTR) ์ „๋žต์„ ์‚ฌ์šฉํ•œ๋‹ค.

RTR ๋™์ž‘ ๋ฐฉ์‹

  • ํด๋ผ์ด์–ธํŠธ๊ฐ€ Refresh Token์œผ๋กœ ์žฌ๋ฐœ๊ธ‰ ์š”์ฒญ

  • ์„œ๋ฒ„๋Š” ์ƒˆ๋กœ์šด Refresh Token์„ ๋ฐœ๊ธ‰

  • ์ด์ „ Refresh Token์€ Redis/DB์—์„œ ์ฆ‰์‹œ ๋ฌดํšจํ™”

ํƒˆ์ทจ ์ƒํ™ฉ์—์„œ์˜ ํšจ๊ณผ

  • ๊ณต๊ฒฉ์ž๊ฐ€ ํ›”์นœ ํ† ํฐ์œผ๋กœ ์žฌ๋ฐœ๊ธ‰์„ ์‹œ๋„ํ•˜๋ฉด,
    ์ด์ „ ํ† ํฐ์ด ์ด๋ฏธ ์‚ฌ์šฉ๋œ ๊ธฐ๋ก์ด ์žˆ์–ด ๋ฐ”๋กœ ์ฐจ๋‹จ(Replayed Token Detection), ๋™์‹œ์— ์‚ฌ์šฉ์ž ๊ณ„์ •์„ ๋ณดํ˜ธํ•˜๊ธฐ ์œ„ํ•ด Refresh Token ์ „์ฒด ๋ฌดํšจํ™”, ๋กœ๊ทธ์•„์›ƒ ๊ฐ•์ œ, ์‚ฌ์šฉ์ž์—๊ฒŒ ์žฌ๋กœ๊ทธ์ธ ์š”๊ตฌ

3) ๊ฒฐ๋ก : JWT ํƒˆ์ทจ ๋Œ€์‘์˜ ํ•ต์‹ฌ

  • Access Token: ํ”ผํ•ด ์ตœ์†Œํ™”(์งง์€ TTL + ๋ธ”๋ž™๋ฆฌ์ŠคํŠธ + ๋น„์ •์ƒ ํƒ์ง€)

  • Refresh Token: RTR๋กœ ์žฌ์‚ฌ์šฉ ๋ถˆ๊ฐ€ + ์„œ๋ฒ„ ์ €์žฅ์†Œ ๊ธฐ๋ฐ˜ ๊ฐ•์ œ ํ๊ธฐ

์ถ”๊ฐ€ ๋ณด์•ˆ: HttpOnly ์ฟ ํ‚ค ์‚ฌ์šฉ, TLS ๊ฐ•์ œ, ํ† ํฐ ๋ˆ„์ถœ ์ง€์  ์ตœ์†Œํ™”

ํ•ต์‹ฌ์€ โ€œJWT๋Š” ๋ฌดํšจํ™”๊ฐ€ ์–ด๋ ต๊ธฐ ๋•Œ๋ฌธ์—,
์• ์ดˆ์— ํƒˆ์ทจ ์ „์ œ๋กœ ์„ค๊ณ„ํ•˜๊ณ , ํ”ผํ•ด๋ฅผ ์ตœ์†Œํ™”ํ•˜๋Š” ํ๋ฆ„์„ ๋งŒ๋“ ๋‹ค.


์ฐธ๊ณ 

[Tecoble]์ธ์ฆ ๋ฐฉ์‹ : Cookie & Session vs JWT

profile
๋…ธ๋ ฅ์€ ๋ฐฐ์‹ ํ•˜์ง€ ์•Š์•„ ๐Ÿ”ฅ

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