[CL] CI(지속적 통합) 파이프라인 구축 설계 - 100-hours-a-week/2-hertz-wiki GitHub Wiki

📚 목차


📄 CI 설계 문서

1. CI 설계 배경

1.1 도입 배경

현재 프로젝트는 프론트엔드(Next.js), 백엔드(Spring Boot), AI 서버(FastAPI) 로 구성되어 있으며,
각 파트는 개별적으로 개발·운영되고 팀원 또한 나뉘어 병렬적으로 작업 중입니다.

이러한 구조에서 다음과 같은 문제가 반복되고 있습니다:

  • 코드 병합 시 의존성 충돌
  • 테스트 누락 또는 빌드 실패
  • 개발 환경 차이로 인한 실행 오류

특히 CI 없이 수동으로 검증하던 방식은 다음과 같은 한계를 드러냈습니다:

  • 반복적인 빌드/테스트 수행으로 인한 생산성 저하
  • PR 병합 이후 오류 발견으로 인한 피드백 지연
  • 테스트 누락이나 실행 실패를 늦게 인지하여 품질 관리에 어려움 발생

1.2 CI 도입의 목적

이러한 문제를 해결하고자, 자동화된 CI 파이프라인을 도입하여 다음을 실현하고자 합니다:

  • 모든 개발자가 코드를 병합하기 전, 기초적인 빌드 및 테스트 검증 자동화
  • 프론트/백/AI 각 파트에 맞춘 분리된 CI 구성으로 기술 스택별 검증 수행
  • GitHub Actions 기반으로 운영 부담이 적은 경량 CI 환경 구축
  • 반복 작업을 줄이고, PR 단계에서 오류를 조기에 감지하여
    협업 효율성과 코드 품질을 동시에 확보

2. CI 도구 선택

선택 도구: GitHub Actions

선택 배경 및 논리

현재 프로젝트는 GitHub 저장소를 중심으로 관리되고 있으며,
개발 효율성과 운영 편의성을 고려할 때 GitHub Actions는 가장 적합한 선택입니다.
CI 도입의 궁극적인 목적은 “지속 가능한 품질 관리와 협업 자동화의 정착” 이며, GitHub Actions는 이 목적을 충족하는 다음과 같은 강점을 갖고 있습니다.

2.1 선택 이유 및 근거

항목 설명
즉시 활용 가능 GitHub 저장소에 .github/workflows/ 디렉토리만 추가하면 별도 설정 없이 바로 CI 파이프라인 구성 가능
외부 의존성 없음 Jenkins, GitLab CI처럼 별도 서버나 러너 설치가 필요 없어 초기 진입 비용이 낮음
표준 이벤트 지원 push, pull_request, release 등 GitHub 중심의 워크플로와 유기적으로 동작
보안성 확보 (Secrets 관리) GitHub Secrets를 통해 SSH 키, AWS 인증 정보 등을 안전하게 암호화하여 관리 가능
확장성 Docker 빌드, AWS S3/ECS 배포, Slack/Discord 알림 등 다양한 액션으로 확장 가능
유지보수 용이성 워크플로 설정이 코드(YAML)로 관리되므로 Git 기반 협업, 리뷰, 버전 추적이 쉬움

3. CI 도입 효과

CI 도입을 통해 기존 Big Bang 배포 방식에서 발생하던 문제를 자동화 기반으로 개선하고, 각 파트별 개발 안정성과 협업 효율성을 강화할 수 있습니다.
특히 PR 병합 이전 단계에서 테스트와 빌드 검증을 통해 문제를 조기 감지하고, 일관된 품질을 유지할 수 있는 기반이 마련됩니다.

3.1 백엔드(Spring Boot)

  • Gradle 기반 빌드 및 테스트 자동화
  • JUnit 기반 유닛/통합 테스트로 기능 이상 조기 발견
  • PR 병합 전 선제적 검증으로 QA 이전 품질 확보

3.2 프론트엔드(Next.js)

  • 유닛 테스트 자동 실행 (예: Jest)으로 빠른 피드백 제공
  • Pull Request 시점에서 UI/비즈니스 로직 문제 조기 발견
  • 빌드 성공 여부 확인을 통한 배포 안정성 확보
  • 스토리북 등과 연계한 UI 컴포넌트 테스트 도입 기반 마련

3.3 AI 서버(FastAPI)

  • pytest 기반 테스트 자동화로 예측 결과 검증
  • 주요 API와 모델 로직 변경 시 회귀 테스트 자동 수행
  • 테스트 누락 방지 및 코드 신뢰성 향상

3.4 공통 효과

항목 설명
개발 생산성 향상 반복적인 수작업 최소화 및 자동화 기반 개발 프로세스 정립
테스트 누락 방지 커밋/PR 시 자동 테스트 실행으로 품질 확보
협업 안정성 증가 표준화된 워크플로를 통한 팀 간 협업 효율 향상
이슈 조기 감지 빌드 실패, 의존성 충돌, 기능 오류 등을 초기에 감지
일관된 배포 품질 유지 배포 전 테스트 및 빌드를 통과한 코드만 병합 가능

4. CI 파이프라인 설계

4.1 CI 트리거 조건

조건 설명
main 브랜치 Push 또는 PR 전체 파이프라인 실행
PR 발생 시 정적 분석, 빌드, 테스트까지만 수행, CD는 미포함
GitHub Actions 러너 ubuntu-latest 환경 사용

4.2 브랜치별 목적 요약

브랜치 목적 특징 요약
develop 기능 통합 및 시나리오 검증 통합 테스트 포함, 전체 서비스 흐름 검증
main 최종 품질 인증 및 배포 전 빌드/아티팩트 확보 가장 강도 높은 검증, 최종 산출물 생성

4.3 파이프라인 구성

4.3.1 프론트엔드 (Next.js)

단계 develop 브랜치 main 브랜치 사용 기술
1. 코드 체크아웃 O O actions/checkout
2. 의존성 설치 O O npm, yarn
3. 유닛 테스트 O O Jest
4. 빌드 O O npm run build
5. 아티팩트 저장 X O (.next, 정적 파일 등) GitHub artifact
6. 배포 (CD) X (향후 추가 예정) 실행: pm2 start npm -- start

4.3.2 백엔드 (Spring Boot)

단계 develop 브랜치 main 브랜치 사용 기술
1. 코드 체크아웃 O O actions/checkout
2. 의존성 설치 O O Gradle, Java 17
3. 유닛 테스트 O O JUnit
4. 통합 테스트 O O SpringBootTest
5. 빌드 O O ./gradlew build
6. 아티팩트 저장 X O (.jar) GitHub artifact
7. 배포 (CD) X (향후 추가 예정) 실행: java -jar

4.3.3 AI 서버 (FastAPI)

단계 develop 브랜치 main 브랜치 사용 기술
1. 코드 체크아웃 O O actions/checkout
2. 의존성 설치 O O pip, requirements.txt
3. 유닛 테스트 O O pytest
4. 빌드 O O (optional: Python 패키징)
5. 아티팩트 저장 X O (모델 파일, 설정 등) GitHub artifact
6. 배포 (CD) X (향후 추가 예정) 실행: uvicorn, pm2

5. CI 파이프라인

5.1 CI 파이프라인 다이어그램

카카오테크 - Tuning 아키텍처-빅뱅배포 - CI 파이프라인

5.2 브랜치 전략

Git Branch 전략

6. 스크립트

백엔드

# .github/workflows/backend-ci.yml

name: Backend CI

on:
  pull_request:
    branches: [ main, develop ]  # PR이 main 또는 develop 브랜치로 열릴 때 트리거

jobs:
  backend-ci:
    runs-on: ubuntu-latest  # GitHub Actions의 실행 환경(OS)

    steps:
    # 1. 소스 코드 체크아웃
    - name: Checkout repository
      uses: actions/checkout@v3

    # 2. Java 17 설치 (Spring Boot 3.x 기준)
    - name: Set up JDK
      uses: actions/setup-java@v3
      with:
        java-version: '17'
        distribution: 'temurin'

    # 3. Gradle 캐시 적용 (빌드 시간 단축)
    - name: Cache Gradle
      uses: actions/cache@v3
      with:
        path: |
          ~/.gradle/caches
          ~/.gradle/wrapper
        key: gradle-${{ runner.os }}-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
        restore-keys: gradle-${{ runner.os }}-

    # 4. 단위 테스트 실행 (JUnit 기반)
    - name: Run Unit Tests
      run: ./gradlew test

    # 5. 애플리케이션 빌드 (JAR 생성)
    - name: Build Spring Boot Application
      run: ./gradlew build

    # 6. 통합 테스트 실행 (존재하는 경우)
    - name: Run Integration Tests
      run: ./gradlew integrationTest
      continue-on-error: false  # integrationTest task가 없으면 실패하지 않게 설정 가능

    # 7. 빌드 결과(.jar) 아티팩트로 저장
    - name: Upload JAR Artifact
      uses: actions/upload-artifact@v3
      with:
        name: springboot-jar
        path: build/libs/*.jar

    # 8. Discord로 CI 결과 전송 (성공/실패 상관없이 항상 실행)
    - name: Notify Discord
      if: always()
      run: |
        STATUS="${{ job.status }}"  # success / failure
        curl -H "Content-Type: application/json" \
             -X POST \
             -d "{\"username\": \"Backend CI\", \"content\": \"📦 Backend CI 결과: **${STATUS}**\n🔗 [${{ github.repository }}@${{ github.ref_name }}](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})\"}" \
             ${{ secrets.DISCORD_WEBHOOK_URL }}

프론트엔드

# .github/workflows/frontend-ci.yml

name: Frontend CI

on:
  pull_request:
    branches: [ main, develop ]  # PR 발생 시 CI 트리거

jobs:
  frontend-ci:
    runs-on: ubuntu-latest  # Node.js 환경 제공

    steps:
    # 1. 코드 체크아웃
    - name: Checkout repository
      uses: actions/checkout@v3

    # 2. Node.js 설치 (Next.js 13/14 권장 버전)
    - name: Setup Node.js
      uses: actions/setup-node@v3
      with:
        node-version: '18'

    # 3. 의존성 설치 (CI 캐시 최적화된 npm ci 사용)
    - name: Install dependencies
      run: npm ci

    # 4. 유닛 테스트 실행 (Jest 기반)
    - name: Run Unit Tests (Jest)
      run: npm run test

    # 5. 앱 빌드 (.next 디렉토리 생성)
    - name: Build Next.js App
      run: npm run build

    # 6. 빌드 결과 아티팩트 업로드 (.next 폴더)
    - name: Upload .next Build Artifact
      uses: actions/upload-artifact@v3
      with:
        name: nextjs-app
        path: .next

    # 7. Discord 알림 전송 (성공/실패 관계없이 항상 실행됨)
    - name: Notify Discord
      if: always()
      run: |
        STATUS="${{ job.status }}"  # success / failure
        curl -H "Content-Type: application/json" \
             -X POST \
             -d "{\"username\": \"Frontend CI\", \"content\": \"🖥️ Frontend CI 결과: **${STATUS}**\n🔗 [${{ github.repository }}@${{ github.ref_name }}](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})\"}" \
             ${{ secrets.DISCORD_WEBHOOK_URL }}

AI (fastAPI +AI model)

# .github/workflows/fastapi-ai-ci.yml

name: FastAPI + AI Model CI

on:
  pull_request:
    branches: [ main, develop ]  # main, develop 브랜치로 PR 발생 시 CI 실행

jobs:
  fastapi-ai-ci:
    name: FastAPI Server Test and Model Validation
    runs-on: ubuntu-latest  # GitHub Actions에서 사용하는 가상 환경 (우분투)

    steps:
    # 1. GitHub 리포지토리 코드 체크아웃
    - name: Checkout Repository
      uses: actions/checkout@v3

    # 2. Python 3.10 설치
    - name: Set up Python
      uses: actions/setup-python@v4
      with:
        python-version: '3.10'

    # 3. 의존성 설치
    - name: Install Dependencies
      run: |
        python -m pip install --upgrade pip               # pip 최신화
        pip install -r requirements.txt                   # 프로젝트 기본 의존성 설치
        pip install pytest                                # 테스트 프레임워크 설치

    # 4. 유닛 테스트 실행
    - name: Run Unit Tests (pytest)
      run: |
        pytest tests/                                     # tests 디렉토리 내 테스트 실행

    # 5. AI 모델 파일 존재 여부 확인
    - name: Validate AI Model File (optional)
      run: |
        if [ -f "./models/my_model.pt" ]; then
          echo "✅ 모델 파일 존재 확인 완료"
        else
          echo "❌ 모델 파일 누락" && exit 1              # 없으면 CI 실패 처리
        fi

    # 6. 모델 파일을 GitHub Actions 아티팩트로 저장 (다음 워크플로에서 활용 가능)
    - name: Save Artifacts (AI Model)
      if: success()
      uses: actions/upload-artifact@v4
      with:
        name: ai-model
        path: ./models/my_model.pt

    # 7. Discord Webhook으로 CI 결과 알림 전송 (성공/실패 여부 상관없이 항상 실행됨)
    - name: Notify Discord (always)
      if: always()
      run: |
        STATUS="${{ job.status }}"  # CI 성공/실패 상태
        curl -H "Content-Type: application/json" \
             -X POST \
             -d "{\"username\": \"CI Bot\", \"content\": \"📢 FastAPI + AI Model CI 실행 결과: **${STATUS}**\n🔗 [${{ github.repository }}@${{ github.ref_name }}](${{
             github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})\"}" \
             ${{ secrets.DISCORD_WEBHOOK_URL }}