본문 바로가기

웹/React

[React] 파일을 컴포넌트별로 구분하기

반응형

리액트 공식문서를 보면서 틱택토 게임 만들기를 따라하고 있었는데 Game 컴포넌트가 갑자기 최상위 컴포넌트로 되어야하는 상황이었다. (Square 컴포넌트 빼고는 걍 App컴포넌트에 때려넣고 있었음...) 

 

그냥 App 컴포넌트 안에서 간단하게 하려면 할 수 있었지만 파일들이 기능별로 구분되어있으면 좋겠다는 강박 아닌 강박이 있어서 파일들을 구분하기로 했다.


그런데 이미 틱택토 게임 만들기를 따라하면서 App 컴포넌트에 다양한 함수와 state, 변수를 선언해버렸다. 이를 분리하느라 애먹었다. 


Square 컴포넌트는 시작부터 구분해 놓아서 이미 되어있었다.


문제는 App 컴포넌트를 Board 컴포넌트와 Game 컴포넌트로 분리하는 작업이었다.


처음에는 이것 저것을 마구잡이로 복붙을 하다가 방법(?)을 터득했다.



📌해결 방법
먼저 컴포넌트가 할 기능을 딱 정해놓는 것이다. 

 

Board 컴포넌트는 우승자를 가리는 기능과 게임판과 관련된 기능을 넣는 것으로 정하였다.


Game 컴포넌트는 Board 컴포넌트 포함하도록 하였다. 

 

따라서 이 곳에 state들을 선언해야겠다고 생각이 들었다. 

 

왜냐하면 state들을 위에서부터 내려주어야 했기 때문이다. 

 

추가적으로 틱택토를 진행하는 동안 기록되는 history도 필요했기 때문에 이를 표시해주기 위한 div를 만들어 주었다. 

 

이 곳에 리스트 형태로 기록을 나타낼 것이다.


대략적으로 state들은 내리사랑(?)을 받아야 되니까 맨~~ 위(최상위 컴포넌트, 여기서는 Game) 에 선언해준다. 

 

그리고 그 아래에 이 state들을 사용해야 하는 컴포넌트가 선언되어있다면 인자로 넘겨준다. 

 

여기서는 Board 컴포넌트, Game 컴포넌트에서 Board 컴포넌트를 사용(렌더링)했는데, Board는 xIsNext, setHistory, setHistory, setXIsNext, squares를 모두 사용해야되기 때문에 이것들을 모두 인자로 넘겨줌. set 함수들은 항상 같이 실행되기 때문에 handlePlay 라는 함수로 묶어서 보내주었음.


😁바뀐 코드

//Game.js
import React from "react";
import { useState } from "react";
import Board from "./Board";
import "./styles.css";

const Game = () => {
  const [xIsNext, setXIsNext] = useState(true);
  const [history, setHistory] = useState([Array(9).fill(null)]);
  const [currentMove, setCurrentMove] = useState(0);
  const currentSquares = history[currentMove];

  function handlePlay(nextSquares) {
    const nextHistory = [...history.slice(0, currentMove + 1), nextSquares];
    setHistory(nextHistory);
    setCurrentMove(nextHistory.length - 1);
    setXIsNext(!xIsNext);
  }

  const jumpTo = (nextMove) => {
    setCurrentMove(nextMove);
    setXIsNext(nextMove % 2 === 0);
  };

  const moves = history.map((squares, move) => {
    let description;
    if (move > 0) {
      description = `Go to move #${move}`;
    } else {
      description = `Go to game start`;
    }
    return (
      <li key={move}>
        <button onClick={() => jumpTo(move)}>{description}</button>
      </li>
    );
  });

  return (
    <div className="game">
      <div className="game-board">
        <Board xIsNext={xIsNext} squares={currentSquares} onPlay={handlePlay} />
      </div>
      <div className="game-info">
        <ol>{moves}</ol>
      </div>
    </div>
  );
};

export default Game;

 

//Board.js
import React from "react";
import Square from "./Square";
import "./styles.css";

const Board = ({ xIsNext, squares, onPlay }) => {
  const calculatorWinner = (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 = calculatorWinner(squares);

  let status;

  if (winner) {
    status = `Winner : ${winner}`;
  } else {
    status = `Next Player : ${xIsNext ? "X" : "O"}`;
  }
  const handleClick = (i) => {
    if (squares[i] || calculatorWinner(squares)) {
      return;
    }
    const nextSquares = squares.slice();
    if (xIsNext) {
      nextSquares[i] = "X";
    } else {
      nextSquares[i] = "O";
    }

    onPlay(nextSquares);
  };
  return (
    <div>
      <div className="status">{status}</div>
      <div className="board-row">
        <Square value={squares[0]} onSquareClick={() => handleClick(0)} />
        <Square value={squares[1]} onSquareClick={() => handleClick(1)} />
        <Square value={squares[2]} onSquareClick={() => handleClick(2)} />
      </div>
      <div className="board-row">
        <Square value={squares[3]} onSquareClick={() => handleClick(3)} />
        <Square value={squares[4]} onSquareClick={() => handleClick(4)} />
        <Square value={squares[5]} onSquareClick={() => handleClick(5)} />
      </div>
      <div className="board-row">
        <Square value={squares[6]} onSquareClick={() => handleClick(6)} />
        <Square value={squares[7]} onSquareClick={() => handleClick(7)} />
        <Square value={squares[8]} onSquareClick={() => handleClick(8)} />
      </div>
    </div>
  );
};

export default Board;
//Square.js
import "./styles.css";

import React from "react";

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

export default Square;
반응형