Skip to content

feat: 예측 이벤트 구독#52

Merged
vivivim merged 3 commits intomainfrom
SPM-480
Nov 9, 2025
Merged

feat: 예측 이벤트 구독#52
vivivim merged 3 commits intomainfrom
SPM-480

Conversation

@vivivim
Copy link
Contributor

@vivivim vivivim commented Nov 9, 2025

📝 Summary

재고 붙여서 전달

🙏 Question & PR point

📬 Reference

Summary by CodeRabbit

  • New Features

    • 부품 예측 이벤트 수신 및 처리 기능 추가
    • 예측 페이로드에 현재 재고 수량을 자동 연계해 예측 데이터 업데이트
    • 예측 이벤트 타입 매핑 지원 추가
  • Chores

    • 잘못된 페이로드 타입에 대한 명확한 오류 상태 추가 및 유효성 보강

재고 붙여서 전달
@coderabbitai
Copy link

coderabbitai bot commented Nov 9, 2025

Warning

Rate limit exceeded

@vivivim has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 10 minutes and 13 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between 6742bd9 and 8fd8e27.

📒 Files selected for processing (2)
  • src/main/java/com/sampoom/backend/api/inventory/service/InventoryService.java (2 hunks)
  • src/main/java/com/sampoom/backend/common/response/ErrorStatus.java (1 hunks)

Walkthrough

Kafka의 part-forecast 토픽으로 들어온 이벤트를 처리하기 위한 DTO와 컨슈머, 재고 서비스 로직을 추가하여 예측 페이로드에 현재 재고를 첨부하고 업데이트된 이벤트를 아웃박스로 보냅니다.

Changes

Cohort / File(s) Summary
DTO: ForecastPayload
src/main/java/com/sampoom/backend/api/inventory/dto/ForecastPayload.java
신규 DTO 추가: warehouseId, partId, demandQuantity, demandMonth, stock 필드 및 Lombok 접근자/빌더/생성자
이벤트 페이로드 매핑
src/main/java/com/sampoom/backend/api/part/event/EventPayloadMapper.java
EventPayloadMapper"PartForecast" -> ForecastPayload.class 매핑 추가
Kafka 컨슈머
src/main/java/com/sampoom/backend/api/inventory/event/ForecastEventConsumer.java
part-forecast 토픽 소비자 추가: JSON 역직렬화, EventPayloadMapper로 타입 결정, InventoryService.attachStocksToForecast 호출, 오류 로깅 및 예외 재발생
비즈니스 로직: InventoryService
src/main/java/com/sampoom/backend/api/inventory/service/InventoryService.java
public <T> void attachStocksToForecast(Event<T> event) 추가: 페이로드 타입 검증, 재고 조회로 stock 설정, 이벤트를 "part-forecast-events" 아웃박스로 직렬화·저장
에러 상태
src/main/java/com/sampoom/backend/common/response/ErrorStatus.java
INVALID_PAYLOAD_TYPE(HttpStatus.BAD_REQUEST, "부정확한 이벤트 페이로드입니다.") 상수 추가

Sequence Diagram(s)

sequenceDiagram
    participant Kafka as Kafka Broker
    participant Consumer as ForecastEventConsumer
    participant Mapper as EventPayloadMapper
    participant Service as InventoryService
    participant Repo as InventoryRepository
    participant EventSvc as EventService

    Kafka->>Consumer: 메시지 전달 (consume(message))
    activate Consumer

    Consumer->>Consumer: ObjectMapper.readValue(message)
    Note over Consumer: JSON -> Event (eventType, payload)

    Consumer->>Mapper: getPayloadClass(eventType)
    Mapper-->>Consumer: ForecastPayload.class

    Consumer->>Service: attachStocksToForecast(Event<ForecastPayload>)
    activate Service

    Service->>Service: payload 타입 검증
    Service->>Repo: findByWarehouseIdAndPartId(warehouseId, partId)
    Repo-->>Service: Inventory (quantity)

    Service->>Service: payload.setStock(inventory.getQuantity())
    Service->>EventSvc: serialize & save outbox ("part-forecast-events", payload)
    EventSvc-->>Service: 저장 완료

    Service-->>Consumer: 성공 리턴
    deactivate Service

    Consumer->>Consumer: log.info("success")
    deactivate Consumer
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

  • 중점 검토 항목:
    • ForecastEventConsumer의 예외 처리 및 RuntimeException 재발생이 Kafka 재시도/DLQ 동작에 미치는 영향
    • InventoryService.attachStocksToForecast의 트랜잭션 경계와 EventService(아웃박스) 호출 일관성
    • EventPayloadMapper에 추가된 매핑이 기존 매핑과 충돌하지 않는지(이름 중복 등)
    • ForecastPayload 필드 타입(예: demandMonth의 LocalDateTime)이 직렬화/역직렬화 요구사항과 일치하는지

Possibly related PRs

  • feat: 재고 업데이트 #15: InventoryService에 새로운 메서드(예: updateParts) 추가 — InventoryService 변경과 직접적인 코드 접점 존재
  • Spm 371 #33: 이벤트 아웃박스 페이로드 직렬화 및 EventService/setEventOutBox 도입 — attachStocksToForecast의 아웃박스 로직과 연관
  • feat: 출고 시 내역 저장 #34: InventoryService 및 ErrorStatus 관련 변경 — 에러 상태와 재고 처리 흐름이 겹침

Suggested reviewers

  • Lee-Jong-Jin
  • CHOOSLA
  • yangjiseonn
  • taemin3

Poem

🐰 예측이 도착했네, 토끼가 보러와,
메시지 타닥, JSON 꿈을 풀고,
창고의 수량을 살포시 안겨주며,
아웃박스로 훨훨 새로운 길을 가고,
와르르 웃음으로 배를 두드려요 🥕✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed 제목은 PR의 주요 변경사항을 정확하게 설명합니다. 예측 이벤트 구독 기능이 추가되었으며, 이는 ForecastEventConsumer, ForecastPayload, InventoryService 개선 등 전체 변경사항의 핵심을 잘 반영합니다.

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.

Copy link
Member

@Sangyoon98 Sangyoon98 left a comment

Choose a reason for hiding this comment

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

승인

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/main/java/com/sampoom/backend/api/inventory/service/InventoryService.java (1)

304-332: 읽기 전용 트랜잭션에서 쓰기 작업 수행

메서드가 @Transactional(readOnly = true)로 표시되어 있지만, 329번 라인에서 eventService.setEventOutBox()를 호출하여 데이터베이스에 쓰기 작업을 수행합니다. 읽기 전용 트랜잭션에서는 쓰기 작업이 커밋되지 않거나 런타임 예외가 발생할 수 있습니다.

다음과 같이 수정하세요:

-@Transactional(readOnly = true)
+@Transactional
 public <T> void attachStocksToForecast(Event<T> event) {
🧹 Nitpick comments (5)
src/main/java/com/sampoom/backend/api/inventory/dto/ForecastPayload.java (2)

7-11: @builder@Setter 동시 사용 확인

@Builder@Setter를 함께 사용하면 빌더로 생성한 불변 객체처럼 보이지만 실제로는 가변 객체가 됩니다. InventoryService의 328번 라인에서 payload.setStock()을 호출하므로 의도된 설계로 보이지만, 이는 예측하기 어려운 동작을 유발할 수 있습니다.

더 명확한 설계를 위해 다음을 고려해보세요:

  • stock 필드만 setter를 제공하거나
  • withStock(Integer stock) 같은 명시적인 메서드를 추가하는 방법
-@Setter
 @Builder
 @NoArgsConstructor
 @AllArgsConstructor
 public class ForecastPayload {
     private Long warehouseId;
     private Long partId;
     private Integer demandQuantity;
     private LocalDateTime demandMonth;
+    @Setter
     private Integer stock;
 }

16-16: 필드명과 타입 불일치 고려

demandMonth 필드명은 월 단위 데이터를 암시하지만 타입은 LocalDateTime입니다. 일/시간 정보도 포함되는 경우 demandDate 또는 demandDateTime으로 명명하는 것이 더 명확할 수 있습니다.

src/main/java/com/sampoom/backend/api/inventory/event/ForecastEventConsumer.java (2)

27-33: JSON 이중 파싱 최적화 고려

메시지를 두 번 파싱합니다(27-28번 라인에서 eventType 추출, 30-33번 라인에서 전체 역직렬화). 이는 성능에 영향을 줄 수 있습니다.

다음과 같이 개선할 수 있습니다:

 try {
     JsonNode root = objectMapper.readTree(message);
     String eventType = root.get("eventType").asText();
     Class<?> payloadClass = eventPayloadMapper.getPayloadClass(eventType);
-    Event<?> event = objectMapper.readValue(
-            message,
-            objectMapper.getTypeFactory().constructParametricType(Event.class, payloadClass)
-    );
+    Event<?> event = objectMapper.convertValue(
+            root,
+            objectMapper.getTypeFactory().constructParametricType(Event.class, payloadClass)
+    );

35-38: 이벤트 타입 문자열 상수화 고려

"PartForecast" 문자열이 하드코딩되어 있습니다. 이벤트 타입을 상수로 정의하면 유지보수가 쉬워집니다.

private static final String PART_FORECAST_EVENT = "PartForecast";

// ...
if (PART_FORECAST_EVENT.equals(eventType)) {
src/main/java/com/sampoom/backend/api/inventory/service/InventoryService.java (1)

330-331: 코드 스타일 일관성

else 블록에 중괄호가 없습니다. 가독성과 일관성을 위해 중괄호를 추가하는 것이 좋습니다.

-    } else
-        throw new BadRequestException(ErrorStatus.INVALID_PAYLOAD_TYPE.getMessage());
+    } else {
+        throw new BadRequestException(ErrorStatus.INVALID_PAYLOAD_TYPE.getMessage());
+    }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 069ebad and bd6e15d.

📒 Files selected for processing (5)
  • src/main/java/com/sampoom/backend/api/inventory/dto/ForecastPayload.java (1 hunks)
  • src/main/java/com/sampoom/backend/api/inventory/event/ForecastEventConsumer.java (1 hunks)
  • src/main/java/com/sampoom/backend/api/inventory/service/InventoryService.java (2 hunks)
  • src/main/java/com/sampoom/backend/api/part/event/EventPayloadMapper.java (2 hunks)
  • src/main/java/com/sampoom/backend/common/response/ErrorStatus.java (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
src/main/java/com/sampoom/backend/api/inventory/event/ForecastEventConsumer.java (1)
src/main/java/com/sampoom/backend/api/inventory/service/InventoryService.java (1)
  • Service (40-333)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Analyze (java-kotlin)
🔇 Additional comments (2)
src/main/java/com/sampoom/backend/common/response/ErrorStatus.java (1)

32-32: LGTM!

에러 상수가 다른 에러 정의들과 일관되게 구현되었습니다.

src/main/java/com/sampoom/backend/api/part/event/EventPayloadMapper.java (1)

5-5: LGTM!

PartForecast 이벤트 타입 매핑이 기존 패턴과 일관되게 추가되었습니다.

Also applies to: 31-32

트랜잭셔널 수정
이벤트 저장하고 있는데 웬 리드온리..
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 (1)
src/main/java/com/sampoom/backend/api/inventory/service/InventoryService.java (1)

319-320: 이벤트 파라미터에 대한 방어적 null 검증은 선택사항입니다.

현재 코드는 Kafka 컨슈머의 정상 동작 범위에서 안전합니다. ObjectMapper.readValue()는 역직렬화 실패 시 예외를 발생시키므로 null을 반환하지 않습니다. ForecastEventConsumer에서 event 객체가 정상적으로 생성되면 InventoryService의 attachStocksToForecast() 메서드는 null 이벤트를 받지 않습니다.

다만 추가적인 방어 계층을 원한다면, 다음과 같이 검증을 추가할 수 있습니다:

 @Transactional
 public <T> void attachStocksToForecast(Event<T> event) {
+    if (event == null || event.getPayload() == null) {
+        throw new BadRequestException(ErrorStatus.INVALID_PAYLOAD_TYPE.getMessage());
+    }
     if (event.getPayload() instanceof ForecastPayload payload) {

이는 선택적 개선사항이며, 코드의 견고성을 높이고자 할 때 고려할 수 있습니다.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between bd6e15d and 6742bd9.

📒 Files selected for processing (2)
  • src/main/java/com/sampoom/backend/api/inventory/event/ForecastEventConsumer.java (1 hunks)
  • src/main/java/com/sampoom/backend/api/inventory/service/InventoryService.java (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/main/java/com/sampoom/backend/api/inventory/event/ForecastEventConsumer.java
🔇 Additional comments (4)
src/main/java/com/sampoom/backend/api/inventory/service/InventoryService.java (4)

14-14: LGTM!

새로운 메서드의 타입 파라미터를 위한 필요한 import입니다.


321-321: 패턴 매칭 사용이 적절합니다.

Modern Java의 instanceof 패턴 매칭을 사용하여 타입 체크와 캐스팅을 간결하게 처리했습니다.


328-329: 이벤트 enrichment 패턴이 올바르게 구현되었습니다.

payload에 재고 정보를 설정한 후 event를 직렬화하여 outbox로 전송하는 로직이 적절합니다. 기존 코드(line 259)와 일관된 패턴을 따르고 있습니다.


330-331: 예외 처리가 적절합니다.

payload 타입이 ForecastPayload가 아닌 경우에 대한 예외 처리가 올바르게 구현되었습니다. 에러 상태와 예외 타입이 적절합니다.

@vivivim vivivim merged commit a95c6ac into main Nov 9, 2025
5 checks passed
This was referenced Nov 10, 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