ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Django, Nginx CORS 설정
    💻 프로그래밍/Django 2021. 1. 17. 21:51

    친구와 토이프로젝트를 진행하던 도중 CORS문제를 만났다.  Web에서 서버로 API를 호출했는데 아래와 같은 오류를 만났다.

    Access to XMLHttpRequest at '{target url}' from origin '{current url}' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

    아마 웹개발을 하는 사람들이라면 종종 마주친 오류였을 것이다. 간단히 말하자면 CORS 정책을 지키지 않았기 때문에 api호출이 금지 되었다는 것이다. 오류를 보면 Access-Control-Allow-Origin 가 헤더에 업기 때문에 CORS 정책에 의해 Block 되었다.

     

    이미 Django에서 CORS 관련 설정을 허용해주는 라이브러리가 있고, 이미 여러번 겪어봤던 문제이기 때문에 바로 해결될 문제이라고 생각했다. 먼저 CORS가 뭔지, Django에서 CORS를 어떻게 해결하는지 알아보겠다.

     

     

    1. CORS (Cross-Origin Resource Sharing)


    CORS 는 한국말로 하면 교차 출처 리소스 공유 라고 하는데 간단히 설명하자면 서로 다른 도메인에서 리소스를 공유(?)하지 못하도록 하는 정책이다.

     

    예를 들자면, A라는 도메인에서 B라는 도메인에 대해 API를 호출을 CORS정책으로 막는 것이다. 이렇게 CORS를 사용하는 이유는 보안적인 문제가 있다. 허용하지 않은 도메인에서 호출을 금지하는 것이다.

     

    반대로 CORS를 허용하는 경우는, 도메인 상관없이 모든 곳에서 API 호출(리소스 공유)을 가능하게 할때이다. 서비스 특성에 따라 다르고 나의 경우는 토이프로젝트를 진행하면서 친구 로컬(local)에서 개발서버 API를 호출해야 했기 때문에 CORS를 허용해줘야 했다.

     

    그래서 Django 에서 CORS 허용을 하는 방법에 대해 알아보겠다.

     

     

    2. Django 에서 CORS 허용


    Django 라이브러리중 django-cors-headers 라는 라이브러리가 있다. 간단한 config 설정을 통해 CORS 허용, 그리고 허용할 도메인을 정할 수 도 있다. 자세한 내용은 공식 문서를 참고하길 바라며, CORS전체 허용에 대해서만 알아 보겠다.

     

    pip install django-cors-headers  명령어로 최신 버전의 패키지를 설치한다.

    INSTALLED_APPSMIDDLEWARE에 cors 관련 내용을 추가해준다.

    1
    2
    3
    4
    5
    INSTALLED_APPS = [
        ...
        'corsheaders',
        ...
    ]
    cs

     

    1
    2
    3
    4
    5
    6
    MIDDLEWARE = [
        ...
        'corsheaders.middleware.CorsMiddleware',
        'django.middleware.common.CommonMiddleware',
        ...
    ]
    cs

    그리고 마지막으로 config에 CORS_ORIGIN_ALLOW_ALL = True 로 넣어주면 끝!

    이렇게 해주면 Access-Control-Allow-Origin : *  설정이 response  헤더에 추가되어 CORS 허용하게 된다.

     

    과연 정말 될까? 뭐 되겠지...................하고 예전같았으면 넘어가고 동작만 확인 했을 것이다! ㅋㅋㅋ

    config에 따른 동작이 어떻게 될지 CorsMiddleware를 보면 확인 할 수 있다. 디버깅 찍어서 동작을 직접 확인하는게 제일 확실하고, 패키지를 까서 보기만 해도 동작을 확인할 수 있다.

     

    process_response 메소드 로직중 CORS관련 내용
    config파일에 설정한 CORS_ORIGN_ALLOW_ALL 값을 가져오는 프로퍼티 함수

    로직을 살펴보면 CORS_ALLOW_ALL_ORIGINTrue, CORS_ALLOW_CREDENTIALSFALSE인 경우 

    Access-Control-Allow-Origin : * 

    를 헤더에 넣어주는걸 알 수 있다. 여기서 CORS_ALLOW_ALL_ORIGIN은 우리가 config에 세팅한 CORS_ORIGIN_ALLOW_ALL 값을 가져와 설정해고 그 결과를 리턴해주는 property함수이다. CORS_ALLOW_CREDENTIALS는 default 값이 False.

     

    여기까지 보면, Django에서 CORS허용을 해주는 방법에 대해 알게 되었을 것이다. 그리고 이것만 하면 나도 동작이 될 줄 알았다...

     

    어...음?!

    근데, 왜 Why?!!!! 동일하게 CORS 오류가 발생... 프론트엔드에서도 뭐 해줘야 하나...?

    이것저것 생각해보다가 결론은 Niginx 밖에 없다고 생각했고 관련 내용을 찾아봤다.

     

     

    3. NginX 에서 CORS 허용


    Django App 에서 CORS를 허용을 해줬지만, 결국에 Niginx를 통해서 repsonse를 전달하기 때문에 Nginx에서 CORS 설정을 해줘야 했다. 게다가 Nginx를 proxy처럼 사용하고 있었기 때문에 Nginx에 설정을 따로 해주는게 맞다고 생각 되었다. <-잘못된 생각(이유는 결론에..)

    1
    2
    3
    4
    location / {
       add_header 'Access-Control-Allow-Origin' '*';
        proxy_pass "블라블라블라"
    }
    cs

    열심히 구글링을 해보고 nginx.conf 쪽 CORS 허용해주는 방법을 찾았는데 위처럼, 

    location / 에 add_header 'Access-Control-Allow-Origin' '*'; 를 넣어주면 된다고 했다. 

     

    Nginx를 restart 하고 다시 API를 호출했는데, 그래도 되지 않는 것이였다.

    2차 멘붕...

    흠... 그래서 다시 열심히 구글링 하다가 발견한게 Nginx에서 header를 적용할때 원본 서버(나의 겨우 Django App)의 헤더가 있다면 저 설정만으로는 바뀌지 않는다는 스택오버플로우 내용을 확인 했다.

    대충, proxy_hide_header 를 사용해야 하위 서버의 헤더 옵션에 Niginx 헤더 옵션을 적용할 수 있는 권한을 가져올 수 있다라는 내용이였다. proxy_hide_header를 사용하지 않은 value는 스택된다라고 한다.

    1
    2
    3
    4
    5
    location / {
        proxy_hide_header Access-Control-Allow-Origin;
        add_header 'Access-Control-Allow-Origin' '*';
        proxy_pass "블라블라블라"
    }
    cs

    그래서 위처럼 설정을 해야 하위 서버의 헤더 옵션을 덮어 씌울수 있다.

    그리고 API호출을 해보니 정상적으로 호출되는 것을 확인 할 수 있었다!!!!

    개행복

     

    4. 정말 Nginx 헤더 세팅이 문제였을까?


    그래서 이문제를 해결하고

    Nignx를 사용하면 Django 에서 CORS 설정을 해줘도, 따로 NginX에서 안해주면 안되는 구나..

    라고 생각을 했는데.... 지금 글을 쓰면서 차근차근 다시 생각해보니...

    1. Nginx에서 add_header를 해줬는데, 안되었다 
    2. 이미 Django App response header가 있기 때문에 Nginx header 세팅은 적용되지 않는다.
    3. 2번 때문에 CORS오류 나니 Nginx에서 proxy_hide_header  Access-Control-Allow-Origin 로 Nginx가 헤더를 세팅할 수 있는 권한을 가져온다.
    4. CORS 문제 해결!

    어...음...

    2번...에서 오류가 났다 -> Django App response Header에 Access-Control-Allow-Origin 값이 없거나 잘못된 경우

    일단 로컬에서 확인할때는 CORS_ORIGIN_ALLOW_ALL = True 로 해주면 CORS 허용되는건 맞고 header에 들어가 있는 것도 확인했다.

     

    곰곰히 생각하다가... 로컬은 되었다? + ALLOWED_HOSTS .... 이 두가지에 꽂혔다..

    결론은 Nginx문제가 아니고 ALLOWED_HOSTS 문제였다😭

     

    로컬에서는 DBUG=True라서 ALLOWED_HOSTSlocalhost는 허용이라 문제없이 호출 되었던 거였고, 실서버에서는 DEBUG=False라서 ALLOWED_HOSTS 설정에 따라 허용된 HOST만 접근이 가능 했던 것이다.

     

    그래서 django 미들웨어(Middleware)에서 CORS middleware 를 통과 하기전에 허용되지 않은 host접근을 차단했기 때문에 CORS오류도 프론트엔드에서 난것이다.

     

    Nginx config에서 header 설정을 제거하고 django config 파일에 ALLOWED_HOSTS = ['*']를 넣어주니까 제대로 동작이 되었다...ㅎㅎㅎ

    이상한 곳에서 삽질을 했던 것이다 ㅋㅋㅋㅋㅋㅋㅋㅋㅋ

     

    결론 정리

    1. Django config ALLOWED_HOSTS 설정을 안해줘서 CORS 미들웨어 통과 전에 접근 제한됨.
    2. CORS설정은 Django APP, Nginx 두곳에서 가능

     

    후... 다행히 블로그로 글을쓰면서 뭔가 이상한점을 발견했고 정확히 이유를 알게되었다.ㅋㅋㅋ 뭐 이렇게 삽질하면서 배우는거지...ㅋㅋㅋ 무튼 정확한 이유를 알게 되어서 기분좋게 하루를 마무리 할 것 같고, 이 글을 보는 다른 분들도 뭔가 이상하다 싶거나 새로 배운것들은 글로 정리해가며 다시한번 확인해 보길 바란다.

     

     

    댓글

운동하는 개발자 JAY-JI