맨땅헤딩! 2023. 5. 9. 10:55

해당 포스트는 제로베이스 오프라인스쿨 진행 과정 중 JS PairProgramming과제를 진행하며 회고를 정리하는 글이다.

09 AutoComplete TIL
1. 정규표현식을 이용한 검사

const searchStr = e.target.value.toLowerCase().replace(/[^a-z\s+]/g, '');

const searchedCountryCode = countryCode.filter(_countryCode => _countryCode[1].toLowerCase().includes(searchStr));


상기 코드는 국가 코드의 국가명(countryCode[1])에서 includes로 검색할 문자(searchStr)를 포함하는지 포함하지 않는지를 검사하면서 검색된 문자열만을 반환하는 코드이다.

이 방식으로 하면, 소문자로도 검색했을 때에도 true가 나와야 하기 때문에 toLowerCase를 2번 써야 하는 불편함이 있다. 또한, 검색 문자(searchStr)가 전부 소문자로 변환되어, 나중에 render에서 No results matched ${searchStr}를 출력할 때, 원래 사용자가 입력했던 문자열이 아닌 소문자로 변환되고, 정규표현식으로 바뀐 문자열이 출력된다.

이러한 이유로, 불필요한 반복을 피하면서 검색 문자(searchStr)을 변경시키지 않기 위해, 정규표현식 객체를 생성하고 filter에서 test함수를 사용해 검사를 했다.

const searchStr = e.target.value;
const searchRegExp = new RegExp(searchStr, 'i');
const searchedCountryCode = countryCode.filter(_countryCode => searchRegExp.test(_countryCode[1]));


2. 상태 관리
검색이 되었던 국가목록(countryCode)과 검색 문자(searchStr)은 드롭다운을 닫고 다시 열어도 국가 검색 결과가 유지되어야 하기 때문에 상태를 만들었다.

2.1 상태 초기화

// state
let state = {
  countryCode,
  searchStr: '',
};


render를 할 때는, 상태를 매개변수로 받는다. 가장 처음에는 countryCode는 모든 countryCode를 담고 있어야 하기 때문에 import로 받아온 countryCode로 초기화 시켰다. 그리고, searchStr은 가장 처음에 아무 것도 입력되지 않은 빈 문자열이기 때문에 빈문자열로 초기화 시켰다.

2.2 setState

const setState = (newState = {}) => {
  state = { ...state, ...newState };
  render(state);
};


setState는 새로운 newState 객체를 받아 상태를 update 시킨 후, update된 상태로 render하는 함수이다.

setState는 input 이벤트 타입과 click 이벤트 타입을 가지는 이벤트 핸들러에서 호출된다.

$autocompleteToggleButton.addEventListener('click', () => {
  $autocompleteSuggester.classList.toggle('hide');

  // focus
  activeFocus($autocompleteSearch);

  // update state
  setState();
});


setState는 $autocompleteToggleButton이 toggle될 때, render를 하기 위해 호출된다.

이 때는, 기존의 상태 값으로 render하면 된다.

따라서, setState에 상태를 update시키기 위해 매개변수 값을 넘길 필요가 없다.

그런 경우를 대비해, newState에 빈 객체를 기본값으로 설정했다.

$autocompleteSearch.addEventListener(
  'input',
  _.debounce(e => {
    const searchStr = e.target.value;
    const searchRegExp = new RegExp(searchStr, 'i');
    const searchedCountryCode = countryCode.filter(_countryCode => searchRegExp.test(_countryCode[1]));

    // update state
    setState({
      countryCode: searchedCountryCode,
      searchStr,
    });
  }, 100)
);


$autocompleteSearch에서는 input 값과, filter가 된 countryCode 값이 계속해서 만들어 진다.

그때마다, setState를 호출해서 상태를 update시킨다.

3. debounce vs throttle
디바운스와 스로틀은 짧은 시간 간격으로 연속해서 발생하는 이벤트를 그룹화해서 과도한 이벤트 핸들러의 호출을 방지하는 프로그래밍 기법이다.

디바운스는 짧은 시간 간격으로 발생하는 이벤트를 그룹화하여 마지막에 한 번만 이벤트 핸들러가 호출되도록 한다. 

 

스로틀은 짧은 시간 간격으로 연속해서 발생하는 이벤트를 그룹화해서 일정 시간 단위 이벤트 핸들러가 호출되도록 호출 주기를 만든다.

디바운스는 input요소에 입력된 값으로 입력 필드 자동완성 UI구현, 버튼 중복 클릭 방지 등 유용하게 사용 되고 스로틀은 무한스크롤UI구현, 스크롤 이벤트 처리에 유용하게 사용된다고 배웠기에 디바운스를 적용했다.

디바운스(debounce) 함수
https://lodash.com/docs/4.17.15#debounce

스로틀(throttle) 함수
https://lodash.com/docs/4.17.15#throttle

4. document.activeElement
방향키와 Enter키가 눌렸을 때 포커스된 요소에 다음 요소 또는 이전 요소로 focus가 이동되거나 Select a country 버튼에 표시되는 기능을 구현할 때 현재 포커스된 요소를 알기 위해 document.activeElement 사용했다.

document.activeElement

https://developer.mozilla.org/en-US/docs/Web/API/Document/activeElement

 

<배운점>
++ document.activeElement에 대해 알게 되었다. 상황에 맞는 적절한 프로퍼티를 사용하면 가독성도 높아지고, 편리하게 코드를 만들 수 있다.

++ 이전 과제에서는 throttle함수를 사용해 보았는데 이번 과제에서는 debounce함수를 사용 해 보았다. debounce를 사용해야 하는 경우와 throttle을 사용해야 하는 경우를 적절히 판단해 써야겠다고 생각했다.