고양이 사진 검색기(디바운스, 로컬 캐쉬)

최훈오·2023년 11월 3일
0

데브코스

목록 보기
6/29

고양이 사진첩에 이어 고양이 사진 검색기를 만들어 보았다. 이전에 비해 검색이 들어가다보니 조금? 더 어려운 느낌이었다. 컴포넌트 간의 의존성 분리, 렌더링의 흐름은 이전 강의랑 코드 흐름은 매우 비슷하여서 성능 최적화와 관련된 디바운스와 로컬 캐쉬를 중심으로 글을 작성하였다.

문제점

    onKeywordInput: async keyword => {
      if (keyword.trim().length > 1) {
        const keywords = await request(`/keywords?q=${keyword}`);
        this.setState({
          ...this.state,
          keyword,
          keywords
        });
      }
    },

현재는 매번 검색창에 키워드를 입력할 때마다 onKeywordInput에 검색어인 keyword를 전달하여 서버에 API 요청을 보내므로 매우 비효율적이다.

디바운스

타이머를 통해 이벤트를 지연시키다가 이벤트가 발생하기 전에 같은 이벤트가 들어오면 전의 이벤트를 취소하고 다시 타이머를 거는 방식이다.


// debounce.js
// 타이머를 통해 이벤트를 지연시키다가 이벤트가 발생하기 전에 같은 이벤트가 들어오면 전의 이벤트를 취소하고
// 다시 타이머를 검
export default function debounce(fn, delay) {
  let timer = null;
  // 이벤트 핸들러로 사용될 함수
  return function () {
    const context = this; // 함수가 호출된 컨텍스트 저장
    const args = arguments; // keyword
    clearTimeout(timer);
    timer = setTimeout(() => {
      fn.apply(context, args);
    }, delay);
  };
}
  onKeywordInput: debounce(async keyword => {
      if (keyword.trim().length > 1) {
        const keywords = await request(`/keywords?q=${keyword}`);
        this.setState({
          ...this.state,
          keyword,
          keywords
        });
      }
    }, 300),

지연시간(0.3초)안에 함수를 호출하면 이전의 걸어놨던 타이머를 제거하고, 새로운 타이머를 setTimeout으로 설정하는데 apply를 사용하는 이유가 뭔지 궁금했다.

우선 debounce의 함수 인자로는 (이벤트 핸들러로 사용될 함수, 지연시간)으로 구성되어 있다.
그런데 apply가 왜 있는걸까?

debounce 내에서 setTimeout을 통해 함수를 호출하는데 이때 호출문 안에서의 thisApp 객체를 가리켜야 한다. 하지만, 기본적으로 setTimeout(비동기 함수)의 콜백 함수의 this는 전역객체를 가리키므로this 바인딩을 해줘야 하는데 이때 apply를 사용한 것이다. 또한 keyword를 담은 arugments를 저장했다가 함수 호출 시 두번째 인자로 넣어주었다.

local cache

키워드를 로컬에 저장했다가 다음에 같은 키워드를 입력했을시 서버에 다시 api 요청을 해서 값을 받아오는 것이 아니라 로컬에서 받아온 값을 이용하여 api 호출을 줄여 성능을 개선하였다.

  this.cache = storage.getItem('keywords_cache', {});
  
      onKeywordInput: debounce(async keyword => {
      if (keyword.trim().length > 1) {
        let keywords = null;
        if (this.cache[keyword]) {
          keywords = this.cache[keyword];
        } else {
          keywords = await request(`/keywords?q=${keyword}`);
          this.cache[keyword] = keywords;
          storage.setItem('keywords_cache', this.cache);
        }
        this.setState({
          ...this.state,
          keyword,
          keywords
        });
      }
    }, 300),
  

0개의 댓글