๐ ์ค๋์ ์ด๋ค ํ๋ฃจ์์ง..
์ฐ์ ํ์์ ํผ๋๋ฐฑ๋ถํฐ ์ฝ๋์ ๋ฐ์ํ๋ค.
Kafka๋ก ์ฌ๊ณ ๋ณต์ ์์ฒญ ๋ฉ์์ง๋ฅผ ๋ณด๋ด๋ ๋ถ๋ถ์ด ์ค๋ณต๋์ด์ ๋ฉ์๋๋ก ์ถ์ถํ๋ ๊ฒ์ด ๋์ ๋ณด์ธ๋ค๋ ์๊ฒฌ์ ์๊ธํ๊ณ ๋ฉ์๋๋ฅผ ์ถ์ถํ๋ค.
private void sendInventoryRestoreMessage(CreateDeliveryRequest request) {
String payload = JsonHelper.toJson(request);
OperationWrapperDto wrapper = OperationWrapperDto.from(OperationType.DELIVERY_FAIL_INVENTORY_RESTORE, payload);
String wrappedMessage = JsonHelper.toJson(wrapper);
kafkaTemplate.send(productTopic, request.orderId().toString(), wrappedMessage);
log.info("\uD83D\uDCE4 Kafka ์ฌ๊ณ ๋ณต์ ๋ฉ์์ง ์ ์ก: {}", wrappedMessage);
}
@Transactional
public CreateDeliveryResponse createDelivery(CreateDeliveryRequest request) {
if (!orderRepository.existsById(request.orderId())) {
sendInventoryRestoreMessage(request);
slackNotifier.sendDeliveryCreateFailureAlert(
request.orderId(),
"ORDER_NOT_FOUND",
"ID๊ฐ " + request.orderId() + "์ธ ์ฃผ๋ฌธ์ด ์กด์ฌํ์ง ์์ ๋ฐฐ์ก ์์ฑ์ ์คํจํ์ต๋๋ค.");
throw new NotFoundException(ErrorCode.ORDER_NOT_FOUND);
}
// ํด๋น ์ฃผ๋ฌธ์ ๋ํ ๋ฐฐ์ก์ด ์กด์ฌํ ๊ฒฝ์ฐ ์์ธ ์ฒ๋ฆฌ
if (deliveryRepository.existsByOrderId(request.orderId())) {
sendInventoryRestoreMessage(request);
slackNotifier.sendDeliveryCreateFailureAlert(
request.orderId(),
"DELIVERY_ALREADY_EXISTS",
"ID๊ฐ " + request.orderId() + "์ธ ์ฃผ๋ฌธ์ ๋ํ ๋ฐฐ์ก์ด ์ด๋ฏธ ์กด์ฌํ์ฌ ๋ฐฐ์ก ์์ฑ์ ์คํจํ์ต๋๋ค.");
throw new ConflictException(ErrorCode.DELIVERY_ALREADY_EXISTS);
}
if (!sellerRepository.existsById(request.sellerId())) {
sendInventoryRestoreMessage(request);
slackNotifier.sendDeliveryCreateFailureAlert(
request.orderId(),
"SELLER_NOT_FOUND",
"ID๊ฐ " + request.sellerId() + "์ธ ํ๋งค์๊ฐ ์กด์ฌํ์ง ์์ ๋ฐฐ์ก ์์ฑ์ ์คํจํ์ต๋๋ค.");
throw new NotFoundException(ErrorCode.SELLER_NOT_FOUND);
}
if (!senderRepository.existsById(request.senderId())) {
sendInventoryRestoreMessage(request);
slackNotifier.sendDeliveryCreateFailureAlert(
request.orderId(),
"SENDER_NOT_FOUND",
"ID๊ฐ " + request.senderId() + "์ธ ์ถ๊ณ ์ง(๋ฌผ๋ฅ์ผํฐ)๊ฐ ์กด์ฌํ์ง ์์ ๋ฐฐ์ก ์์ฑ์ ์คํจํ์ต๋๋ค.");
throw new NotFoundException(ErrorCode.SENDER_NOT_FOUND);
}
if (!addressRepository.existsById(request.addressId())) {
sendInventoryRestoreMessage(request);
slackNotifier.sendDeliveryCreateFailureAlert(
request.orderId(),
"ADDRESS_NOT_FOUND",
"ID๊ฐ " + request.addressId() + "์ธ ์ฃผ์๊ฐ ์กด์ฌํ์ง ์์ ๋ฐฐ์ก ์์ฑ์ ์คํจํ์ต๋๋ค.");
throw new NotFoundException(ErrorCode.ADDRESS_NOT_FOUND);
}
Order order = orderRepository.getReferenceById(request.orderId());
Seller seller = sellerRepository.getReferenceById(request.sellerId());
Sender sender = senderRepository.getReferenceById(request.senderId());
Address address = addressRepository.findById(request.addressId())
.orElseThrow(() -> new NotFoundException(ErrorCode.ADDRESS_NOT_FOUND));
Delivery delivery = Delivery.of(order, seller, sender, address, request.deliveryRequest());
deliveryRepository.save(delivery);
// ์ฌ๋ ์๋ฆผ ๋ฉ์์ง ์ ์ก
slackNotifier.sendDeliveryCreateAlert(
request.orderId(),
delivery.getOrder().getMember().getName(),
delivery.getOrder().getMember().getPhoneNumber(),
delivery.getReceiverName(),
delivery.getReceiverPhone(),
delivery.getReceiverAddress(),
delivery.getReceiverDetailAddress());
return CreateDeliveryResponse.from(delivery);
}
๊ทธ๋ค์ ์นด์นด์คํก ์๋ฆผ ๋ฉ์์ง ์ ์ก์์ ๋ฐฐ์ก ์ค๊ณผ ๋ฐฐ์ก ์๋ฃ์ ์ํฉ์ ๋ง๊ฒ ์นด์นด์คํก ์๋ฆผ ๋ฉ์์ง๊ฐ ์ ์ก๋ ์ ์๋๋ก ๊ตฌํํ๋ค.
@Transactional
public UpdateDeliveryResponse updateTrackingNumber(Long deliveryId, UpdateDeliveryRequest request) {
Delivery delivery = getDelivery(deliveryId);
delivery.updateTrackingNumber(request.trackingNumber());
// ์นด์นด์คํก ์ถ๊ณ ์๋ฃ ์๋ฆผ ๋ฉ์์ง ์ ์ก
UserNotification userNotification = UserNotification.from(delivery);
kakaoMessageService.sendShippedMessage(userNotification);
return UpdateDeliveryResponse.from(delivery);
}
๋ฐฐ์ก ์ํ๊ฐ ๋ณ๊ฒฝ๋ ๋ ์นด์นด์คํก ์๋ฆผ ๋ฉ์์ง๊ฐ ์ ์ก๋ ์ ์๋๋ก ํ๋ค!
๊ทธ ํ์ ๊ฒฐ์ ์์ ๋์ด์ค๋ ๋ฐ์ดํฐ๋ฅผ ํ์ฉํด์ ์ฌ๊ณ ๊ฐ์๋ฅผ ์งํํ๊ณ ๋ฐฐ์ก ์์ฑ๊น์ง ์ด์ด์ง ์ ์๋๋ก ๋ก์ง์ ์์ฑํ๋ค.
CreateDeliveryRequest request = (CreateDeliveryRequest) session.getAttribute("delivery");
String payload = JsonHelper.toJson(request);
String key = "";
String value = JsonHelper.toJson(OperationWrapperDto.from(OperationType.ORDER_PAYMENT_INVENTORY_DECREASE, payload));
stringKafkaTemplate.send("order_make_topic", key, value);
return ResponseEntity.status(code).body(jsonObject);
// ์ฌ๊ณ ๊ด๋ จ Kafka Listener
@KafkaListener(topics = "${kafka.topic.product}", groupId = "${spring.kafka.consumer.product-combination.group-id}")
public void listenInventory(@Header(KafkaHeaders.RECEIVED_KEY) String key, String value) {
try {
OperationWrapperDto wrapperDto = JsonHelper.fromJson(value, OperationWrapperDto.class);
OperationType type = wrapperDto.operationType();
String payload = wrapperDto.payload();
switch (type) {
case ORDER_PAYMENT_INVENTORY_DECREASE -> routeInventoryDecreaseMessage(key, payload);
case DELIVERY_FAIL_INVENTORY_RESTORE -> routeInventoryRestoreMessage(key, payload);
default -> log.error("โ ์ง์ํ์ง ์๋ ๋ฉ์์ง ํ์
์์ : {}", type);
}
} catch (ConflictException e) {
log.warn("โ ๏ธ ์ฌ๊ณ ๋ถ์กฑ์ผ๋ก ์ฌ๊ณ ๊ฐ์ ๋ฉ์์ง ์ฒ๋ฆฌ ์คํจ: {}", e.getMessage());
} catch (NotFoundException e) {
log.warn("โ ๏ธ ํ์ ๋ฐ์ดํฐ ๋๋ฝ์ผ๋ก ์ฌ๊ณ ๊ฐ์ ๋ฉ์์ง ์ฒ๋ฆฌ ์คํจ ({}): {}", e.getErrorCode(), e.getMessage());
} catch (NumberFormatException e) {
log.warn("โ ๏ธ ์๋ชป๋ orderId ํ์์
๋๋ค. key: {}, message: {}", key, e.getMessage());
} catch (Exception e) {
log.error("โ ์๊ธฐ์น ๋ชปํ ์ค๋ฅ๋ก ์ฌ๊ณ ๊ฐ์ ๋ฉ์์ง ์ฒ๋ฆฌ ์คํจ: {}", e.getMessage());
}
}
private void routeInventoryDecreaseMessage(String key, String value) {
// ๊ฒฐ์ ํํธ์์ ์ ๋ฌ๋๋ value ํ์ธ ํ DTO ์ ์
log.info("๐ฅ Kafka ์ฌ๊ณ ๊ฐ์ ๋ฉ์์ง ์์ : key: {}, value: {}", key, value);
CreateDeliveryRequest request = JsonHelper.fromJson(value, CreateDeliveryRequest.class);
// ์ฌ๊ณ ๊ฐ์ ๋ก์ง ์งํ
productOptionCombinationService.decreaseProductOptionCombinationInventory(Long.valueOf(key));
log.info("๐ ์ฌ๊ณ ๊ฐ์ ์ฑ๊ณต! ๋ชจ๋ ์ํ์ ์ฌ๊ณ ์ฐจ๊ฐ์ด ์ ์์ ์ผ๋ก ์ฒ๋ฆฌ๋์์ต๋๋ค.");
// ๋ฐฐ์ก ์์ฑ ์์ฒญ ๋ฉ์์ง ๋ฐํ
String payload = JsonHelper.toJson(request);
String message = JsonHelper.toJson(OperationWrapperDto.from(OperationType.DELIVERY_CREATE, payload));
kafkaTemplate.send(deliveryTopic, key, message);
log.info("\uD83D\uDCE4 Kafka ๋ฐฐ์ก ์์ฑ ๋ฉ์์ง ์ ์ก: {}", request);
}
private void routeInventoryRestoreMessage(String key, String value) {
log.info("๐ฅ Kafka ์ฌ๊ณ ๋ณต์ ๋ฉ์์ง ์์ : key={}, value={}", key, value);
// ์ฌ๊ณ ๋ณต์ ๋ก์ง ์งํ
Long orderId = Long.valueOf(key);
productOptionCombinationService.restoreProductOptionCombinationInventory(orderId);
}
์ด๋ ๊ฒ ๊ฒฐ์ ์๋ฃ ํ ๋์ด์ค๋ ๋ฐ์ดํฐ๋ก ์ฌ๊ณ ๊ฐ์ ์งํ ํ ๋ฐฐ์ก ์์ฑ๊น์ง ๋ง๋ฌด๋ฆฌ๋ ์ ์๋๋ก ๊ตฌํํ๋ค!
๋ง์ง๋ง์ผ๋ก ๊ฐ๊ฒฉ ๋ณ๋์ ์ด๋ป๊ฒ ์ฒ๋ฆฌํ ์ ์์์ง ๊ณ ๋ฏผ์ ํ๋ค๊ฐ ํ๋ฃจ๊ฐ ๋๋๋ฒ๋ ธ๋ค..
๐๏ธ ๋ด์ผ์ ๋ญ ํ์ง?!
โ๏ธ ๊ฐ๊ฒฉ ๋ณ๋ ๋ก์ง ๋ง๋ฌด๋ฆฌํ๊ธฐ
โ๏ธ API ๋ช
์ธ์ ์์ ํ๊ธฐ
โ๏ธ TIL ์์ฑํ๊ธฐ