As i wish

[React 숫자 야구] props 와 component 분리 본문

React JS

[React 숫자 야구] props 와 component 분리

어면태 2019. 6. 21. 01:01

안녕하세요 엄티 입니다.

오늘은 component 분리와 props에 대하여 공부해봤습니다.

 

저는 제로초 님의 웹게임 강좌를 기준으로 공부 합니다.

 

제로초임의 웹게임 강좌

 

리액트 무료 강좌(웹게임) - YouTube

 

www.youtube.com

일단 그 전에 간단하게 React 에서 파일을 불러오는 두 방법 import 와 require에 대하여 알아보겠습니다.

 

import ClassApp 을 하려면 파일에 export default ClassApp 이라고 정의되어 있어야 하며

import { hello, bye } 같이 다중으로 import 하려면 export const hello = 'hello', export const bye = 'bye' 라고 정의되어 있어야 합니다.

이와 마찬가지로

const ClassApp = require('filePath') 처럼 하려면 파일에 module.exports = ClassApp 이라고 정의되어 있어야 하고

const { hello, bye }  = require('filePath') 처럼 하기 위해선 exports.hello = 'hello', exports.bye = 'bye' 라고 정의되어 있어야 합니다.

 

같은 기능을 하지만 import는 es2015 문법, require 는 commonJS 문법이라고 보통 지칭 하죠.

 

각설 하고 component 분리 부터 해보겠습니다.

일단 한 파일에 여러 컴포넌트를 쓰는것이 react에 핵심이라고 할 수 있는데요.

이전 포스팅에서는 한 파일에 한 컴포넌트만 썼었죠. 그러나 지금은 다른 컴포넌트를 import 해서 사용 하도록 해보겠습니다.

 

위처럼 숫자 야구를 하는 결과창을 만들건데요. 일단 간단히 생각해 봐도 컴포넌트가 여러개 있죠.

사실 테스트이기 때문에 결과값을 맨위에 보여주었지만 사실은 안보여줘야 겠죠?

 

무튼 숫자를 입력하는 컴포넌트, 내가 입력한 결과에 대한 답을 보여주는 컴포넌트가 있겠죠.

특히 결과 값을 보여주는 컴포넌트는 반복문을 사용 (오늘은 map) 을 사용해서 구성해 보겠습니다.

 

number-baseball.jsx

import React, { Component, createRef} from 'react';
import Try from './try';

// 재사용 가능성이 있는 함수는 밖에다
const getNumbers = () => {
  const candidates = [1, 2, 3, 4, 5, 6, 7, 8, 9];
  const array = [];
  for (let i=0; i<4; i++) {
    const randIndex = Math.floor(Math.random() * (9 - i));
    const chosen = candidates.splice(randIndex , 1)[0];
    array.push(chosen);
  }

  return array;
}

class ClassApp extends Component {
  state = {
    result: '',
    value: '',
    answer: getNumbers(),
    tries: [] // {try, result}
  }
  input = createRef();

  _initState = () => {
    this.setState({
      value: '',
      answert: getNumbers(),
      tries: []
    }); 
  }

  _onSubmit = (event) => {
    const { value, tries, answer } = this.state
    event.preventDefault();

    if (value === answer.join('')) {
      this.setState({
        result: 'HOMERUN!!!'
      });

      alert('Restart game');
      this._initState();
    } else {
      if (tries.length >= 4) {
        this.setState({
          result: 'You wrong in 5 times. answer is ' + answer.join('')
        });
        alert('Restart game');
        this._initState();
      } else {
        const valueArray = value.split('').map((v) => parseInt(v));
        let strike = 0;
        let ball = 0;

        for (let i=0; i<4; i++) {
          if (valueArray[i] === answer[i]) {
            strike ++;
          } else if (answer.includes(valueArray[i])) {
            ball ++;
          }
        } // 몇 strike, 몇 ball 인지 판단하는 로직

        this.setState((prevState) => {
          return {
            value: '',
            // state에 있는 value는 immutable 이여야 한다. react가 변화는 감지해서 렌더링 하는데 push를 써서 변하게 하면 변화를 감지 못한다.
            tries: [...prevState.tries, {try: value, result: strike + ' strike ' + ball + ' ball'}]
          }
        });
      }
      this.input.current.focus(); // Create ref 사용
    }
  }

  _onChange = (e) => {
    this.setState({
      value: e.target.value
    });
  }

  render() {
    const { result, value, tries } = this.state;
    return (
      <>
        <div>{this.state.answer}</div>
        <div>{result}</div>
        <form onSubmit={this._onSubmit}>
          <input ref={this.input} type='text' value={value} onChange={this._onChange}/>
          <button>Insert!</button>
        </form>
        <div>
          {/* map 함수, jsx 에서 반복문 사용시 key값을 꼭 넣어주어야 한다. */}
          {/* 단순히 index를 넣어주면 key를 기준으로 엘리먼트를 추가, 수정, 삭제 하는데 배열 순이 바뀌는 경우에 문제가 생길 수 있다. */}
          {tries.map((v, i) => {
            return (
              <Try key={v.try + v.result} tryInfo={v}></Try>
            )
          })}
        </div>
      </>
    );
  }
}

export default ClassApp;



 

대략적인 주석으로 코드 설명이 되어있는데요. 

오늘의 핵심인 component 분리와 props 부분만 잠깐 설명하자면, <Try></Try> 라는 component를 만들어서 거기에는 결과 값을 보여주도록 하였죠. 

그런데 결과 값은 props를 통해 전달을 했죠. 

이런식으로 react 에서는 여러개의 component를 사용하고 각각의 component에 props를 전달해서 사용하도록 되어 있습니다.

즉, 상위 component를 부모 component, 하위를 자식 component라고 가정 했을 때, 부모 -> 자식 에게 props 를 통해 정보를 전달해 주는 역할을 하게 되죠.

허나 부모의 부모의 부모가 자식에게 전달해 주면 props가 어디서 왔는지 정말 찾기가 힘들죠. 이점을 해결 하기 위해 여러가지 방법들이 있는데 Redux, Mobx, Context API 등이 있는데 이는 나중에 포스팅 해보도록 하겠습니다.

 

또 한가지 핵심은 state 에 있는 변수들은 불변함을 가지고 있어야 한다는 점입니다.

위 코드에서 tries: [...tries, {}] 같은 코드를 보실 수 있는데 기존에 tries 배열은 건드리지 않고 새로운 배열을 만들어 내는 것이죠.

주석에서도 써있듯이 기존에 배열을 건드리면 react가 감지를 못하고 rendering를 안해주기 때문에 기존에 값을 건들이지 않는 불변성을 가지고 있어야 하죠.

 

try.jsx

import React, { Component } from 'react';

class Try extends Component {
  render() {
    // 구조 분해 문법을 통해 props 를 받아 올 수 있다.
    const { tryInfo } = this.props;

    return (
      <>
        <div>
          {tryInfo.try}
        </div>
        <div>
          {tryInfo.result}
        </div>
      </>
    );
  }
}

export default Try;



 

사실 try.jsx 는 별다른건 없습니다. 부모로 부터 받은 정보를 띄워주는 역할을 할 뿐이죠.

단지 오늘은 앞서 계속 언급 했듯이 component 분리, (만약 component 분리를 하지 않았다면 위 return 되는 jsx 코드를 number-baseball.jsx 에 써줘야 겠죠? ) 분리를 통한 정보 전달 (props) 이 핵심이죠.

 

기존 세팅은 늘 해왔던 구구단, 끝말잇기와 똑같기 때문에 참조 하시면 될듯 합니다.

 

프로젝트 세팅

 

[React 구구단] React 프로젝트 셋팅

안녕하세요. 엄티 입니다. 이제부터 React를 사용해서 간단한 구구단 게임을 만들어 보겠습니다. 그전에 React를 사용하기 위한 간단한 프로젝트 세팅을 해보겠습니다. 참고로 제 포스팅 유명하신 '제로초'님 강좌..

eomtttttt-develop.tistory.com

webpack, hotloader 세팅

 

[React 끝말잇기] webpack-dev-server 및 react-hot-loader

안녕하세요. 엄티 입니다. 오늘은 리액트 프로젝트를 만들 시에 코드 수정과 함께 자동으로 결과창이 변할 수 있도록 세팅을 한번 해보겠습니다. 사실 앞선 포스팅에서는 계속해서 코드를 바꾸고, webpack 빌드를..

eomtttttt-develop.tistory.com

 

number-baseball foluder structure

Comments