import { useFoStore } from '@/app/store'; import type { facilityListProps } from '@/app/store/processFacility'; import { Label } from '@common/components/ui'; import React, { useEffect, useState, useRef, useCallback } from 'react' type thisProps = { label: string | undefined; setValue : (fcltyCd?: string, fcltyNm?: string, fcltyCpc?: number | '', trtFrfNm?: string | '', efflWqltStndrdNm?: string | '') => void; sidoSigun? : string | undefined; fcltyName? : string | undefined; } function useDebouncedValue(value: T, delay = 500) { const [v, setV] = useState(value); useEffect(() => { const t = setTimeout(() => setV(value), delay); return () => clearTimeout(t); }, [value, delay]); return v; } export default function SearchInputText({label=undefined, setValue=()=>{}, sidoSigun=undefined, fcltyName=undefined} :thisProps) { const { ProcessFacilityList, searchProcessFacilitySearching } = useFoStore((state) => state); const [isOpen, setIsOpen] = useState(false); const [textValue, setTextValue] = useState(fcltyName || ''); const debouncedTextValue = useDebouncedValue(textValue); const wrapperRef = useRef(null); const processFacilityList = ProcessFacilityList?.processFacilityList || []; const handleInputChange = useCallback((e: React.ChangeEvent) => { const value = e.target.value; setTextValue(value); if (value) { setIsOpen(true); // 추천 목록 표시 // 삭제된 기존 코드: // if(sidoSigun) { // searchProcessFacilitySearching(1, 4999, 'A', sidoSigun, '0', '0', '0', value); // } else { // searchProcessFacilitySearching(1, 4999, '0', '0', '0', '0', '0', value); // } // onChange 안에서 바로 검색하면 글자를 입력할 때마다 요청이 발생해서 // debounce가 사실상 동작하지 않습니다. // 실제 검색은 아래 debounced useEffect에서만 실행되도록 변경했습니다. } else { setIsOpen(false); } }, []); const onInputFocus = useCallback(() => { setIsOpen(true); if (textValue.trim()) return; // 입력값이 없을 때만 기본 목록을 조회합니다. // 이미 입력 중인 경우에는 기존 값으로 다시 전체/광범위 조회하지 않습니다. if(sidoSigun) { searchProcessFacilitySearching(1, 4999, 'A', sidoSigun, '0', '0', '0'); } else { searchProcessFacilitySearching(1, 4999, '0', '0', '0', '0', '0'); } }, [searchProcessFacilitySearching, sidoSigun, textValue]); useEffect(() => { setTextValue(fcltyName || ''); }, [fcltyName]); useEffect(() => { const keyword = debouncedTextValue.trim(); if (!keyword) return; // debounce가 적용된 값이 확정된 뒤에만 검색해서 // 시설명을 입력하는 도중의 과도한 검색 요청을 막습니다. if(sidoSigun) { searchProcessFacilitySearching(1, 4999, 'A', sidoSigun, '0', '0', '0', keyword); } else { searchProcessFacilitySearching(1, 4999, '0', '0', '0', '0', '0', keyword); } }, [debouncedTextValue, searchProcessFacilitySearching, sidoSigun]); const handleSuggestionClick = useCallback((bscFctCd: string, bscFctNm: string, fctCpc: number | '', trtFrfNm: string | '', efflWqltStndrdNm: string | '') => { setTextValue(bscFctNm); setValue(bscFctCd, bscFctNm, fctCpc, trtFrfNm, efflWqltStndrdNm); setIsOpen(false); }, [setValue]); useEffect(() => { const handleClickOutside = (event: MouseEvent) => { if (wrapperRef.current && !wrapperRef.current.contains(event.target as Node)) { setIsOpen(false); } }; document.addEventListener('mousedown', handleClickOutside); return () => { document.removeEventListener('mousedown', handleClickOutside); }; }, []); return (
{label && } {isOpen && processFacilityList.length > 0 && (
{processFacilityList.map((item : facilityListProps, index : number) => (
handleSuggestionClick(item.bscFctCd, item.bscFctNm, Number(item.fctCpc), item.trtFrfNm, item.efflWqltStndrdNm)} role="button" tabIndex={0} onKeyDown={(e) => { if (e.key === 'Enter' || e.key === ' ') { handleSuggestionClick(item.bscFctCd, item.bscFctNm, Number(item.fctCpc), item.trtFrfNm, item.efflWqltStndrdNm); } }} > {item.sido} {item.gugun} {item.bscFctNm}
))}
)}
) }