0. Environments
node
: v18.15.0,react
: v18.2.0DVCS
: git,Server
: GitHubCI/CD
: Github Actions
1. Github Pages + React ๐
1.1 Why choose these stack โค๏ธ
ํ์คํ ๊ฐ๋ฐ์๋ผ๊ธฐ์ ๋ถ๋๋ฝ์ง๋ง, ์ ๋ React ํ๋ก์ ํธ ๊ฒฝํ์ด ์์ต๋๋ค. (์์ค๊ฐ ์์์ง ์์๋ ์ํด๋ถํํด์๐ซก)
๊ทธ์น๋ง ํฌํ ํด๋ฆฌ์ค ํ์ด์ง๋ฅผ React
๊ธฐ์ ์ ๋ฐํ์ผ๋ก ๊ฐ๋ฐํด๋ณด๊ณ ์ ํฉ๋๋ค. ๋์ ~~!๐
Github Pages
์ฐ์ GitHub Pages๋ฅผ ์ ํํ ์ด์ ๋ <ํธ์คํ
, ๋๋ฉ์ธ, ์๋ฒ> ๋ฑ ๋์ ํ์ด์ง
๋ฅผ ๊ฐ๋ฐ ๋ฐ ์ ์งํ๊ธฐ ์ํด ํ์ํ ๋ชจ๋ ๋น์ฉ์ด ํ์ ์๊ธฐ ๋๋ฌธ์
๋๋ค.
๋์ ๊ฐ์ฅ ํฐ ๋จ์ ์ด ์ ์ ๋ฆฌ์์ค(html, css, js, ๋ฑ) ๋ง์ด ๊ฐ๋ฅํ๋ค๋ ์ ์ด์ฃ .
Why React?
์์ ์ ์ ๋ฆฌ์์ค๋ก ๊ฐ๋ฐํ์๋ ์๋ฒ ๊ฐ๋ฐ์๋ก์๋ ์๊ฐ์ธ๋ก ๋ถํธํ ์ ์ด ๋ง๋๋ผ๊ตฌ์.
- IDE๋ฅผ ์ฐ๊ธฐ์๋ ์ ๋งคํ๊ณ โ Notepad๋ฅผ ์จ์ผ๋๋..?
- ๊ด๋ฆฌํ๊ธฐ๋ ๋ถํธํ๊ณ โ html ํต์ฑ๋ก ์์ ํด์ผ ๋๋๊น, ํ์ผ๋ถ๋ฆฌ๋ ์ด๋ ค์
- ๋ฌด์๋ณด๋ค ๊ฐ๋ฐ์๋ก์ ๋ฉ์ด ์๋ค โ ๊ฐ์ง์ ์ฃฝ๊ณ ์ฌ๋ ๊ฐ๋ฐ์๋ก์ ์ฉ๋ฉํ ์ ์๋ค๐คฆโโ๏ธ
React๋ก ๊ฐ๋ฐํ๋ฉด ๋ฌ๋์ปค๋ธ๋ฅผ ๊ฐ๋นํ ๋งํผ ์์ ๋ฌธ์ ๋ค์ด ํด์๋๊ณ , ๋ค์ํ ๊ฒฝํ์ ํด๋ณผ ์ ์๋ ๊ธฐํ๊ธฐ ๋๋ฌธ์ ๊ธฐ์ ์คํ์ ์ ์ ํ๊ฒ ๋์์ต๋๋ค!
React๋ก ๊ฐ๋ฐํ๋ฉด gh-pages
์ ์ง์์ผ๋ก ๊ฐ๋ฐ ์์ค
์ ๋ฐฐํฌ ํ์ผ
ํ๋ก์ ํธ๋ฅผ ๋ถ๋ฆฌํ์ง ์์๋ ๋๋ ์ฅ์ ์ด ๋งค๋ ฅ์ ์ธ ๊ฒ ๊ฐ์ต๋๋ค.
1.2 How to run react app with github pages โค๏ธ
github pages์์ react ์ฑ์ ๋ฐฐํฌํ๋ ๋ฐฉ๋ฒ์ ์ค๋ช ํฉ๋๋ค.
1) repository ์์ฑ
์ฐ์ , ์์ ์ ๊ณ์ ์ผ๋ก ๋ github pages
repository๋ฅผ ์์ฑํฉ๋๋ค. โ {๊ณ์ ๋ช
}.github.io
2) React Project ๋ฐ Local Git Repository ์์ฑ
# React Project ์์ฑ
$ npx create-react-app {๊ณ์ ๋ช
}.github.io --template typescript
# Local Git ์ค์
$ git init
$ git add .
$ git commit -m "Initial Commit"
# Server์ P์ฌ๋ฆฌ๊ธฐ
$ git remote add origin https://github.com/{username}/{repository-name}.git
$ git push origin master
3) gh-pages ํจํค์ง ์ค์น
react ์ฑ์ github pages์ ๋ฐฐํฌํ๋๋ก ๋์์ค gh-pages
ํจํค์ง๋ฅผ ์ค์นํฉ๋๋ค.
$ npm install --save gh-pages
4) package.json ํ์ผ ์์
root
์ homepage
ํ๋กํผํฐ์ ํธ์คํ
๋ github pages ๊ฒฝ๋ก๋ฅผ ๋ฃ์ด์ค๋๋ค.
{
"name": "hjkim1004.github.io",
"version": "0.1.0",
// -------- homepage ํ๋กํผํฐ ์ถ๊ฐ --------- //
"homepage": "https://hjkim1004.github.io",
// --------------------------------------- //
"private": true,
"dependencies": {},
"scripts": {
// --- predeploy ๋ฐ deploy ์คํฌ๋ฆฝํธ ์ถ๊ฐ --- //
"predeploy": "npm run build",
"deploy": "gh-pages -d build",
// --------------------------------------- //
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
}
5) React App ๋ฐฐํฌ
๋ง์ง๋ง์ผ๋ก React ์ฑ์ deployํฉ๋๋ค.
$ npm run deploy
6) GitHub Repository ์ค์
Repository์ [Settings
> Pages
> Build and deployment
]์์ Branch
๋ฅผ master
์์ gh-pages
๋ก ๋ณ๊ฒฝํฉ๋๋ค.
๋ณ๊ฒฝ ํ์๋ https://{๊ณ์ ๋ช
}.github.io
์์ ๋ฐฐํฌ๋ ์ฌ์ดํธ๋ฅผ ๋ณด์ค ์ ์์ต๋๋ค.
1.3 CI/CD with Github Actions โค๏ธ
ํ๋ ๋๋ง๋ค ๋ค์ํ ํ๊ฒฝ์์ ๊ฐ๋ฐํ๋ค ๋ณด๋, ๋ฐฐํฌ๊ฐ ์๋นํ ๊ท์ฐฎ๊ณ ๋ฒ๊ฑฐ๋ก์ GitHub Actions
์ผ๋ก ๊ฐ๋จํ๊ฒ CI/CD
๋ฅผ ๊ตฌ์ถํด๋ณด์์ต๋๋ค.
Github Actions โจ
Github Actions๋ ๊นํ๋ธ์์ ์ ๊ณตํ๋ ์ํฌํ๋ก์ฐ (์์ฐจ์์
)
๋ฅผ ์๋ํํ๋๋ก ๋์์ฃผ๋ ๋๊ตฌ์
๋๋ค. ํ
์คํธ, ๋น๋, ๋ฐฐํฌ ๋ฑ์ ๋ค์ํ ์์
๋ค์ ์๋ํํ์ฌ ์ฒ๋ฆฌํฉ๋๋ค.
New Workflow
.github/workflows
ํด๋์ .yml
ํ์ผ์ ์ถ๊ฐํ๊ฑฐ๋, [Repository
> Actions
> New workflow
]๋ฅผ ํตํด ์ํฌํ๋ก์ฐ๋ฅผ ๋ฑ๋กํ ์ ์์ต๋๋ค.
workflow ํ์ผ์ ๋ค์ํ ๋ฌธ๋ฒ์ ๋งํฌ์์ ๋ณด์ค ์ ์์ต๋๋ค.
gh-pages workflow
nodejs
๊ฐ v18.15.0
๋ฒ์ ์ด๋ผ, ์๋์ ๊ฐ์ด workflow ํ์ผ์ ๊ตฌ์ฑํ์ต๋๋ค.
name: Node.js CI
on:
push:
branches: [ "master" ]
pull_request:
branches: [ "master" ]
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [18.x]
steps:
- uses: actions/checkout@v3
- name: Setup Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}
cache: 'npm'
- name: Intall Dependencies
run: npm install
- name: Build Projects
run: npm run build
- name: Deploy Docs
uses: peaceiris/actions-gh-pages@v3
with:
github_token: ${{ secrets.GH_ACTIONS_TOKEN }}
publish_dir: ./build
Secrets ๊ด๋ฆฌ
์ฝ๋์ ์๋ ${{ secrets.GH_ACTIONS_TOKEN }}
์ ๊ฐ์ ๋ฃ๊ธฐ ์ํด์๋ [Repository
> Settings
> Secrets and variables
> Actions
]์์ Secret Key๋ฅผ ๊ด๋ฆฌํ ์ ์์ต๋๋ค.
2. Start React App ๐
React ํ๋ก์ ํธ๋ฅผ ์์ฑํ๊ณ , ํฌํ ํด๋ฆฌ์ค ํ์ด์ง ๊ฐ๋ฐ์ ์ํ ์ถ๊ฐ ์ค์ ๋ด์ฉ๋ค์ ์ ๋ฆฌํด๋ณด์์ต๋๋ค.
2.1 create project โค๏ธ
node.js
๋ฅผ ์ค์นํ๊ณ , ํฉ๋๋ค.
$ node --version
v18.15.0
create-react-app
ํจํค์ง๋ฅผ ์ค์นํ๊ณ , React ํ๋ก์ ํธ๋ฅผ ๋ง๋ญ๋๋ค.
$ npm install -g create-react-app
$ npx create-react-app {ํ๋ก์ ํธ๋ช
} --template typescript
--template typescript
์ต์ : ํด๋น ์ต์ ์ ์ฌ์ฉํ๋ฉด, ํ์ ์คํฌ๋ฆฝํธ๋ฅผ ์ธํ ํ ํ๋ก์ ํธ๋ฅผ ์์ฑํ ์ ์์ต๋๋ค. (์ ๋ ํ์ ์ง์ ์ ์ ํธํ๊ธฐ ๋๋ฌธ์ ํด๋น ์ต์ ์ ๋ฃ์์ต๋๋ค.)
2.2 Path Configuration โค๏ธ
webpack
, babel
์ ์ด์ฉํด ๋ชจ๋์ ๋ฒ๋ค๋งํ๊ณ , ๋ชจ๋๋ช
์ ๋ณ๊ฒฝํ ์ ์๋๋ก ์ธํ
ํฉ๋๋ค.
์ ๋ ๊ฒฝ๋ก vs ์๋ ๊ฒฝ๋ก
์ ๋ ๊ฒฝ๋ก
๋ ์ต์์ ๋๋ ํ ๋ฆฌ๊ฐ ๋ฐ๋์ ํฌํจ ๋ ๊ฒฝ๋ก๋ฅผ ์๋ฏธํ๋ฉฐ,์๋ ๊ฒฝ๋ก
๋ ํ์ฌ ๋๋ ํ ๋ฆฌ(๋น๊ต ๋์)๋ฅผ ๊ธฐ์ค์ผ๋ก ์์ฑ๋ ๊ฒฝ๋ก๋ฅผ ์๋ฏธํฉ๋๋ค.
// ์๋ ๊ฒฝ๋ก๋ก ์๋ฐํ ์ปดํฌ๋ํธ ๋ถ๋ฌ์ค๊ธฐ
import Avatar from '../../../components/Avatar'
// ์ ๋ ๊ฒฝ๋ก๋ก ์๋ฐํ ์ปดํฌ๋ํธ ๋ถ๋ฌ์ค๊ธฐ
import Avatar from 'components/Avatar'
webpack, babel
- ์ฐธ๊ณ ๋ธ๋ก๊ทธ
- ์ถํ ์ถ๊ฐ ์์
Edit tsconfig.json
typescript๋ฅผ ์ด์ฉํ๋ ๊ฒฝ์ฐ, webpack ์ค์ ๋ง์ผ๋ก๋ Compile Error๊ฐ ๋ฐ์ํ๋ฏ๋ก, tsconfig.json
์ ์๋ ๋ด์ฉ์ ์ถ๊ฐํ์ฌ ์ ๋๊ฒฝ๋ก๋ฅผ ์ด์ฉํ ์ ์๋๋ก ์ค์ ํฉ๋๋ค.
โป tsconfig.json
ํ์ผ์ ํ๋ก์ ํธ๋ฅผ ์ปดํ์ผํ๋๋ฐ ํ์ํ ๋ฃจํธ ํ์ผ๊ณผ ์ปดํ์ผ๋ฌ ์ต์
์ ์ง์ ํ๋๋ฐ ์ฌ์ฉํฉ๋๋ค.
baseUrl
์์ฑ์ ์ต์์ ์์ค ๋๋ ํฐ๋ฆฌ ๊ฒฝ๋ก๋ฅผ ๋ฃ์ด์ฃผ๊ณ , paths
์์ฑ์ ์ด์ฉํ์ฌ ์ฌ์ฉํ ๊ฒฝ๋ก๋ช
์ ๋ฃ์ด์ค๋๋ค.
{
"compilerOptions": {
// ์ต์์ ์์ค ๋๋ ํฐ๋ฆฌ ๊ฒฝ๋ก
"baseUrl": "src",
// ์์ ์ด ์ํ๋ ๊ฒฝ๋ก ์ค์ ๊ฐ๋ฅ
"paths": {
"~/*": [
"~/*"
],
"@components/*": [
"component/*"
],
"@pages/*": [
"page/*"
],
"@assets/*": [
"assets/*"
]
}
},
}
2.3 Router Configuration
React Router โจ
๊ฐ ํ์ด์ง๋ณ๋ก routing
์ ์ ์ฉํ๊ธฐ ์ํด์๋ React Router
๋ฅผ ์ด์ฉํด์ผํฉ๋๋ค.
React Router
์ ํจํค์ง๋ react-router
(์น&์ฑ), react-router-dom
(์น), react-router-native
(์ฑ)์ผ๋ก ์ธ๊ฐ์ง๊ฐ ์์ง๋ง, ์น์ ๊ฐ๋ฐํ ๊ฒ์ด๋ฏ๋ก react-router-dom
์ ์ค์นํฉ๋๋ค.
$ npm i react-router-dom # react-router ์ค์น
โป ํด๋น ๋ฒ์ ์ react-router v6
์ด ๋ฐํ์ด ๋์์ต๋๋ค.
BrowserRouter ๐ถ
React Router
์๋ HashRouter
, StaticRouter
, BrowserRouter
๋ฑ ์ฌ๋ฌ๊ฐ์ง Router๊ฐ ์์ง๋ง, ๊ทธ์ค์์๋ URL์ด ๊น๋ํ BrowserRouter
๋ฅผ ์ฌ์ฉํ ๊ฒ์
๋๋ค.
import React from 'react';
import { BrowserRouter, Route, Link, Routes } from "react-router-dom";
Import๋ BrowserRouter, Route, Link, Switch๋ ์๋์ ๊ฐ์ ์ญํ ์ ํฉ๋๋ค.
- BrowserRouter - history API๋ฅผ ์ฌ์ฉํด URL๊ณผ UI๋ฅผ ๋๊ธฐํํ๋ ๋ผ์ฐํฐ์ ๋๋ค.
- Route - ์ปดํฌ๋ํธ์ ์์ฑ์ ์ค์ ๋ URL๊ณผ ํ์ฌ ๊ฒฝ๋ก๊ฐ ์ผ์นํ๋ฉด ํด๋นํ๋ ์ปดํฌ๋ํธ, ํจ์๋ฅผ ๋ ๋๋งํฉ๋๋ค.
- Link - 'a'ํ๊ทธ์ ๋น์ทํฉ๋๋ค. to์์ฑ์ ์ค์ ๋ ๋งํฌ๋ก ์ด๋ํฉ๋๋ค. ๊ธฐ๋ก์ด history์คํ์ ์ ์ฅ๋ฉ๋๋ค.
- Routes - ์์ ์ปดํฌ๋ํธ Route๋๋ Redirect ์ค ๋งค์น๋๋ ์ฒซ ๋ฒ์งธ ์์๋ฅผ ๋ ๋๋งํฉ๋๋ค. Routes๋ฅผ ์ฌ์ฉํ๋ฉด BrowserRouter๋ง ์ฌ์ฉํ ๋์ ๋ค๋ฅด๊ฒ ํ๋์ ๋งค์นญ๋๋ ์์๋ง ๋ ๋๋งํ๋ค๋ ์ ์ ๋ณด์ฅํด์ค๋๋ค. ์ฌ์ฉํ์ง ์์ ๊ฒฝ์ฐ ๋งค์นญ๋๋ ๋ชจ๋๋ฅผ ๋ ๋๋งํฉ๋๋ค.
Example
<BrowserRouter>
<div style={{padding:20, border:'5px solid gray'}}>
<Link to="/">ํ</Link><br/>
<Link to="/profile">ํ๋กํ</Link><br/>
<Link to="/about">About</Link><br/>
<Routes>
<Route path="/" component={Home}/>
<Route path="/photo" component={Photo}/>
<Route path="/rooms" component={Rooms}/>
</Routes>
</div>
</BrowserRouter>
My App์ ์ ์ฉ
์ต์ข
์ ์ผ๋ก ํ๋ก๊ทธ๋จ ๋ถ๊ธฐ๋ฅผ ์ํด App.tsx
๋ฅผ ์์ ํฉ๋๋ค. ์๋์ ๊ฐ์ด Navigation
๋ฐ Router
๋ฅผ ์ค์ ํด์ค๋๋ค.
export default class App extends Component {
render() {
return (
<div className="">
<Header title="Twinkle's Portfolio" />
<BrowserRouter basename={process.env.PUBLIC_URL}>
<Navigation />
<Routes>
<Route path="/" element={<Home />} />
<Route path="/project" element={<Project />} />
<Route path="/about" element={<About />} />
<Route path="/contact" element={<Contact />} />
</Routes>
</BrowserRouter>
</div>
);
}
}
Referecnes
์๋ ๋งํฌ๋ฅผ ์ฐธ๊ณ ํ์ธ์.