React 기본 학습 -8. 숫자야구 예제

React 기본 학습 -8. 숫자야구 예제

리액트 강좌를 보면서 학습한 내용 정리용.

리액트 무료 강좌(웹게임) - ZeroCho TV

1. Class 문법

NumberBaseball.jsx

import React, { Component } from 'react';
import Try from "./try"; // '', "" 둘다 가능함

// 1. class 외부의 function 은 class 문법에서 hooks로 바꾸어도 독립적으로 존재가능.
// 2. 예를 들어 랜덤한 숫자배열을 반환하는 getNumbers() 처럼 
// 다른 곳에서도 사용될 가능성이 있는 함수는 class 외부에 선언한다.
function getNumbers() { // 숫자 4개 겹치지 않고 랜덤하게 만들어주는 함수 
    const candidate = [1,2,3,4,5,6,7,8,9];
    const array = [];
    for(let i = 0; i < 4; i += 1) {
        const chosen = candidate.splice(Math.floor(Math.random() * (9-i)), 1)[0];
        array.push(chosen);
    }
    return array;
}

class NumberBaseball extends Component {
    state = {
        result: '',
        value : '',
        answer: getNumbers(), // [1, 3, 5, 7]
        tries: [], // 시도한 횟수. push 쓰면 안됨.
    }

    onSubmitForm = (e) => {
        const { result, value, answer, tries } = this.state;
        e.preventDefault();
        if(value === answer.join('')) {
            this.setState((prevState) => {
                return {
                    result: '홈런!',
                    tries: [...prevState.tries, { try: value, result:'홈런!'}],
                }
            });
            alert('게임을 다시 시작합니다!');
            this.setState((prevState) => {
                return {
                    value: '',
                    answer: getNumbers(),
                    tries: [],
            }
            });
        } else { // 답 틀렸으면
            const answerArray = value.split('').map((v) => parseInt(v));
            let strike = 0;
            let ball = 0;
            if (tries.length >= 9) {
                this.setState({
                    result: `10번 넘게 틀려서 실패! 답은 ${answer.join(',')}였습니다!`,
                });
                alert('게임을 다시 시작합니다!');
                this.setState((prevState) => {
                    return {
                        value: '',
                        answer: getNumbers(),
                        tries: [],
                }
                });
            } else {
                for (let i = 0; i < 4; i += 1) {
                    if(answerArray[i] === answer[i]) {
                        strike += 1;                        
                    } else if (answer.includes(answerArray[i])){
                        ball += 1;
                    }
                }
                this.setState((prevState) => {
                    return {
                        tries: [...prevState.tries, { try: value, result: `${strike} 스트라이크, ${ball} 볼입니다.` }],
                        value: '',    
                    }
                });
            }
        }
    };

    onChangeInput = (e) => {
        console.log(this.state.answer);
        this.setState({
            value: e.target.value,
        });
    };

    // 리액트 반복문 - map 이용
    render(){
        const { result, value, answer, tries } = this.state;
        return(
            <>
                <h1>{result}</h1>
                <form onSubmit={this.onSubmitForm}>
                    <input maxLength={4} value={value} onChange={this.onChangeInput}/>
                </form>
                <div>시도: {tries.length}</div>
                <ul>
                    {tries.map((v, i) => {
                        return (
                            <Try key={`${i + 1}차 시도 : `} tryInfo={v} />
                        );
                    })}
                </ul>
            </>
        );
    }
}

export default NumberBaseball;

try.jsx

import React, {Component} from 'react';

class Try extends Component {
    render() {
        const { tryInfo } = this.props;
        return(
         <li>
            <div>{tryInfo.try}</div>
            <div>{tryInfo.result}</div>
         </li>
        )
    }
}

export default Try;

2. Hooks 문법(함수형)

특이사항

NumberBaseball.jsx

import React, { useState } from 'react';
import Try from './try';

function getNumbers() { // 숫자 4개 겹치지 않고 랜덤하게 만들어주는 함수 
    const candidate = [1,2,3,4,5,6,7,8,9];
    const array = [];
    for(let i = 0; i < 4; i += 1) {
        const chosen = candidate.splice(Math.floor(Math.random() * (9-i)), 1)[0];
        array.push(chosen);
    }
    return array;
};

const NumberBaseball = () => {
    const [result, setResult] = useState('');
    const [value, setValue] = useState('');
    const [answer, setAnswer] = useState(getNumbers());
    const [tries, setTries] = useState([]);

    const onSubmitForm = (e) => {
        e.preventDefault();
        if(value === answer.join('')) {
            setResult('홈런!');
            // 옛날 값으로 새 값을 만들때는 함수형 사용
            setTries((prevTries) => {
                return [...prevTries, { try: value, result:'홈런!'}]
            });
            alert('게임을 다시 시작합니다!');
            setValue('');
            setAnswer(getNumbers());
            setTries([]);

        } else { // 답 틀렸으면
            const answerArray = value.split('').map((v) => parseInt(v));
            let strike = 0;
            let ball = 0;
            if (tries.length >= 9) {
                setResult(`10번 넘게 틀려서 실패! 답은 ${answer.join(',')}였습니다!`);
                alert('게임을 다시 시작합니다!');
                setValue('');
                setAnswer(getNumbers());
                setTries([]);
            } else {
                for (let i = 0; i < 4; i += 1) {
                    if(answerArray[i] === answer[i]) {
                        strike += 1;                        
                    } else if (answer.includes(answerArray[i])){
                        ball += 1;
                    }
                }
                setTries((prevTries) => [...prevTries, { try: value, result: `${strike} 스트라이크, ${ball} 볼입니다.` }]);
                setValue('');
            }
        }
    };

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

    return (
        <>
        <h1>{result}</h1>
        <form onSubmit={onSubmitForm}>
            <input maxLength={4} value={value} onChange={onChangeInput}/>
        </form>
        <div>시도: {tries.length}</div>
        <ul>
            {tries.map((v, i) => {
                return (
                    <Try key={`${i + 1}차 시도 : `} tryInfo={v} />
                );
            })}
        </ul>
        </>
    );
};

export default NumberBaseball;

try.jsx

import React from 'react';

// const Try = (props) => { // 자식컴포넌트의 디폴트 상태. 내부에서 사용할땐 props.tryInfo 로 접근한다.
const Try = ({ tryInfo }) => { // props를 구조분해 한 상태. 내부에선 tryInfo 로 바로 접근한다.
    return (
        <li>
            <div>{tryInfo.try}</div>
            <div>{tryInfo.result}</div>
         </li>
    )
};

export default Try;

Github : React-Study-1/3.숫자야구/

Pie's Tech Note

생계형 개발자의 메모장

comments powered by Disqus

    rss facebook twitter github youtube mail spotify instagram linkedin google google-plus pinterest medium vimeo stackoverflow reddit quora