Blog

[Spring][MiniProject 6일차]61 검색 기능 구현

Author
Summary
간략한 검색 기능 구현
Category
Study
Tags
Spring
Favorite
Memory Date
2023/09/21
Cross Reference Study
Related Media
Related Thought
Related Lessons
tag
날짜
작성자
진행상황
진행 전
태그구분
6 more properties
💡
해당 기능 구현에 대한 세부 로드맵
1.
검색 기능을 위한 데이터베이스 모델 설계:
가게 정보를 저장하는 데이터베이스 테이블을 설계해야 합니다. 이 테이블은 가게 이름, 위치, 메뉴, 등등의 정보를 포함
지금은 테이블 자체 컬럼명으로 하고있지만 챌린지로 넘어간다면
검색을 용이하게 하기 위해 인덱스를 활용 → 한다면 어떤 성능 차이가 있을지? 인덱스는 유용하게 하기 위한 자료구조다 라는 전제에서
2.
검색 기능 구현:
사용자가 키워드로 가게를 검색할 수 있는 검색 기능을 구현해야 함
Spring Boot에서는 JPA 또는 Hibernate를 사용하여 데이터베이스와 연동하고 검색 쿼리를 실행하는 것이 주 목적
a.
검색 기능 엔드포인트 추가:
검색 결과를 반환할 수 있는 API 엔드포인트를 추가
이 엔드포인트는 사용자가 키워드를 입력하고 검색을 요청할 때 호출
b.
검색 결과를 처리하는 비즈니스 로직 구현:
검색 결과를 처리하고 필요한 데이터를 데이터베이스에서 검색하는 비즈니스 로직을 구현
c.
검색 결과 반환:
검색된 가게 정보를 클라이언트에 반환
JSON 형식으로 결과를 제공하는 것이 목표
프론트에서는 ${storename}과 같이 뿌려지도록 해야하기 때문
3.
검색 관련 UI 구현 (프론트엔드)
검색 기능을 사용할 수 있는 사용자 인터페이스를 구현
이를 통해 사용자는 키워드를 입력하고 검색을 실행할 상세 페이지 필요
4.
검색 기능 테스트:
검색 기능을 테스트하여 예상대로 작동하는지 확인
검색어의 일치 여부, 정확성 및 성능을 확인
5.
보안 고려사항:
검색 기능에서는 입력 데이터의 검증과 SQL 인젝션 방지를 고려
추가적 연구 대상 : (미예정)
검색 기능을 구현할 때 검색 엔진을 사용하기 위해 Elasticsearch 또는 Solr와 같은 도구를 고려
검색과 관련된 기능을 빠르게 구현하는 데 도움이 될 수 있음.
→ 이것을 적용했을때 어떤 차이나, 그런것들이 예측
💡
플로우 차트, 와이어 프레임 설계로부터 유저의 이동, 실질적인 사용 추측
원칙적으로는 유저 프로세스에 따라 플로우 차트를 구성하고, 와이어프레임으로 유저의 사용 이동을 추적하고 설계해야 한다.
이 과정은 시간을 조금 소모하기 때문에 우선 텍스트로 구도를 잡고 추후 플로우 차트와 와이어프레임으로 표현하는 것으로 일의 우선순위를 변경했다.
💡
1. DB 모델 설계 여부 확인
우선 다른 기능들이 구현되어있는 상태로, 검색 기능은 “어떤 키워드로” + “어떤 내용을 조회할 것인가” 가 핵심인 상태이다.
나는 메뉴 이름 ex) 아메리카노, 카페라떼 라는 텍스트를 통해서
1.
이 메뉴를 가지고 있는(또는 포함하는) 카페이름을 조회 할 것
2.
이 메뉴의 이름과 동일한(또는 포함하는) 메뉴를 조회 할 것
이라는 두가지 목표를 세웠다.
💡
각각 구현해보고 키워드를 통해 메뉴 또는 카페 이름을 통합검색하도록함
컨트롤러에서는 쿼리스트링으로 클라이언트의 입력값을 keyword를 받아오게 한다.
SearchController.java
@RestController @RequiredArgsConstructor @RequestMapping("/api") public class SearchController { private final SearchService searchService; // 통합버전 @GetMapping("/search/stores-and-menus") public List<SearchResponseDto> searchStoresAndMenusByKeyword(@RequestParam("keyword") String keyword) { return searchService.searchStoresAndMenusByKeyword(keyword); } }
Java
복사
서비스에서는 각 Repository로부터 정보를 받아온다. 현재는 목적상 키워드를 통해 “가게”를 찾는 것으로 집중되어 있다.(ex: 메뉴를 검색해도 그 메뉴가 있는 가게를 연결해주기 위함)
SearchService.java
@Service @RequiredArgsConstructor public class SearchService { private final MenuRepository menuRepository; private final StoreRepository storeRepository; @Transactional(readOnly = true) public List<SearchResponseDto> searchStoresAndMenusByKeyword(String keyword) { List<SearchResponseDto> responseDtos = new ArrayList<>(); // 메뉴 이름에 키워드를 포함하는 메뉴 검색 List<Menu> menus = menuRepository.findByMenuNameContaining(keyword); for (Menu menu : menus) { SearchResponseDto responseDto = new SearchResponseDto(); responseDto.setStoreName(menu.getStore().getStoreName()); responseDto.setStoreAddress(menu.getStore().getStoreAddress()); responseDto.setInformation(menu.getStore().getInformation()); responseDto.setType("menu"); responseDtos.add(responseDto); } // 가게 이름에 키워드를 포함하는 가게 검색 List<Store> stores = storeRepository.findByStoreNameContaining(keyword); for (Store store : stores) { SearchResponseDto responseDto = new SearchResponseDto(); responseDto.setStoreName(store.getStoreName()); responseDto.setStoreAddress(store.getStoreAddress()); responseDto.setInformation(store.getInformation()); responseDto.setType("store"); responseDtos.add(responseDto); } return responseDtos; } }
Java
복사
레포지토리는 각 항목의 주인 레포지토리를 사용하게 되므로, StoreRepository, MenuRepository에서 JPA 쿼리메소드를 통해 해당 정보를 DB에서 가져오게 된다.
StoreRepository.java
@Repository public interface StoreRepository extends JpaRepository<Store, Long> { List<Store> findByStoreNameContaining(String keyword); }
Java
복사
MenuRepository.java
@Repository public interface MenuRepository extends JpaRepository<Menu, Long> { ... List<Menu> findByMenuNameContaining(String keyword); }
Java
복사
마지막으로 가져온 정보는 SearchResponseDto를 통해 필요한 정보만 클라이언트로 반환하게 된다.
@Getter @Setter @NoArgsConstructor public class SearchResponseDto { private String storeName; // 가게 이름 private String storeAddress; // 가게 주소 private String information; // 가게 정보 private String type; // 결과의 유형 (예: "menu" 또는 "store") }
Java
복사
여기서 응답을 받는 프론트엔드가 정보를 분류해서 작업 할 수 있도록 type을 통해서 각각 검색된 결과의 출처(origin)을 알 수 있도록 분류해주었다.
아래와 같이 테스트 되었으며, cafe라는 하나의 키워드로
cafe라는 키워드가 가게이름에 포함된 가게
cafe라는 키워드가 메뉴이름에 포함된 가게
가 나타나고있으며, Key가 type인 부분에서 menu, store로 분류되고 있다.