React로 로또번호 생성기 만들기
약 9개월 전에 바닐라 자바스크립트로 만든 로또 추첨기 를 리액트로 다시 제작해봤습니다.
손보는 김에 예전엔 모바일 기기에선 공을 세로로 일렬로 표시했으나, 이젠 3열로 표시하게 해뒀습니다.
버리는 공간이 훨씬 적어진 느낌이라 마음에 드네요.
바닐라로 짤 땐 html에 .ball이란 div를 만들고, #button을 클릭하면 document.querySelectorAll(“.ball”)로 DOM에 접근해 내용을 바꾸는 방식으로 작업하는데, 리액트로 짜니 컴포넌트를 만들고, 버튼을 클릭하면 컴포넌트의 상태를 업데이트하는 전혀 다른 방식으로 진행해야 해 난항을 겪었네요.
처음엔 바닐라만큼 직관적이진 않다고 느꼈으나, 한 엘리먼트에 여러 곳에서 접근하다 코드가 꼬이는 등의 불상사는 일어날 일이 없어 보이니 단순한 접근 방식의 차이일 뿐이지 직관성은 뛰어날 수도 있겠다 싶습니다.
import React from "react";
import ReactDOM from "react-dom";
import "./index.css";
const numbers = [...Array(45).keys()];
class LotteryBox extends React.Component {
state = { number: [0, 0, 0, 0, 0, 0, 0] };
randomize = () => {
if (!this.state.effect) {
const numberCopy = numbers.map((x) => x);
const arr = [];
for (let i = 0; i <= 7; i++) {
const random = Math.floor(
Math.random() * (numberCopy.length - 1)
);
arr.push(numberCopy[random] + 1);
numberCopy.splice(random, 1);
}
this.setState({ number: arr, effect: true });
setTimeout(() => {
this.setState({ effect: false });
}, 8000);
}
};
render() {
return (
<React.Fragment>
<h1 id="title">Lotto</h1>
<div id="numbers">
<LotteryItem
index="0"
color="blue"
number={this.state.number[0]}
decrypting={this.state.effect}
/>
<LotteryItem
index="1"
color="blue"
number={this.state.number[1]}
decrypting={this.state.effect}
/>
<LotteryItem
index="2"
color="blue"
number={this.state.number[2]}
decrypting={this.state.effect}
/>
<LotteryItem
index="3"
color="red"
number={this.state.number[3]}
decrypting={this.state.effect}
/>
<LotteryItem
index="4"
color="red"
number={this.state.number[4]}
decrypting={this.state.effect}
/>
<LotteryItem
index="5"
color="grey"
number={this.state.number[5]}
decrypting={this.state.effect}
/>
<div className="plus">+</div>
<LotteryItem
index="6"
color="bonus"
number={this.state.number[6]}
decrypting={this.state.effect}
/>
</div>
<div>
<LotteryButton
decrypting={this.state.effect}
run={this.randomize}
/>
</div>
</React.Fragment>
);
}
}
class LotteryButton extends React.Component {
render() {
return (
<button
id="btn"
className={this.props.decrypting && "hide"}
onClick={this.props.run}
>
추첨!
</button>
);
}
}
class LotteryItem extends React.Component {
constructor(props) {
super(props);
this.state = {
number: "?",
decryptingDone: "",
};
}
decryptEffect() {
this.setState({ decryptingDone: "" });
this.timer = setInterval(() => {
this.randomNumber();
}, 10);
setTimeout(() => {
this.setState({
decryptingDone: "done",
number: this.props.number,
});
clearTimeout(this.timer);
}, 1000 * +this.props.index + 1000);
}
randomNumber() {
this.setState({ number: Math.round(Math.random() * 44) + 1 });
}
componentDidUpdate(nextProps) {
const { decrypting } = this.props;
if (nextProps.decrypting !== decrypting) {
if (decrypting) {
this.decryptEffect();
}
}
}
render() {
return (
<div
className={`ball ${this.props.color} ${this.state.decryptingDone}`}
>
{this.state.number}
</div>
);
}
}
ReactDOM.render(, document.getElementById("root"));
아직 React 초보라, LotteryItem에 index를 일일이 넣어주는 점이며, LotteryBox의 number란 array의 아이템을 LotteryItem에 하나씩 전달해주는 점이 굉장히 우아하지 못한 것 같은데, 첫술에 배를 불리려는 건 과욕이겠지요. 😂