Skip to content

Browser Storage โ€‹

๐Ÿงฉ Reference

์ฟ ํ‚ค (Cookie)

  • ์„œ๋ฒ„๊ฐ€ ๋ธŒ๋ผ์šฐ์ €์— ์‚ฌ์šฉ์ž์— ๋Œ€ํ•œ ์ž‘์€ ๋ฐ์ดํ„ฐ ์กฐ๊ฐ์„ ์ €์žฅํ•ด๋‘๋Š” ๋ฐฉ์‹.
  • ๋ธŒ๋ผ์šฐ์ €๋Š” ์ดํ›„ ๊ฐ™์€ ๋„๋ฉ”์ธ์œผ๋กœ ์š”์ฒญ์„ ๋ณด๋‚ผ ๋•Œ ์ด ์ฟ ํ‚ค๋ฅผ ์ž๋™์œผ๋กœ ํ•จ๊ป˜ ์ „์†กํ•œ๋‹ค.

์„ธ์…˜ (Session)

  • ์„œ๋ฒ„๊ฐ€ ๋ฉ”๋ชจ๋ฆฌ์— ์ด ์‚ฌ์šฉ์ž๋Š” ๋ˆ„๊ตฌ์ธ์ง€์— ๋Œ€ํ•œ ์ƒํƒœ(์„ธ์…˜ ๋ฐ์ดํ„ฐ)๋ฅผ ๋“ค๊ณ  ์žˆ๊ณ ,
  • ๋ธŒ๋ผ์šฐ์ €์—๋Š” ๊ทธ ์„ธ์…˜์„ ๊ฐ€๋ฆฌํ‚ค๋Š” ์„ธ์…˜ ID๋งŒ ์ฟ ํ‚ค๋กœ ์ €์žฅํ•˜๋Š” ๋ฐฉ์‹์˜ ์ธ์ฆ ๋ชจ๋ธ.

ํ† ํฐ (Token)

  • ์„œ๋ฒ„๊ฐ€ ๋ฐœ๊ธ‰ํ•˜๋Š” โ€œ์„œ๋ช…๋œ ์ธ์ฆ ์ •๋ณด ๋ญ‰์น˜โ€.
  • ๋ณดํ†ต ์ด ํ† ํฐ์„ ๋“ค๊ณ  ๋‹ค๋‹ˆ๋ฉด์„œ, ๋งค ์š”์ฒญ๋งˆ๋‹ค Authorization ํ—ค๋” ๋“ฑ์— ์ง์ ‘ ์‹ค์–ด ๋ณด๋‚ด ์ธ์ฆํ•œ๋‹ค.

JWT (JSON Web Token)

  • ํ† ํฐ ํฌ๋งท์˜ ํ•œ ์ข…๋ฅ˜๋กœ,
    header.payload.signature ๊ตฌ์กฐ๋ฅผ ๊ฐ€์ง„ JSON ๊ธฐ๋ฐ˜ ์„œ๋ช… ํ† ํฐ.
  • payload ์•ˆ์— ์œ ์ € ID, ๋งŒ๋ฃŒ ์‹œ๊ฐ„ ๊ฐ™์€ ์ •๋ณด๋ฅผ ๋‹ด๊ณ ,
    ์„œ๋ฒ„๋Š” ์ด ํ† ํฐ์ด ์œ„์กฐ๋˜์ง€ ์•Š์•˜๋Š”์ง€๋งŒ ๊ฒ€์ฆํ•˜๊ณ  ์„ธ์…˜์€ ๋”ฐ๋กœ ๋“ค๊ณ  ์žˆ์ง€ ์•Š์•„๋„ ๋œ๋‹ค.

๐Ÿงฉ ์•„๋ž˜ ์ •๋ฆฌ๋ฅผ ํ•˜๊ณ  ๊นจ๋‹ฌ์€ ์ !

  • RefreshToken์„ ์™œ localStorage์— ์ €์žฅํ•˜๋ฉด ์œ„ํ—˜ํ•œ์ง€
    โ†’ localStorage๋Š” JS๋กœ ์ฝํ˜€์„œ XSS์— ๊ทธ๋Œ€๋กœ ํ„ธ๋ฆฌ๊ธฐ ๋•Œ๋ฌธ.
  • httpOnly ์ฟ ํ‚ค๊ฐ€ ์™œ XSS์— ์•ˆ์ „ํ•œ์ง€
    โ†’ JS ์ ‘๊ทผ์ด ๋ถˆ๊ฐ€๋Šฅํ•ด ์Šคํฌ๋ฆฝํŠธ๊ฐ€ ๋– ๋„ ์ฟ ํ‚ค ๊ฐ’์„ ์ฝ์„ ์ˆ˜ ์—†๊ธฐ ๋•Œ๋ฌธ.
  • localStorage vs sessionStorage vs memory
    โ†’ ๋ชจ๋‘ JS ์ ‘๊ทผ ๊ฐ€๋Šฅํ•˜์ง€๋งŒ, ๋ณด์กด ๊ธฐ๊ฐ„ยท๋ฒ”์œ„๊ฐ€ ๋‹ฌ๋ผ์„œ ์šฉ๋„์— ๋งž๊ฒŒ ์„ ํƒํ•ด์•ผ ํ•œ๋‹ค.
  • AccessToken์„ ์™œ Authorization ํ—ค๋”์— ์‹ค์–ด์•ผ ํ•˜๋Š”์ง€ โ†’ ์ฟ ํ‚ค ์ž๋™ ์ „์†ก์„ ํ”ผํ•˜๊ณ , ๋ช…์‹œ์ ์œผ๋กœ ์ธ์ฆ ์ •๋ณด๋ฅผ ๋ณด๋‚ด CSRF ์œ„ํ—˜์„ ์ค„์ด๊ธฐ ์œ„ํ•ด์„œ.
  • JWT ์ธ์ฆ์€ HTTP/HTTPS ๊ทœ์•ฝ ์œ„์—์„œ ์›€์ง์ธ๋‹ค๋Š” ์ 
    โ†’ ํ† ํฐ ์ „๋‹ฌยท์ฟ ํ‚ค ์ €์žฅยท401 ์‘๋‹ตยท์žฌ๋ฐœ๊ธ‰ ํ๋ฆ„์€ ๋ชจ๋‘ HTTP ํ”„๋กœํ† ์ฝœ ๊ทœ์น™์— ๋”ฐ๋ฅธ ๋™์ž‘์ด๋‹ค.

์›น ์ธ์ฆ์„ ์ดํ•ดํ•˜๋ ค๋ฉด, ๋จผ์ € ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ์–ด๋–ค ์ €์žฅ์†Œ๋“ค์„ ์ œ๊ณตํ•˜๋Š”์ง€ ํ™•์‹คํžˆ ์•Œ์•„์•ผ ํ•œ๋‹ค!

๋ธŒ๋ผ์šฐ์ €์—๋Š” ํฌ๊ฒŒ ๋„ค ๊ฐ€์ง€ ์ €์žฅ์†Œ๊ฐ€ ์žˆ๋‹ค:

  • Memory (JS ๋ฉ”๋ชจ๋ฆฌ)
  • LocalStorage
  • SessionStorage
  • Cookie

Memory (์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋ฉ”๋ชจ๋ฆฌ) โ€‹

๋ฉ”๋ชจ๋ฆฌ๋ž€ ๋ธŒ๋ผ์šฐ์ € ํƒญ์ด ์—ด๋ ค ์žˆ๋Š” ๋™์•ˆ, JS ๋Ÿฐํƒ€์ž„์ด ๋“ค๊ณ  ์žˆ๋Š” ํœ˜๋ฐœ์„ฑ ๋ฐ์ดํ„ฐ ์˜์—ญ์ด๋‹ค.
React state, Zustand, context ๋“ฑ์— ์ €์žฅ๋˜๋Š” ๋ชจ๋“  ๋ฐ์ดํ„ฐ๋Š” ๋ฉ”๋ชจ๋ฆฌ์— ์žˆ๋‹ค.

ํŠน์ง• โ€‹

  • ์ƒˆ๋กœ๊ณ ์นจํ•˜๋ฉด ์‚ฌ๋ผ์ง„๋‹ค.
  • XSS์— ์ทจ์•ฝํ•˜๋‹ค. (JS๊ฐ€ ์ฝ์„ ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ)
  • ์†๋„๋Š” ๊ฐ€์žฅ ๋น ๋ฅด๋‹ค.
  • ๋ธŒ๋ผ์šฐ์ € ํƒญ์„ ๋‹ซ์œผ๋ฉด ์‚ฌ๋ผ์ง„๋‹ค.
  • ์„œ๋ฒ„๋กœ ์ž๋™ ์ „์†ก๋˜์ง€ ์•Š๋Š”๋‹ค. (์ฟ ํ‚ค์™€ ๋‹ค๋ฆ„)

JWT์—์„œ ์–ด๋–ป๊ฒŒ ์“ธ๊นŒ? โ€‹

AccessToken์„ ๋ฉ”๋ชจ๋ฆฌ์— ๋„ฃ๋Š” ์„œ๋น„์Šค๊ฐ€ ๋งŽ๋‹ค.

  • AT๋Š” ์งง๊ฒŒ ์‚ด์•„์„œ ํ„ธ๋ ค๋„ ํ”ผํ•ด ์ž‘๋‹ค.
  • JS์—์„œ ๊ด€๋ฆฌํ•ด๋„ ๋œ๋‹ค.
  • ์ž๋™ ์ „์†ก๋˜์ง€ ์•Š์•„ CSRF ์œ„ํ—˜์ด ์—†๋‹ค.
  • ์ƒˆ๋กœ๊ณ ์นจํ•ด๋„ RefreshToken์ด ์ฟ ํ‚ค์— ์žˆ์œผ๋‹ˆ ์žฌ๋ฐœ๊ธ‰ํ•˜๋ฉด ๋œ๋‹ค.

์ฆ‰, AccessToken์€ ๋ฉ”๋ชจ๋ฆฌ์— ์ €์žฅํ•ด๋„ ๊ดœ์ฐฎ๋‹ค.


LocalStorage โ€‹

๋„๋ฉ”์ธ๋ณ„๋กœ ํ‚ค-๊ฐ’ ์Œ์„ ์˜๊ตฌ์ ์œผ๋กœ ์ €์žฅํ•˜๋Š” ๋ธŒ๋ผ์šฐ์ € ๋‚ด์žฅ ์ €์žฅ์†Œ์ด๋‹ค.
๋ธŒ๋ผ์šฐ์ €๋ฅผ ๊บผ๋„ ๋‚จ์•„ ์žˆ๊ณ , JS์—์„œ localStorage API๋กœ ์ฝ๊ณ  ์“ธ ์ˆ˜ ์žˆ๋‹ค.

ํŠน์ง• โ€‹

  • ๋ธŒ๋ผ์šฐ์ €๋ฅผ ๊บผ๋„ ์œ ์ง€๋œ๋‹ค.(์˜๊ตฌ ์ €์žฅ)
  • ์ €์žฅํ•  ์ˆ˜ ์žˆ๋Š” ์šฉ๋Ÿ‰์ด ํฌ๋‹ค.
  • JS๋กœ ์ ‘๊ทผ ๊ฐ€๋Šฅ โ†’ XSS ๊ณต๊ฒฉ์— ์˜ํ•ด ๊ฐ’์ด ๊ทธ๋Œ€๋กœ ์œ ์ถœ๋  ์ˆ˜ ์žˆ๋‹ค.
  • ์„œ๋ฒ„๋กœ ์ž๋™ ์ „์†ก๋˜์ง€ ์•Š์Œ (์ฟ ํ‚ค์™€ ๋‹ค๋ฆ„)

์–ธ์ œ ์‚ฌ์šฉํ• ๊นŒ? โ€‹

  • ์‚ฌ์šฉ์ž ์„ค์ •(ํ…Œ๋งˆ, ์–ธ์–ด, UI ์ƒํƒœ)
  • ๋กœ๊ทธ์ธ ์—ฌ๋ถ€(Boolean๋งŒ)
  • ๊ฐ„๋‹จํ•œ ์บ์‹œ ๋“ฑ

JWT์—์„œ๋Š”? โ€‹

AccessToken์„ ์—ฌ๊ธฐ์— ์ €์žฅํ•˜๋Š” ์„œ๋น„์Šค๋„ ์žˆ์ง€๋งŒ ๋ณด์•ˆ ๊ด€์ ์—์„œ๋Š” ๋น„์ถ”์ฒœ์ด๋‹ค.

  • XSS ๊ณต๊ฒฉ์—์„œ localStorage๋Š” ๋ฐ”๋กœ ํ„ธ๋ฆฐ๋‹ค. = AccessToken ํƒˆ์ทจ
  • ๋งค์šฐ ์œ„ํ—˜ํ•˜์ง€๋Š” ์•Š์ง€๋งŒ(AT๋งŒ ํ„ธ๋ฆฌ๋ฉด ์žฌ๋ฐœ๊ธ‰ ๊ฐ€๋Šฅํ•˜๋‹ˆ๊นŒ) ๊ทธ๋ž˜๋„ ์ข‹์ง€๋Š” ์•Š๋‹ค.

์ถ”๊ฐ€๋กœ, localStorage๋Š” ๋ธŒ๋ผ์šฐ์ €๋ฅผ ๊บผ๋„ ๊ณ„์† ๋‚จ์•„ ์žˆ๋Š” ์˜๊ตฌ ์ €์žฅ์†Œ๋ผ์„œ,
์›๋ž˜ ์งง๊ฒŒ ์“ฐ๊ณ  ๋ฒ„๋ ค์•ผ ํ•  AccessToken์ด ๋ถˆํ•„์š”ํ•˜๊ฒŒ ์˜ค๋ž˜ ์‚ด์•„๋‚จ๋Š”๋‹ค๋Š” ์ ๋„ ์œ„ํ—˜ ์š”์†Œ๋‹ค.
โ†’ ํ† ํฐ์ด ์œ ํšจ ๊ธฐ๊ฐ„ ๋™์•ˆ ๋ธŒ๋ผ์šฐ์ €์— ๊ณ„์† ๋‚จ์•„ ์žˆ์œผ๋‹ˆ, ํƒˆ์ทจ ๊ฐ€๋Šฅ ๊ธฐ๊ฐ„๋„ ํ•จ๊ป˜ ๊ธธ์–ด์ง„๋‹ค.

๐Ÿ’ก

AccessToken = localStorage ๊ฐ€๋Šฅํ•˜๋‚˜ XSS ์ทจ์•ฝ
RefreshToken = ์ ˆ๋Œ€ localStorage ๊ธˆ์ง€


SessionStorage โ€‹

localStorage์™€ ๋น„์Šทํ•˜์ง€๋งŒ ๋ธŒ๋ผ์šฐ์ € ํƒญ(์„ธ์…˜) ๋‹จ์œ„๋กœ ์œ ์ง€๋˜๋Š” ํ‚ค-๊ฐ’ ์ €์žฅ์†Œ์ด๋‹ค.
๊ตฌ์กฐ๋Š” localStorage์™€ ๊ฐ™์ง€๋งŒ, ํƒญ์„ ๋‹ซ์œผ๋ฉด ํ•จ๊ป˜ ์‚ฌ๋ผ์ง„๋‹ค.

ํŠน์ง• โ€‹

  • ํƒญ ๋‹ซ์œผ๋ฉด ๋‚ ์•„๊ฐ„๋‹ค.
  • ์ƒˆ๋กœ๊ณ ์นจํ•ด๋„ ์œ ์ง€๋œ๋‹ค.
  • JS ์ ‘๊ทผ ๊ฐ€๋Šฅ โ†’ XSS ์ทจ์•ฝํ•˜๋‹ค.

JWT์—์„œ ์–ด๋–ป๊ฒŒ ์“ธ๊นŒ? โ€‹

JWT์—์„œ๋Š” sessionStorage ๊ฑฐ์˜ ์“ฐ์ง€ ์•Š๋Š”๋‹ค.


HTTP ์š”์ฒญ/์‘๋‹ต ํ—ค๋”๋ฅผ ํ†ตํ•ด ์„œ๋ฒ„์™€ ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ์ฃผ๊ณ ๋ฐ›๋Š” ์ž‘์€ ํ…์ŠคํŠธ ์กฐ๊ฐ์ด๋‹ค.
๋ธŒ๋ผ์šฐ์ €๊ฐ€ ์ž๋™์œผ๋กœ ์ €์žฅยท๊ด€๋ฆฌํ•˜๋ฉฐ, ๊ฐ™์€ ๋„๋ฉ”์ธ์œผ๋กœ ์š”์ฒญ์„ ๋ณด๋‚ผ ๋•Œ Cookie ํ—ค๋”์— ์ž๋™์œผ๋กœ ์‹ค์–ด ๋ณด๋‚ธ๋‹ค.

์ฟ ํ‚ค๋Š” ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ์„œ๋ฒ„ ๋ง์„ ๋“ฃ๊ณ  ์ง์ ‘ ๊ด€๋ฆฌํ•˜๋ฏ€๋กœ ํ”„๋ก ํŠธ์—์„œ ์ฟ ํ‚ค ์ €์žฅ์„ ํ•˜์ง€ ์•Š์•„๋„ ๋œ๋‹ค.


์„œ๋ฒ„๊ฐ€ ์ฟ ํ‚ค๋ฅผ ์ €์žฅํ•˜๋Š” ๊ณผ์ • โ€‹

http
Set-Cookie: refresh_token=abc123; HttpOnly; Secure; SameSite=Strict

์ด๋ ‡๊ฒŒ ์„œ๋ฒ„๊ฐ€ ์‘๋‹ต์„ ๋‚ด๋ ค์ฃผ๋ฉด:

  • ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ์ฟ ํ‚ค๋ฅผ ์ž๋™์œผ๋กœ ์ €์žฅํ•œ๋‹ค.
  • ๊ฐ™์€ ๋„๋ฉ”์ธ์œผ๋กœ ์š”์ฒญ์„ ๋ณด๋‚ผ ๋•Œ ์ž๋™์œผ๋กœ Cookie ํ—ค๋”์— ์‹ค๋ ค ๋‚˜๊ฐ„๋‹ค.
http
Cookie: refresh_token=abc123;

์ฟ ํ‚ค ์†์„ฑ๋“ค โ€‹

์†์„ฑ๊ฐ’์„ค๋ช…์˜ˆ์‹œ
httpOnlytrue/falseJS ์ ‘๊ทผ ์ฐจ๋‹จ โ†’ XSS ๋ฐฉ์–ดHttpOnly
Securetrue/falseHTTPS์—์„œ๋งŒ ์ „์†กSecure
SameSiteStrict/Lax/NoneCSRF ๋ฐฉ์ง€ ์ˆ˜์ค€ ์กฐ์ ˆSameSite=Strict
Domain๋„๋ฉ”์ธ๋ช…์ฟ ํ‚ค ์ ์šฉ ๋„๋ฉ”์ธ ๋ฒ”์œ„Domain=.example.com
Path๊ฒฝ๋กœ์ฟ ํ‚ค ์ ์šฉ ๊ฒฝ๋กœ ๋ฒ”์œ„Path=/api
Max-Age์ดˆ์ฟ ํ‚ค ์ˆ˜๋ช… (์ดˆ ๋‹จ์œ„)Max-Age=604800 (7์ผ)
Expires๋‚ ์งœ์ฟ ํ‚ค ๋งŒ๋ฃŒ ์ ˆ๋Œ€ ์‹œ๊ฐ„Expires=Wed, 21 Oct 2025

ํŠนํžˆ Path๋ฅผ /auth ๋‚˜ /api/auth์ฒ˜๋Ÿผ ์ตœ๋Œ€ํ•œ ์ข๊ฒŒ ์„ค์ •ํ•˜๋ฉด,
์ฟ ํ‚ค๊ฐ€ ์ „์†ก๋˜๋Š” ์š”์ฒญ ๋ฒ”์œ„๋ฅผ ์ค„์ผ ์ˆ˜ ์žˆ์–ด์„œ ๊ณต๊ฒฉ ํ‘œ๋ฉด์„ ์ค„์ด๋Š” ๋ฐ ๋„์›€์ด ๋œ๋‹ค.

SameSite ์ƒ์„ธ:

  • Strict: ์™„์ „ ๋™์ผ ์‚ฌ์ดํŠธ๋งŒ โ†’ ๊ฐ€์žฅ ์•ˆ์ „, UX ๋ถˆํŽธ
  • Lax: GET + ํƒ‘๋ ˆ๋ฒจ ๋„ค๋น„๊ฒŒ์ด์…˜ ํ—ˆ์šฉ โ†’ ๊ธฐ๋ณธ๊ฐ’, ๊ท ํ˜•์ 
  • None: ๋ชจ๋“  ํฌ๋กœ์Šค์‚ฌ์ดํŠธ ํ—ˆ์šฉ โ†’ Secure ํ•„์ˆ˜

์ผ๋ฐ˜ ์ฟ ํ‚ค vs httpOnly ์ฟ ํ‚ค ๋น„๊ต โ€‹

๊ตฌ๋ถ„Cookie (์ผ๋ฐ˜)Cookie (httpOnly)
JS ์ ‘๊ทผ ๊ฐ€๋Šฅ ์—ฌ๋ถ€O (document.cookie)X (JS ์ ‘๊ทผ ๋ถˆ๊ฐ€)
XSS ๊ณต๊ฒฉ๋งค์šฐ ์ทจ์•ฝ์•ˆ์ „
CSRF ์œ„ํ—˜๋™์ผ (๋‘˜ ๋‹ค ์ž๋™ ์ „์†ก)๋™์ผ
JWT์— ์ ํ•ฉ?๋ถ€์ ํ•ฉRefreshToken ์ €์žฅ ํ‘œ์ค€ ๋ฐฉ์‹
์„œ๋ฒ„ ์ œ์–ด ๊ฐ€๋Šฅ?OO

์™œ RefreshToken์€ ์ฟ ํ‚ค์—ฌ์•ผ ํ• ๊นŒ? โ€‹

1. ๊ธฐ๋ณธ์ ์œผ๋กœ ์ž๋™ ์ „์†ก์ด ํ•„์š”ํ•˜๋‹ค. โ€‹

RefreshToken์€ ์„œ๋ฒ„๊ฐ€ ํด๋ผ์ด์–ธํŠธ ์‹ ์›์„ ํ™•์ธํ•˜๋Š” ์šฉ๋„๋‹ค.
์ด๊ฑธ ํ”„๋ก ํŠธ์—์„œ ์ง์ ‘ ๊บผ๋‚ด์„œ ๋ถ™์ด๋Š” ๊ตฌ์กฐ๋กœ ๋งŒ๋“ค๋ฉด ์•ˆ๋œ๋‹ค.

์ฟ ํ‚ค๋Š” ์ž๋™ ์ €์žฅ, ์ž๋™ ์ „์†ก, JS ์ ‘๊ทผ ๋ถˆ๊ฐ€
์ด ํŠน์ง• ๋•Œ๋ฌธ์— RefreshToken์„ ๋„ฃ๊ธฐ ์ตœ์ ํ™”๋œ ์ €์žฅ ๊ณต๊ฐ„์ด๋‹ค.

2. XSS๋กœ๋ถ€ํ„ฐ ๋ณดํ˜ธํ•ด์•ผ ํ•œ๋‹ค. โ€‹

localStorage / sessionStorage / ๋ฉ”๋ชจ๋ฆฌ๋Š” JS๋กœ ์ ‘๊ทผ ๊ฐ€๋Šฅ โ†’ XSS ๊ณต๊ฒฉ์— ์ทจ์•ฝ
httpOnly ์ฟ ํ‚ค๋Š” JS๋กœ ์ฝ์„ ์ˆ˜ ์—†๋‹ค.

3. RefreshToken ํƒˆ์ทจ = ๊ณ„์ • ์˜๊ตฌ ์žฅ์•… โ€‹

RT๊ฐ€ ์žฌ๋ฐœ๊ธ‰ ํ† ํฐ์ด๋ฏ€๋กœ ํ„ธ๋ฆฌ๋ฉด ์„ธ์…˜ ์˜๊ตฌ ์žฅ์•…์ด๋‹ค.
โ†’ RT๋Š” ์ ˆ๋Œ€ ๋…ธ์ถœ๋˜๋ฉด ์•ˆ ๋˜๋ฏ€๋กœ httpOnly + Secure + SameSite ์ฟ ํ‚ค๋ฅผ ์‚ฌ์šฉ.


Memory vs LocalStorage ์ €์žฅ ์ „๋žต โ€‹

AccessToken ์ €์žฅ ์œ„์น˜ ์„ ํƒ ๊ธฐ์ค€:

๊ธฐ์ค€MemoryLocalStorage
๋ณด์•ˆ(XSS)์œ„ํ—˜๋งค์šฐ ์œ„ํ—˜
CSRF์•ˆ์ „์•ˆ์ „
์ƒˆ๋กœ๊ณ ์นจ์‚ฌ๋ผ์ง์œ ์ง€๋จ
UX ํŽธ์˜์„ฑ๋‚ฎ์Œ๋†’์Œ
๊ถŒ์žฅ ์—ฌ๋ถ€๊ถŒ์žฅ์กฐ๊ฑด๋ถ€ ๊ฐ€๋Šฅ

์ •๋ฆฌ :

  • ์ตœ๋Œ€ ๋ณด์•ˆ = Memory
  • ํŽธ์˜์„ฑ ์ค‘์‹œ = LocalStorage

์„ธ์…˜ ๊ธฐ๋ฐ˜ ์ธ์ฆ vs JWT ์ธ์ฆ โ€‹

๊ตฌ๋ถ„์„ธ์…˜ ๊ธฐ๋ฐ˜JWT ๊ธฐ๋ฐ˜
์„œ๋ฒ„ ์ €์žฅ์†Œ์„ธ์…˜ ์ €์žฅ์†Œ ํ•„์š”๋ฌด์ƒํƒœ(stateless)
ํ™•์žฅ์„ฑ๋‚ฎ์Œ๋†’์Œ (๋ถ„์‚ฐ ์„œ๋ฒ„์—์„œ ์ข‹์Œ)
ํ† ํฐ ํฌ๊ธฐ์ž‘์Œ (์„ธ์…˜ ID)ํผ (header+payload+signature)
RT ํ•„์š” ์—ฌ๋ถ€ํ•„์š” ์—†์Œํ•„์š” (์žฌ๋ฐœ๊ธ‰์šฉ)
๋ณด์•ˆ ์ˆ˜์ค€๋†’์Œ๊ตฌํ˜„์— ๋”ฐ๋ผ ๋‹ฌ๋ผ์ง

๋ธŒ๋ผ์šฐ์ € ์ €์žฅ์†Œ ์šฉ๋Ÿ‰ ์ œํ•œ โ€‹

์ €์žฅ์†Œ์šฉ๋Ÿ‰ ์ œํ•œ์ถ”๊ฐ€ ์ œํ•œ์‚ฌํ•ญ
Memory๋ธŒ๋ผ์šฐ์ € ๋ฉ”๋ชจ๋ฆฌ์— ๋”ฐ๋ผํƒญ ๋‹ซ์œผ๋ฉด ์‚ฌ๋ผ์ง
LocalStorage5-10MB (๋ธŒ๋ผ์šฐ์ €๋ณ„ ์ƒ์ด)๋„๋ฉ”์ธ๋‹น ์ œํ•œ
SessionStorage5-10MBํƒญ๋ณ„ ๋…๋ฆฝ์ 
Cookie4KB/๊ฐœ, 50๊ฐœ/๋„๋ฉ”์ธ๋ชจ๋“  ์š”์ฒญ์— ์ž๋™ ์ „์†ก

JWT RefreshToken ์ฃผ์˜์ :

  • JWT๊ฐ€ ๊ธธ๋ฉด ์ฟ ํ‚ค 4KB ์ดˆ๊ณผ ๊ฐ€๋Šฅ
  • ์ด๋•Œ๋Š” JWT ๋Œ€์‹  ์„ธ์…˜ ID ๋ฐฉ์‹ ๊ณ ๋ ค

์ •๋ฆฌ โ€‹

๊ตฌ๋ถ„MemoryLocalStorageSessionStorageCookie (httpOnly)
์œ ์ง€ ๊ธฐ๊ฐ„X (ํƒญ ์ข…๋ฃŒ ์‹œ ์‚ญ์ œ)O (๋ธŒ๋ผ์šฐ์ € ๊บผ๋„ ์œ ์ง€)X (ํƒญ ๋‹ซ์œผ๋ฉด ์‚ญ์ œ)O (๋งŒ๋ฃŒ ์‹œ๊ฐ„๊นŒ์ง€ ์œ ์ง€)
์ƒˆ๋กœ๊ณ ์นจ ์˜ํ–ฅX (์‚ฌ๋ผ์ง)O (์œ ์ง€๋จ)O (์œ ์ง€๋จ)O (์œ ์ง€๋จ)
๋ธŒ๋ผ์šฐ์ € ์ž๋™ ์ „์†กXXXO (Cookie ํ—ค๋”๋กœ ์ž๋™ ์ „์†ก)
JS ์ ‘๊ทผ ๊ฐ€๋Šฅ ์—ฌ๋ถ€O (์ ‘๊ทผ ๊ฐ€๋Šฅ โ†’ XSS ์ทจ์•ฝ)O (์ ‘๊ทผ ๊ฐ€๋Šฅ โ†’ XSS ๋งค์šฐ ์ทจ์•ฝ)O (์ ‘๊ทผ ๊ฐ€๋Šฅ โ†’ XSS ์ทจ์•ฝ)X (์ ‘๊ทผ ๋ถˆ๊ฐ€ โ†’ XSS ์•ˆ์ „)
์‚ฌ์šฉ ์šฉ๋„AccessToken, ์•ฑ ์ƒํƒœ์‚ฌ์šฉ์ž ์„ค์ •, ์บ์‹œ, UI ์ƒํƒœํƒญ ๊ธฐ๋ฐ˜ ์ž„์‹œ ๋ฐ์ดํ„ฐRefreshToken, ์ธ์ฆ ์œ ์ง€
๋ณด์•ˆ ์œ„ํ˜‘ ๋ชจ๋ธXSS ์ทจ์•ฝXSS ๋งค์šฐ ์ทจ์•ฝXSS ์ทจ์•ฝXSS ์•ˆ์ „ / CSRF ๊ณ ๋ ค ํ•„์š”
์„œ๋ฒ„๊ฐ€ ํ†ต์ œ ๊ฐ€๋Šฅ?XXXO (์„œ๋ฒ„๊ฐ€ Set-Cookie๋กœ ๊ด€๋ฆฌ)
์ ํ•ฉํ•œ JWT ์—ญํ• AccessToken(์ถ”์ฒœ)AccessToken(์กฐ๊ฑด๋ถ€)๊ฑฐ์˜ ์‚ฌ์šฉ XRefreshToken(ํ‘œ์ค€)

๐Ÿ’ก ์ €์žฅ์†Œ ์„ ํƒ ๊ธฐ์ค€ ์š”์•ฝ

JWT ์ธ์ฆ์—์„œ ์ €์žฅ์†Œ๋ฅผ ์„ ํƒํ•  ๋•Œ ํ•ต์‹ฌ ๊ธฐ์ค€์€ ๋‹ค์Œ ๋„ค ๊ฐ€์ง€๋‹ค:

  • XSS ๋ณดํ˜ธ๊ฐ€ ํ•„์š”ํ•œ๊ฐ€?
    โ†’ RefreshToken์ฒ˜๋Ÿผ ์ ˆ๋Œ€ ๋…ธ์ถœ๋˜๋ฉด ์•ˆ ๋˜๋Š” ๊ฐ’์€ httpOnly Cookie

  • ์ž๋™ ์ „์†ก์ด ํ•„์š”ํ•œ๊ฐ€?
    โ†’ ์„œ๋ฒ„๊ฐ€ ์•Œ์•„์„œ ํ™•์ธํ•ด์•ผ ํ•˜๋Š” ๊ฐ’์€ Cookie

  • ์ƒˆ๋กœ๊ณ ์นจํ•ด๋„ ์œ ์ง€ํ•ด์•ผ ํ•˜๋‚˜?
    โ†’ ์˜ค๋ž˜ ์‚ด์•„์•ผ ํ•˜๋Š” ๊ฐ’์€ LocalStorage,
    โ†’ ์งง๊ฒŒ๋งŒ ์œ ์ง€ํ•ด๋„ ๋˜๋Š” ๊ฐ’์€ Memory

  • CSRF ๋ณดํ˜ธ๊ฐ€ ํ•„์š”ํ•œ๊ฐ€?
    โ†’ Cookie ์“ฐ๋ฉด SameSite/CSRF ํ† ํฐ์„ ๋ฐ˜๋“œ์‹œ ๊ณ ๋ คํ•ด์•ผ ํ•จ

๊ฒฐ๋ก :

  • AccessToken โ†’ Memory(๊ถŒ์žฅ) / LocalStorage(์กฐ๊ฑด๋ถ€)
  • RefreshToken โ†’ ๋ฐ˜๋“œ์‹œ httpOnly Cookie