@RequestParm을 이용해 가게 이름에 특정 키워드가 포함되어 있다면 모두 가져오는 로직을 만들었다. JPQL을 이용해 직접 쿼리를 작성했고 특정 키워드를 포함시키기 때문에 LIKE를 사용했다.
Repostory
Service
Controller
postman을 이용해 가게 생성 API로 미리 몇 여러 가게들을 만들어 놓고 가게 조회 API를 테스트해봤다.
DB에는 정상적으로 여러 가게들이 생성된 모습을 볼 수 있다. 하지만 조회된 결과가 없어서 빈 배열을 반환하고있다.
JPQL이 잘 못 작성되었거나, 파라미터 값을 잘 못 넣고 있는건 아닌지 추론할 수 있었다.
먼저 Repository를 살펴보았다. LIKE 연산자는 기본적으로 포함하려는 문자열 앞 뒤로 '%'가 필요하다. 그러나 :name 파라미터 값에 %가 포함되지 않으면 문자열 매칭이 정확히 일치해야 하므로 결과가 반환되지 않을 수도 있다는 자료를 찾을 수 있었다.
@Query("select s from Store s where s.name like concat('%', :name, '%')")
List<Store> findAllByStoreName(@Param("name") String name);
concat을 이용해 쿼리를 수정해서 다시 시도 -> 실패
다음은 파라미터 자체를 잘 못 입력하고 있는건 아닌지 postman으로 돌아와봤다.
""를 없애고 바로 문자를 입력 -> 성공..
postman에서 요청을 보낼 때 ""를 포함하면, 쿼리 파라미터 값이 그대로 "키워드"로 처리된다. 이로 인해 문자열 비교에서 "키워드"와 키워드가 서로 다른 값으로 간주되어 매칭이 되지 않았던 것이다.
하지만 의문점이 생겼다. 쿼리에서 LIKE로 비교해서 찾으면 ""이 들어가도 어쨌든 안에 키워드가 포함되어 있는데 찾을 수 있어야되는게 아닐까? 이 부분에 대해서 알아보았다.
논리적으로는 LIKE 쿼리를 사용할 때, 파라미터 값에 "(따옴표)가 포함되더라도 SQL 자체는 문자열로 처리하기 때문에 문제가 없어야한다. 그러나 실제로는 따옴표가 포함된 값이 데이터베이스에 저장된 값과 매칭되지 않아서 결과가 나오지 않을 수 있다. 이를 이해하려면 몇 가지 세부 사항을 살펴보아야 한다.
SELECT * FROM store WHERE name LIKE '%"메가커피"%';
이 쿼리는 name 컬럼에 메가커피가 아닌 정확히 "메가커피"라는 값을 가진 레코드를 찾는다. 따라서 name 컬럼에 실제 데이터가 "메가커피"로 저장되지 않았다면 결과가 반환되지 않는다.
WHERE s.name LIKE '%"메가커피"%'
결국 따옴표가 매칭되지 않기 때문에 기대하는 값이 나오지 않는다.
만약 입력 값에 따옴표가 포함되더라도 문제가 없도록 만들고 싶다면, 파라미터를 사전 처리하여 따옴표를 제거하거나 변환해줄 수 있다.
@GetMapping("/store")
public List<StoreResponseDto> getStores(@RequestParam String name) {
name = name.replace("\"", ""); // 따옴표 제거
return storeService.findAll(name);
}
@Query("select s from Store s where replace(s.name, '\"', '') like concat('%', replace(:name, '\"', ''), '%')")
List<Store> findAllByStoreName(@Param("name") String name);
결론적으로는 ""를 없애고 바로 문자를 입력해서 이슈는 해결되었다.
요약
LIKE는 따옴표가 포함된 문자열도 찾을 수 있지만, 따옴표를 포함한 값이 데이터와 정확히 매칭되어야 한다.
해결책
Postman이나 클라이언트 측 입력값이 항상 일관적이지 않을 수 있으니, 서버 쪽에서 사전처리를 통해 유연하게 대응하는 것이 좋겠다.