웹 게임을 만들며 배우는 React #1 구구단 (babel + class->hooks)

김지원·2020년 10월 22일
0

React

목록 보기
5/31

리액트를 왜 쓰는가?

React: 사용자 인터페이스를 만들기 위한 JavaScript 라이브러리

  • 사용자 경험이 좋아진다. 웹에서 앱과 같은 사용자 경험을 만들어 준다.
    (사용자 인터페이스 쉽게 만들기 위해 (single page application) )

  • 데이터 화면 일치 (데이터 처리를 쉽게 하기 위해 )

  • 재활용 가능한 웹 컴포넌트 (중복 피하기. 유지 보수가 쉬워짐)

상태 : 바뀌는 부분, 바뀔 수 있는 부분

1. 원시적인 React 코드 (like버튼 누르면 liked로 변경)

<!DOCTYPE html>
<html lang="en">
<head>
    <script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
    <script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
</head>
<body>
    <div id="root"></div><!--결과: <div id="root"><button>Like</button></div>-->
    <script>
        const e = React.createElement; //태그 만드는거

        class LikeButton extends React.Component{
            constructor(props) {
                super(props);
                this.state = {
                    liked:false,
                };
            }
            render(){
                return e('button', { onClick: () => { this.setState({liked:true})} , type: 'submit'},
                    this.state.liked === true ? 'Liked' : 'Like'//버튼을 클릭하면 liked로 바뀜!
                    //상태가 바뀌면 화면이 저절로 바뀜
                );
                //<button type="submit">Like</button>
                //버튼 태그를 만들겠다.
            }
        }
    </script>
    <script>
        ReactDOM.render(e(LikeButton), document.querySelector('#root'));
        //루트태그에다가 like 버튼을 그린다
    </script>
</body>
</html>

2. Babel을 사용한 JSX 코드

자바스크립트 안에서 html 태그 문법을 쓰기위해 babel을 script로 넣어주고, type을 babel로 설정
=>JSX 라고 부름

Babel이 JSX 문법을 위에 했던 e (=createElement)로 바꿔줌

<!DOCTYPE html>
<html lang="en">
<head>
    <script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
    <script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
    <script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
</head>
<body>
    <div id="root"></div><!--결과: <div id="root"><button>Like</button></div>-->
    <script type="text/babel">
        class LikeButton extends React.Component{
            constructor(props) {
                super(props);
                this.state = {
                    liked:false,
                };
            }
            render() {
                return <button type="submit" onClick={() => {this.setState({liked: true})}}>
                    {this.state.liked ? 'Liked' : 'Like'}
                </button>;
                //JSX( JS+XML )
            }
        }
    </script>
    <script type="text/babel">
        ReactDOM.render(<LikeButton />, document.querySelector('#root'));
        //루트태그에다가 like 버튼을 그린다
    </script>
</body>
</html>

3.구구단 게임 코드(JSX랑 JavaScript랑 섞어쓴거)

<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: '',
               result: '',
           };

       }
       render(){
           return(
               <div>
                   <div>{this.state.first}곱하기{this.state.second}?</div>
                   <form onSubmit={(e) => {
                       e.preventDefault();
                       if(parseInt(value) === this.state.first * this.state.second) {
                           this.setState({
                               result: '정답',
                               first: Math.ceil(Math.random() * 9),
                               second: Math.ceil(Math.random() * 9),
                               value: '',
                           });
                       }else{
                           this.setState({
                               result:'땡',
                               value:'',
                           });
                       }
                   }}>
                       <input type="number" value={this.state.value} onChange={(e) => this.setState({ value: e.target.value})}/>
                        //form 안에 숫자를 입력하기 위해 onChange (상태를 바꾸려면 setState)
                       <button>입력</button>
                   </form>
                   <div>{this.state.result}</div>
               </div>
           );


       }

   }
</script>
<script type="text/babel">
    ReactDOM.render(<GuGuDan />, document.querySelector('#root'));

</script>
</body>

4.구구단 게임(JavaScript를 클래스의 메소드로)
function을 밖으로 뺄때는 무조건 화살표 함수로 표기해야함.
function을 붙여서 사용하면 this가 달라지게 되는 문제 발생.

<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: '',
               result: '',
           };
       }
       onSubmit = (e) => {
           e.preventDefault();
           if (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),
                   value: '',
               });
           } else {
               this.setState({
                   result: '땡',
                   value: '',
               });
           }
       };

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

       render(){
           return(
               <div>
                   <div>{this.state.first}곱하기{this.state.second}?</div>
                   <form onSubmit={this.onSubmit}>
                       <input type="number" value={this.state.value} onChange={this.onChange}/>
                       {/*form 안에 숫자를 입력하기 위해 onChange (상태를 바꾸려면 setState)*/}
                       <button>입력</button>
                   </form>
                   <div>{this.state.result}</div>
               </div>
           );


       }

   }
</script>
<script type="text/babel">
    ReactDOM.render(<GuGuDan />, document.querySelector('#root'));

</script>
</body>

5-1. 쓸데없는 <div>태그로 감싸야할까?

render(){
           return(
               <div>
                   <div>{this.state.first}곱하기{this.state.second}?</div>
                   <form onSubmit={this.onSubmit}>
                       <input type="number" value={this.state.value} onChange={this.onChange}/>
                       {/*form 안에 숫자를 입력하기 위해 onChange (상태를 바꾸려면 setState)*/}
                       <button>입력</button>
                   </form>
                   <div>{this.state.result}</div>
               </div>
           );
       }

5-2. 빈 태그로 <> <div>태그 없애기 가능해짐
babel tool을 설치해야지만 지원해서 React.Fragment로 대체

render(){
          return(
              <React.Fragment>
                  <div>{this.state.first}곱하기{this.state.second}?</div>
                  <form onSubmit={this.onSubmit}>
                      <input type="number" value={this.state.value} onChange={this.onChange}/>
                      {/*form 안에 숫자를 입력하기 위해 onChange (상태를 바꾸려면 setState)*/}
                      <button>입력</button>
                  </form>
                  <div>{this.state.result}</div>
              </React.Fragment>
          );
      }

6. constructor 없애는 법

class GuGuDan extends React.Component {
          state = {
              first: Math.ceil(Math.random()*9),
              second: Math.ceil(Math.random()*9),
              value: '',
              result: '',
          };
      }

7. 예전 상태 값을 좀 더 구분해서 다음 상태 값에 사용할 수 있다.

수정 전)

onSubmit = (e) => {
           e.preventDefault();
           if (parseInt(this.state.value) === this.state.first * this.state.second) {
               this.setState({
                   result: this.state.value+'은 정답',
                   first: Math.ceil(Math.random() * 9),
                   second: Math.ceil(Math.random() * 9),
                   value: '',
               });
           } else {
               this.setState({
                   result: '땡',
                   value: '',
               });
           }
       };

수정 후)

 onSubmit = (e) => {
          e.preventDefault();
          if (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: '',
                  };
              });
          } else {
              this.setState({
                  result: '땡',
                  value: '',
              });
          }
      };

8.함수형 setState
setState안에 this.state가 들어가면 return을 해주는 함수를 쓰는 걸로!

카운터 예를 생각해보면

this.setState({
   value: this.state.value +1,
   });
this.setState({
   value: this.state.value +1,
   });
this.setState({
   value: this.state.value +1,
   });

setState는 비동기이기 때문에 전의 값보다 3이 증가하는게 아니라 1이 증가 될 수 도 있음.
따라서 return 해주는 함수를 쓰는 걸로

this.setState((prevState) => {
  return {
    value: prevState.value +1
    };
  });
  this.setState((prevState) => {
  return {
    value: prevState.value +1
    };
  });
  this.setState((prevState) => {
  return {
    value: prevState.value +1
    };
  });

9.input 창에 focus를 주고싶다!
ref 하고 함수를 넣어준다

class 안에 input 선언해준다
<input ref={(c)=>{this.input = c}}

this.input에다가 input 태그를 실제로 넣어줬기 때문에 document.querySelector('input').focus() 하는 것과 동일

정답과 땡 출력후 input에 focus

onSubmit = (e) => {
          e.preventDefault();
          if (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.input.focus();
          } else {
              this.setState({
                  result: '땡',
                  value: '',
              });
              this.input.focus();
          }
      };

dom에 직접 접근하고 싶을때는 ref라는 것을 붙여서 접근하면 된다.

setState를 할때는 render함수가 다시 실행된다.

최종 코드

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8" />
    <title>구구단</title>
    <script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
    <script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></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: '',
               result: '',
           };
       }
       onSubmit = (e) => {
           e.preventDefault();
           if (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.input.focus();
           } else {
               this.setState({
                   result: '땡',
                   value: '',
               });
               this.input.focus();
           }
       };

       onChange=(e) => {
           this.setState({ value: e.target.value})
       };
    input;
    onRefInput = (c) => {this.input = c};

       render(){
           return(
               <React.Fragment>
                   <div>{this.state.first}곱하기{this.state.second}?</div>
                   <form onSubmit={this.onSubmit}>
                       <input ref={this.onRefInput} type="number" value={this.state.value} onChange={this.onChange}/>
                       {/*form 안에 숫자를 입력하기 위해 onChange (상태를 바꾸려면 setState)*/}
                       <button>입력</button>
                   </form>
                   <div>{this.state.result}</div>
               </React.Fragment>
           );
       }

   }
</script>
<script type="text/babel">
    ReactDOM.render(<GuGuDan />, document.querySelector('#root'));

</script>
</body>
</html>

1.React Hooks 사용하기

class 형
class GuGuDan extends React.component{ ... }
함수 컴포넌트
setState랑 ref를 안쓸 때 사용했었음
const GuGuDan = () => { return <div> HELLO HOOKS </div>; }

함수 컴포넌트에서도 state랑 ref를 사용하게 해주세요~ => react hooks

처음 짠 구구단 클래스 코드 state

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

hooks 방식으로 변경

const GuGuDan = () => {
	const [first,setFirst] = React.useState(Math.ceil(Math.random()*9));
    const [second,setSecond] = React.useState(Math.ceil(Math.random()*9));
    const [value, setValue] = React.useState('');
    const [result, setResult] = React.useState('');
    const inputRef = React.useRef(null);//ref 쓰는 법도 변경
  
  const onChangeInput = (e) => {
    setValue(e.target.value);
  };
  
  const onSubmitForm = (e) => {
     e.preventDefault();
           if (parseInt(value) === first * second) {
             setResult('정답' + value);
             setFirst(Math.ceil(Math.random() * 9));
             setSecond(Math.ceil(Math.random() * 9));
             setValue('');
            inputRef.current.focus();
           } else {
              setResult('땡');
              setValue('');
              inputRef.current.focus();
           }
       };
  
  
  return (
  <React.Fragment>
    <div> {first} 곱하기 {second}? </div>
    <form onSubmit={onSubmitForm}>
      <input ref={inputRef} onChange={onChangInput} value={value}/>
      <button>입력!</button>
    </form>
    <div id="result">{result}</div>
    </React.Fragment>
    );
 }
  • hooks에서 ref는 useRef를 사용해서 dom에 접근 current를 붙여줘야하는게 달라짐
  • state가 바뀌면 구구단 함수 자체가 통제로 다시 실행되기 때문에 조금더 느릴 수 있다.
  • html 속성 class->className for->htmlFor

예전 값 사용해서 비동기 문제 방지하기

setCounter((c) => c+1)

0개의 댓글

관련 채용 정보