Sam Baek, The Dev's Corner

๐Ÿš€ CI/CD ํŒŒ์ดํ”„๋ผ์ธ ๊ตฌ์ถ• ์™„๋ฒฝ ๊ฐ€์ด๋“œ (GitHub Actions, Docker, ๋ฐฐํฌ ์ „๋žต)

09 Nov 2025

CI/CD๋ž€ ๋ฌด์—‡์ธ๊ฐ€


๊ฐœ๋ฐœ์ž๊ฐ€ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๊ณ  ๋ฐฐํฌํ•˜๋Š” ๊ณผ์ •,
๋งค๋ฒˆ ์ˆ˜๋™์œผ๋กœ ํ•˜๋ฉด ์‹œ๊ฐ„์ด ์˜ค๋ž˜ ๊ฑธ๋ฆฌ๊ณ  ์‹ค์ˆ˜ํ•  ์ˆ˜ ์žˆ๋‹ค.

CI/CD๋Š” ์ด๋Ÿฐ ๊ณผ์ •์„ ์ž๋™ํ™”ํ•˜๋Š” ๋ฐฉ๋ฒ•์ด๋‹ค.

๋งˆ์น˜ ๊ณต์žฅ์˜ ์ž๋™ํ™” ์ƒ์‚ฐ ๋ผ์ธ์ฒ˜๋Ÿผ,
์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๋ฉด ์ž๋™์œผ๋กœ ํ…Œ์ŠคํŠธํ•˜๊ณ ,
๋ฌธ์ œ๊ฐ€ ์—†์œผ๋ฉด ์ž๋™์œผ๋กœ ๋ฐฐํฌ๋œ๋‹ค.

CI (Continuous Integration): ์ง€์†์  ํ†ตํ•ฉ
๊ฐœ๋ฐœ์ž๋“ค์ด ์ž‘์„ฑํ•œ ์ฝ”๋“œ๋ฅผ ์ž๋™์œผ๋กœ ํ†ตํ•ฉํ•˜๊ณ  ํ…Œ์ŠคํŠธ

CD (Continuous Deployment/Delivery): ์ง€์†์  ๋ฐฐํฌ
ํ…Œ์ŠคํŠธ๋ฅผ ํ†ต๊ณผํ•œ ์ฝ”๋“œ๋ฅผ ์ž๋™์œผ๋กœ ์„œ๋ฒ„์— ๋ฐฐํฌ

์™œ CI/CD๋ฅผ ๋ฐฐ์›Œ์•ผ ํ• ๊นŒ?


์ด์œ  1: ์‹œ๊ฐ„ ์ ˆ์•ฝ
์ˆ˜๋™ ๋ฐฐํฌ 30๋ถ„ โ†’ ์ž๋™ ๋ฐฐํฌ 5๋ถ„

์ด์œ  2: ๋ฒ„๊ทธ ์กฐ๊ธฐ ๋ฐœ๊ฒฌ
์ฝ”๋“œ๋ฅผ ํ‘ธ์‹œํ•˜์ž๋งˆ์ž ์ž๋™ ํ…Œ์ŠคํŠธ ์‹คํ–‰

์ด์œ  3: ์•ˆ์ •์ ์ธ ๋ฐฐํฌ
์‚ฌ๋žŒ์˜ ์‹ค์ˆ˜๋ฅผ ์ค„์ด๊ณ  ์ผ๊ด€์„ฑ ์œ ์ง€

์ด์œ  4: ๋ฉด์ ‘ ํ•„์ˆ˜
DevOps, GitHub Actions๋Š” ๋ฉด์ ‘ ๋‹จ๊ณจ ์งˆ๋ฌธ

๊ธฐ๋ณธ ๊ฐœ๋… ์š”์•ฝ


๐Ÿท๏ธ CI/CD ํŒŒ์ดํ”„๋ผ์ธ ๋‹จ๊ณ„


1. [๊ฐœ๋ฐœ์ž] โ†’ [Git Push]
   โ†“
2. [CI ์„œ๋ฒ„] : ์ฝ”๋“œ ๋นŒ๋“œ
   - ์˜์กด์„ฑ ์„ค์น˜
   - ์ปดํŒŒ์ผ
   โ†“
3. [CI ์„œ๋ฒ„] : ํ…Œ์ŠคํŠธ ์‹คํ–‰
   - ๋‹จ์œ„ ํ…Œ์ŠคํŠธ
   - ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ
   - E2E ํ…Œ์ŠคํŠธ
   โ†“
4. [CI ์„œ๋ฒ„] : ์ฝ”๋“œ ํ’ˆ์งˆ ๊ฒ€์‚ฌ
   - ๋ฆฐํŠธ(Lint)
   - ์ฝ”๋“œ ์ปค๋ฒ„๋ฆฌ์ง€
   - ๋ณด์•ˆ ์Šค์บ”
   โ†“
5. [CD ์„œ๋ฒ„] : Docker ์ด๋ฏธ์ง€ ๋นŒ๋“œ
   - Dockerfile๋กœ ์ด๋ฏธ์ง€ ์ƒ์„ฑ
   - ๋ ˆ์ง€์ŠคํŠธ๋ฆฌ์— ํ‘ธ์‹œ
   โ†“
6. [CD ์„œ๋ฒ„] : ๋ฐฐํฌ
   - ๊ฐœ๋ฐœ ํ™˜๊ฒฝ
   - ์Šคํ…Œ์ด์ง• ํ™˜๊ฒฝ
   - ํ”„๋กœ๋•์…˜ ํ™˜๊ฒฝ
   โ†“
7. [๋ชจ๋‹ˆํ„ฐ๋ง]
   - ํ—ฌ์Šค ์ฒดํฌ
   - ๋กœ๊ทธ ํ™•์ธ
   - ์•Œ๋ฆผ


๐Ÿท๏ธ CI/CD ๋„๊ตฌ ๋น„๊ต


๋„๊ตฌ ํƒ€์ž… ๊ฐ€๊ฒฉ ํŠน์ง• ์‚ฌ์šฉ ์˜ˆ์‹œ
GitHub Actions ํด๋ผ์šฐ๋“œ ๋ฌด๋ฃŒ~์œ ๋ฃŒ GitHub ํ†ตํ•ฉ, ๊ฐ„ํŽธ ์˜คํ”ˆ์†Œ์Šค, ์Šคํƒ€ํŠธ์—…
GitLab CI/CD ํด๋ผ์šฐ๋“œ/์˜จํ”„๋ ˆ๋ฏธ์Šค ๋ฌด๋ฃŒ~์œ ๋ฃŒ ์˜ฌ์ธ์› ํ”Œ๋žซํผ ๋Œ€๊ธฐ์—…
Jenkins ์˜จํ”„๋ ˆ๋ฏธ์Šค ๋ฌด๋ฃŒ ๊ฐ•๋ ฅํ•˜๊ณ  ์œ ์—ฐ ๋ ˆ๊ฑฐ์‹œ ์‹œ์Šคํ…œ
CircleCI ํด๋ผ์šฐ๋“œ ๋ฌด๋ฃŒ~์œ ๋ฃŒ ๋น ๋ฅธ ๋นŒ๋“œ SaaS ์„œ๋น„์Šค
Travis CI ํด๋ผ์šฐ๋“œ ๋ฌด๋ฃŒ~์œ ๋ฃŒ ์˜คํ”ˆ์†Œ์Šค ์นœํ™”์  ์˜คํ”ˆ์†Œ์Šค ํ”„๋กœ์ ํŠธ


๐Ÿท๏ธ GitHub Actions ํ•ต์‹ฌ ๊ฐœ๋…


1. Workflow (์›Œํฌํ”Œ๋กœ์šฐ)


๊ฐœ๋…: ์ž๋™ํ™”๋œ ํ”„๋กœ์„ธ์Šค ์ „์ฒด
.github/workflows/ ๋””๋ ‰ํ† ๋ฆฌ์— YAML ํŒŒ์ผ๋กœ ์ •์˜

2. Event (์ด๋ฒคํŠธ)


๊ฐœ๋…: ์›Œํฌํ”Œ๋กœ์šฐ๋ฅผ ์‹คํ–‰์‹œํ‚ค๋Š” ํŠธ๋ฆฌ๊ฑฐ

์ฃผ์š” ์ด๋ฒคํŠธ:

  • push: ์ฝ”๋“œ ํ‘ธ์‹œ
  • pull_request: PR ์ƒ์„ฑ/์ˆ˜์ •
  • schedule: ์ฃผ๊ธฐ์  ์‹คํ–‰ (cron)
  • workflow_dispatch: ์ˆ˜๋™ ์‹คํ–‰


3. Job (์ž‘์—…)


๊ฐœ๋…: ํ•˜๋‚˜ ์ด์ƒ์˜ Step์œผ๋กœ ๊ตฌ์„ฑ๋œ ์ž‘์—… ๋‹จ์œ„
์—ฌ๋Ÿฌ Job์€ ๋ณ‘๋ ฌ ๋˜๋Š” ์ˆœ์ฐจ ์‹คํ–‰ ๊ฐ€๋Šฅ

4. Step (๋‹จ๊ณ„)


๊ฐœ๋…: Job ๋‚ด์˜ ๊ฐœ๋ณ„ ์ž‘์—…
๋ช…๋ น์–ด ์‹คํ–‰ ๋˜๋Š” Action ์‚ฌ์šฉ

5. Action (์•ก์…˜)


๊ฐœ๋…: ์žฌ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ์ž‘์—… ๋ชจ๋“ˆ
GitHub Marketplace์—์„œ ๋‹ค์šด๋กœ๋“œ ๊ฐ€๋Šฅ

6. Runner (๋Ÿฌ๋„ˆ)


๊ฐœ๋…: ์›Œํฌํ”Œ๋กœ์šฐ๋ฅผ ์‹คํ–‰ํ•˜๋Š” ์„œ๋ฒ„
GitHub ์ œ๊ณต ๋˜๋Š” Self-hosted

๐Ÿท๏ธ ๋ฐฐํฌ ์ „๋žต


1. Rolling Deployment (๋กค๋ง ๋ฐฐํฌ)


๊ฐœ๋…: ์„œ๋ฒ„๋ฅผ ์ˆœ์ฐจ์ ์œผ๋กœ ๊ต์ฒด

[์„œ๋ฒ„1] [์„œ๋ฒ„2] [์„œ๋ฒ„3] [์„œ๋ฒ„4]
  โ†“
[์ƒˆ๋ฒ„์ „] [์„œ๋ฒ„2] [์„œ๋ฒ„3] [์„œ๋ฒ„4]
  โ†“
[์ƒˆ๋ฒ„์ „] [์ƒˆ๋ฒ„์ „] [์„œ๋ฒ„3] [์„œ๋ฒ„4]
  โ†“
[์ƒˆ๋ฒ„์ „] [์ƒˆ๋ฒ„์ „] [์ƒˆ๋ฒ„์ „] [์„œ๋ฒ„4]
  โ†“
[์ƒˆ๋ฒ„์ „] [์ƒˆ๋ฒ„์ „] [์ƒˆ๋ฒ„์ „] [์ƒˆ๋ฒ„์ „]


์žฅ์ : ๋ฌด์ค‘๋‹จ ๋ฐฐํฌ, ์ ์ง„์  ์—…๋ฐ์ดํŠธ
๋‹จ์ : ๋ฐฐํฌ ์‹œ๊ฐ„ ์˜ค๋ž˜ ๊ฑธ๋ฆผ, ๋ฒ„์ „ ํ˜ผ์žฌ

2. Blue-Green Deployment


๊ฐœ๋…: ๋‘ ๊ฐœ์˜ ํ™˜๊ฒฝ์„ ๊ต๋Œ€๋กœ ์‚ฌ์šฉ

[Blue ํ™˜๊ฒฝ - ๊ตฌ๋ฒ„์ „] โ† ํ˜„์žฌ ํŠธ๋ž˜ํ”ฝ
[Green ํ™˜๊ฒฝ - ์‹ ๋ฒ„์ „] โ† ๋ฐฐํฌ ์ค‘

๋ฐฐํฌ ์™„๋ฃŒ ํ›„:
[Blue ํ™˜๊ฒฝ - ๊ตฌ๋ฒ„์ „]
[Green ํ™˜๊ฒฝ - ์‹ ๋ฒ„์ „] โ† ํŠธ๋ž˜ํ”ฝ ์ „ํ™˜

๋ฌธ์ œ ๋ฐœ์ƒ ์‹œ ์ฆ‰์‹œ Blue๋กœ ๋กค๋ฐฑ


์žฅ์ : ์ฆ‰์‹œ ๋กค๋ฐฑ ๊ฐ€๋Šฅ, ๋ฌด์ค‘๋‹จ ๋ฐฐํฌ
๋‹จ์ : 2๋ฐฐ์˜ ๋ฆฌ์†Œ์Šค ํ•„์š”

3. Canary Deployment (์นด๋‚˜๋ฆฌ ๋ฐฐํฌ)


๊ฐœ๋…: ์ผ๋ถ€ ํŠธ๋ž˜ํ”ฝ๋งŒ ์‹ ๋ฒ„์ „์œผ๋กœ ์ „ํ™˜

[๊ตฌ๋ฒ„์ „] โ† 90% ํŠธ๋ž˜ํ”ฝ
[์‹ ๋ฒ„์ „] โ† 10% ํŠธ๋ž˜ํ”ฝ

๋ฌธ์ œ์—†์œผ๋ฉด ์ ์ง„์ ์œผ๋กœ ์ฆ๊ฐ€:
[๊ตฌ๋ฒ„์ „] โ† 50% ํŠธ๋ž˜ํ”ฝ
[์‹ ๋ฒ„์ „] โ† 50% ํŠธ๋ž˜ํ”ฝ

์ตœ์ข…:
[์‹ ๋ฒ„์ „] โ† 100% ํŠธ๋ž˜ํ”ฝ


์žฅ์ : ์œ„ํ—˜ ์ตœ์†Œํ™”, ์ ์ง„์  ๊ฒ€์ฆ
๋‹จ์ : ๋ณต์žกํ•œ ํŠธ๋ž˜ํ”ฝ ๊ด€๋ฆฌ

์‹ค์ „ ์˜ˆ์‹œ


๐Ÿท๏ธ GitHub Actions ๊ธฐ๋ณธ ์›Œํฌํ”Œ๋กœ์šฐ


1. ํ”„๋กœ์ ํŠธ ๊ตฌ์กฐ


project/
โ”œโ”€โ”€ .github/
โ”‚   โ””โ”€โ”€ workflows/
โ”‚       โ”œโ”€โ”€ ci.yml          # CI ์›Œํฌํ”Œ๋กœ์šฐ
โ”‚       โ”œโ”€โ”€ deploy-dev.yml  # ๊ฐœ๋ฐœ ํ™˜๊ฒฝ ๋ฐฐํฌ
โ”‚       โ””โ”€โ”€ deploy-prod.yml # ํ”„๋กœ๋•์…˜ ๋ฐฐํฌ
โ”œโ”€โ”€ src/
โ”œโ”€โ”€ tests/
โ”œโ”€โ”€ Dockerfile
โ””โ”€โ”€ package.json


2. CI ์›Œํฌํ”Œ๋กœ์šฐ (ci.yml)


name: CI

on:
  push:
    branches: [ main, develop ]
  pull_request:
    branches: [ main, develop ]

jobs:
  test:
    runs-on: ubuntu-latest
    
    strategy:
      matrix:
        node-version: [18.x, 20.x]
    
    steps:
      # 1. ์ฝ”๋“œ ์ฒดํฌ์•„์›ƒ
      - name: Checkout code
        uses: actions/checkout@v4
      
      # 2. Node.js ์„ค์ •
      - name: Setup Node.js $
        uses: actions/setup-node@v4
        with:
          node-version: $
          cache: 'npm'
      
      # 3. ์˜์กด์„ฑ ์„ค์น˜
      - name: Install dependencies
        run: npm ci
      
      # 4. ๋ฆฐํŠธ ๊ฒ€์‚ฌ
      - name: Run linter
        run: npm run lint
      
      # 5. ๋‹จ์œ„ ํ…Œ์ŠคํŠธ
      - name: Run unit tests
        run: npm test
      
      # 6. ์ฝ”๋“œ ์ปค๋ฒ„๋ฆฌ์ง€
      - name: Upload coverage to Codecov
        uses: codecov/codecov-action@v3
        with:
          files: ./coverage/coverage-final.json
          fail_ci_if_error: true
  
  e2e-test:
    runs-on: ubuntu-latest
    needs: test
    
    steps:
      - uses: actions/checkout@v4
      
      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: 20.x
      
      - name: Install dependencies
        run: npm ci
      
      # 7. Playwright ์„ค์น˜
      - name: Install Playwright
        run: npx playwright install --with-deps
      
      # 8. E2E ํ…Œ์ŠคํŠธ
      - name: Run E2E tests
        run: npm run test:e2e
      
      # 9. ํ…Œ์ŠคํŠธ ์‹คํŒจ ์‹œ ์Šคํฌ๋ฆฐ์ƒท ์—…๋กœ๋“œ
      - name: Upload test results
        if: failure()
        uses: actions/upload-artifact@v3
        with:
          name: playwright-report
          path: playwright-report/
          retention-days: 30
  
  security-scan:
    runs-on: ubuntu-latest
    needs: test
    
    steps:
      - uses: actions/checkout@v4
      
      # 10. ๋ณด์•ˆ ์ทจ์•ฝ์  ์Šค์บ”
      - name: Run security audit
        run: npm audit --audit-level=moderate
      
      # 11. SAST (์ •์  ๋ถ„์„)
      - name: Run CodeQL Analysis
        uses: github/codeql-action/init@v2
        with:
          languages: javascript
      
      - name: Perform CodeQL Analysis
        uses: github/codeql-action/analyze@v2


๐Ÿท๏ธ Docker ๋นŒ๋“œ ๋ฐ ๋ฐฐํฌ


1. Dockerfile


# ๋ฉ€ํ‹ฐ ์Šคํ…Œ์ด์ง€ ๋นŒ๋“œ
FROM node:20-alpine AS builder

WORKDIR /app

# ์˜์กด์„ฑ ์„ค์น˜
COPY package*.json ./
RUN npm ci --only=production

# ์†Œ์Šค ๋ณต์‚ฌ ๋ฐ ๋นŒ๋“œ
COPY . .
RUN npm run build

# ํ”„๋กœ๋•์…˜ ์ด๋ฏธ์ง€
FROM node:20-alpine

WORKDIR /app

# ๋นŒ๋“œ ๊ฒฐ๊ณผ๋ฌผ๋งŒ ๋ณต์‚ฌ
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
COPY package.json ./

# ๋น„root ์‚ฌ์šฉ์ž๋กœ ์‹คํ–‰
RUN addgroup -g 1001 -S nodejs
RUN adduser -S nodejs -u 1001
USER nodejs

EXPOSE 3000

CMD ["node", "dist/main.js"]


2. Docker ๋นŒ๋“œ ์›Œํฌํ”Œ๋กœ์šฐ


name: Build and Push Docker Image

on:
  push:
    branches: [ main ]

env:
  REGISTRY: ghcr.io
  IMAGE_NAME: $

jobs:
  build-and-push:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      packages: write
    
    steps:
      - name: Checkout
        uses: actions/checkout@v4
      
      # 1. Docker Buildx ์„ค์ •
      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3
      
      # 2. GitHub Container Registry ๋กœ๊ทธ์ธ
      - name: Log in to Container Registry
        uses: docker/login-action@v3
        with:
          registry: $
          username: $
          password: $
      
      # 3. ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ ์ถ”์ถœ
      - name: Extract metadata
        id: meta
        uses: docker/metadata-action@v5
        with:
          images: $/$
          tags: |
            type=ref,event=branch
            type=sha,prefix=-
            type=semver,pattern=
      
      # 4. Docker ์ด๋ฏธ์ง€ ๋นŒ๋“œ ๋ฐ ํ‘ธ์‹œ
      - name: Build and push
        uses: docker/build-push-action@v5
        with:
          context: .
          push: true
          tags: $
          labels: $
          cache-from: type=gha
          cache-to: type=gha,mode=max


๐Ÿท๏ธ ๋ฐฐํฌ ์›Œํฌํ”Œ๋กœ์šฐ (AWS ECS)


name: Deploy to Production

on:
  push:
    branches: [ main ]

jobs:
  deploy:
    runs-on: ubuntu-latest
    environment: production
    
    steps:
      - name: Checkout
        uses: actions/checkout@v4
      
      # 1. AWS ์ž๊ฒฉ์ฆ๋ช… ์„ค์ •
      - name: Configure AWS credentials
        uses: aws-actions/configure-aws-credentials@v4
        with:
          aws-access-key-id: $
          aws-secret-access-key: $
          aws-region: ap-northeast-2
      
      # 2. ECR ๋กœ๊ทธ์ธ
      - name: Login to Amazon ECR
        id: login-ecr
        uses: aws-actions/amazon-ecr-login@v2
      
      # 3. Docker ์ด๋ฏธ์ง€ ๋นŒ๋“œ ๋ฐ ํ‘ธ์‹œ
      - name: Build and push image to ECR
        id: build-image
        env:
          ECR_REGISTRY: $
          ECR_REPOSITORY: my-app
          IMAGE_TAG: $
        run: |
          docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG .
          docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG
          echo "image=$ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG" >> $GITHUB_OUTPUT
      
      # 4. ECS ํƒœ์Šคํฌ ์ •์˜ ์—…๋ฐ์ดํŠธ
      - name: Fill in the new image ID in the ECS task definition
        id: task-def
        uses: aws-actions/amazon-ecs-render-task-definition@v1
        with:
          task-definition: task-definition.json
          container-name: my-app
          image: $
      
      # 5. ECS ์„œ๋น„์Šค ๋ฐฐํฌ
      - name: Deploy to Amazon ECS
        uses: aws-actions/amazon-ecs-deploy-task-definition@v1
        with:
          task-definition: $
          service: my-app-service
          cluster: my-app-cluster
          wait-for-service-stability: true
      
      # 6. Slack ์•Œ๋ฆผ
      - name: Notify Slack
        if: always()
        uses: 8398a7/action-slack@v3
        with:
          status: $
          text: 'Deployment to production: $'
          webhook_url: $


๐Ÿท๏ธ Blue-Green ๋ฐฐํฌ ์ „๋žต


name: Blue-Green Deployment

on:
  workflow_dispatch:
    inputs:
      environment:
        description: 'Target environment'
        required: true
        type: choice
        options:
          - blue
          - green

jobs:
  deploy:
    runs-on: ubuntu-latest
    
    steps:
      - name: Checkout
        uses: actions/checkout@v4
      
      - name: Configure AWS credentials
        uses: aws-actions/configure-aws-credentials@v4
        with:
          aws-access-key-id: $
          aws-secret-access-key: $
          aws-region: ap-northeast-2
      
      # 1. Blue ๋˜๋Š” Green ํ™˜๊ฒฝ์— ๋ฐฐํฌ
      - name: Deploy to $ environment
        run: |
          echo "Deploying to $ environment"
          aws ecs update-service \
            --cluster my-cluster \
            --service my-app-$ \
            --force-new-deployment
      
      # 2. ํ—ฌ์Šค ์ฒดํฌ
      - name: Wait for deployment
        run: |
          aws ecs wait services-stable \
            --cluster my-cluster \
            --services my-app-$
      
      # 3. ์Šค๋ชจํฌ ํ…Œ์ŠคํŠธ
      - name: Run smoke tests
        run: |
          ENDPOINT="https://$.example.com"
          curl -f $ENDPOINT/health || exit 1
          
      # 4. ํŠธ๋ž˜ํ”ฝ ์ „ํ™˜ (์ˆ˜๋™ ์Šน์ธ ํ•„์š”)
      - name: Switch traffic
        if: inputs.environment == 'green' && success()
        run: |
          echo "Ready to switch traffic to green"
          # Route53์ด๋‚˜ ALB ์„ค์ • ๋ณ€๊ฒฝ


๐Ÿท๏ธ ํ™˜๊ฒฝ๋ณ„ ๋ฐฐํฌ ์„ค์ •


name: Deploy to Environment

on:
  push:
    branches: [ main, develop ]

jobs:
  determine-environment:
    runs-on: ubuntu-latest
    outputs:
      environment: $
    steps:
      - id: set-env
        run: |
          if [[ "$" == "refs/heads/main" ]]; then
            echo "environment=production" >> $GITHUB_OUTPUT
          else
            echo "environment=development" >> $GITHUB_OUTPUT
          fi
  
  deploy:
    needs: determine-environment
    runs-on: ubuntu-latest
    environment: $
    
    steps:
      - name: Checkout
        uses: actions/checkout@v4
      
      - name: Deploy to $
        run: |
          echo "Deploying to $"
          
          # ํ™˜๊ฒฝ๋ณ„ ์„ค์ • ํŒŒ์ผ ์‚ฌ์šฉ
          cp .env.$ .env
          
          # ๋ฐฐํฌ ์Šคํฌ๋ฆฝํŠธ ์‹คํ–‰
          ./deploy.sh $


์‹ค์ „ ์ฒดํฌ๋ฆฌ์ŠคํŠธ


โœ… CI ์„ค์ •


  • ์ž๋™ ๋นŒ๋“œ ์„ค์ •
  • ๋‹จ์œ„ ํ…Œ์ŠคํŠธ ์ž๋™ ์‹คํ–‰
  • ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ ์ž๋™ ์‹คํ–‰
  • E2E ํ…Œ์ŠคํŠธ ์ž๋™ ์‹คํ–‰
  • ์ฝ”๋“œ ๋ฆฐํŠธ ๊ฒ€์‚ฌ
  • ์ฝ”๋“œ ์ปค๋ฒ„๋ฆฌ์ง€ ์ธก์ •


โœ… ๋ณด์•ˆ


  • ์˜์กด์„ฑ ์ทจ์•ฝ์  ์Šค์บ”
  • SAST (์ •์  ๋ถ„์„)
  • ์‹œํฌ๋ฆฟ ๊ด€๋ฆฌ (GitHub Secrets)
  • ์ปจํ…Œ์ด๋„ˆ ์ด๋ฏธ์ง€ ์Šค์บ”
  • ์ตœ์†Œ ๊ถŒํ•œ ์›์น™


โœ… CD ์„ค์ •


  • Docker ์ด๋ฏธ์ง€ ์ž๋™ ๋นŒ๋“œ
  • ๋ ˆ์ง€์ŠคํŠธ๋ฆฌ ํ‘ธ์‹œ
  • ํ™˜๊ฒฝ๋ณ„ ๋ฐฐํฌ ์„ค์ •
  • ๋กค๋ฐฑ ์ „๋žต
  • ํ—ฌ์Šค ์ฒดํฌ


โœ… ๋ชจ๋‹ˆํ„ฐ๋ง


  • ๋ฐฐํฌ ์„ฑ๊ณต/์‹คํŒจ ์•Œ๋ฆผ
  • ๋นŒ๋“œ ์‹œ๊ฐ„ ๋ชจ๋‹ˆํ„ฐ๋ง
  • ํ…Œ์ŠคํŠธ ๊ฒฐ๊ณผ ๋ฆฌํฌํŠธ
  • ๋กœ๊ทธ ์ˆ˜์ง‘
  • ๋ฉ”ํŠธ๋ฆญ ๋Œ€์‹œ๋ณด๋“œ


โœ… ๋ฐฐํฌ ์ „๋žต


  • ๋ฐฐํฌ ์ „๋žต ์„ ํƒ (Rolling/Blue-Green/Canary)
  • ๋ฌด์ค‘๋‹จ ๋ฐฐํฌ ๊ตฌํ˜„
  • ๋กค๋ฐฑ ์ ˆ์ฐจ ๋ฌธ์„œํ™”
  • ๋ฐฐํฌ ์Šน์ธ ํ”„๋กœ์„ธ์Šค
  • ๋ฐฐํฌ ์ผ์ • ๊ด€๋ฆฌ


์š”์•ฝ


CI/CD๋Š” ๊ฐœ๋ฐœ๋ถ€ํ„ฐ ๋ฐฐํฌ๊นŒ์ง€์˜ ๊ณผ์ •์„ ์ž๋™ํ™”ํ•˜๋Š” ํ•„์ˆ˜ ๋„๊ตฌ๋‹ค.

๐Ÿ’Ž ํ•ต์‹ฌ ํฌ์ธํŠธ:

  1. CI: ์ฝ”๋“œ ํ†ตํ•ฉ ๋ฐ ํ…Œ์ŠคํŠธ ์ž๋™ํ™”
  2. CD: ๋ฐฐํฌ ์ž๋™ํ™”
  3. GitHub Actions: ๊ฐ„ํŽธํ•œ CI/CD ๊ตฌํ˜„
  4. Docker: ์ผ๊ด€๋œ ๋ฐฐํฌ ํ™˜๊ฒฝ
  5. ๋ฐฐํฌ ์ „๋žต: Rolling, Blue-Green, Canary
  6. ๋ชจ๋‹ˆํ„ฐ๋ง: ๋ฐฐํฌ ํ›„ ์•ˆ์ •์„ฑ ํ™•์ธ


๐ŸŽฏ CI/CD ํŒŒ์ดํ”„๋ผ์ธ ๋‹จ๊ณ„:

  1. ์ฝ”๋“œ ํ‘ธ์‹œ โ†’ ๊ฐœ๋ฐœ์ž๊ฐ€ ์ฝ”๋“œ ์ปค๋ฐ‹
  2. ๋นŒ๋“œ โ†’ ์˜์กด์„ฑ ์„ค์น˜ ๋ฐ ์ปดํŒŒ์ผ
  3. ํ…Œ์ŠคํŠธ โ†’ ๋‹จ์œ„/ํ†ตํ•ฉ/E2E ํ…Œ์ŠคํŠธ
  4. ์ฝ”๋“œ ํ’ˆ์งˆ โ†’ ๋ฆฐํŠธ, ์ปค๋ฒ„๋ฆฌ์ง€, ๋ณด์•ˆ ์Šค์บ”
  5. ์ด๋ฏธ์ง€ ๋นŒ๋“œ โ†’ Docker ์ด๋ฏธ์ง€ ์ƒ์„ฑ
  6. ๋ฐฐํฌ โ†’ ํ™˜๊ฒฝ๋ณ„ ์ž๋™ ๋ฐฐํฌ
  7. ๋ชจ๋‹ˆํ„ฐ๋ง โ†’ ํ—ฌ์Šค ์ฒดํฌ ๋ฐ ์•Œ๋ฆผ


๐Ÿ“Œ ๋ฐฐํฌ ์ „๋žต ๋น„๊ต:

์ „๋žต ์†๋„ ๋กค๋ฐฑ ๋ฆฌ์†Œ์Šค ์œ„ํ—˜๋„
Rolling ๋А๋ฆผ ๋А๋ฆผ 1๋ฐฐ ์ค‘๊ฐ„
Blue-Green ๋น ๋ฆ„ ์ฆ‰์‹œ 2๋ฐฐ ๋‚ฎ์Œ
Canary ์ค‘๊ฐ„ ๋น ๋ฆ„ 1.1๋ฐฐ ๋งค์šฐ ๋‚ฎ์Œ


๐Ÿš€ Best Practices:

  • ์ž‘์€ ๋‹จ์œ„๋กœ ์ž์ฃผ ๋ฐฐํฌ: ์œ„ํ—˜ ์ตœ์†Œํ™”
  • ์ž๋™ํ™” ์šฐ์„ : ์ˆ˜๋™ ์ž‘์—… ์ตœ์†Œํ™”
  • ํ…Œ์ŠคํŠธ๋Š” ํ•„์ˆ˜: ๋ฐฐํฌ ์ „ ์ถฉ๋ถ„ํ•œ ํ…Œ์ŠคํŠธ
  • ๋กค๋ฐฑ ๊ณ„ํš: ๋ฌธ์ œ ๋ฐœ์ƒ ์‹œ ์ฆ‰์‹œ ๋ณต๊ตฌ
  • ๋ชจ๋‹ˆํ„ฐ๋ง: ๋ฐฐํฌ ํ›„ ์ง€์†์ ์ธ ๊ด€์ฐฐ
  • ๋ฌธ์„œํ™”: ๋ฐฐํฌ ํ”„๋กœ์„ธ์Šค ๋ช…ํ™•ํžˆ ๊ธฐ๋ก


CI/CD๋Š” ํ•œ ๋ฒˆ ์„ค์ •ํ•˜๋ฉด ๊ณ„์† ํ˜œํƒ์„ ๋ฐ›๋Š”๋‹ค.
์ดˆ๊ธฐ ์„ค์ •์— ์‹œ๊ฐ„์ด ๊ฑธ๋ฆฌ๋”๋ผ๋„,
์žฅ๊ธฐ์ ์œผ๋กœ๋Š” ๊ฐœ๋ฐœ ์†๋„์™€ ์•ˆ์ •์„ฑ์„ ํฌ๊ฒŒ ํ–ฅ์ƒ์‹œํ‚จ๋‹ค.