[React] 몬스터를 7번 때려잡으면?

OROSY·2021년 8월 28일
9

React

목록 보기
16/27
post-thumbnail

👹 Monsters

어제부로 드디어 길고 긴 2주의 파운데이션 기간이 끝났습니다. 리액트와 처음 접하게 되고, 여러 기능을 구현하는 데에 온 신경을 집중했던 것 같습니다.

그렇기 때문에 리액트를 사용할 줄은 있게 되었지만 온전히 활용하는 단계까지는 가지 못한 것 같습니다. 그래서 이번 주말에 받게 된 과제인 Monsters라는 과제를 7번 반복해보려 합니다.

우리가 반복해서 하는 행동이 바로 우리이다.
그러므로 탁월함이란, 행동이 아니라 습관이다.

아리스토텔레스

과연 리액트라는 몬스터를 7번 때려 잡고나면, 습관적인 리액트 활용이라는 전리품을 남겨줄 수 있을지 확인해보도록 하겠습니다.

Warning! 해당 과제는 state, props, componentDidMount(), fetch(), map(), filter() 정도의 초급 레벨의 몬스터이니 리액트 천상계의 고이신 분들은 초보자들의 먹이로 남겨주시길 부탁드립니다.

🎥 구현 화면

🌲 컴포넌트 구조

💁‍♂️ 과제 안내

  1. API 호출
  2. API 호출의 결과값 props로 자식에게 전달
  3. Array.map() 사용
  4. props 활용

1. API 호출

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 }));
  }
}

2. API 호출의 결과값 props로 자식에게 전달

- 파일: Monsters.js
- API 호출 후 저장한 배열을 자식 컴포넌트인 `<CardList />` 에 넘겨주세요. (props 활용)

Monsters.js

render() {
  const { monsters } = this.state;
  return (
    <div className="Monsters">
      <h1>컴포넌트 재사용 연습!</h1>
      <CardList monsters={monsters} />
    </div>
  );
}

3. Array.map() 사용

- 파일: 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>
);

4. props 활용

- 파일: 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>
    );
  }
}

🏋️‍♀️ 추가 과제) 검색 기능

1. 이벤트 객체 활용한 메소드

- 파일: 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>
    );
  }
}

2. 필터링 로직 구현 (filter 메소드 활용)

- 여기서 비교 대상은 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번 완료!

profile
Life is a matter of a direction not a speed.

0개의 댓글