Skip to content

[FEAT] 배포 환경 구성 (Flyway / Redis / InfluxDB / docker-compose / CI-CD)#322

Open
jongjunn wants to merge 1 commit into
developfrom
feature/#312-deploy-infra-setup
Open

[FEAT] 배포 환경 구성 (Flyway / Redis / InfluxDB / docker-compose / CI-CD)#322
jongjunn wants to merge 1 commit into
developfrom
feature/#312-deploy-infra-setup

Conversation

@jongjunn

@jongjunn jongjunn commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

📌 연관 이슈


📝 작업 내용

  • Flyway 의존성 추가 (flyway-core, flyway-mysql) + ddl-auto: validate 전환
  • V1__baseline.sql 정비 — FK 순서(FOREIGN_KEY_CHECKS), 누락 컬럼(email_verifications.status/used), video 테이블명 보정
  • application.yaml 작성 — DB/JWT/Mail/Redis/InfluxDB 환경변수 분리, application-secret.yaml gitignore 처리
  • RedisConfig@EnableCaching, RedisCacheManager, StringRedisTemplate 빈 등록
  • InfluxDbConfigInfluxDBClient 빈 등록
  • SecurityConfig@EnableMethodSecurity + RoleHierarchy(ADMIN > INSTRUCTOR > STUDENT) 추가
  • PlayableVideoProgressReader — 영상 접근 검증 + 진도 조회 공통 컴포넌트 분리
  • docker-compose.yml — MySQL 8.0 / Redis 7 / InfluxDB 2 로컬 환경 구성
  • Dockerfile — eclipse-temurin:17-jre-alpine 기반
  • .github/workflows/ci.yml — PR 대상 develop/main 빌드+테스트 자동 실행
  • .github/workflows/deploy.yml — main push 시 EC2 자동 배포

🖥️ 프론트엔드 연동 가이드 (API 명세)

인프라 설정 PR로 API 변경 없음


🚨 주요 에러 코드 및 예외 (Exceptions)

해당 없음


💡 백엔드 리뷰 포인트 (Backend Review)

  • V1__baseline.sql: SET FOREIGN_KEY_CHECKS=0/1 감싸기 — 기존 베이스라인 덤프 순서 문제 해결
  • application.yaml: spring.config.import: optional:classpath:application-secret.yaml 패턴으로 로컬 시크릿 분리
  • SecurityConfig: 경로별 세부 권한 분기는 @PreAuthorize로 위임 예정 — 현재는 공통 인증만 처리
  • 우려되는 부분: deploy.yml은 EC2 Secrets(HOST/KEY/ENV) 등록 후 정상 동작. 현재 GitHub Secrets 미등록 시 배포 단계 실패

✅ 체크리스트

  • 코드 컨벤션을 준수했습니다.
  • 불필요한 주석 및 콘솔 로그를 제거했습니다.
  • API 기능이 정상 동작하는지 테스트했습니다. (./gradlew build 성공, 앱 32초 기동 확인)
  • 예외(잘못된 값) 상황에 대한 검증 및 테스트를 통과했습니다.

Summary by CodeRabbit

릴리스 노트

  • New Features

    • 비디오 재생 진행률 조회 기능 추가
    • 역할 기반 접근 제어 계층화
  • Chores

    • CI/CD 자동화 파이프라인 구축 (GitHub Actions)
    • Docker 기반 배포 워크플로우 추가
    • Redis 캐싱 및 InfluxDB 모니터링 인프라 통합
    • 데이터베이스 마이그레이션 및 설정 파일 추가

@coderabbitai

coderabbitai Bot commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

.coderabbit.yaml has a parsing error

The CodeRabbit configuration file in this repository has a parsing error and default settings were used instead. Please fix the error(s) in the configuration file. You can initialize chat with CodeRabbit to get help with the configuration file.

💥 Parsing errors (1)
Validation error: Invalid input: expected object, received null at "reviews.finishing_touches"
⚙️ Configuration instructions
  • Please see the configuration documentation for more information.
  • You can also validate your configuration using the online YAML validator.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json
📝 Walkthrough

워크스루

배포 환경 구성을 위해 docker-compose로 MySQL, Redis, InfluxDB 로컬 서비스를 정의하고, GitHub Actions CI/CD 파이프라인을 추가했다. Gradle 의존성과 애플리케이션 YAML 설정을 확장하며, Redis 캐싱, InfluxDB 메트릭, 메서드 보안 역할 계층을 Spring으로 구성했다. 데이터베이스 마이그레이션에서 스키마를 정제하고, 비디오 진행 읽기 서비스와 결제 상태 열거형을 추가했다.

변경 사항

배포 환경 구성 및 인프라 통합

레이어 / 파일 설명
Docker 서비스 구성 및 컨테이너화
docker-compose.yml, Dockerfile
MySQL 8.0, Redis 7, InfluxDB 2 서비스를 docker-compose로 정의하고, Java 17 Temurin JRE 기반 애플리케이션 컨테이너를 구성한다. 데이터 영속성을 위해 named volume 3개를 선언한다.
CI/CD 워크플로우 및 자동화
.github/workflows/ci.yml, .github/workflows/deploy.yml
pull_request(develop/main)에서 ./gradlew test를 실행하는 CI 워크플로우와, main 푸시 시 JAR 빌드 후 SCP로 EC2 전송, SSH로 Docker 이미지 빌드 및 컨테이너 실행하는 CD 워크플로우를 구성한다.
의존성 관리 및 애플리케이션 설정
build.gradle, src/main/resources/application.yaml, src/test/resources/application-test.yaml, .gitignore
Flyway, Spring Data Redis, InfluxDB 클라이언트 의존성을 추가하고, 데이터베이스, 메일(SMTP), Redis, JWT, 업로드, 로깅, InfluxDB 연결을 환경변수 기반 YAML로 정의한다. 테스트 환경에서 Flyway를 비활성화하고, 설정 파일을 gitignore에 명시한다.
Spring 인프라 설정
src/main/java/com/wanted/backend/global/config/RedisConfig.java, src/main/java/com/wanted/backend/global/config/InfluxDbConfig.java, src/main/java/com/wanted/backend/global/config/SecurityConfig.java
Redis 캐싱(10분 TTL, JSON 직렬화)과 StringRedisTemplate을 구성하는 RedisConfig, InfluxDB 클라이언트를 생성하는 InfluxDbConfig, ROLE_ADMIN > ROLE_INSTRUCTOR > ROLE_STUDENT 역할 계층 및 메서드 보안을 활성화하는 SecurityConfig를 추가한다.
데이터베이스 스키마 마이그레이션
src/main/resources/db/migration/V1__baseline.sql
외래키 체크를 마이그레이션 시작/종료 시 제어하고, email_verifications 테이블에 상태(PENDING 기본값) 및 사용 여부(false 기본값) 컬럼을 추가하며, videos 테이블명을 video로 정규화한다.
도메인 로직 및 열거형 업데이트
src/main/java/com/wanted/backend/domain/learning_activity/application/service/PlayableVideoProgressReader.java, src/main/java/com/wanted/backend/domain/payment/domain/model/PaymentStatus.java
비디오 존재 확인, 접근 가능 검증, 진행 조회를 통합한 PlayableVideoProgressReader 서비스를 추가하고, PlayableVideoProgress 레코드로 접근 정보와 진행을 함께 반환한다. PaymentStatus 열거형의 취소 상태를 CANCELED로 표준화한다.

예상 코드 리뷰 노력

🎯 4 (Complex) | ⏱️ ~45분

관련된 가능성 있는 PR

  • Hard-Click/Hard-Click-BackEnd-Module3#303: 이 PR의 데이터베이스 마이그레이션이 email_verifications 테이블의 status, used 컬럼을 추가하여, 검색된 PR의 이메일 검증 상태 머신 기반 리팩토링을 직접 지원한다.

시를 짓다면

🐰 Docker는 춤을 추고 (compose dance)
Redis는 캐시에 숨어든다
Flyway가 스키마를 정렬하는 동안
GitHub Actions는 밤새 배포한다
배포 환경이 완성되어 모두 웃는다! 🚀

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning PaymentStatus.java의 CANCELLED→CANCELED 변경 및 SecurityConfig의 roleHierarchy 추가는 배포 인프라와 직접 관련 없는 부분입니다. PaymentStatus 열거형 변경과 SecurityConfig의 roleHierarchy는 별도 PR로 분리하거나 #312 요구사항과의 연관성을 명확히 문서화하세요.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed PR 제목이 주요 변경 사항(배포 환경 구성)을 명확하고 구체적으로 설명합니다.
Description check ✅ Passed PR 설명이 템플릿 구조를 따르며 연관 이슈, 작업 내용, 리뷰 포인트, 체크리스트를 완벽하게 포함합니다.
Linked Issues check ✅ Passed 모든 #312 요구사항(Flyway, Redis, InfluxDB 설정, docker-compose, CI/CD 워크플로우)이 구현되어 완벽하게 충족됩니다.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/#312-deploy-infra-setup

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 8

Caution

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

⚠️ Outside diff range comments (1)
src/main/java/com/wanted/backend/global/config/SecurityConfig.java (1)

54-73: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

/api/courses/*/reviews가 모든 HTTP 메서드에 대해 공개됩니다.

Line 67에서 메서드 제한 없이 permitAll()을 걸어 둬서, src/main/java/com/wanted/backend/domain/community/presentation/ReviewController.java/api/courses/{courseId}/reviews 하위 POST/DELETE/PATCH도 전부 먼저 열립니다. 바로 아래 Line 73에 GET 전용 공개 규칙이 따로 있는 걸 보면 의도는 조회만 공개였던 것 같아서, 위쪽의 전역 matcher에서는 이 경로를 빼야 합니다.

수정 예시
                 .authorizeHttpRequests(auth -> auth
                         .requestMatchers(
                                 "/api/auth/login",
                                 "/api/auth/signup",
                                 "/api/auth/check-username",
                                 "/api/auth/check-email",
                                 "/api/auth/email/send",
                                 "/api/auth/email/verify",
                                 "/api/auth/account-locks/email",
                                 "/api/auth/account-locks/verify",
                                 "/api/auth/account-locks/password",
                                 "/api/auth/password-reset/**",
-                                "/api/auth/account-locks/verify",
-                                "/api/auth/account-locks/password",
-                                "/api/courses/*/reviews",
                                 "/swagger-ui.html",
                                 "/swagger-ui/**",
                                 "/v3/api-docs/**"
                         ).permitAll()
                         .requestMatchers(HttpMethod.GET, "/uploads/**").permitAll()
                         .requestMatchers(HttpMethod.GET, "/api/courses", "/api/courses/*", "/api/courses/*/reviews").permitAll()
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/java/com/wanted/backend/global/config/SecurityConfig.java` around
lines 54 - 73, In SecurityConfig, remove the "/api/courses/*/reviews" entry from
the broad requestMatchers(...).permitAll() list so you don't expose non-GET
methods; keep the existing .requestMatchers(HttpMethod.GET, "/api/courses",
"/api/courses/*", "/api/courses/*/reviews").permitAll() line to allow only GETs;
verify ReviewController POST/DELETE/PATCH endpoints are protected after the
change.
🧹 Nitpick comments (1)
build.gradle (1)

3-4: build.gradle의 org.springframework.boot 플러그인 버전 3.5.14 오타 의심은 불필요합니다(존재하는 배포 버전). (build.gradle 3-4)

  • org.springframework.boot Gradle 플러그인 3.5.14는 Gradle Plugin Portal과 Spring Boot 릴리즈(GitHub releases)에서 실제로 확인됩니다.
  • 다만 Spring Boot 3.5 계열 최신 릴리즈는 3.5.15이므로, 업데이트를 고려할 만합니다.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@build.gradle` around lines 3 - 4, Summary: The org.springframework.boot
Gradle plugin version is set to 3.5.14 (valid), but you may want to bump it to
the latest 3.5.15. Update the plugin declaration in build.gradle by replacing
the version string '3.5.14' with '3.5.15' on the id 'org.springframework.boot'
line (or leave as-is if you intentionally target 3.5.14), then refresh
dependencies / run a build to verify compatibility.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/ci.yml:
- Around line 3-5: 현재 워크플로우에 "on: pull_request"만 설정되어 있어 직접 푸시(push)로는 실행되지
않습니다; 수정하려면 워크플로우의 트리거 섹션(현재 "on: pull_request"와 "branches: [ develop, main ]"
설정)을 확장해 동일한 브랜치들에 대해 "push" 트리거도 추가하여 push/PR 둘 다 CI가 실행되도록 만드세요.

In @.github/workflows/deploy.yml:
- Around line 73-74: Current deploy step uses `docker ps | grep hard-click-app`
which only shows a running process and can falsely mark deployment success;
replace it with a robust readiness check: wait for the container
`hard-click-app` to report healthy via its Docker HEALTHCHECK (`docker inspect`
reading `.State.Health.Status`) or poll the application's readiness HTTP
endpoint inside the container (e.g., curl
http://localhost:PORT/actuator/health/readiness), retry with a timeout, and fail
the job if it doesn't become healthy within the timeout so the workflow
accurately fails on startup or migration errors.

In `@docker-compose.yml`:
- Around line 6-10: Remove hardcoded credentials from the docker-compose service
environment block (MYSQL_DATABASE, MYSQL_USER, MYSQL_PASSWORD,
MYSQL_ROOT_PASSWORD) and switch to using environment variable interpolation or
an env_file; update the compose file to reference ${MYSQL_DATABASE},
${MYSQL_USER}, ${MYSQL_PASSWORD}, ${MYSQL_ROOT_PASSWORD} (or point to an
env_file) and ensure local .env or .env.local is added to .gitignore and
contains the actual secret values for local development.

In `@Dockerfile`:
- Around line 1-4: The Dockerfile runs the app as root; create a non-root user
and switch to it before ENTRYPOINT: add steps after WORKDIR/COPY to create a
dedicated unprivileged user/group, chown the application files (app.jar) and
WORKDIR to that user, and then add USER to run the container as that user so
ENTRYPOINT ["java", "-jar", "app.jar"] executes without root privileges; update
Dockerfile symbols WORKDIR, COPY, app.jar, and ENTRYPOINT accordingly.

In
`@src/main/java/com/wanted/backend/domain/payment/domain/model/PaymentStatus.java`:
- Around line 8-11: PaymentStatus.from currently delegates to
PaymentStatus.valueOf and will break on legacy DB values like "CANCELLED";
update from(String value) in PaymentStatus to normalize and accept legacy
spellings (e.g., trim, uppercase, map "CANCELLED" -> "CANCELED") and return the
correct enum, and/or add a migration path for existing DB rows; check usages in
PaymentJpaEntity and MyPaymentHistoryQueryAdapter to ensure they keep calling
PaymentStatus.from(...) so the compatibility mapping handles old values.

In `@src/main/java/com/wanted/backend/global/config/InfluxDbConfig.java`:
- Around line 24-26: InfluxDbConfig의 influxDBClient 빈이 항상 생성되어 INFLUX_TOKEN이 없는
테스트/로컬 환경에서 실패할 수 있으니, InfluxDB가 선택적 인프라인 경우 빈 등록을 조건부로 변경하세요: InfluxDbConfig 또는
influxDBClient 메서드에 `@ConditionalOnProperty`(prefix="influx", name="enabled",
havingValue="true", matchIfMissing=false) 또는 프로파일 기반(`@Profile`("!test") 또는 별도
테스트용 구성) 어노테이션을 추가해 url/token/org/bucket 값이 없을 때 빈이 생성되지 않도록 하고, 테스트 환경에서는 해당 빈을
비활성화하거나 모킹된 대체 빈을 제공하도록 수정하세요.

In `@src/main/resources/application.yaml`:
- Around line 44-45: 현재 application.yaml의 config.import 항목이
optional:classpath:application-secret.yaml로 되어 있어 루트에 둔 application-secret.yaml을
로드하지 못합니다; config.import 설정을 optional:file:./application-secret.yaml로 변경하거나 실제로
리소스 경로에 파일을 두려면 application-secret.yaml을 src/main/resources로 이동하는 방식 중 하나로 고치세요
(참조 식별자: config.import, application-secret.yaml).

In `@src/main/resources/db/migration/V1__baseline.sql`:
- Line 1: 현재 migration이 세션 전역 변수 FOREIGN_KEY_CHECKS를 0으로 강제로 설정만 하고 복원하지 않아 같은
커넥션을 쓰는 이후 migration의 동작에 영향을 줄 수 있습니다; V1__baseline.sql에서 실행 전에 현재
값(@@FOREIGN_KEY_CHECKS)을 임시 변수에 저장하고 SET FOREIGN_KEY_CHECKS=0을 실행한 뒤 migration
끝에서 저장한 변수값으로 복원하도록 수정하세요 (즉, SELECT @@FOREIGN_KEY_CHECKS INTO `@old_fk_checks`;
SET FOREIGN_KEY_CHECKS=0; ... SET FOREIGN_KEY_CHECKS=`@old_fk_checks`; 형식으로
FOREIGN_KEY_CHECKS 값을 저장·복원하도록 변경).

---

Outside diff comments:
In `@src/main/java/com/wanted/backend/global/config/SecurityConfig.java`:
- Around line 54-73: In SecurityConfig, remove the "/api/courses/*/reviews"
entry from the broad requestMatchers(...).permitAll() list so you don't expose
non-GET methods; keep the existing .requestMatchers(HttpMethod.GET,
"/api/courses", "/api/courses/*", "/api/courses/*/reviews").permitAll() line to
allow only GETs; verify ReviewController POST/DELETE/PATCH endpoints are
protected after the change.

---

Nitpick comments:
In `@build.gradle`:
- Around line 3-4: Summary: The org.springframework.boot Gradle plugin version
is set to 3.5.14 (valid), but you may want to bump it to the latest 3.5.15.
Update the plugin declaration in build.gradle by replacing the version string
'3.5.14' with '3.5.15' on the id 'org.springframework.boot' line (or leave as-is
if you intentionally target 3.5.14), then refresh dependencies / run a build to
verify compatibility.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 9258aaaf-598a-4471-a528-645eaf736a1e

📥 Commits

Reviewing files that changed from the base of the PR and between 9fabb76 and 0a07466.

📒 Files selected for processing (14)
  • .github/workflows/ci.yml
  • .github/workflows/deploy.yml
  • .gitignore
  • Dockerfile
  • build.gradle
  • docker-compose.yml
  • src/main/java/com/wanted/backend/domain/learning_activity/application/service/PlayableVideoProgressReader.java
  • src/main/java/com/wanted/backend/domain/payment/domain/model/PaymentStatus.java
  • src/main/java/com/wanted/backend/global/config/InfluxDbConfig.java
  • src/main/java/com/wanted/backend/global/config/RedisConfig.java
  • src/main/java/com/wanted/backend/global/config/SecurityConfig.java
  • src/main/resources/application.yaml
  • src/main/resources/db/migration/V1__baseline.sql
  • src/test/resources/application-test.yaml

Comment thread .github/workflows/ci.yml
Comment on lines +3 to +5
on:
pull_request:
branches: [ develop, main ]

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

push 트리거가 빠져 있어서 CI 목표를 완전히 못 채웁니다.

현재는 PR에서만 테스트가 돌고, develop/main 직접 푸시는 이 워크플로우를 타지 않습니다. 링크된 목표가 push/PR 양쪽 자동 실행이라면 push도 같이 등록해야 합니다.

예시 수정
 on:
   pull_request:
     branches: [ develop, main ]
+  push:
+    branches: [ develop, main ]
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
on:
pull_request:
branches: [ develop, main ]
on:
pull_request:
branches: [ develop, main ]
push:
branches: [ develop, main ]
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/ci.yml around lines 3 - 5, 현재 워크플로우에 "on: pull_request"만
설정되어 있어 직접 푸시(push)로는 실행되지 않습니다; 수정하려면 워크플로우의 트리거 섹션(현재 "on: pull_request"와
"branches: [ develop, main ]" 설정)을 확장해 동일한 브랜치들에 대해 "push" 트리거도 추가하여 push/PR 둘 다
CI가 실행되도록 만드세요.

Comment on lines +73 to +74
echo "=== Deploy complete ==="
docker ps | grep hard-click-app

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

배포 성공 판정이 너무 이릅니다.

docker ps는 컨테이너 프로세스가 잠깐 떠 있는지만 보여 주고, 스프링 부트가 실제로 기동 완료됐는지는 보장하지 않습니다. 이번 PR 설명대로 기동에 수십 초가 걸리고 Flyway/외부 연결까지 걸려 있으니, 지금 상태에선 시작 직후 실패해도 배포가 성공으로 끝날 수 있습니다. 컨테이너 HEALTHCHECKdocker inspect, 또는 애플리케이션 readiness 확인을 추가해서 타임아웃 시 워크플로우를 실패시키는 편이 안전합니다.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/deploy.yml around lines 73 - 74, Current deploy step uses
`docker ps | grep hard-click-app` which only shows a running process and can
falsely mark deployment success; replace it with a robust readiness check: wait
for the container `hard-click-app` to report healthy via its Docker HEALTHCHECK
(`docker inspect` reading `.State.Health.Status`) or poll the application's
readiness HTTP endpoint inside the container (e.g., curl
http://localhost:PORT/actuator/health/readiness), retry with a timeout, and fail
the job if it doesn't become healthy within the timeout so the workflow
accurately fails on startup or migration errors.

Comment thread docker-compose.yml
Comment on lines +6 to +10
environment:
MYSQL_DATABASE: Hard-Click
MYSQL_USER: Hard-Click
MYSQL_PASSWORD: Hard-Click
MYSQL_ROOT_PASSWORD: Hard-Click

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

로컬 Compose 자격 증명을 코드에 하드코딩하지 마세요.

MYSQL_PASSWORDMYSQL_ROOT_PASSWORD가 저장소에 평문으로 들어가 있습니다. 지금처럼 고정값을 커밋하면 재사용·유출 위험이 커지고, 이후 운영값과도 쉽게 혼동됩니다. 이 파일도 ${...} 또는 env_file 기반으로 바꿔서 비밀값을 저장소 밖으로 빼는 편이 안전합니다.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docker-compose.yml` around lines 6 - 10, Remove hardcoded credentials from
the docker-compose service environment block (MYSQL_DATABASE, MYSQL_USER,
MYSQL_PASSWORD, MYSQL_ROOT_PASSWORD) and switch to using environment variable
interpolation or an env_file; update the compose file to reference
${MYSQL_DATABASE}, ${MYSQL_USER}, ${MYSQL_PASSWORD}, ${MYSQL_ROOT_PASSWORD} (or
point to an env_file) and ensure local .env or .env.local is added to .gitignore
and contains the actual secret values for local development.

Comment thread Dockerfile
Comment on lines +1 to +4
FROM eclipse-temurin:17-jre-alpine
WORKDIR /app
COPY build/libs/*.jar app.jar
ENTRYPOINT ["java", "-jar", "app.jar"]

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

컨테이너를 root로 실행하고 있습니다.

현재 이미지가 기본 사용자(root)로 애플리케이션을 실행합니다. 애플리케이션 취약점이 발생했을 때 컨테이너 내부 권한이 과도하므로, 전용 비특권 사용자를 만든 뒤 USER로 전환하는 편이 안전합니다.

예시 수정
 FROM eclipse-temurin:17-jre-alpine
 WORKDIR /app
+RUN addgroup -S app && adduser -S app -G app
 COPY build/libs/*.jar app.jar
+RUN chown -R app:app /app
+USER app
 ENTRYPOINT ["java", "-jar", "app.jar"]
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
FROM eclipse-temurin:17-jre-alpine
WORKDIR /app
COPY build/libs/*.jar app.jar
ENTRYPOINT ["java", "-jar", "app.jar"]
FROM eclipse-temurin:17-jre-alpine
WORKDIR /app
RUN addgroup -S app && adduser -S app -G app
COPY build/libs/*.jar app.jar
RUN chown -R app:app /app
USER app
ENTRYPOINT ["java", "-jar", "app.jar"]
🧰 Tools
🪛 Trivy (0.69.3)

[error] 1-1: Image user should not be 'root'

Specify at least 1 USER command in Dockerfile with non-root user as argument

Rule: DS-0002

Learn more

(IaC/Dockerfile)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Dockerfile` around lines 1 - 4, The Dockerfile runs the app as root; create a
non-root user and switch to it before ENTRYPOINT: add steps after WORKDIR/COPY
to create a dedicated unprivileged user/group, chown the application files
(app.jar) and WORKDIR to that user, and then add USER to run the container as
that user so ENTRYPOINT ["java", "-jar", "app.jar"] executes without root
privileges; update Dockerfile symbols WORKDIR, COPY, app.jar, and ENTRYPOINT
accordingly.

Source: Linters/SAST tools

Comment on lines +8 to 11
CANCELED;

public static PaymentStatus from(String value) {
return PaymentStatus.valueOf(value);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

기존 CANCELLED 저장값과의 호환성이 깨집니다.

PaymentJpaEntitypayments.status를 문자열로 그대로 읽고, MyPaymentHistoryQueryAdapter는 그 값을 PaymentStatus.from(...)로 바로 enum 변환합니다. 그래서 상수만 CANCELED로 바꾸면 DB에 남아 있는 기존 CANCELLED 데이터는 여기서 IllegalArgumentException으로 깨집니다. 이 변경은 enum rename만으로 끝낼 게 아니라, 데이터 migration으로 값을 정규화하거나 from()에서 구표기를 한시적으로 호환해야 합니다.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@src/main/java/com/wanted/backend/domain/payment/domain/model/PaymentStatus.java`
around lines 8 - 11, PaymentStatus.from currently delegates to
PaymentStatus.valueOf and will break on legacy DB values like "CANCELLED";
update from(String value) in PaymentStatus to normalize and accept legacy
spellings (e.g., trim, uppercase, map "CANCELLED" -> "CANCELED") and return the
correct enum, and/or add a migration path for existing DB rows; check usages in
PaymentJpaEntity and MyPaymentHistoryQueryAdapter to ensure they keep calling
PaymentStatus.from(...) so the compatibility mapping handles old values.

Comment on lines +24 to +26
@Bean
public InfluxDBClient influxDBClient() {
return InfluxDBClientFactory.create(url, token.toCharArray(), org, bucket);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

InfluxDB 클라이언트가 항상 생성돼서 테스트/로컬 기동을 깨뜨릴 수 있습니다.

지금은 INFLUX_TOKEN이 없는 환경에서도 InfluxDBClient 빈을 무조건 만들게 됩니다. 그런데 src/test/resources/application-test.yaml에는 influx.* 오버라이드가 없어서, @SpringBootTest가 컨텍스트를 띄우는 순간 여기서 실패할 가능성이 큽니다. InfluxDB가 선택 인프라라면 @ConditionalOnProperty로 감싸거나, 테스트 프로필에서는 비활성화되도록 분리하는 편이 안전합니다.

수정 예시
 import com.influxdb.client.InfluxDBClient;
 import com.influxdb.client.InfluxDBClientFactory;
 import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;

 `@Configuration`
 public class InfluxDbConfig {
@@
     `@Bean`
+    `@ConditionalOnProperty`(prefix = "influx", name = "token")
     public InfluxDBClient influxDBClient() {
         return InfluxDBClientFactory.create(url, token.toCharArray(), org, bucket);
     }
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
@Bean
public InfluxDBClient influxDBClient() {
return InfluxDBClientFactory.create(url, token.toCharArray(), org, bucket);
`@Bean`
`@ConditionalOnProperty`(prefix = "influx", name = "token")
public InfluxDBClient influxDBClient() {
return InfluxDBClientFactory.create(url, token.toCharArray(), org, bucket);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/java/com/wanted/backend/global/config/InfluxDbConfig.java` around
lines 24 - 26, InfluxDbConfig의 influxDBClient 빈이 항상 생성되어 INFLUX_TOKEN이 없는 테스트/로컬
환경에서 실패할 수 있으니, InfluxDB가 선택적 인프라인 경우 빈 등록을 조건부로 변경하세요: InfluxDbConfig 또는
influxDBClient 메서드에 `@ConditionalOnProperty`(prefix="influx", name="enabled",
havingValue="true", matchIfMissing=false) 또는 프로파일 기반(`@Profile`("!test") 또는 별도
테스트용 구성) 어노테이션을 추가해 url/token/org/bucket 값이 없을 때 빈이 생성되지 않도록 하고, 테스트 환경에서는 해당 빈을
비활성화하거나 모킹된 대체 빈을 제공하도록 수정하세요.

Comment on lines +44 to +45
config:
import: optional:classpath:application-secret.yaml

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

application-secret.yaml import 경로가 의도와 안 맞습니다.

.gitignore에서는 루트의 application-secret.yaml을 숨기고 있는데, 여기서는 classpath:로 읽고 있습니다. 지금 형태면 로컬에서 루트에 둔 시크릿 파일은 로드되지 않고 optional: 때문에 조용히 넘어가서, 실제로는 필수 설정이 빠진 채 기동하게 됩니다. 루트 파일을 쓸 계획이면 optional:file:./application-secret.yaml로 바꾸거나, 반대로 classpath:를 유지할 거면 파일 위치를 src/main/resources로 고정해야 합니다.

수정 예시
  config:
-   import: optional:classpath:application-secret.yaml
+   import: optional:file:./application-secret.yaml
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
config:
import: optional:classpath:application-secret.yaml
config:
import: optional:file:./application-secret.yaml
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/resources/application.yaml` around lines 44 - 45, 현재
application.yaml의 config.import 항목이 optional:classpath:application-secret.yaml로
되어 있어 루트에 둔 application-secret.yaml을 로드하지 못합니다; config.import 설정을
optional:file:./application-secret.yaml로 변경하거나 실제로 리소스 경로에 파일을 두려면
application-secret.yaml을 src/main/resources로 이동하는 방식 중 하나로 고치세요 (참조 식별자:
config.import, application-secret.yaml).

@@ -1,4 +1,5 @@
/*!40101 SET @saved_cs_client = @@character_set_client */;
SET FOREIGN_KEY_CHECKS=0;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

FOREIGN_KEY_CHECKS는 이전 세션 값을 복원해야 합니다.

지금은 시작 시 0, 종료 시 1로 고정 복원해서 이 migration을 실행한 커넥션의 기존 세션 상태를 덮어씁니다. Flyway가 같은 커넥션을 이어서 쓰면 이후 migration의 FK 검증 동작까지 바뀔 수 있으니, 현재 값을 저장해뒀다가 마지막에 그 값으로 되돌리는 편이 안전합니다.

제안 diff
-SET FOREIGN_KEY_CHECKS=0;
+SET `@OLD_FOREIGN_KEY_CHECKS` = @@FOREIGN_KEY_CHECKS;
+SET FOREIGN_KEY_CHECKS=0;
...
-SET FOREIGN_KEY_CHECKS=1;
+SET FOREIGN_KEY_CHECKS=`@OLD_FOREIGN_KEY_CHECKS`;

Also applies to: 371-371

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/resources/db/migration/V1__baseline.sql` at line 1, 현재 migration이 세션
전역 변수 FOREIGN_KEY_CHECKS를 0으로 강제로 설정만 하고 복원하지 않아 같은 커넥션을 쓰는 이후 migration의 동작에
영향을 줄 수 있습니다; V1__baseline.sql에서 실행 전에 현재 값(@@FOREIGN_KEY_CHECKS)을 임시 변수에 저장하고
SET FOREIGN_KEY_CHECKS=0을 실행한 뒤 migration 끝에서 저장한 변수값으로 복원하도록 수정하세요 (즉, SELECT
@@FOREIGN_KEY_CHECKS INTO `@old_fk_checks`; SET FOREIGN_KEY_CHECKS=0; ... SET
FOREIGN_KEY_CHECKS=`@old_fk_checks`; 형식으로 FOREIGN_KEY_CHECKS 값을 저장·복원하도록 변경).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[FEAT] 배포 환경 구성 (Flyway / Redis / InfluxDB / docker-compose / CI-CD)

1 participant