๐Ÿ“˜ [Recat] React - tic tac toe ๊ฒŒ์ž„

๐Ÿ“˜ [Recat] React - tic tac toe ๊ฒŒ์ž„

[Recat] React - tic tac toe ๊ฒŒ์ž„

์ปดํฌ๋„ŒํŠธ ๋ถ„๋ฆฌ & ์žฌ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ์ปดํฌ๋„ŒํŠธ ๊ตฌ์ถ•

์ปดํฌ๋„ŒํŠธ ๋ถ„๋ฆฌ

src/components/Player.jsx ํŒŒ์ผ์„ ์ƒ์„ฑํ•˜์—ฌ, ํ”Œ๋ ˆ์ด์–ด ์ •๋ณด๋ฅผ ๋ณ„๋„ ์ปดํฌ๋„ŒํŠธ๋กœ ์ถ”์ถœํ•ฉ๋‹ˆ๋‹ค.

function Player({ name, symbol }) {
  return (
    <li>
      <span className="player">
        <span className="player-name">{name}</span>
        <span className="player-symbol">{symbol}</span>
        <button>Edit</button>
      </span>
    </li>
  );
}
  • name๊ณผ symbol์„ props๋กœ ์ „๋‹ฌ๋ฐ›์•„ ์ถœ๋ ฅํ•ฉ๋‹ˆ๋‹ค.
  • ๊ธฐ์กด ๋งˆํฌ์—…์˜ ๊ตฌ์กฐ๋Š” ๊ทธ๋Œ€๋กœ ์œ ์ง€ํ•˜๋ฉด์„œ ์žฌ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•˜๊ฒŒ ํ–ˆ์Šต๋‹ˆ๋‹ค.

App.jsx์—์„œ ์ปดํฌ๋„ŒํŠธ ์‚ฌ์šฉ

  • ๊ธฐ์กด์— ํ•˜๋“œ์ฝ”๋”ฉ ๋˜์–ด ์žˆ๋˜ ํ”Œ๋ ˆ์ด์–ด ๋งˆํฌ์—…์„ ์‚ญ์ œํ•˜๊ณ ,
  • ๋Œ€์‹  Player ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋‘ ๋ฒˆ ํ˜ธ์ถœํ•ด ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ```jsx

### State(์ƒํƒœ) ํ™œ์šฉ๋ฒ•
Edit(์ˆ˜์ •) ๋ฒ„ํŠผ์„ ํด๋ฆญํ•ด ํ”Œ๋ ˆ์ด์–ด ์ด๋ฆ„์„ ์ˆ˜์ •ํ•  ์ˆ˜ ์žˆ๋Š” ์ƒํ˜ธ์ž‘์šฉ ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„
`useState` ํ›…์„ ์‚ฌ์šฉํ•ด ํ”Œ๋ ˆ์ด์–ด ์ด๋ฆ„์ด ํŽธ์ง‘ ์ค‘์ธ์ง€ ์—ฌ๋ถ€๋ฅผ ๊ด€๋ฆฌํ•จ.
```jsx
const [isEditing, setIsEditing] = useState(false);

handleEditClick ํ•จ์ˆ˜ ์ •์˜ โ†’ ๋ฒ„ํŠผ ํด๋ฆญ ์‹œ setIsEditing(true) ํ˜ธ์ถœ

function handleEditClick() {
  setIsEditing(true);
}

isEditing ๊ฐ’์— ๋”ฐ๋ผ ์ด๋ฆ„์„ ๋ณด์—ฌ์ฃผ๊ฑฐ๋‚˜ ์ž…๋ ฅ ํ•„๋“œ๋ฅผ ์ถœ๋ ฅํ•จ.

let playerName = <span className="player-name">{name}</span>;

if (isEditing) {
  playerName = <input type="text" required />;
}

์ดํ›„ JSX ๋‚ด๋ถ€์—์„œ playerName์„ ์ถœ๋ ฅํ•˜๋ฉด ์กฐ๊ฑด์— ๋”ฐ๋ผ ๋™์ ์œผ๋กœ UI๊ฐ€ ๋ Œ๋”๋ง๋จ ํ˜„์žฌ๋Š” ์ด๋ฆ„๋งŒ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ๋Š” ์ž…๋ ฅ์ฐฝ์ด ๋ณด์ด๋ฉฐ, ์ €์žฅ ๊ธฐ๋Šฅ์€ ์•„์ง ๊ตฌํ˜„ํ•˜์ง€ ์•Š์Œ

์ปดํฌ๋„ŒํŠธ ์ธ์Šคํ„ด์Šค์˜ ๋ถ„๋ฆฌ๋œ ๋™์ž‘๋ฒ•

function App() {
  return (
    <div id="game-container">
      <ol id="players">
        <Player name="Player 1" symbol="X" />
        <Player name="Player 2" symbol="O" />
      </ol>
    </div>
  );
}
  • Player ์ปดํฌ๋„ŒํŠธ๋Š” ํ•˜๋‚˜์˜ ์ •์˜๋œ ์ปดํฌ๋„ŒํŠธ์ง€๋งŒ, ์•ฑ์—์„œ ๋‘ ๋ฒˆ ์‚ฌ์šฉ ์ค‘.
  • ๊ฐ ์‚ฌ์šฉ ์‹œ ๋ฆฌ์•กํŠธ๋Š” ๊ฐ๊ฐ ๋…๋ฆฝ๋œ ์ปดํฌ๋„ŒํŠธ ์ธ์Šคํ„ด์Šค๋ฅผ ์ƒ์„ฑํ•จ.
  • ์ด ์ธ์Šคํ„ด์Šค๋Š” ์ž์ฒด ์ƒํƒœ (useState)๋ฅผ ๊ฐ–๊ณ  ์žˆ์œผ๋ฉฐ, ๋‹ค๋ฅธ ์ธ์Šคํ„ด์Šค์™€ ๊ณต์œ ํ•˜์ง€ ์•Š์Œ.
  • ๊ฐ ์ปดํฌ๋„ŒํŠธ๋Š” ์ž์‹ ๋งŒ์˜ ์ƒํƒœ์™€ ๋™์ž‘์„ ์œ ์ง€ํ•จ.

์กฐ๊ฑด์  ์ฝ˜ํ…์ธ  & State(์ƒํƒœ) ์—…๋ฐ์ดํŠธ

๋ฒ„ํŠผ ํ…์ŠคํŠธ๋ฅผ ์ƒํƒœ์— ๋”ฐ๋ผ ๋™์ ์œผ๋กœ ๋ณ€๊ฒฝ

const btnCaption = isEditing ? 'Save' : 'Edit';

๋ฒ„ํŠผ์˜ ํ…์ŠคํŠธ๋ฅผ Edit(์ˆ˜์ •) ๋˜๋Š” Save(์ €์žฅ)์œผ๋กœ ์กฐ๊ฑด๋ถ€๋กœ ํ‘œ์‹œ.

<input type="text" value={name} />

์ž…๋ ฅ ํ•„๋“œ์— value ์†์„ฑ์„ ์ถ”๊ฐ€ํ•ด ํ˜„์žฌ ํ”Œ๋ ˆ์ด์–ด ์ด๋ฆ„์ด ์ž๋™์œผ๋กœ ์ฑ„์›Œ์ง€๊ฒŒ ์„ค์ •

const handleEditClick = () => {
  setIsEditing(!isEditing);
};

๋ฒ„ํŠผ์„ ํด๋ฆญํ•˜๋ฉด isEditing ์ƒํƒœ๋ฅผ ํ† ๊ธ€(toggle) ํ•˜๋„๋ก handleEditClick ํ•จ์ˆ˜ ๊ฐœ์„ 

์ƒํƒœ ์—…๋ฐ์ดํŠธ ์‹œ ์ฃผ์˜ํ•  ์ : ์ด์ „ ๊ฐ’์— ๊ธฐ๋ฐ˜ํ•œ ๋ณ€๊ฒฝ์€ ํ•จ์ˆ˜ํ˜• ์—…๋ฐ์ดํŠธ๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ•จ

setIsEditing(!isEditing);
  • ์ด ๋ฐฉ์‹์€ ๊ฐ„๋‹จํ•˜์ง€๋งŒ, ํ˜„์žฌ ์ปดํฌ๋„ŒํŠธ ์‹คํ–‰ ์ฃผ๊ธฐ์˜ isEditing ๊ฐ’์„ ๊ธฐ์ค€์œผ๋กœ ์ž‘๋™ํ•ฉ๋‹ˆ๋‹ค.
  • ๋น„๋™๊ธฐ ์—…๋ฐ์ดํŠธ ํŠน์„ฑ์ƒ, ์—ฐ์† ํ˜ธ์ถœ ์‹œ ์˜ˆ์ƒ๊ณผ ๋‹ค๋ฅธ ๊ฒฐ๊ณผ๋ฅผ ๋งŒ๋“ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
setIsEditing((prevEditing) => !isEditing);
  • ์ด ๋ฐฉ์‹์€ React๊ฐ€ ์ตœ์‹  ์ƒํƒœ๊ฐ’์„ ์ž๋™์œผ๋กœ ๋งค๊ฐœ๋ณ€์ˆ˜(prevEditing)๋กœ ์ „๋‹ฌํ•ฉ๋‹ˆ๋‹ค.
  • ์ƒํƒœ๊ฐ€ ์—ฌ๋Ÿฌ ๋ฒˆ ๋ณ€๊ฒฝ๋˜์–ด๋„ ํ•ญ์ƒ ์ตœ์‹  ์ƒํƒœ๊ฐ’์„ ๊ธฐ์ค€์œผ๋กœ ๋ณ€๊ฒฝ ๊ฐ€๋Šฅ.
  • React ๊ณต์‹ ๊ถŒ์žฅ ๋ฐฉ์‹.

์ด์œ : React์˜ ์ƒํƒœ ๋ณ€๊ฒฝ์€ ์Šค์ผ€์ค„๋ง๋˜๊ณ  ๋น„๋™๊ธฐ์ ์œผ๋กœ ์ฒ˜๋ฆฌ๋จ

  • setState(์˜ˆ: setIsEditing) ํ˜ธ์ถœ ํ›„ ์ƒํƒœ๊ฐ€ ์ฆ‰์‹œ ๋ณ€๊ฒฝ๋˜์ง€ ์•Š์Œ.
  • ๋Œ€์‹  React๊ฐ€ ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋‹ค์‹œ ๋ Œ๋”๋งํ•  ๋•Œ ๋ณ€๊ฒฝ๋œ ์ƒํƒœ๋ฅผ ๋ฐ˜์˜ํ•จ.
  • ๋”ฐ๋ผ์„œ ๊ฐ™์€ ์‹คํ–‰ ์ฃผ๊ธฐ ๋‚ด์—์„œ ์ƒํƒœ๋ฅผ ์—ฐ์†์œผ๋กœ ์„ค์ •ํ•˜๋ฉด, ๊ทธ ๊ฐ’์ด ์ด๋ฏธ ์ตœ์‹ ์ด ์•„๋‹ ์ˆ˜ ์žˆ์Œ

์‚ฌ์šฉ์ž ์ž…๋ ฅ & ์–‘๋ฐฉํ–ฅ ๋ฐ”์ธ๋”ฉ

  • React์—์„œ๋Š” useState๋ฅผ ์ด์šฉํ•ด ์ž…๋ ฅ ํ•„๋“œ์˜ ๊ฐ’์„ ์ƒํƒœ๋กœ ์ œ์–ดํ•จ.
  • value๋ฅผ ์ง์ ‘ ์„ค์ •ํ•˜๋Š” ๋Œ€์‹ , state๋ฅผ ์ด์šฉํ•œ ๋™์  ๊ฐ’์œผ๋กœ ๊ด€๋ฆฌ. ```jsx const [editablePlayerName, setEditablePlayerName] = useState(initialName);

<input type=โ€textโ€ value={editablePlayerName} onChange={handleChange} />


#### ์ด๋ฒคํŠธ ์ฒ˜๋ฆฌ ํ•จ์ˆ˜: handleChange

- `input` ๊ฐ’์ด ๋ฐ”๋€” ๋•Œ๋งˆ๋‹ค ์‹คํ–‰๋จ (`onChange` ์ด๋ฒคํŠธ)
- `event.target.value`๋ฅผ ํ†ตํ•ด ์‚ฌ์šฉ์ž๊ฐ€ ์ž…๋ ฅํ•œ ๊ฐ’์„ ์ฝ์Œ
- ์ด ๊ฐ’์„ ์ƒํƒœ๋กœ ์ €์žฅํ•˜์—ฌ ์ฆ‰์‹œ ๋ฐ˜์˜
```jsx
function handleChange(event) {
  setEditablePlayerName(event.target.value);
}

์–‘๋ฐฉํ–ฅ ๋ฐ”์ธ๋”ฉ (Two-way Binding)

  • ์‚ฌ์šฉ์ž๊ฐ€ ์ž…๋ ฅํ•œ ๊ฐ’์„ state๋กœ ์—…๋ฐ์ดํŠธํ•˜๊ณ 
  • state์˜ ๊ฐ’์„ ๋‹ค์‹œ input์˜ value๋กœ ๋ณด์—ฌ์คŒ
  • ์‚ฌ์šฉ์ž ์ž…๋ ฅ๊ณผ UI๊ฐ€ ๋™๊ธฐํ™”๋จ

๋‹ค์ฐจ์› ๋ฆฌ์ŠคํŠธ ๋ Œ๋”๋ง

const initialGameBoard = [
  [null, null, null],
  [null, null, null],
  [null, null, null],
];
export default function GameBoard() {
  return (
    <ol id="game-board">
      {initialGameBoard.map((row, rowIndex) => (
        <li key={rowIndex}>
          <ol>
            {row.map((playerSymbol, colIndex) => (
              <li key={colIndex}>
                <button>{playerSymbol}</button>
              </li>
            ))}
          </ol>
        </li>
      ))}
    </ol>
  );
}
  • GameBoard.jsx ํŒŒ์ผ์„ ์ƒ์„ฑ
  • <ol> ํƒœ๊ทธ๋ฅผ ์‚ฌ์šฉํ•ด ๊ฒฉ์ž ๊ตฌ์กฐ๋ฅผ ์ถœ๋ ฅ
  • ์ค‘์ฒฉ <ol>์„ ํ†ตํ•ด 3ํ–‰ 3์—ด ๊ตฌ์„ฑ
  • .map()์„ ๋‘ ๋ฒˆ ์‚ฌ์šฉํ•ด ํ–‰(row)๊ณผ ์—ด(col)์„ ๊ฐ๊ฐ ๋ Œ๋”๋ง
  • ํ˜„์žฌ๋Š” ๋ฒ„ํŠผ ํด๋ฆญ ์‹œ ์•„๋ฌด ๋™์ž‘ ์—†์Œ

 const [gameBoard, setGameBoard] = useState(initialGameBoard);

  function handleSelectSquare(rowIndex, colIndex) {
    setGameBoard((prevGameBoard) => {
     // ๊นŠ์€ ๋ณต์‚ฌ๋กœ ๋ถˆ๋ณ€์„ฑ ์œ ์ง€
      const updatedBoard = [
        ...prevGameBoard.map((innerArray) => [...innerArray]),
      ];
      // ํ•ด๋‹น ์œ„์น˜์— ํ”Œ๋ ˆ์ด์–ด ๊ธฐํ˜ธ ์ง€์ • (์ž„์‹œ๋กœ 'X')
      updatedBoard[rowIndex][colIndex] = "X";
      return updatedBoard;
    });
  }
  • setGameBoard์— ํ•จ์ˆ˜ ์ „๋‹ฌ โ†’ ์ด์ „ ์ƒํƒœ์— ๊ธฐ๋ฐ˜ํ•œ ์—…๋ฐ์ดํŠธ
  • ๊นŠ์€ ๋ณต์‚ฌ(deep copy) ์‚ฌ์šฉ
  • ์›๋ณธ ๋ฐฐ์—ด์„ ์ง์ ‘ ์ˆ˜์ •ํ•˜์ง€ ์•Š์Œ (๋ถˆ๋ณ€์„ฑ ์œ ์ง€)
<button onClick={() => handleSelectSquare(rowIndex, colIndex)}>
  {playerSymbol}
</button>
  • ๊ฐ ๋ฒ„ํŠผ์€ ํด๋ฆญ ์‹œ ํ•ด๋‹น ์ขŒํ‘œ(rowIndex, colIndex)๋ฅผ handleSelectSquare๋กœ ์ „๋‹ฌ
  • ์ด๋กœ์จ ๊ฐ ์นธ์˜ ์ƒํƒœ๊ฐ€ ๋™์ ์œผ๋กœ ์—…๋ฐ์ดํŠธ๋จ

App ์ปดํฌ๋„ŒํŠธ์— ์ƒํƒœ ์ถ”๊ฐ€ (์ƒํƒœ ๋Œ์–ด์˜ฌ๋ฆฌ๊ธฐ)

const [activePlayer, setActivePlayer] = useState('X');
  • ์–ด๋–ค ํ”Œ๋ ˆ์ด์–ด ์ฐจ๋ก€์ธ์ง€ ์ค‘์•™(App)์—์„œ ๊ด€๋ฆฌ
  • ์ด์œ : GameBoard์™€ Player ์ปดํฌ๋„ŒํŠธ ๋ชจ๋‘ ์ด ์ •๋ณด๋ฅผ ํ•„์š”๋กœ ํ•จ
function handleSelectSquare() {
  setActivePlayer((prevPlayer) => prevPlayer === 'X' ? 'O' : 'X');
}
  • ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅผ ๋•Œ๋งˆ๋‹ค ์ฐจ๋ก€๊ฐ€ ๋ฐ”๋€Œ๋„๋ก ๊ตฌํ˜„
  • ์ด์ „ ์ƒํƒœ๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ๋‹ค์Œ ์ƒํƒœ ๊ฒฐ์ •

๊ฒŒ์ž„ํŒ ์ƒํƒœ ์ œ๊ฑฐ ๋ฐ ์ƒํƒœ ๋Œ์–ด์˜ฌ๋ฆฌ๊ธฐ

const [gameBoard, setGameBoard] = useState(initialGameBoard);//์ œ๊ฑฐ

GameBoard.jsx์—์„œ ๊ด€๋ฆฌํ•˜๋˜ ์ƒํƒœ๊ฐ’์„ ๋ถ€๋ชจ ์ปดํผ๋„ŒํŠธ์—์„œ ๊ด€๋ฆฌํ•˜๊ฒŒ ๋ณ€๊ฒฝ ์ƒํƒœ๋Š” ๋” ์ด์ƒ ์ด ์ปดํฌ๋„ŒํŠธ์—์„œ ๊ด€๋ฆฌํ•˜์ง€ ์•Š์Œ

//์ƒˆ๋กœ์šด ์ƒํƒœ ์ถ”๊ฐ€
const [gameTurns, setGameTurns] = useState([]);

function handleSelectSquare(rowIndex, colIndex) {
  setGameTurns((prevTurns) => {
    let currentPlayer = 'X';

    if (prevTurns.length > 0 && prevTurns[0].player === 'X') {
      currentPlayer = 'O';
    }
	//๊นŠ์€๋ณต์‚ฌ ์›๋ณธ ๋ฐฐ์—ด์„ ์ง์ ‘ ์ˆ˜์ •ํ•˜์ง€ ์•Š์Œ (๋ถˆ๋ณ€์„ฑ ์œ ์ง€)
    const updatedTurns = [
      { square: { row: rowIndex, col: colIndex }, player: currentPlayer },
      ...prevTurns,
    ];

    return updatedTurns;
  });
}
  • ์ด ๋ฐฐ์—ด์€ โ€œ๋ˆ„๊ฐ€ ์–ธ์ œ ์–ด๋””๋ฅผ ํด๋ฆญํ–ˆ๋Š”์ง€โ€ ์ •๋ณด๋ฅผ ๋‹ด์Œ
  • ๋ฐฐ์—ด์˜ ์ฒซ ์š”์†Œ๊ฐ€ ๊ฐ€์žฅ ์ตœ๊ทผ ํ„ด์ด ๋˜๋„๋ก ์•ž์ชฝ์— ์ถ”๊ฐ€
  • prevTurns[0]: ๊ฐ€์žฅ ์ตœ๊ทผ ํ„ด
  • ๊ฐ€์žฅ ์ตœ๊ทผ์— X๊ฐ€ ๋ˆŒ๋ ธ๋‹ค๋ฉด โ†’ ํ˜„์žฌ๋Š” O์˜ ์ฐจ๋ก€
  • ์ƒํƒœ๋ฅผ ์ง์ ‘ ๋ณ€๊ฒฝํ•˜์ง€ ์•Š๊ณ , ๋ณต์‚ฌ ํ›„ ์ƒˆ ๋ฐฐ์—ด์„ ๋งŒ๋“ค์–ด ๋ถˆ๋ณ€์„ฑ ์œ ์ง€
  • player, row, col ์ •๋ณด๋ฅผ ๊ฐ์ฒด๋กœ ๋ฌถ์–ด ๊ธฐ๋ก

ย Props(์†์„ฑ)์—์„œ State(์ƒํƒœ) ํŒŒ์ƒํ•˜๊ธฐ

์ƒํƒœ๋Š” App์—์„œ๋งŒ ๊ด€๋ฆฌ GameBoard.jsx์—์„œ๋Š” ์ƒํƒœ๊ฐ’์„ ๊ด€๋ฆฌ ํ•˜์ง€ ์•Š์Œ ๋ถ€๋ชจ ์ปดํฌ๋„ŒํŠธ๋กœ๋ถ€ํ„ฐ ๋ฐ›์•„์˜จ props ๋ฐ›์•„์„œ ๊ฐ’๋งŒ ์„ธํŒ…ํ•ด์ค€๋‹ค.

let gameBoard = initialGameBoard;

  for (const turn of turns) {
    const { square, player } = turn;
    const { row, col } = square;
    gameBoard[row][col] = player;
  }

์กฐ๊ฑด์  ๋ฒ„ํŠผ ๋น„ํ™œ์„ฑํ™”

GameBoard.jsx์—์„œ playerSymbol ํ™•์ธ

<button
  onClick={() => onSelectSquare(rowIndex, colIndex)}
  disabled={playerSymbol !== null}
>
  {playerSymbol}
</button>
  • playerSymbol์€ ํ˜„์žฌ ์นธ์— ํ‘œ์‹œ๋œ ๊ธฐํ˜ธ (null, 'X', 'O')
  • playerSymbol !== null
    • ์ด๋ฏธ ํด๋ฆญ๋œ ์นธ์ด๋ผ๋ฉด โ†’ disabled=true โ†’ ํด๋ฆญ ๋ถˆ๊ฐ€๋Šฅ
  • playerSymbol === null
    • ์•„์ง ํด๋ฆญ๋˜์ง€ ์•Š์€ ์นธ โ†’ disabled=false โ†’ ํด๋ฆญ ๊ฐ€๋Šฅ

์Šน์ž ์ •ํ•˜๊ธฐ

export const WINNING_COMBINATIONS = [
  [
    { row: 0, column: 0 },
    { row: 0, column: 1 },
    { row: 0, column: 2 },
  ],
  [
    { row: 1, column: 0 },
    { row: 1, column: 1 },
    { row: 1, column: 2 },
  ],
  [
    { row: 2, column: 0 },
    { row: 2, column: 1 },
    { row: 2, column: 2 },
  ],
  [
    { row: 0, column: 0 },
    { row: 1, column: 0 },
    { row: 2, column: 0 },
  ],
  [
    { row: 0, column: 1 },
    { row: 1, column: 1 },
    { row: 2, column: 1 },
  ],
  [
    { row: 0, column: 2 },
    { row: 1, column: 2 },
    { row: 2, column: 2 },
  ],
  [
    { row: 0, column: 0 },
    { row: 1, column: 1 },
    { row: 2, column: 2 },
  ],
  [
    { row: 0, column: 2 },
    { row: 1, column: 1 },
    { row: 2, column: 0 },
  ],
];

์šฐ์Šน ์กฐํ•ฉ ๋ฐฐ์—ด ๊ณต์‹ winning-combination.js ์ƒ์„ฑ ํ‹ฑํƒํ†  ๊ฒŒ์ž„์„ ์ด๊ธฐ๊ธฐ ์œ„ํ•ด์„œ๋Š” ํ•œ ์ค„์„ ์™„์„ฑํ•ด์•ผ ํ•จ ์œ„ ์ฝ”๋“œ๋Š” ๊ฒŒ์ž„์„ ์ด๊ธฐ๊ธฐ ์œ„ํ•œ ์กฐ๊ฑด ์ •๋ณด๋ฅผ ๋‹ด์€ ๋ฐฐ์—ด

๋ฐ˜๋ณต๋ฌธ์„ ํ†ตํ•ด ์œ„ ๋ฐฐ์—ด์„ ์ˆœํšŒํ•˜์—ฌ ์Šน๋ฆฌ์กฐ๊ฑด์— ๋งŒ์กฑํ•˜๋Š”์ง€ ๊ฒ€์‚ฌํ•œ๋‹ค.

let winner;

for (const combination of winningCombinations) {
  const firstSquare = gameBoard[combination[0].row][combination[0].col];
  const secondSquare = gameBoard[combination[1].row][combination[1].col];
  const thirdSquare = gameBoard[combination[2].row][combination[2].col];

  if (
    firstSquare &&
    firstSquare === secondSquare &&
    firstSquare === thirdSquare
  ) {
    winner = firstSquare; // 'X' or 'O'
    break;
  }
}
  • ์šฐ์Šน ์กฐํ•ฉ ๋ฃจํ”„ ์ˆœํšŒ
  • ๊ฐ ์กฐํ•ฉ์—์„œ 3์นธ์˜ ๊ธฐํ˜ธ ์ถ”์ถœ
  • ๋ชจ๋‘ ๋™์ผํ•œ ๊ธฐํ˜ธ('X', 'O')์ด๊ณ  null์ด ์•„๋‹ˆ๋ฉด โ†’ ์Šน๋ฆฌ
  • ํ•ด๋‹น ๊ธฐํ˜ธ๋ฅผ winner ๋ณ€์ˆ˜์— ์ €์žฅ

๊ฒŒ์ž„ ์˜ค๋ฒ„ & ๋ฌด์Šน๋ถ€ ์—ฌ๋ถ€ ํ™•์ธ

GameOver.jsx ์ปดํฌ๋„ŒํŠธ ์ƒ์„ฑ

export default function GameOver({ winner }) {
  return (
    <div id="game-over">
      <h2>Game Over!</h2>
      <p>{winner ? `${winner} won!` : "It's a draw!"}</p>
      <p>
        <button>Rematch!</button>
      </p>
    </div>
  );
}
  • winner๋ฅผ props๋กœ ๋ฐ›์•„์„œ ๋ฉ”์‹œ์ง€๋ฅผ ์กฐ๊ฑด๋ถ€๋กœ ์ถœ๋ ฅ
  • winner๊ฐ€ ์—†์œผ๋ฉด ๋ฌด์Šน๋ถ€ ์ฒ˜๋ฆฌ

app์ปดํฌ๋„ŒํŠธ์— ๋ฌด์Šน๋ฌด ํŒ๋‹จ ๋กœ์ง ์ถ”๊ฐ€

const hasDraw = gameTurns.length === 9 && !winner;

๊ฒŒ์ž„ ํ„ด์ด 9๋ฒˆ ์ง„ํ–‰๋๊ณ  ์Šน์ž๊ฐ€ ์—†์œผ๋ฉด โ†’ ๋ฌด์Šน๋ถ€

{(winner || hasDraw) && <GameOver winner={winner} />}

์Šน์ž ์žˆ๊ฑฐ๋‚˜ ๋ฌด์Šน๋ถ€์ธ ๊ฒฝ์šฐ์—๋งŒ GameOver ์ถœ๋ ฅ

ReMatch ๋ฒ„ํŠผ ํด๋ฆญ ์ฒ˜๋ฆฌ

gameTurns ์ƒํƒœ๋ฅผ ์ดˆ๊ธฐํ™”ํ•˜์—ฌ ๊ฒŒ์ž„์„ ์žฌ์‹œ์ž‘

function handleRestart() {
  setGameTurns([]);
}

GameOver ์ปดํฌ๋„ŒํŠธ์—์„œ ๋ฒ„ํŠผ๊ณผ ์ด๋ฒคํŠธ ์ „๋‹ฌ

export default function GameOver({ winner, onRestart }) {
  return (
    <div id="game-over">
      <h2>Game Over!</h2>
      <p>{winner ? `${winner} won!` : "It's a draw!"}</p>
      <p>
        <button onClick={onRestart}>Rematch!</button>
      </p>
    </div>
  );
}

๋ฒ„๊ทธ ๋ฐœ์ƒ - Rematch ๋ฒ„ํŠผ ๋ˆŒ๋Ÿฌ๋„ ๊ฒŒ์ž„ํŒ ์ดˆ๊ธฐํ™”๋˜์ง€ ์•Š์Œ

  • ๋ฌธ์ œ: initialGameBoard๋Š” ์ฐธ์กฐ ํƒ€์ž… (๋ฐฐ์—ด)
  • ์ˆ˜์ • ์ „: gameBoard๋Š” ํ•ญ์ƒ ๊ฐ™์€ ๋ฉ”๋ชจ๋ฆฌ ์ฃผ์†Œ๋ฅผ ์ฐธ์กฐ
  • ๊ทธ๋ž˜์„œ ๊ฒŒ์ž„ ์ข…๋ฃŒ ํ›„ Rematch๋ฅผ ๋ˆŒ๋Ÿฌ๋„ ๊ฒŒ์ž„ํŒ์ด ์ดˆ๊ธฐํ™”๋˜์ง€ ์•Š์Œ
ย let gameBoard = initialGameBoard; //์ˆ˜์ •์ „
ย let gameBoard = [...initialGameBoard.map((array) => [...array])];//์ˆ˜์ •ํ›„
ย 
  • ๊นŠ์€ ๋ณต์‚ฌ๋กœ ๋ฌธ์ œ ํ•ด๊ฒฐ

์ตœ์ข…์ฝ”๋“œ ์ฐธ๊ณ 

์ƒ๊ฐ๋ณด๋‹ค ๋ถ„๋Ÿ‰์ด ๋งŽ์•„์„œ ์ค‘๊ฐ„์ค‘๊ฐ„ ๋น ์ง„ ๋ถ€๋ถ„์ด ์žˆ์Šต๋‹ˆ๋‹ค. ์ตœ์ข…์ฝ”๋“œ๋Š” ์•„๋ž˜ ์ฃผ์†Œ์—์„œ ํ™•์ธ๋ฐ”๋ž๋‹ˆ๋‹ค. react-guide-2025/04 Essentials Deep Dive/07-tic-tac-toe-starting-project at main ยท sosiluv/react-guide-2025 ยท GitHub

๐Ÿ“‘ Reference



Pagination


ยฉ 2025. All rights reserved.

Powered by Hydejack v9.2.1