https://taegon.kim/archives/9919
위 글 공부용
import { escapeRegExp } from 'lodash';
//초성검색
function ch2pattern(ch: string) {
  const offset = 44032; // 44032 : '가' 코드
  // 한국어 음절에 해당한다면?
  if (/[가-힣]/.test(ch)) {
    // ch에서 44032를 빼주고
    const chCode = ch.charCodeAt(0) - offset;
    // 28로 나눠서 떨어지면 중성까지만 있는거고 나머지가 남으면 종성까지 있는거.
    if (chCode % 28 > 0) {
      // 종성이 있는 경우는 ch를 그대로 반환
      return ch;
    }
    //종성이 없을 때는
    const begin = Math.floor(chCode / 28) * 28 + offset; // ex) 돈을 종성 빼버려서 '도'로.
    const end = begin + 27; //  end는 '돟'
    return `[\\u${begin.toString(16)}-\\u${end.toString(16)}]`; // 도~돟에 해당하는 유니코드
  }
  // 한글 자음만 있을 때는
  if (/[ㄱ-ㅎ]/.test(ch)) {
    const con2syl = {
      ㄱ: '가'.charCodeAt(0),
      ㄲ: '까'.charCodeAt(0),
      ㄴ: '나'.charCodeAt(0),
      ㄷ: '다'.charCodeAt(0),
      ㄸ: '따'.charCodeAt(0),
      ㄹ: '라'.charCodeAt(0),
      ㅁ: '마'.charCodeAt(0),
      ㅂ: '바'.charCodeAt(0),
      ㅃ: '빠'.charCodeAt(0),
      ㅅ: '사'.charCodeAt(0),
    };
    //as keyof typeof
    /*
    interface User {
     name: string;
     age: number;
     email: string;
     }
     keyof typeof User
     type UserKeys = "name" | "age" | "email";
    */
    // ㄱ~ㅅ 까지는 그대로 begin 주고
    // ㅅ 이후로는 해당 유니코드에서 'ㅅ'을 뺀다음 588을 곱하고 다시 ㅅ를 더해서 시작점 설정
    // end 는 중성과 종성을 곱해서 더해주기
    // ex)  ㅎ => begin : 하  end : 힣
    const begin =
      con2syl[ch as keyof typeof con2syl] ||
      (ch.charCodeAt(0) - 12613) * 588 + con2syl['ㅅ']; /* 'ㅅ'의 코드 */
    const end = begin + 587;
    return `[${ch}\\u${begin.toString(16)}-\\u${end.toString(16)}]`; // ex) 하-힣 에 대한 유니코드 범위
  }
  // 그 외엔 그대로 내보냄(영어 등등..)
  // escapeRegExp : 특수문자 있는 문자열을 정규표현식으로 바꿔주는 메서드(유니코드의 경우 특수문떄문에
  // 이스케이프 안하면 에러 뜰 수 있기 때문)
  return escapeRegExp(ch);
}
export default function createFuzzyMatcher(input: string) {
  const pattern = input.split('').map(ch2pattern).join('.*?');
  // 인풋을 하나씩 정규표현식화 시켜서 c a t  이런식으로 만듦.
  return new RegExp(pattern);
}