본문 바로가기
React

프론트와 백에서의 CORS 문제 완벽 해결법

by kingyejin 2024. 8. 9.

현재 프론트는 REACT, 백엔드는 Django를 사용하고 있다.

두 서버 모두 도메인이 있으며, Nginx가 설치되어 있는 상황이다.

 

편의를 위해 백엔드의 도메인은 http://back.com

프론트의 도메인은 http://front.com 이라고 하겠다.


CORS 문제 해결 전 기본적인 프론트 백엔드 연동 과정을 정리해보자

=> 다들 여기까진 한 상태일 것이므로 바로 CORS 문제 해결방법으로 넘기셔도 된다.

 

 

1) 프론트의 env 파일 내 URL을 백엔드의 도메인(http://back.com/)으로 설정 후, build 파일을 프론트 서버에 올린다.

*build 파일을 프론트 서버에 올리는 방법은 이 전 게시글에 자세히 설명해놨다.

env 파일

 

 

 

2) 브라우저에서 프론트 토메인(http://front.com/)으로 접속한다.

여기까지 해주면 아마 프론트의 서버 설정만 제대로 해준 이상, 구성해준 프론트 화면이 뜰 것이다.

 

하지만..!

이제 아래와 같은 CORS 문제가 뜰 것이다.

/#/settings:1 Access to XMLHttpRequest at 'http://back.com' from origin 'http://front.com' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.

[CORS 문제 해결방법]

 

그럼 이제 본격적으로 CORS 문제 해결방법을 알아보자.

CORS 문제를 해결하려면 크게 다음의 과정을 거쳐야 한다.

1) 백앤드 (저자는 Django의 setting.py) 설정 수정
2) 백앤드 서버에 설치한 nginx.conf 파일 설정 수정

 


1. 백앤드  설정 수정

1) 'djangocors-headers' 설치

pip install django-cors-headers

 

2) 백앤드 설정 파일 ('setting.py') 수정

* 나는 Django를 사용했으며, 각각의 개발방법에 맞는 설정 파일에 들어가서 아래와 같이 수정해주면 된다.

 

아래에 있는 것을 모두 추가해주되, 여기서 중요한 것은 바로  ALLOWED_HOSTS = [*] 이다.

이것을 설정해줘야 CORS 문제가 해결된다.

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

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

CORS_ALLOW_ALL_ORIGINS = True  # 모든 도메인에서의 요청을 허용

ALLOWED_HOSTS = ["*"]

# 또는 특정 도메인만 허용
CORS_ORIGIN_WHITELIST = (
    "https://front.com",
    "https://localhost",
)

# 추가 설정
CORS_ALLOW_METHODS = [
    'GET',
    'POST',
    'PUT',
    'PATCH',
    'DELETE',
    'OPTIONS',
]

CORS_ALLOW_HEADERS = [
    'content-type',
    'authorization',
    'x-requested-with',
]

 

해당 과정을 모두 해주었으면 이제 백엔드 서버 문제는 없다.

nginx 서버를 수정해주러 가자.


2. nginx.conf 파일 설정 수정

여기서 중요한 것은 수정해야 하는 파일이 nginx.conf 라는 것이다!

 

헷갈리면 안되는 것이

 /etc/nginx/nginx.conf (메인 설정 파일)

 /etc/nginx/sites-available/default (개별 서버 설정)

 두 파일은 완전히 다르다는 것을 인지해야 하며, 우리는 메인 설정 파일을 수정해줘야 한다!


그렇다면 두 파일의 차이점은 무엇일까?

 

nginx.conf는 메인 설정 파일로, events 및 http 블록을 포함할 수 있다.

반면 default 파일은 events 및 html 와 같은 블럭은 포함하지 못하고 server 블럭을 주로 포함하고 다룬다.

default 파일은 certbot으로 인증서를 설치할 때, 서버 네임을 지정해주기 위해 사용한다.

즉, CORS 문제를 해결하기 위해 사용하는 문서가 아닌 것이다.

 

이에 CORS 관련된 문제를 해결하기 위한 코드는 default 파일이 아닌 해당 nginx.conf에 설정해주어야 한다.

 

너무 당연한 소리지만 저자는 계속 아래 코드를 default 파일에 해놓고서는

CORS 문제가 왜 해결이 안되지 하며 삽질을 하고 있었기 때문에 보시는 분들도 꼭꼭! 다시 한 번 확인하기 바란다. 


백앤드 - [/etc/nginx/nginx.conf]

 

이제 가장 중요한 nginx.conf 설정 부분이다.

아래 코드와 같이 설정해주되, 여기서 중요한 건  if ($request_method = 'OPTIONS') { ~ } 부분이다.

 

현재 오류를 보면  "Response to preflight request doesn't pass access control check"인데

preflight는 OPTIONS 메소드를 사용하기 때문에 다음과 같이 location 내에 따로 설정해주어야 한다.

*참고로 add_header는 location 블럭 내에서만 사용할 수 있다!

if ($request_method = 'OPTIONS') {
                add_header 'Access-Control-Allow-Origin' 'https://front.com';
                add_header 'Access-Control-Allow-Methods' 'GET, POST, DELETE, PATCH, OPTIONS';
                add_header 'Access-Control-Allow-Headers' 'Content-Type, Authorization';
                add_header 'Access-Control-Allow-Credentials' 'true';
                return 204;
            }

그리고  처음엔 계속 CORS 문제가 나니까 아래와 같이 설정해주어 차라리 모두 허용해버리려고 했다.

add_header 'Access-Control-Allow-Origin' '*';

 

 그런데 이 코드는 아래의  Credentials 설정 코드와 아예 상반된 내용이기 때문에 두 개를 함께 쓸 수 없다.

add_header 'Access-Control-Allow-Credentials' 'true';

 

 

그래서 Credentials 부분 코드를 빼거나 아니면 '*'가 아닌 특정 도메인을 가르키게 해야한다.

그래서 계속 두 개를 같이 했을 때 CORS 문제가 났던 것이다.

 

근데 또 CORS 문제는 Credentials 설정을 꼭 해주어야 해결된다하여

Credentials 부분 코드를 빼지 않고 프론트 도메인만 안전하게 허용하는 식으로 해주었다.


 

이제 아래 코드처럼만 해주면 CORS 문제가 완벽 해결될 것이다!!

user www-data;
worker_processes auto;
pid /run/nginx.pid;
include /etc/nginx/modules-enabled/*.conf;


events {
    worker_connections 1024;
}

http {
    upstream backend {
        server localhost:8100; #8100은 백앤드(장고)포트 번호
    }

    server {
        server_name back.com;

        location / {
            if ($request_method = 'OPTIONS') {
                add_header 'Access-Control-Allow-Origin' 'https://front.com';
                add_header 'Access-Control-Allow-Methods' 'GET, POST, DELETE, PATCH, OPTIONS';
                add_header 'Access-Control-Allow-Headers' 'Content-Type, Authorization';
                add_header 'Access-Control-Allow-Credentials' 'true';
                return 204;
            }

            proxy_pass http://backend;
            add_header 'Access-Control-Allow-Credentials' 'true';
            add_header 'Access-Control-Allow-Origin' 'https://front.com' always;
            proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection "upgrade";
            proxy_set_header Origin "";
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header Host $http_host;
        }


    listen 443 ssl; # managed by Certbot
    ssl_certificate /etc/letsencrypt/live/api.goatfarm.ai/fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/api.goatfarm.ai/privkey.pem; # managed by Certbot
    include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot

}


    server {
    if ($host = api.goatfarm.ai) {
        return 301 https://$host$request_uri;
    } # managed by Certbot


        server_name api.goatfarm.ai;
    listen 80;
    return 404; # managed by Certbot