2024. 4. 22. 18:57γBackend/Spring
κ°μ
νμ¬ μ§μΈ 3λΆκ³Ό ν¨κ» λμμ± λ¬Έμ μ λν κ²½νμ μκΈ° μν΄ ν°μΌν μλΉμ€λ₯Ό κ°λ°νλ νλ‘μ νΈλ₯Ό μ§ννκ³ μμ΅λλ€.
ν΄λΉ νλ‘μ νΈλ₯Ό μμνλ©΄μ λ¬μ±νκ³ μ νλ ν΅μ¬ λͺ©ν μ€ νλλ, λμμ± λ¬Έμ λ₯Ό μ§μ κ²ͺμ΄λ³΄κ³ μ΄λ₯Ό ν΄κ²°νλ λ°©λ²λ‘ μ λν νμ΅κ³Ό μ΄λ₯Ό μ€μ λ‘ ν΄κ²°ν΄λ³Έ κ²½νμ κ°λ κ²μ΄μμ΅λλ€.
μ ν¬ νλ‘μ νΈ λ΄μμ λμμ± λ¬Έμ κ° λ°μνλ μ§μ μ λ°λ‘ ν°μΌν μ μννλ λ‘μ§μ λλ€. μλ² μμλ νμ λ μμ ν°μΌμ΄ μ‘΄μ¬νκ² λλλ°, μ΄μ λν΄ ν°μΌλ³΄λ€ λ λ§μ κ³ κ°μ΄ λμλ€λ°μ μΌλ‘ ν°μΌμ νλνλ €λ κ³Όμ μμ λ°μ΄ν° μ ν©μ± λ¬Έμ κ° λ°μνλ κ²μ΄μ£ . μ ν¬ νμ μ΄ λ¬Έμ λ₯Ό λ€μν λ°©λ²μΌλ‘ μ κ·Όν΄λ³΄κΈ° μν΄ κ°μ λΉκ΄μ λ½(P-lock), λκ΄μ λ½(O-lock), λΆμ° λ½(D-lock)μ μ΄μ©νμ¬ λ¬Έμ λ₯Ό ν΄κ²°ν΄λ³΄λ κ³Όμ μ κ±°μ³€μ΅λλ€.
κ·Έλ¦¬κ³ μ΅μ’ μ μΌλ‘ μ ν¬ μν©μ κ°μ₯ λΆν©νλ νλμ λ°©λ²λ‘ μ νμ νκΈ° μν ν μ€νΈ λ° κ²°κ³Ό λΆμ λ¨κ³λ₯Ό μλκ³ μμ΅λλ€.
μ ν¬λ μλνλ ν μ€νΈλ₯Ό μνκ³ , μ΄λ₯Ό μν΄ μΆκ°μ μΈ κ°λ³ μ½λ μμ μμ΄ μ½λ 컨ν μ€νΈ μΈλΆμμ μ΄λ€ λ°©λ²λ‘ μ μ¬μ©νμ¬ λ½μ μνν μ§ κ²°μ νκ³ , μ£Όμ ν΄μ£ΌκΈ°λ₯Ό μνμ΅λλ€.
μ΄λ¬ν μꡬμ¬νμ κ²°κ΅ κ°μ²΄μ§ν₯ μ€κ³ μμΉμ ‘κ°λ°©-νμ μμΉ’μ ν΄λΉλλ λ¬Έμ μ΄κ³ , λ°λΌμ νμ₯ κ°λ₯ν κ°μ²΄μ§ν₯ μ€κ³κ° νμν μν©μ΄μμ΅λλ€. μ΄λ₯Ό μν΄ μΆκ°μ μΈ μν€ν μ³ λ―Έν μ μ§ννκ³ κ²°μ λ μν€ν μ³μ λ§κ² κ°μ λ‘μ§μ ꡬμ±νμμ΅λλ€.
νμ§λ§ μ΄ κ³Όμ μμ λ€μν λ¬Έμ λ€μ΄ λ°μνμ¬ μ΅μ’ μ μΌλ‘λ μ΄κΈ° λ Όμνλ μν€ν μ³μλ μ¬λ λ€λ₯Έ λ°©μμΌλ‘ κ°λ°μ΄ μ§νλμμ΅λλ€.
ν΄λΉ κΈμμλ κ°λ° μ€μ κ²ͺμ λ€μν λ¬Έμ μ λ€κ³Ό λλΆμ΄ λ¬Έμ κ° μ λ°μνλμ§μ λν κ³ μ°°μ μ§ννκ³ , μ΅μ’ μ μΌλ‘λ λ¬Έμ μν©λ€μ κ³ λ €ν μλ‘μ΄ μν€ν μ³ κ°μ μμ λμΆν΄λ΄κ³ μ ν©λλ€.
λ€μ΄κ°κΈ° μμ
κΈ λ΄μ©μ λν΄ μ½κ² μ΄ν΄νκΈ° μν΄μλ λ½μ ꡬννλ λ°©λ²λ‘ λ€μ μ’ λ₯μ κ·Έ νΉμ§μ μκ³ μλ νΈμ΄ μ’μ΅λλ€.
μ΄λ₯Ό μν΄ κ° λ°©λ²λ‘ μ λν΄ κ°λ¨ν λ€λ£¨λ©΄ μλμ κ°μ΅λλ€.
- O-lock
- λκ΄μ λ½μ΄λΌκ³ μΌμ»¬μ΄μ§λ λ°©μμ λλ€.
- ν
μ΄λΈ λ΄ λ²μ μΉΌλΌμ μΆκ°νκ³ ν΄λΉ λ μ½λκ° μμ λ λλ§λ€ μ΄ κ°μ ν¨κ» μμ νλ λ°©μμ
λλ€.
- λ²μ μΉΌλΌμ κ²½μ°, λ¨μ Integer νμμ μ¬μ©ν μλ, νΉμ μμ λ μκ°μ κΈ°λ‘νλ Timestamp νμμ μ¬μ©ν μλ μμ΅λλ€.
- μΌλ°μ μΌλ‘ JPAμμ μ 곡νλ @Version μ΄λ Έν μ΄μ μ ν΅ν΄ κ°λ¨ν μ€μ ν μ μμ΅λλ€.
- λ‘μ§ λ΄μμ μ
λ°μ΄νΈλ₯Ό μν΄ μ½μ΄μ¨ κ°μ DBλ‘ λ€μ λκΈ°ννλ κ³Όμ μμ μ΄κΈ° μ½μ΄μλ λ²μ κ°κ³Ό λκΈ°ννλ μκ°μ λ²μ κ°μ΄ λ€λ₯Ό κ²½μ° λ°μνλ μμΈλ₯Ό λ°λ‘ μ²λ¦¬νλ μμΌλ‘ ꡬνν©λλ€.
- μ€ν 쿼리 μμ
- UPDATE users SET name = "Jamal", version = version + 1 WHERE id = 10 and version = ?;
- μ ν리μΌμ΄μ λ 벨μμ λμμ±μ λν΄ λ€λ£¨μ§ μκ³ , μΌλ¨ 쿼리λ₯Ό λ λ¦° λ€ μμΈκ° λ°μν κ²½μ° κ·Έμ μμΌ λμμ± νΈλ€λ§μ νλ λ°©μμΌλ‘ μ‘°ν μ μ΄λ ν λ½λ μ¬μ©νμ§ μκΈ° λλ¬Έμ μ±λ₯ μ νκ° μμ§λ§ λ§μ½ λμμ± λ¬Έμ κ° λ°μν μν©μ΄λΌλ©΄ λ€νΈμν¬ λΉμ© + μμΈ μ²λ¦¬ λΉμ© + μ¬μμ² λΉμ©μ΄ μΆκ°λμ΄ μ±λ₯μ μΌλ‘ ν° μ νλ₯Ό κ²ͺμ μ μμ΅λλ€.
- P-lock
- λΉκ΄μ λ½μ΄λΌκ³ μΌμ»¬μ΄μ§λ λ°©μμ λλ€.
- λ°μ΄ν°λ₯Ό μ½μ΄μ¬ λ SELECT ~ FOR UPDATE λ¬Έμ μ΄μ©νμ¬, νκ² λ μ½λμ λ°°νλ½μ κ±°λ λ°©μμΌλ‘ λμν©λλ€.
- μΌλ°μ μΈ νΈλμμ 격리μμ€(READ COMMITED, REPEATABLE READ)μμλ MVCCλ‘ μΈν΄ λ€λ₯Έ νΈλμμ μμ λ°°νλ½μ΄ κ±Έλ¦° λ μ½λλ μ½μ μ μμΌλ(λ¨μ μ‘°ν), λ°μ΄ν° μμ μ΄λ νΉμ λ€λ₯Έ SELECT ~ FOR UPDATE λ¬Έμ ν΅ν΄μλ λμ μ κ·Όμ΄ λΆκ°λ₯ν΄μ§λλ€. μ΄λ‘ μΈν΄ μ±λ₯μ μ νκ° λ°μν μ μμ΅λλ€.
- D-lock
- λΆμ° λ½μ΄λΌκ³ μΌμ»¬μ΄μ§λ λ°©μμ λλ€.
- ν΄λΉ λ°©μμ RDB, μλ² μΈμ€ν΄μ€, μ€λ λ λ±μ μ’ μμ μ΄μ§ μκ³ μ€λ‘μ§ λ½μ μ μ₯ν μΈλΆ μ μ₯μμλ§ μμ‘΄μ ν©λλ€. μ΄λ‘ μΈν΄ μλ², RDB λ±μ μ€μΌμΌμμμΌλ‘ μΈν λΆμ° νκ²½μμλ μμ λ‘κ² νμ©μ΄ κ°λ₯ν©λλ€.
- μΌλ°μ μΌλ‘ μΈλ©λͺ¨λ¦¬ DBμΈ λ λμ€λ₯Ό λ½ μ μ₯μλ‘ νμ©ν©λλ€.
κΈ°μ‘΄ μν€ν μ³
μ UMLμ μ΄κΈ° λ―Έν μ ν΅ν΄ ꡬμ±ν μΌλΆ μν€ν μ³μ λν λ΄μ©μ λλ€.
CreatePurchaseUseCase ν΄λμ€μ μ€μ§μ μΈ ν°μΌ ꡬ맀μ λν λΉμ¦λμ€ λ‘μ§μ΄ μμ±λμ΄ μλλ°, μ΄ λ λμμ± λ¬Έμ κ° λ°μν μ μλ μ μ - ν°μΌ λ°°μ μμ μ λμμ±μ λ€λ£¨λ TicketConcurrencyServiceμ μμνμ¬ μννλ κ΅¬μ‘°λ‘ μμ±λμ΄ μμ΅λλ€.
μ ꡬ쑰μμ νΈλμμ μμ μ§μ κΉμ§ ν¨κ» νμνλ©΄ μλμ κ°μ΅λλ€.
λ¬Έμ λ°μ
μ μν€ν μ³λ₯Ό κΈ°λ°μΌλ‘ TicketConcurrencyService μΈν°νμ΄μ€μ ꡬν체λ₯Ό νμ λΆλ€κ»μ νλμ© λ΄λΉνμ¬ κ΅¬νν΄μ£Όμ ¨μ΅λλ€.
κ·Έλ¦¬κ³ μ€μ ν μ€νΈλ₯Ό μ§νν΄λ³΄λ, μ΄ μ€ DBμ μ’ μλλ λ°©μμΌλ‘ ꡬνλλ λΉκ΄μ λ½μ μ μΈν λλ¨Έμ§ λ λ°©λ²μ΄ μ ꡬ쑰μμ λ¬Έμ λ₯Ό λ°μμμΌ°μ΅λλ€.
μ λ μ΄ λ¬Έμ λ₯Ό ν΄κ²°νκΈ° μν΄ ν μ€νΈ μ€ν μ λ°μνλ λ‘κ·Έλ€μ μΌμΌμ΄ λ°λΌκ°λ©° λλ²κΉ μ μ§ννμκ³ μ΅μ’ μ μΌλ‘λ λ¬Έμ κ° λ°μνκ³ μλ λΆλΆλ€μ νμΈν μ μμμ΅λλ€.
μ€μ ꡬνμ λν κ°λ΅ν μ€λͺ κ³Ό λ°μνλ λ¬Έμ λ₯Ό μ€λͺ νλ©΄ μλμ κ°μ΅λλ€.
- O-lock (λκ΄μ λ½)
- ꡬν
- CreatePurchaseUseCaseμμ μ νλ νΈλμμ μ TicketOptimisticLockConcurrencyService.assignPurchaseToTicket λ©μλκ° μ°Έμ¬νλ λ°©μμ λλ€.
- μ΄ λ λκ΄μ λ½ λ°©μμμ λ°μνλ μμΈλ₯Ό assignPurchaseToTicket λ©μλ λ΄μμ try - catchλ‘ μ‘μλ΄κ³ @Retryable μ΄λ Έν μ΄μ μ ν΅ν΄ FailOverλ₯Ό μννλ λ°©μμΌλ‘ ꡬνμ μ§ννμ΅λλ€.
@Retryable( retryFor = OptimisticLockingFailureException.class, backoff = @Backoff(delay = 100), maxAttempts = 100 ) public void assignPurchaseToTicket(UUID ticketingId, UUID purchaseId, int ticketCount) throws ConcurrencyFailureException { var purchase = purchaseCrudService.findById(purchaseId); var tickets = ticketRepository.findByTicketingIdAndPurchaseIsNullOrderByIdWithOptimisticLock( ticketingId, Limit.of(ticketCount)); if (tickets.size() < ticketCount) { throw new NotEnoughTicketException(); } tickets.forEach(ticket -> { ticket.setPurchase(purchase); }); ticketRepository.flush(); }
- λ¬Έμ
- 첫λ²μ§Έλ‘, REPEATABLE READ κ³ λ¦½ λ 벨μμλ μ μ λμνμ§ μμ΅λλ€. ꡬ체μ μΌλ‘λ ν΄λΉ λ©μλμμ FailOverκ° λλλΌλ μμ νΈλμμ μ΄ λ‘€λ°±λμ§ μκΈ° λλ¬Έμ μ²μ μ½μλ λ²μ μ΄ λ³κ²½λμ§ μλλ°, κ·Έλ κΈ° λλ¬Έμ νλ² μ€ν¨ν κ²½μ° λͺ¨λ μ¬μλ νμκ° μ λΆ μλͺ¨λλλΌλ κ³μν΄μ OptimisticLockExceptionμ΄ λ°μν©λλ€.
- λλ²μ§Έλ‘, OptimisticLockExceptionμ RuntimeExceptionμ μΌμ’ μ΄κΈ° λλ¬Έμ νμ¬ νΈλμμ μ rollbackOnly λ§νΉμ΄ λλλ°, μ΄ κ²½μ°μ μ μ 컀λ°μ΄ λλ μν©μμλ λͺ μμ μΈ λ‘€λ°±μ΄ μ€νλ©λλ€. λ°λΌμ νλ²μ΄λΌλ μ€ν¨ν κ²½μ° μ¬μλμλ κ΄λ ¨μμ΄ μ΅μ’ μ μΈ κ²°κ³Όλ νμ Rollbackμ΄ λλ λ¬Έμ κ° μμμ΅λλ€.
- ꡬν
- D-lock (λΆμ° λ½)
- ꡬν
- CreatePurchaseUseCaseμμ μ νλ νΈλμμ μ TicketDistributedLockConcurrencyService.assignPurchaseToTicket λ©μλκ° μ°Έμ¬νλ λ°©μμ λλ€.
- μ΄ λ λκ΄μ λ½ λ°©μμμ λ°μνλ μμΈλ₯Ό assignPurchaseToTicket λ©μλ λ΄μμ try - catchλ‘ μ‘μλ΄κ³ @Retryable μ΄λ Έν μ΄μ μ ν΅ν΄ FailOverλ₯Ό μννλ λ°©μμΌλ‘ ꡬνμ μ§ννμ΅λλ€.
public void assignPurchaseToTicket(UUID ticketingId, UUID purchaseId, int ticketCount) { String lockName = ticketingId.toString(); RLock rLock = redissonClient.getLock(lockName); final long waitTime = 10L; final long leaseTime = 3L; TimeUnit timeUnit = TimeUnit.SECONDS; try { boolean available = rLock.tryLock(waitTime, leaseTime, timeUnit); if (!available) { throw new TicketConcurrencyException(); } var purchase = purchaseCrudService.findById(purchaseId); var tickets = ticketRepository.findByTicketingIdAndPurchaseIsNullOrderById( ticketingId, Limit.of(ticketCount)); if (tickets.size() < ticketCount) { throw new NotEnoughTicketException(); } tickets.forEach(ticket -> { ticket.setPurchase(purchase); }); } catch (InterruptedException e) { throw new TicketConcurrencyException(); } finally { TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() { public void afterCompletion(int status) { rLock.unlock(); } }); } }
- λ¬Έμ
- μ΄λ²μλ REPEATABLE READ κ³ λ¦½ λ λ²¨μ΄ λ¬Έμ κ° λμμ΅λλ€. ꡬ체μ μΌλ‘ μμ assignPurchaseToTicket λ©μλλ₯Ό μ§λ ¬ννλ€κ³ ν΄λ μ΄λ―Έ κ·Έ μμ λ©μλμΈ CreatePurchaseUseCase.createPurchaseμμ μ΄λ―Έ μ‘°νκ° μΌμ΄λκΈ° λλ¬Έμ μ§λ ¬ν μ§μ μ μ€λ μ·μ΄ μμ±λκ² λκ³ μ΄λ‘ μΈν΄ μ§λ ¬νλ λ©μλ λ΄μμλ λͺ¨λ λμΌν λ²μ μ ν°μΌλ€μ μ‘°ννκ² λ©λλ€. λ°λΌμ λμμ± λ¬Έμ κ° ν΄κ²°λμ§ μμ΅λλ€.
- ꡬν
λ¬Έμ ν΄κ²° λ°©μ
λκ΄μ λ½ λ°©μμμ λ°μνλ λ¬Έμ λ κ²°κ΅ Retryableμ ν΅ν΄ μμ ν μλ‘μ΄ νΈλμμ μ μμμμΌμΌ ν΄κ²°ν μ μμ΅λλ€. λν 곡μ λ½ λ°©μμμ λ°μνλ λ¬Έμ λ μ΅μμ νΈλμμ μμ 첫λ²μ§Έ μ‘°νκ° λ°μνκΈ° μ μ 곡μ λ½ λ‘μ§μ κ°μΈμΌ ν΄κ²°μ΄ κ°λ₯ν©λλ€.
μ¦, λ λ°©μ λͺ¨λ μ΅μμ νΈλμμ μ κ΄λ ¨ λ‘μ§μ΄ λμνλλ‘ μ½λλ₯Ό μμ νλ©΄ ν΄κ²°μ΄ λλ λ¬Έμ μΈ κ²μ λλ€.
λ°λΌμ λ‘μ§μ ν΅μΌμ±μ μν΄ λ½μ λν νμ₯ ν¬μΈνΈλ₯Ό TicketConcurrencyService λ 벨μμ μ‘λ κ²μ΄ μλ, CreatePurchaseUseCase λ 벨μμ μ‘λ λ°©ν₯μΌλ‘ μ½λλ₯Ό μμ νλ κ²°λ‘ μ λμ΅λλ€.
λ¬Έμ ν΄κ²° λ°©μ μ¬ν
κ°μ₯ λ¨μν ν΄κ²°μ± μ μ무λλ CreatePurchaseUseCaseλ₯Ό ꡬ체 ν΄λμ€κ° μλ μΈν°νμ΄μ€λ‘ λ³κ²½νκ³ μ΄λ€μ κ°κ° λΉμ¦λμ€ λ‘μ§κ³Ό λμμ± μ μ΄ λ‘μ§μ ν¨κ» μΆκ°νλ κ²μ λλ€.
νμ§λ§ κ°μΈμ μΌλ‘ μ΄ λ°©μμ CreatePurchaseUseCase μΈν°νμ΄μ€λ₯Ό ꡬννμ¬ μλ‘ μ겨λ 3κ°μ ꡬ체 ν΄λμ€μ μ€λ³΅ λ‘μ§μ΄ κ³μ λ°μν κ²μ΄λΌ μκ°νμ΅λλ€. CreatePurchaseUseCase λ΄μλ λμμ± μ μ΄ λ‘μ§λ μμ§λ§, λΉμ¦λμ€ λ‘μ§λ μ‘΄μ¬νλ μν©μ λλ€.
// ν°μΌ ꡬ맀 λΉμ¦λμ€ λ‘μ§
public CreatePurchaseResultDto createPurchase(CreatePurchaseCommandDto command) {
var ticketingId = command.getTicketingId();
var count = command.getCount();
var member = memberCrudService.findByEmail(command.getMemberEmail());
purchaseService.validateTicketingSalePeriod(ticketingId, command.getCommandCreatedAt());
var ticketing = ticketingService.findById(ticketingId);
memberPointService.subtractPoint(member.getId(), ticketing.getPrice() * count);
var purchase = purchaseRepository.save(Purchase.builder().member(member).build());
// #1 λ½ μ’
λ₯μ λ°λΌ λ€λ₯Έ λ©μλλ₯Ό νΈμΆν΄μΌ ν¨
var tickets = ticketRepository.findByTicketingIdAndPurchaseIsNullOrderById(ticketingId, Limit.of(count));
if (tickets.size() < count) {
throw new NotEnoughTicketException();
}
tickets.forEach(ticket -> {
ticket.setPurchase(purchase);
});
return CreatePurchaseResultDto.builder()
.purchaseId(purchase.getId())
.createdAt(purchase.getCreatedAt())
.build();
}
κ³Όμ° μ΄ λ‘μ§λ€μ΄ μ΄λ€ λ½μ μ¬μ©νλλμ λ°λΌ λ°λμ΄μΌ ν μ΄μ κ° μμκΉμ? κ°μΈμ μΌλ‘ λ΄λ¦° κ²°λ‘ μ “λΆνμνλ€.”μ λλ€. μ΄μ, μλμ μꡬμ¬νλ€μ μ μνκ³ μ΄λ€ μν€ν μ³λ₯Ό κ°μ Έκ°λ©΄ μ’μμ§ κ³ λ―Όμ μμνμ΅λλ€.
- λκ΄μ λ½ λ‘μ§
- μ΄μ μ λ°μνλ λ¬Έμ μ ν΄κ²°μ μν΄, Retryableμ΄ Transaction μλͺ μ£ΌκΈ°λ₯Ό μμ ν κ°μΈλ ννκ° λμ΄μΌ ν¨.
- μ λ‘μ§μ #1μμ λκ΄μ λ½ μ μ© TicketRepository.findByTicketingIdAndPurchaseIsNullOrderByIdWithOptimisticLock λ©μλλ₯Ό νΈμΆν΄μΌ ν¨.
- λΉκ΄μ λ½ λ‘μ§
- μ λ‘μ§μ #1μμ λΉκ΄μ λ½ μ μ© TicketRepository.findByTicketingIdAndPurchaseIsNullOrderByIdWithPessimisticLock λ©μλλ₯Ό νΈμΆν΄μΌ ν¨.
- 곡μ λ½ λ‘μ§
- Redissonμ ν΅ν 곡μ λ½ νλ - λ°ν νλ¦μ΄ Transaction μλͺ μ£ΌκΈ°λ₯Ό μμ ν κ°μΈλ ννκ° λμ΄μΌ ν¨.
- μ λ‘μ§μ #1μμ λ½μ΄ μ μ©λμ§ μμ TicketRepository.findByTicketingIdAndPurchaseIsNullOrderById λ©μλλ₯Ό νΈμΆν΄μΌ ν¨.
μ μꡬμ¬νμ μ’ ν©νλ©΄, 1) νμ₯ ν¬μΈνΈμΈ λμμ± μ μ΄ λ‘μ§μ΄ λΉμ¦λμ€ λ‘μ§μ κ°μΈμΌ νκ³ , 2) λΉμ¦λμ€ λ‘μ§ λ΄μ μ΄λ»κ² ν보 ν°μΌμ κ°μ Έμ¬μ§μ λν λ°©λ²λ‘ μ μ£Όμ ν΄μ€ μ μμ΄μΌ ν©λλ€.
μ΄λ₯Ό ν΅ν΄ μ λ μꡬμ¬νμ λ§μ‘±μν€κΈ° μν λ°©μμΌλ‘ κ°μ²΄μ§ν₯ λμμΈ ν¨ν΄μ λ°μ½λ μ΄ν° ν¨ν΄μ λ μ¬λ Έμ΅λλ€.
μλλ λ°μ½λ μ΄ν° ν¨ν΄μ μ΄ν΄λ₯Ό λκΈ° μν μν€ν μ³ μμμ λλ€.
μ΄λ₯Ό ν΅νλ©΄ μ½μ΄ λ‘μ§μ κ·Έλλ‘ λ΄λ²λ € λ μ± μ½μ΄ λ‘μ§μ κ°μΈλ μΌλ ¨μ μμ (λμμ± μ μ΄)λ€μ λν νμ₯μ νΈνκ² κ°λ₯ν©λλ€. λν λ°μ½λ μ΄ν° λ μ΄μ΄μμ μ½μ΄ λ‘μ§μ νΈμΆν λ λ©μλ μΈμλ‘ ν보 ν°μΌλ€μ μ‘°ννλ λ°©λ²λ‘ μ λν ν¨μν μΈν°νμ΄μ€λ₯Ό μ λ¬νλ DIλ₯Ό ν΅ν΄ λ½μ λ°λΌ λ€λ₯Έ ν°μΌ μ‘°ν λ°©λ²λ‘ μ λν μꡬμ¬νμ λ§μ‘±μν€κ³ μ νμ΅λλ€.
μμ μν€ν μ²
μμ λ μν€ν μ²λ μμ κ°μ΅λλ€.
κ°λ΅ν μ€λͺ μ λ§λΆμ΄μλ©΄, CreatePurchaseUseCaseCore λ΄μ λ©μΈ λΉμ¦λμ€ λ‘μ§μ΄ ν¬ν¨λκ³ μ΄λ₯Ό κ°μΈλ λ°μ½λ μ΄ν° κ°μ²΄μΈ CreatePurchaseUseCaseOLock, CreatePurchaseUseCasePLock, CreatePurchaseUseCaseDLockμ΄ κ°κ° μ‘΄μ¬ν©λλ€. λν κ°κ°μ λμμ± μ μ΄ λ°©λ²λ‘ μμ μ¬μ©λμ΄μΌ ν TicketRepository λ©μλλ₯Ό μ£Όμ ν΄μ£ΌκΈ° μν΄ ListTicketStrategyλΌλ ν¨μν μΈν°νμ΄μ€λ₯Ό μμ±νκ³ κ°κ°μ λ°μ½λ μ΄ν°μμ μ½μ΄ λ‘μ§μ νΈμΆν λ μ§μ μ£Όμ ν΄μ£Όλ λ°©μμΌλ‘ λ‘μ§μ΄ μμ±λμ΄ μμ΅λλ€.
ꡬ체μ μΈ μ½λλ μλ λ§ν¬λ₯Ό ν΅ν΄ νμΈμ΄ κ°λ₯ν©λλ€.
κ²°λ‘
κΈ°μ‘΄μ λμμ± λ¬Έμ λ₯Ό ν΄κ²°νκΈ° μν΄ κ΅¬μΆν CreatePurchaseUseCase - TicketConcurrency ꡬ쑰μμ λ°μνλ μΆκ°μ μΈ λμμ± μ΄μμ λν νΈλ€λ§μ λ¬Όλ‘ , λ°μ½λ μ΄ν° ν¨ν΄μ ν΅ν 리ν©ν λ§μΌλ‘ λ‘μ§ μμ λ³νμ§ μλ λΆλΆ(μ½μ΄ λΉμ¦λμ€ λ‘μ§)κ³Ό λ³νλ λΆλΆ(λμμ± μ μ΄ λ°©λ²λ‘ )μ ν¨κ³Όμ μΌλ‘ λΆλ¦¬νμ¬ κ²°λ‘ μ μΌλ‘λ μ½λμ μ¬μ¬μ©μ±κ³Ό μ μ§λ³΄μμ±μ΄ λ°μ΄λ μν€ν μ³λ₯Ό ꡬμ±ν μ μμμ΅λλ€.