어제부로 드디어 길고 긴 2주의 파운데이션 기간이 끝났습니다. 리액트와 처음 접하게 되고, 여러 기능을 구현하는 데에 온 신경을 집중했던 것 같습니다.
그렇기 때문에 리액트를 사용할 줄은 있게 되었지만 온전히 활용하는 단계까지는 가지 못한 것 같습니다. 그래서 이번 주말에 받게 된 과제인 Monsters
라는 과제를 7번 반복해보려 합니다.
우리가 반복해서 하는 행동이 바로 우리이다.
그러므로 탁월함이란, 행동이 아니라 습관이다.아리스토텔레스
과연 리액트라는 몬스터를 7번 때려 잡고나면, 습관적인 리액트 활용이라는 전리품을 남겨줄 수 있을지 확인해보도록 하겠습니다.
Warning! 해당 과제는 state
, props
, componentDidMount()
, fetch()
, map()
, filter()
정도의 초급 레벨의 몬스터이니 리액트 천상계의 고이신 분들은 초보자들의 먹이로 남겨주시길 부탁드립니다.
API 주소: https://jsonplaceholder.typicode.com/users
위 주소를 호출하여 데이터 로딩을 처리해주세요!
- componentDidMount()
- fetch()
- setState(monsters에 저장)
Monsters.js
class Monsters extends Component { state = { monsters: [], userInput: "", }; componentDidMount() { fetch("https://jsonplaceholder.typicode.com/users") .then((res) => res.json()) .then((data) => this.setState({ monsters: data })); } }
- 파일: Monsters.js
- API 호출 후 저장한 배열을 자식 컴포넌트인 `<CardList />` 에 넘겨주세요. (props 활용)
Monsters.js
render() { const { monsters } = this.state; return ( <div className="Monsters"> <h1>컴포넌트 재사용 연습!</h1> <CardList monsters={monsters} /> </div> ); }
- 파일: CardList.js
- Card 컴포넌트를 import 한 뒤, props로 내려받은 데이터에
map 함수를 호출해 각각 다른 데이터를 가진 Card 컴포넌트들을 리턴해주세요!
- Card 컴포넌트에서 필요로 하는 데이터는 id, name, email 입니다.
CardList.js
const { monsters } = this.props; return ( <div className="card-list"> {monsters.map((monster) => { return ( <Card key={monster.id} id={monster.id} name={monster.name} email={monster.email} /> ); })} </div> );
- 파일: Card.js
- Card 컴포넌트 구조
<img src=이미지주소 alt="">
<h2>Name</h2>
<p>Email</p>
- Card 컴포넌트에서 props로 받아야하는 데이터는 id, name, email 입니다.
- props에서 해당하는 키값들을 추출하여 위와 같은 구조로 만들어주세요!
Card.js
class Card extends Component { render() { const { id, name, email } = this.props; return ( <div className="card-container"> <img src={`https://robohash.org/${id}?set=set2&size=180x180`} alt="" /> <h2>{name}</h2> <p>{email}</p> </div> ); } }
- 파일: monsters.js
- SearchBox 컴포넌트에 정의한 handleChange 메소드를 넘겨주고,
호출 시 인자로 들어오는 이벤트 객체(e)를 활용해 userInput으로 setState.
Monsters.js
class Monsters extends Component { state = { monsters: [], userInput: "", }; // 데이터 로딩 handleChange(e) { const { value } = e.target; this.setState({ userInput: value, }); } render() { const { monsters } = this.state; const { handleChange } = this; return ( <div className="Monsters"> <h1>컴포넌트 재사용 연습!</h1> // SearchBox에 props로 넘겨줄 handleChange 메소드 정의 <SearchBox handleChange={handleChange} /> <CardList monsters={monsters} /> </div> ); } }
- 여기서 비교 대상은 monster 객체의 name 값입니다.
- 소문자로 바꾼 monster.name 값과 userInput값을 비교.
- filter 메소드가 반환하는 값을 변수에 저장 후 return 문 안에 CardList에 props로 전달
Monsters.js
render() { const { monsters, userInput } = this.state; const { handleChange } = this; // 변수로 선언한 함수 const filteredMonsters = monsters.filter((monster) => { return monster.name.toLowerCase().includes(userInput); }); return ( <div className="Monsters"> <h1>컴포넌트 재사용 연습!</h1> <SearchBox handleChange={handleChange} /> <CardList monsters={filteredMonsters} /> </div> ); }
이 코드 제외하고는 직접 구현이 가능했습니다만, 이 부분은 아직 이해가 잘 되지 않습니다.
먼저, filter
함수를 render()
내에서 변수로 정의하여 진행한 이유는 검색창에 입력된 value
인 몬스터의 이름의 값이 데이터의 있는 이름과 일치하는 몬스터를 화면에 그려내야 하기 때문입니다.
여기까지는 이해가 되지만, 소문자로 바꾼 monster.name 값과 userInput값을 비교라는 점에서 뭔가 아래의 코드처럼 진행해야한다고 생각했습니다.
searchMonsters = id => {
const filteredMonsters = this.state.monsters.filter(
monster => monster.id === id
);
this.setState({ monsters: filteredMonsters });
};
그러나 이렇게 간단하게 render()
내의 함수 로직으로 간단하게 필터링을 진행할 수 있다는 점에서 또 다시 한 번 저의 머리를 강하게 내리친 코드였습니다.
이제 문제 풀이는 마무리 하고 쌓인 경험치에 대해 이야기를 나눠봅시다.
위와 같이 몬스터를 검색하고, 이름을 검색하여 잡아서 색출해내는 미니 프로젝트를 진행해보았습니다. 굉장히 단순한 프로젝트지만 여러 번 반복하다보면 리액트의 핵심 개념인 state
, props
, fetch()
, map()
, filter()
, componentDidMount()
의 이해를 한층 높여줄 것이라 생각합니다.
이번 주말을 통해서 7번 몬스터를 때려잡고 레벨업에 매진하도록 하겠습니다. 그럼 다음 글에서 뵙도록 하겠습니다! 👹🗡🏃♂️
[2021.08.29] 9번 완료!