② 끝말잇기

hhkim·2020년 1월 24일
0
post-thumbnail

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

1. React Hooks 사용하기

  • 리액트는 클래스를 사용하지 않도록 권고
    👉 Hooks 사용 (함수형)
  • state를 하나씩 분리하고 함수와 초기값 선언
    - setState 대신 함수를 사용하여 state 변경
  • 구조 분해 할당(Destructuring): 변수에 배열이나 객체를 선언하는 것
<div id="root"></div>
<script type="text/babel">
  const GuGuDan = () => {
    // state
    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('');
    // ref
    const inputRef = React.useRef(null);

    const onChangeInput = (e) => {
      setValue(e.target.value);	// setValue로 value state 변경
    };

    const onSubmitForm = (e) => {
      e.preventDefault();
      if(parseInt(this.state.value) === this.state.first * this.state.second) {
        setResult('정답 ' + value);
        setFirst(Math.ceil(Math.random() * 9));
        setSecond(Math.ceil(Math.random() * 9));
        setValue('');
        inputRef.current.focus();	// ref 사용 방식도 변화
      }else{
        setResult('땡');
        setValue('');
        inputRef.current.focus();
      }
    };
    return (
      <React.Fragment>
        <div>{first} 곱하기 {second}()?</div>
        <form onSubmit={onSubmitForm}>
          <input ref={inputRef} onChange={onChangeInput} value={value}/>    
          <button>입력!</button>
          <div id="result">{result}</div>
        </form>
      </React.Fragment>
    );
  }
</script>
<script type="text/babel">
  ReactDOM.render(<GuGuDan />, document.querySelector('#root'));   
</script>

2. Class와 Hooks 비교하기

  • Hooks: state가 변경되면 render 함수가 다시 실행되어 속도가 저하될 수 있음
  • 리액트에서 html 문법과 혼동되어 새롭게 정의된 이름
    • class => className
    • for => htmlFor

3. 웹팩 설치하기

  • 컴포넌트가 늘어나면 중복이 발생되는 문제를 해결하기 위해 웹팩 등장
    👉 여러 개의 js 파일을 합쳐서 하나로 만들어 줌
  • create_react_app: 웹팩 설치 자동화

1) 설치

  • npm init: node 프로그램 시작, package.json 파일 생성
  • npm i react react-dom: react와 react_dom 패키지 설치
  • npm i -D webpack webpack-cli: webpack, webpack-cli 패키지를 개발 모드로 설치
    👉 -D: 개발모드, package.json의 devDependencies에 설정 추가

2) 사용

  • index.html
<html>
    <head>
        <title>끝말잇기</title>>
    </head>
    <body>
        <div id="root"></div>
        <script src="./dist/app.js"></script>
    </body>
</html>
  • 모든 파일을 app.js로 합쳐야 함
    👉 webpack.config.js에서 설정

  • client.jsx

// npm에 설치한 프로그램 불러오기
const React = require('react');
const ReactDom = require('react-dom');

4. 모듈 시스템과 웹팩 설정

  • 원하는 모듈만 불러올 수 있으므로 효율적
  • WordRelay.jsx
// 필요한 프로그램 가져오기
const React = require('react');
const { Componet } = React;

class WordRelay extends Componet {
    state = {

    };

    render() {

    }
}
// 모듈 내보내기
module.exports = WordRelay;
  • client.jsx
// npm에 설치한 프로그램 불러오기
const React = require('react');
const ReactDom = require('react-dom');
// WordRelay 모듈 불러오기
const WordRelay = require('./WordRelay');

ReactDom.render(<WordRelay />, document.querySelector('#root'));
  • webpack.config.js
const path = require('path');

module.exports = {
    name: 'wordplay-setting',
    mode: 'development',    // 실서비스: production
    devtool: 'eval',    // 빠르게
    resolve: {
        extensions: ['.js', 'jsx'], // 확장자 자동으로 설정
    },

    entry: {
        app: ['./client'],
    },  // 입력
    output: {
        path: path.join(__dirname, 'dist'), // 현재 폴더에 dist 폴더 생성
        filename: 'app.js'
    },  // 출력
};

5. 웹팩으로 빌드하기


'webpack'은(는) 내부 또는 외부 명령, 실행할 수 있는 프로그램, 또는 배치 파일이 아닙니다.

  • 명령어로 등록을 하거나 package.json 파일에 설정
"scripts": {
    "dev": "webpack"
  },

1) babel 설치

  • npm i -D @babel/core @babel/preset-env @babel/preset-react babel-loader
  • @babel/core: 바벨 핵심
  • @babel/preset-env: 브라우저에 따른 지원
  • @babel/preset-react: 리액트 지원 (jsx)
  • babel-loader: babel과 webpack 연결

2) module 설정

webpack.config.js

entry: {
        app: './client',    // client 파일을 읽어서 app.js 생성
    },  // 입력

    module: {
        rules: [{
            test: /\.jsx?/, // jsx 파일에 babel적용
            loader: 'babel-loader',
            options: {
                presets: ['@babel/preset-env', '@babel/preset-react'],
                plugins: ['@babel/plugin-proposal-class-properties'],
            },
        }],
    },

3) app.js 생성

  • npm run dev 또는 npx webpack: webpack 명령 실행


Error: Package exports for '...\lecture\node_modules\@babel\helper-compilation-targets' do not define a '.' subpath

  • 에러 해결 안 돼서 시간 다 버렸다...
    👉 Windows10 node 최신버전으로 재설치!! 업데이트 지원을 제대로 안 하기 때문에 node.js 웹사이트에서 수동으로 프로그램을 설치해주어야 함.

6. 구구단 웹팩으로 빌드하기

  • 5강 활용해서 구구단 웹팩으로 만들기

7. @babel/preset-env와 plugins

1) @babel/preset-env

  • preset: plugin의 모음
  • 모든 브라우저를 지원하려면 작업이 많아져서 느려질 수 있으므로 브라우저 제한 설정 필요
options: {
    presets: [
      ['@babel/preset-env', {
        targets: {
          browsers: ['> 5% in KR', 'last 2 chrome versions'],
        },
      }], '@babel/preset-react'
    ],
}

2) plugins

  • entry - module - plugins - output 순서로 코드 작성하는 것이 좋음 (코드 흐름대로)
module.exports = {
    entry: {
        app: './client',
    },
    module: {
        rules: [{...}],
    },
    plugins: [
        new webpack.LoaderOptionsPlugin({debug: true}),
    ],
    output: {
        filename: 'app.js',
        path: path.join(__dirname, 'dist'),
    },
}

8. 끝말잇기 Class 만들기

WordRelay.jsx

const React = require('react');
const { Component } = React;

class WordRelay extends Component {
    state = {
        word: '제로초',
        value: '',
        result: '',
    };

    onSubmitForm = (e) => {
        e.preventDefault();
        if(this.state.word[this.state.word.length - 1] === this.state.value[0]) {
            this.setState({
                result: '딩동댕',
                word: this.state.value,
                value: '',
            });
            this.input.focus();
        }else{
            this.setState({
                result: '땡',
                value: '',
            });
            this.input.focus();
        }
    };

    input;

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

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

    render() {
        return (
            <>
                <div>{this.state.word}</div>
                <form onSubmit={this.onSubmitForm}>
                    <input ref={this.onRefInput} value={this.state.value} onChange={this.onChangeInput}/>
                    <button>입력!</button>
                </form>
                <div>{this.state.result}</div>
            </>
        );
    }
}
// 모듈 내보내기
module.exports = WordRelay;
  • 변경사항이 있을 때마다 새로 빌드해야 함(npx webpack)

9. webpack-dev-server와 핫 리로딩

webpack-cli 업데이트로 인해 hot-loader가 아니라 react-refresh로 변경됨 (2021-07-20 수정)

  • 변경사항 자동 반영 설정
  • npm i -D react-refresh @pmmmwh/react-refresh-webpack-plugin
  • npm i -D webpack-dev-server: 개발용 서버
  • package.json
  "scripts": {
    "dev": "webpack serve --env development"
  },

👉 로컬 서버에서 앱 실행 가능

  • webpack.config.js
    • plugins에 react-refresh-webpack-plugin 추가
const RefreshWebpackPlugin = require('@pmmmwh/react-refresh-webpack-plugin');
...
plugins: [new RefreshWebpackPlugin()],
  • output에 publicPath 추가
        output: {
            filename: 'app.js',
            path: path.join(__dirname, 'dist'),	// 실제 경로
            publicPath: '/dist/',	// 가상 경로
    },
  • output 아래에 devServer 추가
devServer: {
    publicPath: '/dist/',
    hot: true,
},

❓ 원래도 리로딩 되는데 굳이 핫 리로딩 설정하는 이유

  • 일반 리로딩은 말그대로 새로고침이기 때문에 기존 데이터가 유지되지 않음
  • 핫 리로딩은 기존 데이터를 유지하면서 새로고침
  • 또 오류를 브라우저 콘솔에서 바로 확인할 수 있어서 효율적

10. 끝말잇기 Hooks로 전환하기

WordRelay.jsx

const React = require('react');
const { useState, useRef } = React;

const WordRelay = () => {
    const [word, setWord] = useState('제로초');
    const [value, setValue] = useState('');
    const [result, setResult] = useState('');
    const inputRef = useRef(null);

    const onSubmitForm = (e) => {
        e.preventDefault();
        if(word[word.length - 1] === value[0]) {
            setResult('딩동댕');
            setWord(value);
            setValue('');
            inputRef.current.focus();
        }else{
            setResult('땡');
            setValue('');
            inputRef.current.focus();
        }
    };

    const onChangeInput = (e) => {
        setValue(e.target.value);
    };

    return (
        <>
            <div>{word}</div>
            <form onSubmit={onSubmitForm}>
                <input ref={inputRef} value={value} onChange={onChangeInput}/>
                <button>입력!</button>
            </form>
            <div>{result}</div>
        </>
    );
};
    
// 모듈 내보내기
module.exports = WordRelay;

브라우저 콘솔

  • HMR: Hot Module Reloader
  • WDS: Webpack Dev Server

0개의 댓글