Conversation
WalkthroughBOM, Material, Part 도메인에서 표준 수량/원가 필드를 추가하고 여러 수량 타입을 Integer/Long에서 Double로 변경했으며, 수량 계산에서 소수 보존과 반올림 로직(Math.round 등)을 도입해 관련 서비스·엔티티·DTO와 업데이트 메서드를 확장했습니다. Changes
Sequence Diagram(s)sequenceDiagram
participant Event as Event Source
participant Service as ProjectionService
participant Entity as Projection Entity
participant Repo as Repository
Note over Event,Service: 도메인 이벤트 수신 (BOM/Material/Part)
Event->>Service: Emit Created/Updated/Deleted(payload with new fields)
Service->>Entity: builder()/updateFromEvent(..., standardQuantity, standardTotalCost, totalCost, ...)
Entity->>Repo: save()
Note right of Repo: 저장 완료
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes
Possibly related PRs
Suggested labels
Suggested reviewers
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 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.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
src/main/java/com/sampoom/factory/api/material/entity/MaterialProjection.java (1)
75-96:handleMaterialUpdated와handleMaterialCreated에서 null 안전성 처리가 필요합니다.
standardQuantity와standardTotalCost는 이벤트 페이로드에서 직접 전달되며, null 체크 없이 builder에 전달됩니다. 구버전 이벤트나 불완전한 페이로드에서 이 필드들이 null이면 entity 컬럼 제약(@Column(nullable = false))을 위반할 수 있습니다.권장사항:
handleMaterialDeleted에서 사용한 패턴처럼 기존 값을 보존하거나, 명시적 null 체크와 기본값을 추가하세요.// handleMaterialUpdated에서 StandardQuantity standardQuantity = payload.getStandardQuantity() != null ? payload.getStandardQuantity() : currentMaterial.getStandardQuantity(); StandardTotalCost standardTotalCost = payload.getStandardTotalCost() != null ? payload.getStandardTotalCost() : currentMaterial.getStandardTotalCost();또는
handleMaterialDeleted처럼 기존 값을 그대로 사용하는 것이 더 간단합니다.src/main/java/com/sampoom/factory/api/material/service/MaterialProjectionService.java (1)
57-76: 이벤트 페이로드의 null 안전성을 확인하세요.검증 결과, 두 개의 새로 추가된 필드가 nullable 형식인 상태로 NOT NULL 제약 조건이 있는 엔티티에 직접 할당되고 있습니다:
standardQuantity(Integer, nullable) → MaterialProjection의@Column(nullable = false)standardTotalCost(Long, nullable) → MaterialProjection의@Column(nullable = false)MaterialEventDto의 Payload 클래스에는 이 필드들에 대한 유효성 검증 애너테이션이 없으며, MaterialProjectionService의 handleMaterialCreated 메서드에도 null 체크가 없습니다. 구버전 이벤트나 불완전한 페이로드에서 이 값들이 null인 경우 데이터베이스 제약 조건 위반이 발생할 수 있습니다.
권장사항:
- MaterialEventDto.Payload의 필드에
@NotNull또는@NonNull검증 추가- 또는 handleMaterialCreated에서 null 체크 및 기본값 처리 추가
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (12)
src/main/java/com/sampoom/factory/api/bom/dto/BomEventDto.java(2 hunks)src/main/java/com/sampoom/factory/api/bom/entity/BomMaterialProjection.java(1 hunks)src/main/java/com/sampoom/factory/api/bom/entity/BomProjection.java(2 hunks)src/main/java/com/sampoom/factory/api/bom/service/BomProjectionService.java(2 hunks)src/main/java/com/sampoom/factory/api/material/dto/MaterialEventDto.java(1 hunks)src/main/java/com/sampoom/factory/api/material/entity/MaterialProjection.java(3 hunks)src/main/java/com/sampoom/factory/api/material/service/MaterialProjectionService.java(3 hunks)src/main/java/com/sampoom/factory/api/part/dto/PartEventDto.java(1 hunks)src/main/java/com/sampoom/factory/api/part/entity/PartProjection.java(4 hunks)src/main/java/com/sampoom/factory/api/part/service/PartOrderSchedulerService.java(1 hunks)src/main/java/com/sampoom/factory/api/part/service/PartOrderService.java(5 hunks)src/main/java/com/sampoom/factory/api/part/service/PartProjectionService.java(3 hunks)
⏰ 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 (12)
src/main/java/com/sampoom/factory/api/material/dto/MaterialEventDto.java (1)
28-33: 새 필드 추가가 적절하게 구현되었습니다.두 개의 새 필드(
standardQuantity,standardTotalCost)가 이벤트 페이로드에 추가되었습니다. 타입 선택(수량은 Integer, 비용은 Long)이 적절합니다.다만, AI 요약에서는 "수량 타입을 Integer에서 Double로 변경"한다고 언급되어 있으나, 실제 코드에서는
standardQuantity가 여전히Integer타입으로 선언되어 있습니다. 소수점 계산이 필요한 경우 이 부분을 재검토해야 할 수 있습니다.src/main/java/com/sampoom/factory/api/material/service/MaterialProjectionService.java (3)
73-74: 프로젝션 타임스탬프 필드 초기화가 올바르게 추가되었습니다.
updatedAt과sourceUpdatedAt필드 초기화가 MaterialCreated 핸들러에 추가되었습니다. 이는 프로젝션의 시간 추적을 위한 중요한 개선사항입니다.
127-141: MaterialUpdated 핸들러가 새 필드를 올바르게 전파합니다.
standardQuantity(line 132)와standardTotalCost(line 135)가 업데이트 경로를 통해 적절히 전파되고 있습니다.다만 MaterialCreated와 동일하게 페이로드 값이 null일 가능성에 대한 검증이 필요합니다.
159-173: MaterialDeleted 핸들러 구현이 안전합니다.삭제 시
currentMaterial의 기존 필드 값들을 사용하여 업데이트하므로(lines 164, 167), null 안전성이 보장됩니다. 이는 올바른 소프트 삭제 패턴입니다.src/main/java/com/sampoom/factory/api/material/entity/MaterialProjection.java (1)
41-42: 마이그레이션 전략 존재 여부 수동 확인 필요저장소 내에서 데이터베이스 마이그레이션 인프라를 찾을 수 없습니다. 다음을 수동으로 확인하세요:
- SQL 마이그레이션 스크립트 존재 여부 (Flyway/Liquibase 등)
application.properties또는application.yml에서spring.jpa.hibernate.ddl-auto또는 마이그레이션 도구 설정 여부- 새로 추가된
standardQuantity(41-42줄)와standardTotalCost(50-51줄) 필드를 처리하는 마이그레이션 로직 존재 여부기존 데이터가 있는 테이블에
nullable = false인 컬럼을 추가할 경우, 마이그레이션 실패를 방지하려면 반드시 기본값 설정 또는 데이터 백필 로직이 필요합니다.src/main/java/com/sampoom/factory/api/bom/dto/BomEventDto.java (1)
32-45: 이벤트 스키마 확장 확인했습니다.
BOM 이벤트 페이로드에 totalCost 추가와 수량의 Double 전환이 프로젝션·서비스 층과 정합하게 맞춰져 있어 새 필드를 안정적으로 흘려보낼 수 있겠습니다.src/main/java/com/sampoom/factory/api/bom/entity/BomProjection.java (1)
44-72: 프로젝션 필드 추가가 DTO 변경과 정합합니다.
totalCost를 컬럼으로 유지하면서 updateFromEvent에서 빌더에 실어 보내도록 한 덕분에 이벤트·프로젝션 사이 스키마가 깨지지 않습니다.src/main/java/com/sampoom/factory/api/part/dto/PartEventDto.java (1)
30-37: Part 이벤트 페이로드 확장 잘 반영되었습니다.
standardQuantity와 standardTotalCost를 공개해 프로젝션 서비스가 새 기준값을 즉시 소비할 수 있게 되어 흐름이 자연스럽습니다.src/main/java/com/sampoom/factory/api/part/entity/PartProjection.java (1)
44-105: 새 필드의 널 처리 확인 부탁드립니다.
standardQuantity와standardTotalCost를nullable = false로 두었는데, 이벤트 페이로드는 Lombok 기본값이라 누락되면 null이 그대로 전달됩니다. 기존 이벤트 재생성이나 레거시 발행자가 아직 해당 필드를 채우지 않는 경우ConstraintViolationException이 날 수 있으니, 발행 측에서 항상 값을 보내는지 한 번 더 점검하거나 디폴트/방어 로직을 추가해 주세요.src/main/java/com/sampoom/factory/api/bom/entity/BomMaterialProjection.java (1)
37-45: Double 전환으로 이벤트 데이터를 충분히 담을 수 있게 되었습니다
수량을Double로 전환하고 이벤트 업데이트 경로까지 일관되게 맞춰 주신 덕분에 소수점 데이터를 잃지 않고 반영할 수 있습니다. 👍src/main/java/com/sampoom/factory/api/bom/service/BomProjectionService.java (1)
76-125: totalCost 전파가 전체 이벤트 흐름에 잘 맞춰졌습니다
생성/수정 경로 모두에서totalCost와 이벤트 메타데이터를 일관되게 넘겨 주셔서 추후 감사나 재처리에도 안전하게 사용할 수 있겠습니다.src/main/java/com/sampoom/factory/api/part/service/PartProjectionService.java (1)
62-137: standard 필드 전파가 균일하게 처리되었습니다*
생성·수정·삭제 전 경로에서standardQuantity와standardTotalCost를 잃지 않도록 잘 반영해 두셔서, projection 일관성이 유지됩니다.
src/main/java/com/sampoom/factory/api/part/service/PartOrderSchedulerService.java
Outdated
Show resolved
Hide resolved
src/main/java/com/sampoom/factory/api/part/service/PartOrderService.java
Show resolved
Hide resolved
There was a problem hiding this comment.
Actionable comments posted: 2
♻️ Duplicate comments (2)
src/main/java/com/sampoom/factory/api/part/service/PartOrderService.java (2)
122-124: Math.round 사용으로 부족량이 계속 누락됩니다이전 리뷰에서 동일한 문제를 지적드렸지만 아직 Math.round가 남아 있어 1.2 → 1처럼 필요한 자재가 부족하다고 판단되지 않습니다. 생산·구매 전 과정에서 부정확한 수요가 이용되므로 반드시 올림(ceil)으로 교체해 주세요.
- Long requiredQuantity = Math.round(bomMaterial.getQuantity() * item.getQuantity()); // Double에서 Long으로 변환 + long requiredQuantity = (long) Math.ceil(bomMaterial.getQuantity() * item.getQuantity());위 패턴을 이 파일 내 다른 동일 구간에도 적용해 주세요.
Also applies to: 139-141
291-309: Math.round로 부족량이 0이 되어 구매요청이 빠집니다Math.round로 재고를 정수 반올림하면서
factoryMaterial.getQuantity() < required는 true인데shortageAmount는 0이 되어 실제로 구매요청이 발생하지 않는 케이스가 그대로 남아 있습니다. 예) 필요량 1.6, 재고 1.6 → required=2(ceil 적용 시), 하지만 Math.round(1.6)=2라 shortageAmount=0이 되어 주문이 생략됩니다. 이전 코멘트에서 안내드린 Math.ceil 전환과 함께, 부족량은 실수 차이를 기준으로 올림해 정수화해야 안전합니다.- long required = Math.round(bomMaterial.getQuantity() * item.getQuantity()); // Double에서 long으로 변환 + double requiredExact = bomMaterial.getQuantity() * item.getQuantity(); + long required = (long) Math.ceil(requiredExact); ... - long shortageAmount = required - (factoryMaterial != null ? Math.round(factoryMaterial.getQuantity()) : 0); + double currentStock = factoryMaterial != null ? factoryMaterial.getQuantity() : 0d; + long shortageAmount = (long) Math.max(0, Math.ceil(requiredExact - currentStock)); ... - long required = Math.round(bomMaterial.getQuantity() * item.getQuantity()); // Double에서 long으로 변환 - long currentStock = factoryMaterial != null ? Math.round(factoryMaterial.getQuantity()) : 0; // Double을 long으로 변환 + double requiredExact = bomMaterial.getQuantity() * item.getQuantity(); + long required = (long) Math.ceil(requiredExact); + double currentStock = factoryMaterial != null ? factoryMaterial.getQuantity() : 0d; ... - long shortageAmount = required - currentStock; + long shortageAmount = (long) Math.max(0, Math.ceil(requiredExact - currentStock));이렇게 해야 부족량이 정밀하게 계산되고 표준수량 배수 로직도 정상적으로 동작합니다.
Also applies to: 813-879
🧹 Nitpick comments (1)
src/main/java/com/sampoom/factory/api/material/entity/FactoryMaterial.java (1)
36-48: 부동소수점 비교 시 엡실론 값 고려 권장
newQty < 0직접 비교는 대부분의 경우 정상 동작하지만, 부동소수점 연산 오차로 인해 극히 작은 음수 값(예: -1e-15)이 발생할 수 있습니다. 더 안전한 비교를 위해 엡실론 값을 사용하는 것을 고려하세요.다음과 같이 개선할 수 있습니다:
+ private static final double EPSILON = 1e-10; + public void decreaseQuantity(Double amount) { if (amount == null || amount <= 0) { throw new BadRequestException(ErrorStatus.INVALID_QUANTITY); } if (this.quantity == null) { this.quantity = 0.0; } double newQty = this.quantity - amount; - if (newQty < 0) { + if (newQty < -EPSILON) { throw new BadRequestException(ErrorStatus.INSUFFICIENT_MATERIAL_QUANTITY); } + // 엡실론 범위 내의 음수는 0으로 처리 + if (newQty < 0) { + newQty = 0.0; + } this.quantity = newQty; }단,
BigDecimal을 사용하면 이러한 문제를 근본적으로 해결할 수 있습니다.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (7)
src/main/java/com/sampoom/factory/api/factory/service/BranchProjectionService.java(1 hunks)src/main/java/com/sampoom/factory/api/material/dto/MaterialResponseDto.java(2 hunks)src/main/java/com/sampoom/factory/api/material/entity/FactoryMaterial.java(1 hunks)src/main/java/com/sampoom/factory/api/material/service/MaterialProjectionService.java(4 hunks)src/main/java/com/sampoom/factory/api/part/service/PartOrderSchedulerService.java(1 hunks)src/main/java/com/sampoom/factory/api/part/service/PartOrderService.java(10 hunks)src/main/java/com/sampoom/factory/api/purchase/service/PurchaseEventService.java(1 hunks)
⏰ 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 (5)
src/main/java/com/sampoom/factory/api/material/dto/MaterialResponseDto.java (1)
20-36: 소수 수량 대응 DTO 정합성 확보quantity 필드와 withQuantity 메서드를 함께 Double로 맞춰주셔서 Projection/Service 전반의 소수 재고 흐름이 자연스럽게 이어지네요. 별도 캐스팅 부담 없이 그대로 직렬화할 수 있어 보입니다.
src/main/java/com/sampoom/factory/api/material/service/MaterialProjectionService.java (1)
65-175: standard 지표 및 타임스탬프 연동 반영 확인standardQuantity/standardTotalCost를 생성·갱신·삭제 경로 모두에서 전달하고 updatedAt/sourceUpdatedAt까지 채워 주신 덕분에 이벤트 재생 시 Projection 정보가 완결되겠습니다. 초기 FactoryMaterial도 0.0으로 통일된 점이 전체 Double 전환과 잘 어울립니다.
src/main/java/com/sampoom/factory/api/part/service/PartOrderSchedulerService.java (1)
99-100: 소수 재고 차감 로직 개선Math.round 대신 그대로 double을 넘겨주면서 소수 단위 BOM 사용량이 유실되지 않게 되었네요. 이전에 지적되었던 0으로 떨어지는 문제를 자연스럽게 해소한 것으로 보입니다.
src/main/java/com/sampoom/factory/api/factory/service/BranchProjectionService.java (1)
101-107: Double 초기값 정렬factoryMaterial 초기화 수량을 0.0으로 전환해 다른 모듈과 타입 일관성을 맞춘 선택이네요. 추가 후처리 없이 바로 Double 연산이 들어갈 수 있겠습니다.
src/main/java/com/sampoom/factory/api/purchase/service/PurchaseEventService.java (1)
99-109: 입고 시 재고 누적 Double화입고 처리에서 doubleValue를 사용해 FactoryMaterial 증가 로직과 정합성을 확보해 주셔서 Purchase 이벤트 흐름에서도 동일한 스케일을 유지할 수 있게 되었습니다.
📝 Summary
🙏 Question & PR point
📬 Reference
Summary by CodeRabbit
릴리스 노트
New Features
Bug Fixes