250x250
aaa222
a22aa22
aaa222
전체 방문자
오늘
어제
  • 분류 전체보기 (37)
    • AWS (1)
      • AWS (1)
    • HTML (6)
    • CSS (2)
    • Javascript (3)
    • node.js (19)
      • express (2)
      • node (4)
      • mysql (3)
      • JWT (1)
      • AJAX (1)
      • react (6)
      • redux (1)
      • next (1)
    • windows powershell (2)
      • wsl (1)
      • Ubuntu (0)
    • mac (2)
    • git github (2)
    • Cryptocurrencey (0)

블로그 메뉴

  • 홈
  • 태그
  • 방명록

공지사항

인기 글

태그

  • 웹
  • 코드
  • node.js
  • CODE
  • NVM
  • REQ
  • npm
  • 코딩
  • Express
  • 노드
  • CSS
  • nodejs
  • res
  • JS
  • node
  • HTML
  • 개발자
  • JavaScript
  • VSCode
  • 웹개발

최근 댓글

최근 글

티스토리

hELLO · Designed By 정상우.
aaa222

a22aa22

React 틱택토
node.js/react

React 틱택토

2022. 4. 25. 09:09
728x90

틱택토 게임은 두 명이 번갈아가며 O와 X를 3×3 판에 써서 같은 글자를 가로, 세로,

혹은 대각선 상에 놓이도록 하는 놀이이다.

이 게임을 구현하기위한 단계를 하나하나 작성해서 써볼 예정

 

 

 

 

 

컴포넌트

Game : Board, Square컴포넌트의 부모컴포넌트

Board : Square컴포넌트에서 버튼엘리먼트를 만들었다면 9개의 버튼을 만들어주고 이벤트를 주기 위한 컴포넌트 

Square : 버튼 엘리먼트를 만드는 컴포넌트

 

       class Square extends React.Component{
            render(){
                return(
                    <button>
                        버튼
                    </button>
                )
            }    
        }
        class Board extends React.Component{
            render(){
                return(
                        <div></div>
                )
            }    
        }
        class Game extends React.Component{
            render(){
                return(
                    <div>
                        <Square />    
                    </div>
                )
            }    
        }
Game 컴포넌트에 Square 컴포넌트에 button을 가져왔다.
Document
​이 버튼을 9개를 만들어주기위해
Square 컴포넌트에 버튼엘리먼트를 만들고 만든 버튼을 Board에 <div></div> 3개씩 만들어 3x3을 구현했다.
Board에서 만든 button 엘리먼트들을 Game 컴포넌트에 불러왔다

 

class Square extends React.Component{
    render(){
        return(
            <button>
                버튼
            </button>
        )
    }    
}
class Board extends React.Component{
    render(){
        return(
            <div>
                <div></div>
                <div>
                    <Square /> 
                    <Square /> 
                    <Square /> 
                </div>
                <div>
                    <Square /> 
                    <Square /> 
                    <Square /> 
                </div>
                <div>
                    <Square /> 
                    <Square /> 
                    <Square /> 
                </div>    
            </div>
        )
    }    
}
class Game extends React.Component{
    render(){
        return(
            <div>
                <Board />    
            </div>
        )
    }    
}

결과

해석

Game 컴포넌트에서 Board컴포넌트를 가져오고 Board컴포넌트에서 Square 컴포넌트를 가져오는 식으로 썼으며
이렇게 만들경우 Square컴포넌트에 버튼엘리먼트를 Board에 가져와
Board에 가져온 버튼을 Game로 브라우저에 랜더시키는 방식이다.

 

그후

ReactDOM.render(
<Game/>,document.querySelector('#root')
)
ReactDOM.render을 통해 브라우저에 랜더를 위해 작성해야하며 <Game/>컴포넌트를 불러와
document.querySelector를 이용해 <div id="root"> 를 '#root'로 내용물들을 보내주며 브라우저에 랜더시키는 형식이다.
Square 컴포넌트에 button 엘리먼트를 생성하고  
그 버튼엘리먼트를 board에 각각 3개씩 3줄로 총 9개를 만들었다.

 

 위에 처럼 컴포넌트를 불러오거나 props를 쓰기에는 너무 많이 작성해야하기때문에

class안에변수를 만들어 renderSquare함수를 따로 뺴서  <Square /> 를 넣어줫다

renderSquare = () => {
    return <Square />
}
해석
Square컴포넌트를 직접호출을 하는것보다 함수를 이용해 호출해주는것이 '효율적'이라
renderSquare 함수에 리턴값을 Square Component 에 내용인 button을 반복해 9개의 {this.renderSquare()}를 작성해 줬다.

왜 효율적인가 

<Square/> 를 9개를 작성하고 <Square value={this.props.value}}해줄때 넘길게 너무 많아서 하나하나 작성할때 오래걸릴뿐 아니라 props로 가져오기때문에 너무 길게 복잡하게 작성할수있기때문이다.

 

 

그후

 <div>
    <Square /> 
    <Square /> 
    <Square /> 
</div>

에서

<div>
    {this.renderSquare()}
    {this.renderSquare()}
    {this.renderSquare()}
</div>

로 변경해줫다.

 

this.renderSquare는

renderSquare 를 this 가져오겠다. 하는뜻이고

 

renderSquare 함수를 작성해준다

renderSquare = () =>{
         return <Square />
}

해석

renderSquare 라는 화살표함수를 ()=> {} 화살표 함수를
return을 통해 Square 컴포넌트를 넣어주며 버튼을 랜더시켜주었다.

그후 

 

함수의 내용을 value로 채우기위해 

<Square value={i} />

를 추가해주고

 

Square 컴포넌트에 

class Square extends React.Component {
    render() {
        return (
        <button>
            {this.props.value}
        </button>
        );
    }
}

버튼이라고 써있던곳에 {this.porps.value}를 넣어줫다

 

해석

Board Compenent


renderSquare = i => {
    return <Square value={i} />
}

renderSquare함수선언 = (매개변수 i 값) =>{
return을 통해 (<Square 컴포넌트의  props로 value의 값을 = { i }로 넘겨주겠다. />
 
 
 즉 Square 컴포넌트의 value를 props를 줘서 
 <button> {this.props.value} </button>
 을 이용해 value값인 i를 props로 가져오겠다.

결과

0~8 9개의 버튼


이제 누르면 내용이 바뀌게 만들어야한다.

 

Board 컴포넌트에 state(상태)를 작성해주면된다.

state = {
      squares:Array(9).fill(null),
      }

버튼 클릭시 값이 바뀌게 Array 9 개  fill로 null값을 채웠다 

 

Array(9).fill(null), 이 코드의 형태는 

[null,null,null,null,null,null,null,null,null]로 나온다

state(상태) 에 squares라는 배열안에 null 이라는값을 채웠고 null의 값에 0~8이라는 9개의 버튼의 값을 채웟다.

 

이 null 값에 O와 X 를 채워야한다.

즉

<div>
    {this.renderSquare(0)}
    {this.renderSquare(1)}
    {this.renderSquare(2)}
</div>
<div>
    {this.renderSquare(3)}
    {this.renderSquare(4)}
    {this.renderSquare(5)}
</div>
<div>
    {this.renderSquare(6)}
    {this.renderSquare(7)}
    {this.renderSquare(8)}
</div>

이 인덱스 값에 

  0     1   2     3   4     5    6   7    8 
[null,null,null,null,null,null,null,null,null]

을 채워 버튼에 넣을 값을을 배열에 담아두고 쓰기때문이다.

 

이 값을 채우기위해 이벤트를 넣어주고 값을 채워야한다.

handleClick = i =>{
        console.log(this.state.squares)
}

renderSquare = i => {
    return <Square onClick={this.handleClick(i)}
    value={this.state.squares[i]} />
}

renderSquare의 매개변수인 I를 handleClick매개변수인 i 로 전달해야한다.

renderSquare 함수에 onClick={this.handleClick(i)} 를 넣어주면 handleClick을 불러올수있다.

handleClick의 역할은 클릭했을때에 이벤트를 걸어줄수있는 함수를 선언해주는 역할이다.

 

즉 클릭을 했을때 실행을 할수 있는 함수를 만든것이다.

 

하지만 랜더과정에서 바로 실행이되어 진행할수없어 onClick에 함수를 더 감싸줘야한다

 

왜? onClick={this.handleClick(i)에서 함수를 선언 한 상태이기 때문에 

실행할수있게끔 해줘야한다 

handleClick = i =>{
        console.log(this.state.squares)
}

renderSquare = i => {
    return <Square onClick={() => {this.handleClick(i)}
    value={this.state.squares[i]} />
}

그후 onClick를 실행시키기위해

class Square extends React.Component {
    render() {
        return (
        <button onClick={this.props.onClick}>
            {this.props.value}
        </button>
        );
    }
}

button 엘리먼트에 onClick 를 넣어준다 

button을 눌렀을때 onClick 를 실행시켜 

handleClick 함수를 실행시켜 squares배열의 null값을 X 로채워준다.

onClick={this.handleClick(i)}

button을 클릭했을때 onClick 이벤트를 실행시켜 X와 O를 나타내게 해야한다

 

 

state = {
            // squares:[null,null,null,null,null,null,null,null,null]
            squares:Array(9).fill(null),//[null,]

            xIsNext: true,
        }
        handleClick = (i) => {
            const squares = [...this.state.squares]
        squares[i] = this.state.xIsNext ? 'X' : 'O'
        this.setState({
            squares: squares,
            xIsNext: !this.state.xIsNext,

        })​
xIsNext 는 boolean 불리언 값이므로 참과 거짓 을 가지고있는 데이터타입이니 X 와 O를
true일때 'X' false일때 O 값으로해준다.

Board 컴포넌트에  squares[i] = this.state.xIsNext ? 'X' : 'O'
state(상태)값의 xIsNext(boolean)의 value값이 true일때 'X' false일때 O 값으로
Square 컴포넌트의 button 에 렌더시킬 X 와 O를 번갈아가면서 나타내게 해준다
즉
if(xIsNext) {
         true 'X'
} else {
        false 'O'
}

xIsNext ? 'X' : 'O'​


squares의 value가 9개의 배열인 null값을 const squares = [...this.state.squares] Squares 컴포넌트안에 변수를 squares로 주고 배열인 state의 ...this로 state의 전체값을 불러와 squares값을 변수에 담는다. 담은 변수를 squares[i]9개의 배열을 클릭했을때 true일때 X false일때 O로 만들어줬다.

xIsNext: !this.state.xIsNext,​
xIsNext의 값을 !(주로 false)로 주어 원래 X의 값을 만들어주는것을 O로 만들어주어 X와 O를 번갈아 가게 만들어주었다.
한번이라도 승리하는 조건이 squares에 있으면 Board 컴포넌트에 handleClick 가 실행되지않도록 해주었다.
승자를 결정하기위해 만들어야할 코드

const 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
}​

이 함수는 클릭할때 실행 시켜주는 함수이고 컴포넌트안에 들어가있지않다.
for  (let i=0; i<lines.length; i++)
반복문에 i는 0부터 lines.length만큼  i++로  즉 8번 반복하는데
반복할때마다 

[0, 1, 2],
[3, 4, 5],
[6, 7, 8],
[0, 3, 6],
[1, 4, 7],
[2, 5, 8],
[0, 4, 8],
[2, 4, 6],

만큼 반복한다.

 const [a,b,c] = lines[i]


어떻게?  lines[i]만큼 반복 [0,1,2] 즉 a:0 ,b:1 ,c:2
를 8번 반복하여 winner를 찾아내고 a,b,c는 squares[b]여도되고 [c]여도 어차피 내용은 똑같으니 결과는 같다.

if(squares[a] && squares[a] === squares[b] && squares[a] === squares[c]){
  return squares[a]
}

 &&을 통해 a,b,c가 모두 true인경우 게임을 끝낸다는뜻이다.

이 조건을 return을 통해 Winner를 정하거나 만약 승자가없을때

 return null

을 통해 승자가 없다는것을 작성했다.

그후 작성한 calculateWinner는 선언을 한 상태기 때문에 랜더가 되지 않으므로

calculateWinner 함수를 호출해야 한다 호출한 함수는

 

const winner = calculateWinner(squares)

calculateWinner함수를 가져와 winner 이라는 변수로 담아와 

 

if( calculateWinner(squares) || squares[i]){
	return
}

둘중 하나가 true인 경우 게임을 종료시키는 조건문을 작성했고 

 

그후 승자와 패자의 표시 무승부 를 랜더시킬 코드를 작성해야한다

 

let status = winner
	? `Winner: ${winner}` : `Next player : ${ this.state.xIsNext
   		? 'X' : 'O'}`

Next player 에 X 와 O 를나타내 순서를 만들어주고 winner에 X 와 O의 승자를 만들어준다.

 

그후 status라는 변수에 담아두어

 

<div className="status">{status}</div>

로 무승부와 승자 를 나타내고 다음 차례를 브라우저에 출력시킬수있게 작성했다.

 

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script crossorigin src="https://unpkg.com/react@17/umd/react.development.js"></script> <!-- 객체 -->
    <script crossorigin src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"></script> <!-- 객체 -->
    <script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
    <style>
        body {
  font: 14px "Century Gothic", Futura, sans-serif;
  margin: 20px;
}

ol, ul {
  padding-left: 30px;
}

.board-row:after {
  clear: both;
  content: "";
  display: table;
}

.status {
  margin-bottom: 10px;
}

.square {
  background: #fff;
  border: 1px solid #999;
  float: left;
  font-size: 24px;
  font-weight: bold;
  line-height: 34px;
  height: 34px;
  margin-right: -1px;
  margin-top: -1px;
  padding: 0;
  text-align: center;
  width: 34px;
}

.square:focus {
  outline: none;
}

.kbd-navigation .square:focus {
  background: #ddd;
}

.game {
  display: flex;
  flex-direction: row;
}

.game-info {
  margin-left: 20px;
}

    </style>
</head>
<body>
    <div id="root"></div>
    <script type="text/babel">
        const 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
        }

        class Square extends React.Component{
          render(){
            return(
              <button className="square" 
              onClick={ ()=>{ this.props.onClick()}}>
                {this.props.value}  
              </button>
            )
          }
        } 

        class Board extends React.Component{
          
            state = {
              squares:Array(9).fill(null),
              xIsNext:true,
            }
            handleClick = (i) => {
              const squares = [ ...this.state.squares]

              if( calculateWinner(squares) || squares[i]){
                return
              }
              squares[i] = this.state.xIsNext ? 'X' : 'O'


              this.setState({
                squares,
                xIsNext:!this.state.xIsNext
              })


            }
            renderSquare = (i) => {
              return <Square onClick={ ()=> { this.handleClick(i)}}
                value={this.state.squares[i]}/>
            }
          render (){
            const { renderSquare,state:{squares}} = this

            const winner = calculateWinner(squares)
            let status = winner
             ? `Winner: ${winner}` : `Next player : ${ this.state.xIsNext
               ? 'X' : 'O'}`
            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>  
            )
          }
        }

        class Game extends React.Component{
          render(){
            return(
              <div className="game">
                <div className="game-board">
                    <Board/>
                </div>
              </div>
            )
          }
        }

        ReactDOM.render(
            <Game />,
            document.querySelector('#root')
        )
    </script>
</body>
</html>

 

728x90

'node.js > react' 카테고리의 다른 글

React-Router_DOM(작성중)  (0) 2022.05.02
react 커스텀 훅  (0) 2022.04.26
React 댓글기능(작성중)  (0) 2022.04.25
[React] Component, props, State  (0) 2022.04.13
React  (0) 2022.04.12
    'node.js/react' 카테고리의 다른 글
    • react 커스텀 훅
    • React 댓글기능(작성중)
    • [React] Component, props, State
    • React
    aaa222
    aaa222

    티스토리툴바