코디잉
8주차 - WIL (Weekly I Learned) 본문
🧐 8주차 과제
1. Application Event: 이벤트를 활용하여 트랜잭션과 관심사를 분리하기
2. Asynchronous Design: 대기열 기능에 대해 Redis 기반의 설계를 진행
3. feedback - RedisReservationRankingManager 상수값 외부 설정으로 빼기
4. feedback - redis 분산락 TTL 충분히 길게 줘서 중복실행 확률 낮추기
🙋♀️ 고민했던 부분
① Event 활용하여 트랜잭션과 관심사 분리
◾ 기존에는 모든 로직이 ReservationService.pay() 내에 포함되어 있어, 핵심로직과 부가로직이 혼재된 구조였다.
◽ 핵심 로직: 대기열 토큰 검증, 포인트 사용, 좌석 상태 변경
◽ 부가 로직: 예매율 랭킹 갱신, 대기열 토큰 완료 처리, 결제 내역 저장
◾ 위 부가로직은 예외가 발생하더라도 핵심 로직에는 영향을 주지 않아야 했고, 성능과 유지보수 측면에서도 이벤트 기반 처리가 적절하다고 판단했다.
◾ 특히 결제 실패 이력 저장의 경우, 핵심 트랜잭션이 롤백되더라도 반드시 저장되어야 하므로 @Transactional(propagation = REQUIRES_NEW) 로 별도 트랜잭션으로 분리해 처리함.
② Redis로 대기열 기능 구현
◾ Redis ZSET 사용 결정. 무제한 입장을 막기 위해 allowedLimit(ex. 1000명) 설정
◾ 스케줄러 처리 방식
◽ ALLOWED 상태에서 TTL이 지난 토큰은 TIMEOUT으로 변경
◽ WAITING 상태 중 상위 토큰들을 ALLOWED로 전환 (현재 허용 인원 기준)
➡️ 한 번에 너무 많은 대기열을 조회하지 않기 위해 Redis에서 limit × 2 정도만 추출 후 상태 체크
✅ Keep (잘한 점 / 유지할 점)
- @TransactionalEventListener 를 활용해 AFTER_COMMIT 시점에만 부가로직 수행
- 이벤트 리스너를 비동기 처리(@Async)하여 주 쓰레드의 성능 저하 방지
- 예외 발생 시 REQUIRES_NEW 로 실패 이력 저장 → 핵심 트랜잭션의 영향을 받지 않도록 설계
- 예매율 랭킹 / 토큰 상태 변경 / 결제 성공 내역 저장을 각각 이벤트 리스너로 분리하여 단일 책임 원칙 지킴
- 대기열 진입 로직을 별도의 Facade 서비스로 구성하여 토큰 발급 → 대기열 진입 흐름을 하나의 API로 처리
- 토큰 상태값은 RDB에서 관리하고, 큐 순번은 Redis에서 분리 관리하여 상태/순번 책임 분리
❗️Problem (문제점) &💡Try (해결을 위한 시도)
- JPA dirty checking 기반의 토큰 상태 변경(엔티티 변경만)도 이벤트로 분리하려 했지만, 트랜잭션 범위를 벗어나면 dirty checking이 적용되지 않아 다시 save 호출이 필요함 → 이를 고려해 save 호출을 명시적으로 추가함
- 처음에는 ReservationService가 QueueService를 호출하는 구조로 구현했으나 서비스 간 의존성 문제 발생 가능성 있어서 → Facade 패턴 도입
💬 이번 주 알게 된 것들
- @TransactionalEventListener 의 phase 종류 (BEFORE_COMMIT, AFTER_COMMIT, AFTER_ROLLBACK) 와 각각의 동작 시점
- 서비스 간 호출이 필요할 경우 Facade 계층 사용
'PROJECT > 항해플러스 Lite 백엔드' 카테고리의 다른 글
9주차 - WIL (Weekly I Learned) (0) | 2025.07.20 |
---|---|
7주차 - WIL (Weekly I Learned) (0) | 2025.07.06 |
6주차 - WIL (Weekly I Learned) (0) | 2025.07.01 |
5주차 - WIL (Weekly I Learned) (0) | 2025.06.21 |
4주차 - WIL (Weekly I Learned) (1) | 2025.06.15 |