React | 틱택토 게임 만들기 (1/2)

Positive Ko·2020년 11월 6일
2

React

목록 보기
5/17
post-thumbnail

오늘은 리액트로 간단한 틱택토 게임을 만들어보려고 한다!

튜토리얼은 리액트 공식 문서를 참고했다.
자세한 내용은 여기에서 찾을 수 있다.


초기 세팅

먼저 CRA로 간단하게 tic-tac-toe 폴더를 만들어주었다.
초기 세팅을 해두고 CSS툴은 공식 문서에서 제공하는 것을 가져왔다.
필요 없는 파일들은 미리 지워두었고 (로고, 기타 js 파일)

먼저 index.js의 코드를 살펴보면 세 가지 리액트 컴포넌트를 확인할 수 있다.

1) Square 2) Board 3) Game이다.


컴포넌트 소개

Squre 컴포넌트와 Board 컴포넌트

Squre 컴포넌트는 button을 렌더링하고 Board 컴포넌트는 사각형 9개를 렌더링한다. 따라서 여기서 부모는 누구일까? 바로 Board이다. 부모 Board 컴포넌트에서 자식 Square 컴포넌트로 'props'을 전달한다.


Game 컴포넌트

게임판을 렌더링하고 나중에 수정할 자리 표시자 값을 가지고 있다. 지금은 상호작용하는 컴포넌트가 없다.




Props를 통해 데이터 전달하기

Board에서 Square 컴포넌트 props 전달

먼저 Board에서 Square 컴포넌트로 데이터를 전달해야 한다.
Square에 value prop을 전달하기 위해 Board의 renderSquare 함수 코드를 수정했다.

그 다음 값을 표시하기 위해 Square의 render함수에서 {this.props.value}로 수정한다.

여기까지 진행한다면 다음과 같이 화면에 숫자가 렌더링 된다.

onClick 속성 넣기

그 다음으로는 사용자와 상호작용할 수 있도록 Square 컴포넌트를 클릭하면 'X'가 나오도록 만들어보자. 먼저 Square 컴포넌트의 render() 함수에서 반환하는 버튼 태그에 onClick 이벤트를 넣어준다. 이렇게 넣어주면 square를 클릭했을 때 경고창이 뜨게 된다.

state 설정하기

다음 단계로는 Square 컴포넌트를 클릭한 것을 '기억하게' 만들어 'X' 표시 되도록 할 것이다. React 컴포넌트는 생성자에 this.state를 설정하는 것으로 state를 가질 수 있다. this.state는 정의된 React 컴포넌트에 대해 비공개로 간주해야 한다. 이제 Square의 현재 값을 this.state에 저장하고 Square를 클릭하는 경우 변경하도록 하겠다.

우선 클래스에 생성자를 추가하여 state를 초기화한다.

그 다음 render함수까지 변경하면 다음과 같이 코드가 정리된다.

변경 사항을 정리하자면,

  • 버튼 태그 안의 this.props.value를 this.state.value로 변경했다.
  • onClick 이벤트 핸들러를 onClick={()=>{this.setState({value: 'X'})}} 으로 변경했다.
    이처럼 Square의 render 함수 내부에서 onClick핸들러를 통해 this.setState를 설정하는 것 만으로도 버튼을 클릭할 때 해당 컴포넌트가 렌더링되도록 할 수 있다!( 신기해👀 !!)

부모의 자식 컴포넌트 설정하기

현재 모든 Square 컴포넌트에서 각각 state를 갖고 있는데, 승자를 확인하기 위해선 9개의 square 값을 한 곳에서 확인할 수 있어야 한다.

어떻게 할 수 있을까? state를 각각의 square가 아니라 부모인 Board에 저장하는 것이다. 그리고 부모인 Board는 자식인 Square에게 prop으로 상태를 전달할 수 있다.

여러 개의 자식 컴포넌트로부터 데이터를 모으거나 두 개 이상의 자식 컴포넌트를 연결하려면 부모 컴포넌트에 공유 state를 정의해야 한다. 그래야 서로 동기화할 수 있다.

state를 부모 컴포넌트로 끌어올리는 것은 React 컴포넌트를 리팩토링할 때 흔히 사용한다. (흔한 리액트 강의를 듣다보면 꼭 나오는 과정이니 반드시 따라해보자!)

Board 컴포넌트에 생성자 추가하기

먼저 Board 컴포넌트에 생성자를 추가하고 9개의 사각형에 해당하는 9개의 null배열을 초기 state로 설정해주었다. 그리고 renderSquare에도 {i}가 아니라 {this.state.squares[i]}로 바꾸어주었다. 정리한 코드는 아래와 같다.

따라서 이제 Board에서 Square로 value와 onClick 두 개의 props를 전달하게 된다.

Square 컴포넌트 변경하기

스스로 상태를 정했던 Square 컴포넌트를 이제 부모인 Board 컴포넌트에서 props를 받아 쓰는 자식 컴포넌트로 바꿔보겠다.

변경해야 할 사항은 다음과 같다.

  • constructor 삭제: 이제 square는 게임의 상태를 유지할 필요가 없기 때문에 생성자는 지운다.
  • render: this.state.value => this.props.value 로 바꾸기
  • render: this.setState() => this.props.onClick()으로 바꾸기

정리하면 다음과 같다.

이 때, Square를 클릭하면 어떻게 될까?
Square의 render() 함수에 정의된 onClick 이벤트를 호출한다. 그 다음 this.props.onClick()을 호출한다. 이 prop은 Board에 있다. 그리고 Board에서 Square로 onClick={() => this.handleClick(i)}를 전달했다. 따라서 결과적으로 사각형을 클릭하면 this.handleClick(i)를 호출한다.

handleClick() 정의하기

다음과 같이 Board 컴포넌트에 handleClick()을 정의해주었다.

Squares를 클릭하면 사각형이 X로 변하는 것은 이전과 같지만, 이제는 state가 개별적인 Square가 아니라 Board에 저장된다. Board 상태가 변할 때 Square 컴포넌트는 자동으로 다시 렌더링 된다. (비로소 Board가 어떤 '승자'를 정할 수 있게 된다)
Square가 더이상 상태를 저장할 수 없게 되면서 Square 컴포넌트는 controlled components가 된다.

그리고 handleClick에서 .slice() 메소드로 기존 배열을 수정하지 않고 squares 배열의 복사본을 생성해서 수정했다. 왜 이렇게 할까?





(다음 편에서 이어진다...)

profile
내 이름 고은정, 은을 180deg 돌려 고긍정 🤭

1개의 댓글

comment-user-thumbnail
2020년 11월 7일

저도 알려주세요~~

답글 달기