Skip to content

Conversation

@eraser502
Copy link
Contributor

Overview

  • 실서버 배포

Related Issue

  • Closes

PR Checklist

  • [ ]

Additional Information

@coderabbitai
Copy link

coderabbitai bot commented Dec 7, 2025

Walkthrough

이 PR은 대규모 구조적 개선으로, 다수의 컨트롤러 클래스에 Swagger/OpenAPI 문서화 인터페이스 계약을 도입하고, 새로운 상담 알림 시스템을 구현합니다. 구체적으로, 관리자, 인증, CMS, Redot 도메인의 약 20개 컨트롤러가 해당 *ControllerDocs 인터페이스를 구현하도록 변경되었으며, ConsultationNotificationChannel 및 DiscordConsultationNotificationChannel을 통한 다중 채널 알림 인프라가 추가되었습니다. 또한 ConsultationNotificationProperties 설정이 활성화되었고 ConsultationService에 알림 발송 로직이 통합되었습니다.

Sequence Diagram

sequenceDiagram
    actor User
    participant ConsultationController
    participant ConsultationService
    participant ConsultationRepository
    participant ConsultationNotificationService
    participant ChannelImpl as ConsultationNotification<br/>Channel Impl
    participant Discord as Discord API

    User->>ConsultationController: createConsultation(request)
    ConsultationController->>ConsultationService: createConsultation(request)
    ConsultationService->>ConsultationRepository: save(consultation)
    ConsultationRepository-->>ConsultationService: saved consultation
    ConsultationService->>ConsultationNotificationService: sendConsultationCreated(consultation)
    
    rect rgb(200, 220, 240)
        Note over ConsultationNotificationService: For each channel
        ConsultationNotificationService->>ChannelImpl: send(consultation)
        alt Discord Webhook Configured
            ChannelImpl->>ChannelImpl: Build embed payload
            ChannelImpl->>Discord: POST webhook
            Discord-->>ChannelImpl: 204 No Content
        else Exception Handling
            ChannelImpl->>ChannelImpl: Log error (continue)
        end
        ChannelImpl-->>ConsultationNotificationService: return
    end
    
    ConsultationNotificationService-->>ConsultationService: return
    ConsultationService-->>ConsultationController: ConsultationResponse
    ConsultationController-->>User: 200 OK
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20-25 minutes

주의 깊게 검토할 영역:

  • ConsultationNotificationServiceDiscordConsultationNotificationChannel: 다중 채널 알림 로직, 예외 처리, Discord 페이로드 구성 세부사항
  • ConsultationService.createConsultation(): 알림 서비스 통합 지점과 트랜잭션 범위
  • RedotServerApplication: ConsultationNotificationProperties 활성화 설정
  • CMSMemberControllerCMSMemberControllerDocs: 서비스 메서드 변경사항 확인 (getCMSMemberListBySearchCondition)

Possibly related PRs

  • Feature / consultation / discord webhook #114: 동일 상담 알림 하위 시스템을 수정하며, ConsultationNotificationProperties, ConsultationNotificationChannel, DiscordConsultationNotificationChannel 등을 추가하는 직접 관련 PR
  • Docs / swagger api #113: 다수의 컨트롤러에 동일한 Swagger 문서화 인터페이스를 추가하고 구현하는 패턴이 일치하는 PR
  • Feature / redot member oauth #81: RedotServerApplication의 @EnableConfigurationProperties 어노테이션을 확장하는 동일 위치 변경으로 관련된 PR

Suggested labels

feature

Poem

🐰 상담 알림이 살금살금 다가오고 🌙
디스코드 훅이 메시지를 보내고
많은 컨트롤러들이 손을 맞잡고
문서화 인터페이스로 춤을 춘다네
API 세상이 한층 더 밝아졌고려!

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings, 1 inconclusive)
Check name Status Explanation Resolution
Description check ⚠️ Warning PR 설명이 매우 불완전합니다. Overview에 '실서버 배포' 만 있을 뿐 구체적인 변경사항이나 이유가 설명되어 있지 않으며, Related Issue와 PR Checklist, Additional Information이 모두 비워져 있습니다. PR 설명을 상세히 작성하세요. 추가된 문서화 인터페이스 목록, 컨설테이션 알림 기능, 설정 속성 추가 등의 주요 변경사항을 구체적으로 설명하고 관련 이슈와 체크리스트를 작성해주세요.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.72% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
Title check ❓ Inconclusive PR 제목이 주요 변경사항을 명확하게 설명하지 못하고 있습니다. 'Deploy / Merger to Main'은 배포 관련 제목이지만, 변경사항 요약은 다양한 컨트롤러 인터페이스 문서화 추가 및 설정 속성 추가 등으로 구성되어 있어, 제목이 실제 코드 변경의 핵심을 충분히 반영하지 않습니다. 제목을 변경하여 주요 변경사항을 더 명확하게 반영하도록 하세요. 예: 'Add controller documentation interfaces and consultation notification features' 또는 'Refactor controllers to implement documentation interfaces'
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch develop

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.

@eraser502 eraser502 changed the title Deploy / Merger to Main Deploy / Merge to Main Dec 7, 2025
@eraser502 eraser502 merged commit 046f4cc into main Dec 7, 2025
1 of 2 checks passed
Copy link

@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: 1

🧹 Nitpick comments (17)
src/main/java/redot/redot_server/domain/admin/controller/docs/AdminRedotAppControllerDocs.java (1)

15-18: 에러 응답 문서화 추가 고려

현재 200 응답만 문서화되어 있습니다. API 문서의 완성도를 높이기 위해 발생 가능한 에러 응답(400 유효성 검증 실패, 401/403 인증/권한 오류 등)도 문서화하면 API 사용자에게 더 유용할 수 있습니다.

     @Operation(summary = "Redot 앱 생성", description = "관리자 권한으로 신규 Redot 앱을 생성합니다.")
     @ApiResponse(responseCode = "200", description = "생성 성공",
             content = @Content(schema = @Schema(implementation = RedotAppInfoResponse.class)))
+    @ApiResponse(responseCode = "400", description = "잘못된 요청")
+    @ApiResponse(responseCode = "401", description = "인증 실패")
+    @ApiResponse(responseCode = "403", description = "권한 없음")
     ResponseEntity<RedotAppInfoResponse> createRedotApp(RedotAppCreateRequest request);
src/main/java/redot/redot_server/domain/site/domain/controller/docs/DomainControllerDocs.java (1)

12-19: 문서화 인터페이스 패턴이 올바르게 구현되었습니다.

Swagger 문서화를 별도의 인터페이스로 분리하는 것은 좋은 관행입니다. 컨트롤러 코드의 가독성이 향상되고 문서화 계약이 명확해집니다.

에러 응답 문서화를 고려해보세요.

현재 200 성공 케이스만 문서화되어 있습니다. 검증 오류(400), 서버 오류(500) 등 발생 가능한 에러 응답도 문서화하면 API 사용자에게 더 유용한 문서가 됩니다.

필요시 다음과 같이 에러 응답을 추가할 수 있습니다:

     @Operation(summary = "서브도메인 조회", description = "사용 가능한 서브도메인인지 확인합니다.")
     @ApiResponse(responseCode = "200", description = "조회 성공",
             content = @Content(schema = @Schema(implementation = SubdomainLookupResponse.class)))
+    @ApiResponse(responseCode = "400", description = "잘못된 요청")
+    @ApiResponse(responseCode = "500", description = "서버 오류")
     ResponseEntity<SubdomainLookupResponse> getSubdomain(SubdomainLookupRequest request);
src/main/java/redot/redot_server/domain/redot/plan/controller/docs/PlanControllerDocs.java (1)

15-18: 에러 응답 문서화를 추가하는 것을 권장합니다.

Swagger 어노테이션이 올바르게 작성되었으며, 200 성공 응답에 대한 문서화가 적절합니다. 다만, 잠재적인 에러 케이스(예: 500 Internal Server Error)에 대한 @ApiResponse 어노테이션을 추가하면 API 문서의 완성도가 높아집니다.

다음과 같이 에러 응답을 추가할 수 있습니다:

 @Operation(summary = "플랜 목록 조회", description = "이용 가능한 모든 플랜 정보를 조회합니다.")
 @ApiResponse(responseCode = "200", description = "조회 성공",
         content = @Content(array = @ArraySchema(schema = @Schema(implementation = PlanResponse.class))))
+@ApiResponse(responseCode = "500", description = "서버 오류")
 List<PlanResponse> getAllPlans();
src/main/java/redot/redot_server/domain/admin/controller/docs/AdminConsultationControllerDocs.java (1)

1-37: Admin 상담 API 문서 인터페이스 구조 전반적으로 적절합니다

@Tag, 각 메서드의 @Operation/@ApiResponse, 파라미터별 @Parameter/@ParameterObject 구성이 컨트롤러 시그니처와 잘 맞고, 한글 설명도 명확합니다. 응답 타입도 실제 구현(ResponseEntity<ConsultationResponse>, ResponseEntity<PageResponse<ConsultationResponse>>)과 일치해 보입니다.

다만 getAllConsultations의 응답 스키마가 PageResponse.class로만 지정되어 있어, 실제 문서에서 제네릭 타입(ConsultationResponse)이 어떻게 표현되는지 한 번만 확인해 보시면 좋겠습니다. 설정에 따라 제네릭 정보가 충분히 드러나지 않을 수 있습니다.

src/main/java/redot/redot_server/domain/admin/controller/docs/AdminControllerDocs.java (1)

25-66: OpenAPI 문서화가 체계적으로 구성되었습니다.

모든 엔드포인트에 대해 명확한 한글 설명과 함께 적절한 응답 코드(200, 204)와 스키마가 정의되어 있습니다. @Parameter(hidden = true)를 통해 Spring Security가 주입하는 파라미터들을 API 문서에서 숨긴 것도 적절합니다.

선택적 개선사항: 에러 응답(400, 401, 403, 404 등)에 대한 @ApiResponse 문서화를 추가하면 API 사용자에게 더 유용할 수 있습니다. 예시:

@ApiResponse(responseCode = "404", description = "관리자를 찾을 수 없음")
@ApiResponse(responseCode = "403", description = "권한 없음")
src/main/java/redot/redot_server/domain/cms/member/controller/docs/CMSMemberControllerDocs.java (1)

38-44: PageResponse 제네릭 타입 스키마 문서화 개선 제안

PageResponse.class는 제네릭 타입이므로 Swagger UI에서 내부 CMSMemberResponse 구조가 표시되지 않을 수 있습니다. 더 정확한 문서화를 위해 커스텀 스키마 클래스를 정의하거나, 프로젝트 전반에서 일관된 방식을 사용하고 있다면 현재 방식을 유지해도 무방합니다.

// 옵션: 명시적 스키마 클래스 정의 예시
@Schema(name = "PageResponseCMSMember")
public class CMSMemberPageResponse extends PageResponse<CMSMemberResponse> {}
src/main/java/redot/redot_server/domain/cms/inquiry/controller/docs/RedotAppInquiryControllerDocs.java (1)

33-39: PageResponse 제네릭 타입을 명시적으로 드러내는 스키마 개선을 고려해 볼 수 있습니다.

getAllInquiries@ApiResponse에서 implementation = PageResponse.class만 지정되어 있어 Swagger UI에서 내부 요소 타입(RedotAppInquiryResponse)이 덜 직관적으로 보일 수 있습니다. 필요하다면 PageResponse<RedotAppInquiryResponse>를 표현하는 별도 응답 DTO나 커스텀 스키마를 두고 그 타입을 implementation으로 참조하는 방식도 고려해 보셔도 좋겠습니다.

src/main/java/redot/redot_server/domain/auth/controller/docs/EmailVerificationControllerDocs.java (1)

17-25: 성공(200) 외의 에러 응답 스펙도 명시하면 좋습니다

현재는 200 응답만 정의되어 있어, 인증 코드 형식 오류, 만료, 속도 제한(too many requests) 등 에러 케이스가 문서에서 보이지 않습니다. 공통 에러 응답 DTO가 있다면 400, 401, 429, 500 정도를 추가로 명시해 두면 클라이언트 개발자가 사용하기 훨씬 수월할 것 같습니다.

src/main/java/redot/redot_server/domain/auth/controller/EmailVerificationController.java (1)

24-35: 검증/바디 애노테이션을 인터페이스에 옮길 필요가 있는지 한 번만 정리하면 좋겠습니다

현재 @RequestBody @Valid는 구현체(컨트롤러)에만 있고, docs 인터페이스에는 없습니다. 프로젝트 전반의 컨벤션이 “매핑/검증은 컨트롤러, 문서 메타데이터는 *Docs 인터페이스”라면 그대로 유지해도 되지만, 향후 다른 컨트롤러와 일관되게 할지(예: 인터페이스에까지 붙일지)는 팀 컨벤션으로 한 번만 정리해 두면 좋겠습니다.

src/main/java/redot/redot_server/domain/cms/site/setting/controller/SiteSettingController.java (1)

41-48: 업로드 파라미터 이름을 문서에서도 명시하는 방안 제안

실제 구현에서는 @RequestPart("logo") MultipartFile logoFile로 파트 이름을 "logo"로 고정하고 있는데, Docs 인터페이스 쪽에서는 단순히 MultipartFile logoFile만 선언되어 있어서 OpenAPI 스키마 상에서 파라미터 이름/설명이 다소 모호하게 보일 수 있습니다.
필요하다면 Docs 인터페이스에 @Parameter(name = "logo", description = "...") 정도를 추가해 실제 요청 파트 이름과 문서를 더 명확히 맞춰두는 것을 고려해 볼 수 있겠습니다.

src/main/java/redot/redot_server/domain/cms/site/setting/controller/docs/SiteSettingControllerDocs.java (1)

15-34: 컨트롤러-Docs 분리 구조는 좋고, 응답/에러 스펙 확장 여지가 있습니다

SiteSettingControllerDocs로 엔드포인트 계약과 Swagger 메타데이터를 분리한 구조는 재사용성과 가독성 면에서 좋습니다. 각 메서드의 ResponseEntity 제네릭 타입도 실제 DTO와 일치해 문서-구현 간 괴리가 생길 가능성이 낮아 보입니다.

추가로, 필요하다면 다음 정도를 고려해 볼 수 있겠습니다.

  • 공통으로 발생 가능한 에러 응답 코드(예: 400/401/403/404/500 등)를 @ApiResponse에 추가해 실제 동작과 문서의 갭을 줄이기
  • SiteSettingUpdateRequest나 파일 업로드 파라미터에 대해 @Parameter 또는 @io.swagger.v3.oas.annotations.parameters.RequestBody 등을 활용해 설명/예시를 조금 더 보강하기

현재 상태만으로도 배포에 문제는 없고, 위 내용은 추후 문서 품질을 높이기 위한 선택 사항 정도로 보입니다.

src/main/java/redot/redot_server/domain/cms/site/style/controller/docs/StyleInfoControllerDocs.java (1)

13-25: Swagger 문서용 Docs 인터페이스 설계가 명확합니다

컨트롤러 메서드 시그니처와 잘 맞고, @Tag, @Operation, @ApiResponse로 기본 성공 응답 스펙이 잘 드러납니다.
다만 추후 4xx/5xx와 같은 에러 응답도 @ApiResponse로 명시해 두면 클라이언트 입장에서 더 완전한 스펙을 참고할 수 있을 것 같습니다.

src/main/java/redot/redot_server/domain/auth/controller/docs/CMSAuthControllerDocs.java (1)

20-46: API 메서드 문서화가 명확하고 일관성 있게 작성되었습니다.

모든 엔드포인트에 대한 성공 응답이 적절히 문서화되었으며, 숨겨진 파라미터 처리도 올바릅니다.

다만, API 사용자에게 더 나은 가이드를 제공하기 위해 주요 에러 응답(예: 401 Unauthorized, 400 Bad Request 등)도 @apiresponse로 문서화하는 것을 고려해볼 수 있습니다. 이는 프로젝트 전체의 문서화 패턴에 따라 선택적으로 적용 가능합니다.

src/main/java/redot/redot_server/domain/auth/controller/docs/AdminAuthControllerDocs.java (1)

27-30: 리소스 생성 시 HTTP 201 상태 코드 고려해보세요.

createAdmin 메서드가 현재 200 상태 코드를 반환하고 있습니다. 새로운 리소스를 생성하는 POST 요청의 경우 HTTP 201 (Created) 상태 코드가 RESTful 관례에 더 부합합니다.

-    @ApiResponse(responseCode = "200", description = "생성 성공",
+    @ApiResponse(responseCode = "201", description = "생성 성공",
             content = @Content(schema = @Schema(implementation = AdminResponse.class)))
     ResponseEntity<AdminResponse> createAdmin(AdminCreateRequest request);

이 변경을 적용하는 경우, AdminAuthController의 구현부도 함께 수정해야 합니다:

@PostMapping("/sign-up")
@Override
public ResponseEntity<AdminResponse> createAdmin(@Valid @RequestBody AdminCreateRequest request) {
    return ResponseEntity.status(HttpStatus.CREATED).body(adminService.createAdmin(request));
}
src/main/java/redot/redot_server/domain/redot/consultation/controller/docs/ConsultationControllerDocs.java (1)

15-18: HTTP 상태 코드 고려: 생성 API는 201 사용 권장

POST 생성 엔드포인트의 경우 200 OK 대신 201 Created가 RESTful 관례에 더 적합합니다. 현재 컨트롤러가 ResponseEntity.ok()를 반환하므로 문서와 일치하지만, 향후 REST 표준 준수를 위해 둘 다 201로 변경하는 것을 고려해 주세요.

src/main/java/redot/redot_server/domain/redot/consultation/service/ConsultationService.java (1)

20-27: 트랜잭션 경계 내 외부 호출 주의

현재 알림 발송이 @Transactional 메서드 내에서 실행됩니다. ConsultationNotificationService가 예외를 catch하므로 롤백은 발생하지 않지만, 외부 서비스(Discord webhook) 호출 동안 DB 커넥션이 불필요하게 점유됩니다.

트래픽이 증가할 경우, @TransactionalEventListener(phase = AFTER_COMMIT)를 사용하거나 알림 로직을 트랜잭션 외부로 분리하는 것을 권장합니다.

// 대안 1: 이벤트 기반 분리
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
public void handleConsultationCreated(ConsultationCreatedEvent event) {
    consultationNotificationService.sendConsultationCreated(event.consultation());
}
src/main/java/redot/redot_server/domain/redot/consultation/config/ConsultationNotificationProperties.java (1)

5-11: 설정값 검증 추가 고려

현재 DiscordConsultationNotificationChannel에서 런타임에 null/empty 체크를 수행하지만, 필수 설정값의 경우 애플리케이션 시작 시 검증하면 설정 오류를 조기에 발견할 수 있습니다.

+import jakarta.validation.constraints.NotBlank;
+import org.springframework.validation.annotation.Validated;

+@Validated
 @ConfigurationProperties(prefix = "notifications.consultation")
 public record ConsultationNotificationProperties(
         String adminConsoleBaseUrl,
         Discord discord
 ) {
-    public record Discord(String webhookUrl) {}
+    public record Discord(@NotBlank String webhookUrl) {}
 }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6ae37d4 and 8bdbae5.

📒 Files selected for processing (42)
  • src/main/java/redot/redot_server/RedotServerApplication.java (1 hunks)
  • src/main/java/redot/redot_server/domain/admin/controller/AdminConsultationController.java (3 hunks)
  • src/main/java/redot/redot_server/domain/admin/controller/AdminController.java (3 hunks)
  • src/main/java/redot/redot_server/domain/admin/controller/AdminRedotAppController.java (1 hunks)
  • src/main/java/redot/redot_server/domain/admin/controller/docs/AdminConsultationControllerDocs.java (1 hunks)
  • src/main/java/redot/redot_server/domain/admin/controller/docs/AdminControllerDocs.java (1 hunks)
  • src/main/java/redot/redot_server/domain/admin/controller/docs/AdminRedotAppControllerDocs.java (1 hunks)
  • src/main/java/redot/redot_server/domain/auth/controller/AdminAuthController.java (5 hunks)
  • src/main/java/redot/redot_server/domain/auth/controller/AdminImpersonationController.java (2 hunks)
  • src/main/java/redot/redot_server/domain/auth/controller/CMSAuthController.java (5 hunks)
  • src/main/java/redot/redot_server/domain/auth/controller/EmailVerificationController.java (2 hunks)
  • src/main/java/redot/redot_server/domain/auth/controller/RedotMemberAuthController.java (7 hunks)
  • src/main/java/redot/redot_server/domain/auth/controller/docs/AdminAuthControllerDocs.java (1 hunks)
  • src/main/java/redot/redot_server/domain/auth/controller/docs/AdminImpersonationControllerDocs.java (1 hunks)
  • src/main/java/redot/redot_server/domain/auth/controller/docs/CMSAuthControllerDocs.java (1 hunks)
  • src/main/java/redot/redot_server/domain/auth/controller/docs/EmailVerificationControllerDocs.java (1 hunks)
  • src/main/java/redot/redot_server/domain/auth/controller/docs/RedotMemberAuthControllerDocs.java (1 hunks)
  • src/main/java/redot/redot_server/domain/cms/inquiry/controller/RedotAppInquiryController.java (4 hunks)
  • src/main/java/redot/redot_server/domain/cms/inquiry/controller/docs/RedotAppInquiryControllerDocs.java (1 hunks)
  • src/main/java/redot/redot_server/domain/cms/member/controller/CMSMemberController.java (5 hunks)
  • src/main/java/redot/redot_server/domain/cms/member/controller/docs/CMSMemberControllerDocs.java (1 hunks)
  • src/main/java/redot/redot_server/domain/cms/site/menu/controller/CMSMenuController.java (1 hunks)
  • src/main/java/redot/redot_server/domain/cms/site/menu/controller/docs/CMSMenuControllerDocs.java (1 hunks)
  • src/main/java/redot/redot_server/domain/cms/site/setting/controller/SiteSettingController.java (4 hunks)
  • src/main/java/redot/redot_server/domain/cms/site/setting/controller/docs/SiteSettingControllerDocs.java (1 hunks)
  • src/main/java/redot/redot_server/domain/cms/site/style/controller/StyleInfoController.java (2 hunks)
  • src/main/java/redot/redot_server/domain/cms/site/style/controller/docs/StyleInfoControllerDocs.java (1 hunks)
  • src/main/java/redot/redot_server/domain/redot/app/controller/RedotAppController.java (2 hunks)
  • src/main/java/redot/redot_server/domain/redot/app/controller/docs/RedotAppControllerDocs.java (1 hunks)
  • src/main/java/redot/redot_server/domain/redot/consultation/config/ConsultationNotificationProperties.java (1 hunks)
  • src/main/java/redot/redot_server/domain/redot/consultation/controller/ConsultationController.java (1 hunks)
  • src/main/java/redot/redot_server/domain/redot/consultation/controller/docs/ConsultationControllerDocs.java (1 hunks)
  • src/main/java/redot/redot_server/domain/redot/consultation/notification/ConsultationNotificationChannel.java (1 hunks)
  • src/main/java/redot/redot_server/domain/redot/consultation/notification/ConsultationNotificationService.java (1 hunks)
  • src/main/java/redot/redot_server/domain/redot/consultation/notification/DiscordConsultationNotificationChannel.java (1 hunks)
  • src/main/java/redot/redot_server/domain/redot/consultation/service/ConsultationService.java (2 hunks)
  • src/main/java/redot/redot_server/domain/redot/member/controller/RedotMemberController.java (4 hunks)
  • src/main/java/redot/redot_server/domain/redot/member/controller/docs/RedotMemberControllerDocs.java (1 hunks)
  • src/main/java/redot/redot_server/domain/redot/plan/controller/PlanController.java (1 hunks)
  • src/main/java/redot/redot_server/domain/redot/plan/controller/docs/PlanControllerDocs.java (1 hunks)
  • src/main/java/redot/redot_server/domain/site/domain/controller/DomainController.java (1 hunks)
  • src/main/java/redot/redot_server/domain/site/domain/controller/docs/DomainControllerDocs.java (1 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-11-05T12:16:07.014Z
Learnt from: eraser502
Repo: redotlabs/server PR: 67
File: src/main/java/redot/redot_server/domain/cms/controller/CustomerController.java:19-22
Timestamp: 2025-11-05T12:16:07.014Z
Learning: In the redotlabs/server repository, the `/api/v1/customer` endpoint intentionally exposes customer owner email in CMSMemberResponse as public information without authentication. This is an accepted design decision and should not be flagged as a security concern in future reviews.

Applied to files:

  • src/main/java/redot/redot_server/domain/cms/member/controller/docs/CMSMemberControllerDocs.java
  • src/main/java/redot/redot_server/domain/auth/controller/RedotMemberAuthController.java
  • src/main/java/redot/redot_server/domain/auth/controller/docs/RedotMemberAuthControllerDocs.java
🧬 Code graph analysis (4)
src/main/java/redot/redot_server/domain/redot/consultation/notification/DiscordConsultationNotificationChannel.java (1)
src/main/java/redot/redot_server/domain/redot/consultation/notification/ConsultationNotificationService.java (1)
  • Slf4j (9-25)
src/main/java/redot/redot_server/domain/redot/consultation/notification/ConsultationNotificationService.java (2)
src/main/java/redot/redot_server/domain/redot/consultation/notification/DiscordConsultationNotificationChannel.java (1)
  • Slf4j (23-133)
src/main/java/redot/redot_server/domain/redot/consultation/service/ConsultationService.java (1)
  • Service (12-29)
src/main/java/redot/redot_server/domain/cms/site/style/controller/StyleInfoController.java (2)
src/main/java/redot/redot_server/domain/cms/site/menu/controller/CMSMenuController.java (1)
  • RestController (14-28)
src/main/java/redot/redot_server/domain/site/domain/controller/DomainController.java (1)
  • RestController (15-28)
src/main/java/redot/redot_server/domain/admin/controller/AdminRedotAppController.java (2)
src/main/java/redot/redot_server/domain/redot/app/controller/RedotAppController.java (1)
  • RestController (26-63)
src/main/java/redot/redot_server/domain/redot/plan/controller/PlanController.java (1)
  • RestController (12-24)
🔇 Additional comments (52)
src/main/java/redot/redot_server/domain/admin/controller/AdminRedotAppController.java (1)

10-26: LGTM!

AdminRedotAppControllerDocs 인터페이스 구현과 @Override 어노테이션 추가가 코드베이스의 기존 패턴(RedotAppController, PlanController)과 일관성 있게 적용되었습니다.

src/main/java/redot/redot_server/domain/admin/controller/docs/AdminRedotAppControllerDocs.java (1)

1-13: 구조가 잘 정리되어 있습니다.

Swagger/OpenAPI 문서화 인터페이스가 프로젝트의 기존 패턴을 따르며 깔끔하게 구현되었습니다. @Tag 어노테이션을 통한 API 그룹화도 적절합니다.

src/main/java/redot/redot_server/domain/auth/controller/AdminImpersonationController.java (1)

13-13: 문서화 인터페이스 구현이 적절합니다.

컨트롤러가 AdminImpersonationControllerDocs 인터페이스를 구현하여 API 문서화와 구현 로직을 명확히 분리하는 패턴이 잘 적용되었습니다. 메서드 시그니처가 인터페이스 계약과 일치하며, @Override 어노테이션도 올바르게 추가되었습니다.

Also applies to: 23-23, 27-27

src/main/java/redot/redot_server/domain/auth/controller/docs/AdminImpersonationControllerDocs.java (1)

1-24: OpenAPI 문서화 인터페이스가 잘 구성되었습니다.

Swagger 어노테이션을 활용한 API 문서화 계약이 명확하게 정의되었습니다. 특히 다음 사항들이 적절합니다:

  • 인프라 관련 파라미터(HttpServletRequest, JwtPrincipal)는 @Parameter(hidden = true)로 API 문서에서 숨김 처리
  • 비즈니스 요청 DTO(CMSAdminImpersonationRequest)만 API 문서에 노출
  • 한글 설명이 명확하고 적절함

이러한 문서화 패턴은 API 사용자에게 필요한 정보만 제공하여 가독성을 높입니다.

src/main/java/redot/redot_server/domain/site/domain/controller/DomainController.java (1)

18-18: 인터페이스 구현이 정확합니다.

DomainControllerDocs 구현과 @Override 어노테이션 추가로 컴파일 타임에 인터페이스 계약 준수를 보장할 수 있습니다. @Valid@RequestBody 같은 구현 세부사항은 컨트롤러에 남겨두는 것이 올바른 패턴입니다.

Also applies to: 23-23

src/main/java/redot/redot_server/domain/redot/plan/controller/PlanController.java (1)

8-8: 문서화 인터페이스 구현이 올바르게 적용되었습니다.

PlanControllerDocs 인터페이스 구현을 통해 Swagger 문서화와 컨트롤러 로직을 깔끔하게 분리했습니다. @Override 어노테이션도 적절하게 추가되었으며, 메서드 시그니처가 인터페이스와 정확히 일치합니다.

Also applies to: 15-15, 20-20

src/main/java/redot/redot_server/domain/cms/site/menu/controller/CMSMenuController.java (1)

8-8: 문서화 인터페이스 구현이 올바르게 적용되었습니다.

CMSMenuControllerDocs 인터페이스를 구현하여 API 문서화를 분리한 것은 좋은 설계 패턴입니다. @OverRide 어노테이션을 통해 인터페이스 계약 준수를 컴파일 타임에 검증할 수 있습니다.

Also applies to: 17-17, 23-23

src/main/java/redot/redot_server/domain/cms/site/menu/controller/docs/CMSMenuControllerDocs.java (1)

13-19: API 문서화 인터페이스가 올바르게 구성되었습니다.

Swagger/OpenAPI 어노테이션이 적절하게 설정되어 있으며, @Parameter(hidden = true) 처리를 통해 @CurrentRedotApp으로 자동 주입되는 redotAppId 파라미터를 API 문서에서 숨긴 것은 올바른 접근입니다.

src/main/java/redot/redot_server/domain/admin/controller/AdminConsultationController.java (1)

15-52: Docs 인터페이스 구현으로 컨트롤러 계약이 명확해졌습니다

AdminConsultationControllerAdminConsultationControllerDocs를 구현하도록 변경되고, 각 엔드포인트에 @Override가 추가된 구조가 일관되고 안전합니다. 기능 변화 없이 OpenAPI 문서 계약만 분리·강화된 형태라 리스크도 없고, 시그니처 불일치 시 컴파일 타임에 잡을 수 있는 점도 좋습니다.

src/main/java/redot/redot_server/domain/admin/controller/AdminController.java (1)

22-22: API 문서화를 위한 인터페이스 구현 패턴이 잘 적용되었습니다.

AdminControllerDocs 인터페이스 구현을 통해 OpenAPI 문서와 실제 구현을 명확히 분리하는 좋은 설계입니다. 모든 메서드에 @OverRide가 적절히 추가되었고 기존 로직에는 변경이 없습니다.

Also applies to: 37-37, 43-43, 49-49, 55-55, 61-61, 67-67, 74-74, 82-82, 95-95

src/main/java/redot/redot_server/domain/cms/member/controller/CMSMemberController.java (2)

44-70: 인터페이스 기반 문서화 패턴이 잘 적용되었습니다.

CMSMemberControllerDocs 인터페이스 구현을 통해 API 문서화 계약이 명확하게 분리되었으며, @Override 어노테이션이 적절하게 추가되어 컴파일 타임에 계약 준수 여부를 검증할 수 있습니다.


88-116: 삭제 로직이 적절히 구현되었습니다.

deleteCMSMember에서 자기 자신 삭제 방지 로직과 deleteCurrentCMSMember에서 토큰 쿠키 정리 로직이 올바르게 구현되어 있습니다.

src/main/java/redot/redot_server/domain/cms/member/controller/docs/CMSMemberControllerDocs.java (2)

23-44: Swagger 문서화 인터페이스가 잘 구성되었습니다.

@Tag, @Operation, @ApiResponse 어노테이션이 적절히 사용되었고, 내부 파라미터들(redotAppId, jwtPrincipal)에 @Parameter(hidden = true)를 적용하여 API 문서에서 숨김 처리한 점이 좋습니다.


46-77: 나머지 메서드 문서화가 적절합니다.

각 메서드의 응답 코드(200/204)와 설명이 적절하게 정의되어 있으며, 컨트롤러 구현과 시그니처가 일치합니다.

src/main/java/redot/redot_server/domain/redot/member/controller/RedotMemberController.java (2)

15-15: 문서화 인터페이스 구현이 올바르게 적용되었습니다.

RedotMemberControllerDocs 인터페이스를 구현하여 API 문서화 계약을 명확히 하는 접근 방식이 좋습니다. 이를 통해 컨트롤러와 문서가 일관성을 유지할 수 있습니다.

Also applies to: 25-25


31-31: @OverRide 어노테이션이 올바르게 추가되었습니다.

세 개의 메서드에 @Override 어노테이션을 추가하여 인터페이스 계약과의 일치 여부를 컴파일 타임에 검증할 수 있습니다. 메서드 시그니처가 인터페이스와 정확히 일치하는지 확인되었습니다.

Also applies to: 42-42, 51-51

src/main/java/redot/redot_server/domain/redot/member/controller/docs/RedotMemberControllerDocs.java (4)

16-17: API 문서화 인터페이스가 올바르게 정의되었습니다.

@Tag 어노테이션을 통해 Swagger UI에서 명확한 그룹핑이 가능하며, 인터페이스를 통한 문서화 계약 패턴이 적절하게 적용되었습니다.


19-23: 회원 정보 수정 엔드포인트가 명확하게 문서화되었습니다.

@Operation@ApiResponse 어노테이션이 적절히 사용되었으며, JwtPrincipal@Parameter(hidden = true)로 처리하여 API 문서에서 숨기는 것이 보안 주체 처리에 적합합니다.


25-29: 프로필 이미지 업로드 엔드포인트가 올바르게 문서화되었습니다.

멀티파트 파일 업로드에 대한 문서화가 명확하며, 응답 스키마로 UploadedImageUrlResponse를 지정한 것이 적절합니다.


31-33: 회원 탈퇴 엔드포인트가 적절하게 문서화되었습니다.

204 No Content 응답 코드를 사용하는 것이 DELETE 작업의 표준 관례에 부합합니다.

src/main/java/redot/redot_server/domain/cms/inquiry/controller/RedotAppInquiryController.java (1)

17-73: Docs 인터페이스 연동 및 @OverRide 추가가 일관되고 안전해 보입니다.

RedotAppInquiryControllerRedotAppInquiryControllerDocs를 구현하도록 변경된 부분에서, 각 메서드의 시그니처와 파라미터 순서/타입이 인터페이스와 정확히 일치하고, 기능 로직은 그대로 유지되어 Swagger 문서만 강화된 형태라 컴파일/런타임 리스크는 없어 보입니다.

src/main/java/redot/redot_server/domain/cms/inquiry/controller/docs/RedotAppInquiryControllerDocs.java (1)

18-50: 컨트롤러와의 계약 정의가 잘 맞는 Swagger 문서 인터페이스입니다.

각 메서드의 반환 타입과 파라미터 구조가 실제 RedotAppInquiryController와 정확히 일치하고, redotAppId/JwtPrincipal를 hidden 파라미터로 처리한 점, 검색 조건/페이지네이션을 명시적으로 문서화한 점이 일관적입니다. 실제 구현과 문서 스펙 간 괴리가 없어 보입니다.

src/main/java/redot/redot_server/domain/auth/controller/docs/EmailVerificationControllerDocs.java (1)

1-25: EmailVerification 문서용 인터페이스 구조 적절합니다

컨트롤러와 분리된 *ControllerDocs 패턴과 일관되고, 두 메서드의 시그니처도 실제 컨트롤러 구현과 잘 맞습니다. 태그/요약/설명도 용도에 맞게 정리되어 있어 유지보수에 좋아 보입니다.

src/main/java/redot/redot_server/domain/auth/controller/EmailVerificationController.java (1)

10-36: Docs 인터페이스 구현으로 문서/구현 연결 잘 되어 있습니다

EmailVerificationControllerDocs를 구현하도록 변경하고 각 메서드에 @Override를 단 것으로, 문서용 인터페이스와 실제 엔드포인트가 자연스럽게 동기화될 수 있어 좋습니다. 시그니처도 인터페이스와 완전히 일치해서 별다른 문제는 없어 보입니다.

src/main/java/redot/redot_server/domain/redot/app/controller/RedotAppController.java (2)

17-17: 문서화 인터페이스 구현이 올바르게 추가되었습니다.

RedotAppControllerDocs 인터페이스를 구현하여 Swagger/OpenAPI 문서화를 위한 계약을 도입한 것은 좋은 접근 방식입니다. 이는 PR 전반에서 일관되게 적용된 패턴입니다.

Also applies to: 29-29


35-35: @OverRide 어노테이션이 올바르게 추가되었습니다.

모든 메서드가 RedotAppControllerDocs 인터페이스의 계약을 명시적으로 이행하고 있으며, 시그니처가 정확하게 일치합니다. 이는 타입 안전성을 높이고 인터페이스 계약을 명확하게 합니다.

Also applies to: 41-41, 49-49, 56-56

src/main/java/redot/redot_server/domain/redot/app/controller/docs/RedotAppControllerDocs.java (2)

1-19: API 문서화 인터페이스가 올바르게 구성되었습니다.

패키지 구조, import, 그리고 @tag 어노테이션이 적절하게 설정되어 있으며, SpringDoc/OpenAPI 표준을 잘 따르고 있습니다.


21-43: API 메서드 문서화가 정확하게 작성되었습니다.

각 메서드의 시그니처가 컨트롤러 구현과 정확히 일치하며, 다음 사항들이 올바르게 처리되었습니다:

  • 리졸버로 주입되는 파라미터(JwtPrincipal, @CurrentRedotApp)는 @Parameter(hidden = true)로 올바르게 숨김 처리
  • Pageable@ParameterObject 어노테이션 적용
  • 경로 변수에 적절한 설명과 예시 제공
  • 응답 코드와 스키마 정의가 명확함
src/main/java/redot/redot_server/domain/cms/site/setting/controller/SiteSettingController.java (2)

3-39: Docs 인터페이스 구현 + @Valid 추가 방향 좋습니다

SiteSettingControllerDocs를 구현하면서 세 메서드 시그니처를 정확히 맞추고, updateSiteSetting@RequestBody @Valid를 추가해 DTO 제약 조건을 컴파일 타임/런타임 모두에서 안전하게 가져가고 있습니다. 현재 구조 기준으로 컨트롤러와 Docs 인터페이스 간 계약도 잘 정렬되어 보여 별도 수정 필요 없어 보입니다.


50-57: 조회 엔드포인트는 Docs 인터페이스와 잘 정렬되어 있습니다

getSiteSetting은 반환 타입과 파라미터 모두 Docs 인터페이스와 정확히 일치하고, 비즈니스 로직도 단순 위임이라 유지보수 측면에서 무난합니다. 별다른 추가 수정 없이 그대로 가셔도 될 것 같습니다.

src/main/java/redot/redot_server/domain/cms/site/style/controller/StyleInfoController.java (2)

11-25: Docs 인터페이스 구현과 @OverRide로 API 계약이 잘 고정되었습니다

StyleInfoControllerStyleInfoControllerDocs를 구현하고 각 메서드에 @Override를 명시해 둔 구조가 다른 CMS 컨트롤러 패턴과도 자연스럽게 맞고, 문서 계약과 실제 구현이 어긋나기 어렵게 잘 묶여 있습니다.


3-3: @Valid 추가로 요청 DTO 검증이 활성화된 점 좋습니다 (예외 처리 흐름만 한 번 확인 요청)

updateStyleInfoStyleInfoUpdateRequest@Valid를 붙여 Bean Validation 제약이 적용되도록 한 점은 좋습니다. 다만 검증 실패 시 400 등의 응답으로 매핑해 주는 글로벌 예외 핸들러(예: @ControllerAdvice)가 이미 잘 동작 중인지 한 번만 확인해 두면 좋겠습니다.

Also applies to: 30-33

src/main/java/redot/redot_server/domain/auth/controller/CMSAuthController.java (3)

15-15: 문서화 인터페이스 임포트가 올바르게 추가되었습니다.

CMSAuthControllerDocs 인터페이스를 구현하기 위한 필요한 임포트가 추가되었습니다.


30-30: 문서화 인터페이스 구현이 적절합니다.

CMSAuthControllerDocs 인터페이스 구현을 통해 API 문서화 계약을 명시적으로 선언하여, Swagger/OpenAPI 문서와 실제 구현 간의 일관성을 보장합니다.


36-36: 인터페이스 메서드에 @OverRide 어노테이션이 올바르게 적용되었습니다.

모든 인터페이스 메서드에 @OverRide 어노테이션이 추가되어 컴파일 타임에 시그니처 불일치를 감지할 수 있으며, 코드 가독성과 유지보수성이 향상되었습니다.

Also applies to: 46-46, 57-57, 64-64, 76-76

src/main/java/redot/redot_server/domain/auth/controller/docs/CMSAuthControllerDocs.java (1)

1-18: 문서화 인터페이스 구조가 잘 설계되었습니다.

패키지 구조, 임포트, 그리고 @tag 어노테이션을 통한 API 그룹화가 적절하게 구성되어 Swagger UI에서 명확한 API 문서를 제공합니다.

src/main/java/redot/redot_server/domain/auth/controller/RedotMemberAuthController.java (1)

19-19: 문서화 인터페이스 구현이 깔끔하게 적용되었습니다.

컨트롤러가 RedotMemberAuthControllerDocs 인터페이스를 구현하도록 변경되어, API 문서와 실제 구현 간의 계약이 컴파일 타임에 검증됩니다. 모든 메서드에 @Override 어노테이션이 올바르게 추가되었으며, 로직 변경 없이 구조적 개선만 이루어졌습니다.

Also applies to: 39-39, 57-57, 63-63, 74-74, 84-84, 93-93, 105-105, 112-112

src/main/java/redot/redot_server/domain/auth/controller/docs/RedotMemberAuthControllerDocs.java (1)

19-58: OpenAPI 문서화 인터페이스가 잘 설계되었습니다.

새로운 문서화 인터페이스가 모든 인증 엔드포인트를 적절히 정의하고 있습니다. 각 메서드는 명확한 @Operation@ApiResponse 어노테이션으로 문서화되어 있으며, 내부 파라미터(HttpServletRequest, JwtPrincipal)는 @Parameter(hidden=true)로 적절히 숨겨져 있습니다. springdoc-openapi 의존성이 올바르게 구성되어 있으며, 배포 시 Swagger UI에서 정상적으로 표시될 것입니다.

src/main/java/redot/redot_server/domain/auth/controller/AdminAuthController.java (2)

15-15: 인터페이스 구현이 올바르게 적용되었습니다.

AdminAuthControllerDocs 인터페이스 구현은 Swagger 문서화를 위한 깔끔한 관심사 분리를 제공합니다. 컨트롤러 로직과 API 문서를 분리하는 이 패턴은 유지보수성을 향상시킵니다.

Also applies to: 31-31


38-38: @OverRide 애노테이션이 일관되게 추가되었습니다.

모든 public 메서드에 @Override 애노테이션이 적용되어 인터페이스 계약과의 컴파일 타임 검증이 보장됩니다. 메서드 시그니처가 변경되지 않아 기존 동작이 유지됩니다.

Also applies to: 48-48, 54-54, 65-65, 72-72, 84-84

src/main/java/redot/redot_server/domain/auth/controller/docs/AdminAuthControllerDocs.java (3)

1-19: Swagger 문서화 인터페이스 구조가 잘 설계되었습니다.

패키지 구조와 임포트가 적절하며, @Tag 애노테이션이 API 그룹화를 명확하게 제공합니다. 한국어 설명도 일관성 있게 작성되었습니다.


24-25: 인프라 객체에 대한 파라미터 숨김 처리가 적절합니다.

HttpServletRequestJwtPrincipal 파라미터에 @Parameter(hidden = true) 애노테이션을 사용하여 Swagger UI에서 이러한 인프라 객체를 숨긴 것은 올바른 접근입니다. 이는 API 문서를 더 깔끔하고 사용자 친화적으로 만듭니다.

Also applies to: 35-35, 40-40, 44-44


21-48: Method signature annotations between interface and implementation do not need to match.

The interface uses @Parameter(hidden = true) for OpenAPI/Swagger documentation purposes, while the implementation uses @AuthenticationPrincipal and other Spring annotations for runtime dependency injection. This is a valid and consistent pattern used throughout the codebase (as seen in AdminControllerDocs and AdminController). Documentation annotations and runtime annotations serve different purposes and are not required to be identical. No compilation or runtime errors will occur from this difference.

Likely an incorrect or invalid review comment.

src/main/java/redot/redot_server/domain/redot/consultation/notification/ConsultationNotificationChannel.java (1)

5-7: LGTM!

알림 채널에 대한 깔끔한 추상화입니다. Strategy 패턴을 적절히 활용하여 다중 채널 알림 시스템을 지원합니다.

src/main/java/redot/redot_server/domain/redot/consultation/controller/ConsultationController.java (1)

10-27: LGTM!

기존 컨트롤러들(PlanController, RedotAppController 등)과 동일한 패턴으로 Docs 인터페이스를 구현했습니다. @Override 어노테이션으로 컴파일 타임에 계약 준수를 보장합니다.

src/main/java/redot/redot_server/RedotServerApplication.java (1)

6-12: LGTM!

ConsultationNotificationProperties가 기존 속성 클래스들과 동일한 방식으로 등록되었습니다.

src/main/java/redot/redot_server/domain/redot/consultation/notification/ConsultationNotificationService.java (1)

12-24: LGTM! 채널별 에러 핸들링 적절

각 채널의 예외를 개별적으로 catch하여 한 채널의 실패가 다른 채널에 영향을 주지 않도록 설계되었습니다.

향후 채널이 추가되거나 지연 시간이 문제될 경우, @Async 적용을 고려할 수 있습니다.

src/main/java/redot/redot_server/domain/redot/consultation/notification/DiscordConsultationNotificationChannel.java (5)

23-32: LGTM!

Spring 컴포넌트 설정과 필드 선언이 적절합니다. RestTemplate@PostConstruct에서 초기화하는 패턴은 타임아웃 설정을 위해 적합합니다.


34-46: LGTM!

타임아웃 설정(연결 5초, 읽기 10초)이 적절합니다. 외부 서비스 호출 시 무한 대기를 방지하는 좋은 패턴입니다.


48-66: LGTM!

Webhook URL 미설정 시 조기 반환과 예외 처리가 적절합니다. ConsultationNotificationService에서도 예외 처리를 하지만, 여기서 catch되므로 중복 로깅은 발생하지 않습니다.


116-124: LGTM!

컨텐츠 길이 제한(1000자) 및 마크다운 래핑이 Discord embed 필드 제한(1024자) 내에서 잘 처리됩니다.


126-131: No null-safety concern needed for getType()

The type field is protected by @NotNull constraint in ConsultationCreateRequest (request validation) and @Column(nullable = false) in the Consultation entity (database constraint). In the normal application flow, getType() cannot return null, so the switch expression is safe from NullPointerException.

Likely an incorrect or invalid review comment.

Comment on lines +72 to +84
Map<String, Object> embed = new HashMap<>();
embed.put("title", "Consultation #" + consultation.getId());
embed.put("url", buildConsultationUrl(consultation));
embed.put("description", "유형: **" + translateType(consultation) + "**");
embed.put("color", 0x3498DB);

List<Map<String, Object>> fields = new ArrayList<>();
fields.add(field("이메일 ✉️", consultation.getEmail(), true));
fields.add(field("연락처 📞", consultation.getPhone(), true));
fields.add(field("기존 웹사이트 주소 🌐", consultation.getCurrentWebsiteUrl(), true));
fields.add(field("상담 관리 🔗", buildConsultationUrl(consultation), true));
fields.add(field("내용", formatContent(consultation.getContent()), false));
embed.put("fields", fields);
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

adminConsoleBaseUrl 미설정 시 불필요한 필드 노출

buildConsultationUrl()null을 반환하면 embed URL에 null이 설정되고, "상담 관리 🔗" 필드에는 "-"가 표시됩니다. URL이 없을 때는 해당 필드들을 아예 생략하는 것이 사용자 경험에 더 좋습니다.

-        embed.put("url", buildConsultationUrl(consultation));
+        String consultationUrl = buildConsultationUrl(consultation);
+        if (StringUtils.hasText(consultationUrl)) {
+            embed.put("url", consultationUrl);
+        }
         embed.put("description", "유형: **" + translateType(consultation) + "**");
         embed.put("color", 0x3498DB);
 
         List<Map<String, Object>> fields = new ArrayList<>();
         fields.add(field("이메일 ✉️", consultation.getEmail(), true));
         fields.add(field("연락처 📞", consultation.getPhone(), true));
         fields.add(field("기존 웹사이트 주소 🌐", consultation.getCurrentWebsiteUrl(), true));
-        fields.add(field("상담 관리 🔗", buildConsultationUrl(consultation), true));
+        if (StringUtils.hasText(consultationUrl)) {
+            fields.add(field("상담 관리 🔗", consultationUrl, true));
+        }
📝 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
Map<String, Object> embed = new HashMap<>();
embed.put("title", "Consultation #" + consultation.getId());
embed.put("url", buildConsultationUrl(consultation));
embed.put("description", "유형: **" + translateType(consultation) + "**");
embed.put("color", 0x3498DB);
List<Map<String, Object>> fields = new ArrayList<>();
fields.add(field("이메일 ✉️", consultation.getEmail(), true));
fields.add(field("연락처 📞", consultation.getPhone(), true));
fields.add(field("기존 웹사이트 주소 🌐", consultation.getCurrentWebsiteUrl(), true));
fields.add(field("상담 관리 🔗", buildConsultationUrl(consultation), true));
fields.add(field("내용", formatContent(consultation.getContent()), false));
embed.put("fields", fields);
Map<String, Object> embed = new HashMap<>();
embed.put("title", "Consultation #" + consultation.getId());
String consultationUrl = buildConsultationUrl(consultation);
if (StringUtils.hasText(consultationUrl)) {
embed.put("url", consultationUrl);
}
embed.put("description", "유형: **" + translateType(consultation) + "**");
embed.put("color", 0x3498DB);
List<Map<String, Object>> fields = new ArrayList<>();
fields.add(field("이메일 ✉️", consultation.getEmail(), true));
fields.add(field("연락처 📞", consultation.getPhone(), true));
fields.add(field("기존 웹사이트 주소 🌐", consultation.getCurrentWebsiteUrl(), true));
if (StringUtils.hasText(consultationUrl)) {
fields.add(field("상담 관리 🔗", consultationUrl, true));
}
fields.add(field("내용", formatContent(consultation.getContent()), false));
embed.put("fields", fields);
🤖 Prompt for AI Agents
In
src/main/java/redot/redot_server/domain/redot/consultation/notification/DiscordConsultationNotificationChannel.java
around lines 72-84, buildConsultationUrl(consultation) can return null which
currently causes an embed URL of "null" and a placeholder field; change the code
to call buildConsultationUrl once into a local String (e.g. consultationUrl),
then only call embed.put("url", consultationUrl) if consultationUrl is
non-null/non-empty, and only add the "상담 관리 🔗" field to fields when
consultationUrl is present; this ensures the embed URL and that specific field
are omitted when adminConsoleBaseUrl is not configured.

@eraser502 eraser502 self-assigned this Dec 8, 2025
@coderabbitai coderabbitai bot mentioned this pull request Dec 17, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants