본문 바로가기

Project/Maturi

[MATURI] 검색 조건들 상세설명(카테고리, 팔로우, 관심지역...)

목차

  1. 검색 조건에 사용되는 DTO
  2. 카테고리 검색 조건 상세 설명
  3. 팔로우,관심지역,좋아요, 검색 조건 상세 설명
  4. 현재위치 검색 조건 상세 설명
  5. keyword검색, 차단회원 필터링 조건 상세설명

1. 검색 조건에 사용되는 DTO

클라이언트에서 검색을 시작하면 javascript로 검색조건을 가공해서 Controller단에 요청을 보냅니다. 이 데이터를 기반으로 service단에서 사용되는 검색조건으로 변환 후 repository단에 전달을 해줍니다.

Controller단에서 받는 검색조건 요청DTO
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Data
public class ArticleSearchRequest {

    /**
     * 옵션 검색 조건들
     * 1. 전체
     * 2. 팔로우
     * 3. 관심지역
     * 4. 현재위치
     * 5. 카테고리
     * 6. 좋아요
     */
    private String radioCond;//ex)all,follow,interLocal,myLocal,category
    //전체를 눌렀을 때는 where문에 싹다 null로 들어가야한다.
    //팔로우, 좋아요, 관심지역를 눌렀을 떄는 session에 있는 user의 정보를 기반으로 가져와야해서 value가 필요없다.
    //현재위치를 눌렀을 때
    private Double latitude;//내위도
    private Double longitude;//내경도
    //카테고리를 눌렀을 때
    private String category;//ex) 한식, 중식, 패스트푸드

    /**
     * 키워드 검색 조건
     * 1. 전체
     * 2. 글 내용
     * 3. 작성자
     * 4. 해시태그
     * 5. 가게명
     */
    private String all;
    private String content;
    private String writer;
    private String tag;
    private String restaurantName;
    private String keyword;//input의 value가 되어준 값 ex) 사용자가 입력한 keyword


}
Controller단에서 받은 DTO를 Service단에서 변환하여 repository에서 사용되는 DTO
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Data
public class ArticleSearchCond {

    private List<Long> blockedMemberIds;
    private List<Member> followMembers;//DB에서 가져옴 : 팔로우한 유저의
    private String sido;//DB에서 가져옴 : 관심지역(sido)
    private String sigoon;//DB에서 가져옴 : 관심지역(sigoon)
    private String dong;//DB에서 가져옴 : 관심지역(dong)
    private Double latitude;//프론트에서 받음 : 위도 1도 : 111.1412Km
    private Double longitude;//프론트에서 받음 : 경도 1도=111Km+
    private String category;//프론트에서 받음 : 카테고리(한식,중식,...)
    private List<Article> likeArticles;//DB에서 가져옴 : 유저가 좋아요를 누른 게시판

    private String content;//프론트에서 받음 : 글 내용
    private String writer;//프론트에서 받음 : 작성자
    private List<Article> articlesByTagValue;//DB에서 가져옴 : 키워드를 가지고있는 태그명이 참조하는 게시글
    private String restaurantName;//프론트에서 받음 : 레스토랑 이름
}

2. 카테고리 검색 조건 상세 설명

게시글을 검색할 때 리뷰글을 작성하 음식점의 카테고리로 검색을 할 수 있습니다.

이 때 카테고리 값을 가져올 때 group by함수를 사용하여 음식점의 카테고리 값들 중복없이 조회하였습니다.

카테고리 값을 group by함수로 가져오는 repository단 소스코드
public List<String> getCategory(){
    return query.select(restaurant.category)
            .from(article)
            .join(article.restaurant,restaurant)
            .on(article.status.eq(ArticleStatus.NORMAL))
            .groupBy(restaurant.category)
            .fetch();
}

3. 팔로우,관심지역,좋아요, 검색 조건 상세 설명

팔로우, 관심지역, 좋아요를 선택시 front단(javascript)에서 데이터를 전달 받는 것이 아닌, 로그인한 유저의 정보를 기반으로 DB에서 값을 통해 게시글을 검색하도록 구현하였습니다.

 

Controller단에서 받은 검색 조건 DTO(ArticleSearchRequest)를 통해 어떤 검색조건을 선택했는지 경우의 수에 따라 DB에서 값을 가져오는 메서드를 통해 적절한 값을 DB에서 가져와서 repository단에서 사용하는 DTO(ArticleSearchCond)로 변환하도록 구현하였습니다.

ArticleSearchRequest를 ArticleSearchCond으로 변환하는 메서드
/**
     * 클라이언트로 부터 받은 정보를 기반으로 검색조건에 데이터로 변환해줌(ArticleSearchRequest -> ArticleSearchCond)
     */
    public ArticleSearchCond getSearchCond(ArticleSearchRequest searchRequest, Long memberId) {
        ArticleSearchCond searchCond = modelMapper.map(searchRequest, ArticleSearchCond.class);
        switch (searchRequest.getRadioCond() != null ? searchRequest.getRadioCond() : NULL){
            case follow://유저가 팔로우한 유저들
                List<Member> followMember = memberQRepository.findFollowingsById(memberId);
                searchCond.setFollowMembers(followMember);
                break;
            case interestArea://유저의 관심 지역
                Area interArea = memberRepository.findById(memberId).orElseThrow(() ->
                        new IllegalArgumentException("맴버가 없습니다!")).getArea();
                if (interArea == null ){
                    searchCond.setSido(null);
                    searchCond.setSigoon(null);
                    searchCond.setDong(null);
                } else {
                    searchCond.setSido(interArea.getSido());
                    searchCond.setSigoon(interArea.getSigoon());
                    searchCond.setDong(interArea.getDong());
                }
                break;
            case like://유저가 좋아요를 누른 게시판들
                List<Article> likeArticles =    articleQRepository.findByLike(memberId);
                searchCond.setLikeArticles(likeArticles);
                break;
        }
        
        생략...

        return searchCond;
    }

switch문을 사용하여 팔로우, 관심지역, 좋아요 중 어떤 것을 선택했는지에 따라 따로 처리하였습니다. 생략 부분은 keyword검색 조건과 차단 검색 조건을 처리하는 부분으로 후에 뒤에 따로 설명하겠습니다.

4. 현재 위치 검색 조건 상세 설명

현제 위치 검색은 javascript의 geolocation을 통해 현재위치의 위도, 경도 값을 가져와서 이를 통해 DB에 저장된 음식점의 위도 경도와 비교하여 게시글을 조회하는 식으로 구현하였습니다.

geolocation을 통해 위도 경도 값을 가져오는 javascript코드
let myLatitude;
let myLongitude;

//위도 경도 값 가져오기
function onGeoOk(position){
    myLatitude = position.coords.latitude; // 위도
    myLongitude = position.coords.longitude; // 경도
    // console.log({"latitude":myLatitude,"longitude":myLongitude});
    document.querySelector("input[name=latitude]").value = myLatitude;
    document.querySelector("input[name=longitude]").value = myLongitude;console.log()
}
function onGeoError(){
     Swal.fire({
         title: "현재위치를 찾을 수 없습니다.",
         icon: 'error',
         confirmButtonColor: '#3085d6', // confrim 버튼 색깔 지정
     })
    console.log("현재위치를 찾을 수 없습니다.");
}
navigator.geolocation.getCurrentPosition(onGeoOk,onGeoError);
repository단의 where조건절을 통해 현재위치 주변의 게시글을 조회하는 소스코드
.where(// 검색조건들
		생략...
        latitudeBetween(searchCond.getLatitude()),//위도로 검색
        longitudeBetween(searchCond.getLongitude()),//경도로 검색
);

...

//현재 위치주면(위도) where절
private BooleanExpression latitudeBetween(Double latitude) {
        return latitude != null ? article.restaurant.location.latitude.between(latitude - kmToLat, latitude + kmToLat) : null;
}

//현재 위치주면(경도) where절
private BooleanExpression longitudeBetween(Double longitude) {
    return longitude != null ? article.restaurant.location.longitude.between(longitude - kmToLat, longitude + kmToLat) : null;
}

latitudeBetwenn, longitudeBetwenn 메서드를 보면 querydsl의 between메서드를 통해 위도 경도 값에서 kmToLat 상수 값을 +,- 값을 조회하도록 코드를 작성하였습니다.

이 때 kmToLat 값은 위도 1도 값을 지하철역 8개를 기준으로 km로 변환하였습니다.

위도1도를 km로 변환한 상수
public class LocationConst {
    //위도 1도 : 111.1412Km = ? : 7.1km
    //지하철역 8개 (성서산업단지역 ~ 청라언덕역 기준) = 7.1km(차로 이동했을 때 최단경로)
    // 7.1 ÷ 111.1412 ÷ 2 를 계산하면 +, - 해줘야할 값이 나옴
    public final static Double kmToLat = 0.03194135028234354;
}

지구가 완전히 둥글지 않기 때문에 위도와 경도를 km로 변환한 값이 약간의 차이는 있지만 이는 무시하였습니다.

5. keyword검색 조건, 차단회원 필터링 상세설명

ArticleSearchRequest를 ArticleSearchCond으로 변환하는 메서드
public ArticleSearchCond getSearchCond(ArticleSearchRequest searchRequest, Long memberId) {
    
    생략...
    
    //keyword검색의 dropdown메뉴중 전체를 선택했을 때
    if (StringUtils.hasText(searchRequest.getAll())){
        String keyword = searchRequest.getKeyword();
        searchCond.setContent(keyword);
        searchCond.setWriter(keyword);
        searchCond.setRestaurantName(keyword);
    }
    
    //keyword검색의 dropdown메뉴중 태그를 선택했을 때
    if (StringUtils.hasText(searchRequest.getTag())) {
        List<Article> articlesByTag = articleQRepository.findByTagValue(searchRequest.getTag().trim());
        log.info("[getSearchCond] articlesByTag = {}",articlesByTag);
        searchCond.setArticlesByTagValue(articlesByTag);
        List<Article> articlesByTagValue = searchCond.getArticlesByTagValue();
        for (Article article : articlesByTagValue) {
            log.info("[getSearchCond] article각각의 값 = {}",article);
        }
    }
	
    //차단한 회원 목록을 가져오는 코드
    List<Long> blockMemberIds = new ArrayList<>();
    List<MemberBlockDTO> blockMembers = blockQRepository.findBlockMembers(memberId);
    for (MemberBlockDTO blockMember : blockMembers) {
        blockMemberIds.add(blockMember.getId());
    }
    searchCond.setBlockedMemberIds(blockMemberIds);

    log.info("searchCond = {}",searchCond);

    return searchCond;
}

팔로우,관심지역,좋아요, 검색 조건 상세 설명 설명한 DTO를 변환하는 메서드의 생략부분으로 keyword검색 조건, 차단회원을 조회하는 로직이 있는 소스코드입니다.

 

전체를 선택했을 경우 유저가 input태그에 입력한 keyword값을 내용, 작성자, 음식점 값을 DTO에 입력 시킵니다.

 

내용, 작성자, 음식점으로 검색을 했을 경우 Controller단에서 해당 컬럼에 바로 keyword값을 받아 따로 service단에서 처리해줄 필요없도록 설계하였습니다.

 

차단한 회원의 게시글은 보여지지 않도록 하기위해 DB에서 차단한 회원을 가져와서 repository단에서 not in 절을 통해 차단한 회원을 제외한 조건의 게시글을 가져오도록 코드를 설계하였습니다.

not in을 사용하여 조건을 처리하는 메서드
private BooleanExpression blockMemberIdNotIn(List<Long> blockedMemberIds) {
    return blockedMemberIds != null && !blockedMemberIds.isEmpty() ? article.member.id.notIn(blockedMemberIds) : null;
}