본문 바로가기
Front End/React

[React] Deck of cards

by 옐 FE 2021. 9. 1.

The Modern React Bootcamp / Colt Steele

 

API와 React Lifecycle methods 중 하나인 componentDidMount를 사용하는 실습이었다. 사실 강의를 들으면서도 이 lifecycle methods를 정확히 어느 때에 사용을 하는건지 잘 모르겠지만, 이 componentDidMount의 경우 API를 fetch할 때 사용하는 거 같다. 리액트의 컴포넌트가 render될 때 constructor - render - componentDidMount 순으로 되는데 이 componentDidMount의 경우 받아오는 데이터가 로딩되기에 좋은 곳이라고 하네... 아무래도 API에서 데이터를 받아오는데 시간이 걸리니까 갖춰져있는 걸 먼저 다 보여준 뒤에 그리고 데이터가 다 받아지면 다시 렌더하는 건가 싶다. 그래서 async/await를 쓰는 거고. 이 강사님도 그렇고, 다른 강사님도 그렇고 fetch를 쓰기보단 axios라는 라이브러리를 써서 fetch를 하더라. 이게 조금 더 깔끔하고 간단한 코드를 쓸 수 있어서 그런 거 같다.

 

예제에 대한 실습을 먼저 해보고 나서 의문이었던 건 '카드를 어떻게 위에다가 제각기 쌓을 수 있는가' 였다. 내가 API를 받아오고 버튼을 누르면 카드가 보이게까지는 했는데 카드가 한 장씩 따로 보이지 않는 것. 이건 결국 스타일링으로 해결할 수 있었다. 그리고 전에 JS 강의를 들을 때 error도 반드시 잘 다룰 수 있어야 한다고 강조했는데, 이 실습에서 그걸 또 한 번 강조했다. ...사실 잊고 있었다. API를 다룰 땐 에러 역시 다룰 수 있어야 한다는 걸.

 

 

 

컴포넌트의 구성은 App > Deck > Card

class Deck extends React.Component {
.
.
  componentDidMount = async () => {
    const { data } = await axios.get(`${BASE_URL}/new/shuffle`);
    this.setState({
      deck: data,
      remaining: data.remaining,
      DECK_ID: data.deck_id
    });
  };

axios로 받아온 데이터를 다시 변수에 할당하고 그걸 setState를 이용해서 state를 바꿔주었다.

 

 

 

  onBtnClick = async () => {
    try {
      const { data } = await axios.get(
        `${BASE_URL}/${this.state.DECK_ID}/draw/`
      );
      if (!data.success) throw new Error('No card remaining 🃏');

      const cards = data.cards[0];
      this.setState(state => ({
        drawn: [
          ...state.drawn,
          {
            id: cards.code,
            name: `${cards.value} of ${cards.suit}`,
            image: cards.image
          }
        ]
      }));
    } catch (err) {
      alert(err);
    }
  };

그리고 내가 흥미롭게 본 코드. try와 catch를 에러도 다룰 수 있게 코드를 썼다. 카드의 갯수가 52장이고 조건을 remaining === 0이 아닌 !data.success로 한 건 마지막 한 장까지 받아오기 위해서인다. success: false일 때, 이 에러를 다루기 위해서 쓴 코드. 물론 에러를 다룰 때는 저렇게 alert으로 대체하면 안되지만... 전에 JS 강의 들을 때 에러를 사용자가 볼 수 있도록 코드를 썼었는데 기억이 나질 않네. 다시 한 번 복습하러 가야겠다. 여튼 API에서 받아온 데이터를 state.drawn 배열에 하나씩 차곡차곡 넣는다. 그래서 이걸로 map을 이용해 하위 컴포넌트인 Card에 props으로 데이터를 전달한다.

 

 

그러면 이제 의문, 카드를 어떻게 한 장씩 랜덤으로 따로 따로 엇갈리게 놓을 수 있을까. 

class Cards extends React.Component {
  constructor(props) {
    super(props);

    const positionX = Math.random() * 40 - 20;
    const positionY = Math.random() * 40 - 20;
    const angle = Math.random() * 90 - 45;
    this._transform = `translate(${positionX}px, ${positionY}px) rotate(${angle}deg)`;
  }

  render() {
    return (
      <img
        className="Card"
        style={{ transform: this._transform }}
        src={this.props.image}
        alt={this.props.name}
      />
    );
  }
}

버튼을 누를 때마다 카드를 한 장씩 받아오게 했는데 이걸 렌더 자체에서 실행하면 버튼을 누를 때마다 전에 받아온 카드까지 전부 다 다시 렌더되기 때문에 새롭게 받아온 카드만 랜덤으로 놓일 수 있도록 constructor에 설정을 했다. 이렇게 코드를 쓰니 새롭게 받아온 카드만 이미 받아온 카드 위에 놓이게 된다. (물론 카드의 position: absolute를 해줘야 함)

 

 

 


의문인 건 이 카드를 저렇게 또 새롭게 받아오도록 해야하는 것이다. 버튼을 누를 때마다 API가 매번 호출이 되는데 카드를 한 번만 다 받아와서 한 장씩 보여주게 할 순 없을까 라는 생각이 스쳤다. 그래서 deckofcards api에 들어가서 문서를 봤는데 deck_id마다 52개의 카드가 주어지고 이걸 한 장씩 받아오려면 또 다른 api를 써야해서 그런가 싶기도 하고... 어떻게 하는 게 제일 좋은 지 몰라서 의문인 것.

댓글