개발 낙서장

[TIL] 내일배움캠프 53일차 - 불필요한 쿼리 개선 본문

Java/Sparta

[TIL] 내일배움캠프 53일차 - 불필요한 쿼리 개선

권승준 2024. 3. 12. 21:16

 

 

오늘의 학습 키워드📚

QueryDSL 페이징 + 성능 개선

https://dachomi97.tistory.com/130

 

[Spring] QueryDSL 페이징

QueryDSL 페이징? 기존에 레포지토리에서 페이징 된 값을 받을 때처럼 Pageable 객체를 만들어 페이지 정보를 보내 QueryDSL로 작성된 쿼리문을 통해 받아오면 된다. List content = queryFactory.selectFrom(todo).wh

dachomi97.tistory.com

불필요한 조회 쿼리 개선

Todo에 관한 쿼리를 수행할 때 User 조회 쿼리도 항상 같이 날아가는 이슈가 있었다.

 

Hibernate: 
    /* select
        todo 
    from
        Todo todo 
    where
        todo.user = ?1 */ select
            t1_0.todo_id,
            t1_0.content,
            t1_0.created_at,
            t1_0.finished,
            t1_0.modified_at,
            t1_0.todo_name,
            t1_0.user_id 
        from
            todo_tb t1_0 
        where
            t1_0.user_id=? 
        limit
            ?, ?
Hibernate: 
    select
        u1_0.user_id,
        u1_0.created_at,
        u1_0.email,
        u1_0.modified_at,
        u1_0.password,
        u1_0.role,
        u1_0.user_name 
    from
        user_tb u1_0 
    where
        u1_0.user_id=?

특정 유저의 Todo를 조회한 후 해당 User를 찾는 쿼리가 또 날아간다.
왜 이럴까 고민을 했는데 Dto에서 Entity를 다루려 했기 때문에 발생한 이슈였다.

@Getter
@ToString(exclude = {"createdAt", "modifiedAt"})
public class TodoResponseDto {

    private String userName;
    private String todoName;
    private String content;
    private boolean finished;
    private LocalDateTime createdAt;
    private LocalDateTime modifiedAt;

    public TodoResponseDto(Todo todo) {
        this.userName = todo.getUser().getUserName();
        this.todoName = todo.getTodoName();
        this.content = todo.getContent();
        this.finished = todo.isFinished();
        this.createdAt = todo.getCreatedAt();
        this.modifiedAt = todo.getModifiedAt();
    }
}

TodoResponseDto를 생성할 때 userName을 todo의 getUser를 통해 가져오고 있었다.
저 한 줄 때문에 todo에서 getUser를 하면서 해당 유저를 조회하는 쿼리가 실행되는 것이었다.
그냥 userName만 필요한 것이기 때문에 생성자를 바꿔주었다.

@Getter
@ToString(exclude = {"createdAt", "modifiedAt"})
public class TodoResponseDto {

    private String userName;
    private String todoName;
    private String content;
    private boolean finished;
    private LocalDateTime createdAt;
    private LocalDateTime modifiedAt;

    public TodoResponseDto(Todo todo, User user) {
        this.userName = user.getUserName();
        this.todoName = todo.getTodoName();
        this.content = todo.getContent();
        this.finished = todo.isFinished();
        this.createdAt = todo.getCreatedAt();
        this.modifiedAt = todo.getModifiedAt();
    }
}

Controller에서 받아온 인증된 User 객체를 Service에서 받아와 생성자에 넣어주어 불필요한 쿼리를 방지했다.

    public Page<TodoResponseDto> getTodosByUser(Long userId, int page, int size, boolean isAsc,
        String sortBy, User user) {
        if (!userId.equals(user.getUserId())) {
            throw new IllegalArgumentException("유저 정보가 일치하지 않습니다.");
        }

        // 페이징 처리
        Sort.Direction direction = isAsc ? Direction.ASC : Direction.DESC;
        Sort sort = Sort.by(direction, sortBy);
        Pageable pageable = PageRequest.of(page, size, sort);

        Page<Todo> todos = todoRepository.findAllByUser(user, pageable);

        return todos.map(todo -> new TodoResponseDto(todo, user));
    }


오늘의 회고💬

QueryDSL에서 페이징을 하는 방법, 성능을 개선하는 방법을 공부하면서 의문점이 많이 생겨 시간을 꽤 쏟았던 것 같다.

 

내일의 계획📜

내일은 예비군이다....

Comments