# korean-search-utils

React 기반 프로젝트에서 **한글 검색**을 지원하는 유틸리티 함수와 커스텀 훅입니다.

## ✨ 주요 기능

- 한글 자모 분리 (`disassembleHangul`)
- 초성/중성/종성 fuzzy 검색 (`isMatch`)
- 매칭 구간 하이라이팅 세그먼트 반환 (`getMatchSegments`) — v1.1.0
- React 없이 쓸 수 있는 필터+정렬 유틸 (`filterAndSort`) — v1.1.0
- React 커스텀 훅 (`useKoreanSearch`)
- ESM + CJS 듀얼 빌드 지원 — v1.1.0

## 📦 설치

```bash
npm install korean-search-utils
```

## 📘 사용법

### useKoreanSearch 훅

```tsx
import { useKoreanSearch } from "korean-search-utils";

const fruits = [
  { name: "사과 apple" },
  { name: "오렌지 orange" },
  { name: "바나나 banana" },
];

function FruitSearch() {
  const { searchTerm, setSearchTerm, filteredItems, resetSearch } =
    useKoreanSearch(fruits, (fruit) => fruit.name);

  return (
    <div>
      <input
        placeholder="과일 검색 (ㅅㄱ, 바나, apple...)"
        value={searchTerm}
        onChange={(e) => setSearchTerm(e.target.value)}
      />
      <button onClick={resetSearch}>초기화</button>
      <ul>
        {filteredItems.map((fruit) => (
          <li key={fruit.name}>{fruit.name}</li>
        ))}
      </ul>
    </div>
  );
}
```

### getMatchSegments — 하이라이팅

```tsx
import { getMatchSegments } from "korean-search-utils";

const segments = getMatchSegments("사과 apple", "ㅅ");
// → [{ text: "사", matched: true }, { text: "과 apple", matched: false }]

// 하이라이팅 렌더링 예시
segments.map((seg) =>
  seg.matched ? <mark>{seg.text}</mark> : <span>{seg.text}</span>
);
```

### filterAndSort — React 없이 사용

```ts
import { filterAndSort } from "korean-search-utils";

const fruits = ["사과", "바나나", "오렌지", "딸기", "수박"];

filterAndSort(fruits, "ㅅ", (x) => x);
// → ["사과", "수박"] (ㅅ으로 시작하는 항목, 매칭 위치 순 정렬)

filterAndSort(fruits, "바나", (x) => x);
// → ["바나나"]
```

### 유틸 함수

```ts
import { disassembleHangul, isMatch, getMatchStartIndex, getMatchScore } from "korean-search-utils";

disassembleHangul("사과");          // "ㅅㅏㄱㅘ"
disassembleHangul("한글 hangul");   // "ㅎㅏㄴㄱㅡㄹhangul"

isMatch("사과", "ㅅㄱ");            // true
isMatch("바나나", "ㅂㄴ");          // true
isMatch("딸기", "포도");            // false

getMatchStartIndex("사과", "ㅅ");   // 0 (자모 기준 인덱스)
getMatchStartIndex("사과", "없음"); // -1

getMatchScore("사과", "ㅅ");        // 숫자 (낮을수록 상위)
getMatchScore("사과", "포도");      // Infinity (매칭 없음)
```

### 매칭 우선순위

- 검색어가 더 **앞에서** 매칭되는 항목을 우선 정렬합니다.
- 같은 시작 위치라면 **짧은 문자열**이 우선됩니다.

```ts
const list = [{ name: "사과 오렌지" }, { name: "오렌지 사과" }];

filterAndSort(list, "ㅅㄱ", (x) => x.name);
// 1) "사과 오렌지"  (매칭 시작 인덱스 0)
// 2) "오렌지 사과"  (매칭 시작 인덱스 더 뒤)
```

## 📖 API

| 함수 | 설명 |
|------|------|
| `disassembleHangul(str)` | 문자열을 자모로 분리 (공백 제거) |
| `isMatch(source, target)` | source가 target을 포함하는지 여부 |
| `getMatchStartIndex(source, target)` | 매칭 시작 자모 인덱스 (-1이면 불일치) |
| `getMatchScore(source, target)` | 매칭 품질 점수 (낮을수록 상위, 불일치 시 Infinity) |
| `getMatchSegments(source, target)` | 하이라이팅용 세그먼트 배열 반환 |
| `filterAndSort(items, target, getField)` | 필터+정렬 (React 불필요) |
| `useKoreanSearch(items, getField)` | React 커스텀 훅 |

## 📄 라이선스

MIT
