๐Ÿ”Ž ์ถ”์ฒœ ๊ฒ€์ƒ‰์–ด ๊ธฐ๋Šฅ

๐Ÿ›ซ ๋ฐฑ์—”๋“œ

  • backend - prisma
// /api/keyword/[keyword].ts
// async ํ•จ์ˆ˜ ๋‚ด๋ถ€์— ์žˆ๋‹ค๊ณ  ๊ฐ€์ •
const keywords = await prisma.keyword.findMany({
  where: {
    keyword: {
      contains: keyword,
    },
  },
  select: {
    keyword: true,
  },
});

๐Ÿ›ฌ ํ”„๋ก ํŠธ

1. ๋””๋ฐ”์šด์Šค ์ ์šฉ

๋””๋ฐ”์šด์Šค๋ž€ ์—ฐ์†ํ•ด์„œ ๊ฐ™์€ ์š”์ฒญ์ด ๋“ค์–ด์˜ฌ ๊ฒฝ์šฐ ์ œ์ผ ๋งˆ์ง€๋ง‰ ์š”์ฒญ๋งŒ ์œ ํšจํ•˜๊ฒŒ ์ฒ˜๋ฆฌํ•˜๋Š” ๋ฐฉ๋ฒ•

์ƒํ’ˆ ๊ฒ€์ƒ‰์˜ ๊ฒฝ์šฐ ๊ฒ€์ƒ‰์–ด๋ฅผ ์ž…๋ ฅํ•  ๋•Œ์˜ ์ด๋ฒคํŠธ์— ์ถ”์ฒœ ๊ฒ€์ƒ‰์–ด๋ฅผ ๋ฐ›์•„์™€์„œ ๋ณด์—ฌ์ฃผ๋Š” ๊ธฐ๋Šฅ์ด ์žˆ๋Š”๋ฐ ์ด๊ฒƒ์„ ๋””๋ฐ”์šด์Šค๋ฅผ ์ ์šฉํ•˜์ง€ ์•Š๊ฒŒ๋˜๋ฉด ์‚ฌ์šฉ์ž๊ฐ€ ํ•œ๊ธ€์ž๋ฅผ ๋ˆ„๋ฅผ ๋•Œ๋งˆ๋‹ค ํ•œ๋ฒˆ์˜ ์š”์ฒญ์ด ๋‚ ์•„๊ฐ€๊ธฐ ๋•Œ๋ฌธ์— ์„œ๋ฒ„์— ๋ถ€๋‹ด์ด ์‹ฌํ•ด์งˆ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ด๋Ÿฐ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด์„œ ๋””๋ฐ”์šด์Šค๋ฅผ ์ ์šฉํ•ด์„œ ๊ฒ€์ƒ‰์–ด๋ฅผ ์—ฐ์†์ ์œผ๋กœ ์ž…๋ ฅํ•  ๋•Œ๋Š” ์„œ๋ฒ„๋กœ ์š”์ฒญ์„ ๋ณด๋‚ด์ง€ ์•Š๋„๋ก ์ฒ˜๋ฆฌํ–ˆ์Šต๋‹ˆ๋‹ค.

2. ํฌ์ปค์‹ฑ

์ถ”์ฒœ ๊ฒ€์ƒ‰์–ด๋ฅผ ๋ฐ›์•„์˜ค๊ณ ๋‚˜์„œ ๋žœ๋”๋งํ–ˆ์„ ๊ฒฝ์šฐ ์‚ฌ์šฉ์ž๊ฐ€ ๊ฒ€์ƒ‰์ฐฝ์— ํฌ์ปค์Šค๋ฅผ ์คฌ์„ ๊ฒฝ์šฐ์—๋งŒ ์ถ”์ฒœ ๊ฒ€์ƒ‰์–ด๋ฅผ ๋ณด์—ฌ์ฃผ๋„๋ก ๋งŒ๋“ค์—ˆ์Šต๋‹ˆ๋‹ค.

์ดˆ๊ธฐ์—๋Š” onFocus์™€ onBlur๋ฅผ ์ด์šฉํ•ด์„œ ํฌ์ปค์Šค๊ฐ€ ๋– ๋‚˜๋ฉด ์ถ”์ฒœ ๊ฒ€์ƒ‰์–ด๋“ค์ด ์‚ฌ๋ผ์ง€๋„๋ก ๋งŒ๋“ค์—ˆ๋Š”๋ฐ ๊ทธ๋ ‡๊ฒŒ ๊ตฌํ˜„ํ•˜๋ฉด ์ถ”์ฒœ ๊ฒ€์ƒ‰์–ด๋ฅผ ํด๋ฆญํ•˜๊ฑฐ๋‚˜ ์ถ”์ฒœ ๊ฒ€์ƒ‰์–ด์— ํฌ์ปค์Šค๋ฅผ ์ฃผ๋„๋ก ์ด๋™ํ•  ๊ฒฝ์šฐ์— ๊ฒ€์ƒ‰์ฐฝ์˜ onBlur๊ฐ€ ์‹คํ–‰๋˜์–ด ์ถ”์ฒœ ๊ฒ€์ƒ‰์–ด๋“ค์ด ์‚ฌ๋ผ์ง€๋Š” ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค.

๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด์„œ ์ด์ „์— ์‚ฌ์šฉํ•ด๋ดค๋˜ ๋ชจ๋‹ฌ ์˜์—ญ์™ธ ํด๋ฆญ ์‹œ ๋‹ซ๋Š” ๊ธฐ๋Šฅ์„ ๊ฐ€์ ธ์™€์„œ ์ ์šฉํ–ˆ์Šต๋‹ˆ๋‹ค.

// 2022/04/16 - ํ‚ค์›Œ๋“œ ๊ฐ’ - by 1-blue ( react-hook-form์˜ useForm() )
const keyword = watch("keyword");
// 2022/04/16 - ํ‚ค์›Œ๋“œ ํฌ์ปค์Šค ์—ฌ๋ถ€ ๋ฐ ๊ด€๋ จ ํ‚ค์›Œ๋“œ ๋ณด์—ฌ์ค„์ง€ ๊ฒฐ์ •ํ•  ๋ณ€์ˆ˜ - by 1-blue
const [isFocus, setIsFocus] = useState(false);
// 2022/04/16 - ํ‚ค์›Œ๋“œ ๊ฒ€์ƒ‰ ์‹œ ๋””๋ฐ”์šด์Šค ์ ์šฉํ•  ๋•Œ ์‚ฌ์šฉํ•˜๋Š” ๋ณ€์ˆ˜ - by 1-blue
const [debounce, setDebounce] = useState(false);
// 2022/04/16 - ํ‚ค์›Œ๋“œ ๊ฒ€์ƒ‰ ์‹œ ๋””๋ฐ”์šด์Šค ์ ์šฉํ•  ๋•Œ ์‚ฌ์šฉํ•˜๋Š” ํ•จ์ˆ˜ - by 1-blue
const debounceKeyword = useCallback(() => setDebounce(true), [setDebounce]);
// 2022/04/16 - ํ‚ค์›Œ๋“œ ๊ฒ€์ƒ‰ ์‹œ ๋””๋ฐ”์šด์Šค ์ ์šฉ - by 1-blue
useEffect(() => {
  const timerId = setTimeout(debounceKeyword, 300);

  return () => {
    clearTimeout(timerId);
    setDebounce(false);
  };
}, [debounceKeyword, keyword, setDebounce]);
// ๊ฒ€์ƒ‰์ฐฝ๊ณผ ์ถ”์ฒœ ๊ฒ€์ƒ‰์–ด๋ฅผ ์ž์‹์œผ๋กœ ๊ฐ€์ง€๋Š” element
const wrapperRef = useRef<HTMLDivElement>(null);
// 2022/04/16 - ์˜์—ญ์™ธ ํด๋ฆญ ์‹œ ์ถ”์ฒœ ํ‚ค์›Œ๋“œ ์ฐฝ ๋‹ซ๊ธฐ - by 1-blue
const handleCloseModal = useCallback(
  (e: any) => {
    if (
      isFocus &&
      (!wrapperRef.current || !wrapperRef.current.contains(e.target))
    )
      setIsFocus(false);
  },
  [isFocus, setIsFocus, wrapperRef]
);
// 2022/04/16 - ์ถ”์ฒœ ํ‚ค์›Œ๋“œ ์ฐฝ ๋‹ซ๊ธฐ ์ด๋ฒคํŠธ ๋“ฑ๋ก - by 1-blue
useEffect(() => {
  setTimeout(() => window.addEventListener("click", handleCloseModal), 0);
  return () => window.removeEventListener("click", handleCloseModal);
}, [handleCloseModal]);
// 2022/04/16 - ์ถ”์ฒœ ํ‚ค์›Œ๋“œ ํŒจ์น˜ - by 1-blue
const { data: recommendKeywords } = useSWR<IResponseOfRecommendKeywords>(
  debounce && keyword ? `/api/keyword/${keyword}` : null
);

์ข‹์€ ์›นํŽ˜์ด์ง€ ์ฆ๊ฒจ์ฐพ๊ธฐ