JPA3 포인트는 왜 안 쌓였을까 — @TransactionalEventListener 함정 파헤치기 들어가며주문 서비스를 개발하면서 이런 코드를 마주치거나 직접 작성한 경험이 있을 것이다.@TransactionalEventListener(phase = AFTER_COMMIT)void onOrderCreated(OrderCreatedEvent event) { pointRepository.save(new Point(event.userId(), event.amount()));} 커밋이 끝난 뒤에 포인트를 적립한다. 트랜잭션이 성공했을 때만 실행되니까 언뜻 완벽해 보인다. 정말 그런지 테스트로 확인해봤다 @Test void AFTER_COMMIT_시점에_Spring_TX_플래그는_아직_true지만_JPA_세션은_이미_커밋됐다() { orderService.createOrder("user-tx.. 2026. 3. 27. [Java / Spring Boot] 쿠폰 사용에 대한 처리는 어떤 락이 적절할까 - 4편 3편 마지막에 이런 질문을 던졌다.(3편: 비관적 락 vs 낙관적 락, 선택 기준은 무엇이었나 - 3편)유니크 키는 어디에 쓰는 걸까?쿠폰 로직을 구현하면서 답을 찾았다.재고 vs 쿠폰: 충돌 패턴이 다르다재고는 여러 사람이 하나를 두고 경쟁하는 문제다. 쿠폰은 다르다.재고 차감: 불특정 다수가 동시에 같은 상품에 달려듦쿠폰 발급: 동일 사용자의 중복 요청이 문제쿠폰 사용: 같은 쿠폰으로 동시에 두 번 주문 시도충돌 주체가 다르니까 전략도 달라야 한다.쿠폰 중복 발급: DB 유니크 키로 막는다애플리케이션 코드에서 이렇게 체크하면 충분할 것 같다.@Transactionalpublic void issue(Long userId, Long couponId) { boolean exists = us.. 2026. 3. 6. [Java / Spring Boot] 스투시 반팔티를 10명이 동시에 주문했을 때 발생한 일-2편 배경재고가 1개뿐인 상품에 10명이 동시에 주문을 넣었다.결과는 10명 전부 주문 성공이었다. 에러도 없이. 재고는 -9가 됐다. 음.....?뭐가 문제인지 보기 전엔 몰랐다재고 차감 코드는 단순해 보인다.@Transactionalpublic void order(Long optionId, int quantity) { Stock stock = stockRepository.findByOptionId(optionId); // 1. 재고 읽기 if (stock.getQuantity() @Transactional도 붙어있고, 재고 확인도 하고 있으니 괜찮은 거 아닌가?문제는 2번 재고 확인 시점에서 생긴다.왜 이런 일이 생기나: Lost Update10개 트랜잭션이 동시에 실행될 때 실제로 어떤 일이 .. 2026. 3. 6. 이전 1 다음 반응형