Frontend/React

[React] TicTacToe앱 만들기(2) - 리액트 Hooks, 순서, 승자 판별

gamzaggang7 2024. 6. 18. 20:20
728x90

1. React Hooks

Hooks는 리액트 v16.8에 새로 도입된 기능으로, 함수형 컴포넌트에서 상태와 생명주기 기능을 사용할 수 있도록 해주는 기능이다. 함수 컴포넌트에서도 상태 관리를 할 수 있는 useState, 렌더링 직후 작업을 설정하는 udeEffect 등의 기능을 제공한다.

 

지금 앱은 클래스형 컴포넌트로 작성했지만 Hooks를 사용하기 위해 함수형 컴포넌트로 바꾼다. 함수형에서는 render()없이 바로 리턴한다.

 

Board.js

import React, { Component } from 'react';
import Square from './Square';
import "./Board.css";

const Board = () => {
  constructor(props) {
    super(props);
    this.state = {
      squares: Array(9).fill(null)
    }
  }
  handleClick(i) {
    const squares = this.state.squares.slice();
    squares[i] = 'X';
    this.setState({ squares: squares })
  }
  renderSquare(i) {
    return <Square value={this.state.squares[i]} onClick={() => this.handleClick(i)} />
  }
  return (
    <div>
      <div className="status">Next Player: X, O</div>
      <div className="board-row">
        {this.renderSquare(0)}
        {this.renderSquare(1)}
        {this.renderSquare(2)}
      </div>
      <div className="board-row">
        {this.renderSquare(3)}
        {this.renderSquare(4)}
        {this.renderSquare(5)}
      </div>
      <div className="board-row">
        {this.renderSquare(6)}
        {this.renderSquare(7)}
        {this.renderSquare(8)}
      </div>
    </div>
  )
};

export default Board;

 

state부분은 Hooks의 useState로 바꾼다.

const Board = () => {
  const [squares, setSquares] = useState(Array(9).fill(null));

첫 번째 원소는 상태 값(getter), 두 번째 원소는 상태를 설정하는 함수(setter)이다.

 

나머지 메소드도 화살표 함수로 바꾸고 this.state 부분은 삭제한다.

  const handleClick = (i) => {
    const newSquares = squares.slice();
    newSquares[i] = 'X';
    setSquares(newSquares)
  }
 
  const renderSquare = (i) => {
    return <Square value={squares[i]} onClick={() => handleClick(i)} />
  }

  return (
    <div>
      <div className="status">Next Player: X, O</div>
      <div className="board-row">
        {renderSquare(0)}
        {renderSquare(1)}
        {renderSquare(2)}
      </div>
      <div className="board-row">
        {renderSquare(3)}
        {renderSquare(4)}
        {renderSquare(5)}
      </div>
      <div className="board-row">
        {renderSquare(6)}
        {renderSquare(7)}
        {renderSquare(8)}
      </div>
    </div>
  )

 

Square.js

구조 분해 할당으로 코드를 간결하게 표현한다.

const Square = ({ onClick, value }) => {
  return (
    <button className='square' onClick={onClick}>
      {value}
    </button>
  );
};

export default Square;
728x90

 

2. 순서 만들기

X와 O가 번갈아가면서 입력되도록 한다.

xIsNext 변수를 생성하고 xIsNext가 true일 때는 X를, false일 때는 O를 입력하도록 한다.

const Board = () => {
  const [squares, setSquares] = useState(Array(9).fill(null));
  const [xIsNext, setXIsNext] = useState(true);

  const handleClick = (i) => {
    const newSquares = squares.slice();
    newSquares[i] = xIsNext ? 'X' : 'O';
    setSquares(newSquares)
    setXIsNext(!xIsNext);
  }

 

status 태그 부분에도 다음 플레이어가 누구 차례인지 보이도록 한다.

  const [squares, setSquares] = useState(Array(9).fill(null));
  const [xIsNext, setXIsNext] = useState(true);
  const status = `Next player: ${xIsNext ? 'X' : 'O'}`;
  return (
    <div>
      <div className="status">{status}</div>

 

3. 승자 결정

승자 판별을 위해 승부 결정 계산 함수를 만든다. X나 O가 한 줄이 되는 경우의 수를 나열하고 한 줄이 나오면 해당 플레이어를 return한다.

const Board = () => {
  const [squares, setSquares] = useState(Array(9).fill(null));
  const [xIsNext, setXIsNext] = useState(true);

  const calculateWinner = (squares) => {
    const lines = [
      [0, 1, 2],
      [3, 4, 5],
      [6, 7, 8],
      [0, 3, 6],
      [1, 4, 7],
      [2, 5, 8],
      [0, 4, 8],
      [2, 4, 6]
    ]

    for (let i = 0; i < lines.length; i++) {
      const [a, b, c] = lines[i];
      if (squares[a] && squares[a] === squares[b] && squares[a] === squares[c])
        return squares[a];
    }

    return null;
  }

  const winner = calculateWinner(squares);
 

승자가 판별되면 status가 winner로 바뀌게 한다.

  const winner = calculateWinner(squares);
  let status;
  if (winner) {
    status = 'Winner: ' + winner;
  } else {
    status = `Next player: ${xIsNext ? 'X' : 'O'}`;
  }

 

승자가 판별되거나 board에 값이 있으면 handleClick 메서드에서 클릭 이벤트를 무시하도록 한다.

  const handleClick = (i) => {
    const newSquares = squares.slice();

    if(calculateWinner(newSquares) || newSquares[i]){
      return;
    }

 

728x90