์ด ๊ธ์์๋ ๊ธฐ์กด์ ํ๋์ ํธ๋์ญ์ ์์์ ๋์ํ๋ ์ฃผ๋ฌธ ์์ฑ ๋ก์ง์ Spring Application Event ๊ธฐ๋ฐ์ผ๋ก ๋ถ๋ฆฌํ๋ฉฐ, ํธ๋์ญ์ ์์ ์ฑ๊ณผ ์ฝ๋ ๊ตฌ์กฐ ๊ฐ์ ์ ๋์์ ๋ฌ์ฑํ ๊ณผ์ ์ ์ ๋ฆฌํฉ๋๋ค.
Spring Application Event๋ JVM ๋ด๋ถ ๋ฉ๋ชจ๋ฆฌ์์ ๋์ํ๋ in-memory ์ด๋ฒคํธ ์์คํ
์
๋๋ค.
์๋น์ค ๊ฐ ๊ฐํ ๊ฒฐํฉ์ ์ค์ด๊ณ ํ์ ๋ก์ง์ ๋ณ๋์ ์ด๋ฒคํธ ํธ๋ค๋ฌ๋ก ๋ถ๋ฆฌํ๋ ๋ฐ ํจ๊ณผ์ ์
๋๋ค.
๋จ์ผ ์๋ฒ ํ๊ฒฝ์์๋ ๊ตฌํ์ด ๊ฐ๋จํ๊ณ ์ฑ๋ฅ๋ ์ข์ ๋๋ฆฌ ์ฌ์ฉํ๋ ๋ฐฉ์์
๋๋ค.
๋ค๋ง ๊ตฌ์กฐ์ ์ผ๋ก JVM ๋จ์ผ ์ธ์คํด์ค๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ํ๊ธฐ ๋๋ฌธ์ ๋ช ๊ฐ์ง ๋ช ํํ ํ๊ณ๊ฐ ์กด์ฌํฉ๋๋ค.
Spring Event๋ JVM ์์์๋ง ์ฒ๋ฆฌ๋๋ ๋ฉ๋ชจ๋ฆฌ ๊ธฐ๋ฐ ์ด๋ฒคํธ์ด๊ธฐ ๋๋ฌธ์ ์๋์ ๊ฐ์ ์ ์ฝ์ ๊ฐ์ต๋๋ค.
Spring Event๋ ์ธ์คํด์ค๋ง๋ค ๋
๋ฆฝ์ ์ผ๋ก ์กด์ฌํ๊ธฐ ๋๋ฌธ์
๋์ผํ ์ด๋ฒคํธ๊ฐ ์ฌ๋ฌ ์ธ์คํด์ค์์ ๊ฐ๊ฐ ์คํ๋ ์ ์์ต๋๋ค.
โํ์ ์ฒ๋ฆฌ๊ฐ ๋จ ํ ๋ฒ๋ง ์คํ๋ผ์ผ ํ๋ ๋ก์งโ์๋ ์ ํฉํ์ง ์์ต๋๋ค.
์ด๋ฒคํธ๊ฐ ์ ์ฅ๋์ง ์๊ธฐ ๋๋ฌธ์
์ฒ๋ฆฌ๋๊ธฐ ์ ์ ์๋ฒ๊ฐ ์ข
๋ฃ๋๋ฉด ํด๋น ์ด๋ฒคํธ๋ ๋ณต๊ตฌํ ์ ์์ต๋๋ค.
๊ฒฐ์ ยท์ ์ฐยทํฌ์ธํธ ๋ฑ ์ ๋ขฐ์ฑ์ด ์ค์ํ ์์ญ์์ ์ฌ์ฉํ๊ธฐ ์ด๋ ต์ต๋๋ค.
์ด๋ฒ ํ๋ก์ ํธ์์๋ ์ฐ์ ํธ๋์ญ์
๋ถ๋ฆฌ๋ฅผ ๋ชฉ์ ์ ๋๊ณ Spring Event๋ฅผ ์ฌ์ฉํ์ต๋๋ค.
๊ธฐ์กด ์ฃผ๋ฌธ ์์ฑ ๋ก์ง์ ๋ค์๊ณผ ๊ฐ์ ๋ฌธ์ ๊ฐ ์์์ต๋๋ค.
์ด๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด ์ฃผ๋ฌธ ์์ฑ๊ณผ ํ์ ์ฒ๋ฆฌ(์ฟ ํฐ, PG ์์ฒญ)๋ฅผ ์ด๋ฒคํธ ๊ธฐ๋ฐ์ผ๋ก ๋ถ๋ฆฌํ์ต๋๋ค.
ํญ๊ณต๊ถ/์์ฝ ์์คํ ๋ฑ ์ ํ์ฑ์ด ์ค์ํ ํ๊ฒฝ์์ ์ฌ์ฉํ๋ ๋ฐฉ์์ ๋๋ค.
ํธ๋ํฝ์ด ๋์ ์ปค๋จธ์ค์์ ์์ฃผ ์ฌ์ฉํ๋ ๋ฐฉ์์ ๋๋ค.
์๋น์ค ํน์ฑ์ ์๋ ๊ธฐ์ค์ ์ธ์ ์ต๋๋ค.
@Transactional
public OrderInfo createOrder(CreateOrderCommand command) {
List<Product> products = productService.getExistingProducts(
command.orderItemRequests().stream()
.map(CreateOrderCommand.OrderItemRequest::productId)
.toList()
);
deductStock(command.orderItemRequests());
Order order = Order.create(command.userId(), createOrderItems(command.orderItemRequests(), products));
Order savedOrder = orderService.save(order);
eventPublisher.publishEvent(new OrderCreatedEvent(
savedOrder.getId(),
command.userId(),
command.couponId(),
command.cardType(),
command.cardNo()
));
return OrderInfo.from(savedOrder);
}
ํต์ฌ ์๋
@TransactionalEventListener(phase = AFTER_COMMIT)
@Async
public void handleOrderCreated(OrderCreatedEvent event) {
Order order = orderService.getOrder(event.orderId());
Money originalPrice = order.getTotalPrice();
Money finalPrice = couponService.useCouponById(
event.couponId(),
event.userId(),
originalPrice
);
paymentService.requestPayment(event.orderId(), event.cardType(), event.cardNo(), finalPrice);
eventPublisher.publishEvent(new OrderDataTransferEvent(
event.orderId(),
event.userId(),
order.getStatus(),
order.getTotalPrice().getAmount(),
LocalDateTime.now(),
"ORDER_CREATED"
));
}
์ข์์ ์ด๋ฒคํธ๋ ๋ฐ์ ๋น๋๊ฐ ๋์
๊ฐ ์์ฒญ๋ง๋ค ์ฆ์ ์ง๊ณ ํ
์ด๋ธ์ ๊ฐฑ์ ํ๋ฉด ์ฑ๋ฅ ๋ถํ๊ฐ ์ปค์ง๋๋ค.
์ด๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด ์ด๋ฒคํธ ๊ธฐ๋ฐ ์ง๊ณ + ๋๋ฐ์ด์ค ๋ฐฉ์์ ์ ์ฉํ์ต๋๋ค.
Long lastTime = lastProcessedTime.get(event.productId());
long currentTime = System.currentTimeMillis();
if (lastTime != null && (currentTime - lastTime) < debounceInterval) {
return;
}
long actualLikeCount = likeService.getLikeCount(event.productId());
productListViewService.syncLikeCount(event.productId(), actualLikeCount);
lastProcessedTime.put(event.productId(), currentTime);
๊ตฌ์กฐ ๊ฐ์ ์ ํตํด ๋ค์๊ณผ ๊ฐ์ ํจ๊ณผ๋ฅผ ์ป์์ต๋๋ค.
๋ค๋ง Spring Event๋ ๊ทผ๋ณธ์ ์ธ ์ด๋ฒคํธ ํ๋ซํผ์ด ์๋๊ธฐ ๋๋ฌธ์
์ด๋ฒคํธ ์ ์คยท์ค๋ณต ์ฒ๋ฆฌ ๋ฑ ๊ตฌ์กฐ์ ํ๊ณ๊ฐ ์กด์ฌํฉ๋๋ค.
์ด๋ฒ ๊ฐํธ์์๋ ํธ๋์ญ์
๋ถ๋ฆฌ์ ์ฝ๋ ๊ตฌ์กฐ ๊ฐ์ ์ ์ฃผ ๋ชฉ์ ์ ๋์์ผ๋ฉฐ,
์ถํ Kafka/RabbitMQ ๋ฑ MQ ๊ธฐ๋ฐ ๊ตฌ์กฐ๋ก ํ์ฅํ ์์ ์
๋๋ค.