[FEAT] 배포 환경 구성 (Flyway / Redis / InfluxDB / docker-compose / CI-CD)#322
[FEAT] 배포 환경 구성 (Flyway / Redis / InfluxDB / docker-compose / CI-CD)#322jongjunn wants to merge 1 commit into
Conversation
|
Warning
|
| 레이어 / 파일 | 설명 |
|---|---|
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 | PaymentStatus.java의 CANCELLED→CANCELED 변경 및 SecurityConfig의 roleHierarchy 추가는 배포 인프라와 직접 관련 없는 부분입니다. | PaymentStatus 열거형 변경과 SecurityConfig의 roleHierarchy는 별도 PR로 분리하거나 #312 요구사항과의 연관성을 명확히 문서화하세요. |
|
| Docstring Coverage | 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.
There was a problem hiding this comment.
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.bootGradle 플러그인 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
📒 Files selected for processing (14)
.github/workflows/ci.yml.github/workflows/deploy.yml.gitignoreDockerfilebuild.gradledocker-compose.ymlsrc/main/java/com/wanted/backend/domain/learning_activity/application/service/PlayableVideoProgressReader.javasrc/main/java/com/wanted/backend/domain/payment/domain/model/PaymentStatus.javasrc/main/java/com/wanted/backend/global/config/InfluxDbConfig.javasrc/main/java/com/wanted/backend/global/config/RedisConfig.javasrc/main/java/com/wanted/backend/global/config/SecurityConfig.javasrc/main/resources/application.yamlsrc/main/resources/db/migration/V1__baseline.sqlsrc/test/resources/application-test.yaml
| on: | ||
| pull_request: | ||
| branches: [ develop, main ] |
There was a problem hiding this comment.
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.
| 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가 실행되도록 만드세요.
| echo "=== Deploy complete ===" | ||
| docker ps | grep hard-click-app |
There was a problem hiding this comment.
배포 성공 판정이 너무 이릅니다.
docker ps는 컨테이너 프로세스가 잠깐 떠 있는지만 보여 주고, 스프링 부트가 실제로 기동 완료됐는지는 보장하지 않습니다. 이번 PR 설명대로 기동에 수십 초가 걸리고 Flyway/외부 연결까지 걸려 있으니, 지금 상태에선 시작 직후 실패해도 배포가 성공으로 끝날 수 있습니다. 컨테이너 HEALTHCHECK와 docker 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.
| environment: | ||
| MYSQL_DATABASE: Hard-Click | ||
| MYSQL_USER: Hard-Click | ||
| MYSQL_PASSWORD: Hard-Click | ||
| MYSQL_ROOT_PASSWORD: Hard-Click |
There was a problem hiding this comment.
로컬 Compose 자격 증명을 코드에 하드코딩하지 마세요.
MYSQL_PASSWORD와 MYSQL_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.
| FROM eclipse-temurin:17-jre-alpine | ||
| WORKDIR /app | ||
| COPY build/libs/*.jar app.jar | ||
| ENTRYPOINT ["java", "-jar", "app.jar"] |
There was a problem hiding this comment.
컨테이너를 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.
| 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
(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
| CANCELED; | ||
|
|
||
| public static PaymentStatus from(String value) { | ||
| return PaymentStatus.valueOf(value); |
There was a problem hiding this comment.
기존 CANCELLED 저장값과의 호환성이 깨집니다.
PaymentJpaEntity는 payments.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.
| @Bean | ||
| public InfluxDBClient influxDBClient() { | ||
| return InfluxDBClientFactory.create(url, token.toCharArray(), org, bucket); |
There was a problem hiding this comment.
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.
| @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 값이 없을 때 빈이 생성되지 않도록 하고, 테스트 환경에서는 해당 빈을
비활성화하거나 모킹된 대체 빈을 제공하도록 수정하세요.
| config: | ||
| import: optional:classpath:application-secret.yaml |
There was a problem hiding this comment.
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.
| 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; | |||
There was a problem hiding this comment.
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 값을 저장·복원하도록 변경).
📌 연관 이슈
📝 작업 내용
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.yamlgitignore 처리RedisConfig—@EnableCaching,RedisCacheManager,StringRedisTemplate빈 등록InfluxDbConfig—InfluxDBClient빈 등록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 명세)
🚨 주요 에러 코드 및 예외 (Exceptions)
💡 백엔드 리뷰 포인트 (Backend Review)
SET FOREIGN_KEY_CHECKS=0/1감싸기 — 기존 베이스라인 덤프 순서 문제 해결spring.config.import: optional:classpath:application-secret.yaml패턴으로 로컬 시크릿 분리@PreAuthorize로 위임 예정 — 현재는 공통 인증만 처리deploy.yml은 EC2 Secrets(HOST/KEY/ENV) 등록 후 정상 동작. 현재 GitHub Secrets 미등록 시 배포 단계 실패✅ 체크리스트
./gradlew build성공, 앱 32초 기동 확인)Summary by CodeRabbit
릴리스 노트
New Features
Chores