[TIL] 자동완성 만들기

기성·2024년 9월 22일
1

TIL

목록 보기
59/81

프로젝트중에 훈련소 검색을 하는 부분이 있었는데 처음에 사용자가 어떤 훈련소가 있을지 잘 모르겠다라는 생각이 들었다. 그래서 검색을 할 때 비슷한 키워드를 입력을 받게되면 그 키워드가 포함된 리스트를 띄워주도록 만들어야 겠다 생각했다.

전체 코드

import { useState } from 'react';
import { FiSearch } from 'react-icons/fi';
import { useNavigate, useSearchParams } from 'react-router-dom';
import useRestaurantsStore from '../store/useRestaurantsInfo';

// 상수 파일로 분리
const OPTION_LIST = [
  '3사단',
  '5사단',
  '6사단',
  '7사단',
  '12사단',
  '15사단',
  '21사단',
  '28사단',
  '31사단',
  '32사단',
  '35사단',
  '36사단',
  '37사단',
  '39사단',
  '50사단',
  '51사단',
  '55사단',
  '육군훈련소',
  '해병대',
  '해군교육사령부',
  '공군교육사령부'
];

const AutoComplete = ({ mode }) => {
  const [filteredOptions, setFilteredOptions] = useState([]);
  const [searchInput, setSearchInput] = useState('');
  //NOTE searchParams 미사용
  const [_, setSearchParams] = useSearchParams();
  const navigate = useNavigate();

  //변수명
  const { setIsOpen } = useRestaurantsStore((state) => state);

  const handleSearch = (e) => {
    e.preventDefault();
    if (searchInput.trim() === '') return;
    //입력값이 비슷한 신교대나 훈련소를 찾아서 검색 ex)육군=>육군훈련소, 공군=>공군교육사령부

    const keyword = OPTION_LIST.find((e) => e.includes(searchInput));
    if (!OPTION_LIST.includes(keyword)) {
      alert('존재하지 않는 교육대입니다.');
      return;
    }

    if (mode === 'home') {
      // 쿼리스트링을 포함한 리다이렉트
      navigate(`/mainpage?query=${encodeURIComponent(keyword)}`);
      return;
    }
    if (searchInput.trim() === '') return;
    setSearchParams({ query: keyword });
    setIsOpen(false);
    setFilteredOptions([]);
  };

  const handleInputChange = (e) => {
    const value = e.target.value;
    setSearchInput(value);

    if (value) {
      setFilteredOptions(OPTION_LIST.filter((option) => option.includes(value)));
    } else {
      setFilteredOptions([]);
    }
  };

  const handleOptionClick = (option) => {
    setSearchInput(option);
    setFilteredOptions([]);
  };

  return (
    <div className={`relative w-full ${mode === 'home' ? '' : 'flex items-center max-w-sm'}`}>
      <form className="flex w-full" onSubmit={handleSearch}>
        <div className="w-full">
          <input
            type="text"
            value={searchInput}
            onChange={handleInputChange}
            placeholder="검색"
            className="pl-4 pr-10 py-2 w-full h-full border border-gray-300 rounded-md focus:outline-none"
          />
          {filteredOptions.length > 0 && (
            <ul className="max-h-[150px] w-full overflow-y-auto mt-[5px] absolute rounded-md">
              {filteredOptions.map((option, index) => (
                <li
                  className="p-[10px] cursor-pointer bg-white border-2 border-[--dark-khaki-color] mb-1 rounded-md"
                  key={index}
                  onClick={() => handleOptionClick(option)}
                >
                  {option}
                </li>
              ))}
            </ul>
          )}
        </div>

        <button
          type="submit"
          className={
            mode === 'home'
              ? 'ml-3 bg-[--dark-khaki-color] text-white px-6 h-12 rounded-md flex items-center justify-center whitespace-nowrap focus:outline-none'
              : 'absolute right-0 pr-3 bottom-2.5'
          }
        >
          {mode === 'home' ? '검색' : <FiSearch className="text-gray-500" size={20} />}
        </button>
      </form>
    </div>
  );
};

export default AutoComplete;

사단 전체에 대한 리스트를 상수로 만들어주고 입력을 받으면 includes()를 통해 키워드의 포함 여부를 찾고 필터된 리스트를 보여주는 방식으로 구현하였다.

그러면 요렇게 리스트를 보여준다!

profile
프론트가 하고싶어요

0개의 댓글