자세한 정보는 ⬇️아래 포스팅을 참고해주세요 !
https://velog.io/@dooo_it_ly/SpringBoot-Paging-구현
프로젝트를 진행하면서, 간단한 검색 기능을 구현해보았습니다!
makeupName
, info
field에서 검색어 찾기@Builder
@Getter
@AllArgsConstructor
@NoArgsConstructor
@Entity
public class Portfolio extends BaseEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "portfolio_id")
private Long portfolioId;
@ManyToOne
@JoinColumn(name="user_id", nullable = false)
private Artist artist;
@Enumerated(EnumType.STRING)
@Column(nullable = false)
private Category category;
@Column(nullable = false)
private String makeupName;
@Column(nullable = false)
private int price;
@Column(nullable = false)
private String info;
@OneToMany(cascade = CascadeType.ALL, mappedBy = "portfolio")
private List<PortfolioImg> portfolioImgList;
@Column
private String averageStars;
@Column(nullable = false, columnDefinition = "TINYINT(1) default 0")
private boolean isBlock;
@OneToMany(cascade = CascadeType.ALL, mappedBy = "portfolio")
private List<Review> reviewList;
검색어, 페이지 번호, 정렬 기준을 인자로 받아 서비스에 넘겨줍니다
@PageDefault
를 이용해 인자를 받는 방식도 존재하나, 페이지 크기를 임의로 지정해주었기 때문에 해당 방법을 사용했습니다
@Operation(summary = "메이크업 검색", description = "메이크업을 검색/최근 검색어로 검색하는 API입니다.")
@GetMapping("")
public ApiResponse search(@RequestParam(value = "query") String query,
@RequestParam(value = "page", defaultValue = "0", required = false) int page,
@RequestParam(value = "sort", defaultValue = "desc") String sort){
return ApiResponse.SuccessResponse(SuccessStatus.SEARCH_GET, searchService.search(query, page, sort));
}
Pageable
인스턴스 만들기Page<>
리턴PageDto
로 결과 반환 //검색
public PortfolioPageDto search(String query, int page, String sortBy){
Pageable pageable = setPageRequest(page, sortBy);
//query 검색
Page<Portfolio> portfolioPage = portfolioRepository.search(query, pageable);
return PortfolioPageDto.from(portfolioPage);
}
위의 코드에서 사용한 setPageRequest()
함수입니다
sortBy
에 따라 정렬 기준을 설정한 후, 별점 높은 순 정렬을 추가로 설정해줍니다PageRequest.of
를 사용해 Pageable 인스턴스 생성of
메소드에 인자로 넘김 //검색하기 정렬 기준 설정
private Pageable setPageRequest(int page, String sortBy){
//정렬 기준 설정
Sort sort = switch (sortBy) {
case "desc" -> Sort.by("price").descending();
case "asc" -> Sort.by("price").ascending();
case "review" -> Sort.by("averageStars").descending();
case "recent" -> Sort.by("createdAt").descending();
default -> throw new GlobalException(ErrorStatus.INVALID_SORT_CRITERIA);
};
//별점 높은 순 정렬 추가
Sort finalSort = sort.and(Sort.by("averageStars").descending());
return PageRequest.of(page, 30, finalSort);
}
repository에서는 인자로 들어오는 query
와 Pageable
인터페이스를 기반으로 Page<Portfolio>
를 리턴합니다
현재 이 pageable에는 요청받은 페이지 번호와 페이지 크기, 정렬 기준이 설정되어 있습니다
LIKE
를 이용해 makeUpName
과 Info
에서 검색어를 찾고 블락 상태가 아닌 포트폴리오만 필터링해 결과를 반환합니다
@Query("SELECT p FROM Portfolio p " +
"WHERE (p.makeupName LIKE %:query% OR p.info LIKE %:query%) " +
"AND p.isBlock = false" )
Page<Portfolio> search(@Param("query") String query, Pageable pageable);
⬇️아래 포스팅에서 사용한 PageDto와 동일한 dto를 사용했습니다
https://velog.io/@dooo_it_ly/SpringBoot-Paging-구현
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class PortfolioPageDto {
private List<PortfolioDto> content;
private int currentPage; //현재 페이지 번호
private int pageSize; //페이지 크기
private int totalNumber; //전체 메이크업 개수
private int totalPage; //전체 페이지 개수
public static PortfolioPageDto from(Page<Portfolio> page){
//검색 결과가 없을 시
if(page.getContent().isEmpty())
throw new GlobalException(ErrorStatus.SEARCH_NOT_FOUNT);
List<PortfolioDto> content = page.stream()
.map(PortfolioDto::from)
.toList();
return PortfolioPageDto.builder()
.content(content)
.pageSize(page.getSize())
.currentPage(page.getNumber())
.totalNumber(page.getNumberOfElements())
.totalPage(page.getTotalPages())
.build();
}
}
qeury를 p1으로 지정했을 때, p1, p11, p12 등 해당 검색어가 포함된 포트폴리오들이 리턴되는 것을 확인할 수 있습니다
{
"result": "SUCCESS",
"message": "조회가 완료되었습니다",
"data": {
"content": [
{
"portfolioId": 1,
"category": "WEDDING",
"artistNickName": "Artist1",
"userId": 1,
"makeupName": "p1",
"price": 300000,
"makeupLocation": "SHOP",
"shopLocation": "",
"region": [
"JONGNO",
"DONGJAK"
],
"isBlock": false,
"averageStars": "0.00",
"reviewCount": 0,
"portfolioImgDtoList": [
{
"portfolioImgId": 1,
"portfolioImgSrc": "string",
"delete": false
},
{
"portfolioImgId": 2,
"portfolioImgSrc": "string2",
"delete": false
}
]
},
{
"portfolioId": 10,
"category": "WEDDING",
"artistNickName": "Artist1",
"userId": 1,
"makeupName": "p10",
"price": 270000,
"makeupLocation": "SHOP",
"shopLocation": "",
"region": [
"JONGNO",
"DONGJAK"
],
"isBlock": false,
"averageStars": "0.00",
"reviewCount": 0,
"portfolioImgDtoList": [
{
"portfolioImgId": 19,
"portfolioImgSrc": "string",
"delete": false
},
{
"portfolioImgId": 20,
"portfolioImgSrc": "string2",
"delete": false
}
]
},
{
"portfolioId": 11,
"category": "WEDDING",
"artistNickName": "Artist1",
"userId": 1,
"makeupName": "p11",
"price": 370000,
"makeupLocation": "SHOP",
"shopLocation": "",
"region": [
"JONGNO",
"DONGJAK"
],
"isBlock": false,
"averageStars": "0.00",
"reviewCount": 0,
"portfolioImgDtoList": [
{
"portfolioImgId": 21,
"portfolioImgSrc": "string",
"delete": false
},
{
"portfolioImgId": 22,
"portfolioImgSrc": "string2",
"delete": false
}
]
},
{
"portfolioId": 12,
"category": "WEDDING",
"artistNickName": "Artist1",
"userId": 1,
"makeupName": "p12",
"price": 410000,
"makeupLocation": "SHOP",
"shopLocation": "",
"region": [
"JONGNO",
"DONGJAK"
],
"isBlock": false,
"averageStars": "0.00",
"reviewCount": 0,
"portfolioImgDtoList": [
{
"portfolioImgId": 23,
"portfolioImgSrc": "string",
"delete": false
},
{
"portfolioImgId": 24,
"portfolioImgSrc": "string2",
"delete": false
}
]
},
{
"portfolioId": 13,
"category": "WEDDING",
"artistNickName": "Artist1",
"userId": 1,
"makeupName": "p13",
"price": 410000,
"makeupLocation": "SHOP",
"shopLocation": "",
"region": [
"JONGNO",
"DONGJAK"
],
"isBlock": false,
"averageStars": "0.00",
"reviewCount": 0,
"portfolioImgDtoList": [
{
"portfolioImgId": 25,
"portfolioImgSrc": "string",
"delete": false
},
{
"portfolioImgId": 26,
"portfolioImgSrc": "string2",
"delete": false
}
]
},
{
"portfolioId": 14,
"category": "WEDDING",
"artistNickName": "Artist1",
"userId": 1,
"makeupName": "p14",
"price": 400000,
"makeupLocation": "SHOP",
"shopLocation": "",
"region": [
"JONGNO",
"DONGJAK"
],
"isBlock": false,
"averageStars": "0.00",
"reviewCount": 0,
"portfolioImgDtoList": [
{
"portfolioImgId": 27,
"portfolioImgSrc": "string",
"delete": false
},
{
"portfolioImgId": 28,
"portfolioImgSrc": "string2",
"delete": false
}
]
},
{
"portfolioId": 15,
"category": "WEDDING",
"artistNickName": "Artist1",
"userId": 1,
"makeupName": "p15",
"price": 430000,
"makeupLocation": "SHOP",
"shopLocation": "",
"region": [
"JONGNO",
"DONGJAK"
],
"isBlock": false,
"averageStars": "0.00",
"reviewCount": 0,
"portfolioImgDtoList": [
{
"portfolioImgId": 29,
"portfolioImgSrc": "string",
"delete": false
},
{
"portfolioImgId": 30,
"portfolioImgSrc": "string2",
"delete": false
}
]
},
{
"portfolioId": 16,
"category": "WEDDING",
"artistNickName": "Artist1",
"userId": 1,
"makeupName": "p16",
"price": 430000,
"makeupLocation": "SHOP",
"shopLocation": "",
"region": [
"JONGNO",
"DONGJAK"
],
"isBlock": false,
"averageStars": "0.00",
"reviewCount": 0,
"portfolioImgDtoList": [
{
"portfolioImgId": 31,
"portfolioImgSrc": "string",
"delete": false
},
{
"portfolioImgId": 32,
"portfolioImgSrc": "string2",
"delete": false
}
]
},
{
"portfolioId": 17,
"category": "ETC",
"artistNickName": "Artist1",
"userId": 1,
"makeupName": "p17",
"price": 430000,
"makeupLocation": "SHOP",
"shopLocation": "",
"region": [
"JONGNO",
"DONGJAK"
],
"isBlock": false,
"averageStars": "0.00",
"reviewCount": 0,
"portfolioImgDtoList": [
{
"portfolioImgId": 33,
"portfolioImgSrc": "string",
"delete": false
},
{
"portfolioImgId": 34,
"portfolioImgSrc": "string2",
"delete": false
}
]
},
{
"portfolioId": 18,
"category": "ETC",
"artistNickName": "Artist1",
"userId": 1,
"makeupName": "p18",
"price": 430000,
"makeupLocation": "SHOP",
"shopLocation": "",
"region": [
"JONGNO",
"DONGJAK"
],
"isBlock": false,
"averageStars": "0.00",
"reviewCount": 0,
"portfolioImgDtoList": [
{
"portfolioImgId": 35,
"portfolioImgSrc": "string",
"delete": false
},
{
"portfolioImgId": 36,
"portfolioImgSrc": "string2",
"delete": false
}
]
},
{
"portfolioId": 19,
"category": "DAILY",
"artistNickName": "Artist1",
"userId": 1,
"makeupName": "p19",
"price": 230000,
"makeupLocation": "SHOP",
"shopLocation": "",
"region": [
"JONGNO",
"DONGJAK"
],
"isBlock": false,
"averageStars": "0.00",
"reviewCount": 0,
"portfolioImgDtoList": [
{
"portfolioImgId": 37,
"portfolioImgSrc": "string",
"delete": false
},
{
"portfolioImgId": 38,
"portfolioImgSrc": "string2",
"delete": false
}
]
}
],
"currentPage": 0,
"pageSize": 30,
"totalNumber": 11,
"totalPage": 1
},
"statusCode": 200
}