LIKE 쿼리에서 "_" 검색이 안되는 이유
안녕하세요. CareerBee 서비스에서 백엔드 개발을 맡고있는 mumu입니다.
해당 글은 기업 검색시 발생한 문제에 대해서 설명하고 어떻게 해결했는지를 서술하고 있습니다.
🔍 문제점
CareerBee에는 사용자가 입력한 키워드로 기업을 검색하는 기능이 있습니다.
문득, 이름에 _(언더바) 를 포함한 기업이 있는지 궁금해 "_"를 검색해봤습니다.
하지만 결과는 엉뚱했습니다. 언더바가 들어가지 않은 기업들이 검색되는 것이었습니다.
❓ 왜 이런 현상이 발생했을까?
결론부터 말하자면 LIKE 쿼리에서 특수문자 _를 escape 처리하지 않았기 때문입니다.
검색 결과를 가져오는 Repository 의 코드를 보면 다음과 같이 LIKE 구문이 사용됨을 볼 수 있습니다.
@Override
public CompanySearchResp fetchMatchingCompaniesByKeyword(String keyword) {
List<CompanySearchInfo> result = queryFactory
.select(
Projections.constructor(
CompanySearchInfo.class,
company.id,
company.name
)
)
.from(company)
.where(company.name.like("%" + keyword + "%"))
.limit(8)
.fetch();
return new CompanySearchResp(result);
}
이 코드에서 사용자가 "_"를 입력하면 실제 쿼리는 다음과 같이 완성됩니다.
WHERE name LIKE '%_%'
이 쿼리의 뜻은 "한 글자를 포함하는 모든 문자열" 입니다. 따라서 위의 문제가 발생하게 된 것입니다.
SQL에서 _ , %, ! 는 모두 패턴 문자로 취급되며 이를 진짜 문자열로써 검색하기 위해선 추가 처리가 필요합니다.
✅ 해결방법
특수문자를 문자 그대로 검색하려면 다음과 같은 처리가 필요합니다.
- 해당 문자 앞에 escape 문자 삽입
- SQL 에서 ESCAPE 옵션 추가
이를 적용하기 위해서 Service 와 Repository Layer 에서 다음과 같이 처리를 할 수 있습니다.
// Service
@Override
public CompanySearchResp fetchMatchingCompaniesByKeyword(String keyword) {
return companyRepository.fetchMatchingCompaniesByKeyword(escapeLike(keyword.strip()));
}
private String escapeLike(String keyword) {
// !를 escape 문자로 사용하며 패턴 문자 앞에 삽입
return keyword
.replace("!", "!!")
.replace("%", "!%")
.replace("_", "!_");
}
// Repository
@Override
public CompanySearchResp fetchMatchingCompaniesByKeyword(String keyword) {
...
// QueryDSL의 like() 함수에서 escape 명시
.where(company.name.like("%" + keyword + "%", '!'))
...
}
📚 배운 점
LIKE 쿼리를 종종 사용했지만, escape 처리까지 신경 써 본 것은 이번이 처음이었습니다.
현재는 검색 결과가 최대 8개로 한정되어있어 성능상 큰 문제는 없지만,
제한이 없는 서비스였다면 _(언더바) 검색시 대량의 데이터를 반환해 성능 저하로 이어질 수 있겠다는 생각이 들었습니다.
이번 경험을 통해, 완성도 높은 검색 기능을 제공하기 위해선 Java, SQL 등 다양한 분야에 있어 고루 지식을 갖추는 것이 필요하다는 것을 배웠습니다.