java
CHAPTER 77 / 99
읽기 약 2분
FUNCTION
비동기 처리: @Async + 이벤트
핵심 개념
@Async·@EventListener·ApplicationEvent — 이메일 발송 비동기·이벤트 기반 아키텍처.
본문
@Async 비동기 실행
@Configuration
@EnableAsync
public class AsyncConfig {
@Bean(name = "taskExecutor")
public Executor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(10);
executor.setMaxPoolSize(50);
executor.setQueueCapacity(100);
executor.setThreadNamePrefix("async-");
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
return executor;
}
}
@Service
@RequiredArgsConstructor
public class EmailService {
@Async("taskExecutor")
public CompletableFuture<Void> sendWelcomeEmail(String to) {
log.info("이메일 발송 시작: {}", to);
// ... SMTP 호출 (느림)
Thread.sleep(2000);
log.info("이메일 발송 완료");
return CompletableFuture.completedFuture(null);
}
}
@Service
@RequiredArgsConstructor
public class UserService {
private final EmailService emailService;
private final UserRepository userRepo;
public User signup(SignupDto dto) {
User user = userRepo.save(new User(dto));
// 이메일 발송이 회원가입 응답을 막지 않음
emailService.sendWelcomeEmail(user.getEmail());
return user;
}
}이벤트 기반 — ApplicationEvent
// 이벤트 정의
public record OrderPlacedEvent(Long orderId, String userEmail, BigDecimal total) {}
// 발행
@Service
@RequiredArgsConstructor
public class OrderService {
private final ApplicationEventPublisher publisher;
private final OrderRepository orderRepo;
@Transactional
public Order placeOrder(OrderRequest req) {
Order order = orderRepo.save(new Order(req));
// 이벤트 발행 — 트랜잭션 커밋 후 처리됨
publisher.publishEvent(new OrderPlacedEvent(
order.getId(), order.getUserEmail(), order.getTotal()
));
return order;
}
}
// 리스너
@Component
@Slf4j
@RequiredArgsConstructor
public class OrderEventListener {
private final EmailService emailService;
private final NotificationService notificationService;
private final InventoryService inventoryService;
@Async
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
public void handleOrderPlaced(OrderPlacedEvent event) {
log.info("주문 후처리: {}", event.orderId());
emailService.sendOrderConfirmation(event.userEmail(), event.orderId());
notificationService.send(event.userEmail(), "주문이 접수되었습니다");
inventoryService.decrementForOrder(event.orderId());
}
}CompletableFuture 체이닝
public CompletableFuture<UserDashboard> getDashboard(Long userId) {
CompletableFuture<User> userFuture = userService.findByIdAsync(userId);
CompletableFuture<List<Order>> ordersFuture = orderService.findByUserAsync(userId);
CompletableFuture<List<Notification>> notifsFuture = notifService.findByUserAsync(userId);
return CompletableFuture.allOf(userFuture, ordersFuture, notifsFuture)
.thenApply(v -> new UserDashboard(
userFuture.join(),
ordersFuture.join(),
notifsFuture.join()
));
}다음 챕터
CH.9 "파일 업로드와 S3" — 멀티파트와 클라우드 스토리지.
AI 프롬프트
🤖 AI에게 잘 물어보는 법 — 모델·전략별 프롬프트
Claude
무료: Sonnet 4.6 / Pro $20/mo: Opus 4.6
내 Spring 코드의 비동기 처리 부분을 분석해서 스레드 풀·rejection 정책와 개선 우선순위를 알려줘.
ChatGPT
무료: GPT-5.5 / Plus $20/mo: GPT-5.5 Pro
비동기 처리 vs 다른 패턴 비교를 실전 사례 5개로 보여주고 @Async vs Kafka 트레이드오프를 알려줘.
Gemini
무료: 2.5 Flash / Pro $19.99/mo: 3.1 Pro
내 코드베이스 전체를 분석해서 비동기 처리 관련 동기 호출이 막는 위치를 보고해줘.
Grok
무료: Grok 4.1 / SuperGrok $30/mo
2026년 한국 기업의 비동기 처리 채택률과 한국 SaaS의 비동기 처리 패턴를 솔직히 알려줘.
⭐ 이것만 기억하세요
비동기 처리: @Async + 이벤트는 이 3가지만 확실히 잡으세요
1.@Async는 별도 스레드 풀 권장 — 기본 스레드는 무한 생성될 수 있어 OOM
2.TransactionalEventListener로 트랜잭션 커밋 후 처리 — 롤백 시 이벤트 무시
3.다음 챕터 CH.9에서 파일 업로드 — S3 + pre-signed URL
공유하기
진행도 77 / 99