๐ŸŒŸ CORS Error

Jnnsยท2024๋…„ 2์›” 4์ผ
post-thumbnail

ํ”„๋กœ์ ํŠธ์—์„œ ๋‚ ์”จ ๋ฐ์ดํ„ฐ๋ฅผ ๋ถˆ๋Ÿฌ์™€ ํ‘œ์‹œํ•ด์ฃผ๊ธฐ ์œ„ํ•ด openWeathermap์˜ api๋ฅผ ์‚ฌ์šฉํ–ˆ์—ˆ๋‹ค.
๋ฐ์ดํ„ฐ๊ฐ€ ์ž˜ ๋ถˆ๋Ÿฌ์™€์ง€๊ธธ ๋ฐ”๋ผ๋ฉฐ Run ํ•ด๋ณด์•˜์ง€๋งŒ.. CORS ์—๋Ÿฌ๊ฐ€ ๋“ฑ์žฅ๐Ÿ˜‚

์ผ๋‹จ CORS๊ฐ€ ๋ญ”์ง€๋ฅผ ์•Œ์•„์•ผ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ์„๊ฑฐ ๊ฐ™์•„ ์ฐพ์•„๋ณด์•˜๋‹ค.


๐Ÿ“Œ CORS๋ž€?

CORS๋Š” Cross Origin Resource Sharing์˜ ์•ฝ์ž์ธ๋ฐ ํ•œ๊ตญ์–ด๋กœ ๋ฐ”๊ฟ” ๊ต์ฐจ ์ถœ์ฒ˜ ๋ฆฌ์†Œ์Šค ๊ณต์œ  ์ •์ฑ…์ด๋‹ค. ์ฆ‰, ์„œ๋กœ ๋‹ค๋ฅธ ๋„๋ฉ”์ธ๊ฐ„์— ์ž์›์„ ๊ณต์œ ๋ฅผ ์˜๋ฏธํ•œ๋‹ค.
์›น ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ ๋‹ค๋ฅธ ๋„๋ฉ”์ธ์˜ ๋ฆฌ์†Œ์Šค์— ์ ‘๊ทผํ•  ๋•Œ ๋ฐœ์ƒํ•˜๋Š” ๋ณด์•ˆ ์ด์Šˆ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•œ ํ‘œ์ค€ ๋ฐฉ๋ฒ•์ด๊ณ  ๋ณด์•ˆ์ ์ธ ๋ฌธ์ œ๋กœ ๊ฐ™์€ ์ถœ์ฒ˜(์ฃผ์†Œ)๋ฅผ ๊ฐ€์ง„ ์• ๋“ค๋ผ๋ฆฌ๋งŒ ๋ฆฌ์†Œ์Šค๋ฅผ ์ฃผ๊ณ ๋ฐ›์„ ์ˆ˜ ์žˆ๋Š” SOP(Same Origin Policy / ๋™์ผ ์ถœ์ฒ˜ ์ •์ฑ…)๋ผ๋Š” ์ •์ฑ…์„ ์šฐํšŒํ•˜๊ธฐ ์œ„ํ•œ ์ •์ฑ…์ด๋ผ๊ณ  ํ•  ์ˆ˜ ์žˆ๋‹ค.

๊ทธ๋ ‡๋‹ค๋ฉด ๋™์ผ ์ถœ์ฒ˜ ์ •์ฑ…์ด๋ผ๋Š” ๊ฒƒ์€ ๋ฌด์—‡์ผ๊นŒ?

๋™์ผ ์ถœ์ฒ˜ ์ •์ฑ…์€ ๋ง ๊ทธ๋Œ€๋กœ ๋™์ผํ•œ ์ถœ์ฒ˜์˜ ๋ฆฌ์†Œ์Šค์—๋งŒ ์ ‘๊ทผํ•˜๋„๋ก ์ œํ•œํ•˜๋Š” ์ •์ฑ…์„ ๋งํ•œ๋‹ค.
์—ฌ๊ธฐ์„œ ๋งํ•˜๋Š” ์ถœ์ฒ˜๋ผ๋Š”๊ฒƒ์€ ํ”„๋กœํ† ์ฝœ, ํ˜ธ์ŠคํŠธ๋ช…, ํฌํŠธ๊ฐ€ ๊ฐ™๋‹ค๋Š” ๊ฒƒ์„ ์˜๋ฏธํ•œ๋‹ค.
๋™์ผ ์ถœ์ฒ˜ ์ •์ฑ…์˜ ๊ฒฝ์šฐ์— ๋ธŒ๋ผ์šฐ์ €์—์„œ ์ž๋™์œผ๋กœ ์ฟ ํ‚ค๊ฐ€ ์ฒจ๋ถ€๋œ๋‹ค๋Š” ํŠน์ง•์—์„œ ํ•ด๋‹น ๋ถ€๋ถ„์ด ๋ณด์•ˆ์ƒ์˜ ๋ฌธ์ œ๋ฅผ ๋ฐœ์ƒ์‹œํ‚ฌ ์ˆ˜ ์žˆ๊ฒ ๋‹ค๋Š” ์ด์Šˆ๋กœ ์ƒ๊ฒจ๋‚˜๊ฒŒ ๋˜์—ˆ๊ณ  ์ด๋ ‡๊ฒŒ ๋“ฑ์žฅํ•˜๊ฒŒ ๋œ ๋™์ผ ์ถœ์ฒ˜ ์ •์ฑ…์œผ๋กœ ์ธํ•ด CORS๊ฐ€ ๋ฐœ์ƒํ•˜๊ฒŒ ๋œ ๊ฒƒ์ด๋‹ค.


๐Ÿ”ฅ CORS ํด๋ผ์ด์–ธํŠธ ๋‹จ์—์„œ ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•

CORS ์—๋Ÿฌ ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•์€ ํฌ๊ฒŒ ํด๋ผ์ด์–ธํŠธ์—์„œ ํ•ด๊ฒฐ, ์„œ๋ฒ„์—์„œ ํ•ด๊ฒฐ์ด ์žˆ์ง€๋งŒ ์ด๋ฒˆ ํ”„๋กœ์ ํŠธ ๋™์•ˆ ์„œ๋ฒ„์—์„œ ํ•ด๊ฒฐ์€ ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋Š” ๋ฐฉ๋ฒ•์ด ์—†์—ˆ๊ธฐ์— ๋จผ์ € ํด๋ผ์ด์–ธํŠธ ์ธก์—์„œ ํ•ด๊ฒฐํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์ ์–ด๋ณด๊ณ ์ž ํ•œ๋‹ค.

1๏ธโƒฃ http-proxy-middleware ์‚ฌ์šฉํ•˜๊ธฐ(๋กœ์ปฌ)

1. ์„ค์น˜

  • http-proxy-middleware๋ฅผ ์„ค์น˜ํ•œ๋‹ค.
npm install http-proxy-middleware --save

2. setupProxy.js ์„ธํŒ…

  • src/setupProxy.js๋ฅผ ์ƒ์„ฑ
// src/setupProxy.js
const { createProxyMiddleware } = require('http-proxy-middleware');

module.exports = function (app) {
    app.use(
        createProxyMiddleware('/api', {
            target: 'https://api.openweathermap.org',
            changeOrigin: true,
        })
    );
};
  • โ€˜/apiโ€™๋Š” โ€˜/apiโ€™๋กœ ์‹œ์ž‘ํ•˜๋Š” endpoint๋ฅผ ๊ฐ€์ง„ api์™€ ๋ชจ๋‘ ๋งค์นญ์‹œํ‚จ๋‹ค.
  • target์—๋Š” url์˜ endpoint๋ฅผ ์ œ์™ธํ•œ ์ถœ์ฒ˜๋งŒ ๋ช…์‹œํ•œ๋‹ค. https://api.openweathermap.org/api์ธ ๊ฒฝ์šฐ, endpoint์ธ /api ์ œ์™ธํ•œ https://api.openweathermap.org๋ฅผ ๋ช…์‹œ
  • changeOrigin :ํ˜ธ์ŠคํŠธ ํ—ค๋”์˜ ์ถœ์ฒ˜๋ฅผ ๋Œ€์ƒ URL๋กœ ๋ณ€๊ฒฝ ํ•˜๋Š”์ง€ ์—ฌ๋ถ€์ด๋‹ค. CORS ์ฒ˜๋ฆฌ๋ฅผ ์œ„ํ•ด ์ถœ์ฒ˜๋ฅผ ์ˆ˜์ •ํ•ด์ฃผ์–ด์•ผ ํ•œ๋‹ค. ๊ธฐ๋ณธ๊ฐ’: false

3. App.js์—์„œ api ํ˜ธ์ถœ

// src/App.js
useEffect(() => {
  async function fetchdata() {
    const { data } = await axios.get('/api');
    console.log(data);
  }
  fetchdata();
}, []);
  • App.js์—์„œ url์˜ endpoint์ธ /api๋กœ api๋ฅผ ํ˜ธ์ถœํ•œ๋‹ค. proxy๋ฅผ ์„ธํŒ…ํ•˜๋ฉด ์„œ๋ฒ„๋ฅผ ์žฌ์‹œ์ž‘ ํ•ด์ฃผ์–ด์•ผ ํ•œ๋‹ค.

4. package.json์— ํ”„๋ก์‹œ ์ถ”๊ฐ€ํ•ด์ฃผ๊ธฐ
package.json ํŒŒ์ผ์—

"proxy": "https://api.openweathermap.org"

์ด ํ•œ์ค„์„ ์ถ”๊ฐ€ํ–ˆ๋”๋‹ˆ ๋กœ์ปฌ์—์„œ๋Š” ์ •์ƒ์ž‘๋™ํ•˜๋Š” ๊ฒƒ์„ ํ™•์ธํ–ˆ๋‹ค.

ํ•˜์ง€๋งŒ ๋ฐฐํฌ๊ณผ์ •์„ ๊ฑฐ์ณ์•ผ ํ–ˆ๊ธฐ์— ๋กœ์ปฌ์—์„œ์˜ CORS ์šฐํšŒ๋งŒ์œผ๋กœ๋Š” ๋ฌธ์ œ๊ฐ€ ํ•ด๊ฒฐ๋๋‹ค๊ณ  ๋ณผ ์ˆ˜ ์—†์—ˆ๋‹ค.
๊ฒฐ๊ตญ ๋ฐฐํฌ๋œ ์‚ฌ์ดํŠธ์˜ CORS ์šฐํšŒ ๋ฐฉ๋ฒ•๋„ ์ถ”๊ฐ€๋กœ ์•Œ์•„์•ผ ํ–ˆ๋‹ค. ๊ทธ ๋‚ด์šฉ์€ ์•„๋ž˜์™€ ๊ฐ™๋‹ค.



2๏ธโƒฃ Netlify proxy server ์„ธํŒ…(๋ฐฐํฌ)

ํ”„๋กœ์ ํŠธ ๋ฐฐํฌ๋ฅผ Netlify๋ฅผ ํ†ตํ•ด ๋ฐฐํฌํ–ˆ๊ธฐ ๋•Œ๋ฌธ์—, Netlify ํ”„๋ก์‹œ ์„œ๋ฒ„ ์„ธํŒ… ๋ฐฉ๋ฒ•์„ ์‹œ๋„ํ•ด๋ณด์•˜๋‹ค.

1. ๋ฃจํŠธ์— netlify.toml ํŒŒ์ผ ์ƒ์„ฑ
netlify.toml์€ ํ•œ๋งˆ๋””๋กœ netlify ์„ค์ • ํŒŒ์ผ์ด๋‹ค. ์—ฌ๊ธฐ์„œ ๋ฆฌ๋‹ค์ด๋ ‰ํŠธ ์„ค์ •์„ ํ•ด์ฃผ์ž.

[[redirects]]
  from = "/api/*"
  to = "https://api.openweathermap.org/:splat"
  status = 200
  force = true
  headers = {X-From = "Netlify"}
  • /api/*๋กœ ์‹œ์ž‘ํ•˜๋Š” ๋ชจ๋“  ์š”์ฒญ์„ ๋ฐ›์œผ๋ฉด, ๊ทธ๊ฑธ https://api.openweathermap.org/:splat์œผ๋กœ ๋ฆฌ๋‹ค์ด๋ ‰ํŠธํ•œ๋‹ค.
  • :splat
    ์™€์ผ๋“œ์นด๋“œ๋กœ, ํ•ด๋‹น ์œ„์น˜์— ๋งค์นญ๋œ ๊ฐ’์ด ๊ทธ๋Œ€๋กœ ์‚ฌ์šฉ๋œ๋‹ค๋Š” ์˜๋ฏธํ•œ๋‹ค.
    ์˜ˆ๋ฅผ ๋“ค์–ด, /api/* ํŒจํ„ด์—์„œ /api/weather๋กœ ๋“ค์–ด์˜ค๋Š” ์š”์ฒญ์ด ์žˆ๋‹ค๋ฉด, :splat์€ "weather"๊ฐ€ ๋œ๋‹ค. ์‹ค์ œ๋กœ๋Š” https://api.openweathermap.org/weather๋กœ ๋ฆฌ๋‹ค์ด๋ ‰ํŠธ ๋  ๊ฒƒ์ด๋‹ค.
  • status = 200
    ๋ฆฌ๋‹ค์ด๋ ‰์…˜์— ๋Œ€ํ•œ ์‘๋‹ต ์ƒํƒœ ์ฝ”๋“œ๋ฅผ 200์œผ๋กœ ์„ค์ •ํ•˜๋Š” ๊ฒƒ. ์ฆ‰, ๋ฆฌ๋‹ค์ด๋ ‰์…˜์ด ์„ฑ๊ณต์ ์œผ๋กœ ์ด๋ฃจ์–ด์กŒ์„ ๋•Œ ํด๋ผ์ด์–ธํŠธ์—๊ฒŒ 200 OK ์‘๋‹ต์„ ๋ณด๋‚ธ๋‹ค๋Š” ๋œป
  • force = true
    ์ด ์˜ต์…˜์€ ๋ฆฌ๋‹ค์ด๋ ‰์…˜์„ ๊ฐ•์ œ๋กœ ์ˆ˜ํ–‰ํ•˜๋„๋ก ํ•˜๋Š” ๊ฒƒ์ด๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, force = false๋กœ ์„ค์ •ํ•˜๋ฉด ๋ฆฌ๋‹ค์ด๋ ‰์…˜ ๋Œ€์ƒ์œผ๋กœ์˜ ๊ฒฝ๋กœ๊ฐ€ ์ด๋ฏธ ์กด์žฌํ•  ๋•Œ์—๋งŒ ๋ฆฌ๋‹ค์ด๋ ‰์…˜์ด ์ˆ˜ํ–‰๋˜์ง€๋งŒ, force = true๋กœ ์„ค์ •ํ•˜๋ฉด ํ•ญ์ƒ ๋ฆฌ๋‹ค์ด๋ ‰์…˜์ด ์ด๋ฃจ์–ด์ ธ์„œ ๋ชฉ์ ์ง€๋กœ ๊ฐ•์ œ๋กœ ์ด๋™ํ•˜๊ฒŒ ๋œ๋‹ค.
  • headers = {X-From = "Netlify"}
    ์ด๋Š” ๋ฆฌ๋‹ค์ด๋ ‰์…˜๋œ ์š”์ฒญ์— ํ—ค๋”๋ฅผ ์ถ”๊ฐ€ํ•˜๋Š” ๊ฒƒ์ด๋‹ค. ์—ฌ๊ธฐ์„œ๋Š” X-From์ด๋ผ๋Š” ํ—ค๋”๋ฅผ ์ถ”๊ฐ€ํ•˜๊ณ , ๊ทธ ๊ฐ’์œผ๋กœ "Netlify"๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค. ์ด๋Š” ๋ฆฌ๋‹ค์ด๋ ‰์…˜๋œ ์š”์ฒญ์ด Netlify์—์„œ ์‹œ์ž‘๋˜์—ˆ์Œ์„ ๋‚˜ํƒ€๋‚ด๊ธฐ ์œ„ํ•œ ๊ฒƒ์ด๋‹ค.

2. api ์„ค์ •

const PROXY = window.location.hostname === 'localhost' ? '' : '/api';

export const getWeatherData = async (lat, lon) => {
  try {
    const response = await fetch(
      `${PROXY}/data/2.5/weather?lat=${lat}&lon=${lon}&appid=${process.env.REACT_APP_WEATHER_API_KEY}&units=metric`,
      {
        method: 'GET',
        headers: {
          'Content-Type': 'application/json',
        },
      },
    );
    if (!response.ok) {
      throw new Error(`HTTP error! status: ${response.status}`);
    }
    const weatherData = await response.json();
    return weatherData;
  } catch (error) {
    return `Error: ${error.message}`;
  }
};

๋กœ์ปฌ์—์„œ๋Š” localhost๊ฐ€ ๋ถ™๊ณ , ๋ฐฐํฌ๋œ ์‚ฌ์ดํŠธ์—์„œ๋Š” ๋ฆฌ๋‹ค์ด๋ ‰ํŠธ๋˜๊ฒŒ ์„ค์ •์„ ํ•ด์ฃผ์—ˆ์ง€๋งŒ ์ž˜ ๋˜์ง€ ์•Š์•˜๋‹ค..
lat, lon, api key๋ชจ๋‘ ์ •์ƒ์ ์œผ๋กœ url์— ์ถ”๊ฐ€๋˜๋Š” ๊ฒƒ๋„ ํ™•์ธํ–ˆ๋Š”๋ฐ ๋ญ๊ฐ€ ๋ฌธ์ œ์˜€์„๊นŒ..?
์–ด๋””์„œ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•œ ๊ฒƒ์ธ์ง€ ๊ณ ๋ฏผํ•ด๋ณด์•˜์ง€๋งŒ ํ”„๋กœ์ ํŠธ ๋งˆ๊ฐ ๊ธฐํ•œ์ด ๋‹ค๊ฐ€์™€์„œ ์ผ๋‹จ ๋‹ค์Œ ๋ฐฉ๋ฒ•์œผ๋กœ ๋„˜์–ด๊ฐ”๋‹ค. ๋ฌธ์ œ์ ์ด ๋ญ”์ง€ ๋ฆฌํŒฉํ† ๋ง ์‹œ ์ฐพ์•„๋ณด๊ณ  ์ •๋ฆฌํ•ด์•ผ๊ฒ ๋‹ค.



3๏ธโƒฃ ๋‹ค๋ฅธ ์‚ฌ๋žŒ์ด ๋งŒ๋“  ํ”„๋ก์‹œ ์„œ๋ฒ„ ์ด์šฉํ•˜๊ธฐ(๋กœ์ปฌ, ๋ฐฐํฌ)

์š”์ฒญํ•ด์•ผ ํ•˜๋Š” URL ์•ž์— ํ”„๋ก์‹œ ์„œ๋ฒ„ URL ์„ ๋ถ™์—ฌ ์š”์ฒญํ•˜๊ฒŒ ๋˜๋ฉด ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ๋‹ค.

๊ตฌ๊ธ€์— ๊ฒ€์ƒ‰ํ•ด๋ณด๋ฉด ๋‚˜์˜ค๋Š” ํ”„๋ก์‹œ ์„œ๋ฒ„๋Š” ๋ช‡๊ฐ€์ง€๊ฐ€ ์žˆ๋‹ค.

์ด ์„œ๋ฒ„๋ฅผ API ์š”์ฒญ์˜ ์•ž์— ๋‘๋ฉด CORS ๋ฌธ์ œ๋ฅผ ์šฐํšŒํ•  ์ˆ˜ ์žˆ๋‹ค. ์‚ฌ์šฉ๋ฒ•์€ ๊ต‰์žฅํžˆ ๊ฐ„๋‹จํ•˜๋‹ค.
API ์š”์ฒญ ์ฃผ์†Œ ์•ž์— ํ•ด๋‹นํ•˜๋Š” ์„œ๋ฒ„ ์ฃผ์†Œ๋ฅผ ๋ถ™์ด๋ฉด ๋œ๋‹ค.

export const getWeatherData = async (lat, lon) => {
  try {
    const response = await fetch(
      'https://corsproxy.io/?' + // ์ด๋ ‡๊ฒŒ ์ถ”๊ฐ€ํ•ด์ฃผ๋ฉด ๋œ๋‹ค.
        encodeURIComponent(
          `https://api.openweathermap.org/data/2.5/weather?lat=${lat}&lon=${lon}&appid=${process.env.REACT_APP_WEATHER_API_KEY}&units=metric`,
        ),
      {
        method: 'GET',
        headers: {
          'Content-Type': 'application/json',
        },
      },
    );

    if (!response.ok) {
      throw new Error(`HTTP error! status: ${response.status}`);
    }

    const weatherData = await response.json();
    return weatherData;
  } catch (error) {
    return `Error: ${error.message}`;
  }
};

์ด๋Ÿฐ์‹์œผ๋กœ ์ž‘์„ฑํ•ด์ฃผ๋ฉด ์ž˜ ์ž‘๋™ํ•œ๋‹ค!!



์•ž์œผ๋กœ๋Š”..?

์ผ๋‹จ ์ด ๋ฐฉ๋ฒ•์œผ๋กœ ์ž„์‹œ๋กœ ํ•ด๊ฒฐํ•˜์˜€์ง€๋งŒ, ์œ„ ๋ฐฉ๋ฒ•๋“ค์„ ์ฐพ์•„๋ณด๊ณ ๋„ ์ ์šฉํ•˜์ง€ ๋ชปํ•ด ๋‹ค์‹œ ์ •๋ฆฌํ•ด๋ณด๋Š” ์‹œ๊ฐ„์ด ํ•„์š”ํ•  ๋“ฏ ํ•˜๋‹ค.
ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•์€ ๋งŽ์ง€๋งŒ ๊ทธ ๋ฐฉ๋ฒ•๋Œ€๋กœ ์ ์šฉํ•ด๋ณด์•„๋„ ์•ˆ๋˜๊ธฐ๋„ ํ•ด์„œ ๋งค์šฐ ๋‹นํ™ฉ์Šค๋Ÿฌ์› ๋˜ CORS ์˜€๋‹ค..
์ด์ „์— ๋ฉ˜ํ† ๋‹˜์ด ์„ค๋ช…ํ•ด์ฃผ์‹  ์ ์ด ์žˆ์ง€๋งŒ, ์†”์งํžˆ ๋ง๋กœ๋งŒ ๋“ค์–ด์„œ๋Š” ์ž˜ ์ฒด๊ฐ๋„ ์ดํ•ด๋„ ์•ˆ๋์—ˆ๋‹ค.
๊ทผ๋ฐ ์ด๋ ‡๊ฒŒ ์ง์ ‘ ๋ถ€๋”ชํ˜€๋ณด๋‹ˆ ์ด๋†ˆ์ด ์™œ ๊ฐœ๋ฐœ์ž๋“ค์˜ ๋จธ๋ฆฌ๋ฅผ ๋ถ€์—ฌ์žก๊ฒŒ ๋งŒ๋“œ๋Š” ์—๋Ÿฌ์ธ์ง€ ์•Œ ๊ฒƒ ๊ฐ™์•˜๋‹ค.๐Ÿ˜‚
ํ•˜์ง€๋งŒ ์ด ๊ธฐํšŒ์— CORS๋ž‘ ์ข€ ๊ฐ€๊นŒ์›Œ์ง„ ๊ฒƒ ๊ฐ™์œผ๋‹ˆ ์•ž์œผ๋กœ๋Š” ๊ฐ™์€ ์ƒํ™ฉ์—์„œ ํ•ด๊ฒฐ๋ฐฉ๋ฒ•๋“ค์„ ์ถ”๋ ค๋‚ด๋ฉฐ ๋Œ€์‘ํ•  ์ˆ˜ ์žˆ์„ ๊ฒƒ์ด๋‹ค.

0๊ฐœ์˜ ๋Œ“๊ธ€