[N-Back] 토이 프로젝트 - 2

Stolen Moments·2020년 5월 14일
0
post-thumbnail

컴포넌트

※ 프로젝트는 create-react-app 으로 시작 ※

  • App : N-Back의 N, 그리고 몇 회 수행할 것인지 입력 받기. 게임 시작 로직
import React, { useRef, useState } from 'react';
import Container from "./Container";
import Blocks from "./Blocks";

function makeRandom() {
    const rand = [];

    for (let i = 0; i < 20; i++)
        rand.push(Math.floor(Math.random() * 12));

    return rand;
}

const App = () => {

    const [start, setStart] = useState(true); // 입력 받기 전
    const [idx, setIdx] = useState(0); // 현재 위치를 가리키는 인덱스
    const [num, setNum] = useState(12); // Container에 전달할 props.num
	const rand = useRef(makeRandom());
  
    return (
        <>
            {
                start ?

                    <div>
                        N, Count 입력 받기 구현 예정 ㅎㅎ
                        <button onClick={() => setStart(false)}>게임 시작</button>
                    </div>
                    :

                    <Container num={num}>
                        <Blocks/>
                    </Container>

            }
        </>
    )
}

export default App;

  • Container : grid 3x4 컨테이너. 블록 스타일링 담당. props를 받아 난수가 가리키는 블록 색칠하기. (styled-components 사용)
import styled from 'styled-components'
export default styled.div`
  display: grid;
  justify-content: center;
  margin-top: 12rem;
  grid-template-rows: 150px 150px 150px;
  grid-template-columns: 115px 115px 115px 115px;
  grid-gap: 5px;
 
  div {
    border-style: solid;
    border-color: darkblue;
    border-width: 1px;
  }
  
  #id${props => props.num} {
    background: greenyellow;
  }
`

  • Blocks : <div> 12개.
import React from "react";

const Blocks = () => {
    return (
        <>
            <div id={"id0"}> </div> <div id={"id1"}> </div> <div id={"id2"}> </div> <div id={"id3"}> </div>
            <div id={"id4"}> </div> <div id={"id5"}> </div> <div id={"id6"}> </div> <div id={"id7"}> </div>
            <div id={"id8"}> </div> <div id={"id9"}> </div> <div id={"id10"}> </div> <div id={"id11"}> </div>
        </>
    )
}
export default Blocks



구현한 grid는 이렇게 나온다.

어떻게 위치를 일정 시간동안 표시할까?

일정 시간 및 횟수 만큼 동일한 로직이 반복되기 때문에 setInterval을 생각했다.

예시)
3초 후 특정 위치 표시 -> 1초 후 사라짐 -> 2초 후 다시 특정 위치 표시 ...


const App = () => { 
	
    (...)
    
  // 시작 버튼을 눌렀을 때 게임 시작~
    const handleOnClick = () => {
        setStart(false);

        const interval = setInterval(() => {

            setNum(rand.current[idx]);
            console.log(rand.current[idx] + " 켜기");

            setTimeout(() => {
                setNum(12);
                setIdx(idx => idx + 1);

                console.log("위치 표시 끄기");
            }, 1000)

            if (idx === 5)
                clearInterval(interval);

        }, 3000)
    }
    
    (...)

버튼을 클릭하고 3초 후, 위치가 표시된다. 그리고 1초 뒤에 표시가 꺼진다.

하지만 같은 위치만 표시됐다 꺼지고 당연히 clearInterval 또한 되지 않았다.

업데이트된 state를 가지고 오는게 아니더라

  • useEffect로 console.log를 찍어보니 num, idx 업데이트는 잘 되고 있었다. 그래서 렌더링이 될 때 마다 위치 표시되고 사라지는 건 잘 동작했던 것.
  • setInterval 안에서 console.log를 찍어보니 num, idx 초기값이 계속 떴다. 그래서 setNum(rand[0]) 이 인터벌 마다 호출됐다.

-> 즉, setInterval 안에서의 state 들은 초기값으로 뱅뱅 돌고 있던 것...

몇 시간을 헤멨는지,,

변수 선언해서 때우자

인덱스 state를 선언하지 않고 인터벌 횟수를 카운트하는 변수를 가지고 고쳐봤다.
일단 작동은 잘 되는데 좋은 방법은 아닌 느낌...

   ...
   
	// 시작 버튼을 눌렀을 때 게임 시작~
   const handleOnClick = () => {
        setStart(false);
        let intervalCnt = 0;

        const interval = setInterval(() => {
            setNum(rand.current[intervalCnt]);
            console.log(rand.current[intervalCnt] + " 켜기");

            setTimeout(() => {
                setNum(12);
                intervalCnt++;
                console.log("위치 표시 끄기");
            }, 1000)

            if (intervalCnt === 5) {
                clearInterval(interval);
                console.log("인터벌 종료")
            }

        },3000)
    }
   
   ...

useRef를 쓰자

집에와서 useRef를 사용해 idx 변수를 선언해서 돌려봤다. 그리고 잘 돌아가는 것을 확인.
아까 했을 땐 왜 안 됐던건지 모르겠다...왜그랬을까...

	...
	
  const idx = useRef(0);

    const handleOnClick = () => {
        setStart(false);

        const interval = setInterval(() => {
            setNum(rand.current[idx.current]);
            console.log(rand.current[idx.current] + " 켜기");
            setFlag(true);


            setTimeout(() => {
                setNum(12);
                console.log("위치 표시 끄기");
                setFlag(false);
                idx.current++;
            }, 1000)

            if (idx.current === 5) {
                clearInterval(interval);
                console.log(" 인터벌 종료")
            }

        },3000)
    }
    
    ...

결론 : 일단 완성시키고 리팩토링을 진행하자

토이 프로젝트인 만큼 규모도 크지 않기 때문에 먼저 기능 구현에 집중할 것이다.

완성 후에 리팩토링을 시도할 생각.

학습 목적이기 때문에 이렇게 난관에 부딪히는게 오히려 좋다고 본다 ㅎㅎ

다음 주제는 사용자 입력을 처리하는 로직을 구현하기~

github https://github.com/StolenMoments/n-back

profile
갈 길이 멀구만~

0개의 댓글