[V1] Backend CI - 100-hours-a-week/KTB4-10th-Cloud GitHub Wiki
[ 1. 고려사항 ]
<3단계 내용 중 일부>
#### 3.1.1. 우리팀은 git flow 방식을 사용
- main(배포) - dev(중간 병합) - feature(기능)
- 브랜치에 따라 CI의 구성을 따로 고려
#### 3.1.2. 빌드 구성에서 캐시 정책
- 깃 공유 저장소는 프라이빗과 다르게 Github actions 사용시간에 제한이 없음
- 시간 제한이 없는 상태에서 빠른 배포를 위해 CI시간을 줄이는데 추가적인 작업을 진행해야할 필요를 못느낌
- v1은 기능이 간소
- 프론트의 경우 정적 파일
- ci에서 많은 시간이 걸리지 않을 것이라 가정
- 실제 CI 실행시간을 측정한 후 Build 시간이 개발 및 배포 속도에 영향을 주는 수준으로 증가하면 도입을 검토
⇒ 초기에는 파이프라인의 단순성을 우선시, 별도의 Cache 최적화 미적용
#### 3.2. feature → dev (PR)
- 기능의 변경사항을 dev branch에 통합하는 단계
- 기본적인 검증만 수행
- 애플리케이션 빌드 가능 여부
- 코드 규칙 검증
- 라이브러리, 패키지 검사
- 실패시 Merge 차단
#### 3.3. dev → main (PR)
- main은 실제 배포의 기준이 되는 branch
- 해당 단계에서는 마지막으로 도커 이미지 빌드 가능여부 확인
- 애플리케이션 빌드 가능 여부
- 코드 규칙 검증
- 라이브러리, 의존성 검사
- 도커 이미지 빌드 가능 여부 확인
- 배포 대상인 이미지만 레지스트리에서 관리하기 위해 최종 머지때 이미지 빌드 및 레지스트리에 푸시
- 실패시 Merge 차단
[ 2. 전체 CI흐름 ]
feature
│
│ PR
▼
┌─────────────────────────┐
│ CI │
│ Build / Compile │
│ Lint │
│ Dependency │
└───────────┬─────────────┘
│ Success
▼
dev
│
│ PR
▼
┌─────────────────────────┐
│ CI │
│ Build / Compile │
│ Lint │
│ Dependency │
│ Docker build │
└───────────┬─────────────┘
│ Success
▼
main
│
│ Merge
▼
┌─────────────────────────┐
│ Image Build │
│ Tag │
│ Commit SHA + 생성일 │
│ GHCR Push │
└───────────┬─────────────┘
│
▼
GHCR
│
├─ backend:a81c29f-20260905
│
└─ ai:921bf30-20260904
[ 3. 세부 도구 확정 ]
3.1. Build/compile검증 ⇒ Gradle
- 현재 BE는 Spring boot + Gradle 기반
- 별도의 빌드 도구 추가없이 Gradle을 사용
- 검증의 이유
- 변경된 코드의 정상적 컴파일 가능 여부 확인
- 프로젝트 정상 빌드 가능 여부 확인
- 의존성 문제로 인한 빌드 실패 여부 확인
- 실행 가능한 jar 생성 가능 여부 확인
- jar(자바 애플리케이션 실행 배포 위해 여러 파일을 하나로 묶은 파일 Java ARchive)
3.2. Lint ⇒ Spotbugs, Checkstyle
3.2.1. 린트란?
- 정적 분석 도구 / 코드를 실행하지 않고 소스코드나 바이트 코드를 분석
- 잠재적인 버그 가능성, 위험한 패턴, 스타일등의 문제 찾아내는 도구
- 한계 : 실행 경로 알 수 없음, 런타임에 어떤 값 들어오는지 모름 → 실제 동작 기준의 검증은 불가
3.2.2. 도구 종류
- 최소 CI 를 위한 목적이 분리된 2가지 도구
- V1에서는 검사 범위 중복과 설정·관리 복잡도 증가를 고려하여 최소한의 정적 분석 도구만 적용
- 코드의 복잡도, 패턴등을 분석하는 PMD나 별도의 분석환경, 퀄리티게이트가 필요한 SonarQube는 제외
| 도구 | 설치 | 위치목적 |
|---|---|---|
| SpotBugs | Gradle 플러그인 | 빌드/CI에서 버그 차단 |
| Checkstyle | Gradle 플러그인 | 스타일 규칙 강제 |
- SpotBug
- 빌드 도중 실행
- 빌드 단계에서 다시 한번 코드를 검사해서 문제가 있으면 빌드를 실패시킴
- 실제 사고로 이어지는 버그 패턴을 잡는데 집중된 도구
- nullpointerexeption, == equals, 리소스 누수, 동시성 관련 문제등
- 빌드 도중 실행
- checkstyle
- 버그를 찾는 도구라기보다는, 코드 스타일 및 컨벤션을 검사하는 도구
- 들여 쓰기, 중괄호, 네이밍 규칙, import 순서 등
- 버그를 찾는 도구라기보다는, 코드 스타일 및 컨벤션을 검사하는 도구
3.3. Dependency ⇒ Github Dependency Review
3.3.1. Dependency 검사란?
- 프로젝트에서 사용하는 외부 라이브러리 및 패키지의 의존성을 검사
- 현재 사용하는 의존성 또는 새롭게 추가되는 의존성에 알려진 보안 취약점이 존재하는지 확인
- 애플리케이션 코드 자체의 버그를 검사하는 Lint와는 검사 대상이 다름
- Lint → 직접 작성한 코드의 문제 검사
- Dependency 검사 → 프로젝트가 가져와 사용하는 외부 라이브러리의 문제 검사
- 알려진 취약점 정보를 기반으로 검사하므로 아직 공개되거나 등록되지 않은 취약점은 탐지할 수 없음
3.3.2. 비교
| 기준 | OWASP Dependency-Check | GitHub Dependency Review |
|---|---|---|
| 주요 검사 대상 | 프로젝트의 의존성 | PR에서 추가/변경되는 의존성 |
| 검사 시점 | Build / CI 실행 시 | Pull Request |
| Gradle 설정 | Plugin 설정 필요 | 필요 없음 |
| GitHub Actions 연동 | Gradle 명령을 통해 실행 | Action 직접 사용 |
| 취약점 기준 | 공개 취약점 DB 기반 | GitHub Advisory Database 기반 |
| CI 실패 설정 | 가능 | 가능 |
| 별도 서버 | 필요 없음 | 필요 없음 |
| 관리 부담 | 상대적으로 존재 | 낮음 |
| V1 PR Merge 차단 목적 | 가능 | 적합 |
3.3.3. 결정
현재 V1의 Dependency 검사의 주된 목적은 Dependency 전체에 대한 복잡한 보안 관리 체계를 구축하는 것이 아니라, 개발 과정에서 취약한 Dependency가 새롭게 추가되어 Merge되는 것을 사전에 차단하는 것
현재 CI 환경
- GitHub Repository 사용
- GitHub Actions 사용
feature → dev,dev → main의 PR 기반 Git Flow 사용- V1에서는 CI Pipeline의 단순성과 관리 부담 최소화를 우선
- 별도의 Dependency 검사 서버 및 복잡한 보안 관리 기능을 요구하지 않음
[ 4. CI 실패 기준 확립 ]
-
Gradle
⇒ build,compile 실패시 merge 차단
-
checkstyle
- ci결과에는 표시 (경고)
- merge 차단은 안함
-
spotbugs
- 버그 패턴 발견시 spotbugs가 bug rank 판단 → 1~20 존재
- 1~4(scariest)부분만 merge 차단
-
github dependency review
- severity 사용
- github advisory database에 있는 취약점 정보를 활용하는데
- 이 취약점 정보 → low, moderate, high, critical
- 너무 낮은 레벨부터 막을 경우 낮은 위험도에도 개발이 막힘
⇒ High 이상의 Severity를 가진 취약점이 포함된 Dependency가 새롭게 추가될 경우 CI 실패
-
docker image build
- 이미지 빌드 실패시 merge차단
- github-hosted라 workflow 종료되면 만들었던 이미지도 같이 폐기됨
#CI
#해당 파이프라인은 PR때만 작동
name: KGB Backend CI
on:
# feature -> dev / dev -> main PR
pull_request:
branches:
- dev
- main
permissions:
contents: read
# packages: write
jobs:
# PR 검증
# ==================================================
pr-ci:
name: PR CI
if: github.event_name == 'pull_request'
runs-on: ubuntu-latest
steps:
# Repository Checkout
- name: Checkout
uses: actions/checkout@v4
# JDK 설정
- name: Set up JDK
uses: actions/setup-java@v4
with:
distribution: temurin
java-version: '21'
# Gradle Wrapper 실행 권한
- name: Grant Gradle Permission
run: chmod +x gradlew
# Build / Compile 검증
# 실패 시 Merge 차단
- name: Gradle Build
run: ./gradlew build
# Checkstyle
# 위반 내용 표시
# build.gradle의 ignoreFailures = true 적용
# Merge 차단 X
- name: Checkstyle
run: ./gradlew checkstyleMain
# SpotBugs
# Rank 5~20 제외
# Rank 1~4 발견 시 실패
- name: SpotBugs
run: ./gradlew spotbugsMain
# Dependency Review
# 새롭게 추가/변경된 Dependency 대상
# High 이상 취약점 발견 시 실패
- name: Dependency Review
uses: actions/dependency-review-action@v4
with:
fail-on-severity: high
# Docker Image Build 검증
# dev -> main PR에서만 실행
# Image Push X
- name: Docker Build Test
if: github.base_ref == 'main'
run: docker build -t backend:ci .