상세 컨텐츠

본문 제목

[React.js] React Web Games - Multiplication Game

Knowledge/React.js

by winCow 2021. 7. 8. 17:32

본문

1. Multiplication Game

 

 

랜덤으로 두 개의 숫자를 전달하고, 사용자로부터 숫자를 입력받아 곱셈이 맞는지를 체크한 뒤 '정답' 혹은 '땡'을 출력하는 게임(...)이다.

 

 

2. React Code

<html>
  <head>
    <script
      src="https://unpkg.com/react@17/umd/react.development.js"
      crossorigin
    ></script>
    <script
      src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"
      crossorigin
    ></script>
    <script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
  </head>
  <body>
    <div id="root"></div>
    <script type="text/babel">
      class Gugudan extends React.Component {
        constructor(props) {
          super(props);
          this.state = {
            first: Math.ceil(Math.random() * 9),
            second: Math.ceil(Math.random() * 9),
            value: "",
            display: "",
            result: "",
          };
        }

        onSubmit = (e) => {
          e.preventDefault();
          parseInt(this.state.value) === this.state.first * this.state.second
            ? this.setState({
                result: "정답",
                first: Math.ceil(Math.random() * 9),
                second: Math.ceil(Math.random() * 9),
                display: this.state.value,
                value: "",
              })
            : this.setState({
                result: "땡",
                first: Math.ceil(Math.random() * 9),
                second: Math.ceil(Math.random() * 9),
                display: this.state.value,
                value: "",
              });
        };

        render() {
          return (
            <div>
              <div>
                {this.state.first} x {this.state.second} = ?
              </div>
              <form onSubmit={this.onSubmit}>
                <input
                  type="number"
                  value={this.state.value}
                  onChange={(e) => this.setState({ value: e.target.value })}
                />
                <button>입력</button>
              </form>
              <div>{this.state.display}</div>
              <div>{this.state.result}</div>
            </div>
          );
        }
      }
    </script>
    <script type="text/babel">
      ReactDOM.render(<Gugudan />, document.querySelector("#root"));
    </script>
  </body>
</html>

이를 constructor, render, onSubmit으로 나누어 보면 아래와 같다.

 

        constructor(props) {
          super(props);
          this.state = {
            first: Math.ceil(Math.random() * 9),
            second: Math.ceil(Math.random() * 9),
            value: "",
            display: "",
            result: "",
          };
        }

컴포넌트의 constructor에 this.state 객체를 생성하고, 바뀔 여지가 있는 것들을 state로 설정하여 각각의 최초의 값을 입력한다. first와 second는 각각 1~9 사이의 랜덤한 숫자를, value, display, result는 각각 빈 문자열을 주었다.

state = {
   first: Math.ceil(Math.random() * 9),
   second: Math.ceil(Math.random() * 9),
   value: "",
   result: "",
 };

이 때, constructor와 super는 생략할 수 있으므로, 위와 같이 state의 값만을 설정해 줘도 된다.

 

        render() {
          return (
            <div>
              <div>
                {this.state.first} x {this.state.second} = ?
              </div>
              <form onSubmit={this.onSubmit}>
                <input
                  ref={this.onRefInput}
                  type="number"
                  value={this.state.value}
                  onChange={(e) => this.setState({ value: e.target.value })}
                />
                <button>입력</button>
              </form>
              <div>{this.state.display}</div>
              <div>{this.state.result}</div>
            </div>
          );
        }

다음으로 render 함수를 정의하는데, JSX 문법에 따라, 자바스크립트 문법을 이용할 때는 중괄호{}를 사용한다. 먼저 문제를 출력하는 div 태그에서는 first x second = ?을 입력하는데, first, second는 계속 변할 것이므로 {this.state.first}, {this.state.second}를 사용한다.

다음으로, 사용자가 form 태그를 통해 값을 제출하면 해당 값이 맞는지를 확인할 것이다. form 태그에 onSubmit 속성을 적용하면 render 함수 이전에 정의한 onSubmit 함수를 실행하도록 하여, 해당 값이 맞는지를 확인한다. onSubmit 함수를 따로 정의하는 이유는, 가독성의 향상을 위하여, 가급적 render 함수 내에는 JavaScript 코드를 쓰지 않기 위해서이다. input 태그에는 type, value, onChange 속성을 적용하는데, type는 입력받는 값을 숫자로 제한하는 HTML 속성이고, value는 state에서 설정한 value(초기값 "")로 설정하였다. onChange는 전달된 이벤트(e)의 target.value(즉, 사용자가 입력한 값)를 value로 설정하도록 한다. 여기서 onChange를 사용하는 이유는, value의 값이 state이기 때문에, 사용자가 입력하는 값이라고 할지라도 setState를 통하지 않고는 변경할 수 없기 때문이다. onChange를 사용하지 않으면 사용자가 값을 입력할 수 없다.

마지막으로 display와 result를 div 태그 안에서 표시되로도록 했다.

 

        onSubmit = (e) => {
          e.preventDefault();
          parseInt(this.state.value) === this.state.first * this.state.second
            ? this.setState((prevState) => {
                return {
                  result: "정답: " + prevState.value,
                  first: Math.ceil(Math.random() * 9),
                  second: Math.ceil(Math.random() * 9),
                  value: "",
                };
              })
            : this.setState({
                result: "땡",
                first: Math.ceil(Math.random() * 9),
                second: Math.ceil(Math.random() * 9),
                value: "",
              });
          // (7.15) 3. 그리고 마지막으로 아래처럼 this.input.focus()를 하면 됨
          // (7.15) 원래 자바스크립트라면 document.querySelector('input').focus()를 해야 되는데 리액트에서는 이런 과정을 따른다.
          this.input.focus();
        };
        
        onRefInput = (c) => {
          this.input = c;
        };

마지막으로 onSubmit 함수는 위와 같이 정의한다. 이벤트(e)를 받으면 preventDefault로 새로고침을 방지하고, 입력된 값인 this.state.value가 first와 second의 곱과 같은지를 체크한다. 맞는다면 result를 정답으로, 틀린다면 땡으로 표시를 할 것이고, fisrt, second를 다시 랜덤 숫자로, display는 value로, 입력 창은 공백으로 변경해 준다. 이 때, prevState를 작성했는데, 사실 여기서는 필요가 없지만, prevState는 setState가 반복적으로 사용될 때에는 필요하다. prevState는 setState에 의해 값이 변경되기 이전의 값을 의미한다. 이를테면, 숫자를 세는 함수에서, prevState를 사용하지 않고 setState를 세 번 사용하더라도, 마지막으로 사용한 setState만이 적용되어 버릴 수 있다. 이를 방지하기 위해 prevState를 이용하면 정상적으로 모든 setState가 적용된 값을 얻을 수 있을 것이다.

함수를 정의할 때는 가급적 render 함수 바깥에서 정의하는 것이 좋은데, 그 이유는, setState가 실행될 때마다 render 함수가 다시 호출되는데, 함수가 render 내부에서 정의되어 있으면 그 때마다 새롭게 정의되어 버리기 때문이다.

 

 

3. Ref

react에서 dom 요소에 직접 접근하기 위해서는 ref를 사용해야 한다. 이를 위해서는 다음과 같은 과정을 따라야 한다.

onRefInput = (c) => {
  this.input = c;
};

먼저, 위와 같이 render 함수 외부에 onRefInput 함수를 선언해 준다.

 

<input
	ref={this.onRefInput}
	type="number"
	value={this.state.value}
	onChange={(e) => this.setState({ value: e.target.value })}
/>

다음으로, render 함수의 return 부분에서, 원하는 요소에 ref={this.onRefInput}를 입력한다.

 

onSubmit = (e) => {
	this.input.focus();
};

onSubmit 함수에 this.input.focus()를 실행하면 입력 버튼을 클릭해도 여전히 포커싱이 input 박스 안에 맞춰진다. 자바스크립트에서 document.querySelector('input').focus()를 실행한 것과 같은 결과이다.

 

 

관련글 더보기

댓글 영역