검색하는 단어에 강조 스타일 주기

가은·2024년 11월 13일
0

검색을 하면 결과 테이블에 검색 단어에 대한 강조 스타일을 주고 싶다는 요구사항이 있어서 뚝딱 만들어 봤당

실제 서비스 개발을 하니까 포트폴리오 할 때랑은 다른 여러가지 구현을 도전할 수 있어서 재밌다!.!

상태 관리

const [searchTerm, setSearchTerm] = useState(searchParam || '');
const searchInputRef = useRef<HTMLInputElement>(null);
  • searchTerm: 현재 검색어를 저장하는 state
  • searchInputRef: 검색 input 요소를 참조하기 위한 ref
  • URL의 검색 파라미터(searchParam)가 있으면 초기값으로 사용

검색어 입력 처리

const handleSearchInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
  setSearchTerm(e.target.value);
  
  const searchParams = new URLSearchParams(window.location.search);
  searchParams.set('page', '1');
  navigate(`?${searchParams.toString()}`);
};

const handleSearch = (e: FormEvent) => {
  e.preventDefault();
  const query = searchInputRef.current?.value || '';
  const searchParams = new URLSearchParams(window.location.search);

  if (query) {
    searchParams.set('search', query);
    searchParams.set('page', '1');
  } else {
    searchParams.delete('search');
    searchParams.set('page', '1');
  }
  navigate(`?${searchParams.toString()}`);
};
  • handleSearchInputChange: 입력값이 변경될 때마다 실시간으로 searchTerm 업데이트
  • handleSearch: 검색 폼 제출 시 URL의 검색 파라미터 업데이트

⚠️ 1페이지가 아닌 다른 페이지에서 검색어를 입력해서 새로 데이터를 렌더링 하면 검색이 제대로 되지 않아서 검색 동작 시엔 페이지를 무조건 1페이지로 보내고 검색하도록 page 관련 코드 추가

데이터 필터링

const filteredData = tableData.filter(row => {
  const matchesFilter =
    filterType === '전체' || row.방문유형 === (filterType === '개인' ? 1 : 2);

  const matchesSearch = searchTerm
    ? [row.체험자, row.체험학교, row.전화번호].some(field =>
        field?.toString().toLowerCase().includes(searchTerm.toLowerCase())
      )
    : true;

  return matchesFilter && matchesSearch;
});
  • 방문유형 필터와 검색어를 기준으로 데이터 필터링
  • 체험자, 체험학교, 전화번호 필드에서 검색어 포함 여부 확인
  • 대소문자 구분 없이 검색

⭐️ 하이라이트 처리

const highlightText = (text: string) => {
  if (!searchTerm?.trim() || !text) return <>{text}</>;

  const escapedSearchTerm = searchTerm.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');
  const regex = new RegExp(`(${escapedSearchTerm})`, 'gi');
  const parts = text.split(regex);

  return (
    <>
      {parts.map((part, index) => {
        if (part.toLowerCase() === searchTerm.toLowerCase()) {
          return <em key={index}>{part}</em>;
        }
        return part;
      })}
    </>
  );
};
  • 검색어를 정규식 특수문자로부터 보호
  • 검색어와 정확히 일치하는 부분을 <em> 태그로 감싸서 하이라이트
  • 대소문자 구분 없이 매칭
  • 연속된 문자열 단위로 하이라이트 처리 (예: "010" 검색 시 "010" 전체가 하이라이트)

📌

  • /[.*+?^${}()|[\]\\]/g
    • []: 문자 클래스를 정의. 이 안에 있는 문자들 중 하나와 매칭
    • 특수문자들: .*+?^${}()|[\]\
      • .: 모든 문자와 매칭
      • ``: 0회 이상 반복
      • +: 1회 이상 반복
      • ?: 0회 또는 1회 매칭
      • ^: 문자열의 시작
      • $: 문자열의 끝
      • {}: 반복 횟수 지정
      • (): 그룹화
      • |: OR 연산
      • []: 문자 클래스
      • \: 이스케이프 문자
    • g: 전역 검색 (모든 매칭을 찾음)
  • \\$&
    • \\: 백슬래시를 이스케이프
    • $&: 매칭된 전체 문자열을 참조
  • gi: 전역(g)과 대소문자 구분 없이(i) 검색
  • (): 매칭된 부분을 그룹화하여 split 결과에 포함

사용 방법

<td>{highlightText(row.체험자)}</td>
<td>{highlightText(row.체험학교)}</td>
<td>{highlightText(row.전화번호)}</td>

각 셀에 highlightText 함수를 적용하여 검색어와 일치하는 부분 하이라이트

짜잔

profile
일이 재밌게 진행 되겠는걸?

0개의 댓글