V-RIS 개발일지 (12) 필터로 음식점 검색 구현

KHJ·2022년 5월 26일
1

V-RIS 개발일지

목록 보기
12/15

저번 글에서 말했던 대로 우리는 음식점을 검색할 수 있는 방법으로

  1. 필터를 사용하여 검색
  2. 음식점 이름으로 검색

두가지 방법을 구현하기로 했다.


우선 우리가 구현하려는 필터는 다음 사진과 같이 [지역, 비건 단계, 음식점 종류]에서 원하는 항목들을 선택하고, 검색버튼을 누르면 항목에 일치하는 음식점 목록들이 검색되는 방식이다.


우리가 설정한 필터의 조건은

  1. 한 카테고리에서 여러개의 조건을 선택할 수 있다.
  2. 만약 한 카테고리에서 아무것도 선택하지 않는다면, 카테고리에 존재하는 모든 조건을 만족하는 결과값을 보여준다.

예를 들어 채식단계에서 아무것도 선택하지 않는다면 모든 채식단계에 대한 식당 목록들을 보여준다.


이름으로 검색하는 방법은 처음부터 LIKE로 구현을 하면 되겠다고 생각을 하고 실제로 그렇게 구현을 했는데, 필터를 사용하여 검색하는 방법은 어떻게 구현할지 고민이 많았다.


프론트 -> 백

우선 프론트에서 데이터가 어떻게 넘어오는지 물어봤는데 선택한 항목들을 문자열 그대로 보낼 수도 있고, 항목마다 번호를 붙여 숫자 배열로 넘겨줄 수도 있다고 했다.

처음에는 db에서 LIKE를 사용하여 검색할 생각으로 문자열로 넘겨달라고 했다가, 생각해보니 단순히 문자열만 넘어오면 어디서부터 어디까지가 지역, 채식단계, 음식종류인지 구별하지 못해 검색이 어려워져 int형 배열을 받는 것으로 수정했다.


그 후 controller에 문자열 배열을 만들어 매치하기로 했다.

String resFilterList[]=new String[]{
"강서구", "양천구", "구로구", "금천구", "영등포구", "마포구", "은평구", "서대문구", "동작구",
"관악구", "용산구", "서초구", "강남구", "송파구", "강동구", "중구", "종로구", "성북구", "강북구",
"도봉구", "노원구", "중랑구", "동대문구", "성동구", "광진구",

"비건", "락토", "오보", "락토 오보", "페스코", "폴로", "플렉시테리언",

"한식", "분식", "카페", "베이커리", "양식", "술집", "인도", "중식", "동남아", "일식"};

예를 들어 프론트에서 다음과 같이 들어온다면

[ 1, 2, 26, 33 ]

사용자가 다음과 같이 선택했다는 뜻이다.

[ "강서구", "양천구", "비건", "한식" ]





카테고리 나누기

그 후, 카테고리별로 나눠야 하기 때문에 List<String> 3개와 HashSet<Restaurant> 3개를 선언했다.

  • List : <String> : resFilterList에서 매칭한 문자열들 저장할 문자열 리스트

  • HashSet : <Restaurant> : 쿼리문을 통해 추출한 음식정 정보들을 저장할 hashSet

음식점 정보들을 HashSet 으로 설정한 이유는 카테고리가 3개이고 여러개 선택이 가능하기 때문에 합집합 연산과 교집합 연산이 필요하다고 생각했기 때문이다.


위에서 든 예시로 설명해보면
"강서구", "양천구", "비건", "한식" 을 선택했을 때


1. ( 강서구 음식점들 ) 합집합 ( 양천구 음식점들 )
2. 비건 음식점들
3. 한식 음식점들

을 검색 한 후 1번, 2번, 3번에 대해 교집합 연산을 해줘야 한다.

		List<String> resFilterListArea = new ArrayList<>();
        List<String> resFilterListLevel = new ArrayList<>();
        List<String> resFilterListType = new ArrayList<>();

        HashSet<Restaurant> hashListArea=new HashSet<>();
        HashSet<Restaurant> hashListLevel=new HashSet<>();
        HashSet<Restaurant> hashListType=new HashSet<>();





키워드 분류하기

프론트에서 들어온 정수값들을 분류해주는 작업이다.

정수값이 25보다 작으면 지역에 대한 키워드, 32보다 작으면 비건레벨에 대한 키워드, 42보다 작으면 음식점 타입에 관한 키워드이므로 각각 분류해줬다.

		// List<Integer>에 있는 값들 분류해서 String으로 변환
        for(Integer i : filteredValue){
            if(i <= 24)
                resFilterListArea.add(resFilterList[i]);
            else if(i <= 31)
                resFilterListLevel.add(resFilterList[i]);
            else if(i <= 41)
                resFilterListType.add(resFilterList[i]);
        }





선택된 값 없을땐?

위에서 설명한 대로 만약 선택된 값이 없을 땐 그 카테고리에 대한 모든 정보를 보여주기로 했다.

예를 들어 채식단계에서 아무것도 선택하지 않는다면 모든 채식단계에 대한 식당 목록들을 보여준다.

		// List<String> 비어있으면 모든 값 넣기
        if(resFilterListArea.isEmpty()){
            for(int i=0 ; i<=24 ; i++)
                resFilterListArea.add(resFilterList[i]);
        }
        if(resFilterListLevel.isEmpty()){
            for(int i=25 ; i<=31 ; i++)
                resFilterListLevel.add(resFilterList[i]);
        }
        if(resFilterListType.isEmpty()){
            for(int i=32 ; i<=41 ; i++)
                resFilterListType.add(resFilterList[i]);
        }





HashSet에 검색 결과 저장

HashSet의 가장 큰 특징이 중복을 허용하지 않는 것이라는 점을 이용했다.

		// List에 있는 값들 하나씩 검색해서 HashSet에 합집합
        for(String str : resFilterListArea){
            HashSet<Restaurant> hashTmp=restaurantRepository.findRestaurantByFilterArea(str);
            hashListArea.addAll(hashTmp);
        }
        for(String str : resFilterListLevel){
            HashSet<Restaurant> hashTmp=restaurantRepository.findRestaurantByFilterLevel(str);
            hashListLevel.addAll(hashTmp);
        }
        for(String str : resFilterListType){
            HashSet<Restaurant> hashTmp=restaurantRepository.findRestaurantByFilterType(str);
            hashListType.addAll(hashTmp);
        }





HashSet을 이용해 교집합 처리

마지막으로 세 개의 연산 결과에 대해 교집합 처리를 해줬다.

기본으로 제공되는 retainAll() 을 이용하여 동일한 요소는 남기고 나머지는 제거해 주는 연산이다.

		// 3개의 HashSet 교집합
        hashListArea.retainAll(hashListLevel);
        hashListArea.retainAll(hashListType);

        return hashListArea;





(전체코드) Restaurant.java

@Data
@AllArgsConstructor
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Entity(name="restaurant")
public class Restaurant {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer resIdx;
    private String resName;
    private String resAddress;
    private String resNum;
    private String resCategory;
    private String resMenu;
    private Integer userIdx;
}



(전체코드) RestaurantJpaController.java

@RestController
@RequiredArgsConstructor
@RequestMapping("/v1/vegan-res")
public class RestaurantJpaController {
    @Autowired
    private RestaurantRepository restaurantRepository;

    // 0 ~ 24 : 지역
    // 25 ~ 31 : 비건단계
    // 32 ~ 41 : 음식점 타입
    String resFilterList[]=new String[]{
"강서구", "양천구", "구로구", "금천구", "영등포구", "마포구", "은평구", "서대문구", "동작구",
"관악구", "용산구", "서초구", "강남구", "송파구", "강동구", "중구", "종로구", "성북구", "강북구",
"도봉구", "노원구", "중랑구", "동대문구", "성동구", "광진구",

"비건", "락토", "오보", "락토 오보", "페스코", "폴로", "플렉시테리언",

"한식", "분식", "카페", "베이커리", "양식", "술집", "인도", "중식", "동남아", "일식"};


    // 음식점 필터로 조회
    @GetMapping("/restaurant/filter")
    public HashSet<Restaurant> searchByFilter(@RequestBody List<Integer> filteredValue){

        List<Restaurant> resFilterListFinal=new ArrayList<>();

        List<String> resFilterListArea = new ArrayList<>();
        List<String> resFilterListLevel = new ArrayList<>();
        List<String> resFilterListType = new ArrayList<>();

        HashSet<Restaurant> hashListArea=new HashSet<>();
        HashSet<Restaurant> hashListLevel=new HashSet<>();
        HashSet<Restaurant> hashListType=new HashSet<>();


        // List<Integer>에 있는 값들 분류해서 String으로 변환
        for(Integer i : filteredValue){
            if(i <= 24)
                resFilterListArea.add(resFilterList[i]);
            else if(i <= 31)
                resFilterListLevel.add(resFilterList[i]);
            else if(i <= 41)
                resFilterListType.add(resFilterList[i]);
        }


        // List<String> 비어있으면 모든 값 넣기
        if(resFilterListArea.isEmpty()){
            for(int i=0 ; i<=24 ; i++)
                resFilterListArea.add(resFilterList[i]);
        }
        if(resFilterListLevel.isEmpty()){
            for(int i=25 ; i<=31 ; i++)
                resFilterListLevel.add(resFilterList[i]);
        }
        if(resFilterListType.isEmpty()){
            for(int i=32 ; i<=41 ; i++)
                resFilterListType.add(resFilterList[i]);
        }


        // List에 있는 값들 하나씩 검색해서 HashSet에 합집합
        for(String str : resFilterListArea){
            HashSet<Restaurant> hashTmp=restaurantRepository.findRestaurantByFilterArea(str);
            hashListArea.addAll(hashTmp);
        }
        for(String str : resFilterListLevel){
            HashSet<Restaurant> hashTmp=restaurantRepository.findRestaurantByFilterLevel(str);
            hashListLevel.addAll(hashTmp);
        }
        for(String str : resFilterListType){
            HashSet<Restaurant> hashTmp=restaurantRepository.findRestaurantByFilterType(str);
            hashListType.addAll(hashTmp);
        }


        // 3개의 HashSet 교집합
        hashListArea.retainAll(hashListLevel);
        hashListArea.retainAll(hashListType);

        return hashListArea;
    }
}



(전체코드) RestaurantRepository.java

@Repository
public interface RestaurantRepository extends JpaRepository<Restaurant, Integer> {

    // 음식점 필터로 검색 (지역)
    @Query("Select r From restaurant r where r.resAddress LIKE %:area%")
    HashSet<Restaurant> findRestaurantByFilterArea(@Param("area") String area);

    // 음식점 필터로 검색 (비건레벨)
    @Query("select r from restaurant r where r.resMenu like %:level%")
    HashSet<Restaurant> findRestaurantByFilterLevel(@Param("level") String level);

    // 음식점 필터로 검색 (타입)
    @Query("select r from restaurant r where r.resCategory like %:type%")
    HashSet<Restaurant> findRestaurantByFilterType(@Param("type") String type);
}

0개의 댓글