Youngjae

@toss hangul을 이용한 검색 컴포넌트 구현하기

@toss hangul을 이용한 검색 컴포넌트 구현하기

2022.10.14

dev
blog
react
nextjs
 
지난 13일, 토스에서 실제로 사용하는 라이브러리들을 모은 @toss/slash 라이브러리가 GitHub에 오픈소스로 공개됐다.
FEConf 박서진님의 세션 끝에서 토스 프론트엔드 팀에서 사용하고 있다는 수 많은 라이브러리가 공개된다고 말씀하셨는데, 확인해보니 실제로 수많은 TypeScript/JavaScript 기반의 30개 이상의 npm 패키지들이 오픈됐다.
단숨에 GitHub Trending 2위를 찍고 있었는데 역시 대단하다..
 
그 중 나의 눈길을 사로잡은 라이브러리는 @toss/hangul 였는데 초성검색(chosungIncludes)이었다. 이를 지금 내 블로그의 검색 기능에 적용해보기로 했다.
 
notion image
 
필요한 라이브러리를 설치해주자.
yarn add @toss/hangul
 

AS-IS: 현재 포스트 검색창

notion image
현재 내 블로그에서는 초성으로 포스트 검색이 되지 않는다. 자바스크립트의 String.includes() 메서드로 포스트 제목에 검색어가 포함되어있는지를 판단하기 때문에.

TO-BE: hangul이 적용된 검색창

import React, { useState, useEffect } from "react"; // ... import { chosungIncludes, hangulIncludes } from "@toss/hangul"; // @toss/hangul import import Layout from "@/components/layout"; import PostCard from "@/components/pages/blog/PostCard"; import SearchBar from "@/components/pages/blog/SearchBar"; import TagSelector from "@/components/pages/blog/TagSelector"; export interface Post { id: string; properties: any; [key: string]: any; } const Blog: React.FC = ({ posts }: any) => { const tags = ["dev", "design", "life", "blog"]; const [selectedTag, setSelectedTag] = useState<string>("All"); const [filteredPosts, setFilteredPosts] = useState<Post[]>(posts); const [searchValue, setSearchValue] = useState(""); const filteredBlogPosts = filteredPosts.filter((frontMatter) => { const postTitle = frontMatter.properties.Name.title[0].plain_text; const searchContent = postTitle; // AS-IS // return searchContent.toLowerCase().includes(searchValue.toLowerCase()); // TO-BE return ( chosungIncludes(searchContent.toLowerCase(), searchValue.toLowerCase()) || // 초성 검색용 hangulIncludes(searchContent.toLowerCase(), searchValue.toLowerCase()) // 한글 검색용 ); }); const displayPosts = filteredPosts.length > 0 && !searchValue ? filteredPosts : filteredBlogPosts; // ... return ( <Layout> ... <div id="top-bar" className="flex justify-between items-center flex-wrap gap-4" > <TagSelector selectedTag={selectedTag} handleTagClick={handleTagClick} tags={tags} /> <SearchBar onChangeHandler={(e) => setSearchValue(e.target.value)} /> </div> {displayPosts.length > 0 ? ( displayPosts.map((post: Post, idx: number) => ( <PostCard key={idx} post={post} /> )) ) : ( <div className="h-80 flex justify-center items-center text-xl"> Noting Found </div> )} </div> ... </Layout> ); }; // ... export default Blog
 
 
초성으로 검색이 되는 Search
초성으로 검색이 되는 Search
간단하게 초성으로 검색을 할 수 있게됐다. slash library의 한글 초성 검색 기능 덕분에 직접 구현하지 않아도 되고 아주 편리하다.
 
├── src │   ├── chosungIncludes.spec.ts │   ├── chosungIncludes.ts // -> chosungIncludes │   ├── constants.ts │   ├── disassemble.spec.ts │   ├── disassemble.ts // -> disassembleHangulToGroups, disassembleHangul │   ├── disassembleCompleteHangulCharacter.spec.ts │   ├── disassembleCompleteHangulCharacter.ts │   ├── hangulIncludes.spec.ts │   ├── hangulIncludes.ts │   ├── index.ts │   ├── josa.spec.ts │   ├── josa.ts │   ├── utils.spec.ts │   └── utils.ts // ├── tsconfig.esm.json └── tsconfig.json
구현이 어떻게 되어있나 확인해보니 chosungIncludes 함수의 경우 disassembleHangulToGroups 를 통해 글자별로 초성/중성/종성 단위로 완전히 분리된 배열을 getFirstConsonants 함수에서 초성을 추출하여(예: 토스 -> 'ㅌㅅ') 초성을 포함하고 있는지 확인(includes)하는 식으로 구현되어있다.
description에서 사용 예시까지 깔끔하게 제공해서 이해가 쉬웠다
description에서 사용 예시까지 깔끔하게 제공해서 이해가 쉬웠다
또 chosungIncludes는 초성만 검사가 가능하기 때문에 위에 나의 코드에서는 hangulIncludes도 써야했는데, ‘블로그’는 ‘블록’을 포함할 수 있는 이유는 disassembleHangul(한글 문자열을 글자별로 초성/중성/종성 단위로 완전히 분리하여, 하나의 문자열로 만듦)을 이용해서 include 검사를 했기 때문이다.
이렇게 선언형으로 잘 분리된 라이브러리 코드를 보니 많이 배우게 된다.
 

 
이 포스트는 사실 위 사례처럼 slash 라이브러리에서 쓸 수 있는 몇 가지 편리한 유틸 함수들을 소개하고자 적었다.
@toss/hangul 말고도 @toss/react(유용한 커스텀훅들이 많이 보인다.. 참고가 많이 될 듯🥺), Web Storage를 환경에 구애받지 않고 쓸 수 있도록 하는 @toss/storage 등도 신기하고 코드를 뜯어보는 재미가 있었다.
slash 라이브러리를 쭉 살펴보며 다른 좋은 라이브러리를 가져다쓸 수 있는 것이 있다면 찾아보고자 한다.
 
공유하기

Youngjae Jang

Copyright © 2022, All right reserved.