๐ ๊ฐ์
Indp ํ๋ก์ ํธ์์ ์๋ฒ์ ํด๋ผ์ด์ธํธ๋ฅผ ์ฐ๋ํ๋ฉด์ CORS ์๋ฌ๊ฐ ๋ฐ์ํ์๋ค. ํด๋ผ์ด์ธํธ๋ AWS CloudFront, ์๋ฒ๋ AWS EC2 ๋ก ๋ฐฐํฌํด ๋์ ์ํ์๋ค. CORS ์๋ฌ๋ ์ ๋ฐ์ํ๊ณ ์ด๋ป๊ฒ ํด๊ฒฐํ๋์ง ํ์ตํด๋ณด๊ณ ์ ์ฉํด๋ณด์.
๐ CORS
Cross-Origin Resource Sharing ๋ฅผ ์๋ฏธํ๋ฉฐ ์์ฒญ ๋ธ๋ผ์ฐ์ ์์ ๋ค๋ฅธ ์ถ์ฒ๋ฅผ ๊ฐ๋ ์๋ฒ๋ก ์์ฒญ์ด ๊ฐ ๋ ๋ธ๋ผ์ฐ์ ์์ ๋ฐ์ํ๋ ๋ณด์ ์ ์ฑ ์ด๋ค. ์ฌ๊ธฐ์ ํฌ์ธํธ๋ ๋ค๋ฅธ ์ถ์ฒ, ๋ธ๋ผ์ฐ์ ์์ ๋ฐ์, ๋ณด์ ์ ์ฑ ์ด๋ค. ๊ฐ๊ฐ์ด ์๋ฏธํ๋ ๊ฒ์ด ๋ฌด์์ธ์ง ์์๋ณด์.
๋ค๋ฅธ ์ถ์ฒ
URL ์์ ์ถ์ฒ๋ ํ๋กํ ์ฝ, ํธ์คํธ, ํฌํธ ๋ฒํธ๋ฅผ ์๋ฏธํ๊ณ ์ด ์ค ๋์ผํ์ง ์์ ๊ฒ์ด ์๋ค๋ฉด ์ถ์ฒ๊ฐ ๋ค๋ฅธ ๊ฒ์ด๋ค. ๋ค๋ฅธ ์ถ์ฒ์ ์์๋ ๋ค์๊ณผ ๊ฐ๋ค.
- ํ๋กํ ์ฝ http โ https
- ํธ์คํธ www.aaa.com โ www.api.aaa.com
- ํฌํธ ๋ฒํธ www.aaa.com:8080 โ www.aaa.com:3000
๋ธ๋ผ์ฐ์ ์์ ๋ฐ์
CORS ์๋ฌ๋ ๋ธ๋ผ์ฐ์ ์ ์ํด ๋ฐ์ํ๋ ์๋ฌ์ด๋ค. ๋ฐ๋ผ์ ์๋ฒ to ์๋ฒ ํต์ ์์๋ CORS ์๋ฌ๊ฐ ๋ฐ์ํ์ง ์๋๋ค.
๋ณด์ ์ ์ฑ
์น ์ํ๊ณ๋ SOP(Same-Origin Policy) ๋ผ๋ ์ ์ฑ ์ด ์กด์ฌํ๋ค. ์ด๋ฆ์์๋ ์ ์ ์๋ฏ์ด SOP ๋ ๊ฐ์ ์ถ์ฒ์ ๋ฆฌ์์ค๋ง ๊ณต์ ํ๋๋ก ์ ํํ๋ ์ ์ฑ ์ด๋ค. ๊ทธ๋ ๋ค๋ฉด ๋ค๋ฅธ ์ถ์ฒ์ ๋ฆฌ์์ค๋ฅผ ์ ํํ๋ ์ด์ ๋ ๋ฌด์์ผ๊น?
๋ธ๋ผ์ฐ์ ์๋ ํ ํฐ๊ณผ ๊ฐ์ ์ธ์ฆ ์ ๋ณด๋ค์ด ์ ์ฅ๋ ์ ์๋ค. ์ด ์ธ์ฆ ์ ๋ณด๋ค์ ํด๋น ์ฌ์ดํธ๋ก ์์ฒญ์ ๋ณด๋ผ ๋๋ง๋ค ํจ๊ป ๋ณด๋ด์ง๋ค. ์ฌ๊ธฐ์ ๋ฌธ์ ๋ ์ ๋ถ์ฆ๊ณผ ๊ฐ์ ์ค์ํ ์ธ์ฆ ์ ๋ณด๊ฐ ๋ธ๋ผ์ฐ์ ์ ์ ์ฅ๋๋ค๋ ๊ฒ์ด๋ค. ๋ง์ฝ ์ฌ์ฉ์๊ฐ ๋ก๊ทธ์ธ์ ํ์ฌ ํ ํฐ์ด ๋ธ๋ผ์ฐ์ ์ ์ ์ฅ๋ ์ํ์์ ํผ์ฑ ๋ฉ์ผ์ด๋ ์ ์ฑ ์ฌ์ดํธ๋ฅผ ํตํด ์ฌ์ฉ์์ ๋ธ๋ผ์ฐ์ ์์ ์ ์์ ์ธ ์ฝ๋๊ฐ ์คํ๋๊ฒ ํ๋ค๊ณ ๊ฐ์ ํด๋ณด์. SOP ์ ์ฑ ์ด ์๋ค๋ฉด ์๋ก ๋ค๋ฅธ ์ถ์ฒ ๋ผ๋ฆฌ ๋ฐ์ดํฐ๋ฅผ ์ฃผ๊ณ ๋ฐ์ ์ ์๊ฒ ๋๊ณ ๋ธ๋ผ์ฐ์ ์ ์ ์ฅ๋ ํ ํฐ์ผ๋ก ํด๋น ์ฌ์ดํธ์์ ์ฌ์ฉ์์ ์ ๋ณด๋ฅผ ์กฐํ ํ ์ ์๊ฒ ๋ ๊ฒ์ด๋ค. ์ด์ ๊ฐ์ ์ํ์ ๋ฐฉ์งํ๋ ๊ฒ์ด SOP ์ ์ฑ ์ด๋ค.
๐ CORS ์์ฒญ์ ์ข ๋ฅ
Simple Request
- GET, POST, HEAD ๋ฉ์๋ ์ค ํ๋๋ฅผ ์ฌ์ฉํ๋ ์์ฒญ
- Accept, Accept-Language, Content-Language, Content-Type, DPR, Downlink, Save-Data, Viewport-Width, Width ํค๋์ผ ๊ฒฝ์ฐ
- Content-Type ํค๋๊ฐ application/x-www-form-urlencoded, multipart/form-data, text/plain ์ค ํ๋์ผ ๊ฒฝ์ฐ
- ํด๋ผ์ด์ธํธ๊ฐ ๋ค๋ฅธ ์ถ์ฒ๋ก ์์ฒญ์ ๋ณด๋ผ ๋ ๋ธ๋ผ์ฐ์ ๊ฐ ์์ฒญ ๋ฉ์์ง์ Origin ํค๋ (ํ์ฌ ์ฌ์ดํธ์ ํ๋กํ ์ฝ + ํธ์คํธ + ํฌํธ)๋ฅผ ์ถ๊ฐ
- ์๋ฒ๋ Access-Control-Allow-Origin ํค๋์ ํ์ฉํ๋ Origin ์ ๋ช ์
- ์๋ต์ ๋ฐ์ ๋ธ๋ผ์ฐ์ ๋
- Access-Control-Allow-Origin ๊ฐ์ด ์์ฒญ์ ๋ณด๋ธ Origin ๊ณผ ๋ค๋ฅด๋ฉด ์๋ต์ ์ฌ์ฉํ์ง ์๊ณ ๋ฒ๋ฆฌ๊ณ CORS ์๋ฌ ๋ฐ์
- Access-Control-Allow-Origin ๊ฐ์ด ์์ฒญ์ ๋ณด๋ธ Origin ๊ณผ ๊ฐ๋ค๋ฉด ์๋ต ๋ฐ์ดํฐ๋ฅผ ํด๋ผ์ด์ธํธ๋ก ์ ๋ฌ
Preflight Request
- Simple Request ์กฐ๊ฑด์ ๋ง์กฑํ์ง ์๋ ์์ฒญ
1. ๋ธ๋ผ์ฐ์ ๋ ๋ด๋ถ์ ์ผ๋ก ์ง์ง ์์ฒญ์ ๋ณด๋ด๊ธฐ ์ ์ OPTIONS ๋ผ๋ ๋ฉ์๋๋ก preflight ์์ฒญ์ ๋ณด๋ด์ CORS ์ ๊ด๋ จํ์ฌ ์๋ฒ์๊ฒ ํด๋น ์์ฒญ์ ๋ณด๋ด๋ ๋๋์ง ๋ฌผ์ด๋ด
OPTIONS /resource
Access-Control-Request-Method: ์์ฒญ์์ ์ฌ์ฉ๋ ๋ฉ์๋
Origin: ์์ฒญ์ ๋ณด๋ธ ์ถ์ฒ
2. ์๋ฒ๋ ํ์ฉ๋๋ Origin, Method ๋ฑ์ ์๋ต
HTTP/1.1 204 No Content
Access-Control-Allow-Origin: ํ์ฉ๋๋ ์ถ์ฒ
Access-Control-Allow-Methods: ํ์ฉ๋๋ ๋ฉ์๋
Access-Control-Max-Age: preflight ์์ฒญ์ ์บ์ฑํ ์๊ฐ(์ด)
3. ์๋ต์ ๋ฐ์ ๋ธ๋ผ์ฐ์ ๋ ์๋ต ํค๋ ๊ฐ์ด ์์ฒญ์ ๋ณด๋ธ Origin ๊ณผ ๋ค๋ฅด๋ฉด ์ง์ง ์์ฒญ์ ๋ณด๋ด์ง ์๊ณ CORS ์ค๋ฅ ๋ฐ์
Credential Request
- ํด๋ผ์ด์ธํธ์์ ์๋ฒ์๊ฒ ์๊ฒฉ ์ธ์ฆ ์ ๋ณด๋ฅผ ์ค์ด ์์ฒญํ ๊ฒฝ์ฐ
- ์๊ฒฉ ์ธ์ฆ ์ ๋ณด ์ฟ ํค, Authorization ํค๋ ๋ฑ
- ํด๋ผ์ด์ธํธ์์ withCredentials ์ต์ ์ true ๋ก ์ค์ ํด์ผ ์ธ์ฆ ๊ด๋ จ ํค๋๋ฅผ ์์ฒญ์ ์ค์ด ๋ณด๋ผ ์ ์์
- ์๋ฒ์์ Access-Control-Allow-Credentials ํค๋๋ฅผ true ๋ก ์ค์ ํด์ผ ์๋ต ๋ฐ์ดํฐ๋ฅผ ํด๋ผ์ด์ธํธ๋ก ์ ๋ฌํ ์ ์์
๐ CORS ํด๊ฒฐ ๋ฐฉ๋ฒ
CORS ํด๊ฒฐ์ฑ ์ ์๋ฒ์ ์๋ต ํค๋์ธ Access-Control-Allow-Origin ์ ๋ฌ๋ ธ๋ค. ์๋ฒ์์ ํด๋ผ์ด์ธํธ์ ์์ฒญ์ ํ์ฉํ ์ง ๋ง์ง ๊ฒฐ์ ํ๋ ๊ฒ์ด๋ค.
- ํด๋ผ์ด์ธํธ์์ ์๋ฒ๋ก ์์ฒญ์ ๋ณด๋ด๋ ๋์ ํด๋ผ์ด์ธํธ์์ ํ๋ก์ ์๋ฒ๋ก ์์ฒญ์ ๋ณด๋ด๊ณ ํ๋ก์ ์๋ฒ์์ ์๋ฒ๋ก ์์ฒญ์ ๋ณด๋ด๋ ๋ฐฉ๋ฒ
- ์๋ฒ์์ CORS ๊ด๋ จ ์๋ต ํค๋๋ฅผ ์ค์
๐ ์ ์ฉ ํด๋ณด๊ธฐ
CORS ์๋ฌ๋ ๋ฌด์์ด๊ณ ์ ๋ฐ์ํ๋ฉฐ ์ด๋ป๊ฒ ํด๊ฒฐํ๋ ์ง๋ฅผ ์์๋ดค๋ค. ์ด์ ํ๋ก์ ํธ์์ ๋ฐ์ํ CORS ์๋ฌ๋ฅผ ํด๊ฒฐํด๋ณด์.
๐จ CORS ์๋ฌ ๋ฐ์
ํด๋ผ์ด์ธํธ Origin ์ธ https://verby.co.kr ๊ณผ ์๋ฒ์ Origin์ธ https://api.verby.co.kr ์ด ๋ฌ๋ผ ๋ค์๊ณผ ๊ฐ์ด CORS ์๋ฌ๊ฐ ๋ฐ์ํ์๋ค. ๋ธ๋ผ์ฐ์ ๋ ์๋ต ํค๋์ธ Access-Control-Allow-Credentials ๋ฅผ ๋ณด๊ณ ํ์ฌ์ Origin ์ด ํ์ฉ๋ Origin ์ธ์ง ํ์ธํด์ผํ๋๋ฐ ํด๋น ํค๋๊ฐ ์กด์ฌํ์ง ์์ CORS ์๋ฌ๊ฐ ๋ฐ์ํ์๋ค.


โ Nginx ๋ฅผ ํตํ ์๋ต ํค๋ ์ค์
์๋ฒ์ ์๋ต ํค๋์ธ Access-Control-Allow-Origin ์ 'https://verby.co.kr' ๋ก ์ค์ ํ์ฌ ๋ธ๋ผ์ฐ์ ์๊ฒ https://verby.co.kr ๊ฐ ํ์ฉ๋ Origin ์์ ์๋ ค์ฃผ์.
location / {
if ($request_method = 'OPTIONS') {
add_header 'Access-Control-Allow-Origin' 'https://verby.co.kr';
add_header 'Access-Control-Allow-Methods' 'GET, POST, DELETE, PUT, PATCH, OPTIONS';
add_header 'Access-Control-Allow-Headers' 'Content-Type';
add_header 'Access-Control-Max-Age' 86400;
return 204;
}
add_header 'Access-Control-Allow-Origin' 'https://verby.co.kr' always;
proxy_pass http://localhost:8080/;
}
- Preflight Request ์ ์๋ต ํค๋ ์ค์ (Method: OPTIONS)
- Simple Request ์ ์๋ต ํค๋ ์ค์ (always ์ค์ ์ ํ์ฌ ์คํจํ ์์ฒญ์ ๊ฒฝ์ฐ์๋ cors ์ค์ )
Nginx ๋ฅผ ์ฌ์์ํ์ฌ ๋ค์ ์์ฒญ์ ๋ณด๋ด๋ฉด ๋์ด์ CORS ์๋ฌ๊ฐ ๋ฐ์ํ์ง ์๋๋ค.

- ๋ธ๋ผ์ฐ์ ๊ฐ ์์ฒญ ํค๋์ ํ์ฌ ์์ฒญ์ ๋ณด๋ด๋ Origin ์ธ 'https://verby.co.kr' ์ ์ถ๊ฐ
- ํด๋ผ์ด์ธํธ๊ฐ ์๋ฒ์ ์์ฒญ
- ์๋ฒ๋ ์๋ต ํค๋์ธ Access-Control-Allow-Origin ์ 'https://verby.co.kr' ๋ก ์ค์ ํ์ฌ ํ์ฉ๋๋ Origin ๋ช ์
- ์๋ต์ ๋ฐ์ ๋ธ๋ผ์ฐ์ ๋ Access-Control-Allow-Origin ๊ฐ์ ํตํด ํ์ฌ Origin ๊ณผ ๋์ผํ์ง ํ์ธ
- ํ์ฉ๋ Origin ์์ ํ์ธํ๊ณ ์๋ต ๋ฐ์ดํฐ๋ฅผ ํด๋ผ์ด์ธํธ๋ก ์ ๋ฌ
- ํต์ ์ฑ๊ณต!
2๊ฐ ์ด์์ Origin ์ค์ ํ๊ธฐ
์๋ฒ๊ฐ ํ์ฉํ๋ Origin ์ https://verby.co.kr๊ณผ https://www.verby.co.kr์ด๋ค. ๋งจ ์ฒ์์๋ ๋จ์ํ๊ฒ ์ฌ๋ฌ๊ฐ์ ํค๋๋ฅผ ์ค์ ํด์ฃผ์๋ค.
add_header 'Access-Control-Allow-Origin' 'https://verby.co.kr';
add_header 'Access-Control-Allow-Origin' 'https://www.verby.co.kr';
ํ์ง๋ง, ์์ฒญ์ ๋ณด๋ผ ๊ฒฝ์ฐ CORS ์๋ฌ๊ฐ ๋ฐ์ํ๋ค.

Access-Control-Allow-Origin ํค๋๋ ๋ ๊ฐ(multiple) ์ด์์ด๋ฉด ์๋๋ค. ์ด๊ฒ๋ CORS ๊ด๋ จ ์ ์ฑ ์ธ๊ฐ๋ณด๋ค. ๋ฌผ๋ก ๋ค์๊ณผ ๊ฐ์ด ์ค์ ํด๋ ๋์ง ์๋๋ค.
add_header 'Access-Control-Allow-Origin' 'https://verby.co.kr, https://www.verby.co.kr';
์ฐพ์๋ณด๋ Nginx ์๋ ๋ณ์์ ๋์ ์ผ๋ก ๊ฐ์ ๋์ ํ ์ ์๋ map ๋ชจ๋์ด๋ผ๋๊ฒ ์์๋ค. ์ด๋ฅผ ์ฌ์ฉํ์ฌ ๋์ ์ผ๋ก ํ์ฉ Origin ์ ์ค์ ํ ์ ์์๋ค.
# nginx.conf
http {
...
map $http_origin $allowed_origin {
default "https://verby.co.kr";
https://www.verby.co.kr $http_origin;
}
}
# sites-available/verby.co.kr
location / {
if ($request_method = 'OPTIONS') {
add_header 'Access-Control-Allow-Origin' $allowed_origin;
add_header 'Access-Control-Allow-Methods' 'GET, POST, DELETE, PUT, PATCH, OPTIONS';
add_header 'Access-Control-Allow-Headers' 'Content-Type, Authorization';
add_header 'Access-Control-Max-Age' 86400;
return 204;
}
add_header 'Access-Control-Allow-Origin' $allowed_origin always;
proxy_pass http://localhost:8080/;
}