배포 QA 고도화 TDD

Background

Codex Git Hook을 통합해 로컬 코드 품질 게이트를 완성했다. 이제 서버 환경에서의 품질 보증(CI/CD)과 UI 레벨 검증(E2E)이 필요하다.

Overview

GitHub Actions 기반 CI/CD 파이프라인을 구축하고, Playwright E2E 테스트로 UI-API 전체 플로우를 자동 검증한다. 인프라는 Docker Compose로 표준화하고, main 머지 시 SSH를 통해 즉시 배포된다.

Terminology

용어설명
TBDTrunk-Based Development — main을 항상 배포 가능 상태로 유지
CIContinuous Integration — PR 시 자동 빌드·테스트
CDContinuous Deployment — main 머지 시 자동 배포
E2EEnd-to-End — UI에서 API까지 전체 플로우 검증
Branch ProtectionGitHub 브랜치 보호 규칙 — CI 통과 없이 머지 불가
ghcr.ioGitHub Container Registry — Docker 이미지 무료 저장소
Smoke Test핵심 기능 동작 여부만 빠르게 검증하는 최소 E2E

Define Problem

AS-IS

  • 배포 자동화 없음 — 수동 로컬 빌드 후 배포
  • CI 없음 — PR 시 코드 품질 서버 검증 불가
  • E2E 없음 — UI API 장애 사전 감지 불가
  • git hooks 존재하나 로컬에서만 동작 (GitHub Actions 미적용)
  • Docker 이미지 없음 — 일관된 배포 단위 부재

TO-BE

  • GitHub Actions CI: PR to main 시 전체 테스트 + E2E + 빌드 자동 실행
  • GitHub Actions CD: main 머지 시 Docker 이미지 빌드 → ghcr.io 푸시 → SSH 배포
  • Playwright E2E: 핵심 UI 플로우 6개, API 실패 자동 감지
  • Dockerfile: 백엔드(Spring Boot) + 프론트엔드(Next.js) 이미지화
  • Branch Protection: CI 필수 통과 후 머지 가능

Possible Solutions

CI/CD 플랫폼

방안설명왜 채택미채택 대안
GitHub Actions (채택)GitHub 내장, 별도 서버 불필요, 레포 연동 즉시코드가 GitHub에 있어 추가 설정 없음. Public 무제한 무료Jenkins(별도 서버), GitLab CI(플랫폼 이전), CircleCI(유료)

E2E 도구

방안설명왜 채택미채택 대안
Playwright (채택)Microsoft 제공, API 인터셉트 내장, CI 최적화Next.js 공식 권장. page.on('response')로 API 실패 자동 감지 용이Cypress(메모리 높음·CI 느림), Selenium(레거시·설정 복잡)

배포 전략

방안설명왜 채택미채택 대안
Docker Compose + SSH (채택)기존 docker-compose.yml 확장, SSH로 서버 접근 후 pull & up이미 docker-compose.yml 존재. 인프라 비용 없음K8s(오버스펙), Railway/Render(vendor lock-in), Heroku(유료)

E2E 실행 환경

방안설명왜 채택미채택 대안
CI docker service + bootRun (채택)MySQL을 GitHub Actions service 컨테이너로, 백엔드를 bootRun으로 기동Docker 이미지 없어도 실행 가능. CI 단계별 독립성 유지Docker Compose 전체 빌드(느림·이미지 선행 필요), 외부 스테이징 서버(비용)

Detail Design

CI 파이프라인 (.github/workflows/ci.yml)

PR to main 트리거
  ├── Job: test-backend
  │     GitHub Actions service: MySQL 8.0
  │     ./gradlew test --no-daemon
  │
  ├── Job: test-frontend
  │     npm ci && npm test
  │
  ├── Job: e2e (needs: test-backend, test-frontend)
  │     GitHub Actions service: MySQL 8.0
  │     ./gradlew bootRun & (백그라운드 기동, health 대기)
  │     npm run dev & (프론트 기동, health 대기)
  │     npx playwright test e2e/smoke/
  │
  └── Job: build (needs: e2e)
        ./gradlew build -x test
        docker build backend/
        docker build frontend/

CD 파이프라인 (.github/workflows/cd.yml)

main push 트리거
  ├── Job: build-and-push
  │     docker build backend/ → ghcr.io/biuea3866/stock-backend:latest
  │     docker build frontend/ → ghcr.io/biuea3866/stock-frontend:latest
  │
  └── Job: deploy (needs: build-and-push)
        SSH → 프로덕션 서버
        docker-compose -f docker-compose.prod.yml pull
        docker-compose -f docker-compose.prod.yml up -d

Docker 구성

backend/Dockerfile

eclipse-temurin:21-jre-alpine
COPY build/libs/*.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]

frontend/Dockerfile (Multi-stage, standalone output)

Stage 1 (builder): node:20-alpine → npm ci → next build
Stage 2 (runner): node:20-alpine → standalone 결과물 복사
EXPOSE 3000
CMD ["node", "server.js"]

docker-compose.prod.yml (3개 서비스)

mysql:  ghcr.io MySQL 8.0, healthcheck
backend: ghcr.io/biuea3866/stock-backend:latest
frontend: ghcr.io/biuea3866/stock-frontend:latest

E2E 테스트 구조

frontend/e2e/
  helpers/
    api-monitor.ts     ← 모든 API 호출을 수집, 4xx/5xx 시 에러 throw
  smoke/
    homepage.spec.ts   ← 메인 화면 로딩 + API 정상 여부
    stocks.spec.ts     ← 종목 목록 조회
    watchlist.spec.ts  ← 관심종목 CRUD
    order.spec.ts      ← 주문 화면 접근
    alert.spec.ts      ← 알림 설정 화면
    chat.spec.ts       ← AI 챗봇 접근

api-monitor.ts 동작 방식

page.on('response', response => {
  if (response.url().includes('/api/') && response.status() >= 400) {
    errors.push(`${response.status()} ${response.url()}`)
  }
})
// 테스트 종료 시 errors.length > 0 이면 fail

Component Diagram

flowchart LR
    subgraph GitHub
        PR[Pull Request]
        Main[main merge]
    end
    subgraph CI["CI (ci.yml)"]
        BTest[test-backend]
        FTest[test-frontend]
        E2E[e2e]
        Build[build]
    end
    subgraph CD["CD (cd.yml)"]
        Push[build-and-push]
        Deploy[deploy SSH]
    end
    subgraph Server["프로덕션 서버"]
        BE[Spring Boot :8080]
        FE[Next.js :3000]
        DB[MySQL :3306]
    end
    PR --> CI
    CI -->|pass| Main
    Main --> CD
    Push --> GHCR[ghcr.io]
    Deploy --> Server
    GHCR -.->|pull| Server

Sequence Diagram (PR QA 흐름)

sequenceDiagram
    participant Dev as 개발자
    participant GH as GitHub
    participant CI as GitHub Actions CI
    participant Prod as 프로덕션 서버

    Dev->>GH: PR 생성
    GH->>CI: ci.yml 트리거
    CI->>CI: test-backend (5분)
    CI->>CI: test-frontend (1분)
    CI->>CI: e2e smoke (3분)
    CI->>CI: build check (2분)
    CI-->>GH: CI pass
    Dev->>GH: PR 머지 (CI 통과 후 가능)
    GH->>CI: cd.yml 트리거
    CI->>CI: Docker 빌드 + ghcr.io 푸시
    CI->>Prod: SSH → docker-compose up -d
    Prod-->>GH: 배포 완료

ERD

변경 없음 (인프라 레이어만 추가)

Testing Plan

  • BE 단위 테스트 (Kotest + MockK): 도메인·앱 레이어 비즈니스 로직 검증 — test-backend
  • BE 통합 테스트 (Kotest + TestContainers): 인프라·프레젠테이션 레이어, 실제 MySQL 연동 검증 — test-backend
  • FE 단위 테스트 (vitest): 컴포넌트·훅 동작 검증 — test-frontend
  • E2E 스모크 테스트 (Playwright): 핵심 UI 플로우 6개(메인·종목·관심종목·주문·알림·챗봇) 검증 — e2e
  • API 장애 감지 (Playwright response listener): 모든 /api/* 호출에서 4xx/5xx 응답 자동 감지 → 테스트 실패 처리 — e2e

Observability

항목방법
CI 결과GitHub PR Status Check (required)
CD 결과GitHub Actions 성공/실패 알림 (이메일)
E2E 실패 상세Playwright HTML Report (CI artifact, 7일 보관)
배포 이력GitHub Actions 실행 로그

Release Scenario

  1. 개발자 PR 생성 → CI 자동 실행 (~11분)
  2. CI 통과 → 코드 리뷰(기존 code-reviewer hook) → main 머지
  3. main 머지 → CD 자동 트리거 → Docker 빌드 + 배포 (~5분)
  4. 배포 완료 → GitHub Actions 성공 알림

롤백: 이전 이미지 태그로 재배포

# 서버에서 실행
IMAGE_TAG=<이전 SHA> docker-compose -f docker-compose.prod.yml up -d

Project Information

  • 담당자: biuea
  • 스택: GitHub Actions, Playwright, Docker, docker-compose
  • 티켓: STK9-01 ~ STK9-06

Document History

날짜변경 내용작성자
2026-06-20최초 작성biuea