[React] 리스트 검색 기능 / 컴포넌트 재사용

hongduhyeon·2022년 3월 26일
22
post-thumbnail
지나친 음주는 건강에 악영향을 미칩니다.

예전부터 구현해보고 싶었던 기능중에 하나가 검색했을 때 단어마다 내가 검색하고자 했던 키워드가 나오는 기능을 구현 해보고 싶었다. 하지만 무서워서(?) 시도해보지 않았지만 이번에 좋은 예제를 받아 도전을 해봤다.

함수형 컴포넌트입니다.

어떤 기능을 구현하니 ?

  1. open API에서 useEffectfetch 그리고 setState 사용해서 데이터 로딩 처리
  2. 검색 컴포넌트에서 정의한 function을 넘겨주고 값을 setState를 사용해서 저장
  3. filter()를 사용해서 받아온 리스트의 list.name에 값과 검색 컴포넌트에서 작성한 리스트의 값이 일치하는 리스트를 보여주기

크게 말하면 이렇게 3가지로 나뉜다.
벌써 무섭다

1. 데이터 로딩

우선 fetch 함수를 사용해서 API 주소를 받아온 후 useState로 빈 배열을 만들고 거기에 저장을 해줬다.
fetch 함수는 사용하면 사용할수록 신기방기

const [monsters, setMonsters] = useState([]);

useEffect(() => {
  fetch("작고 소중한 오픈 API 주소 🖥 ", {
    method: "GET",
  })
    .then((res) => res.json())
    .then((res) => {
    setMonsters(res);
  });
}, []);

2. 검색 컴포넌트

먼저 살며시 위에 useState로 빈 문자열을 하나 만들어주자 그리고 검색 기능과 내가 받아온 데이터에게 보금자리 컴포넌트까지 작성해준다.

각 컴포넌트의 주요 기능은 이렇게 된다.

  • <SearchBox /> : input 태그의 컴포넌트, userInput의 값이 전달되는 컴포넌트이다.
  • <CardList /> : search와 filter()를 사용해서 걸러진 값을 토대로 검색한 단어를 가진 리스트가 나오는 컴포넌트

function Monsters() {
  const [monsters, setMonsters] = useState([]);
  const [userInput, setUserInput] = useState("");

  useEffect(() => {
    fetch("작고 소중한 오픈 API 주소 🖥 ", {
      method: "GET",
    })
      .then((res) => res.json())
      .then((res) => {
        setMonsters(res);
      });
  }, []);
  
  const handleChange = (e) => {
    setUserInput(e.target.value);
  };
  return (
    <div className="monsters">
      <h1>컴포넌트 재사용 연습!</h1>
      <SearchBox handleChange={handleChange} />
    </div>
  );
}

handleChange 함수는 input에 입력되는 값들을 저장하는 역할을 하며 e(event) 를 사용해서 event를 주고 setUserInput에 value 값을 저장해주는 방식으로 작성헀고 props를 이용해 SearchBox에게 전달해주고 SearchBox에 있는 input에 props를 전달해줬다.

console.log에서 확인해보면 input에 입력한 값들이 정상적으로 들어가는게 확인이 된다.

3. filter 기능

function Monsters() {
  const [monsters, setMonsters] = useState([]);
  const [userInput, setUserInput] = useState("");

  // 데이터 로딩
  useEffect(() => {
    fetch("작고 소중한 오픈 API 주소 🖥 ", {
      method: "GET",
    })
      .then((res) => res.json())
      .then((res) => {
        setMonsters(res);
      });
  }, []);
  // SearchBox 에 props로 넘겨줄 handleChange 메소드 정의
  const handleChange = (e) => {
    setUserInput(e.target.value);
  };
  const filterdMonster = monsters.filter((monster) => {
    return monster.name.toLowerCase().includes(userInput.toLowerCase());
  });
  return (
    <div className="monsters">
      <h1>컴포넌트 재사용 연습!</h1>
      <SearchBox handleChange={handleChange} />
      <CardList key={monsters.id} data={filterdMonster} />
    </div>
  );
}

처음에 데이터 로딩으로 받아온 monsters state를 내가 검색한 값에 맞게 확인을 해주는 작업을 해야한다.

요즘 들이고 있는 습관중에 하나는 값이 제대로 전달이 됐고 내가 전달한 값이 데이터 타입에 맞게 들어오는지 확인을 하는 습관을 기르고 있다.
그래서 못참고 console.log()에 monsters를 찍어봤더니 원했던 객체 10개의 값이 제대로 들어왔고 이젠 난 이걸 올바르게 사용할 로직을 짜면 된다.

(10) [{…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}]

이제 이 값을 내가 검색한 단어에 맞게 filter 되는 기능을 구현해보려 하는데 하나의 기준을 잡고 filter 기능을 만들었다.

대문자도 소문자로 인식해서 값을 가져오기

이렇게 해야 굳이 대문자로 검색하지 않아도 input에 작성한 값을 가져올 수 있다.

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

먼저 데이터를 올바르게 가져왔고 그 데이터를 상대로 filter()를 사용했다. javascript - filter
filter를 하는 조건을 해석해보자면 각 요소의 name값을 소문자로 변경하고 includes를 사용해서 내가 입력한 값들의 소문자가 맞는걸 확인해주는 기능이다.

이렇게 작성하고 컴포넌트에 key값도 걸러진 배열의 id값으로 작성해주고 props를 전달하면 걸러진 값들을 전달할 수 있게 된다. map() 작성시 key값의 중요성

4. map()을 이용한 리스트 보여주기

자.... 메인과 검색 컴포넌트에서 작성해줄수 있는건 다 작성을 해줬다. 그럼 이제 리스트를 뽑아보자.

// CardList.js
function CardList({ data }) {
  return (
    <div className="cardList">
      {data.map((card) => (
        <Card key={card.id} {...card} />
      ))}
    </div>
  );
}

되게 뭐가 없다.
하지만 약방에 감초같은 녀석들이 있기 때문에 블로깅을 참을 수 없다.

받아온 props는 객체 데이터로 전달되기 때문에 function CardList 옆에 props가 오는 곳에서 {data}로 전달 받아야한다. 예전에 쓰린 아픔을 겪어본 입장으로써 놓칠 수 없는 부분이다.

그리고 이제 리스트를 보여줘야하는데 메인에서 검색하면 input에 값이 입력되고 값이 입력된 리스트들로 나뉘기 때문에
filter가 된 값들이 들어오게 된다.
그렇다는건 검색을 하지 않으면 내가 원하는 값들을 보여주고 검색어를 입력하면 ?
내가 원하는 리스트를 보여줄 수 있다.

들어오는 값들을 보여주기위해서 map()을 사용했고 Card 컴포넌트를 순회하면 보여주는 방식으로 작성했다.
여기서도 중요한 map() 작성시 key값의 중요성.

코드를 잠시 해석해보자면 key값을 filter로 분류된 객체들의 id를 부여해줘서 각각 고유한 키값을 줬고,
spread 연산자를 사용해서 받아온 리스트를 props로 전달하는 방식으로 작성했다.

또 console.log를 참지 못하고 {data} 값을 찍어봤을때 원했던 값들이 제대로 전달이 되는걸 확인할 수 있었다. (짜릿해..)

5. 리스트를 가지는 컴포넌트 props 받기

function Card(props) {
  const { id, email, name } = props;
  return (
    <div className="cardContainer">
      <img src={`https://이미지 주소.org/${id}?set=set2&size=180x180`} alt="" />
      <h2>{name}</h2>
      <p>{email}</p>
    </div>
  );
}

상위부모에서 짜릿하게 전달 받은 props 값을 구조 분해 할당으로 좀 더 사용하기 쉽게 작성을 하고 각 위치에 맞는 값들을 넣어줬다.

하지만

리스트에 맞는 이미지 값들을 전달해줘야한다. 이게 뭐람 끝난 줄 알았는데 아니었다.
하지만 과거의 홍두현의 공부 행적을 보면 과거의 부지런했던 홍두현의 Templete Literal 에서 영감을 받았다.
map은 배열안에 요소를 순회하면서 돌기 때문에 props로 전달받은 고유의 key값을 사용해서 각각 이미지 주소를 변경해줄 수 있었다.
(이래서 기본기가 중요한듯함.)

결과

  • 검색어 입력 전
  • 검색어 입력 후

    ps. 이미지 hover 이벤트를 줘서 사이즈가 안맞는 것이니 불편하지 않길 바람

후기

만들고자 했던 filter() 기능의 작업을 끝냈다. 뭔가 시작전에 되게 무조건 어려울 것 같다는 생각이 있었는데 막상 해보고 나니 그렇게 어렵지만은 않았으나 기본기가 갖춰져있지 않다면 어려웠을 것 같다는 생각이 들었다. 분명 다양한 곳에서 이런 형식이나 이런 형식 보다 좀 더 심화된 형식으로 작업을 할 수도 있을 것 같다는 느낌이 들었다. 그리고 직접 과거에 작성한 본인의 개념 정리 게시글에 도움을 많이 받았다. 이 긴 글을 보는 누구라도 본인이 과거의 본인에게 도움을 받을 수 있다는걸 알려주고 싶다. 끝 !

profile
마음이 시키는 프론트엔드

6개의 댓글

comment-user-thumbnail
2022년 4월 2일

코드 리뷰가 필요이상으로 꿀잼이네요 !! 유익하게 잘봤습니다!

1개의 답글
comment-user-thumbnail
2022년 4월 3일

계장님 몬스터 엄청 때려잡으셨네요! 축하드립니다 !

1개의 답글
comment-user-thumbnail
2022년 4월 3일

정말,, 블로그 우마이~

1개의 답글