[React] Lights Out : 셀의 불을 모두 끄자
노란색으로 된 셀이 불이 켜진거라는 가정하에 노란색 셀을 다 진한 회색으로 만들면 이기는 게임 그리고 완성된 결과물(이 게임에 자신이 없어서 끝에 YOU WON이 나오도록 조작함...). 사실 초반에는 설명만으로도 감을 잡을 수 없어서 강사분이 어떻게 짜는 지 보고 코드를 나름대로 짜봤다. 설명을 보기 전에 이런 결과물만 보고 어떤 식으로 component를 나눌지, defaultProps와 state는 어떤 것으로 정할 지 생각해보라고 했는데 이 때부터 감이 잡히지 않았다. 난 셀에다가 켜지고 꺼지는 state를 반영해야 하나 싶었는데 풀이방법은 여러가지가 있겠지만 셀은 담고 있는 보드를 만들 때부터 각각의 셀에 랜덤으로 true와 false의 값을 부여한다. 그리고 이 값으로 게임 시작 때부터 색상을 준다.
// index.js > App.js > Board.js > Cell.js로 Component를 구성
class Board extends Component {
static defaultProps = {
rows: 5,
columns: 5,
lightsOutRandomly: 0.25
};
constructor(props) {
super(props);
this.state = {
board: this.setBoard(),
hasWon: false
};
}
setBoard() {
const rows = Array.from({ length: this.props.rows });
const columns = Array.from({ length: this.props.columns });
const board = rows.map(() =>
columns.map(() => Math.random() < this.props.lightsOutRandomly)
);
return board;
}
그리고 의문이었던 것은 내가 누른 셀 주변의 셀들은 어떻게 동작하게끔 하는 가 싶었는데, 셀 자체 좌표를 부여해서 위치 파악을 할 수 있게 한다. 강사분은 for loop를 써서 row로 잡은 것 안에서 column을 loop하는 걸로 각각의 셀의 좌표를 주었는데, 난 map 함수를 사용하고 여기서 얻을 수 있는 index를 각각의 셀에 적용했다.
createBoard() {
const rows = Array.from({ length: this.props.rows });
const columns = Array.from({ length: this.props.columns });
const lightsBoard = rows.map((_, y) => (
<tr key={y} className="Board-rows">
{columns.map((_, x) => (
<Cell
flipCells={() => this.flipCellsAround(y, x)}
key={`${y}-${x}`}
lightsOut={this.state.board[y][x]}
/>
))}
</tr>
));
return lightsBoard;
}
이렇게 각 셀마다 좌표를 가지고 있으니까 무엇이 바뀌어야 하는지 설정할 수 있다. 이를 바탕으로 가운데 셀을 클릭하면 클릭한 셀 포함, 이 셀의 주변에 있는(위, 아래, 양 옆) 셀들을 계산해서 바뀌는 식으로 동작.
flipCellsAround(y, x) {
const { columns, rows } = this.props;
const board = this.state.board;
const flipCell = function(y, x) {
if (x >= 0 && x < columns && y >= 0 && y < rows) {
board[y][x] = !board[y][x];
}
};
flipCell(y, x);
flipCell(y - 1, x);
flipCell(y, x + 1);
flipCell(y + 1, x);
flipCell(y, x - 1);
const hasWon = board.every(row => row.every(column => !column));
this.setState({ board, hasWon });
}
Lights Out과 YOU WON의 스타일링은 codepen에서 'neon'으로 검색했을 때 나오는 코드를 복사해서 클래스 이름만 바꿔서 사용했다.
쓰고 나니 엄청 복잡한 코드는 아닌데 비슷한 걸 접한 적이 없으면 처음에 어떻게 구현할 지 조금 막막할 수도 있겠단 생각. (제가 그러했습니다...) 그리고 변수의 이름을 쓸 때 딱 맞는 이름을 간단하게 쓰고 싶은데 이름을 어떻게 적어야 할 까 고민하기도 했다. createBoard를 하면서 동시에 setBoard를 할 수 없을까 궁리했는데 결론은 각각 하는 게 낫지 않을까 싶다. 하나의 함수, 하나의 method에 너무 많은 기능을 담으려고 하는 건 좋지 않다고 했으니...
참고 : The Modern React Bootcamp / Udemy
Neon Flux / codepen