CORS(๊ต์ฐจ ์ถ์ฒ ๋ฆฌ์์ค ๊ณต์ )๋?
์๋ก
๋ฐฑ์๋๋ฅผ ๊ฐ๋ฐํ๋ฉด์ ํ๋ฐํธ ๊ฐ๋ฐ์์ ํ์ ์ ์งํํ๋ค ๋ณด๋ฉด ํ ๋ฒ์ฏค์ ์ ์ด๋ฏธ์ง์ฒ๋ผ CORS ์๋ฌ์ ๋ํด ๋ง๋ฅ๋จ๋ฆฐ ์ ์ด ์์ ๊ฒ์ ๋๋ค. ์ด๋ฒ ๊ฒ์๊ธ์์๋ ์ด๋ฌํ CORS๊ฐ ๋ญ์ง, ๊ทธ๋ฆฌ๊ณ ์ ๋ฐ์ํ๊ณ ์ด๋ป๊ฒ ํด๊ฒฐํ ์ ์๋์ง ํ๋ฒ ์ ๋ฆฌํด ๋ณด๊ฒ ์ต๋๋ค.
SOP(Same Origin Policy)
CORS๋ฅผ ์๊ธฐ ์ ์ ๋จผ์ SOP๋ผ๋ ๊ฐ๋ ์ ๋ํด ๋จผ์ ์์์ผ ํฉ๋๋ค.
๋์ผ ์ถ์ฒ ์ ์ฑ (same-origin policy)์ ์ด๋ค ์ถ์ฒ(origin)์์ ๋ถ๋ฌ์จ ๋ฌธ์๋ ์คํฌ๋ฆฝํธ๊ฐ ๋ค๋ฅธ ์ถ์ฒ์์ ๊ฐ์ ธ์จ ๋ฆฌ์์ค์ ์ํธ์์ฉํ๋ ๊ฒ์ ์ ํํ๋ ๋ณด์ ๋ฐฉ์์ ๋๋ค. ๋์ผ ์ถ์ฒ ์ ์ฑ ์ ์ ์ฌ์ ์ผ๋ก ํด๋ฅผ ๋ผ์น ์ ์๋ ์์ฒญ์ ๋ถ๋ฆฌํจ์ผ๋ก์จ ๊ณต๊ฒฉ๋ฐ์ ์ ์๋ ๊ฒฝ์ฐ๋ฅผ ์ค์ฌ์ค๋๋ค.
์ฌ๊ธฐ์ ์ถ์ฒ(origin)๋ ์ ๊ทผํ ๋ ์ฌ์ฉํ๋ URL์ ํ๋กํ ์ฝ, ํธ์คํธ(๋๋ฉ์ธ), ํฌํธ๋ฅผ ์๋ฏธํฉ๋๋ค. ๋ง์ฝ ๋ ๊ฐ์ฒด์ ํ๋กํ ์ฝ, ํธ์คํธ, ํฌํธ๊ฐ ๋ชจ๋ ์ผ์นํ๋ ๊ฒฝ์ฐ ๊ฐ์ ์ถ์ฒ๋ฅผ ๊ฐ์ก๋ค๊ณ ๋งํฉ๋๋ค.
์ ์ด๋ฏธ์ง์์ HTTP ํน์ HTTPS์ ๊ฒฝ์ฐ๊ฐ ํ๋กํ ์ฝ์ด ๋๊ณ , www.example.com์ ๊ฒฝ์ฐ ํธ์คํธ(๋๋ฉ์ธ)๊ฐ ๋๋ฉฐ, 80์ ๊ฒฝ์ฐ ํฌํธ๊ฐ ๋ฉ๋๋ค.
๊ธฐ๋ณธ์ ์ผ๋ก ํ๋กํ ์ฝ์ HTTP๋ 80๋ฒ, HTTPS๋ 443๋ฒ ํฌํธ๋ฅผ ์ฌ์ฉํ๋ฉฐ, 80๋ฒ๊ณผ 443๋ฒ ํฌํธ๋ ์๋ต์ด ๊ฐ๋ฅํฉ๋๋ค.
์ฆ, ์ ๋ฆฌํ๋ฉด ์ถ์ฒ(Origin)๋ URL ๊ตฌ์กฐ์์ ์ดํด๋ณธ ํ๋กํ ์ฝ, ํธ์คํธ, ํฌํธ๋ฅผ ํฉ์น ๊ฒ์ ์๋ฏธํฉ๋๋ค.
๋์ผ ์ถ์ฒ VS ๋ค๋ฅธ ์ถ์ฒ
URL | ๊ฒฐ๊ณผ | ์ด์ |
http://example.com/app1/index.html http://example.com/app2/index.html |
๋์ผ ์ถ์ฒ | ํ๋กํ ์ฝ(http)์ ํธ์คํธ(example.com) ์ผ์น |
http://Example.com:80 http://example.com |
๋์ผ ์ถ์ฒ | HTTP์ ๊ธฐ๋ณธ ํฌํธ๋ 80์ด๋ฏ๋ก ์๋ต๊ฐ๋ฅ ํ๊ธฐ์ ๋์ผ ์ถ์ฒ (๋์๋ฌธ์๋ ๋์ผํ ๊ฒ์ผ๋ก ๊ฐ์ฃผ) |
http://example.com/app1 https://example.com/app2 |
๋ค๋ฅธ ์ถ์ฒ | ์๋ก ํ๋กํ ์ฝ(http, https)๊ฐ ๋ค๋ฆ |
http://example.com http://www.example.com http://myapp.example.com |
๋ค๋ฅธ ์ถ์ฒ | ์๋ก ํธ์คํธ๊ฐ ๋ค๋ฆ |
http://example.com http://example.com:8080 |
๋ค๋ฅธ ์ถ์ฒ | ์๋ก ํฌํธ๊ฐ ๋ค๋ฆ |
๊ทธ๋ ๋ค๋ฉด ์ ๋์ผ ์ถ์ฒ ์ ์ฑ (same-origin policy, SOP)์ ์ฌ์ฉํ๋ฉด ๋ณด์์ ๋์์ด ๋๋์ง ์์๋ณด๊ฒ ์ต๋๋ค.
์ ์ด๋ฏธ์ง์์ domainA๋ผ๋ ์ฌ์ดํธ์ ์ ์ํด์ ๋ก๊ทธ์ธ์ ํ๋ค๊ณ ๊ฐ์ ํฉ์๋ค. ๊ทธ๋ฆฌ๊ณ domainA๋ผ๋ ์ฌ์ดํธ ๋ด์์ pg2๋ผ๋ ํ์ด์ง๋ก ์ด๋ํ ์๋ ์๊ณ , domainB๋ผ๋ ์ฌ์ดํธ๋ก ์ด๋ํ ์ ์๋ค๊ณ ๊ฐ์ ํฉ์๋ค.
domainA/pg2๋ผ๋ ํ์ด์ง์ ๊ฒฝ์ฐ ์ด์ ํ์ด์ง์ธ domainA/pg์ ๊ฐ์ ์ถ์ฒ(origin) ์ด๊ธฐ ๋๋ฌธ์ ๋์ผํ ๋ก๊ทธ์ธ ์ ๋ณด๋ฅผ ๊ฐ์ง๊ณ ์๋น์ค๋ฅผ ์ด์ฉํ ์ ์์ต๋๋ค. ํ์ง๋ง domainB/pg๋ผ๋ ํ์ด์ง๋ domainA์๋ ๋ค๋ฅธ ์ถ์ฒ(origin)์ ๋๋ค. ๋ฐ๋ผ์ domainB๋ผ๋ ์ฌ์ดํธ์์๋ domainA์ ๋ก๊ทธ์ธ๋ ์ ๋ณด๋ฅผ ํ์ทจํ์ฌ ๋ฏผ๊ฐํ ์ ๋ณด๋ฅผ ๊ฐ์ง๊ณ ์จ๋ค๊ฑฐ๋ ํ๋ ๋ถ๋ฏธ์ค๋ฌ์ด ์ํฉ์ด ์ผ์ด๋ ์๋ ์์ต๋๋ค.
๊ทธ๋ ๊ธฐ ๋๋ฌธ์ ์ด์ ์ฌ๊ธฐ์ SOP์ ๊ธฐ๋ฅ์ด ๋ฐํํ๊ฒ ๋ฉ๋๋ค.
- domainA์ ์ ์ฅ์์๋ ๋ค์ ์์ฒญ(domainB)์ ์ถ์ฒ(origin)๋ฅผ ํ์ธํฉ๋๋ค.
- ํ์ธํด ๋ณด๋ ์ด ์์ฒญ์ domainB์์ ์์ต๋๋ค.
- domainA๋ ์ด ์์ฒญ์ด ๋ค๋ฅธ ์ถ์ฒ(origin)์ด๋ผ๊ณ ํ๋จ ์ฆ, cross origin์ด๋ผ๊ณ ํ๋จ์ ํ๊ฒ ๋ฉ๋๋ค.
- ๋ฐ๋ผ์ domainB์ ์์ฒญ์ ๋ฐ์๋ค์ฌ์ง์ง ์์ต๋๋ค.
๊ทธ๋ ๋ค๋ฉด ๋ง์ฝ ๋ค๋ฅธ ์ถ์ฒ(domainB)์ ๋ฆฌ์์ค๊ฐ ํ์ํ๋ค๋ฉด ์ด๋ป๊ฒ ํ ๊น์?
๋ฐ๋ก ์ด๋ CORS๊ฐ ์ฌ์ฉ๋ฉ๋๋ค.
CORS
๊ต์ฐจ ์ถ์ฒ ๋ฆฌ์์ค ๊ณต์ (Cross Origin Resource Sharing, CORS)๋ ๋ค๋ฅธ ์ถ์ฒ์ ์์์ ๊ณต์ ํ๋ ๊ฒ์ ์๋ฏธํฉ๋๋ค.
๊ต์ฐจ ์ถ์ฒ ๋ฆฌ์์ค ๊ณต์ (Cross-Origin-Resource Sharing, CORS)๋ ์ถ๊ฐ HTTP ํค๋๋ฅผ ์ฌ์ฉํ์ฌ, ํ ์ถ์ฒ(Origin)์์ ์คํ ์ค์ธ ์น ์ ํ๋ฆฌ์ผ์ด์ ์ด ๋ค๋ฅธ ์ถ์ฒ์ ์ ํํ ์์์ ์ ๊ทผํ ์ ์๋ ๊ถํ์ ๋ถ์ฌํ๋๋ก ๋ธ๋ผ์ฐ์ ์ ์๋ ค์ฃผ๋ ์ฒด์ ์ด๋ค.
์ฌ๊ธฐ์ ์ค์ํ ์ ์ ์ฒซ์งธ, ๋ค๋ฅธ ์ถ์ฒ์ ์ ๊ทผ์ ํ ์ ์๋๋ก ํ๋ ๊ฒ์ด๊ณ ๋์งธ, ๊ถํ์ ๋ธ๋ผ์ฐ์ ์๊ฒ ์๋ ค์ฃผ๋ ์ฒด์ ๋ผ๋ ๊ฒ์ ๋๋ค.
CORS์ ์ ๊ทผ ๋ฐฉ๋ฒ์ ํฌ๊ฒ ์ธ ๊ฐ์ง๊ฐ ์์ต๋๋ค.
- ๋จ์ ์์ฒญ(Simple Request)
- ์ฌ์ ์์ฒญ(Prefilight Request)
- ์ธ์ฆ์ ๋ณด ํฌํจ ์์ฒญ(Credentialed Request)
๋จ์ ์์ฒญ(Simple Request)
๋จ์ ์์ฒญ์ ์๋ฒ์๊ฒ ๋ฐ๋ก ์์ฒญ์ ๋ณด๋ด๋ ๋ฐฉ๋ฒ์ ๋๋ค.
์ ์ด๋ฏธ์ง๋ ์๋ฐ์คํฌ๋ฆฝํธ์์ API๋ฅผ ์์ฒญํ ๋ ๋ธ๋ผ์ฐ์ (ํฌ๋กฌ, ์จ์ผ ๋ฑ)์ ์๋ฒ์ ๋์์ ๋ํ๋ด๋ ์ด๋ฏธ์ง์ ๋๋ค.
๋จ์ ์์ฒญ์ ์๋ฒ์ API๋ฅผ ์์ฒญํ๊ณ , ์๋ฒ๋ Access-Control-Allow-Origin ํค๋๋ฅผ ํฌํจ ํ ์๋ต์ ๋ธ๋ผ์ฐ์ ์ ๋ณด๋ ๋๋ค. ๋ธ๋ผ์ฐ์ ๋ Access-Control-Allow-Origin ํค๋๋ฅผ ํ์ธํด์ CORS ๋์์ ์ํํ ์ง ํ๋จํฉ๋๋ค.
Simple request ์กฐ๊ฑด
์๋ฒ๋ก ์ ๋ฌํ๋ ์์ฒญ(request)์ด ์๋์ 3๊ฐ์ง ์กฐ๊ฑด์ ๋ง์กฑํด์ผ ์๋ฒ๋ก ์ ๋ฌํ๋ ์์ฒญ์ด ๋จ์ ์์ฒญ์ผ๋ก ๋์ํฉ๋๋ค.
- ์์ฒญ ๋ฉ์๋(method)๋ GET, HEAD, POST ์ค ํ๋์ฌ์ผ ํ๋ค.
- 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 ์ค ํ๋๋ฅผ ์ฌ์ฉํด์ผ ํ๋ค.
์ฒซ ๋ฒ์งธ ์กฐ๊ฑด์ ์ด๋ ต์ง ์์ ์กฐ๊ฑด์ด์ง๋ง 2๋ฒ, 3๋ฒ ์กฐ๊ฑด์ ๊น๋ค๋ก์ด ์กฐ๊ฑด์ ๋๋ค. 2๋ฒ ์กฐ๊ฑด์ ์ฌ์ฉ์ ์ธ์ฆ์ ์ฌ์ฉ๋๋ Authorization ํค๋๋ ํฌํจ๋์ง ์์ ๊น๋ค๋ก์ด ์กฐ๊ฑด์ด๋ฉฐ, 3๋ฒ ์กฐ๊ฑด์ ๋ง์ REST API๋ค์ด Content-Type์ผ๋ก application/json์ ์ฌ์ฉํ๊ธฐ ๋๋ฌธ์ ์ง์ผ์ง๊ธฐ ์ด๋ ค์ด ์กฐ๊ฑด์ ๋๋ค.
์ฌ์ ์์ฒญ(Preflight request)
์ฌ์ ์์ฒญ(Preflight request) ์์ฒญ์ ์๋ฒ์ ์๋น ์์ฒญ์ ๋ณด๋ด์ ์์ ํ์ง ํ๋จ ํ ๋ณธ ์์ฒญ์ ๋ณด๋ด๋ ๋ฐฉ๋ฒ์ ๋๋ค.
์ฌ์ ์์ฒญ์ ์ค์ ๋ฆฌ์์ค๋ฅผ ์์ฒญํ๊ธฐ ์ ์ OPTIONS๋ผ๋ ๋ค๋ฅธ ๋๋ฉ์ธ์ ๋ฆฌ์์ค์ ์์ฒญ์ด ๊ฐ๋ฅํ์ง ํ์ธ ํ, ๊ฐ๋ฅํ๋ค๋ฉด ์ค์ ์์ฒญ(Actual Request)์ ๋ณด๋ ๋๋ค.
OPTIONS ๋ฉ์๋๋ก ์๋ฒ์ ์๋น ์์ฒญ์ ๋จผ์ ๋ณด๋ด๊ณ , ์๋ฒ๋ ์ด ์๋น ์์ฒญ์ ๋ํ ์๋ต์ผ๋ก Access-Control-Allow-Origin ํค๋๋ฅผ ํฌํจํ ์๋ต์ ๋ธ๋ผ์ฐ์ (ํฌ๋กฌ, ์จ์ผ ๋ฑ)์ ๋ณด๋ ๋๋ค. ๋ธ๋ผ์ฐ์ ๋ ๋จ์ ์์ฒญ๊ณผ ๋์ผํ๊ฒ Access-Control-Allow-Origin ํค๋๋ฅผ ํ์ธํด์ CORS ๋์์ ์ํํ ์ง ํ๋จํฉ๋๋ค.
์ฌ์ ์์ฒญ ๋ฐฉ์์ ์ฌ์ ์์ฒญ๊ณผ ์ค์ ์์ฒญ ์ด ๋ ๋ฒ ๋ณด๋ด์ง๊ฒ ๋ฉ๋๋ค. ์ด๋ ๊ฒ ๋๋ฉด์ ๋งค๋ฒ ํ๋์ ์์ฒญ์ ๋ณด๋ผ ๋๋ง๋ค ๋ ๋ฒ์ ์์ฒญ์ด ์๋ค ๊ฐ๋ค ํ๊ฒ ๋ฉ๋๋ค. ๋ฐ๋ผ์ ๊ต์ฅํ ๋ฆฌ์์ค์ ์ผ๋ก ๋ถ๋ด์ด ๋๊ฒ ๋ฉ๋๋ค. ๋ฐ๋ผ์ ์ด๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด OPTIONS ๋ฉ์๋์ ์๋ต์ ๋ํด์ ๋ธ๋ผ์ฐ์ ๋ ์บ์ฑ์ ํด๋๊ณ ์ผ์ ์๊ฐ ๋์ ๋ค์ ๋๊ฐ์ ์์ฒญ์ ๋ณด๋ผ ๋ preflight ์บ์ฑ๋ ๊ฒ์ ํ์ธํ๊ณ ๋์ผํ ์์ฒญ์ด๋ผ๋ฉด ์ฌ์ ์์ฒญ์ ๋ฏธ๋ฆฌ ๋ณด๋ด์ง ์๊ณ ๋ฐ๋ก ๋ณธ ์์ฒญ์ ๋ณด๋ด๋ ๋ฐฉ์์ผ๋ก ๋ถ๋ด์ ์ค์ ๋๋ค.
Preflight Request
- Origin : ์์ฒญ ์ถ์ฒ
- Access-Control-Request-Method : ์ค์ ์์ฒญ์ ๋ฉ์๋
- Access-Control-Request-Headers : ์ค์ ์์ฒญ์ ์ถ๊ฐ ํค๋
Preflight Response
- Access-Control-Allow-Origin : ํ๊ฐ ์ถ์ฒ
- Access-Control-Allow-Methods : ํ๊ฐ ๋ฉ์๋
- Access-Control-Allow-Headers : ํ๊ฐ ํค๋
- Access-Control-Max-Age : Preflight ์๋ต ์บ์ ์๊ฐ
์ฌ๊ธฐ์ Preflight Response์ ์๋ต ์ฝ๋๋ 200๋์ฌ์ผ ํ๊ณ Body๋ ๋น์ด์๋ ๊ฒ์ด ์ข๋ค.
์ธ์ฆ ์์ฒญ(Credentialed Request)
์ธ์ฆ ์์ฒญ์ ์ธ์ฆ ๊ด๋ จ ํค๋๋ฅผ ํฌํจํ ๋ ์ฌ์ฉํ๋ ์์ฒญ์ ๋๋ค.
- ํด๋ผ์ด์ธํธ: ์ฟ ํค ๋๋ JWT ํ ํฐ์ ๋ด์ ๋ณด๋ผ ๊ฒฝ์ฐ credentials : include๋ฅผ ํฌํจํ์ฌ ๋ณด๋ ๋๋ค.
- ์๋ฒ: Access-Control-Allow-Credentials๋ฅผ true๋ก ์ค์ ์ ํด๋ผ์ด์ธํธ์ ์ธ์ฆ ํฌํจ ์์ฒญ ํ์ฉ์ด ๊ฐ๋ฅํด์ง๋๋ค.
CORS์ ํด๊ฒฐ
CORS์ ํด๊ฒฐ์๋ ์ธ ๊ฐ์ง ๋ฐฉ๋ฒ์ด ์์ต๋๋ค.
- ํ๋ฐํธ ํ๋ก์ ์๋ฒ ์ค์ : ํ๋ฐํธ ์๋ฒ์์ ๋ฐฑ์๋ ์๋ฒ๋ก ์์ฒญ์ ๋ณด๋ผ ๋, ๋์์ URL์ ๋ณ๊ฒฝํฉ๋๋ค.
- ์ง์ ํค๋ ์ค์ : ์ง์ ํค๋์ ์ค์ ์ ์ถ๊ฐํฉ๋๋ค.
- ์๋ฒ์์ ์คํ๋ง ๋ถํธ ๋ด ์ค์ : ์ค์ ํด๋์ค๋ฅผ ๋ง๋ค๊ณ WebMvcConfigurer์ ๊ตฌํํ๋ฉด addCorsMappings๋ ๋ฉ์๋๋ฅผ ์ฌ์ฉํ์ฌ CORS์ ์ถ์ฒ ๋ฐ ์ค์ ์ ๊ด๋ฆฌํ ์ ์์ต๋๋ค.
3๋ฒ์ ๋ฐฉ๋ฒ์ ์๋๋ก ํด๊ฒฐ์ด ๊ฐ๋ฅํฉ๋๋ค.
package com.example.demo.src;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class WebConfig implements WebMvcConfigurer {
private static final long MAX_AGE_SECOND = 3600;
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOriginPatterns("*")
.allowedMethods("GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS")
.allowedHeaders("*")
.allowCredentials(true)
.maxAge(MAX_AGE_SECOND);
}
}
src ํด๋์ WebConfig.java ํ์ผ์ ์์ฑํ์ฌ ์ ์ฝ๋๋ฅผ ์์ฑํด ์ฃผ์๋ฉด ๋ฉ๋๋ค.