티스토리 뷰

Back-End/Django

Django(DRF) CORS 정책 해결

안톨리니 2023. 3. 6. 21:06

Product를 가져오는 API를 만들고 DRF를 통해 구현하고 테스트해봤지만

CORS정책에 의해 데이터를 가져오기 위한 접근이 차단되었다는 에러문구를 마주했습니다.

 

CORS정책이라는게 뭘까요??

CORS(Cross-Origin Resource Sharing)

 

CORS는 말 그대로 자신의 Origin과 다른 Origin에 대한 요청(교차 출처)의 대해서

자원을 공유할 것이냐? 에 대한 물음과 같습니다.

 

한 가지 예시를 들자면

 

http://localhost:3000에서 http://localhost:3000 서버로 자원 요청을 하는 건 문제없지만

http://localhost:3000에서 http://localhost:8000으로 요청을 하는 건 교차 출처 HTTP요청으로

( 도메인, 프로토콜, 포트가 하나라도 다르면 Origin(출처)이 다릅니다. )

SOP(Same-Origin Policy)를 위반하는 행위가 됩니다.

브라우저는 기본적으로 SOP라는 동일 출처 정책을 가지는데

동일 출처 정책은 어떤 출처에서 불러온 문서나 스크립트가 다른 출처에서 가져온 리소스와

상호작용하는 것을 제한하는 중요한 보안 방식입니다.

이러한 보안 방식 때문에 http://localhost:3000에서 http://localhost:8000으로 자원을 요청하게 되면

위에서 봤던 CORS에러가 나타납니다.

 

정확히 말하자면 SOP에러지만 CORS를 사용한다면 해결할 수 있습니다.

 

 

CORS 사용

CORS를 사용하는 방법은 굉장히 간단합니다.

사실 위에서 봤던 에러를 잘 보면 해결방법을 알 수 있는데

두 번째 줄을 보면

요청한 리소스에 'Access-Control-Allow-Origin'헤더가 없다고 나와있습니다.

 

그러므로 서버 쪽에서 해당 헤더를 설정하기만 하면 문제를 해결할 수 있습니다.

CORS헤더 관련 조작은 django-cors-headers를 통해서 쉽게 조작할 수 있습니다.

~ pip install django-cors-headers

먼저 django-cors-headers 모듈을 설치해 준 뒤 settings.py INSTALLED_APPS, MIDDLEWARE부분에

아래 코드를 추가해 줍니다.

#settings.py

INSTALLED_APPS =[
    ...
    'corsheaders',
    ...
]

MIDDLEWARE = [
    'corsheaders.middleware.CorsMiddleware'
    ...
]

참고로 MIDDLEWARE부분에 있는 코드는 상단에 위치하는 게 좋습니다.

이제 CORS를 통해 정보를 공유할 출처들을 적어주면 됩니다.

CORS_ALLOWED_ORIGINS = [
    "https://example.com",
    "https://sub.example.com",
    "http://localhost:8080",
    "http://127.0.0.1:9000",
]

만약 어떠한 출처든 상관없이 정보를 공유하려면 ALLOW_ALL값을 True로 설정해주면 됩니다.

CORS_ORIGIN_ALLOW_ALL = True

이외에도 CORS를 통해 공유하는 출처끼리 HTTP METHOD에 대한 제한도 줄 수 있는 등 다양한 기능들이 많지만

이번 포스팅과는 관련없기 때문에 패스하겠습니다.

(자세한 내용은 django-cors-headers공식문서에서 확인할 수 있습니다.)

django-cors-headers 공식문서

 

django-cors-headers

django-cors-headers is a Django application for handling the server headers required for Cross-Origin Resource Sharing (CORS).

pypi.org

모든 설정을 완료했으니 이제 테스트를 해보겠습니다.

export default function MainBase() {

  async function getProductData() {
  const response = await fetch('http://127.0.0.1:8000/api/product/');
  const products = await response.json();
  return products
}

  const loadProductData = async () => {
    const products = await getProductData();
    console.log(products);
  }
  
  return(
    <div>
      <h1>Hello World!</h1>
      <button onClick={loadProductData}>제품 가져오기!</button>
    </div>

  )
}

위 코드는 homepage에 있는 버튼을 클릭하게 되면

http://127.0.0.1:8000/api/product/ --> 해당 8000포트인 장고 백엔드에

api요청을 보내 product데이터를 가져와 콘솔에 출력합니다.

이전과 달리 성공적으로 동작하는 걸 확인할 수 있습니다.

HTTP 요청, 응답메시지 부분을 보면

요청메시지 헤더 부분에 Origin으로 자원을 요청한 사이트의 출처가 담겨있고

응답메시지 헤더에는 Access-Control-Allow-Origin이 *로 어떠한 출처든 상관없이

교차출처에 대한 자원 공유를 허락한다고 표시되어 있는 걸 확인할 수 있습니다.

 

지금까지 CORS를 통해 동일 출처 정책을 뛰어넘어

교차 출처끼리 자원을 공유할 수 있도록 만들어 보았습니다.

 

사실 CORS말고도 Proxy를 통해서도 위 방법을 해결할 수 있습니다.

 

 

 

Proxy 사용

 

먼저 프록시에 대해 조금 알아보자면

프록시는 로컬 네트워크와 더 큰 네트워크 간의 게이트웨이 같은 역할을 하는 중간 서버입니다.(중개자)

우리가 3000포트인 리액트앱에서 8000포트인 장고 백엔드 api에 요청을 보내면

해당 요청을 프록시서버가 가로챈 뒤 백엔드서버에 요청을 전달하고

이에 대해 받은 응답은 브라우저에게 전달하는 구조입니다.

프록시에 대한 설정은 이전 django-cors-headers에 비해 더 간단합니다.

해당 react-app의 package.json파일에 백엔드 출처를 입력하기만 하면 됩니다.

{
    ...
    "proxy": "http://localhost:8000"
    ....
}

이제 클라이언트 측에서의 api요청은 프록시 서버가 가로챈 뒤 해당 서버에 요청을 보냅니다.

가로챈 요청은 proxy에 설정된 출처로 다시 요청을 보내기 때문에

동일 출처로 보내는 모습처럼 되어 CORS문제가 발생하지 않습니다.

 

보통 api 요청에서 Path부분만 적게되면 (본인 출처 + Path)로 요청을 보내게 되는데

Proxy사용으로 (Proxy에서 설정한 출처 + Path)로 사실상 요청하는것이기 때문에

경로만 적어주면 됩니다.

CORS

async function getProductData() {
  const response = await fetch('http://127.0.0.1:8000/api/product/');
  const products = await response.json();
  return products
}

PROXY

async function getProductData() {
  const response = await fetch('/api/product/');
  const products = await response.json();
  return products
}

이제 다시 테스트해보면 api요청이 성공적으로 잘 동작하는 걸 확인할 수 있습니다.

필자의 테스트에서는 그저 api요청에 대한 성공만을 위해서 Proxy를 사용했지만

Proxy가 가진 장점을 더 극대화하기 위해서는

http-proxy-middleware모듈을 사용하는 걸 권장드립니다.

(Proxy에 대한 설정을 더 디테일하게 다룰 수 있습니다.)

http-proxy-middleware document

 

http-proxy-middleware

The one-liner node.js proxy middleware for connect, express and browser-sync. Latest version: 2.0.6, last published: 10 months ago. Start using http-proxy-middleware in your project by running `npm i http-proxy-middleware`. There are 3345 other projects in

www.npmjs.com

댓글
공지사항