[React] 몬스터 이름 검색하기

혜린·2022년 9월 9일
1

React

목록 보기
9/18
post-thumbnail

들어가며


추석연휴 전 마지막 리액트 과제가 주어졌어요. 이 과제의 목표는 아래와 같이 크게 세 가지였답니다.

  1. API 호출
  2. 컴포넌트 재사용
  3. 이름 필터링

API호출과 컴포넌트 재사용은 이전에 연습했던 내용이었기에 복습해볼 수 있었어요. 하지만 필터링 부분에서 🚨경고 문구🚨를 마주하고 해결하는 과정을 통해 useEffect에 대해 좀 더 이해 할 수 있게 됐어요.



1. API 호출


✅ 호출할 API

아래는 json placeholder의 테스트 데이터의 일부랍니다. 배열 속에 다양한 user의 정보가 객체의 형태로 저장되어있는걸 알 수 있어요.


✅ 데이터 로딩

setState로 monsters라는 state에 API를 호출해 받아온 정보를 저장해줘요. 첫 렌더링이 끝나 UI가 화면에 모두 그려진 뒤, 데이터를 불러올거기 때문에 useEffect속에 fetch를 넣어 불러온 데이터를 저장해줬어요.

// Monsters 컴포넌트
const [monsters, setMonsters] = useState([]);

useEffect(() => {
  fetch("https://jsonplaceholder.typicode.com/users")
    .then((res) => res.json()) // JSON형태의 데이터를 자바스크립트 형태로 변환해 반환
    .then((data) => setMonsters(data)); // 반환한 결과를 monsters에 저장
}, []);



2. 컴포넌트 재사용


✅ 컴포넌트 구조

컴포넌트 구조는 아래의 이미지와 같아요. Monsters컴포넌트 속 CardList컴포넌트가 있죠. 그리고 CardList에는 다양한 사용자의 정보가 담긴 Card가 똑같은 형태로 내용만 다르게 반복적으로 담겨있어요. 그래서 CardListmap()을 사용해 Card를 반복적으로 그려줬어요.

CardList 컴포넌트는 부모인 Monsters컴포넌트로부터 monsters를 받아와요. monsters에는 배열 속에 다양한 user의 정보가 객체의 형태로 담겨있죠? 각 객체를 map()으로 돌며 Card컴포넌트에 다양한 정보를 넘겨줘요. id, name, email을 넘겨주었어요. map()을 사용할 때 key를 설정하지 않으면 error메세지가 뜬다는 점!

// CardList 컴포넌트
function CardList({ monsters }) {
  return (
    <div>
      {monsters.map((data) => (
            <Card
              key={data.id}
              id={data.id}
              name={data.name}
              email={data.email}
            />
       ))}
    </div>
  );
}

아래는 CardList 컴포넌트 속에 있는 Card컴포넌트랍니다. 앞서 CardList 컴포넌트가 보내준 id, name, email을 구조분해 할당으로 받아왔어요. 이렇게 되면 CardList로부터 받아온 id, name, email의 내용에 따라 각각의 Card가 다른 내용을 담게 되겠죠?

// Card 컴포넌트
function Card({ id, name, email }) {
  return (
    <div>
      <img
        src={`https://robohash.org/${id}?set=set2&size=180x180`}
        alt="몬스터 사진"
      />
      <h2>{name}</h2>
      <p>{email}</p>
    </div>
  );
}



3. 이름 필터링


✅ input 저장

아래 보이는 Search검색창을 통해 아래 몬스터의 이름을 필터링한 결과를 볼 수 있어요. e를 치면 e가 이름에 포함된 몬스터들만이 화면에 그려지죠. ervin을 치면 아래 이미지의 왼쪽 몬스터만 나오게 되겠죠?

이를 구현하기 위해 우선 input에 사용자가 어떤 값을 입력하는지 저장해줘야해요. onChange이벤트의 콜백함수를 handleChange로 따로 선언해줬답니다. input내용이 바뀔때마다 setUserInput을 통해 그 내용이 저장되겠죠?!

// input의 onChange 이벤트로 사용자가 입력한 input 저장
const [userInput, setUserInput] = useState("");

const handleChange = (e) => {
  setUserInput(e.target.value);
};

✅ 필터링

만약에 사용자가 input에 한 글자라도 입력하면 그 내용이 몬스터의 이름에 있는지 확인해줘야해요. 그래서 몬스터의 이름을 toLowerCase()로 모두 소문자로 바꿔준 뒤, 사용자가 input에 입력한 값이 몬스터 이름에 들어있는지 확인해줬어요. 그리고, 만약 몬스터 이름에 해당 값이 있으면 filteredMonster라는 새로운 배열에 저장해줬어요. 검색하면 검색결과에 해당되는 몬스터만을 화면에 그려줘야하기 때문이죠!

// 수정 전 코드
const [filteredMonster, setFilteredMonster] = useState([]);

useEffect(() => {
  setFilteredMonster(
    monsters.filter((v) => v.name.toLowerCase().includes(userInput))
  );
}, [userInput]);

그런데 작동은 잘 됐지만 경고 메세지가 뜨더라구요. useEffect속에 monsters라는 state를 사용하면서 의존성 배열에는 왜 그 state가 없냐!는 경고였어요.


useEffect내에서 사용하는 state는 의존성 배열에 넣어줘야한다는 것! 의존성 배열에 넣은 state가 변화할 때마다 첫번째 인자에 있는 내용이 실행되니까요.

// 수정 후 코드
const [filteredMonster, setFilteredMonster] = useState([]);

useEffect(() => {
  setFilteredMonster(
    monsters.filter((v) => v.name.toLowerCase().includes(userInput))
  );
}, [monsters, userInput]);

input값을 입력하면 필터링된 정보를 갖고 있는 filteredMonster가 그려지고, 입력하지 않았을 땐 monsters가 화면에 그려져요. 구현에는 성공했지만 그렇게 효율적인 코드는 아니란 생각이 들어요. filteredMonster라는 새로운 배열을 만들지 않고 해결할 수 있는 방법은 없는지 좀 더 고민해봐야겠습니다!

function CardList({ monsters, filteredMonster, userInput }) {
  return (
    <div>
      {userInput.length > 0
        ? filteredMonster.map((data) => (
            <Card
              key={data.id}
              id={data.id}
              name={data.name}
              email={data.email}
            />
          ))
        : monsters.map((data) => (
            <Card
              key={data.id}
              id={data.id}
              name={data.name}
              email={data.email}
            />
          ))}
    </div>
  );
}



마치며


# 리팩토링
이름 필터링처럼 구현해 작동하긴 하는데 코드가 제가 봐도 효율적이지 않다는 생각이 들 때가 많아요. 구현에서 끝내지 말고 리팩토링에도 시간을 투자해야함을 느끼는 요즘입니다!


# 이론 & 실습
이론으로 배우는 것과 직접 적용해보는거랑 정말 다르더라구요. 이론적으로 어느정도 안다고 생각했는데 직접 적용해보면 제대로 알고 있는게 아닐 때가 많아요. 실습해보며 부족한 내용을 채워나가는게 가장 기억에도 잘 남는 것 같아요. 실습하며 새롭게 알게 된 내용이나 개념은 꼭 정리하고 넘어가자는 다짐을 해봅니닷📝💪🏻

profile
FE Developer

0개의 댓글