틱택토(tic-tac-toe)
틱택토 게임은 두 명이 번갈아가며 O와 X를 3x3 판에 써서 같은 글자를 가로, 세로, 혹은 대각선 상에 놓이도록 하는 놀이이다. 이번 글에서는 틱택토 게임을 진행할 수 있게 구현하고, 게임이 끝난 시점에서 승자를 결정하는 것과 다음 플레이어가 누구인지에 대해서도 나타내고자 한다.
자 이제 리액트로 코드를 구현해보도록 하자. 우리는 3개의 컴포넌트를 사용할 것이다 .
1.Game 컴포넌트
2. Board 컴포넌트
3. Square 컴포넌트
class Square extends React.Component {
render(){
return(
)
}
}
class Board extends React.Component {
render(){
return(
)
}
}
class Game extends React.Component {
render(){
return(
)
}
}
ReactDOM.render(
<Game />,
document.querySelector('#root')
)
여기서 이제 3개의 컴포넌트를 어떤 구성으로 짤지 그림으로 간략하게 보여주도록 하겠다.
Game 컴포넌트를 큰 틀로 두고 그 안에 Board 컴포넌트를 두고 Board 컴포넌트 안에 Square 컴포넌트를 두는 구성을 둘 것이다.
첫 번째로 Game 컴포넌트 안에 Board 컴포넌트를 넣고 <div>로 감싸도록 하자.
class Game extends React.Component {
render(){
return(
<div className="game">
<div className="game-board">
<Board />
</div>
</div>
)
}
}
자 그러면 이제 Game 컴포넌트가 실행이 될 때 Board 컴포넌트가 실행이 되는 것을 알 수 있다. 이제 Board 컴포넌트로 가보도록 하자.
Board 컴포넌트의 구성은 보도록 하자
틱택토 게임을 보면 정사작형 모양에 3줄로 채워져있고 9칸으로 나눠져 있는 것을 알 수 있다. 그래서 우리는 <div>엘리먼트로 3줄을 만들고 그 안에 3칸을 채울 수 있도록 로직을 구성해야 한다.
그리고 우리는 총 9칸에서 하나를 선택했을 때 'O' , ' X ' 가 순서대로 나오게하면서 둘 중 하나가 한줄로 채워지면 승자가 정해지는 로직을 포함할 것이다.
class Board extends React.Component {
state = {
squares:Array(9).fill(null),
xIsNext:true
}
handleClick = (i) => {
const squares = [...this.state.squares]
if(squares[i] || calcuateWinner(squares)){
return
}
const xIsNext = this.state.xIsNext
squares[i] = xIsNext ? 'X' : 'O'
this.setState({
...this.state,
squares,
xIsNext:!xIsNext
})
}
renderSquare = (i) => {
return(
<Square
value={this.state.squares[i]}
onClick={()=>{this.handleClick(i)}}
/>
)
}
render(){
const {renderSquare,state:{squares}} = this
return(
<div>
<div className="status">{status}</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>
)
}
}
Board Component 상태값으로 null값으로 채워진 배열을 9개 생성했다. 그리고 xIsNext 는 true 값을 기본값으로 두었다.
state = {
squares:Array(9).fill(null),
xIsNext:true
}
3줄과 9개의 칸을 구성하고 이벤트 함수를 넣기 위해 renderSquare( ) 함수를 따로 만들어서 사용하도록 했다. renderSquare 함수에는 상태값에서 { this.state.squares[i] }로 배열을 가져와서 value라는 변수명으로 Square 컴포넌트에 props로 보내줬다. 추가로 handleClick이라는 함수또한 onClick이라는 변수명으로 Square 컴포넌트에 보내줬다.
renderSquare = (i) => {
return(
<Square
value={this.state.squares[i]}
onClick={()=>{this.handleClick(i)}}
/>
)
}
Square 컴포넌트에 보낸 handleClick 함수에 대해서도 알아보도록 하자 . handleClick 함수의 매개변수를 i 로 두고 i를 통해서 9개의 배열의 인덱스 값을 나타내도록 했다.
그 다음 깊은복사 스프레드 연산자를 통해서 상태값에 있는 squares 값을 새로운 변수명인 squares에 넣었다.
그 다음 xIsNext도 상태값에서 가져오도록 하자 => this.state.xIsNext
이 이벤트 함수를 넣은 이유가 플레이어가 9칸 중 하나를 눌렀을 때 ' X '가 뜨게 하고 다음 순서로 다른 플레이어가 다른 칸을 클릭했을 때 ' O ' 가 뜨게 하고, 이미 값이 나온 칸에는 더이상 선택이 되지 않도록 기능을 넣을려고 한다.
그걸 위한 코드가 아래와 같다.
handleClick = (i) => {
const squares = [...this.state.squares]
if(squares[i] || calcuateWinner(squares)){
return
}
const xIsNext = this.state.xIsNext
squares[i] = xIsNext ? 'X' : 'O'
this.setState({
...this.state,
squares,
xIsNext:!xIsNext
})
}
상태값을 가져왔을 때 이 값을 변경하기 위해서는 setState() 문법을 사용해야 한다. 그 안에 깊은 복사로 state 값을 가져오고 sqaures를 가져왔으며
xIsNext : !xIsNext로 true와 false를 계속 반복하며 왔다갔다하게 설정해놨다. => 'X' 와 'O'을 반복적으로 실행하기 위해
자 이제 renderSquare 함수를 통해서 <Square /> 컴포넌트에 props로 던져주고 받는 작업을 해보자
class Square extends React.Component {
render(){
return(
<button className="square"
onClick={this.props.onClick}
>
{this.props.value}
</button>
)
}
}
자 이제 승자를 정하는 함수를 만들어 보자.
calcuateWinner 함수를 만들어서 승자 공식을 만들어보자.
const calcuateWinner = (squares) => {
const line = [
[0,1,2],
[3,4,5],
[6,7,8],
[0,4,8],
[2,4,6],
[0,3,6],
[1,4,7],
[2,5,8]
]
for(let i=0; i<line.length; i++){
const [a,b,c] = line[i]
if(squares[a] != null && squares[a] == squares[b] && squares[a] == squares[c]){
return squares[a]
}
}
return null
}
line 배열을 만들어서 승자가 정해지는 경우의 수를 배열로 만들었다.
그리고 for문을 돌려서 0 부터 8까지 경우의 수를 돌려서 하나의 경우가 존재할 경우 승자를 정하는 공식이다.
이 함수를 Board 컴포넌트에 있는 handleClick 함수안에다가 넣어서 if문으로 calcuateWinner가 null 값이 아닐 경우 바로 코드가 종료됨으로써 승자가 결정이 될 수 있게 설정을 해줬다.
handleClick = (i) => {
const squares = [...this.state.squares]
if(squares[i] || calcuateWinner(squares)){
return
}
마지막으로 승자가 결정이 날 경우 그 결과화면을 브라우저에 렌더링하는 작업을 해보자.
Board 컴포넌트에 render안에다가 삼항연산자로 설정한 값들을 넣은 코드이다
render(){
const {renderSquare,state:{squares}} = this
const winner = calcuateWinner(squares)
const status = winner != null ? `Winner : ${winner}` : `Next Player : ${this.state.xIsNext ? 'X' : 'O'}`
return(
<div>
<div className="status">{status}</div>
아래의 사진이 결과로 나온 화면의 렌더링된 모습이다.
'React' 카테고리의 다른 글
React의 함수형 Component ( feat. Hooks ) (0) | 2022.04.25 |
---|---|
React 댓글 구현하기 (0) | 2022.04.24 |
Class와 기본 문법 (0) | 2022.04.22 |
Webpack 소개, 설치법 (0) | 2022.04.21 |
리스트와 key (0) | 2022.04.21 |