[team-project_종합 게임 사이트 만들기] 오목 게임 만들기 (1)
[ 오늘의 작업 과정 ]
1. 오목판 및 위에 문구 3초 뒤 없어지는 동적인 UI 만들기
2. 오목판에 칸 클릭 시 클릭된 칸 검정색으로 바뀌는 UI 만들기
1. 상단의 글자 3초뒤 없어지는 UI 구현 (Game2.js)
리액트에서 동적인 UI 만드는 3step을 따르면 된다.
먼저 html css 로 미리 UI 디자인을 다 해두고 UI의 현재 상태를 state로 저장해둔 후 state에 따라 UI가 어떻게 보일지 삼항연산자로 작성해줬다.
function Game2() {
let [start, setStart] = useState(true);
return (
<>
<div>
{start == true ? <p>* 흑돌 먼저 시작 !</p> : <p> </p>}
여기서 3초 뒤 없어지는 기능인 타이머 기능이 필요하다. 이건 js로 x초후에 코드를 실행하고 싶을 때 사용했던 setTimeout 함수를 사용하면 된다. html 렌더링 이후에 동작하는 코드는 useEffect안에 써주는 것이 유용하다. 반복연산, 타이머 등등.. 컴포넌트 mount(로드)시 1회 실행만 하면 되므로 , 뒤에 []를 써준다. 버그를 방지하기 위해 useEffect에서 타이머 만들기 전에 기존 타이머를 싹 제거하라고 clearTimeout()도 써줬다.
useEffect(() => {
let a = setTimeout(() => {
setStart(false);
}, 3000);
return () => {
clearTimeout(a);
};
}, []);
2. 오목판에 칸 클릭 시 클릭된 칸 검정색으로 바뀌는 기능 구현 (Game2.js)
[ Trouble-Shooting ]
- 문제 1 : 오목판에 각 칸을 클릭하였을 때 한 칸이 아닌 전체 오목판이 검정색으로 변했던 문제
col라는 변수를 만들어 오목판의 각 행의 수 13개를 배열 형태로 저장해뒀고, col 배열에 map 함수를 이용해 중첩시켜 테이블을 만들고 color 라는 state를 만들어 각 칸을 클릭했을 때 black이라는 클래스명이 추가되어 각 칸이 검정으로 바뀌도록 했지만 적용되지 않았다. 문제의 아래 코드이다.. console로 각 칸의 자리를 출력했을 때는 분명 자리값은 (1,1), (12,3)... 이런식으로 각 칸마다 잘 출력되는데 클릭 시 오목판 전체가 검정색으로 바뀐다.
import { useEffect, useState } from "react";
function Game2() {
... 생략
let [color, setColor] = useState("");
var col = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13];
return (
<>
<div>
... 생략
<table className="tb2">
<tbody>
{col.map(function (i) {
return (
<tr>
{col.map(function (j) {
return (
<td className={color}
onClick={() => setColor("black") // 클릭시 오목판 전체가 검정으로 변함
// console.log(i,j) // 각 칸의 좌표 정상적으로 출력
}
></td>
);
})}
</tr>
);
})}
</tbody>
</table>
</div>
</>
);
}
- 해결 과정
클릭된 칸의 좌표의 상태를 담을 state를 만들어 클릭된 칸의 좌표의 초기 state 값은 빈 배열로 지정하고 클릭된 칸의 위치를 배열 안에 담기로 했다. ([1,1] 이런식으로) 그 후 칸을 클릭을 하면 저장되있던 배열의 첫번째 값과 해당 테이블의 i 값이 같고 , 배열의 두번째 값과 j 값이 같으면 칸 색이 변하도록 했다.
function Game2() {
var col = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13];
var [color, setColor] = useState(''); // 돌의 색상
var [clickedCell, setClickedCell] = useState([]); // 클릭한 칸의 좌표
const handleCellClick = (i,j) => {
setClickedCell([i,j])
setColor('black')
}
return (
<div>
<table className="tb2" onClick={() => {
}}>
<tbody>
{col.map(function (i) {
return (
<tr>
{col.map(function (j) {
return <td className={(clickedCell[0] == i && clickedCell[1] == j) ? color : ''}
onClick={() => handleCellClick(i,j)}></td>;
})}
</tr>
);
})}
</tbody>
</table>
</div>
결과는 아래와 같다. 이전에 클릭한 칸의 색은 유지되어야 하는데 칸을 누를 때마다 이전의 칸의 색은 지워졌다.,ㅠㅜ
이전의 클릭했던 칸의 좌표값을 저장해둬야 한다. 지금 칸을 누를 때마다 칸의 좌표값이 [1,2] 이런식으로 상태가 변경되므로 누른 좌표값이 [1,2], [2,1] ... 이런식으로 누적되도록 state안에 저장해야 한다. 어떻게 해야할까..?
object 형식으로 [ { i: 1, j : 1}, { i : 1, j : 2 } ... ] 이런식으로 누적해서 저장해보기로 했다.
아래 코드와 같이 클릭했을 때 좌표가 object 형식으로 출력되도록 했다.
var [clickedCell, setClickedCell] = useState([]); // 클릭한 칸의 좌표
const handleCellClick = (i,j) => {
setClickedCell([{i,j}])
console.log(clickedCell)
}
하지만 object가 누적되서 저장되야하므로 앞에 이전의 좌표값이 들어있는 clickedCell을 넣어주기로 했다. 그냥
[ clickedCell, { i, j} ] 이런식으로 넣다보면 앞에 clickedCell은 array 형태로 추가되기 때문에 겉의 괄호를 없애주는 스프레드 연산자가 필요하다 . 아래 코드와 같이 짜면 정상적으로 좌표값이 object 형태로 누적되어 추가되는 걸 확인할 수 있었다.
var [clickedCell, setClickedCell] = useState([]); // 클릭한 칸의 좌표
const handleCellClick = (i,j) => {
setClickedCell([...clickedCell, {i,j}])
console.log(clickedCell)
}
다음은 위의 클릭했던 누적된 좌표값과 현재 테이블의 i 값과 j 값이 일치하면 색이 바뀌도록 짜줘야 한다. JS의 some 함수를 사용해서 object 내 원하는 값이 있는지 확인하는 방법을 사용하기로 했다. (참고로 some 함수는 배열 내 원하는 값이 있는지도 확인 가능)
return <td className={ clickedCell.some(e => e.i === i && e.j === j) ? color : '' }
onClick={() => handleCellClick(i,j)}></td>;
})}
위에처럼 짜면 정상적으로 동작한다.