intro
이번 시간에는 리액터 공식문서 초반 페이지에 있는 틱택토 게임을 만들어 보고, 학습한 내용을 정리해보겠습니다.
1. full code
import { useState } from "react";
function Square({ value, onSquareClick }) {
return (
<button className="square" onClick={onSquareClick}>
{value}
</button>
);
}
function 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;
}
export default function Board() {
const [xIsNext, setXIsNext] = useState(true);
const [squares, setSquares] = useState(Array(9).fill(null));
const winner = calculateWinner(squares);
let status;
if (winner) {
status = "Winner: " + winner;
} else {
status = "Next player: " + (xIsNext ? "X" : "O");
}
function handleClick(i) {
if (squares[i] || calculateWinner(squares)) {
// 애초에 square에 값이 있으면 || 승자가 정해졌다면 => 조기리턴
return;
}
const nextSquares = squares.slice();
if (xIsNext) {
nextSquares[i] = "X";
} else {
nextSquares[i] = "O";
}
setSquares(nextSquares);
setXIsNext(!xIsNext);
}
return (
<>
<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 className="status">{status}</div>
</>
);
}
2. 주요 핵심개념
- 컴포넌트 재사용성: Square라는 작은 단위의 컴포넌트를 만들어 Board에서 9번 재사용
- State 끌어올리기: 각 사각형의 상태를 Square가 직접 가지지 않고, 부모 컴포넌트인 Board에서 관리하게 함으로써 데이터의 일관성을 유지했음
- 불변성 지키기: 배열의 값을 직접 수정x, squares.slice()를 사용하여 기존 배열을 직접 수정하지 않고 복사본을 만들어 업데이트함.
3. 코드 분석하기
Square 컴포넌트
- value를 받아 화면에 표시하고, 클릭 시 부모로부터 받은 onSquareClick 함수를 실행
calcualteWinner(승리에 관한 로직)
- 8가지 승리 조합(가로, 세로, 대각선)을 미리 정의해두고, 현재 squares 상태와 비교하여 승자를 판별함.
handleClick (게임 제어 관련 로직)
- 조기리턴: 이미 값이 있거나 승자가 결정되었다면 클릭을 무시
- 상태 업데이트: 현재 턴에 맞춰 배열을 업데이트하고 다음 턴으로 넘김
전체 코드 flow 요약
1. 사용자가 Square를 클릭
2. Board의 handleClick함수가 호출됨
3. squares배열의 복사본을 만들어 해당 인덱스에 x또는 o를 채움
4. setSAquares와 setXIsNext로 상태를 업데이트함
5. 상태가 변했으므로 화면이 다시 그려지고, 승리 여부를 체크
틱택토 만들기를 마치며,,
리액트에서 배열을 복사해서 써야 한다는 개념이 때로는 낯설었지만, 리액트의 렌더링 원리를 이해하는 데 큰 도움이 되었고, 여러가지 로직과 props, state, 변수의 현재 상태 등의 개념을 학습하면서 리액트의 기초에 대해 많이 배워가는 느낌이었습니다.
다음시간에도 리액트 공부로 돌아오겠습니다~
'web > react' 카테고리의 다른 글
| 리액트 Context에 대해 알아보고 다크모드를 만들어보자! (feat: custom hook) (0) | 2026.03.30 |
|---|---|
| 리액트에서 왜 불변성을 지켜야 할까? (0) | 2026.03.13 |
| useRef는 어떻게 데이터를 변화시키지 않을 수 있을까? (0) | 2026.02.27 |
| 리액트 공식문서 과제 오답노트 (0) | 2026.02.19 |
| 리액트 기본 - 공식문서 리딩 (0) | 2026.02.11 |