ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Node.js] JWT(Json Web Token)๋ฅผ ์ด์šฉํ•œ ๋กœ๊ทธ์ธ ์ธ์ฆ ๊ตฌํ˜„
    ๐Ÿ“˜ Node.js 2026. 7. 14. 01:25

    ๐Ÿ”‘ JWT๋ž€?

    JWT(Json Web Token)๋Š” ์„œ๋ฒ„๊ฐ€ ์‚ฌ์šฉ์ž์˜ ๋กœ๊ทธ์ธ ์ •๋ณด๋ฅผ ์ธ์ฆํ•œ ํ›„ ๋ฐœ๊ธ‰ํ•˜๋Š” ํ† ํฐ(Token) ๊ธฐ๋ฐ˜์˜ ์ธ์ฆ ๋ฐฉ์‹์ด๋‹ค.

    ๊ธฐ์กด์˜ Session ๋ฐฉ์‹์€ ์„œ๋ฒ„๊ฐ€ ๋กœ๊ทธ์ธํ•œ ์‚ฌ์šฉ์ž์˜ ์ •๋ณด๋ฅผ ๋ฉ”๋ชจ๋ฆฌ๋‚˜ DB์— ์ €์žฅํ•˜์—ฌ ๊ด€๋ฆฌํ•˜์ง€๋งŒ,

    JWT๋Š” ๋กœ๊ทธ์ธ ์ •๋ณด๋ฅผ ํ† ํฐ ์•ˆ์— ๋‹ด์•„ ํด๋ผ์ด์–ธํŠธ์—๊ฒŒ ์ „๋‹ฌํ•œ๋‹ค.

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

    ์ฆ‰, ์„œ๋ฒ„๊ฐ€ ๋ณ„๋„์˜ ๋กœ๊ทธ์ธ ์„ธ์…˜์„ ์ €์žฅํ•˜์ง€ ์•Š์•„๋„ ์‚ฌ์šฉ์ž๋ฅผ ์‹๋ณ„ํ•  ์ˆ˜ ์žˆ๋‹ค.

     

    JWT ๋™์ž‘๊ณผ์ •

    JWT ์ธ์ฆ์€ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์ˆœ์„œ๋กœ ๋™์ž‘ํ•œ๋‹ค.

     

    1. ์‚ฌ์šฉ์ž ๋กœ๊ทธ์ธ

    2. ์•„์ด๋”” / ๋น„๋ฐ€๋ฒˆํ˜ธ ์ „์†ก

    3. ์„œ๋ฒ„์—์„œ ์‚ฌ์šฉ์ž ํ™•์ธ

    4. JWT ์ƒ์„ฑ

    5. HttpOnly Cookie ์ €์žฅ

    6. ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ์ดํ›„ ์š”์ฒญ๋งˆ๋‹ค Cookie ์ž๋™ ์ „์†ก

    7. ์„œ๋ฒ„๊ฐ€ JWT ๊ฒ€์ฆ

    8. ์ธ์ฆ ์„ฑ๊ณต ํ›„ ์š”์ฒญ ์ฒ˜๋ฆฌ

     

    ์—ฌ๊ธฐ์—์„œ ์™œ HttpOnly Cookie์— ์ €์žฅ์„ ํ•˜๋Š”์ง€ ๊ฐ„๋žตํžˆ ์•Œ์•„๋ณด์ž.

     

    ๋ณดํ†ต JWT๋Š” ํฌ๊ฒŒ ์„ธ ๊ณณ์— ์ €์žฅ์„ ํ•  ์ˆ˜ ์žˆ๋‹ค.

     

     

    1. LocalStorage

    - ์‚ฌ์šฉ๋ฐฉ์‹

    localStorage.setItem("token", token)

     

    • ๋ธŒ๋ผ์šฐ์ €๋ฅผ ์ข…๋ฃŒํ•ด๋„ ๋‚จ์•„ ์žˆ๋‹ค.
    • ์ปดํ“จํ„ฐ๋ฅผ ์žฌ๋ถ€ํŒ…ํ•ด๋„ ๋‚จ์•„ ์žˆ๋‹ค.
    • ์‚ฌ์šฉ์ž๊ฐ€ ์ง์ ‘ ๋กœ๊ทธ์•„์›ƒํ•˜๊ฑฐ๋‚˜ localStorage.removeItem()์„ ํ˜ธ์ถœํ•ด์•ผ ์‚ญ์ œ๋œ๋‹ค.

    2. SessionStorage

    - ์‚ฌ์šฉ๋ฐฉ์‹

    sessionStorage.setItem("token",token)

     

    • ํ˜„์žฌ ๋ธŒ๋ผ์šฐ์ € ํƒญ์—์„œ๋งŒ ์œ ์ง€๋œ๋‹ค.
    • ํƒญ์„ ๋‹ซ์œผ๋ฉด ์‚ฌ๋ผ์ง„๋‹ค.
    • ๋‹ค๋ฅธ ํƒญ์—์„œ๋Š” ๊ณต์œ ๋˜์ง€ ์•Š๋Š”๋‹ค.

    3. HttpOnly Cookie

    - ์‚ฌ์šฉ๋ฐฉ์‹

    res.cookie("token",token, {
          httpOnly:true,
          maxAge:60*60*1000
    })

     

    ๊ทผ๋ฐ ์—ฌ๊ธฐ์„œ๋Š” 2๊ฐ€์ง€ ๋ฐฉ์‹์ธ Session Cookie, Persistent Cookie ๋กœ ๋‚˜๋‰œ๋‹ค.

    Session Cookie๋Š”

    - res.cookie("token", token, { httpOnly: true })

    maxAge๋‚˜ expires๋ฅผ ์ง€์ •ํ•˜์ง€ ์•Š์œผ๋ฉด, ๋ธŒ๋ผ์šฐ์ €๋ฅผ ์ข…๋ฃŒํ•˜๋Š” ์ˆœ๊ฐ„ ์‚ญ์ œ๋œ๋‹ค.

     

    Persistent Cookie ๋Š”

    - res.cookie("token", token, { httpOnly: true, maxAge: 1000 * 60 * 60 * 24 * 7 })

    ์œ„์™€ ๊ฐ™์ด maxAge๋ฅผ ์„ค์ •ํ•จ์œผ๋กœ์จ 7์ผ๋™์•ˆ ์œ ์ง€๋œ๋‹ค.

     

    ๊ทธ๋ž˜์„œ ์‚ฌ์šฉ๋ฐฉ์‹ ์•„๋ž˜์— ์žˆ๋Š” ์ฝ”๋“œ๋ฅผ ๊ธฐ์ค€์œผ๋กœ๋Š”

    • ๋ธŒ๋ผ์šฐ์ €๋ฅผ ๊ป๋‹ค ์ผœ๋„ 1์‹œ๊ฐ„ ๋™์•ˆ์€ ๋กœ๊ทธ์ธ ์œ ์ง€
    • 1์‹œ๊ฐ„์ด ์ง€๋‚˜๋ฉด Cookie๊ฐ€ ๋งŒ๋ฃŒ๋˜์–ด ๋‹ค์‹œ ๋กœ๊ทธ์ธํ•ด์•ผ ํ•จ
    • JavaScript์—์„œ๋Š” ํ† ํฐ์„ ์ฝ์„ ์ˆ˜ ์—†์Œ
    • ๋ธŒ๋ผ์šฐ์ €๊ฐ€ API ์š”์ฒญ ์‹œ Cookie๋ฅผ ์ž๋™์œผ๋กœ ์ „์†ก

    ์™€ ๊ฐ™์€ ํŠน์ง•์ด ์žˆ๋‹ค.

     

    ๊ฐ€์žฅ ํฐ ๋ฌธ์ œ๋Š” XSS(Cross Site Scripting) ์ด๋‹ค.

    ๋งŒ์•ฝ LocalStorage๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ์ƒํƒœ์—์„œ ์‚ฌ์ดํŠธ์— ์•…์„ฑ ์Šคํฌ๋ฆฝํŠธ๊ฐ€ ์‚ฝ์ž…๋˜๋ฉด

    const token = localStorage.getItem("token")
    ์ด๋ ‡๊ฒŒ JWT๋ฅผ ๊ทธ๋Œ€๋กœ ์ฝ์–ด๊ฐˆ ์ˆ˜ ์žˆ๋‹ค.
    ๋ฌผ๋ก  SessionStorage์—์„œ๋„
    sessionStorage.getItem("token")
     
    ์œ„์™€ ๊ฐ™์€ ํ˜•๋Œ€๋กœ ํƒˆ์ทจ๋‹นํ•˜๊ธฐ ์‰ฌ์šด ๊ฒƒ์€ ๋งˆ์ฐฌ๊ฐ€์ง€์ด๋‹ค.
     
     
    ํ•˜์ง€๋งŒ HttpOnly Cookie๋Š” ๋กœ๊ทธ์ธ์„ ํ•˜๊ฒŒ ๋˜๋ฉด
    res.cookie("token", token, {
        httpOnly: true
    })

     

    ๋ฅผ ๋ณด๋‚ธ๋‹ค.

     

    ๊ทธ๋Ÿฌ๋ฉด ๋ธŒ๋ผ์šฐ์ €๊ฐ€ Cookie๋ฅผ ์ €์žฅํ•˜๋Š”๋ฐ, ์ด Cookie๋Š”

    document.cookie

     

    ์—์„œ๋„ ๋ณด์ด์ง€ ์•Š๋Š”๋‹ค. ์ฆ‰ JavaScript๊ฐ€ ์ ‘๊ทผ ์ž์ฒด๋ฅผ ๋ชปํ•˜๊ธฐ ๋•Œ๋ฌธ์— ํ† ํฐ์„ ํƒˆ์ทจํ•˜๊ธฐ ํ›จ์”ฌ ์–ด๋ ต๋‹ค.

     

    ๋ฌผ๋ก  HttpOnly Cookie๋„ ๋ฌด์ ์€ ์•„๋‹ˆ๋‹ค.

     

    Cookie๋Š” ์ž๋™์œผ๋กœ ์ „์†ก๋˜๊ธฐ ๋•Œ๋ฌธ์— CSRF(Cross-Site Request Forgery) ๊ณต๊ฒฉ์— ๋…ธ์ถœ๋  ์ˆ˜ ์žˆ๋‹ค.

    ๊ทธ๋ž˜์„œ ์‹ค์ œ ์„œ๋น„์Šค์—์„œ๋Š” ๋ณดํ†ต ๋‹ค์Œ๊ณผ ์•„๋ž˜์™€ ๊ฐ™์€ ์˜ต์…˜์„ ํ•จ๊ป˜ ์‚ฌ์šฉํ•œ๋‹ค.

    res.cookie("token", token, {
        httpOnly: true,
        secure: true,
        sameSite: "Strict"
    })
    • httpOnly: JavaScript์—์„œ ์ ‘๊ทผ ๋ถˆ๊ฐ€
    • secure: HTTPS์—์„œ๋งŒ ์ „์†ก
    • sameSite: ๋‹ค๋ฅธ ์‚ฌ์ดํŠธ์—์„œ ์ž๋™ ์ „์†ก ์ œํ•œ(CSRF ์™„ํ™”)

     

    ๋ธŒ๋ผ์šฐ์ €๋Š” ex) localhost:3000 ๋กœ ์š”์ฒญ์„ ๋ณด๋‚ด๊ฒŒ ๋˜๋ฉด, Cookie๋ฅผ ์ž๋™์œผ๋กœ ๊ฐ™์ด ๋ณด๋‚ด๊ฒŒ ๋œ๋‹ค.

     

    ๊ทธ๋ž˜์„œ ์ด์ „์— ์„ค๋ช…ํ•œ

    axios.defaults.withCredentials = true

    ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด๋‹ค.

     

    ์ด ์˜ต์…˜์„ ์ผœ๋ฉด Cookie, JWT ๊ฐ€ ์ž๋™์œผ๋กœ ํ•จ๊ป˜ ์ „์†ก๋œ๋‹ค.

     

    ๊ฒฐ๊ตญ ๋Š˜ ์š”์ฒญ๋•Œ๋งˆ๋‹ค ๋นˆ๋ฒˆํžˆ ๋„ฃ์—ˆ๋˜

    headers: {
        Authorization: ...
    }

     

    ๋ฅผ ์ง์ ‘ ๋„ฃ์ง€ ์•Š์•„๋„ ๋œ๋‹ค.

Designed by Tistory.