Cloud Dev 1st CI FE - 100-hours-a-week/KTB4-1st-wiki GitHub Wiki

v1 CI ์›Œํฌํ”Œ๋กœ์šฐ ๊ตฌํ˜„ - FE

2๋‹จ๊ณ„. CI(์ง€์†์  ํ†ตํ•ฉ) ํŒŒ์ดํ”„๋ผ์ธ ์„ค๊ณ„์—์„œ ์ •ํ•œ CI Trigger๋ฅผ KTB4-1st-FE ๋ ˆํฌ์— ์‹ค์ œ๋กœ ๊ตฌํ˜„ํ•œ ๋‚ด์šฉ์ด๋‹ค.

  • ๋Œ€์ƒ ๋ ˆํฌ: KTB4-1st-FE
  • ์›Œํฌํ”Œ๋กœ์šฐ ํŒŒ์ผ: fe-pr-check.yml, fe-docker-publish.yml

์›Œํฌํ”Œ๋กœ์šฐ ๊ตฌ์„ฑ

ํŒŒ์ผ ํŠธ๋ฆฌ๊ฑฐ ์‹คํ–‰ ํ•ญ๋ชฉ
fe-pr-check.yml feat โ†’ dev, hotfix โ†’ main PR ESLint ์ •์  ๋ถ„์„, ๋นŒ๋“œ(npm run build), Docker Image ๋นŒ๋“œ(push ์—†์Œ)
fe-docker-publish.yml dev push(merge) Docker Image ๋นŒ๋“œ + GHCR push (latest, dev-<short SHA>)
fe-docker-publish.yml release ํƒœ๊ทธ(v*.*.*) push Docker Image ๋นŒ๋“œ + GHCR push (Semantic Version)

1) fe-pr-check.yml - PR ๊ฒ€์ฆ

name: FE CI - PR Check

# feat -> dev PR, hotfix -> main PR: ์ •์  ๋ถ„์„, (๋‹จ์œ„ ํ…Œ์ŠคํŠธ - ํ…Œ์ŠคํŠธ ์ฝ”๋“œ ์—†์–ด ํ˜„์žฌ ์ œ์™ธ), ๋นŒ๋“œ, Docker Image ๋นŒ๋“œ(push ์—†์Œ)
on:
  pull_request:
    branches:
      - dev
      - main
    paths:
      - "ktb_agile_frontend/**"
      - ".github/workflows/fe-pr-check.yml"

# ๋™์ผ PR์— ์—ฐ์† Push ๋ฐœ์ƒ ์‹œ ์ง„ํ–‰ ์ค‘์ด๋˜ ์ด์ „ ์‹คํ–‰ ์ž๋™ ์ทจ์†Œ
concurrency:
  group: fe-pr-check-${{ github.event.pull_request.number }}
  cancel-in-progress: true

defaults:
  run:
    working-directory: ktb_agile_frontend

jobs:
  verify:
    name: ์ •์  ๋ถ„์„ ๋ฐ ๋นŒ๋“œ
    runs-on: ubuntu-latest
    steps:
      - name: Checkout # ์†Œ์Šค ์ฝ”๋“œ ๋‹ค์šด๋กœ๋“œ
        uses: actions/checkout@v7

      - name: Setup Node.js
        uses: actions/setup-node@v7
        with:
          node-version: "24.21.0"
          cache: npm
          cache-dependency-path: ktb_agile_frontend/package-lock.json

      - name: ์˜์กด์„ฑ ์„ค์น˜
        run: npm ci

      - name: ์ •์  ๋ถ„์„ (ESLint)
        run: npm run lint

      # ๋‹จ์œ„ ํ…Œ์ŠคํŠธ - ํ˜„์žฌ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๊ฐ€ ์—†์–ด ์ƒ๋žต

      - name: ๋นŒ๋“œ
        run: npm run build

  docker-build:
    name: Docker Image ๋นŒ๋“œ (๊ฒ€์ฆ์šฉ, push ์—†์Œ)
    needs: verify
    # ARM64 ์ „์šฉ GitHub Hosted ๋Ÿฌ๋„ˆ ์‚ฌ์šฉ
    runs-on: ubuntu-24.04-arm
    steps:
      - name: Checkout
        uses: actions/checkout@v7

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v4

      - name: Docker Image ๋นŒ๋“œ (push ์—†์Œ)
        uses: docker/build-push-action@v7
        with:
          context: ktb_agile_frontend # ๋นŒ๋“œ ์ปจํ…์ŠคํŠธ ๊ฒฝ๋กœ
          push: false
          tags: ktb4-1st-fe:pr-${{ github.event.pull_request.number }}

์„ธ๋ถ€ ๊ฒฐ์ • ์‚ฌํ•ญ

  • ์ •์  ๋ถ„์„: ESLint โ€” ํ˜‘์—…-๊ฐ€์ด๋“œ-v1์—์„œ Next.js ํŒŒํŠธ์˜ CI ํ’ˆ์งˆ ๊ฒŒ์ดํŠธ ๋„๊ตฌ๋กœ ์ง€์ •๋จ
  • ๋นŒ๋“œ: npm run build
  • ๋‹จ์œ„ ํ…Œ์ŠคํŠธ ์ œ์™ธ - ํ˜„์žฌ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๊ฐ€ ์—†์–ด ์ƒ๋žต
  • PR ๋‹จ๊ณ„ Docker ๋นŒ๋“œ๋Š” push ์•ˆ ํ•จ - PR์€ merge๊ฐ€ ๊ฑฐ์ ˆ๋˜์–ด ์ฝ”๋“œ๊ฐ€ ๋ฐ”๋€” ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ, ๋นŒ๋“œ ์„ฑ๊ณต ์—ฌ๋ถ€๋งŒ ํ™•์ธํ•˜๊ณ  ๋ ˆ์ง€์ŠคํŠธ๋ฆฌ์—๋Š” ์˜ฌ๋ฆฌ์ง€ ์•Š์Œ

2) fe-docker-publish.yml - ์ด๋ฏธ์ง€ ๋นŒ๋“œ/ํ‘ธ์‹œ

name: FE Docker Image Publish

# dev push(merge): Docker Image ๋นŒ๋“œ ๋ฐ GHCR(100-hours-a-week ์กฐ์ง ์†Œ์† ํŒจํ‚ค์ง€) Push
#   - latest(๋กœ์ปฌ ํ…Œ์ŠคํŠธ์šฉ ํฌ์ธํ„ฐ), dev-<short SHA>(๋ถˆ๋ณ€ ์ถ”์ ์šฉ) ํƒœ๊ทธ
# release tag(v*.*.*) push: ๋ฐฐํฌ์šฉ Docker Image ๋นŒ๋“œ ๋ฐ GHCR Push
#   - main์œผ๋กœ dev/hotfix๊ฐ€ merge๋œ ๋’ค, ๋ฆด๋ฆฌ์ฆˆ ๋‹ด๋‹น์ž๊ฐ€ ํ•ด๋‹น ์ปค๋ฐ‹์— Semantic Version ํƒœ๊ทธ(์˜ˆ: v1.0.1)๋ฅผ pushํ•˜๋ฉด ํŠธ๋ฆฌ๊ฑฐ๋จ
#   - Semantic Version ํƒœ๊ทธ(v ์ ‘๋‘์–ด ์ œ์™ธ)๋งŒ ์ด๋ฏธ์ง€ ํƒœ๊ทธ๋กœ ์‚ฌ์šฉ
on:
  push:
    branches:
      - dev
    tags:
      - "v*.*.*"
    paths:
      - "ktb_agile_frontend/**"
      - ".github/workflows/fe-docker-publish.yml"

# ๋™์ผ ๋ธŒ๋žœ์น˜/ํƒœ๊ทธ์— ์—ฐ์† Push ๋ฐœ์ƒ ์‹œ ์ง„ํ–‰ ์ค‘์ด๋˜ ์ด์ „ ์‹คํ–‰ ์ž๋™ ์ทจ์†Œ
concurrency:
  group: fe-docker-publish-${{ github.ref }} # dev์— pushํ•˜๋ฉด refs/heads/dev, ํƒœ๊ทธ pushํ•˜๋ฉด refs/tags/v1.0.1
  cancel-in-progress: true

env:
  REGISTRY: ghcr.io
  # github.repository๋Š” "100-hours-a-week/KTB4-1st-FE" ํ˜•ํƒœ(docker/metadata-action์ด images ๊ฐ’์„ ์ž๋™์œผ๋กœ ์†Œ๋ฌธ์ž ๋ณ€ํ™˜)
  IMAGE_NAME: ${{ github.repository }}

jobs:
  build-and-push:
    name: Docker Image ๋นŒ๋“œ ๋ฐ GHCR Push
    # ARM64 ์ „์šฉ GitHub Hosted ๋Ÿฌ๋„ˆ ์‚ฌ์šฉ
    runs-on: ubuntu-24.04-arm
    permissions:
      contents: read
      packages: write
    steps:
      - name: Checkout
        uses: actions/checkout@v7

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v4

      - name: Login to GHCR
        uses: docker/login-action@v4
        with:
          registry: ${{ env.REGISTRY }}
          username: ${{ github.actor }}
          password: ${{ secrets.GHCR_TOKEN }}

      - name: Docker ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ(ํƒœ๊ทธ/๋ผ๋ฒจ) ์ƒ์„ฑ
        id: meta
        uses: docker/metadata-action@v6
        with:
          images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
          # dev          : latest(๋กœ์ปฌ ํ…Œ์ŠคํŠธ์šฉ ํฌ์ธํ„ฐ), dev-<short SHA>(๋ถˆ๋ณ€ ์ถ”์ ์šฉ)
          # release tag  : push๋œ ํƒœ๊ทธ(v1.0.1)์—์„œ v๋ฅผ ๋—€ Semantic Version์„ ๊ทธ๋Œ€๋กœ ํƒœ๊ทธ๋กœ ์‚ฌ์šฉ
          tags: |
            type=raw,value=latest,enable=${{ github.ref == 'refs/heads/dev' }}
            type=sha,format=short,prefix=dev-,enable=${{ github.ref == 'refs/heads/dev' }}
            type=semver,pattern={{version}}
          labels: |
            org.opencontainers.image.authors=${{ github.actor }}

      - name: Docker Image ๋นŒ๋“œ ๋ฐ Push
        id: build-push
        run: |
          set -e

          # ํƒœ๊ทธ ๋ณ€ํ™˜
          TAG_ARGS=()
          while IFS= read -r tag; do
            [ -z "$tag" ] && continue
            TAG_ARGS+=(--tag "$tag")
          done <<< "${{ steps.meta.outputs.tags }}"

          # ๋ผ๋ฒจ ๋ณ€ํ™˜
          LABEL_ARGS=()
          while IFS= read -r label; do
            [ -z "$label" ] && continue
            LABEL_ARGS+=(--label "$label")
          done <<< "${{ steps.meta.outputs.labels }}"

          # ์ด๋ฏธ์ง€ ๋นŒ๋“œ + GHCR push
          docker buildx build \
            --push \
            "${TAG_ARGS[@]}" \
            "${LABEL_ARGS[@]}" \
            ./ktb_agile_frontend

์„ธ๋ถ€ ๊ฒฐ์ • ์‚ฌํ•ญ

  • dev ํƒœ๊ทธ: latest(๋กœ์ปฌ ํ…Œ์ŠคํŠธ์šฉ ํฌ์ธํ„ฐ), dev-<short SHA>(๋ถˆ๋ณ€ ์ถ”์ ์šฉ)
  • main ๋ฐฐํฌ ๋ฒ„์ „ = Git tag ๊ธฐ๋ฐ˜ Semantic Version
    • ์ฒ˜์Œ์—๋Š” package.json์˜ version ๊ฐ’์„ ์ฝ์–ด ํƒœ๊ทธ๋กœ ์“ฐ๋Š” ๋ฐฉ์‹์œผ๋กœ ๊ตฌํ˜„ํ–ˆ์—ˆ์œผ๋‚˜, AI ๋ ˆํฌ์™€ ๋ฒ„์ „ ๊ด€๋ฆฌ ๋ฐฉ์‹์„ ํ†ต์ผํ•˜๊ธฐ ์œ„ํ•ด Git tag(v*.*.*) push ๊ธฐ์ค€์œผ๋กœ ๋ณ€๊ฒฝํ–ˆ๋‹ค. (์•„๋ž˜ "์„ค๊ณ„ ๋ฌธ์„œ์™€ ๋‹ฌ๋ผ์ง„ ์ " ์ฐธ๊ณ )
    • docker/metadata-action์˜ type=semver,pattern={{version}}์ด push๋œ ํƒœ๊ทธ(์˜ˆ: v1.0.1)์—์„œ v ์ ‘๋‘์–ด๋ฅผ ๋—€ 1.0.1์„ ์ž๋™์œผ๋กœ ์ด๋ฏธ์ง€ ํƒœ๊ทธ๋กœ ์ถ”์ถœ
    • main์œผ๋กœ ๋ณ‘ํ•ฉ๋งŒ ํ•ด์„œ๋Š” ์ด๋ฏธ์ง€๊ฐ€ ๋งŒ๋“ค์–ด์ง€์ง€ ์•Š๋Š”๋‹ค. ๋ฐ˜๋“œ์‹œ ํƒœ๊ทธ๋ฅผ pushํ•ด์•ผ ํŠธ๋ฆฌ๊ฑฐ๋œ๋‹ค.
  • ๋ฆด๋ฆฌ์ฆˆ ์ ˆ์ฐจ (dev/hotfix โ†’ main PR merge ์ดํ›„)
    1. GitHub ์ €์žฅ์†Œ โ†’ Releases โ†’ Create a new release
    2. Target: main, ์ƒˆ ํƒœ๊ทธ๋ฅผ v1.0.1 ํ˜•์‹์œผ๋กœ ์ž…๋ ฅ ํ›„ ์ƒ์„ฑ
    3. Publish release ํด๋ฆญ โ†’ ํƒœ๊ทธ๊ฐ€ push๋˜์–ด ์›Œํฌํ”Œ๋กœ์šฐ๊ฐ€ ์ž๋™ ์‹คํ–‰๋จ
    • CLI๋กœ git tag/git push ํ•ด๋„ ๋™์ผํ•˜๊ฒŒ ๋™์ž‘ํ•˜์ง€๋งŒ, GitHub Release ์ชฝ์ด CLI ์—†์ด ๋ธŒ๋ผ์šฐ์ €์—์„œ ๋๋‚˜๊ณ  ๋ฆด๋ฆฌ์ฆˆ ๋…ธํŠธ๋„ ํ•จ๊ป˜ ๋‚จ์•„ ์ด ๋ฐฉ๋ฒ•์„ ์‚ฌ์šฉํ•˜๊ธฐ๋กœ ๊ฒฐ์ •ํ•จ
  • ๋ผ๋ฒจ ์ž‘์„ฑ์ž ์ •๋ณด - github.event.head_commit.author.name ๋Œ€์‹  github.actor๋ฅผ ์‚ฌ์šฉ
    • Annotated tag๋ฅผ pushํ•˜๋Š” ์ด๋ฒคํŠธ์—์„œ๋Š” head_commit์ด ๋น„์–ด ์žˆ์„ ์ˆ˜ ์žˆ์–ด(GitHub ์›นํ›… ํŠน์„ฑ), ํ•ญ์ƒ ๊ฐ’์ด ์ฑ„์›Œ์ง€๋Š” github.actor(ํƒœ๊ทธ๋ฅผ pushํ•œ ์‚ฌ๋žŒ)๋กœ ๋Œ€์ฒด

์„ค๊ณ„ ๋ฌธ์„œ(2๋‹จ๊ณ„ CI ํŒŒ์ดํ”„๋ผ์ธ ์„ค๊ณ„)์™€ ๋‹ฌ๋ผ์ง„ ์ 

ํ•ญ๋ชฉ ์„ค๊ณ„ ๋ฌธ์„œ(2๋‹จ๊ณ„) ์‹ค์ œ ๊ตฌํ˜„ ์‚ฌ์œ 
๋‹จ์œ„ ํ…Œ์ŠคํŠธ (PR ๋‹จ๊ณ„) ํฌํ•จ ์ œ์™ธ ํ˜„์žฌ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ ์—†์Œ
dev โ†’ main PR ์ „์ฒด ํ…Œ์ŠคํŠธ ์›Œํฌํ”Œ๋กœ์šฐ ํฌํ•จ ๋ฏธ๊ตฌํ˜„ ํ˜„์žฌ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ ์—†์Œ
main ๋ฐฐํฌ ๋ฒ„์ „ ๊ด€๋ฆฌ ๋ช…์‹œ ์•ˆ ๋จ(Semantic Version ํƒœ๊ทธ ์‚ฌ์šฉ๋งŒ ๊ธฐ์ˆ ) Git tag(v*.*.*) push ๊ธฐ๋ฐ˜, GitHub Release๋กœ ์ƒ์„ฑ ์ตœ์ดˆ ๊ตฌํ˜„์€ package.json ๋ฒ„์ „ ๋ฐฉ์‹์ด์—ˆ์œผ๋‚˜, ๋ฒ„์ „์„ ์ฝ”๋“œ ํŒŒ์ผ๋กœ ๊ด€๋ฆฌํ•˜๋ฉด ๋งค ๋ฐฐํฌ๋งˆ๋‹ค ๊ทธ ํŒŒ์ผ์„ ๊ณ ์น˜๋Š” PR์ด ๋ณ„๋„๋กœ ํ•„์š”ํ•จ. Git tag๋ฅผ ๋‹จ์ผ ์ง„์‹ค ๊ณต๊ธ‰์›์œผ๋กœ ์‚ผ์œผ๋ฉด ๊ทธ ์ ˆ์ฐจ๊ฐ€ ์—†์–ด์ง€๊ณ , "ํƒœ๊ทธ push = ๋ฐฐํฌ ํŠธ๋ฆฌ๊ฑฐ"๋กœ ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ๋ถ„๋ฆฌ๋œ๋‹ค. AI ๋ ˆํฌ์™€ ๋™์ผํ•œ ๋ฐฉ์‹์œผ๋กœ ํ†ต์ผ
main push(merge) ํŠธ๋ฆฌ๊ฑฐ main push(merge) ์‹œ ์ด๋ฏธ์ง€ ๋นŒ๋“œ release ํƒœ๊ทธ(v*.*.*) push ์‹œ ์ด๋ฏธ์ง€ ๋นŒ๋“œ ์œ„ ๋ฒ„์ „ ๊ด€๋ฆฌ ๋ฐฉ์‹ ๋ณ€๊ฒฝ์— ๋”ฐ๋ฅธ ํŠธ๋ฆฌ๊ฑฐ ๋ณ€๊ฒฝ

์ฐธ๊ณ 

  • Cloud-Design-2nd โ€” 2๋‹จ๊ณ„ CI ํŒŒ์ดํ”„๋ผ์ธ ์„ค๊ณ„ (Trigger, CI Stages, ์‹คํŒจ ์ฒ˜๋ฆฌ ์ •์ฑ… ๋“ฑ ๊ทผ๊ฑฐ)
  • Cloud-Dev-v1-Dockerfile โ€” Frontend Dockerfile ๊ตฌ์„ฑ
โš ๏ธ **GitHub.com Fallback** โš ๏ธ