React 스터디 2주차

RIHO·2022년 1월 28일
0

React STUDY

목록 보기
2/11
post-thumbnail

🎮 웹 게임을 만들며 배우는 React
2주차: 끝말잇기

1. Hooks

Hooks를 사용하는 함수형 컴포넌트는 클래스형 컴포넌트보다 깔끔하다. React 또한 클래스형보다 Hooks를 사용하는 것을 권장하고 있다.

// setState 사용
// useState 내부에는 초깃값 입력
const [first, setFirst] = React.useState(); 
const [second, setSecond] = React.useState();
const [value, setValue] = React.useState();

이때 useState는 꼭 컴포넌트 내에 있어야 한다.

이전에 작성하였던 구구단 컴포넌트를 함수형으로 변경해 보자.

                    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);

                    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 (
                        <div>
                            <div>{first} 곱하기 {second} 는? </div>
                            <form onSubmit={onSubmitForm}>
                                <input ref={InputRef} onChange={onChangeInput} value={value} />
                                <button>입력</button>
                            </form>
                            <div id="result">{result}</div>
                        </div>
                    );
                };

클래스형 컴포넌트에 비하면 코드 줄 수가 짧다.
하지만 state가 변경될 때마다 함수가 통째로 다시 실행되므로 속도가 느릴 수 있다.



2. 웹팩

웹팩을 사용하면 여러 개의 자바스크립트 파일을 모아서 하나의 JS 파일로 만들어준다.

$ npm i react react-dom
$ npm i -D webpack webpack-cli

웹팩 설정을 끝낸 후, 끝말잇기 실습을 위해 우선 클래스형 컴포넌트를 만든다.

WordRelay.jsx

const React = require('react');
const { Component } = React; 
// 파일에서 필요로 하는 패키지 및 라이브러리

class WordRelay extends React.Component {
    state = {
        text: 'hello, webpack',
    };
    render() {
        return (
            <div>
               <h1> {this.state.text} </h1>
            </div>

        )
    }
}

module.exports = WordRelay;
// 현재 컴포넌트를 바깥에서도 사용할 수 있도록

웹팩은 webpack.config.js로 돌아간다!

webpack.config.js

const path = require('path');

module.exports = {
    name: 'wordrelay-setting',
    mode: 'development', // 실서비스: production
    devtool: 'eval',
    resolve: {
        extensions: ['.js', '.jsx'], // 웹팩이 알아서 확장자를 찾아준다!
    },

    // 중요
    entry: {
        app: ['./client.jsx', './WordRelay.jsx'],
    }, // 입력
    output: {
        path: path.join(__dirname, 'dist'), // 현재 폴더의 'dist'라는 폴더
        filename: 'app.js',
    }, // 출력
}

이때 터미널에 webpack 명령어를 입력하면
이와 같은 오류가 발생한다.

이에 대한 해결 방법은 1) 명령어로 등록하거나 2) 스크립트에 작성하는 것 3) npx webpack 를 사용하는 것이다. 나는 3번 방법을 이용하였다.


이 에러를 해결하기 위하여 @babel/core(babel 최신 문법으로 바꾸어주는), @babel/preset-env(개발 환경에 맞도록 바꾸어주는), @babel/preset-react(JSX로 바꾸어주는), babel-loader(babel과 webpack을 연결해주는) 패키지를 설치한다.

webpack의 모듈화가 완료되면 dist/app.js에 파일들이 추가된다.


plugin들의 모음이 사진의 preset이다. 만약 각각의 plugin에 설정을 추가하고 싶다면,


이와 같이 작업해주면 좋다.

3. 끝말잇기

WordRelay.jsx

const React = require('react');
const { Component } = React; 
// 파일에서 필요로 하는 패키지 및 라이브러리

class WordRelay extends React.Component {
    state = {
        text: '김밥',
        value: '',
        result: '',
    };

    onSubmitForm = () => {
        e.prevetDefault();
        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();
        }
    }

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

    input;
    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;
// 현재 컴포넌트를 바깥에서도 사용할 수 있도록


이제 핫 리로딩을 하는 방법을 알아보자.
(이쯤에서 강의자 분께서 갑자기 미래에서 오셨다. 웃느라 죽는 줄 알았다.)


핫 리로딩을 위한 2개의 패키지와 개발용 서버 패키지를 설치하자.

$ npm i react-refresh @pmmmwh/react-refresh-webpack-plugin -D
$ npm i -D webpack-dev-server

webpack.config.js

const path = require('path');
const RefreshWebpackPlugin = require('@pmmmwh/react-refresh-webpack-plugin');

module.exports = {
    name: 'wordrelay-setting',
    mode: 'development', // 실서비스: production
    devtool: 'eval',
    resolve: {
        extensions: ['.js', '.jsx'], // 웹팩이 알아서 확장자를 찾아준다!
    },

    // 중요
    entry: {
        app: ['./client.jsx', './WordRelay.jsx'],
    }, // 입력

    module: {
        rules: [{
            test: /\.jsx?/, // js , jsx 파일에 rule 적용
            loader: 'babel-loader', 
            options: {
                presets: [
                    ['@babel/preset-env', {
                        targets: {
                            browsers: ['> 1% in KR'], // browserlist
                        },
                        debug: true,
                    }], 
                    ['@babel/preset-react'],
                ],
                plugins: [
                    '@babel/plugin-proposal-class-properties',
                    'react-refresh/babel',
                ],
            },
        }],
    },

    plugins: [
        new RefreshWebpackPlugin(),
    ],

    output: {
        path: path.join(__dirname, 'dist'), // 현재 폴더의 'dist'라는 폴더
        filename: 'app.js',
        publicPath: '/dist/',
    }, // 출력

    devServer: { // 변경점을 감지함
        devMiddleware: {
            publicPath: '/dist',
        },
        static: { directory: path.resolve(__dirname) },
        hot: true,
    },
};


사진으로만 확인하기는 어렵지만... 아무튼 localhost:8080에서 핫 리로딩이 잘 이루어진다!

이제 클래스형 컴포넌트를 hooks 방식으로 바꾸어 보자.

WordRelay.js

const { useState, useRef } = require('react');
const React = require('react');
const { Component } = 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('딩동댕');
            setValue('');
            setWord(value);

            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;
// 현재 컴포넌트를 바깥에서도 사용할 수 있도록

추가

1.

ERROR in ./client.jsx 5:16-38
Module not found: Error: Can't resolve './WordRelay' in 'D:\공부\React\react-game\wordchain'
resolve './WordRelay' in 'D:\공부\React\react-game\wordchain'
  using description file: D:\공부\React\react-game\wordchain\package.json (relative path: .)
    Field 'browser' doesn't contain a valid alias configuration
    using description file: D:\공부\React\react-game\wordchain\package.json (relative path: ./WordRelay)
      no extension
        Field 'browser' doesn't contain a valid alias configuration
        D:\공부\React\react-game\wordchain\WordRelay doesn't exist
      .js
        Field 'browser' doesn't contain a valid alias configuration
        D:\공부\React\react-game\wordchain\WordRelay.js doesn't exist
      jsx
        Field 'browser' doesn't contain a valid alias configuration
        D:\공부\React\react-game\wordchain\WordRelayjsx doesn't exist
      as directory
        D:\공부\React\react-game\wordchain\WordRelay doesn't exist

중간에 npx webpack을 했을 때, 이와 같은 오류가 발생하였다.

범인은 이곳...

2.

Uncaught ReferenceError: e is not defined
Uncaught Error: A cross-origin error was thrown. React doesn't have access to the actual error object in development. See https://reactjs.org/link/crossorigin-error for more information.

끝말잇기 클래스 컴포넌트를 만들고 input에 텍스트를 입력하자 이와 같은 오류가 발생하였다.


이 부분에 e가 빠져 있어서 생긴 오류였다! 😟

3.

[webpack-cli] Invalid options object. Dev Server has been initialized using an options object that does not match the API schema.he API schema.
 - options has an unknown property 'publicPath'. These properties are valid:                                       , http2?, https?, ipc?, liveReload?, magicHtml?, o
   object { allowedHosts?, bonjour?, client?, compress?, devMiddleware?, headers?, historyApiFallback?, host?, hot?etupMiddlewares?, static?, watchFiles?, webSocketS, http2?, https?, ipc?, liveReload?, magicHtml?, onAfterSetupMiddleware?, onBeforeSetupMiddleware?, onListening?, open?, port?, proxy?, server?, setupExitSignals?, setupMiddlewares?, static?, watchFiles?, webSocketServer? }   

핫 리로딩 도중 이 오류가 발생했다. 찾아보니 publicPath 속성은 devMiddleware 내부에서 사용해야 한다고 한다.

    devServer: { // 변경점을 감지함
        devMiddleware: {
            publicPath: '/dist/',
        },
        hot: true,
    },

그런데 강의를 듣다 보니 강의자 분께서 이 부분에 대해 설명을 해 주셨다!! 머쓱.

4.

핫 리로딩을 진행하는데, Cannot get / 페이지가 뜨고 404 오류가 발생하였다. 😂
서버를 끄고

$ npm i

를 한 뒤, 다시 서버를 재시작하니 해결되었다.

profile
Front-End / 기록용

0개의 댓글

관련 채용 정보