목록전체 글 (56)
josolha
0. 출발점 - "트랜잭션이면 되겠지?"처음엔 서비스 메서드에 @Transactional을 붙이면 주문 생성, 재고 차감, 쿠폰 발급이 하나의 작업 단위로 묶이니까 실패 시 롤백도 되고 안전할 거라 생각했다.@Servicepublic class OrderService { @Transactional public void createOrder(Long productId, Long userId) { Product product = productRepository.findById(productId); if (product.getStock() 하지만 곧 깨달았다.트랜잭션은 내 작업의 원자성만 보장한다. 동시에 시작되는 다른 요청을 막아주지는 않는다.Lost Update (갱신 ..
1. 아주 옛날: 애플리케이션이 DB에 바로 붙던 시대초창기 시스템 구조는 단순했다.프로그램 A, B, C가 각각 DB에 직접 접속했고, 여러 프로그램이 동시에 쿼리를 날렸다.이때도 하나의 작업을 여러 쿼리로 처리해야 했기 때문에 DB는 트랜잭션을 제공했다.BEGIN TRANSACTION;UPDATE accounts SET balance = balance - 100 WHERE id = 1;UPDATE accounts SET balance = balance + 100 WHERE id = 2;COMMIT; 여러 쿼리를 하나의 작업 단위로 묶기 위한 장치가 트랜잭션이다. (원자성) 하지만 중요한 점이 있었다.트랜잭션이 있어도 동시 실행 자체는 기본으로 발생했고, 동시에 실행되는 트랜잭션들은 서로 영향을 줄 수..
JDK vs CGLIB, self-invocation, final, 인터페이스 주입까지 한 번에 이해하기 스프링에서 @Transactional, @Async, @Cacheable 같은 기능은 마치 마법처럼 보인다.하지만 이 기능들의 실체는 프록시(Proxy) 다. 이 글에서는 프록시가 무엇인지왜 JDK 프록시와 CGLIB 프록시가 나뉘는지왜 final, private, 내부 호출에서 AOP가 깨지는지왜 빈 주입은 인터페이스 타입이 권장되는지그리고 실제로 JDK / CGLIB 프록시를 직접 만들어보며스프링 AOP의 동작 원리를 정리한다. 1. 프록시란 무엇인가? 프록시는 대상 객체를 대신해서 호출을 가로채는 객체다.Client → Proxy → (부가기능) → Target 스프링에서 프록시가 하는 대표적..
UserCoupon Dirty Checking 실패 이슈 해결 과정문제 상황주문 생성 프로세스에서 UserCoupon의 used_at 필드가 DB에 업데이트되지 않는 문제가 발생했다:쿠폰 사용 처리 시 userCoupon.use() 호출로 메모리상에서는 usedAt 필드가 변경됨하지만 트랜잭션 커밋 후에도 DB에는 used_at 컬럼이 여전히 NULL로 남아있음Hibernate 로그에서도 UPDATE 쿼리가 전혀 생성되지 않음로그 증거[2025-12-18 20:28:01] 쿠폰 사용 처리 - userCouponId=2e6ba6fd-114d-4cdb-aa46-67357c49d304, usedAt=null[2025-12-18 20:28:01] 쿠폰 사용 처리 후 - userCouponId=2e6ba6fd-1..