개발일지/2023_한이음

[개발] Querydsl를 사용하여 원하는 정보 조회/반환하기(Refactoring)

기억지기 개발자 2023. 7. 18. 10:49

🏕️상황 

https://grogrammer.tistory.com/53

 

[개발] Querydsl, DSL를 사용하여 원하는 정보 조회/반환하기(코드분석)

🏕️상황 //school 리스트를 반환하는 메소드 //school_tb : id와 schoolName, tag_tb : name(태그명), user_tb : schoolId를 counting. 총 3개의 테이블을 조인하여 반환 public List findSchoolInfoWithTagsAndUserCount() { JPAQueryFac

grogrammer.tistory.com

best처럼 표현히기 위해서 내내 고생했다.

  • school의 list를 반환하는 메서드에 반환 값이 까다로워서 코드가 좀 복잡해지고, 기능이 정상적으로 작동하니 일단 깃랩에 업로드하였다.
  • 하지만 for문 안에 sql문이 있었고, 잘은 모르더라도 한눈에 보기에 코드가 복잡하고 가시성이 좋지 않은 느낌이 있었다. 그래서 일단 for문을 제거하기로 하였다.
  • 위에 블로그 링크가 리팩토링 전의 코드를 작성한 모습이다.
  • worst처럼 작성하면 그만큼 속도나 네트워크 비용이 커지고, 보기에도 복잡해보이기 때문에 저 형태는 무조건 피하고 싶었다. 반드시 best 형태로 반환해야 했다.

💚과정

  • 단순히 테이블을 4개를 사용하는 것 자체는 어렵지 않았지만 sql의 반환 값이 자료형도 다르고, list로 여러 개인 경우도 있어서 어떻게 작성해야 하는지 막막했다.
  • 거의 이틀 이상 소모된 거 같다...ㅠㅠ
  • 가장 중요한 건 동일한 schoolId를 가진 데이터(결과 값)들은 하나의 객체로 저장되어 반환해야 하기 때문에 값이 여러 개인 경우에... 그걸 처리하는데 제일 많은 시간을 소모했다.
  • return값으로 Tuple을 전달받은 뒤 DTO 등으로 변환하여 리턴하는 방식도 생각해 봤지만 과정이 2번으로 나뉘기 때문에 멀리 봤을 때는 결국 좋은 방식이 아닐 거 같다는 생각이 들었다.
  • 최대한 한 쿼리로 한 번에 끝내고 싶었다.

🗝️해결

//school 리스트를 반환하는 메소드
    public ResponseDTO<?> findSchoolInfoWithTagsAndUserCount() {

        QSchool school = QSchool.school;
        QTag tag = QTag.tag;
        QUser user = QUser.user;

        List<SchoolInfoDTO> schoolInfoList = jpaQueryFactory
                .selectFrom(school)
                .leftJoin(tag).on(tag.schoolId.eq(school.id))
                .leftJoin(user).on(user.schoolId.id.eq(school.id))
                .groupBy(school.id, school.schoolName, tag.name)
                .transform(GroupBy.groupBy(school.id).list(Projections.constructor(
                        SchoolInfoDTO.class,
                        school.id,
                        school.schoolName,
                        user.schoolId.id.count().intValue(),
                        GroupBy.list(tag.name)
                )));

        return ResponseDTO.success(schoolInfoList);
    }
  • 기존 코드보다 훨~씬 짧다.
  • 가독성 있다.
  • for문을 제거하였다.
  • 한 번의 과정으로 해결 가능하다.

<<새롭게 알게 된 친구들>>

💜transform이란??

.transform() 메서드는 Querydsl의 결과를 변환하는 역할을 수행한다.
주어진 쿼리 결과를 지정한 변환 방식에 따라 변환하여 원하는 형태로 데이터를 가공할 수 있다.

.transform() 메서드는 Projections 클래스의 정적 메서드를 사용하여 변환 방식을 지정한다.

  • Projections.bean() : DTO 클래스로 변환하도록 한다.
  • Projections.constructor() : 생성자를 이용하여 객체를 생성하도록 한다.

 

💜Projections란??

Projections은 Querydsl에서 결과를 변환하는 데 사용되는 클래스로 SELECT 절에서 반환할 엔티티나 필드, 그리고 특정 변환 방식을 지정하는 데에 활용된다.

projections를 사용하는 이유는?

  1. 필요한 필드만 선택하여 반환: 쿼리 결과에서 필요한 필드만 선택하여 반환하고 싶을 때, Projections를 사용하여 필요한 필드만 지정할 수 있다. 이를 통해 불필요한 데이터를 조회하지 않고 필요한 데이터만 효율적으로 가져올 수 있다.
  2. 데이터 변환: 쿼리 결과를 원하는 형태로 변환하여 반환하고 싶을 때, Projections를 사용하여 데이터 변환을 할 수 있다.    예를 들어, DTO 클래스를 생성하고 Projections를 사용하여 엔티티와 DTO 간의 필드 매핑을 지정할 수 있고, 이를 통해 엔티티의 데이터를 DTO로 변환하여 반환할 수 있다.'
  3. Projections은 데이터를 select 할때부터 저장되고자 하는 class를 지정하여 필드에 맞는 데이터를 바로 넣어줄 수 있다.

 

💜.selectFrom()은 왜 쓰지 ?

.selectFrom()은 Querydsl에서 SELECT와 FROM 절을 동시에 작성하는 메서드다.
selectFrom()을 사용하면 "SELECT * FROM table"과 같이 SELECT와 FROM을 한 번에 작성할 수 있다.
.select().from()을 따로 호출하는 것보다 코드가 더 간결해지고, 엔티티 클래스를 직접 사용하여 SELECT와 FROM을 지정하므로 코드 가독성이 향상된다. 

넘 행복하다ㅎ