[React] Monster 미니 프로젝트

devCecy·2020년 12월 11일
1

React

목록 보기
11/14
post-thumbnail

우여곡절 끝에 위스타그램을 끝냈지만, 스스로 프로젝트 하나를 처음부터 끝까지 해낼 수 있을까? 라는 마음속 질문이 생겼다. 그래서 Monster 미니프로젝트를 혼자 해내리! 라고 다짐했지만 역시나 '이정도면 이해된것같아~'라며 안일하게 넘겨버렸던 fetch함수에서 코드가 턱 막혀버렸다. 다시 이런 실수를 반복하지 않기위해 몬스터 프로젝트 진행 시 내가 처음 작성한 것과, 솔루션 영상에서 제시하는 것들이 무엇이 다른지 낱낱이 남겨 놓으려고 한다!


몬스터 프로젝트 최종 화면

1. fetch() 함수로 API 호출

1-1) 막혔던 코드

일단, fetch함수로 API를 불러오기위해 기존 로그인 세션때 배웠던 함수 형식을 그대로 가져와 내용을 변경했다. 그러자 method부분을 get이라고 적어준 부분에서 1차오류가 났고, body안에는 무엇을 적어줘야하나 난감한 상황이 되었다. 이상태로 콘솔을 찍어주면 {id: 11}을 반환하는데, API데이터 안에는 id 10까지만 존재했기에 어리둥절 했다. API에서 뭔가 가져온것 같긴한데 온전한 데이터 값이 아니였다.

 componentDidMount = () => {
    fetch("https://jsonplaceholder.typicode.com/users", {
      method: "POST",
      body: JSON.stringify({
        name: this.state.monsters.name,
        email: this.state.monsters.email
      }),
    })
      .then(response => response.json())
      .then(result => {
        this.setState({
          monsters: result
        })
      });
  }

1-2) 솔루션 코드

아래 코드는 componentDidMount함수 안에 fetch함수를 사용하여 가져올 API를 적어주었고, API의 결과값(response)를 가져오면 그것을 json형식으로 만들고 그 결과를 console.log로 찍어주는 코드다. 여기까지하면, 콘솔창에 API 데이터가 예쁘게 찍히는 것을 확인 할 수 있다.

배웠던 형식안에 무언가 우겨넣으려고 하지말고, 일단 본질에 충실해야함을 느꼈다. API를 가져오고 싶다면 일단 API를 가져왔는지 먼저 확인할 것 !!! 아, 참고로, 본 프로젝트에서 API를 GET방식으로 가져오는데, 디폴트값이 GET이기 때문에 생략해도 무방하다.

componentDidMount = () => {
    fetch("https://jsonplaceholder.typicode.com/users")
      .then(response => response.json())
      .then(response => {
        console.log(response)
      });
  }

데이터 잘 들어왔는지 확인했으니, setState로 monsters를 response로 할당해주면 API데이터 가져오는 것은 끄읕 !💜

componentDidMount = () => {
    fetch("https://jsonplaceholder.typicode.com/users")
      .then(response => response.json())
      .then(response => this.setState({
        monsters: response
      }));
  }

2.Array.map() 함수사용

API로 받아온 monster Array를 map함수로 돌려 주었다. 배열을 돌리는건 데이터를를 돌리는 것이기 때문에 Card컴포넌트에 만들어놓은 예쁜 레이아웃에 그 데이터를 찍어주면된다. monster를 인자로 받아 그 안에 id와, name, email을 넣어 주었다.

class CardList extends Component {
  render() {
    return (
      <div className="card-list">
        {this.props.monsters.map(monster => (
          <Card
            id={monster.id}
            name={monster.name}
            email={monster.email} />
        ))}
      </div>
    );
  }
}

2-1) map함수를 쓰고있다면, key값 잊지말것❗️

여기서 내가 놓쳤던 부분은, 리액트에서 map함수를 돌릴때는 꼭 유니크한 key값이 필요하다는 것! key값이 없다고해서 프로그램이 작동하지 않는 것은 아니지만, 빨간 오류 화면이 콘솔창에서 나를 따라다닌다.

{this.props.monsters.map(monster => {
          return <Card
            key={monster.id}  //키값을 잊지말자 
            id={monster.id}
            name={monster.name}
            email={monster.email} />
        }
      )}

3.props를 사용해 데이터 전달

하나씩 찍힐 몬스터 Card 컴포넌트안에 data를 props로 가져오면 끄읕. 😘
props랑은 이제 좀 친해진 기분이다.

class Card extends Component {
  render() {
    return (
      <div className="card-container">
        <img
          src={`https://robohash.org/${this.props.id}?set=set2&size=180x180`}
          alt={this.props.id} />
        <h2>{this.props.name}</h2>
        <p>{this.props.email}</p>
      </div>
    );
  }
}

4.filter()함수로 검색기능 구현


이 부분에서 시간이 좀 걸렸다. filter함수는 코드카타때 사용 해봤지만, 검색기능을 구현하는 것은 처음이였다. 그래도 머릿속으로 생각한 로직대로라면 금방 구현이 될 줄 알았는데, 이상하게도 잘 나오지 않았다. 문제가 뭘까 고민하다 구글링을 시작했는데 그렇게 몬스터 프로젝트 관련글은 정말 다 본것 같다. 😅

4-1) 막혔던 코드

처음에는 두가지 함수를 만들었다.
1) 검색바에 들어가는 Value를 가져오는 함수,
2) filter함수가 작동하는 함수.

그런데 이상하게도 함수 두가지를 부를 방법이 없는것이다... 그래서 SearchBox 컴포넌트 안에 onchange를 두번 불러보기도하고, handleInputValue안에서 handleFilterValue를 불러보기도하고, 정말 이리저리 다해보았다. 도데체 무엇이 문제란 말인가...검색이 잘 되지 않았다.😭

  handleInputValue = (event) => {
    this.setState({
      userInput: event.target.value
    })
  }

  handleFilterValue = () => {
    const filterMonster = this.state.monstersData.filter(monster => {
      return monster.name.toLowerCase().includes(this.state.userInput);
    });
    this.setState({ monsters: filterMonster })
  }

4-2) 솔루션 코드

문제는 바로, 역시나 조금은 가볍게여겼거나, 이미 다 이해했다고 생각했던 lifeCycle에 대한 이해로 부터 비롯된 것이였다. 필터함수를 render()안에 넣어야 한다는 생각을 꿈에도 못했던 것이다. 검색창에 입력된 value값이 데이터에 있는 이름값과 같다면 그 모습을 화면에 실시간으로 보여주어야 하기 때문에 filter함수가 render안에 있어야 했다.🔥

그리고 그렇게 정의된 filterMonster를 CardList의 Props안에 전달해주었다. 와, 사실 이부분도 또잉🙄? 했다. filterMonster안에는 Searchbar에 찍힌 value를 API데이터의 name과 비교해 포함된 것이 있다면 그것만 화면에 띄우도록 해주는 것이다.

render() {
    const filterMonster = this.state.monsters.filter((monster) => {
      return monster.name.toLowerCase().includes(this.state.userInput.toLowerCase());
    });
    return (
      <div className="Monsters">
        <h1>Monster 미니프로젝트</h1>
        <SearchBox
          onInputValue={this.handleInputValue}
        />
        <CardList monsters={filterMonster} />
      </div>
    );
  }

5. 마지막으로 꼭 짚고 넘어가자.

5-1) componentDidMount()

fetch함수를 사용해 API를 불러오는데 왜 componentDidMount()안에 넣어야 했을까? 이 부분을 정확히 이해하기 위해서는 lifeCycle을 따로 빼서 좀 더 정리해 봐야할 것 같다.
일단,componentDidMount()는 컴포넌트가 만들어지고 첫 렌더링을 다 마친 후 실행되는 메소드다. 이 메소드 안에서는 주로 다른 JavaScript 프레임워크를 연동하거나, setTimeout, setInterval 및 AJAX 처리 등을 넣으며, 이번 프로젝트의 경우와 같이 외부에서 Data를 가져올때도 componentDidMount()안에 넣어줘야한다고 한다.

5-2) filter()를 render()안에 넣는다.

흠, 실시간으로 filter된것을 보여주기위해 render()에 넣는다는것이 어렴풋 이해가되지만, 확 와닿진 않는다. 이 부분은 구글링과 질문으로 좀 더 명확하게 채워넣을 것.


이렇게 몬스터 미니 프로젝트도 끄읕 !
검색기능이 정말 흥미로웠고 한번 더 만들어 봐야겠다.👻💕

profile
🌈그림으로 기록하는 개발자🌈

0개의 댓글