React/기타

[React]CORS 에러 해결

DevStory 2021. 6. 25.

사전 지식
Client-Server 구조에 대한 이해가 필요합니다.
request와 response에 대한 이해가 필요합니다.
url에 구조에 대한 이해가 필요합니다.
proxy에 대한 이해가 필요합니다.

Client에서 Server로 API 통신을 했는데, 아래 사진처럼 에러가 발생하는 경우가 있습니다.

(REST, SOAP, GraphQL 등 다양한 API 통신 방법이 많으므로 API 통신이라고 지칭하겠습니다.)

Access to fetch at 'http://localhost:3001/test' from orgin 'http://localhost:3000' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response servers your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.

 

아래는 에러 내용을 번역한 글입니다.

 

『 http://localhost:3000에서 http://localhost:3001/test로 fetch로 접근할려고 했는데, CORS 정책에 의해서 막혔다.

request header에 'Access-Control-Allow-Origin'에 대한 설정이 안되어 있어서 response에 접근할 수 없으니깐, CORS을 사용안할려면 request header에 'no-cors'를 설정을 해라.  』

 

그렇다면, CORS 정책은 무엇인지 'Access-Control-Allow-Origin'에 대한 설정은 어떻게 하는지 간단하게 알아보도록 합니다.


CORS(Cross-Origin Resource Sharing)

- 지정된 도메인 외부에 있는 자원에 대한 접근을 통제하는 브라우저 메커니즘

- 자원에 대한 접근은 http 헤더를 사용하여 허용함

- SOP(Same-Origin Policy)가 확장된 정책

 

SOP(Same-Origin Policy)

- 동일한 port, http, host에서 자원에 대한 접근을 허용

 

자원은 서버에서 응답(Response)한 데이터라고 생각하시면 됩니다.

아래 표는 http://localhost:3000에서 다른 URL에 접근하였을 경우 자원에 대한 접근 유무를 나타냅니다.

접근한 URL 허용 여부 이유
http://localhost:3000/login O 동일한 port, http, host
http://localhost:3000/logout O 동일한 port, http, host
http://localhost:9999/login X port가 다름
https://localhost:3000/login X http가 다름
http://en.localhost:3000/login X host가 다름

해결 방법

크게 2가지 방법이 존재합니다.

 

첫 번째 방법으로는 proxy 설정을 하거나

두 번째 방법으로는 response headerAccess-Control-Allow-Origin 설정을 합니다.

두 번째 방법을 사용하게 되면, 요청마다 response에 header를 설정해주어야 합니다.


1. proxy 설정

[FrontEnd - package.json]

{
  ...,
  "proxy": "http://localhost:3001",
  ...
}

package.json 파일을 수정합니다.

"proxy": 접근할 도메인

 

[FrontEnd]

fetch('/test')
  .then((response) => {
    if(response.ok) {
      return response.json();
    }  
    throw new Error('Network response was not ok.');
  }).then((data) => {
    console.log(JSON.stringify(data));
  }).catch((error) => {
    console.log(`error: ${error}`)
});

End-Point만 작성해도 됩니다.


2. response header에 Access-Control-Allow-Origin 설정

[FrontEnd]

fetch('http://localhost:3001/test')
  .then((response) => {
    if(response.ok) {
      return response.json();
    }  
    throw new Error('Network response was not ok.');
  }).then((data) => {
    console.log(JSON.stringify(data));
  }).catch((error) => {
    console.log(`error: ${error}`)
});

 

[BackEnd - Node Express]

app.get('/test', async (req,res) => {
  try{
    res.header("Access-Control-Allow-Origin", "*");
    const pool = await poolPromise;
    
    const result = await pool.request()
         .input('B', sql.Numeric, 2)
         .execute('KJS_TEST_PROCEDURE');
        
    res.send(JSON.stringify(result));
  } catch(err) {
    res.status(500);
    res.send(err.message);
  }
});

app.get('/about', async(req,res) => {
  res.header("Access-Control-Allow-Origin", "*");
  res.send('about.html');
});

app.get('/about', async(req,res) => {
  res.header("Access-Control-Allow-Origin", "*");
  res.send({ token: 'testToken' });
});

Node에서 res.header("Access-Control-Allow-Origin", "*"); 추가합니다.


no-cors로 설정시 문제사항

에러 메세지를 보았을 때, 'no-cors 설정을 하면 되는거 아닌가?'라는 의문이 있을수도 있습니다.

no-cors로 설정한다는 의미는 cors 정책을 사용안한다는 의미이므로 동일한 port, http, host가 아닐 경우 자원에 접근이 불가능합니다.

그리고 JavaScript에서 response의 header와 body를 볼 수 없는 문제가 발생합니다.

 

아래는 no-cors로 설정했을 경우 발생하는 문제점입니다.

fetch('http://localhost:3001/test', {
      mode : 'no-cors',
})
.then((response) => {
    if(response.ok) {
      return response.json();
    }
    throw new Error(`Network response was not ok.`);
}).then((data) => {
      console.log(JSON.stringify(data));
}).catch((error) => {
      console.log(`error: ${error}`)
});

mode를 'no-cors'로 설정하였고 http://localhost:3001/test로 API 요청을 하는 코드입니다.

크롬 개발자 도구에서 4~5번째 줄에서 BreakPoint를 설정합니다.

 

여담이지만, fetch 메소드는 Promise 기반의 메커니즘입니다.

페이지가 존재하지 않는 404 error에 대해 정상적으로 처리가 되지 않으므로 response.ok가 'true'의 값을 가지고 있는지 확인해야 합니다.

 

BreakPoint에서 response의 값을 확인하였습니다.

body는 null이고 headers에서 ok는 false, status는 0으로 나옵니다.

 

하지만, 크롬 개발자 도구에서 Network 탭에서 통신 결과를 확인해보면, 정상적으로 통신이 되었음을 확인 가능합니다.

실제로 BackEnd에서는 아래 데이터를 전달하고 있습니다.

하지만, 크롬 개발자 도구에서 Response 탭을 확인해보면, data를 불러오는데 실패하였다고 합니다.

즉, 'no-cors'를 설정하는 것은 해결 방법이 되지 않습니다.


CORS 통신 과정

대부분 알고 있는 Client와 Server구조입니다.

CORS에 대해 이해하기 위해서는 JS와 Browser 관점에서 이해를 해야합니다.

자바스크립트에서 fetch()를 실행하면 Browser는 Server로 요청을 합니다.

그리고 Server는 요청에 대한 응답을 합니다.

여기까지는 정상적으로 동작하나 CORS 정책에 의해 자바스크립트에서 response 데이터를 접근 불가능합니다.

 

개발자 도구를 확인하면, API 통신은 정상적으로 이루어지고 있으나 Access-Control-Allow-Origin 설정이 되어 있지 않아서 문제가 발생합니다.

 

1. Access-Control-Allow-Origin 허용한 상태

2. Access-Control-Allow-Origin 허용하지 않은 상태

두 캡처 사진에서 볼 수 있듯이 응답 상태는 200이지만, Access-Control-Allow-Origin에서 차이가 있는 것을 확인 가능합니다.

그리고 Access-Control-Allow-Origin 허용하지 않은 상태에서는 CORS 정책 에러가 발생합니다.


마무리

React와 Node Express와 API 통신 중 발생한 에러를 해결하는 방법에 대해 정리하였습니다.

추후 시간이 될 때, CORS에 대해 상세한 내용을 포스팅 하도록 하겠습니다.

반응형

댓글