문제의 핵심 원인: “서버는 /about이라는 파일을 모른다”
- 클라이언트 사이드 라우팅 (React Router의 작동 방식):
- 사용자가 웹사이트에 처음 접속하면 (
http://example.com/), 서버는index.html파일 하나만 보내줍니다. - 이후 사용자가 메뉴를 클릭하여
/about페이지로 이동하면, React Router는 브라우저의 주소창만/about으로 바꾸고, 서버에 새로운 요청을 보내지 않습니다. 대신, 미리 로드된 JavaScript가 화면 내용만/about에 해당하는 컴포넌트로 교체해 줍니다. 이것이 ‘클라이언트 사이드 라우팅’입니다.
- 사용자가 웹사이트에 처음 접속하면 (
- 서버 사이드 라우팅 (새로고침 또는 직접 주소 입력 시):
- 사용자가 주소창에
http://example.com/about을 직접 입력하거나,/about페이지에서 새로고침(F5)을 누르면, 브라우저는 서버에 “/about이라는 파일을 주세요!”라고 직접 요청을 보냅니다. - 하지만 서버(Nginx, Apache 등) 입장에서는
/about이라는 이름의 파일이나 폴더가 존재하지 않습니다. 서버가 아는 파일은 오직index.html과static폴더 안의 CSS/JS 파일뿐입니다. - 따라서 서버는 “요청하신
/about파일을 찾을 수 없습니다”라는 의미로 404 Not Found 에러를 응답하는 것입니다.
- 사용자가 주소창에
해결 방법: “모든 경로 요청을 index.html로 보내라”
해결책은 간단합니다. 서버에게 “어떤 경로로 요청이 들어오든(예: /, /about, /users/123), 일단 무조건 index.html 파일을 먼저 보내줘. 라우팅은 그 안에 있는 React Router가 알아서 할 거야.” 라고 알려주는 것입니다.
사용하고 계신 웹 서버에 맞춰 아래 설정을 적용하시면 됩니다.
1. Nginx 사용 시 (가장 일반적인 경우)
Nginx 설정 파일 (/etc/nginx/sites-available/your-config 등)의 server 블록 안에 있는 location / 부분을 수정합니다.
try_files 지시어를 사용하는 것이 핵심입니다.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
<code class="whitespace-pre-wrap break-words"><span class="line"><span>server {</span></span> <span class="line"><span> listen 80;</span></span> <span class="line"><span> server_name your-domain.com;</span></span> <span class="line"><span></span></span> <span class="line"><span> # React 빌드 폴더의 경로</span></span> <span class="line"><span> root /var/www/react-app/build; </span></span> <span class="line"><span> index index.html;</span></span> <span class="line"><span></span></span> <span class="line"><span> location / {</span></span> <span class="line"><span> # 1. 요청된 URI($uri)에 해당하는 파일이 있는지 찾아본다. (예: /static/js/main.js)</span></span> <span class="line"><span> # 2. 파일이 없으면, 해당 URI가 디렉토리($uri/)인지 찾아본다.</span></span> <span class="line"><span> # 3. 둘 다 없으면, /index.html 파일을 반환한다. (이 부분이 핵심!)</span></span> <span class="line"><span> try_files $uri $uri/ /index.html;</span></span> <span class="line"><span> }</span></span> <span class="line"><span></span></span> <span class="line"><span> # (선택사항이지만 권장) css, js 등 정적 파일 처리를 위한 location</span></span> <span class="line"><span> # 이 블록이 없으면 MIME 타입 에러가 발생할 수 있습니다.</span></span> <span class="line"><span> location ~* \.(?:css|js|ico|png|jpg|jpeg|gif|svg)$ {</span></span> <span class="line"><span> expires 1y;</span></span> <span class="line"><span> add_header Cache-Control "public";</span></span> <span class="line"><span> }</span></span> <span class="line"><span>}</span></span></code> |
|
1 2 |
<span style="font-family: Roboto, Arial;">설정을 수정한 후에는 반드시 Nginx를 재시작해야 합니다. </span> |
|
1 2 |
<code class="whitespace-pre-wrap break-words"><span class="line"><span>sudo</span><span> nginx</span><span> -t</span><span> # 설정 파일 문법 검사</span></span> <span class="line"><span>sudo</span><span> systemctl</span><span> restart</span><span> nginx</span></span><span style="font-family: Consolas, Monaco, monospace; background-color: #ffffff; color: #333333; font-size: 16px;"></span></code> |
2. Apache 사용 시
Apache에서는 mod_rewrite 모듈을 사용하여 모든 요청을 index.html로 리디렉션합니다.
React 프로젝트의 **루트 디렉토리(보통 build 폴더 안)**에 .htaccess 파일을 생성하고 아래 내용을 추가합니다.
.htaccess
|
1 2 3 4 5 6 7 8 9 |
<code class="whitespace-pre-wrap break-words"><span class="line"><span><IfModule mod_rewrite.c></span></span> <span class="line"><span> RewriteEngine On</span></span> <span class="line"><span> RewriteBase /</span></span> <span class="line"><span> RewriteRule ^index\.html$ - [L]</span></span> <span class="line"><span> RewriteCond %{REQUEST_FILENAME} !-f</span></span> <span class="line"><span> RewriteCond %{REQUEST_FILENAME} !-d</span></span> <span class="line"><span> RewriteCond %{REQUEST_FILENAME} !-l</span></span> <span class="line"><span> RewriteRule . /index.html [L]</span></span> <span class="line"><span></IfModule></span></span></code> |
설명:
RewriteCond %{REQUEST_FILENAME} !-f: 요청된 경로가 실제 파일이 아니면RewriteCond %{REQUEST_FILENAME} !-d: 그리고 실제 디렉토리가 아니면RewriteRule . /index.html [L]: 모든 요청을/index.html로 보낸다.
이를 통해 실제 존재하는 CSS, JS 파일 등은 직접 제공되고, /about과 같은 가상 경로는 index.html로 처리됩니다.
3. Firebase Hosting 사용 시
firebase.json 설정 파일에 rewrites 규칙을 추가합니다.
firebase.json
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
<code class="whitespace-pre-wrap break-words"><span class="line"><span>{</span></span> <span class="line"><span> "hosting"</span><span>: {</span></span> <span class="line"><span> "public"</span><span>: </span><span>"build"</span><span>,</span></span> <span class="line"><span> "ignore"</span><span>: [</span></span> <span class="line"><span> "firebase.json"</span><span>,</span></span> <span class="line"><span> "**/.*"</span><span>,</span></span> <span class="line"><span> "**/node_modules/**"</span></span> <span class="line"><span> ],</span></span> <span class="line"><span> "rewrites"</span><span>: [</span></span> <span class="line"><span> {</span></span> <span class="line"><span> "source"</span><span>: </span><span>"**"</span><span>,</span></span> <span class="line"><span> "destination"</span><span>: </span><span>"/index.html"</span></span> <span class="line"><span> }</span></span> <span class="line"><span> ]</span></span> <span class="line"><span> }</span></span> <span class="line"><span>}</span></span><span style="font-family: Consolas, Monaco, monospace; background-color: #ffffff; color: #333333; font-size: 16px;"></span></code> |
"source": "**"는 모든 경로를 의미하며, 이를 모두 /index.html로 보내도록 설정합니다.
4. Netlify / Vercel 등 정적 호스팅 플랫폼 사용 시
이런 최신 호스팅 플랫폼들은 React 같은 SPA 프레임워크를 자동으로 감지하여 이 설정을 알아서 적용해 줍니다. 특별히 설정할 필요가 없는 경우가 대부분입니다. 만약 문제가 발생한다면, 각 플랫폼의 리디렉션 및 재작성(Redirects & Rewrites) 규칙 문서를 참고하여 위와 동일한 원리의 규칙을 추가해주면 됩니다.
요약
| 문제 상황 | 원인 | 해결책 |
|---|---|---|
| React Router 페이지에서 새로고침/직접접근 시 404 발생 | 서버가 React Router가 만든 가상 경로(/about 등)를 모르기 때문 |
Nginx 등 웹 서버 설정에서 모든 경로 요청을 **index.html**로 보내도록 설정 |
Nginx를 사용하고 계신다면, 위 Nginx 설정 예시를 적용하는 것만으로 문제가 바로 해결될 것입니다.