-
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주차 기본/심화/공유 과제] 1 to 50 게임 #3
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.
준혁님 안녕하세요 ~~ 금단디조 정완이에요
역시 OB답게 폴더 구조도 너무 깔끔하고 코드들도 다 가독성 있게 느껴졌어요
희선님과 준혁님의 코드를 보면서 많이 배웠어요
저도 얼른 그렇게 할 수 있는 날이 오기를 바라면서 더 열심히 해보겠습니다 !!! 🤩
게임 기능 구현할 때 레벨마다 value에 들어오는 값을 활용하여 배열의 크기를 조정하는 것 저도 참고하여 리팩토링해보겠습니다! 그리고 이미 알고 계실 것 같지만 !! css 스타일 주실 때 px 단위를 거의 사용하셨는데 rem 값을 사용하시면 좋을 것 같아요
과제하시느라 고생 많으셨습니다 앞으로도 남은 활동 화이팅해요
setIsModalOpen(false); | ||
setTimeout(()=>initGame(),0); //비동기 활용 -> 초기화 안되는 거 방지 | ||
} | ||
|
||
|
||
const initGame = () => { | ||
const initialCells = Array.from({length: cellCount}, (_,i)=> i+1).sort(() => Math.random() - 0.5); |
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.
sort() 메서드가 배열을 무작위 정렬할 때 배열이 클수록 성능이 떨어질 수 있고, 완전히 공평하게 무작위 정렬이 되지 않다고 하여 피셔-예이츠 셔플 알고리즘을 사용하면 시간복잡도를 줄일 수도 있고 더 공평하게 섞을 수 있다고 하여 관련 글 남겨드립니다
padding: 20px; | ||
border-radius: 8px; |
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.
반응형 웹 만들 때는 상대 단위 rem을 사용하는 것이 더 좋다고 저도 저번 과제할 때 배워서
px는 rem으로 수정하면 좋을 것 같습니다
const lineCount = (level + 2); | ||
const cellCount = lineCount ** 2; | ||
const endNumber = cellCount * 2; |
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.
역시 OB는 다르군요 🫢
//기본적으로 모달 컨텐트를 제외한 부분 누르면 닫히도록 구현 | ||
return createPortal( | ||
<Overlay 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.
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.
stopPropagation... 처음봤어요... 알아갑니다!
|
||
&.flash{ | ||
animation: flash 0.5s ease-in-out; | ||
} |
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.
이런 식으로 깜빡거림을 구현할 수 있는 것 덕분에 알아갑니다 !!!!
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.
PR 보고 남겨요! 저는 카드를 섞고 화면에 렌더링 하는 과정을 다음과 같이 분리했었습니다
- 섞는 로직, 카드 렌더링 컴포넌트 분리
- 카드 배열은 첫번째와 두번째로 보여질 카드 배열을 각각 분리했습니다. 첫 배열의 인덱스를 클릭하면 해당 인덱스의 숫자가 두번째 배열의 값으로 교체됩니다
- 두번째 배열의 카드는 클릭시 빈 배열로 설정하여 값을 비우고, 이 경우에는 inVisible 처리가 되도록 구현했습니다!
background-color: ${({ theme, isVisible, isSecondSet }) =>
isVisible
? isSecondSet
? theme.colors.mediumblue
: theme.colors.blue
: 'transparent'};
이런식으로 작성해서 e.target같은 직접 조작 없이 동적으로 설정했었어요. 참고해보셔도 좋을 것 같습니다 :)
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.
이건 정말 좋은 방법인 것 같아요! 추후 적용해볼게요 !!
배워갑니다 😊
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.
OB의 품격이 느껴지는 코드였습니다.. 준혁님 코드를 보고나니까 저는 굳이 필요하지 않은 상태를 추가해서 관리한 것 같기도 하고 ㅎㅎㅎ 🥹 레벨을 상수로 설정하는 것 대신 number로 구현하고 값을 활용하는 방식 등 많이 알아가는 것 같습니다 !! 또 중간중간 주석에 고민하셨던 흔적이 남아있어서 ㅋㅋㅋㅋ 어떤 부분에서 고민하셨는지 공감되기도 하고 이해도 더 잘 갔던 것 같아요
3주차 과제 너무너무 고생많으셨고, 남은 기간도 화이팅입니다 ~~ 👍👍👍
<!doctype html> | ||
<html lang="en"> | ||
<head> | ||
<meta charset="UTF-8" /> | ||
<link rel="icon" type="image/svg+xml" href="/vite.svg" /> | ||
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> | ||
<title>Vite + React</title> |
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 [mode, setMode] = useState("game"); | ||
const [level, setLevel] = useState(1); | ||
const [time, setTime] = useState(0); //ms 단위 (1000ms -> 1s) |
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 [isRankingMode, setIsRankingMode] = useState(false);
로 설정해 랭킹모드가 아니면 게임모드를 보여주게 만들었는데 mode, setMode에 game을 넣어주는게 가독성이 훨씬 좋네요 .. !!
clearInterval(intervalRef.current); | ||
//시작 timestamp, level, time -> 로컬 스토리지 저장 필요 | ||
|
||
const prevRecords = localStorage.getItem('records') ? JSON.parse(localStorage.getItem('records')) : null; |
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.
삼항 연산자로 쓰신 이유가 있을까요?? || 로 or처리를 할수도 있을 것 같은데, 작성하신 이유가 궁금합니다 !
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문 사용하긴 싫었고, 한 줄에 표현하고 싶어서 바로 삼항 연산자로 사용했습니다.
근데 희선님 코멘트대로 생각해보니
const prevRecords = JSON.parse(localStorage.getItem('records')) || null;
라고 하는 게 단락 평가를 잘 이용해서 더 깔끔하긴 한 것 같아요! 좋은 의견 감사합니다 !
// 배경색 초기화 | ||
document.querySelectorAll('.flash').forEach(element => { | ||
element.classList.remove('flash'); | ||
element.style.backgroundColor = 'pink'; | ||
}); | ||
|
||
setGameCount((prev)=>prev+1); | ||
} | ||
|
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.
지금 배경색 초기화하는 부분에서 DOM을 직접 조작하고 있는데, 리액트의 특성을 생각해보면 컴포넌트 상태를 통해 스타일을 제어하는 것도 좋은 방법일 것 같아요 :)
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.
확실히 좋은 방법인 거 같아요! DOM을 직접 조작하는 건 추후 리팩토링에 힘들긴 하니까..!
transition: background-color 0.2s ease, transform 0.1s ease; // 부드러운 전환
${({ isClicked }) =>
isClicked &&
background-color: rgba(255, 255, 255, 0.2); // 클릭 시 배경색 변경 transform: scale(1.02); // 클릭 시 약간 확대
}
이런 방식으로 코드를 작성하셨던데, 그냥 색상 천천히 변하게 만들고, visible이 아닐 때는 아예 없애버리는 방식 너무 좋은 것 같아요~! 배워갑니다 !!
if(targetNumber >= 2){ | ||
intervalRef.current = setInterval(()=>{ | ||
setTime((prev)=> prev + 10); | ||
},10) | ||
} |
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 createPortal( | ||
<Overlay 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.
stopPropagation... 처음봤어요... 알아갑니다!
const GameSection = styled.section` | ||
display: flex; | ||
flex-direction: column; | ||
justify-content: center; | ||
align-items: center; | ||
|
||
gap: 3vh; |
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.
gap에 3vh도 화면 크기에 따른 반응형 때문에 사용하시는 걸까요 ?!? 궁금합니다!
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.
넵 맞습니다 ~~ 제가 vw, vh를 많이 써놔서 gap도 그에 맞게 vh 단위로 작성했어요!
width: 500px; | ||
min-height: 50px; | ||
max-height: 500px; | ||
overflow-y: scroll; |
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 RankingTitle = styled.h1` | ||
|
||
`; | ||
|
||
const ResetButton = styled.button` | ||
width: 90px; | ||
height: 30px; | ||
border: 1px solid black; | ||
white-space: nowrap; | ||
|
||
position: absolute; | ||
top: 5px; | ||
right: 10px; | ||
|
||
background-color: #58a3e4; | ||
color: white; | ||
` |
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.
RankingTitle을 h1으로 설정하기 위해 사용하신 것 같은데 사실상 안에 들어가는 스타일이 없어서 스타일 컴포넌트로 작성하시는 기준이 궁금해요! 랭킹 제목과 초기화 버튼을 담는 div는 또 인라인 스타일로 처리되어있더라구요.
저는, RankingTitle과 ResetButton을 포함하는 div도 컴포넌트로 만들고 타이틀이나 버튼은 그냥 <h1></h1>
, 으로 작성한뒤 div컴포넌트안에서 $ h1{}
으로 주로 처리를 하거나, 아님 다 컴포넌트로 만들어서 작성하는 편이어서, 의견이 궁금합니다 !
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.
인라인 스타일은 실제 프로젝트나 협업에서는 거의 사용안하는 편이긴 합니다!
그러나 종종 사용할 떄가 있는데, 특별한 네이밍을 할 정도로 의미가 없거나, 네이밍이 어려울 정도로 복잡한 경우! 입니다.
이번 과제에서는 아무리 css 속성이 없다고 하더라도, 추후 변경될 수도 있는 부분이나, 의미가 충분한 경우 위의 예시처럼 StyledComponent 로 만들어주었습니다. 반대로, 큰 의미가 없고, 앞으로 수정될 것 같지도 않으며, 반복되는 형식의 css도 아닌 경우 그냥 빠르게 코딩하기 위해서 인라인 스타일을 사용했어요!
그래서 정리하자면 정말 별 의미없고, 반복되지 않는 간단한 css를 적용하려는 경우에는 종종 인라인스타일을 쓰지만
왠만해서는 styled-component로 사용하자~ 라는 생각을 하고 있습니다 😊
( + 요즘 tailwind를 사용해보고 있는데, 거기에 조금 익숙해져서 그런 것도 있습니다 ㅎㅎ..)
<RankingHeader> | ||
<tr> | ||
<th style={{textAlign:"center", border: "1px solid black"}}>타임스탬프</th> | ||
<th style={{textAlign:"center", border: "1px solid black"}}>레벨</th> | ||
<th style={{textAlign:"center", border: "1px solid black"}}>플레이 시간</th> | ||
</tr> | ||
</RankingHeader> | ||
<RankingBody> | ||
{records.map((record,index) => ( | ||
<tr key={`record-${index}`}> | ||
<td style={{textAlign:"center", border: "1px solid black"}}>{record.timestamp}</td> | ||
<td style={{textAlign:"center", border: "1px solid black"}}>{`Level${record.level}`}</td> | ||
<td style={{textAlign:"center", border: "1px solid black"}}>{formattedTime(record.time)}</td> | ||
</tr> | ||
))} | ||
</RankingBody> |
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.
엇 th와 td 반복되는 스타일을 밑에 선언되어 있는 RankingHeader와 RankingBody에 적용할 수 있지 않나요?? & th {
}
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.
맞습니다! 이건 그냥 제가 게으른 게 맞아요 ㅎ..
좋은 지적 감사합니다 !
✨ 구현 기능 명세
💡 기본 과제
18까지 클릭해야한다면, 처음에는 19까지의 숫자가 랜덤으로 보여짐현재 시각
,게임의 레벨
,플레이 시간
3개의 정보를 localStorage에 저장 (랭킹에서 사용)🔥 심화 과제
Level 1:
3 x 3
, Level 2:4 x 4
, Level 3:5 x 5
createPortal
공유과제
제목: state 변경에 따른 리렌더링, useEffect, setTimeout 이해하기
링크 첨부 :
https://wave-web.tistory.com/103
❗️ 내가 새로 알게 된 점
❓ 구현 과정에서의 어려웠던/고민했던 부분
🥲 소요 시간
8h
🖼️ 구현 결과물
week3_1.mp4
week_2.mp4
week_3.mp4