0. Environments

  • node: v18.15.0, react: v18.2.0
  • DVCS: git, Server: GitHub
  • CI/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

์•„๋ž˜ ๋งํฌ๋ฅผ ์ฐธ๊ณ ํ•˜์„ธ์š”.