[React] Context API로 검색 결과 키워드를 강조해보자!

2025. 4. 26. 18:20·Web/React

 

 

안녕하세요👋

 

요즘 사이드 프로젝트로 '그리미티'라는 플랫폼을 개발하고 있습니다.
해당 프로젝트에서 검색 기능을 구현하면서 검색한 키워드를 결과 리스트 내에서 강조(하이라이트) 하는 기능을 새로 도입하게 되었는데요.

 

처음에는 검색어를 props로 하나하나 내려서 강조하려고 했지만,
컴포넌트가 많아질수록 관리가 어려워질 것이라는 생각이 들었습니다.

따라서 Context API를 사용해 검색 키워드와 하이라이트 함수를 전역으로 공유하는 방법을 선택하였습니다!

 

Context를 사용한 이유?

  1. 검색 결과는 한 번의 API 호출로 처리됨
    사용자가 검색어를 입력하면, 해당 검색어에 맞는 결과를 API 호출로 받아옵니다. 이 결과는 검색어가 바뀔 때마다 새로운 데이터를 가져와서 보여주는 방식이기 때문에 어차피 하위 컴포넌트들이 전부 리렌더링됩니다. 따라서 굳이 Zustand로 새로운 전역 상태관리를 저장할 필요없이, Context API만으로 처리하기에 충분하다고 생각했습니다.
  2. props drilling을 방지할 수 있음
    상태를 Context API로 관리하면, 여러 컴포넌트에서 동일한 데이터를 공유할 수 있게 됩니다. 이로 인해 프롭스 드릴링 문제를 방지할 수 있습니다. 즉, DOM 트리에서 중간의 여러 컴포넌트를 거쳐서 상태를 전달할 필요 없이, 컨텍스트를 통해 직접 상태를 가져다 사용할 수 있어 코드가 간결하고 유지보수가 용이합니다.

 

 

react-highlight-words 라이브러리 사용

검색 키워드를 강조할 때는 직접 문자열을 파싱하여 가공하는 것을 처리할 수도 있지만,
복잡한 케이스(HTML, 특수문자, 대소문자 구분 등)등이 생각보다 많았습니다.

그래서 이미 안정적으로 검증된 라이브러리인 react-highlight-words를 사용하여 해결했습니다.

 

해당 라이브러리를 선택한 이유는 다음과 같습니다.

  • 자동으로 특수문자, HTML 이스케이프 등을 안전하게 처리해줍니다.
  • autoEscape, highlightClassName 등 유용한 옵션을 제공합니다.
  • 텍스트 내 다중 키워드 강조가 매우 쉽게 가능합니다.

 

다이어그램

아래와 같이 Context API로 키워드 강조 기능을 설계했습니다.

 

키워드 하이라이팅은 검색 페이지에만 사용되는 기능이기 때문에, 이를 전역 상태(zustand)로 관리하는 것은 과도하다고 판단했습니다. 대신, 이를 위해, SearchHighlightContext를 사용하여 서치 페이지에서만 해당 상태를 관리하고, 상태를 SearchPage에 전달했습니다.

 

  • SearchPage에서 Context를 공급하고, 프로바이더로 감싸줍니다.
  • SearchProfile, SearchCard 등의 하위 컴포넌트에서 useContext로 하이라이트 함수를 사용합니다.

 

구현 방법

1. Context 생성

createContext를 이용해서 하이라이팅용 인터페이스를 생성합니다.

import React, { createContext } from "react";

interface SearchHighlightContextType {
  highlight: (text: string) => React.ReactNode;
  keyword: string;
}

export const SearchHighlightContext = createContext<SearchHighlightContextType>({
  highlight: (text: string) => text,
  keyword: "",
});

2. Provider 구성

react-highlight-words 라이브러리를 활용하여, 하이라이팅 기능을 제공하는 highlight 함수와 검색어(keyword)를 SearchHighlightContext.Provider로 검색 페이지에 공급합니다.

import { useRouter } from "next/router";
import Highlighter from "react-highlight-words";

export default function Search() {
  const router = useRouter();
  const keyword =
    typeof router.query.keyword === "string" ? decodeURIComponent(router.query.keyword) : "";

  const highlight = (text: string): React.ReactNode => {
    if (!text || !keyword) return text;

    return (
      <Highlighter
        highlightClassName="highlighted-keyword"
        searchWords={[keyword]}
        autoEscape
        textToHighlight={text}
        highlightStyle={{
          color: "#2bc466",
          backgroundColor: "transparent", 
        }}
      />
    );
  };

  return (
    <SearchHighlightContext.Provider value={{ highlight, keyword }}>
      <SearchPage />
    </SearchHighlightContext.Provider>
  );
}

 

 

기본적으로 배경색을 지정해주지 않으면 노란 형광펜처럼 배경색이 칠해집니다. (위 사진 참고)

따라서 저는 배경색을 따로 투명으로 설정했습니다!

 

3. 하위 컴포넌트 적용

SearchProfile, SearchCard, AllCard와 같은 하위 컴포넌트에서는 useContext를 사용하여 하이라이팅 함수를 받아와 검색어가 포함된 텍스트를 강조합니다.

// SearchProfile 컴포넌트
import { useContext } from "react";
import { SearchHighlightContext } from "@/pages/search";

export default function SearchProfile({ name, description, ... }: SearchProfileProps) {
  const { highlight } = useContext(SearchHighlightContext); // useContext 사용

  return (
    <div>
    ...
      <p>{highlight(name)}</p>
      <p>{highlight(description)}</p>
    ...
    </div>
  );
}

 

 

구현 화면 

이렇게 프로필, 그림 등의 검색 결과 페이지에서 하이라이팅 기능이 제대로 적용된 모습을 보실 수 있습니다!

실제 페이지 링크: https://www.grimity.com/search

 

 

저작자표시 비영리 동일조건

'Web > React' 카테고리의 다른 글

[React] 낙관적 업데이트(Optimistic Update) 제대로 구현해보기  (0) 2025.04.09
[React] 스크롤 애니메이션을 구현하는 다양한 방법을 알아보자  (0) 2025.04.03
[React] 웹 브라우저 최적화 입문 - Lighthouse를 통한 성능 분석  (0) 2024.10.12
[Tailwind CSS] React(JavaScript) & Vite + Tailwind CSS + PostCSS & AutoFixer로 프론트엔드 레포를 초기 구성하는 법  (0) 2024.10.03
'Web/React' 카테고리의 다른 글
  • [React] 낙관적 업데이트(Optimistic Update) 제대로 구현해보기
  • [React] 스크롤 애니메이션을 구현하는 다양한 방법을 알아보자
  • [React] 웹 브라우저 최적화 입문 - Lighthouse를 통한 성능 분석
  • [Tailwind CSS] React(JavaScript) & Vite + Tailwind CSS + PostCSS & AutoFixer로 프론트엔드 레포를 초기 구성하는 법
abyss-s
abyss-s
프론트엔드 공부합니다.
  • abyss-s
    abyss-s의 블로그입니다.
    abyss-s
  • 전체
    오늘
    어제
    • 분류 전체보기 (188)
      • Web (16)
        • JavaScript (6)
        • TypeScript (1)
        • React (5)
        • Vue (0)
        • Storybook (1)
        • Next.js (1)
      • Backend & Infra (8)
        • Database (3)
        • Node.js (2)
        • SpringBoot (1)
      • PS (71)
      • CS (30)
        • OS (13)
        • Structure & Algorithm (5)
        • Network (10)
        • 정보처리기사 (2)
      • Language (18)
        • OOP (1)
        • JAVA (13)
        • C++ (4)
      • Activities (12)
        • 멋쟁이 사자처럼 (2)
        • OSSCA (3)
        • LG U+ URECA (3)
        • Project (2)
      • AI (0)
      • Git & Github (5)
      • Notion (1)
      • IT (4)
      • Statistics (11)
      • Book (4)
      • Diary (1)
      • Game (1)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

    • 깃허브
    • 백준
    • 트위터
  • 공지사항

    • abyss-s의 티스토리에 오신 것을 환영합니다.
  • 인기 글

  • 태그

    Java
    BFS
    BAEKJOON
    파이썬
    DP
    OS
    자바기반응용프로그래밍
    github
    통계학
    코드트리
    운영체제
    생활코딩
    백준
    C++
    그리디
    JavaScript
    네트워크
    자바스크립트
    Python
    React
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.0
abyss-s
[React] Context API로 검색 결과 키워드를 강조해보자!
상단으로

티스토리툴바