Skip to content

feat: slack 알림 추가 및 재시도 추가#161

Merged
20152842 merged 1 commit intodevelopfrom
feat/#160-notification-service-slack
Jun 7, 2025
Merged

feat: slack 알림 추가 및 재시도 추가#161
20152842 merged 1 commit intodevelopfrom
feat/#160-notification-service-slack

Conversation

@letsgilit
Copy link
Member

@letsgilit letsgilit commented May 8, 2025

💡 이슈

🤩 개요

slack 알림 추가 및 재시도 추가

🧑‍💻 작업 사항

-webhook알림 추가
-slackbot을 사용한 사용자 DM추가
-재처리 로직추가

📖 참고 사항

UserEmail을 통해 사용자 SlackId를 가져오는 과정에서 UserId를 헤더에 포함시켜서 알림이 있는 메소드 마다 UserEmail을 넣어줘야 할 것같아요 일단 하드코딩으로 테스트 해봤을때 메시지 수신은 확인했습니다. gateway에서 feign으로 email을 받아서 넣어줄지, 아니면 token에 email을 넣어서 헤더에 넣어줄지는 잘모르겠어서 나뒀습니다.

Summary by CodeRabbit

  • 신규 기능

    • 슬랙 메시지 전송 기능이 추가되어 알림을 슬랙 채널 및 사용자에게 직접 보낼 수 있습니다.
    • 슬랙 사용자 이메일을 통한 사용자 식별 및 메시지 전송이 지원됩니다.
  • 버그 수정

    • 알림 이벤트 처리 시 슬랙 메시지 전송이 정상적으로 동작하도록 개선되었습니다.
  • 리팩터

    • 알림 타입 및 이벤트 타입 관리 방식이 개선되어 외부 DTO를 사용하도록 변경되었습니다.
    • 카프카 이벤트 발행 로직이 제거되고 슬랙 연동 중심으로 전환되었습니다.
  • 라이브러리/의존성

    • 슬랙 및 장애 복원 관련 라이브러리가 추가되었습니다.

-webhook알림 추가
-slackbot을 사용한 사용자 DM추가
-재처리 로직추가
@letsgilit letsgilit self-assigned this May 8, 2025
@letsgilit letsgilit added the enhancement New feature or request label May 8, 2025
@letsgilit letsgilit linked an issue May 8, 2025 that may be closed by this pull request
1 task
@coderabbitai
Copy link

coderabbitai bot commented May 8, 2025

Walkthrough

이번 변경 사항에서는 notification-service에 Slack 알림 기능이 추가되었습니다. Slack 메시지 송신을 위한 클라이언트, DTO, 서비스 메서드가 도입되었으며, Kafka 이벤트 소비 시 Slack 알림을 전송하도록 로직이 확장되었습니다. 관련 도메인 enum 및 이벤트 클래스는 외부 공용 패키지로 이동되었습니다.

Changes

파일/경로 요약 변경 요약
common/src/main/java/com/timebank/common/infrastructure/external/notification/dto/NotificationEvent.java NotificationEvent 클래스에 slackUserEmail 필드 추가
notification-service/build.gradle spring-cloud-starter-openfeign, resilience4j-spring-boot3, resilience4j-retry 의존성 추가
notification-service/src/main/java/com/timebank/notification_service/application/client/SlackClient.java SlackClient 인터페이스 신규 생성, Slack 메시지 송신 및 이메일로 유저 ID 조회 메서드 선언
notification-service/src/main/java/com/timebank/notification_service/application/dto/SlackBotMessage.java
notification-service/src/main/java/com/timebank/notification_service/application/dto/SlackWebHookMessage.java
notification-service/src/main/java/com/timebank/notification_service/application/dto/SlackUserResponse.java
Slack 메시지 및 유저 응답용 DTO 클래스 신규 생성
notification-service/src/main/java/com/timebank/notification_service/application/dto/NotificationDto.java NotificationType import 경로 공용 패키지로 변경
notification-service/src/main/java/com/timebank/notification_service/application/event/NotificationEvent.java
notification-service/src/main/java/com/timebank/notification_service/domain/entity/NotificationType.java
notification-service/src/main/java/com/timebank/notification_service/domain/entity/NotificationEventType.java
NotificationEvent, NotificationType, NotificationEventType 클래스/enum 삭제 (공용 패키지로 이동)
notification-service/src/main/java/com/timebank/notification_service/application/service/NotificationService.java Kafka 이벤트 발행 코드 제거, Slack 메시지 송신 메서드 sendMessage 추가, SlackClient 의존성 주입
notification-service/src/main/java/com/timebank/notification_service/domain/entity/Notification.java NotificationType, NotificationEventType 필드 enum으로 변경, 생성자 2개 추가
notification-service/src/main/java/com/timebank/notification_service/infrastructure/kafka/NotificationConsumer.java NotificationService 의존성 추가, Kafka 이벤트 수신 시 Slack 메시지 송신 로직 추가
notification-service/src/main/java/com/timebank/notification_service/infrastructure/slack/SlackBotClient.java
notification-service/src/main/java/com/timebank/notification_service/infrastructure/slack/SlackWebHookClient.java
Slack API, Webhook 연동용 FeignClient 인터페이스 신규 생성
notification-service/src/main/java/com/timebank/notification_service/infrastructure/slack/SlackFeignClientImpl.java SlackClient 구현체 신규 생성, Resilience4j 기반 재시도 및 fallback 적용, Slack 메시지 송신 및 유저 ID 조회 구현

Sequence Diagram(s)

sequenceDiagram
    participant Kafka as Kafka
    participant Consumer as NotificationConsumer
    participant Service as NotificationService
    participant SlackClient as SlackClient
    participant SlackAPI as Slack

    Kafka->>Consumer: NotificationEvent 수신
    Consumer->>Service: sendMessage(SlackWebHookMessage, email)
    Service->>SlackClient: sendMessage(SlackWebHookMessage)
    SlackClient->>SlackAPI: Webhook 메시지 전송
    Service->>SlackClient: getUserIdByEmail(email)
    SlackClient->>SlackAPI: /users.lookupByEmail 호출
    SlackAPI-->>SlackClient: userId 반환
    Service->>SlackClient: sendMessage(SlackBotMessage)
    SlackClient->>SlackAPI: Bot 메시지 전송
Loading

Assessment against linked issues

Objective (issue) Addressed Explanation
Slack message 송신 기능 구현 (#160)

Poem

슬랙에 알림이 간다, 깡총깡총 토끼처럼!
메시지 보내고, 유저 찾아
재시도도 걱정 마라
Kafka 알림도 슬랙으로 뿅!
오늘도 코드밭에 당근 한 아름
🥕✨

— 슬랙 알림을 전하는 토끼 드림

Tip

⚡️ Faster reviews with caching
  • CodeRabbit now supports caching for code and dependencies, helping speed up reviews. This means quicker feedback, reduced wait times, and a smoother review experience overall. Cached data is encrypted and stored securely. This feature will be automatically enabled for all accounts on May 16th. To opt out, configure Review - Disable Cache at either the organization or repository level. If you prefer to disable all data retention across your organization, simply turn off the Data Retention setting under your Organization Settings.

Enjoy the performance boost—your workflow just got faster.

✨ Finishing Touches
  • 📝 Generate Docstrings

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
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@github-actions github-actions bot requested a review from 20152842 May 8, 2025 10:38
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: 19

🔭 Outside diff range comments (3)
notification-service/src/main/java/com/timebank/notification_service/domain/entity/Notification.java (2)

58-65: 🧹 Nitpick (assertive)

생성자 중복 코드 확인

생성자 내에서 isRead 필드를 false로 명시적 초기화하는데, 이미 클래스 레벨에서 초기화되어 있습니다(56줄).

클래스 레벨 초기화가 있으므로 생성자에서 중복 초기화는 제거할 수 있습니다:

public Notification(Long recipientId, Long senderId, NotificationType notificationType, String message) {
	this.recipientId = recipientId;
	this.senderId = senderId;
	this.notificationType = notificationType;
	this.message = message;
-	this.isRead = false;
}

67-76: 🧹 Nitpick (assertive)

생성자 코드 중복 개선

두 생성자간 중복 코드가 존재합니다. 이는 유지보수성을 저하시킬 수 있습니다.

this() 키워드를 사용해 생성자 체이닝을 적용하세요:

public Notification(Long recipientId, Long senderId, NotificationType notificationType,
	NotificationEventType eventType, String message) {
-	this.recipientId = recipientId;
-	this.senderId = senderId;
-	this.notificationType = notificationType;
+	this(recipientId, senderId, notificationType, message);
	this.eventType = eventType;
-	this.message = message;
-	this.isRead = false;
}
notification-service/src/main/java/com/timebank/notification_service/infrastructure/kafka/NotificationConsumer.java (1)

57-67: 🛠️ Refactor suggestion

예외 처리 개선 필요

saveNotification 메서드에 예외 처리 로직이 없습니다. DB 저장 실패 시 슬랙 메시지 전송에도 영향을 줄 수 있습니다.

try-catch 블록을 추가하여 예외 처리 로직을 강화하세요:

private void saveNotification(NotificationEvent event) {
+	try {
		Notification notification = Notification.builder()
			.recipientId(event.getRecipientId())
			.notificationType(event.getType())
			.eventType(event.getEventType())
			.message(event.getMessage())
			.isRead(false)
			.build();
		notificationRepository.save(notification);
		log.info("Notification saved: {}", notification);
+	} catch (Exception e) {
+		log.error("알림 저장 실패: {}", e.getMessage(), e);
+		// 필요에 따라 예외를 다시 던지거나 다른 대응 방법 적용
+	}
}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ed57efb and 68dde6d.

📒 Files selected for processing (16)
  • common/src/main/java/com/timebank/common/infrastructure/external/notification/dto/NotificationEvent.java (1 hunks)
  • notification-service/build.gradle (1 hunks)
  • notification-service/src/main/java/com/timebank/notification_service/application/client/SlackClient.java (1 hunks)
  • notification-service/src/main/java/com/timebank/notification_service/application/dto/NotificationDto.java (1 hunks)
  • notification-service/src/main/java/com/timebank/notification_service/application/dto/SlackBotMessage.java (1 hunks)
  • notification-service/src/main/java/com/timebank/notification_service/application/dto/SlackUserResponse.java (1 hunks)
  • notification-service/src/main/java/com/timebank/notification_service/application/dto/SlackWebHookMessage.java (1 hunks)
  • notification-service/src/main/java/com/timebank/notification_service/application/event/NotificationEvent.java (0 hunks)
  • notification-service/src/main/java/com/timebank/notification_service/application/service/NotificationService.java (4 hunks)
  • notification-service/src/main/java/com/timebank/notification_service/domain/entity/Notification.java (1 hunks)
  • notification-service/src/main/java/com/timebank/notification_service/domain/entity/NotificationEventType.java (0 hunks)
  • notification-service/src/main/java/com/timebank/notification_service/domain/entity/NotificationType.java (0 hunks)
  • notification-service/src/main/java/com/timebank/notification_service/infrastructure/kafka/NotificationConsumer.java (3 hunks)
  • notification-service/src/main/java/com/timebank/notification_service/infrastructure/slack/SlackBotClient.java (1 hunks)
  • notification-service/src/main/java/com/timebank/notification_service/infrastructure/slack/SlackFeignClientImpl.java (1 hunks)
  • notification-service/src/main/java/com/timebank/notification_service/infrastructure/slack/SlackWebHookClient.java (1 hunks)
💤 Files with no reviewable changes (3)
  • notification-service/src/main/java/com/timebank/notification_service/application/event/NotificationEvent.java
  • notification-service/src/main/java/com/timebank/notification_service/domain/entity/NotificationType.java
  • notification-service/src/main/java/com/timebank/notification_service/domain/entity/NotificationEventType.java
🔇 Additional comments (9)
notification-service/build.gradle (1)

41-41: Feign 클라이언트 의존성 추가
Spring Cloud OpenFeign 의존성이 BOM을 통해 잘 관리되고 있습니다. Slack 통신을 위한 클라이언트 구현에 필요한 의존성 추가가 적절합니다.

notification-service/src/main/java/com/timebank/notification_service/application/dto/NotificationDto.java (1)

3-3: 외부 공통 DTO 사용 일관성 확인
com.timebank.common.infrastructure.external.notification.dto.NotificationType로 변경되어 모듈 간 일관성이 높아졌습니다. 관련 엔티티(Notification)의 타입 필드도 동일한 enum을 참조하는지 확인했습니다.

common/src/main/java/com/timebank/common/infrastructure/external/notification/dto/NotificationEvent.java (1)

26-26: Slack 사용자 이메일 필드 추가
slackUserEmail 필드가 새롭게 추가되어 Slack 알림 전송 시 사용자 식별에 필요한 이메일 데이터를 보관합니다. Builder 패턴을 통해 설정 가능하며, 소비자에서 해당 값이 정상으로 세팅되는지 확인이 필요합니다.

notification-service/src/main/java/com/timebank/notification_service/infrastructure/slack/SlackWebHookClient.java (1)

9-14: Feign 클라이언트 구현이 잘 되었습니다.

Slack webhook을 사용해 메시지를 보내는 Feign 클라이언트가 잘 정의되어 있습니다. 구성 속성을 사용하여 URL과 경로를 설정한 것은 좋은 접근 방식입니다.

notification-service/src/main/java/com/timebank/notification_service/application/service/NotificationService.java (2)

14-19: 필요한 Slack 관련 클래스 가져오기가 잘 되었습니다.

Slack 알림 기능을 위한 필수 클래스들의 import가 적절히 추가되었습니다.


34-34: 의존성 주입 확인

SlackClient 의존성이 올바르게 주입되었습니다. 이는 서비스 계층에서 Slack 메시지 전송 기능을 사용하기 위한 필수 구성 요소입니다.

notification-service/src/main/java/com/timebank/notification_service/application/dto/SlackWebHookMessage.java (1)

7-18: DTO 구현이 적절합니다.

Slack webhook 메시지를 위한 DTO 클래스가 잘 구현되어 있으며, 롬복 어노테이션을 활용하여 보일러플레이트 코드를 줄였습니다. 팩토리 메소드 from을 제공하여 객체 생성을 용이하게 한 것도 좋은 접근입니다.

notification-service/src/main/java/com/timebank/notification_service/application/dto/SlackBotMessage.java (1)

7-20: Slack 봇 메시지 DTO 구현이 잘 되었습니다.

SlackBotMessage DTO 클래스가 잘 설계되어 있으며, 필요한 필드와 생성 메소드가 적절히 구현되어 있습니다. 롬복 어노테이션을 활용하여 코드를 간결하게 유지한 것도 좋은 방법입니다.

notification-service/src/main/java/com/timebank/notification_service/domain/entity/Notification.java (1)

4-5: 공통 패키지 의존성 활용이 잘 되었습니다

기존 내부 열거형 대신 공통 패키지의 NotificationType과 NotificationEventType을 활용하는 방식으로 잘 리팩토링하였습니다.

Comment on lines +46 to +47
implementation 'io.github.resilience4j:resilience4j-spring-boot3'
implementation 'io.github.resilience4j:resilience4j-retry'
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Resilience4j 모듈 버전 명시 필요
resilience4j-spring-boot3resilience4j-retry 의존성에 버전이 지정되어 있지 않습니다. 이는 빌드 시 불확실한 버전이 적용될 수 있어 안정성을 해칩니다. 명시적 버전 또는 BOM 관리를 통해 원하는 버전을 지정해주세요.

Comment on lines +1 to +23
package com.timebank.notification_service.application.dto;

import lombok.Getter;

@Getter
public class SlackUserResponse {
private boolean ok;
private SlackUser user;

@Getter
public static class SlackUser {
private String id;
private String teamId;
private String name;
private String realName;
private SlackProfile profile;

@Getter
public static class SlackProfile {
private String email;
}
}
}
Copy link

Choose a reason for hiding this comment

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

🧹 Nitpick (assertive)

Slack API 응답 매핑 검증
Lombok @Getter만 사용하여 DTO를 정의했으나, Slack API의 필드명이 snake_case(team_id, real_name)를 사용합니다. Jackson 설정에서 PropertyNamingStrategy.SNAKE_CASE를 활성화하지 않았다면, @JsonProperty("team_id"), @JsonProperty("real_name") 등을 추가해 필드 매핑을 보장해야 합니다.

Comment on lines +1 to +13
package com.timebank.notification_service.application.client;

import com.timebank.notification_service.application.dto.SlackBotMessage;
import com.timebank.notification_service.application.dto.SlackWebHookMessage;

public interface SlackClient {
void sendMessage(SlackWebHookMessage message);

void sendMessage(SlackBotMessage request);

String getUserIdByEmail(String email);

}
Copy link

Choose a reason for hiding this comment

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

🧹 Nitpick (assertive)

SlackClient 인터페이스 확인
Slack WebHook 전송과 Bot 메시지 전송을 위한 오버로드된 sendMessage 메서드가 정의되어 있습니다. 타입 별로 메서드명을 구분(sendWebHookMessage, sendBotMessage)하면 가독성이 더 높아질 수 있습니다.

Comment on lines +12 to +13
@PostMapping("${slack.webhook.path}")
void sendMessage(@RequestBody SlackWebHookMessage message);
Copy link

Choose a reason for hiding this comment

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

🧹 Nitpick (assertive)

에러 처리 메커니즘 추가 고려

현재 구현에는 Slack API 호출 실패 시의 에러 처리가 포함되어 있지 않습니다. Resilience4j를 사용한 재시도 메커니즘이나 폴백 전략을 고려해 보세요.

@PostMapping("${slack.webhook.path}")
+@CircuitBreaker(name = "slackWebhook")
+@Retry(name = "slackWebhook")
void sendMessage(@RequestBody SlackWebHookMessage message);
📝 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
@PostMapping("${slack.webhook.path}")
void sendMessage(@RequestBody SlackWebHookMessage message);
@PostMapping("${slack.webhook.path}")
@CircuitBreaker(name = "slackWebhook")
@Retry(name = "slackWebhook")
void sendMessage(@RequestBody SlackWebHookMessage message);

Comment on lines +111 to 114
// // Kafka 이벤트 발행 (DELETED)
// NotificationEvent event = new NotificationEvent(notification, NotificationEventType.DELETED);
// kafkaTemplate.send(NotificationEventType.DELETED.getTopic(), event);
}
Copy link

Choose a reason for hiding this comment

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

🧹 Nitpick (assertive)

주석 처리된 코드 제거 필요

Kafka 이벤트 발행 관련 주석 처리된 코드는 제거하는 것이 좋습니다. 주석 처리된 코드는 유지보수를 어렵게 만들고 코드 이해도를 떨어뜨립니다.

-	// // Kafka 이벤트 발행 (DELETED)
-	// NotificationEvent event = new NotificationEvent(notification, NotificationEventType.DELETED);
-	// kafkaTemplate.send(NotificationEventType.DELETED.getTopic(), event);
📝 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
// // Kafka 이벤트 발행 (DELETED)
// NotificationEvent event = new NotificationEvent(notification, NotificationEventType.DELETED);
// kafkaTemplate.send(NotificationEventType.DELETED.getTopic(), event);
}
}

Comment on lines +37 to +43

@Override
@Retry(name = "slackMessageRetry", fallbackMethod = "lookupByEmailFallback")
public String getUserIdByEmail(String email) {
SlackUserResponse response = slackBotClient.lookupByEmail(bearerPrefix + slackBotToken, email);
return response.getUser().getId();
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Null 처리 로직 개선 필요

getUserIdByEmail 메서드에서 response의 null 확인이 없어 NPE가 발생할 수 있습니다.

response 또는 response.getUser()가 null일 경우 안전하게 처리하는 로직을 추가하세요:

@Override
@Retry(name = "slackMessageRetry", fallbackMethod = "lookupByEmailFallback")
public String getUserIdByEmail(String email) {
	SlackUserResponse response = slackBotClient.lookupByEmail(bearerPrefix + slackBotToken, email);
-	return response.getUser().getId();
+	if (response == null || response.getUser() == null) {
+		log.warn("사용자 정보를 찾을 수 없습니다: {}", email);
+		return null;
+	}
+	return response.getUser().getId();
}
📝 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
@Override
@Retry(name = "slackMessageRetry", fallbackMethod = "lookupByEmailFallback")
public String getUserIdByEmail(String email) {
SlackUserResponse response = slackBotClient.lookupByEmail(bearerPrefix + slackBotToken, email);
return response.getUser().getId();
}
@Override
@Retry(name = "slackMessageRetry", fallbackMethod = "lookupByEmailFallback")
public String getUserIdByEmail(String email) {
SlackUserResponse response = slackBotClient.lookupByEmail(bearerPrefix + slackBotToken, email);
if (response == null || response.getUser() == null) {
log.warn("사용자 정보를 찾을 수 없습니다: {}", email);
return null;
}
return response.getUser().getId();
}

Comment on lines +23 to +24
@Value("${slack.bot.token}")
private String slackBotToken;
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

토큰 주입 방식 개선 필요

Slack 토큰이 @value 어노테이션을 통해 직접 주입되고 있습니다. 이는 테스트와 보안 측면에서 문제가 될 수 있습니다.

별도의 configuration 클래스를 통해 관리하거나 Vault 같은 보안 시스템 활용을 고려하세요:

-@Value("${slack.bot.token}")
-private String slackBotToken;
+private final String slackBotToken;

+public SlackFeignClientImpl(SlackWebHookClient slackWebHookClient, 
+                          SlackBotClient slackBotClient,
+                          @Value("${slack.bot.token}") String slackBotToken) {
+    this.slackWebHookClient = slackWebHookClient;
+    this.slackBotClient = slackBotClient;
+    this.slackBotToken = slackBotToken;
+}
📝 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
@Value("${slack.bot.token}")
private String slackBotToken;
private final String slackBotToken;
public SlackFeignClientImpl(SlackWebHookClient slackWebHookClient,
SlackBotClient slackBotClient,
@Value("${slack.bot.token}") String slackBotToken) {
this.slackWebHookClient = slackWebHookClient;
this.slackBotClient = slackBotClient;
this.slackBotToken = slackBotToken;
}

Comment on lines +28 to 31

notificationService.sendMessage(
SlackWebHookMessage.from(event.getMessage()), event.getSlackUserEmail());
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

코드 일관성 개선 필요

consumeCreated 메서드에서는 저장 후 메시지를 보내는 반면, consumeUpdated에서는 메시지를 보낸 후 저장합니다.

일관된 동작을 위해 패턴을 통일하세요:

public void consumeCreated(NotificationEvent event) {
	log.info("Consumed CREATED event: {}", event);
	saveNotification(event);

	notificationService.sendMessage(
		SlackWebHookMessage.from(event.getMessage()), event.getSlackUserEmail());
}

혹은 모든 메서드가 같은 순서를 따르도록 하세요. 트랜잭션 경계와 실패 시나리오를 고려하여 순서를 결정하는 것이 중요합니다.

Committable suggestion skipped: line range outside the PR's diff.

Comment on lines +29 to +30
notificationService.sendMessage(
SlackWebHookMessage.from(event.getMessage()), event.getSlackUserEmail());
Copy link

Choose a reason for hiding this comment

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

🧹 Nitpick (assertive)

슬랙 메시지 생성 로직 검토 필요

SlackWebHookMessage.from 메서드가 event.getMessage()만 사용하고 있어 정보가 부족할 수 있습니다.

이벤트의 다른 속성(타입, 이벤트 유형 등)을 활용하여 더 풍부한 슬랙 메시지를 생성하는 것이 좋습니다:

-notificationService.sendMessage(
-	SlackWebHookMessage.from(event.getMessage()), event.getSlackUserEmail());
+notificationService.sendMessage(
+	SlackWebHookMessage.builder()
+		.text(String.format("[%s] %s", event.getType(), event.getMessage()))
+		.eventType(event.getEventType().name())
+		.build(), 
+	event.getSlackUserEmail());
📝 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
notificationService.sendMessage(
SlackWebHookMessage.from(event.getMessage()), event.getSlackUserEmail());
notificationService.sendMessage(
SlackWebHookMessage.builder()
.text(String.format("[%s] %s", event.getType(), event.getMessage()))
.eventType(event.getEventType().name())
.build(),
event.getSlackUserEmail());

Comment on lines +49 to +51
notificationService.sendMessage(
SlackWebHookMessage.from(event.getMessage()), event.getSlackUserEmail());

Copy link

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

DELETE 이벤트 처리 개선 필요

DELETED 이벤트도 데이터베이스에 저장된 후 메시지를 보내고 있습니다. 삭제된 항목이 저장되는 로직이 있습니다.

DELETED 이벤트의 경우 로직 검토가 필요합니다. 정말 저장이 필요한지, 아니면 해당 알림을 DB에서 실제로 삭제해야 하는지 확인하세요.


🏁 Script executed:

#!/bin/bash
# saveNotification 메서드가 어떻게 호출되는지 확인
rg -n "saveNotification" --type java notification-service/

Length of output: 673


🏁 Script executed:

#!/bin/bash
sed -n '1,200p' notification-service/src/main/java/com/timebank/notification_service/infrastructure/kafka/NotificationConsumer.java

Length of output: 2510


DELETED 이벤트의 DB 저장 로직 검토 필요

현재 NotificationConsumer.consumeDeleted()에서 삭제 이벤트 수신 시에도 saveNotification(event)를 호출해 알림을 저장하고 있습니다. 삭제된 알림을 별도로 기록(audit)할 의도가 아니라면, 다음을 검토해주세요:

  • NotificationConsumer.java
    • consumeDeleted() (약 49–57행)에서 saveNotification(event) 호출 여부
  • 삭제 이벤트의 처리 방식
    • DB에 그대로 저장이 필요한지
    • 기존 알림을 삭제(notificationRepository.deleteById(...))하거나, 저장 호출을 제거할지

의도에 맞춰 로직을 수정해주세요.

@20152842 20152842 merged commit 8df538a into develop Jun 7, 2025
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feat] notification-service slack알림 추가

2 participants