연관 내용
[검색 프로세스]
[debouncing]
[버튼 눌러서 검색하기]
검색 하기 버튼을 누르지 않아도 자동으로 검색이 되도록 하려면,
검색창의 onChange
함수가 실행될 때 refetch
가 일어나면 된다.
하나하나 입력할 때마다 refetch가 일어나면 무수히 많은 요청이 가서 서버에 부하를 줄 수 있으므로, debouncing을 이용해서 일정 기간 텀을 두고 요청을 보내게 만든다.
yarn add lodash
yarn add '@types/lodash' --dev
import _ from 'lodash'
const getDebounce = _.debounce((data) => {
// data: event.target.value
// 0.2초 동안 재작업이 없으면 실행되는 부분
setKeyword(data);
refetch({ search: data, page: 1 }); // 바로 실행되지 않고, 0.2초 동안 재입력이 일어나지 않으면 그 때 리페치가 실행된다.
}, 200); // 0.2초
const onChangeSearch = (event: ChangeEvent<HTMLInputElement>) => {
getDebounce(event.target.value);
};
원리
전체 문자열을 단어 단위로 쪼개놓고 span 태그로 만들어준다.
검색된 태그에만 스타일을 준다.
const [keyword, setKeyword] = useState("");
const getDebounce = _.debounce((data) => {
setKeyword(data);
}, 200);
{el.title
.replaceAll(keyword, `#$%${keyword}#$%`)
.split("#$%")
.map((el) => (
<Word key={uuidv4()} isMatched={keyword === el}>
{el}
</Word>
))}
interface IProps {
isMatched: boolean;
}
const Word = styled.span`
color: ${(props: IProps) => (props.isMatched ? "red" : "black")};
`;
전체 코드
import { useQuery, gql } from "@apollo/client";
import styled from "@emotion/styled";
import { ChangeEvent, useState } from "react";
import {
IQuery,
IQueryFetchBoardsArgs,
} from "../../src/commons/types/generated/types";
import _ from "lodash";
import { v4 as uuidv4 } from "uuid";
import * as S from "./day20.style";
const FETCH_BOARDS = gql`
query fetchBoards($search: String, $page: Int) {
fetchBoards(search: $search, page: $page) {
_id
writer
title
contents
}
}
`;
export default function MapBoardPage() {
const [keyword, setKeyword] = useState("");
const { data, refetch } = useQuery<
Pick<IQuery, "fetchBoards">,
IQueryFetchBoardsArgs
>(FETCH_BOARDS);
const getDebounce = _.debounce((data) => {
// data: event.target.value
// 0.2초 동안 재작업이 없으면 실행되는 부분
setKeyword(data);
refetch({ search: data, page: 1 }); // 바로 실행되지 않고, 0.2초 동안 재입력이 일어나지 않으면 그 때 리페치가 실행된다.
}, 200);
const onChangeSearch = (event: ChangeEvent<HTMLInputElement>) => {
getDebounce(event.target.value);
};
const onClickPage = (e: any) => {
refetch({ page: Number(e.target.id) });
};
return (
<>
<S.Wrapper>
<S.SearchWrapper>
검색어 입력
<S.Search type="text" onChange={onChangeSearch} />
</S.SearchWrapper>
{data?.fetchBoards.map(
(
el // 인자로 index를 써서 사용할 수 있음: 순서, 필요 없으면 안 써도 됨
) => (
<S.Row key={el._id}>
<S.Title>
{el.title
.replaceAll(keyword, `#$%${keyword}#$%`)
.split("#$%")
.map((el) => (
<S.Word key={uuidv4()} isMatched={keyword === el}>
{el}
</S.Word>
))}
</S.Title>
<S.Writer>{el.writer}</S.Writer>
</S.Row>
)
)}
<S.PageWrapper>
{new Array(10).fill(1).map((_, index) => (
<S.Page
onClick={onClickPage}
id={String(index + 1)}
key={index + 1}
>
{index + 1}
</S.Page>
))}
</S.PageWrapper>
</S.Wrapper>
</>
);
}