-
Notifications
You must be signed in to change notification settings - Fork 2
feat: dev, prod 환경별 xml 로그 파일 관리 및 dev 환경 http응답/요청 디스코드 웹훅 연동 - #153 #154
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
📝 WalkthroughSummary by CodeRabbit
Walkthroughdev/prod 로그 구성 재편, 개발환경의 HTTP 요청·응답을 Discord 웹훅으로 비동기 전송하도록 추가, Feign 재시도 및 비동기 Executor(MDC 전파) 도입, 날짜 포맷 하이픈으로 통일, Lombok 유틸 클래스 정리, 빌드·의존성 및 .gitignore 조정. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
participant Client as 클라이언트
participant App as PermitServer (dev)
participant Filter as DevRequestResponseLoggingFilter
participant Builder as HttpReqResLogJsonBuilder
participant Sender as DiscordSender
participant Executor as alertExecutor (Async)
participant Webhook as DiscordFeignClient
Client->>App: HTTP 요청
App->>Filter: doFilter(request,response,chain)
Filter->>App: chain.doFilter(...)
App-->>Filter: 응답 완료
Filter->>Builder: buildJsonLog(req,res,duration)
Builder-->>Filter: JSON 로그 문자열
Filter->>Sender: send(json) -- 비동기 호출
Sender-)Executor: enqueue task (MDC 복원)
Executor->>Webhook: POST /webhook {content: formatted message}
Webhook-->>Executor: 응답 (Feign Retryer 적용)
Executor-->>Sender: 완료
Filter-->>Client: HTTP 응답(본문 복원)
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing touches🧪 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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 4
♻️ Duplicate comments (1)
src/main/java/com/permitseoul/permitserver/domain/admin/ticket/api/dto/req/TicketRoundWithTypeUpdateRequest.java (1)
20-20: 생성 요청과 일관된 날짜 형식 변경입니다.
TicketRoundWithTypeCreateRequest와 동일한 형식 변경이 적용되어 create/update API 간 일관성이 유지됩니다.단, 이는 API 호환성을 깨는 변경이므로 클라이언트 업데이트가 필요합니다 (이미 TicketRoundWithTypeCreateRequest.java에서 플래그됨).
Also applies to: 24-24
🧹 Nitpick comments (14)
src/main/java/com/permitseoul/permitserver/global/util/TimeFormatterUtil.java (1)
5-7: @UtilityClass 적용 승인, 사용하지 않는 import 제거 권장Lombok의
@UtilityClass로 전환하여 유틸리티 클래스 의미를 명시적으로 표현한 것은 좋은 개선입니다.다만 5-6번째 줄의
AccessLevel과NoArgsConstructorimport는 더 이상 사용되지 않으므로 제거하는 것이 좋습니다:-import lombok.AccessLevel; -import lombok.NoArgsConstructor; import lombok.experimental.UtilityClass;src/main/java/com/permitseoul/permitserver/global/util/LogFormUtil.java (1)
4-6: @UtilityClass 적용 승인, 사용하지 않는 import 제거 권장다른 유틸리티 클래스들과 일관되게
@UtilityClass로 전환한 것은 좋은 개선입니다.4-5번째 줄의 사용되지 않는 import를 제거하세요:
-import lombok.AccessLevel; -import lombok.NoArgsConstructor; import lombok.experimental.UtilityClass;src/main/java/com/permitseoul/permitserver/global/util/PriceFormatterUtil.java (1)
4-6: @UtilityClass 적용 승인, 사용하지 않는 import 제거 권장프로젝트 전반에 걸쳐 유틸리티 클래스를
@UtilityClass로 일관되게 전환하는 것은 좋은 패턴입니다.4-5번째 줄의 사용되지 않는 import를 제거하세요:
-import lombok.AccessLevel; -import lombok.NoArgsConstructor; import lombok.experimental.UtilityClass;src/main/java/com/permitseoul/permitserver/global/util/DateFormatterUtil.java (1)
6-8: @UtilityClass 적용 승인, 사용하지 않는 import 제거 권장다른 유틸리티 클래스들과 일관되게
@UtilityClass로 전환한 것은 좋습니다.6-7번째 줄의 사용되지 않는 import를 제거하세요:
-import lombok.AccessLevel; -import lombok.NoArgsConstructor; import lombok.experimental.UtilityClass;src/main/resources/logback/logback-prod-file.xml (2)
4-4: 로그 파일 경로 설정 검토 필요하드코딩된 절대 경로
/home/ubuntu/logs/가 사용되고 있습니다. 배포 환경이 변경될 경우 문제가 될 수 있으므로, 환경 변수나 Spring 속성을 통해 경로를 설정하는 것을 권장합니다.다음과 같이 환경 변수를 사용하도록 수정할 수 있습니다:
- <file>/home/ubuntu/logs/PERMIT-INFO-LOG.log</file> + <file>${LOG_PATH:-/home/ubuntu/logs}/PERMIT-INFO-LOG.log</file>동일하게 line 24, 32, 65에도 적용해주세요.
76-76: discardingThreshold 설정 검토
discardingThreshold=0으로 설정하면 큐가 가득 찰 때 로그가 버려지지 않고 대기하게 됩니다. 이는 로그 유실을 방지하지만, 높은 부하 시 애플리케이션 스레드가 블로킹될 수 있습니다.성능과 로그 완전성 간의 트레이드오프를 고려하여 적절한 값(예: 20% = queueSize * 0.2)을 설정하는 것을 권장합니다.
src/main/resources/logback/logback-dev-loki.xml (1)
11-11: HOSTNAME 변수 확인
${HOSTNAME}환경 변수가 설정되어 있지 않으면 로그에 빈 값이 포함될 수 있습니다. 기본값을 설정하거나 시스템 속성을 사용하는 것을 권장합니다.다음과 같이 기본값을 추가할 수 있습니다:
- <pattern>app="permitseoul-dev", level="%level", logger="%logger{20}", host="${HOSTNAME}"</pattern> + <pattern>app="permitseoul-dev", level="%level", logger="%logger{20}", host="${HOSTNAME:-unknown}"</pattern>src/main/resources/logback-spring.xml (1)
8-13: 일관성을 위한 리팩토링 제안Local 프로파일은 inline root logger 설정을 유지하고 있지만, dev와 prod는 완전히 외부 파일로 위임하고 있습니다. 일관성을 위해 local도 logback-local.xml에서 root logger를 설정하고 이 파일에서는 include만 하는 것을 고려해보세요.
예시:
<springProfile name="local"> <include resource="logback/logback-local.xml"/> - <root level="INFO"> - <appender-ref ref="LOCALCONSOLE"/> - </root> </springProfile>그리고 logback-local.xml에 root logger 설정을 추가합니다.
src/main/java/com/permitseoul/permitserver/global/external/discord/dto/DiscordMessage.java (1)
4-8: 입력 검증 추가 고려
content필드에 대한 null 또는 빈 문자열 검증이 없습니다. Discord API가 빈 메시지를 허용하는지 확인하고, 필요하다면 factory method에서 검증을 추가하는 것을 고려해보세요.예시:
public static DiscordMessage of(final String message) { + if (message == null || message.isBlank()) { + throw new IllegalArgumentException("Discord message content cannot be null or empty"); + } return new DiscordMessage(message); }build.gradle (1)
85-86: Loki4j 버전 업그레이드 권장: 현재 Stable은 2.0.0이며(Java 8 유지 불필요 시) 기능 개선 및 추가 버그 픽스를 위해 2.0.0으로 업데이트하세요. 1.5.2는 JSON nullable 문자열·동시 쓰기 시 JSON 손상 버그를 해결한 버전입니다.src/main/java/com/permitseoul/permitserver/global/config/AsyncConfig.java (1)
16-24: 거부 정책(RejectionPolicy) 명시 권장.큐 용량(20)과 최대 스레드 수(4)를 초과하는 작업이 제출되면 기본 거부 정책인
AbortPolicy가 작동하여RejectedExecutionException이 발생합니다. Discord 알림이 급증하는 상황(예: 여러 요청이 동시에 실패)을 고려하면, 명시적으로CallerRunsPolicy또는 커스텀 정책을 설정하여 예외 발생을 방지하고 로그 누락을 줄이는 것이 좋습니다.다음 diff를 적용하여 거부 정책을 명시하세요:
executor.setThreadNamePrefix("DiscordAlert-"); executor.setAllowCoreThreadTimeOut(true); + executor.setRejectedExecutionHandler(new java.util.concurrent.ThreadPoolExecutor.CallerRunsPolicy());src/main/java/com/permitseoul/permitserver/global/external/discord/DiscordDevHttpLogSender.java (1)
21-30: 예외 처리 개선 고려.현재 모든 예외를 catch하여 로그만 남기고 있습니다. @async 메서드이므로 예외가 호출자에게 전파되지 않는 것은 의도된 동작입니다. 다만, 다음을 고려하세요:
- JSON 파싱 실패(JsonProcessingException)와 Feign 호출 실패(FeignException)를 구분하여 처리하면 디버깅이 용이합니다.
- 메트릭이나 모니터링을 추가하여 Discord 전송 실패율을 추적하면 운영에 도움이 됩니다.
예외를 구분하여 처리하는 예시:
@Async("alertExecutor") public void send(final String jsonMessage) { try { final JsonNode root = OBJECT_MAPPER.readTree(jsonMessage); final String content = DiscordMessageFormatterUtil.formatDevHttpLogToMarkdown(root); discordFeignClient.sendMessage(DiscordMessage.of(content)); + } catch (com.fasterxml.jackson.core.JsonProcessingException e) { + log.error("🚨 [DiscordDevHttpLogSender] JSON 파싱 실패: {}", jsonMessage, e); + } catch (feign.FeignException e) { + log.error("🚨 [DiscordDevHttpLogSender] Discord API 호출 실패 (status: {})", e.status(), e); } catch (Exception e) { log.error("🚨 [DiscordDevHttpLogSender] Discord 전송 실패", e); } }src/main/java/com/permitseoul/permitserver/global/util/HttpReqResLogJsonBuilder.java (1)
40-40: LocalDateTime 대신 타임존 명시 권장.
LocalDateTime.now()는 시스템의 기본 타임존을 사용하므로, 서버 환경에 따라 타임존이 다를 수 있습니다. 로그 시간을 명확하게 하려면ZonedDateTime.now()또는Instant.now()를 사용하여 UTC 기준으로 기록하거나, 명시적으로 타임존을 지정하세요.다음 중 하나를 적용하세요:
옵션 1: UTC 기준 사용 (권장)
- logMap.put(FIELD_TIME, LocalDateTime.now().toString()); + logMap.put(FIELD_TIME, java.time.Instant.now().toString());옵션 2: Asia/Seoul 타임존 명시
+import java.time.ZonedDateTime; +import java.time.ZoneId; + - logMap.put(FIELD_TIME, LocalDateTime.now().toString()); + logMap.put(FIELD_TIME, ZonedDateTime.now(ZoneId.of("Asia/Seoul")).toString());src/main/java/com/permitseoul/permitserver/domain/admin/ticket/api/dto/req/TicketRoundWithTypeCreateRequest.java (1)
44-44: 중첩된 TicketTypeRequest의 날짜 형식도 일관되게 업데이트되었습니다.외부 레코드와 동일한 형식 변경이 적용되어 일관성이 유지됩니다.
티켓 시스템의 특성상 타임존 정보가 중요할 수 있습니다. 현재 형식은 타임존 정보가 없어 서버와 클라이언트의 타임존 해석이 다를 경우 문제가 발생할 수 있습니다. 향후 ISO-8601 완전 형식(예: "yyyy-MM-dd'T'HH:mm:ssXXX")을 고려하거나, 최소한 모든 날짜를 UTC로 처리하는 규칙을 문서화하는 것을 권장합니다.
Also applies to: 48-48
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (23)
.gitignore(1 hunks)build.gradle(1 hunks)src/main/java/com/permitseoul/permitserver/domain/admin/ticket/api/dto/req/TicketRoundWithTypeCreateRequest.java(2 hunks)src/main/java/com/permitseoul/permitserver/domain/admin/ticket/api/dto/req/TicketRoundWithTypeUpdateRequest.java(2 hunks)src/main/java/com/permitseoul/permitserver/global/config/AsyncConfig.java(1 hunks)src/main/java/com/permitseoul/permitserver/global/config/FeignConfig.java(1 hunks)src/main/java/com/permitseoul/permitserver/global/external/discord/DiscordDevHttpLogSender.java(1 hunks)src/main/java/com/permitseoul/permitserver/global/external/discord/DiscordFeignClient.java(1 hunks)src/main/java/com/permitseoul/permitserver/global/external/discord/DiscordSender.java(1 hunks)src/main/java/com/permitseoul/permitserver/global/external/discord/dto/DiscordMessage.java(1 hunks)src/main/java/com/permitseoul/permitserver/global/external/discord/util/DiscordMessageFormatterUtil.java(1 hunks)src/main/java/com/permitseoul/permitserver/global/filter/DevRequestResponseLoggingFilter.java(1 hunks)src/main/java/com/permitseoul/permitserver/global/filter/MDCLoggingFilter.java(2 hunks)src/main/java/com/permitseoul/permitserver/global/util/DateFormatterUtil.java(2 hunks)src/main/java/com/permitseoul/permitserver/global/util/HttpReqResLogJsonBuilder.java(1 hunks)src/main/java/com/permitseoul/permitserver/global/util/LogFormUtil.java(1 hunks)src/main/java/com/permitseoul/permitserver/global/util/PriceFormatterUtil.java(1 hunks)src/main/java/com/permitseoul/permitserver/global/util/TimeFormatterUtil.java(1 hunks)src/main/resources/logback-spring.xml(1 hunks)src/main/resources/logback/logback-dev-error.xml(0 hunks)src/main/resources/logback/logback-dev-info.xml(0 hunks)src/main/resources/logback/logback-dev-loki.xml(1 hunks)src/main/resources/logback/logback-prod-file.xml(1 hunks)
💤 Files with no reviewable changes (2)
- src/main/resources/logback/logback-dev-error.xml
- src/main/resources/logback/logback-dev-info.xml
👮 Files not reviewed due to content moderation or server errors (2)
- src/main/java/com/permitseoul/permitserver/domain/admin/ticket/api/dto/req/TicketRoundWithTypeCreateRequest.java
- src/main/java/com/permitseoul/permitserver/domain/admin/ticket/api/dto/req/TicketRoundWithTypeUpdateRequest.java
🧰 Additional context used
🧬 Code graph analysis (4)
src/main/java/com/permitseoul/permitserver/global/util/LogFormUtil.java (3)
src/main/java/com/permitseoul/permitserver/global/util/DateFormatterUtil.java (1)
UtilityClass(17-107)src/main/java/com/permitseoul/permitserver/global/util/PriceFormatterUtil.java (1)
UtilityClass(12-41)src/main/java/com/permitseoul/permitserver/global/util/TimeFormatterUtil.java (1)
UtilityClass(12-23)
src/main/java/com/permitseoul/permitserver/global/util/TimeFormatterUtil.java (3)
src/main/java/com/permitseoul/permitserver/global/util/DateFormatterUtil.java (1)
UtilityClass(17-107)src/main/java/com/permitseoul/permitserver/global/util/LogFormUtil.java (1)
UtilityClass(13-43)src/main/java/com/permitseoul/permitserver/global/util/PriceFormatterUtil.java (1)
UtilityClass(12-41)
src/main/java/com/permitseoul/permitserver/global/util/PriceFormatterUtil.java (3)
src/main/java/com/permitseoul/permitserver/global/util/DateFormatterUtil.java (1)
UtilityClass(17-107)src/main/java/com/permitseoul/permitserver/global/util/LogFormUtil.java (1)
UtilityClass(13-43)src/main/java/com/permitseoul/permitserver/global/util/TimeFormatterUtil.java (1)
UtilityClass(12-23)
src/main/java/com/permitseoul/permitserver/global/util/DateFormatterUtil.java (2)
src/main/java/com/permitseoul/permitserver/global/util/LogFormUtil.java (1)
UtilityClass(13-43)src/main/java/com/permitseoul/permitserver/global/util/TimeFormatterUtil.java (1)
UtilityClass(12-23)
🔇 Additional comments (25)
.gitignore (1)
42-45: 환경별 설정 파일 무시 여부 재확인 필요
application*.yml을.gitignore에 추가하면 이후에는 이 설정 파일들을 새로 버전 관리할 수 없습니다. 이번 PR에서 환경별 로그 설정을 위해 해당 파일들을 커밋·배포할 계획이라면 이 무시 규칙은 변경 내용이 반영되지 못하게 만듭니다. 의도가 맞는지 확인해 주세요.src/main/java/com/permitseoul/permitserver/global/util/DateFormatterUtil.java (1)
24-24: 주석 업데이트 및 소비처 포맷 검증
DateFormatterUtil의 주석을 아래와 같이 수정하고, formatyyyyMMdd()를 사용하는 모든 위치(예: AdminEventService, AdminTicketService)에서 하이픈 형식이 올바르게 처리되는지 확인하세요.- private static final DateTimeFormatter DATE = DateTimeFormatter.ofPattern("yyyy-MM-dd"); // "2025.08.15" 포맷팅 + private static final DateTimeFormatter DATE = DateTimeFormatter.ofPattern("yyyy-MM-dd"); // "2025-08-15" 포맷팅src/main/java/com/permitseoul/permitserver/domain/admin/ticket/api/dto/req/TicketRoundWithTypeUpdateRequest.java (3)
20-20: 날짜 형식 변경이 create request와 일관되게 적용되었습니다.TicketRoundWithTypeCreateRequest와 동일하게 날짜 형식이 "yyyy-MM-dd HH:mm"로 통일되어 일관성 있는 API 인터페이스를 제공합니다.
Also applies to: 24-24, 47-47, 51-51
47-47: 중첩된 TicketTypeUpdateRequest도 일관되게 업데이트되었습니다.생성 요청의
TicketTypeRequest와 동일한 형식이 적용되어 전체적인 일관성이 확보되었습니다.Also applies to: 51-51
20-25: 생성/수정 DTO 간 일관성이 유지되었습니다.
TicketRoundWithTypeCreateRequest와 동일한 날짜 형식 변경이 적용되어 생성 및 수정 작업 간 일관성이 보장됩니다. 단, 첫 번째 파일에서 언급한 API 호환성 확인 사항이 이 파일에도 동일하게 적용됩니다.Also applies to: 47-52
src/main/resources/logback/logback-prod-file.xml (1)
85-88: 중복 로그 기록 확인Root logger가 INFO 레벨로 설정되어 있고
ASYNC_INFO와ASYNC_ERROR모두를 참조하므로, ERROR 레벨 로그가 두 파일에 모두 기록됩니다.이것이 의도된 동작인지 확인해주세요. ERROR 로그를 ERROR_FILE에만 기록하려면 별도의 logger 설정이나 필터 조정이 필요합니다.
src/main/java/com/permitseoul/permitserver/global/config/FeignConfig.java (1)
14-18: LGTM!Feign 재시도 설정이 적절합니다. 초기 100ms 지연에서 최대 1초까지 지수 백오프를 사용하고 최대 3회 재시도하는 설정은 일반적인 API 호출에 적합합니다.
src/main/java/com/permitseoul/permitserver/global/filter/MDCLoggingFilter.java (1)
22-22: LGTM!로컬 환경에서 MDC 로깅 필터를 제외하는 것은 적절한 프로파일 분리 전략입니다. 개발 및 운영 환경에서만 trace_id 추적이 활성화됩니다.
src/main/java/com/permitseoul/permitserver/global/external/discord/DiscordSender.java (1)
1-5: LGTM!Discord 메시지 전송을 위한 깔끔한 인터페이스 추상화입니다. 단일 책임 원칙을 따르며 테스트 가능성이 좋습니다.
src/main/resources/logback/logback-dev-loki.xml (1)
6-6: Loki 엔드포인트 확인 필요
http://localhost:3100으로 설정되어 있습니다. Dev 환경에서 Loki가 로컬에서 실행되는지 확인해주세요. 컨테이너화된 환경이나 원격 Loki 인스턴스를 사용하는 경우 호스트명이나 서비스 이름으로 변경이 필요할 수 있습니다.환경 변수를 사용하도록 개선할 수 있습니다:
- <url>http://localhost:3100/loki/api/v1/push</url> + <url>${LOKI_URL:-http://localhost:3100/loki/api/v1/push}</url>src/main/resources/logback-spring.xml (1)
16-23: LGTM!프로파일별 로깅 설정을 별도 파일로 분리하여 관심사를 명확히 구분했습니다. Dev 환경은 Loki를, Prod 환경은 파일 기반 로깅을 사용하는 구조가 명확합니다.
src/main/java/com/permitseoul/permitserver/global/external/discord/dto/DiscordMessage.java (1)
4-4: LGTM!Discord 메시지 페이로드를 위한 깔끔한 record 구현입니다. 불변성이 보장되며 factory method를 통한 생성 패턴을 따릅니다.
src/main/java/com/permitseoul/permitserver/global/external/discord/DiscordFeignClient.java (1)
17-18: void 반환 타입은 전송 실패를 감지하지 못함.메서드가 void를 반환하므로 호출자는 Discord 전송 성공 여부를 알 수 없습니다. 현재 DiscordDevHttpLogSender에서 try-catch로 예외를 처리하고 있으나, Feign 레벨에서 발생하는 오류(4xx/5xx 응답, 타임아웃 등)가 예외로 전파되는지 확인하세요. 모니터링이나 메트릭 수집을 고려한다면 성공/실패 상태를 추적하는 방안도 검토하세요.
src/main/java/com/permitseoul/permitserver/global/config/AsyncConfig.java (1)
26-39: MDC 전파 로직 잘 구현됨.비동기 스레드로 MDC 컨텍스트를 전파하는 로직이 올바르게 구현되어 있습니다.
contextMap을 캡처하고, 작업 실행 전에 설정한 후finally블록에서 정리하는 패턴이 정확합니다.src/main/java/com/permitseoul/permitserver/global/external/discord/DiscordDevHttpLogSender.java (1)
18-18: 정적 ObjectMapper 인스턴스는 안전함.Jackson의
ObjectMapper는 스레드 안전하므로 정적 인스턴스로 재사용하는 것이 올바른 패턴입니다. 성능상 이점도 있습니다.src/main/java/com/permitseoul/permitserver/global/util/HttpReqResLogJsonBuilder.java (2)
56-62: 본문 추출 및 자르기 로직 정확함.null/빈 배열 처리와 최대 길이 제한이 올바르게 구현되어 있습니다. UTF-8 인코딩을 명시적으로 사용하는 것도 좋습니다.
14-15: @UtilityClass 사용 적절함.Lombok의
@UtilityClass를 사용하여 유틸리티 클래스를 정의하는 것이 적절합니다. 이는 private 생성자를 자동으로 생성하고, 인스턴스화를 방지합니다.Based on learnings
src/main/java/com/permitseoul/permitserver/global/external/discord/util/DiscordMessageFormatterUtil.java (3)
31-66: Markdown 포맷팅 로직 잘 구현됨.Discord 메시지로 변환하는 로직이 명확하고 가독성이 좋습니다. 텍스트 블록(text block)을 사용하여 템플릿을 정의한 것이 효과적이며, 모든 필드에 대한 기본값을 제공하여 누락된 데이터를 안전하게 처리합니다.
68-78: prettyJson 메서드의 안전한 처리.JSON 파싱 실패 시 원본 문자열을 반환하는 것이 적절합니다. null/빈 문자열/EMPTY_BODY 처리도 올바릅니다.
7-8: @UtilityClass 사용 적절함.Lombok의
@UtilityClass를 사용하여 유틸리티 클래스를 정의하는 것이 적절합니다.Based on learnings
src/main/java/com/permitseoul/permitserver/global/filter/DevRequestResponseLoggingFilter.java (3)
20-23: 생성자 주입 방식 적절함.Lombok의
@RequiredArgsConstructor를 사용하여final필드에 대한 생성자 주입을 구현한 것이 Spring의 권장 패턴입니다.Based on learnings
35-40: finally 블록에서 copyBodyToResponse 호출 보장됨.
finally블록에서res.copyBodyToResponse()를 호출하여, 예외 발생 여부와 관계없이 응답 본문이 클라이언트에게 전송되도록 보장합니다. 다만,discordSender.send()나HttpReqResLogJsonBuilder.buildJsonLog()에서 예외가 발생하면copyBodyToResponse()가 실행되지 않을 수 있습니다.다음과 같이 개선하여 로그 전송 실패가 응답 전송을 방해하지 않도록 하세요:
} finally { final long duration = System.currentTimeMillis() - start; - final String jsonLog = HttpReqResLogJsonBuilder.buildJsonLog(req, res, duration); - discordSender.send(jsonLog); + try { + final String jsonLog = HttpReqResLogJsonBuilder.buildJsonLog(req, res, duration); + discordSender.send(jsonLog); + } catch (Exception e) { + log.error("Discord 로그 전송 중 오류 발생", e); + } res.copyBodyToResponse(); }
29-30: ContentCaching 래퍼 사용 적절함.요청과 응답 본문을 여러 번 읽기 위해
ContentCachingRequestWrapper와ContentCachingResponseWrapper를 사용하는 것이 올바른 패턴입니다.src/main/java/com/permitseoul/permitserver/domain/admin/ticket/api/dto/req/TicketRoundWithTypeCreateRequest.java (2)
18-18: 아래 스크립트를 실행해 프론트엔드 및 API 문서에서 이전 날짜 형식(yyyy.MM.dd) 사용 여부를 재검증하세요:#!/bin/bash # 프론트엔드 코드에서 이전 날짜 형식 사용 여부 확인 rg -n 'yyyy\.MM\.dd' -g '*.js' -g '*.jsx' -g '*.ts' -g '*.tsx' || echo "No old format found in frontend" # API 문서/스펙에서 형식 업데이트 확인 fd -e md -e yaml -e json | xargs rg -l 'yyyy\.MM\.dd' || echo "No old format in docs"
44-49: 중첩 레코드의 날짜 형식도 일관되게 업데이트되었습니다.외부 레코드와 동일하게 내부
TicketTypeRequest의 날짜 형식도 업데이트되어 일관성이 유지되고 있습니다.
|
|
||
| @NotNull(message = "티켓라운드 판매 시작일은 필수입니다.") | ||
| @JsonFormat(pattern = "yyyy.MM.dd HH:mm") | ||
| @JsonFormat(pattern = "yyyy-MM-dd HH:mm") |
There was a problem hiding this comment.
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
날짜 형식 변경으로 인한 API 호환성 영향을 확인하세요.
날짜 형식을 "yyyy.MM.dd HH:mm"에서 "yyyy-MM-dd HH:mm"로 변경한 것은 ISO 8601 표준에 더 가깝게 정렬하는 좋은 개선입니다. 하지만 이는 기존 API 클라이언트에 영향을 줄 수 있는 breaking change입니다.
다음 사항들을 확인해주세요:
-
코드베이스 전체의 일관성: 다른 DTO 파일들에서도 동일한 날짜 형식 패턴이 사용되는지 확인이 필요합니다.
-
클라이언트 영향도: 기존 API 클라이언트들이 새로운 형식으로 요청을 보낼 수 있도록 업데이트되어야 합니다.
-
API 문서: Swagger/OpenAPI 문서나 기타 API 문서에 날짜 형식 변경 사항이 반영되어야 합니다.
아래 스크립트를 실행하여 코드베이스 전체에서 다른 날짜 형식 패턴이 남아있는지 확인하세요:
Also applies to: 22-22, 44-44, 48-48
🏁 Script executed:
#!/bin/bash
# 설명: 이전 날짜 형식 패턴이 남아있는 파일 검색
# yyyy.MM.dd 패턴을 사용하는 @JsonFormat 어노테이션 검색
rg -n '@JsonFormat.*yyyy\.MM\.dd' --type java -g '!**/test/**'Length of output: 148
다음 스크립트를 실행하여 리포지토리 내 모든 Java 파일에서 @JsonFormat 어노테이션에 남아있는 이전 날짜 형식(yyyy.MM.dd) 패턴을 검색해 주세요:
🏁 Script executed:
#!/bin/bash
# 이전 날짜 형식 'yyyy.MM.dd' 패턴 검색 (.java 파일 전체 대상)
rg -n '@JsonFormat.*yyyy\.MM\.dd' -g '*.java' -g '!**/test/**'Length of output: 1159
일관성: 모든 @JsonFormat 어노테이션 패턴을 "yyyy-MM-dd HH:mm"으로 통일하세요
-
아래 파일들에 아직
"yyyy.MM.dd HH:mm"패턴이 남아있습니다. 모두"yyyy-MM-dd HH:mm"으로 수정해야 합니다.
• src/main/java/com/permitseoul/permitserver/domain/eventtimetable/timetable/api/dto/TimetableResponse.java (11,13,42,44)
• src/main/java/com/permitseoul/permitserver/domain/eventtimetable/timetable/api/dto/TimetableDetailResponse.java (17,19)
• src/main/java/com/permitseoul/permitserver/domain/admin/coupon/api/dto/response/CouponResponse.java (10) -
Swagger/OpenAPI 문서 및 기타 API 문서에도 날짜 형식 변경 내용을 반영하세요.
-
기존 클라이언트가 새 포맷을 사용하도록 공지・업데이트 계획을 검토하세요.
🤖 Prompt for AI Agents
In
src/main/java/com/permitseoul/permitserver/domain/admin/ticket/api/dto/req/TicketRoundWithTypeCreateRequest.java
around line 18, ensure the @JsonFormat pattern is the unified "yyyy-MM-dd
HH:mm"; if it already is, verify the other listed files use the same pattern and
update any occurrences of "yyyy.MM.dd HH:mm" to "yyyy-MM-dd HH:mm", then update
Swagger/OpenAPI date format annotations and API documentation accordingly and
coordinate a client-notification/update plan for the new format.
| @JsonFormat(pattern = "yyyy-MM-dd HH:mm") | ||
| LocalDateTime ticketRoundSalesStartDate, | ||
|
|
||
| @NotNull(message = "티켓라운드 판매 종료일은 필수입니다.") | ||
| @JsonFormat(pattern = "yyyy.MM.dd HH:mm") | ||
| @JsonFormat(pattern = "yyyy-MM-dd HH:mm") | ||
| LocalDateTime ticketRoundSalesEndDate, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
API 호환성 깨짐 가능성을 확인하세요.
날짜 형식을 "yyyy.MM.dd HH:mm"에서 "yyyy-MM-dd HH:mm"로 변경했습니다. 이는 기존 API 클라이언트에 영향을 주는 breaking change입니다. 다음을 확인하세요:
- 모든 API 소비자(프론트엔드, 모바일 앱 등)가 새 형식을 사용하도록 업데이트되었는지
- 관련 테스트 케이스가 새 형식으로 업데이트되었는지
- API 문서가 새 형식을 반영하도록 업데이트되었는지
다음 스크립트로 코드베이스 전체에서 날짜 형식 일관성을 확인하세요:
🏁 Script executed:
#!/bin/bash
# 설명: 다른 DTO에서 여전히 구 형식을 사용하는지 확인
# 구 형식 패턴 검색
echo "=== 구 형식 (yyyy.MM.dd) 사용 중인 파일 ==="
rg -n '@JsonFormat\(pattern\s*=\s*"yyyy\.MM\.dd' --type=java
# 새 형식 패턴 검색
echo -e "\n=== 새 형식 (yyyy-MM-dd) 사용 중인 파일 ==="
rg -n '@JsonFormat\(pattern\s*=\s*"yyyy-MM-dd' --type=javaLength of output: 2388
날짜 형식 일관화 필요
TimetableResponse.java, TimetableDetailResponse.java 등에서 여전히 "yyyy.MM.dd HH:mm" 패턴을 사용 중입니다. 모든 @JsonFormat(pattern = ...)을 "yyyy-MM-dd HH:mm"으로 통일하고, 관련 API 문서·테스트·클라이언트도 함께 업데이트하세요.
🤖 Prompt for AI Agents
In
src/main/java/com/permitseoul/permitserver/domain/admin/ticket/api/dto/req/TicketRoundWithTypeCreateRequest.java
around lines 18 to 23, the @JsonFormat pattern is "yyyy-MM-dd HH:mm" but other
DTOs (e.g., TimetableResponse.java, TimetableDetailResponse.java) still use
"yyyy.MM.dd HH:mm"; update those files to use the unified "yyyy-MM-dd HH:mm"
pattern, then search the codebase for any remaining @JsonFormat usages and
replace the old pattern, and finally update API documentation, tests, and any
client code or test fixtures to reflect the new date format so
serialization/deserialization remains consistent across services.
| @FeignClient( | ||
| name = "discordClient", | ||
| url = "${discord.webhook-url}", | ||
| configuration = FeignConfig.class | ||
| ) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
FeignConfig에서 재시도 및 타임아웃 설정 확인 필요.
Feign 클라이언트가 외부 Discord webhook을 호출하는데, 네트워크 장애나 일시적인 오류 발생 시 재시도 로직과 적절한 타임아웃 설정이 중요합니다. FeignConfig에 Retryer와 타임아웃이 적절히 구성되어 있는지 확인하세요.
다음 스크립트로 FeignConfig의 구성을 확인하세요:
🏁 Script executed:
#!/bin/bash
# FeignConfig에서 Retryer 및 타임아웃 설정 확인
rg -n -A 10 "class FeignConfig" --type=javaLength of output: 717
FeignConfig에 Timeout 설정 추가 필요
FeignConfig에 Retryer는 정의되어 있으나, connect 및 read timeout이 설정되어 있지 않습니다. Request.Options 빈을 추가해 적절한 타임아웃을 설정하세요. 예시:
@Bean
public Request.Options options() {
return new Request.Options(
5000, TimeUnit.MILLISECONDS, // connect timeout
10000, TimeUnit.MILLISECONDS, // read timeout
true
);
}🤖 Prompt for AI Agents
In
src/main/java/com/permitseoul/permitserver/global/external/discord/DiscordFeignClient.java
around lines 10 to 14, the FeignConfig currently defines a Retryer but lacks
connect/read timeouts; add a Request.Options bean in the FeignConfig to set
sensible connect and read timeouts (for example 5000ms connect and 10000ms read)
and register it as a @Bean so Feign uses those timeouts; ensure you import/use
java.util.concurrent.TimeUnit and return a new Request.Options with the connect
timeout, read timeout and followRedirects flag set appropriately.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
build.gradle
Outdated
| implementation "software.amazon.awssdk:bom:2.34.0" | ||
| implementation "software.amazon.awssdk:s3:2.34.0" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
AWS SDK BOM 선언 방식 오류 수정 필요
implementation "software.amazon.awssdk:bom:2.34.0"처럼 BOM을 일반 의존성으로 추가하면 Gradle이 POM 패키징을 직접 가져오려다 빌드가 실패합니다. platform(...) 또는 enforcedPlatform(...)으로 선언을 바꿔 주세요.
- implementation "software.amazon.awssdk:bom:2.34.0"
+ implementation platform("software.amazon.awssdk:bom:2.34.0")📝 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.
| implementation "software.amazon.awssdk:bom:2.34.0" | |
| implementation "software.amazon.awssdk:s3:2.34.0" | |
| // use platform() for BOM imports to avoid POM resolution errors | |
| implementation platform("software.amazon.awssdk:bom:2.34.0") | |
| implementation "software.amazon.awssdk:s3:2.34.0" |
🤖 Prompt for AI Agents
In build.gradle around lines 82 to 83, the AWS SDK BOM is declared as a normal
dependency which causes Gradle to attempt to fetch the BOM POM as a jar and
fail; replace the BOM declaration by using platform(...) or
enforcedPlatform(...) (e.g., change the implementation line that references
software.amazon.awssdk:bom:2.34.0 to use platform/enforcedPlatform so the BOM is
imported correctly) and keep the s3 dependency as an implementation dependency.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (1)
build.gradle (1)
82-85: AWS SDK 버전 관리를 BOM으로 일관되게 가져가세요.지금은 S3 모듈만 직접 버전 지정하고 있어 다른 AWS 모듈을 추가할 때마다 일일이 버전을 맞춰야 합니다.
platform으로 BOM을 가져오면 모든 AWS SDK 모듈이 같은 릴리스 라인으로 정렬되어 유지보수와 보안 패치 적용이 쉬워집니다.+ implementation platform("software.amazon.awssdk:bom:2.34.0") - implementation "software.amazon.awssdk:s3:2.34.0" + implementation "software.amazon.awssdk:s3"
🔥Pull requests
⛳️ 작업한 브랜치
👷 작업한 내용
[ dev ]
[ prod ]
🚨 참고 사항
[ todo ] - 변경 가능성 있음