Skip to content

[UPLUS-143] 알림 이벤트 Consumer 로직 수정#6

Merged
starboxxxx merged 2 commits into
developfrom
feat/UPLUS-143
Jan 26, 2026
Merged

[UPLUS-143] 알림 이벤트 Consumer 로직 수정#6
starboxxxx merged 2 commits into
developfrom
feat/UPLUS-143

Conversation

@starboxxxx
Copy link
Copy Markdown
Contributor

🍀 이슈 번호

  • UPLUS-143

✅ 작업 사항

알림 이벤트 Consumer 로직 수정


📋 체크리스트

  • 코드가 정상적으로 빌드됩니다.
  • 관련 테스트 코드를 작성했습니다.
  • 기존 테스트가 모두 통과합니다.
  • 코드 스타일(Spotless, Checkstyle)을 준수합니다.

⌨ 기타

@starboxxxx starboxxxx self-assigned this Jan 26, 2026
@starboxxxx starboxxxx added the feat 새로운 기능 label Jan 26, 2026
@github-actions github-actions Bot added the size/M PR size: M label Jan 26, 2026
@gemini-code-assist
Copy link
Copy Markdown

Summary of Changes

Hello @starboxxxx, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

이 PR은 알림 이벤트 처리 로직을 개선하고 확장하기 위한 변경 사항을 포함합니다. 기존의 특정 용도 이벤트 클래스를 범용적인 NotificationEvent로 리팩토링하여 유연성을 확보했으며, 새로운 InvoiceConsumer를 도입하여 invoice 관련 알림을 독립적으로 처리할 수 있도록 했습니다. 또한, MessageSendService에 이벤트 필터링 및 로깅 기능을 추가하여 안정성과 가시성을 높였습니다.

Highlights

  • 이벤트 클래스 리팩토링: 기존의 UsageNotificationEventNotificationEvent로 이름을 변경하여 이벤트 클래스의 범용성을 높였습니다. 이 변경은 관련 컨트롤러, 컨슈머 및 서비스 전반에 걸쳐 적용되었습니다.
  • 새로운 Kafka Consumer 추가: invoice 토픽의 알림 이벤트를 처리하기 위한 InvoiceConsumer가 새로 추가되었습니다. 이 컨슈머는 배치 처리, 중복 제거, 메시지 전송 및 메트릭 기록 기능을 포함합니다.
  • 기존 NotificationConsumer 업데이트: NotificationConsumer의 Kafka 리스너 토픽이 usage-noti에서 usage로 변경되었고, autoStartup 설정이 false로 조정되었습니다. 또한, NotificationEvent를 사용하도록 업데이트되었습니다.
  • 메시지 전송 서비스 로직 개선: MessageSendService에 특정 subId (1001 또는 1002)에 대한 이벤트만 처리하도록 필터링 로직이 추가되었습니다. 또한, 암호화된 필드 키 이름이 phone_number에서 phoneNumber로 변경되었고, 복호화 과정에 로깅이 추가되었습니다.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

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

Code Review

이 PR은 UsageNotificationEventNotificationEvent로 리팩토링하고, 새로운 InvoiceConsumer를 추가하여 알림 이벤트 처리 로직을 수정합니다. 전반적으로 코드의 일반성을 높이는 좋은 방향이지만, 몇 가지 개선점이 보입니다. 특히 InvoiceConsumerNotificationConsumer 간에 상당한 코드 중복이 발생했으며, 이는 유지보수성을 저해할 수 있습니다. 또한, 서비스 로직에 하드코딩된 값과 디버깅 목적으로 보이는 과도한 로깅이 포함되어 있어 주의가 필요합니다. 아래에 자세한 리뷰 의견을 남겼습니다.

Comment on lines +24 to +98
public class InvoiceConsumer {

private final NotificationSendDedupService dedupService;
private final ObjectMapper objectMapper;
private final MessageSendService messageSendService;
private final Counter kafkaBatchProcessedCounter;
private final Timer kafkaBatchProcessingTimer;

@KafkaListener(
topics = "invoice",
containerFactory = "kafkaListenerContainerFactory",
autoStartup = "true")
public void consume(List<ConsumerRecord<String, String>> records, Acknowledgment ack) {

final String threadName = Thread.currentThread().getName();
final int batchSize = records.size();

log.info("[BATCH START] thread={}, records={}", threadName, batchSize);

long batchStart = System.currentTimeMillis();

List<NotificationEvent> events = new ArrayList<>(batchSize);
List<String> eventIds = new ArrayList<>(batchSize);

for (ConsumerRecord<String, String> record : records) {
try {
NotificationEvent event =
objectMapper.readValue(record.value(), NotificationEvent.class);
events.add(event);
eventIds.add(event.eventId().toString());
} catch (Exception e) {
log.warn("[DESERIALIZE FAIL] offset={}", record.offset(), e);
}
}

List<Boolean> dedupResults = dedupService.tryAcquireBatch(eventIds);

int processed = 0;
int skipped = 0;

for (int i = 0; i < events.size(); i++) {
if (!dedupResults.get(i)) {
skipped++;
continue;
}

try {
NotificationEvent event = events.get(i);
messageSendService.processEvent(event);
processed++;
} catch (Exception e) {
log.error("[PROCESS FAIL] eventId={}", eventIds.get(i), e);
}
}

ack.acknowledge();

long elapsedMs = System.currentTimeMillis() - batchStart;
double tps = elapsedMs > 0 ? processed / (elapsedMs / 1000.0) : 0;

// Record metrics
kafkaBatchProcessedCounter.increment(processed);
kafkaBatchProcessingTimer.record(elapsedMs, TimeUnit.MILLISECONDS);

log.info(
"[BATCH DONE] thread={}, batchSize={}, processed={}, skipped={}, elapsed={}ms,"
+ " tps={}",
threadName,
batchSize,
processed,
skipped,
elapsedMs,
String.format("%.0f", tps));
}
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

high

새로 추가된 InvoiceConsumer의 로직이 NotificationConsumer의 로직과 거의 동일합니다. 유일한 차이점은 @KafkaListenertopicsautoStartup 속성뿐입니다. 이렇게 코드가 중복되면 향후 로직 변경 시 두 군데를 모두 수정해야 하므로 유지보수가 어려워집니다.

공통 로직을 처리하는 추상 클래스를 만들고, InvoiceConsumerNotificationConsumer가 이를 상속받아 @KafkaListener 어노테이션만 각자 정의하도록 리팩토링하는 것을 강력히 권장합니다. 이렇게 하면 코드 중복을 제거하고 유지보수성을 높일 수 있습니다.

Comment on lines +64 to +66
if (event.subscriptionInfo().subId() != 1001 && event.subscriptionInfo().subId() != 1002) {
return;
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

high

subId가 1001 또는 1002인 경우에만 이벤트를 처리하도록 하드코딩되어 있습니다. 이러한 '매직 넘버'는 코드의 의도를 파악하기 어렵게 만들고, 향후 요구사항 변경 시 유연하게 대처하기 어렵게 합니다.

만약 특정 구독자만 처리하기 위한 로직이라면, 이 ID 목록을 외부 설정(예: application.yml)으로 분리하고, 코드에서는 설정 값을 읽어 사용하도록 변경하는 것이 좋습니다. 또한, 상수를 사용하여 1001, 1002와 같은 값에 의미를 부여하는 것을 고려해 보세요. 만약 테스트를 위한 임시 코드였다면, 병합 전에 반드시 제거해야 합니다.


public List<Boolean> tryAcquireBatch(List<String> eventIds) {
if (eventIds == null || eventIds.isEmpty()) {
log.info("evenIds is null or empty");
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

로그 메시지에 오타가 있습니다. 'evenIds'를 'eventIds'로 수정해야 합니다.

Suggested change
log.info("evenIds is null or empty");
log.info("eventIds is null or empty");

Comment on lines +102 to +103
log.info("Encrypted key: {}", key);
log.info("Encrypted value: {}", value);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

암호화된 키와 값을 INFO 레벨로 로깅하고 있습니다. 비록 값이 암호화되어 있더라도, 프로덕션 환경에서 민감할 수 있는 정보를 INFO 레벨로 기록하는 것은 과도한 로그를 유발하고 잠재적인 보안 위험을 초래할 수 있습니다.

이 로그는 디버깅 목적으로 추가된 것으로 보입니다. 디버깅이 필요한 경우에만 볼 수 있도록 로그 레벨을 DEBUGTRACE로 낮추거나, 더 이상 필요하지 않다면 제거하는 것이 좋습니다.

Suggested change
log.info("Encrypted key: {}", key);
log.info("Encrypted value: {}", value);
log.debug("Encrypted key: {}", key);
log.debug("Encrypted value: {}", value);

@github-actions
Copy link
Copy Markdown

SonarQube Quality Summary (Community)

Quality Gate FAILED

Branch: feat/UPLUS-143
Compared to: default branch

Issues

  • 🐞 Bugs: 0
  • 🔐 Vulnerabilities: 2
  • 📎 Code Smells: 52

Measures

  • Coverage: 0%
  • Duplication: 0%

🔗 Dashboard: https://sonarqube.swthewhite.store/dashboard?id=api-message&branch=feat/UPLUS-143

Generated automatically by GitHub Actions.

@starboxxxx starboxxxx merged commit f3f686a into develop Jan 26, 2026
8 of 10 checks passed
@starboxxxx starboxxxx deleted the feat/UPLUS-143 branch January 26, 2026 13:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feat 새로운 기능 size/M PR size: M

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant