프로젝트 전에 처음으로 주말 과제가 나왔다.
첫번째는 fetch함수를 이용하여 데이터를 받아온뒤, 그대로 하위 컴포넌트들에 뿌려 몬스터 카드를 노출시키도록 하는 과제고,
또 하나는 검색창을 활성화하여 검색창에 몬스터 이름을 검색하면 필터링되어 그 몬스터만 나오게 하는 것이다.
성공한 구현 영상 먼저 확인!
카드 컴포넌트는 하나이고, 출력해야 할 카드의 개수는 10개이며 fetch함수를 통해 전달받은 데이터들도 10개이다.
그런데 여기서 카드들을 하나 하나 하드코딩으로 할 수는 없고 검색 기능 또한 구현해야 하니 react의 장점을 살려서 구현해 보도록 하자
const [monsters, setMonsters] = useState([]);
useEffect(() => {
fetch('데이터를 받아올 주소', {
method: 'GET' // GET은 default값이라 생략이 가능하다
})
.then(res => res.json())
.then(data => {
setMonsters(data);
});
}, [])
받아올 몬스터 데이터는 배열의 형태이다. 따라서 useState에 초기값을 배열로 주어 저장하였다.
그 다음 monsters를 props에 담아 하위 컴포넌트로 보내주어야 한다.
monster.js에서 CardList 컴포넌트에 monsters값을 보내주고,
<CardList monsters={monster} />
CardList 컴포넌트에서는 Card 컴포넌트에 그대로 다시 보내준다.
<Card userInfo={monsters} />
아마 CardList 컴포넌트에서는 카드들을 scss를 통해 정렬해 주는 것 밖에 없는 것 같다.
근데 난 왜 두 줄로 안나오고 일렬로 쫙 나오지
function Card({userInfo}) {
const {id, name, email} = userInfo; // 구조분해할당
return (
<div className="cardContainer">
<img src={`https://robohash.org/${id}?set=set2&size=180x180`} alt="카드 이미지"/>
<h2>{name}</h2>
<p>{email}</p>
</div>
)
}
Card 컴포넌트에서는 상위 컴포넌트에서 받아온 props들을 뿌려서 출력되도록 한다.
처음에는 막막했었다. 평소 하던대로 한다면 같은 컴포넌트 안에서 useRef 등을 이용해 값을 주고받을 수 있었는데, 이건 컴포넌트를 넘나들면서 값을 주고 받아야 했기 때문이다.
또한 입력값이 없을 경우 모든 카드들을 출력해 줘야 했기에 반쯤 포기 상태였는데,
<SearchBox handleChange={handleChange} />
props를 통해 하위 컴포넌트로 전달할 수 있는 것은 값에 한정되어 있는 것이 아니었다.
함수 또한 props를 통해 전달할 수 있었다.
따라서 useState를 통해 관리하는 값과 실행하는 함수가 동일 선상에서 실행을 하고, 함수의 내용만 하위 컴포넌트로 보내 input값을 setState값으로 저장하게 할 수 있다.
const handleChange = (e) => { setUserInput(e.target.value) }
이런 함수가 있다고 하자. 이것을 하위 컴포넌트로 보내기 위해서는
<SearchBox handleChange={handleChange} />
이런 식으로 함수명을 props로 전달할 수 있다.
이렇게 넘기고 하위 컴포넌트에서 인자로 전달받아 console.log를 찍어보게 되면function SearchBox({handleChange}) { console.log(handleChange) return ( <input
![]()
<input
className="search"
type="search"
placeholder="Search..."
onChange={handleChange}
/>
Input 태그에서 onChange속성으로 handleChange라는 함수를 실행시킨다면 상위 컴포넌트에 있는 handleChange 함수가 실행되면서 setUserInput 함수로 인해 userinput에 value값이 저장된다.
그렇게 되면 상위 컴포넌트에서 저장된 value값을 가지고 물고 뜯고 맛보고 즐기고 할 수 있는 것이다.
그렇다면 이제 input값에 저장된 데이터를 기반으로 몬스터들을 검색해 보자
여러 컴포넌트들을 재사용하기 위한 방법으로는 물론 컴포넌트를 여러 번 작성하는 방법도 있다.
하지만 이 방법은 값을 하나 하나 주어야 하는 불편함도 있을 뿐더러 React에서 지향하는 방법과는 전혀 거리가 멀다.
따라서 array.method를 자주, 그리고 또 많이 사용한다.
지금까지 나는 array.map 함수 만을 써 봤고, 이번 연습을 통해서 filter도 써 보게 되었다.
예를 들어서 devil이라는 이름을 가진 몬스터가 있다고 가정하자. 그렇다면 devil을 검색하기 위해 검색 input창에 키보드를 누르게 되는데, 위에서 userinput에 저장된 값은 input 태그의 onChage속성 때문에 키보드를 입력함에 따라 d
de
dev
devi
devil
이런 식으로 userinput이 업데이트 될 것이다.
그렇다면 검색창에 이름을 입력할 수록 해당하는 이름이 들어있는 카드만을 노출시켜야 하는데,
devil과 develop, 또 depository라는 이름이 있다고 하고, dev까지 입력했을 때는 이름 세개 중에 devil, develop 두개가 보여야 한다는 것이다.
그렇다면 userinput에 들어오는 값을 가지고 filter를 생각해 볼 수 있다.
const filtered = monsters.filter(x=>x.name.toLowerCase().includes(userinput))
monsters
는 몬스터의 여러 정보들을 담은 객체들의 배열이다.
따라서 객체들을 하나씩 인자로 받아 그 안에 name이라는 객체의 value값이 userinput을 포함한다면,
그 filter로 걸러진 객체들(비교는 객체 안에 있는 name만 했지만 props로 넘겨줄 때는 객체 전체를 넘겨주어야 한다.)을 하위 컴포넌트로 넘겨주면 된다는 소리이다.
그렇다면 card 컴포넌트에 넘겨줄 props들을 바꿔보자
{
filtered.map((user, index) => (
<CardList key={index} monsters={user} />
))
}
기존에 monsters 배열 그대로를 props로 넘겨줬다면 이번에는 filter된 객체들만 넘겨주면 된다.
key값은 안줘도 에러가 뜨는 것은 아니니 상관 없지만 경고 내용을 보기 싫으신 분들이라면 꼭 넣어 주시면 된다.
그렇다면 여기서 하나 의문이 드는 문제가 있다.
검색창에 아무것도 입력하지 않으면
userinput
값이 없으니includes
함수는''
를 false로 반환해서 아무것도 출력하지 않아야 하는 것이 정상 아닌가?
나도 여기서 꽤 해멨었다. 그래서 컴포넌트 앞 부분에
{
monsters && filtered.map((user, index) => (
<CardList key={index} monsters={user} />
))
}
비교 연산자를 제대로 이해하지 못하고 이렇게도 해봤었는데(&& 연산자는 좌항과 우항 모두가 true 일 때만 true를 반환한다),
배열의 includes는 빈 문자열을 무조건 true로 반환한다. 그러니 모든 몬스터 카드가 출력되는 것이었다.