2023. 8. 1. 16:54ㆍ트러블슈팅
문제점 발견
기존 필터링,정렬 코드
private List<LikeablePerson> findByIdFilteredAndSortedList(Long instaMemberId, int sortCode, String gender, Integer attractiveTypeCode) {
switch (sortCode) {
case 1:
return likeablePersonRepository.findByIdFilteredAndSortedOrderByCreateDateDesc(instaMemberId, gender, attractiveTypeCode);
case 2:
return likeablePersonRepository.findByIdFilteredAndSortedOrderByHotOfFromInstaMemberAsc(instaMemberId, gender, attractiveTypeCode);
case 3:
return likeablePersonRepository.findByIdFilteredAndSortedOrderByHotOfFromInstaMemberDesc(instaMemberId, gender, attractiveTypeCode);
case 4:
return likeablePersonRepository.findByIdFilteredAndSortedOrderByGenderOfFromInstaMemberAsc(instaMemberId, gender, attractiveTypeCode);
case 5:
return likeablePersonRepository.findByIdFilteredAndSortedOrderByAttractiveTypeCodeAsc(instaMemberId, gender, attractiveTypeCode);
default:
return likeablePersonRepository.findByIdFilteredAndSortedOrderByCreateDateAsc(instaMemberId, gender, attractiveTypeCode);
}
}
//최신순
@Query("select L from LikeablePerson L inner join L.toInstaMember T inner join L.fromInstaMember F on T.id=:id where (:attractiveTypeCode is null or L.attractiveTypeCode=:attractiveTypeCode) and (:gender is null or F.gender=:gender) order by L.createDate asc")
List<LikeablePerson> findByIdFilteredAndSortedOrderByCreateDateAsc(@Param("id") Long id , @Param("gender") String gender, @Param("attractiveTypeCode") Integer attractiveTypeCode);
//오래된순
@Query("select L from LikeablePerson L inner join L.toInstaMember T inner join L.fromInstaMember F on T.id=:id where (:attractiveTypeCode is null or L.attractiveTypeCode=:attractiveTypeCode) and (:gender is null or F.gender=:gender) order by L.createDate desc")
List<LikeablePerson> findByIdFilteredAndSortedOrderByCreateDateDesc(@Param("id") Long id , @Param("gender") String gender, @Param("attractiveTypeCode") Integer attractiveTypeCode);
//인기순
@Query("select L from LikeablePerson L inner join L.toInstaMember T inner join L.fromInstaMember F on T.id=:id where (:attractiveTypeCode is null or L.attractiveTypeCode=:attractiveTypeCode) and (:gender is null or F.gender=:gender) ORDER BY SIZE(F.toLikeablePeople) desc")
List<LikeablePerson> findByIdFilteredAndSortedOrderByHotOfFromInstaMemberAsc(@Param("id") Long id , @Param("gender") String gender, @Param("attractiveTypeCode") Integer attractiveTypeCode);
//인기적은순
@Query("select L from LikeablePerson L inner join L.toInstaMember T inner join L.fromInstaMember F on T.id=:id where (:attractiveTypeCode is null or L.attractiveTypeCode=:attractiveTypeCode) and (:gender is null or F.gender=:gender) ORDER BY SIZE(F.toLikeablePeople) asc")
List<LikeablePerson> findByIdFilteredAndSortedOrderByHotOfFromInstaMemberDesc(@Param("id") Long id, @Param("gender") String gender, @Param("attractiveTypeCode") Integer attractiveTypeCode);
//성별순
@Query("select L from LikeablePerson L inner join L.toInstaMember T inner join L.fromInstaMember F on T.id=:id where (:attractiveTypeCode is null or L.attractiveTypeCode=:attractiveTypeCode) and (:gender is null or F.gender=:gender) order by F.gender asc")
List<LikeablePerson> findByIdFilteredAndSortedOrderByGenderOfFromInstaMemberAsc(@Param("id") Long id, @Param("gender") String gender, @Param("attractiveTypeCode") Integer attractiveTypeCode);
//호감사유순
@Query("select L from LikeablePerson L inner join L.toInstaMember T inner join L.fromInstaMember F on T.id=:id where (:attractiveTypeCode is null or L.attractiveTypeCode=:attractiveTypeCode) and (:gender is null or F.gender=:gender) order by L.attractiveTypeCode asc")
List<LikeablePerson> findByIdFilteredAndSortedOrderByAttractiveTypeCodeAsc(@Param("id") Long id, @Param("gender") String gender, @Param("attractiveTypeCode") Integer attractiveTypeCode);
}
위 코드는 instaMemberId에 따른 인스타계정을 호감표시한 목록을 나타내는 코드이다.
gender(성별)과 attractiveTypeCode(호감코드) 로 필터링을 거쳐 sortCode(정렬코드)에 따라 정렬되어서 리스팅 된다.
JPQL로 처리할 수 있는 것은 필터링이 한계이며, 그마저도 가독성이 떨어진다고 느껴졌다.
그래서 정렬에 따른 메소드를 여러 개를 구현했다.
한 눈에 봐도 가독성이 떨어지고 복잡도가 높은 것이 느껴진다.
만약 서비스가 확장되고 정렬 방식에 대한 요구사항이 들어온다면 계속해서 메서드를 늘려나가야 한다.
이러한 구조는 유지보수 및 확장성에도 적합하지 않다.
--> 개선의 여지가 있다.
그래서 Querydsl 을 도입하여 코드를 개선하여 가독성을 높이고자 한다.
Querydsl을 선택한 이유는 default값 설정이 용이하고, null값이 들어왔을 때에 대비하기가 쉬워 오류를 방지할 수 있다.
무엇보다 메소드 하나로 모든 필터링과 동적 정렬을 처리할 수 있기 때문이다.
개선 작업
private BooleanExpression eqGender(String gender) {
if (StringUtils.isEmpty(gender)) {
return null;
}
return likeablePerson.fromInstaMember.gender.eq(gender);
}
private BooleanExpression eqAttractiveTypeCode(Integer attractiveTypeCode) {
if (ObjectUtils.isEmpty(attractiveTypeCode)) {
return null;
}
return likeablePerson.attractiveTypeCode.eq(attractiveTypeCode);
}
위와 같은 방법으로 파라미터가 들어올 경우와 들어오지 않을 경우가 있을 수 있기에, 값의 유무 체크 후에 조건절을 검사하게 된다.
값이 없는 경우는 null을 반환함으로 해당 eq조건절을 생략한다.
private static OrderSpecifier<?>
orderSelector(Integer sortCode) {
if (sortCode == null) {
return likeablePerson.createDate.desc();
}
return switch (sortCode) {
case 2 -> likeablePerson.fromInstaMember.toLikeablePeopleCount.asc();
case 3 -> likeablePerson.fromInstaMember.toLikeablePeopleCount.desc();
case 4 -> likeablePerson.fromInstaMember.gender.asc();
case 5 -> likeablePerson.attractiveTypeCode.desc();
default -> likeablePerson.createDate.asc();
};
}
정렬은 OrderSpecifier 객체를 사용해서 구현했다.
sortCode가 따로 들어오지 않을 경우에는 null 체크를 함으로써 default인 최신순으로 정렬되도록 했다.
@Override
public List<LikeablePersonResponse> findByIdFilteredAndSorted(Long instaMemberId, Integer sortCode, String gender, Integer attractiveTypeCode) {
List<LikeablePersonResponse> result = queryFactory
.select(new QLikeablePersonResponse(
likeablePerson.createDate,
likeablePerson.id,
likeablePerson.fromInstaMember.id,
likeablePerson.toInstaMember.id,
likeablePerson.fromInstaMember.gender,
likeablePerson.attractiveTypeCode
)
)
.from(likeablePerson)
.where(
likeablePerson.toInstaMember.id.eq(instaMemberId),
eqGender(gender),
eqAttractiveTypeCode(attractiveTypeCode)
)
.orderBy(orderSelector(sortCode))
.fetch();
return result;
}
전체 코드이다. Dto에 필요한 값을 바로 찾아서 매칭시켜줌으로 Entity의 캡슐화를 더 보장할 수 있었다.
다음은 리팩토링 후에 파라미터에 따라서 생성되는 쿼리를 살펴봤다.
아무런 필터링을 하지 않고 목록을 조회하는 경우는 아래와 같다.
where 절을 보면 반드시 필요한 id값 비교밖에 없는 것을 확인할 수 있다.
*/ select
l1_0.create_date,
l1_0.id,
l1_0.from_insta_member_id,
l1_0.to_insta_member_id,
f1_0.gender,
l1_0.attractive_type_code
from
likeable_person l1_0
join
insta_member f1_0
on f1_0.id=l1_0.from_insta_member_id
where
l1_0.to_insta_member_id=?
order by
l1_0.create_date desc
필터링과 정렬을 선택해보았다.
// where절을 보면 id뿐만 아니라 호감표시를 한 대상의 gender, attractiveTypeCode오 조건절에 사용되었고,
// order by에서도 sortCode에 따른 정렬 쿼리가 날라가는 것을 확인할 수 있다.
*/ select
l1_0.create_date,
l1_0.id,
l1_0.from_insta_member_id,
l1_0.to_insta_member_id,
f1_0.gender,
l1_0.attractive_type_code
from
likeable_person l1_0
left join
insta_member f1_0
on f1_0.id=l1_0.from_insta_member_id
where
l1_0.to_insta_member_id=?
and f1_0.gender=?
and l1_0.attractive_type_code=?
order by
f1_0.to_likeable_people_count desc
'트러블슈팅' 카테고리의 다른 글
[트러블슈팅] TrendPick 프로젝트 주문 생성 시 상품 재고 처리 동시성 이슈 (1) | 2023.10.04 |
---|---|
[트러블슈팅] TrendPick 프로젝트 / 추천 시스템 Spring batch 도입 및 이슈 (0) | 2023.08.29 |
[트러블슈팅] ILikeYou 프로젝트 - 스프링 이벤트(pub/sub) 처리에서 트랜잭션 관리 (0) | 2023.08.02 |
[트러블슈팅] TrendPick 프로젝트 추천 기능 구현 / 연관관계 및 쿼리 이슈 (0) | 2023.06.07 |