feat: 1:1문의 목록에 질문내용·질문자명 검색 상자를 둔다
1:1문의는 앱이 제목(bbsNm)에 유형 이름만 넣어 백엔드 검색(BBS_NM LIKE)이 쓸모없다. 검색 상자가 없던 이유가 그것이라, 백엔드가 못 거르는 두 대상은 Repository가 전 페이지(500건씩·최대 20쪽)를 받아 걸러 페이지로 자른다. 검색어가 없으면 종전처럼 서버 페이징을 쓴다. 백엔드에 searchCondition 2(BBS_CN)·3(작성자)이 생기면 서버 검색으로 바꾼다. Co-Authored-By: Claude Opus 5
@0af7c0dc127dee39474ca6999d0e28ad51b62e15
--- lib/data/repositories/board-post-repository.ts
+++ lib/data/repositories/board-post-repository.ts
... | ... | @@ -141,6 +141,35 @@ |
| 141 | 141 |
totalCount: number; |
| 142 | 142 |
}; |
| 143 | 143 |
|
| 144 |
+// 질문내용·질문자명 검색은 백엔드에 조건이 없어 전 페이지를 받아 거른다. 상한을 넘으면 그 뒤는 잘린다. |
|
| 145 |
+// TODO(백엔드): MngrBbsMapper에 searchCondition 2(BBS_CN)·3(작성자)이 생기면 서버 검색으로 바꾼다. |
|
| 146 |
+const SCAN_PAGE_SIZE = 500; |
|
| 147 |
+const MAX_SCAN_PAGES = 20; |
|
| 148 |
+ |
|
| 149 |
+async function fetchAllBoardPosts( |
|
| 150 |
+ boardType: BoardType, |
|
| 151 |
+ params: Record<string, string | number> |
|
| 152 |
+): Promise<BoardPost[]> {
|
|
| 153 |
+ const items: BoardPost[] = []; |
|
| 154 |
+ for (let pageIndex = 1; pageIndex <= MAX_SCAN_PAGES; pageIndex += 1) {
|
|
| 155 |
+ const page = await requestBoardPage(boardType, {
|
|
| 156 |
+ ...params, |
|
| 157 |
+ pageIndex, |
|
| 158 |
+ recordCountPerPage: SCAN_PAGE_SIZE, |
|
| 159 |
+ }); |
|
| 160 |
+ items.push(...page.items); |
|
| 161 |
+ if (page.items.length < SCAN_PAGE_SIZE || items.length >= page.totalCount) break; |
|
| 162 |
+ } |
|
| 163 |
+ return items; |
|
| 164 |
+} |
|
| 165 |
+ |
|
| 166 |
+function matchesLocalSearch(post: BoardPost, field: string, keyword: string): boolean {
|
|
| 167 |
+ const needle = keyword.toLowerCase(); |
|
| 168 |
+ if (field === 'content') return post.content.toLowerCase().includes(needle); |
|
| 169 |
+ if (field === 'author') return (post.authorName ?? '').toLowerCase().includes(needle); |
|
| 170 |
+ return true; |
|
| 171 |
+} |
|
| 172 |
+ |
|
| 144 | 173 |
/** |
| 145 | 174 |
* 검색·필터·페이징이 적용된 목록을 조회한다. |
| 146 | 175 |
* |
... | ... | @@ -151,10 +180,7 @@ |
| 151 | 180 |
boardType: BoardType, |
| 152 | 181 |
query: BoardPostQuery |
| 153 | 182 |
): Promise<BoardPostPage> {
|
| 154 |
- const params: Record<string, string | number> = {
|
|
| 155 |
- pageIndex: query.page, |
|
| 156 |
- recordCountPerPage: query.pageSize, |
|
| 157 |
- }; |
|
| 183 |
+ const params: Record<string, string | number> = {};
|
|
| 158 | 184 |
|
| 159 | 185 |
// 백엔드가 거를 수 있는 둘만 넘긴다 — 제목(searchCondition=1)과 노출여부(RLS_YN). |
| 160 | 186 |
// 나머지 조건은 화면에서 감췄다(`BOARD_SEARCH_FIELDS`·`BOARD_QUERY_FIELDS`). |
... | ... | @@ -169,7 +195,21 @@ |
| 169 | 195 |
params.searchUseYn = 'N'; |
| 170 | 196 |
} |
| 171 | 197 |
|
| 172 |
- return requestBoardPage(boardType, params); |
|
| 198 |
+ // 백엔드가 못 거르는 대상(질문내용·질문자명)은 전부 받아 여기서 거르고 페이지로 자른다. |
|
| 199 |
+ const isLocalSearch = keyword !== '' && (query.searchField === 'content' || query.searchField === 'author'); |
|
| 200 |
+ if (isLocalSearch) {
|
|
| 201 |
+ const matched = (await fetchAllBoardPosts(boardType, params)).filter((post) => |
|
| 202 |
+ matchesLocalSearch(post, query.searchField, keyword) |
|
| 203 |
+ ); |
|
| 204 |
+ const start = (query.page - 1) * query.pageSize; |
|
| 205 |
+ return { items: matched.slice(start, start + query.pageSize), totalCount: matched.length };
|
|
| 206 |
+ } |
|
| 207 |
+ |
|
| 208 |
+ return requestBoardPage(boardType, {
|
|
| 209 |
+ ...params, |
|
| 210 |
+ pageIndex: query.page, |
|
| 211 |
+ recordCountPerPage: query.pageSize, |
|
| 212 |
+ }); |
|
| 173 | 213 |
} |
| 174 | 214 |
|
| 175 | 215 |
/** 단건 조회 — 수정·답변 팝업의 진입점. */ |
--- lib/domain/board-post-query.ts
+++ lib/domain/board-post-query.ts
... | ... | @@ -46,8 +46,12 @@ |
| 46 | 46 |
: []), |
| 47 | 47 |
{ value: 'title', label: '제목' },
|
| 48 | 48 |
], |
| 49 |
- // 1:1문의는 제목 없이 내용만 쓴다 — 백엔드가 내용을 못 걸러 남는 대상이 없다. |
|
| 50 |
- inquiry: [], |
|
| 49 |
+ // 1:1문의는 제목(bbsNm)에 유형 이름만 들어 있어 백엔드 검색(제목 LIKE)이 쓸모없다 — 질문내용· |
|
| 50 |
+ // 질문자명은 Repository가 전 페이지를 받아 거른다(백엔드에 조건이 생기면 서버 검색으로 바꾼다). |
|
| 51 |
+ inquiry: [ |
|
| 52 |
+ { value: 'content', label: '질문내용' },
|
|
| 53 |
+ { value: 'author', label: '질문자명' },
|
|
| 54 |
+ ], |
|
| 51 | 55 |
faq: [{ value: 'title', label: '제목' }],
|
| 52 | 56 |
}; |
| 53 | 57 |
|
Add a comment
Delete comment
Once you delete this comment, you won't be able to recover it. Are you sure you want to delete this comment?