안녕하세요. 이번 시간에는 웹 개발자라면 누구나 한 번은 겪는다는 CORS 문제에 대해서 포스팅해보겠습니다. 포스팅 자체는 노드 서버를 기반으로 하지만 노드 서버가 아니더라도 해결할 수 있는 방법을 포스팅 최하단에 적어두었습니다.
클라이언트에서 AJAX 요청을 보내는데 갑자기 다음과 같은 에러가 뜰 때가 있습니다. 같은 요청이더라도 서버에서 서버로 보냈을 때는 되는데 브라우저에서 서버로 보내는 것은 안 되니 당황스러울 것입니다.
Failed to load https://stackoverflow.com/: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'https://www.zerocho.com' is therefore not allowed access. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
이 에러를 재현해보려면 제 블로그에서 F12로 개발자 도구를 연 후, 다음 코드를 복사해서 콘솔 탭에 붙여넣으시면 됩니다.
var xhr = new XMLHttpRequest();
xhr.onload = function() {
console.log('xhr loaded');
};
xhr.open('GET', 'https://stackoverflow.com/');
xhr.send();
재밌는 것은 주소를 스택오버플로우 대신 https://zerocho.com으로 바꾸면 잘 동작합니다. 당연한 것이, zerocho.com에서 stackoverflow.com으로 요청을 보내는 것은 도메인이 다르기 때문에 CORS 에러가 뜨는 것이고, zerocho.com에서 zerocho.com으로 보내는 것은 도메인이 같아서 괜찮은 것입니다.
var xhr = new XMLHttpRequest();
xhr.onload = function() {
console.log('xhr loaded');
};
xhr.open('GET', 'https://www.zerocho.com/');
xhr.send();
이제 콘솔에 xhr loaded가 뜨면서 성공적으로 응답이 왔습니다.
위의 에러는 보안상의 이유로 브라우저들이 다른 도메인에게 XHR 요청을 보내는 것을 제한한 것입니다. 다행히 피해갈 수 있는 방법이 있습니다. 하지만 클라이언트 쪽에서는 힘들고 응답을 받는 서버쪽에서 해결해야 합니다. 클라이언트는 누군지 모르니까 클라이언트의 요청을 함부로 믿을 수 없는 탓이겠지요. 제가 아무리 스택오버플로우에 XHR 요청을 보내려해봐도 스택오버플로우가 저를 CORS 허용 목록에 추가하지 않는 이상 되지 않는 것입니다.
익스프레스에서는 정말 간단하게 해결할 수 있습니다.
npm i cors
cors 패키지를 설치한 뒤, CORS 요청을 허용하고자 하는 익스프레스 라우터에서 다음과 같이 해주면 됩니다.
const cors = require('cors');
const express = require('express');
const router = express.Router();
router.get('/', cors(), (req, res) => { res.send('cors!') });
모든 라우터에 cors()를 적용하고 싶다면 다른 미들웨어들이 있는 부분에 app.use(cors())
를 합니다.
단, cors()
의 경우에는 모든 요청 오리진을 허용하는 것이기 때문에 위험하니 cors({ origin: 허용 오리진 주소 })
처럼 일부 허용할 주소를 넣는 게 좋습니다. cors({ origin: 'https://www.zerocho.com' })
이런 식으로요.
익스프레스를 쓰지 않더라도, 서버가 노드가 아니더라도 기본적인 원리는 간단합니다. 요청 응답 헤더에 Access-Control-Allow-Origin: '*'을 넣어주면 됩니다. '*'은 모든 요청 오리진을 허용하는 것이기 때문에 위험하니 이 부분만 허용하는 오리진으로 바꿔주면 되겠죠.
노드의 경우는 res.writeHead(200, { 'Access-Control-Allow-Origin': '*' });
이렇게 하면 됩니다. 다른 서버도 응답 헤더를 다음과 같이 바꿔줍시다.
크롬 등의 브라우저는 localhost에서는 CORS 요청이 안 되도록 막아두기도 하여 저렇게 허용을 해줘도 안 될 수도 있습니다. localhost의 경우에는 안 돼도 너무 당황하지 맙시다.
또한 CORS 외에도 CORB(cross origin read blocking) 현상도 있습니다. CORS를 허용했더라도 POST, PUT, DELETE 요청에서 json을 전송하는 경우 요청이 차단됩니다. 이럴 때는 json 대신 www-form-urlencoded 형식으로 데이터를 보내면 됩니다.
Access-Control-Allow-Origin 외에도 관련 헤더들이 많은데 모질라 에 잘 설명되어 있습니다.
콘솔탭에
var xhr = new XMLHttpRequest();
xhr.onload = function() {
console.log('xhr loaded');
};
xhr.open('GET', 'https://www.zerocho.com/');
xhr.send();
입력했더니 해결됐네요!!
초보라서 이해도 못하구 무작정 해봤거든요..
감사합니다!