간단한 틱택토 게임을 구현하여 리액트의 기능을 자세히 알아봅시다!
리액트 기본 문서에는 기본 개념 설명 외에도 리액트의 기술을 사용하면서 간단한 게임을 구현할 수 있는 튜토리얼 문서가 있습니다!
오늘은 해당 문서를 참고하여 틱택토 게임을 만들면서 리액트의 기능을 더 깊이 있게 이해할 수 있는 시간이 가져보겠습니다.
그런데 틱택토는 어떤 게임일까요?
제가 찾아본 결과 오목과 매우 비슷한 점이 많습니다!
쉽게 말하면 오목이 5줄을 이어야 승리를 한다면 틱택토는 3줄을 이으면 승리를 하는 게임입니다!
해당 튜토리얼이 끝난다면 이런 틱택토 게임이 구현될 겁니다!
이제 어떤 게임인지도 알았으니 처음부터 차근차근 구현해보겠습니다!
일단 기본적인 파일 세팅과 CSS 설정은 공식 튜토리얼 문서를 참고하여서 설정하시면 됩니다!
해당 단계를 완료하였다고 생각하고 바로 시작해보겠습니다.
export default function Square() {
return <button className="square">X</button>;
}
정상적으로 초기 세팅을 완료하였다면 위 코드를 App.js
에 작성하였을 때 아래와 같이 화면에 나타나야 합니다.
틱택토 게임판을 만들려면 3x3 그리드 형태의 보드, 즉 9개의 칸이 필요합니다.
그렇다면 여러 개의 정사각형을 만들려면 어떻게 해야 할까요?
export default function Square() {
return <button className="square">X</button><button className="square">X</button>;
}
이렇게 단순히 button
태그를 연속으로 사용하면 두 개의 정사각형이 나오지 않을까요?
아쉽게도 이렇게 코드를 작성한다면 오류가 발생합니다.
JSX 프래그먼트 사용
여러 JSX 요소를 반환하려면 이 요소들을 감싸는 하나의 요소가 필요합니다.
이를 해결하기 위해 리액트에서는 JSX 프래그먼트(<>와 </>)를 제공합니다.
JSX 프래그먼트를 사용하면 여러 JSX 요소를 묶을 수 있습니다.
리액트에서 컴포넌트는 반드시 하나의 루트 요소를 반환해야 한다는 점을 항상 기억해주셔야 합니다.
export default function Square() {
return (
<>
<button className="square">X</button>
<button className="square">X</button>
</>
);
}
이제 이렇게 두 개의 정사각형이 정상적으로 화면에 보일겁니다!
하지만 이렇게 계속 정사각형을 추가하면 우리가 원하는 3x3 모양이 아닌 한 줄로 이어지는 모양이 될겁니다.
이 문제를 해결하려면 정사각형들을 div
태그로 그룹화하고 약간의 CSS
클래스를 추가해야 합니다.
export default function Square() {
return (
<>
<div className="board-row">
<button className="square">1</button>
<button className="square">2</button>
<button className="square">3</button>
</div>
<div className="board-row">
<button className="square">4</button>
<button className="square">5</button>
<button className="square">6</button>
</div>
<div className="board-row">
<button className="square">7</button>
<button className="square">8</button>
<button className="square">9</button>
</div>
</>
);
}
이제 정상적으로 틱택토 보드 모양이 만들어졌습니다!
컴포넌트의 이름도 현재 Square
이지만 더 직관적으로 알 수 있게 Board
라는 이름으로 변경해 보겠습니다.
export default function Board() {
//...
}
이제 리액트에서 데이터를 컴포넌트 간에 전달하는 방법인 props
를 사용할 차례입니다!
props
는 간단하게 설명하면 컴포넌트 간에 데이터를 전달하는 방법입니다.
이를 이해하는 것은 리액트에서 정말 중요한 개념 중 하나입니다.
function Square() {
return <button className="square">1</button>;
}
export default function Board() {
// ...
}
먼저, Board
컴포넌트에서 Square
컴포넌트를 분리하게 됩니다.
이렇게 하면 복잡한 코드를 간결하게 만들고 재사용성을 높일 수 있습니다.
Square
컴포넌트는 Board
컴포넌트 안에서 여러 번 호출되며, 각 Square
컴포넌트는 게임 보드의 한 칸을 나타냅니다.
현재까지의 전체 코드를 작성하자면 아래와 같습니다!
function Square() {
return <button className="square">1</button>;
}
export default function Board() {
return (
<>
<div className="board-row">
<Square />
<Square />
<Square />
</div>
<div className="board-row">
<Square />
<Square />
<Square />
</div>
<div className="board-row">
<Square />
<Square />
<Square />
</div>
</>
);
}
코드는 전보다 훨씬 깔끔해졌고 정사각형도 정상적으로 나오는 것을 확인할 수 있습니다.
다만 전과 달리 모든 정사각형이 1
을 가지고 있습니다.
이제 props
를 사용하여 Board
컴포넌트에서 Square
컴포넌트로 데이터를 전달하게 할 것입니다!
value
라는 prop
을 전달하면, 각 Square
컴포넌트는 이 값을 받아 출력할 수 있습니다.
function Square({ value }) {
return <button className="square">{value}</button>;
}
value
를 출력하기 위해 JSX 안에서 자바스크립트 변수를 사용하려면 중괄호({}
)를 사용해야 합니다.
예를 들어, <button className="square">{value}</button>
와 같이 작성하면 됩니다.
이렇게 하면, 변수 value
의 값이 출력됩니다.
export default function Board() {
return (
<>
<div className="board-row">
<Square value="1" />
<Square value="2" />
<Square value="3" />
</div>
<div className="board-row">
<Square value="4" />
<Square value="5" />
<Square value="6" />
</div>
<div className="board-row">
<Square value="7" />
<Square value="8" />
<Square value="9" />
</div>
</>
);
}
props
를 사용하여 처음보다 코드의 가독성을 깔끔하게 수정하였습니다!
게시물이 조금 길어질 것 같아서 다음 포스팅에 이어서 소개하는 시간을 가지겠습니다~