v2의 CICD의 파이프라인 - 100-hours-a-week/KTB4-17th-wiki GitHub Wiki

기존 설계 문서: V2 CI Pipeline


Backend CI

1. 단위 테스트 도입

단위 테스트는 외부 시스템과의 연결을 최소화하고, 테스트 대상 클래스의 동작을 독립적으로 검증한다.

도구 역할
JUnit 5 테스트를 실행하고 결과를 검증한다.
Mockito 실제 데이터베이스나 외부 서버 대신 가짜 객체와 응답을 사용하여 테스트 대상 클래스만 검증한다.
AssertJ 예외 타입, 에러 코드 등의 검증 조건을 읽기 쉬운 문장 형태로 작성한다.
MockRestServiceServer 외부 AI 서버를 가짜로 구성하여 요청 형식과 응답·오류 처리 동작을 검증한다.

2. API 테스트 도입

API 테스트는 스프링 애플리케이션의 구성 요소가 실제로 연결된 상태에서 요청과 응답이 올바르게 처리되는지 검증한다.

도구 역할
JUnit 5, Mockito, AssertJ 테스트 실행, 객체 대체 및 결과 검증에 사용한다.
Spring Boot Test 스프링 컨텍스트를 실행하여 구성 요소 간 연동 상태를 검증한다.
MockMvc 실제 네트워크 포트를 열지 않고 Spring MVC 계층에 가상의 HTTP 요청을 전송한다.

3. 의존성 검사를 시크릿 보안 검사로 변경

기존 의존성 검사는 의존성 트리를 출력하는 데 그쳐 보안 취약점이 발견되어도 CI가 실패하지 않았다. 이를 보완하기 위해 Gitleaks를 도입하여 전체 커밋 기록에 비밀번호, API 키, 토큰 등의 민감 정보가 포함되어 있는지 검사한다.

  1. Gitleaks 공식 이미지를 확인한다.
  2. 컨테이너를 생성한다.
  3. 현재 저장소를 컨테이너에 연결한다.
  4. Gitleaks 명령어를 실행한다.
  5. 검사 결과에 따라 작업의 성공 여부를 결정한다.

Backend CI 워크플로

name: CI backend

on:
  push:
    branches:
      - chore/backend-CD
  pull_request:
    branches:
      - dev

permissions:
  contents: write
  pull-requests: write

jobs:
  backend_ci:
    runs-on: ubuntu-latest

    steps:
      - name: Check PR source
        uses: actions/checkout@v7
        with:
          fetch-depth: 0 # 커밋 기록 전체를 볼 수 있도록 전부 받옴 =0 (시크릿 스캔에서 사용)

      - name: Set up Java
        uses: actions/setup-java@v6
        with:
          distribution: temurin
          java-version: "25"

      - name: Set up Gradle
        uses: gradle/actions/setup-gradle@v6

      - name: Formatter Check
        run: ./gradlew spotlessCheck


        # 의존성 검사 대신 Gitleaks으로 변경
      - name: Secret Scan
        env:
          # PR이면 PR 브랜치의 최신 커밋이나 push면 방금 올라온 커밋만 검사
          COMMIT: ${{ github.event.pull_request.head.sha || github.sha }}
        run: |
          docker run --rm -v "$PWD:/repo" ghcr.io/gitleaks/gitleaks:v8.30.1 \
            git /repo --log-opts="-1 $COMMIT" --redact --no-banner --verbose


        #단위 테스트
      - name: Unit Test
        run: ./gradlew unitTest


        #Api테스트
      - name: API Test
        run: ./gradlew apiTest

        #테스트 레포트 작성
      - name: Upload Test Report
        if: failure()
        uses: actions/upload-artifact@v4
        with:
          name: test-report
          path: build/reports/tests
          retention-days: 7

      - name: Build
        run: ./gradlew clean bootJar -x test


Frontend

1. 단위 테스트 도입

src 디렉터리 아래의 모든 *.test.js 파일을 실행하도록 단위 테스트 단계를 추가한다. 테스트 파일이 늘어나더라도 CI 설정에 각 파일을 별도로 추가할 필요가 없다.

2. CodeQL 보안 검사 도입

CodeQL은 소스 코드를 분석 가능한 데이터 형태로 변환한 뒤, 알려진 취약점 패턴을 쿼리로 검색하는 정적 분석 도구다.

프런트엔드 영역은 전담 개발자가 별도로 배정되어 있지 않고 AI를 활용해 코드를 작성하므로, 일반적인 코드 리뷰만으로 보안 취약점을 충분히 식별하기 어려울 수 있다. 이에 따라 dev 브랜치를 대상으로 Pull Request가 생성되거나 변경될 때마다 CodeQL 정적 분석을 수행하여 잠재적인 보안 문제를 자동으로 탐지한다.

CodeQL 분석은 일반적인 Lint, 타입 검사 및 빌드보다 상대적으로 많은 시간이 소요되며 기존 CI 작업의 결과를 선행 조건으로 요구하지 않는다. 따라서 CodeQL을 기존 CI와 독립된 작업으로 구성하고 병렬 실행한다. 이를 통해 전체 검증 시간을 줄이고 일반 품질 검사와 보안 분석의 결과를 명확히 구분할 수 있다.

필요한 경우 브랜치 보호 규칙에서 일반 CI와 CodeQL 분석을 모두 필수 상태 검사로 지정하여, 두 검사가 모두 통과해야 dev 브랜치에 병합할 수 있도록 관리한다.

Frontend CI 워크플로

name: Frontend CI

# package.json은 프로젝트가 요구하는 패키지를 정의하고,
# package-lock.json은 실제로 설치되는 패키지 버전을 고정한다.
# 별도의 TypeScript 검사가 필요하지 않아 해당 단계는 제거했다.

on:
  pull_request:
    branches:
      - dev
  # PR 생성·변경 또는 수동 실행 시에만 CI를 수행한다.
  workflow_dispatch:

permissions:
  contents: read

jobs:
  feature_ci:
    name: Feature CI
    runs-on: ubuntu-latest

    steps:
      - name: Check source
        uses: actions/checkout@v7
        with:
          persist-credentials: false

      - name: Set up Node.js
        uses: actions/setup-node@v7
        with:
          node-version: "24"
          cache: npm
          cache-dependency-path: package-lock.json

      - name: Install dependencies
        run: npm ci

      - name: Biome Check
        run: npm run lint:biome

      - name: HTML Lint
        run: npm run lint:html

      - name: CSS Lint
        run: npm run lint:css

      # 현재 제외된 의존성 취약점 검사
      # - name: Dependency Vulnerability Check
      #   run: npx -y audit-ci@7 --high --allowlist GHSA-vfj7-8cjw-p6xm

      - name: Unit Test
        run: node --test "src/**/*.test.js"

      - name: Build
        run: npm run build

  # CodeQL은 선행 작업이 필요하지 않으므로 일반 CI와 병렬로 실행한다.
  codeql:
    name: CodeQL Analysis
    runs-on: ubuntu-latest

    permissions:
      contents: read
      actions: read
      security-events: write

    steps:
      - name: Checkout source
        uses: actions/checkout@v7
        with:
          persist-credentials: false

      - name: Initialize CodeQL
        uses: github/codeql-action/init@v4
        with:
          languages: javascript-typescript
          build-mode: none

      - name: Perform CodeQL Analysis
        uses: github/codeql-action/analyze@v4

AI

1. 테스트 분리

현재 단위 테스트와 API 테스트가 모두 tests 디렉터리에 혼재되어 있으므로, 파일명 패턴을 기준으로 테스트 대상을 구분한다.

  • 단위 테스트: tests/test_*_api.py 패턴에 해당하는 파일을 제외하고 실행한다.
  • API 테스트: tests/test_*_api.py 패턴에 해당하는 파일만 실행한다.

AI CI 워크플로

name: CI AI

on:
  pull_request:
    branches:
      - dev

jobs:
  feature_ci:
    name: AI feature validation
    runs-on: ubuntu-latest

    permissions:
      contents: read

    steps:
      - name: Check source
        uses: actions/checkout@v7

      - name: Install uv
        uses: astral-sh/setup-uv@v7
        with:
          python-version: "3.12"
          enable-cache: true

      - name: Install dependencies
        run: uv sync --all-groups --locked

      - name: Lint
        run: uv run ruff check .

      - name: Formatter Check
        run: uv run ruff format --check .

      - name: Dependency Vulnerability Check
        run: uvx pip-audit .

      # API 테스트 파일을 제외한 단위 테스트 실행
      - name: Unit Test
        run: uv run pytest --ignore-glob='tests/test_*_api.py'

      # 핵심 기능 API 테스트 실행
      - name: API Test
        run: uv run pytest tests/test_*_api.py

      - name: Build
        run: uv run python -m compileall -q .

참고 자료