feat: GET /festivals/{festivalId}/maps 지도 이미지 조회 API 구현#57
feat: GET /festivals/{festivalId}/maps 지도 이미지 조회 API 구현#57
Conversation
- Dockerfile, .dockerignore, docker-compose*.yml, start-app.sh, trigger-ai-fix.sh → infra/ - db/ 디렉토리 → infra/db/ - 기존 infra/docker-compose.yml(MySQL+Nginx) 통합 후 nginx-storage 서비스 추가 - _deploy.yml: Dockerfile 및 docker-compose.yml 경로 업데이트 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- FestivalMapService: festival 존재 검증 + sequence 오름차순 지도 목록 조회 - FestivalMapRepository: findByFestivalIdOrderBySequenceAsc() 추가 - FestivalMapController: 서비스 호출로 연결 (null → 실제 데이터) - GeneralErrorCode: FESTIVAL_NOT_FOUND(404) 추가 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
📝 Walkthrough개요Docker Compose 구성을 루트에서 변경사항
시퀀스 다이어그램sequenceDiagram
participant Client
participant Controller as FestivalMapController
participant Service as FestivalMapService
participant FestRepo as FestivalMapRepository
participant FestRepo2 as FestivalRepository
participant Database as Database
Client->>Controller: GET /festivals/{festivalId}/maps
Controller->>Service: getFestivalMaps(festivalId)
Service->>FestRepo2: existsById(festivalId)
FestRepo2->>Database: SELECT * FROM festival WHERE id=?
Database-->>FestRepo2: Result
alt Festival Not Found
FestRepo2-->>Service: false
Service-->>Controller: throw GeneralException(FESTIVAL_NOT_FOUND)
Controller-->>Client: 404 에러 응답
else Festival Found
FestRepo2-->>Service: true
Service->>FestRepo: findByFestivalIdOrderBySequenceAsc(festivalId)
FestRepo->>Database: SELECT * FROM festival_map WHERE festival_id=? ORDER BY sequence
Database-->>FestRepo: FestivalMap List
FestRepo-->>Service: FestivalMap entities
Service->>Service: Convert to FestivalMapInfo DTOs
Service-->>Controller: List<FestivalMapInfo>
Controller-->>Client: 200 성공 응답 (지도 목록)
end
예상 코드 리뷰 시간🎯 3 (보통) | ⏱️ ~20분 관련 가능성 있는 PR
시
🚥 Pre-merge checks | ✅ 2 | ❌ 3❌ Failed checks (3 warnings)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 5
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@infra/docker-compose.yml`:
- Around line 29-31: The MySQL Docker service creates database MYSQL_DATABASE
(current key MYSQL_DATABASE: wagle) but the application JDBC URL (in
src/main/resources/application.yml) points to schema name "waglewagle", causing
startup failures; pick one canonical name and make both match: either change the
Docker env var MYSQL_DATABASE to "waglewagle" or update the JDBC URL in
application.yml to "wagle" (or centralize both to an env var), and ensure any
references (tests, migrations) use the chosen name so the container provides the
expected schema at startup.
In `@infra/trigger-ai-fix.sh`:
- Around line 28-33: When CURRENT_COUNT exceeds MAX_RETRIES the script currently
removes COUNTER_FILE then exits (exit 2), which allows the next run to reset to
attempt `#1`; instead preserve the counter or write a persistent stop flag so the
forced-stop state remains; modify the branch handling CURRENT_COUNT and
MAX_RETRIES (the if block around those variables and the rm -f "$COUNTER_FILE" /
exit 2 lines) to either not delete COUNTER_FILE or to create a new STOP_FLAG
file (e.g., STOP_FLAG path variable) and ensure the script’s startup logic
checks that STOP_FLAG before attempting retries so human intervention is
required to clear it.
- Around line 15-22: 현재 COUNTER_FILE을 읽고 증분한 뒤 덮어쓰는 작업(CURRENT_COUNT read -> +1
-> echo)은 락 없이 수행되어 동시성 문제를 일으키므로, COUNTER_FILE(또는 별도 lock 파일)을 대상으로 배타적 락을 획득한
후에 CURRENT_COUNT를 읽고 증가시키고 파일에 쓰는 전체 read-modify-write를 하나의 락 구역으로 감싸도록 수정하세요;
구체적으로 COUNTER_FILE과 CURRENT_COUNT 참조 지점을 찾아 flock을 사용해 배타적 잠금(flock -x 또는 파일
디스크립터 기반 락)을 얻은 다음 파일을 읽고 CURRENT_COUNT=$((CURRENT_COUNT+1))로 계산하고 덮어쓴 뒤 락을
해제하도록 구현합니다.
- Around line 87-99: Capture the AI agent's stdout/stderr into a variable when
invoking $AI_CLI_COMMAND (instead of discarding it) and check that captured
output for the sentinel string "MANUAL_INTERVENTION_REQUIRED"; only remove
COUNTER_FILE and print the success reset message if that sentinel is NOT present
and AI_EXIT_CODE is zero. If the sentinel is found, do not rm "$COUNTER_FILE",
print a clear message indicating manual intervention is required (including the
sentinel and exit code), and exit non‑zero so the caller knows this is not an
automatic success; reference the existing variables AI_EXIT_CODE, PROMPT_FILE,
COUNTER_FILE and the sentinel "MANUAL_INTERVENTION_REQUIRED" to locate where to
add the capture-and-check logic.
In
`@src/main/java/com/waglewagle/server/domain/festivalMap/repository/FestivalMapRepository.java`:
- Line 9: The derived query method in FestivalMapRepository uses a non-existent
property festivalId, causing PropertyReferenceException; rename the repository
method from findByFestivalIdOrderBySequenceAsc to
findByFestival_IdOrderBySequenceAsc and update any callers (notably the service
method in FestivalMapService that invokes findByFestivalIdOrderBySequenceAsc) to
call the new method name so Spring can resolve the nested property via the
festival relationship.
🪄 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: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: b058b886-1755-472b-b586-99e84f83939f
⛔ Files ignored due to path filters (1)
.github/workflows/_deploy.ymlis excluded by!**/.github/**
📒 Files selected for processing (12)
docker-compose.ymlinfra/.dockerignoreinfra/Dockerfileinfra/docker-compose-local.ymlinfra/docker-compose.ymlinfra/start-app.shinfra/trigger-ai-fix.shsrc/main/java/com/waglewagle/server/domain/festivalMap/controller/FestivalMapController.javasrc/main/java/com/waglewagle/server/domain/festivalMap/dto/CongestionDTO.javasrc/main/java/com/waglewagle/server/domain/festivalMap/repository/FestivalMapRepository.javasrc/main/java/com/waglewagle/server/domain/festivalMap/service/FestivalMapService.javasrc/main/java/com/waglewagle/server/global/apiPayload/code/GeneralErrorCode.java
💤 Files with no reviewable changes (2)
- src/main/java/com/waglewagle/server/domain/festivalMap/dto/CongestionDTO.java
- docker-compose.yml
| environment: | ||
| MYSQL_ROOT_PASSWORD: "${DB_ROOT_PASSWORD}" | ||
| MYSQL_DATABASE: "${DB_NAME}" | ||
| MYSQL_ROOT_PASSWORD: ${DB_PASSWORD:-root} | ||
| MYSQL_DATABASE: wagle |
There was a problem hiding this comment.
MySQL 초기 스키마명을 애플리케이션 설정과 맞춰 주세요.
Line 31에서는 wagle만 생성하지만, src/main/resources/application.yml:4-6의 JDBC URL은 waglewagle에 접속합니다. 새 볼륨으로 올리면 스키마가 없어 애플리케이션이 바로 기동 실패합니다. 둘 중 한쪽 이름을 통일해야 합니다.
수정 예시
- MYSQL_DATABASE: wagle
+ MYSQL_DATABASE: waglewagle📝 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.
| environment: | |
| MYSQL_ROOT_PASSWORD: "${DB_ROOT_PASSWORD}" | |
| MYSQL_DATABASE: "${DB_NAME}" | |
| MYSQL_ROOT_PASSWORD: ${DB_PASSWORD:-root} | |
| MYSQL_DATABASE: wagle | |
| environment: | |
| MYSQL_ROOT_PASSWORD: ${DB_PASSWORD:-root} | |
| MYSQL_DATABASE: waglewagle |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@infra/docker-compose.yml` around lines 29 - 31, The MySQL Docker service
creates database MYSQL_DATABASE (current key MYSQL_DATABASE: wagle) but the
application JDBC URL (in src/main/resources/application.yml) points to schema
name "waglewagle", causing startup failures; pick one canonical name and make
both match: either change the Docker env var MYSQL_DATABASE to "waglewagle" or
update the JDBC URL in application.yml to "wagle" (or centralize both to an env
var), and ensure any references (tests, migrations) use the chosen name so the
container provides the expected schema at startup.
| if [ -f "$COUNTER_FILE" ]; then | ||
| CURRENT_COUNT=$(cat "$COUNTER_FILE") | ||
| else | ||
| CURRENT_COUNT=0 | ||
| fi | ||
|
|
||
| CURRENT_COUNT=$((CURRENT_COUNT + 1)) | ||
| echo "$CURRENT_COUNT" > "$COUNTER_FILE" |
There was a problem hiding this comment.
파일 카운터 갱신이 락 없이 수행됩니다.
Line 16-22의 cat -> +1 -> echo read-modify-write가 보호되지 않아, 두 웹훅이 동시에 들어오면 증가분이 유실되거나 카운트가 꼬일 수 있습니다. 파일 카운터를 계속 쓸 거라면 읽기/쓰기/삭제를 같은 락으로 감싸야 합니다.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@infra/trigger-ai-fix.sh` around lines 15 - 22, 현재 COUNTER_FILE을 읽고 증분한 뒤 덮어쓰는
작업(CURRENT_COUNT read -> +1 -> echo)은 락 없이 수행되어 동시성 문제를 일으키므로, COUNTER_FILE(또는
별도 lock 파일)을 대상으로 배타적 락을 획득한 후에 CURRENT_COUNT를 읽고 증가시키고 파일에 쓰는 전체
read-modify-write를 하나의 락 구역으로 감싸도록 수정하세요; 구체적으로 COUNTER_FILE과 CURRENT_COUNT 참조
지점을 찾아 flock을 사용해 배타적 잠금(flock -x 또는 파일 디스크립터 기반 락)을 얻은 다음 파일을 읽고
CURRENT_COUNT=$((CURRENT_COUNT+1))로 계산하고 덮어쓴 뒤 락을 해제하도록 구현합니다.
| # 파일 카운터 기준 최대 재시도 초과 시 강제 중단 (AI API 실패 포함 모든 실패 케이스 대응) | ||
| if [ "$CURRENT_COUNT" -gt "$MAX_RETRIES" ]; then | ||
| echo "🚨 [강제 중단] ${MAX_RETRIES}회 연속 복구 실패 (API 크레딧 소진 등 포함). 휴먼 개입이 필요합니다." | ||
| rm -f "$COUNTER_FILE" | ||
| exit 2 | ||
| fi |
There was a problem hiding this comment.
강제 중단 직전에 카운터를 삭제하면 보호 장치가 바로 해제됩니다.
Line 31에서 카운터를 지운 뒤 종료하면 다음 호출은 다시 1회차로 시작합니다. 지금 로직은 "최대 횟수 초과 시 휴먼 개입 전까지 중단"이 아니라 "해당 호출만 건너뛰고 곧바로 다시 재시도"가 됩니다. 강제 중단 상태를 유지하려면 카운터를 남기거나 별도 stop flag를 기록해야 합니다.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@infra/trigger-ai-fix.sh` around lines 28 - 33, When CURRENT_COUNT exceeds
MAX_RETRIES the script currently removes COUNTER_FILE then exits (exit 2), which
allows the next run to reset to attempt `#1`; instead preserve the counter or
write a persistent stop flag so the forced-stop state remains; modify the branch
handling CURRENT_COUNT and MAX_RETRIES (the if block around those variables and
the rm -f "$COUNTER_FILE" / exit 2 lines) to either not delete COUNTER_FILE or
to create a new STOP_FLAG file (e.g., STOP_FLAG path variable) and ensure the
script’s startup logic checks that STOP_FLAG before attempting retries so human
intervention is required to clear it.
| $AI_CLI_COMMAND "$(cat "$PROMPT_FILE")" | ||
| AI_EXIT_CODE=$? | ||
|
|
||
| rm "$PROMPT_FILE" | ||
| echo ">> AI 에이전트 실행이 완료되었습니다." | ||
|
|
||
| if [ $AI_EXIT_CODE -ne 0 ]; then | ||
| echo "❌ AI 에이전트 실행 실패 (exit code: $AI_EXIT_CODE). 카운터 유지 후 종료합니다." | ||
| exit 1 | ||
| fi | ||
|
|
||
| # AI 성공 시 카운터 리셋 | ||
| rm -f "$COUNTER_FILE" | ||
| echo "✅ AI 에이전트 실행 완료. 카운터를 리셋합니다." |
There was a problem hiding this comment.
AI의 수동 개입 신호를 성공으로 취급하지 마세요.
프롬프트는 수정 불가한 인프라/권한 오류에서 MANUAL_INTERVENTION_REQUIRED를 출력하라고 지시하지만, 현재는 exit code만 보고 Line 98에서 카운터를 리셋합니다. 그러면 사람이 개입해야 하는 장애도 다음 호출에서 다시 1회차가 되어 반복됩니다. 성공 리셋 전에 이 sentinel을 별도로 감지해야 합니다.
수정 예시
echo ">> AI 에이전트를 실행합니다..."
-$AI_CLI_COMMAND "$(cat "$PROMPT_FILE")"
-AI_EXIT_CODE=$?
+AI_OUTPUT=$($AI_CLI_COMMAND "$(cat "$PROMPT_FILE")" 2>&1)
+AI_EXIT_CODE=$?
+printf '%s\n' "$AI_OUTPUT"
rm "$PROMPT_FILE"
-if [ $AI_EXIT_CODE -ne 0 ]; then
+if [ "$AI_EXIT_CODE" -ne 0 ]; then
echo "❌ AI 에이전트 실행 실패 (exit code: $AI_EXIT_CODE). 카운터 유지 후 종료합니다."
exit 1
fi
+
+if printf '%s\n' "$AI_OUTPUT" | grep -q 'MANUAL_INTERVENTION_REQUIRED'; then
+ echo "🚨 수동 개입이 필요한 장애입니다. 카운터를 유지한 채 종료합니다."
+ exit 2
+fi
# AI 성공 시 카운터 리셋
rm -f "$COUNTER_FILE"🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@infra/trigger-ai-fix.sh` around lines 87 - 99, Capture the AI agent's
stdout/stderr into a variable when invoking $AI_CLI_COMMAND (instead of
discarding it) and check that captured output for the sentinel string
"MANUAL_INTERVENTION_REQUIRED"; only remove COUNTER_FILE and print the success
reset message if that sentinel is NOT present and AI_EXIT_CODE is zero. If the
sentinel is found, do not rm "$COUNTER_FILE", print a clear message indicating
manual intervention is required (including the sentinel and exit code), and exit
non‑zero so the caller knows this is not an automatic success; reference the
existing variables AI_EXIT_CODE, PROMPT_FILE, COUNTER_FILE and the sentinel
"MANUAL_INTERVENTION_REQUIRED" to locate where to add the capture-and-check
logic.
| import java.util.List; | ||
|
|
||
| public interface FestivalMapRepository extends JpaRepository<FestivalMap, Long> { | ||
| List<FestivalMap> findByFestivalIdOrderBySequenceAsc(Long festivalId); |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Verify that FestivalMap has association field `festival` (not `festivalId`)
# and locate derived query methods using incorrect/correct property path.
rg -n --type=java -C3 'class FestivalMap|private Festival festival|private .*festivalId|findByFestivalIdOrderBySequenceAsc|findByFestival_IdOrderBySequenceAsc'Repository: wagle-project/wagle-server
Length of output: 7261
Spring Data 파생 메서드명이 부트스트랩 시점에 PropertyReferenceException을 발생시킵니다.
FestivalMap 엔티티에는 festivalId 필드가 없고 festival 필드(관계 객체)만 존재하므로, 메서드명을 findByFestival_IdOrderBySequenceAsc로 수정해야 합니다. 서비스(FestivalMapService.java 27행)도 함께 변경되어야 합니다.
수정안
public interface FestivalMapRepository extends JpaRepository<FestivalMap, Long> {
- List<FestivalMap> findByFestivalIdOrderBySequenceAsc(Long festivalId);
+ List<FestivalMap> findByFestival_IdOrderBySequenceAsc(Long festivalId);
}📝 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.
| List<FestivalMap> findByFestivalIdOrderBySequenceAsc(Long festivalId); | |
| List<FestivalMap> findByFestival_IdOrderBySequenceAsc(Long festivalId); |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In
`@src/main/java/com/waglewagle/server/domain/festivalMap/repository/FestivalMapRepository.java`
at line 9, The derived query method in FestivalMapRepository uses a non-existent
property festivalId, causing PropertyReferenceException; rename the repository
method from findByFestivalIdOrderBySequenceAsc to
findByFestival_IdOrderBySequenceAsc and update any callers (notably the service
method in FestivalMapService that invokes findByFestivalIdOrderBySequenceAsc) to
call the new method name so Spring can resolve the nested property via the
festival relationship.
Summary
GET /festivals/{festivalId}/maps엔드포인트 구현 (JWT 인증 필수)sequence오름차순으로 반환Dockerfile,docker-compose*.yml등)infra/디렉토리로 정리Changes
feat
FestivalMapService신규 생성 — festival 존재 검증 + 지도 목록 조회FestivalMapRepository—findByFestivalIdOrderBySequenceAsc()추가FestivalMapController— 서비스 호출 연결GeneralErrorCode—FESTIVAL_NOT_FOUND(404) 추가chore
Dockerfile,.dockerignore,docker-compose*.yml,start-app.sh,trigger-ai-fix.sh,db/→infra/이동_deploy.yml경로 업데이트Test Plan
festivalId요청 시FESTIVAL_NOT_FOUND404 반환content항목이sequence오름차순으로 정렬되어 있는지 확인Closes #56
🤖 Generated with Claude Code
Summary by CodeRabbit
릴리스 노트
New Features
Chores