ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 부하테스트를 위한 더미데이터 준비
    개발 2024. 8. 8. 22:08

    SSAFY 11기 공통프로젝트 기능 개발이 완료되었습니다.

    고가용성을 가진 프로젝트를 만들어보는 것이 팀의 목표였기에 부하테스트를 통한 성능개선을 진행하기로 하였습니다.

    부하테스트와 관련된 글은 따로 정리하여 게시할 예정입니다.

     

    어플리케이션에서 가장 중요하고 직관적인 신입 개발자가 필수적으로 할 수 있어야하는 성능 개선이 무엇이 있을까? 생각하였습니다.

     

    쿼리 시간 개선을 1순위 목표로 잡고 성능 최적화 작업에 들어갔습니다.

     

    테이블에 데이터가 10건, 100건인 상황에서의 테스트는 무의미하다고 생각하였고, 따라서 테이블마다 더미데이터를 100만건 씩 준비한 뒤 부하테스트를 진행하기로 했습니다.

     

    Java 코드로 더미데이터를 준비한 과정들을 공유하고자 합니다.


    ApplicationRunner 인터페이스

    ApplicationRunner 인터페이스

    • ApplicationRunner 인터페이스는 함수형 인터페이스로서 보시는 바와 같이 run 이라는 메서드를 가지고 있습니다.
    • @Component 어노테이션을 사용하여 ApplicationRunner 인터페이스를 구현한 클래스를 빈으로 등록하면 스프링부트가 실행되면 run 메서드에 정의한 코드들이 실행됩니다.
    • 평소에도 유용하게 사용하던 인터페이스였기에 더미데이터를 해당 인터페이스를 활용하여 준비하고자 했습니다.

    더미데이터에 들어올 데이터

    • for문, IntStream 등 정수형 값을 붙인 무의미한 더미데이터를 추가하여 테스트를 하는 방법도 있습니다.
    IntStream.range(0, totalRecords).forEach(i -> {
        String username = "username" + i;
    });
    • 이렇게 상황에 맞게 더미데이터를 준비하는 것은 많이 귀찮은 작업이었고 랜덤한 데이터를 생성하여 테스트를 하고 싶었습니다.
    • 그리고 구글링을 통해 Faker 라는 라이브러리를 알게 되었습니다. https://github.com/DiUS/java-faker
     

    GitHub - DiUS/java-faker: Brings the popular ruby faker gem to Java

    Brings the popular ruby faker gem to Java. Contribute to DiUS/java-faker development by creating an account on GitHub.

    github.com

    • faker 라이브러리는 더미데이터에 필요한 다양한 형식의 랜덤한 데이터를 제공해주는 라이브러리입니다.

    Faker github

    • 한 눈에 보기에도 다양한 형식의 데이터를 제공해주고 있습니다. 이제 이 라이브러리를 사용하여 더미데이터를 준비해보겠습니다.

    1.  의존성 추가

    implementation 'com.github.javafaker:javafaker:1.0.2'
    implementation 'org.yaml:snakeyaml:1.33'

     

     

    2. 더미데이터를 추가할 ApplicationRunner 인터페이스 구현

     

    @Slf4j
    @RequiredArgsConstructor
    @Component
    public class AppRunner implements ApplicationRunner {
    
        @PersistenceContext
        private EntityManager em;
    
        @Transactional
        @Override
        public void run(ApplicationArguments args) throws Exception {
            int totalRecords = 1000000;
    
            StopWatch stopWatch = new StopWatch();
            stopWatch.start();
            Faker faker = new Faker();
            Set<String> uniqueIsbns = new HashSet<>();
    
            for (int i = 0; i < totalRecords; i++) {
                String isbn;
                do {
                    isbn = faker.code().isbn10();
                } while (!uniqueIsbns.add(isbn));
    
                Book book = Book.builder()
                        .title(faker.book().title())
                        .author(faker.book().author())
                        .isbn(isbn)
                        .description(faker.lorem().characters(100))
                        .publisher(faker.company().name())
                        .thumbnail(String.valueOf(faker.lorem().characters(100)))
                        .publicationDate(date2LocalDate(faker.date().past(30, TimeUnit.DAYS)))
                        .build();
                em.persist(book);
            }
            em.flush();
            em.clear();
            stopWatch.stop();
    
            log.info("Total time: " + stopWatch.getTotalTimeMillis() + " ms");
        }
    
        private LocalDate date2LocalDate(Date date) {
            return date.toInstant()
                    .atZone(ZoneId.systemDefault())
                    .toLocalDate();
        }
    }
    

     

    • 엔티티에서는 LocalDate 타입의 날짜 형식의 데이터가 필요하였지만 아쉽게도 Date 타입밖에 지원하지 않았기에 date 타입을 LocalDate 타입으로 변환해주는 메서드를 생성하여 사용했습니다.
    • 100만 건의 데이터를 저장하는 데 걸린 시간은 99076 ms 였습니다.

    실행 결과

     

    데이터를 만드는 준비도 끝났으니 이제 본격적으로 부하테스트를 진행하며 성능 개선을 하면 되겠습니다.

Designed by Tistory.