본문 바로가기
React/Example

React - Input 자동완성(Auto Completing) 기능 만들기

by 로터스233 2023. 4. 16.

구현할 예제

1. 만들고 싶은 기능 정리하기

auto completing은 검색기능을 만들 때 필수적으로 구현해야 하는 기능입니다.

알파벳을 하나 이상 input에 입력하면 그 입력값으로 시작하는 추천 검색어가 랜더링돼 user에게 보여지는 것이 핵심입니다.

  1. 입력 알파벳이 하나 이상이면 suggestion list 랜더링하기
  2. 입력 알파벳이 하나 이상이면 clear button이 보이도록 하기
  3. suggestion을 클릭하면 그 값을 input value로 세팅하기
  4. input에 알파벳을 입력 후 suggestion을 클릭하지 않은 상태에서, input과 suggestion 외부에서 클릭이벤트가 발생했을 때
    4-1. suggestion list가 닫히도록 하기
    4-2. input값은 입력값 그대로 남아 있게 하기
  5. clear button 클릭시 input값 비우기, suggestion list 닫기

여기에서는 세부적으로 5개 기능을 구현했습니다.

 

2. Auto complete Component

컴포넌트 내에서 상태관리가 필요한 것은 input의 text value와 suggestion값, 그리고 search가 시작됐는지 확인하기 위한 값 3가지입니다.

const [suggestions, setSuggestions] = useState([]);
const [text, setText] = useState("");
const [isAutoCompleting, setIsAutoCompleting] = useState(false);

2-1. 입력 알파벳이 하나 이상이면 suggestion list 렌더링하기

컴포넌트가 처음 랜더링될 때 나올 element들을 작성해줍니다.

return (
  <div
    className={styles.autoCompleteBox}
    >
    <input
      value={text}
      type="text"
      onChange={handleTextChanged}
      onClick={() => setIsAutoCompleting(true)}
      />
    {suggestions.length > 0 && renderSuggestions()}
  </div>
);

input에 값이 들어오면 onChange 함수를 이용해 textvalue를 세팅해주고, 이 값을 이용해 data에 들어있는 값들과 같은 것들을 찾아주면 됩니다.

배열의 list 만들고 textvalue값으로 data 있는 값들을 조회해 list 넣어줍니다. suggestions length 1 이상일 경우 랜더링이 일어나도록 했으므로 list배열을 suggestion 세팅해주면 됩니다.

  const handleTextChanged = e => {
    let suggestedDataList = [];
    let textValue = e.target.value;

    if (textValue !== "") {
      const regex = new RegExp(`^${tempValue}`, "i");
      suggestedDataList = initData
        .filter(e => regex.test(e.label))
    }
    setSuggestions(suggestedDataList);
    setText(textValue);
  };

만약 data에 같은 알파벳으로 시작하는 추천검색어가 많을 경우, 알파벳 순서대로 정렬해주기 위해 sort method를 이용해주면 됩니다.

MDN sort()

 

Array.prototype.sort() - JavaScript | MDN

sort() 메서드는 배열의 요소를 적절한 위치에 정렬한 후 그 배열을 반환합니다. 정렬은 stable sort가 아닐 수 있습니다. 기본 정렬 순서는 문자열의 유니코드 코드 포인트를 따릅니다.

developer.mozilla.org

const items = [
  { name: 'Edward', value: 21 },
  { name: 'Sharpe', value: 37 },
  { name: 'And', value: 45 },
  { name: 'The', value: -12 },
  { name: 'Magnetic', value: 13 },
  { name: 'Zeros', value: 37 }
];

// value 기준으로 정렬
items.sort(function (a, b) {
  if (a.value > b.value) {
    return 1;
  }
  if (a.value < b.value) {
    return -1;
  }
  // a must be equal to b
  return 0;
});

// name 기준으로 정렬
items.sort(function(a, b) {
  let nameA = a.name.toUpperCase(); // ignore upper and lowercase
  let nameB = b.name.toUpperCase(); // ignore upper and lowercase
  if (nameA < nameB) {
    return -1;
  }
  if (nameA > nameB) {
    return 1;
  }

  // 이름이 같을 경우
  return 0;
});

MDN 예제를 코드에 적용해줍니다.

const compareLabel = (a, b) => {
  const labelA = a.label.toUpperCase();
  const labelB = b.label.toUpperCase();

  if (labelA < labelB) return -1;
  if (labelA > labelB) return 1;

  return 0;
};

if (textValue !== "") {
  const regex = new RegExp(`^${tempValue}`, "i");
  suggestedDataList = initData
    .filter(e => regex.test(e.label))
  	.sort(compareLabel)
}

2.2 Clear button

입력값과 추천검색어 랜더링을 초기화할 버튼을 만들어줍니다.

 const clear = () => {
    setIsAutoCompleting(false);
    setText("");
    setSuggestions([]);
 };

{isAutoCompleting &&
  <button
    onClick={clear}
    >
    x
  </button>}

2.3 Suggestion값을 input값으로 세팅하기

알파벳을 입력하면 추천검색어가 나오기 시작합니다. 원하는 검색어를 마우스로 클릭하면 sugggestion list 감추고 input textContent 세팅되도록 했습니다.

const selectSuggestion = value => {
    setSuggestions([]);
    setText(value);
  }

<ul data-testid="test-suggestionlist">
      {suggestions.map((item, index) =>
      <li
         key={index}
         onClick={() => selectSuggestion(item.label)}
         >
         {item.label}
       </li>
      )}
</ul>

 

3. 예외처리

suggestion 선택이전, 외부에서 클릭이벤트가 발생했을 때 검색을 중단하도록 해야 합니다. 

input에 알파벳을 입력 후 suggestion을 클릭하지 않은 상태에서, input과 suggestion 외부에서 클릭이벤트가 발생했을 때를 위한 예외처리가 필요했습니다. 검색입력어는 input에 그대로 남아 있지만 suggestion list는 닫히도록 해야합니다.

 

input과 suggestion 외부에서 이벤트가 발생하므로, 그 둘을 감싸고 있는 부모 태그에 ref를 걸어두고 클릭 이벤트가 발생하는 곳이 부모 태그 외부인지 확인합니다. 검색어 search 시작됐는지를 확인하는 상태변수를 false 바꿔 search 중단하도록 하면 됩니다.

const handleClick = e => {
  if (boxRef.current && !boxRef.current.contains(e.target)) {
    setIsAutoCompleting(false);
  }
}

return (
	<div className={styles.autoCompleteBox} ref={boxRef}
)

'React > Example' 카테고리의 다른 글

React - Input 값을 Tag로 만들기  (0) 2023.04.16
React - Modal만들기(이벤트 버블링 차단)  (0) 2023.04.16