쌩로그

검색 조건을 가진 Condition클래스 작성시 바인딩 되지 않던 문제 해결 본문

TroubleShooting & 고민/BE

검색 조건을 가진 Condition클래스 작성시 바인딩 되지 않던 문제 해결

.쌩수. 2023. 7. 23. 11:47
반응형

목록

  1. 포스팅 개요

  2. 본론
      2-1. 구성 클래스
      2-2. 문제 살펴보기
      2-3. 문제 해결하기
        2-3-1. 번외 @Getter 사용해보기
        2-3-2. @Setter로 진짜 문제 해결하기

  3. 요약

1. 포스팅 개요

이번 프로젝트에서 동행 도메인 같은 경우,
쿼리스트링으로, 지역과 날짜(동행시작 날짜)를 검색해서 조회하는 기능을 구현했는데,
쿼리스트링으로 검색 조건을 넣더라도,
검색조건을 가지는 클래스(AccompanySearchCondition)의 필드와 바인딩 되지 않던 문제를 해결하는 포스팅이다.

결론부터 말하자면 @Setter를 쓰면 된다.

2. 본론

2-1. 구성 클래스

먼저 관련된 클래스 코드를 보면 다음과 같다.

Controller

// 동행 전체 조회(날짜, 지역)  
@GetMapping  
public ResponseEntity searchLocalAndDate(AccompanySearchCondition accompanySearchCondition,  
                                         @RequestParam(name = "page", defaultValue = "1") Integer page) throws ParseException {  
    return ResponseEntity.ok(accompanyService.findByLocalAndDate(accompanySearchCondition, page));  
}

검색 조건 클래스

@AllArgsConstructor  
@NoArgsConstructor  
public class AccompanySearchCondition {  
    private String local;  
    @DateTimeFormat(pattern = "yyyy-MM-dd")  
    private LocalDate startDate;  

    public Local getLocal() {  
        return this.local == null ? null : Local.findByLocal(this.local);  
    }  

    public LocalDate getDate() {  
        return startDate;  
    }  
}

검색 조건을 가지고 select 쿼리를 날리는 QueryDsl(with 페이지)

public PageResponseDto<AccompanyResponseListDto> searchByLocalAndDate(AccompanySearchCondition accompanySearchDto, Integer page) throws ParseException {  
    List<AccompanyResponseListDto> accompanyDtoList;  
    accompanyDtoList = queryFactory  
            .select(new QAccompanyResponseListDto(  
                    accompany.accompanyId.as("accompanyId"),  
                    accompany.nickname,  
                    accompany.local.stringValue(),  
                    accompany.accompanyMemberList.size().longValue(),  
                    accompany.maxMemberNum,  
                    accompany.accompanyStartDate,  
                    accompany.accompanyEndDate,  
                    accompany.title,  
                    accompany.recruitComplete,  
                    accompany.createdAt))  
            .from(accompany)  
            .where(  
                    localEq(accompanySearchDto.getLocal()),  
                    startDateEq(accompanySearchDto.getDate())  
            )  
            .offset(page*10-10)  // 해당 페이지의 시작데이터 
            .limit(10)  
            .fetch();  

    Long totalElements = queryFactory  
            .select(accompany.count())  
            .from(accompany)  
            .where(  
                    localEq(accompanySearchDto.getLocal()),  
                    startDateEq(accompanySearchDto.getDate())  
            ).fetchOne();  

    Long totalPage = (totalElements/10) + (totalElements%10 > 0 ? 1 : 0);  // totalPage  

    // 마지막 페이지보다 작으면 10 아니라면, 총 요소갯수에서 % 10(페이지 사이즈)  
    Long currentPageElements = page < totalPage ? 10 : totalElements % 10;  

    return PageResponseDto.of(accompanyDtoList, totalPage, totalElements, currentPageElements, page);  
}

// local이 있는지 확인 (AccompanySearchCondition으로부터 받아옴)
private BooleanExpression localEq(Local local) {  
    return Objects.nonNull(local) ? accompany.local.eq(local) : null;  
}  

// startDate가 있는지 확인 (AccompanySearchCondition으로부터 받아옴)
private BooleanExpression startDateEq(LocalDate startDate) {  
    return Objects.nonNull(startDate) ? accompany.accompanyStartDate.eq(startDate) : null;  
}

2-2. 문제 살펴보기

2-1 처럼 클래스가 구성되어있을 때 어떻게 받아오는지 보자.

검색조건을 쿼리스트링으로 주면,
AccompanySearchCondition클래스에서
지역을 뜻하는 local, 여행시작날짜를 의미하는 필드인 startDate 필드가 검색조건으로 준 값(value)과 각각 바인딩되어 변수에 담겨야한다.

컨트롤러에는 쿼리스트링이 잘 받아오는지 확인하기 위해서
@Slf4j를 이용해서 log.info를 다음과 같이 발.라.놨다.

다음은 검색조건을 받는 클래스이다.

그리고 다음과 같이 데이터가 두 건 있다고 가정하자...

✅참고로 다른 데이터들 보다 검색조건을 받는 지역을 의미하는 local과, 여행 시작날짜를 뜻하는 변수 accompanyStartDate를 주목하자.

두 데이터 다 지역은 서울이다.
만약, 제주도를 검색하면 결과는 아무것도 없어야 한다.

하지만, 결과는 서울이 잘 나온다. ..;;;

음.. 그럼 서울로 검색하면 과연 값이 잘 들어갈까?

뭔가... 잘 들어간 것 처럼 보인다.
하지만 로그를 보면... null이 나온다.

;;;;;

흠... 그럼 날짜 조회는 어떨까?
아까 두 개의 데이터는

지역은 서울, 
날짜는 2023-07-18

이다.

2023-07-20으로 조회해보자.

20일로 조회했지만, 18일이 나온다..
로그를 확인해보자.

결과는 null이다.

2-3. 문제 해결하기

그럼 과연 어떤 것이 문제일까?
포스팅 개요에서 말했지만, 다시 말하자면 @Setter를 쓰면 된다.

@Setter를 넣어주지 않으면, 쿼리스트링으로 받는 값과 클래스의 각 필드가 바인딩되지 않는다.

2-3-1. 번외 @Getter 사용해보기

@Setter를 사용하기 이전에 @Getter를 넣어보자

참고로 그리도 유용한 @Getter가 여기선 쓸모없지만,
그냥 실험차 보는 것이다.

위와 같이 @Setter는 주석처리하고, @Getter만 적어줬다.
데이터는 다음과 같이 있다.

이번엔 3개의 데이터다.

이번에도 제주도로 검색해보자.

서울로만 3개인데, 제주도로 검색하니깐 서울 3개가 나왔다.
(사실 위의 3개의 데이터는 제주도로 검색한 결과였다..그래서 스샷이 같음;;)

로그는 다음과 같다...

예상했지만, 역시 null이다.

2-3-2. @Setter로 진짜 문제 해결하기

이제 @Setter로 가자
@Getter는 쓸모없으니 지우자.

이번엔 '서울', '2023-07-18'만 4개를 넣어놨다.
(accompanyId가 4이다.포스트맨 send 광클)

@Setter로 바꾼 이후,
'서울', '2023-07-18'만 4개있을 때
지역으로 '제주도'를 검색하면,,?

이야아!!!!

이야아!!!!

오...아무것도 뜨지 않는다!!!
(참고로 지금 나오는 결과 값들은 페이지 네이션 정보들이다.)

드디어 성공!!!!

다시 살펴보자.

하나도 나오지 않는다.
안 나오는 게 맞다.
왜냐하면 우리에게 있는 데이터는 '서울','2023-07-18' 만 4건 있다.

이제 로그도 한번 보자.

크...잘 들어간다~

그리고 서울로 한 번 검색해보겠다.

지역을 서울로 검색하면 4건의 데이터가 다 나온다.
그럼 왜 다 나올까??
다 나오는 게 맞다... 아까 말했지만,
우리에게 있는 데이터는 '서울','2023-07-18' 만 4건 있다.
로그를 보자.

역시 잘 들어간다.

그럼 날짜도 잘 들어갈까?

데이터가 나오지 않는다.
왜냐하면 검색은 17일로 했지만, 우리에게 있는 데이터는 4건 모두 18일이다.
로그를 확인하면,

로그에도 값이 잘 들어왔다고 알려준다.
이제 Id가 2,3인 데이터를 수정해보려고 한다.

Id 2는 부산, 08-01,
Id 3은 제주도 08-01
이렇게 수정해보겠다.

수정을 한 결과다.

4건의 데이터는 다음과 같다.

Id 지역 날짜
1 서울 07-18
2 부산 08-01
3 제주도 08-01
4 서울 07-18

이제 조회 결과를 보자.

'부산'으로 조회해보자.

부산에 해당하는 데이터가 잘 나온다.
다음은 '제주도'로 검색해보자.

제주도에 해당하는 데이터도 잘 나온다.
이제 '서울'로 조회해보자

크... 서울에 해당하는 데이터 역시 잘 나온다.

이제 날짜다.
날짜 '7월18일'을 조회해보자.

여행 시작날짜가 7월 18일에 해당하는 데이터가 잘 나온다.

다음은 '8월 1일' 조회

날짜가 8월 1일에 해당하는 데이터도 잘 나온다.

다음은 지역과 날짜를 같이 조회해보자!
'부산', '7월20일'로 조회하려고 한다.
결과는 나오지 않아야 정상이다.

나오지 않는다.
왜냐하면, '부산', '7월20일'에 해당하는 데이터가 당연히 없기 때문이다.

(갑자기 든 생각이다. 지금 조건은 and 조건인데, 만약 or 조건으로는..?
나중에 해봐야겠다.)

~~할 일에 메모해놓음ㅎㅎ~~

이제 '부산', '8월1일'로 검색해보자.
당연하게도 잘 나올 것이다.

캬...
로그는 어떻게 되있을까??

3. 요약

검색 조건을 쿼리스트링으로 받아서 검색 조건을 가지는 클래스를 생성하려고 할 때,
값이 바인딩되지 않던 문제를 마주하게 되었고,
@Setter를 사용함으로 이를 해결한 것에 대한 포스팅이다.

(요약에 비해선 스크롤이 긴거 같기도...;;)

참고블로그

728x90
Comments