[React] 검색 기능 (feat. filter, includes 메서드)

Minjae JO·2022년 8월 13일
1

filter 메서드는 배열의 각 요소에 대해서 주어진 함수의 결과값을 만족하는(true인) 요소를 모아서 새로운 배열로 반환해주는 함수입니다.

배열에 요소가 없다면 함수를 실행하지 않고, 원본 배열을 변경하지 않습니다.

const words = ['spray', 'limit', 'elite', 'exuberant', 'destruction', 'present'];

const result = words.filter(word => word.length > 6);

console.log(result);
// expected output: Array ["exuberant", "destruction", "present"]

이 메서드를 이용하여 어떻게 검색 기능을 구현할 수 있을까요?

filter 메서드의 기능을 생각해본다면, 배열의 모든 요소들 중에 검색창에 입력한 값(input의 value)과 일치하는 값을 포함하고 있는 요소만을 반환하도록 하면 될 것 같은데요?

그렇다면, 배열의 요소가 검색창의 입력값과 일치하는 값을 포함하고 있는지는 어떻게 알 수 있을까요?

const array1 = [1, 2, 3];

console.log(array1.includes(2));
// expected output: true

const pets = ['cat', 'dog', 'bat'];

console.log(pets.includes('cat'));
// expected output: true

console.log(pets.includes('at'));
// expected output: false

includes 메서드는 배열이 특정 요소를 포함하고 있는지 아닌지를 판별해 boolean값, 즉 true or false로 반환해 줍니다.

앞서 filter 메서드가 주어진 함수의 조건에 결과값이 true인 요소들만 반환해준다고 했던 것, 기억하시나요?

이제 두 메서드를 이용해 리액트에서 검색 기능을 구현해 봅시다.

위에 보시는 monster 카드들은, 각각의 이름과 이메일 정보를 각각 객체로 담고 있는 monsters라는 배열(array)에 속해 있습니다.
맨 위의 검색창(searchbox)이 보이시나요? 검색창 컴포넌트는 input태그로 구현되어 있구요.

우리는 아래와 같이 검색창에 특정 값을 입력하면, 이름에 그 값을 포함하고 있는 monster 카드들만 보이게 하고 싶습니다.

우선 검색창(SearchBox) 컴포넌트의 구조를 보면 다음과 같습니다.

import React from "react";
import "./SearchBox.scss";

function SearchBox(props) {
  return (
    <input
      className="search"
      type="search"
      placeholder="Search..."
      onChange={props.handleChange}
    />
  );
}

export default SearchBox;

보시다시피, 검색창은 부모 요소로부터 props로 이벤트 함수를 받아오고 있습니다. 검색창에 입력 이벤트가 일어나면, handleChange 함수가 실행되겠군요.

그럼 검색창 컴포넌트를 품고 있는 부모 요소를 확인해볼까요?

import React, { useState, useEffect } from "react";
import SearchBox from "./Components/SearchBox/SearchBox";
import CardList from "./Components/CardList/CardList";
import "./Monsters.scss";

function Monsters() {
  const [monsters, setMonsters] = useState([]);
  const [userInput, setUserInput] = useState("");
  
  const handleChange = (e) => {
    setUserInput(e.target.value);
  };

  const searchedMonster = monsters.filter((monster) => {
    return monster.name.toUpperCase().includes(userInput.toUpperCase());
  });

  return (
    <div className="monsters">
      <SearchBox handleChange={handleChange} />
      <CardList key={monsters.i} monsters={searchedMonster} />
    </div>
  );
}

export default Monsters;

(monsters 배열은 fetch 함수로 외부에서 받아오고 있습니다)

검색창 컴포넌트가 props로 받고 있는 handleChange 함수를 찾으셨나요?
사용자가 값을 입력하면, setState 함수를 통해 userInput에 저장되고 있네요.

바로 이 userInput값이 monster의 이름에 포함이 되었는지 아닌지를 확인하고, 포함된 카드들(true인)만 화면에 보여주게끔 함수를 만들면 되지 않을까요?

위 코드의 searchedMonster가 바로 그렇게 만들어진 함수입니다.
monsters 배열에 filter 메서드가 사용되었네요. monsters 배열의 각 요소인 monster를 모두 확인한 후, return문의 조건에 해당하는 monster 카드만 새로운 배열로 반환하도록 해줍니다. return문을 자세히 봅시다.

return monster.name.toUpperCase().includes(userInput.toUpperCase());

(monsters배열의 요소인 monster는 name이란 키를 가진 객체입니다)

앞서 말했던 includes 메서드가 사용되었네요. 이 메서드가 인자로 받은 userInput이, monster.name에 포함되었는지 아닌지를 확인하고 있습니다.

그런데 toUpperCase는 무엇이며, 왜 사용된걸까요?
toUpperCase는 영문 문자열을 모두 대문자로 바꿔줍니다. 모두 소문자로 바꿔주는 toLowerCase라는 메서드도 있죠. 이 메서드를 사용한 이유는, 사용자가 대문자나 소문자, 어느 것으로 검색해도 같은 결과를 얻기 위해서입니다.

위 코드처럼 toUpperCase를 통해 사용자의 입력값과 monster의 이름을 모두 대문자로 한번 바꾸어준다면, 대소문자 여부에 상관없이 일치하는 값을 찾을 수 있겠죠? 같은 맥락으로 toLowerCase를 사용해도 됩니다.

마지막으로 searchedMonster 함수를 props로 monster 카드 자식 컴포넌트(CardList)에 넘겨주기만하면 끝입니다. 이제 CardList 컴포넌트는 searchedMonster를 통해 필터링된 monster 카드만을 렌더링 해 줄 것입니다.

대문자 S를 입력해도, 이름에 소문자 s를 가진 monster 카드도 잘 보여지고 있습니다.

0개의 댓글