korean-search-utils
Version:
한글 초성 검색 및 React 훅 지원 라이브러리
1 lines • 15.4 kB
Source Map (JSON)
{"version":3,"file":"index.cjs","sources":["../src/utils/koreanSearchUtils.ts","../src/hooks/useKoreanSearch.ts"],"sourcesContent":["const CHO = [\n \"ㄱ\", \"ㄲ\", \"ㄴ\", \"ㄷ\", \"ㄸ\", \"ㄹ\", \"ㅁ\", \"ㅂ\", \"ㅃ\", \"ㅅ\",\n \"ㅆ\", \"ㅇ\", \"ㅈ\", \"ㅉ\", \"ㅊ\", \"ㅋ\", \"ㅌ\", \"ㅍ\", \"ㅎ\",\n];\n\nconst JUNG = [\n \"ㅏ\", \"ㅐ\", \"ㅑ\", \"ㅒ\", \"ㅓ\", \"ㅔ\", \"ㅕ\", \"ㅖ\", \"ㅗ\", \"ㅘ\",\n \"ㅙ\", \"ㅚ\", \"ㅛ\", \"ㅜ\", \"ㅝ\", \"ㅞ\", \"ㅟ\", \"ㅠ\", \"ㅡ\", \"ㅢ\", \"ㅣ\",\n];\n\nconst JONG = [\n \"\", \"ㄱ\", \"ㄲ\", \"ㄳ\", \"ㄴ\", \"ㄵ\", \"ㄶ\", \"ㄷ\", \"ㄹ\", \"ㄺ\",\n \"ㄻ\", \"ㄼ\", \"ㄽ\", \"ㄾ\", \"ㄿ\", \"ㅀ\", \"ㅁ\", \"ㅂ\", \"ㅄ\", \"ㅅ\",\n \"ㅆ\", \"ㅇ\", \"ㅈ\", \"ㅊ\", \"ㅋ\", \"ㅌ\", \"ㅍ\", \"ㅎ\",\n];\n\nconst HANGUL_START = 0xac00;\nconst HANGUL_END = 0xd7a3;\nconst JUNG_COUNT = 21;\nconst JONG_COUNT = 28;\n\nconst splitHangulChar = (char: string): string[] => {\n const code = char.charCodeAt(0);\n if (code < HANGUL_START || code > HANGUL_END) return [char];\n const idx = code - HANGUL_START;\n const cho = CHO[Math.floor(idx / (JUNG_COUNT * JONG_COUNT))];\n const jung = JUNG[Math.floor((idx % (JUNG_COUNT * JONG_COUNT)) / JONG_COUNT)];\n const jong = JONG[idx % JONG_COUNT];\n return jong ? [cho, jung, jong] : [cho, jung];\n};\n\n/**\n * 문자열 전체를 자모로 분리 (공백 제거)\n */\nexport const disassembleHangul = (str: string): string =>\n str\n .replace(/\\s+/g, \"\")\n .toLowerCase()\n .split(\"\")\n .flatMap(splitHangulChar)\n .join(\"\");\n\n/**\n * 한글 문자열이 다른 문자열을 포함하는지 확인 (초성/자모 fuzzy 검색 지원)\n */\nexport const isMatch = (source: string, target: string): boolean => {\n if (!source || !target) return target === \"\";\n\n const src = disassembleHangul(source);\n const tgt = disassembleHangul(target);\n\n let i = 0;\n for (let j = 0; j < tgt.length; j++) {\n let found = false;\n while (i < src.length) {\n if (src[i++] === tgt[j]) { found = true; break; }\n }\n if (!found) return false;\n }\n return true;\n};\n\n/**\n * 검색어가 문자열에서 매칭되는 시작 위치 인덱스를 반환 (자모 기준, 매칭 실패 시 -1)\n */\nexport const getMatchStartIndex = (source: string, target: string): number => {\n if (!source || !target) return target === \"\" ? 0 : -1;\n\n const src = disassembleHangul(source);\n const tgt = disassembleHangul(target);\n\n let i = 0;\n let matchStartIndex = -1;\n\n for (let j = 0; j < tgt.length; j++) {\n let found = false;\n while (i < src.length) {\n if (src[i] === tgt[j]) {\n if (j === 0) matchStartIndex = i;\n found = true;\n i++;\n break;\n }\n i++;\n }\n if (!found) return -1;\n }\n\n return matchStartIndex;\n};\n\n/**\n * 매칭 품질 점수를 반환 (낮을수록 상위, 매칭되지 않으면 Infinity)\n * 매칭 시작 위치가 빠를수록, 원본 문자열이 짧을수록 높은 우선순위\n */\nexport const getMatchScore = (source: string, target: string): number => {\n const matchIndex = getMatchStartIndex(source, target);\n if (matchIndex === -1) return Infinity;\n return matchIndex * 1000 + source.length;\n};\n\nexport type MatchSegment = { text: string; matched: boolean };\n\n/**\n * 검색어와 매칭되는 구간을 세그먼트 배열로 반환 — UI 하이라이팅에 활용\n * @example\n * getMatchSegments(\"홍길동\", \"길\")\n * // → [{text:\"홍\", matched:false}, {text:\"길\", matched:true}, {text:\"동\", matched:false}]\n */\nexport const getMatchSegments = (\n source: string,\n target: string\n): MatchSegment[] => {\n if (!source) return [];\n if (!target.trim()) return [{ text: source, matched: false }];\n\n const tgt = disassembleHangul(target);\n const charCount = source.length;\n const charMatched = new Uint8Array(charCount);\n\n let tgtIdx = 0;\n for (let i = 0; i < charCount && tgtIdx < tgt.length; i++) {\n const char = source[i];\n if (char === \" \") continue;\n for (const jamo of splitHangulChar(char.toLowerCase())) {\n if (tgtIdx < tgt.length && jamo === tgt[tgtIdx]) {\n charMatched[i] = 1;\n tgtIdx++;\n }\n }\n }\n\n if (tgtIdx < tgt.length) return [{ text: source, matched: false }];\n\n const segments: MatchSegment[] = [];\n let i = 0;\n while (i < charCount) {\n const matched = charMatched[i] === 1;\n let j = i + 1;\n while (j < charCount && (charMatched[j] === 1) === matched) j++;\n segments.push({ text: source.slice(i, j), matched });\n i = j;\n }\n return segments;\n};\n\n/**\n * React 없이 사용 가능한 필터+정렬 유틸리티\n * @param items 검색 대상 배열\n * @param target 검색어\n * @param getField 각 아이템에서 검색할 문자열을 반환하는 함수\n */\nexport const filterAndSort = <T>(\n items: T[],\n target: string,\n getField: (item: T) => string\n): T[] => {\n if (!target.trim()) return items;\n\n const tgt = disassembleHangul(target);\n const scored: { item: T; score: number }[] = [];\n\n for (const item of items) {\n const field = getField(item);\n const src = disassembleHangul(field);\n\n let i = 0;\n let matchStartIndex = -1;\n\n outer: {\n for (let j = 0; j < tgt.length; j++) {\n let found = false;\n while (i < src.length) {\n if (src[i] === tgt[j]) {\n if (j === 0) matchStartIndex = i;\n found = true;\n i++;\n break;\n }\n i++;\n }\n if (!found) { matchStartIndex = -1; break outer; }\n }\n }\n\n if (matchStartIndex !== -1) {\n scored.push({ item, score: matchStartIndex * 1000 + field.length });\n }\n }\n\n return scored.sort((a, b) => a.score - b.score).map(({ item }) => item);\n};\n","import { useMemo, useState } from \"react\";\nimport {\n isMatch,\n disassembleHangul,\n getMatchScore,\n getMatchSegments,\n filterAndSort,\n type MatchSegment,\n} from \"../utils/koreanSearchUtils\";\n\nexport type UseKoreanSearchOptions = {\n /** 검색 대상 필드를 여러 개 지정할 때 사용. getSearchField와 함께 사용하지 않는다. */\n getSearchFields?: never;\n};\n\nfunction useKoreanSearch<T>(\n items: T[],\n getSearchField: (item: T) => string,\n): {\n searchTerm: string;\n setSearchTerm: React.Dispatch<React.SetStateAction<string>>;\n filteredItems: T[];\n resetSearch: () => void;\n isMatch: (source: string, target: string) => boolean;\n disassembleHangul: (text: string) => string;\n getMatchSegments: (source: string, target: string) => MatchSegment[];\n filterAndSort: <U>(items: U[], target: string, getField: (item: U) => string) => U[];\n} {\n const [searchTerm, setSearchTerm] = useState<string>(\"\");\n\n const filteredItems = useMemo(() => {\n const trimmed = searchTerm.trim();\n if (!trimmed) return items;\n\n // 검색어 자모 분해를 아이템 순회 전에 한 번만 수행\n const tgt = disassembleHangul(trimmed);\n\n const scored: { item: T; score: number }[] = [];\n\n for (const item of items) {\n const field = getSearchField(item);\n const src = disassembleHangul(field);\n\n let i = 0;\n let matchStartIndex = -1;\n let matched = true;\n\n for (let j = 0; j < tgt.length; j++) {\n let found = false;\n while (i < src.length) {\n if (src[i] === tgt[j]) {\n if (j === 0) matchStartIndex = i;\n found = true;\n i++;\n break;\n }\n i++;\n }\n if (!found) { matched = false; break; }\n }\n\n if (matched && matchStartIndex !== -1) {\n scored.push({ item, score: matchStartIndex * 1000 + field.length });\n }\n }\n\n return scored.sort((a, b) => a.score - b.score).map(({ item }) => item);\n }, [items, searchTerm, getSearchField]);\n\n return {\n searchTerm,\n setSearchTerm,\n filteredItems,\n resetSearch: () => setSearchTerm(\"\"),\n isMatch,\n disassembleHangul,\n getMatchSegments,\n filterAndSort,\n };\n}\n\nexport default useKoreanSearch;\n"],"names":["useState","useMemo"],"mappings":"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"}