고마워요 넘블님덜 저에게 이런 공짜로 공부해볼 기회주셔서!!!
링크를 클론코딩하여라
조건은 모두 충족하였고
결과물은 vercel 로 배포하였다.
!! 추가로 Next.js를 사용했고, Eslint을 사용하여서 코드포매팅에 강제성을 주었다.!
정말 간단한 게임이라고 생각했었다.. 하지만 간단한건 내 실력이었음 ㅋ..
다른 색깔 찾기 게임이라서 금방하겠지? 하고 3시간만에 일단 뚝딱 만들긴했음
근데 오류가 잔뜩 발생했고 구글링해서 오류마다 일어나는 이유를 계속 정독했던것같다.
역시 나는 아직도 찔밥이고 계속 찔밥이지만 성장하는 찔밥이 되고싶다...
요구사항을 분석하며 state와 props를 어떻게 구상할까 고민을 해보았다
부모 컴포넌트에서 필요한 상태
스테이지:stage , 잔여시간:time , 점수:score
이정도가 부모컴포넌트에서의 요구사항이라고 생각됨
그래서 필요한 액션은 세개라고 생각된다
정답 div를 클릭하였을때?
/*
* 정답을 맞췄을때 실행될 함수
*/
const handleCorrect = () => {
setStage(stage + 1);
setTime((intervalRef.current = initVal.time));
setScore(score + 1);
};
오답 div를 클릭하였을때?
/*
* 오답을 눌렀을때 실행될 함수
*/
const handleWrong = () => {
setTime((intervalRef.current -= 1));
if (intervalRef.current === 0) {
endGame();
return;
}
};
게임종료
/*
* 게임종료
*/
const endGame = useCallback(() => {
alert(`GAME OVER!\n스테이지: ${stage}, 점수: ${score}`);
window.location.reload();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [stage, score]);
그리고 1초마다 시간을 차감해주어야하는데
나는 너무 당연하게도 setInterval을 사용해서 구현해보았었다... 이게 내 발목을 잡을줄은 몰라씀,,,, ㅠ
이유는 useEffect와 setInterval의 호환성이 안좋은 이슈가 있었다.
(링크 걸어두었으니 번역해서 보면 좋을듯하다)
setInterval을 사용하면 데이터가 누수되어서 발생하였었는데
이문제는 다른 포스트에서 제대로 다루어보겠다..!!
이렇게해서 자식 컴포넌트에게 넘겨줄 props는 정해졌다
자식 컴포넌트에서 필요한 상태
배열:array , 색상:color , 랜덤상수:randomInt
이렇게 생긴 영역이 필요했다
그리드를 사용하기로했고 스테이지마다 크기가 변경되어야한다
stage_1 2 x 2
stage_2 3 x 3
stage_3 4 x 4
.
.
.
stage_N (N+1) x (N+1)
props
로 넘겨받은 stage로 (stage+1) * (stage + 1)이 필요하다!
이제 스테이지별로 그리드 컬럼 설정을 어떻게 해줄까 했다
자식컴포넌트에서는 useEffect
가 두번 선언되었다.
리액트는 Virtual Dom을 이전상태와 비교하여 달라지면 바꿔주는 라이브러리로 이해하고있다. 그래서 useRef를 사용하였다
useEffect(() => {
if (ref.cur![](https://velog.velcdn.com/images%2Fwnsguddl789%2Fpost%2Fd1ba5403-496c-4f63-b453-a99a0af9a07e%2Fimage.png)rent) {
let repeat = Array(stage + 2).join('1fr ');
ref.current.setAttribute('style', `grid-template-columns : ${repeat}`);
}
}, [NUMBER, ref]);
컨테이너 div 에게 ref
를 주었고 dom에 접근하여 인라인스타일을 변경해주었다.
색상변경을 위한 상수를 받아왔다.
export const COLOR = [
{ originColor: '#eee8aa', diffColor: '#ffe4e1' },
{ originColor: 'pink', diffColor: '#ffe4e1' },
{ originColor: '#ffdead', diffColor: '#ffe4b5' },
{ originColor: '#d2b48c', diffColor: '#f5deb3' },
{ originColor: '#00ced1', diffColor: '#00bfff' },
{ originColor: '#87cefa', diffColor: '#afeeee' },
{ originColor: '#7b68ee', diffColor: '#6a5acd' },
];
(stage+1) * (stage+1) 배열의 크기만큼의 Card를 생성하였고 index
값과 randomInt
값이 일치할때
위의 COLOR
배열의 index번째 originColor
을 색상으로 주었고
불일치할때 diffColor
주었다
여기서 고민을하였다
또 랜덤색상값을 만들어주는 상태를 사용할까 아니면
저 상수를 계속 사용할까 했다
내가 생각했던 랜덤색상값은 정답에는 rgba(n,n,n)
을
오답에는 rgba(n-10,n-10,n-10)
줄 생각이었지만
그냥 상수배열을 사용하여 돌려막기 하기로 하였다
import { COLOR } from '../../constant/index';
...
const [color, setColor] = useState(COLOR);
...
useEffect(() => {
setRandomInt(Math.floor(Math.random() * ((stage+1) * (stage+1))) + 0);
if (stage % (COLOR.length - 1) === 0) {
setColor((prevList) => [...prevList, ...prevList]);
}
}, [NUMBER, stage]);
색상을 담을 color상태를 COLOR로 배열 초기화시켜주었었고
stage가 COLOR상수의 크기-1 의 배수일때 계속 배열의 크기를 덧붙여주었다.
오답을 눌렀을때는 부모에게 받은 handleWrong
함수를 사용하면 됐지만,,?
정답을 눌렀을때는 자식컴포넌트의 상태가 변경이 되어야했다
const Correct = () => {
setArray(Array.from(Array((stage + 2) * (stage + 2)).keys()));
setRandomInt(Math.floor(Math.random() * ((stage+1) * (stage+1))) + 0);
handleCorrect();
};
n*n 크기의 배열을 재설정해야하고
랜덤상수를 다시 초기화해야했다
그러고 부모에게 받은 정답이벤트를 넣어주었다
이 함수를 정답Card의 click 이벤트로 걸어놓았다.
위에서도 말했지만 아 ㅋㅋ 나는 이제 리액트좀하는뎅ㅋㅋ 이렇게 생각했던 무매몽지했던 박준형을 양쪽 뺨을 때려주고싶다,,,,
아직도 난 부족했고 더 좋은 코드가 있을거라고 믿고있다..
아쉽게 어제 술먹냐고 챌린지 신청을 깜빡했지만?? 이렇게 글써보니 다시 복기해보는것도 나쁘지는 않은것같다
넘블에서 챌린지가 또 올라온다면 도전해봐야겠고
지난 챌린지 미션 두가지가 있는데 곧 해봐야겠다