Conversation
GraphicsEnvironment에 폰트를 명시적으로 등록하여 Docker 환경에서 fontconfig 없이도 Pretendard 폰트가 정상 동작하도록 수정. 폰트 로드 실패 시 깨진 이미지 생성 대신 즉시 예외를 던지도록 변경. 기존 깨진 배너 복구를 위한 임시 Admin 재생성 API 추가. Constraint: Docker 베이스 이미지(eclipse-temurin:17-jdk)에 fontconfig 미포함 Rejected: Dockerfile에 fontconfig 설치 | 인프라 변경 없이 코드 레벨에서 해결 가능 Rejected: SansSerif fallback 유지 | Linux 서버에서 한글 글리프 미지원 Confidence: high Scope-risk: narrow Directive: 재생성 API는 임시이므로 배너 복구 후 다음 PR에서 제거할 것 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
요약Walkthrough새로운 관리자 엔드포인트 Changes
Sequence Diagram(s)sequenceDiagram
actor Admin as 관리자
participant API as AdminBannerApi
participant Controller as AdminBannerController
participant Service as FacadeRankingBannerServiceImpl
participant Repo as FeedMonthlyRankingRepository
participant DB as Database
Admin->>API: POST /ranking/regenerate
API->>Controller: regenerateRankingBanners()
Controller->>Service: regenerateLatestRankingBanners()
Service->>Service: 이전 달 계산 (LocalDate.now().minusMonths(1))
Service->>Repo: findAllByTargetYearAndTargetMonthAndRanking(year, month, 1)
Repo->>DB: 1위 랭킹 조회
DB-->>Repo: 랭킹 데이터 반환
Repo-->>Service: 결과 목록
alt 결과 존재
Service->>Service: createRankingBanners(firstPlaceRankings)
Service->>DB: 기존 배너 삭제 및 새 배너 생성
DB-->>Service: 완료
else 결과 없음
Service->>Service: 로그 기록 후 종료
end
Service-->>Controller: void
Controller-->>API: HTTP 204 No Content
API-->>Admin: 응답
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related PRs
Suggested labels
Suggested reviewers
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 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: 2
🧹 Nitpick comments (1)
src/main/java/ddingdong/ddingdongBE/domain/banner/service/FacadeRankingBannerServiceImpl.java (1)
58-59: 1등 랭크 값은 상수로 분리하는 편이 가독성이 좋습니다.Line [59]의
1은 의미 있는 상수(FIRST_PLACE_RANK)로 분리하면 의도를 더 빠르게 파악할 수 있습니다.제안 diff
public class FacadeRankingBannerServiceImpl implements FacadeRankingBannerService { private static final String RANKING_BANNER_DIRECTORY = "ranking-banner"; private static final String IMAGE_CONTENT_TYPE = "image/png"; + private static final int FIRST_PLACE_RANK = 1; @@ List<FeedMonthlyRanking> firstPlaceRankings = feedMonthlyRankingRepository.findAllByTargetYearAndTargetMonthAndRanking( - lastMonth.getYear(), lastMonth.getMonthValue(), 1); + lastMonth.getYear(), lastMonth.getMonthValue(), FIRST_PLACE_RANK);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/main/java/ddingdong/ddingdongBE/domain/banner/service/FacadeRankingBannerServiceImpl.java` around lines 58 - 59, The literal '1' passed to feedMonthlyRankingRepository.findAllByTargetYearAndTargetMonthAndRanking(...) should be replaced with a named constant to improve readability; add a private static final int FIRST_PLACE_RANK = 1 (or an equivalent constant) in FacadeRankingBannerServiceImpl and use FIRST_PLACE_RANK in the repository call to make the intent explicit.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/main/java/ddingdong/ddingdongBE/domain/banner/api/AdminBannerApi.java`:
- Around line 55-57: The POST endpoint in AdminBannerApi currently declares a
204 status via `@ApiResponse`(responseCode = "204") and
`@ResponseStatus`(HttpStatus.NO_CONTENT); change both to return 201 by updating
`@ApiResponse`(responseCode = "201", description = "...") and
`@ResponseStatus`(HttpStatus.CREATED) so the POST follows the guideline (HTTP POST
-> 201 Created); locate these annotations above the method that performs the
ranking banner recreation in AdminBannerApi.java and adjust the values
accordingly.
In
`@src/main/java/ddingdong/ddingdongBE/domain/banner/service/BannerImageGenerator.java`:
- Around line 200-204: The catch blocks in BannerImageGenerator swallowing the
exception lose the stacktrace and the first catch for
BannerImageGenerationException is redundant; remove the separate catch
(BannerImageGenerationException e) block, and in the remaining catch (Exception
e) include the Throwable when logging (use log.error with the exception
parameter) and rethrow a new or the original BannerImageGenerationException as
appropriate so the root cause is preserved; update the log.error call that
references path to pass e as the last argument (or include e) and
construct/throw BannerImageGenerationException with the original exception as
its cause.
---
Nitpick comments:
In
`@src/main/java/ddingdong/ddingdongBE/domain/banner/service/FacadeRankingBannerServiceImpl.java`:
- Around line 58-59: The literal '1' passed to
feedMonthlyRankingRepository.findAllByTargetYearAndTargetMonthAndRanking(...)
should be replaced with a named constant to improve readability; add a private
static final int FIRST_PLACE_RANK = 1 (or an equivalent constant) in
FacadeRankingBannerServiceImpl and use FIRST_PLACE_RANK in the repository call
to make the intent explicit.
🪄 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: CHILL
Plan: Pro
Run ID: 6d3d537f-2436-4cc8-92c7-7a6047255e13
📒 Files selected for processing (5)
src/main/java/ddingdong/ddingdongBE/domain/banner/api/AdminBannerApi.javasrc/main/java/ddingdong/ddingdongBE/domain/banner/controller/AdminBannerController.javasrc/main/java/ddingdong/ddingdongBE/domain/banner/service/BannerImageGenerator.javasrc/main/java/ddingdong/ddingdongBE/domain/banner/service/FacadeRankingBannerService.javasrc/main/java/ddingdong/ddingdongBE/domain/banner/service/FacadeRankingBannerServiceImpl.java
| @ApiResponse(responseCode = "204", description = "랭킹 배너 재생성 성공") | ||
| @ResponseStatus(HttpStatus.NO_CONTENT) | ||
| @SecurityRequirement(name = "AccessToken") |
There was a problem hiding this comment.
POST 재생성 엔드포인트의 상태 코드는 201로 맞춰주세요.
Line [56]이 204 NO_CONTENT로 선언되어 있어 저장소 규칙과 충돌합니다.
제안 diff
- `@ApiResponse`(responseCode = "204", description = "랭킹 배너 재생성 성공")
- `@ResponseStatus`(HttpStatus.NO_CONTENT)
+ `@ApiResponse`(responseCode = "201", description = "랭킹 배너 재생성 성공")
+ `@ResponseStatus`(HttpStatus.CREATED)As per coding guidelines, HTTP status codes: POST returns 201 Created, GET returns 200 OK, PUT/PATCH/DELETE returns 204 No Content 규칙을 따라야 합니다.
📝 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.
| @ApiResponse(responseCode = "204", description = "랭킹 배너 재생성 성공") | |
| @ResponseStatus(HttpStatus.NO_CONTENT) | |
| @SecurityRequirement(name = "AccessToken") | |
| `@ApiResponse`(responseCode = "201", description = "랭킹 배너 재생성 성공") | |
| `@ResponseStatus`(HttpStatus.CREATED) | |
| `@SecurityRequirement`(name = "AccessToken") |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/main/java/ddingdong/ddingdongBE/domain/banner/api/AdminBannerApi.java`
around lines 55 - 57, The POST endpoint in AdminBannerApi currently declares a
204 status via `@ApiResponse`(responseCode = "204") and
`@ResponseStatus`(HttpStatus.NO_CONTENT); change both to return 201 by updating
`@ApiResponse`(responseCode = "201", description = "...") and
`@ResponseStatus`(HttpStatus.CREATED) so the POST follows the guideline (HTTP POST
-> 201 Created); locate these annotations above the method that performs the
ranking banner recreation in AdminBannerApi.java and adjust the values
accordingly.
| } catch (BannerImageGenerationException e) { | ||
| throw e; | ||
| } catch (Exception e) { | ||
| log.warn("커스텀 폰트 로드 실패 ({}), 기본 폰트 사용: {}", path, e.getMessage()); | ||
| log.error("커스텀 폰트 로드 실패 ({}): {}", path, e.getMessage()); | ||
| throw new BannerImageGenerationException(); |
There was a problem hiding this comment.
예외 로깅에서 스택트레이스가 유실됩니다.
현재는 메시지만 기록되어 폰트 로드 실패 원인 추적이 어렵습니다. 동시에 재던지기용 catch (BannerImageGenerationException e)는 제거해도 동작이 같습니다.
수정 예시
- } catch (BannerImageGenerationException e) {
- throw e;
- } catch (Exception e) {
- log.error("커스텀 폰트 로드 실패 ({}): {}", path, e.getMessage());
+ } catch (Exception e) {
+ if (e instanceof BannerImageGenerationException) {
+ throw (BannerImageGenerationException) e;
+ }
+ log.error("커스텀 폰트 로드 실패 ({})", path, e);
throw new BannerImageGenerationException();
}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In
`@src/main/java/ddingdong/ddingdongBE/domain/banner/service/BannerImageGenerator.java`
around lines 200 - 204, The catch blocks in BannerImageGenerator swallowing the
exception lose the stacktrace and the first catch for
BannerImageGenerationException is redundant; remove the separate catch
(BannerImageGenerationException e) block, and in the remaining catch (Exception
e) include the Throwable when logging (use log.error with the exception
parameter) and rethrow a new or the original BannerImageGenerationException as
appropriate so the root cause is preserved; update the log.error call that
references path to pass e as the last argument (or include e) and
construct/throw BannerImageGenerationException with the original exception as
its cause.
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
🚀 작업 내용
BannerImageGenerator에서 Pretendard 폰트를 JavaGraphicsEnvironment에 명시적으로 등록하여fontconfig시스템 라이브러리 없이도 한글이 정상 렌더링되도록 변경했습니다🤔 고민했던 내용
GraphicsEnvironment.registerFont()를 사용하면 인프라 변경 없이 해결 가능하여 코드 레벨 방식을 선택했습니다FacadeRankingBannerService에 재생성 로직을 배치했습니다💬 리뷰 중점사항
@PostConstruct에서 폰트 로드 실패 시 애플리케이션 기동이 실패하는데, 폰트 파일이 resources에 포함되어 있으므로 fail-fast가 적절하다고 판단했습니다🤖 Generated with Claude Code
Summary by CodeRabbit
릴리스 노트
새로운 기능
버그 수정