Skip to content

[FEAT] 배포 설정#243

Merged
JiwonLee42 merged 7 commits intodevelop-demofrom
deploy/#240-deploy-settings
Apr 8, 2026
Merged

[FEAT] 배포 설정#243
JiwonLee42 merged 7 commits intodevelop-demofrom
deploy/#240-deploy-settings

Conversation

@JiwonLee42
Copy link
Copy Markdown
Contributor

@JiwonLee42 JiwonLee42 commented Apr 8, 2026

📄 작업 내용 요약

CICD 배포 파이프라인 설정

  • RestDocs, Jacoco 문서 생성
  • 실패시 디스코드 알림
  • 도커 이미지 push
  • blue green 배포 설정

API V1으로 일괄수정

TODO

  • NPM 설정 -> 관리자 페이지에서 설정
  • 그라파냐 alert 설정

📎 Issue 번호


✅ 작업 목록

  • 기능 구현
  • 코드 리뷰 반영
  • 테스트 코드 작성
  • 문서 업데이트

📝 기타 참고사항

Summary by CodeRabbit

릴리스 노트

  • New Features

    • API 버전 관리 체계 도입 - 모든 API 엔드포인트가 /api/v1 접두사 사용
    • 헬스 체크 및 애플리케이션 정보 엔드포인트 공개
  • Chores

    • CI-CD 파이프라인 개선 - 빌드, 배포, 모니터링 단계 분리 및 취약성 스캔 추가
    • Docker 빌드 프로세스 최적화
  • Tests

    • 모든 API 테스트 경로를 버전화된 엔드포인트로 업데이트

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 8, 2026

Warning

Rate limit exceeded

@JiwonLee42 has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 6 minutes and 39 seconds before requesting another review.

Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 6 minutes and 39 seconds.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 112fe14c-ceca-4077-bd3e-5cd99d965121

📥 Commits

Reviewing files that changed from the base of the PR and between 665d24c and e022d1e.

📒 Files selected for processing (4)
  • .github/workflows/deploy-cicd.yml
  • build.gradle
  • src/docs/asciidoc/library.adoc
  • src/test/resources/application-test.yml

Walkthrough

API 버전 관리 시스템을 도입하여 모든 컨트롤러를 /api/v1 접두어로 경로를 재구성하고, CI/CD 파이프라인을 멀티 잡으로 재설계하며, Actuator 및 관련 설정을 추가했습니다.

Changes

코호트 / 파일 요약
CI/CD 파이프라인 재설계
.github/workflows/deploy-cicd.yml
워크플로우를 develop-demo 브랜치 기반 멀티 잡 구조로 재구성: PR 검증용 ci 잡(빌드, RestDocs, JaCoCo), 푸시 시 Docker 빌드/ECR 푸시 및 Trivy 스캔하는 docker-dev 잡, deploy-dev 잡으로 dev 환경 배포, 실패 시 Discord 알림 notify-failure 잡 추가
Docker 빌드 최적화
Dockerfile
멀티 스테이지 빌드 제거: 사전 빌드된 JAR를 build/libs/에서 직접 복사하도록 변경
스프링 의존성 추가
build.gradle
Spring Boot Actuator 의존성 추가
API 버전 관리 설정
src/main/java/app/nook/api/Api1Version.java, src/main/java/app/nook/api/APIVersionConfig.java
새로운 @Api1Version 어노테이션 정의 및 APIVersionConfig 클래스 추가: 어노테이션이 붙은 컨트롤러에 /api/v1 경로 접두어 자동 적용
컨트롤러 API 버전 적용
src/main/java/app/nook/book/controller/BookController.java, BookSearchController.java, CategoryController.java, src/main/java/app/nook/focus/controller/FocusController.java, src/main/java/app/nook/library/controller/LibraryController.java, LibraryStatsController.java, src/main/java/app/nook/r2/controller/ImageUploadController.java, src/main/java/app/nook/record/controller/RecordController.java, src/main/java/app/nook/timeline/controller/TimelineController.java, src/main/java/app/nook/user/controller/AuthController.java, OnboardingController.java
모든 컨트롤러에 @Api1Version 어노테이션 추가 및 @RequestMapping 경로에서 /api 접두어 제거 (예: /api/books/books)
보안 및 설정 업데이트
src/main/java/app/nook/global/config/WebSecurityConfig.java, src/main/resources/application.yml, src/main/java/app/nook/user/dev/MockUserInitializer.java
Actuator 엔드포인트(/actuator/health, /actuator/info) 및 /api/v1/auth/** 공개 허용, 기본 프로파일을 dev로 변경, 로깅 레벨 설정, Actuator 설정 추가, 모의 사용자 초기화 시 UserRole.USER 명시
테스트 경로 업데이트
src/test/java/app/nook/controller/book/BookControllerTest.java, BookSearchControllerTest.java, CategoryControllerTest.java, src/test/java/app/nook/controller/focus/FocusControllerTest.java, FocusThemeControllerTest.java, src/test/java/app/nook/controller/library/LibraryControllerTest.java, LibraryStatsControllerTest.java, src/test/java/app/nook/controller/r2/ImageUploadControllerTest.java, src/test/java/app/nook/controller/record/RecordControllerTest.java, src/test/java/app/nook/controller/timeline/TimelineControllerTest.java, src/test/java/app/nook/controller/user/AuthControllerTest.java, OnboardingControllerTest.java, src/test/java/app/nook/global/common/TestSecurityConfig.java
모든 컨트롤러 테스트의 HTTP 요청 경로를 /api/v1 접두어로 업데이트 (예: /api/books/api/v1/books), 테스트 보안 설정에 /api/v1/auth/** 공개 허용 규칙 추가

Sequence Diagram

sequenceDiagram
    participant Client as 클라이언트
    participant DispatcherServlet as DispatcherServlet
    participant APIVersionConfig as APIVersionConfig<br/>(PathMatchConfigurer)
    participant Controller as 버전 관리 컨트롤러<br/>(`@Api1Version`)
    
    Client->>DispatcherServlet: GET /api/v1/books
    DispatcherServlet->>APIVersionConfig: 경로 매칭 검사
    APIVersionConfig->>APIVersionConfig: HandlerTypePredicate로<br/>@Api1Version 확인
    APIVersionConfig->>APIVersionConfig: /api/v1 접두어 제거<br/>실제 경로: /books
    APIVersionConfig->>Controller: /books로 라우팅
    Controller->>Controller: 비즈니스 로직 처리
    Controller-->>DispatcherServlet: 응답 반환
    DispatcherServlet-->>Client: HTTP 200 + JSON
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~45분

Possibly related PRs

Poem

🐰 API 버전들을 곱게 정리하고,
CI/CD 파이프라인 깔끔하게 흘러가고,
모든 경로에 /v1 마크를 붙이니,
배포가 한결 더 명확해졌네요!
토끼도 기뻐서 팔짝 뛸 일이에요! 🚀✨

🚥 Pre-merge checks | ✅ 1 | ❌ 4

❌ Failed checks (3 warnings, 1 inconclusive)

Check name Status Explanation Resolution
Linked Issues check ⚠️ Warning PR은 API 버전 관리, CI-CD 파이프라인, controller 경로 변경 등을 포함하지만, 링크된 이슈 #240의 요구사항(Flyway 설정, dev/prod 브랜치 분리, YAML 설정 분리)을 충족하지 않습니다. Flyway 데이터베이스 마이그레이션 설정 구현, dev/prod YAML 설정 분리, 500 에러 Discord 알림 기능을 추가하세요.
Out of Scope Changes check ⚠️ Warning PR에는 링크된 이슈 #240의 요구사항과 무관한 범위 밖의 변경사항들이 포함되어 있습니다: Spring Boot Actuator 의존성 추가, 모든 controller의 @Api1Version 주석 추가 및 경로 변경, 테스트 파일 수정 등. 범위 밖의 변경사항(API 버전 관리, Actuator 추가)과 #240의 요구사항을 분리하여 별도의 PR로 관리하시기 바랍니다.
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.
Title check ❓ Inconclusive 제목이 배포 설정 관련 변경사항을 지칭하지만, 실제 변경사항(API 버전 관리, CI-CD 파이프라인 구축, 다양한 controller 경로 변경 등)을 구체적으로 설명하지 못하고 있습니다. 제목을 더 구체적으로 작성하시기 바랍니다. 예: '[FEAT] API v1 버전 관리 및 CI-CD 파이프라인 구축' 또는 '[FEAT] API 버전 관리 시스템 도입 및 배포 설정'
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch deploy/#240-deploy-settings

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
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 5

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/app/nook/user/dev/MockUserInitializer.java (1)

29-31: ⚠️ Potential issue | 🟠 Major

관리자 목업 계정이 일반 사용자 권한으로 생성됩니다.

Line 29-31에서 DEV_ADMIN 계정을 만들지만, Line 51에서 role을 UserRole.USER로 고정해 admin@test.com도 USER 권한으로 저장됩니다. 관리자 시나리오(권한 테스트/운영 점검) 검증이 깨질 수 있습니다. createIfNotExists에 role 파라미터를 추가해 계정별로 명시하세요.

수정 제안 diff
@@
         createIfNotExists(
                 "dev@test.com",
-                "DEV_USER"
+                "DEV_USER",
+                UserRole.USER
         );

         createIfNotExists(
                 "admin@test.com",
-                "DEV_ADMIN"
+                "DEV_ADMIN",
+                UserRole.ADMIN
         );
@@
     private void createIfNotExists(
             String email,
-            String nickname
+            String nickname,
+            UserRole role
     ) {
@@
         User user = User.builder()
                 .email(email)
                 .nickName(nickname)
                 .provider("DEV")
                 .providerId("DEV_" + email)
-                .role(UserRole.USER)
+                .role(role)
                 .build();

Also applies to: 46-52

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/java/app/nook/user/dev/MockUserInitializer.java` around lines 29 -
31, The admin mock account is being created but later forced to UserRole.USER;
update the MockUserInitializer to accept and propagate a role parameter to
createIfNotExists and use it when saving the user instead of hardcoding
UserRole.USER. Specifically, add a role argument to the createIfNotExists method
(and its callers), ensure the method uses that role when constructing/persisting
the account (instead of UserRole.USER), and call it with UserRole.ADMIN for
"admin@test.com" while keeping other calls using UserRole.USER as appropriate;
update any references to the method signature (e.g.,
MockUserInitializer.createIfNotExists and its invocations) accordingly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.github/workflows/deploy-cicd.yml:
- Around line 116-122: 현재 워크플로우가
src/main/resources/application-secret.properties를 만들어서 ./gradlew clean build로
JAR에 비밀을 패키징하고 있으며(application.yml이
optional:classpath:application-secret.properties로 로드함), 이 파일 생성을 제거하고 비밀을 런타임에
주입하도록 변경하세요: .github/workflows/deploy-cicd.yml에서 application-secret.properties
생성 스텝을 삭제하고 대신 컨테이너 런타임(ECS task definition / Kubernetes secret / Docker run -v
또는 엔트리포인트에서 읽는 환경변수)으로 주입하도록 구성하며, 필요하면 애플리케이션이 application-secret.properties 대신
환경변수나 외부 파일 위치(SPRING_CONFIG_LOCATION 또는 spring.config.import 등)를 사용하도록 코드/
Dockerfile과 설정을 업데이트하세요 so the ./gradlew clean build step no longer bakes
secrets into the JAR.
- Around line 174-178: Replace the runtime git pull with a deterministic
checkout to the exact triggered commit: instead of running "git pull origin
develop-demo" use a fetch+reset to the workflow's SHA (referencing github.sha /
GITHUB_SHA) so the server working tree matches the image build commit exactly;
update the deployment step that currently calls git pull to fetch the specific
commit (e.g., fetch origin <SHA> and git reset --hard <SHA> or use
actions/checkout equivalent) and then continue to run chmod +x scripts/deploy.sh
and ./scripts/deploy.sh "$IMAGE".
- Line 160: Replace the mutable `@master` action refs with immutable commit SHAs:
locate the two uses entries referencing appleboy/ssh-action@master and
Ilshidur/action-discord@master and change each to the full 40-character commit
SHA for the intended release (or a released tag plus the matching SHA), leaving
a comment next to the entry documenting the original tag/version and the SHA you
pinned; ensure the new uses values are the exact commit SHAs so the SSH key and
Discord webhook steps are immutable and cannot be changed by branch updates.
- Around line 55-59: The JaCoCo action step named "JaCoCo PR Report" is passing
the binary exec file via the paths input; change it to point at the generated
JaCoCo XML report instead (the XML produced by jacocoTestReport). Update the
paths value for the madrapps/jacoco-report step (the step with name "JaCoCo PR
Report" and input key `paths`) to the XML report path produced by your Gradle
task (e.g., the jacocoTestReport XML under
build/reports/jacoco/.../jacocoTestReport.xml) so the action can parse and post
PR coverage correctly.

In `@Dockerfile`:
- Line 11: The Dockerfile's COPY line "COPY build/libs/*.jar app.jar" fails when
multiple JARs exist; update the build and Dockerfile so only one artifact is
copied: configure Gradle to produce a single expected filename (e.g., set
bootJar.enabled = true and disable plain jar or set
archiveBaseName/archiveClassifier) or explicitly reference the exact jar name in
the Dockerfile COPY, and replace the glob COPY with the explicit artifact
filename; ensure the changes reference the Dockerfile COPY line and the Gradle
tasks (bootJar, jar) so CI builds a single predictable JAR and the Docker COPY
succeeds.

---

Outside diff comments:
In `@src/main/java/app/nook/user/dev/MockUserInitializer.java`:
- Around line 29-31: The admin mock account is being created but later forced to
UserRole.USER; update the MockUserInitializer to accept and propagate a role
parameter to createIfNotExists and use it when saving the user instead of
hardcoding UserRole.USER. Specifically, add a role argument to the
createIfNotExists method (and its callers), ensure the method uses that role
when constructing/persisting the account (instead of UserRole.USER), and call it
with UserRole.ADMIN for "admin@test.com" while keeping other calls using
UserRole.USER as appropriate; update any references to the method signature
(e.g., MockUserInitializer.createIfNotExists and its invocations) accordingly.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 28947c3d-ff74-43b4-b5f8-9783a7625047

📥 Commits

Reviewing files that changed from the base of the PR and between 2c73c61 and 665d24c.

📒 Files selected for processing (32)
  • .github/workflows/deploy-cicd.yml
  • Dockerfile
  • build.gradle
  • src/main/java/app/nook/api/APIVersionConfig.java
  • src/main/java/app/nook/api/Api1Version.java
  • src/main/java/app/nook/book/controller/BookController.java
  • src/main/java/app/nook/book/controller/BookSearchController.java
  • src/main/java/app/nook/book/controller/CategoryController.java
  • src/main/java/app/nook/focus/controller/FocusController.java
  • src/main/java/app/nook/global/config/WebSecurityConfig.java
  • src/main/java/app/nook/library/controller/LibraryController.java
  • src/main/java/app/nook/library/controller/LibraryStatsController.java
  • src/main/java/app/nook/r2/controller/ImageUploadController.java
  • src/main/java/app/nook/record/controller/RecordController.java
  • src/main/java/app/nook/timeline/controller/TimelineController.java
  • src/main/java/app/nook/user/controller/AuthController.java
  • src/main/java/app/nook/user/controller/OnboardingController.java
  • src/main/java/app/nook/user/dev/MockUserInitializer.java
  • src/main/resources/application.yml
  • src/test/java/app/nook/controller/book/BookControllerTest.java
  • src/test/java/app/nook/controller/book/BookSearchControllerTest.java
  • src/test/java/app/nook/controller/book/CategoryControllerTest.java
  • src/test/java/app/nook/controller/focus/FocusControllerTest.java
  • src/test/java/app/nook/controller/focus/FocusThemeControllerTest.java
  • src/test/java/app/nook/controller/library/LibraryControllerTest.java
  • src/test/java/app/nook/controller/library/LibraryStatsControllerTest.java
  • src/test/java/app/nook/controller/r2/ImageUploadControllerTest.java
  • src/test/java/app/nook/controller/record/RecordControllerTest.java
  • src/test/java/app/nook/controller/timeline/TimelineControllerTest.java
  • src/test/java/app/nook/controller/user/AuthControllerTest.java
  • src/test/java/app/nook/controller/user/OnboardingControllerTest.java
  • src/test/java/app/nook/global/common/TestSecurityConfig.java

Comment on lines +116 to +122
- name: application-secret.properties 생성
run: |
mkdir -p ./src/main/resources
echo "${{ secrets.APPLICATION_SECRET_PROPERTIES_DEV }}" > ./src/main/resources/application-secret.properties

- name: Docker 이미지용 JAR + RestDocs 생성
run: ./gradlew clean build
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

비밀값을 빌드 산출물에 내장하고 있습니다.

여기서 src/main/resources/application-secret.properties를 만든 뒤 바로 ./gradlew clean build를 돌리면 그 파일이 JAR 클래스패스 리소스로 패키징됩니다. 현재 src/main/resources/application.yml 1-3행도 이를 optional:classpath:application-secret.properties로 읽게 되어 있어서, 결과적으로 dev 비밀값이 ECR 이미지에 고정됩니다. 런타임 주입으로 바꾸지 않으면 비밀값 회전과 이미지 접근 권한 분리가 어려워집니다.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/deploy-cicd.yml around lines 116 - 122, 현재 워크플로우가
src/main/resources/application-secret.properties를 만들어서 ./gradlew clean build로
JAR에 비밀을 패키징하고 있으며(application.yml이
optional:classpath:application-secret.properties로 로드함), 이 파일 생성을 제거하고 비밀을 런타임에
주입하도록 변경하세요: .github/workflows/deploy-cicd.yml에서 application-secret.properties
생성 스텝을 삭제하고 대신 컨테이너 런타임(ECS task definition / Kubernetes secret / Docker run -v
또는 엔트리포인트에서 읽는 환경변수)으로 주입하도록 구성하며, 필요하면 애플리케이션이 application-secret.properties 대신
환경변수나 외부 파일 위치(SPRING_CONFIG_LOCATION 또는 spring.config.import 등)를 사용하도록 코드/
Dockerfile과 설정을 업데이트하세요 so the ./gradlew clean build step no longer bakes
secrets into the JAR.


steps:
- name: Dev 서버 배포
uses: appleboy/ssh-action@master
Copy link
Copy Markdown

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 check the workflow file exists and view the relevant lines
wc -l .github/workflows/deploy-cicd.yml

Repository: UMC-NOOK/Server

Length of output: 96


🏁 Script executed:

# View lines around 160 and 195 to confirm the `@master` references
sed -n '150,170p' .github/workflows/deploy-cicd.yml
sed -n '185,205p' .github/workflows/deploy-cicd.yml

Repository: UMC-NOOK/Server

Length of output: 1074


🏁 Script executed:

# Get full context of the workflow file to understand the jobs and their purposes
cat -n .github/workflows/deploy-cicd.yml

Repository: UMC-NOOK/Server

Length of output: 7089


🌐 Web query:

GitHub Actions security best practices pinning actions to commit SHA

💡 Result:

Why pin GitHub Actions to a commit SHA?

Using uses: owner/repo@v1 (or @main) is risky because tags/branches can move (intentionally or after a compromise), causing your workflow to run different code than you reviewed. GitHub’s guidance is to pin to a full-length commit SHA to make the reference immutable. (docs.github.com)

Recommended pattern (with human-readable context)

steps:
  - uses: actions/checkout@<FULL_LENGTH_COMMIT_SHA> # v4.2.2

This keeps the workflow locked to the exact commit, while the comment documents the intended release/tag. (docs.github.com)

Enforce SHA pinning (org/enterprise governance)

GitHub Actions policy can require actions/reusable workflows to be pinned to a full commit SHA; workflows that aren’t pinned can be blocked from running. (github.blog)

Handling the “but we need updates” tradeoff

Pinning means you must deliberately update SHAs. Common approaches:

  • Use a tool/bot to open PRs updating pinned SHAs (e.g., “pin-github-action” style tooling). (github.com)
  • Add a CI check that fails if any uses: isn’t SHA-pinned (helps prevent regressions). (github.com)

Prefer immutable releases (if you publish actions)

If you maintain actions, GitHub recommends release practices (including immutable releases/tags) to make what consumers depend on harder to change unexpectedly. (docs.github.com)

Citations:


@master 액션 참조는 배포 워크플로에서 보안 위험입니다.

appleboy/ssh-action@master (160줄)와 Ilshidur/action-discord@master (195줄)는 @master 참조를 사용하고 있습니다. GitHub의 공식 보안 가이드에 따르면 브랜치나 태그는 의도적으로 또는 손상 후 변경될 수 있으므로, 액션을 전체 길이의 커밋 SHA로 고정하는 것이 가장 안전한 방식입니다. SSH 개인 키와 Discord webhook secret을 다루는 이 두 액션은 반드시 immutable reference(커밋 SHA)로 변경해야 합니다. 커밋 SHA와 함께 의도한 버전/태그를 주석으로 남길 수 있습니다.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/deploy-cicd.yml at line 160, Replace the mutable `@master`
action refs with immutable commit SHAs: locate the two uses entries referencing
appleboy/ssh-action@master and Ilshidur/action-discord@master and change each to
the full 40-character commit SHA for the intended release (or a released tag
plus the matching SHA), leaving a comment next to the entry documenting the
original tag/version and the SHA you pinned; ensure the new uses values are the
exact commit SHAs so the SSH key and Discord webhook steps are immutable and
cannot be changed by branch updates.

Comment on lines +174 to +178
cd ~/app
git pull origin develop-demo

chmod +x scripts/deploy.sh
./scripts/deploy.sh "$IMAGE"
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

서버에서 git pull하지 말고 트리거된 SHA로 고정하세요.

이미지는 docker-dev job에서 특정 커밋 기준으로 빌드했는데, 배포 시점에 서버에서 다시 develop-demo HEAD를 pull 하면 그 사이 들어온 더 새로운 스크립트/compose 파일과 이전 이미지 태그가 섞일 수 있습니다. 재현성과 롤백이 바로 깨지는 지점이라, 최소한 ${{ github.sha }}로 fetch/reset 해서 동일 커밋만 배포하게 고정하는 편이 낫습니다.

예시 수정
-            cd ~/app
-            git pull origin develop-demo
+            cd ~/app
+            git fetch origin develop-demo --depth=1
+            git checkout develop-demo
+            git reset --hard ${{ github.sha }}
+            git clean -fd
📝 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
cd ~/app
git pull origin develop-demo
chmod +x scripts/deploy.sh
./scripts/deploy.sh "$IMAGE"
cd ~/app
git fetch origin develop-demo --depth=1
git checkout develop-demo
git reset --hard ${{ github.sha }}
git clean -fd
chmod +x scripts/deploy.sh
./scripts/deploy.sh "$IMAGE"
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/deploy-cicd.yml around lines 174 - 178, Replace the
runtime git pull with a deterministic checkout to the exact triggered commit:
instead of running "git pull origin develop-demo" use a fetch+reset to the
workflow's SHA (referencing github.sha / GITHUB_SHA) so the server working tree
matches the image build commit exactly; update the deployment step that
currently calls git pull to fetch the specific commit (e.g., fetch origin <SHA>
and git reset --hard <SHA> or use actions/checkout equivalent) and then continue
to run chmod +x scripts/deploy.sh and ./scripts/deploy.sh "$IMAGE".

apt-get clean

COPY --from=build /app/build/libs/*.jar app.jar
COPY build/libs/*.jar app.jar
Copy link
Copy Markdown

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:

#!/bin/bash
set -e

# 1) Dockerfile의 와일드카드 COPY 사용 여부 확인
rg -n 'COPY\s+build/libs/\*\.jar\s+app\.jar|COPY\s+build/libs/app\.jar\s+app\.jar' Dockerfile

# 2) Gradle에서 bootJar 이름 고정 / plain jar 비활성화 여부 확인
rg -n 'tasks\.named\("bootJar"\)|archiveFileName\s*=\s*"app\.jar"|tasks\.named\("jar"\)|enabled\s*=\s*false' build.gradle

Repository: UMC-NOOK/Server

Length of output: 91


🏁 Script executed:

# build.gradle 파일 찾기 및 내용 확인
fd -type f -name 'build.gradle*'

Repository: UMC-NOOK/Server

Length of output: 229


🏁 Script executed:

# build.gradle 내용 조회 (jar 관련 설정 포함)
cat build.gradle

Repository: UMC-NOOK/Server

Length of output: 5243


COPY build/libs/*.jar app.jar는 다중 JAR 매칭 시 Docker 빌드를 실패시킵니다.

현재 build.gradle에 bootJar 또는 jar 태스크 설정이 없으므로, Spring Boot 기본 동작에 따라 build/libs/ 디렉토리에 plain JAR과 boot JAR 2개가 생성됩니다. COPY 명령어가 여러 파일을 단일 파일 목적지(app.jar)로 복사하려 하면 Docker 빌드가 실패합니다. 산출물을 명시적으로 관리하고 단일 파일만 복사하도록 변경해야 합니다.

🔧 제안 수정안
# Dockerfile
-COPY build/libs/*.jar app.jar
+COPY build/libs/app.jar app.jar
# build.gradle (맨 아래에 추가)
+tasks.named("bootJar") {
+    archiveFileName = "app.jar"
+}
+
+tasks.named("jar") {
+    enabled = false
+}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Dockerfile` at line 11, The Dockerfile's COPY line "COPY build/libs/*.jar
app.jar" fails when multiple JARs exist; update the build and Dockerfile so only
one artifact is copied: configure Gradle to produce a single expected filename
(e.g., set bootJar.enabled = true and disable plain jar or set
archiveBaseName/archiveClassifier) or explicitly reference the exact jar name in
the Dockerfile COPY, and replace the glob COPY with the explicit artifact
filename; ensure the changes reference the Dockerfile COPY line and the Gradle
tasks (bootJar, jar) so CI builds a single predictable JAR and the Docker COPY
succeeds.

@JiwonLee42
Copy link
Copy Markdown
Contributor Author

Overall Project 63.16% 🍏
Files changed 100% 🍏

File Coverage
TimelineController.java 100% 🍏
APIVersionConfig.java 100% 🍏
LibraryController.java 100% 🍏
LibraryStatsController.java 100% 🍏
RecordController.java 100% 🍏
ImageUploadController.java 100% 🍏
OnboardingController.java 100% 🍏
AuthController.java 100% 🍏
FocusController.java 100% 🍏
CategoryController.java 100% 🍏
BookSearchController.java 100% 🍏
BookController.java 100% 🍏
MockUserInitializer.java 90.38% 🍏

@JiwonLee42 JiwonLee42 merged commit d84d9d5 into develop-demo Apr 8, 2026
5 checks passed
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.

[DEPLOY] 배포 관련 설정 작업

1 participant