๐Ÿ” ํ—ท๊ฐˆ๋ฆฌ๋Š” ์ธ์ฆ ๊ฐœ๋… (JWT ยท Bearer ยท Access Token ยท Session ยท Storage) ์ •๋ฆฌ

Jisunminยท2025๋…„ 12์›” 8์ผ
post-thumbnail

์ด ๋ฌธ์„œ์—์„œ๋Š” ์›น ์ธ์ฆ ๊ณผ์ •์—์„œ ์ž์ฃผ ๋“ฑ์žฅํ•ด ํ—ท๊ฐˆ๋ฆฌ๊ธฐ ์‰ฌ์šด(์ œ๊ฐ€ ํ—ท๊ฐˆ๋ ค์š”) JWT, Bearer, access_token, refresh_token,
์ฟ ํ‚คยทlocalStorageยท๋ฉ”๋ชจ๋ฆฌ ์ €์žฅ ๋ฐฉ์‹, Session ๋ฐฉ์‹, ๊ทธ๋ฆฌ๊ณ  ์„ธ์…˜ ํ† ํฐ ๊ฐœ๋…์„ ๋ช…ํ™•ํžˆ ์ •๋ฆฌํ•ด๋ดค์Šต๋‹ˆ๋‹ค.


1. ์ธ์ฆ์˜ ํฐ ํ๋ฆ„ ์ดํ•ดํ•˜๊ธฐ

์›น์—์„œ ๋กœ๊ทธ์ธ ๊ด€๋ จ ์ž‘์—…์€ ํฌ๊ฒŒ ๋‘ ๋‹จ๊ณ„๋กœ ๋‚˜๋‰ฉ๋‹ˆ๋‹ค.

  1. Authentication (์ธ์ฆ) โ€” ๐Ÿ‘ค ๋ˆ„๊ตฌ์ธ์ง€ ํ™•์ธํ•˜๊ธฐ: ์‚ฌ์šฉ์ž๊ฐ€ ์ •๋ง ๊ทธ ์‚ฌ๋žŒ์ธ์ง€ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค.
  2. Authorization & Session ์œ ์ง€ โ€” ๐Ÿ” ๊ถŒํ•œ์ฃผ๊ณ  ์œ ์ง€ํ•˜๊ธฐ: ์ธ์ฆ๋œ ์‚ฌ์šฉ์ž๊ฐ€ ๋กœ๊ทธ์ธ ์ƒํƒœ๋ฅผ ์œ ์ง€ํ•˜๊ณ , ๊ถŒํ•œ๋Œ€๋กœ ํ–‰๋™ํ•  ์ˆ˜ ์žˆ๋„๋ก ๊ด€๋ฆฌํ•ฉ๋‹ˆ๋‹ค.

์ด ๋‘ ๋‹จ๊ณ„ ๊ณผ์ •์—์„œ ์•„๋ž˜ ๊ฐœ๋…๋“ค์ด ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ๋“ฑ์žฅํ•ฉ๋‹ˆ๋‹ค:

  • ์„ธ์…˜(session)
  • ์ฟ ํ‚ค(cookie)
  • JWT(JSON Web Token)
  • access_token / refresh_token
  • ์ €์žฅ ๋ฐฉ์‹(localStorage, cookie, in-memory)
  • Opaque Token
  • id_token, API Key ๋“ฑ

2. JWT(JSON Web Token)

โœ” ๋ฌด์—‡์ธ๊ฐ€?

JWT๋Š” ํ† ํฐ์˜ ๋ชจ์–‘(ํฌ๋งท) ์ž…๋‹ˆ๋‹ค. ์ฆ‰, โ€œํ† ํฐ์ด ์–ด๋–ค ๊ตฌ์กฐ๋กœ ์ƒ๊ฒผ๋ƒ?โ€๋ฅผ ์ •์˜ํ•œ ๊ทœ๊ฒฉ์ž…๋‹ˆ๋‹ค. => "ํ† ํฐ์ด ์–ด๋–ค ๋ชจ์–‘์ธ๊ฐ€"

xxxxx.yyyyy.zzzzz
Header.Payload.Signature

๊ตฌ์กฐ

  • Header โ€” ์–ด๋–ค ์•Œ๊ณ ๋ฆฌ์ฆ˜์œผ๋กœ ์„œ๋ช…ํ–ˆ๋Š”์ง€
  • Payload โ€” ์œ ์ € ์ •๋ณด, ๊ถŒํ•œ, ๋งŒ๋ฃŒ ์‹œ๊ฐ„(exp) ๋“ฑ
  • Signature โ€” ์œ„์กฐ ๋ฐฉ์ง€๋ฅผ ์œ„ํ•œ ์„œ๋ช…๊ฐ’

โœ” ์–ด๋””์— ์“ฐ๋Š”๊ฐ€?

  • access_token ์šฉ๋„๋กœ ๊ฐ€์žฅ ๋„๋ฆฌ ์‚ฌ์šฉ๋จ
  • ์„œ๋ฒ„๊ฐ€ ์ƒํƒœ๋ฅผ ์ €์žฅํ•˜์ง€ ์•Š๋Š” Stateless ์ธ์ฆ์— ์ ํ•ฉ

    Stateless = ์„œ๋ฒ„๊ฐ€ ์•„๋ฌด ์ƒํƒœ๋„ ๋ณด๊ด€ํ•˜์ง€ ์•Š๊ณ , ์š”์ฒญ๋งˆ๋‹ค ์ธ์ฆ ์ •๋ณด๋ฅผ ๋‹ค์‹œ ๋ฐ›์•„ ํ™•์ธํ•˜๋Š” ๋ฐฉ์‹

โœ” ์–ด๋””์—์„œ ๋งŽ์ด ์“ฐ๋Š”๊ฐ€?

  • access_token ๊ตฌํ˜„ ๋ฐฉ์‹์œผ๋กœ ๊ฐ€์žฅ ๋„๋ฆฌ ์‚ฌ์šฉ๋จ
  • ํด๋ผ์ด์–ธํŠธ๊ฐ€ API ์„œ๋ฒ„์— ์ธ์ฆํ•  ๋•Œ (์›นยท๋ชจ๋ฐ”์ผ ๊ณตํ†ต)
  • OAuth์˜ id_token
  • ์„œ๋ฒ„๊ฐ€ ์ƒํƒœ๋ฅผ ์ €์žฅํ•˜์ง€ ์•Š๋Š” Stateless ํ™˜๊ฒฝ

โœ” ์ค‘์š”ํ•œ ํฌ์ธํŠธ

  • JWT๋Š” ์•”ํ˜ธํ™”๋œ ๊ฒƒ์ด ์•„๋‹ˆ๋ผ ์„œ๋ช…(Signature)๋œ ๊ฒƒ์ด๋‹ค.
    โ†’ payload๋Š” Base64 ๋””์ฝ”๋”ฉํ•˜๋ฉด ๋ˆ„๊ตฌ๋‚˜ ๋ณผ ์ˆ˜ ์žˆ์Œ
  • ๋ฏผ๊ฐํ•œ ์ •๋ณด ์ €์žฅ ๊ธˆ์ง€
  • JWT๋Š” ํฌ๋งท์ผ ๋ฟ, ์ €์žฅ ๋ฐฉ์‹์€ ์ฟ ํ‚ค/์Šคํ† ๋ฆฌ์ง€/๋ฉ”๋ชจ๋ฆฌ ์ค‘ ์•„๋ฌด๊ฑฐ๋‚˜ ์‚ฌ์šฉ ๊ฐ€๋Šฅ

3. Bearer

โœ” ๋ฌด์—‡์ธ๊ฐ€?

Bearer๋Š” ์ธ์ฆ ํ† ํฐ์„ ์ „์†กํ•˜๋Š” ๋ฐฉ์‹(์Šคํ‚ด) ์ž…๋‹ˆ๋‹ค. => "์ „์†ก ๋ฐฉ์‹"

Authorization: Bearer <token>
  • JWT, Opaque Token, access_token ๋“ฑ ๋ชจ๋“  ์ข…๋ฅ˜์˜ ํ† ํฐ ์ „๋‹ฌ ๊ฐ€๋Šฅ
  • ํ† ํฐ์„ ํ—ค๋”์— ๋„ฃ์–ด ๋ณด๋‚ด๊ธฐ ์œ„ํ•œ ๊ฐ€์žฅ ํ‘œ์ค€์ ์ธ ๋ฐฉ์‹
  • API ์ธ์ฆ์—์„œ ๊ฐ€์žฅ ํ”ํ•˜๊ฒŒ ์‚ฌ์šฉ๋จ

โœ” ์–ด๋””์—์„œ ๋งŽ์ด ์“ฐ๋Š”๊ฐ€?

ํด๋ผ์ด์–ธํŠธ๊ฐ€ ์„œ๋ฒ„ API์— ์ธ์ฆ ์ •๋ณด๋ฅผ ์ „๋‹ฌํ•  ๋•Œ ๊ฑฐ์˜ ํ‘œ์ค€์ฒ˜๋Ÿผ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค.

  • REST API
  • GraphQL API
  • OAuth ๊ธฐ๋ฐ˜ access_token ์ธ์ฆ

๐Ÿ”— jwt์™€ Bearer์˜ ๊ด€๊ณ„

Bearer๋Š” โ€œ์ „์†ก ๋ฐฉ์‹โ€์ด๋ผ JWT๊ฐ€ ์•„๋‹Œ ํ† ํฐ๋„ ์–ผ๋งˆ๋“ ์ง€ ์‹ค์–ด ๋ณด๋‚ผ ์ˆ˜ ์žˆ๋‹ค.
JWT๋Š” Bearer์—์„œ ์“ฐ์ด๋Š” ์—ฌ๋Ÿฌ ํ† ํฐ ์ค‘ ํ•˜๋‚˜์ผ ๋ฟ์ด๋‹ค.


4. ๐Ÿ’พ Token "์ €์žฅ ๋ฐฉ์‹" (์ฟ ํ‚ค ยท localStorage ยท ๋ฉ”๋ชจ๋ฆฌ)

๐Ÿช ์ฟ ํ‚ค(Cookie)

  • ์„œ๋ฒ„๊ฐ€ Set-Cookie ๋กœ ๋‚ด๋ ค์ฃผ๋ฉด ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ์ €์žฅ
  • ์š”์ฒญ ์‹œ ์ž๋™ ์ „์†ก
  • httpOnly, secure, sameSite ์˜ต์…˜์œผ๋กœ ๋ณด์•ˆ ๊ฐ•ํ™” ๊ฐ€๋Šฅ

๐Ÿ” httpOnly๋ž€?
์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ์—์„œ ์ฟ ํ‚ค๋ฅผ ์ฝ์ง€ ๋ชปํ•˜๋„๋ก ์ฐจ๋‹จํ•˜๋Š” ์˜ต์…˜
โ†’ refresh_token, session_id์ฒ˜๋Ÿผ ์œ ์ถœ๋˜๋ฉด ์œ„ํ—˜ํ•œ ๊ฐ’ ๋ณดํ˜ธ์— ํ•„์ˆ˜

์™œ refresh_token / session_id ๋Š” ๋ฐ˜๋“œ์‹œ httpOnly ์ฟ ํ‚ค์— ์ €์žฅํ• ๊นŒ?

์ด ๋‘ ๊ฐ’์€ ํƒˆ์ทจ๋˜๋ฉด ๊ณ„์ •์ด ๊ทธ๋Œ€๋กœ ๋„๋‚œ๋˜๋Š” โ€˜๊ฐ€์žฅ ์œ„ํ—˜ํ•œ ํ† ํฐโ€™์ด๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.

ํ•ญ๋ชฉํ„ธ๋ ธ์„ ๋•Œ ์œ„ํ—˜๋„
access_token์งง๊ฒŒ ์‚ด์•„์„œ ํ”ผํ•ด ์ œํ•œ์ 
refresh_token์ƒˆ access_token ๋ฌดํ•œ ์žฌ๋ฐœ๊ธ‰ โ†’ ๊ณ„์ • ์žฅ์•…
session_id์„œ๋ฒ„ ์„ธ์…˜ ๊ทธ๋Œ€๋กœ ํƒˆ์ทจ โ†’ ์™„์ „ ๋„๋‚œ

์ฆ‰, ์ ˆ๋Œ€ JavaScript์—์„œ ์ฝํžˆ๋ฉด ์•ˆ ๋˜๋Š” ํ† ํฐ๋“ค์ž…๋‹ˆ๋‹ค.

  • localStorage โŒ
    โ†’ JS๋กœ ์ฝํž˜ โ†’ XSS ๊ณต๊ฒฉ์— ๊ทธ๋Œ€๋กœ ๋…ธ์ถœ

  • httpOnly ์ฟ ํ‚ค โญ•
    โ†’ JS ์ ‘๊ทผ ์™„์ „ ์ฐจ๋‹จ
    โ†’ ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ์ž๋™ ์ „์†กํ•˜๋ฏ€๋กœ ์ธ์ฆ ํ๋ฆ„๋„ ์ž์—ฐ์Šค๋Ÿฌ์›€
    ์ผ๋ฐ˜ ์ฟ ํ‚ค๋Š” JS์—์„œ ์ฝํžˆ๊ธฐ ๋•Œ๋ฌธ์— XSS ๊ณต๊ฒฉ์— ์ทจ์•ฝํ•ฉ๋‹ˆ๋‹ค.

XSS(Cross-Site Scripting)?

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

Set-Cookie: refresh_token=abcd; HttpOnly;

์ฃผ๋กœ ์–ด๋””์— ์“ฐ๋Š”๊ฐ€?

  • refresh_token ์ €์žฅ
  • session_id ์ €์žฅ

โœ” localStorage / sessionStorage

  • JS์—์„œ ์ง์ ‘ ์ ‘๊ทผ ๊ฐ€๋Šฅํ•œ ์ €์žฅ์†Œ
  • ์ž๋™ ์ „์†ก X โ†’ Bearer ํ—ค๋”์— ์ง์ ‘ ๋„ฃ์–ด์•ผ ํ•จ
  • ์›๋ž˜๋Š” ์ผ๋ฐ˜ ๋ฐ์ดํ„ฐ ์ €์žฅ์šฉ
  • XSS ์ทจ์•ฝ

์ฃผ ์‚ฌ์šฉ์ฒ˜

  • ์ผ๋ฐ˜ UI ์„ค์ •๊ฐ’, Key-Value ๋ฐ์ดํ„ฐ ์ €์žฅ (์˜ˆ: ํ…Œ๋งˆ, ์–ธ์–ด ์„ค์ •, ํ•„ํ„ฐ ๊ฐ’ ๋“ฑ)
  • ์ธ์ฆ ์ •๋ณด ์ค‘ ๋ณด์•ˆ ์ค‘์š”๋„๊ฐ€ ๋‚ฎ์€ ๊ฐ’ ์ €์žฅ ์‹œ (์˜ˆ: access_token โ€” ๋‹จ, ์•ˆ์ „์„ฑ์€ ๋–จ์–ด์ง)
  • ๊ฐ„๋‹จํ•œ SPA, ๋ฐ๋ชจ/ํ”„๋กœํ† ํƒ€์ž…์—์„œ ์ž„์‹œ ๋ฐ์ดํ„ฐ ์ €์žฅ

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

  • ์ƒˆ๋กœ๊ณ ์นจ ์‹œ ์‚ฌ๋ผ์ง€๋Š” ์ €์žฅ ๋ฐฉ์‹
  • XSS ์œ„ํ—˜์„ ์ค„์ด๊ธฐ ์œ„ํ•ด access_token์„ ๋ฉ”๋ชจ๋ฆฌ์— ์ €์žฅํ•˜๋Š” ํŒจํ„ด ์ฆ๊ฐ€

์ธ๋ฉ”๋ชจ๋ฆฌ๋ž€?

React state, Context, Redux store, Zustand, ์ „์—ญ JS ๋ณ€์ˆ˜ ๋“ฑ
๋Ÿฐํƒ€์ž„ ๋ฉ”๋ชจ๋ฆฌ์—๋งŒ ๋“ค๊ณ  ์žˆ๋Š” ์ƒํƒœ๋ฅผ ์˜๋ฏธ

์ฃผ ์‚ฌ์šฉ์ฒ˜

  • access_token ๋ณด๊ด€ (๊ฐ€์žฅ ๊ถŒ์žฅ๋˜๋Š” ๋ฐฉ์‹)

๐Ÿค” ์ƒˆ๋กœ ๊ณ ์นจํ•˜๋ฉด ์‚ฌ๋ผ์ง€๋Š”๋ฐ ์™œ ์‚ฌ์šฉํ• ๊นŒ?

์ผ๋ถ€๋Ÿฌ ์‚ฌ๋ผ์ง€๋„๋ก ์„ค๊ณ„ํ•œ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

์ด์œ : XSS ๊ณต๊ฒฉ์—์„œ ์•ˆ์ „

localStorage์— ์ €์žฅํ•˜๋ฉด ์Šคํฌ๋ฆฝํŠธ๋กœ ์‰ฝ๊ฒŒ ํƒˆ์ทจ๋จ:

localStorage.getItem("access_token")

๊ทธ๋Ÿฌ๋‚˜ ๋ฉ”๋ชจ๋ฆฌ๋Š” ํŽ˜์ด์ง€ Reload ์‹œ ์‚ฌ๋ผ์ ธ ์ง€์†์ ์ธ ๊ณต๊ฒฉ์ด ๋ถˆ๊ฐ€๋Šฅ.


๊ทธ๋ ‡๋‹ค๋ฉด ์ƒˆ๋กœ๊ณ ์นจ ํ›„์—๋Š” ์–ด๋–ป๊ฒŒ ๋กœ๊ทธ์ธ ์ƒํƒœ๋ฅผ ์œ ์ง€ํ• ๊นŒ?

์ธ๋ฉ”๋ชจ๋ฆฌ์— ์žˆ๋˜ access_token์€ ์ƒˆ๋กœ๊ณ ์นจ ์‹œ ์‚ฌ๋ผ์ง€๊ธฐ ๋•Œ๋ฌธ์—,
์•„๋ฌด ๋กœ์ง๋„ ์—†์œผ๋ฉด ์‚ฌ์‹ค์ƒ โ€œ๋กœ๊ทธ์•„์›ƒ" ๋ฉ๋‹ˆ๋‹ค.

๊ทธ๋ž˜์„œ ๋ณดํ†ต ์•ฑ์—์„œ ์ด๋Ÿฐ ํ๋ฆ„์œผ๋กœ ๊ตฌํ˜„ํ•ฉ๋‹ˆ๋‹ค:

  1. ์•ฑ์ด ์ฒ˜์Œ ๋กœ๋“œ๋  ๋•Œ(์˜ˆ: App ๋˜๋Š” Layout ๋งˆ์šดํŠธ ์‹œ์ ์—)
    /auth/refresh ๊ฐ™์€ ํ† ํฐ ์žฌ๋ฐœ๊ธ‰ API๋ฅผ ์ž๋™์œผ๋กœ ํ•œ ๋ฒˆ ํ˜ธ์ถœํ•ฉ๋‹ˆ๋‹ค.

  2. ์ด๋•Œ ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ์•Œ์•„์„œ refresh_token(httpOnly ์ฟ ํ‚ค)์„ ์„œ๋ฒ„๋กœ ์ „์†กํ•ฉ๋‹ˆ๋‹ค.

  3. ์„œ๋ฒ„๋Š” refresh_token์„ ๊ฒ€์ฆํ•˜๊ณ  ์ƒˆ access_token์„ ์‘๋‹ต์œผ๋กœ ๋‚ด๋ ค์ค๋‹ˆ๋‹ค.

  4. ํ”„๋ก ํŠธ๋Š” ๋ฐ›์€ access_token์„ ๋‹ค์‹œ ๋ฉ”๋ชจ๋ฆฌ์— ์ €์žฅํ•ฉ๋‹ˆ๋‹ค.

์ด๋ ‡๊ฒŒ ํ•˜๋ฉด, ์œ ์ € ์ž…์žฅ์—์„œ๋Š” ์ƒˆ๋กœ๊ณ ์นจ์„ ํ•ด๋„ ๋กœ๊ทธ์ธ์ด ์œ ์ง€๋œ ๊ฒƒ์ฒ˜๋Ÿผ ๋ณด์ž…๋‹ˆ๋‹ค.


โœ” ์‹ค์ œ ๊ถŒ์žฅ ํŒจํ„ด

  • refresh_token โ†’ httpOnly ์ฟ ํ‚ค
  • access_token โ†’ ๋ฉ”๋ชจ๋ฆฌ ๋ณด๊ด€
  • access_token ๋งŒ๋ฃŒ ์‹œ โ†’ refresh_token์œผ๋กœ ์žฌ๋ฐœ๊ธ‰

5. access_token / refresh_token

๐Ÿ”‘ access_token

  • ์งง๊ฒŒ ์‚ฌ๋Š” ์ธ์ฆ ํ† ํฐ (5๋ถ„~1์‹œ๊ฐ„)
  • ๋ณดํ†ต JWT
  • Bearer๋กœ API ์ธ์ฆ

๐Ÿ”‘ refresh_token

  • access_token ์žฌ๋ฐœ๊ธ‰์šฉ
  • ์˜ค๋ž˜ ์‚ด๊ธฐ ๋•Œ๋ฌธ์— ๋ณด์•ˆ ์ค‘์š”
  • ๋ณดํ†ต ๋žœ๋ค ๋ฌธ์ž์—ด(Opaque Token) + httpOnly ์ฟ ํ‚ค

๋น„์œ 

  • access_token = ๐Ÿšช โ€œ๋ฌธ ์•ž์—์„œ ๋ณด์—ฌ์ฃผ๋Š” ์ถœ์ž…์ฆโ€
  • refresh_token = ๐ŸŽซ โ€œ์ถœ์ž…์ฆ ์žฌ๋ฐœ๊ธ‰๊ถŒโ€

6. Session ๋ฐฉ์‹ (sessionStorage์™€ ์™„์ „ํžˆ ๋‹ค๋ฆ„)

์„ธ์…˜(Session) ๋ฐฉ์‹์€ ์„œ๋ฒ„๊ฐ€ ๋กœ๊ทธ์ธ ์ƒํƒœ๋ฅผ ์ง์ ‘ ๊ธฐ์–ตํ•˜๋Š” ๋ฐฉ์‹์ž…๋‹ˆ๋‹ค.

โœ” ์ž‘๋™ ํ๋ฆ„

  1. ๋กœ๊ทธ์ธ ์„ฑ๊ณต โ†’ ์„œ๋ฒ„๊ฐ€ ์„ธ์…˜ ์ƒ์„ฑ

    session_id โ†’ { userId: 1, role: 'admin' }
  2. session_id๋ฅผ ์ฟ ํ‚ค๋กœ ๋‚ด๋ ค์คŒ

    Set-Cookie: session_id=xyz123; HttpOnly;
  3. ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ์š”์ฒญ๋งˆ๋‹ค ์ž๋™ ์ „์†ก

    Cookie: session_id=xyz123
  4. ์„œ๋ฒ„๋Š” ์ด session_id๋กœ ์œ ์ € ์ •๋ณด ์กฐํšŒ

์„œ๋ฒ„๊ฐ€ ์œ ์ € ์ƒํƒœ๋ฅผ ์ €์žฅํ•˜๊ณ ,
๋ธŒ๋ผ์šฐ์ €๋Š” session_id๋ผ๋Š” ์—ด์‡ ๋งŒ ๋“ค๊ณ  ๋‹ค๋‹ˆ๋Š” ๊ตฌ์กฐ

โœ” ์žฅ์ 

  • ์„œ๋ฒ„์—์„œ ๊ฐ•์ œ ๋กœ๊ทธ์•„์›ƒ ๊ฐ€๋Šฅ
  • ๋ฏผ๊ฐ ์ •๋ณด๊ฐ€ ์„œ๋ฒ„์— ์žˆ์–ด ์•ˆ์ „

โœ” ๋‹จ์ 

  • ์„œ๋ฒ„๊ฐ€ ์ƒํƒœ๋ฅผ ์ €์žฅํ•ด์•ผ ํ•จ โ†’ ์Šค์ผ€์ผ๋ง ๋น„์šฉ

์Šค์ผ€์ผ๋ง ๋น„์šฉ์ด๋ž€?

์‚ฌ์šฉ์ž ์ฆ๊ฐ€์— ๋งž์ถฐ ์„œ๋ฒ„๋ฅผ ํ™•์žฅํ•  ๋•Œ ํ•„์š”ํ•œ ์ถ”๊ฐ€ ๋ถ€๋‹ด


๐Ÿšซ sessionStorage๋Š” ์™„์ „ํžˆ ๋‹ค๋ฅธ ๊ฐœ๋…์ž…๋‹ˆ๋‹ค.

๊ฐœ๋…์ €์žฅ ์œ„์น˜๋ชฉ์ ์ธ์ฆ ์—ฌ๋ถ€
Session(์„œ๋ฒ„ ์„ธ์…˜)์„œ๋ฒ„๋กœ๊ทธ์ธ ์ƒํƒœ ์œ ์ง€โœ”
sessionStorage๋ธŒ๋ผ์šฐ์ €์ผ๋ฐ˜ ๋ฐ์ดํ„ฐ ์ž„์‹œ ์ €์žฅโŒ

์„ธ์…˜ ๋ฐฉ์‹์€ ์–ด๋–ป๊ฒŒ ๊ตฌํ˜„ํ• ๊นŒ?

โœ” ์„œ๋ฒ„์—์„œ ํ•˜๋Š” ์ผ

  • ๋กœ๊ทธ์ธ ์‹œ ์„ธ์…˜ ์ €์žฅ
  • session_id ์ฟ ํ‚ค ๋ฐœ๊ธ‰
  • ์š”์ฒญ๋งˆ๋‹ค session_id๋กœ ์œ ์ € ์กฐํšŒ
  • ๋กœ๊ทธ์•„์›ƒ ์‹œ ์„ธ์…˜ ์‚ญ์ œ

โœ” ํ”„๋ก ํŠธ์—์„œ ํ•˜๋Š” ์ผ

fetch('/api/login', {
  method: 'POST',
  credentials: 'include'
});
  • ์ฟ ํ‚ค ์ž๋™ ์ „์†ก โ†’ ํ† ํฐ ๊ด€๋ฆฌ ๋ถˆํ•„์š”
  • ๋กœ๊ทธ์•„์›ƒ API๋งŒ ํ˜ธ์ถœํ•˜๋ฉด ๋

7. Opaque Token(๋ถˆํˆฌ๋ช… ํ† ํฐ)

Opaque Token์€ ์˜๋ฏธ๋ฅผ ์•Œ ์ˆ˜ ์—†๋Š” ๋žœ๋ค ๋ฌธ์ž์—ด ํ† ํฐ์ž…๋‹ˆ๋‹ค.

์˜ˆ:

4f8d1b28e92c4a6ca8948d91a3f8a0d7

โœ” ํŠน์ง•

  • ํ† ํฐ ๋‚ด๋ถ€ ์ •๋ณด๋ฅผ ์•Œ ์ˆ˜ ์—†์Œ
  • ๋ฐ˜๋“œ์‹œ ์„œ๋ฒ„ DB์— ์ €์žฅํ•ด ๋งค์นญํ•ด์•ผ ํ•จ
  • refresh_token์—์„œ ๊ฐ€์žฅ ๋งŽ์ด ์‚ฌ์šฉ๋จ
  • OAuth2 ๊ธฐ๋ณธ access_token ํ˜•ํƒœ

โœ” ์™œ ์“ฐ๋Š”๊ฐ€?

  • JWT์ฒ˜๋Ÿผ ๋‚ด๋ถ€ ์ •๋ณด๊ฐ€ ๋…ธ์ถœ๋˜์ง€ ์•Š์•„ ์•ˆ์ „
  • refresh_token์ฒ˜๋Ÿผ โ€œ๋งค์šฐ ๋ฏผ๊ฐํ•œ ํ† ํฐโ€์— ์ ํ•ฉ

โœ” ๋ˆ„๊ฐ€ ๋ฐœ๊ธ‰ํ•˜๋‚˜?

๋ฐฑ์—”๋“œ ์ธ์ฆ ์„œ๋ฒ„๊ฐ€ ์ง์ ‘ ์ƒ์„ฑํ•ด ํด๋ผ์ด์–ธํŠธ์—๊ฒŒ ์ „๋‹ฌํ•ฉ๋‹ˆ๋‹ค.

  1. ์„œ๋ฒ„๊ฐ€ Opaque Token์„ ๋งŒ๋“ ๋‹ค
  2. ์„œ๋ฒ„๋Š” ์ด ํ† ํฐ์„ DB/Redis ๋“ฑ ์ €์žฅ์†Œ์— ์ €์žฅํ•œ๋‹ค
  3. ํด๋ผ์ด์–ธํŠธ๋Š” ์ด ๋ฌธ์ž์—ด์„ ๋“ค๊ณ  ๋‹ค๋‹Œ๋‹ค
  4. ์š”์ฒญ ์˜ฌ ๋•Œ ์„œ๋ฒ„๋Š” DB์—์„œ ํ† ํฐ์„ ์ฐพ์•„์•ผ ์œ ์ € ํ™•์ธ ๊ฐ€๋Šฅ

์˜ˆ)

98de12ccf39a49dbb041a01bdac20345

์„œ๋ฒ„ DB์—๋Š” ์ด๋ ‡๊ฒŒ ์ €์žฅ๋จ:

refresh_token -> userId 123

โœ” JWT๋Š” ์™œ DB๊ฐ€ ํ•„์š” ์—†์„๊นŒ?

ํ† ํฐ ์ข…๋ฅ˜์„œ๋ฒ„์—์„œ DB ํ•„์š”?์ด์œ 
JWTโŒ ํ•„์š” ์—†์Œํ† ํฐ ์•ˆ์— userId/exp/role ๋“ฑ์ด ๋‹ค ๋“ค์–ด์žˆ๊ณ  ์„œ๋ช…์œผ๋กœ ๊ฒ€์ฆ ๊ฐ€๋Šฅ
Opaque Tokenโœ” ํ•„์š”ํ•จ๋‚ด๋ถ€ ์ •๋ณด ์—†์Œ โ†’ ์„œ๋ฒ„๊ฐ€ ์ €์žฅํ•œ ์ •๋ณด์™€ ๋งค์นญํ•ด์•ผ ํ•จ

โ†’ ๊ทธ๋ž˜์„œ refresh_token์„ ์“ฐ๋ฉด

๋…ธ์ถœ๋์„ ๋•Œ ๋‚ด๋ถ€ ์ •๋ณด๊ฐ€ ์—†์œผ๋ฏ€๋กœ ์•ˆ์ „

์„œ๋ฒ„ DB์—์„œ ํ•ด๋‹น ํ† ํฐ๋งŒ ์ง€์šฐ๋ฉด ์ฆ‰์‹œ ๋ฌดํšจํ™” ๊ฐ€๋Šฅ (๋กœ๊ทธ์•„์›ƒ ๊ฐ€๋Šฅ)

JWT refresh_token๋ณด๋‹ค ํ›จ์”ฌ ์ปจํŠธ๋กค์ด ์‰ฌ์›€


8. JWT ๋ง๊ณ ๋„ ์กด์žฌํ•˜๋Š” ์ค‘์š”ํ•œ ํ† ํฐ๋“ค

โœ” ์„ธ์…˜ ํ† ํฐ(Session ID)

  • ์„ธ์…˜ ๋ฐฉ์‹์—์„œ ์‚ฌ์šฉ
  • ์ฟ ํ‚ค์— ์ €์žฅ
  • ๋‚ด๋ถ€ ์ •๋ณด๋Š” ์„œ๋ฒ„์— ์žˆ์Œ

โœ” Opaque Token

  • ๋žœ๋ค ๋ฌธ์ž์—ด
  • OAuth2 ๊ธฐ๋ณธ ํ† ํฐ
  • refresh_token์— ์ฃผ๋กœ ์‚ฌ์šฉ

โœ” OAuth ํ† ํฐ๋“ค

  • access_token (opaque ๋˜๋Š” JWT)
  • refresh_token (opaque)
  • id_token (ํ•ญ์ƒ JWT)

โœ” API Key

  • ์™ธ๋ถ€ ์„œ๋น„์Šค ์ธ์ฆ

โœ” id_token

  • OAuth ๋กœ๊ทธ์ธ์—์„œ ์‚ฌ์šฉ์ž ํ”„๋กœํ•„์„ ๋‹ด๋Š” JWT

โœ… ์–ด๋–ค ๊ฒƒ์„ ์–ด๋””์—์„œ ์ฃผ๋กœ ์“ธ๊นŒ?

๊ฐœ๋…์—ญํ• ์ฃผ๋กœ ์“ฐ๋Š” ๊ณณ
JWTํ† ํฐ ํฌ๋งท(Stateless ์ธ์ฆ์— ์ ํ•ฉ)API ์ธ์ฆ, OAuth id_token
access_token์งง๊ฒŒ ์‚ฌ๋Š” ์ธ์ฆ ํ† ํฐBearer ํ—ค๋”, ์ธ๋ฉ”๋ชจ๋ฆฌ ์ €์žฅ
refresh_tokenaccess_token ์žฌ๋ฐœ๊ธ‰httpOnly ์ฟ ํ‚ค(์„œ๋ฒ„ DB ๊ฒ€์ฆ)
Bearer์ธ์ฆ ์ „๋‹ฌ ๋ฐฉ์‹(ํ—ค๋” ์Šคํ‚ด)๋ชจ๋“  API ์ธ์ฆ ์š”์ฒญ
์ฟ ํ‚ค(httpOnly)๋ฏผ๊ฐ ํ† ํฐ ๋ณด๊ด€/์ž๋™ ์ „์†กrefresh_token, session_id
localStorage / sessionStorageJS ์ ‘๊ทผ ๊ฐ€๋Šฅ Key-Value ์ €์žฅ์†Œ์ผ๋ฐ˜ UI ๊ฐ’, ๋‚ฎ์€ ๋ณด์•ˆ ๋ฐ์ดํ„ฐ
๋ฉ”๋ชจ๋ฆฌ(In-Memory)JS ๋Ÿฐํƒ€์ž„ ์ƒํƒœ ์ €์žฅaccess_token ์ž„์‹œ ๋ณด๊ด€
์„ธ์…˜(SessionID)์„œ๋ฒ„๊ฐ€ ์ƒํƒœ ์ €์žฅํ•˜๋Š” ์ธ์ฆ ๋ฐฉ์‹SSR/์ „ํ†ต ์›น, ์‚ฌ๋‚ด ์‹œ์Šคํ…œ
Opaque Token์„œ๋ฒ„๊ฐ€ DB์— ์ €์žฅํ•˜๋Š” ๋žœ๋ค ํ† ํฐrefresh_token, OAuth2 access_token
API Key์„œ๋น„์Šค/์„œ๋ฒ„ ๊ฐ„ ์ธ์ฆ์™ธ๋ถ€ API, ๋ฐฑ์—”๋“œ ํ†ต์‹ 
id_token์‚ฌ์šฉ์ž ์ •๋ณด(JWT)์†Œ์…œ ๋กœ๊ทธ์ธ(OAuth/OpenID Connect)

๐Ÿ“ ์ „์ฒด ์ธ์ฆ ํ๋ฆ„ ์š”์•ฝ

  1. ์‚ฌ์šฉ์ž๊ฐ€ ๋กœ๊ทธ์ธ ์š”์ฒญ์„ ๋ณด๋ƒ„
  2. ์„œ๋ฒ„๋Š” ๋‹ค์Œ ๋‘ ๊ฐ’์„ ์ƒ์„ฑํ•จ
    • access_token (JWT, ์งง์€ ์ˆ˜๋ช…)
    • refresh_token (Opaque Token, ์žฅ๊ธฐ ์ˆ˜๋ช…)
  3. ์„œ๋ฒ„๋Š” refresh_token์„ httpOnly ์ฟ ํ‚ค๋กœ ๋‚ด๋ ค ์ €์žฅํ•˜๊ฒŒ ํ•จ
  4. ํ”„๋ก ํŠธ๋Š” access_token์„ ๋ฉ”๋ชจ๋ฆฌ(In-Memory) ๋“ฑ์— ๋ณด๊ด€ํ•˜์—ฌ
    API ์š”์ฒญ ์‹œ Bearer ํ—ค๋”๋กœ ์ „์†ก
  5. access_token์ด ๋งŒ๋ฃŒ๋˜๊ฑฐ๋‚˜ ์ƒˆ๋กœ๊ณ ์นจ์œผ๋กœ ์‚ฌ๋ผ์ง€๋ฉด
    ํ”„๋ก ํŠธ๋Š” /auth/refresh ์š”์ฒญ์„ ๋ณด๋‚ด๊ณ 
    ๋ธŒ๋ผ์šฐ์ €๋Š” refresh_token ์ฟ ํ‚ค๋ฅผ ์ž๋™ ์ „์†ก
  6. ์„œ๋ฒ„๋Š” refresh_token์„ ๊ฒ€์ฆํ•ด ์ƒˆ๋กœ์šด access_token์„ ์žฌ๋ฐœ๊ธ‰
  7. ์„ธ์…˜ ๊ธฐ๋ฐ˜ ๋ฐฉ์‹์ด๋ผ๋ฉด, ์„œ๋ฒ„๋Š” session_id๋ฅผ ์ฟ ํ‚ค๋กœ ๋ฐœ๊ธ‰ํ•˜๊ณ 
    ์ดํ›„ ์š”์ฒญ๋งˆ๋‹ค ์ž๋™์œผ๋กœ ์ „์†ก๋˜๋Š” session_id๋กœ ๋กœ๊ทธ์ธ ์ƒํƒœ๋ฅผ ์œ ์ง€

โ†’ ๊ฒฐ๊ณผ์ ์œผ๋กœ ์‚ฌ์šฉ์ž๋Š” ์ƒˆ๋กœ๊ณ ์นจ์„ ํ•˜๊ฑฐ๋‚˜ access_token์ด ๋งŒ๋ฃŒ๋˜๋”๋ผ๋„
refresh_token(์ฟ ํ‚ค) ๋˜๋Š” session_id ๋•๋ถ„์— ๋กœ๊ทธ์ธ ์ƒํƒœ๊ฐ€ ์œ ์ง€๋จ


profile
๋‹จ์ˆœ ์‚ฌ์šฉ์ž๊ฐ€ ์•„๋‹Œ, ์ดํ•ดํ•˜๊ณ  ๋งŒ๋“œ๋Š” ๊ฐœ๋ฐœ์ž๊ฐ€ ๋˜๊ธฐ ์œ„ํ•œ ๊ธฐ๋ก.

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