์ฟ ํ‚ค๊ฐ€ ๋ฐœ๊ธ‰๋˜์ง€ ์•Š์„ ๋•Œ, Access-Control-Allow-Origin ์˜ค๋ฅ˜ ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•

์ฟ ํ‚ค๊ฐ€ ๋ฐœ๊ธ‰๋˜์ง€ ์•Š์„ ๋•Œ

KOS ํ”„๋กœ์ ํŠธ๋Š” ํ”„๋ก ํŠธ์—”๋“œ์— React.js๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ์„œ๋ฒ„๋Š” go lang ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ gin์„ ์ด์šฉํ•˜์—ฌ ์ง„ํ–‰์ค‘์ด๋‹ค. ์ฒ˜์Œ์— api๋ฅผ ๋งŒ๋“ค๊ณ  ๋‚˜์„œ๋Š” postman์ด๋ผ๋Š” ํˆด๋กœ ์ œ๋Œ€๋กœ ๋™์ž‘ํ•˜๋Š”์ง€ ๊ฒ€์‚ฌํ•œ๋‹ค. ์ฒ˜์Œ์—๋Š” api์—ฐ๊ฒฐ์ด ์ˆœ์กฐ๋กญ๊ฒŒ ๋˜๋‹ค๊ฐ€ ๋กœ๊ทธ์ธ์„ ์œ„ํ•ด์„œ ์ฟ ํ‚ค๋ฅผ ๋ฐœ๊ธ‰ํ•˜๋Š” api๋ฅผ ์ž‘์„ฑํ•œ ์ดํ›„์— ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ–ˆ๋‹ค.

 

์ฟ ํ‚ค๊ฐ€ ๋ฐœ๊ธ‰๋˜์ง€ ์•Š๋Š”๋‹ค.

ํ”„๋ก ํŠธ์—”๋“œ์—์„œ ๋ฐ›์€ Network๋„ 200์œผ๋กœ ์ •์ƒ์ด๊ณ  set-cookie๋˜๋Š” ๊ฒƒ๊นŒ์ง€ ๋ณด์ด๋ฉฐ response๋ฅผ ์ถœ๋ ฅํ•ด๋ณด์•„๋„ ์ •์ƒ์ด๋‹ค. ๊ทธ๋ ‡๋‹ค๋ฉด ์™œ ์ฟ ํ‚ค๊ฐ€ ๋ฐœ๊ธ‰๋˜์ง€ ์•Š์„๊นŒ?

๋ฐฑ์—”๋“œ์—์„œ ํ”„๋ก ํŠธ์—”๋“œ์— ์ฟ ํ‚ค๋ฅผ ์ €์žฅ์‹œํ‚ค๊ธฐ ์œ„ํ•ด์„œ๋Š” ์–‘์ชฝ์˜ credential ์†์„ฑ์„ ๋ชจ๋‘ ON ํ•ด์ฃผ์–ด์•ผํ•˜๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

๋จผ์ € ๋ฐฑ์—”๋“œ(go lang)์˜ ์ฝ”๋“œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

 

// router

import {
    "github.com/gin-gonic/gin"
    "github.com/gin-contrib/cors"
}

r := gin.Default()

config := cors.DefaultConfig()
config.AllowOrigins = []string{"http://localhost:3000", "http://127.0.0.1:3000"} // origin ํ—ˆ์šฉ
config.AllowCredentials = true // credential ํ—ˆ์šฉ

r.Use(cors.New(config))

// Controller
c.SetCookie("access-token", signed, 60*60, "/", "", false, true)

router ๋‹จ์—์„œ config๋กœ ํ—ˆ์šฉํ•˜๋Š” origin์— ํ”„๋ก ํŠธ์—”๋“œ์˜ origin์„ ์ถ”๊ฐ€ํ•ด์ค€๋‹ค. ๋˜ํ•œ AllowCredential๋„ true๋กœ ๋ณ€๊ฒฝํ•˜์—ฌ ํ—ˆ์šฉํ•ด์ค€๋‹ค.

์—ฌ๊ธฐ์„œ AllowOrigins๋ฅผ *(์™€์ผ๋“œ์นด๋“œ)๋กœ ์ฒ˜๋ฆฌํ•˜๋ฉด ์•ˆ๋˜๋Š”๋ฐ ์ด์œ ๋Š” [์ฃผ์˜ํ•  ์ ](#์ฃผ์˜ํ•  ์ )์— ๊ธฐ์žฌ๋˜์–ด์žˆ๋‹ค.

 

Controller์—์„œ ์ฟ ํ‚ค๋ฅผ ๋ฐœ๊ธ‰ํ•˜๋Š” ์ฝ”๋“œ๋Š” *gin.Context ๊ฐ์ฒด์ธ c์˜ SetCookie๋ฅผ ์ด์šฉํ•˜์˜€๋‹ค.

 

func (c *Context) SetCookie(name, value string, maxAge int, path, domain string, secure, httpOnly bool)

์ถœ์ฒ˜ : https://pkg.go.dev/github.com/gin-gonic/gin

httpOnly ์†์„ฑ์€ true๋กœ ํ•ด์ฃผ์–ด์•ผ ์œ ์ €๊ฐ€ ์ฟ ํ‚ค๋ฅผ ์ง์ ‘ ์ˆ˜์ •ํ•  ์ˆ˜ ์—†๋‹ค.

 

๊ทธ๋ž˜๋„ ์ฟ ํ‚ค๊ฐ€ ๋ฐœ๊ธ‰๋˜์ง€ ์•Š๋Š” ์ด์œ ๋Š” ํ”„๋ก ํŠธ์—”๋“œ ๋‹จ์—์„œ๋„ credential์„ ONํ•ด์ฃผ์–ด์•ผํ•˜๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

 

const login = () => {
        // ๋กœ๊ทธ์ธ
    axios.post('http://localhost:8080/v1/user-api/login', {
        ID: 'heejin',
        Password: '1234'
    }, { withCredentials: true }) // withCredential ์˜ต์…˜ ON
        .then((res) => {
        // ํ† ํฐ ๋ฐœ๊ธ‰๋จ
        console.dir(res);
        window.location.href = `${window.origin}/home`; // home์œผ๋กœ redirect
    })
        .catch((e) => {
        console.dir(e);
    });
};

axios์—์„œ ์ œ๊ณตํ•˜๋Š” withCredentials ์˜ต์…˜์„ true๋กœ ์„ค์ •ํ•˜๋‹ˆ, ๋ฐฑ์—”๋“œ ์ฝ”๋“œ์—์„œ setCookie๋ฅผ ์ •์ƒ์ ์œผ๋กœ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋˜์—ˆ๊ณ  ํ† ํฐ์ด ์ •์ƒ์ ์œผ๋กœ ์ €์žฅ๋˜์—ˆ๋‹ค.

axios ์‚ฌ์ดํŠธ๋ฅผ ๋ณด๋ฉด ๊ธฐ๋ณธ ๊ฐ’์œผ๋กœ withCredentials ์†์„ฑ์ด false๋กœ ๋˜์–ด์žˆ์Œ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค. ๋”ฐ๋ผ์„œ ์ง์ ‘ true๋กœ ๋ณ€๊ฒฝํ•ด์ฃผ์–ด์•ผ ํ•œ๋‹ค.

 

// `withCredentials`๋Š” ์ž๊ฒฉ ์ฆ๋ช…(credentials)์„ ์‚ฌ์šฉํ•˜์—ฌ
// ํฌ๋กœ์Šค ์‚ฌ์ดํŠธ ์ ‘๊ทผ ์ œ์–ด(cross-site Access-Control) ์š”์ฒญ์ด ํ•„์š”ํ•œ ๊ฒฝ์šฐ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.
withCredentials: false, // ๊ธฐ๋ณธ ๊ฐ’

์ด๋Š” MDN Web Docs์‚ฌ์ดํŠธ๋ฅผ ์ฐธ์กฐํ•ด๋ณด๋ฉด XMLHttpRequest ๊ฐ์ฒด์˜ withCredentials ๊ธฐ๋Šฅ์„ ๊ทธ๋Œ€๋กœ ์ด์šฉํ•œ ๊ฒƒ์œผ๋กœ ๋ณผ ์ˆ˜ ์žˆ๋‹ค. ์ด ๊ธฐ๋Šฅ์€ boolean ๊ฐ’์œผ๋กœ ์„ธํŒ…๋˜์–ด cookie์™€ tls ์ธ์ฆ์„œ์™€ ๊ฐ™์€ ์ธ์ฆ ์š”์†Œ๋ฅผ ์„œ๋ฒ„์—์„œ ์„ค์ •ํ•  ์ˆ˜ ์žˆ๋Š”์ง€๋ฅผ ๊ฒฐ์ •ํ•œ๋‹ค.

 

๊ฒฐ๋ก ์ ์œผ๋กœ, ํ”„๋ก ํŠธ์—”๋“œ ์ฝ”๋“œ์˜ credential ์˜ต์…˜๊ณผ ์„œ๋ฒ„์˜ credential ์˜ต์…˜์„ ๋ชจ๋‘ ON ํ•ด์ฃผ๋ฉด ์ฟ ํ‚ค๋ฅผ setting ํ•  ์ˆ˜ ์žˆ๋‹ค. 

 

์ฃผ์˜ํ•  ์ 

๊ทธ๋Ÿผ์—๋„ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์˜ค๋ฅ˜๊ฐ€ ๋ธŒ๋ผ์šฐ์ €์— ๋œจ๋Š” ๊ฒฝ์šฐ๊ฐ€ ์žˆ๋‹ค.

XMLHttpRequest cannot load http://localhost:8080/v1/user-api/login. The value of the 'Access-Control-Allow-Origin' header in the response must not be the wildcard '*' when the request's credentials mode is 'include'. Origin 'http://localhost:3000' is therefore not allowed access. The credentials mode of requests initiated by the XMLHttpRequest is controlled by the withCredentials attribute.

ํ•ด์„ํ•ด๋ณด๋ฉด ์„œ๋ฒ„์—์„œ credential์„ true๋กœ ํ•  ๊ฑฐ๋ฉด, ๋ฐ›๋Š” allow-origin์„ ์™€์ผ๋“œ์นด๋“œ์ธ *๋กœ ์„ธํŒ…ํ•˜์ง€ ๋ง๊ณ  ์ง์ ‘ ์„ธํŒ…ํ•˜๋ผ๋Š” ๊ฒƒ์ด๋‹ค. ์ƒ๊ฐํ•ด๋ณด๋ฉด ๋‹น์—ฐํ•œ ์–˜๊ธฐ์ด๋‹ค. AllowOrigins๊ฐ€ *๋กœ ์„ธํŒ…๋˜์–ด์žˆ์œผ๋ฉด ์„œ๋ฒ„์— credential์„ true๋กœ ํ•˜์—ฌ ์š”์ฒญํ•œ ๋ชจ๋“  ์‚ฌ์ดํŠธ์˜ credential์˜ ์ •๋ณด๋ฅผ ์„œ๋ฒ„๊ฐ€ ๋ง˜๋Œ€๋กœ ๋ฐ”๊ฟ€ ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

config.AllowOrigins = []string{"http://localhost:3000", "http://127.0.0.1:3000"} // origin ํ—ˆ์šฉ

๋”ฐ๋ผ์„œ ์œ„์˜ ์ฝ”๋“œ์™€ ๊ฐ™์ด origin๋„ ์ œํ•œํ•ด์ฃผ๋ฉด ์ œ๋Œ€๋กœ ๋™์ž‘ํ•œ๋‹ค.


์œ„์—์„œ ๊ณ„์† ์–ธ๊ธ‰๋œ same site, cross-site์— ๋Œ€ํ•ด์„œ ๊ถ๊ธˆํ•˜๋‹ค๋ฉด ๋‹ค์Œ ํฌ์ŠคํŒ…์„ ์ฐธ์กฐ!

SOP, CORS

axios๋‚˜ fetch, XMLhttpRequest์— ๋Œ€ํ•ด ๊ถ๊ธˆํ•˜๋‹ค๋ฉด ๋‹ค์Œ ํฌ์ŠคํŒ…์„ ์ฐธ์กฐ!

Ajax ๊ธฐ์ˆ 

 

๋ฐ˜์‘ํ˜•