회사 프로젝트를 진행하면서 React + Tailwind 프로젝트를 GitLab Pages에 배포했는데,
라우팅과 관련된 문제가 발생했다. 로컬 개발 서버에서는 문제가 없었지만, 배포된 페이지에서 새로고침 시 404 에러가 뜨는 상황이었다.
이 글에서는 내가 겪은 문제 상황과 원인 분석, 해결책 비교, 그리고 최종적으로 HashRouter로 임시 해결한 과정을 정리한다.
1. 문제 상황
- React 프로젝트를 GitLab Pages로 배포
- 라우팅에
BrowserRouter 사용
- 로컬 개발 서버에서는 라우팅 및 새로고침 모두 정상 동작
- 문제: GitLab Pages 배포 후 특정 URL 직접 접근 또는 새로고침 시 GitLab 기본 404 페이지가 표시됨
증상
/dashboard 같은 URL을 직접 입력하면 GitLab의 404 UI 표시
- 페이지 내에서 클릭으로 이동하면 UI 정상 작동
- 이동 후 새로고침하면 다시 메인 페이지(
/)로 리다이렉트됨
- URL은 바뀌지만 React Router에서 페이지를 렌더링하지 못함
2. 원인 분석
- GitLab Pages는 정적 호스팅으로 동작
- 서버 측 라우팅 설정 불가 → 존재하지 않는 경로 요청 시 404 반환
- React Router의
BrowserRouter는 HTML5 History API를 사용
- 따라서 새로고침 시 서버에 직접 요청 → 서버는 해당 경로의 정적 파일 없음 → 404 발생
dist 폴더를 확인해보자. index.html 하나만 있는걸 봐도 알 수 있다. gitlab pages는 정적 호스팅이기 때문에 링크를 전달해서 요청하면 배포주소는 index.html하나만 바라보고 있어서 '전달받은 링크같은 그런 파일은 없는데..?'라고 생각하고 404를 뱉어버리는 것이다.
3. 시도한 해결 방법들
방법 1: 404.html + sessionStorage 복원
- 404.html에서 현재 경로를
sessionStorage에 저장
/로 리다이렉트 후 App.tsx에서 sessionStorage 값을 읽어 원래 경로로 복원
장점
- URL 깔끔하게 유지 (
/dashboard 그대로 사용)
- React Router 그대로 사용 가능
단점
- 존재하지 않는 URL도 메인 페이지로 리다이렉트 → SEO 문제
- GitLab Pages의 404 응답이 항상 200으로 변환 → 에러 구분 불가
- sessionStorage 복원 로직이 복잡하고 유지보수 어려움
방법 2: 404.html → index.html 유사 내용으로 처리 (sessionStorage 사용 안 함)
404.html을 index.html과 동일한 React 앱으로 구성
- 사용자가 잘못된 URL을 직접 입력하면 404.html이 로드되고, React Router가 즉시 실행되어 해당 경로를 해석
- 존재하지 않는 경로는
NotFoundPage로 처리, 존재하는 경로는 정상 라우팅
장점
- sessionStorage 없이 자연스럽게 동작
- 클릭/새로고침/직접 접근 모두 정상 작동
단점
- 잘못된 URL도 200 상태로 서빙되어 SEO 문제
- GitLab Pages 특성상 서버 측 404 구분 불가
방법 3: GitLab Pages CI 수정 (모든 경로 index.html로 fallback)
- GitLab CI 설정에서 모든 요청을 index.html로 리다이렉트하도록 설정
- 404.html 활용 없이 서버 레벨에서 우회
장점
단점
- GitLab Pages는 커스텀 리라이트 규칙을 지원하지 않음 (불가능에 가까움)
- 결국 404.html 방식과 유사한 효과만 기대 가능
방법 4: HashRouter 사용
- React Router에서
BrowserRouter → HashRouter로 변경
- URL이
/#/dashboard 형태로 변경됨
- 서버는
# 이후 경로를 무시하므로 항상 index.html 반환
- 클라이언트 라우팅은 해시 기반으로 정상 작동
장점
- 가장 단순하고 빠른 해결책
- GitLab Pages 특성과 100% 호환
- 별도 서버 설정, 404.html 필요 없음
- 새로고침, 직접 접근 모두 문제 없이 동작
단점
- URL에
# 포함 → SEO에 불리
- HTTP 상태 코드와 무관하게 항상 200 응답
4. 최종 선택: HashRouter
선택 이유
- 빠른 배포 필요
- 회사 프로젝트이고 GitLab Group 비공개 레포
- Vercel/Netlify는 그룹 비공개 레포 무료 연결 불가
- 유료 서비스 사용 불가한 상황
- 임시 배포
- 최종적으로는 AWS 등 서버 기반 배포로 이관 예정
- SEO 문제는 이후 서버 배포 시 BrowserRouter로 복원하며 해결 가능
- 적용 난이도 최소
- BrowserRouter → HashRouter 변경만으로 문제 해결
- 404.html 및 복원 로직 제거 가능
5. 적용 방법
1) main.tsx 수정
import { HashRouter } from 'react-router-dom';
ReactDOM.createRoot(document.getElementById('root')!).render(
<React.StrictMode>
<HashRouter>
<App />
</HashRouter>
</React.StrictMode>
);