우당탕탕 개발일지

[트러블 슈팅] 템플릿 목록 출력 본문

개발 프로젝트/Hot Docs

[트러블 슈팅] 템플릿 목록 출력

ujin302 2025. 2. 11. 10:17
반응형

[ 문제 ] 조건이 너무 많음...

  1. 최신순
  2. 인기순
  3. 최신순 + 타입별
  4. 인기순 + 타입별
  5. 최신순 + 검색
  6. 인기순 + 검색
  7. 최신순 + 타입별 + 검색
  8. 인기순 + 타입별 + 검색

이렇게 8개.....

이걸 조건문으로 구현하니까 보기 불편하고 약간 의미없는 느낌....?? 그리고 나중에 정렬 조건이 많아지면 확장성에 어려움이 생기지 않을까? 고민이됨...

그래서 일단 GPT한테 물어봄

Page<TemplateEntity> entityList = null;
        if (sortby == 0 && type == 0 && query == null) {
            // 최신순
            entityList = templateRepository.findByAgree(true, pageable);
        } else if (sortby == 1 && type == 0 && query == null) {
            // 인기순
            entityList = templateRepository.findByAgreeOrderByWishSizeDesc(true, pageable);
        } else if (sortby == 0 && type != 0 && query == null) {
            // 최신순 + 타입별
            TypeEntity typeEntity = typeRepository.findById(Long.parseLong(type + "")).orElseThrow();
            entityList = templateRepository.findByTypeEntityAndAgree(typeEntity, true, pageable);
        } else if (sortby == 1 && type != 0 && query == null) {
            // 인기순 + 타입별
            entityList = templateRepository.findByAgreeAndTypeidOrderByWishSizeDesc(true, Long.parseLong(type + ""),
                    pageable);
        } else if (sortby == 0 && type == 0 && query != null) {
            // 최신순 + 검색
            entityList = templateRepository.findByAgreeAndTitleContaining(query, true, pageable);
        } else if (sortby == 1 && type == 0 && query != null) {
            // 인기순 + 검색

        } else if (sortby == 0 && type != 0 && query != null) {
            // 최신순 + 타입별 + 검색

        } else if (sortby == 1 && type != 0 && query != null) {
            // 인기순 + 타입별 + 검색

        }

 

 

GPT한테....

 

일단 이렇다구 함.

근데 Specification가 뭘까... 모르는 용어 나옴...

코드 봐도 이해도 안됑...

 

 

Specification

specification를 사용하면 동적 쿼리를 만들 수 있다고 한다..!

그게 무슨말이지..? 했는데 여러 블로그에 들어가 코드를 보니 쪼오금 알 것 같다

 

specification 객체를 선언해서 그 객체에 and로 계속 쿼리를 추가하여 사용한다.

agree = true인 쿼리 객체에 추가로 type = n 인 쿼리를 추가로 더하여

agree = true & type = n 조건으로 데이터를 뽑아낼 수 있다

 

 

이를 하기 위해 세팅이 필요하다.

1. TemplateRepository인터페이스에 JpaSpecificationExecutor 인터페이스 상속

JpaRepository 인터페이스: 기본적인 CRUD

JpaSpecificationExecutor 인테페이스: 동적 쿼리 (일종의 검색 조건을 추상화한 인테페이스)

public interface TemplateRepository
        extends JpaRepository<TemplateEntity, Long>, JpaSpecificationExecutor<TemplateEntity> {
        
}

 

2. Specificartion 파일 및 함수 생성

root

엔티티의 루트!

퀴리를 적용하고자 하는 엔티티의 속성에 접근하기 위해 사용함

 

query

구성된 쿼리 자체를 의미

퀴리의 구조와 반환할 결과 타입 정의

쿼리의 SELECT, WHEREM, GROUP BY, ORDER BY 등의 절을 정의하는데 사용되며, 최종적으로 생성되는 쿼리의 골격을 결정함.

 

criteriaBuilder

JPA Criteria API를 사용하여 쿼리 조건을 생성하는데 사용되는 빌더 객체

* Criteria

: JPQL의 작성을 도와주는 빌더 클래스

* JPQL (Java Persistencs Query Language)

: 엔티티 객체를 조회하는 객체지향 쿼리

JPA에서 제공하는 메소드 호출만으로 섬세한 쿼리 작성의 어려움으로 인해 탄생함

 

즉, 엔티티 객체를 조회하는 객체지향 쿼리를 프로그래밍 방식으로 조건을 생성하는데 사용되는 빌더 객체이다. 이는 JPA 제공 메소드보다 섬세한 쿼리 작성이 가능하다. Critieria API에서 타입-세이프 방식을 통해 컴파일 안정성을 제공하면서 컴파일 타임에 오류를 검출할 수 있다.

 

쿼리를 프로그래밍 방식으로 안전하게 구성할 수 있는 API (JPA Criteria API)제공

  • criteriaBuilder.equal(   )
  • criteriaBuilder.like(   )
package com.example.backend.domain.template.repository;

import org.springframework.data.jpa.domain.Specification;

import com.example.backend.domain.template.entity.TemplateEntity;

public class TemplateSpecification {
    // agree 조건 - uj
    public static Specification<TemplateEntity> hasAgree(Boolean agree) {
        return (root, query, criteriaBuilder) -> criteriaBuilder.equal(root.get("agree"), agree);
    }

    // type 종류 - uj
    public static Specification<TemplateEntity> equalType(Long type_id) {
        return (root, query, criteriaBuilder) -> criteriaBuilder.equal(root.get("typeEntity").get("id"), type_id);
    }

    // title 검색 - uj
    public static Specification<TemplateEntity> hasTitleContaining(String title) {
        return (root, query, criteriaBuilder) -> criteriaBuilder.like(root.get(title), "%" + title + "%");
    }

    // wish개수 내림차순 - uj
    public static Specification<TemplateEntity> orderByWishSizeDesc() {
        return (root, query, criteriaBuilder) -> {
            query.orderBy(criteriaBuilder.desc(criteriaBuilder.size(root.get("wishEntityList"))));
            return null;
        };
    }
}

 

3. Service 계층에서 Specification 활용

동적쿼리를 사용하니 알아보기 힘든 조건문에서 좀더 간편한? 로직이 되었다!

 

1. 무조건 agree=true 여야 하기에 Specification 객체를 agree=true로 세팅

2. 전달받은 매개변수에 따라 필요한 쿼리를 add()를 통해서 더함!

3. Repository.findAll(Specification 객체, pageable)를 통해 쿼리에 해당되는 entity 추출

List<TemplateResponseDTO> list = new LinkedList<>();
// Agree = true
Specification<TemplateEntity> templateSpec = Specification.where(TemplateSpecification.hasAgree(true));

if (type != 0) { // 타입이 존재하는 경우
    templateSpec = templateSpec.and(TemplateSpecification.equalType(Long.parseLong(type + "")));
}
if (query != null) { // 검색어가 존재하는 경우
    templateSpec = templateSpec.and(TemplateSpecification.hasTitleContaining(query));
}
if (sortby == 1) { // 인기순 정렬
    templateSpec = templateSpec.and(TemplateSpecification.orderByWishSizeDesc());
}

// 3. Entity -> DTO 변환
for (TemplateEntity entity : templateRepository.findAll(templateSpec, pageable)) {
    list.add(templateMapper.toDTO(entity));
}

 

 

참고 블로그

https://velog.io/@alsrb5606/JPA-Specification-%EC%82%AC%EC%9A%A9%ED%95%98%EC%97%AC-%EC%A1%B0%ED%9A%8C%EA%B8%B0%EB%8A%A5-%EA%B0%9C%EB%B0%9C

 

JPA Specification 사용하여 조회기능 개발

우리는 데이터베이스를 사용하여 조회쿼리를 수행할 때 정말 다양한 조건들을 적용하여 사용하고 있다.또한, 특정한 상황에서 몇몇 조건을 추가하거나, 사용하지 않는 일 또한 존재한다. 만약 J

velog.io

 

https://velog.io/@dvshinny/JpaSpecificationExecutor%EB%A5%BC

 

JpaSpecificationExecutor를

JpaSpecificationExecutor를 사용하면 데이터베이스에서 데이터를 조회할 때 동적인 쿼리를 작성할 수 있습니다. 일반적인 쿼리 메소드와는 달리, 메소드 선언에 기술적인 조건을 추가할 수 있습니다.

velog.io

 

 

일단 이정도...? 

처음보는 객체와 개념이 많아 완벽하게 의미을 이해하진 못했지만 동적 쿼리가 있다는 것을 배웠다..! 

사용법도 얼추? 알 것 같다!

반응형

'개발 프로젝트 > Hot Docs' 카테고리의 다른 글

[트러블슈팅] 템플릿 인기순 출력  (1) 2025.02.09
[트러블 슈팅] Swagger 500 error  (1) 2025.02.02