Skip to content

[Chore] mannabom-server 초기 세팅 및 환경설정 파일 추가#2

Open
kimjuneon wants to merge 11 commits into
mainfrom
dev
Open

[Chore] mannabom-server 초기 세팅 및 환경설정 파일 추가#2
kimjuneon wants to merge 11 commits into
mainfrom
dev

Conversation

@kimjuneon
Copy link
Copy Markdown
Contributor

@kimjuneon kimjuneon commented Apr 9, 2026

작업 내용

  • 기존 mannabom 프로젝트의 리팩토링 버전 리포지토리 초기 구조 구성함
  • 애플리케이션 실행을 위한 기본 Spring Boot 프로젝트 파일과 환경설정 파일 이관함
  • 로컬 개발용 및 운영 배포용 Docker Compose 파일 분리하여 추가함
  • 기존 DB 스키마 및 변경 이력을 반영할 수 있도록 Flyway 마이그레이션 파일 함께 이관함
  • 지역 데이터 초기 적재에 사용할 CSV 파일 함께 추가함

변경 이유

  • 기존 프로젝트를 기반으로 리팩토링 작업을 진행하기 위해 먼저 실행 가능한 최소 프로젝트 구조를 새 리포지토리에 마련할 필요가 있었음
  • 환경설정, 배포 설정, DB 마이그레이션 파일을 초기 단계에서 함께 정리해두어 이후 기능 리팩토링 시 기반 작업을 줄이기 위함
  • 기존 프로젝트의 실행 방식과 데이터 구조를 최대한 유지한 상태에서 리팩토링을 진행하기 위한 초기 세팅 목적임

주요 변경 사항

프로젝트 기본 실행 구조 추가

MannabomServerApplication.java
MannabomServerApplicationTests.java

환경설정 파일 추가

application.yaml
application-local.yml
application-prod.yml

빌드 및 실행 환경 파일 추가

build.gradle
settings.gradle
gradlew
gradle/wrapper/*
Dockerfile
Docker Compose 파일 추가
docker-compose.local.yml
docker-compose.prod.yml

DB 마이그레이션 파일 추가

V1__baseline.sql
V2__add_meeting_and_chat.sql
V3__add_request_table_and_extra_photo_table_and_rating_table.sql
V4__add_reject_reason_to_like_request.sql
V5__add_reject_reason_to_message_request.sql
V6__add_source_to_like_request.sql
V7__add_source_to_message_request.sql
V8__migrate_profile_data_and_add_column_chat_room_table.sql

초기 데이터 파일 추가

regions.csv
regions_original.csv

테스트

  • 로컬 실행 확인
  • API 테스트 확인
  • DB 마이그레이션 확인

관련 이슈

closes #

리뷰 포인트

  • 리팩토링 리포지토리의 초기 구조로 적절한지 확인
  • application.yaml, application-local.yml, application-prod.yml 분리 방식이 적절한지 검토
  • 기존 Flyway 마이그레이션 파일을 그대로 유지할지, 이후 리팩토링 리포 기준으로 baseline을 재구성할지 의견 첨부
  • docker-compose.local.yml, docker-compose.prod.yml 분리 방향이 적절한지 확인

참고 사항

  • 이번 PR은 기능 리팩토링보다는 기존 프로젝트의 실행 및 설정 기반을 새 리포지토리로 옮기는 초기 세팅 작업에 해당함
  • 기존 DB 마이그레이션 이력은 우선 그대로 가져왔으며, 이후 필요 시 리팩토링 리포지토리 기준의 단일 baseline 형태로 재정리할 예정임

Summary by CodeRabbit

Release Notes

  • Chores

    • 새로운 백엔드 서버 초기 설정 완료
    • PostgreSQL 데이터베이스 및 Redis 캐시 인프라 구성
    • Docker를 통한 컨테이너화 및 배포 환경 구축
    • 다중 환경(로컬, 테스트, 프로덕션) 설정 파일 추가
  • Tests

    • 자동화된 테스트 및 코드 커버리지 검증 파이프라인 구성
    • GitHub Actions CI/CD 워크플로우 설정

@kimjuneon kimjuneon requested a review from Sehi55 April 9, 2026 11:14
@kimjuneon kimjuneon self-assigned this Apr 9, 2026
@kimjuneon kimjuneon added the good first issue Good for newcomers label Apr 9, 2026
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 9, 2026

📝 Walkthrough

개요

mannabom-server 프로젝트의 초기 설정을 구성합니다. Gradle 빌드 시스템, Spring Boot 애플리케이션 구조, Docker 컨테이너화, PostgreSQL 및 Redis 데이터베이스 초기화, GitHub Actions CI/CD 워크플로우, 그리고 관련 설정 파일을 추가합니다.

변경사항

응집 집합 / 파일(들) 요약
Gradle 및 프로젝트 설정
mannabom-server/gradle/wrapper/gradle-wrapper.properties, mannabom-server/gradlew, mannabom-server/gradlew.bat, mannabom-server/settings.gradle, mannabom-server/build.gradle
Gradle 9.4.1 래퍼 구성, Java 17 도구체인 설정, Spring Boot 3.5.4, JaCoCo 코드 커버리지(70% 이상 필수), 그리고 WebSocket, JPA, Redis, JWT, AWS S3, QueryDSL, Firebase 등 주요 의존성을 포함한 완전한 빌드 구성 추가
Docker 및 배포
mannabom-server/.gitattributes, mannabom-server/Dockerfile, mannabom-server/docker-compose.local.yml, mannabom-server/docker-compose.prod.yml
eclipse-temurin:17-jre-alpine 기반 컨테이너 이미지, 로컬 개발용 PostgreSQL 13 및 Redis 6 서비스, 프로덕션 ECR 이미지 및 Redis 구성 정의
Spring Boot 애플리케이션 및 설정
mannabom-server/src/main/java/com/mannabom/mannabom_server/MannabomServerApplication.java, mannabom-server/src/main/resources/application.yaml, mannabom-server/src/main/resources/application-prod.yml, mannabom-server/src/main/resources/application-test.yml
@SpringBootApplication 진입점 클래스, 데이터베이스, Redis, SMTP, AWS S3, JWT, Kakao OAuth, FCM, 비즈니스 정책(ting/benefit/match) 설정을 포함한 기본/프로덕션/테스트 프로필별 Spring Boot 설정
데이터베이스 마이그레이션
mannabom-server/src/main/resources/db/migration/V1__baseline.sql, mannabom-server/src/main/resources/db/migration/V2__add_meeting_and_chat.sql, mannabom-server/src/main/resources/db/migration/V3__add_request_table_and_extra_photo_table_and_rating_table.sql, mannabom-server/src/main/resources/db/migration/V4__add_reject_reason_to_like_request.sql, mannabom-server/src/main/resources/db/migration/V5__add_reject_reason_to_message_request.sql, mannabom-server/src/main/resources/db/migration/V6__add_source_to_like_request.sql, mannabom-server/src/main/resources/db/migration/V7__add_source_to_message_request.sql, mannabom-server/src/main/resources/db/migration/V8__migrate_profile_data_and_add_column_chat_room_table.sql
사용자, 프로필, 매칭, 채팅, 요청, 평점, 지역 정보 등 핵심 테이블 정의 및 단계적 스키마 진화(사용자 관계, 회의, 채팅방, 메시지, 승인/거부 사유, 데이터 마이그레이션)
테스트 및 CI/CD
mannabom-server/src/test/java/com/mannabom/mannabom_server/MannabomServerApplicationTests.java, mannabom-server/.github/workflows/ci.yml
Spring Boot 컨텍스트 로드 테스트, PostgreSQL/Redis 서비스 컨테이너, JaCoCo 커버리지 검증(70%), diff-cover 기반 PR 커버리지 평가(80% 최소) GitHub Actions 워크플로우
저장소 설정
.gitignore, mannabom-server/.github/PULL_REQUEST_TEMPLATE/default.md
Gradle/IDE 빌드 산출물, 로컬 환경 변수, Firebase 자격증명, .DS_Store 무시, PR 템플릿 기본값(설명, 근거, 변경사항, 테스트 항목, 이슈 연결, 리뷰 포인트)

예상 코드 리뷰 노력

🎯 4 (복잡함) | ⏱️ ~45분

관련 가능성 있는 PR

  • [Setting] 테스트 자동화 설정 #3: CI/빌드/테스트 관련 파일(.github/workflows/ci.yml, build.gradle, application-test.yml, .gitignore, PR 템플릿)을 동일하게 수정하므로 직접 관련이 있습니다.

제안 리뷰어

  • Sehi55

🐰 당근밭에서 자란 작은 프로젝트,
Gradle의 손길로 빌드되고,
Docker 상자에 담겨 나르고,
데이터베이스 정원을 가꾸며,
Spring Boot 봄을 맞이한다! 🌱✨

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed PR 제목은 주요 변경사항을 명확하게 요약합니다. '[Chore] mannabom-server 초기 세팅 및 환경설정 파일 추가'는 저장소의 초기 설정과 환경 구성이라는 PR의 핵심 목적을 정확하게 반영합니다.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ 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 dev

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

🧹 Nitpick comments (7)
mannabom-server/.github/pull_request_template.md (2)

10-14: 테스트 체크리스트에 추가 항목을 고려해볼 수 있습니다.

현재 체크리스트는 잘 구성되어 있으나, 다음 항목들도 유용할 수 있습니다:

  • - [ ] 관련 문서 업데이트 (README, API 문서 등)
  • - [ ] Breaking changes 확인 (기존 기능에 영향을 주는 변경 사항)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@mannabom-server/.github/pull_request_template.md` around lines 10 - 14, Add
two additional checklist items under the existing "## 테스트" section: include "- [
] 관련 문서 업데이트" to ensure README/API docs are updated and "- [ ] Breaking changes
확인" to verify whether changes introduce breaking behavior; update the checklist
near the existing items (the "## 테스트" heading and the three checkboxes) so
reviewers must confirm documentation updates and breaking-change checks before
merging.

22-22: 파일 끝에 빈 줄 추가를 권장합니다.

POSIX 표준과 Git 관례에 따라 텍스트 파일은 newline으로 끝나는 것이 좋습니다. 일부 에디터나 Git 도구에서 경고를 표시할 수 있습니다.

📝 제안하는 수정

파일 마지막에 빈 줄을 하나 추가하세요:

 ## 참고 사항
 - 
+
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@mannabom-server/.github/pull_request_template.md` at line 22, Add a single
trailing newline to the end of the pull request template file so the file ends
with a blank line; open mannabom-server/.github/pull_request_template.md and
append one newline character at the end of the file (ensure the file terminates
with '\n') to comply with POSIX/Git conventions.
mannabom-server/Dockerfile (1)

5-6: COPY build/libs/*.jar는 다중 JAR 산출 시 빌드가 불안정할 수 있습니다.

배포 대상 JAR를 명시적으로 지정하는 편이 안전합니다.

🔧 제안 수정
-ARG JAR_FILE=build/libs/*.jar
+ARG JAR_FILE=build/libs/mannabom-server.jar
 COPY ${JAR_FILE} app.jar
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@mannabom-server/Dockerfile` around lines 5 - 6, The Dockerfile currently uses
a glob ARG and COPY (${JAR_FILE} / app.jar), which is unstable when multiple
JARs exist; update the ARG JAR_FILE and COPY to reference the exact artifact
name (e.g., set ARG JAR_FILE=build/libs/your-artifact-name.jar and keep COPY
${JAR_FILE} app.jar) or require passing the precise --build-arg
JAR_FILE=<artifact> during docker build so the intended JAR is unambiguous;
ensure the change touches the ARG JAR_FILE and the COPY ${JAR_FILE} app.jar
lines.
mannabom-server/src/main/resources/db/migration/V3__add_request_table_and_extra_photo_table_and_rating_table.sql (1)

46-77: 타임스탬프 컬럼의 일관성을 검토해 주세요.

like_requestmessage_request 테이블은 created_at NOT NULL을 사용하지만, profile_extra_photo_unlockprofile_rating 테이블은 created_at이 nullable입니다.

도메인 요구사항에 따른 의도적인 설계라면 문제없지만, 일관성을 위해 모든 테이블에서 created_at NOT NULL을 적용하는 것을 권장합니다.

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

In
`@mannabom-server/src/main/resources/db/migration/V3__add_request_table_and_extra_photo_table_and_rating_table.sql`
around lines 46 - 77, Update the two CREATE TABLE statements so their timestamp
columns follow the same not-null policy as other tables: change
profile_extra_photo_unlock.created_at and profile_rating.created_at to be
declared TIMESTAMP NOT NULL (and optionally make updated_at NOT NULL as well if
your schema requires it) so the tables profile_extra_photo_unlock and
profile_rating match the non-null created_at behavior of like_request and
message_request.
mannabom-server/docker-compose.prod.yml (2)

2-17: 운영 안정성을 위해 restart 정책과 헬스체크 추가를 권장합니다.

mannabom-server 서비스에 재시작 정책이 없어 컨테이너 충돌 시 자동 복구되지 않습니다. 또한 depends_on만으로는 Redis가 실제로 요청을 받을 준비가 되었는지 보장하지 않습니다.

♻️ 제안된 개선사항
   mannabom-server:
     image: ${ECR_REGISTRY_URI}:${IMAGE_TAG}
     container_name: mannabom-server
     environment:
       - SPRING_PROFILES_ACTIVE=prod
     env_file:
       - .env
     ports:
       - 8080:8080
+    restart: unless-stopped
     depends_on:
-      - redis
+      redis:
+        condition: service_healthy
     networks:
       - mannabom-net
     volumes:
       - /opt/mannabom/firebase:/opt/mannabom/firebase
+    healthcheck:
+      test: ["CMD", "curl", "-f", "http://localhost:8080/actuator/health"]
+      interval: 30s
+      timeout: 10s
+      retries: 3
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@mannabom-server/docker-compose.prod.yml` around lines 2 - 17, 서비스 블록
mannabom-server에 restart 정책과 헬스체크를 추가하여 컨테이너 충돌 시 자동 재시작과 상태 기반 의존성을 보장하십시오:
mannabom-server에 restart (예: "always" 또는 "unless-stopped")를 설정하고 healthcheck를
추가하여 HTTP/TCP 또는 커맨드 기반 상태 확인을 수행하도록 구성하고, depends_on 항목을 condition:
service_healthy로 전환하거나 redis에 healthcheck를 추가하여 Redis가 실제로 준비되었을 때만
mannabom-server가 시작되게 하세요; 관련 키: mannabom-server, restart, healthcheck,
depends_on, redis, condition: service_healthy.

19-26: 프로덕션 환경에서 Redis 인증 설정을 권장합니다.

현재 Redis가 인증 없이 구성되어 있습니다. 네트워크가 격리되어 있더라도 프로덕션 환경에서는 보안을 위해 비밀번호를 설정하는 것이 좋습니다.

🔒 제안된 개선사항
   redis:
     image: redis:alpine
     container_name: redis
+    command: redis-server --requirepass ${REDIS_PASSWORD}
     volumes:
       - redis_data:/data
     restart: unless-stopped
     networks:
       - mannabom-net
+    healthcheck:
+      test: ["CMD", "redis-cli", "-a", "${REDIS_PASSWORD}", "ping"]
+      interval: 10s
+      timeout: 5s
+      retries: 3

.env 파일에 REDIS_PASSWORD를 추가하고, application-prod.yml의 Redis 설정에도 비밀번호를 반영해야 합니다.

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

In `@mannabom-server/docker-compose.prod.yml` around lines 19 - 26, Add Redis
authentication by requiring a password for the redis service: add a
REDIS_PASSWORD variable to your environment (.env) and update the redis service
in docker-compose.prod.yml to pass that password into the container (e.g., via
environment or command/entrypoint that sets Redis' requirepass). Also update
your application-prod.yml Redis client configuration to use REDIS_PASSWORD for
authentication so the app connects with the password. Ensure the redis service
name "redis", the env var "REDIS_PASSWORD", and the Redis config in
application-prod.yml are all updated consistently.
mannabom-server/src/main/resources/application.yaml (1)

5-10: 베이스 설정 파일에 로컬 전용 값이 너무 많이 들어가 있습니다.

application.yaml은 모든 프로필의 공통 베이스인데, 여기서 로컬 DB/Redis/Mail/File 경로와 개발용 JWT/Kakao fallback을 함께 들고 있으면 프로필 누락 시 그대로 적용됩니다. 이미 application-local.yml를 분리해 둔 만큼, 공통값만 남기고 환경별 값은 각 프로필 파일로 옮기는 편이 안전합니다.

Also applies to: 33-57, 67-95

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

In `@mannabom-server/src/main/resources/application.yaml` around lines 5 - 10, The
base application.yaml currently contains environment-specific values (e.g.,
datasource.url, datasource.username, datasource.password,
datasource.driver-class-name and similar Redis, mail, file path, JWT and Kakao
fallback settings) that should live in profile-specific files; remove those
local/dev values from application.yaml leaving only generic/common keys and move
the local DB/Redis/Mail/File paths and development JWT/Kakao fallback settings
into application-local.yml (and other profile files as needed), ensuring
application.yaml contains defaults or placeholders only and reference to
profile-specific properties remains via the existing property names (e.g.,
datasource.*, redis.*, mail.*, jwt.*, kakao.*) so code (config classes or `@Value`
usages) continues to load them per active Spring profile.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@mannabom-server/.gitignore`:
- Around line 47-48: The .gitignore entries "mannabom-server/.env*" and
"mannabom-server/src/.env*" are wrong for a .gitignore located inside the
mannabom-server directory; update them to use repository-relative patterns so
local .env files are actually ignored—for example replace those two lines with
".env*" and "src/.env*" (or use "/.env*" and "/src/.env*" if you prefer explicit
root-anchored ignores) so the .env files in the project root and src are
reliably excluded.

In `@mannabom-server/build.gradle`:
- Around line 30-33: The JJWT dependencies use mixed versions (jjwt-api and
jjwt-jackson at 0.12.3 while jjwt-impl is 0.12.6); update the dependency
declarations so jjwt-api, jjwt-impl, and jjwt-jackson all use the exact same
version (e.g., change jjwt-api and jjwt-jackson to 0.12.6 to match jjwt-impl),
then rebuild and run tests to ensure no runtime NoSuchMethodError or
serialization mismatches; check the dependency lines for jjwt-api, jjwt-impl,
and jjwt-jackson and make the version uniform.

In `@mannabom-server/Dockerfile`:
- Around line 1-11: The Dockerfile currently runs the container as root
(ENTRYPOINT ["java","-jar","app.jar"]); create a non-root user and group (e.g.,
appuser), chown the working directory and app.jar to that user, and switch to it
via USER before the ENTRYPOINT so the JVM runs unprivileged; update referenced
Dockerfile steps around WORKDIR, COPY, and ENTRYPOINT to add user/group
creation, chown for app.jar, and the USER instruction.

In `@mannabom-server/src/main/resources/application-prod.yml`:
- Around line 93-95: 현재 application-prod.yml의 meeting.cursor.secret에 예측 가능한
기본값(local-dev-key-do-not-user-in-prod)이 설정되어 있어 운영에서 환경변수가 없을 때 취약해집니다; 수정 방법은
기본값을 제거하고 secret을 환경변수로 강제하도록 설정하세요 — 즉, 변경 대상은 키 meeting.cursor.secret과 환경변수 이름
MEETING_SECRET_KEY이며 파일 값을 ${MEETING_SECRET_KEY}로 바꾸고(기본값 제거) 애플리케이션 시작 시 해당 변수
누락 시 실패하도록(예: 스프링 프로퍼티로 기본값 없이 사용하거나 추가적인 프로퍼티 검증/빈 초기화 검증을 추가) 구성하세요.
- Around line 58-65: The production config under the kakao block currently
enables development overrides (development.skip-age-verification: true and dummy
default-birth-year/default-gender) which bypass age checks and inject fake
profiles in prod; turn off or remove these development keys for production by
deleting or moving the development subtree (including skip-age-verification,
default-birth-year, default-gender) into a non-prod/dev-only config and ensure
production uses only kakao.client-id and kakao.redirect-uri so age verification
runs normally in production.
- Around line 19-22: The Flyway config sets baseline-on-migrate: true which can
skip V1 and cause failures if migrations V2+ assume V1 objects; update the
flyway configuration in the application YAML (the flyway block containing
enabled, locations, baseline-on-migrate) to remove or set baseline-on-migrate to
false, and instead handle baselining explicitly via a controlled migration or a
documented manual baseline step so that V1 migrations always run in fresh
schemas and partially-populated schemas are treated safely.

In `@mannabom-server/src/main/resources/application.yaml`:
- Around line 13-20: The Hibernate properties are placed under the wrong path
and one key is duplicated; move default_batch_fetch_size into the
spring.jpa.properties.hibernate namespace and rename/remove the duplicated
hibernate.hibernate.use_sql_comments key so the setting becomes
spring.jpa.properties.hibernate.use_sql_comments (or remove the extra
"hibernate." prefix) to ensure both default_batch_fetch_size and
use_sql_comments live under spring.jpa.properties.hibernate.* and are applied by
Spring Boot 3.5.

In `@mannabom-server/src/main/resources/db/migration/V1__baseline.sql`:
- Around line 7-17: Add a foreign key constraint on public.device_token.user_id
to ensure it references public.users(user_id) and prevent orphan tokens; modify
the V1__baseline.sql by adding a CONSTRAINT (e.g., fk_device_token_user)
referencing public.users(user_id) with an appropriate ON DELETE behavior
(recommend ON DELETE CASCADE to remove tokens when a user is deleted, or ON
DELETE SET NULL if you prefer keeping tokens) so look for table device_token and
column user_id and add the FK constraint alongside the existing primary
key/unique definitions.

In
`@mannabom-server/src/main/resources/db/migration/V2__add_meeting_and_chat.sql`:
- Around line 90-110: The column chat_members.last_read_message_id uses int4
while chat_messages.id is int8; change chat_members.last_read_message_id to int8
(bigint) to match chat_messages.id and add a nullable foreign key constraint
(e.g., fk_chat_members_last_read_message) referencing public.chat_messages(id)
so the read-pointer aligns with the message PK and can enforce referential
integrity; ensure nullability remains allowed if no read message exists.

In
`@mannabom-server/src/main/resources/db/migration/V7__add_source_to_message_request.sql`:
- Around line 1-2: 현재 마이그레이션(V7__add_source_to_message_request.sql)의 ALTER TABLE
message_request ADD COLUMN source 구문이 비멱등이라 이미 컬럼이 있는 DB에서 실패할 수 있으니, 마이그레이션을 컬럼
존재 여부를 확인한 후에만 컬럼을 추가하도록 수정하세요; 구체적으로 V7__add_source_to_message_request.sql에서
컬럼이 없을 때만 추가되도록 하는 방식(예: DB가 지원하면 ALTER TABLE ... ADD COLUMN IF NOT EXISTS를
사용하거나, 정보 스키마를 조회해 존재 여부를 확인하고 조건부로 ALTER를 실행하는 로직으로 변경)으로 바꿔 배포 실패를 방지하세요.

In
`@mannabom-server/src/main/resources/db/migration/V8__migrate_profile_data_and_add_column_chat_room_table.sql`:
- Around line 3-21: The migration drops original columns immediately after a
best-effort UPDATE, risking data loss; modify the migration to (1) preserve
source data by copying public.profile.university, region_sido, region_sigungu
into a backup table or new backup columns before any DELETE/DROP, (2) add a
verification step that compares how many rows were successfully migrated to
university_id and region_id (using selectors on public.profile.university_id and
region_id) and reports or fails the migration if there are any unmatched rows,
and (3) only perform ALTER TABLE DROP COLUMN for university, region_sido,
region_sigungu after successful verification (or provide a safe manual
remediation path), referencing the UPDATE statements on public.profile (SET
university_id = u.university_id ... FROM public.university u) and (SET region_id
= r.sigungu_code ... FROM public.regions r) and the ALTER TABLE DROP COLUMN
statements so you can locate and change them.

---

Nitpick comments:
In `@mannabom-server/.github/pull_request_template.md`:
- Around line 10-14: Add two additional checklist items under the existing "##
테스트" section: include "- [ ] 관련 문서 업데이트" to ensure README/API docs are updated
and "- [ ] Breaking changes 확인" to verify whether changes introduce breaking
behavior; update the checklist near the existing items (the "## 테스트" heading and
the three checkboxes) so reviewers must confirm documentation updates and
breaking-change checks before merging.
- Line 22: Add a single trailing newline to the end of the pull request template
file so the file ends with a blank line; open
mannabom-server/.github/pull_request_template.md and append one newline
character at the end of the file (ensure the file terminates with '\n') to
comply with POSIX/Git conventions.

In `@mannabom-server/docker-compose.prod.yml`:
- Around line 2-17: 서비스 블록 mannabom-server에 restart 정책과 헬스체크를 추가하여 컨테이너 충돌 시 자동
재시작과 상태 기반 의존성을 보장하십시오: mannabom-server에 restart (예: "always" 또는
"unless-stopped")를 설정하고 healthcheck를 추가하여 HTTP/TCP 또는 커맨드 기반 상태 확인을 수행하도록 구성하고,
depends_on 항목을 condition: service_healthy로 전환하거나 redis에 healthcheck를 추가하여 Redis가
실제로 준비되었을 때만 mannabom-server가 시작되게 하세요; 관련 키: mannabom-server, restart,
healthcheck, depends_on, redis, condition: service_healthy.
- Around line 19-26: Add Redis authentication by requiring a password for the
redis service: add a REDIS_PASSWORD variable to your environment (.env) and
update the redis service in docker-compose.prod.yml to pass that password into
the container (e.g., via environment or command/entrypoint that sets Redis'
requirepass). Also update your application-prod.yml Redis client configuration
to use REDIS_PASSWORD for authentication so the app connects with the password.
Ensure the redis service name "redis", the env var "REDIS_PASSWORD", and the
Redis config in application-prod.yml are all updated consistently.

In `@mannabom-server/Dockerfile`:
- Around line 5-6: The Dockerfile currently uses a glob ARG and COPY
(${JAR_FILE} / app.jar), which is unstable when multiple JARs exist; update the
ARG JAR_FILE and COPY to reference the exact artifact name (e.g., set ARG
JAR_FILE=build/libs/your-artifact-name.jar and keep COPY ${JAR_FILE} app.jar) or
require passing the precise --build-arg JAR_FILE=<artifact> during docker build
so the intended JAR is unambiguous; ensure the change touches the ARG JAR_FILE
and the COPY ${JAR_FILE} app.jar lines.

In `@mannabom-server/src/main/resources/application.yaml`:
- Around line 5-10: The base application.yaml currently contains
environment-specific values (e.g., datasource.url, datasource.username,
datasource.password, datasource.driver-class-name and similar Redis, mail, file
path, JWT and Kakao fallback settings) that should live in profile-specific
files; remove those local/dev values from application.yaml leaving only
generic/common keys and move the local DB/Redis/Mail/File paths and development
JWT/Kakao fallback settings into application-local.yml (and other profile files
as needed), ensuring application.yaml contains defaults or placeholders only and
reference to profile-specific properties remains via the existing property names
(e.g., datasource.*, redis.*, mail.*, jwt.*, kakao.*) so code (config classes or
`@Value` usages) continues to load them per active Spring profile.

In
`@mannabom-server/src/main/resources/db/migration/V3__add_request_table_and_extra_photo_table_and_rating_table.sql`:
- Around line 46-77: Update the two CREATE TABLE statements so their timestamp
columns follow the same not-null policy as other tables: change
profile_extra_photo_unlock.created_at and profile_rating.created_at to be
declared TIMESTAMP NOT NULL (and optionally make updated_at NOT NULL as well if
your schema requires it) so the tables profile_extra_photo_unlock and
profile_rating match the non-null created_at behavior of like_request and
message_request.
🪄 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: c410a967-d772-4c70-89a8-12c7b8c4dd8c

📥 Commits

Reviewing files that changed from the base of the PR and between 1cbbf77 and 9ed14be.

⛔ Files ignored due to path filters (3)
  • mannabom-server/gradle/wrapper/gradle-wrapper.jar is excluded by !**/*.jar
  • mannabom-server/src/main/resources/data/regions.csv is excluded by !**/*.csv
  • mannabom-server/src/main/resources/data/regions_original.csv is excluded by !**/*.csv
📒 Files selected for processing (23)
  • mannabom-server/.gitattributes
  • mannabom-server/.github/pull_request_template.md
  • mannabom-server/.gitignore
  • mannabom-server/Dockerfile
  • mannabom-server/build.gradle
  • mannabom-server/docker-compose.local.yml
  • mannabom-server/docker-compose.prod.yml
  • mannabom-server/gradle/wrapper/gradle-wrapper.properties
  • mannabom-server/gradlew
  • mannabom-server/gradlew.bat
  • mannabom-server/settings.gradle
  • mannabom-server/src/main/java/com/mannabom/mannabom_server/MannabomServerApplication.java
  • mannabom-server/src/main/resources/application-prod.yml
  • mannabom-server/src/main/resources/application.yaml
  • mannabom-server/src/main/resources/db/migration/V1__baseline.sql
  • mannabom-server/src/main/resources/db/migration/V2__add_meeting_and_chat.sql
  • mannabom-server/src/main/resources/db/migration/V3__add_request_table_and_extra_photo_table_and_rating_table.sql
  • mannabom-server/src/main/resources/db/migration/V4__add_reject_reason_to_like_request.sql
  • mannabom-server/src/main/resources/db/migration/V5__add_reject_reason_to_message_request.sql
  • mannabom-server/src/main/resources/db/migration/V6__add_source_to_like_request.sql
  • mannabom-server/src/main/resources/db/migration/V7__add_source_to_message_request.sql
  • mannabom-server/src/main/resources/db/migration/V8__migrate_profile_data_and_add_column_chat_room_table.sql
  • mannabom-server/src/test/java/com/mannabom/mannabom_server/MannabomServerApplicationTests.java

Comment thread .gitignore
Comment thread mannabom-server/build.gradle
Comment on lines +1 to +11
FROM eclipse-temurin:17-jre-alpine

WORKDIR /app

ARG JAR_FILE=build/libs/*.jar
COPY ${JAR_FILE} app.jar

ENV TZ=Asia/Seoul
RUN apk add --no-cache tzdata

ENTRYPOINT ["java", "-jar","app.jar"] No newline at end of file
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

컨테이너가 root 사용자로 실행됩니다.

런타임은 non-root로 강제하는 것이 안전합니다.

🔧 제안 수정
 FROM eclipse-temurin:17-jre-alpine

 WORKDIR /app

 ARG JAR_FILE=build/libs/*.jar
 COPY ${JAR_FILE} app.jar

 ENV TZ=Asia/Seoul
 RUN apk add --no-cache tzdata
+RUN addgroup -S app && adduser -S app -G app
+RUN chown -R app:app /app
+USER app

 ENTRYPOINT ["java", "-jar","app.jar"]
📝 Committable suggestion

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

Suggested change
FROM eclipse-temurin:17-jre-alpine
WORKDIR /app
ARG JAR_FILE=build/libs/*.jar
COPY ${JAR_FILE} app.jar
ENV TZ=Asia/Seoul
RUN apk add --no-cache tzdata
ENTRYPOINT ["java", "-jar","app.jar"]
FROM eclipse-temurin:17-jre-alpine
WORKDIR /app
ARG JAR_FILE=build/libs/*.jar
COPY ${JAR_FILE} app.jar
ENV TZ=Asia/Seoul
RUN apk add --no-cache tzdata
RUN addgroup -S app && adduser -S app -G app
RUN chown -R app:app /app
USER app
ENTRYPOINT ["java", "-jar","app.jar"]
🧰 Tools
🪛 Trivy (0.69.3)

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

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

Rule: DS-0002

Learn more

(IaC/Dockerfile)

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

In `@mannabom-server/Dockerfile` around lines 1 - 11, The Dockerfile currently
runs the container as root (ENTRYPOINT ["java","-jar","app.jar"]); create a
non-root user and group (e.g., appuser), chown the working directory and app.jar
to that user, and switch to it via USER before the ENTRYPOINT so the JVM runs
unprivileged; update referenced Dockerfile steps around WORKDIR, COPY, and
ENTRYPOINT to add user/group creation, chown for app.jar, and the USER
instruction.

Comment on lines +19 to +22
flyway:
enabled: true
locations: classpath:db/migration
baseline-on-migrate: true
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

🌐 Web query:

Flyway에서 baselineOnMigrate=true일 때, non-empty 스키마에 flyway_schema_history가 없으면 V1__baseline.sql은 실제로 실행되나요? 아니면 baseline version으로만 마킹하고 V2부터 진행하나요?

💡 Result:

baselineOnMigrate=true이고 (1) 스키마가 non-empty인데 (2) flyway_schema_history가 없으면, migrate 실행 시 Flyway는 먼저 **baseline을 수행(= 스키마 히스토리 테이블 생성 + baseline row 기록)**하고, 그 다음 baseline version보다 큰 마이그레이션부터 적용합니다. 즉, 보통 baselineVersion 기본값이 1이면:

  • V1__baseline.sql실제로 실행되지 않습니다.
  • DB에는 flyway_schema_historytype=BASELINE, version=1 같은 식으로 “baseline으로 마킹”만 되고
  • 실제 마이그레이션은 V2...부터 진행됩니다. (documentation.red-gate.com)

추가로, 파일로 “baseline 스크립트”를 두고 실행시키는 방식(보통 B... prefix의 baseline migration)은 baselineOnMigrate만으로 자동 실행되는 개념이 아니라, 별도 프로세스/콜백 등으로 명시적으로 구성해야 한다는 점도 문서에서 언급됩니다. (documentation.red-gate.com)

Citations:


baseline-on-migrate: true은 이번 마이그레이션 구성과 충돌할 수 있습니다.

운영 DB가 비어 있지 않지만 flyway_schema_history만 없는 상태에서 마이그레이션을 실행하면, Flyway는 V1__baseline.sql을 실행하지 않고 baseline으로 표시한 뒤 V2부터 진행합니다. 이번 PR에서 V2+ 마이그레이션들이 V1의 객체 존재를 가정한다면, 부분적으로 채워진 스키마에서 배포가 중간에 멈출 수 있습니다.

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

In `@mannabom-server/src/main/resources/application-prod.yml` around lines 19 -
22, The Flyway config sets baseline-on-migrate: true which can skip V1 and cause
failures if migrations V2+ assume V1 objects; update the flyway configuration in
the application YAML (the flyway block containing enabled, locations,
baseline-on-migrate) to remove or set baseline-on-migrate to false, and instead
handle baselining explicitly via a controlled migration or a documented manual
baseline step so that V1 migrations always run in fresh schemas and
partially-populated schemas are treated safely.

Comment on lines +58 to +65
# 카카오 OAuth 설정 (프로덕션)
kakao:
client-id: ${KAKAO_CLIENT_ID}
redirect-uri: ${KAKAO_REDIRECT_URI:https://your-domain.com/api/auth/login/kakao}
development:
skip-age-verification: true # 이거 아직 카카오 디벨로퍼 준비 안되서
default-birth-year: 2000
default-gender: "MALE"
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

운영 프로필에서 연령 검증 우회가 켜져 있습니다.

Line 63의 skip-age-verification: true와 Line 64-65의 더미값은 운영 가입 데이터를 검증 없이 통과시키거나 가짜 프로필 값으로 채우게 만들 수 있습니다. prod 설정에서는 우회를 꺼야 합니다.

🔧 제안 수정
   kakao:
     client-id: ${KAKAO_CLIENT_ID}
     redirect-uri: ${KAKAO_REDIRECT_URI:https://your-domain.com/api/auth/login/kakao}
     development:
-      skip-age-verification: true  # 이거 아직 카카오 디벨로퍼 준비 안되서
-      default-birth-year: 2000
-      default-gender: "MALE"
+      skip-age-verification: false
📝 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
# 카카오 OAuth 설정 (프로덕션)
kakao:
client-id: ${KAKAO_CLIENT_ID}
redirect-uri: ${KAKAO_REDIRECT_URI:https://your-domain.com/api/auth/login/kakao}
development:
skip-age-verification: true # 이거 아직 카카오 디벨로퍼 준비 안되서
default-birth-year: 2000
default-gender: "MALE"
# 카카오 OAuth 설정 (프로덕션)
kakao:
client-id: ${KAKAO_CLIENT_ID}
redirect-uri: ${KAKAO_REDIRECT_URI:https://your-domain.com/api/auth/login/kakao}
development:
skip-age-verification: false
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@mannabom-server/src/main/resources/application-prod.yml` around lines 58 -
65, The production config under the kakao block currently enables development
overrides (development.skip-age-verification: true and dummy
default-birth-year/default-gender) which bypass age checks and inject fake
profiles in prod; turn off or remove these development keys for production by
deleting or moving the development subtree (including skip-age-verification,
default-birth-year, default-gender) into a non-prod/dev-only config and ensure
production uses only kakao.client-id and kakao.redirect-uri so age verification
runs normally in production.

Comment thread mannabom-server/src/main/resources/application.yaml
Comment on lines +7 to +17
CREATE TABLE public.device_token (
id int8 GENERATED BY DEFAULT AS IDENTITY( INCREMENT BY 1 MINVALUE 1 MAXVALUE 9223372036854775807 START 1 CACHE 1 NO CYCLE) NOT NULL,
active bool NOT NULL,
last_seen_at timestamptz(6) NULL,
"token" varchar(255) NOT NULL,
user_id int8 NULL,
CONSTRAINT device_token_pkey PRIMARY KEY (id),
CONSTRAINT idx_device_token_token UNIQUE (token)
);
CREATE INDEX idx_device_token_active ON public.device_token USING btree (active);
CREATE INDEX idx_device_token_user ON public.device_token USING btree (user_id);
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

device_token.user_id에 FK가 빠져 있습니다.

public.device_token.user_id는 인덱스만 있고 public.users(user_id)를 보장하지 않습니다. 사용자 삭제/정리 이후 고아 토큰이 남으면 푸시 대상 조회가 틀어질 수 있습니다.

🔧 제안 수정
 CREATE TABLE public.refresh_tokens (
                                        token_id int8 GENERATED BY DEFAULT AS IDENTITY( INCREMENT BY 1 MINVALUE 1 MAXVALUE 9223372036854775807 START 1 CACHE 1 NO CYCLE) NOT NULL,
                                        created_at timestamp(6) NOT NULL,
                                        updated_at timestamp(6) NOT NULL,
                                        expires_at timestamp(6) NOT NULL,
                                        refresh_token varchar(512) NOT NULL,
                                        user_id int8 NOT NULL,
                                        CONSTRAINT refresh_tokens_pkey PRIMARY KEY (token_id),
                                        CONSTRAINT uk1yihy5j142kjit22kgccjixro UNIQUE (refresh_token),
                                        CONSTRAINT uk7tdcd6ab5wsgoudnvj7xf1b7l UNIQUE (user_id),
                                        CONSTRAINT fk1lih5y2npsf8u5o3vhdb9y0os FOREIGN KEY (user_id) REFERENCES public.users(user_id)
 );
+
+ALTER TABLE public.device_token
+    ADD CONSTRAINT fk_device_token_user
+        FOREIGN KEY (user_id) REFERENCES public.users(user_id);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@mannabom-server/src/main/resources/db/migration/V1__baseline.sql` around
lines 7 - 17, Add a foreign key constraint on public.device_token.user_id to
ensure it references public.users(user_id) and prevent orphan tokens; modify the
V1__baseline.sql by adding a CONSTRAINT (e.g., fk_device_token_user) referencing
public.users(user_id) with an appropriate ON DELETE behavior (recommend ON
DELETE CASCADE to remove tokens when a user is deleted, or ON DELETE SET NULL if
you prefer keeping tokens) so look for table device_token and column user_id and
add the FK constraint alongside the existing primary key/unique definitions.

Comment on lines +90 to +110
CREATE TABLE public.chat_members (
id int8 GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
joined_at timestamptz(6) NULL,
last_read_message_id int4 NULL,
room_id int8 NOT NULL,
user_id int8 NOT NULL,
CONSTRAINT fk_chat_members_room FOREIGN KEY (room_id) REFERENCES public.chat_rooms(id),
CONSTRAINT fk_chat_members_user FOREIGN KEY (user_id) REFERENCES public.users(user_id)
);

CREATE TABLE public.chat_messages (
id int8 GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
created_at timestamptz(6) NOT NULL,
updated_at timestamptz(6) NOT NULL,
"content" varchar(255) NULL,
"type" int2 NULL,
room_id int8 NULL,
sender_user_id int8 NULL,
CONSTRAINT fk_chat_msg_room FOREIGN KEY (room_id) REFERENCES public.chat_rooms(id),
CONSTRAINT fk_chat_msg_user FOREIGN KEY (sender_user_id) REFERENCES public.users(user_id)
);
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

last_read_message_id 타입이 실제 메시지 PK와 다릅니다.

Line 93은 int4인데, 같은 파일 Line 101의 public.chat_messages.idint8입니다. 읽음 포인터를 메시지 PK로 쓰면 범위 초과 가능성이 있고, 나중에 FK를 붙이기도 어렵습니다.

🔧 제안 수정
 CREATE TABLE public.chat_members (
                                      id int8 GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
                                      joined_at timestamptz(6) NULL,
-                                     last_read_message_id int4 NULL,
+                                     last_read_message_id int8 NULL,
                                      room_id int8 NOT NULL,
                                      user_id int8 NOT NULL,
                                      CONSTRAINT fk_chat_members_room FOREIGN KEY (room_id) REFERENCES public.chat_rooms(id),
                                      CONSTRAINT fk_chat_members_user FOREIGN KEY (user_id) REFERENCES public.users(user_id)
 );
📝 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
CREATE TABLE public.chat_members (
id int8 GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
joined_at timestamptz(6) NULL,
last_read_message_id int4 NULL,
room_id int8 NOT NULL,
user_id int8 NOT NULL,
CONSTRAINT fk_chat_members_room FOREIGN KEY (room_id) REFERENCES public.chat_rooms(id),
CONSTRAINT fk_chat_members_user FOREIGN KEY (user_id) REFERENCES public.users(user_id)
);
CREATE TABLE public.chat_messages (
id int8 GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
created_at timestamptz(6) NOT NULL,
updated_at timestamptz(6) NOT NULL,
"content" varchar(255) NULL,
"type" int2 NULL,
room_id int8 NULL,
sender_user_id int8 NULL,
CONSTRAINT fk_chat_msg_room FOREIGN KEY (room_id) REFERENCES public.chat_rooms(id),
CONSTRAINT fk_chat_msg_user FOREIGN KEY (sender_user_id) REFERENCES public.users(user_id)
);
CREATE TABLE public.chat_members (
id int8 GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
joined_at timestamptz(6) NULL,
last_read_message_id int8 NULL,
room_id int8 NOT NULL,
user_id int8 NOT NULL,
CONSTRAINT fk_chat_members_room FOREIGN KEY (room_id) REFERENCES public.chat_rooms(id),
CONSTRAINT fk_chat_members_user FOREIGN KEY (user_id) REFERENCES public.users(user_id)
);
CREATE TABLE public.chat_messages (
id int8 GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
created_at timestamptz(6) NOT NULL,
updated_at timestamptz(6) NOT NULL,
"content" varchar(255) NULL,
"type" int2 NULL,
room_id int8 NULL,
sender_user_id int8 NULL,
CONSTRAINT fk_chat_msg_room FOREIGN KEY (room_id) REFERENCES public.chat_rooms(id),
CONSTRAINT fk_chat_msg_user FOREIGN KEY (sender_user_id) REFERENCES public.users(user_id)
);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@mannabom-server/src/main/resources/db/migration/V2__add_meeting_and_chat.sql`
around lines 90 - 110, The column chat_members.last_read_message_id uses int4
while chat_messages.id is int8; change chat_members.last_read_message_id to int8
(bigint) to match chat_messages.id and add a nullable foreign key constraint
(e.g., fk_chat_members_last_read_message) referencing public.chat_messages(id)
so the read-pointer aligns with the message PK and can enforce referential
integrity; ensure nullability remains allowed if no read message exists.

Comment on lines +1 to +2
ALTER TABLE message_request
ADD COLUMN source VARCHAR(30);
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

source 컬럼 추가가 비멱등이라 특정 환경에서 마이그레이션 실패 가능성이 있습니다.

이미 컬럼이 존재하는 DB(수동 반영/부분 이관 환경)에서는 배포가 중단될 수 있습니다.

🔧 제안 수정
 ALTER TABLE message_request
-ADD COLUMN source VARCHAR(30);
+ADD COLUMN IF NOT EXISTS source VARCHAR(30);
📝 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
ALTER TABLE message_request
ADD COLUMN source VARCHAR(30);
ALTER TABLE message_request
ADD COLUMN IF NOT EXISTS source VARCHAR(30);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@mannabom-server/src/main/resources/db/migration/V7__add_source_to_message_request.sql`
around lines 1 - 2, 현재 마이그레이션(V7__add_source_to_message_request.sql)의 ALTER
TABLE message_request ADD COLUMN source 구문이 비멱등이라 이미 컬럼이 있는 DB에서 실패할 수 있으니,
마이그레이션을 컬럼 존재 여부를 확인한 후에만 컬럼을 추가하도록 수정하세요; 구체적으로
V7__add_source_to_message_request.sql에서 컬럼이 없을 때만 추가되도록 하는 방식(예: DB가 지원하면 ALTER
TABLE ... ADD COLUMN IF NOT EXISTS를 사용하거나, 정보 스키마를 조회해 존재 여부를 확인하고 조건부로 ALTER를
실행하는 로직으로 변경)으로 바꿔 배포 실패를 방지하세요.

Comment on lines +3 to +21
UPDATE public.profile p
SET university_id = u.university_id
FROM public.university u
WHERE p.university = u.name
AND p.university_id IS NULL; -- 이미 값이 있는 건 건드리지 않음

-- 2. [데이터 이관] 기존 문자열(region) -> 새로운 ID(region_id) 매핑
-- 시도/시군구 이름이 모두 일치하는 경우 업데이트
UPDATE public.profile p
SET region_id = r.sigungu_code
FROM public.regions r
WHERE p.region_sido = r.sido_name
AND p.region_sigungu = r.sigungu_name
AND p.region_id IS NULL;

-- 3. [컬럼 삭제] 데이터 이관이 끝났으므로 기존 컬럼 삭제
ALTER TABLE public.profile DROP COLUMN IF EXISTS university;
ALTER TABLE public.profile DROP COLUMN IF EXISTS region_sido;
ALTER TABLE public.profile DROP COLUMN IF EXISTS region_sigungu;
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

이관 검증 없이 원본 컬럼을 바로 드롭하면 데이터가 유실됩니다.

현재 UPDATE는 이름이 정확히 일치한 행만 채우고, 바로 university/region_* 컬럼을 삭제합니다. 공백·표기 차이·누락 마스터 데이터가 한 건이라도 있으면 복구 경로 없이 원본값을 잃습니다.

🔧 제안 수정
 UPDATE public.profile p
 SET region_id = r.sigungu_code
     FROM public.regions r
 WHERE p.region_sido = r.sido_name
   AND p.region_sigungu = r.sigungu_name
   AND p.region_id IS NULL;
 
+DO $$
+BEGIN
+    IF EXISTS (
+        SELECT 1
+        FROM public.profile p
+        WHERE p.university IS NOT NULL
+          AND p.university_id IS NULL
+    ) THEN
+        RAISE EXCEPTION 'Unmapped university rows remain in public.profile';
+    END IF;
+
+    IF EXISTS (
+        SELECT 1
+        FROM public.profile p
+        WHERE (p.region_sido IS NOT NULL OR p.region_sigungu IS NOT NULL)
+          AND p.region_id IS NULL
+    ) THEN
+        RAISE EXCEPTION 'Unmapped region rows remain in public.profile';
+    END IF;
+END $$;
+
 -- 3. [컬럼 삭제] 데이터 이관이 끝났으므로 기존 컬럼 삭제
 ALTER TABLE public.profile DROP COLUMN IF EXISTS university;
 ALTER TABLE public.profile DROP COLUMN IF EXISTS region_sido;
 ALTER TABLE public.profile DROP COLUMN IF EXISTS region_sigungu;
📝 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
UPDATE public.profile p
SET university_id = u.university_id
FROM public.university u
WHERE p.university = u.name
AND p.university_id IS NULL; -- 이미 값이 있는 건 건드리지 않음
-- 2. [데이터 이관] 기존 문자열(region) -> 새로운 ID(region_id) 매핑
-- 시도/시군구 이름이 모두 일치하는 경우 업데이트
UPDATE public.profile p
SET region_id = r.sigungu_code
FROM public.regions r
WHERE p.region_sido = r.sido_name
AND p.region_sigungu = r.sigungu_name
AND p.region_id IS NULL;
-- 3. [컬럼 삭제] 데이터 이관이 끝났으므로 기존 컬럼 삭제
ALTER TABLE public.profile DROP COLUMN IF EXISTS university;
ALTER TABLE public.profile DROP COLUMN IF EXISTS region_sido;
ALTER TABLE public.profile DROP COLUMN IF EXISTS region_sigungu;
UPDATE public.profile p
SET university_id = u.university_id
FROM public.university u
WHERE p.university = u.name
AND p.university_id IS NULL; -- 이미 값이 있는 건 건드리지 않음
-- 2. [데이터 이관] 기존 문자열(region) -> 새로운 ID(region_id) 매핑
-- 시도/시군구 이름이 모두 일치하는 경우 업데이트
UPDATE public.profile p
SET region_id = r.sigungu_code
FROM public.regions r
WHERE p.region_sido = r.sido_name
AND p.region_sigungu = r.sigungu_name
AND p.region_id IS NULL;
DO $$
BEGIN
IF EXISTS (
SELECT 1
FROM public.profile p
WHERE p.university IS NOT NULL
AND p.university_id IS NULL
) THEN
RAISE EXCEPTION 'Unmapped university rows remain in public.profile';
END IF;
IF EXISTS (
SELECT 1
FROM public.profile p
WHERE (p.region_sido IS NOT NULL OR p.region_sigungu IS NOT NULL)
AND p.region_id IS NULL
) THEN
RAISE EXCEPTION 'Unmapped region rows remain in public.profile';
END IF;
END $$;
-- 3. [컬럼 삭제] 데이터 이관이 끝났으므로 기존 컬럼 삭제
ALTER TABLE public.profile DROP COLUMN IF EXISTS university;
ALTER TABLE public.profile DROP COLUMN IF EXISTS region_sido;
ALTER TABLE public.profile DROP COLUMN IF EXISTS region_sigungu;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@mannabom-server/src/main/resources/db/migration/V8__migrate_profile_data_and_add_column_chat_room_table.sql`
around lines 3 - 21, The migration drops original columns immediately after a
best-effort UPDATE, risking data loss; modify the migration to (1) preserve
source data by copying public.profile.university, region_sido, region_sigungu
into a backup table or new backup columns before any DELETE/DROP, (2) add a
verification step that compares how many rows were successfully migrated to
university_id and region_id (using selectors on public.profile.university_id and
region_id) and reports or fails the migration if there are any unmatched rows,
and (3) only perform ALTER TABLE DROP COLUMN for university, region_sido,
region_sigungu after successful verification (or provide a safe manual
remediation path), referencing the UPDATE statements on public.profile (SET
university_id = u.university_id ... FROM public.university u) and (SET region_id
= r.sigungu_code ... FROM public.regions r) and the ALTER TABLE DROP COLUMN
statements so you can locate and change them.

[Setting] 테스트 자동화 설정
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: 2

🧹 Nitpick comments (1)
mannabom-server/.github/workflows/ci.yml (1)

72-78: diff-cover 80% 임계치와 jacocoTestCoverageVerification 70% 임계치의 이원화 의도 확인이 필요합니다.

./gradlew ... jacocoTestCoverageVerification은 전체 번들 기준 70% 미만이면 이미 실패하며(build.gradle Line 108), 그 이후 단계인 diff-cover는 PR 변경분에 대해 80%를 요구합니다. 의도한 정책(전체 70% / 변경분 80%)이 맞다면 좋지만, 한 가지 주의점이 있습니다. jacocoTestCoverageVerification 단계에서 먼저 실패하면 JaCoCo XML은 생성되지만 이후 diff-cover 단계는 실행되지 않아 PR 변경분의 커버리지 정보를 얻지 못합니다. 실패 상황에서도 변경분 커버리지 리포트가 필요하다면 diff-cover 단계에 if: always()를 추가하거나 jacocoTestCoverageVerificationcheck에서 분리해 CI에서 별도 단계로 실행하는 방식을 고려해 보세요.

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

In `@mannabom-server/.github/workflows/ci.yml` around lines 72 - 78, The CI
currently enforces a global jacocoTestCoverageVerification threshold
(jacocoTestCoverageVerification) at 70% and then enforces an 80% diff-cover
check for PR changes (diff-cover), which means if the jacoco verification step
fails the job may stop before diff-cover can produce change-level coverage; to
ensure diff-cover still runs (so you get PR-level coverage even when global
verification fails) change the workflow so the diff-cover step runs regardless
of prior failure (use if: always()) or separate jacocoTestCoverageVerification
out from the main check flow into its own step/job so diff-cover always executes
and emits its report.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@mannabom-server/.github/workflows/ci.yml`:
- Around line 15-16: The CI workflow uses an EOL PostgreSQL image ("image:
postgres:13" under the "postgres:" service); update the image tag to a supported
release such as "postgres:16" (or a specific variant like "postgres:16-alpine")
to restore security/bug fixes and reproducible builds—replace "image:
postgres:13" with the newer tag and run the CI to verify
migrations/compatibility.

In `@mannabom-server/build.gradle`:
- Around line 97-114: The current jacocoTestCoverageVerification rule
(jacocoTestCoverageVerification, violationRules -> rule, element = 'BUNDLE',
limit -> minimum = 0.70) will fail CI immediately for initial PRs; update the
task by either lowering the minimum (e.g., 0.0–0.3), removing
jacocoTestCoverageVerification from check (remove check.dependsOn
jacocoTestCoverageVerification) and run it as a separate CI task, or add
excludes via afterEvaluate and classDirectories to omit
generated/entrypoint/config/dto classes (use patterns like **/Q*.class,
**/*Application*, **/config/**, **/dto/**) so the BUNDLE denominator excludes
those files; pick one approach and apply it to jacocoTestCoverageVerification
configuration.

---

Nitpick comments:
In `@mannabom-server/.github/workflows/ci.yml`:
- Around line 72-78: The CI currently enforces a global
jacocoTestCoverageVerification threshold (jacocoTestCoverageVerification) at 70%
and then enforces an 80% diff-cover check for PR changes (diff-cover), which
means if the jacoco verification step fails the job may stop before diff-cover
can produce change-level coverage; to ensure diff-cover still runs (so you get
PR-level coverage even when global verification fails) change the workflow so
the diff-cover step runs regardless of prior failure (use if: always()) or
separate jacocoTestCoverageVerification out from the main check flow into its
own step/job so diff-cover always executes and emits its report.
🪄 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: 7cb4de70-4b32-4571-927c-5de48d6205cd

📥 Commits

Reviewing files that changed from the base of the PR and between 9ed14be and 7334f60.

📒 Files selected for processing (5)
  • .gitignore
  • mannabom-server/.github/PULL_REQUEST_TEMPLATE/default.md
  • mannabom-server/.github/workflows/ci.yml
  • mannabom-server/build.gradle
  • mannabom-server/src/main/resources/application-test.yml
✅ Files skipped from review due to trivial changes (3)
  • mannabom-server/.github/PULL_REQUEST_TEMPLATE/default.md
  • .gitignore
  • mannabom-server/src/main/resources/application-test.yml

Comment on lines +15 to +16
postgres:
image: postgres:13
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

🧩 Analysis chain

🌐 Web query:

PostgreSQL 13 end of life date community support

💡 Result:

PostgreSQL 13 reached end of life for community support on November 13, 2025. After this date, it no longer receives security patches or bug fixes from the PostgreSQL Global Development Group. As of April 23, 2026, it is unsupported.

Citations:


PostgreSQL 13은 이미 EOL 지원이 종료되었습니다.

PostgreSQL 13의 커뮤니티 지원은 2025년 11월 13일에 종료되었으며, 현재 보안 패치나 버그 수정을 받지 않습니다. CI 환경에서 지원 종료된 데이터베이스 버전을 사용하는 것은 보안 및 유지보수 측면에서 권장되지 않습니다. postgres:15 또는 postgres:16으로 업그레이드하고, 재현성을 위해 postgres:16-alpine 같이 좀 더 구체적인 태그를 사용하세요.

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

In `@mannabom-server/.github/workflows/ci.yml` around lines 15 - 16, The CI
workflow uses an EOL PostgreSQL image ("image: postgres:13" under the
"postgres:" service); update the image tag to a supported release such as
"postgres:16" (or a specific variant like "postgres:16-alpine") to restore
security/bug fixes and reproducible builds—replace "image: postgres:13" with the
newer tag and run the CI to verify migrations/compatibility.

Comment on lines +97 to +114
jacocoTestCoverageVerification {
dependsOn test

violationRules {
rule {
enabled = true
element = 'BUNDLE'

limit {
counter = 'LINE'
value = 'COVEREDRATIO'
minimum = 0.70
}
}
}
}

check.dependsOn jacocoTestCoverageVerification No newline at end of file
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 | 🟡 Minor

전체 번들 70% 기준이 초기 이관 단계에서 CI를 즉시 실패시킬 수 있습니다.

초기 세팅 PR 단계에서는 테스트 코드가 거의 없어 check 실행 시 jacocoTestCoverageVerification이 바로 실패하여 빌드·배포 파이프라인이 막힐 수 있습니다. 또한 현재 element = 'BUNDLE' 한 가지 룰만 있어 generated 코드(Q-class, Lombok 산출물), DTO, Config 등이 모두 분모에 포함됩니다. 아래 중 상황에 맞게 선택하시길 권장합니다.

  • 초기 단계에서는 minimum을 낮추거나(예: 0.0~0.3), excludes로 엔트리포인트/Q-class/DTO를 제외
  • CI 워크플로에서 PR 변경분 80%(diff-cover)로 실질 게이팅하고, 번들 기준은 점진적으로 상향
  • jacocoTestCoverageVerificationcheck에서 떼어내고 별도 태스크로 CI에서만 호출

afterEvaluate + classDirectories에 exclude 패턴을 추가하는 패턴도 함께 검토해 보세요 (**/Q*.class, **/*Application*, **/config/**, **/dto/** 등).

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

In `@mannabom-server/build.gradle` around lines 97 - 114, The current
jacocoTestCoverageVerification rule (jacocoTestCoverageVerification,
violationRules -> rule, element = 'BUNDLE', limit -> minimum = 0.70) will fail
CI immediately for initial PRs; update the task by either lowering the minimum
(e.g., 0.0–0.3), removing jacocoTestCoverageVerification from check (remove
check.dependsOn jacocoTestCoverageVerification) and run it as a separate CI
task, or add excludes via afterEvaluate and classDirectories to omit
generated/entrypoint/config/dto classes (use patterns like **/Q*.class,
**/*Application*, **/config/**, **/dto/**) so the BUNDLE denominator excludes
those files; pick one approach and apply it to jacocoTestCoverageVerification
configuration.

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

Labels

good first issue Good for newcomers

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants