Repository navigation
[Fix/#162] 물품·회원 검색에 검색어 이스케이프와 정리 방식 통일 - #163
Merged
Merged
Conversation
|
Important
This repository does not receive automatic reviews because it has fewer than 10 stars. ⚙️ Run configuration
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
github-actions
Bot
requested review from
leegain1,
sangrae2325,
tnals0924 and
xeoxxn
October 22, 2026 12:10
tnals0924
approved these changes
Oct 22, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
#️⃣연관된 이슈
🎯 해결하려는 문제가 무엇인가요?
검색 기능이
LikePatterns(검색어의%·_이스케이프)를 쓰는 곳은 공지뿐이고, 물품 검색과 회비 목록의 이름·학번 검색(회원 ID 조회)은 각자 다르게 구현되어 있습니다.keyword가AdminFeeSearchUseCase를 거쳐 내부 조회MemberService.searchIdsByKeyword(이름·학번이 맞는 회원 ID를 구하는 보조 조회, 사용처는 이 UseCase 하나)로 내려갑니다. 이 조회의MemberJpaRepository.searchIdsByKeyword가LIKE CONCAT('%', :keyword, '%')를 이스케이프 없이 써서, 회비 목록 검색창에%만 입력해도 모든 회원이 걸려 사실상 모든 요청이 조회되고_도 한 글자 와일드카드로 동작합니다(버그). 앞뒤 공백도 정리하지 않아"홍길동 "처럼 공백이 붙은 검색어는 결과가 없을 수 있습니다.ItemJpaRepository.findSlice):LOCATE로 구현되어%·_가 글자 그대로 검색되어 결과는 올바르지만 공지와 방식이 다릅니다.ItemListParams.toKeyword()), 회원은 UseCase에 있거나 아예 없습니다.❓ 왜 해결해야 하나요?
회비 목록 검색의 와일드카드 문제는 실제 오동작입니다(관리자가 검색창에
%를 입력하면 전체가 조회). 내부 조회 메서드라도 사용자 입력이 그대로 닿는 경로입니다. 또 같은 검색 처리가 여러 방식으로 흩어져 있으면 이후 검색을 추가할 때 어느 쪽을 따를지 매번 판단해야 하고, 한 곳만 고치면 나머지가 어긋납니다. 이스케이프와 정리를 한 곳(LikePatterns.escape)으로 모읍니다.⭐ 어떻게 해결했나요?
fix): 쿼리의 이름·학번 두LIKE에ESCAPE '!'를 붙이고,MemberRepositoryImpl.searchIdsByKeyword가LikePatterns.escape(keyword)를 호출합니다.refactor):LOCATE(:keyword, i.name) > 0을i.name LIKE CONCAT('%', :keyword, '%') ESCAPE '!'로 바꾸고,ItemRepositoryImpl.findSlice가LikePatterns.escape를 호출합니다. 검색 결과는 이전과 같습니다.LikePatterns.escape로 통일 (refactor):escape가 이스케이프 전에 정리까지 맡도록 확장했습니다(메서드 이름은 그대로). 검색어가null이거나 공백뿐이면 "검색 없음"이라null을 돌려주고, 아니면 앞뒤 공백을 지운 뒤%·_·!앞에!를 붙입니다. 저장소 구현이escape를 호출하므로 서비스는 검색어를 가공하지 않고 그대로 넘깁니다.ItemListParams.toKeyword()를 삭제하고 컨트롤러는params.keyword()를 그대로 서비스에 넘깁니다.ItemServiceImpl.getItems에도 정리 코드가 없습니다.MemberServiceImpl.searchIdsByKeyword도 정리 없이 저장소에 넘깁니다.ItemService,ItemRepository,MemberService의 Javadoc을 "null·공백이면 검색하지 않음, 앞뒤 공백 제거,%·_는 글자 그대로"로 맞췄습니다.🧩 이 PR의 한계 & 트레이드오프
%→%가 든 이름만,_→_가 든 이름만," 길동 "→ 공백 제거 후 검색, 공백·빈 값·null→ 오류 없이 빈 결과; 물품%·_·100%가 글자 그대로 검색되고 공백·빈 값은 전체 조회," 우산 "→우산검색.LIKE ... ESCAPE '!'는 표준 문법이라 위험이 낮지만 dev에서 쿼리가 정상 기동·조회되는지 확인이 필요합니다. 쿼리가 틀리면 앱 기동이 실패합니다.LikePatterns)으로 이동했습니다. "공백이면 검색하지 않는다"가 서비스가 아니라infrastructure:db에 있어서,getItems나searchIdsByKeyword만 읽어서는 이 규칙이 보이지 않습니다. 대신 서비스마다 같은 한 줄 정리를 반복하지 않고 한 곳에서 관리합니다. 서비스 쪽에는 Javadoc으로 동작을 적었습니다.escape가 이스케이프 외에 공백 정리와null변환도 합니다. 이름이 하는 일보다 작게 읽히는 점은 감수했습니다. 이스케이프만 필요한 호출처가 생기면 분리를 검토합니다.:keyword IS NULL OR …가드로null이면 조건이 빠지지만, 회원 쿼리에는 이 가드가 없어escape가null을 돌려주면LIKE CONCAT('%', NULL, '%')가 아무 행도 걸리지 않습니다. 실제 호출자인AdminFeeSearchUseCase는keyword != null && !keyword.isBlank()로 공백을 먼저 걸러 이 경로를 타지 않으며, 이 검사는 "회원 필터를 쓸지 말지"를 정하는 흐름 제어라 유지했습니다.NoticeServiceImpl.search([Refactor/#144] 공지 관리자 기능을 AdminNoticeService로 분리 #145 머지 후에는AdminNoticeServiceImpl)에 있는normalizedKeyword줄은 이 PR 범위 밖이라 그대로 두었습니다. 이미 정리된 값을escape에 넣어도 결과가 같아서 동작에는 영향이 없고, 해당 줄은 #145를 머지할 때 지우면 됩니다.LIKE '%…%')이라 인덱스를 타지 못하는 점은 이전(LOCATE, 이스케이프 없는LIKE)과 같습니다.⛓️ 기존 기능에 미치는 영향
%·_를 입력했을 때의 결과가 달라집니다(전체 요청 → 해당 문자가 든 이름·학번의 회원 요청만). 검색어 앞뒤 공백이 제거됩니다.ItemListParams.toKeyword()가 사라지므로 이를 쓰던AppRentalController를 고쳤습니다(다른 사용처 없음).LikePatterns.escape의 동작이 확장됐지만 이미 정리된 값만 넘어와 결과가 같습니다.🔀 Edge Case & 실패 시나리오
%또는_!포함null, 빈 문자열, 공백뿐" 우산 ")null, 빈 문자열, 공백뿐keyword가 공백뿐📋 검토한 대안과 선택 이유
core:common에 공용 정리 함수를 두고 서비스가 호출: 규칙을 도메인에 두면서 중복도 없앨 수 있지만 서비스마다 호출이 필요하고 새 공용 유틸이 하나 더 생겨 제외했습니다.LOCATE를 유지: 결과가 이미 올바르므로 바꾸지 않아도 됩니다. 다만 같은 목적의 검색이 두 가지 구현으로 남는 것이 혼란이어서 통일했습니다.escape에서 바꾸기: 하는 일이 더 늘었지만 호출처가 이미escape를 쓰고 있어 이름은 유지하기로 했습니다.💬 리뷰 포인트
[r]회원 ID 조회·물품 JPQL의LIKE … ESCAPE '!'가 dev(MySQL)에서 정상 기동·조회되는지[c]검색어 정리를LikePatterns.escape(인프라)로 모은 것. 서비스 쪽에서는 규칙이 보이지 않는 점을 Javadoc으로 충분히 보완했는지[c]회원 ID 조회가 공백이면 빈 결과가 되는 점과AdminFeeSearchUseCase의 검사를 유지한 판단[a]ItemListParams.toKeyword()삭제, 공지의 중복 정리 줄은 #145에서 정리