ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Vue.js] axios.defaults.withCredentials = true๋Š” ์™œ ์‚ฌ์šฉํ•˜๋Š” ๊ฑธ๊นŒ?
    ๐Ÿ“— Vue.js 2026. 7. 11. 04:52

    JWT ๋กœ๊ทธ์ธ ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„ํ•˜๋‹ค ๋ณด๋ฉด ์ž์ฃผ ๋ณด๊ฒŒ ๋˜๋Š” ์ฝ”๋“œ๊ฐ€ ์žˆ๋‹ค.

    axios.defaults.withCredentials = true;

     

    ์ฒ˜์Œ์—๋Š” "์ฟ ํ‚ค๋ฅผ ๋ณด๋‚ด๊ธฐ ์œ„ํ•œ ์„ค์ •์ธ๊ฐ€?" ์ •๋„๋กœ๋งŒ ์ดํ•ดํ–ˆ์ง€๋งŒ, ์‹ค์ œ๋กœ ๋กœ๊ทธ์ธ ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„ํ•˜๋ฉด์„œ ์ด ์˜ต์…˜์ด ์™œ ํ•„์š”ํ•œ์ง€ ์ •ํ™•ํžˆ ์•Œ๊ฒŒ ๋˜์—ˆ๋‹ค.

     

    ๐Ÿ“— ๋จผ์ € ์•Œ์•„์•ผ ํ•˜๋Š” ๊ฒƒ

    ๋‚˜์˜ ํ”„๋กœ์ ํŠธ์˜ ๊ตฌ์กฐ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

    ํ”„๋ก ํŠธ์—”๋“œ์™€ ๋ฐฑ์—”๋“œ์˜ ํฌํŠธ๊ฐ€ ๋‹ค๋ฅด๊ธฐ ๋•Œ๋ฌธ์— ๋ธŒ๋ผ์šฐ์ € ์ž…์žฅ์—์„œ๋Š” ๋‹ค๋ฅธ Origin(์ถœ์ฒ˜) ์œผ๋กœ ์ธ์‹ํ•œ๋‹ค.

    Origin์€ ๋‹ค์Œ 3๊ฐ€์ง€๊ฐ€ ๋ชจ๋‘ ๊ฐ™์•„์•ผ ํ•œ๋‹ค.

    • ํ”„๋กœํ† ์ฝœ(http/https)
    • ๋„๋ฉ”์ธ(localhost)
    • ํฌํŠธ(5173, 3000)

    ์ฆ‰

    http://localhost:5173

     

    ์—์„œ

    http://localhost:3000

    ์œผ๋กœ ์š”์ฒญํ•˜๋Š” ๊ฒƒ์€ Cross Origin ์š”์ฒญ์ด๋‹ค.

     

    ๐Ÿ’ก ๋กœ๊ทธ์ธ์€ ์–ด๋–ป๊ฒŒ ์ด๋ฃจ์–ด์งˆ๊นŒ?

    ์‚ฌ์šฉ์ž๊ฐ€ ๋กœ๊ทธ์ธ ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅธ๋‹ค.

    Vue
        โ”‚
        โ–ผ
    POST /api/login/auth
        โ”‚
        โ–ผ
    Node ์„œ๋ฒ„

    ๋กœ๊ทธ์ธ์ด ์„ฑ๊ณตํ•˜๋ฉด ์„œ๋ฒ„๋Š” JWT๋ฅผ ์ƒ์„ฑํ•œ๋‹ค.

    const token = jwt.sign(payload, "my_secret_key", {
        expiresIn: "1h"
    });

    ๊ทธ๋ฆฌ๊ณ  ๋ธŒ๋ผ์šฐ์ €์—๊ฒŒ ์ฟ ํ‚ค๋ฅผ ์ €์žฅํ•˜๋ผ๊ณ  ์‘๋‹ตํ•œ๋‹ค.

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

    ๋ธŒ๋ผ์šฐ์ €๋Š” ์‘๋‹ต์„ ๋ฐ›์€ ๋’ค ์ฟ ํ‚ค๋ฅผ ์ €์žฅํ•œ๋‹ค.

    Browser
    
    token = eyJhbGciOiJIUzI1Ni...

    ์—ฌ๊ธฐ๊นŒ์ง€๋Š” ์ •์ƒ์ด๋‹ค.

     

    ๐Ÿงจ ๊ทธ๋Ÿฐ๋ฐ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.

    ๋กœ๊ทธ์ธ ํ›„ ๋ฌธ์„œ ๋ชฉ๋ก์„ ๊ฐ€์ ธ์˜จ๋‹ค๊ณ  ๊ฐ€์ •ํ•ด๋ณด์ž.

    axios.get("http://localhost:3000/api/documents/list")

    ๋Œ€๋ถ€๋ถ„์€ ๋‹น์—ฐํžˆ

    GET /api/documents/list
    
    Cookie:
    token=eyJ...

    ์ด๋ ‡๊ฒŒ ์š”์ฒญ์ด ๊ฐˆ ๊ฒƒ์ด๋ผ๊ณ  ์ƒ๊ฐํ•œ๋‹ค.

    ํ•˜์ง€๋งŒ ์‹ค์ œ๋กœ๋Š”

    GET /api/documents/list
    
    (์ฟ ํ‚ค ์—†์Œ)

    ์œ„์™€ ๊ฐ™์ด ์ „์†ก๋œ๋‹ค.

     

    ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ์ฟ ํ‚ค๋ฅผ ์ผ๋ถ€๋Ÿฌ ๋นผ๋ฒ„๋ฆฌ๋Š” ๊ฒƒ์ด๋‹ค.

     

    ๐Ÿ’‍โ™‚๏ธ ์™œ ์ฟ ํ‚ค๋ฅผ ์•ˆ ๋ณด๋‚ด๋Š” ๊ฑธ๊นŒ?

    ๊ฒฐ๊ตญ์€ ๋ณด์•ˆ ๋•Œ๋ฌธ์ด๋‹ค.

    ๋ธŒ๋ผ์šฐ์ €๋Š” ๋‹ค๋ฅธ Origin์œผ๋กœ ์š”์ฒญํ•  ๋•Œ ์ฟ ํ‚ค๋ฅผ ์ž๋™์œผ๋กœ ๋ณด๋‚ด์ง€ ์•Š๋Š”๋‹ค.

    ๋งŒ์•ฝ ์•„๋ฌด ์‚ฌ์ดํŠธ๋‚˜ ๋‹ค๋ฅธ ์„œ๋ฒ„์˜ ์ฟ ํ‚ค๋ฅผ ๋งˆ์Œ๋Œ€๋กœ ๋ณด๋‚ผ ์ˆ˜ ์žˆ๋‹ค๋ฉด ๋ณด์•ˆ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

    ๊ทธ๋ž˜์„œ ๋ธŒ๋ผ์šฐ์ €๋Š”

     

    "์ด ์š”์ฒญ์€ ๋‹ค๋ฅธ Origin์ธ๋ฐ ์ •๋ง ์ฟ ํ‚ค๋ฅผ ๊ฐ™์ด ๋ณด๋‚ผ ๊ฑฐ์•ผ?"

     

    ๋ผ๊ณ  ํ•œ ๋ฒˆ ๋” ํ™•์ธํ•œ๋‹ค.

    ์ด๋•Œ ์‚ฌ์šฉํ•˜๋Š” ์˜ต์…˜์ด ๋ฐ”๋กœ

    withCredentials

     

    ์ด๋‹ค.

     

    withCredentials๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด

    axios.get(
        "http://localhost:3000/api/documents/list",
        {
            withCredentials: true
        }
    )

    ๋ธŒ๋ผ์šฐ์ €๋Š” ์ด์ œ

    GET /api/documents/list
    
    Cookie:
    token=eyJ...

    ๋ฅผ ํ•จ๊ป˜ ์ „์†กํ•œ๋‹ค.

     

    Node์—์„œ๋Š”

    req.cookies.token

    ์œผ๋กœ JWT๋ฅผ ์ฝ์„ ์ˆ˜ ์žˆ๋‹ค.

     

    axios.defaults.withCredentials = true

    ํ•˜์ง€๋งŒ ๋งค๋ฒˆ

    withCredentials: true

    ๋ฅผ ์ ๋Š” ๊ฒƒ์€ ๋ฒˆ๊ฑฐ๋กญ๋‹ค.

    ๊ทธ๋ž˜์„œ axios์—์„œ๋Š” ๊ธฐ๋ณธ ์„ค์ •์„ ์ œ๊ณตํ•œ๋‹ค.

    axios.defaults.withCredentials = true;

    ์ด ํ•œ ์ค„์„ ์ž‘์„ฑํ•˜๋ฉด

    axios.get(...)
    axios.post(...)
    axios.put(...)
    axios.patch(...)
    axios.delete(...)

    ๋ชจ๋“  ์š”์ฒญ์— ์ž๋™์œผ๋กœ

    withCredentials: true

    ๊ฐ€ ์ ์šฉ๋œ๋‹ค.

     

    ์ฆ‰

    axios.get("/api/list");

    ๋ฅผ ์‹คํ–‰ํ•ด๋„ ๋‚ด๋ถ€์ ์œผ๋กœ๋Š”

    axios.get("/api/list", {
        withCredentials: true
    });

    ์œ„์™€ ๊ฐ™์ด ๋™์ผํ•˜๊ฒŒ ๋™์ž‘ํ•œ๋‹ค.

     

    ๋ฌผ๋ก ! ์„œ๋ฒ„๋„ ์„ค์ •ํ•ด์•ผ ํ•œ๋‹ค.

    ํด๋ผ์ด์–ธํŠธ์—์„œ ์ฟ ํ‚ค๋ฅผ ๋ณด๋‚ด๊ฒ ๋‹ค๊ณ  ํ•ด๋„ ์„œ๋ฒ„๊ฐ€ ํ—ˆ์šฉํ•˜์ง€ ์•Š์œผ๋ฉด ๋ธŒ๋ผ์šฐ์ €๋Š” ์ฟ ํ‚ค๋ฅผ ๋ณด๋‚ด์ง€ ์•Š๋Š”๋‹ค.

    ๊ทธ๋Ÿฌ๊ธฐ์— Express์—์„œ๋Š” CORS ์„ค์ •์—

    app.use(cors({
        origin: "http://localhost:5173",
        credentials: true
    }));

    ๋ฅผ ๋ฐ˜๋“œ์‹œ ์ถ”๊ฐ€ํ•ด์•ผ ํ•œ๋‹ค.

     

    ์—ฌ๊ธฐ์„œ

    credentials: true

    ๋Š”

     

    "์ด Origin์—์„œ ์ฟ ํ‚ค๋ฅผ ํฌํ•จํ•œ ์š”์ฒญ์„ ํ—ˆ์šฉํ•œ๋‹ค."

     

    ๋ผ๋Š” ์˜๋ฏธ์ด๋‹ค.

     

    ๐ŸŽˆ ์ „์ฒด ๋กœ๊ทธ์ธ ํ๋ฆ„

    ์‚ฌ์šฉ์ž ๋กœ๊ทธ์ธ
            โ”‚
            โ–ผ
    Vue (axios.post)
            โ”‚
            โ–ผ
    Node ๋กœ๊ทธ์ธ ์ฒ˜๋ฆฌ
            โ”‚
            โ–ผ
    JWT ์ƒ์„ฑ
            โ”‚
            โ–ผ
    res.cookie("token", token)
            โ”‚
            โ–ผ
    ๋ธŒ๋ผ์šฐ์ €์— ์ฟ ํ‚ค ์ €์žฅ
            โ”‚
            โ–ผ
    axios ์š”์ฒญ ๋ฐœ์ƒ
            โ”‚
            โ–ผ
    axios.defaults.withCredentials = true
            โ”‚
            โ–ผ
    ๋ธŒ๋ผ์šฐ์ €๊ฐ€ Cookie๋ฅผ ํ•จ๊ป˜ ์ „์†ก
            โ”‚
            โ–ผ
    Node
    req.cookies.token
            โ”‚
            โ–ผ
    jwt.verify()
            โ”‚
            โ–ผ
    ๋กœ๊ทธ์ธ ์‚ฌ์šฉ์ž ํ™•์ธ
            โ”‚
            โ–ผ
    API ์‹คํ–‰

     

    ์ตœ์ข…์ •๋ฆฌ

    axios.defaults.withCredentials = true๋Š” JWT๋ฅผ ์ƒ์„ฑํ•˜๋Š” ์ฝ”๋“œ๋„ ์•„๋‹ˆ๊ณ , ์ฟ ํ‚ค๋ฅผ ์ €์žฅํ•˜๋Š” ์ฝ”๋“œ๋„ ์•„๋‹ˆ๋‹ค.

    ์ด ์˜ต์…˜์˜ ์—ญํ• ์€ ๊ทธ์ € ๋ธŒ๋ผ์šฐ์ €์— ์ €์žฅ๋˜์–ด ์žˆ๋Š” ์ฟ ํ‚ค๋ฅผ ์ดํ›„์˜ ๋ชจ๋“  Axios ์š”์ฒญ์— ํ•จ๊ป˜ ์ „์†กํ•˜๋„๋ก ๋ธŒ๋ผ์šฐ์ €์— ์•Œ๋ ค์ฃผ๋Š” ๊ฒƒ์ด๋‹ค.

    JWT๋ฅผ HttpOnly ์ฟ ํ‚ค ๋ฐฉ์‹์œผ๋กœ ๊ด€๋ฆฌํ•˜๋Š” ํ”„๋กœ์ ํŠธ์—์„œ๋Š” ๊ฑฐ์˜ ํ•„์ˆ˜์ ์œผ๋กœ ์‚ฌ์šฉํ•˜๋Š” ์„ค์ •์ด๋ฉฐ, ์„œ๋ฒ„์˜ cors({ credentials: true }) ์„ค์ •๊ณผ ํ•จ๊ป˜ ์‚ฌ์šฉํ•ด์•ผ ์ •์ƒ์ ์œผ๋กœ ์ธ์ฆ์ด ๋™์ž‘ํ•œ๋‹ค.

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

Designed by Tistory.