쿼리 실행 방식에 따른 카드 조회 성능 비교

·

3 min read

Card 엔티티

@Table(name="card")  
public class Card extends BaseEntity {  
    @Id  
    @GeneratedValue(strategy = GenerationType.IDENTITY)  
    private long id;  
    private String title;  
    private String content;  
    @Column(name = "deadline", nullable = true)  
    private LocalDateTime deadline;  

    @ManyToOne  
    @JoinColumn(name = "user_id")  
    private User user;  

    @ManyToOne  
    @JoinColumn(name = "column_id")  
    private Bar bar;  


    //문자열 형식의 날짜 ex)20240319 를 LocalDateTime 형식으로 변환

    public Card(User user, Bar bar,CardRequest cardRequest) {  
        this.title = cardRequest.getTitle();  
        this.content = cardRequest.getContent();  
        this.user = user;  
        this.bar = bar;  
        if (cardRequest.getDeadline() != null &&
         !cardRequest.getDeadline().isEmpty()) {  
            DateTimeFormatter formatter = 
            DateTimeFormatter.ofPattern("yyyyMMdd");  
            LocalDate date = LocalDate.parse(cardRequest.getDeadline(), 
            formatter);  
            this.deadline = date.atStartOfDay();
            // LocalDate를 LocalDateTime으로 변환  
        }  
    }  
    public void updateCard(CardRequest cardRequest){  
        this.title = cardRequest.getTitle();  
        this.content = cardRequest.getContent();  
    if(cardRequest.getDeadline()!=null&&!cardRequest.getDeadline().isEmpty()){ 
            DateTimeFormatter formatter = 
            DateTimeFormatter.ofPattern("yyyyMMdd");  
            LocalDate date = LocalDate.parse(cardRequest.getDeadline(), 
            formatter);  
            this.deadline = date.atStartOfDay(); 
            // LocalDate를 LocalDateTime으로 변환  
        }  
    }
  • 전체 카드를 조회할때 쿼리 실행 방식에 따른 실행 시간을 측정해봤다.

쿼리 방식

1.Controller에서 Service를 호출하여 Repository에서 데이터를 가져오는 방식(findAll( )사용)

//Controller
    @GetMapping("/cards")  
    public ResponseEntity<List<CardResponse>> getAllCards() {  
        List<CardResponse> cardList = cardService.getAllCards();  
        return ResponseEntity.ok().body(cardList);
    }
//Service
    public List<CardResponse> getAllCards() {  
      List<CardResponse> cardList = cardRepository.findAll()  
           .stream().map(CardResponse::new).toList();  
        return cardList;  
    }

List com.sparta.trellowiththreeipeople.card.service.CardService.getAllCards() executed in 12ms

2.데이터베이스로부터 카드 정보를 조회하고, 이를 CardDTO 객체로 매핑하는 방식

//Controller
@GetMapping("/cards")  
public ResponseEntity<List<CardDTO>> getAllCards() {  
    List<CardDTO> cardList = cardService.getAllCards();  
    return ResponseEntity.ok().body(cardList);  
}
//Service
public List<CardDTO> getAllCards() {  
    List<CardDTO> cardList = cardRepository.findAllCardsWithDTO();  
    return cardList;  
}
//Repository
@Query("SELECT new com.sparta.trellowiththreeipeople.card.dto.CardDTO(c.id, c.title, c.content, c.deadline) FROM Card c")  
List<CardDTO> findAllCardsWithDTO();

List com.sparta.trellowiththreeipeople.card.service.CardService.getAllCards() executed in 2ms

3.Fetch,Join을 사용한 방식

//Repository
@Query("SELECT DISTINCT c FROM Card c JOIN FETCH c.user u JOIN FETCH c.bar b WHERE c.deletedAt IS NULL")  
List<Card> findAllWithUserAndBar();

List com.sparta.trellowiththreeipeople.card.service.CardService.getAllCards() executed in 7ms

성능 측정 코드

@Around("execution(* com.sparta.trellowiththreeipeople.card.service.CardService.*(..))")  
public Object logExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {  
    long start = System.currentTimeMillis();  
    Object proceed = joinPoint.proceed();  
    long executionTime = System.currentTimeMillis() - start;  
    System.out.println(joinPoint.getSignature() + " executed in " +
    executionTime + "ms");  
    return proceed;  
}

쿼리 실행 방식에 따른 카드 조회 성능 비교 결과 정리

카드 정보 조회에 있어 다양한 쿼리 실행 방식을 테스트하여 성능을 측정해본 결과, 데이터베이스로부터 직접 카드 정보를 조회하고 이를 CardDTO 객체로 매핑하는 방식이 가장 빠른 실행 시간을 보였다. 각 방식별로 성능을 측정한 결과는 다음과 같다:

1. 기본 Repository 메소드 사용 (findAll())

  • 방식 설명: Controller에서 Service를 호출하여 RepositoryfindAll() 메소드를 통해 데이터를 가져오는 방식.

  • 실행 시간: 12ms

  • 이 방식은 JPA의 기본 제공 메소드를 활용하여 전체 카드 정보를 조회한다. 조회한 엔티티를 CardResponse DTO로 변환하는 과정에서 추가적인 시간이 소요된다.

2. JPQL을 활용한 DTO 직접 매핑

  • 방식 설명: 데이터베이스로부터 카드 정보를 조회하고, 이를 CardDTO 객체로 직접 매핑하는 방식.

  • 실행 시간: 2ms

  • 이 방식은 JPQL 쿼리를 사용하여 필요한 데이터만 선택적으로 조회하고, 조회된 결과를 바로 CardDTO 객체로 매핑한다 데이터 변환 과정이 최소화되며, 실행 시간이 가장 빠르다.

3. Fetch Join 사용

  • 방식 설명: Fetch Join을 사용하여 연관된 UserBar 엔티티를 포함하여 카드 정보를 조회하는 방식.

  • 실행 시간: 7ms

  • 분석: Fetch Join을 사용하면 연관 엔티티를 한 번의 쿼리로 함께 로딩할 수 있어서 성능이 개선된다. 하지만DTO로의 직접 매핑 방식에 비해 느리다.

결론

카드 정보 조회 성능 측정 결과, 직접 DTO 매핑 방식이 2ms의 실행 시간으로 가장 빠르다. 이 방식은 필요한 데이터만 데이터베이스로부터 조회하고, 추가적인 변환 과정 없이 바로 DTO 객체로 매핑하기 때문에 가장 실행시간이 빠르다. 따라서, 성능 최적화가 중요한 상황에서는 이 방식을 활용할수 있을 것 같다.