Nginx로 React 라우터와 API를 함께 배포할 때 발생한 문제와 해결 과정

정혜인·2024년 11월 30일

클라우드

목록 보기
10/15

🛠️ 문제 상황

Docker와 Nginx를 활용해 React와 API를 배포하던 중, 홈 화면(/)을 제외한 다른 경로에서 새로고침 시 "404 Not Found" 오류가 발생하는 문제가 있었습니다.

API 경로(/api/, /ws/, /api-docs/)는 정상적으로 작동했지만, React Router로 구현된 경로(/about, /contact 등)에서 새로고침하거나 직접 URL을 입력하면 오류가 발생했습니다.


💡 원인 파악: React 라우터와 Nginx의 엇박자

React는 SPA(Single Page Application)으로, 클라이언트 측에서 라우팅을 처리합니다.

그런데 새로고침하거나 URL을 직접 입력하면 브라우저는 서버에 요청을 보내게 됩니다.

문제는 여기서 발생한 것인데요….

Nginx는 정적 파일 서버로, /about 같은 요청을 받으면 해당 경로에 파일이 있는지 확인합니다. 파일이 없으면 바로 404 에러를 반환해버리는 거죠.

결론적으로 지금 제 상황에서는 React 라우터가 개입할 기회조차 없었습니다.


🧨 기존 Nginx 설정

location / {
    proxy_pass http://frontend:80;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}

기존 코드에 대해 간단하게 설명해보자면,

먼저 /로 들어오는 요청을 React 프론트엔드 컨테이너로 전달합니다.

하지만 Nginx는 /about, /contact와 같은 요청이 들어왔을 때, 해당 경로에 대응하는 파일이 없으면 404 에러를 반환합니다.


✨ 해결 방안

React Router와 Nginx가 함께 동작하려면, 존재하지 않는 파일 요청은 모두 index.html로 리다이렉트하도록 설정해주어야 합니다.

React 앱이 클라이언트 측에서 해당 경로를 처리해 화면을 올바르게 렌더링합니다.


🔧 수정된 Nginx 설정과 의미

그래서 Nginx 설정을 아래처럼 수정했습니다.

location / {
    root /usr/share/nginx/html;  # React 정적 파일의 루트 디렉토리
    index index.html;            # 기본 파일 설정

    try_files $uri /index.html;  # 없는 파일 요청은 index.html로 전달
}
  1. try_files $uri /index.html
    • $uri는 요청된 경로(/about, /contact 등)에 해당하는 파일을 찾습니다.
    • 파일이 없으면 /index.html을 반환해 React가 경로를 처리하도록 만듭니다.
  2. root /usr/share/nginx/html
    • React 빌드 파일이 저장된 디렉토리를 지정합니다. 이 위치에서 index.html, JS/CSS 파일을 제공합니다.

📡 API와 WebSocket 처리

API(/api/)나 WebSocket(/ws/) 요청은 여전히 백엔드 서버로 전달되어야 하기 때문에

아래 코드는 그대로 두었습니다.

location /api/ {
    proxy_pass http://backend:3001;
    # ...기타 설정
}

location /ws/ {
    proxy_pass http://backend:3001/;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "Upgrade";
    # ...기타 설정
}

📝 최종 Nginx 설정 파일

아래는 수정 후의 Nginx 설정 파일입니다.

React 라우터와 API, WebSocket 요청이 모두 문제없이 처리됩니다.

worker_processes 1;

events {
    worker_connections 1024;
}

http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;

    server {
        listen 80;
        server_name example.com;

        return 301 https://$host$request_uri;  # HTTP를 HTTPS로 리다이렉트
    }

    server {
        listen 443 ssl;
        server_name example.com;

        ssl_certificate /etc/letsencrypt/live/pem주소
        ssl_certificate_key /etc/letsencrypt/live/pem주소
        ssl_protocols TLSv1.2 TLSv1.3;
        ssl_ciphers HIGH:!aNULL:!MD5;

        # API 처리
        location /api/ {
            proxy_pass http://backend:3001;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        }

        location /ws/ {
            proxy_pass http://backend:3001/;
            proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection "Upgrade";
            proxy_set_header Host $host;
        }

        # React 앱 처리
        location / {
            root /usr/share/nginx/html;  # React 빌드 파일 경로 (dockerfile에서 설정
            index index.html;

            try_files $uri /index.html;  # 없는 파일 요청은 index.html로 전달
        }
    }
}

🔍 결론

React Router를 사용하는 SPA를 Nginx로 배포할 때는 클라이언트 라우팅을 처리하기 위해 모든 요청을 index.html로 리다이렉트 해주어야 한다는 것을 알게 되었습니다……….

사실 nignx 설정은 이해하지 못하고 그냥 작성한 경우가 대부분이었는데, 이번에는 정말 제대로 어떤 역할을 하는 코드인지를 알고 사용할 수 있게 되었습니다…

어쨌든 이렇게 해서 새로고침이나 직접 URL 입력 시에도 React 앱이 정상적으로 작동하게 되었고,

API나 WebSocket 경로는 기존과 동일하게 처리할 수 있었습니다.


참고

0개의 댓글