๐ ํด๋ผ์ด์ธํธ ์ธก vs ์๋ฒ ์ธก CORS: ์ค์ ์ฐจ์ด์ ์ดํดํ๊ธฐ
Source: Dev.to

๋ชจ๋ํฐ ์์์ ์์๊ป๋ผ ๊ฐ์ CORS ์ค๋ฅ ๋๋ฌธ์ ์๋ฆฌ๋ฅผ ์ง๋ฌ ๋ณธ ์ ์ด ์๋ค๋ฉดโฆ
์ถํํฉ๋๋ค โ ์ด์ ๊ณต์์ ์ธ ์น ๊ฐ๋ฐ์์
๋๋ค. ๐
CORS๋ ๋ง์น ํด๋ฝ ์์ ์ ์๋ ์๊ฒฉํ ๊ฒฝ๋น์๊ณผ ๊ฐ์์, ๋น์ ์ด ๋ชฉ๋ก์ ์๋ค๊ณ ๋งน์ธํด๋ ๋ค์ด๊ฐ์ง ๋ชปํ๊ฒ ํฉ๋๋ค.
CORS๊ฐ ์ค์ ๋ก ๋ญ๊ฐ์?
CORS๋ CrossโOrigin Resource Sharing์ ์ฝ์์ ๋๋ค โ ๋ฉ์ ธ ๋ณด์ด์ง๋ง ์ค์ ์๋ฏธ๋:
โ๋ธ๋ผ์ฐ์ ์ผ, ์ด ์น์ฌ์ดํธ๊ฐ ์ ๋ค๋ฅธ ์น์ฌ์ดํธ์ ๋ํํด๋ ๋ ๊น? ์๋๋ฉด ๋ฏ์ ์ฌ๋ ์ํ ์ํฉ์ธ๊ฐ?โ
๋ธ๋ผ์ฐ์ ๋ ๋งค์ฐ ํธ์ง์ฆ์ ์
๋๋ค.
๋๋ฉ์ธ A๊ฐ ๋๋ฉ์ธ B๋ฅผ ํธ์ถํ๋ฉด ๋ธ๋ผ์ฐ์ ๋ ์ด๋ ๊ฒ ๋งํฉ๋๋ค:
โ์ ๊น. ๋๊ฐ ๋ณด๋์ด? ์ฌ๊ถ, ์๋ค๋ฅด ์นด๋, PAN ์นด๋, ๊ทธ๋ฆฌ๊ณ 2ร2 ์ฌ์ง ๋ ์ฅ ๋ณด์ฌ์ค.โ
ํด๋ผ์ด์ธํธโ์ธก vs ์๋ฒโ์ธก CORS: ๋ฐ์
์ฌ๋๋ค์ *โํด๋ผ์ด์ธํธโ์ธก CORSโ*์ *โ์๋ฒโ์ธก CORSโ*๋ฅผ ์๊ธฐํ์ง๋ง, ์ง์ค์:
CORS๋ฅผ ๊ฒฐ์ ํ๋ ๊ฒ์ ์ค์ง ์๋ฒ๋ฟ์ ๋๋ค. ํด๋ผ์ด์ธํธ๋ ํ๊ฐ๋ฅผ ์์ฒญํ๋ ํผ๋์ค๋ฌ์ด ์ฒญ์๋ ์ผ ๋ฟ.
์์ชฝ ๋ชจ๋ ์ญํ ์ ํ๋, ์ด์ ์ดํด๋ณด๊ฒ ์ต๋๋ค.
ํด๋ผ์ด์ธํธโ์ธก CORS (์ผ๋ช : ์ ์ด์ ์ฐฉ๊ฐ)
๊ฐ๋ฐ์๋ค์ ์ข ์ข ์ด๋ ๊ฒ ์๋๋ค:
fetch("/api", { mode: "cors" })
๊ทธ๋ฆฌ๊ณ ๋ธ๋ผ์ฐ์ ๊ฐ ์ด๋ ๊ฒ ๋งํด ์ฃผ๊ธธ ๊ธฐ๋ํฉ๋๋ค:
โ๋ค, ์๊ฒ ์ต๋๋ค! CORS๊ฐ ํ์ฑํ๋์์ต๋๋ค! ๋น์ ์ ์ฒ์ฌ๊ตฐ์!โ
ํ์ง๋ง ์ค์ ๋ก ๋ธ๋ผ์ฐ์ ๊ฐ ๊ท์น์ ์งํํฉ๋๋ค:
mode: "cors"โ โ์ ๋ฐ ๋ค์ด๊ฐ๊ฒ ํด ์ฃผ์ธ์!โmode: "no-cors"โ โ์๊ฒ ์ด์, ๋ ๋ ๊ฒ์. ์ด์ฐจํผ ์๋ต์ ๋ณด๊ณ ์ถ์ง ์์๊ฑฐ๋ ์.โcredentials: "include"โ โ์ฟ ํค ์ฌ๊ธฐ ์์ด์, ํ๋จ์ ํ์ง ๋ง์ ์ฃผ์ธ์.โ
โ ๏ธ ํด๋ผ์ด์ธํธ๋ CORS๋ฅผ ํ์ฑํํ ์ ์์ต๋๋ค. ์์ฒญ๋ง ํ ์ ์์ ๋ฟ์ด๋ฉฐ, ํค๋ ์ฌ์ ํ ์๋ฒ์ ์์ต๋๋ค.
์๋ฒโ์ธก CORS (์ง์ง ๋ณด์ค)
์ฌ๊ธฐ๊ฐ ์ค์ ๋ง๋ฒ์ด ์ผ์ด๋๋ ๊ณณ์ ๋๋ค. ์๋ฒ๊ฐ ๋น์ ์ ์ค๋ฆฌ์ง์ ์น์ธํ์ง ์์ผ๋ฉด ๋ธ๋ผ์ฐ์ ๊ฐ ์์ฒญ์ ์ฐจ๋จํฉ๋๋ค.
์ผ๋ฐ์ ์ธ ์๋ต ํค๋:
Access-Control-Allow-Origin: https://myapp.com
Access-Control-Allow-Methods: GET, POST, DELETE
Access-Control-Allow-Headers: Content-Type, Authorization
Access-Control-Allow-Credentials: true
์ฌ๋ฐ๋ฅธ ํค๋๋ฅผ ๋ฐ์ ๋ค์์ผ ๋ธ๋ผ์ฐ์ ๊ฐ ์์ฒญ์ ํ์ฉํฉ๋๋ค.
์์ (Express.js)
const cors = require('cors');
app.use(cors({
origin: "https://myapp.com",
credentials: true
}));
์์ (Next.js API ๋ผ์ฐํธ)
export default function handler(req, res) {
res.setHeader("Access-Control-Allow-Origin", "https://myapp.com");
res.setHeader("Access-Control-Allow-Credentials", "true");
// ...์์ฒญ ์ฒ๋ฆฌ
}
ํค๋ ํ๋๋ผ๋ ๋นผ๋จน์ผ๋ฉด CORS ์ค๋ฅ๊ฐ ๋ฐ์ํฉ๋๋ค.
Simple vs Preflight ์์ฒญ (์ผ๋ช : ๋ธ๋ผ์ฐ์ ๋๋ผ๋ง)
Simple ์์ฒญ
์ด๊ฒ๋ค์ โํธ์ํจโ์ ์๋ฏธํฉ๋๋ค. ๋ธ๋ผ์ฐ์ ๊ฐ ํ์ฉํ๋ ๊ฒฝ์ฐ:
GET๋๋POST์ ๋จ์ ํค๋(Accept,Content-Type์ดtext/plain,multipart/form-data,application/x-www-form-urlencoded์ค ํ๋์ธ ๊ฒฝ์ฐ)
Preflight ์์ฒญ
๋ค์ ์ํฉ์์ ๋ฐ์ํฉ๋๋ค:
PUT,PATCH,DELETE๊ฐ์ ๋ฉ์๋ ์ฌ์ฉ- ์ปค์คํ ํค๋
- JSON ๋ฐ๋(
Content-Type: application/json)
๋ธ๋ผ์ฐ์ ๋ ๋จผ์ OPTIONS ์์ฒญ์ ๋ณด๋ด ์๋ฒ์๊ฒ ํ๊ฐ๋ฅผ ๋ฌป์ต๋๋ค. ์๋ฒ๊ฐ ์ฌ๋ฐ๋ฅด๊ฒ ์๋ตํ์ง ์์ผ๋ฉด ์ค์ ์์ฒญ์ด ์ฐจ๋จ๋ฉ๋๋ค.
๋๋ฌด ๋นจ๋ฆฌ ์คํฌ๋กคํ ์ฒ์ฌ๋ค์ ์ํ ์์ฝ ํ
| ํญ๋ชฉ | ํด๋ผ์ด์ธํธ ์ธก | ์๋ฒ ์ธก |
|---|---|---|
| ๋๊ฐ ์ ์ดํ๋์? | ์์. ๋น์ ์ด ์๋๋๋ค. | ์ง์ง ๋ณด์ค. |
| ์์ฒญ์ ํ์ฉํ ์ ์๋์? | โ ์ ๋ฉ๋๋ค | โ ๋ฌผ๋ก ๊ฐ๋ฅํฉ๋๋ค |
| Preflight๋ฅผ ํธ๋ฆฌ๊ฑฐํ ์ ์๋์? | โ ์ | โ ๏ธ ์ฌ๋ฐ๋ฅด๊ฒ ์๋ตํด์ผ ํจ |
| ์ง์ง CORS์ธ๊ฐ์? | โ ์๋์ | โ ๋ค, 100โฏ% ์ ํต |
๋ง๋ฌด๋ฆฌ ์๊ฐ
CORS ์ค๋ฅ๋ ๋ง์น ์ฐ์ฃผ๊ฐ ๊ฐ์ธ์๊ฒ ๊ณต๊ฒฉ์ ๊ฐํ๋ ๋ฏํ ๋๋์ ์ค ์ ์์ง๋ง, ๊ท์น์ ๊ฐ๋จํฉ๋๋ค:
- ํด๋ผ์ด์ธํธ๊ฐ ์์ฒญ์ ๋ณด๋ ๋๋ค.
- ์๋ฒ๊ฐ ์ ์ ํ ํค๋๋ฅผ ๋ณด๋ด ํ์ฉ ์ฌ๋ถ๋ฅผ ๊ฒฐ์ ํฉ๋๋ค.
- ๋ธ๋ผ์ฐ์ ๊ฐ ๊ทธ ๊ฒฐ์ ์ ๊ฐ์ ํฉ๋๋ค.
๊ฐ ํํธ๊ฐ ์ ์ญํ ์ ํ๋ฉด CORS๋ ๊ณ ํต์ด ์์ต๋๋ค. ๋ญ๊ฐ ์๋ชป๋๋ฉดโฆ ๋ค์ ๋ช ์๊ฐ ๋์ ๋๋ฒ๊น ์ ์ฆ๊ธฐ์ธ์. ๐