Skip to content

Conversation

@move-hoon
Copy link
Member

@move-hoon move-hoon commented Oct 19, 2025

🔗 관련 이슈

📘 작업 유형

  • ✨ Feature (기능 추가)
  • 🐞 Bugfix (버그 수정)
  • 🔧 Refactor (코드 리팩토링)
  • ⚙️ Chore (환경 설정)
  • 📝 Docs (문서 작성 및 수정)
  • ✅ Test (기능 테스트)
  • 🎨 style (코드 스타일 수정)

📙 작업 내역

📋 Summary

  • Spring Boot 마이크로서비스 아키텍처(apis, batch)의 CI/CD 파이프라인을 Matrix strategy 기반으로 리팩토링하여 Dev 환경 병렬 배포, Prod 환경 순차 배포를 구현하고, Blue-Green 배포 안정성을 강화했습니다.
  • actuator 설정 및 앞으로의 관측가능성과 관련된 설정을 관리하기 위해 observability 모듈이 추가되었습니다.

🎯 Motivation

기존 문제점

  1. 비효율적인 배포 프로세스: 공통 의존성(domain, infra, global-utils, observability) 변경 시 모든 모듈을 순차적으로 배포하여 배포 시간이 길어짐
  2. 환경별 전략 부재: Dev와 Prod 환경이 동일한 배포 전략을 사용하여 각 환경 특성을 고려하지 못함
  3. 버전 일관성 부족: Prod 환경에서 모듈 간 버전 불일치 가능성
  4. 롤백 메커니즘 미흡: 배포 실패 시 자동 롤백 및 메타데이터 추적 부재

해결 방안

  • Matrix Strategy: 모듈별 병렬/순차 배포를 전략적으로 제어
  • 변경 감지: dorny/paths-filter로 변경된 모듈만 선택적 배포 (Dev)
  • Blue-Green 배포: 무중단 배포 및 자동 롤백 지원
  • 환경별 최적화: Dev는 속도, Prod는 안정성과 버전 일관성 우선

🚀 Key Changes

1. Dev 환경 - 병렬 배포 (.github/workflows/dev-ci-cd.yml)

변경 감지 기반 선택적 배포

jobs:
  detect-changes:
    steps:
      - uses: dorny/paths-filter@v3
        with:
          filters: |
            apis:
              - 'apis/**'
              - 'domain/**'  # 공통 의존성
              - 'infra/**'
              - 'global-utils/**'
              - 'observability/**'
            batch:
              - 'batch/**'
              - 'domain/**'  # 공통 의존성
              ...

Matrix 병렬 배포

  build-push-and-deploy:
    strategy:
      fail-fast: false  # 한 모듈 실패해도 다른 모듈 계속 배포
      matrix:
        include:
          - module: apis
            changed: ${{ needs.detect-changes.outputs.apis }}
          - module: batch
            changed: ${{ needs.detect-changes.outputs.batch }}

배포 시나리오:

  • apis/** 변경 → apis만 배포
  • batch/** 변경 → batch만 배포
  • domain/** 변경 → apis + batch 병렬 배포

2. Prod 환경 - 순차 배포 (.github/workflows/prod-ci-cd.yml)

Semantic Versioning + Release 트리거

on:
  release:
    types: [published]  # GitHub Release 기반 배포

env:
  IMAGE_PREFIX: ninecraft0523/ninecraft

Matrix 순차 배포로 버전 일관성 보장

  deploy-modules:
    strategy:
      max-parallel: 1      # 순차 배포 강제
      fail-fast: true      # 실패 시 즉시 중단
      matrix:
        module: [apis, batch]  # 배포 순서: apis → batch

주요 특징:

  • 어떤 파일이 변경되든 모든 모듈을 동일한 버전으로 배포 (버전 일관성)
  • max-parallel: 1apisbatch 순서 보장
  • Semantic versioning (v1.2.3) 사용
  • 배포 완료 후 Summary Discord 알림

3. Blue-Green 배포 스크립트

Dev 배포 스크립트 (deploy-dev.sh)

# 핵심 기능
- Blue/Green 자동 감지 및 전환
- 의존성 서비스 헬스체크 (MySQL, Redis)
- 모듈별 차별화된 헬스체크:
  * apis: localhost actuator + domain actuator (Nginx 통한 외부 접근)
  * batch: localhost actuator만 (내부 전용)
- Nginx 설정 검증 및 자동 롤백
- 병렬 배포 지원 (--no-deps로 공유 서비스 재시작 방지)

주요 로직 (deploy-dev.sh:389-534):

  1. Blue/Green 타겟 결정
  2. 의존성 서비스(MySQL, Redis) 헬스체크 및 자동 시작
  3. 새 컨테이너 배포 (docker compose up --no-deps)
  4. Direct 헬스체크 (localhost:actuator-port)
  5. [apis only] Nginx 설정 업데이트 + Domain 헬스체크
  6. 실패 시 자동 롤백 (rollback_deployment())
  7. 성공 시 구 컨테이너 정리

Prod 배포 스크립트 (deploy-prod.sh)

# Dev 대비 추가 기능
+ Pre-deployment checks (디스크 공간, Docker 데몬)
+ 배포 메타데이터 JSON 추적 (timestamp, version, status)
+ RDS 연동 (USE_RDS=true)
+ Post-deployment validation (컨테이너 상태, 메모리 사용량)
+ 강화된 타임아웃 (MAX_RETRIES=60, sleep 5초)
+ Nginx 설정 백업 (타임스탬프별 디렉토리)
+ 최종 시스템 상태 리포트

메타데이터 예시 (deploy-prod.sh:216-228):

{
  "deployment_timestamp": "20250125_143022",
  "module": "apis",
  "release_version": "v1.2.3",
  "image_tag": "ninecraft0523/ninecraft-apis:1.2.3",
  "container": "apis-green",
  "deployment_type": "BLUE_TO_GREEN",
  "status": "SUCCESS",
  "completion_timestamp": "20250125_143245"
}

4. 재사용 가능한 배포 액션 (.github/actions/deploy-module/action.yml)

모듈별 공통 배포 프로세스를 단일 액션으로 추상화:

  1. Secret 주입 (application-secret.properties, AuthKey.p8)
  2. Docker 빌드 및 푸시 (build cache 활용)
  3. SSH를 통한 서버 배포
  4. 환경별 Discord 알림 (성공/실패)

📊 Architecture Diagram

flowchart TB
    %% ======================
    %% GitHub Actions
    %% ======================
    subgraph GA["GitHub Actions Workflow"]
        direction LR

        %% -------- Dev --------
        subgraph DEV["Dev Environment (Parallel Deployment)"]
            direction TB

            D1["Detect Changes (paths-filter)"]
            D2["Matrix Strategy (fail-fast: false)"]

            subgraph DEV_MTX["Services"]
                direction LR
                D_APIS["apis (8080)"]
                D_BATCH["batch (8082)"]
            end

            D1 --> D2 --> DEV_MTX
            D_APIS -->|"Blue-Green Deploy"| DEV_DEPLOY1["Deploy"]
            D_BATCH -->|"Blue-Green Deploy"| DEV_DEPLOY2["Deploy"]
        end

        %% -------- Prod --------
        subgraph PROD["Prod Environment (Sequential Deployment)"]
            direction TB

            P1["Release Published (Semantic Version)"]
            P2["Matrix Strategy (max-parallel: 1, fail-fast: true)"]

            P_APIS["apis"]
            P_BATCH["batch"]

            P1 --> P2
            P2 --> P_APIS -->|"Blue-Green Deploy"| P_BATCH
        end
    end

    %% ======================
    %% Server
    %% ======================
    subgraph EC2["Server (EC2)"]
        direction TB

        subgraph NGINX["Nginx (Reverse Proxy: apis only)"]
            direction TB
            N1["Client Request"]
            N_APIS_B["apis-blue :8080"]
            N_APIS_G["apis-green :8080"]

            N1 --> N_APIS_B
            N1 --> N_APIS_G
        end

        B_BLUE["batch-blue :8082"]
        B_GREEN["batch-green :8082"]

        DB["MySQL / Redis"]
    end

    %% ======================
    %% Connections
    %% ======================
    DEV --> EC2
    PROD --> EC2

    %% ======================
    %% Styles (AREA FOCUS)
    %% ======================
    classDef devArea fill:#E3F2FD,stroke:#1E88E5,stroke-width:2px
    classDef prodArea fill:#FCE4EC,stroke:#C2185B,stroke-width:2px
    classDef infraArea fill:#ECEFF1,stroke:#455A64,stroke-width:2px
    classDef neutral fill:#FFFFFF,stroke:#B0BEC5

    %% Apply area styles
    class DEV devArea
    class PROD prodArea
    class EC2,NGINX infraArea

    %% Neutral nodes
    class D1,D2,DEV_MTX,D_APIS,D_BATCH,DEV_DEPLOY1,DEV_DEPLOY2 neutral
    class P1,P2,P_APIS,P_BATCH neutral
    class N1,N_APIS_B,N_APIS_G,B_BLUE,B_GREEN,DB neutral
Loading

🔍 Technical Details

Actuator 포트 매핑

병렬 배포 시 포트 충돌 방지를 위한 actuator 포트 분리를 진행했습니다.

헬스체크 전략

Dev & Prod (apis)

  1. Direct Check: http://localhost:${ACTUATOR_PORT}/actuator/health (컨테이너 내부)
  2. Domain Check: https://api.{domain}/actuator/health (Nginx를 통한 외부)

Dev & Prod (batch)

  1. Direct Check만: http://localhost:${ACTUATOR_PORT}/actuator/health
    • batch는 내부 전용이므로 domain check 불필요

의존성 서비스 헬스체크

질문 1 답변: 의존성 서비스(MySQL, Redis)는 모든 모듈 배포 시 체크합니다.

  • 이유: domain 모듈이 공유 엔티티를 포함하므로 apisbatch 모두 DB 연결 필요
  • 병렬 배포 시 충돌 방지: --no-deps 플래그로 공유 서비스 재시작 방지
  • 체크 로직: 이미 실행 중이면 재시작하지 않고 헬스체크만 수행 (deploy-dev.sh:210-250)

질문 2 답변: Actuator 포트 충돌 없습니다.

  • 외부 포트 분리: docker-compose에서 각 컨테이너마다 다른 외부 포트 매핑
  • 내부 포트는 동일: 모든 Spring 애플리케이션은 내부적으로 4444 사용
  • 격리: Docker 네트워크 격리로 컨테이너별 독립적 포트 공간

롤백 메커니즘

자동 롤백 트리거

  • Direct 헬스체크 실패
  • Nginx 설정 검증 실패 (nginx -t)
  • Domain 헬스체크 실패
  • Post-deployment validation 실패 (Prod only)

롤백 프로세스 (deploy-dev.sh:330-380)

  1. 구 컨테이너 실행 여부 확인
  2. [apis only] Nginx 설정을 구 컨테이너로 되돌림
  3. Nginx reload 후 domain 헬스체크
  4. 실패한 신규 컨테이너 제거
  5. 메타데이터 업데이트 (ROLLBACK_COMPLETED)

📈 Performance & Reliability Improvements

Dev 환경

  • 배포 시간 단축: 공통 의존성 변경 시 병렬 배포로 약 50% 시간 절감
  • 리소스 효율: 변경되지 않은 모듈은 배포하지 않음
  • 빠른 피드백: fail-fast: false로 한 모듈 실패해도 다른 모듈 배포 계속

Prod 환경

  • 버전 일관성: 모든 모듈이 동일한 release version으로 배포
  • 안정성 우선: max-parallel: 1로 순차 배포, fail-fast: true로 즉시 중단
  • 감사 추적: 배포 메타데이터 JSON 파일로 이력 관리
  • Semantic Versioning: Git tag 기반 버전 관리

공통

  • 무중단 배포: Blue-Green 전환으로 다운타임 제로
  • 자동 롤백: 헬스체크 실패 시 자동으로 이전 버전 복구
  • 강화된 모니터링: Discord 알림 + 배포 메타데이터 + 시스템 상태 리포트

📝 Migration Guide

기존 워크플로우 대비 변경사항

  1. Dev: develop 브랜치 푸시 시 변경 감지 후 선택적 병렬 배포
  2. Prod: GitHub Release 생성 시 전체 모듈 순차 배포
  3. 배포 스크립트: ~/deploy/deploy-dev.sh, ~/deploy/deploy-prod.sh 위치 확인 필요

서버 설정 확인사항

# 1. 배포 스크립트 배치
cd ~/deploy
# deploy-dev.sh, deploy-prod.sh 존재 확인

# 2. Nginx 설정 경로
ls /home/ubuntu/nginx/default.conf

# 3. Redis 설정 경로
ls /home/ubuntu/redis/redis.conf
grep '^requirepass' /home/ubuntu/redis/redis.conf  # 비밀번호 확인

# 4. Docker 네트워크
docker network ls | grep backend  # 'backend' 네트워크 존재 확인

📚 Additional Notes

Future Improvements

  • Admin 모듈 추가 시 matrix에 포함 (주석 처리된 부분 활성화)
  • Canary 배포 전략 도입 검토
  • 배포 메트릭 수집 (Grafana/Prometheus)
  • Slack 알림 추가

🎨 스크린샷 또는 시연 영상 (선택)

기능 미리보기 기능 미리보기
기능 설명 기능 설명

✅ PR 체크리스트

  • 커밋 메시지가 명확합니다
  • PR 제목이 컨벤션에 맞습니다
  • 관련 이슈 번호를 작성했습니다
  • 기능이 정상적으로 작동합니다
  • 불필요한 코드를 제거했습니다

💬 추가 설명 or 리뷰 포인트 (선택)

  • ..

Summary by CodeRabbit

  • 새로운 기능

    • 관찰성(Observability) 모듈 추가 및 관련 의존성 통합
    • 모듈별 재사용 가능한 배포 액션(Deploy Module) 도입 및 CI/CD 워크플로우 모듈화
  • 버그 수정/개선

    • 관리 엔드포인트 포트 1234로 변경
    • 컨테이너 실행 시 환경변수 기반 포트 주입 및 엔트리포인트 실행 방식 개선
    • 개발용 Dockerfile 삭제
  • 리팩토링

    • MDC 기반 로깅 필터 구조 재구성(공통 베이스 + 환경/보안별 구현)
    • 컴포넌트 스캔 및 프로필 그룹 설정 통합 업데이트
  • 기타

    • 여러 모듈에 관찰성 의존성 추가 및 모듈 등록 (설정 파일/빌드 스크립트 업데이트)

@move-hoon move-hoon requested a review from minwoo1999 October 19, 2025 15:59
@move-hoon move-hoon self-assigned this Oct 19, 2025
@coderabbitai
Copy link

coderabbitai bot commented Oct 19, 2025

Walkthrough

관찰성 모듈(observability) 추가, MDC 로깅 필터를 공통 추상으로 이동 및 인증용/단순 구현 추가, 여러 모듈에 observability 의존성 추가, Dockerfile 및 프로필/포트 변경, 개발용 Dockerfile 삭제, CI/CD 워크플로우를 모듈별 배포로 재구성했습니다.

Changes

응집 단위 / 파일(s) 변경 요약
Observability 모듈
observability/build.gradle.kts, observability/src/main/kotlin/org/yapp/observability/logging/filter/BaseMdcLoggingFilter.kt, observability/src/main/kotlin/org/yapp/observability/logging/filter/SimpleMdcLoggingFilter.kt, observability/src/main/kotlin/org/yapp/observability/metrics/config/ActuatorProperties.kt, observability/src/main/resources/application-observability.yml
신규 모듈 추가: Actuator/Prometheus/로깅 의존성 등록, 추상 MDC 필터(BaseMdcLoggingFilter) 및 기본 구현(SimpleMdcLoggingFilter) 추가, 관리 포트 8081→1234 변경.
의존성·설정 등록
buildSrc/src/main/kotlin/Dependencies.kt, settings.gradle.kts
OBSERVABILITY 상수 추가 및 :observability 프로젝트 포함.
모듈 의존성 변경
admin/build.gradle.kts, apis/build.gradle.kts, batch/build.gradle.kts, gateway/build.gradle.kts
admin, apis, batchimplementation(project(Dependencies.Projects.OBSERVABILITY)) 추가; gateway에서 actuator/prometheus 제거 후 observability 의존성으로 대체.
애플리케이션 부트스트랩 변경
admin/src/main/kotlin/.../AdminApplication.kt, apis/src/main/kotlin/.../ApisApplication.kt, batch/src/main/kotlin/.../BatchApplication.kt
각 앱에서 JpaRepositoriesAutoConfiguration 제외 추가 및 @ComponentScan(basePackages = ["org.yapp"])로 통합(기존 scanBasePackages 제거).
프로필·설정 변경
admin/src/main/resources/application.yml, apis/src/main/resources/application.yml, batch/src/main/resources/application.yml
dev/prod/test 그룹에 crosscutting·observability 추가(apis는 webobservability 교체). batch: 포트 8080→8082, multipart max-request-size: 30MB 추가.
Gateway MDC 필터 리팩토링
gateway/src/main/kotlin/org/yapp/gateway/filter/MdcLoggingFilter.kt (삭제), gateway/src/main/kotlin/org/yapp/gateway/filter/SecurityMdcLoggingFilter.kt (추가), gateway/src/main/kotlin/org/yapp/gateway/security/SecurityConfig.kt
기존 MdcLoggingFilter 삭제. 공통 로직은 observability의 BaseMdcLoggingFilter로 이동, 인증용 SecurityMdcLoggingFilter 추가 및 SecurityConfig에서 해당 필터로 교체.
Dockerfile 변경 및 개발 Dockerfile 삭제
Dockerfile, Dockerfile-dev (삭제)
MODULE ARG를 필수화(기본값 제거), Entrypoint를 셸 래퍼로 변경해 SERVER_PORT 환경변수로 포트 주입. Dockerfile-dev 파일 삭제.
CI/CD 및 액션 재구성
.github/workflows/ci-pr.yml, .github/workflows/dev-ci-cd.yml, .github/workflows/prod-ci-cd.yml, .github/actions/deploy-module/action.yml
PR CI의 Gradle 실행을 멀티라인 셸로 변경(주석 추가). Dev/Prod 워크플로우를 모듈 변경 감지 기반 매트릭스·모듈별 배포로 재구성하고, 새 composite action deploy-module 추가(이미지 빌드·푸시·배포·알림 책임 위임), 기존 인라인 빌드/푸시/노티 단계 제거 및 입력 기반 분리.
기타 설정/문서 소소한 변경
.github/workflows/ci-pr.yml
Gradle 실행을 멀티라인 셸 블록으로 교체하고 한글 주석 추가(명령은 동일).

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant Gateway as Gateway\n(Security Filter Chain)
    participant SecFilter as SecurityMdcLoggingFilter
    participant BaseFilter as BaseMdcLoggingFilter
    participant Service as Backend Service

    Client->>Gateway: HTTP Request (헤더 포함)
    Gateway->>SecFilter: 필터 체인 진입
    SecFilter->>BaseFilter: MDC 공통 필드 설정 (traceId, clientIp, requestInfo)
    SecFilter->>SecFilter: SecurityContext에서 userId 추출(Jwt.subject 또는 principal)
    SecFilter->>Service: 요청 전달 (MDC 세팅 유지)
    Service-->>SecFilter: 응답/예외
    SecFilter->>BaseFilter: MDC 정리 (finally)
    SecFilter-->>Client: HTTP Response
Loading
sequenceDiagram
    participant GH as GitHub Workflow
    participant Detect as detect-changes
    participant Matrix as build-push-and-deploy (matrix)
    participant DeployAction as ./.github/actions/deploy-module
    participant Remote as Remote Server

    GH->>Detect: 변경 모듈 감지 (apis,batch,any)
    Detect-->>Matrix: 모듈별 플래그 전달
    Matrix->>DeployAction: 모듈별 빌드·푸시·배포 실행 (입력: module,image-prefix,ssh 등)
    DeployAction->>Remote: SSH로 배포 스크립트 실행 및 알림 전송
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested labels

⚙️ chore

Suggested reviewers

  • minwoo1999

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings, 1 inconclusive)
Check name Status Explanation Resolution
Linked Issues Check ⚠️ Warning 연결된 이슈 #121의 주요 목표는 (1) 알림 작업을 위한 Batch Module Dockerlizing과 (2) CI/CD 워크플로우 수정입니다. 변경 사항을 검토하면 CI/CD 워크플로우는 충분히 수정되었으나(dev-ci-cd.yml, prod-ci-cd.yml에서 멀티 모듈 배포 기능 추가), Batch Module Dockerlizing 관련 요구 사항은 완전히 충족되지 않은 것으로 보입니다. Dockerfile-dev는 삭제되었고 Dockerfile은 수정되었지만, 배치 모듈을 위한 구체적이고 전용의 Dockerfile 구성이 명확하지 않습니다. 이슈 #121의 첫 번째 목표인 "알림 작업을 위한 Batch Module Dockerlizing"이 완전히 구현되었는지 확인이 필요합니다. Batch 모듈을 위한 적절한 Docker 구성(예: Dockerfile, 멀티 스테이지 빌드)이 포함되어 있는지 검토하고, 필요시 추가 커밋을 통해 요구사항을 충족하시기 바랍니다.
Out of Scope Changes Check ⚠️ Warning PR은 연결된 이슈 #121의 범위를 초과하는 광범위한 변경 사항을 포함하고 있습니다. 새로운 observability 모듈 생성(BaseMdcLoggingFilter, SimpleMdcLoggingFilter, SecurityMdcLoggingFilter, ActuatorProperties 등), 여러 모듈(admin, apis, batch)에서 observability 의존성 추가, MDC 로깅 리팩토링, Spring Boot 설정 변경(JpaRepositoriesAutoConfiguration 제외, ComponentScan 추가), application.yml 프로파일 업데이트 등이 포함되어 있습니다. 이들은 CI/CD 워크플로우 수정 범위를 크게 넘어섭니다. observability 모듈 생성 및 통합, Spring Boot 설정 리팩토링 등의 변경 사항이 이슈 #121과 직접적으로 연관되어 있는지 확인하고 문서화하기를 권장합니다. 이러한 광범위한 인프라 변경이 정말 필요하다면, 별도의 이슈로 추적하거나 현재 이슈의 범위를 명확히 업데이트하는 것이 좋습니다.
Title Check ❓ Inconclusive PR 제목 "feat: 멀티 애플리케이션 배포 기능 구현"은 다중 애플리케이션 배포 기능에 대한 내용이지만, 실제 변경 사항의 핵심은 새로운 observability 모듈 생성, MDC 로깅 필터 리팩토링, 그리고 CI/CD 워크플로우 업데이트입니다. 제목은 너무 광범위하며 변경 사항의 구체적인 기술적 내용(observability 모듈 생성, 로깅 인프라 개선)을 명확히 전달하지 못하고 있습니다. 제목만으로는 무엇이 주요 변경 사항인지 팀 멤버들이 정확히 파악하기 어렵습니다. PR 제목을 더 구체적이고 명확하게 수정하기를 권장합니다. 예를 들어 "feat: Observability 모듈 생성 및 멀티 모듈 배포 기능 추가" 또는 "feat: 관찰성 모듈 생성 및 CI/CD 워크플로우 개선"과 같이 실제 변경의 핵심을 반영하는 제목으로 변경하면 더욱 명확할 것입니다.
✅ Passed checks (2 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch BOOK-350-feature/#121

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
Dockerfile (1)

1-45: 선택 사항: Dockerfile 보안 및 운영 개선 사항

정적 분석에서 제안한 다음 항목들을 고려하세요:

  1. HEALTHCHECK 추가: 컨테이너 헬스 상태를 주기적으로 확인하여 자동 재시작 메커니즘을 활성화할 수 있습니다.

    HEALTHCHECK --interval=30s --timeout=3s --start-period=40s --retries=3 \
      CMD curl -f http://localhost:${SERVER_PORT:-8080}/actuator/health || exit 1
  2. 비루트 사용자: 보안 취약성을 줄이기 위해 전용 비루트 사용자를 추가하세요.

    RUN useradd -m -u 1000 appuser
    USER appuser
observability/src/main/kotlin/org/yapp/observability/metrics/config/ActuatorProperties.kt (1)

5-8: 설정 속성에 기본값 제공 권장

basePath 속성이 non-nullable이고 기본값이 없어서, 설정 파일에 management.endpoints.web.base-path 값이 누락되면 애플리케이션 시작 시 바인딩 오류가 발생할 수 있습니다.

다음 중 하나의 방법을 적용하는 것을 권장합니다:

옵션 1: 기본값 제공

 @ConfigurationProperties(prefix = "management.endpoints.web")
 data class ActuatorProperties(
-    val basePath: String
+    val basePath: String = "/actuator"
 )

옵션 2: Nullable 타입으로 변경

 @ConfigurationProperties(prefix = "management.endpoints.web")
 data class ActuatorProperties(
-    val basePath: String
+    val basePath: String?
 )
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6007d66 and b7c598e.

📒 Files selected for processing (23)
  • .github/workflows/ci-pr.yml (1 hunks)
  • Dockerfile (3 hunks)
  • Dockerfile-dev (0 hunks)
  • admin/build.gradle.kts (1 hunks)
  • admin/src/main/kotlin/org/yapp/admin/AdminApplication.kt (1 hunks)
  • admin/src/main/resources/application.yml (1 hunks)
  • apis/build.gradle.kts (1 hunks)
  • apis/src/main/kotlin/org/yapp/apis/ApisApplication.kt (1 hunks)
  • apis/src/main/resources/application.yml (1 hunks)
  • batch/build.gradle.kts (1 hunks)
  • batch/src/main/kotlin/org/yapp/batch/BatchApplication.kt (1 hunks)
  • batch/src/main/resources/application.yml (1 hunks)
  • buildSrc/src/main/kotlin/Dependencies.kt (1 hunks)
  • gateway/build.gradle.kts (1 hunks)
  • gateway/src/main/kotlin/org/yapp/gateway/filter/MdcLoggingFilter.kt (0 hunks)
  • gateway/src/main/kotlin/org/yapp/gateway/filter/SecurityMdcLoggingFilter.kt (1 hunks)
  • gateway/src/main/kotlin/org/yapp/gateway/security/SecurityConfig.kt (3 hunks)
  • observability/build.gradle.kts (1 hunks)
  • observability/src/main/kotlin/org/yapp/observability/logging/filter/BaseMdcLoggingFilter.kt (1 hunks)
  • observability/src/main/kotlin/org/yapp/observability/logging/filter/SimpleMdcLoggingFilter.kt (1 hunks)
  • observability/src/main/kotlin/org/yapp/observability/metrics/config/ActuatorProperties.kt (1 hunks)
  • observability/src/main/resources/application-observability.yml (1 hunks)
  • settings.gradle.kts (1 hunks)
💤 Files with no reviewable changes (2)
  • Dockerfile-dev
  • gateway/src/main/kotlin/org/yapp/gateway/filter/MdcLoggingFilter.kt
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-08-16T06:56:28.923Z
Learnt from: hoonyworld
PR: YAPP-Github/Reed-Server#96
File: gateway/src/main/kotlin/org/yapp/gateway/security/SecurityConfig.kt:0-0
Timestamp: 2025-08-16T06:56:28.923Z
Learning: In Spring Security filter chain, MdcLoggingFilter should be placed after BearerTokenAuthenticationFilter because it depends on SecurityContextHolder to extract userId from the authenticated principal. Placing it before authentication filters would result in null or anonymous authentication context.

Applied to files:

  • gateway/src/main/kotlin/org/yapp/gateway/security/SecurityConfig.kt
  • gateway/src/main/kotlin/org/yapp/gateway/filter/SecurityMdcLoggingFilter.kt
🧬 Code graph analysis (1)
observability/src/main/kotlin/org/yapp/observability/logging/filter/BaseMdcLoggingFilter.kt (2)
gateway/src/main/kotlin/org/yapp/gateway/filter/SecurityMdcLoggingFilter.kt (2)
  • resolveUserId (14-29)
  • resolveUserId (21-28)
observability/src/main/kotlin/org/yapp/observability/logging/filter/SimpleMdcLoggingFilter.kt (2)
  • resolveUserId (11-18)
  • resolveUserId (17-17)
🪛 Checkov (3.2.334)
Dockerfile

[low] 1-45: Ensure that HEALTHCHECK instructions have been added to container images

(CKV_DOCKER_2)


[low] 1-45: Ensure that a user for the container has been created

(CKV_DOCKER_3)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build-validation
🔇 Additional comments (29)
.github/workflows/ci-pr.yml (1)

61-65: 좋은 개선: 빌드 명령 문서화로 가독성 향상

Gradle 명령을 다중 라인 형식으로 변경하고 각 플래그의 목적을 명확히 설명하는 한국어 주석을 추가한 것은 팀의 유지보수성 측면에서 긍정적입니다. 특히 fullCheck 작업이 여러 모듈(apis, admin, batch, gateway 등)을 포함한다는 점이 명확해졌습니다.

다만, 다음 항목을 확인해 주시기 바랍니다:

  1. fullCheck Gradle 작업이 실제로 PR에서 추가되는 새로운 observability 모듈을 포함하도록 설정되었는지 확인
  2. 멀티 애플리케이션 배포 지원을 위해 다른 워크플로우 파일(예: CD, 배포 워크플로우)도 유사한 업데이트가 필요한지 검토
Dockerfile (1)

3-4: 멀티 모듈 빌드를 위한 MODULE 파라미터화가 잘 구현되었습니다.

MODULE ARG를 필수 요구 사항으로 변경하고 명확한 주석을 추가하여 각 모듈별 빌드 시 명시적으로 모듈을 지정하도록 강제하는 것이 좋은 설계입니다. 빌드 단계와 실행 단계에서 일관되게 사용되고 있습니다.

Also applies to: 28-29

observability/src/main/kotlin/org/yapp/observability/metrics/config/ActuatorProperties.kt (1)

1-1: 패키지 이동이 올바르게 완료됨

검증 결과, ActuatorProperties 클래스의 패키지 이동이 완전하게 처리되었습니다. 새로운 패키지 경로(org.yapp.observability.metrics.config)로의 import가 SecurityConfig.kt에서 올바르게 업데이트되었으며, 기존 패키지 경로에 대한 참조는 남아있지 않습니다. 클래스가 SecurityConfig에서 정상적으로 사용 중입니다.

batch/src/main/resources/application.yml (4)

15-15: observability 프로필이 모든 환경에서 일관되게 추가됨

Observability 모듈 통합이 dev, prod, test 프로필에 균일하게 적용되었습니다. 다음을 확인해주세요:

  1. application-observability.yml 또는 해당 프로필 파일이 존재하는지 확인
  2. 다른 모듈(admin, apis, gateway)의 application.yml에서도 동일한 observability 프로필이 추가되었는지 확인하여 일관성 보장

Also applies to: 21-21, 27-27


22-27: test 프로필의 순서가 dev, prod와 다름 - 의도 확인 필요

프로필 로딩 순서가 다릅니다:

  • dev, prod: persistence → crosscutting → redis → external → observability
  • test: persistence → crosscutting → redis → external → observability

현재는 순서가 동일하게 보이지만, 추가 검토 시 다음을 확인하세요:

  1. 프로필 순서가 설정 오버라이드에 영향을 주지 않는지 확인
  2. 각 환경별로 프로필 로딩 순서가 의도한 대로인지 검증

28-31: 멀티파트 최대 요청 크기 30MB 설정 - 용도 확인

max-request-size: 30MB이 신규 추가되었습니다. Batch 모듈에서 이 설정이 필요한 이유를 확인해주세요:

  1. Batch 모듈이 HTTP 요청으로 파일 업로드를 받는 엔드포인트를 가지고 있는지 확인
  2. 30MB 크기가 실제 사용 케이스에 적절한지 검증
  3. 메모리 및 성능 영향도 검토 (특히 동시 요청 시)

일반적인 배치 작업은 주기적 스케줄링이나 메시지 큐로 트리거되므로, HTTP 멀티파트 요청이 필요한지 재검토하세요.


10-27: <!-- [scratchpad_start]
[task review_verification_1/3]
Profile consistency across modules - COMPLETED

[observations]

  • batch/src/main/resources/application.yml: dev/prod/test all have [persistence, crosscutting, redis, external, observability]
  • apis/src/main/resources/application.yml: dev/prod/test all have [persistence, crosscutting, jwt, redis, external(dev/prod only), observability]
  • admin/src/main/resources/application.yml: dev/prod/test all have [persistence, crosscutting, jwt, redis, external(dev/prod only), observability]
  • gateway module: NOT FOUND in search results
  • crosscutting profile: present consistently across all 3 modules
  • observability profile: present consistently across all 3 modules (batch, apis, admin)

[analysis]

  • Profile structure IS synchronized across modules
  • crosscutting migration appears successful (replaced jwt in some contexts)
  • observability added consistently to all modules
  • Discrepancy: batch uses [external + observability] while apis/admin use [external + jwt + observability]
    → This is intentional: batch doesn't need jwt (no API authentication), apis/admin need it

[pending]

  • Dockerfile parameterization verification (NOT in script scope)
  • CI/CD workflow changes verification (NOT in script scope)
  • Gateway module location/status (not found)

[actions]

  • Generate final response noting completed verifications
  • Flag incomplete verifications requiring manual review
  • Use tag due to incomplete verification scope

[done]
Profile consistency check complete. Remaining items require manual review or different verification approach.
[scratchpad_end]
-->

프로필 일관성 검증 완료 - 추가 확인 필요

프로필 그룹의 모듈 간 일관성 검증이 완료되었습니다.

✓ 확인된 사항:

  • Crosscutting 프로필: batch, apis, admin 모듈 모두에서 dev/prod/test 환경에 일관되게 적용됨
  • Observability 프로필: batch, apis, admin 모듈 모두에서 dev/prod/test 환경에 일관되게 적용됨
  • 구조 일관성: 모든 모듈이 동기화되어 있으며, 각 모듈의 특성에 맞게 프로필 구성됨 (batch는 jwt 미포함, apis/admin은 jwt 포함)

⚠ 확인 불가능한 항목:

  • Gateway 모듈: 검색 결과에서 찾을 수 없음 (해당 모듈 확인 필요)
  • Dockerfile 파라미터화: 프로필 구조와의 연결 관계를 직접 확인하려면 Dockerfile 및 배포 스크립트의 수동 검토 필요
  • CI/CD 워크플로우: 새로운 observability 프로필로 인한 빌드 단계 변경사항의 수동 검토 필요
settings.gradle.kts (1)

10-11: LGTM! 새로운 observability 모듈 추가

observability 모듈이 프로젝트에 정상적으로 추가되었습니다. trailing comma 사용도 좋은 패턴입니다.

buildSrc/src/main/kotlin/Dependencies.kt (1)

33-33: LGTM! OBSERVABILITY 상수 추가

새로운 observability 모듈 참조를 위한 상수가 올바르게 추가되었습니다. 기존 패턴을 따르고 있습니다.

admin/src/main/resources/application.yml (1)

8-30: LGTM! 프로필 그룹 업데이트

모든 환경(dev, prod, test)에 crosscuttingobservability 프로필이 일관되게 추가되었습니다. 기존 프로필들은 유지되면서 새로운 관심사를 추가하는 방식으로 안전한 변경입니다.

apis/src/main/resources/application.yml (1)

10-30: 웹 프로필 존재 여부 확인 불가

검색 결과, 코드베이스에 application-web.yml 파일이나 web 프로필 관련 설정이 발견되지 않았습니다. 다음을 확인해야 합니다:

  • 원래 web 프로필이 실제로 존재했는지 여부
  • observability 모듈의 management.endpoints.web 설정(actuator 엔드포인트)이 기존 web 프로필의 모든 설정을 포함하는지 여부
  • 변경 전 상태와의 비교 검증

현재 상태: apis, admin, batch 모두 모든 환경에서 observability 프로필을 활성화하고 있습니다.

admin/src/main/kotlin/org/yapp/admin/AdminApplication.kt (1)

8-10: JpaRepositoriesAutoConfiguration 제외는 의도된 아키텍처 설계로 정당화됩니다

검증 결과, Admin 모듈의 JPA 제외는 명시적이고 필요한 설정입니다:

  • Admin 모듈 자체: JPA 리포지토리 직접 사용 없음
  • 대체 구성: @EnableInfraBaseConfig([InfraBaseConfigGroup.JPA])를 통해 INFRA 모듈의 JpaConfig를 활성화
  • INFRA 모듈: 모든 JPA 엔티티(BookEntity, ReadingRecordEntity)와 리포지토리(JpaBookRepository, JpaReadingRecordRepository)를 관리
  • 아키텍처: Spring Boot 기본 JPA 자동 구성을 제외하고 INFRA 모듈의 커스텀 JPA 설정을 명시적으로 로드하는 다중 모듈 구조

이는 데이터베이스 관심사를 INFRA 모듈에 집중시키는 단일 책임 원칙을 따르는 설계입니다.

batch/src/main/kotlin/org/yapp/batch/BatchApplication.kt (1)

8-10: JpaRepositoriesAutoConfiguration 제외는 적절함 - 우려사항 없음

infra 모듈이 JpaConfig.kt에서 명시적으로 @EnableJpaRepositories(basePackages = ["org.yapp.infra"])를 구성하고 있습니다. batch 모듈의 @ComponentScan이 infra 모듈을 포함하므로, JpaRepositoriesAutoConfiguration을 제외하더라도 infra의 명시적 설정이 JPA 리포지토리 스캔을 처리합니다. batch 모듈은 직접 JPA 리포지토리를 정의하지 않으므로, 이 구성은 올바릅니다.

observability/src/main/kotlin/org/yapp/observability/logging/filter/BaseMdcLoggingFilter.kt (4)

21-31: 상수 정의가 명확하고 적절합니다.

MDC 키와 헤더 이름을 상수로 정의하여 재사용성과 유지보수성을 높였습니다. DEFAULT_GUEST_USER 상수를 통한 폴백 처리도 적절합니다.


33-46: MDC 정리를 finally 블록에서 수행하여 안전합니다.

필터 체인 실행 후 MDC.clear()를 finally 블록에서 호출하여 예외 발생 시에도 MDC 컨텍스트가 정리되도록 보장합니다. OncePerRequestFilter 사용도 요청당 한 번만 실행되도록 보장하여 적절합니다.


74-86: 클라이언트 IP 추출 로직이 올바릅니다.

프록시 환경을 고려한 헤더 우선순위 처리가 적절합니다:

  1. X-Forwarded-For (첫 번째 IP 추출)
  2. X-Real-IP
  3. request.remoteAddr

52-56: TraceId 형식 변경에 대한 외부 시스템 영향 없음 - 검증 완료

코드베이스 전체 검증 결과, traceId는 로컬 로깅 컨텍스트(MDC)에서만 사용되며 외부 시스템으로 전파되지 않습니다.

검증 내용:

  • traceId는 X-Request-ID 헤더에서만 읽음
  • MDC에만 저장되고 log4j2 로깅 패턴에 사용됨
  • HTTP 인터셉터나 아웃바운드 헤더 설정 없음
  • 분산 추적 라이브러리(Sleuth, Jaeger 등) 미사용
  • RestClient 빈에서 traceId 전파 로직 없음

따라서 UUID에서 하이픈을 제거하는 설계는 표준 형식 호환성이나 외부 시스템 통합에 영향을 주지 않습니다. 이는 로그 포맷을 간결하게 유지하기 위한 로컬 설계 결정입니다.

Likely an incorrect or invalid review comment.

batch/build.gradle.kts (1)

7-7: Observability 모듈 의존성 추가가 적절합니다.

Batch 모듈에서 MDC 로깅 및 메트릭 기능을 사용할 수 있도록 observability 의존성이 추가되었습니다.

apis/build.gradle.kts (1)

8-8: Observability 모듈 의존성 추가가 적절합니다.

APIs 모듈에서 MDC 로깅, 메트릭, 모니터링 기능을 사용할 수 있도록 observability 의존성이 추가되었습니다.

admin/build.gradle.kts (1)

7-7: Observability 모듈 의존성 추가가 적절합니다.

Admin 모듈에서 MDC 로깅 및 메트릭 기능을 사용할 수 있도록 observability 의존성이 추가되었습니다.

observability/build.gradle.kts (2)

3-16: Observability 모듈의 의존성 구성이 적절합니다.

메트릭, 모니터링, 로깅을 위한 필수 의존성들이 올바르게 포함되어 있습니다:

  • Web: 필터 구현을 위한 서블릿 API 제공
  • Actuator: 헬스체크 및 메트릭 엔드포인트
  • Prometheus: 메트릭 수집
  • Kotlin Logging: 구조화된 로깅

18-21: 라이브러리 모듈 설정이 올바릅니다.

Jar는 활성화하고 BootJar는 비활성화하여 observability를 다른 모듈에서 의존할 수 있는 라이브러리로 올바르게 구성했습니다.

gateway/build.gradle.kts (2)

5-6: Observability 의존성 통합이 적절합니다.

Actuator와 Prometheus 의존성을 observability 모듈로 이동시키고, 해당 모듈을 의존성으로 추가하여 중복을 제거하고 관찰성 기능을 중앙화했습니다.


14-17: 추가 정보를 수집해야 합니다. 다음을 확인하겠습니다:

추가 정보가 필요합니다. 다음 항목들을 확인하겠습니다:

이제 추가 검증을 위해 다음 항목들을 확인하겠습니다:

Gateway 모듈의 BootJar 비활성화 설정이 올바르게 구현되었습니다.

BootJar를 비활성화하고 Jar를 활성화하는 구성은 Spring Boot 라이브러리 모듈을 만드는 표준 패턴입니다.

검증 결과:

  • APIs 모듈이 Gateway에 의존 - apis/build.gradle.kts에서 Dependencies.Projects.GATEWAY import 확인
  • 라이브러리 모듈로 올바른 구성 - 실행 가능한 über-jar를 만드는 것이 목적이 아닌 라이브러리의 경우 Spring Boot 플러그인이 불필요합니다.
  • 멀티 모듈 멀티 애플리케이션 패턴에 적합 - Spring Boot의 멀티 모듈 구조는 애플리케이션의 여러 부분을 독립적으로 관리할 수 있으며 코드를 모듈화된 상태로 유지합니다.

그러나 다음 사항들을 최종 확인하시기 바랍니다:

  • Admin 모듈도 Gateway를 의존성으로 포함하는지 확인
  • 기존 독립 실행형 Gateway 애플리케이션(있었다면)이 제거되었는지 확인
  • CI/CD 파이프라인에서 apis, admin 모듈이 개별적으로 배포되도록 구성되었는지 검토
gateway/src/main/kotlin/org/yapp/gateway/filter/SecurityMdcLoggingFilter.kt (1)

14-29: 필터 체인 순서 검증 완료 - 모든 우려사항이 올바르게 처리됨

SecurityConfig의 필터 체인 설정을 확인한 결과, SecurityMdcLoggingFilter가 BearerTokenAuthenticationFilter 이후에 정확히 배치되어 있습니다 (64번 라인). 따라서 resolveUserId() 메서드 실행 시점에 SecurityContext에서 인증된 JWT principal을 안정적으로 읽을 수 있습니다.

코드 변경사항은 문제가 없습니다.

observability/src/main/kotlin/org/yapp/observability/logging/filter/SimpleMdcLoggingFilter.kt (1)

11-18: SimpleMdcLoggingFilter의 조건부 등록 설정 필요

검증 결과 원래 리뷰 의견이 타당합니다. 현재 설정의 문제점:

  • SimpleMdcLoggingFilter@Component만 적용되어 조건부 등록 로직이 없음
  • 모든 모듈이 @ComponentScan(basePackages = ["org.yapp"])를 사용하므로, SimpleMdcLoggingFilter가 모든 모듈에서 bean으로 등록됨
  • SimpleMdcLoggingFilter의 JavaDoc에서 "Batch 애플리케이션이나 인증이 없는 내부 서비스"라고 명시했으나, 현재는 gateway 모듈(인증 필요)에서도 bean이 생성됨
  • 웹 검색 결과에 따르면 같은 타입의 여러 Filter bean이 존재할 때 Spring에서 예외가 발생할 수 있음

권장 사항:

SimpleMdcLoggingFilter에 다음 중 하나를 적용하세요:

  1. @ConditionalOnProperty@Profile로 batch/unauthenticated 환경에서만 활성화
  2. 각 모듈별 @ComponentScan 범위를 제한하여 필터별로 명시적으로 등록
  3. FilterRegistrationBean 패턴으로 변경하여 명시적 관리
gateway/src/main/kotlin/org/yapp/gateway/security/SecurityConfig.kt (3)

14-15: LGTM: Observability 모듈로의 올바른 모듈화

MdcLoggingFilter를 SecurityMdcLoggingFilter로 교체하고 ActuatorProperties를 observability 모듈에서 임포트하는 변경사항이 적절합니다. 이는 관찰성 관련 기능을 별도 모듈로 분리하는 리팩토링의 일부로 보입니다.


24-24: LGTM: 생성자 파라미터 변경 적절함

MdcLoggingFilter에서 SecurityMdcLoggingFilter로의 타입 변경과 변수명 변경이 일관되게 적용되었습니다.


64-64: 필터 순서 및 구현 검증 완료 - LGTM

SecurityMdcLoggingFilterBearerTokenAuthenticationFilter 다음에 올바르게 배치되어 있습니다. SecurityContext가 이미 인증 필터에 의해 채워진 후 실행되므로, SecurityContextHolder.getContext().authentication에서 JWT 주제(userId)를 안전하게 추출할 수 있습니다.

검증 내용:

  • ✓ 필터 체인 순서가 올바름 (BearerTokenAuthenticationFilter 이후)
  • resolveUserId()가 SecurityContext에서 JWT subject 추출
  • ✓ JWT가 아닌 principal에 대한 폴백 처리 (principal.toString())
  • ✓ 인증 없을 경우 GUEST로 처리됨
  • ✓ MDC.clear()를 통한 적절한 정리

기존 MdcLoggingFilter 기능이 완전히 유지되고 있습니다.

Comment on lines 19 to +21
management:
server:
port: 8081
port: 1234
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

테스트 프로필의 관리 포트 번호 검토 권장

테스트 환경의 management server port가 1234로 설정되어 있습니다. 이는 일반적인 관리 포트 번호(8081, 9090 등)와 다른 임의의 값으로 보입니다. 다른 서비스나 테스트와 충돌 가능성이 없는지, 또는 더 표준적인 포트 번호를 사용하는 것이 좋지 않은지 검토가 필요합니다.

🤖 Prompt for AI Agents
observability/src/main/resources/application-observability.yml lines 19-21: the
management.server.port is hard-coded to 1234 for the test profile; review and
change it to avoid collisions by either (a) switching to a standard management
port used by your infra (e.g., 8081 or 9090) or (b) making it configurable via
an environment/property (e.g., ${MANAGEMENT_PORT:0}) or profile-specific value
so tests can use a random/ephemeral port; update the YAML to remove the fixed
1234 and document the chosen approach in the profile config.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b7c598e and 7d7dad9.

📒 Files selected for processing (3)
  • .github/actions/deploy-module/action.yml (1 hunks)
  • .github/workflows/dev-ci-cd.yml (1 hunks)
  • .github/workflows/prod-ci-cd.yml (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build-validation
🔇 Additional comments (8)
.github/workflows/dev-ci-cd.yml (3)

14-14: IMAGE_PREFIX 환경 변수의 모듈별 이미지 태그 전략 확인 필요

이미지 접두사가 모든 모듈에 동일하게 적용되므로, 모듈별 이미지 구분이 deploy-module 액션의 ${{ inputs.module }}에만 의존합니다. 배포 스크립트와 Docker 이미지 레지스트리에서 이 명명 규칙이 일관되게 사용되고 있는지 확인하세요.

모듈별 이미지 태그 정책이 일관되는지 검증하기 위해 배포 스크립트를 확인해 주세요:

  • deploy-dev.sh 파일에서 IMAGE_TAG 환경 변수 사용 확인
  • Docker Hub에 ninecraft0523/ninecraft-apis, ninecraft0523/ninecraft-batch 이미지가 실제로 존재하는지 확인

81-85: "Skip if no changes" 로직이 명확하지만, matrix 스트래티지의 실행 흐름 검증 권장

조건부 스킵 단계가 올바르게 구현되어 변경이 없는 모듈은 배포되지 않습니다. 다만, matrix.changed != 'true'일 때 exit 0으로 종료하므로, 실제로는 다른 모듈이 계속 배포될 수 있습니다. 이것이 의도된 동작인지 확인하세요. 만약 어떤 모듈도 변경이 없다면 전체 작업이 스킵되어야 한다면, if: needs.detect-changes.outputs.any == 'true' 조건이 올바르게 작동하는지 테스트하세요.


91-109: deploy-module 액션에 전달된 모든 입력이 올바르게 정의되어 있는지 검증

12개 이상의 입력 파라미터가 deploy-module 액션으로 전달됩니다. 특히 image-prefix, image-tag-type, deploy-script 등 새로운 파라미터들이 액션의 입력 정의(.github/actions/deploy-module/action.yml)와 일치하는지 확인이 필수입니다. 또한, matrix.modulematrix.port 값이 실제로 배포 스크립트와 Dockerfile에서 올바르게 사용되는지 검증하세요.

.github/actions/deploy-module/action.yml (2)

114-147: Discord 알림 조건 로직이 올바르지만, 환경 변수 참조 일관성 확인 필요

개발/프로덕션 환경별로 조건이 구분되어 알림이 전송됩니다. 다만 라인 129, 146에서 embed-color: 65280 (초록색)과 embed-color: 16711680 (빨간색)이 하드코딩되어 있습니다. 테스트 시 색상이 올바르게 렌더링되는지 확인하세요.

또한 continue-on-error: true가 라인 117, 134, 151, 167에서 사용되므로, 알림 전송 실패가 배포 결과에 영향을 주지 않습니다. 이는 의도된 동작이지만, 알림 전송 실패 로그를 모니터링할 수 있는 별도 메커니즘이 있는지 확인하세요.


96-112: SSH 액션을 통한 기밀정보 노출 위험 검증 필요

현재 코드에서 appleboy/ssh-action@v1.2.2를 사용하여 secrets를 환경 변수로 내보내고 있습니다. appleboy/ssh-action v1.2.2는 자체 시크릿 마스킹 로직을 구현하지 않으며, GitHub Actions 러너가 시크릿 마스킹을 처리합니다. 그러나 문제는 script 속성에 secrets를 직접 보간할 경우 이들 값이 GitHub Actions 로그에 노출될 수 있다는 점입니다.

구체적인 위험 요소:

  1. GitHub Actions 로그 노출: export DOCKERHUB_TOKEN="${{ inputs.dockerhub-token }}" 같은 export 문이 SSH 액션에 전송되는 script의 일부로 표시되며, 이는 GitHub Actions 로그에 기록됩니다.

  2. 원격 서버 로그: 배포 스크립트(deploy-dev.sh, deploy.sh)가 set -x 모드로 실행되거나 환경 변수를 echo하면, 원격 서버에서 기밀정보가 기록될 수 있습니다.

  3. 동적 값 마스킹 부재: IMAGE_TAG는 동적으로 생성되므로 GitHub Actions의 자동 마스킹 대상이 아닙니다.

권장사항:

  • 동적으로 생성되거나 디코딩된 시크릿에 대해 ::add-mask:: 명령어를 사용하여 GitHub Actions가 로그를 마스킹하도록 설정하세요
  • 원격 서버의 deploy-dev.sh 및 deploy.sh 스크립트가 set +x를 사용하고 기밀정보를 출력하지 않는지 확인하세요
.github/workflows/prod-ci-cd.yml (3)

14-14: 프로덕션 환경의 IMAGE_PREFIX이 개발 환경과 동일한 Docker Hub 계정을 사용하는지 검증

dev-ci-cd.yml과 prod-ci-cd.yml 모두 ninecraft0523/ninecraft을 이미지 접두사로 사용합니다. 프로덕션과 개발이 동일한 Docker Hub 계정을 사용한다면 문제없지만, 다른 레지스트리나 계정을 사용해야 한다면 환경별로 다른 접두사가 필요합니다. 특히 프로덕션 이미지의 접근 권한을 더 엄격하게 관리해야 하는 경우, 별도의 레지스트리 설정이 필요할 수 있습니다.


36-55: 배포 스크립트는 리포지토리가 아닌 서버의 ~/deploy 디렉토리에 존재해야 함 — 검토 의견 재평가

.github/actions/deploy-module/action.yml 분석 결과, 배포 스크립트는 저장소 내에 포함되지 않습니다. 대신 "Deploy to Server" 단계에서 다음과 같이 실행됩니다:

cd ~/deploy
chmod +x ./${{ inputs.deploy-script }}
./${{ inputs.deploy-script }}

이는 deploy-dev.shdeploy.sh가 각 서버(개발/프로덕션)의 ~/deploy 디렉토리에 별도로 관리되는 서버 측 배포 스크립트임을 의미합니다. SSH를 통해 원격으로 실행되는 구조이므로, 저장소 내에 존재하지 않는 것이 정상이고 설계 의도입니다.

따라서 원본 검토 의견의 검증 요청(저장소에서 스크립트 확인)은 배포 아키텍처에 대한 잘못된 가정을 바탕으로 합니다. 실제 검증이 필요하다면 각 서버의 ~/deploy 디렉토리에서 스크립트 존재 여부와 일관성을 확인해야 합니다.

Likely an incorrect or invalid review comment.


52-54: 프로덕션 이미지 태그 전략 — 구문 유효하나 실제 릴리스 태그 형식 검증 필요

라인 52-54의 type=semver,pattern={{version}}type=raw,value=production-latest 구문은 docker/metadata-action이 지원하는 유효한 Handlebars 템플릿입니다.

다만 현재 저장소에는 기존 Git 릴리스 태그가 없어 실제 GitHub Release 프로세스에서 사용될 태그 형식을 검증할 수 없습니다. 향후 릴리스를 생성할 때 Git 태그가 semver 형식(예: v1.0.0, v1.2.3-rc1)을 정확히 따르도록 주의하세요.

Comment on lines 21 to 30
strategy:
fail-fast: false
matrix:
include:
- module: apis
port: 8080
# - module: admin # TODO: Uncomment when admin module is ready
# port: 8081
- module: batch
port: 8082
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

프로덕션 환경에서는 경로 필터가 없으므로, 모든 모듈이 항상 배포됩니다 — 의도 확인 필수

Matrix 전략이 include 섹션에서 apis(8080)과 batch(8082)를 명시하고 있으며, dev-ci-cd.yml의 path-filter와는 다르게 조건부 실행이 없습니다. Release 이벤트는 의도적으로 모든 모듈을 배포하려는 설계로 보이지만, 특정 모듈의 릴리스만 필요한 경우 대응 불가능합니다. Release workflow에서도 변경된 모듈만 배포하도록 path-filter를 추가할 것을 고려하세요.

또한 matrix fail-fast: false는 한 모듈 배포 실패 시에도 다른 모듈을 계속 배포하므로, 부분 배포 시나리오가 발생할 수 있습니다.

🤖 Prompt for AI Agents
In .github/workflows/prod-ci-cd.yml around lines 21-30, the release matrix
currently unconditionally includes apis and batch so every release deploys all
modules; update the workflow to only include changed modules by adding a
path-filter step (or use actions/changed-files / peter-evans/changed-files to
detect modified module paths) and then dynamically generate the matrix.include
from those results instead of hardcoding modules; additionally, decide whether
to set strategy.fail-fast: true (to stop remaining module jobs on first failure)
or document why false is required and keep it as-is.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
batch/src/main/resources/application.yml (1)

28-31: 배치 모듈에 대한 서블릿 멀티파트 설정 재검토 권장

servlet.multipart.max-request-size: 30MB 설정이 추가되었습니다. 배치 모듈은:

  • REST 컨트롤러나 HTTP 엔드포인트를 노출하지 않음
  • 멀티파트 파일 업로드 요청을 처리하지 않음

이 설정은 APIs/Admin 모듈과 같은 HTTP API 서버에서만 필요합니다. 배치 모듈에 필요한지 재검토하고, 불필요하다면 제거하는 것이 좋습니다.

일반적인 배치 모듈 특성:

  • 파일 처리가 필요하면 로컬 파일시스템 또는 원격 스토리지(S3 등)에서 읽음
  • HTTP 요청이 아닌 내부 로직으로 운영됨
Dockerfile (1)

36-36: 기밀 정보가 Docker 이미지에 포함되는 보안 결함 — 긴급 수정 필수

라인 36의 COPY --from=build /app/secret ./secret/는 런타임 이미지에 기밀 정보를 영구적으로 포함시킵니다. 빌드 컨텍스트에 .dockerignore가 없어서 application-*-secret.propertiesAuthKey.p8이 이미지 레이어에 저장되어 Docker 분석 도구로 노출될 수 있습니다.

필수 해결 방안:

  1. 프로젝트 루트에 .dockerignore 파일 생성:
secret/
  1. Dockerfile 라인 36 제거 및 런타임 기밀 정보 주입으로 변경:

기밀 정보는 빌드 시점이 아닌 컨테이너 실행 시점에 주입해야 합니다. 배포 시스템(Kubernetes Secret, Docker secrets, 또는 환경 변수)에서 기밀 정보를 마운트하도록 수정하세요. 예를 들어 Docker Compose나 Kubernetes에서 볼륨 마운트나 secret 객체를 활용하면 됩니다.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7d7dad9 and 89f3164.

📒 Files selected for processing (5)
  • .github/actions/deploy-module/action.yml (1 hunks)
  • .github/workflows/dev-ci-cd.yml (1 hunks)
  • .github/workflows/prod-ci-cd.yml (1 hunks)
  • Dockerfile (3 hunks)
  • batch/src/main/resources/application.yml (2 hunks)
🧰 Additional context used
🪛 Checkov (3.2.334)
Dockerfile

[low] 1-46: Ensure that HEALTHCHECK instructions have been added to container images

(CKV_DOCKER_2)


[low] 1-46: Ensure that a user for the container has been created

(CKV_DOCKER_3)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build-validation
🔇 Additional comments (7)
batch/src/main/resources/application.yml (3)

1-2: 배치 모듈의 포트 번호 변경 검증 필요

서버 포트가 8080에서 8082로 변경되었습니다. 배치 모듈은 백그라운드 스케줄링 작업을 수행하며 외부 HTTP 엔드포인트를 노출하지 않으므로, 이 포트 변경의 의도를 확인하세요:

  • Actuator 관리 포트 용도인지
  • Docker/Kubernetes 배포 시 포트 충돌 회피 목적인지
  • 인프라 설정 전체에서 일관성 있게 적용되었는지 (docker-compose.yml, Dockerfile, 로드밸런서 설정 등)

14-14: external 프로필 추가 사용성 확인

모든 환경(dev, prod, test) 프로필 그룹에 external 프로필이 추가되었습니다. 배치 모듈이 외부 서비스 통합(알림 전송, 외부 API 호출 등)을 필요로 한다면 이는 적절합니다. 다음을 확인하세요:

  • application-external.yml (또는 유사한 설정 파일)이 모든 모듈에 존재하는지
  • 배치 모듈에서 실제로 외부 프로필 설정을 사용하는지 (예: 알림 서비스 연동)

Also applies to: 20-20, 26-26


12-12: 프로필 그룹 통합 승인 — crosscutting/observability로 마이그레이션 완료

모든 환경에서 jwt 프로필을 crosscuttingobservability 프로필로 통합했습니다. 이는 이전 검토 지적사항을 올바르게 해결하고 아키텍처 요구사항을 충족합니다:

  • ✅ JWT 인증 제외: 배치 모듈은 HTTP 엔드포인트를 노출하지 않으므로 인증이 불필요 (이전 리뷰 확인됨)
  • crosscutting 프로필: 로깅, 캐싱, 모니터링 등 횡단 관심사 포함
  • observability 프로필: 배치 작업 모니터링, 메트릭 수집, MDC 로깅 적용
  • external 프로필: 알림(Batch Module Dockerlizing 목표) 등 외부 서비스 통합

멀티 애플리케이션 배포 기능과 일관성 있게 각 모듈의 역할에 맞춰 프로필을 구성했습니다.

Also applies to: 18-18, 24-24

Dockerfile (1)

46-46: ✅ ENTRYPOINT 개선 — 신호 처리 및 기본값 설정

exec 명령 추가와 ${SERVER_PORT:-8080} 기본값 설정이 올바르게 적용되었습니다. 이제 SIGTERM 신호가 정상적으로 Java 프로세스에 전달되어 graceful shutdown이 가능하고, SERVER_PORT 환경 변수 미설정 시에도 안전한 기본값으로 동작합니다.

.github/workflows/dev-ci-cd.yml (1)

17-59: ✅ 멀티 모듈 배포 감지 로직 — 잘 설계된 조건부 실행

detect-changes 작업이 경로 필터를 통해 각 모듈별 변경 여부를 정확하게 감지하고, build-push-and-deploy 작업의 매트릭스 전략에서 이를 활용해 필요한 모듈만 배포합니다. 특히 if: matrix.changed == 'true' 조건으로 불필요한 배포를 방지하고 있습니다.

observability 경로가 모든 모듈 필터에 포함된 것은 관찰성 기반 시설이 런타임 동작에 직접 영향을 주므로, 버전 일관성을 유지하기 위한 의도된 설계입니다.

.github/actions/deploy-module/action.yml (1)

56-65: ✅ 런타임 기밀 정보 주입 — 보안 우수 사례

라인 56-65에서 기밀 정보를 런타임에 동적으로 생성하고 chmod 600으로 파일 권한을 제한하는 방식은 올바른 보안 관행입니다. 다만, Dockerfile 라인 36의 COPY --from=build /app/secret ./secret/ 부분이 이미지에 기밀을 포함시키지 않도록 반드시 수정되어야 합니다 (Dockerfile 리뷰 참고).

.github/workflows/prod-ci-cd.yml (1)

21-27: 프로덕션 배포 모듈 선택성 — 의도 확인 필요

현재 프로덕션 매트릭스 전략(라인 21-27)은 dev-ci-cd.yml의 경로 필터와 달리 모든 모듈(apis, batch)을 조건 없이 배포합니다. Release 이벤트의 특성상 모든 모듈을 항상 배포하려는 의도로 보이지만, 특정 모듈만 릴리스해야 하는 경우에 대응하지 못합니다.

현재 설계 검토 사항:

  • 의도: Release는 항상 전체 모듈을 배포하는가?
  • fail-fast: false는 한 모듈 배포 실패 시에도 다른 모듈을 계속 배포하는 설계인가?

개발 환경처럼 선택적 배포가 필요하다면, detect-changes 작업과 경로 필터를 추가하여 변경된 모듈만 배포하도록 수정할 수 있습니다.

현재 설계가 의도된 것인지 확인하고, 필요시 경로 기반 필터링을 추가하시기 바랍니다.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

♻️ Duplicate comments (1)
.github/workflows/prod-ci-cd.yml (1)

21-27: 프로덕션 배포 시 변경된 모듈만 선택적으로 배포하도록 경로 필터 추가 필요

현재 구현은 릴리스 이벤트 발생 시 apisbatch 모듈을 항상 배포합니다. fail-fast: false는 한 모듈 배포 실패 시에도 다른 모듈을 계속 배포하므로 부분 배포 시나리오가 발생할 수 있습니다. 개발 환경(dev-ci-cd.yml)에서는 경로 필터로 조건부 실행을 하고 있는 만큼, 프로덕션에서도 변경된 모듈만 배포하도록 다음 중 하나를 고려하세요:

  1. 동적 매트릭스 생성: actions/changed-files 또는 peter-evans/changed-files 액션으로 변경된 모듈을 감지한 후 동적으로 matrix를 생성
  2. fail-fast 동작 명확화: fail-fast: true로 변경하여 모듈 배포 실패 시 다른 모듈 배포도 중단하거나, false인 이유를 문서화
🤖 AI Agent Prompt
.github/workflows/prod-ci-cd.yml의 release 워크플로우를 수정하여:
1. actions/changed-files를 사용해 modules/ 디렉토리의 변경을 감지
2. 변경된 모듈 목록 (예: apis, batch)을 matrix.include에 동적으로 설정
3. fail-fast 전략 명확화 (true/false 선택 및 이유 문서화)
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 89f3164 and 9a2bc77.

📒 Files selected for processing (2)
  • .github/actions/deploy-module/action.yml (1 hunks)
  • .github/workflows/prod-ci-cd.yml (1 hunks)
🔇 Additional comments (3)
.github/workflows/prod-ci-cd.yml (1)

33-50: 모든 필수 입력값이 올바르게 전달되고 있습니다

deploy-module 액션 호출이 올바르게 구성되어 있으며, 환경 변수, 모듈명, 도커 자격증명, 배포 스크립트 등 모든 필수 입력값이 명시적으로 전달되고 있습니다.

.github/actions/deploy-module/action.yml (2)

97-112: 원격 서버 배포 단계가 올바르게 구성됨

SSH 액션을 통한 배포 구성이 적절합니다. 환경 변수가 원격 배포 스크립트에 명시적으로 전달되고 있으며, 권한 설정도 올바릅니다(chmod +x).


83-95: docker/build-push-action의 secrets 매개변수 사용을 검토해야 합니다.

docker/build-push-action@v6secrets 입력을 지원하는 것은 맞습니다. 그러나 이 제안을 적용하려면 추가 고려사항이 있습니다:

  • 현재 워크플로우는 첫 번째 단계(11-14줄)에서 여전히 ./secret/ 디렉토리에 파일을 생성합니다.
  • docker/build-push-actionsecrets 매개변수를 사용하려면 Dockerfile이 RUN --mount=type=secret,id=... 구문을 지원하도록 수정되어야 합니다.
  • 제안된 방식이 현재 파일 생성 단계를 대체하는지, 아니면 추가되는지 명확히 해야 합니다.

다음을 검증하세요:

  • Dockerfile이 BuildKit 시크릿 마운트 구문을 지원하는가?
  • 파일 생성 단계(11-14줄)를 제거하고 secrets 매개변수로만 진행해야 하는가?
  • 전체 구현의 일관성을 확인하고 필요시 Dockerfile도 함께 수정하는가?

Comment on lines +48 to +51
deploy-script:
description: 'Deploy script name (deploy-dev.sh or deploy-prod.sh)'
required: true
default: 'deploy-dev.sh'
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

deploy-script 입력 설명 명확화 제안

라인 51의 기본값 'deploy-dev.sh'는 설명과 실제 사용이 혼동을 줄 수 있습니다. 의존 워크플로우(dev-ci-cd.yml, prod-ci-cd.yml)에서 명시적으로 deploy-dev.sh 또는 deploy-prod.sh를 전달하고 있으므로, 설명을 다음과 같이 개선하면 좋습니다:

deploy-script:
  description: 'Deploy script name (deploy-dev.sh for dev, deploy-prod.sh for prod) - 워크플로우에서 명시적으로 전달됨'
  required: true
  default: 'deploy-dev.sh'

이렇게 하면 향후 독자가 기본값이 개발 환경 용도임을 알 수 있고, 워크플로우에서 의도적으로 override됨을 이해할 수 있습니다.

🤖 Prompt for AI Agents
.github/actions/deploy-module/action.yml around lines 48 to 51: the input
description for `deploy-script` is ambiguous given the default value
'deploy-dev.sh' and workflows explicitly pass either deploy-dev.sh or
deploy-prod.sh; update the description to state that the default is for dev and
that workflows explicitly override it (e.g., "Deploy script name (deploy-dev.sh
for dev, deploy-prod.sh for prod) - workflows explicitly override this value"),
keep required: true and default: 'deploy-dev.sh' unchanged.

Comment on lines +56 to +65
- name: Inject application-secret.properties from Secrets
shell: bash
run: |
mkdir -p ./secret
echo "$SECRET_CONTENT" > ./secret/application-${{ inputs.environment }}-secret.properties
echo "$APPLE_KEY_CONTENT" > ./secret/AuthKey.p8
chmod 600 ./secret/*
env:
SECRET_CONTENT: ${{ inputs.secret-properties }}
APPLE_KEY_CONTENT: ${{ inputs.apple-auth-key }}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🔴 기밀정보 보안: 런타임 시크릿 마운트로 마이그레이션 필수

현재 구현은 GitHub Actions 런너에 기밀정보 파일을 작성한 후 Docker 빌드 컨텍스트에 포함시켜 최종 이미지 레이어에 영구적으로 저장합니다. 빌드 인자와 환경 변수는 최종 이미지에 지속되므로 부적절하며, 대신 시크릿 마운트나 SSH 마운트를 사용해야 합니다.

필수 수정 사항:

  1. docker/build-push-action@v6 단계에 secrets 매개변수 추가 (라인 83-95):
secrets: |
  "application_secret=${{ inputs.secret-properties }}"
  "apple_auth_key=${{ inputs.apple-auth-key }}"
  1. Dockerfile 업데이트: RUN --mount=type=secret 플래그를 사용하여 시크릿을 빌드 컨테이너 내 /run/secrets/<id> 경로에 임시로 마운트합니다.

  2. 기밀정보 파일 작성 단계 제거 (라인 56-65): 런타임에 시크릿을 주입하도록 변경하면, ./secret 디렉토리 생성이 불필요해집니다.

BuildKit은 tmpfs를 사용하여 시크릿을 임시 파일로 마운트하므로 이미지에 시크릿이 남지 않습니다.


시크릿 보안 문제를 검증하기 위해 필요한 정보를 수집하겠습니다.


---

🏁 Script executed:

```shell
#!/bin/bash
# Find and examine the action.yml file
echo "=== Examining action.yml ==="
find . -name "action.yml" -path "*deploy-module*" -type f | head -5

Length of output: 71


🏁 Script executed:

#!/bin/bash
# Look for Dockerfile in the repository
echo "=== Finding Dockerfile ==="
fd -t f -i dockerfile | head -10

Length of output: 37


🏁 Script executed:

#!/bin/bash
# Search for docker/build-push-action usage
echo "=== Searching for docker/build-push-action usage ==="
rg -n "build-push-action" --type yaml -A 10 -B 2 | head -50

Length of output: 52


🏁 Script executed:

#!/bin/bash
# Check for secrets parameter in build-push-action or any secret handling
echo "=== Checking for secrets parameter in workflows ==="
rg -n "secrets:" --type yaml -A 5 -B 5 | head -80

Length of output: 51


🏁 Script executed:

#!/bin/bash
# Examine the specific action.yml file
echo "=== Full action.yml content ==="
cat ".github/actions/deploy-module/action.yml" 2>/dev/null || echo "File not found at standard path"

Length of output: 6582


Dockerfile를 검증하여 시크릿 처리 방식을 확인하겠습니다.


---

🏁 Script executed:

```shell
#!/bin/bash
# Examine the Dockerfile
echo "=== Examining Dockerfile ==="
cat ./Dockerfile

Length of output: 1310


🏁 Script executed:

#!/bin/bash
# Search for secret-related patterns in Dockerfile
echo "=== Checking for ./secret references in Dockerfile ==="
grep -n "secret\|SECRET" ./Dockerfile || echo "No secret references found"

Length of output: 124


🏁 Script executed:

#!/bin/bash
# Check for RUN --mount=type=secret pattern
echo "=== Checking for secret mount patterns ==="
grep -n "mount=type=secret" ./Dockerfile || echo "No secret mount patterns found"

Length of output: 73


🏁 Script executed:

#!/bin/bash
# Check if .dockerignore exists and what it contains
echo "=== Checking .dockerignore ==="
cat ./.dockerignore 2>/dev/null || echo ".dockerignore not found"

Length of output: 54


🔴 중대 보안 이슈: 시크릿이 Docker 이미지 레이어에 영구 저장됨

현재 구현은 시크릿 데이터가 최종 Docker 이미지에 영구적으로 저장되는 심각한 취약점을 가지고 있습니다. 검증 결과:

  • action.yml (56-65줄): ./secret 디렉토리에 시크릿 파일 작성
  • Dockerfile (36줄): COPY --from=build /app/secret ./secret/ 명령으로 시크릿을 최종 이미지로 복사
  • docker/build-push-action@v6 (83-95줄): secrets 파라미터 부재 → 시크릿이 빌드 컨텍스트에 노출
  • .dockerignore: 존재하지 않음 → ./secret 폴더 제외 불가능

결과적으로 시크릿이 Docker Hub에 푸시된 이미지 레이어에 평문으로 저장됩니다.

필수 수정 사항:

  1. docker/build-push-action@v6에 secrets 파라미터 추가 (83-95줄):

    with:
      context: .
      file: ./Dockerfile
      platforms: linux/amd64
      push: true
      tags: ${{ steps.meta.outputs.tags }}
      cache-from: type=gha,scope=${{ inputs.module }}
      cache-to: type=gha,mode=max,scope=${{ inputs.module }}
      build-args: |
        MODULE=${{ inputs.module }}
      secrets: |
        "application_secret=${{ inputs.secret-properties }}"
        "apple_auth_key=${{ inputs.apple-auth-key }}"
  2. Dockerfile 업데이트 (36줄 제거 및 시크릿 마운트 사용):

    • 기존: COPY --from=build /app/secret ./secret/ 제거
    • 변경: RUN --mount=type=secret=application_secret --mount=type=secret=apple_auth_key 사용하여 런타임에 /run/secrets/ 경로에서 접근
  3. action.yml의 시크릿 파일 작성 단계 제거 또는 조건부 처리 (56-65줄):

    • BuildKit 시크릿 마운트 사용 시 ./secret 디렉토리 생성 불필요

Comment on lines +114 to +176
- name: Send Discord notification on success (Development)
uses: tsickert/discord-webhook@b217a69502f52803de774ded2b1ab7c282e99645
if: success() && inputs.environment == 'dev'
continue-on-error: true
shell: bash
with:
webhook-url: ${{ inputs.discord-webhook-url }}
embed-title: "✅ [${{ github.repository }}] Development Deploy Succeeded - ${{ inputs.module }}"
embed-description: |
**Module**: `${{ inputs.module }}`
**Commit**: `${{ github.sha }}`
**Author**: `${{ github.actor }}`
**Message**: `${{ github.event.head_commit.message }}`
[View Committed Changes](https://github.com/${{ github.repository }}/commit/${{ github.sha }})
embed-color: 65280

- name: Send Discord notification on success (Production)
uses: tsickert/discord-webhook@b217a69502f52803de774ded2b1ab7c282e99645
if: success() && inputs.environment == 'prod'
continue-on-error: true
shell: bash
with:
webhook-url: ${{ inputs.discord-webhook-url }}
content: "🚀 **Production Deploy Succeeded!**"
embed-title: "✅ [${{ github.repository }}] Production Deploy Succeeded - ${{ inputs.module }}"
embed-description: |
**Module**: `${{ inputs.module }}`
**Deployed by**: `${{ github.actor }}`
The new version has been successfully deployed to production.
[View Workflow](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }})
embed-color: 65280

- name: Send Discord notification on failure (Development)
uses: tsickert/discord-webhook@b217a69502f52803de774ded2b1ab7c282e99645
if: failure() && inputs.environment == 'dev'
continue-on-error: true
shell: bash
with:
webhook-url: ${{ inputs.discord-webhook-url }}
embed-title: "❌ [${{ github.repository }}] Development Deploy Failed - ${{ inputs.module }}"
embed-description: |
**Module**: `${{ inputs.module }}`
**Commit**: `${{ github.sha }}`
**Author**: `${{ github.actor }}`
An error occurred during the workflow execution.
[View Failed Workflow](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }})
embed-color: 16711680

- name: Send Discord notification on failure (Production)
uses: tsickert/discord-webhook@b217a69502f52803de774ded2b1ab7c282e99645
if: failure() && inputs.environment == 'prod'
continue-on-error: true
shell: bash
with:
webhook-url: ${{ inputs.discord-webhook-url }}
content: "🚨 **Production Deploy Failed!**"
embed-title: "❌ [${{ github.repository }}] Production Deploy Failed - ${{ inputs.module }}"
embed-description: |
**Module**: `${{ inputs.module }}`
**Deployed by**: `${{ github.actor }}`
An error occurred during the production deployment workflow.
[View Failed Workflow](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }})
embed-color: 16711680
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Discord 알림 구성이 적절하며 선택적 개선 가능

환경별 성공/실패 알림이 명확하게 구분되어 있고, continue-on-error: true로 설정되어 웹훅 실패가 배포 프로세스에 영향을 주지 않습니다.

선택사항: 코드 중복 감소

네 개의 Discord 알림 단계에서 상당한 중복이 있습니다. 조건문을 활용하여 단일 단계로 통합할 수 있습니다:

- name: Send Discord notification
  uses: tsickert/discord-webhook@b217a69502f52803de774ded2b1ab7c282e99645
  if: always()
  continue-on-error: true
  shell: bash
  with:
    webhook-url: ${{ inputs.discord-webhook-url }}
    content: ${{ (success() && inputs.environment == 'prod') && '🚀 **Production Deploy Succeeded!**' || (failure() && inputs.environment == 'prod') && '🚨 **Production Deploy Failed!**' || '' }}
    embed-title: |
      ${{ success() && '✅' || '❌' }} [${{ github.repository }}] 
      ${{ inputs.environment == 'prod' && 'Production' || 'Development' }} Deploy 
      ${{ success() && 'Succeeded' || 'Failed' }} - ${{ inputs.module }}
    embed-description: |
      **Module**: `${{ inputs.module }}`
      ...
    embed-color: ${{ success() && 65280 || 16711680 }}

그러나 현재 구현도 명확하고 유지보수하기 좋습니다.

🤖 Prompt for AI Agents
.github/actions/deploy-module/action.yml lines 114-176: the four nearly
identical Discord webhook steps (dev/prod × success/failure) can be consolidated
to remove duplication; replace them with a single step (keep continue-on-error:
true) that runs always() and sets webhook inputs (content, embed-title,
embed-description, embed-color) using GitHub Actions expressions that branch on
success()/failure() and inputs.environment to produce the correct text/color for
dev vs prod and success vs failure, preserving all existing fields (module,
sha/actor/run links) and ensuring empty content when not needed.

@sonarqubecloud
Copy link

Quality Gate Failed Quality Gate failed

Failed conditions
3.4% Coverage on New Code (required ≥ 80%)

See analysis details on SonarQube Cloud

@move-hoon move-hoon merged commit dd42e15 into develop Oct 20, 2025
4 of 5 checks passed
@move-hoon move-hoon changed the title feat: 멀티 애플리케이션 배포 기능 구현 feat: 멀티 애플리케이션 Matrix 병렬 배포 기능 구현 Oct 22, 2025
@move-hoon move-hoon changed the title feat: 멀티 애플리케이션 Matrix 병렬 배포 기능 구현 feat: GitHub Actions 배포 워크플로우 Matrix 전략 도입 및 Blue-Green 배포 구조 개선 Oct 22, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BOOK-350/feat] Batch Module Dockerlizing

2 participants