이전의 gh-pages 로 정적 홈페이지 배포방법에 대해 설명하였고,
2023.08.15 - [리액트(React)] - 웹사이트 Build 및 깃허브(Github) Pages로 배포하기
추가로 깃허브(Github)의 무료 호스팅인 page 로 create-react-app 으로 만든 리액트 프로젝트을 배포하는데 몇가지 주의사항이 있어서 별도로 설명하고자 합니다..
기본적으로 gh-pages의 경우 리액트와 같은 SPA(Single Page Application)를 지원하지 않습니다. 따라서 다양한 페이지로 접근시 404 에러(페이지를 찾을 수 없음)가 나타납니다.
이에 따라 단순하게 페이지 하나만 보여주는 것은 별 문제가 되지 않으나 기본적으로 gh-pages 에서는 https://[사용자명].github.io 로 바로 접속이 되지 않고 하위로 프로젝트명이 추가되어야 합니다.
즉, package.json 에 homepage 로 https://[사용자명].github.io/[프로젝트명] 이 기본 URL이 되고 리액트에서 사용하는 sub page는 그 하위로 붙어야 합니다.
개발작업시에는 기본적으로 localhost:3000 하위로 sub page가 구성되어 문제는 없으나 실제 배포시에는 동작하지 않으므로 다음과 같은 추가작업이 필요합니다.
1. 프로젝트 기본 환경
- npm creat-react-app [프로젝트명] 으로 프로젝트 생성
- 추가 라이브러리로 react-router-dom 설치 (npm install react-router-dom)
- 페이지 라우팅으로 <BrowserRouter />, <Routes />, <Route />, <Link /> 컴포넌트를 사용
- CSS 로 tailwind 설치 (npm install -D tailwindcss)
2. PUBLIC_URL 등록
실제 배포되는 homepage URL을 PUBLIC_URL로 등록하여 사용하면 <Route /> 에 설정한 경로의 앞에 해당 URL을 붙여서 라우팅이 됩니다.
- 프로젝트 상위 폴더에 .env 파일 생성
- 파일에 REACT_APP_PUBLIC_URL=https://홈페이지URL 작성
- 라우트를 설정하는 App.js 에 <BrowserRouter basename={process.env.PUBLIC_URL}> 로 basename 을 추가
(...)
function App() {
return (
<BrowserRouter basename={process.env.PUBLIC_URL}>
<Header />
<Routes>
<Route exact path="/" element={<Home />} />
<Route exact path="/about" element={<About />} />
<Route exact path="/eduprogram" element={<EduProgram />} />
<Route exact path="/eduprogram/:id" element={<EduDetail />} />
<Route exact path="/healingprogram" element={<HealingProgram />} />
<Route exact path="/healingprogram/:id" element={<HealingDetail />} />
<Route exact path="/training" element={<Training />} />
<Route exact path="/counseling" element={<Counseling />} />
</Routes>
</BrowserRouter>
);
}
- 다른 페이지에서 <Link /> 컴포넌트 사용으로 페이지간 이동을 하는 경우도 basename 설정으로 자동 라우팅 변경
- tailwind css 로 navbar 사용시 <Disclosure /> 컴포넌트를 이용하는데 이 때 사용하는 <Disclosure.button /> 컴포넌트에서는 다음과 같이 속성에 href 를 사용하게 되어 {process.env.PUBLIC_URL + item.to} 로 PUBLIC_URL을 추가 적용
<div className="space-y-1 px-2 pb-3 pt-2">
{navigation.map((item) => (
<Disclosure.Button
key={item.name}
as="a"
href={process.env.PUBLIC_URL + item.to}
className={classNames(
item.current
? 'bg-gray-900 text-white'
: 'text-gray-300 hover:bg-gray-700 hover:text-white',
'block rounded-md px-3 py-2 text-base font-medium',
)}
aria-current={item.current ? 'page' : undefined}
>
{item.name}
</Disclosure.Button>
))}
</div>
3. BrowserRouter 사용시 404 오류 해결
BrowserRouter 특성은 동적인 페이지에 적합하나 새로고침시 경로를 찾지 못해 404 에러를 발생시킵니다.
이러한 404 에러를 고치기 위해 추가적인 작업이 필요합니다.
- Public 폴더에 있는 index.html 과 404 오류 발생시 이동할 페이지로 404.html 을 작성
★ MIT license 의 오픈 소스로 프로젝트 포함시 라이센스를 명시
[404.html]
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Single Page Apps for GitHub Pages</title>
<script type="text/javascript">
// Single Page Apps for GitHub Pages
// https://github.com/rafrex/spa-github-pages
// Copyright (c) 2016 Rafael Pedicini, licensed under the MIT License
// ---------------------------------------------- ------------------------
// This script takes the current url and converts the path and query
// string into just a query string, and then redirects the browser
// to the new url with only a query string and hash fragment,
// e.g. http://www.foo.tld/one/two?a=b& c=d#qwe, becomes
// http://www.foo.tld/?p=/one/two& q=a=b~and~c=d#qwe
// Note: this 404.html file must be at least 512 bytes for it to work
// with Internet Explorer (it is currently > 512 bytes)
// If you're creating a Project Pages site and NOT using a custom domain,
// then set segmentCount to 1 (enterprise users may need to set it to > 1).
// This way the code will only replace the route part of the path, and not
// the real directory in which the app resides, for example:
// https://username.github.io/repo-name/one/two? a=b&c=d#qwe becomes
// https://username.github.io/repo-name/? p=/one/two&q=a=b~and~c=d#qwe
// Otherwise, leave segmentCount as 0.
var segmentCount = 1;
var l = window.location;
l.replace(
l.protocol + '//' + l.hostname + (l.port ? ':' + l.port : '') +
l.pathname.split('/').slice(0, 1 + segmentCount).join('/') + '/?p=/' +
l.pathname.slice(1).split('/').slice (segmentCount).join('/').replace(/&/g, '~and~') +
(l.search ? '&q=' + l.search.slice(1) .replace(/&/g, '~and~') : '') +
l.hash
);
</script>
</head>
<body>
</body>
</html>
[index.html scripts 추가]
<script type="text/javascript">
// Single Page Apps for GitHub Pages
// https://github.com/rafrex/spa-github-pages
// Copyright (c) 2016 Rafael Pedicini, licensed under the MIT License
// ----------------------------------------------------------------------
// This script checks to see if a redirect is present in the query string
// and converts it back into the correct url and adds it to the
// browser's history using window.history.replaceState(...),
// which won't cause the browser to attempt to load the new url.
// When the single page app is loaded further down in this file,
// the correct url will be waiting in the browser's history for
// the single page app to route accordingly.
(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>
이와 같이 설정하면 정상적으로 페이지가 표출됩니다.
추가로 BrowserRouter 가 아닌 HashRouter를 사용하면 이와 같이 404 에러가 발생하지 않으므로 단순히 static 페이지는 HashRouter 사용이 편리합니다.
단, HashRouter 사용시 URL에 #(hash)가 붙게 되는데 이럴 경우 검색엔진에서 해당 페이지가 검색되지 않기 때문에 잘 사용하지 않습니다.
'리액트(React)' 카테고리의 다른 글
React 배열 데이터 가공 - map,find,findindex,filter (1) (0) | 2024.01.21 |
---|---|
리액트 - Netlify 배포하는 방법 및 주의사항 (0) | 2023.09.07 |
웹사이트 Build 및 깃허브(Github) Pages로 배포하기 (0) | 2023.08.15 |
esm으로 ES 모듈 import/export 문법 사용하기 (0) | 2023.07.13 |
mongoose의 설치 및 적용 (0) | 2023.07.09 |