Conversation
|
|
||
| @Bean(destroyMethod = "close") | ||
| fun openSearchRestClient(): RestClient = | ||
| RestClient.builder(HttpHost(host, port, "http")).build() |
There was a problem hiding this comment.
[HIGH] core/config에 book 검색 전용 RestClient 빈이 배치됨
Problem: core/ 는 "도메인 지식 0, 거의 안 변함, 여러 모듈이 씀" 3가지를 모두 만족해야 하는 Shared Kernel입니다. OpenSearchConfig가 만드는 RestClient 빈은 전체 코드베이스에서 OpenSearchBookSearchAdapter 단 하나만 소비하는, book 검색 엔진 전용 설정이라 세 번째 조건("여러 모듈이 씀")을 만족하지 못합니다.
Evidence: core/config/ 에는 원래 전역적으로 쓰이는 JpaConfig, WebConfig만 있었는데, 이번 PR에서 단일 소비자용 설정이 함께 들어갔습니다. (git grep -n "RestClient" -- src/main 실행 결과 소비자가 OpenSearchBookSearchAdapter 하나뿐입니다.)
Fix direction: OpenSearchConfig를 core/config/ 가 아니라 어댑터와 같은 위치(예: adapter/book/search/opensearch/OpenSearchClientConfig.kt)로 옮기고, 지금과 동일한 ConditionalOnProperty 가드와 host/port 프로퍼티 주입 방식을 그대로 유지하면 됩니다. mysql_fulltext, mysql_like 어댑터가 별도 core 설정 없이 각자 위치에서 동작하는 것과 동일한 패턴입니다.
There was a problem hiding this comment.
RestClient는 도서 검색에 종속된 설정이 아니라 "OpenSearch 클러스터에 어떻게 붙을지"에 대한 인프라 설정으로 이후 다른 모듈(예: 소장본/대출 검색)이 OpenSearch를 붙이게 되면 재사용할 수 있어서, core/config에 두는 게 맞다고 판단
| "track_total_hits": true, | ||
| "query": $queryClause | ||
| } | ||
| """.trimIndent() |
There was a problem hiding this comment.
[HIGH] OpenSearch 어댑터에 정렬이 전혀 없어 "최신 등록순" API 계약이 깨짐
Problem: BookController의 /books/search Swagger 설명(BookController.kt 68번째 줄)은 검색어 유무와 무관하게 결과를 "최신 등록순"(createdAt DESC)으로 반환한다고 명시합니다. MysqlLikeBookSearchAdapter, MysqlFullTextBookSearchAdapter는 둘 다 Sort.by(DESC, "createdAt")으로 이를 지킵니다. 그런데 이 OpenSearchBookSearchAdapter의 requestBody에는 sort 필드가 전혀 없어 match_all(검색어 없음)일 때는 색인 순서(사실상 무작위), 키워드 검색일 때는 relevance score 순으로 반환되어 문서화된 계약과 어긋납니다.
Evidence: 이 문제는 이미 한 번 sibling PR에서 [HIGH]로 지적되고 수정된 이력이 있습니다. PR #33(MysqlFullTextBookSearchAdapter)에서 동일한 "최신 등록순 계약 깨짐" 이슈가 지적되어 "searchFullText의 정렬기준을 created_at desc 수정"으로 고쳐졌는데, 이번 OpenSearch 구현에서 다시 재발했습니다. 더 근본적으로, opensearch/books_index.json 매핑(17~23번째 줄)에는 createdAt 필드 자체가 색인되어 있지 않고, OpenSearchIndexGenerator도 id/title/author/publisher/isbn만 색인하므로 지금 상태로는 sort 절을 추가해도 정렬할 필드가 아직 없습니다.
Fix direction: (1) books_index.json 매핑에 createdAt(date 타입) 필드 추가, (2) OpenSearchIndexGenerator의 조회 쿼리와 bulk 색인 payload에 created_at 값 포함, (3) OpenSearchBookSearchAdapter의 requestBody JSON에 정렬 절을 추가해 track_total_hits 옆에 다음을 넣으세요.
"sort": [ { "createdAt": "desc" } ]
키워드 검색까지 "최신 등록순"을 유지할지, relevance 순을 허용하도록 API 문서를 바꿀지는 팀 논의가 필요합니다.
관련 링크:
backend/src/main/kotlin/org/library/book/controller/BookController.kt
Lines 66 to 69 in 26b15af
backend/opensearch/books_index.json
Lines 16 to 24 in 26b15af
There was a problem hiding this comment.
수정 완료
초기 데이터 적재 시 OpenSearchIndexGenerator가 MySQL created_at 컬럼을 함께 조회,색인하도록 반영
OpenSearchBookSearchAdapter의 요청 본문에 "sort": [ { "createdAt": "desc" } ] 추가
| emptyMap() | ||
| } else { | ||
| bookItemRepository.countActiveByBookIdIn(bookIds).associate { it.bookId to it.itemCount } | ||
| } |
There was a problem hiding this comment.
[MEDIUM] OpenSearch 인덱스가 book 생성/수정/삭제와 동기화되지 않아 소프트 삭제된 책이 계속 검색됨
Problem: search.engine=opensearch로 활성화하면 이 어댑터가 실제 운영 트래픽에서 BookSearchPort 구현체로 선택됩니다. 그런데 OpenSearch 색인은 devtools의 OpenSearchIndexGenerator가 한 번 배치로 채울 뿐이고, CreateBookService/UpdateBookService/DeleteBookService 어디에도 색인 동기화 로직이 없습니다. 책을 소프트 삭제해도 OpenSearch 인덱스에는 그대로 남아 검색 결과에 계속 노출됩니다.
Evidence: MysqlLikeBookSearchAdapter, MysqlFullTextBookSearchAdapter는 모두 deletedAt is null 조건으로 삭제된 책을 걸러내며, 커밋 b605544("도서 삭제 시 소장본 cascade soft-delete 처리")가 이 불변식을 더 강화했습니다. 이 어댑터에는 그런 필터링 지점 자체가 없습니다(색인이 갱신되지 않으므로). 게다가 bookItemCounts는 countActiveByBookIdIn로 활성 소장본만 세기 때문에, 삭제된 책이 "소장본 0권인 정상 도서"처럼 보이는 부작용도 있습니다(75번째 줄).
Fix direction: 최소한 book 도메인 이벤트(생성/수정/삭제) 발생 시 OpenSearch 문서를 upsert/삭제하는 동기화 경로를 추가하거나, 이 어댑터가 아직 실 트래픽용이 아니라면 그 사실을 PR 설명이나 설정 문서에 명시하세요. 즉시 반영이 부담스럽다면 최소 조치로 검색 결과의 bookId를 bookRepository.findAllByIdInAndDeletedAtIsNull로 한 번 더 필터링해 삭제된 책만이라도 응답에서 제외하는 방법이 있습니다.
연관 이슈
작업 사항
feat(#32): 도서 검색 opensearch 옵션 추가OpenSearchBookSearchAdapter: nori 형태소 분석 + fuzziness 기반 multi_match 검색 추가search.engine=opensearch로 활성화feat(#32): 검색 성능 비교 (opensearch) devtoolsExplainAnalyzer: LIKE / FULLTEXT(MySQL) / OpenSearch 실측 응답시간 비교 추가OpenSearchIndexGenerator: MySQL book 데이터를 OpenSearchbooks인덱스로 색인하는 devtool 추가테스트
ExplainAnalyzer실행해 LIKE/FULLTEXT/OpenSearch 3-way 비교 확인 (관련 노션 - 4장)주의 사항 및 참고사항
search.engine=opensearch수정