- React

리액트 주사위게임&가위바위보게임 만들기 - 2.컴포넌트

나둥식 2023. 2. 17. 12:25

1️⃣ 하나의 컴포넌트 만들기 - Board.js

import { useState } from "react";
import Button from "./Button";
import Dice from "./Dice";

function random(n) {
  return Math.ceil(Math.random() * n);
} // 파라미터 n으로 숫자값을 전달 받아서 1부터 n까지의 랜덤한 정수를 반환하는 함수

function Board({name, color}) {
  const [num, setNum] = useState(1);
  const [sum, setSum] = useState(0); //총점 구하는 state -> 초기값 0으로 줌
  const [gameHistory, setGameHistory] = useState([]); // 빈배열을 초기값으로 받는 state

  const handleRollClick = () => {
    const nextNum = random(6);
    setNum(nextNum);
    setSum(sum + nextNum); // 총점
    // gameHistory.push(nextNum); //gameHistory가 배열이니까 push 메소드로 nextNum을 추가하고
    setGameHistory([...gameHistory, nextNum]); //새 값이 추가된 gameHistory State를 전달
  }; //num state를 바꾸는 함수

  const handleClearClick = () => {
    setNum(1);
    setSum(0);
    setGameHistory([]);
  }; //초기화 기능

  return (
    <div>
      <div>
        <Button onClick={handleRollClick}>던지기</Button>
        <Button onClick={handleClearClick}>처음부터</Button>
      </div>
      <div>
        <h2>{name}</h2>
        <Dice color={color} num={num} />
        <h2>총점</h2>
        <p>{sum}</p>
        <h2>기록</h2>
        <p>{gameHistory.join(", ")}</p>
      </div>
    </div>
  );
}

export default Board;

 

 

2️⃣ 공통된 데이터(자식 컴포넌트)를 부모(App.js)한테 올려주기 (State Lifting)

import Board from "./Board";
import { useState } from "react";
import Button from "./Button";

function random(n) {
  return Math.ceil(Math.random() * n);
} // 파라미터 n으로 숫자값을 전달 받아서 1부터 n까지의 랜덤한 정수를 반환하는 함수

function App() {
  const [num, setNum] = useState(1);
  const [sum, setSum] = useState(0); //총점 구하는 state -> 초기값 0으로 줌
  const [gameHistory, setGameHistory] = useState([]); // 빈배열을 초기값으로 받는 state
  const [othernum, setOtherNum] = useState(1);
  const [othersum, setOtherSum] = useState(0); //총점 구하는 state -> 초기값 0으로 줌
  const [othergameHistory, setOtherGameHistory] = useState([]); // 빈배열을 초기값으로 받는 state

  const handleRollClick = () => {
    const nextNum = random(6);
    const nextOtherNum = random(6);
    setNum(nextNum);
    setSum(sum + nextNum); // 총점
    // gameHistory.push(nextNum); //gameHistory가 배열이니까 push 메소드로 nextNum을 추가하고
    setGameHistory([...gameHistory, nextNum]); //새 값이 추가된 gameHistory State를 전달
    setOtherNum(nextOtherNum);
    setOtherSum(sum+nextOtherNum);
    setOtherGameHistory([...othergameHistory, nextOtherNum]);
  }; //num state를 바꾸는 함수

  const handleClearClick = () => {
    setNum(1);
    setSum(0);
    setGameHistory([]);
    setOtherNum(1);
    setOtherSum(0);
    setOtherGameHistory([]);
  }; //초기화 기능

  return (
    <div>
      <div>
        <div>
          <Button onClick={handleRollClick}>던지기</Button>
          <Button onClick={handleClearClick}>처음부터</Button>
        </div>
        <Board name="나" color="blue" />
        <Board name="상대" color="red" />
      </div>
    </div>
  );
}

export default App;

 

 

3️⃣ state를 각 Board 컴포넌트의 적절한 prop으로 전달해주기

<Board name="나" color="blue" num={num} sum={sum} gameHistory={gameHistory} /> 
<Board name="상대" color="red" num={othernum} sum={othersum} gameHistory={othergameHistory} />

: 상대의 Board 컴포넌트와 prop 이름은 똑같지만, 전달하는 state값을 상대편 state값으로 전달해 줌!

 

 

4️⃣ 값들을 컴포넌트에서 전달받기

🟡변경 전

import Dice from "./Dice";


function Board({name, color}) {

  return (
    
      <div>
        <h2>{name}</h2>
        <Dice color={color} num={num} />
        <h2>총점</h2>
        <p>{sum}</p>
        <h2>기록</h2>
        <p>{gameHistory.join(", ")}</p>
      </div>
  );
}

export default Board;

 

🔴변경 후

import Dice from "./Dice";


function Board({name, color, num, sum, gameHistory}) {

  return (
    
      <div>
        <h2>{name}</h2>
        <Dice color={color} num={num} />
        <h2>총점</h2>
        <p>{sum}</p>
        <h2>기록</h2>
        <p>{gameHistory.join(", ")}</p>
      </div>
  );
}

export default Board;

 

 

5️⃣ 코드 정리하기

① 현재 주사위의 숫자값 = 기록의 마지막 값
② 총점 = 기록이 가지고 있는 모든 숫자의 합
⭐ 주사위를 던진 기록만 있으면 총점과 현재 주사위의 숫자값을 구할 수 있음

 

- App.js

import Board from "./Board";
import { useState } from "react";
import Button from "./Button";

function random(n) {
  return Math.ceil(Math.random() * n);
} // 파라미터 n으로 숫자값을 전달 받아서 1부터 n까지의 랜덤한 정수를 반환하는 함수

function App() {
  const [mygameHistory, setMyGameHistory] = useState([]); // 빈배열을 초기값으로 받는 state
  const [othergameHistory, setOtherGameHistory] = useState([]); // 빈배열을 초기값으로 받는 state

  const handleRollClick = () => {
    const nextMyNum = random(6);
    const nextOtherNum = random(6);
    // gameHistory.push(nextNum); //gameHistory가 배열이니까 push 메소드로 nextNum을 추가하고
    setMyGameHistory([...mygameHistory, nextMyNum]); //새 값이 추가된 gameHistory State를 전달
    setOtherGameHistory([...othergameHistory, nextOtherNum]);
  }; //num state를 바꾸는 함수

  const handleClearClick = () => {
    setMyGameHistory([]);
    setOtherGameHistory([]);
  }; //초기화 기능

  return (
    <div>
      <div>
        <div>
          <Button onClick={handleRollClick}>던지기</Button>
          <Button onClick={handleClearClick}>처음부터</Button>
        </div>
        <Board name="나" color="blue" gameHistory={mygameHistory} /> 
        <Board name="상대" color="red" gameHistory={othergameHistory} />
      </div>
    </div>
  );
}

export default App;

 

- Board.js

import Dice from "./Dice";


function Board({name, color, gameHistory}) {
    const num = gameHistory[gameHistory.length -1] || 1;
    const sum = gameHistory.reduce((a,b) => a+b, 0);

  return (
    
      <div>
        <h2>{name}</h2>
        <Dice color={color} num={num} />
        <h2>총점</h2>
        <p>{sum}</p>
        <h2>기록</h2>
        <p>{gameHistory.join(", ")}</p>
      </div>
  );
}

export default Board;

⭐ num과 sum을 직접 계산해서 변수로 사용함

 

① num 계산

🔎 a || b (OR)
: a가 true값인 경우 b는 무시되고 a가 결과값으로 나오고, 반대로 a가 false값인 경우 b가 결과값으로 나옴
➡ 피연산자가 모두 false인 경우를 제외하고 연산 결과는 항상 true가 나옴

💡 false값의 예시
- null
- NaN
- 0
- 빈 문자열
- undefined
const num = gameHistory[gameHistory.length -1] || 1;

: [gameHistory.length -1] =  gameHistory 배열의 마지막 값을 받아오는 코드

➡ 만약, gamehistory가 빈 배열이면 undefined가 나옴 = undefined || 1 = 1

➡ 반대로, gamehistory에 값이 1개 이상 있으면 배열의 마지막 값이 num에 들어감!

 

 

② sum 계산

🔎 .reduce()
: 배열의 각 요소에 대해 함수를 실행하고 누적된 값을 출력
➡ 가장 기본적인 예제로는, 모든 배열의 합을 구하는 경우!
const sum = gameHistory.reduce((a,b) => a+b, 0);

- initialValue값을 0으로 두었기 때문에 (a,b)의 초기값은 0이 됨

- 배열의 첫 번째 요소부터 a와 b를 더해감 = a+b

 

 


 

 

✅ state가 바뀌었을 때 리액트가 렌더링하는 방식 = Virtual DOM(가상 DOM) 활용!

: 엘리먼트를 새로 렌더링할 때, 리액트는 그 모습을 실제 DOM 트리에 바로 반영하는게 아니라 일단 Virtual DOM에 적용함

 

① 트리를 지우고 새로 만들지만 실제 DOM 트리에 바로 반영하는것이 아님

② 리액트가 State 변경 전의 Virtual DOM과 변경 후의 Virtual DOM을 비교함

③ 바뀐 부분만 찾아낸 다음에, 각각에 해당하는 실제 DOM 노드를 변경함

==> 단순하고 깔끔한 코드를 작성할 수 있고, 무슨 데이터를 어떻게 보여 줄 것인지만 신경 쓰면 되는 장점이 있음!

==> 리액트를 Virtual DOM이 바뀔 때마다 브라우저에 바로 전달하지 않고, 변경 사항들을 모아 뒀다가 적당히 나눠서 브라우저에 전달함(=한정된 브라우저 자원을 효율적으로 활용할 수 있게 됨!)