-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[3주차 기본/심화/생각 과제] 춘식이 카드 뒤집기 게임 #7
base: main
Are you sure you want to change the base?
Conversation
별로 랜덤하게 이미지 쌍 선택해서 보여주기 기능 구현
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
뭐랄까..!!
현수가 말한 대로 정말 계획을 깔끔하게 명세서같이 짜고 한 게 읽히는 느낌??
보면서 굉장히 깔끔한 코드라는 생각이 들었어 // 특히 카드 셔플에 고민을 많이 한 흔적이 보여서 ㅎㅎ
넘 수고했어@~@
const j = Math.floor(Math.random() * (i + 1)); | ||
[pairs[i], pairs[j]] = [pairs[j], pairs[i]]; | ||
} | ||
const shuffled = pairs.flatMap((pair) => pair); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
flat
flatmap
이라는 평탄화용 메소드가 있구나 !!
배열의 메소드 세계란.. 너무 유용한 것 같은데 ~~
//클릭된 카드가 2개 미만이거나 같은 카드를 연속 클릭 시 조기 리턴 | ||
if (clickedCards.length < 2) { | ||
return; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
조기리턴으로 2개 미만의 카드 선택시의 상황을 아주 깔끔하게 만들어주었구나..
비슷하게 매치된 애들 / 클릭되어 후보인 애들로 나눠서 상태를 관리한 것 같은데 전체적인 코드 가독성은
좀 더 좋은 것 같아서 많이 참고해가 ㅎㅎ
똒똒해 ... ### 👍
return ( | ||
<ModalPortal> | ||
<ModalContainer onClick={onClose}> | ||
<ModalContent onClick={(e) => e.stopPropagation()}> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
여기서 e.stopPropagation()
의 의도는 아래의 닫기버튼을 위에 전달하는 걸 방지하기 위함일까??
찾아보니 상위 엘리먼트들로의 이벤트 전파를 중단시키는 용도로 사용된다고 하는데, 의도가 궁금해 !!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
궁금해 22...
changeScore={changeScore} | ||
resetClicked={resetClicked} | ||
/> | ||
<Modal isOpen={showModal} onClose={handleClose} /> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
전체적으로 하위 -> 상위로 전달되는 핸들러는 최상단 컴포넌트에서 지정해주고,
많은 props drilling이 되는 부분은 딱 공통된 상단에 묶어놓은 게 인상깊다 !
pr point를 읽으면서도 구현계획이 명확해서 신기했는데,
혹시 이런 props를 설계할 때 이건 어떤 부분에서 많이 사용될거니까 여기에 지정하는 게 낫겠다
라고 생각하는
주요 포인트가 있을까??
적재적소에 잘 불러서 깔끔하게 짠 것 같아서 넘 잘 읽었어 🥹🔥
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
최상단에서 필요한 state와 변수, 함수들을 딱 정리해서 필요한 컴포넌트에 내려줬다는게 한눈에 보인다! 너무 깔끔해..!!!!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
와우 현수 진짜 똑똑혀... 전반적으로 보면서 느낀건 함수명 변수명이 굉장히 직관적이고 깔끔하다! 근데 이게 구조자체를 명확하게 설계하다보니 함수랑 변수까지도 기능별로 명확하게 정의될 수 있었던 것 같아~~ 나도 이렇게 생각이 묻어나는 코드를 짜야겠다는 반성!
넘 많이 배워간다 ㅎㅎㅎ 진짜 멋짐!!!!!! 고생했어!!! 🤩
if (card1 === card2) { | ||
clickedCards.splice(1, 1); | ||
return; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
연속클릭되는 상황은 생각을 못했는데 꼼꼼한 예외처리 진짜 멋지다!! 그냥 냅두고 넘어갈수도 있었을텐데!!! 👍 나도 리팩토링해보께
changeScore={changeScore} | ||
resetClicked={resetClicked} | ||
/> | ||
<Modal isOpen={showModal} onClose={handleClose} /> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
최상단에서 필요한 state와 변수, 함수들을 딱 정리해서 필요한 컴포넌트에 내려줬다는게 한눈에 보인다! 너무 깔끔해..!!!!
<HeaderContainer score={score}> | ||
<h2>춘식이를 맞춰주세요<button onClick={reset}>Reset</button></h2> | ||
<div className="score"> | ||
<span style={{ transform: `scale(${scale})` }}> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
style 값을 state로 관리하다니 이거 정말 창의적인데..? 현수 코드 볼 때마다 항상 내가 생각지도 못한 방식으로 구현해내서 넘 신기해.... 😆
const [score, setScore] = useState(0); | ||
const [level, setLevel] = useState("EASY"); | ||
const [resetClicked, setResetClicked] = useState(false); | ||
const [showModal, setShowModal] = useState(false); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
아니 상태관리 진짜 깔끔하다. 나는 현수랑 다르게 카드와 관련된 상태를 모두 최상단에서 만들어서 내려줬거든. 그렇게 했던 이유가 level이 바뀔 때마다 카드도 바꿔줘야하니까 그랬던건데 밑에 보니까 현수는 useState로 level이 바뀌면 카드 리스트를 새로 만들어내도록 했구나... useState를 어떻게 써야하는지 잘 몰라서 복잡하게 구현했던 것 같아 ㅋㅋㅋ 이렇게 깔끔하게 할 수 있군!!
return; | ||
} | ||
|
||
if (data[card1].cardImg === data[card2].cardImg) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
모든 card 각각을 구분하는 것과 card img의 일치 여부를 분리하는게 되게 복잡했는데, 이런식으로 한건 또 처음본다!! 나는 카드가 섞이면서 계속 번호가 바뀌니까 이렇게 할 생각은 못했는데.... 굉장히 합리적인 방법인 것 같아 오래 고민한게 느껴지는 깔끔한 로직...👍
return ( | ||
<ModalPortal> | ||
<ModalContainer onClick={onClose}> | ||
<ModalContent onClick={(e) => e.stopPropagation()}> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
궁금해 22...
background-image: url(${(props) => | ||
props.matchedCards | ||
? props.cardImg | ||
: props.clickedCards | ||
? props.cardImg | ||
: backCardImg}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
style에서 props나 비교연산자를 쓸 생각은 못했는데 똑똑한 방법 배워간다..!!
✨ 구현 기능 명세
🌈 구현사항
✅
게임 난이도 선택
V✅
정답 수 노출
V✅
카드 선택
V✅
카드 배열 순서
V🌈 심화과제
✅
애니메이션
V✅
theme
+styled-components
:: 적용 V✅
게임 초기화 버튼
V✅
createPortal
V🌼 PR Point
아래와 같은 코드를 통해 클릭될 때마다 새롭게 실행되는 구간을 만들었어요! 한 가지 예외 케이스들에 대해 설명드리면, 먼저 동일한 카드를 두 번 연속 클릭한 경우는, 매칭이 되면 안 되고 하나만 클릭했다고 생각하게 해야 해요! 그래서 splice 를 통해 하나를 지워주고 하나는 그대로 남겨 주었어요!
다음으론 이 과제의 메인이라 할 수 있는..! 이미지 url 이 서로 같은 경우..! 이 경우는 이미 matched 가 된 카드는 score 를 증가 시키면 안 되니 !matchedCards.includes(card1) 를 사용해서 필터해 주었구요, 매칭된 카드는 matchedCards 에 추가해 주었어요!
match 가 되었다면 clicked 는 비워줘야 다음 click 을 받아올 수 있으니, 약 1초 후에 빈 배열로 초기화를 해 주었구요, match 가 되지 않았다면 약간의 시간이 흐른 뒤에 카드를 다시 뒤집어주어야 하니 마찬가지로 1초 후에 빈 배열로 초기화를 해 주었어요!
저는 레벨이 바뀌면 선택되는 카드쌍이 랜덤이 되면서 랜덤으로 배열되는 기능을 구현하기 위해 같은 이미지 url 을 가지는 데이터를 2개씩 만들어 주었어요. 그리고 이 둘의 순서가 바뀌지 않게, 하나로 묶어서 랜덤 배열을 해 주고,
이 코드를 통해 내가 선택해야 하는 카드 * 2 개 만큼 잘라와서 사용했습니다!
🥺 소요 시간, 어려웠던 점
10h
🤩구조를 짤 때 했던 생각
카드를 클릭해서 뒤집는 게임이다. 이미지가 서로 같으면 뒤집어 놓고, 아니면 다시 원 상태로 복구시켜야 한다.
=> 클릭을 감지하는 state 와 match 된 카드를 저장하는 배열 두 개는 무조건 있어야 겠다.
난이도에 따라서 렌더링되는 카드가 달라야 한다.
=> 난이도를 나누는 state 는 무조건 있어야 하고, 그에 따라 카드를 매번 랜덤으로 선택해야 하니 카드 렌더링도 state 로 만들어야겠다.
똑같은 카드를 2개 만들어야 한다. 이 둘은 이미지는 같지만, id 는 달라야 한다. -> 단순히 map 으로 구현해서 두 개 복제하면 안 되겠다. 그럼 아예 데이터를 만들 때, 똑같은 이미지 url 을 가졌지만 id 는 서로 다른 카드를 2장씩 만들고, 이 둘은 인접하게 놓자. 그리고 나서 랜덤하게 섞을 때는, 인접한 2개의 순서는 바뀌지 않게 섞으면 되겠다!
사실 score 는 matchedCard 의 길이로 얻어낼 수는 있다. 그렇지만 다른 컴포넌트에 전달할 일도 있고, 이예 딸린 애니메이션도 있으니 state 로 구현하는 편이 편하겠다!
이 부분 구현하는 게 생각보다 쉽지 않더라구요.. 거의 알고리즘 문제 하나 푸는 시간이 걸린 것 같습니다..하하..
또 Cards 부분에서 처음에는 Card 컴포넌트 렌더링하는 걸, 카드를 랜덤하게 배열하는 곳에다가 했는데, 이 useEffect 의 의존성 배열엔 level 만 들어있었어요..! 그러다 보니 레벨을 바꾸지 않는 한 state 를 읽어오지 못하더라구요..! 그렇다고 의존성 배열에 clickedCards 를 넣자니 카드가 클릭될 때마다 랜덤하게 배열되어버리고..! 그래서 랜덤하게 배열하는 곳에서는 card 를 렌더링하지 않고, setCards 를 이용해 cards 값만 바꿔주고, 렌더링은 return 문에서 cards 를 읽어와서 해 주었어요! 그제서야 state 를 바르게 읽어와서 문제를 해결했습니다..!
🌈 구현 결과물
춘식이를 찾아라! 게임 하러가기