-
지긋지긋한 CORS error. 이제 제대로 공부해보자.개발 2022. 7. 30. 14:13
프론트엔드 웹개발을 하다 보면 백엔드 서버에 보낸 요청이 CORS로 돌아오는 경우가 종종 있다. 분명히 나는 curl을 이용해서 테스트 했을 때는 문제가 없었는데 이상하게 브라우저상에서 요청을 보낼 때는 빨간 에러가 돌아오는것이 미칠 노릇일 것이다. 이번 포스트에서는 CORS 에러가 무엇인지, 그리고 어떻게 대응하는 것이 적절한 해결책인지 확인해보고자 한다.
1. CORS
CORS는 Cross Origin Resource Sharing에 약자다. Origin은 url 주소상에서 프로토콜, Domain 이름, 포트까지 포함한 개념이다. 예로 로컬에 nextjs 프론트엔드 서버를 3000번으로 띄웠다면 http://localhost:3000 이 Origin에 해당한다. Same Origin이란 같은 Origin을 뜻하고 Cross Origin 이란 서로 다른 Origin을 뜻하는 말이다. 여기에 붙은 Resrouce Sharing 의 단어까지 포함하면 CORS는 서로 다른 Origin간의 리소스 교환을 뜻한다.
2. CORS Error
같은 Origin상에서는 요청이 오는 곳과 처리하는 곳이 동일하기 때문에 특별히 보안상 처리해줄 이유가 없다. 그런데 다른 Origin에서 오는 요청이라면 내가 요청으로 받아온 결과를 믿을만한지 그렇지 않은지 검증하는 과정이 필요하다. 브라우저에서는 결과의 헤더 값을 통해 CORS를 확인한다. 한번 쯤은 봤을 법한 'Access-Control-Allow-Origin', 'Access-Control-Allow-Methods' 가 브라우저에서 CORS를 검증할 때 사용하는 값이다.
브라우저에서는 localhost:8080 서버에서 전달 받은 응답중 헤더에 Access-Control-Allow-Origin 값을 확인하고 이 값에 현재 Origin이 포함되는지 확인한다. 포함되어 있다면 CORS를 수행하고 그렇지 않으면 에러를 낸다. 앞서 사진의 빨간 줄은 Origin에 포함되어 있지 않았기 때문에 브라우저에서 CORS 허용하지 않아 발생한 것이다. Origin이 포함되어 있다면 Method도 확인하고 Content Type도 본다.
엄밀히 말해 CORS 에러는 브라우저에서 내뿜는 에러다. 그래서 curl 이나 postman을 이용해 서버에 요청하면 문제없이 응답이 날라오는데 프론트엔드 작업을 할 때만 CORS 에러가 발생하는 것도 브라우저가 미리 보고 개입했기 때문에 발생한다. 크롬, 사파리, 웨일 구분할 것 없이 모두 문제가 발생하는데 모두 동일한 규약을 따르고 있기 때문에 동일하게 CORS 에러를 내뿜는다.
3. CORS Solution
3.1 Access-Control-Allow-Origin 전체 허용
CORS 에러를 해결하는 방법으로 백엔드에서 모든 주소를 Access-Control-Allow-Origin 로 주면 간단히 해결한다. 실제로 스택 오버플로우에서 CORS 에러를 해결하는 방법으로 백엔드에서 응답에 아래 노드 코드처럼 모든 주소를 허용해 줘버리라는 (무책임한) 답변을 쉽게 접할 수 있다...
app.use((req, res, next) => { res.setHeader('Access-Control-Allow-Origin', "*"); res.header('Access-Control-Allow-Methods', 'GET, OPTIONS, PUT, POST'); res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization'); res.header('Access-Control-Allow-Credentials', 'true');
그러나 이 방법은 개발용도로는 쓸수는 있어도 리얼 배포용으로는 적절한 방법이 아니다. 이렇게 두는 경우 브라우저가 CORS 방어를 해줄 수 있는 최소한의 안전 장치를 날려버린 셈이기 때문에 보안에 취약해질 수 밖에 없다. 여기에서 어떻게 공격이 가능한지 자세한 설명이 있는데 간단히 정리하면 브라우저가 갖고 있는 쿠키 정보를 이용해서 다른 Origin 에서 공격이 가능하기 때문에 취약하다로 요약이 가능하다. 이해가 어렵다면 이 방식은 그냥 쓰지 않거나, 특정 도메인만 추가해서 사용하는 것을 추천한다.
3.2 프록시 서버 사용하기
브라우저에서 보낸 요청을 프론트엔드에서 받아서 대신 보내는 방법이다. 서버간의 데이터 교환상에서는 CORS가 이뤄지지 않기 때문에 프론트엔드 서버에서 받은 정보를 브라우저에 업데이트 해주기만 하면 된다. nextjs의 경우에는 next.config.js 라는 파일을 이용해 상대 주소에 대해서는 미리 요청하는 주소를 바꿔줄 수 있다. 이외에도 nginx를 이용해서 프록시를 대신 구현할 수 있다. 아래 코드는 모든 상대 요청에 대해서 localhost:8080 으로 보내는 nextjs 코드다. 브라우저에서 보내는 요청이 아니기 때문에 CORS에 걸리지 않게 된다.
/** @type {import('next').NextConfig} */ const nextConfig = { reactStrictMode: true, swcMinify: true, async rewrites() { return [ { source: '/:path*', destination: 'http://localhost:8080/:path*' } ] } } module.exports = nextConfig
'개발' 카테고리의 다른 글
REST, REST API, RESTful (0) 2022.09.02 nextjs 스크롤 저장 기억하기 (0) 2022.08.09 css - flex (0) 2022.07.11 css - position (0) 2022.07.10 kubernetes - Service (0) 2022.06.20