1. 기본 설정
택택톡 앱은 크게 게임판 부분과 게임 정보 부분으로 구성된다.
App.js
function App () {
return (
<>
< div className = "game" >
< div className = "game-board" >
</ div >
< div className = "game-info" >
</ div >
</ div >
</>
);
}
export default App ;
App.css
.game {
display : flex ;
flex-direction : row ;
}
.game-info {
margin-left : 20px ;
}
index.css
body {
font : 14px "Century Gothic" , Futura, sans-serif ;
margin : 20px ;
}
ol , ul {
padding-left : 30px ;
}
2. Board, Square 컴포넌트 생성
컴포넌트 폴더를 생성하고 그 안에 Board와 Square 컴포넌트를 생성한다.
Reactjs Code Snippet 확장 프로그램을 설치했다면 단축어로 컴포넌트 코드를 간편하게 생성할 수 있다.
함수형 컴포넌트는 rsc , 클래스형 컴포넌트는 rcc 을 입력하면 된다.
import React , { Component } from 'react' ;
class Board extends Component {
render () {
return (
< div >
</ div >
);
}
}
export default Board ;
import React , { Component } from 'react' ;
class Square extends Component {
render () {
return (
< div >
</ div >
);
}
}
export default Square ;
Square.js는 board를 구성하는 각 칸이고 각 칸은 버튼으로 만든다.
class Square extends Component {
render () {
return (
< button className = 'square' >
Square
</ button >
);
}
}
Board.js에서 Square를 연결하는데 Square 컴포넌트를 바로 연결하지 않고, Square를 리턴하는 함수를 만들어서 그 함수를 호출하도록 한다. 함수는 몇번째 Square인지를 인자로 받는다.
class Board extends Component {
renderSquare ( i ) {
return < Square />
}
render () {
return (
< div >
< div className = "status" > Next Player: X, O </ div >
< div className = "board-row" >
{ this . renderSquare ( 0 ) }
{ this . renderSquare ( 1 ) }
{ this . renderSquare ( 2 ) }
</ div >
< div className = "board-row" >
{ this . renderSquare ( 3 ) }
{ this . renderSquare ( 4 ) }
{ this . renderSquare ( 5 ) }
</ div >
< div className = "board-row" >
{ this . renderSquare ( 6 ) }
{ this . renderSquare ( 7 ) }
{ this . renderSquare ( 8 ) }
</ div >
</ div >
)
}
}
이제 App.js에 Board 컴포넌트를 연결한다.
function App () {
return (
<>
< div className = "game" >
< div className = "game-board" >
< Board />
</ div >
< div className = "game-info" >
game-info
</ div >
</ div >
</>
);
}
Board, Square 스타일링)
각각의 파일에서 css파일을 import한다.
Square.js
.square {
background-color : #fff ;
border : 1px solid #999 ;
font-size : 24px ;
height : 34px ;
margin-right : -1px ;
margin-top : -5px ;
padding : 0 ;
text-align : center ;
width : 34px ;
}
Board.js
.status {
margin-bottom : 10px ;
}
3. 데이터 전달(Props)
board컴포넌트에서 square컴포넌트로 칸 번호를 넘겨준다. props를 렌더링할 때는 JSX에서 {}로 감싸주면 된다.
class Board extends Component {
renderSquare ( i ) {
return < Square value = { i } />
}
class Square extends Component {
render () {
return (
< button className = 'square' >
{ this . props . value }
</ button >
props는 부모 컴포넌트에서만 설정이 가능하며 컴포넌트 자신은 props를 읽기 전용으로만 사용할 수 있다.
4. State 추가
state는 컴포넌트 내부에서 바뀔 수 있는 값이다. state가 변경되면 컴포넌트는 리렌더링되며 state는 컴포넌트 안에서 관리된다.
컴포넌트에 state를 설정할 때는 constructor메서드를 작성하여 설정하며, 클래스형 컴포넌트에서 constructor를 작성할 때는 super(porps)를 호출해야 한다.
class Square extends Component {
constructor ( props ) {
super ( props );
this . state = {
value : null
}
}
render () {
return (
< button className = 'square' onClick = { () => console . log ( 'click' ) } >
{ this . state . value }
</ button >
);
}
}
이렇게 하면 칸의 값은 null이므로 아무것도 안보이게 된다.
칸을 클릭하면 X가 나타나도록 setState를 사용해 state 값을 업데이트한다.
< button className = 'square' onClick = { () => this . setState ({ value : 'X' }) } >
여러 자식으로부터 데이터를 받거나 두 자식이 서로 통신하도록 하려면 부모 컴포넌트에 공유 state를 정의해야 한다. 부모 컴포넌트는 props를 사용해 자식 컴포넌트에 state를 다시 전달할 수 있고, 이는 자식 컴포넌트들이 서로 또는 부모 컴포넌트와 동기화하도록 만든다.
위에서 추가한 state를 부모 컴포넌트인 Board.js로 옮긴다. 9칸의 사각형에 해당하는 길이가 9인 배열을 초기 state로 설정한다.
class Board extends Component {
constructor ( props ) {
super ( props );
this . state = {
s quares : Array ( 9 ). fill ( null )
}
}
renderSquare 메서드는 Squares 배열의 인덱스를 반환하도록 한다.
renderSquare ( i ) {
return < Square value = {this . state . s quares [ i ] } />
}
Square 컴포넌트에서 state를 삭제했으므로 this.state를 this.props로 수정한다.
class Square extends Component {
render () {
return (
< button className = 'square' onClick = { () => this . setState ({ value : 'X' }) } >
{ this . props . value }
</ button >
);
}
}
Board 컴포넌트에 square값을 'X'로 바꾸는 함수를 추가한다.
handleClick ( i ) {
const squares = this . state . squares . slice ();
squares [ i ] = 'X' ;
this . setState ({ squares : squares })
}
스퀘어 값을 반환하는 renderSquare 메서드에서 클릭하면 handleClick 메서드를 호출하도록 한다.
renderSquare ( i ) {
return < Square value = {this . state . squares [ i ] } onClick = { () => this . handleClick ( i ) } />
}
class Square extends Component {
render () {
return (
< button className = 'square' onClick = { () => { this . props . onClick () } } >
{ this . props . value }
</ button >
);
}
}
버튼을 클릭하면 onClick 이벤트 핸들러를 호출하고 이벤트 핸들러는 this.props.onClick()을 호출한다.
Board에서 Square로 onClick={() => this.handleClick(i)}를 전달하기 때문에 Square를 클릭하면 Board의 handleClick(i)를 호출한다.