"나는 서울에사는 로건이다"라는 단어를 예시로 들어보자. "서울"이라는 단어(토큰)를 전체적으로 검색 하면 모든 값을 검색해야하는데, Disk i/o가 생겨 이 접근 방식은 시간이 걸리는 편이다.
✌️ 역인덱스 (inverted-index) 방식
✌️ 토크나이징 (토큰화 하는것 - 단어 쪼개기)
이 과정을 쉽게 해주는 것으로 엘라스틱서치가 있고, 여기서 한 번 이라도 가져온 값은 Redis에서 캐싱하여 더욱 빠르게 접근하게 도와준다.
Redis에 없으면, 엘라스틱서치로, 엘라스틱서치에 없으면 DB로 직접 찾아가는 방식으로 데이터를 가져오게 된다.
✌️ 디바운싱 (Debouncing)
=> 특정 시간이내, 추가 입력 없을 시, 마지막 1회만 실행
✌️ 쓰로틀링 (Throttling)
=> 특정 시간이내, 추가 입력 있어도, 1회만 실행
(모두 setTimeout으로 구현할 수 있음)
디바운싱(Debouncing)은 검색을 할 때 자동으로 결과값이 나오도록 하는 방식에 많이 사용하게 되었다. onChange 이벤트로 검색이 입력될 때마다 결과값을 가져오게 하였는데, 많은 요청이 들어오게 되어 문제가 되었다.
import _ from "lodash"; const getDebounce = _.debounce((value) => { void refetch({ search: value, page: 1 }); setKeyword(value); }, 500); const onChangeSearch = (event: ChangeEvent<HTMLInputElement>): void => {event.currentTarget.value, page: 1 }); getDebounce(event.currentTarget.value); };
위의 방식으로 검색을 하게 되면 onChangeSearch를 지나 getDebounce로 값을 보내게 되고, 이 곳에서 마지막 입력 후 500 => 0.5초가 지난 후에 검색의 결과 값이 나오게 된다.
쓰로틀링(Throttling)은 스크롤 이벤트 시에 많이 사용하게 된다. 특정 시간내에 요청한 이벤트 중 마지막 것을 반영해주어 수많은 스크롤 이벤트를 막아준다.
const handleScroll = (event: UIEvent<HTMLElement>) => { const { scrollTop, clientHeight, scrollHeight } = event.currentTarget; if (clientHeight + scrollTop >= scrollHeight) onLoadMore(); }; const onLoadMore = _.throttle(() => { if (data === undefined) return; void fetchMore({ variables: { page: Math.ceil(data?.fetchUseditems.length ?? 10 / 10) + 1, }, updateQuery: (prev, { fetchMoreResult }) => { if (fetchMoreResult.fetchUseditems === undefined) return { fetchUseditems: [...prev.fetchUseditems] }; return { fetchUseditems: [ ...prev.fetchUseditems, ...fetchMoreResult.fetchUseditems, ], }; }, }); }, 1000);
위 코드는 무한스크롤을 할 때 사용하였는데, 스크롤을 내릴 때 수 많은 onScroll이벤트가 발생하게 된다. throttle을 적용해 주면, 해당 시간인 1000=>1초안에서 발생한 마지막 이벤트의 값이 나오게 된다.