Skip to content

Conversation

@devmizz
Copy link
Contributor

@devmizz devmizz commented Dec 14, 2025

Summary by CodeRabbit

릴리스 노트

  • Chores
    • OCI 개발 및 프로덕션 환경을 위한 CI/CD 배포 파이프라인 추가
    • Oracle 데이터베이스 드라이버 및 보안 라이브러리 통합
    • Loki 기반 로깅 수집 인프라 구성
    • Docker Compose 설정 파일 추가 및 Oracle Wallet 마운트 구성
    • 날짜/시간 데이터 타입 변환기 추가

✏️ Tip: You can customize this high-level summary in your review settings.

@devmizz devmizz requested a review from eeun1225 December 14, 2025 08:09
@devmizz devmizz self-assigned this Dec 14, 2025
@devmizz devmizz added the patch Patch Version label Dec 14, 2025
@coderabbitai
Copy link

coderabbitai bot commented Dec 14, 2025

Walkthrough

Oracle 데이터베이스 JDBC 드라이버와 Loki 로깅 의존성을 추가하고, dev 및 prod 브랜치에 대한 OCI 배포 자동화 워크플로우를 구현했습니다. Docker Compose와 Dockerfile을 OCI 환경용으로 추가하고, JPA 날짜/시간 변환기를 정의하며, OCI 환경 특화 Spring 설정 파일을 작성했습니다.

Changes

Cohort / File(s) 변경 사항
GitHub Actions CI/CD 워크플로우
.github/workflows/yappu-oci-dev-cd.yaml, .github/workflows/yappu-oci-prod-cd.yaml
dev/prod 브랜치 푸시 시 OCI 배포를 자동화하는 완전한 워크플로우 추가: JDK 21 설정, Gradle 빌드(테스트 제외), OCI CLI 구성, NSG 규칙 관리, SCP로 아티팩트 업로드, Docker Compose 배포, Discord 알림
빌드 의존성
build.gradle.kts
Oracle JDBC 드라이버(ojdbc11), Oracle PKI 보안 라이브러리(oraclepki:23.5.0.24.07), Loki4j 로깅 어댑터(loki-logback-appender:2.0.1) 추가
Docker 구성 - Dev OCI
docker/docker-compose-dev-oci.yaml, docker/dockerfile-dev-oci
dev-oci 환경용 Docker Compose 서비스 및 Dockerfile 신규 추가: 이미지명 yappu-world-dev-oci, 포트 8080 매핑, Oracle 지갑 읽기 전용 볼륨 마운트
Docker 구성 - Prod OCI
docker/docker-compose-prod-oci.yaml, docker/dockerfile-prod-oci
prod-oci 환경용 Docker Compose 서비스 및 Dockerfile 신규 추가: 이미지명 yappu-world-prod-oci, 포트 8080 매핑, Oracle 지갑 읽기 전용 볼륨 마운트
Docker 볼륨 마운트
docker/docker-compose-dev.yaml, docker/docker-compose-prod.yaml
기존 dev/prod Compose 파일에 Oracle 지갑 읽기 전용 볼륨(/opt/oracle/wallet/wallet) 추가
JPA 날짜/시간 변환기
src/main/kotlin/co/yappuworld/global/persistence/converter/LocalDateStringConverter.kt, src/main/kotlin/co/yappuworld/global/persistence/converter/LocalTimeStringConverter.kt
LocalDate ↔ ISO_LOCAL_DATE 문자열, LocalTime ↔ "HH:mm:ss" 형식 변환 구현 및 자동 적용
Schedule Entity 수정
src/main/kotlin/co/yappuworld/schedule/infrastructure/entity/ScheduleEntity.kt
date, endDate 추상 속성 제거
Session Entity 수정
src/main/kotlin/co/yappuworld/schedule/infrastructure/entity/SessionEntity.kt
date, endDate, time, endTime 필드에 LocalDate/LocalTime ↔ 문자열 변환 어노테이션 추가
SignUp 로직 수정
src/main/kotlin/co/yappuworld/user/client/application/usecase/SignUpExecutor.kt
제출 흐름에서 lock 관리 제거(MySQL → Oracle 마이그레이션 관련)
OCI 환경 설정 파일
src/main/resources/application-dev-oci.yaml, src/main/resources/application-prod-oci.yaml
새로운 Spring 프로필용 OCI 환경 특화 설정: Oracle 데이터소스, JWT, FCM, Discord 웹훅, Sentry, 관리 엔드포인트
Spring 프로필 매핑
src/main/resources/application.yaml
기존 prod 프로필의 콘솔/sentry 로깅 매핑 제거, dev-ocidev-loki, prod-ociprod-loki 프로필 추가
Logback 로깅 설정
src/main/resources/logback-spring.xml
dev-loki, prod-loki Spring 프로필 추가: Loki4jAppender 통합, HTTP 엔드포인트, 환경별 라벨 및 메시지 패턴 정의

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20–25 minutes

  • 주의 필요 영역:
    • .github/workflows/yappu-oci-*.yaml 파일: 워크플로우 로직의 정확성, 특히 NSG 규칙 관리, SCP 업로드, Docker Compose 배포 순서 및 정리 단계 검증
    • ScheduleEntity.kt에서 date, endDate 추상 속성 제거: 서브클래스 및 관련 비즈니스 로직이 올바르게 동작하는지 확인
    • SignUpExecutor.kt의 lock 제거: 동시성 문제 또는 레이스 조건 유발 가능성 검토
    • OCI 환경 설정 파일: 환경 변수 이름, 보안 설정(Sentry DSN, JWT 시크릿) 정확성 확인
    • SessionEntity.kt의 변환기 어노테이션: 실제 데이터 검색 및 저장 시 변환 동작 검증

Possibly related PRs

Suggested reviewers

  • eeun1225

Poem

🐰 Oracle의 지갑을 매달고
Loki의 시선으로 로그를 노래하네,
OCI에 배포되는 워크플로우의 춤,
락을 내려놓고 흐르는 데이터의 물결,
변환기는 시간을 꿈꾼다. ✨

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Title check ⚠️ Warning PR 제목 'v1.2.3'은 변경사항의 핵심을 전혀 설명하지 않으며, 버전 번호만 제시하고 있습니다. OCI 배포, 데이터베이스 설정, 로깅 통합 등 주요 변경사항을 반영한 구체적인 제목으로 변경하세요. 예: 'Add OCI CI/CD workflows and Oracle database support' 또는 'Configure OCI deployment and Loki logging integration'
Docstring Coverage ⚠️ Warning Docstring coverage is 16.67% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (1 passed)
Check name Status Explanation
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 v1.2.3

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: 13

♻️ Duplicate comments (1)
src/main/kotlin/co/yappuworld/global/persistence/converter/LocalTimeStringConverter.kt (1)

8-9: autoApply = true 설정이 전체 애플리케이션에 영향을 미칩니다.

LocalDateStringConverter와 동일한 문제입니다. autoApply = true는 애플리케이션의 모든 LocalTime 필드에 이 컨버터를 자동으로 적용합니다. 특정 필드에만 문자열 변환이 필요하다면 autoApply = false로 변경하세요.

🧹 Nitpick comments (9)
docker/docker-compose-prod.yaml (1)

10-11: 호스트 경로 하드코딩(/opt/oracle/wallet)은 환경 이식성 리스크가 큽니다.
:ro는 좋습니다. 다만 운영/서버별 경로 차이로 기동 실패하기 쉬우니 환경변수로 경로를 받는 쪽을 권장합니다.

 services:
     app:
@@
         volumes:
-            - /opt/oracle/wallet:/wallet:ro
+            - ${ORACLE_WALLET_DIR:-/opt/oracle/wallet}:/wallet:ro
docker/docker-compose-dev.yaml (1)

10-11: Dev에서도 wallet 경로를 강제하면 로컬 실행이 깨질 수 있습니다.
로컬 개발자 머신에 /opt/oracle/wallet이 없을 가능성이 높아서, 기본값은 유지하되 env로 바꾸거나(dev-oci에서만 mount 등) 조건부로 가져가는 구성이 안전합니다.

         volumes:
-            - /opt/oracle/wallet:/wallet:ro
+            - ${ORACLE_WALLET_DIR:-/opt/oracle/wallet}:/wallet:ro
src/main/resources/application-dev-oci.yaml (1)

28-39: FCM private_key는 멀티라인/개행 이슈로 깨지기 쉬운 포인트입니다.
환경변수로 주입할 때 \n 치환이 필요한 형태인지(예: JSON 키 문자열) 케이스가 많아서, 실제 배포 환경 값 포맷을 확인하고 필요하면 애플리케이션에서 개행 복원 처리(or 파일 마운트)로 바꾸는 걸 권장합니다.

docker/dockerfile-dev-oci (1)

1-10: Docker 빌드 컨텍스트 전제가 강함(COPY 경로) + non-root 실행 권장
Line 7의 COPY build/libs/...는 “Docker build 컨텍스트 안에 build/libs가 존재”해야만 동작합니다. 현재 compose/workflow처럼 deployment/...로 아티팩트를 모아두는 방식이면 OK지만, 레포 루트에서 그대로 빌드하면 경로가 깨질 가능성이 큽니다. 또한 컨테이너가 root로 실행됩니다.

가능하면 아래처럼 (1) 컨텍스트/경로를 명확히 하거나, (2) non-root 유저로 실행을 권장합니다.

 FROM bellsoft/liberica-openjdk-alpine:21

 WORKDIR /app

 COPY build/libs/yappu-world-dev-oci.jar yappu-world-dev-oci.jar

+# (옵션) 최소 권한 실행
+RUN addgroup -S app && adduser -S app -G app
+USER app
+
 ENTRYPOINT ["java", "-Dspring.profiles.active=dev-oci", "-jar", "yappu-world-dev-oci.jar"]
docker/docker-compose-dev-oci.yaml (1)

1-11: 레포에서 바로 실행 시 빌드 실패 가능(컨텍스트/아티팩트 위치) + wallet 경로 파라미터화 권장
현재 context: .(Line 6) 전제에서는 컨텍스트 루트에 build/libs/...가 있어야 해서, “배포용으로 files를 모아둔 deployment 디렉토리”에서는 맞지만 레포 상태 그대로면 깨질 수 있습니다. 또한 /opt/oracle/wallet 하드코딩(Line 11)은 환경별 이식성이 낮습니다.

 services:
     app:
         image: yappu-world-dev-oci
         container_name: yappu-world-dev-oci
         build:
             context: .
             dockerfile: dockerfile-dev-oci
         ports:
             - '8080:8080'
+        restart: unless-stopped
         volumes:
-            - /opt/oracle/wallet:/wallet:ro
+            - ${ORACLE_WALLET_DIR:-/opt/oracle/wallet}:/wallet:ro
.github/workflows/yappu-oci-prod-cd.yaml (2)

63-84: curl | bash로 OCI CLI 설치는 공급망 리스크(버전 고정/검증 권장)
Line 81에서 설치 스크립트를 파이프로 실행합니다. 최소한 버전(또는 커밋) 고정/체크섬 검증/릴리즈 아카이브 사용 등으로 완화하는 게 좋습니다.

-                    curl -L https://raw.githubusercontent.com/oracle/oci-cli/master/scripts/install/install.sh | bash -s -- --accept-all-defaults
+                    # TODO: pin version / verify checksum (avoid curl|bash on master)
+                    curl -L https://raw.githubusercontent.com/oracle/oci-cli/master/scripts/install/install.sh | bash -s -- --accept-all-defaults

108-135: SSH 키 파일 생성(step) 중복 + docker-compose(v1) 의존은 서버 환경 확인 필요
Line 108-113에서 키 파일을 만들지만, scp/ssh 액션(Line 115+, 124+)은 key: ${{ secrets... }}로 직접 사용하고 있어 파일 생성 step이 사실상 중복입니다. 또한 원격 명령이 docker-compose(Line 131+)에 의존하므로, 서버가 Compose v2만 있으면 실패합니다(docker compose 필요).

.github/workflows/yappu-oci-dev-cd.yaml (2)

64-85: OCI CLI 설치 방식(curl|bash)과 docker-compose 의존은 서버/정책에 맞게 재검토 권장
prod workflow와 동일하게, Line 82 설치 방식은 공급망 리스크가 있고, 원격 배포 명령은 docker-compose(v1) 존재를 전제로 합니다.


109-136: SSH 키 파일 생성 step 중복 가능(정리 여지)
키 파일을 만들지만(Line 109-114), scp/ssh 액션은 시크릿을 직접 사용(Line 116+, 125+)합니다. 한쪽으로 통일하는 게 유지보수에 유리합니다.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6141af3 and 2aefde3.

📒 Files selected for processing (18)
  • .github/workflows/yappu-oci-dev-cd.yaml (1 hunks)
  • .github/workflows/yappu-oci-prod-cd.yaml (1 hunks)
  • build.gradle.kts (2 hunks)
  • docker/docker-compose-dev-oci.yaml (1 hunks)
  • docker/docker-compose-dev.yaml (1 hunks)
  • docker/docker-compose-prod-oci.yaml (1 hunks)
  • docker/docker-compose-prod.yaml (1 hunks)
  • docker/dockerfile-dev-oci (1 hunks)
  • docker/dockerfile-prod-oci (1 hunks)
  • src/main/kotlin/co/yappuworld/global/persistence/converter/LocalDateStringConverter.kt (1 hunks)
  • src/main/kotlin/co/yappuworld/global/persistence/converter/LocalTimeStringConverter.kt (1 hunks)
  • src/main/kotlin/co/yappuworld/schedule/infrastructure/entity/ScheduleEntity.kt (0 hunks)
  • src/main/kotlin/co/yappuworld/schedule/infrastructure/entity/SessionEntity.kt (2 hunks)
  • src/main/kotlin/co/yappuworld/user/client/application/usecase/SignUpExecutor.kt (1 hunks)
  • src/main/resources/application-dev-oci.yaml (1 hunks)
  • src/main/resources/application-prod-oci.yaml (1 hunks)
  • src/main/resources/application.yaml (1 hunks)
  • src/main/resources/logback-spring.xml (1 hunks)
💤 Files with no reviewable changes (1)
  • src/main/kotlin/co/yappuworld/schedule/infrastructure/entity/ScheduleEntity.kt
⏰ 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
🔇 Additional comments (3)
src/main/resources/application-dev-oci.yaml (1)

12-22: Oracle Wallet 사용 전제(TNS_ADMIN=/wallet)라면 “컨테이너 사용자 읽기 권한”까지 같이 보장해야 합니다.
Compose에서 :ro로 마운트하는 건 좋지만, 이미지가 non-root로 돌 경우 /wallet 접근 권한 때문에 커넥션이 실패할 수 있어(OCI에서 자주 발생) 권한/UID 전략을 같이 확인해 주세요.

src/main/resources/application.yaml (1)

6-12: prod/prod-oci 그룹에서 secret(및 기존 sentry-logging) 프로필이 빠진 게 의도인지 확인이 필요합니다.
현재 매핑만 보면 localsecret-local이 묶여 있고, prod/prod-oci는 로깅만 묶여서 운영 필수 설정이 누락될 여지가 있습니다.

docker/docker-compose-prod-oci.yaml (1)

5-11: 검증 결과, 현재 구성은 정상적으로 작동합니다. CI/CD 파이프라인에서 배포 시 파일들을 deployment/prod/ 디렉토리에 정리한 후 해당 디렉토리에서 docker-compose를 실행하므로, context: .dockerfile: dockerfile-prod-oci는 올바른 경로입니다.

Comment on lines +26 to +51
- name: Copy Secrets
uses: microsoft/variable-substitution@v1
with:
files:
./src/main/resources/application-dev-oci.yaml
env:
server.port: ${{ secrets.DEV_SERVER_PORT }}
spring.datasource.url: ${{ secrets.DEV_OCI_DB_URL }}
spring.datasource.username: ${{ secrets.DEV_OCI_DB_USERNAME }}
spring.datasource.password: ${{ secrets.DEV_OCI_DB_PASSWORD }}
jwt.secret_key: ${{ secrets.DEV_JWT_SECRET_KEY }}
jwt.access_token_expiration_times: ${{ secrets.DEV_ACCESS_TOKEN_EXPIRATION_TIMES }}
jwt.refresh_token_expiration_times: ${{ secrets.DEV_REFRESH_TOKEN_EXPIRATION_TIMES }}
fcm.private_key_id: ${{secrets.FCM_PRIVATE_KEY_ID}}
fcm.private_key: ${{secrets.FCM_PRIVATE_KEY}}
fcm.client_email: ${{secrets.FCM_CLIENT_EMAIL}}
fcm.client_id: ${{secrets.FCM_CLIENT_ID}}
fcm.client_x509_cert_url: ${{secrets.FCM_X509_CERT_URL}}
discord.webhook: ${{secrets.DISCORD_WEBHOOK_URL_IN_DEV}}
sentry.dsn: ${{secrets.SENTRY_DSN}}
admin.domain: ${{ secrets.DEV_ADMIN_DOMAIN }}
kakao.rest_api_key: ${{ secrets.KAKAO_REST_API_KEY }}

- name: Build with Gradle Wrapper
run: ./gradlew clean build -x test -Dspring.profiles.active=dev-oci

Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# First, let's examine the workflow file to understand the full context
cat -n .github/workflows/yappu-oci-dev-cd.yaml | head -60

Repository: YAPP-admin/yappu-world-server

Length of output: 3033


🏁 Script executed:

# Check if the application-dev-oci.yaml file exists and examine its structure
if [ -f ./src/main/resources/application-dev-oci.yaml ]; then
    echo "=== application-dev-oci.yaml content ==="
    cat ./src/main/resources/application-dev-oci.yaml
else
    echo "File not found"
fi

Repository: YAPP-admin/yappu-world-server

Length of output: 1918


🏁 Script executed:

# Search for how fcm.private_key is used in the codebase
rg "fcm\.private_key" --type yaml --type java --type properties -A 3 -B 1

Repository: YAPP-admin/yappu-world-server

Length of output: 107


🏁 Script executed:

# Look for gradle build configuration to understand what gets included in JAR
fd "build.gradle" | head -5 | xargs cat | head -100

Repository: YAPP-admin/yappu-world-server

Length of output: 3272


🌐 Web query:

microsoft variable-substitution action multiline secrets YAML parsing

💡 Result:

Short answer — microsoft/variable-substitution reads replacement values from environment variables (set in the workflow file) and those values are subject to YAML/workflow interpolation. Multiline secrets with embedded newlines often get mangled by YAML or by how you inject them into env:, so the action doesn't receive the original newline-separated content. Use one of these workarounds:

  • Store the secret base64-encoded, pass the base64 string as the env value, and in a run step decode it back to a file (echo "$SECRET_B64" | base64 --decode > file). [1][2]
  • Don’t put the multiline secret directly into env: for the action; instead in a run: step read the secret and write it with a here-doc or printf to preserve newlines (e.g. printf '%s' "$SECRET" > file). [1][3]
  • If you must use variable-substitution for YAML/JSON keys, convert multiline content to a single-line (base64 or JSON-escaped) token and reverse it at runtime as above.

References:

  • Example & discussion of multiline secrets/YAML issues in GitHub Actions. [1]
  • microsoft/variable-substitution behavior (uses env variables; YAML/JSON processing). [2]
  • StackOverflow examples showing writing PEM/multiline secrets from GitHub Actions using run steps (here-doc / printf / decoding). [3]

If you want, tell me which approach you prefer and paste a minimal example of your workflow + secret and I’ll give the exact YAML to use.


variable-substitution 액션의 멀티라인 시크릿 처리로 인한 YAML 파싱 오류 및 시크릿 노출 위험

Line 26-50의 흐름에서 다음 두 가지 문제가 확인됩니다:

  1. 멀티라인 시크릿 손상: fcm.private_key는 Firebase 서비스 계정 개인키(JSON/PEM 형식의 멀티라인 콘텐츠)인데, env: 변수를 통해 variable-substitution@v1로 전달되면서 YAML 파싱 과정에서 줄바꿈이 손상되어 키 값이 깨질 수 있습니다.

  2. 시크릿이 JAR에 포함됨: ./src/main/resources/application-dev-oci.yaml에 치환된 시크릿은 Spring Boot 빌드 과정에서 JAR의 resources 디렉토리에 그대로 포함되므로, 배포된 JAR 파일에 모든 시크릿이 평문으로 저장됩니다.

멀티라인 시크릿(특히 개인키)은 base64 인코딩 후 런타임에 디코딩하거나, run: 스텝에서 here-doc/printf를 사용해 직접 파일로 작성하는 방식으로 처리해야 합니다.

🤖 Prompt for AI Agents
.github/workflows/yappu-oci-dev-cd.yaml lines 26-51: variable-substitution is
breaking multiline secrets (fcm.private_key) and causes secrets to be embedded
into the built JAR; stop passing multiline keys through env:
variable-substitution and instead pass a base64-encoded secret or the raw secret
only at runtime and write it to a file in a run: step (use here-doc or printf to
decode/base64 -d into ./src/main/resources or an external file consumed at
startup), remove fcm.private_key (and other multiline secrets) from the env
mapping so they are not substituted into application-dev-oci.yaml at build time,
and adjust the build step to use runtime files or external secret manager (OCI
Vault) so secrets are not baked into the JAR.

Comment on lines +26 to +50
- name: Copy Secrets
uses: microsoft/variable-substitution@v1
with:
files:
./src/main/resources/application-prod-oci.yaml
env:
spring.datasource.url: ${{ secrets.PROD_OCI_DB_URL }}
spring.datasource.username: ${{ secrets.PROD_OCI_DB_USERNAME }}
spring.datasource.password: ${{ secrets.PROD_OCI_DB_PASSWORD }}
jwt.secret_key: ${{ secrets.PROD_JWT_SECRET_KEY }}
jwt.access_token_expiration_times: ${{ secrets.PROD_ACCESS_TOKEN_EXPIRATION_TIMES }}
jwt.refresh_token_expiration_times: ${{ secrets.PROD_REFRESH_TOKEN_EXPIRATION_TIMES }}
fcm.private_key_id: ${{secrets.FCM_PRIVATE_KEY_ID}}
fcm.private_key: ${{secrets.FCM_PRIVATE_KEY}}
fcm.client_email: ${{secrets.FCM_CLIENT_EMAIL}}
fcm.client_id: ${{secrets.FCM_CLIENT_ID}}
fcm.client_x509_cert_url: ${{secrets.FCM_X509_CERT_URL}}
discord.webhook: ${{secrets.DISCORD_WEBHOOK_URL_IN_PROD}}
sentry.dsn: ${{secrets.SENTRY_DSN}}
admin.domain: ${{ secrets.PROD_ADMIN_DOMAIN }}
kakao.rest_api_key: ${{ secrets.KAKAO_REST_API_KEY }}

- name: Build with Gradle Wrapper
run: ./gradlew clean build -x test -Dspring.profiles.active=prod-oci

Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

빌드 타임에 시크릿을 리소스에 주입 → JAR에 시크릿 포함(유출면적 확대) 가능
application-prod-oci.yaml에 시크릿을 치환(Line 26-47)한 뒤 빌드(Line 49)를 하면, 최종 JAR에 시크릿이 포함되는 형태가 됩니다(의도라면 OK지만, 아티팩트/서버 파일 접근만으로도 유출 가능). 운영은 보통 “런타임 환경변수/외부 시크릿 매니저”로 주입하는 쪽이 안전합니다.

가능한 대안:

  • 빌드 단계 치환 제거 + 컨테이너 런타임에 env로 주입(설정은 ${...} 유지)
  • 또는 OCI Vault/Parameter Store류로 런타임 fetch

Comment on lines +6 to +10
# 빌드된 JAR 파일 복사
COPY build/libs/yappu-world-prod-oci.jar yappu-world-prod-oci.jar

# prod-oci 프로파일로 애플리케이션 실행
ENTRYPOINT ["java", "-Dspring.profiles.active=prod-oci", "-jar", "yappu-world-prod-oci.jar"] No newline at end of file
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# First, find the build configuration file
fd -t f "build.gradle.kts" -o "build.gradle"

Repository: YAPP-admin/yappu-world-server

Length of output: 216


🏁 Script executed:

# Also check for the Dockerfile in question
fd -t f "dockerfile-prod-oci"

Repository: YAPP-admin/yappu-world-server

Length of output: 99


🏁 Script executed:

# Search for profile-related configuration in Gradle
rg "prod-oci" --type kotlin --type groovy -i

Repository: YAPP-admin/yappu-world-server

Length of output: 55


🏁 Script executed:

# Find gradle configuration files correctly
fd "build.gradle" -t f

Repository: YAPP-admin/yappu-world-server

Length of output: 89


🏁 Script executed:

# Read the Dockerfile in question
cat -n docker/dockerfile-prod-oci

Repository: YAPP-admin/yappu-world-server

Length of output: 418


🏁 Script executed:

# Search for prod-oci more broadly across all files
rg "prod-oci" -i --max-count 20

Repository: YAPP-admin/yappu-world-server

Length of output: 640


🏁 Script executed:

# Read the build.gradle.kts to check JAR naming configuration
cat -n build.gradle.kts

Repository: YAPP-admin/yappu-world-server

Length of output: 3972


🏁 Script executed:

# Check application.yaml for profile configuration
cat -n src/main/resources/application.yaml | head -50

Repository: YAPP-admin/yappu-world-server

Length of output: 470


🏁 Script executed:

# Search for CI/build configuration files (GitHub Actions, GitLab CI, etc.)
fd -t f "\\.yml$|\\.yaml$" | grep -i "workflow\|ci\|build\|action" | head -20

Repository: YAPP-admin/yappu-world-server

Length of output: 232


🏁 Script executed:

# Check for CI configuration in common locations
ls -la .github/workflows/ 2>/dev/null || echo "No .github/workflows found"

Repository: YAPP-admin/yappu-world-server

Length of output: 543


🏁 Script executed:

# Look for other Dockerfiles to see if they have similar issues
fd -t f "dockerfile" -i

Repository: YAPP-admin/yappu-world-server

Length of output: 170


🏁 Script executed:

# Check the prod-oci CD workflow
cat -n .github/workflows/yappu-oci-prod-cd.yaml

Repository: YAPP-admin/yappu-world-server

Length of output: 9157


🏁 Script executed:

# Check other Dockerfiles for similar patterns
for file in docker/dockerfile-dev docker/dockerfile-prod docker/dockerfile-dev-oci; do
  echo "=== $file ==="
  cat -n "$file"
  echo ""
done

Repository: YAPP-admin/yappu-world-server

Length of output: 1156


CI에서 -Dspring.profiles.active=prod-oci을 올바르게 주입하고 있어 현재 빌드는 정상이지만, JAR 파일명 강결합 문제와 non-root 실행 구성이 개선 필요합니다.

build.gradle.kts에서 JAR 이름이 System.getProperty("spring.profiles.active")에 의존하도록 구성되어 있고(라인 92-93), 현재 CI 워크플로우에서는 빌드 시 -Dspring.profiles.active=prod-oci을 올바르게 전달하고 있습니다. 그러나 이는 빌드 파이프라인이 정확하게 구성되어야만 작동하는 구조이므로, CI 설정 변경 시 깨질 수 있는 잠재적 위험이 있습니다. 추가로 모든 프로덕션 Dockerfile(dev-oci, prod-oci)에서 non-root 사용자로 실행하는 보안 설정이 누락되어 있습니다.

 FROM bellsoft/liberica-openjdk-alpine:21
 
 WORKDIR /app
 
 COPY build/libs/yappu-world-prod-oci.jar yappu-world-prod-oci.jar
+
+# non-root 사용자로 실행 (보안 권장)
+RUN addgroup -S app && adduser -S app -G app
+USER app
 
 ENTRYPOINT ["java", "-Dspring.profiles.active=prod-oci", "-jar", "yappu-world-prod-oci.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
# 빌드된 JAR 파일 복사
COPY build/libs/yappu-world-prod-oci.jar yappu-world-prod-oci.jar
# prod-oci 프로파일로 애플리케이션 실행
ENTRYPOINT ["java", "-Dspring.profiles.active=prod-oci", "-jar", "yappu-world-prod-oci.jar"]
FROM bellsoft/liberica-openjdk-alpine:21
WORKDIR /app
COPY build/libs/yappu-world-prod-oci.jar yappu-world-prod-oci.jar
# non-root 사용자로 실행 (보안 권장)
RUN addgroup -S app && adduser -S app -G app
USER app
ENTRYPOINT ["java", "-Dspring.profiles.active=prod-oci", "-jar", "yappu-world-prod-oci.jar"]
🤖 Prompt for AI Agents
docker/dockerfile-prod-oci lines 6-10: the Dockerfile hard-couples the JAR
filename to a build-time Spring profile and runs as root; change it to not
depend on System.getProperty("spring.profiles.active") at build time by using a
stable artifact name (or accept an ARG/JAR_NAME passed from CI) and copy that
stable name (or use a wildcard/copy step that picks up the built jar) instead of
embedding the profile into the filename, and change the ENTRYPOINT to set the
Spring profile via JVM property from an environment variable (e.g.,
-Dspring.profiles.active=${SPRING_PROFILES_ACTIVE:-prod-oci}) so the runtime
profile is injected by the container, and add a non-root user creation and
switch (create a user/group, chown the app directory and files, and add a USER
directive) so the container runs with non-root privileges for improved security.

Comment on lines +8 to +9
@Converter(autoApply = true)
class LocalDateStringConverter : AttributeConverter<LocalDate, String> {
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

🏁 Script executed:

#!/bin/bash
# Find and inspect LocalDateStringConverter.kt
find . -name "LocalDateStringConverter.kt" -type f

Repository: YAPP-admin/yappu-world-server

Length of output: 161


🏁 Script executed:

#!/bin/bash
# Search for @Entity classes with LocalDate fields
rg -t kotlin "@Entity|data class|class.*:" -A 30 | rg -B 5 "LocalDate" | head -100

Repository: YAPP-admin/yappu-world-server

Length of output: 11781


🏁 Script executed:

#!/bin/bash
# Find all LocalDate field declarations in Kotlin entity files
rg -t kotlin "LocalDate" -B 3 -A 1 | head -150

Repository: YAPP-admin/yappu-world-server

Length of output: 16666


🏁 Script executed:

#!/bin/bash
# Read the LocalDateStringConverter file
cat -n "src/main/kotlin/co/yappuworld/global/persistence/converter/LocalDateStringConverter.kt"

Repository: YAPP-admin/yappu-world-server

Length of output: 795


🏁 Script executed:

#!/bin/bash
# Find all @Entity classes in the codebase
fd -e kt -x rg -l "@Entity|@Table" {} \; | head -20

Repository: YAPP-admin/yappu-world-server

Length of output: 1148


🏁 Script executed:

#!/bin/bash
# Search for entity class definitions with LocalDate fields
rg -t kotlin -B 5 "^\s*(var|val)\s+\w+:\s*LocalDate\?" | head -200

Repository: YAPP-admin/yappu-world-server

Length of output: 6859


🏁 Script executed:

#!/bin/bash
# Read SessionEntity to see LocalDate fields
cat -n "src/main/kotlin/co/yappuworld/schedule/infrastructure/entity/SessionEntity.kt" | head -50

Repository: YAPP-admin/yappu-world-server

Length of output: 2473


🏁 Script executed:

#!/bin/bash
# Read GenerationEntity to see LocalDate fields
cat -n "src/main/kotlin/co/yappuworld/operation/domain/GenerationEntity.kt"

Repository: YAPP-admin/yappu-world-server

Length of output: 2204


🏁 Script executed:

#!/bin/bash
# Read ScheduleEntity to see LocalDate fields
cat -n "src/main/kotlin/co/yappuworld/schedule/infrastructure/entity/ScheduleEntity.kt" | head -50

Repository: YAPP-admin/yappu-world-server

Length of output: 2195


🏁 Script executed:

#!/bin/bash
# Search for all @Entity classes and check for LocalDate fields
rg -t kotlin "@Entity|@Table" -A 30 | grep -E "(@Entity|@Table|LocalDate)" | head -80

Repository: YAPP-admin/yappu-world-server

Length of output: 4127


🏁 Script executed:

#!/bin/bash
# Check all entity files for LocalDate fields and their @Convert usage
for entity in \
  "src/main/kotlin/co/yappuworld/user/infrastructure/entity/ActivityUnitEntity.kt" \
  "src/main/kotlin/co/yappuworld/operation/domain/GenerationEntity.kt" \
  "src/main/kotlin/co/yappuworld/schedule/infrastructure/entity/LatePassEntity.kt"
do
  if [ -f "$entity" ]; then
    echo "=== $entity ==="
    grep -n "LocalDate\|@Convert" "$entity" | head -20
    echo ""
  fi
done

Repository: YAPP-admin/yappu-world-server

Length of output: 411


🏁 Script executed:

#!/bin/bash
# Count explicit @Convert annotations vs implicit autoApply usage for LocalDate
echo "=== Entities with LocalDate fields ==="
rg -t kotlin "@Entity" -A 40 | grep -E "@Entity|LocalDate|@Convert.*LocalDate" | head -100

Repository: YAPP-admin/yappu-world-server

Length of output: 3445


🏁 Script executed:

#!/bin/bash
# Look for database migration files that define the schema
find . -name "*migration*" -o -name "*sql" | grep -E "(migration|schema)" | head -20

Repository: YAPP-admin/yappu-world-server

Length of output: 104


🏁 Script executed:

#!/bin/bash
# Check if there's a schema.sql or similar file
find . -type f \( -name "*.sql" -o -name "schema*" \) | head -10

Repository: YAPP-admin/yappu-world-server

Length of output: 134


🏁 Script executed:

#!/bin/bash
# Check git history for comments about LocalDateStringConverter
git log --oneline --all -- "*LocalDateStringConverter*" 2>/dev/null | head -10

Repository: YAPP-admin/yappu-world-server

Length of output: 114


🏁 Script executed:

#!/bin/bash
# Look at the full GenerationEntity to see if there are any Convert annotations we missed
cat "src/main/kotlin/co/yappuworld/operation/domain/GenerationEntity.kt"

Repository: YAPP-admin/yappu-world-server

Length of output: 1700


🏁 Script executed:

#!/bin/bash
# Read the schema.sql file
cat "src/main/resources/schema.sql" | head -200

Repository: YAPP-admin/yappu-world-server

Length of output: 3769


🏁 Script executed:

#!/bin/bash
# Search for "generations" table definition in schema
grep -n -A 10 "generations\|CREATE TABLE" "src/main/resources/schema.sql" | head -100

Repository: YAPP-admin/yappu-world-server

Length of output: 2879


🏁 Script executed:

#!/bin/bash
# Check if the date columns in schema are truly DATE type and confirm the converter creates string mismatch
echo "=== Schema column types for date fields ==="
grep -E "(date|end_date|start_date)" "src/main/resources/schema.sql" | grep -v "created_at\|updated_at"

echo ""
echo "=== LocalDateStringConverter behavior ==="
echo "Converts LocalDate to: String (VARCHAR in SQL)"
echo "Converts String back to: LocalDate"

echo ""
echo "=== SessionEntity explicit @Convert usage ==="
grep -B 2 "@get:Convert.*LocalDateStringConverter" "src/main/kotlin/co/yappuworld/schedule/infrastructure/entity/SessionEntity.kt"

echo ""
echo "=== GenerationEntity implicit usage (no @Convert) ==="
echo "GenerationEntity.startDate and .endDate will use autoApply = true"
echo "These fields will be converted to STRING but schema defines DATE type"

Repository: YAPP-admin/yappu-world-server

Length of output: 912


autoApply = true 설정이 모든 LocalDate 필드에 적용되며, 데이터베이스 스키마와의 타입 불일치를 초래합니다.

데이터베이스 스키마는 schedules 테이블의 date, end_date 컬럼과 generations 테이블의 start_date, end_date 컬럼을 모두 DATE 타입으로 정의하고 있습니다. 그러나 LocalDateStringConverterLocalDate를 문자열로 변환하므로, 실제 저장되는 데이터는 VARCHAR 형식입니다.

SessionEntity가 이를 인식하여 명시적으로 @Convert를 적용하는 반면, GenerationEntity@Convert 어노테이션이 없어서 autoApply = true에 의해 자동으로 컨버터가 적용됩니다. 이는 스키마 정의와 실제 변환 동작 사이의 불일치를 만듭니다.

컨버터의 의도가 모든 LocalDate 필드를 문자열로 저장하는 것이라면, autoApply = false로 변경하고 필요한 필드에만 @Convert를 명시적으로 적용하거나, 스키마의 해당 컬럼들을 VARCHAR로 정정해야 합니다.

🤖 Prompt for AI Agents
In
src/main/kotlin/co/yappuworld/global/persistence/converter/LocalDateStringConverter.kt
around lines 8-9, the converter is declared with autoApply = true which forces
all LocalDate fields to be converted to String (VARCHAR) causing a mismatch with
DB columns defined as DATE; change the converter to autoApply = false and apply
@Convert explicitly only on entities/fields that should be stored as VARCHAR, or
alternatively update the DB schema to use VARCHAR for all columns intended to
store the converted strings so converter behavior matches the schema.

Comment on lines 32 to 38
fun submit(application: SignUpApplicationEntity) {
executeWithLock(application.applicantEmail) {
checkSubmitApplicationAvailability(application)
signUpApplicationCommandService.submit(application)
}
// TODO: MySQL -> Oracle 변경하면서 lock 기능 쓰기 어려운 상황이고 해결 필요
// executeWithLock(application.applicantEmail) {
checkSubmitApplicationAvailability(application)
signUpApplicationCommandService.submit(application)
// }
}
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

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== SignUpApplicationCommandService.submit / existsPendingApplication ==="
find . -type f -name "*.kt" | xargs rg -l "SignUpApplicationCommandService|existsPendingApplication" | head -20

echo
echo "=== Search for SignUpApplicationCommandService class ==="
rg -n --type=kotlin "class\s+SignUpApplicationCommandService" -A 20

echo
echo "=== Search for existsPendingApplication function ==="
rg -n --type=kotlin "fun\s+existsPendingApplication" -A 10

echo
echo "=== Search for submit function in command service ==="
rg -n --type=kotlin "fun\s+submit\s*\(" -B 3 -A 15

echo
echo "=== Search for executeWithLock implementation ==="
rg -n --type=kotlin "fun\s+executeWithLock|executeWithLock\s*\(" -B 2 -A 10

echo
echo "=== Search for schema/migration files ==="
find . -type f \( -name "*.sql" -o -name "*migration*" -o -name "*schema*" \) | grep -i "sign\|application\|user" | head -20

Repository: YAPP-admin/yappu-world-server

Length of output: 13736


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== Search for migration/schema files ==="
find . -type f \( -name "*.sql" -o -name "*migration*" -o -name "*schema*" -o -name "*V*.sql" -o -name "*R*.sql" \) 2>/dev/null | head -30

echo
echo "=== Full SignUpApplicationCommandService implementation ==="
cat ./src/main/kotlin/co/yappuworld/user/infrastructure/SignUpApplicationCommandService.kt

echo
echo "=== Full SignUpExecutor lock/submission logic ==="
sed -n '1,150p' ./src/main/kotlin/co/yappuworld/user/client/application/usecase/SignUpExecutor.kt

echo
echo "=== Search for getLock/releaseLock in repository ==="
rg -n --type=kotlin "getLock|releaseLock" -B 2 -A 8

echo
echo "=== Search for DB migration/flyway config ==="
find . -type d -name "db" -o -name "migration" -o -name "flyway" 2>/dev/null | head -10

Repository: YAPP-admin/yappu-world-server

Length of output: 16803


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== schema.sql content ==="
cat ./src/main/resources/schema.sql

echo
echo "=== Search for UNIQUE/unique index in schema.sql ==="
grep -i "unique\|constraint\|index" ./src/main/resources/schema.sql || echo "No matches found"

Repository: YAPP-admin/yappu-world-server

Length of output: 3867


동시성에서 중복 신청서 생성 위험(락 제거로 인한 TOCTOU 발생)

Line 35~37의 checkSubmitApplicationAvailability() (읽기) → submit() (쓰기) 흐름에서 동일 applicantEmail로 동시 요청이 들어올 경우 둘 다 체크를 통과하여 pending 신청서가 중복 생성될 수 있습니다. 이전 executeWithLock()이 이를 방지했으나, 현재는 주석 처리되어 있습니다.

현황 검증 결과:

  • DB 스키마에 applicantEmail + status 조합의 유니크 제약 없음 (schema.sql 확인)
  • getLock(), releaseLock()executeWithLock() 메서드는 여전히 구현되어 있음
  • 대조: signUp() 메서드는 executeWithLockReturning()으로 보호됨 (line 45)
  • 테스트 코드는 submit()에서도 락 사용을 가정함 (SignUpFacadeUnitTest.kt line 46)

필수 해결 방안 (하나 이상):

  1. 즉시 재적용: submit()executeWithLock() 복구 (Oracle에서 작동 확인된 메커니즘)
  2. DB 제약 추가: pending 상태에 대한 유니크 인덱스 도입 + 제약 위반 시 예외 매핑
  3. 원자적 처리: MERGE 문이나 비관적 락(SELECT ... FOR UPDATE)으로 원자성 보장

TODO 주석(line 33)에는 구체적인 이슈 링크, 일정, 선택된 대안을 명시하고, 현재 lock 코드가 주석으로 남아 있어 운영 중 실수로 누락될 수 있으니 임시 상태를 명확히 하기 바랍니다.

🤖 Prompt for AI Agents
In
src/main/kotlin/co/yappuworld/user/client/application/usecase/SignUpExecutor.kt
around lines 32 to 38, the submit() flow currently performs
checkSubmitApplicationAvailability() (read) followed by
signUpApplicationCommandService.submit() (write) without the previously used
executeWithLock(), causing a TOCTOU race that allows duplicate pending
applications for the same applicantEmail; fix by one of: (A) restore
executeWithLock() around the check+submit (verify Oracle-compatible
implementation and re-enable getLock()/releaseLock()/executeWithLock() usage),
or (B) add a DB-level uniqueness constraint on (applicantEmail,
status='PENDING') and map constraint-violation to a clear domain exception, or
(C) make the operation atomic at DB level (MERGE or SELECT ... FOR UPDATE / a
transactional upsert) to prevent concurrent inserts; additionally update the
TODO comment at line 33 to include a concrete issue/link, chosen mitigation
plan, and timeline so the temporary comment isn’t missed in production.

Comment on lines +52 to +61
management:
endpoints:
enabled-by-default: true
web:
exposure:
include: health, info, prometheus
endpoint:
health:
show-details: always

Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# First, let's explore the repository structure
git ls-files | head -20

Repository: YAPP-admin/yappu-world-server

Length of output: 678


🏁 Script executed:

# Search for Spring Security configurations
fd -e "java" -e "properties" -e "yaml" -e "yml" | xargs grep -l "SecurityConfig\|@EnableWebSecurity\|HttpSecurity\|SecurityFilterChain" 2>/dev/null | head -20

Repository: YAPP-admin/yappu-world-server

Length of output: 55


🏁 Script executed:

# Search for other application yaml/properties files to understand the configuration structure
fd -e "yaml" -e "yml" -e "properties" | grep -E "(application|config)" | sort

Repository: YAPP-admin/yappu-world-server

Length of output: 380


🏁 Script executed:

# Check for ingress or networking configuration files
fd -e "yaml" -e "yml" | xargs grep -l "ingress\|Ingress" 2>/dev/null

Repository: YAPP-admin/yappu-world-server

Length of output: 57


🏁 Script executed:

# Check the specific file and surrounding context
cat -n src/main/resources/application-dev-oci.yaml | head -80

Repository: YAPP-admin/yappu-world-server

Length of output: 2374


🏁 Script executed:

# Search for Java configuration files that might contain security settings
fd -e "java" | xargs grep -l "actuator\|health\|endpoint\|SecurityConfig" 2>/dev/null | head -20

Repository: YAPP-admin/yappu-world-server

Length of output: 55


🏁 Script executed:

# Check the prod-oci configuration for comparison
cat -n src/main/resources/application-prod-oci.yaml | grep -A 10 -B 2 "management:"

Repository: YAPP-admin/yappu-world-server

Length of output: 430


🏁 Script executed:

# Search for Spring Security dependencies or config
cat -n build.gradle.kts | grep -i "security\|actuator" -A 2 -B 2

Repository: YAPP-admin/yappu-world-server

Length of output: 939


🏁 Script executed:

# Check for any actuator-specific security configuration in Java code
fd -e "java" | xargs grep -l "actuator\|health.*endpoint\|AuthenticationFilter" 2>/dev/null | head -10

Repository: YAPP-admin/yappu-world-server

Length of output: 55


🏁 Script executed:

# Check if there's any configuration for actuator security in application.yaml or other base configs
cat -n src/main/resources/application.yaml | grep -A 10 -B 2 "management:"

Repository: YAPP-admin/yappu-world-server

Length of output: 55


🏁 Script executed:

# Search for any SecurityFilterChain or WebSecurityConfigurerAdapter configuration
find src -name "*.java" -type f | xargs grep -l "SecurityFilterChain\|WebSecurityConfigurerAdapter\|HttpSecurity" 2>/dev/null

Repository: YAPP-admin/yappu-world-server

Length of output: 57


🏁 Script executed:

# Let's check if application.yaml exists and has management config
ls -la src/main/resources/application*.yaml

Repository: YAPP-admin/yappu-world-server

Length of output: 704


🏁 Script executed:

# Check if there's any base configuration
cat src/main/resources/application.yaml 2>/dev/null || echo "File does not exist"

Repository: YAPP-admin/yappu-world-server

Length of output: 386


🏁 Script executed:

# Look for any deployment/configuration documentation that might explain the OCI setup
find . -name "*.md" -o -name "*.txt" | xargs grep -l "dev-oci\|security\|actuator" 2>/dev/null | head -5

Repository: YAPP-admin/yappu-world-server

Length of output: 55


dev-oci 환경의 health.show-details: always 설정은 접근 제어가 필수입니다.

prod-oci는 show-details: when-authorized로 설정되어 인증된 사용자만 건강 상태 정보에 접근할 수 있지만, dev-oci는 show-details: always로 설정되어 인증 없이 데이터베이스 연결 상태, 메모리, 디스크 공간 등의 민감한 시스템 정보를 노출합니다. OCI는 외부 클라우드 인프라이므로, 최소한 다음 중 하나를 확인하세요:

  • ingress 또는 네트워크 정책을 통한 제한
  • Spring Security가 actuator 엔드포인트를 실제로 보호하고 있는지 확인
🤖 Prompt for AI Agents
In src/main/resources/application-dev-oci.yaml around lines 52 to 61, the
actuator health.show-details is set to "always", which exposes sensitive health
details in the dev-oci environment; change this to a safer setting (e.g.,
"when-authorized") or restrict access via network/ingress rules and ensure
Spring Security actually protects actuator endpoints (verify security
configuration grants actuator endpoints to authenticated/authorized roles only);
update the YAML to use when-authorized and/or add notes to the
deployment/network policy and Spring Security config to enforce access controls.

Comment on lines +12 to +22
datasource:
driver-class-name: oracle.jdbc.OracleDriver
url: ${PROD_OCI_DB_URL}?TNS_ADMIN=/wallet
username: ${PROD_OCI_DB_USERNAME}
password: ${PROD_OCI_DB_PASSWORD}
hikari:
maximum-pool-size: 3
minimum-idle: 1
connection-timeout: 30000
idle-timeout: 600000

Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

(중요) workflow 치환 방식이면 ?TNS_ADMIN=/wallet가 유실될 수 있음
Line 14에 wallet 파라미터를 붙여두셨는데, GitHub Actions에서 spring.datasource.url을 통째로 치환하면 해당 suffix가 사라질 수 있어 OCI wallet 기반 TCPS 접속이 실패할 가능성이 큽니다.

권장 방향(택1):

  • PROD_OCI_DB_URL 시크릿 자체에 ?TNS_ADMIN=/wallet까지 포함(명시적으로 “완성 URL”로 관리)
  • 또는 빌드 타임 치환을 제거하고, 런타임에 PROD_OCI_DB_URL 환경변수로만 주입(설정 파일은 ${...} 유지)
🤖 Prompt for AI Agents
src/main/resources/application-prod-oci.yaml around lines 12 to 22: 현재
spring.datasource.url에 직접 "?TNS_ADMIN=/wallet"를 붙여두면 GitHub Actions에서
spring.datasource.url 전체를 치환할 때 해당 suffix가 유실되어 OCI wallet(TCPS) 접속에 실패할 수 있으므로
수정이 필요합니다; 해결 방법 중 하나는 PROD_OCI_DB_URL 시크릿을 완성된 URL(예: 기존 URL +
"?TNS_ADMIN=/wallet")로 저장해 치환 시 누락이 없게 하고, 다른 방법은 빌드타임의 전체 치환을 제거하고 설정 파일에
${PROD_OCI_DB_URL}를 그대로 둔 채 런타임에 환경변수로만 PROD_OCI_DB_URL을 주입하여 suffix가 보존되도록
변경하세요.

Comment on lines +28 to +40
fcm:
type: service_account
project_id: yappu-world
private_key_id: ${FCM_PRIVATE_KEY_ID}
private_key: ${FCM_PRIVATE_KEY}
client_email: ${FCM_CLIENT_EMAIL}
client_id: ${FCM_CLIENT_ID}
auth_uri: https://accounts.google.com/o/oauth2/auth
token_uri: https://oauth2.googleapis.com/token
auth_provider_x509_cert_url: https://www.googleapis.com/oauth2/v1/certs
client_x509_cert_url: ${FCM_X509_CERT_URL}
universe_domain: googleapis.com

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

(치명) FCM private_key 멀티라인 시 YAML 깨짐 가능 → 인코딩/이스케이프 전략 필요
fcm.private_key(Line 32)는 멀티라인 PEM일 가능성이 높아서, variable-substitution으로 그대로 주입하면 YAML 파싱 오류 또는 값 손상이 날 수 있습니다.

권장(실무에서 흔한 패턴):

  • 시크릿을 \n 이스케이프된 단일 라인으로 저장 + 애플리케이션에서 replace("\\n", "\n")
  • 또는 base64로 저장 후 런타임에 디코딩(설정은 base64 문자열만)
🤖 Prompt for AI Agents
In src/main/resources/application-prod-oci.yaml around lines 28-40,
fcm.private_key is likely a multiline PEM which will break YAML when injected;
change the secret handling so the YAML only contains a single-line string:
either store the key with escaped newline sequences (\\n) and in application
code replace("\\n","\n") before use, or store the key as base64 and decode it at
startup; also ensure the YAML value is quoted to avoid parser issues and update
deployment/secret docs to reflect the chosen encoding.

Comment on lines +12 to +60
<springProfile name="dev-loki">
<appender name="LOKI" class="com.github.loki4j.logback.Loki4jAppender">
<http>
<url>http://10.0.0.119:3100/loki/api/v1/push</url>
</http>
<labels>
app=yappu-world
host=${HOSTNAME}
level=%level
env=dev
</labels>
<message>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</message>
</appender>

<include resource="org/springframework/boot/logging/logback/defaults.xml"/>
<include resource="org/springframework/boot/logging/logback/console-appender.xml"/>

<root level="INFO">
<appender-ref ref="CONSOLE" />
<appender-ref ref="LOKI" />
</root>
</springProfile>

<springProfile name="prod-loki">
<appender name="LOKI" class="com.github.loki4j.logback.Loki4jAppender">
<http>
<url>http://10.0.0.119:3100/loki/api/v1/push</url>
</http>
<labels>
app=yappu-world
host=${HOSTNAME}
level=%level
env=prod
</labels>
<message>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</message>
</appender>

<include resource="org/springframework/boot/logging/logback/defaults.xml"/>
<include resource="org/springframework/boot/logging/logback/console-appender.xml"/>

<root level="INFO">
<appender-ref ref="CONSOLE" />
<appender-ref ref="LOKI" />
</root>
</springProfile>
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Loki URL 하드코딩 + HTTP 전송은 운영/보안 리스크입니다.
환경별로 URL을 외부화하고(환경변수/스프링 프로퍼티), 가능하면 HTTPS로 보내는 구성이 안전합니다.

-                <url>http://10.0.0.119:3100/loki/api/v1/push</url>
+                <url>${LOKI_URL}</url>

(동일하게 prod-loki에도 적용 권장)

Committable suggestion skipped: line range outside the PR's diff.

@devmizz devmizz merged commit ea423aa into prod Dec 14, 2025
1 of 2 checks passed
@devmizz devmizz deleted the v1.2.3 branch December 14, 2025 08:46
@coderabbitai coderabbitai bot mentioned this pull request Jan 25, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

patch Patch Version

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants