[React]검색 기능 키워드 하이라이팅

전유덕·2024년 8월 27일
0
post-thumbnail

개요

사용자가 검색한 키워드를 하이라이팅하여 시각적으로 강조하는 기능은 사용자 경험을 크게 향상시킬 수 있습니다. 부트캠프 파이널프로젝트 진행 당시 구현 경험을 작성합니다.

기본 구조 설명

우선, 우리가 다룰 컴포넌트 구조를 간단히 살펴보겠습니다.

ComplainsContent 컴포넌트 : 검색 결과를 렌더링하고, 각 결과 항목을 ComplainItem 컴포넌트로 전달합니다.
ComplainItem 컴포넌트 : 개별 항목의 데이터를 표시하며, ComplainsContent로부터 전달된 검색 키워드를 사용하여 제목을 하이라이팅합니다.

ComplainsContent 컴포넌트에서의 구현

ComplainsContent 컴포넌트는 여러 개의 게시글을 검색하고 그 결과를 리스트 형태로 보여줍니다. 여기서 핵심은 사용자가 검색한 키워드를 포함한 게시글 제목을 강조하는 것입니다.

아래는 ComplainsContent 컴포넌트의 일부 코드로, 검색 키워드를 강조하는 기능을 보여줍니다.

const highlightKeyword = (title: string, keyword: string) => {
  if (!keyword) return title; // 키워드가 없으면 원래 제목을 반환
  const parts = title.split(new RegExp(`(${keyword})`, "gi")); // 키워드를 기준으로 제목을 분할
  return (
    <>
      {parts.map((part, index) => (
        <span
          key={index}
          style={
            part.toLowerCase() === keyword.toLowerCase()
              ? { color: "red" } // 키워드와 일치하는 부분은 빨간색으로 강조
              : {}
          }
        >
          {part}
        </span>
      ))}
    </>
  );
};

highlightKeyword 함수

이 함수는 두 개의 인자를 받습니다. 첫 번째는 title (게시글 제목)이고, 두 번째는 keyword (사용자가 검색한 키워드)입니다.

키워드 하이라이팅 로직

  • title을 사용자가 검색한 keyword로 나눕니다. 정규식(Regular Expression)을 사용하여 키워드를 기준으로 제목을 분할합니다. 이때 정규식의 "gi" 플래그를 사용하여 대소문자를 구분하지 않고(global) 모든 일치 항목을 검색(case-insensitive)합니다.
  • 결과로 반환된 배열 parts는 제목의 키워드 부분과 나머지 부분으로 나누어집니다.
  • parts 배열을 반복하며, 키워드 부분은 <span> 태그로 감싸고 빨간색으로 스타일링하여 강조합니다. 나머지 텍스트는 기본 스타일로 렌더링합니다.

ComplainItem 컴포넌트에 프롭스 전달하기

ComplainsContent 컴포넌트는 검색 결과 목록을 렌더링하기 위해 ComplainItem 컴포넌트를 사용합니다. 여기서 highlightKeyword 함수를 호출하여 각 게시글 제목을 하이라이팅 처리한 후, 해당 결과를 title 프롭스로 ComplainItem 컴포넌트에 전달합니다.

{responseData.posts.content.map((item) => (
  <ComplainItem
    key={item.id}
    id={item.id}
    user_nickname={item.user_nickname}
    status={item.status}
    category_name={item.category_name}
    title={highlightKeyword(item.title, keyword)} // 하이라이트 된 제목 전달
    image_urls={item.image_urls}
    visible={item.visible}
    count_reaction_type_good={
      item.reaction_columns
        ? item.reaction_columns.count_reaction_type_good
        : 0
    }
    count_of_comments={item.count_of_comments}
    hits={item.hits}
    created_at={item.created_at}
    ishot={item.hot}
    isnew={item.new}
  />
))}

ComplainItem 컴포넌트에서 하이라이팅된 제목 사용하기

ComplainItem 컴포넌트는 ComplainsContent 컴포넌트로부터 받은 프롭스를 사용하여 게시글 항목을 렌더링합니다. 여기서 하이라이팅된 제목은 이미 highlightKeyword 함수에 의해 <span> 태그와 스타일링이 적용된 상태입니다. 따라서 ComplainItem에서는 추가적인 처리 없이 그대로 제목을 렌더링할 수 있습니다.

import Link from "next/link";

type FreeBoardItemProps = {
  id: number;
  status: string;
  user_nickname: string;
  category_name: string;
  title: string | React.ReactNode; // 제목은 문자열 또는 리액트 노드로 받을 수 있음
  image_urls?: string[] | null;
  visible: boolean;
  count_reaction_type_good: number;
  count_of_comments: string;
  hits: number;
  created_at: string;
  ishot: boolean;
  isnew: boolean;
};

export default function ComplainItem({
  id,
  status,
  user_nickname,
  category_name,
  title, // 프롭스로 전달받은 하이라이트된 제목
  image_urls,
  visible,
  count_reaction_type_good,
  count_of_comments,
  hits,
  created_at,
  ishot,
  isnew,
}: FreeBoardItemProps) {
  const handleClick = () => {
    if (!visible) {
      alert("비밀글입니다.");
    }
  };

  const convertDate = (dateString: string) => {
    const date = new Date(dateString);
    const year = date.getFullYear();
    const month = (date.getMonth() + 1).toString().padStart(2, "0");
    const day = date.getDate().toString().padStart(2, "0");
    return `${year}.${month}.${day}`;
  };

  const convertedDate = convertDate(created_at);

  return (
    <tr className="text-center border-b border-grey_200">
      <td className="px-4 py-7 text-grey_300">
        {status === "RECEIVED" ? "접수" : status === "IN_PROGRESS" ? "처리중" : "처리완료"}
      </td>
      <td className="px-4 py-7 text-grey_300">{category_name}</td>
      <td className="text-left text-grey_900 font-medium">
        <div className="flex items-center">
          <Link
            className="mr-2"
            href={visible ? `/complains/${id}` : "#"}
            onClick={handleClick}
          >
            <p className="font-medium">{title}</p> {/* 하이라이트된 제목 사용 */}
          </Link>
        </div>
      </td>
      <td className="px-4 py-7 text-grey_900">{user_nickname}</td>
      <td className="px-4 py-7 text-grey_900">{count_reaction_type_good}</td>
      <td className="px-4 py-7 text-grey_900">{hits}</td>
      <td className="px-4 py-7 text-grey_900">{convertedDate}</td>
    </tr>
  );
}

컴포넌트 간 데이터 전달과 협력

이 예제에서 ComplainsContent 컴포넌트는 전체 게시글 데이터를 관리하며, 검색 키워드에 따른 하이라이팅 로직도 처리합니다. 그 후, 개별 게시글 항목을 렌더링하기 위해 ComplainItem 컴포넌트를 사용합니다. title 프롭스로 하이라이팅된 제목을 ComplainItem에 전달함으로써, ComplainItem은 이 데이터를 단순히 출력하기만 하면 됩니다.

profile
zi존 개발자 되고싶다ㅏㅏ(훈수 대환영!)

0개의 댓글