๐ฑ Spring @EventListener๋ก ์์ํ๋ ์ด๋ฒคํธ ๊ธฐ๋ฐ ํ๋ก๊ทธ๋๋ฐ์ ์ธ๊ณ - ์ฝ๋๋ฅผ ๋ ์ฐ์ํ๊ฒ ๋ง๋๋ ๋ฐฉ๋ฒ ๐ฑ

์๋ , ๊ฐ๋ฐ์ ์น๊ตฌ๋ค! ์ค๋์ Spring ํ๋ ์์ํฌ์์ ์ ๋ง ์ ์ฉํ์ง๋ง ์์ธ๋ก ๋ง์ ๊ฐ๋ฐ์๋ค์ด ์ ๋๋ก ํ์ฉํ์ง ๋ชปํ๋ ๊ธฐ๋ฅ์ธ @EventListener์ ๋ํด ํจ๊ป ์์๋ณผ ๊ฑฐ์ผ. ์ด๋ฒคํธ ๊ธฐ๋ฐ ํ๋ก๊ทธ๋๋ฐ์ด ์ด๋ป๊ฒ ๋์ ์ฝ๋๋ฅผ ๋ ๊น๋ํ๊ณ , ์ ์ง๋ณด์ํ๊ธฐ ์ฝ๊ณ , ํ์ฅ์ฑ ์๊ฒ ๋ง๋ค์ด์ฃผ๋์ง ์ฌ๋ฏธ์๊ฒ ์ค๋ช ํด ์ค๊ฒ! ๐
๐ ๋ชฉ์ฐจ
- ์ด๋ฒคํธ ๊ธฐ๋ฐ ํ๋ก๊ทธ๋๋ฐ์ด ๋ญ์ผ? ๐ค
- Spring์ ์ด๋ฒคํธ ์์คํ ์๊ฐ ๐
- @EventListener ์ด๋ ธํ ์ด์ ์์ ์ ๋ณต ๐ช
- ์ค์ ์์ ๋ก ๋ฐฐ์ฐ๋ ์ด๋ฒคํธ ์ฒ๋ฆฌ ๐ ๏ธ
- ๋น๋๊ธฐ ์ด๋ฒคํธ ์ฒ๋ฆฌ๋ก ์ฑ๋ฅ ํฅ์์ํค๊ธฐ โก
- ํธ๋์ญ์ ๊ณผ ์ด๋ฒคํธ ์ฒ๋ฆฌ ๐ผ
- ์ด๋ฒคํธ ๊ธฐ๋ฐ ์ํคํ ์ฒ์ ์ฅ๋จ์ โ๏ธ
- Spring Boot 3.x์์์ ์๋ก์ด ์ด๋ฒคํธ ๊ธฐ๋ฅ๋ค ๐
- ์ค๋ฌด์์ ์์ฃผ ์ฌ์ฉ๋๋ ์ด๋ฒคํธ ํจํด ๐จโ๐ป
- ๋ง๋ฌด๋ฆฌ ๋ฐ ์ถ๊ฐ ํ์ต ์๋ฃ ๐
1. ์ด๋ฒคํธ ๊ธฐ๋ฐ ํ๋ก๊ทธ๋๋ฐ์ด ๋ญ์ผ? ๐ค
์ด๋ฒคํธ ๊ธฐ๋ฐ ํ๋ก๊ทธ๋๋ฐ(Event-Driven Programming)์ ํ๋ก๊ทธ๋จ์ ํ๋ฆ์ด ์ด๋ฒคํธ์ ๋ฐ์๊ณผ ์ฒ๋ฆฌ์ ์ํด ๊ฒฐ์ ๋๋ ํ๋ก๊ทธ๋๋ฐ ํจ๋ฌ๋ค์์ด์ผ. ์ฝ๊ฒ ๋งํ๋ฉด, "์ด๋ค ์ผ์ด ๋ฐ์ํ์ ๋(์ด๋ฒคํธ), ๊ทธ์ ๋ง๋ ์ฒ๋ฆฌ๋ฅผ ํ์"๋ผ๋ ๊ฐ๋ ์ด์ง.
์ผ์์ํ์์ ์๋ฅผ ๋ค๋ฉด ์ด๋ ๊ฒ ์๊ฐํด๋ณผ ์ ์์ด:
- ์น๊ตฌ๊ฐ ์์ผ ํํฐ์ ์ด๋์ฅ์ ๋ณด๋์ด (์ด๋ฒคํธ ๋ฐ์)
- ๋๋ ๊ทธ ์ด๋์ฅ์ ๋ฐ๊ณ ์ผ์ ์ ํ์ธํด (์ด๋ฒคํธ ์์ )
- ํํฐ ๋ ์ง์ ์ ๋ฌผ์ ์ฌ์ ์ฐธ์ํด (์ด๋ฒคํธ ์ฒ๋ฆฌ)
ํ๋ก๊ทธ๋๋ฐ์์๋ ๋ง์ฐฌ๊ฐ์ง์ผ. ์ฌ์ฉ์๊ฐ ๋ฒํผ์ ํด๋ฆญํ๊ฑฐ๋, ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ ๋ฐ์ดํฐ๊ฐ ์ถ๊ฐ๋๊ฑฐ๋, ํน์ ์กฐ๊ฑด์ด ์ถฉ์กฑ๋๋ฉด ์ด๋ฒคํธ๊ฐ ๋ฐ์ํ๊ณ , ์ด์ ๋์ํ๋ ์ฝ๋๊ฐ ์คํ๋ผ.
์ด๋ฒคํธ ๊ธฐ๋ฐ ํ๋ก๊ทธ๋๋ฐ์ ํต์ฌ ์ฅ์ ์ ๋ฐ๋ก ๋์จํ ๊ฒฐํฉ(Loose Coupling)์ด์ผ. ์ด๋ฒคํธ๋ฅผ ๋ฐ์์ํค๋ ์ฝ๋์ ์ฒ๋ฆฌํ๋ ์ฝ๋๊ฐ ์๋ก ์ง์ ์ ์ธ ์์กด์ฑ ์์ด ๋ ๋ฆฝ์ ์ผ๋ก ์กด์ฌํ ์ ์์ง. ์ด๊ฑด ๋ง์น ๋์ ์น๊ตฌ๊ฐ ์๋ก ์ง์ ๋ง๋์ง ์๊ณ ๋ SNS๋ฅผ ํตํด ์ํตํ๋ ๊ฒ๊ณผ ๋น์ทํด. ์๋ก ์๊ฐ๊ณผ ๊ณต๊ฐ์ ๊ตฌ์ ๋ฐ์ง ์๊ณ ๋ฉ์์ง๋ฅผ ์ฃผ๊ณ ๋ฐ์ ์ ์์์? ๐
2. Spring์ ์ด๋ฒคํธ ์์คํ ์๊ฐ ๐
Spring์ ์ฒ์๋ถํฐ ์ด๋ฒคํธ ๊ธฐ๋ฐ ํ๋ก๊ทธ๋๋ฐ์ ์ง์ํ์ด. Spring์ ์ด๋ฒคํธ ์์คํ ์ ์ต์ ๋ฒ ํจํด(Observer Pattern)์ ๊ธฐ๋ฐ์ผ๋ก ๊ตฌํ๋์ด ์์ง.
Spring ์ด๋ฒคํธ ์์คํ ์ ์ฃผ์ ๊ตฌ์ฑ์์
- ApplicationEvent: ๋ชจ๋ ์ด๋ฒคํธ์ ๊ธฐ๋ณธ ํด๋์ค
- ApplicationEventPublisher: ์ด๋ฒคํธ๋ฅผ ๋ฐํํ๋ ์ธํฐํ์ด์ค
- ApplicationListener: ์ด๋ฒคํธ๋ฅผ ์์ ํ๋ ์ธํฐํ์ด์ค
- @EventListener: ๋ฉ์๋์ ์ด๋ฒคํธ ๋ฆฌ์ค๋ ๊ธฐ๋ฅ์ ๋ถ์ฌํ๋ ์ด๋ ธํ ์ด์
Spring 4.2 ์ด์ ์๋ ์ด๋ฒคํธ๋ฅผ ์ฒ๋ฆฌํ๊ธฐ ์ํด ApplicationListener
์ธํฐํ์ด์ค๋ฅผ ๊ตฌํํด์ผ ํ์ด. ๊ทธ๋ฐ๋ฐ ์ด ๋ฐฉ์์ ์ข ๋ฒ๊ฑฐ๋ก์ ์ง. ํด๋์ค๊ฐ ํน์ ์ธํฐํ์ด์ค์ ์ข
์๋๊ณ , ์ฝ๋๋ ์ฅํฉํด์ก๊ฑฐ๋ .
Spring 4.2 ์ด์ ์ ์ด๋ฒคํธ ๋ฆฌ์ค๋ ๊ตฌํ ๋ฐฉ์:
public class UserRegistrationListener implements ApplicationListener<UserRegisteredEvent> {
@Override
public void onApplicationEvent(UserRegisteredEvent event) {
// ์ด๋ฒคํธ ์ฒ๋ฆฌ ๋ก์ง
User user = event.getUser();
sendWelcomeEmail(user);
}
private void sendWelcomeEmail(User user) {
// ์ด๋ฉ์ผ ๋ฐ์ก ๋ก์ง
}
}
Spring 4.2๋ถํฐ๋ @EventListener ์ด๋ ธํ ์ด์ ์ด ๋์ ๋๋ฉด์ ํจ์ฌ ๊ฐ๊ฒฐํ๊ณ ์ ์ฐํ๊ฒ ์ด๋ฒคํธ๋ฅผ ์ฒ๋ฆฌํ ์ ์๊ฒ ๋์ด. ์ด์ ์ด๋ค ๋น์ ๋ฉ์๋์๋ ์ด ์ด๋ ธํ ์ด์ ์ ๋ถ์ด๊ธฐ๋ง ํ๋ฉด ์ด๋ฒคํธ ๋ฆฌ์ค๋๊ฐ ๋๋ ๊ฑฐ์ง! ๐
Spring 4.2 ์ดํ์ ์ด๋ฒคํธ ๋ฆฌ์ค๋ ๊ตฌํ ๋ฐฉ์:
@Component
public class UserService {
@EventListener
public void handleUserRegistered(UserRegisteredEvent event) {
// ์ด๋ฒคํธ ์ฒ๋ฆฌ ๋ก์ง
User user = event.getUser();
sendWelcomeEmail(user);
}
private void sendWelcomeEmail(User user) {
// ์ด๋ฉ์ผ ๋ฐ์ก ๋ก์ง
}
}
ํจ์ฌ ๊น๋ํด์ก์ง? ์ด์ ํน๋ณํ ์ธํฐํ์ด์ค๋ฅผ ๊ตฌํํ ํ์ ์์ด, ํ๋ฒํ Spring ๋น์ ๋ฉ์๋์ ์ด๋ ธํ ์ด์ ๋ง ๋ถ์ด๋ฉด ๋ผ. ์ด๊ฒ ๋ฐ๋ก Spring์ด ์ถ๊ตฌํ๋ POJO(Plain Old Java Object) ๊ธฐ๋ฐ ํ๋ก๊ทธ๋๋ฐ์ ์ฒ ํ์ด์ผ. ๐งโโ๏ธ
3. @EventListener ์ด๋ ธํ ์ด์ ์์ ์ ๋ณต ๐ช
์ด์ @EventListener ์ด๋ ธํ ์ด์ ์ ๋ํด ๋ ์์ธํ ์์๋ณด์. ์ด ์์ ์ด๋ ธํ ์ด์ ํ๋๋ก ํ ์ ์๋ ์ผ์ด ์ ๋ง ๋ง์!
๊ธฐ๋ณธ ์ฌ์ฉ๋ฒ
๊ฐ์ฅ ๊ธฐ๋ณธ์ ์ธ ์ฌ์ฉ๋ฒ์ ์ด๋ฒคํธ ํ์ ์ ํ๋ผ๋ฏธํฐ๋ก ๋ฐ๋ ๋ฉ์๋์ @EventListener๋ฅผ ๋ถ์ด๋ ๊ฑฐ์ผ.
@Component
public class MyEventHandler {
@EventListener
public void handleMyEvent(MyCustomEvent event) {
System.out.println("์ด๋ฒคํธ ์์ : " + event.getMessage());
}
}
์กฐ๊ฑด๋ถ ์ด๋ฒคํธ ์ฒ๋ฆฌ
@EventListener๋ SpEL(Spring Expression Language)์ ์ฌ์ฉํ ์กฐ๊ฑด์์ ์ง์ํด. ์ด๋ฅผ ํตํด ํน์ ์กฐ๊ฑด์ ๋ง์กฑํ๋ ์ด๋ฒคํธ๋ง ์ฒ๋ฆฌํ ์ ์์ด.
@EventListener(condition = "#event.success == true")
public void handleSuccessfulEvent(MyCustomEvent event) {
System.out.println("์ฑ๊ณตํ ์ด๋ฒคํธ๋ง ์ฒ๋ฆฌํฉ๋๋ค!");
}
์ ์ฝ๋๋ event.success
๊ฐ์ด true์ธ ์ด๋ฒคํธ๋ง ์ฒ๋ฆฌํด. ์ด๋ ๊ฒ ํ๋ฉด ๋ถํ์ํ if ๋ฌธ์ ์ค์ด๊ณ ์ ์ธ์ ์ผ๋ก ์กฐ๊ฑด์ ํํํ ์ ์์ง.
์ฌ๋ฌ ์ด๋ฒคํธ ํ์ ์ฒ๋ฆฌํ๊ธฐ
ํ๋์ ๋ฆฌ์ค๋๋ก ์ฌ๋ฌ ํ์ ์ ์ด๋ฒคํธ๋ฅผ ์ฒ๋ฆฌํ๊ณ ์ถ์ ๋๋ classes ์์ฑ์ ์ฌ์ฉํ ์ ์์ด.
@EventListener(classes = { UserCreatedEvent.class, UserUpdatedEvent.class })
public void handleUserEvents(ApplicationEvent event) {
if (event instanceof UserCreatedEvent) {
UserCreatedEvent userCreatedEvent = (UserCreatedEvent) event;
// ์ฌ์ฉ์ ์์ฑ ์ด๋ฒคํธ ์ฒ๋ฆฌ
} else if (event instanceof UserUpdatedEvent) {
UserUpdatedEvent userUpdatedEvent = (UserUpdatedEvent) event;
// ์ฌ์ฉ์ ์
๋ฐ์ดํธ ์ด๋ฒคํธ ์ฒ๋ฆฌ
}
}
์ด๋ฒคํธ ๋ฐํํ๊ธฐ
์ด๋ฒคํธ๋ฅผ ๋ฐํ(publish)ํ๋ ค๋ฉด ApplicationEventPublisher
๋ฅผ ์ฌ์ฉํด์ผ ํด. Spring์์๋ ์ด๋ฅผ ์ฝ๊ฒ ์ฃผ์
๋ฐ์ ์ ์์ด.
@Service
public class UserService {
private final ApplicationEventPublisher eventPublisher;
@Autowired
public UserService(ApplicationEventPublisher eventPublisher) {
this.eventPublisher = eventPublisher;
}
public void registerUser(User user) {
// ์ฌ์ฉ์ ๋ฑ๋ก ๋ก์ง
userRepository.save(user);
// ์ด๋ฒคํธ ๋ฐํ
eventPublisher.publishEvent(new UserRegisteredEvent(this, user));
}
}
Spring 4.2๋ถํฐ๋ ApplicationEventPublisher๋ฅผ ํตํด ApplicationEvent๋ฅผ ์์ํ์ง ์๋ POJO ๊ฐ์ฒด๋ ์ง์ ์ด๋ฒคํธ๋ก ๋ฐํํ ์ ์๊ฒ ๋์ด. ์ด๋ ๊ฒ ํ๋ฉด ์ด๋ฒคํธ ํด๋์ค๊ฐ Spring์ ์์กดํ์ง ์๊ฒ ๋์ง!
// POJO ์ด๋ฒคํธ ํด๋์ค
public class UserRegisteredEvent {
private final User user;
public UserRegisteredEvent(User user) {
this.user = user;
}
public User getUser() {
return user;
}
}
// ์ด๋ฒคํธ ๋ฐํ
eventPublisher.publishEvent(new UserRegisteredEvent(user));
์ด๋ฒคํธ ๊ธฐ๋ฐ ํ๋ก๊ทธ๋๋ฐ์ ์ฌ๋ฅ๋ท๊ณผ ๊ฐ์ ๋ค์ํ ์๋น์ค๋ฅผ ์ ๊ณตํ๋ ํ๋ซํผ์์๋ ๋งค์ฐ ์ ์ฉํด. ์๋ฅผ ๋ค์ด, ์ฌ์ฉ์๊ฐ ์๋ก์ด ์ฌ๋ฅ์ ๋ฑ๋กํ์ ๋ ์ด๋ฒคํธ๋ฅผ ๋ฐ์์ํค๊ณ , ์ด๋ฅผ ์ฒ๋ฆฌํ๋ ๋ฆฌ์ค๋์์ ๊ฒ์ ์ธ๋ฑ์ค ์ ๋ฐ์ดํธ, ์๋ฆผ ๋ฐ์ก, ํต๊ณ ์ง๊ณ ๋ฑ์ ์์ ์ ์ํํ ์ ์์ง. ์ด๋ ๊ฒ ํ๋ฉด ํต์ฌ ๋น์ฆ๋์ค ๋ก์ง๊ณผ ๋ถ๊ฐ ๊ธฐ๋ฅ์ ๊น๋ํ๊ฒ ๋ถ๋ฆฌํ ์ ์์ด! ๐ฏ
4. ์ค์ ์์ ๋ก ๋ฐฐ์ฐ๋ ์ด๋ฒคํธ ์ฒ๋ฆฌ ๐ ๏ธ
์ด์ ์ค์ ์์ ๋ฅผ ํตํด ์ด๋ฒคํธ ๊ธฐ๋ฐ ํ๋ก๊ทธ๋๋ฐ์ ์ด๋ป๊ฒ ํ์ฉํ ์ ์๋์ง ์์๋ณด์. ์จ๋ผ์ธ ์ผํ๋ชฐ์์ ์ฃผ๋ฌธ ์ฒ๋ฆฌ ์์คํ ์ ๊ตฌํํ๋ค๊ณ ๊ฐ์ ํด๋ณผ๊ฒ.
์ฃผ๋ฌธ ์ด๋ฒคํธ ์ ์ํ๊ธฐ
๋จผ์ , ์ฃผ๋ฌธ๊ณผ ๊ด๋ จ๋ ์ด๋ฒคํธ๋ค์ ์ ์ํด๋ณด์.
// ์ฃผ๋ฌธ ์์ฑ ์ด๋ฒคํธ
public class OrderCreatedEvent {
private final Order order;
public OrderCreatedEvent(Order order) {
this.order = order;
}
public Order getOrder() {
return order;
}
}
// ์ฃผ๋ฌธ ๊ฒฐ์ ์๋ฃ ์ด๋ฒคํธ
public class OrderPaidEvent {
private final Order order;
private final Payment payment;
public OrderPaidEvent(Order order, Payment payment) {
this.order = order;
this.payment = payment;
}
public Order getOrder() {
return order;
}
public Payment getPayment() {
return payment;
}
}
์ฃผ๋ฌธ ์๋น์ค ๊ตฌํํ๊ธฐ
์ด์ ์ฃผ๋ฌธ ์๋น์ค์์ ์ด๋ฒคํธ๋ฅผ ๋ฐํํ๋ ๋ถ๋ถ์ ๊ตฌํํด๋ณด์.
@Service
public class OrderService {
private final OrderRepository orderRepository;
private final ApplicationEventPublisher eventPublisher;
@Autowired
public OrderService(OrderRepository orderRepository, ApplicationEventPublisher eventPublisher) {
this.orderRepository = orderRepository;
this.eventPublisher = eventPublisher;
}
@Transactional
public Order createOrder(OrderRequest request) {
// ์ฃผ๋ฌธ ์์ฑ ๋ก์ง
Order order = new Order();
order.setCustomerId(request.getCustomerId());
order.setItems(request.getItems());
order.setTotalAmount(calculateTotalAmount(request.getItems()));
order.setStatus(OrderStatus.CREATED);
Order savedOrder = orderRepository.save(order);
// ์ฃผ๋ฌธ ์์ฑ ์ด๋ฒคํธ ๋ฐํ
eventPublisher.publishEvent(new OrderCreatedEvent(savedOrder));
return savedOrder;
}
@Transactional
public Order processPayment(Long orderId, PaymentRequest paymentRequest) {
Order order = orderRepository.findById(orderId)
.orElseThrow(() -> new OrderNotFoundException(orderId));
// ๊ฒฐ์ ์ฒ๋ฆฌ ๋ก์ง
Payment payment = paymentService.processPayment(paymentRequest);
order.setStatus(OrderStatus.PAID);
Order updatedOrder = orderRepository.save(order);
// ๊ฒฐ์ ์๋ฃ ์ด๋ฒคํธ ๋ฐํ
eventPublisher.publishEvent(new OrderPaidEvent(updatedOrder, payment));
return updatedOrder;
}
private BigDecimal calculateTotalAmount(List<orderitem> items) {
// ์ฃผ๋ฌธ ์ด์ก ๊ณ์ฐ ๋ก์ง
return items.stream()
.map(item -> item.getPrice().multiply(BigDecimal.valueOf(item.getQuantity())))
.reduce(BigDecimal.ZERO, BigDecimal::add);
}
}</orderitem>
์ด๋ฒคํธ ๋ฆฌ์ค๋ ๊ตฌํํ๊ธฐ
์ด์ ๋ฐํ๋ ์ด๋ฒคํธ๋ฅผ ์ฒ๋ฆฌํ ๋ฆฌ์ค๋๋ค์ ๊ตฌํํด๋ณด์.
@Component
public class OrderEventListeners {
private final EmailService emailService;
private final InventoryService inventoryService;
private final AnalyticsService analyticsService;
@Autowired
public OrderEventListeners(EmailService emailService,
InventoryService inventoryService,
AnalyticsService analyticsService) {
this.emailService = emailService;
this.inventoryService = inventoryService;
this.analyticsService = analyticsService;
}
@EventListener
public void handleOrderCreatedEvent(OrderCreatedEvent event) {
Order order = event.getOrder();
// ์ฃผ๋ฌธ ํ์ธ ์ด๋ฉ์ผ ๋ฐ์ก
emailService.sendOrderConfirmationEmail(order);
// ์ฌ๊ณ ํ์ธ ๋ฐ ์์ฝ
inventoryService.reserveItems(order.getItems());
// ์ฃผ๋ฌธ ์์ฑ ๋ถ์ ๋ฐ์ดํฐ ๊ธฐ๋ก
analyticsService.trackOrderCreated(order);
System.out.println("์ฃผ๋ฌธ ์์ฑ ์ด๋ฒคํธ ์ฒ๋ฆฌ ์๋ฃ: " + order.getId());
}
@EventListener
public void handleOrderPaidEvent(OrderPaidEvent event) {
Order order = event.getOrder();
Payment payment = event.getPayment();
// ๊ฒฐ์ ์๋ฃ ์ด๋ฉ์ผ ๋ฐ์ก
emailService.sendPaymentConfirmationEmail(order, payment);
// ๋ฐฐ์ก ํ๋ก์ธ์ค ์์
shippingService.initiateShipping(order);
// ๊ฒฐ์ ์๋ฃ ๋ถ์ ๋ฐ์ดํฐ ๊ธฐ๋ก
analyticsService.trackOrderPaid(order, payment);
System.out.println("๊ฒฐ์ ์๋ฃ ์ด๋ฒคํธ ์ฒ๋ฆฌ ์๋ฃ: " + order.getId());
}
// ํน์ ๊ธ์ก ์ด์์ ์ฃผ๋ฌธ์ ๋ํด์๋ง VIP ๊ณ ๊ฐ ์๋น์ค ์๋ฆผ
@EventListener(condition = "#event.order.totalAmount.compareTo(new java.math.BigDecimal('100000')) > 0")
public void handleLargeOrderPaid(OrderPaidEvent event) {
Order order = event.getOrder();
// VIP ๊ณ ๊ฐ ์๋น์คํ์ ์๋ฆผ
customerServiceNotifier.notifyVipTeam(order);
System.out.println("๋๊ท๋ชจ ์ฃผ๋ฌธ ์๋ฆผ ์ฒ๋ฆฌ ์๋ฃ: " + order.getId());
}
}
์ ์์ ์์ ๋ณผ ์ ์๋ฏ์ด, ์ด๋ฒคํธ ๊ธฐ๋ฐ ํ๋ก๊ทธ๋๋ฐ์ ์ฌ์ฉํ๋ฉด ์ฃผ๋ฌธ ์ฒ๋ฆฌ์ ๊ด๋ จ๋ ๋ถ๊ฐ ๊ธฐ๋ฅ๋ค(์ด๋ฉ์ผ ๋ฐ์ก, ์ฌ๊ณ ๊ด๋ฆฌ, ๋ถ์ ๋ฑ)์ ํต์ฌ ๋น์ฆ๋์ค ๋ก์ง์์ ๋ถ๋ฆฌํ ์ ์์ด. ์ด๋ ๊ฒ ํ๋ฉด OrderService
๋ ์ค์ง ์ฃผ๋ฌธ ์์ฑ๊ณผ ๊ฒฐ์ ์ฒ๋ฆฌ๋ผ๋ ํต์ฌ ์ฑ
์์๋ง ์ง์คํ ์ ์์ง. ๐ฏ
๋ํ condition ์์ฑ์ ์ฌ์ฉํด ํน์ ์กฐ๊ฑด(์: ์ฃผ๋ฌธ ๊ธ์ก์ด 10๋ง์ ์ด์)์ ๋ง์กฑํ๋ ๊ฒฝ์ฐ์๋ง ์ด๋ฒคํธ๋ฅผ ์ฒ๋ฆฌํ๋๋ก ํ ์๋ ์์ด. ์ด๋ฐ ๋ฐฉ์์ผ๋ก ๋น์ฆ๋์ค ๊ท์น์ ์ ์ธ์ ์ผ๋ก ํํํ ์ ์์ง.
์ด๋ฐ ์ด๋ฒคํธ ๊ธฐ๋ฐ ์ํคํ ์ฒ๋ ์ฌ๋ฅ๋ท๊ณผ ๊ฐ์ ๋ณต์กํ ๋น์ฆ๋์ค ๋ก์ง์ ๊ฐ์ง ํ๋ซํผ์์ ํนํ ์ ์ฉํด. ์๋ฅผ ๋ค์ด, ์ฌ๋ฅ ๊ฑฐ๋๊ฐ ์๋ฃ๋์์ ๋ ํ๋งค์์๊ฒ ์ ์ฐํ๊ณ , ๊ตฌ๋งค์์๊ฒ ๋ฆฌ๋ทฐ ์์ฒญ์ ๋ณด๋ด๊ณ , ๊ด๋ จ ํต๊ณ๋ฅผ ์ ๋ฐ์ดํธํ๋ ๋ฑ์ ๋ค์ํ ํ์ ์์ ์ ๊น๋ํ๊ฒ ์ฒ๋ฆฌํ ์ ์๊ฑฐ๋ . ๐
5. ๋น๋๊ธฐ ์ด๋ฒคํธ ์ฒ๋ฆฌ๋ก ์ฑ๋ฅ ํฅ์์ํค๊ธฐ โก
์ง๊ธ๊น์ง ์ดํด๋ณธ ์ด๋ฒคํธ ์ฒ๋ฆฌ๋ ๊ธฐ๋ณธ์ ์ผ๋ก ๋๊ธฐ(Synchronous) ๋ฐฉ์์ผ๋ก ๋์ํด. ์ฆ, ์ด๋ฒคํธ๊ฐ ๋ฐํ๋๋ฉด ๋ชจ๋ ๋ฆฌ์ค๋์ ์ฒ๋ฆฌ๊ฐ ์๋ฃ๋ ๋๊น์ง ๋ฐํ ๋ฉ์๋๊ฐ ๋ฐํ๋์ง ์์. ์ด๋ ๋ฆฌ์ค๋์์ ์๊ฐ์ด ์ค๋ ๊ฑธ๋ฆฌ๋ ์์ ์ ์ํํ ๊ฒฝ์ฐ ์ ์ฒด ์๋ต ์๊ฐ์ ์ํฅ์ ์ค ์ ์์ง.
๋คํํ Spring์ @Async ์ด๋ ธํ ์ด์ ์ ํตํด ์ด๋ฒคํธ ๋ฆฌ์ค๋๋ฅผ ๋น๋๊ธฐ์ ์ผ๋ก ์คํํ ์ ์๋ ๊ธฐ๋ฅ์ ์ ๊ณตํด. ์ด๋ฅผ ํ์ฉํ๋ฉด ์ด๋ฒคํธ ๋ฐํ ํ ์ฆ์ ๋ฉ์๋๊ฐ ๋ฐํ๋๊ณ , ๋ฆฌ์ค๋๋ ๋ณ๋์ ์ค๋ ๋์์ ๋น๋๊ธฐ์ ์ผ๋ก ์คํ๋ผ.
๋น๋๊ธฐ ์ด๋ฒคํธ ์ฒ๋ฆฌ ์ค์ ํ๊ธฐ
๋น๋๊ธฐ ์ด๋ฒคํธ ์ฒ๋ฆฌ๋ฅผ ์ฌ์ฉํ๋ ค๋ฉด ๋จผ์ Spring ์ ํ๋ฆฌ์ผ์ด์ ์์ ๋น๋๊ธฐ ์ฒ๋ฆฌ๋ฅผ ํ์ฑํํด์ผ ํด.
@Configuration
@EnableAsync // ๋น๋๊ธฐ ์ฒ๋ฆฌ ํ์ฑํ
public class AsyncConfig {
@Bean
public TaskExecutor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(5);
executor.setMaxPoolSize(10);
executor.setQueueCapacity(25);
executor.setThreadNamePrefix("Async-");
executor.initialize();
return executor;
}
}
๋น๋๊ธฐ ์ด๋ฒคํธ ๋ฆฌ์ค๋ ๊ตฌํํ๊ธฐ
์ด์ @Async ์ด๋ ธํ ์ด์ ์ ์ด๋ฒคํธ ๋ฆฌ์ค๋์ ์ถ๊ฐํด ๋น๋๊ธฐ์ ์ผ๋ก ์ฒ๋ฆฌ๋๋๋ก ํด๋ณด์.
@Component
public class AsyncOrderEventListeners {
private final EmailService emailService;
private final AnalyticsService analyticsService;
@Autowired
public AsyncOrderEventListeners(EmailService emailService, AnalyticsService analyticsService) {
this.emailService = emailService;
this.analyticsService = analyticsService;
}
@Async
@EventListener
public void handleOrderCreatedEvent(OrderCreatedEvent event) {
Order order = event.getOrder();
// ์๊ฐ์ด ์ค๋ ๊ฑธ๋ฆด ์ ์๋ ์ด๋ฉ์ผ ๋ฐ์ก ์์
emailService.sendOrderConfirmationEmail(order);
System.out.println("๋น๋๊ธฐ ์ฃผ๋ฌธ ์์ฑ ์ด๋ฉ์ผ ๋ฐ์ก ์๋ฃ: " + order.getId() +
" (Thread: " + Thread.currentThread().getName() + ")");
}
@Async
@EventListener
public void trackOrderAnalytics(OrderCreatedEvent event) {
Order order = event.getOrder();
// ์๊ฐ์ด ์ค๋ ๊ฑธ๋ฆด ์ ์๋ ๋ถ์ ๋ฐ์ดํฐ ์ฒ๋ฆฌ
analyticsService.trackDetailedOrderMetrics(order);
System.out.println("๋น๋๊ธฐ ์ฃผ๋ฌธ ๋ถ์ ์ฒ๋ฆฌ ์๋ฃ: " + order.getId() +
" (Thread: " + Thread.currentThread().getName() + ")");
}
}
์ ์์ ์์๋ ์ด๋ฉ์ผ ๋ฐ์ก๊ณผ ๋ถ์ ๋ฐ์ดํฐ ์ฒ๋ฆฌ์ ๊ฐ์ด ์๊ฐ์ด ์ค๋ ๊ฑธ๋ฆด ์ ์๋ ์์ ๋ค์ ๋น๋๊ธฐ์ ์ผ๋ก ์ฒ๋ฆฌํ๋๋ก ๊ตฌํํ์ด. ์ด๋ ๊ฒ ํ๋ฉด ์ฃผ๋ฌธ ์์ฑ API์ ์๋ต ์๊ฐ์ด ํฌ๊ฒ ๊ฐ์ ๋ ์ ์์ง!
๋น๋๊ธฐ ์ด๋ฒคํธ ์ฒ๋ฆฌ ์ ์ฃผ์์ฌํญ
- ์์ธ ์ฒ๋ฆฌ: ๋น๋๊ธฐ ๋ฉ์๋์์ ๋ฐ์ํ ์์ธ๋ ํธ์ถ์์๊ฒ ์ ํ๋์ง ์์. ๋ฐ๋ผ์ ์ ์ ํ ์์ธ ์ฒ๋ฆฌ ์ ๋ต์ด ํ์ํด.
- ํธ๋์ญ์ ์ ํ: ๋น๋๊ธฐ ๋ฉ์๋๋ ์๋ก์ด ์ค๋ ๋์์ ์คํ๋๋ฏ๋ก, ์๋ ํธ๋์ญ์ ์ด ์ ํ๋์ง ์์. ํ์ํ๋ค๋ฉด ์๋ก์ด ํธ๋์ญ์ ์ ์์ํด์ผ ํด.
- ๋ฆฌ์์ค ๊ด๋ฆฌ: ์ค๋ ๋ ํ ํฌ๊ธฐ๋ฅผ ์ ์ ํ ์ค์ ํ์ฌ ๋ฆฌ์์ค ๊ณ ๊ฐ์ ๋ฐฉ์งํด์ผ ํด.
- ํ ์คํธ: ๋น๋๊ธฐ ์ฝ๋๋ ํ ์คํธํ๊ธฐ ์ด๋ ค์ธ ์ ์์ผ๋ฏ๋ก, ํ ์คํธ ์ ๋ต์ ์ ์คํ๊ฒ ๊ณํํด์ผ ํด.
@Async
@EventListener
public void handleOrderCreatedEvent(OrderCreatedEvent event) {
try {
// ๋น๋๊ธฐ ์ฒ๋ฆฌ ๋ก์ง
emailService.sendOrderConfirmationEmail(event.getOrder());
} catch (Exception e) {
// ์์ธ ๋ก๊น
log.error("Failed to process order created event", e);
// ์คํจํ ์ด๋ฒคํธ ์ ์ฅ (๋์ค์ ์ฌ์ฒ๋ฆฌํ ์ ์๋๋ก)
failedEventRepository.save(new FailedEvent(event, e.getMessage()));
}
}
๋น๋๊ธฐ ์ด๋ฒคํธ ์ฒ๋ฆฌ๋ ์ฑ๋ฅ ํฅ์์ ํฐ ๋์์ด ๋์ง๋ง, ์์์ ์ธ๊ธํ ์ฃผ์์ฌํญ๋ค์ ๊ณ ๋ คํ์ฌ ์ ์คํ๊ฒ ์ค๊ณํด์ผ ํด. ํนํ ์ค์ํ ๋น์ฆ๋์ค ๋ก์ง์ด๋ ๋ฐ์ดํฐ ์ผ๊ด์ฑ์ด ์ค์ํ ๊ฒฝ์ฐ์๋ ๋๊ธฐ ์ฒ๋ฆฌ ๋ฐฉ์์ด ๋ ์ ํฉํ ์ ์์ด. ๐ค
6. ํธ๋์ญ์ ๊ณผ ์ด๋ฒคํธ ์ฒ๋ฆฌ ๐ผ
Spring์์๋ ํธ๋์ญ์
์๋ฃ ์์ ์ ์ด๋ฒคํธ๋ฅผ ๋ฐํํ ์ ์๋ ํน๋ณํ ๊ธฐ๋ฅ์ ์ ๊ณตํด. ์ด๋ @TransactionalEventListener
์ด๋
ธํ
์ด์
์ ํตํด ๊ตฌํ๋ผ.
์ด ๊ธฐ๋ฅ์ด ์ ์ค์ํ ๊น? ์๋ฅผ ๋ค์ด, ์ฃผ๋ฌธ์ด ์ฑ๊ณต์ ์ผ๋ก ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ ์ฅ๋ ํ์๋ง ์ด๋ฉ์ผ์ ๋ณด๋ด๊ณ ์ถ๋ค๊ณ ๊ฐ์ ํด๋ณด์. ๋ง์ฝ ์ผ๋ฐ์ ์ธ @EventListener๋ฅผ ์ฌ์ฉํ๋ค๋ฉด, ํธ๋์ญ์ ์ด ๋กค๋ฐฑ๋๋๋ผ๋ ์ด๋ฏธ ์ด๋ฉ์ผ์ ๋ฐ์ก๋์์ ์ ์์ด. ์ด๋ฐ ์ํฉ์ ๋ฐฉ์งํ๊ธฐ ์ํด @TransactionalEventListener๋ฅผ ์ฌ์ฉํ ์ ์์ด.
@TransactionalEventListener ์ฌ์ฉํ๊ธฐ
@TransactionalEventListener๋ ํธ๋์ญ์ ์ ํน์ ๋จ๊ณ(phase)์ ์ด๋ฒคํธ๋ฅผ ์ฒ๋ฆฌํ ์ ์๊ฒ ํด์ค.
@Component
public class OrderTransactionalEventListener {
private final EmailService emailService;
@Autowired
public OrderTransactionalEventListener(EmailService emailService) {
this.emailService = emailService;
}
// ํธ๋์ญ์
์ด ์ฑ๊ณต์ ์ผ๋ก ์๋ฃ๋ ํ์๋ง ์คํ
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
public void handleOrderCreatedEvent(OrderCreatedEvent event) {
Order order = event.getOrder();
emailService.sendOrderConfirmationEmail(order);
System.out.println("ํธ๋์ญ์
์ปค๋ฐ ํ ์ด๋ฉ์ผ ๋ฐ์ก ์๋ฃ: " + order.getId());
}
// ํธ๋์ญ์
์ด ๋กค๋ฐฑ๋ ํ์ ์คํ
@TransactionalEventListener(phase = TransactionPhase.AFTER_ROLLBACK)
public void handleOrderCreationFailed(OrderCreatedEvent event) {
Order order = event.getOrder();
System.out.println("์ฃผ๋ฌธ ์์ฑ ํธ๋์ญ์
๋กค๋ฐฑ๋จ: " + order.getId());
// ๋กค๋ฐฑ ๊ด๋ จ ๋ก๊น
๋๋ ๋ชจ๋ํฐ๋ง ์๋ฆผ
}
// ํธ๋์ญ์
์๋ฃ(์ปค๋ฐ ๋๋ ๋กค๋ฐฑ) ํ ์คํ
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMPLETION)
public void handleOrderProcessingCompleted(OrderCreatedEvent event) {
System.out.println("์ฃผ๋ฌธ ์ฒ๋ฆฌ ํธ๋์ญ์
์๋ฃ๋จ (์ปค๋ฐ ๋๋ ๋กค๋ฐฑ)");
// ๋ฆฌ์์ค ์ ๋ฆฌ ๋ฑ
}
// ํธ๋์ญ์
์ปค๋ฐ ์ ์ ์คํ (์ฃผ์: ์ด ์์ ์์ ์์ธ ๋ฐ์ ์ ํธ๋์ญ์
์ด ๋กค๋ฐฑ๋จ)
@TransactionalEventListener(phase = TransactionPhase.BEFORE_COMMIT)
public void validateOrderBeforeCommit(OrderCreatedEvent event) {
Order order = event.getOrder();
// ์ต์ข
์ ํจ์ฑ ๊ฒ์ฌ ๋ฑ
System.out.println("ํธ๋์ญ์
์ปค๋ฐ ์ ์ฃผ๋ฌธ ์ต์ข
๊ฒ์ฆ: " + order.getId());
}
}
@TransactionalEventListener๋ ๋ค์๊ณผ ๊ฐ์ phase ์ต์ ์ ์ ๊ณตํด:
- AFTER_COMMIT (๊ธฐ๋ณธ๊ฐ): ํธ๋์ญ์ ์ด ์ฑ๊ณต์ ์ผ๋ก ์ปค๋ฐ๋ ํ ์คํ
- AFTER_ROLLBACK: ํธ๋์ญ์ ์ด ๋กค๋ฐฑ๋ ํ ์คํ
- AFTER_COMPLETION: ํธ๋์ญ์ ์ด ์๋ฃ(์ปค๋ฐ ๋๋ ๋กค๋ฐฑ)๋ ํ ์คํ
- BEFORE_COMMIT: ํธ๋์ญ์ ์ด ์ปค๋ฐ๋๊ธฐ ์ง์ ์ ์คํ
fallbackExecution ์ต์
๊ธฐ๋ณธ์ ์ผ๋ก @TransactionalEventListener๋ ํธ๋์ญ์
๋ด์์ ์ด๋ฒคํธ๊ฐ ๋ฐํ๋ ๊ฒฝ์ฐ์๋ง ๋์ํด. ํ์ง๋ง fallbackExecution = true
์ต์
์ ์ค์ ํ๋ฉด ํธ๋์ญ์
์ด ์๋ ์ํฉ์์๋ ์ด๋ฒคํธ ๋ฆฌ์ค๋๊ฐ ์คํ๋ผ.
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT, fallbackExecution = true)
public void handleOrderCreatedEvent(OrderCreatedEvent event) {
// ํธ๋์ญ์
์ ๋ฌด์ ๊ด๊ณ์์ด ํญ์ ์คํ๋จ
emailService.sendOrderConfirmationEmail(event.getOrder());
}
@TransactionalEventListener๋ฅผ ์ฌ์ฉํ๋ฉด ๋ฐ์ดํฐ ์ผ๊ด์ฑ์ ์ ์งํ๋ฉด์๋ ์ด๋ฒคํธ ๊ธฐ๋ฐ ์ํคํ ์ฒ์ ์ฅ์ ์ ํ์ฉํ ์ ์์ด. ํนํ ๊ธ์ต ๊ฑฐ๋๋ ์ฃผ๋ฌธ ์ฒ๋ฆฌ์ ๊ฐ์ด ๋ฐ์ดํฐ ์ ํ์ฑ์ด ์ค์ํ ์์คํ ์์ ๋งค์ฐ ์ ์ฉํ์ง. ๐ฐ
์๋ฅผ ๋ค์ด, ์ฌ๋ฅ๋ท์์ ์ฌ๋ฅ ๊ฑฐ๋๊ฐ ์๋ฃ๋๋ฉด ํ๋งค์์๊ฒ ์์ต์ ์ ์ฐํ๋ ๊ณผ์ ์ ์๊ฐํด๋ณด์. ์ ์ฐ ๋ฐ์ดํฐ๊ฐ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ฑ๊ณต์ ์ผ๋ก ์ ์ฅ๋ ํ์๋ง ํ๋งค์์๊ฒ ์๋ฆผ์ ๋ณด๋ด๋ ๊ฒ์ด ์ค์ํ ๊ฑฐ์ผ. ์ด๋ฐ ๊ฒฝ์ฐ @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)๋ฅผ ์ฌ์ฉํ๋ฉด ํธ๋์ญ์ ์ด ์ฑ๊ณต์ ์ผ๋ก ์๋ฃ๋ ํ์๋ง ์๋ฆผ์ด ๋ฐ์ก๋๋๋ก ๋ณด์ฅํ ์ ์์ด. ๐
7. ์ด๋ฒคํธ ๊ธฐ๋ฐ ์ํคํ ์ฒ์ ์ฅ๋จ์ โ๏ธ
์ด๋ฒคํธ ๊ธฐ๋ฐ ํ๋ก๊ทธ๋๋ฐ์ ๋ง์ ์ฅ์ ์ ์ ๊ณตํ์ง๋ง, ๋ชจ๋ ์ํฉ์ ์ ํฉํ ๊ฒ์ ์๋์ผ. ์ด์ ์ด๋ฒคํธ ๊ธฐ๋ฐ ์ํคํ ์ฒ์ ์ฅ๋จ์ ์ ์ดํด๋ณด์.
์ฅ์ ๐
- ๋์จํ ๊ฒฐํฉ(Loose Coupling): ์ด๋ฒคํธ ๋ฐํ์์ ๊ตฌ๋ ์ ๊ฐ์ ์ง์ ์ ์ธ ์์กด์ฑ์ด ์์ด ์ฝ๋์ ์ ์ฐ์ฑ์ด ํฅ์๋ผ.
- ๊ด์ฌ์ฌ์ ๋ถ๋ฆฌ(Separation of Concerns): ํต์ฌ ๋น์ฆ๋์ค ๋ก์ง๊ณผ ๋ถ๊ฐ ๊ธฐ๋ฅ์ ๋ช ํํ ๋ถ๋ฆฌํ ์ ์์ด.
- ํ์ฅ์ฑ(Scalability): ์๋ก์ด ๊ธฐ๋ฅ์ ์ถ๊ฐํ ๋ ๊ธฐ์กด ์ฝ๋๋ฅผ ์์ ํ์ง ์๊ณ ๋ ์๋ก์ด ์ด๋ฒคํธ ๋ฆฌ์ค๋๋ฅผ ์ถ๊ฐํ ์ ์์ด.
- ํ ์คํธ ์ฉ์ด์ฑ: ๊ฐ ์ปดํฌ๋ํธ๋ฅผ ๋ ๋ฆฝ์ ์ผ๋ก ํ ์คํธํ๊ธฐ ์ฌ์์ ธ.
- ๋น๋๊ธฐ ์ฒ๋ฆฌ: @Async์ ํจ๊ป ์ฌ์ฉํ๋ฉด ์ฑ๋ฅ์ ํฅ์์ํฌ ์ ์์ด.
- ์ฝ๋ ๊ฐ๋ ์ฑ: ํต์ฌ ๋ก์ง์ด ๋ถ๊ฐ ๊ธฐ๋ฅ์ผ๋ก ์ค์ผ๋์ง ์์ ์ฝ๋๊ฐ ๋ ๊น๋ํด์ ธ.
๋จ์ ๐
- ๋๋ฒ๊น ์ด๋ ค์: ์ด๋ฒคํธ ๊ธฐ๋ฐ ํ๋ฆ์ ์ถ์ ํ๊ธฐ ์ด๋ ค์ธ ์ ์์ด. ํนํ ๋น๋๊ธฐ ์ฒ๋ฆฌ๋ฅผ ์ฌ์ฉํ ๊ฒฝ์ฐ ๋์ฑ ๊ทธ๋.
- ๋ณต์ก์ฑ ์ฆ๊ฐ: ์์คํ ์ด ์ปค์ง์๋ก ์ด๋ฒคํธ ํ๋ฆ์ ์ดํดํ๊ณ ๊ด๋ฆฌํ๊ธฐ ์ด๋ ค์์ง ์ ์์ด.
- ์ค๋ฒํค๋: ์ด๋ฒคํธ ๋ฐํ๊ณผ ์ฒ๋ฆฌ์ ์ฝ๊ฐ์ ์ฑ๋ฅ ์ค๋ฒํค๋๊ฐ ๋ฐ์ํ ์ ์์ด.
- ์์ ๋ณด์ฅ์ ์ด๋ ค์: ์ฌ๋ฌ ๋ฆฌ์ค๋ ๊ฐ์ ์คํ ์์๋ฅผ ์ ์ดํ๊ธฐ ์ด๋ ค์ธ ์ ์์ด.
- ํธ๋์ญ์ ๊ด๋ฆฌ ๋ณต์ก์ฑ: ํนํ ๋น๋๊ธฐ ์ฒ๋ฆฌ์ ํจ๊ป ์ฌ์ฉํ ๋ ํธ๋์ญ์ ๊ด๋ฆฌ๊ฐ ๋ณต์กํด์ง ์ ์์ด.
์ด๋ฒคํธ ๊ธฐ๋ฐ ์ํคํ ์ฒ๊ฐ ์ ํฉํ ์ํฉ
- ๋ถ๊ฐ ๊ธฐ๋ฅ์ด ๋ง์ ๊ฒฝ์ฐ: ํต์ฌ ๊ธฐ๋ฅ ์ธ์ ์๋ฆผ, ๋ก๊น , ๋ถ์ ๋ฑ ๋ค์ํ ๋ถ๊ฐ ๊ธฐ๋ฅ์ด ํ์ํ ๊ฒฝ์ฐ
- ๋น๋๊ธฐ ์ฒ๋ฆฌ๊ฐ ํ์ํ ๊ฒฝ์ฐ: ์ฌ์ฉ์ ์๋ต ์๊ฐ์ ์ํฅ์ ์ฃผ์ง ์๊ณ ๋ฐฑ๊ทธ๋ผ์ด๋์์ ์ฒ๋ฆฌํด์ผ ํ๋ ์์ ์ด ๋ง์ ๊ฒฝ์ฐ
- ํ์ฅ์ฑ์ด ์ค์ํ ๊ฒฝ์ฐ: ์์คํ ์ด ์ง์์ ์ผ๋ก ์๋ก์ด ๊ธฐ๋ฅ์ ์ถ๊ฐํ๋ฉฐ ํ์ฅ๋ ๊ฒ์ผ๋ก ์์๋๋ ๊ฒฝ์ฐ
- ๋ง์ดํฌ๋ก์๋น์ค ์ํคํ ์ฒ: ์๋น์ค ๊ฐ ํต์ ์ด ๋ง์ ๋ง์ดํฌ๋ก์๋น์ค ํ๊ฒฝ
์ ๋ค์ด์ด๊ทธ๋จ์์ ๋ณผ ์ ์๋ฏ์ด, ์ ํต์ ์ธ ์ํคํ ์ฒ์์๋ OrderService๊ฐ ์ด๋ฉ์ผ ๋ฐ์ก, ์ฌ๊ณ ๊ด๋ฆฌ, ๋ถ์ ๋ฑ์ ๋ชจ๋ ๋ถ๊ฐ ๊ธฐ๋ฅ์ ์ง์ ํธ์ถํด. ๋ฐ๋ฉด ์ด๋ฒคํธ ๊ธฐ๋ฐ ์ํคํ ์ฒ์์๋ OrderService๋ ๋จ์ํ ์ด๋ฒคํธ๋ง ๋ฐํํ๊ณ , ๊ฐ ๋ถ๊ฐ ๊ธฐ๋ฅ์ ์ด ์ด๋ฒคํธ๋ฅผ ๊ตฌ๋ ํ์ฌ ๋ ๋ฆฝ์ ์ผ๋ก ์ฒ๋ฆฌํด.
์ด๋ฒคํธ ๊ธฐ๋ฐ ์ํคํ ์ฒ๋ ํ์ฅ์ฑ๊ณผ ์ ์ง๋ณด์์ฑ์ ํฌ๊ฒ ํฅ์์ํฌ ์ ์์ด. ์๋ฅผ ๋ค์ด, ์ฌ๋ฅ๋ท๊ณผ ๊ฐ์ ํ๋ซํผ์์ ์๋ก์ด ๊ธฐ๋ฅ(์: ๊ฑฐ๋ ์๋ฃ ์ ๋ฆฌ์๋ ํฌ์ธํธ ์ง๊ธ)์ ์ถ๊ฐํ๋ ค๋ฉด, ๊ธฐ์กด ์ฝ๋๋ฅผ ์์ ํ์ง ์๊ณ ์๋ก์ด ์ด๋ฒคํธ ๋ฆฌ์ค๋๋ง ์ถ๊ฐํ๋ฉด ๋ผ. ์ด๋ ๊ธฐ์กด ์์คํ ์ ์์ ์ฑ์ ํด์น์ง ์์ผ๋ฉด์๋ ์๋ก์ด ๊ธฐ๋ฅ์ ์ฝ๊ฒ ์ถ๊ฐํ ์ ์๊ฒ ํด์ฃผ์ง. ๐
8. Spring Boot 3.x์์์ ์๋ก์ด ์ด๋ฒคํธ ๊ธฐ๋ฅ๋ค ๐
Spring Boot 3.x ๋ฒ์ ์์๋ ์ด๋ฒคํธ ์ฒ๋ฆฌ์ ๊ด๋ จ๋ ๋ช ๊ฐ์ง ์๋ก์ด ๊ธฐ๋ฅ๊ณผ ๊ฐ์ ์ฌํญ์ด ์ถ๊ฐ๋์ด. 2025๋ 3์ ๊ธฐ์ค์ผ๋ก ์ต์ Spring Boot ๋ฒ์ ์์ ํ์ฉํ ์ ์๋ ์ด๋ฒคํธ ๊ด๋ จ ๊ธฐ๋ฅ๋ค์ ์ดํด๋ณด์.
ObservationRegistry์ ํตํฉ๋ ์ด๋ฒคํธ ์์คํ
Spring Boot 3.x์์๋ Micrometer์ Observation API์ ์ด๋ฒคํธ ์์คํ ์ด ํตํฉ๋์ด, ์ด๋ฒคํธ ์ฒ๋ฆฌ์ ๋ํ ๋ชจ๋ํฐ๋ง๊ณผ ์ถ์ ์ด ๋์ฑ ๊ฐํ๋์ด.
@Configuration
public class EventObservationConfig {
@Bean
public ApplicationEventObservationConvention applicationEventObservationConvention() {
return new CustomApplicationEventObservationConvention();
}
}
public class CustomApplicationEventObservationConvention implements ApplicationEventObservationConvention {
@Override
public String getName() {
return "events";
}
@Override
public String getContextualName(ApplicationEventObservationContext context) {
return "event-" + context.getApplicationEvent().getClass().getSimpleName();
}
@Override
public KeyValues getLowCardinalityKeyValues(ApplicationEventObservationContext context) {
return KeyValues.of("event.type", context.getApplicationEvent().getClass().getSimpleName());
}
}
์ด๋ฅผ ํตํด ์ด๋ฒคํธ ๋ฐํ๊ณผ ์ฒ๋ฆฌ์ ๋ํ ์งํ๋ฅผ ์์งํ๊ณ , ๋ถ์ฐ ์ถ์ ์์คํ ๊ณผ ์ฐ๋ํ์ฌ ์ด๋ฒคํธ ํ๋ฆ์ ์ถ์ ํ ์ ์์ด.
ApplicationEventMulticaster์ ๊ฐ์
Spring 6.0๋ถํฐ๋ ApplicationEventMulticaster
์ ์ฑ๋ฅ๊ณผ ํ์ฅ์ฑ์ด ๊ฐ์ ๋์ด. ํนํ ๋๋์ ์ด๋ฒคํธ๋ฅผ ์ฒ๋ฆฌํ ๋ ๋ ํจ์จ์ ์ผ๋ก ๋์ํด.
@Configuration
public class EventMulticasterConfig {
@Bean
public ApplicationEventMulticaster applicationEventMulticaster() {
SimpleApplicationEventMulticaster eventMulticaster = new SimpleApplicationEventMulticaster();
// ์ด๋ฒคํธ ์ฒ๋ฆฌ๋ฅผ ์ํ ์ ์ฉ ์ค๋ ๋ ํ ์ค์
ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
taskExecutor.setCorePoolSize(10);
taskExecutor.setMaxPoolSize(50);
taskExecutor.setQueueCapacity(100);
taskExecutor.setThreadNamePrefix("event-");
taskExecutor.initialize();
eventMulticaster.setTaskExecutor(taskExecutor);
// ์ด๋ฒคํธ ์ฒ๋ฆฌ ์ค ์์ธ ๋ฐ์ ์ ์ฒ๋ฆฌํ ํธ๋ค๋ฌ ์ค์
eventMulticaster.setErrorHandler(throwable -> {
log.error("์ด๋ฒคํธ ์ฒ๋ฆฌ ์ค ์ค๋ฅ ๋ฐ์", throwable);
// ์ถ๊ฐ ์ค๋ฅ ์ฒ๋ฆฌ ๋ก์ง
});
return eventMulticaster;
}
}
Reactive ์ด๋ฒคํธ ์ง์ ๊ฐํ
Spring Boot 3.x์์๋ Reactive ์คํ์์์ ์ด๋ฒคํธ ์ฒ๋ฆฌ ์ง์์ด ๊ฐํ๋์ด. WebFlux์ ๊ฐ์ Reactive ํ๊ฒฝ์์๋ ์ด๋ฒคํธ ๊ธฐ๋ฐ ํ๋ก๊ทธ๋๋ฐ์ ํจ๊ณผ์ ์ผ๋ก ํ์ฉํ ์ ์์ด.
@Component
public class ReactiveEventListener {
private final Sinks.Many<ordercreatedevent> orderCreatedSink = Sinks.many().multicast().onBackpressureBuffer();
private final Flux<ordercreatedevent> orderCreatedFlux = orderCreatedSink.asFlux();
@EventListener
public void handleOrderCreatedEvent(OrderCreatedEvent event) {
orderCreatedSink.tryEmitNext(event);
}
public Flux<ordercreatedevent> getOrderCreatedEvents() {
return orderCreatedFlux;
}
}</ordercreatedevent></ordercreatedevent></ordercreatedevent>
์ด๋ฅผ ํตํด ์ด๋ฒคํธ๋ฅผ Reactive ์คํธ๋ฆผ์ผ๋ก ๋ณํํ๊ณ , ๋น๋๊ธฐ์ ์ผ๋ก ์ฒ๋ฆฌํ ์ ์์ด.
AOT(Ahead-of-Time) ์ปดํ์ผ ์ง์
Spring Boot 3.x์์ ๋์ ๋ AOT ์ปดํ์ผ์ ์ด๋ฒคํธ ๋ฆฌ์ค๋์ ์ฒ๋ฆฌ ์ฑ๋ฅ์ ํฅ์์์ผ. ํนํ GraalVM Native Image๋ฅผ ์ฌ์ฉํ ๋ ์ด๋ฒคํธ ์์คํ ์ด ํจ์จ์ ์ผ๋ก ๋์ํ๋๋ก ์ต์ ํ๋์ด.
@EventListener
public void handleOrderCreatedEvent(OrderCreatedEvent event) {
// AOT ์ปดํ์ผ ์ ์ต์ ํ๋๋ ์ด๋ฒคํธ ์ฒ๋ฆฌ ๋ก์ง
// ๋ฆฌํ๋ ์
์ ์ต์ํํ๊ณ ์ง์ ๋ฉ์๋ ํธ์ถ๋ก ๋ณํ๋จ
}
๊ตฌ์กฐํ๋ ๋ก๊น ๊ณผ ์ด๋ฒคํธ ํตํฉ
Spring Boot 3.x์์๋ ๊ตฌ์กฐํ๋ ๋ก๊น (Structured Logging)๊ณผ ์ด๋ฒคํธ ์์คํ ์ ํตํฉ์ด ๊ฐํ๋์ด. ์ด๋ฅผ ํตํด ์ด๋ฒคํธ ๋ฐํ๊ณผ ์ฒ๋ฆฌ์ ๋ํ ๋ก๊ทธ๋ฅผ ๋ ์ฒด๊ณ์ ์ผ๋ก ๊ด๋ฆฌํ ์ ์์ด.
@EventListener
public void handleOrderCreatedEvent(OrderCreatedEvent event) {
// ๊ตฌ์กฐํ๋ ๋ก๊น
log.atInfo()
.addKeyValue("eventType", "OrderCreated")
.addKeyValue("orderId", event.getOrder().getId())
.addKeyValue("customerId", event.getOrder().getCustomerId())
.addKeyValue("amount", event.getOrder().getTotalAmount())
.log("Order created event processed");
// ์ด๋ฒคํธ ์ฒ๋ฆฌ ๋ก์ง
}
์ด๋ฌํ Spring Boot 3.x์ ์๋ก์ด ๊ธฐ๋ฅ๋ค์ ์ด๋ฒคํธ ๊ธฐ๋ฐ ์์คํ ์ ์ฑ๋ฅ, ๋ชจ๋ํฐ๋ง, ๋๋ฒ๊น ์ ํฌ๊ฒ ํฅ์์์ผ. ํนํ ๋๊ท๋ชจ ์์คํ ์์ ์ด๋ฒคํธ ์ฒ๋ฆฌ์ ํจ์จ์ฑ๊ณผ ์์ ์ฑ์ ๋์ด๋ ๋ฐ ๋์์ด ๋ผ. ๐
9. ์ค๋ฌด์์ ์์ฃผ ์ฌ์ฉ๋๋ ์ด๋ฒคํธ ํจํด ๐จโ๐ป
์ด๋ฒคํธ ๊ธฐ๋ฐ ํ๋ก๊ทธ๋๋ฐ์ ์ค๋ฌด์์ ํจ๊ณผ์ ์ผ๋ก ํ์ฉํ๊ธฐ ์ํ ๋ช ๊ฐ์ง ํจํด๊ณผ ๋ฒ ์คํธ ํ๋ํฐ์ค๋ฅผ ์์๋ณด์.
๋๋ฉ์ธ ์ด๋ฒคํธ ํจํด
๋๋ฉ์ธ ์ด๋ฒคํธ(Domain Event)๋ ๋๋ฉ์ธ ๋ชจ๋ธ์์ ์ค์ํ ๋ณ๊ฒฝ์ด ๋ฐ์ํ์ ๋ ์ด๋ฅผ ํํํ๋ ์ด๋ฒคํธ์ผ. ์ด ํจํด์ DDD(Domain-Driven Design)์์ ๋ง์ด ์ฌ์ฉ๋ผ.
@Entity
public class Order {
@Id
@GeneratedValue
private Long id;
private String customerEmail;
private OrderStatus status;
@Transient
private List<domainevent> domainEvents = new ArrayList<>();
public void markAsPaid() {
this.status = OrderStatus.PAID;
// ๋๋ฉ์ธ ์ด๋ฒคํธ ๋ฑ๋ก
this.domainEvents.add(new OrderPaidEvent(this));
}
public List<domainevent> getDomainEvents() {
return Collections.unmodifiableList(domainEvents);
}
public void clearDomainEvents() {
this.domainEvents.clear();
}
}
@Service
public class OrderService {
private final OrderRepository orderRepository;
private final ApplicationEventPublisher eventPublisher;
@Transactional
public void payOrder(Long orderId) {
Order order = orderRepository.findById(orderId).orElseThrow();
order.markAsPaid();
orderRepository.save(order);
// ๋๋ฉ์ธ ์ด๋ฒคํธ ๋ฐํ
order.getDomainEvents().forEach(eventPublisher::publishEvent);
order.clearDomainEvents();
}
}</domainevent></domainevent>
์ด ํจํด์ ์ฅ์ ์ ๋๋ฉ์ธ ๋ชจ๋ธ์ด ์์ ์ ์ํ ๋ณํ์ ๋ํ ์ด๋ฒคํธ๋ฅผ ์ง์ ์์ฑํจ์ผ๋ก์จ, ๋๋ฉ์ธ ๋ก์ง๊ณผ ์ด๋ฒคํธ ๋ฐํ์ด ๋ฐ์ ํ๊ฒ ๊ฒฐํฉ๋๋ค๋ ๊ฑฐ์ผ. ์ด๋ฅผ ํตํด ๋๋ฉ์ธ ๋ชจ๋ธ์ ๋ณ๊ฒฝ์ด ๋๋ฝ๋์ง ์๊ณ ํญ์ ์ด๋ฒคํธ๋ก ํํ๋ ์ ์์ด.
์ด๋ฒคํธ ์์ฑ ํจํด
- ์ง์์ธ์ ์ฒ - ์ง์ ์ฌ์ฐ๊ถ ๋ณดํธ ๊ณ ์ง
์ง์ ์ฌ์ฐ๊ถ ๋ณดํธ ๊ณ ์ง
- ์ ์๊ถ ๋ฐ ์์ ๊ถ: ๋ณธ ์ปจํ ์ธ ๋ ์ฌ๋ฅ๋ท์ ๋ ์ AI ๊ธฐ์ ๋ก ์์ฑ๋์์ผ๋ฉฐ, ๋ํ๋ฏผ๊ตญ ์ ์๊ถ๋ฒ ๋ฐ ๊ตญ์ ์ ์๊ถ ํ์ฝ์ ์ํด ๋ณดํธ๋ฉ๋๋ค.
- AI ์์ฑ ์ปจํ ์ธ ์ ๋ฒ์ ์ง์: ๋ณธ AI ์์ฑ ์ปจํ ์ธ ๋ ์ฌ๋ฅ๋ท์ ์ง์ ์ฐฝ์๋ฌผ๋ก ์ธ์ ๋๋ฉฐ, ๊ด๋ จ ๋ฒ๊ท์ ๋ฐ๋ผ ์ ์๊ถ ๋ณดํธ๋ฅผ ๋ฐ์ต๋๋ค.
- ์ฌ์ฉ ์ ํ: ์ฌ๋ฅ๋ท์ ๋ช ์์ ์๋ฉด ๋์ ์์ด ๋ณธ ์ปจํ ์ธ ๋ฅผ ๋ณต์ , ์์ , ๋ฐฐํฌ, ๋๋ ์์ ์ ์ผ๋ก ํ์ฉํ๋ ํ์๋ ์๊ฒฉํ ๊ธ์ง๋ฉ๋๋ค.
- ๋ฐ์ดํฐ ์์ง ๊ธ์ง: ๋ณธ ์ปจํ ์ธ ์ ๋ํ ๋ฌด๋จ ์คํฌ๋ํ, ํฌ๋กค๋ง, ๋ฐ ์๋ํ๋ ๋ฐ์ดํฐ ์์ง์ ๋ฒ์ ์ ์ฌ์ ๋์์ด ๋ฉ๋๋ค.
- AI ํ์ต ์ ํ: ์ฌ๋ฅ๋ท์ AI ์์ฑ ์ปจํ ์ธ ๋ฅผ ํ AI ๋ชจ๋ธ ํ์ต์ ๋ฌด๋จ ์ฌ์ฉํ๋ ํ์๋ ๊ธ์ง๋๋ฉฐ, ์ด๋ ์ง์ ์ฌ์ฐ๊ถ ์นจํด๋ก ๊ฐ์ฃผ๋ฉ๋๋ค.
์ฌ๋ฅ๋ท์ ์ต์ AI ๊ธฐ์ ๊ณผ ๋ฒ๋ฅ ์ ๊ธฐ๋ฐํ์ฌ ์์ฌ์ ์ง์ ์ฌ์ฐ๊ถ์ ์ ๊ทน์ ์ผ๋ก ๋ณดํธํ๋ฉฐ,
๋ฌด๋จ ์ฌ์ฉ ๋ฐ ์นจํด ํ์์ ๋ํด ๋ฒ์ ๋์์ ํ ๊ถ๋ฆฌ๋ฅผ ๋ณด์ ํฉ๋๋ค.
ยฉ 2025 ์ฌ๋ฅ๋ท | All rights reserved.
๋๊ธ 0๊ฐ