본문 바로가기

Front End/React

[React Exam 37] 로또 번호 자동 생성기 만들기

로또 전호를 자동생성하는 함수를 하나 만들어 보자.

 

LottoGenerator.js 파일을 만듭니다.

let lottoGenerator = () =>{
    let set = new Set();
    while(set.size<6)set.add(parseInt(Math.random()*45)+1); // 중복되지 않는 5개 숫자 만들기
    let ar = [...set]; // 배열로 복사
    ar = ar.sort((a,b)=>a-b); // 숫자 정렬
    return ar; // 배열 리턴
}
export default lottoGenerator;

 

Set객체는 중복을 허용하지 않는 객체입니다.

Math.random()*45)+1 : 1~45까지의 정수 난수 생성

 

while(set.size<6)set.add(parseInt(Math.random()*45)+1); 

내용의 개수가 6개가 될때 까지 난수로 채워주면 중복되지 않는 숫자 6개가 생성이 됩니다.

 

Lotto.js 파일을 만듭니다.

import lottoGenerator from './LottoGenerator';
export const Lotto1 = () =>{
     return lottoGenerator().map((n) => `${n}`.padStart(2, 0) + '  ');
}

 

문자열.padStart(길이, 앞에 채울 문자);

문자열.padEnd(길이, 뒤에 채울 문자);

숫자를 칸을 맞추기 위해 2자리 숫자로 만듭니다. 두자리 숫자가 아닐경우 앞자리 빈칸을 0으로 채웁니다.

 

App.js 파일을 만듭니다.

import { Lotto1 } from "./Lotto";

const App = ()=>{
    return <Lotto1/>
}
export default App;

 

index.js 파일을 만듭니다.

import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';

const root = ReactDOM.createRoot(document.querySelector("#app"));
root.render(
    <React.StrictMode>
        <App/>
    </React.StrictMode>
);    

 

index.html 파일을 만듭니다.

<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <div id="app"></div>
</body>
</html>

 

실행 결과입니다.

 

다음의 압축 파일을 풀어 이미지 파일을 public/images/numbers 폴더에 넣습니다.

직접 이미지 태그를 사용해서 넣으려면 public 폴더에 위치해야 합니다.

numbers.zip
0.50MB

 

 

다음 6개의 파일은 src/ images/numbers 폴더에 넣습니다.

src의 css파일에서 url()로 추가하거나 import 명령으로 이미지를 추가하려면  src  폴더에 위치해야 합니다.

ballh.jpg
0.08MB
ballv.jpg
0.08MB
lotto_ball_horz.jpg
0.14MB
lotto_ball_vert.jpg
0.14MB
img_gnb_game1.jpg
0.00MB
logo-header.png
0.01MB

 

 

조금 더 이쁘게 만들어 보도록 하겠습니다.

 

Lotto.js 파일을 수정합니다.

import lottoGenerator from './LottoGenerator';

export const Lotto1 = () => {
    return lottoGenerator().map((n) => `${n}`.padStart(2, 0) + ' ');
}

const Number = ({ number }) => {
    let num = `${number}`.padStart(2, 0);
    let src = `./images/numbers/ball_${num}.png`;
    return <img src={src} alt={num} style={{ margin: "2px" }} />
}

export const Lotto2 = () => {
    return lottoGenerator().map((n) => <Number number={n} key={n} />);
}

 

번호에 해당하는 이미지를 불러서 출력하는 Number라는 컴포넌트를 만들어 출력했습니다.

 

App.js 파일을  수정합니다.

import { Lotto1, Lotto2 } from "./Lotto";
import logo1 from './images/numbers/logo-header.png'
import logo2 from './images/numbers/img_gnb_game1.jpg'

const App = ()=>{
    return(
        <>
        <img src={logo1} alt={logo1}/>
        <img src={logo2} alt={logo2}/>
        <br/>
        <Lotto1/>
        <br/>
        <Lotto2/>
        </>
    )
}
export default App;

 

실행 결과입니다.

 

이미지가 변경되다가 지정 시간이 되면 멈추는 애니메이션을 추가해 보겠습니다.

 

Lotto.js 파일을 수정합니다.

import lottoGenerator from './LottoGenerator';

export const Lotto1 = () => {
    return lottoGenerator().map((n) => `${n}`.padStart(2, 0) + ' ');
}

const Number = ({ number }) => {
    let num = `${number}`.padStart(2, 0);
    let src = `./images/numbers/ball_${num}.png`;
    return <img src={src} alt={num} style={{ margin: "2px" }} />
}

export const Lotto2 = () => {
    return lottoGenerator().map((n) => <Number number={nkey={n} />);
}

// 번호 이미지가 계속 바뀌다가 지정 번호를 이미지로 출력해주는 컴포넌트
const Number2 = ({number, time }) => {
    const [idx, setIdx] = useState(1);
    const [src, setSrc] = useState(`./images/numbers/ball_01.png`);
    const interval = useRef(1);
    useEffect(() => {
        // 0.1초 간격으로 01~45번 이미지로 변경됩니다.
        interval.current = setInterval(() => {
            setIdx((idx+1)%45+1);
            setSrc( `./images/numbers/ball_` +  `${idx}`.padStart(2, 0) + `.png`);
        }, 100);
        // time 속성 값이 초 단위 입니다. 지정 시간 후 타이머를 종료하고 지정번호로 바꿉니다.
        setTimeout(() => {
            clearInterval(interval.current);
            setSrc( `./images/numbers/ball_` +  `${number}`.padStart(2, 0) + `.png`);
        }, time * 1000)
        return () => {
            clearInterval(interval.current);
        };
    }, [idx, src, number, time]);
    return <img src={src} alt={src} style={{ margin: "2px" }} />
}

export const Lotto3 = () => {
    return lottoGenerator().map((n, index) => <Number2 number={n} time={index+1} key={n}/>);
}

 

번호에 해당하는 이미지를 불러서 출력하는 Number라는 컴포넌트를 만들어 출력했습니다.

 

App.js 파일을  수정합니다.

import { Lotto1, Lotto2 } from "./Lotto";
import logo1 from './images/numbers/logo-header.png'
import logo2 from './images/numbers/img_gnb_game1.jpg'

const App = ()=>{
    return(
        <>
        <img src={logo1} alt={logo1}/>
        <img src={logo2} alt={logo2}/>
        <br/>
        <Lotto1/>
        <br/>
        <Lotto2/>
        <br/>
        <Lotto3/>
        </>
    )
}
export default App;

 

실행 결과입니다.

이미지가 바뀌다가 1초 간격으로 하나씩 멈춰집니다.

 

다음은 스크롤을 해보도록 하겠습니다.

 

표시될 크기보다 커다란 배경이미지의 top/left 값을 변경해서 일부분 만 보이게 하도록하면 스크롤 기능을 구현 할 수 있습니다.

 

ScrollNumber.css 파일을 다음과 같이 만듭니다.

.numbers {
    width: 52px;
    height: 52px;
    margin: 2px;
    background-repeat: no-repeat;
    display: inline-block;
}
.numbersh {
    background-image:  url("./images/numbers/ballh.jpg");
}
.numbersv {
    background-image:  url("./images/numbers/ballv.jpg");
}

.lottos {
    width: 80px;
    height: 80px;
    margin: 2px;
    background-repeat: no-repeat;
    display: inline-block;
}
.lottosh {
    background-image:  url("./images/numbers/lotto_ball_horz.jpg");
}
.lottosv {
    background-image:  url("./images/numbers/lotto_ball_vert.jpg");
}

 

 

Lotto.js 파일을 수정합니다.

import { useEffect, useRef, useState } from 'react';
import lottoGenerator from './LottoGenerator';
import './ScrollNumber.css'

export const Lotto1 = () => {
    return lottoGenerator().map((n) => `${n}`.padStart(2, 0) + ' ');
}

// 번호를 이미지로 출력해주는 컴포넌트
const Number = ({ number }) => {
    let num = `${number}`.padStart(2, 0);
    let src = `./images/numbers/ball_${num}.png`;
    return <img src={src} alt={num} style={{ margin: "2px" }} />
}
export const Lotto2 = () => {
    return lottoGenerator().map((n) => <Number number={n} key={n}/>);
}

// 번호 이미지가 계속 바뀌다가 지정 번호를 이미지로 출력해주는 컴포넌트
const Number2 = ({number, time }) => {
    const [idx, setIdx] = useState(1);
    const [src, setSrc] = useState(`./images/numbers/ball_01.png`);
    const interval = useRef(1);
    useEffect(() => {
        // 0.1초 간격으로 01~45번 이미지로 변경됩니다.
        interval.current = setInterval(() => {
            setIdx((idx+1)%45+1);
            setSrc( `./images/numbers/ball_` +  `${idx}`.padStart(2, 0) + `.png`);
        }, 100);
        // time 속성 값이 초 단위 입니다. 지정 시간 후 타이머를 종료하고 지정번호로 바꿉니다.
        setTimeout(() => {
            clearInterval(interval.current);
            setSrc( `./images/numbers/ball_` +  `${number}`.padStart(2, 0) + `.png`);
        }, time * 1000)
        return () => {
            clearInterval(interval.current);
        };
    }, [idx, src, number, time]);
    return <img src={src} alt={src} style={{ margin: "2px" }} />
}

export const Lotto3 = () => {
    return lottoGenerator().map((n, index) => <Number2 number={n} time={index+1} key={n}/>);
}

// 위로 스크롤됩니다. 이미지크기가 52px이므로 52를 곱해서 스크롤을 처리했습니다.
const ScrollNumberV = ({time=1, number=1}) => {
    const [top, setTop] = useState(-1);
    const interval = useRef(1);
    let style = {backgroundPosition: `0px ${-1*top}px` }
    useEffect(() => {
        interval.current = setInterval(() => {
            setTop(top + 10);
            if (top >= 52*45) { // 이미지 끝에 도달하면
                setTop(0);
            }
        }, 20);
        setTimeout(()=>{
            clearInterval(interval.current);
            setTop((number-1)*52)
        },  time*1000);

        return () => {
            clearInterval(interval.current);
        }

    }, [top, number, time]);
    return <div className="numbers numbersv" style={style}></div>
}

export const Lotto4 = () => {
    return lottoGenerator().map((n, index) => <ScrollNumberV number={n} time={index+1} key={n}/>);
}
// 왼쪽으로 스크롤됩니다.
const ScrollNumberH = ({time=1, number=1}) => {
    const [left, setLeft] = useState(-1);
    const interval = useRef(1);
    let style = {backgroundPosition: `${-1*left}px 0px` }
    useEffect(() => {
        interval.current = setInterval(() => {
            setLeft(left + 10);
            if (left >= 52*45) {
                setLeft(0);
            }
        }, 20);
        setTimeout(()=>{
            clearInterval(interval.current);
            setLeft((number-1)*52)
        },  time*1000);

        return () => {
            clearInterval(interval.current);
        }

    }, [left, number, time]);
    return <div className="numbers numbersh" style={style}></div>
}

export const Lotto5 = () => {
    return lottoGenerator().map((n, index) => <ScrollNumberH number={n} time={index+1} key={n}/>);
}

// 위로 스크롤됩니다.  이미지크기가 80px이므로 80을 곱해서 스크롤을 처리했습니다.
const ScrollNumberV2 = ({time=1, number=1}) => {
    const [top, setTop] = useState(-1);
    const interval = useRef(1);
    let style = {backgroundPosition: `0px ${-1*top}px` }
    useEffect(() => {
        interval.current = setInterval(() => {
            setTop(top + 10);
            if (top >= 80*45) {
                setTop(0);
            }
        }, 20);
        setTimeout(()=>{
            clearInterval(interval.current);
            setTop((number-1)*80)
        },  time*1000);

        return () => {
            clearInterval(interval.current);
        }

    }, [top, number, time]);
    return <div className="lottos lottosv" style={style}></div>
}

export const Lotto6 = () => {
    return lottoGenerator().map((n, index) => <ScrollNumberV2 number={n} time={index+1} key={n}/>);
}

// 왼쪽으로 스크롤됩니다.
const ScrollNumberH2 = ({time=1, number=1}) => {
    const [left, setLeft] = useState(-1);
    const interval = useRef(1);
    let style = {backgroundPosition: `${-1*left}px 0px` }
    useEffect(() => {
        interval.current = setInterval(() => {
            setLeft(left + 10);
            if (left >= 80*45) {
                setLeft(0);
            }
        }, 20);
        setTimeout(()=>{
            clearInterval(interval.current);
            setLeft((number-1)*80)
        },  time*1000);

        return () => {
            clearInterval(interval.current);
        }

    }, [left, number, time]);
    return <div className="lottos lottosh" style={style}></div>
}

export const Lotto7 = () => {
    return lottoGenerator().map((n, index) => <ScrollNumberH2 number={n} time={index+1} key={n}/>);
}

 

번호에 해당하는 이미지를 불러서 출력하는 Number라는 컴포넌트를 만들어 출력했습니다.

 

App.js 파일을  수정합니다.

import { Lotto1, Lotto2 } from "./Lotto";
import logo1 from './images/numbers/logo-header.png'
import logo2 from './images/numbers/img_gnb_game1.jpg'

const App = ()=>{
    return(
        <>
        <img src={logo1} alt={logo1}/>
        <img src={logo2} alt={logo2}/>
        <br/>
        <br/>
        <Lotto1/>
        <br/>
        <Lotto2/>
        <br/>
        <Lotto3/>
        <br/>
        <Lotto4/>
        <br/>
        <Lotto5/>
        <br/>
        <Lotto6/>
        <br/>
        <Lotto7/>
        </>
    )
}
export default App;

 

 

실행 결과입니다.

실행해 보시면 스크롤되는 결과를 확인하실 수 있습니다.