본문 바로가기
Computer Science/Server

상품 전체 리스트 API 부하 테스트 결과 분석. [JMeter]

by 우지uz 2024. 7. 22.

서론

최근 우리 애플리케이션의 성능을 평가하기 위해 상품 전체 리스트를 가져오는 API에 대한 부하 테스트를 수행했습니다. 이 테스트는 AWS EC2 T2 Large 인스턴스에 배포된 서버에서 다양한 시나리오에서 시스템의 성능을 평가하여 최적화 포인트를 찾는 데 목적이 있습니다. 이번 포스팅에서는 테스트 결과와 분석 내용을 공유합니다.

테스트 설정

  • 테스트 환경: AWS EC2 T2 Large 인스턴스, 장비 스펙 (듀얼 코어, 8gb Ram)
  • 테스트 도구: Apache JMeter
  • 테스트 시나리오: 1초당 10명, 20명, 30명, 40명, 50명의 사용자가 API를 호출하는 상황을 시뮬레이션
  • 루프 횟수: 각 사용자당 1회 요청

실 사용자가 존재하지 않는 테스트 및 개발 목적의 서버이기 때문에 , EC2 T2 인스턴스를 선택했고

간단한 서비스 아키텍처를 첨부하겠습니다. 

그리고 상품 관련 api 에 대해서, 트래픽 테스트를 하기 전, N+1 문제를 fetch join 을 통해 해결한 상태입니다. 
상품을 조회할때, 상품 Dto 에서 상품 썸네일을 가져오는데 

// productListDto

@Data
@AllArgsConstructor
@NoArgsConstructor
public class ProductListDto {
    private Long productId;
    private ProductType productType;
    private String productName;
    private Integer price;
    private LocalDateTime createdAt;
    private Long wishListCount;
    private Boolean isDiscount;
    private Integer discountRate;
    private Boolean isRecommend;
    private List<String> productThumbnails; // 썸네일 리스트

    public ProductListDto(Product product) {
        this(
                product.getProductId(),
                product.getProductType(),
                product.getProductName(),
                product.getPrice(),
                product.getCreatedAt(),
                product.getWishListCount(),
                product.getIsDiscount(),
                product.getDiscountRate(),
                product.getIsRecommend(),
                product.getProductThumbnails().stream().map(ProductThumbnail::getImagePath).toList() // 경로만 가져오기

        );
    }
}

여기서 상품을 가져올때, 썸네일도 묶어서 가져올 수 있도록 

@Repository
public interface ProductRepository extends JpaRepository<Product, Long> {

    @Query("SELECT p FROM Product p JOIN FETCH p.productThumbnails")
    List<Product> findAllWithThumbnails();
}

Fetch Join 을 걸어두었습니다.

select
        p1_0.product_id,
        p1_0.created_at,
        p1_0.discount_rate,
        p1_0.is_discount,
        p1_0.is_recommend,
        p1_0.manufacturer,
        p1_0.price,
        p1_0.product_info,
        p1_0.product_name,
        pt1_0.product_id,
        pt1_0.thumbnail_id,
        pt1_0.created_at,
        pt1_0.image_path,
        p1_0.product_type,
        p1_0.updated_at,
        p1_0.wishlist_count 
    from
        products_table p1_0 
    join
        product_thumbnails pt1_0 
            on p1_0.product_id=pt1_0.product_id

 

 

저는 테스트 서버 ubuntu.pjundemand.shop 에서 상품 전체 리스트에 대한 api 에 대해서 각각의 상황에 대해서 테스트를 진행했습니다. 1회, 초당 10명의 유저, 20명, 30명, 40명, 50명의 유저에 대해서 Latency 와 Connect Tome 을 정리해보았습니다.

 

테스트 결과

1. 1초당 10명의 사용자(10 Threads)

전체적으로 1700~2400ms 응답 시간을 보여줌

2. 1초당 20명의 사용자

평균적으로 8000ms 의 응답 시간을 보여주고 있다.

3. 1초당 30명의 사용자

4. 1초당 40명의 사용자

5. 1초당 50명의 사용자

 

 

분석

  • 성능 저하: 사용자가 증가함에 따라 응답 시간이 급격히 증가하는 것을 확인할 수 있었습니다. 특히, 1초당 30명 이상의 사용자가 동시에 요청할 경우 응답 시간이 20000ms를 초과하여 응답 시간에 엄청난 지연이 생긴 것을 확인할 수 있었습니다.
  • 병목 현상: 최대 응답 시간이 길어진 주요 원인은 서버의 처리 능력에 병목이 발생한 것으로 분석됩니다. 
    개인 로컬 환경에서는 1초당 100명의 유저에 대해서 3번의 Loop 를 진행하여도 안정적인 응답 속도를 보여주고 있었지만, 
    테스트 서버 환경 EC2 T2 Large 인스턴스에서는 갑작스런 트래픽 환경에 대해서, 응답 시간이 100ms 에서 최대 30000ms 까지 지연되었습니다.

 

로컬 환경에서 테스트 결과(1초당 100명의 유저에 대해서 3번의 Loop )

301~600 Sample 에서 기껏해야 150ms 의 응답 시간이 나타났다. 

결론

로컬 환경에서의 테스트 결과는 매우 빠르고 일관된 응답 시간을 보여주었으며, 이는 성능 저하가 발생하지 않는 이상적인 상황입니다. 이를 바탕으로 다음과 같은 결론을 내릴 수 있습니다:

  1. 서버 인스턴스 성능 문제:
    • 로컬 환경에서는 성능이 매우 우수하지만, 실제 테스트 서버에서는 성능 저하가 발생했다면, 이는 서버 인스턴스의 성능이 원인일 가능성이 높습니다.
    • 현재 사용 중인 T2 Large 인스턴스가 충분한 성능을 제공하지 못할 수 있습니다
    • T2 Large 인스턴스를 M5, C5와 같은 더 높은 성능의 인스턴스로 업그레이드하여 CPU 및 메모리 자원을 늘립니다.
  2. 네트워크 지연 문제:
    • 로컬 환경에서는 네트워크 지연이 거의 없으므로, 실제 환경에서 네트워크 지연이 성능 저하의 원인일 수 있습니다.
    • VPC 엔드포인트를 정하는 등 네트워크 성능을 최적화하여 클라이언트와 서버 간의 지연을 최소화합니다.
    • 로드 밸런싱, 오토스케일링과 같은 분산 처리를 통해서 ,부하를 분산 시키는 것이 좋을 것 같습니다. 
    • 오토 스케일링
      • 오토 스케일링은 액세스 상태에 따라, 서버 대수를 증감하는 기능입니다.  오토 스케일링 자체는 무료이지만, Amazon EC2 나 Amazon CloudWatch 등의 서비스 증가분의 요금이 부과됩니다. CloudWatch 를 통해서 감시할 항목이 늘어난다면, 요금이 많이 늘어난다고 합니다.  

 

EC2 인스턴스 옵션에 대한 자료

AWS 의 다양한 서비스와 서버 인프라 환경에 대한 이해를 돕기 위해서 , 
 오가사와라 시게타카 저서의 [그림으로 이해하는 AWS 구조와 기술] 책을 구매하였고 
다음을 확인할 수 있었습니다.

여기서 T2 Large 인스턴스 유형은, 부하가 일정한 서버의 경우이며 
서버용 노트북 성능은 듀얼 코어 및 8gb 램을 탑재하고 있습니다. 테스트 및 사이드 프로젝트 진행하는데에는 문제가 없다고 생각했습니다.

다만, 실 배포 서버에서 1초당 100명의 유저가 상품의 전체 리스트에 대해서 조회하기 어렵고, 
백엔드에서 상품에 대한 페이지네이션을 진행했으며, 클라이언트에서도 구현이 되었습니다. 
현재 메인 페이지와 상품 카테고리 페이지에서는 페이징 뿐만 아니라, 상품 유형과 카테고리에 따라서 데이터를 보여주고 있으며

카테고리 및 상품에 대해서 , 캐싱을 적용한 상태입니다. 
상품 더미데이터 및 카테고리는 실시간으로(분,초 단위로) 업데이트 해야하는 중요한 정보는 아니기 때문에
1시간 단위로 api 를 다시 가져올 수 있도록 클라이언트에서 캐싱을 적용했습니다. 

 

느낀 점

사실 대용량 트래픽에 대해서, 좀 더 세세하고 깊이 있게 공부하고 싶었으나
현재 사용중인 EC2 환경 및 노트북 성능으로는 한계가 있다는 걸 알게 되었습니다. (현재 T2 인스턴스 내에서는, 충분한 스펙임)

트래픽 테스트를 하고 나니 

  1. 클라이언트와 서버에서 캐싱의 중요성
  2. 트래픽 과부하에 대한 로드 밸런싱이나, 네트워크 최적화의 중요성
  3. 인프라의 구조와 각 인프라의 역할에 대한 이해

가 중요하다는 것을 느끼게 되었습니다. 

인프라 환경에 대해서는 따로 공부를 하지 않았기 때문에, 모르는 것이 많은 것 같아

유튜브와 구글링을 통해서, Docker 와 서버에 대한 기본 지식들을 공부하고 있으며
백엔드를 중심으로, 서버에 대한 지식도 갖춰나가야 겠다는 생각이 들었습니다.

 

참고 문헌

  1. [그림으로 이해하는 AWS 구조와 기술] 오가사와라 시게타카 지음
  2. Amazon EC2 T2 인스턴스