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

Positive Ko·2020년 11월 6일
0

React

목록 보기
6/17
post-thumbnail

(1편에서 이어지는 문서입니다..)

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

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


Immutability is Important!!!!!

리액트를 공부하다보면 Immutable data라는 말을 자주 듣는다. 즉 불변하는 데이터를 만들어놓는 것이 중요하다. 일반적으로 데이터 변경에는 두 가지 방법이 있다. 첫 번째는 데이터의 값을 직접 변경하는 것이다. 두 번째는 원하는 변경 값을 가진 새로운 사본으로 데이터를 교체하는 것이다.

만약 원본 데이터를 유지한 채 결과를 이끌어내는 것이 왜 중요할까?
1) Complex Features Become Simple: 이 게임을 할 때, 게임이 완료되면 초기의 값으로 돌아가야 한다. 특정 액션을 실행하고 다시 취소하는 기능은 대부분의 개발에서 필요한 기능일 것이다. 따라서 초기의 데이터를 유지해둔다면 나중에 재사용하는 것이 간단하고 용이하다.
2) Detecting Changes: 변화된 부분을 체크할 때, 이미 수정된 데이터 자체에서 알아내려고 하기 보다는 복제한 데이터를 변화시켜서 기존의 데이터와 비교하는 것이 쉽다.
3) Determining When to Re-Render in React: Immutability의 가장 큰 장점은 React에서 Pure Components를 만드는 데 도움을 준다는 것이다. 앞에서 보았듯이 변화된 부분을 체크하는 것이 쉬워진다면 이를 바탕으로 컴포넌트가 다시 렌더링할지를 결정하는 것이 쉬워진다.

Square를 함수 컴포넌트로 바꾸기

함수 컴포넌트는 클래스 컴포넌트보다 더 간단하게 작성할 수 있다. state 없이 render 함수만 갖는다. React.Component를 확장하는 클래스를 정의하는 대신 props를 입력받아서 렌더링할 대상을 반환하는 함수를 작성할 수 있다.

주석 처리된 클래스 컴포넌트를 펑션 컴포넌트로 바꾸어주었다.

순서 만들기

이제 플레이어가 같이 게임을 할 수 있도록 O와 X가 표시되도록 해줄 차례다.

  • xIsNext 불린값 설정: Board 생성자의 초기 state에 xIsNext라는 초깃값을 설정해주었다. 플레이어가 수를 둘 때마다 xIsNext가 뒤집혀 다음 플레이어가 누군지 결정하고 게임의 state가 저장될 것이다.
  • handleClick 함수 수정: 삼항연산자를 넣어서 xIsNext가 true이면 X가 표시되고 아니라면 O가 표시될 것이다.

승자 결정하기

다음은 승자를 체크하는 calculateWinner() 함수를 최하단에 만들어 주었다.
그 후 Board 클래스의 render를 다음과 같이 변경해주었다.

그리고 한 명이라도 승리하거나 Square가 다 채워지면 Board의 handleClick 함수가 클릭을 무시하도록 변경했다.

시간 여행 추가하기

마지막으로 경기에서 초기 세팅으로 돌아가는 시간 여행을 추가하겠다.
만약 Board의 handleClick()에서 squares 배열을 slice()하지 않고 그대로 사용했다면 초기 세팅으로 돌리기가 어려웠을 것이다. 하지만 slice()를 이용해서 매 동작 이후에 squares 배열의 새로운 복사본을 만들었고 immutable하게 만들었다. 이를 통해 과거의 squares 배열의 모든 버전을 저장했고, history라는 다른 배열에 저장할 수 있다. 어떤 컴포넌트가 history state을 가지고 있는 게 좋을까?

Game 컴포넌트로 state 끌어 올리기

이전 동작에 대한 리스트를 보여주려면 최상위 단계의 Game 컴포넌트가 필요하다. history를 이용해야 하기 때문에 최상위 단계인 Game에 history state를 둔다.

history state를 Game 컴포넌트에 두었기 때문에 자식 Board 컴포넌트에서 squares state를 더이상 사용하지 않아도 된다. Square 컴포넌트에서 Board 컴포넌트로 state를 끌어올렸던 것처럼 이번에는 Board에서 Game으로 state를 끌어올리자. 이를 통해서 Game 컴포넌트는 Board의 데이터를 완벽히 제어할 수 있고 history에 저장된 과거의 차례를 Board가 렌더링할 수 있게 한다.

Game 컴포넌트 생성자에 초기 state 설정

Board 컴포넌트 수정

Game 컴포넌트 render 함수 수정

Game 컴포넌트에 해당 내용을 추가했기 때문에 Board에서는 삭제한다.

handleClick 함수를 Game으로 옮기기

Game 컴포넌트의 state가 Board와 다르기 때문에 수정이 필요하다.

배열 push() 메소드보다 concat()을 사용해서 기존 배열을 immutable하게 유지하자!

render 함수에서 history map하기

틱택토 게임의 이동 정보를 기록했기 때문에 이제 플레이어에게 과거의 이동을 목록으로 표시할 수 있다. 이 때, map을 사용하면 클릭할 때마다 자동으로 리스트가 추가되도록 만들 수 있다.

여기서 경고창이 뜬다.
키를 부여하지 않았다는 내용인데, 키가 있어야 리액트가 어떤 컴포넌트를 업데이트할 지 판단할 수 있다.

이 오류를 해결하는 자세한 방법은 여기를 참고하자


위처럼 동적으로 추가되는 list에 아이디 값을 부여해서 해결할 수 있다!

jumpTo 함수 구현하기

jumpTo 함수를 Game 컴포넌트에 구현해준다. 그 전에 생성자 constructor 안에 stepNumber: 0 이라는 상태를 추가해주고, 아래와 같이 업데이트할 함수를 설정한다.

그 다음으로 Game의 handleClick() 함수에도 변화를 줘야한다.

  • stepNumber: history.length로 업데이트 될 때마다 넘버 추가가 되도록 한다.
  • history: this.state.history에 slice 메소드로 새로운 배열을 복사한다. 또한 this.state.stepNumber + 1 까지 자르도록해서 배열을 업데이트 해준다. (원문: This ensures that if we “go back in time” and then make a new move from that point, we throw away all the “future” history that would now become incorrect.)

후기

리액트 재미난다..! 리액트가 아직 익숙지 않아서 무작정 따라해보자 시작한 게임 메이킹 튜토리얼이었는데 기대 이상으로 흥미롭고 신기했다👍 (리액트가 이렇게 돌아간다구요...?) 추가로 게임 개선 포인트들을 잡아서 개선해보면 좋을 것 같은데 차후에 진행해서 다시 후기를 올리도록 하겠다.. 일단 지금 하고 있는 프로젝트가 마무리된다면 소일거리로 주말에 시간을 내서 진행해보면 좋을 듯!

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

0개의 댓글