๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ
๐ŸŒŒ | WEB DEV/React

[React] CORS ์—๋Ÿฌ ํ•ด๊ฒฐํ•˜๊ธฐ (http-proxy-middleware)

by KASSID 2023. 6. 30.

๋ชฉ์ฐจ

    728x90

    ์™ธ์ฃผ ์ž‘์—… ๋„์ค‘ api์— ์š”์ฒญ์„ ํ•  ๋•Œ CORS ์—๋Ÿฌ๋ฅผ ๋งˆ์ฃผํ•˜์˜€๋‹ค!

    ๋Œ€๋žต ์œ„์™€ ๊ฐ™์€ ์—๋Ÿฌ ๋ฉ”์„ธ์ง€๋ฅผ ์ฝ˜์†”์ฐฝ์—์„œ ๋ณผ ์ˆ˜ ์žˆ๋‹ค.

     

    1. CORS (Cross Origin Resource Sharing)

     

    1) CORS๋ž€?

    https://developer.mozilla.org/ko/docs/Web/HTTP/CORS/Errors

     

    CORS(๊ต์ฐจ ์ถœ์ฒ˜ ๋ฆฌ์†Œ์Šค ๊ณต์œ )๋Š” ๋‹ค๋ฅธ ์ถœ์ฒ˜ ๊ฐ„์˜ ์ž์› ๊ณต์œ ๋ฅผ ๊ฐ€๋Šฅํ•˜๊ฒŒ ํ•˜๊ณ 

    http ํ—ค๋”๋ฅผ ์ด์šฉํ•˜์—ฌ

    ์‹คํ–‰ ์ค‘์ธ ์›น ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ๋‹ค๋ฅธ ์ถœ์ฒ˜(Origin)์˜ ์ž์›์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋Š” ๊ถŒํ•œ์„ ๋ถ€์—ฌํ•˜๋„๋ก

    ๋ธŒ๋ผ์šฐ์ €์— ์•Œ๋ ค์ฃผ๋Š” ์ฒด์ œ์ด๋‹ค.

     

     

    2) SOP (Same Origin Policy)

    https://developer.mozilla.org/ko/docs/Web/Security/Same-origin_policy

    CORS ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•˜๋Š” ์ด์œ ๋ฅผ ์•Œ์•„๋ณด๊ธฐ ์ „์— ๋จผ์ € SOP์— ๋Œ€ํ•ด์„œ ์•Œ์•„๋ณด์ž.

     

    SOP(๋™์ผ ์ถœ์ฒ˜ ์ •์ฑ…)๋Š” ๊ฐ™์€ ์ถœ์ฒ˜(Origin)์—์„œ๋งŒ ๋ฆฌ์†Œ์Šค๋ฅผ ๊ณต์œ ํ•˜๋Š” ๊ทœ์น™์„ ๊ฐ€์ง„ ์ •์ฑ…์ด๋‹ค.

    ์ด ์ •์ฑ…์„ ์ด์šฉํ•˜์—ฌ ์™ธ๋ถ€์˜ ํ—ˆ๊ฐ€๋˜์ง€ ์•Š์€ ์š”์ฒญ์„ ๋ฐฉ์–ดํ•  ์ˆ˜ ์žˆ๋‹ค.

     

    ์—ฌ๊ธฐ์—์„œ ๋™์ผ ์ถœ์ฒ˜๋ž€ ์–ด๋–ค ๊ฒƒ์„ ๋งํ•˜๋Š” ๊ฒƒ์ผ๊นŒ?

     

    ์œ„์˜ URL์—์„œ protocol, host, port ๊ฐ€ ๊ฐ™์€ ๊ฒฝ์šฐ ๋™์ผ ์ถœ์ฒ˜์ด๋‹ค.

     

    ์ด SOP์— ๋ฐ˜ํ•˜์—ฌ ๋‹ค๋ฅธ ์ถœ์ฒ˜์˜ ๋ฆฌ์†Œ์Šค๊ฐ€ ํ•„์š”ํ•œ ๊ฒฝ์šฐ CORS๋ฅผ ์ด์šฉํ•˜๋Š” ๊ฒƒ์ด๋‹ค.

     

     

    3) CORS ์—๋Ÿฌ

    CORS ์—๋Ÿฌ๋Š” ํด๋ผ์ด์–ธํŠธ์—์„œ ์„œ๋ฒ„(API)์— ์ ‘๊ทผํ•˜์—ฌ ๋ฆฌ์†Œ์Šค๋ฅผ ๋ฐ›์•„์˜ฌ ๋•Œ

    ๋งŒ์•ฝ ๋™์ผ ์ถœ์ฒ˜๊ฐ€ ์•„๋‹ ๊ฒฝ์šฐ CORS Error๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค.

     

    ์ด๋†ˆ์ž‰..? (์œ ๋ช…ํ•œ CORS ๋ฐˆ๐Ÿฅน)

     

    ์ด ๋ฌธ์ œ๋ฅผ ์„œ๋ฒ„ ์ฐจ์›์—์„œ ํ•ด๊ฒฐํ•˜๋ ค๋ฉด

    CORS ๋ฏธ๋“ค์›จ์–ด๋ฅผ ์‚ฌ์šฉํ•˜๊ฑฐ๋‚˜, Access-access-control-allow-origin ํ—ค๋”๋ฅผ ์„ธํŒ…ํ•ด์ฃผ์–ด์•ผํ•œ๋‹ค.

     

    ๋งŒ์•ฝ, ์„œ๋ฒ„๋ฅผ ์ง์ ‘ ์ˆ˜์ •ํ•  ์ˆ˜ ์—†๋Š” ์ƒํ™ฉ์ด๊ฑฐ๋‚˜ ํƒ€ API๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ

    ํด๋ผ์ด์–ธํŠธ์—์„œ ์ฒ˜๋ฆฌ๋ฅผ ํ•ด์ฃผ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

    ์ด ์ƒํ™ฉ์ด ํ˜„์žฌ ๋‚ด๊ฐ€ ๋งˆ์ฃผํ•œ ๊ฒƒ์ด๋‹ค!

     

     

    2. React์—์„œ CORS ์—๋Ÿฌ ํ•ด๊ฒฐํ•˜๊ธฐ

    https://create-react-app.dev/docs/proxying-api-requests-in-development/

    (๊ณต์‹ ๋ฌธ์„œ)

     

    ํด๋ผ์ด์–ธํŠธ์—์„œ CORS ์—๋Ÿฌ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ํ”„๋ก์‹œ ์„œ๋ฒ„๋ฅผ ์ด์šฉํ•ด์•ผํ•œ๋‹ค.

    (ํ”„๋ก์‹œ ์„œ๋ฒ„ == ๋‹ค๋ฅธ ๋„คํŠธ์›Œํฌ์— ๊ฐ„์ ‘ ์ ‘์†ํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ฃผ๋Š” ์„œ๋ฒ„)

     

    ์ฆ‰, ํ”„๋ก์‹œ ์„œ๋ฒ„๋ฅผ ํ†ตํ•ด ๋ฆฌ์†Œ์Šค๋ฅผ ๊ฐ€์ ธ์˜ฌ ์„œ๋ฒ„์™€ ๋™์ผ ์ถœ์ฒ˜๋กœ ๋งŒ๋“œ๋Š” ๊ฒƒ์ด๋‹ค.

     

    ์ด๋ฅผ ์œ„ํ•œ ๋ฐฉ๋ฒ•์€ 2๊ฐ€์ง€๊ฐ€ ์กด์žฌํ•œ๋‹ค.

     

    1) package.json์— proxy ์ถ”๊ฐ€ํ•˜๊ธฐ

    //package.json
    {
      ...
      "proxy": "์›ํ•˜๋Š” ์„œ๋ฒ„(api)์ฃผ์†Œ"
    }

     

    ์œ„์™€ ๊ฐ™์ด package.json์„ ์ˆ˜์ •ํ•˜๋ฉด

     

    axios๋ฅผ ํ†ตํ•œ response๋ฅผ ํ•  ๋•Œ localhost:3000์„ ๋“ฑ๋กํ•œ ์ฃผ์†Œ๋กœ ๋ณ€๊ฒฝํ•œ๋‹ค.

     

    ํ•˜์ง€๋งŒ ์ด ๋ฐฉ๋ฒ•์€ ํ•˜๋‚˜์˜ api ์ฃผ์†Œ์—์„œ๋งŒ ๊ฐ€๋Šฅํ•˜๋ฏ€๋กœ

    ์—ฌ๋Ÿฌ api๋ฅผ ํ™œ์šฉํ•˜๊ณ  ์‹ถ์„ ๋•Œ๋Š” ์ ์ ˆํ•˜์ง€ ์•Š๋‹ค.

     

    2) http-proxy-middleware ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ํ™œ์šฉํ•˜๊ธฐ

    (1) ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์„ค์น˜

    npm install http-proxy-middleware

     

    (2) src/setupProxy.js ์ƒ์„ฑ ๋ฐ ์„ธํŒ…

    //setupProxy.js
    const { createProxyMiddleware } = require('http-proxy-middleware');
    
    module.exports = function (app) {
      app.use(
        createProxyMiddleware('/๊ฒฝ๋กœ1', {
          target: '์›ํ•˜๋Š” ์ฃผ์†Œ',
          changeOrigin: true,
        }),
      );
      
        app.use(
        createProxyMiddleware('/๊ฒฝ๋กœ2', {
          target: '์›ํ•˜๋Š” ์ฃผ์†Œ',
          changeOrigin: true,
        }),
      );
    
      ...
      
    };

    โ‘  createProxyMiddleware ('/๊ฒฝ๋กœ', { ... }) :  '/๊ฒฝ๋กœ' ๋กœ ์‹œ์ž‘ํ•˜๋Š” endpoint๋ฅผ ๊ฐ€์ง„ api๋ฅผ ๋งค์นญ์‹œํ‚จ๋‹ค.

        (endpoint == ๊ฐ™์€ URL์—์„œ ์„œ๋กœ ๋‹ค๋ฅธ ์š”์ฒญ์„ ํ•˜๋„๋ก ๊ตฌ๋ณ„ํ•˜๊ฒŒ ํ•ด์ฃผ๋Š” ๊ฒƒ)

     

    โ‘ก target : url์˜ endpoint ๋“ค์„ ์ œ์™ธํ•œ ์ถœ์ฒ˜ (์ฆ‰, ๊ฐ€์žฅ base url)

     

    โ‘ข changeOrigin : ํ˜ธ์ŠคํŠธ ํ—ค๋”์˜ ์ถœ์ฒ˜๋ฅผ target์œผ๋กœ ๋ฐ”๊ฟ€ ๊ฒƒ์ธ์ง€ ์—ฌ๋ถ€ (true๋กœ ํ•ด์•ผ ํ•จ!)

     

    (3) App.js์—์„œ ์š”์ฒญ

     

    // App.js
    
    
    //POST ์š”์ฒญ์ผ ๊ฒฝ์šฐ ์˜ˆ์‹œ
    
    //ํด๋ฆญ ์ด๋ฒคํŠธ ๊ด€๋ จ ํ•จ์ˆ˜ ๋“ฑ
    const handleSubmit = async (event)=>{
      event.preventDefault(); 
    
      axios.post('/๊ฒฝ๋กœ1', //setupProxy.js์— ์ถ”๊ฐ€ํ•œ endpoint์ด์–ด์•ผ ํ•จ
      {
        // ์š”์ฒญ์— ๋‹ด์„ ๊ฒƒ ์ถ”๊ฐ€
      },{withCredentials : true})
      .then((res)=>{
        console.log(res);
      })
      .catch((error)=>{
        console.log(error);alert("ERROR");
      })
      
      ... 
    
    }
      
      
    
    //GET ์š”์ฒญ์ผ ๊ฒฝ์šฐ ์˜ˆ์‹œ
    {
      ...
      
      async function fetchdata() {
        const { data } = await axios.get(
          '/๊ฒฝ๋กœ2',
        );
        console.log(data);
        
      ... 
    }

     

    ์œ„์™€ ๊ฐ™์ด POST, GET ์š”์ฒญ์„ App.js์—์„œ ์ •์˜ํ•œ๋‹ค.

     

     

    ๋!

     

     

    ์˜ˆ์‹œ โ–ผ

    ๋”๋ณด๊ธฐ

    ์˜ˆ์‹œ) ๋กœ๊ทธ์ธ POST

    //App.js
    const LoginForm = () =>{
      const [values, setValues] = useState({
        loginId: "",
        password: "",
      });
    
      const [error, setError] = useState(undefined);
    
      const handleSubmit = async (event)=>{
        event.preventDefault();
    
        axios.post("/login", 
        {
          loginId: values.loginId,
          password: values.password,
        },{withCredentials : true})
        .then((res)=>{
          console.log(res);
        })
        .catch((error)=>{
          console.log(error);alert("ERROR");
        })
      };
    
      return(
        <div className="login-page-container">
          <form onSubmit={handleSubmit}>
            <div className="field">
              <label htmlFor="loginId">์•„์ด๋””</label>
              <input
                type="text" name='loginId'
                onChange={(e)=>setValues({...values, loginId: e.target.value})}
                value={values.loginId}
              />
            </div>
            <div className="field">
              <label htmlFor="password">๋น„๋ฐ€๋ฒˆํ˜ธ</label>
              <input
                type="password" name='password'
                onChange={(e)=>setValues({...values, password: e.target.value})}
                value={values.password}
              />
            </div>
            {error ? <p className='error'>{error}</p> : <p></p>}
            <button type='submit' className='loginBtn'>๋กœ๊ทธ์ธ</button>
            <button>ํšŒ์›๊ฐ€์ž…</button>
          </form>
          
        </div>
      );
    }

     

    //setupProxy.js
    const { createProxyMiddleware } = require('http-proxy-middleware');
    
    module.exports = function(app){
      app.use(
        createProxyMiddleware('/login',{
          target:"https://www.kassid.net",
          changeOrigin: true,
        })
      )
    };

     

    ๋Œ“๊ธ€