Skip to main content
์„ธ์…˜๊ณผ JWT ํ† ํฐ์˜ ์ƒ๋ช…์ฃผ๊ธฐ๋ฅผ ํšจ๊ณผ์ ์œผ๋กœ ๊ด€๋ฆฌํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์•Œ์•„๋ด…๋‹ˆ๋‹ค. ์„ธ์…˜ ๊ด€๋ฆฌ๋Š” ์‚ฌ์šฉ์ž ๊ฒฝํ—˜๊ณผ ๋ณด์•ˆ์— ์ง์ ‘์ ์ธ ์˜ํ–ฅ์„ ๋ฏธ์น˜๋Š” ์ค‘์š”ํ•œ ์ฃผ์ œ์ž…๋‹ˆ๋‹ค.

์„ธ์…˜ ๊ด€๋ฆฌ ๊ฐœ์š”

์„ธ์…˜ ์Šคํ† ์–ด

Redis, PostgreSQL ํ™•์žฅ ๊ฐ€๋Šฅํ•œ ์ €์žฅ์†Œ

์„ธ์…˜ ๋งŒ๋ฃŒ

์ž๋™ ๋งŒ๋ฃŒ ์Šฌ๋ผ์ด๋”ฉ ๋งŒ๋ฃŒ

ํ† ํฐ ๊ฐฑ์‹ 

Refresh Token Access Token ์žฌ๋ฐœ๊ธ‰

๋กœ๊ทธ์•„์›ƒ

์„ธ์…˜ ๋ฌดํšจํ™” ํ† ํฐ ๋ธ”๋ž™๋ฆฌ์ŠคํŠธ

์„ธ์…˜ ๊ธฐ๋ฐ˜ ์ธ์ฆ

์„ธ์…˜์˜ ๋™์ž‘ ์›๋ฆฌ

์„ธ์…˜ ๊ธฐ๋ฐ˜ ์ธ์ฆ์€ ์„œ๋ฒ„๊ฐ€ ์‚ฌ์šฉ์ž์˜ ๋กœ๊ทธ์ธ ์ƒํƒœ๋ฅผ ์„œ๋ฒ„ ์ธก์— ์ €์žฅํ•˜๋Š” ๋ฐฉ์‹์ž…๋‹ˆ๋‹ค. ๋™์ž‘ ๋ฐฉ์‹์„ ์ดํ•ดํ•˜๋ฉด ๋ณด์•ˆ๊ณผ ํ™•์žฅ์„ฑ ๋ฌธ์ œ๋ฅผ ๋” ์ž˜ ๋‹ค๋ฃฐ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์„ธ์…˜ ์ธ์ฆ ํ๋ฆ„:
  1. ์‚ฌ์šฉ์ž๊ฐ€ ๋กœ๊ทธ์ธํ•˜๋ฉด ์„œ๋ฒ„๊ฐ€ ์„ธ์…˜ ID๋ฅผ ์ƒ์„ฑ
  2. ์„ธ์…˜ ๋ฐ์ดํ„ฐ(์‚ฌ์šฉ์ž ID ๋“ฑ)๋ฅผ ์„ธ์…˜ ์Šคํ† ์–ด์— ์ €์žฅ
  3. ์„ธ์…˜ ID๋ฅผ ์ฟ ํ‚ค๋กœ ํด๋ผ์ด์–ธํŠธ์— ์ „์†ก
  4. ์ดํ›„ ์š”์ฒญ๋งˆ๋‹ค ์ฟ ํ‚ค์˜ ์„ธ์…˜ ID๋กœ ์‚ฌ์šฉ์ž ์ธ์ฆ
์žฅ์ :
  • ์„œ๋ฒ„๊ฐ€ ์„ธ์…˜์„ ์™„์ „ํžˆ ์ œ์–ด (์–ธ์ œ๋“  ๋ฌดํšจํ™” ๊ฐ€๋Šฅ)
  • ๋ฏผ๊ฐํ•œ ์ •๋ณด๊ฐ€ ํด๋ผ์ด์–ธํŠธ์— ๋…ธ์ถœ๋˜์ง€ ์•Š์Œ
  • ๊ตฌํ˜„์ด ๊ฐ„๋‹จํ•˜๊ณ  ๊ฒ€์ฆ๋œ ๋ฐฉ์‹
๋‹จ์ :
  • ์„œ๋ฒ„์— ์ƒํƒœ๋ฅผ ์ €์žฅํ•˜๋ฏ€๋กœ ํ™•์žฅ์„ฑ ๊ณ ๋ ค ํ•„์š”
  • ์„ธ์…˜ ์Šคํ† ์–ด๊ฐ€ ๋‹จ์ผ ์‹คํŒจ ์ง€์ (SPOF)์ด ๋  ์ˆ˜ ์žˆ์Œ

์„ธ์…˜ ์Šคํ† ์–ด ์„ค์ •

๋ฉ”๋ชจ๋ฆฌ ์Šคํ† ์–ด (๊ฐœ๋ฐœ์šฉ): ๋ฉ”๋ชจ๋ฆฌ ์Šคํ† ์–ด๋Š” ๊ฐœ๋ฐœ ํ™˜๊ฒฝ์—์„œ๋งŒ ์‚ฌ์šฉํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์„œ๋ฒ„๋ฅผ ์žฌ์‹œ์ž‘ํ•˜๋ฉด ๋ชจ๋“  ์„ธ์…˜์ด ์‚ฌ๋ผ์ง€๊ณ , ์—ฌ๋Ÿฌ ์„œ๋ฒ„ ์ธ์Šคํ„ด์Šค ๊ฐ„์— ์„ธ์…˜์„ ๊ณต์œ ํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.
๋ฉ”๋ชจ๋ฆฌ ์Šคํ† ์–ด์˜ ๋ฌธ์ œ์ : - ์„œ๋ฒ„ ์žฌ์‹œ์ž‘ ์‹œ ๋ชจ๋“  ์„ธ์…˜ ์†์‹ค - ์—ฌ๋Ÿฌ ์„œ๋ฒ„ ์ธ์Šคํ„ด์Šค ๊ฐ„ ์„ธ์…˜ ๊ณต์œ  ๋ถˆ๊ฐ€
  • ๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์šฉ๋Ÿ‰ ์ฆ๊ฐ€๋กœ ์„ฑ๋Šฅ ์ €ํ•˜ ๊ฐ€๋Šฅ - ํ”„๋กœ๋•์…˜์—์„œ๋Š” ์ ˆ๋Œ€ ์‚ฌ์šฉ ๊ธˆ์ง€
Redis ์Šคํ† ์–ด (ํ”„๋กœ๋•์…˜): ํ”„๋กœ๋•์…˜ ํ™˜๊ฒฝ์—์„œ๋Š” Redis๋ฅผ ์„ธ์…˜ ์Šคํ† ์–ด๋กœ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ์—…๊ณ„ ํ‘œ์ค€์ž…๋‹ˆ๋‹ค. Redis๋Š” ๋น ๋ฅด๊ณ , ํ™•์žฅ ๊ฐ€๋Šฅํ•˜๋ฉฐ, ์—ฌ๋Ÿฌ ์„œ๋ฒ„ ์ธ์Šคํ„ด์Šค ๊ฐ„ ์„ธ์…˜ ๊ณต์œ ๋ฅผ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค.
Redis ์„ค์ • ์„ค๋ช…:
  • prefix: ์„ธ์…˜ ํ‚ค์— ์ ‘๋‘์‚ฌ๋ฅผ ๋ถ™์—ฌ ๋‹ค๋ฅธ ๋ฐ์ดํ„ฐ์™€ ๊ตฌ๋ถ„
  • ttl: Time To Live, ์„ธ์…˜ ์ž๋™ ๋งŒ๋ฃŒ ์‹œ๊ฐ„ (์ดˆ ๋‹จ์œ„)
  • client: Redis ํด๋ผ์ด์–ธํŠธ ์ธ์Šคํ„ด์Šค
Redis๋Š” ํด๋Ÿฌ์Šคํ„ฐ ๋ชจ๋“œ๋ฅผ ์ง€์›ํ•˜์—ฌ ๊ณ ๊ฐ€์šฉ์„ฑ(HA)์„ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. AWS ElastiCache, Azure Cache for Redis ๋“ฑ์˜ ๊ด€๋ฆฌํ˜• ์„œ๋น„์Šค๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์šด์˜์ด ๋” ํŽธ๋ฆฌํ•ฉ๋‹ˆ๋‹ค.
PostgreSQL ์Šคํ† ์–ด (๋Œ€์•ˆ): ์ด๋ฏธ PostgreSQL์„ ์‚ฌ์šฉ ์ค‘์ด๋ผ๋ฉด ๋ณ„๋„์˜ Redis ์—†์ด PostgreSQL์„ ์„ธ์…˜ ์Šคํ† ์–ด๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋‹จ, ์„ฑ๋Šฅ์€ Redis๋ณด๋‹ค ๋‚ฎ์Šต๋‹ˆ๋‹ค.

์„ธ์…˜ ๋งŒ๋ฃŒ ์ „๋žต

๊ณ ์ • ๋งŒ๋ฃŒ (Fixed Expiration): ์„ธ์…˜์ด ์ƒ์„ฑ๋œ ์‹œ์ ๋ถ€ํ„ฐ ์ •ํ•ด์ง„ ์‹œ๊ฐ„์ด ์ง€๋‚˜๋ฉด ๋ฌด์กฐ๊ฑด ๋งŒ๋ฃŒ๋ฉ๋‹ˆ๋‹ค. ๋ณด์•ˆ์„ฑ์€ ๋†’์ง€๋งŒ ์‚ฌ์šฉ์ž ๊ฒฝํ—˜์ด ์ข‹์ง€ ์•Š์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
์Šฌ๋ผ์ด๋”ฉ ๋งŒ๋ฃŒ (Sliding Expiration): ์‚ฌ์šฉ์ž๊ฐ€ ํ™œ๋™ํ•  ๋•Œ๋งˆ๋‹ค ์„ธ์…˜ ๋งŒ๋ฃŒ ์‹œ๊ฐ„์ด ๊ฐฑ์‹ ๋ฉ๋‹ˆ๋‹ค. โ€œRemember Meโ€ ๊ธฐ๋Šฅ์— ์ ํ•ฉํ•˜๋ฉฐ, ์‚ฌ์šฉ์ž ๊ฒฝํ—˜์ด ์ข‹์Šต๋‹ˆ๋‹ค.
ํ•˜์ด๋ธŒ๋ฆฌ๋“œ ์ „๋žต: ์ดˆ๊ธฐ์—๋Š” ์งง์€ ๋งŒ๋ฃŒ ์‹œ๊ฐ„์„ ์„ค์ •ํ•˜๊ณ , โ€œRemember Meโ€๋ฅผ ์„ ํƒํ•˜๋ฉด ๊ธด ๋งŒ๋ฃŒ ์‹œ๊ฐ„์„ ์ ์šฉํ•ฉ๋‹ˆ๋‹ค.

์„ธ์…˜ ๋ฐ์ดํ„ฐ ๊ด€๋ฆฌ

์„ธ์…˜์—๋Š” ์ตœ์†Œํ•œ์˜ ์ •๋ณด๋งŒ ์ €์žฅํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์„ธ์…˜ ํฌ๊ธฐ๊ฐ€ ์ปค์ง€๋ฉด ์„ฑ๋Šฅ์ด ์ €ํ•˜๋˜๊ณ , ์Šคํ† ๋ฆฌ์ง€ ๋น„์šฉ์ด ์ฆ๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.
์„ธ์…˜ ๋ฐ์ดํ„ฐ ์ ‘๊ทผ:

JWT ํ† ํฐ ๊ด€๋ฆฌ

JWT์˜ ๋™์ž‘ ์›๋ฆฌ

JWT(JSON Web Token)๋Š” ์ƒํƒœ๋ฅผ ์ €์žฅํ•˜์ง€ ์•Š๋Š”(stateless) ์ธ์ฆ ๋ฐฉ์‹์ž…๋‹ˆ๋‹ค. ์„œ๋ฒ„๊ฐ€ ํ† ํฐ์„ ์ƒ์„ฑํ•œ ํ›„์—๋Š” ํ† ํฐ ์ž์ฒด์— ๋ชจ๋“  ์ •๋ณด๊ฐ€ ํฌํ•จ๋˜์–ด ์žˆ์–ด, ์„œ๋ฒ„๊ฐ€ ๋ณ„๋„๋กœ ์ €์žฅํ•  ํ•„์š”๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค. JWT ๊ตฌ์กฐ:
  • Header: ํ† ํฐ ํƒ€์ž…๊ณผ ์•Œ๊ณ ๋ฆฌ์ฆ˜ (์˜ˆ: {"alg": "HS256", "typ": "JWT"})
  • Payload: ์‚ฌ์šฉ์ž ์ •๋ณด์™€ ํด๋ ˆ์ž„ (์˜ˆ: {"userId": 123, "role": "user"})
  • Signature: ํ† ํฐ์˜ ๋ฌด๊ฒฐ์„ฑ์„ ๊ฒ€์ฆํ•˜๋Š” ์„œ๋ช…
JWT์˜ ์žฅ์ :
  • ์„œ๋ฒ„๊ฐ€ ์ƒํƒœ๋ฅผ ์ €์žฅํ•˜์ง€ ์•Š์•„ ํ™•์žฅ์„ฑ์ด ์ข‹์Œ
  • ๋งˆ์ดํฌ๋กœ์„œ๋น„์Šค ํ™˜๊ฒฝ์— ์ ํ•ฉ
  • ํ† ํฐ ์ž์ฒด์— ์ •๋ณด๊ฐ€ ํฌํ•จ๋˜์–ด ์žˆ์–ด ๋ณ„๋„ ์กฐํšŒ ๋ถˆํ•„์š”
JWT์˜ ๋‹จ์ :
  • ํ† ํฐ์„ ์„œ๋ฒ„์—์„œ ๊ฐ•์ œ๋กœ ๋ฌดํšจํ™”ํ•˜๊ธฐ ์–ด๋ ค์›€
  • ํ† ํฐ ํฌ๊ธฐ๊ฐ€ ์ฟ ํ‚ค๋ณด๋‹ค ํผ
  • ํ† ํฐ์ด ํƒˆ์ทจ๋˜๋ฉด ๋งŒ๋ฃŒ ์ „๊นŒ์ง€ ์‚ฌ์šฉ ๊ฐ€๋Šฅ

Access Token๊ณผ Refresh Token

Access Token์€ API ์š”์ฒญ์— ์‚ฌ์šฉ๋˜๋Š” ์งง์€ ์ˆ˜๋ช…์˜ ํ† ํฐ์ž…๋‹ˆ๋‹ค. ๋ณดํ†ต 15๋ถ„~1์‹œ๊ฐ„ ์ •๋„์˜ ์งง์€ ์œ ํšจ๊ธฐ๊ฐ„์„ ๊ฐ€์ง‘๋‹ˆ๋‹ค. ํƒˆ์ทจ๋˜๋”๋ผ๋„ ํ”ผํ•ด๋ฅผ ์ตœ์†Œํ™”ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. Refresh Token์€ Access Token์„ ์žฌ๋ฐœ๊ธ‰๋ฐ›๊ธฐ ์œ„ํ•œ ๊ธด ์ˆ˜๋ช…์˜ ํ† ํฐ์ž…๋‹ˆ๋‹ค. ๋ณดํ†ต 7์ผ~30์ผ ์ •๋„์˜ ์œ ํšจ๊ธฐ๊ฐ„์„ ๊ฐ€์ง€๋ฉฐ, ์„œ๋ฒ„์— ์ €์žฅํ•˜์—ฌ ๊ด€๋ฆฌํ•ฉ๋‹ˆ๋‹ค. ์™œ ๋‘ ๊ฐœ์˜ ํ† ํฐ์„ ์‚ฌ์šฉํ•˜๋‚˜์š”? ๋งŒ์•ฝ Access Token์˜ ์œ ํšจ๊ธฐ๊ฐ„์„ 30์ผ๋กœ ์„ค์ •ํ•˜๋ฉด:
  • ํ† ํฐ์ด ํƒˆ์ทจ๋˜๋ฉด 30์ผ ๋™์•ˆ ์•…์šฉ๋  ์ˆ˜ ์žˆ์Œ
  • ์‚ฌ์šฉ์ž ๊ถŒํ•œ ๋ณ€๊ฒฝ์ด ์ฆ‰์‹œ ๋ฐ˜์˜๋˜์ง€ ์•Š์Œ
Access Token์„ ์งง๊ฒŒ ์„ค์ •ํ•˜๊ณ  Refresh Token์œผ๋กœ ๊ฐฑ์‹ ํ•˜๋ฉด:
  • ํƒˆ์ทจ๋œ Access Token์€ ์งง์€ ์‹œ๊ฐ„๋งŒ ์œ ํšจ
  • ๊ถŒํ•œ ๋ณ€๊ฒฝ์„ ๋น ๋ฅด๊ฒŒ ๋ฐ˜์˜ ๊ฐ€๋Šฅ
  • Refresh Token์€ ์„œ๋ฒ„์— ์ €์žฅํ•˜์—ฌ ๋ฌดํšจํ™” ๊ฐ€๋Šฅ
๋ณด์•ˆ ์ฃผ์˜์‚ฌํ•ญ: - Access Token๊ณผ Refresh Token์˜ Secret์„ ๋ฐ˜๋“œ์‹œ ๋‹ค๋ฅด๊ฒŒ ์„ค์ • - Refresh Token์€ ์„œ๋ฒ„์— ์ €์žฅํ•˜์—ฌ ๋ฌดํšจํ™” ๊ฐ€๋Šฅํ•˜๊ฒŒ ๋งŒ๋“ค๊ธฐ - XSS ๊ณต๊ฒฉ์„ ๋ง‰๊ธฐ ์œ„ํ•ด ํ† ํฐ์„ localStorage ๋Œ€์‹  httpOnly ์ฟ ํ‚ค์— ์ €์žฅ ๊ถŒ์žฅ

Refresh Token ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์Šคํ‚ค๋งˆ

ํ† ํฐ ๊ฐฑ์‹  ๊ตฌํ˜„

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

ํ† ํฐ ๋ฌดํšจํ™” (Logout)

JWT๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ ๋งŒ๋ฃŒ ์ „๊นŒ์ง€ ์œ ํšจํ•˜์ง€๋งŒ, ๋ธ”๋ž™๋ฆฌ์ŠคํŠธ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๊ฐ•์ œ๋กœ ๋ฌดํšจํ™”ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

ํ† ํฐ ๋ธ”๋ž™๋ฆฌ์ŠคํŠธ ๋ฏธ๋“ค์›จ์–ด

์‹ค์ „ ํŒจํ„ด

๋™์‹œ ์ ‘์† ์ œํ•œ

ํ•œ ๊ณ„์ •์œผ๋กœ ์—ฌ๋Ÿฌ ๊ธฐ๊ธฐ์—์„œ ๋™์‹œ ์ ‘์†์„ ์ œํ•œํ•˜๋Š” ๋ฐฉ๋ฒ•์ž…๋‹ˆ๋‹ค.

๋งŒ๋ฃŒ๋œ ์„ธ์…˜ ์ •๋ฆฌ

์ฃผ๊ธฐ์ ์œผ๋กœ ๋งŒ๋ฃŒ๋œ Refresh Token์„ ์‚ญ์ œํ•˜์—ฌ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ํฌ๊ธฐ๋ฅผ ๊ด€๋ฆฌํ•ฉ๋‹ˆ๋‹ค.

์ฃผ์˜์‚ฌํ•ญ

์„ธ์…˜/ํ† ํฐ ๊ด€๋ฆฌ ์ฃผ์˜์‚ฌํ•ญ: 1. ์„ธ์…˜ Secret๊ณผ JWT Secret์€ ํ™˜๊ฒฝ๋ณ€์ˆ˜๋กœ ๊ด€๋ฆฌ 2. ํ”„๋กœ๋•์…˜์—์„œ๋Š” Redis ๋“ฑ ์™ธ๋ถ€ ์„ธ์…˜ ์Šคํ† ์–ด ํ•„์ˆ˜ 3. Refresh Token์€ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์ €์žฅํ•˜์—ฌ ๋ฌดํšจํ™” ๊ฐ€๋Šฅํ•˜๊ฒŒ 4. Access Token์€ ์งง๊ฒŒ, Refresh Token์€ ๊ธธ๊ฒŒ ์„ค์ • 5. HTTPS ์‚ฌ์šฉ ์‹œ์—๋งŒ secure: true ์„ค์ • 6. XSS ๋ฐฉ์ง€๋ฅผ ์œ„ํ•ด httpOnly: true ํ•„์ˆ˜ 7. ๋ธ”๋ž™๋ฆฌ์ŠคํŠธ๋Š” Redis์˜ TTL ๊ธฐ๋Šฅ ํ™œ์šฉ

๋‹ค์Œ ๋‹จ๊ณ„

์ธ์ฆ ์„ค์ •

์ธ์ฆ ์‹œ์Šคํ…œ ๊ตฌ์ถ•

๋ณดํ˜ธ๋œ ๋ผ์šฐํŠธ

Guards๋กœ ๊ถŒํ•œ ์ œ์–ด

Context

Context ์ž์„ธํžˆ ์•Œ์•„๋ณด๊ธฐ

Workflows

์ž‘์—… ์Šค์ผ€์ค„๋ง