https://github.com/Eui9179/Mission_LeeEuiChan/tree/main
미션 프로젝트인 그램그램을 진행하면서 나를 좋아하는 사람들의 필터링 기능을 적용해야 했다.
총 세가지 기능을 추가해야 한다.
https://localhost/usr/likeablePerson/toList?
gender=M&
attractiveTypeCode=&
sortCode=1
쿼리 파라미터로 넘어오기 때문에 @ModelAttribute
로 리퀘스트 DTO를 만들어 주었다.
@Setter
@Getter
public class SearchFilter {
private String gender;
private Integer attractiveTypeCode;
private Integer sortCode = 1; // 기본값
}
기본적으로 sortCode의 기본값이 1이기 때문에 넘어오지 않아도 1로 설정되게 하였다.
@PreAuthorize("isAuthenticated()")
@GetMapping("/toList")
public String showToList(Model model, SearchFilter searchFilter) {
InstaMember instaMember = rq.getMember().getInstaMember();
// 인스타인증을 했는지 체크
if (instaMember != null) {
// 해당 인스타회원이 좋아하는 사람들 목록
RsData likeablePeople = likeablePersonService.findByToInstaMemberWithFilter(
instaMember, searchFilter.gender, searchFilter.attractiveTypeCode, searchFilter.sortCode
);
model.addAttribute("likeablePeople", likeablePeople.getData());
}
return "usr/likeablePerson/toList";
}
Controller에서 @ModelAttribute
로 SearchFilter를 받았다.
검색 조건이 추가되면 DTO와 Controller, Service 등 코드를 수정해야되는 부분이 많은데 Map으로 받아서 처리하는 게 좋을까?
public RsData<List<LikeablePerson>> findByToInstaMemberWithFilter(
InstaMember toInstaMember, String gender, Integer attractiveTypeCode, int sortCode) {
List<LikeablePerson> likeablePeople = likeablePersonRepository
.findQslByToInstaMemberWithFilter(toInstaMember, gender, attractiveTypeCode, sortCode);
return RsData.of("S-1", "", likeablePeople);
}
sortCode는 null을 허용하지 않기 위해 int로 설정하였다.
public interface LikeablePersonRepositoryCustom {
Optional<LikeablePerson> findQslByFromInstaMemberIdAndToInstaMember_username(long fromInstaMemberId, String toInstaMemberUsername);
List<LikeablePerson> findQslByToInstaMemberWithFilter(
InstaMember toInstaMember, String gender, Integer attractiveTypeCode, int sortCode);
}
@Override
public List<LikeablePerson> findQslByToInstaMemberWithFilter(
InstaMember toInstaMember, String gender, Integer attractiveTypeCode, int sortCode) {
if (toInstaMember == null) {
return null;
}
return jpaQueryFactory
.selectFrom(likeablePerson)
.where(
eqToInstaMember(toInstaMember),
eqGender(gender),
eqAttractiveTypeCode(attractiveTypeCode)
)
.orderBy(resolveSortCode(sortCode))
.fetch();
}
.where()
and로 처리하였는데 eqToInstaMember()
와 eqGender()
, eqAttractiveTypeCode()
가 null이라면 쿼리의 조건을 처리하지 않고 빼는 특성을 사용하여 구현하였다.
아래 코드를 보면 BooleanExpression
만약 null이거나 빈 값으면 null을 리턴하여서 조건에서 제외되게 하였다.
@RequiredArgsConstructor
@Slf4j
public class LikeablePersonRepositoryImpl implements LikeablePersonRepositoryCustom {
private final JPAQueryFactory jpaQueryFactory;
...생략
private BooleanExpression eqToInstaMember(InstaMember toInstaMember) {
return likeablePerson.toInstaMember.eq(toInstaMember);
}
private BooleanExpression eqGender(String gender) {
if (gender == null || gender.equals("")) {
return null;
}
return likeablePerson.fromInstaMember.gender.eq(gender);
}
private BooleanExpression eqAttractiveTypeCode(Integer attractiveTypeCode) {
if (attractiveTypeCode == null) {
return null;
}
return likeablePerson.attractiveTypeCode.eq(attractiveTypeCode);
}
...생략
}
@RequiredArgsConstructor
@Slf4j
public class LikeablePersonRepositoryImpl implements LikeablePersonRepositoryCustom {
private final JPAQueryFactory jpaQueryFactory;
...생략
private OrderSpecifier[] resolveSortCode(Integer sortCode) {
List<OrderSpecifier> orderSpecifiers = new ArrayList<>();
switch (sortCode) {
case 2 -> orderSpecifiers.add(likeablePerson.modifyDate.asc());
case 3 -> orderSpecifiers.add(likeablePerson.fromInstaMember.toLikeablePeople.size().desc());
case 4 -> orderSpecifiers.add(likeablePerson.fromInstaMember.toLikeablePeople.size().asc());
case 5 -> {
orderSpecifiers.add(likeablePerson.fromInstaMember.gender.desc());
orderSpecifiers.add(likeablePerson.modifyDate.desc());
}
case 6 -> {
orderSpecifiers.add(likeablePerson.attractiveTypeCode.asc());
orderSpecifiers.add(likeablePerson.modifyDate.desc());
}
default -> orderSpecifiers.add(likeablePerson.modifyDate.desc());
}
return orderSpecifiers.toArray(new OrderSpecifier[orderSpecifiers.size()]);
}
}
성별과 호감사유로 정렬할 경우 최신순으로 같이 정렬해야 하기때문에 .orderBy(resolveSortCode(sortCode))
에 OrderSpectifier[] 다중 정렬 조건을 넣어 처리하였다.
기존에 존재했던 데이터로 성별, 호감사유 필터와 인기도 많은 순으로 정렬하는 테스트 코드를 작성하였다.
필터링 - 성별
@Test
@DisplayName("필터링 기능 테스트 - 성별")
void t013() {
//given
String gender = "W";
String instaUserName = "insta_user6";
InstaMember toInstaMember = instaMemberService.findByUsername(instaUserName)
.orElseThrow(() -> new RuntimeException("데이터가 없습니다. NotProd.java 참조"));
//when
List<LikeablePerson> likeablePeople = likeablePersonService
.findByToInstaMemberWithFilter(toInstaMember, gender, null, 1)
.getData();
//then
assertThat(likeablePeople.size()).isEqualTo(2);
}
기존 데이터에 나를 좋아하는 사람이 남자 1명, 여자 2명으로 설정되어 있으므로 여자를 넣었을 때, 두명이 나와야 한다.
필터링 - 호감사유
@Test
@DisplayName("필터링 기능 테스트 - 호감사유")
void t014() {
//given
Integer attractiveTypeCode = 1;
String instaUserName = "insta_user6";
InstaMember toInstaMember = instaMemberService.findByUsername(instaUserName)
.orElseThrow(() -> new RuntimeException("데이터가 없습니다. NotProd.java 참조"));
//when
List<LikeablePerson> likeablePeople = likeablePersonService
.findByToInstaMemberWithFilter(toInstaMember, null, attractiveTypeCode, 1)
.getData();
//then
likeablePeople.forEach(
lp -> assertThat(lp.getAttractiveTypeCode()).isEqualTo(attractiveTypeCode)
);
}
기존 데이터에 호감사유가 1인 유저가 2명 있었고 1로 필터링 하였을 때 모든 데이터가 호감사유가 1이어야 한다.
정렬 - 인기도
@Test
@DisplayName("정렬 기능 테스트 - 정렬 - 인기도 많은 순")
void t015() {
//given
ToListSearchForm toListSearchForm = ToListSearchForm.builder()
.gender(null)
.attractiveTypeCode(null)
.sortCode(3)
.build();
//when
List<LikeablePerson> likeablePeople = likeablePersonService
.findByToInstaMemberWithFilter(toInstaMember, toListSearchForm)
.getData();
//then
assertThat(likeablePeople).isSortedAccordingTo(
Comparator.comparing(e -> e.getFromInstaMember().getLikesCount(), Comparator.reverseOrder())
);
}
나를 좋아하는 유저의 호감 표시를 받은 개수로 정렬하였다. Comparator의 comparing()을 사용하여서 정렬이 되었는지 테스트하였다.
인기도 적은 순
인기 많은 순과 마찬가지로 역순을 테스트하였다.
@Test
@DisplayName("정렬 기능 테스트 - 정렬 - 인기도 적은 순")
void t016() {
//given
ToListSearchForm toListSearchForm = ToListSearchForm.builder()
.gender(null)
.attractiveTypeCode(null)
.sortCode(4)
.build();
//when
List<LikeablePerson> likeablePeople = likeablePersonService
.findByToInstaMemberWithFilter(toInstaMember, toListSearchForm)
.getData();
//then
assertThat(likeablePeople).isSortedAccordingTo(
Comparator.comparing(e -> e.getFromInstaMember().getLikesCount())
);
}
이외에도 호감사유 정렬, 성별 정렬, querydsl 테스트 등 진행하였다.