본문 바로가기
스파르타 내일배움캠프/TIL(Today I learned)

예약 동시성 제어 테스트 코드

by pandastic 2025. 6. 17.
반응형

 

 

 

목차

     

     

     

    1. 회원 수를 증가시키기 위한 로직

    @BeforeEach
    void setUp() {
        for (int i = 1; i <= 1000; i++) {
            User mentee = new User(
                    "이멘티" + i,                             // 이름
                    "mentee" + i + "@naver.com",             // 이메일
                    "이고수" + i,                             // 닉네임
                    "!@#1Qwer",                              // 비밀번호
                    "010-1111-" + String.format("%04d", i),  // 번호
                    SocialType.LOCAL,
                    null,
                    UserRole.MENTEE,
                    UserGrade.SEED,
                    null
            );
            userRepository.save(mentee);
        }
    }
    • 동시에 한 예약을 시도할 사용자들을 1000명 정도 생성함.

     

     

     

     

    2. 예약 동시성 제어 테스트 코드

    @Test
    @DisplayName("AOP 를 활용한 분산락")
    void concurrencyTestWithAop() {
    
        LocalDateTime startAt = LocalDateTime.of(2025, 6, 15, 10, 0);
        LocalDateTime endAt = LocalDateTime.of(2025, 6, 15, 10, 30);
    
        AtomicInteger successCount = new AtomicInteger();
        AtomicInteger failCount = new AtomicInteger();
    
        ReservationRequestDto dto = new ReservationRequestDto(
                1L, 5L, ReservationStatus.REQUESTED, startAt, endAt);
    
        System.out.println("\n\n\n\n[concurrencyTestWithAOP]");
        IntStream.range(1, 1000).parallel().forEach(i -> {
            try{
                reservationService.save((long) i, dto);
                successCount.incrementAndGet();
    
            }catch(BaseException e){
                Log.error("실패: menteeId = " + i + ", 메시지 = " + e.getMessage());
                failCount.incrementAndGet();
            }
        });
        System.out.println("성공: " + successCount.get());
        System.out.println("실패: " + failCount.get());
    }

     

     

    AtomicInteger successCount = new AtomicInteger();
    AtomicInteger failCount = new AtomicInteger();
    • 동시성(멀티 스레드) 환경에서 안전하게 값을 증가/ 감소시킬 수 있는 int 타입 변수.

     

    IntStream.range(1, 1000).parallel().forEach(i -> {
        try{
            reservationService.save((long) i, dto);
            successCount.incrementAndGet();
    
        }catch(BaseException e){
            Log.error("실패: menteeId = " + i + ", 메시지 = " + e.getMessage());
            failCount.incrementAndGet();
        }
    });
    • IntStream.range(1, 1000) → 1부터 999까지 정수 스트림을 생성함.
    • parallel() → 해당 스트림을 병렬(멀티스레드) 스트림으로 변경.

     

     

     

    3. 최종 코드

    더보기
    package caffeine.nest_dev.domain.reservation.service;
    
    import caffeine.nest_dev.common.config.WebSocketConfig;
    import caffeine.nest_dev.common.exception.BaseException;
    import caffeine.nest_dev.domain.reservation.dto.request.ReservationRequestDto;
    import caffeine.nest_dev.domain.reservation.enums.ReservationStatus;
    import caffeine.nest_dev.domain.reservation.repository.ReservationRepository;
    import caffeine.nest_dev.domain.user.entity.User;
    import caffeine.nest_dev.domain.user.enums.SocialType;
    import caffeine.nest_dev.domain.user.enums.UserGrade;
    import caffeine.nest_dev.domain.user.enums.UserRole;
    import caffeine.nest_dev.domain.user.repository.UserRepository;
    import com.esotericsoftware.minlog.Log;
    import jakarta.validation.constraints.AssertTrue;
    import java.time.LocalDateTime;
    import java.util.concurrent.atomic.AtomicInteger;
    import java.util.stream.IntStream;
    import org.junit.jupiter.api.BeforeEach;
    import org.junit.jupiter.api.DisplayName;
    import org.junit.jupiter.api.Test;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
    import org.springframework.boot.test.context.SpringBootTest;
    import org.springframework.test.context.ActiveProfiles;
    
    @SpringBootTest
    @ActiveProfiles("test")
    @ImportAutoConfiguration(exclude = WebSocketConfig.class)
    public class ReservationTest {
    
        @Autowired
        private ReservationService reservationService;
    
        @Autowired
        private UserRepository userRepository;
        @Autowired
        private ReservationRepository reservationRepository;
    
        @BeforeEach
        void setUp() {
            for (int i = 1; i <= 1000; i++) {
                User mentee = new User(
                        "이멘티" + i,                             // 이름
                        "mentee" + i + "@naver.com",             // 이메일
                        "이고수" + i,                             // 닉네임
                        "!@#1Qwer",                              // 비밀번호
                        "010-1111-" + String.format("%04d", i),  // 번호
                        SocialType.LOCAL,
                        null,
                        UserRole.MENTEE,
                        UserGrade.SEED,
                        null
                );
                userRepository.save(mentee);
            }
        }
    
    
        @Test
        @DisplayName("AOP 를 활용한 분산락")
        void concurrencyTestWithAop() {
    
            LocalDateTime startAt = LocalDateTime.of(2025, 6, 15, 10, 0);
            LocalDateTime endAt = LocalDateTime.of(2025, 6, 15, 10, 30);
    
            AtomicInteger successCount = new AtomicInteger();
            AtomicInteger failCount = new AtomicInteger();
    
            ReservationRequestDto dto = new ReservationRequestDto(
                    1L, 5L, ReservationStatus.REQUESTED, startAt, endAt);
    
            System.out.println("\n\n\n\n[concurrencyTestWithAOP]");
            IntStream.range(1, 1000).parallel().forEach(i -> {
                try{
                    reservationService.save((long) i, dto);
                    successCount.incrementAndGet();
    
                }catch(BaseException e){
                    Log.error("실패: menteeId = " + i + ", 메시지 = " + e.getMessage());
                    failCount.incrementAndGet();
                }
            });
    
            System.out.println("성공: " + successCount.get());
            System.out.println("실패: " + failCount.get());
    
    
        }
    
    }

     

     

     

     

     

     

    반응형