React - 구구단 (1)

박종휘·2022년 12월 16일
0

React

목록 보기
4/5
post-thumbnail

1. 리액트를 사용하는 이유

  • 복잡한 웹앱에서 데이터와 화면 일치 문제 같은 것을 쉽게 풀어준다.
  • 요즘 트렌드

단점 : 검색 엔진 노출에 어려움이 있다.

2. 리액트

  • 리액트는 JavaScript다.
  • 데이터 중심으로 움직인다.
  • 함수형과 클래스형으로 나뉜다.
    • 현재 클래스형은 거의 사용하지 않으나, ErrorBoundary에서는 사용한다. (사용빈도 : 1%)
class LikeButton extends React.Component {

}
=> 클래스형

const LikeButton = () => {};
function LikeButton() {}
=> 함수형

like-button.html


<html>
<head>
    <meta charset="utf-8">
    <title>웹게임</title>
</head>
<body>
<div id="root"></div>
<!-- 남의 코드 불러올 때 사용 -->
<!-- 개발용 -->
<script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script> 
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<!-- 배포용 -->
<!--<script src="https://unpkg.com/react@18/umd/react.production.min.js" crossorigin></script>-->
<!--<script src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js" crossorigin></script>-->
<script>
    'use strict';

    const e = React.createElement;
    
    // 원시적인 형태
    // 리액트 = js
    // 리액트는 데이터 중심으로 움직인다.
    class LikeButton extends React.Component {
        constructor(props) {
            super(props);
            this.state = {liked: false}; // 데이터
        }

        render() {
            if(this.state.liked) {
                return 'You liked this.';
            }

            return e('button', {onClick: () => this.setState({liked: true})}, 'Like'); // 화면
        } // => 데이터가 바뀌면 화면이 바뀐다. : 화면에서 바뀔 부분을 state로 만든다.
    } // class는 거의 사용하지 않음 : ErrorBoundary에서 사용 (1%)
    // => 컴포넌트 : 데이터와 화면을 하나로 묶어둔 덩어리

    //const LikeButton = () => {};
    //function LikeButton() {}
</script>
<script>
    ReactDOM.createRoot(document.querySelector('#root')).render(e(LikeButton));
    // LikeButton 이라는 컴포넌트를 <div id="root"></div> 여기 안에다가 컴포넌트를 그린다.
</script>
</body>
</html> 
  • 컴포넌트 : 데이터와 화면을 하나로 묶어둔 덩어리

3. JSX (XML)

like-button-jsx.html


<html>
<head>
</head>
<body>
<div id="root"></div>
<script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script> <!-- babel을 사용하기 위한 script -->
<!--<script src="https://unpkg.com/react@18/umd/react.production.min.js" crossorigin></script>-->
<!--<script src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js" crossorigin></script>-->
<script type="text/babel">
  'use strict';

  class LikeButton extends React.Component {
    constructor(props) {
      super(props);
      this.state = {liked: false};
    }

    render() {
      if (this.state.liked) {
        return 'You liked this.';
      }

      return (
        <button onClick={() => this.setState({liked: true})}>
          Like
        </button>
      ); // 실무에서 주로 쓰임
    } // => babel을 추가하지 않으면 작동하지 않음
  }
</script>
<script type="text/babel">
    // ReactDOM.render(<LikeButton />, document.querySelector('#root')); // React 17버전 코드
    ReactDOM.createRoot(document.querySelector('#root')).render(<LikeButton/>); // React 18버전 코드
</script>
</body>
</html>
  • babel을 사용하면
return (
	<button onClick={() => this.setState({liked: true})}>
		Like
    </button>
);

이 것을

return e('button', {onClick: () => this.setState({liked: true})}, 'Like');

이 것으로 바꾸어준다.


4. 클래스 컴포넌트의 형태와 리액트 데브툴즈

  • 객체를 함부로 바꾸지 말 것 (불변성) -> 복사하기
    : 기존 객체는 놔두고 객체를 복사해서 새로운 객체를 만들어서 바꾸기
예시

const array = []; // 배열도 객체
pop, push, shift, unshift, splice -> 배열을 직접적으로 수정 // React에서는 사용하지 않음
concat, slice -> 새로운 배열을 만들어냄
return (
	<button onClick={() => {
      this.setState({liked: true});
    }}>
		Like
    </button>
);
  • 클릭을 하면 데이터가 true로 바뀌고 true로 바뀌는 즉시 화면이 바뀐다. -> 데이터를 바꾸면 화면을 바꾸는 것이다.

5. 함수 컴포넌트 (함수형 X)

  • 코드가 간략하다.
  • this를 쓸 일이 없다.
// 함수 컴포넌트는 this를 쓸 일이 없다.
  // const LikedButton = () => {} // 화살표 함수
  function LikeButton() { // 함수형 컴포넌트 X 함수 컴포넌트 O
        const [liked, setLiked] = React.useState(false); // 구조분해
        // liked : 데이터, setLiked : 데이터를 바꾸는 함수
        // -> 뭔 짓을 하든, 결국 return한게 화면이다.
        /*
            js 옛날 문법
            const result = React.useState(false);
            const liked = result[0];
            const setLiked = result[1];
        */

        /*
        // 예시
        const[news, setNews] = React.useState(['뉴스1', '뉴스2', '뉴스3', '뉴스4', '뉴스5'])
        const [category, setCategory] = React.useState(['스포츠', '연예', '경제', '시사', '정치'])

        if(category === '뉴스') {
            return '뉴스화면'
        } // 데이터들을 사용해서 화면 리턴
        */

        if(liked) {
            return 'You liked this.';
        }
        return (
            <button onClick={() => {setLiked(true);}}>Like</button>
        );

    }

6. 구구단

index.html


<html>
    <head>
        <meta charset="UTF-8" />
        <title>구구단</title>
        <script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script>
        <script crossorigin src="https://unpkg.com/react-dom@18/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 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>
                            {/* 중간에 js를 삽입하고 싶으면 {}를 사용한다. */}
                            <form>
                                <input type="number" value={this.state.value} onChange={(e) => this.setState({value: e.target.value})} />
                                {/* input.onChange = (e) => {console.log(e.target.value)} : 글자 치는 것이 콘솔에 나오게 함 ↑ */}
                                <button>입력!</button>
                            </form>
                            <div>{this.state.result}</div>
                        </div>
                    );
                }
            }
        </script>
        <script type="text/babel">
            ReactDOM.createRoot(document.querySelector('#root')).render(<GuGuDan/>);
        </script>
    </body>
</html>

6-1. 클래스 메서드

index.html
구구단 컴포넌트


<html>
    <head>
        <meta charset="UTF-8" />
        <title>구구단</title>
        <script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script>
        <script crossorigin src="https://unpkg.com/react-dom@18/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 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) => { // submit할 때 실행
                    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>
                            {/* 중간에 js를 삽입하고 싶으면 {}를 사용한다. */}
                            <form onSubmit={this.onSubmit}>
                                <input type="number" value={this.state.value} onChange={this.onChange} />
                                {/* input.onChange = (e) => {console.log(e.target.value)} : 글자 치는 것이 콘솔에 나오게 함 ↑ */}
                                <button>입력!</button>
                                {/* 기본 이벤트 핸들러 : onClick, onChange, onSubmit, onLoad, onInput, onFocus, onBlur */}
                            </form>
                            <div>{this.state.result}</div>
                        </div>
                    );
                }
            }
        </script>
        <script type="text/babel">
            ReactDOM.createRoot(document.querySelector('#root')).render(<div><GuGuDan/><GuGuDan/></div>);
        </script>
    </body>
</html>

6-2. Fragment와 기타 팁들

  • 의미없는 div 태그
<div>
  <div>{this.state.first} 곱하기 {this.state.second}는?</div>
  {/* 중간에 js를 삽입하고 싶으면 {}를 사용한다. */}
  <form onSubmit={this.onSubmit}>
    <input type="number" value={this.state.value} onChange={this.onChange} />
    {/* input.onChange = (e) => {console.log(e.target.value)} : 글자 치는 것이 콘솔에 나오게 함 ↑ */}
    <button>입력!</button>
    {/* 기본 이벤트 핸들러 : onClick, onChange, onSubmit, onLoad, onInput, onFocus, onBlur */}
  </form>
  <div>{this.state.result}</div>
</div>

개선 사항 ↓
  • 빈 태그로 묶어주기
    : 현재 babel에서는 지원을 하지 않는다
    (babel2에서 지원)
<>
  <div>{this.state.first} 곱하기 {this.state.second}는?</div>
  <form onSubmit={this.onSubmit}>
    <input type="number" value={this.state.value} onChange={this.onChange} />
    <button>입력!</button>
  </form>
  <div>{this.state.result}</div>
</>

  • 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>입력!</button>
  </form>
  <div>{this.state.result}</div>
</>

  • form 태그 유무
form 태그가 있을 때는 onSubmit 사용
<form onSubmit={this.onSubmit}>
  <input type="number" value={this.state.value} onChange={this.onChange} />
  <button type="submit">입력!</button>
</form>
--------------------------------------------------------
form이 없으면 onClick 사용
<input type="number" value={this.state.value} onChange={this.onChange} />
<button onClick={this.onSubmit}>입력!</button>

  • 직접 만든 함수는 무조건 화살표 함수로
    -> function으로 바꾸게 되면 작동을 하지 않는다.
    : this의 의미가 달라진다.
onSubmit = (e) => { // submit할 때 실행
	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})
};

  • constructor 생략 가능
    : 실무에서 많이 쓰임

// constructor 포함
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: '',
    };
  }
}

----------------------------------------

// constructor 미포함
class GuGuDan extends React.Component {
    state = { // 변할 것들
      first: Math.ceil(Math.random() * 9),
      second: Math.ceil(Math.random() * 9),
      value: '',
      result: '',
    };
}

6-3. 함수형 setState

  • 정답 표시하기 (this.state.value)

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: '',
        });
        this.input.focus();
      } else {
        this.setState({
          result: '땡',
          value: '',
        });
        this.input.focus();
      }
    };
  • 예전 상태 값을 다음 상태 값에 활용할 수 있다.

onSubmit = (e) => {
      e.preventDefault();
      if (parseInt(this.state.value) === this.state.first * this.state.second) {
        this.setState((prevState) => {
          return {
            result: '정답: ' + prevState.value, // 정답 값 표시하기 (현재 값) => 현재 state
            first: Math.ceil(Math.random() * 9),
            second: Math.ceil(Math.random() * 9),
            value: '',
            // => 미래 state
          };
        });
        this.input.focus();
      } else {
        this.setState({
          result: '땡',
          value: '',
        });
        this.input.focus();
      }
    };
  • preveState 사용
  • 예전 state 값으로 새로운 state 값을 만들 때 return을 해주는 함수 사용 (setState에 this.state가 들어갈 때 사용)


6-4. ref

<html>
<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> <!-- 결과: <div id="root"><button>Like</button></div> -->
<script type="text/babel">
  class GuGuDan extends React.Component {
    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, // 정답 값 표시하기 (현재 값) => 현재 state
            first: Math.ceil(Math.random() * 9),
            second: Math.ceil(Math.random() * 9),
            value: '',
            // => 미래 state
          };
        });
        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() {
        console.log('렌더링'); // setState를 할 때마다 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}/>
            <button>입력!</button>
          </form>
          <div>{this.state.result}</div>
        </React.Fragment>
      );
    }
  }

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




📚 Referecne

  • 학습 자료 및 그림 출처

    Inflearn - 웹 게임을 만들며 배우는 React (조현영 (제로초))

profile
개린이의 개발 고수되기 작전!

0개의 댓글