[웹 게임을 만들며 배우는 React] ① 구구단

chez_bono·2020년 1월 19일
0
post-thumbnail

🔗 웹 게임을 만들며 배우는 React

1. 리액트를 왜 쓰는가

  • 사용자 경험 (UX) 개선
  • 데이터 - 화면 일치
  • 재사용 컴포넌트

2. 첫 리액트 컴포넌트

  • react, react-dom 설치 필요 (🔗 링크)
    • 배포할 때는 development.js를 production.min.js로 대체 필요
<script src="https://unpkg.com/react@16/umd/react.development.js" crossorigin></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js" crossorigin></script>
  • 완성된 웹 페이지
<html>
    <head>
        <!-- 주의: 사이트를 배포할 때는 "development.js""production.min.js"로 대체하세요. -->
        <script src="https://unpkg.com/react@16/umd/react.development.js" crossorigin></script>
        <script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js" crossorigin></script>
    </head>
    <body>
        <div id="root"></div>
        <script>
            // react.js
            const e = React.createElement;

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

                render() {
                    return e('button', null, 'Like');   // <button>Like</button> 생성하겠다는 계획
                }
            }
        </script>
        <script>
            // react-dom.js
            ReactDOM.render(e(LikeButton), document.querySelector('#root'));    // root div에 Like 버튼 DOM 생성
        </script>
    </body>
</html>

3. HTML 속성과 상태(state)

  • 버튼에 html 속성 추가
    • 속성은 camel case로 작성
      ex) onclick => onClick
return e('button', {onClick: () => {console.log('clicked')}, type: 'submit'}, 'Like');
  • 상태(state): 바뀔 여지가 있는 부분
  • 상태값 활용
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' : 'Liked',
      );   // 버튼 클릭하면 liked state를 true로 변경 / liked가 true면 버튼 텍스트를 Liked로 변경
    }
    }
  }

❗ 크롬 react 확장 프로그램을 설치하고 Components에서 컴포넌트 단위로 state 등 확인 가능

4. JSX와 바벨(babel)

  • 코드 간결화
    • js에 html을 사용하기 위해서 babel 설치 (🔗 링크)
      👉 JSX (JS + XML)
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
<script type="text/babel">...</script>
<div id="root"></div>
<script type="text/babel">
  const e = React.createElement;

  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 === true ? 'Liked' : 'Like'}
      </button>;
    }
  }
</script>
<script type="text/babel">
  ReactDOM.render(<LikeButton />, document.querySelector('#root'));    // root div에 Like 버튼 DOM 생성
</script>

👉 똑같은 버튼을 여러 개 만들고 싶으면 만 추가하면 되고, 수정은 한 번만 하면 되므로 재사용이 쉽다.

5. 첫 번째 Q&A

  • 바벨(babel): 최신 js 문법을 모든 브라우저에서 사용할 수 있도록 변환해주는 자바스크립트 컴파일러
  • 어떤 동작을 했을 때 변하는 부분을 state로 등록!

6. 구구단 리액트로 만들기

  • 기본 화면 작성
    • xml은 문법에 엄격하므로 싱글 태그도 꼭 닫아줘야 함!
      ex) <input />
    • 중괄호({}) 안에서 state값 사용
<div id="root"></div>
<script type="text/babel">
  class GuGuDan extends React.Component {
    constructor(props) {
      super(props);
      this.state = {	// 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>
            <input type="number" value={this.state.value} />
            <button>입력!</button>
          </form>
          <div>{this.state.result}</div>
        </div>
      );
    }
  }
</script>
<script type="text/babel">
  ReactDOM.render(<GuGuDan />, document.querySelector('#root'));   
</script>
  • input에 입력하여 value가 변하는 것도 상태가 바뀌는 것이므로 state 설정 필요
<input ... onChange={(e) => this.setState({value: e.target.value})} />

7. 클래스 메서드

  • 이벤트에서 동작하는 부분을 클래스에 정의하여 사용
  • 직접 함수를 작성하는 경우 function(){}을 사용할 수 없고 화살표 함수를 사용해야 함!
class GuGuDan extends React.Component {
  constructor(props) {
    ...
  }
    
    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: '',
        });
      }
    };

    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} /> <!-- 클래스 메서드 사용 -->
          <button type="submit">입력!</button>
        </form>
        <div>{this.state.result}</div>
      </div>
    );
  }
}
  • 컴포넌트를 여러 개 생성해도 각각 다른 state를 가지기 때문에 재사용성이 매우 높음
ReactDOM.render(<div><GuGuDan /><GuGuDan /><GuGuDan /></div>, document.querySelector('#root'));  

8. Fragment와 기타 팁들

  • react의 모든 컴포넌트는 <div> 태그로 감싸야 하는 규칙이 있음
    - css 적용 등에서 번거롭기 때문에 `<React.Fragment>`로 대체하여 제거할 수 있음
    ```jsx
    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} />
          <button type="submit">입력!</button>
        </form>
        <div>{this.state.result}</div>
      </React.Fragment>
    );
    }
- 실무에서는 `constructor`를 사용하지 않는 경우가 많음
	- 바로 `state`를 선언하여 사용
    ```jsx
state = {
  first: Math.ceil(Math.random() * 9),
  second: Math.ceil(Math.random() * 9),
  value: '',
  result: '',
};

9. 함수형 setState

  • 이전 문제의 답 출력
if(parseInt(this.state.value) === this.state.first * this.state.second) {
  this.setState((prevState) => {	// 함수형 setState에서 이전 state를 활용 가능
    return {
      result: '정답 ' + prevState.value,
      first: Math.ceil(Math.random() * 9),
      second: Math.ceil(Math.random() * 9),
      value: '',
    }
  });
}

❗ react는 비동기 방식이므로 이전 state를 활용하려면 안전하게 함수형 setState를 사용하도록 한다.

10. ref

  • 입력 후 input에 포커스 주고 싶을 때
onSubmit = (e) => {
  e.preventDefault();
  if(parseInt(this.state.value) === this.state.first * this.state.second) {
   ...
    this.input.focus();	// input에 포커스
  }else{... }
};

input;	// input 선언

render() {
  return (   
        <input ref={(c) => {this.input = c;}} ... />	// input이 이 태그를 참조하도록 함
  );
}

setState가 실행될 때마다 render()가 실행된다. 페이지가 무거워지면 과부하가 생길 수 있으므로 유의하도록 한다.

  • render()가 실행될 때마다 함수를 생성하는 일이 없도록 메서드를 따로 선언하는 것이 좋음
onRefInput = (c) => {this.input = c;};
            
render() {
  console.log('렌더링');
  return ( 
    ...
        <input ref={this.onRefInput} ... />
    ...
  );
}
profile
목표는 행복한 베짱이

0개의 댓글