- Published on
[Solving] CSR(react)을 이용해서 gh-pages 배포를 할 때 생기는 라우팅 문제(새로고침, 404 등)
- Authors
- Name
- Easyoon
CSR(react)을 이용해서 gh-pages 배포를 할 때 생기는 라우팅 문제(새로고침, 404 등)
결론부터 말하자면, gh-pages를 사용하여 정적페이지를 배포할 때에는, 가급적이면 SSG 프레임워크를 사용하는 게 정신건강에 좋습니다. create-react-app(CRA)로 개발된 React 애플리케이션을 GitHub Pages(gh-pages)에 배포할 때 발생하는 라우팅 문제, 특히 새로고침 시 404 에러가 발생하는 문제에 대해 고민합니다.
문제 상황
웹 애플리케이션의 초기 진입 후 내부 페이지로의 라우팅은 정상적으로 작동하지만, 다음 두 가지 경우에 404 에러가 발생합니다.
- 페이지 새로고침 시
- 초기 페이지 진입 없이 특정 내부 페이지 URL로 직접 접근 시
예를 들어, https://is404notfound.github.io/stylish/components
와 같은 URL로 직접 접근하거나 해당 페이지에서 새로고침을 하면 404 에러가 발생하는 상황입니다.
문제 원인
이 문제는 GitHub Pages가 클라이언트 사이드 라우팅(CSR)에 사용되는 HTML5 pushState
history API를 직접적으로 지원하지 않기 때문에 발생합니다.
history API?
HTML5 history API는 브라우저의 히스토리 스택을 조작하여 페이지 새로고침 없이 URL을 변경할 수 있도록 합니다. React Router의 BrowserRouter
는 이 API를 사용하여 클라이언트 사이드 라우팅을 구현합니다.
하지만 GitHub Pages는 정적 파일 호스팅 서비스이기 때문에, 서버는 클라이언트 측에서 처리하는 라우팅 정보를 알지 못합니다. 따라서 https://is404notfound.github.io/stylish/components
와 같은 URL로 요청이 들어오면, GitHub Pages 서버는 해당 경로에 맞는 파일을 찾지 못해 404 에러를 반환하게 됩니다. 서버는 /components
라는 경로에 대한 파일을 가지고 있지 않기 때문입니다. 모든 라우팅은 클라이언트 사이드에서 JavaScript를 통해 처리되어야 합니다.
해결 방법
CRA 문서에서는 이 문제를 해결하기 위한 두 가지 방법을 제시합니다.
1. HashRouter 사용
첫 번째 방법은 BrowserRouter
대신 HashRouter
를 사용하는 것입니다. HashRouter
는 URL의 해시(#
) 부분을 사용하여 라우팅을 처리합니다. 예를 들어, https://is404notfound.github.io/stylish/#/components
와 같은 URL을 사용합니다.
해시 이후의 URL 변경은 브라우저에서만 처리되며, 서버에 요청을 보내지 않습니다. 따라서 404 에러가 발생하지 않습니다.
장점: 간단하게 적용 가능합니다.
단점:
- URL이 다소 지저분해 보일 수 있습니다.
- 검색 엔진 최적화(SEO)에 불리합니다. 해시 이후의 내용은 검색 엔진 크롤러가 제대로 인식하지 못할 수 있기 때문입니다.
2. BrowserRouter + 404 리디렉션 처리 (권장)
두 번째 방법은 BrowserRouter
를 유지하면서 404 에러 발생 시 리디렉션을 통해 문제를 해결하는 것입니다. 이 방법은 CRA 문서에서 제공하는 MIT 라이선스의 스크립트를 활용합니다.
구현 단계:
public
폴더에404.html
파일 생성: 다음 내용을404.html
파일에 복사합니다.<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Single Page Apps for GitHub Pages</title> <script type="text/javascript"> (function(l) { if (l.search) { var q = {}; l.search.slice(1).split('&').forEach(function(v) { var a = v.split('='); q[a[0]] = a.slice(1).join('=').replace(/~and~/g, '&'); }); if (q.p !== undefined) { window.history.replaceState(null, null, l.pathname.slice(0, -1) + (q.p || '') + (q.q ? ('?' + q.q) : '') + l.hash ); } } }(window.location)) </script> </head> <body></body> </html>
index.html
에 스크립트 추가:index.html
의<head>
태그 안에 다음 스크립트를 추가합니다. 이 스크립트는404.html
에서 쿼리 문자열로 전달된 원래 URL을 복원하는 역할을 합니다.<head> <script type="text/javascript"> (function(l) { if (l.search) { var q = {}; l.search.slice(1).split('&').forEach(function(v) { var a = v.split('='); q[a[0]] = a.slice(1).join('=').replace(/~and~/g, '&'); }); if (q.p !== undefined) { window.history.replaceState(null, null, l.pathname.slice(0, -1) + (q.p || '') + (q.q ? ('?' + q.q) : '') + l.hash ); } } }(window.location)) </script> </head>
package.json
에homepage
속성 추가:package.json
파일에homepage
속성을 추가합니다.repo-name
부분을 실제 저장소 이름으로 바꿔야 합니다.{ "homepage": "[https://user.github.io/repo-name](https://user.github.io/repo-name)", // ... other properties }
BrowserRouter
에basename
속성 추가:BrowserRouter
를 사용하는 곳에basename
속성을 추가합니다.import { BrowserRouter } from 'react-router-dom'; const root = ReactDOM.createRoot(document.getElementById('root')); root.render( <BrowserRouter basename={process.env.PUBLIC_URL}> <App /> </BrowserRouter> );
이 방법을 사용하면 URL이 깔끔하게 유지되고 SEO에도 유리합니다.
대안 : Next JS SSG
Next.js는 정적 사이트 생성(Static Site Generation, SSG) 및 서버 사이드 렌더링(Server-Side Rendering, SSR)을 지원합니다.
- Next.js의 정적 사이트 생성 (SSG)
Next.js는 빌드 시점에 모든 페이지를 HTML 파일로 미리 생성할 수 있습니다. 즉, 각 라우트에 해당하는 HTML 파일이 생성되어 서버에 저장됩니다. 사용자가 특정 URL에 접근하면, 서버는 해당 URL에 해당하는 HTML 파일을 바로 제공합니다. 이는 마치 일반적인 정적 웹사이트와 동일한 방식으로 작동합니다.
gh-pages는 정적 파일 호스팅 서비스이므로, 미리 생성된 HTML 파일을 호스팅하는 데 매우 적합합니다. 따라서 Next.js로 SSG를 사용하여 빌드한 결과물을 gh-pages에 배포하면, 각 라우트에 대한 HTML 파일이 존재하기 때문에 404 에러가 발생하지 않습니다.
예를 들어, /about 페이지가 있다면, Next.js는 빌드 시점에 about.html 파일을 생성하고, gh-pages는 사용자가 your-username.github.io/your-repo/about 에 접속했을 때 이 about.html 파일을 제공합니다.
- Next.js의 서버 사이드 렌더링 (SSR)
Next.js는 SSR도 지원합니다. SSR은 클라이언트가 요청할 때마다 서버에서 페이지를 렌더링하여 HTML을 생성하는 방식입니다. 기본적으로 gh-pages는 서버리스 환경이므로, SSR을 직접적으로 지원하지 않습니다. 하지만 Next.js에서 next export 명령어를 사용하면 SSR로 생성된 페이지들을 정적 HTML 파일로 변환하여 내보낼 수 있습니다. 이렇게 내보낸 파일들을 gh-pages에 배포하면 SSG와 마찬가지로 404 에러 없이 작동할 수 있지만, SSG를 쓰는 것이 정신건강에 이롭습니다.