-
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 게임 #4
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.
우선 너무너무 고생많으셨습니다 😂 아무래도 시험기간도 겹치고, 리액트 과제도 처음인데 카드 게임 구현을 위한 여러 요구사항이 많다보니 힘드셨을 것 같아요 ㅠㅠ
그럼에도 불구하고 함수 네이밍, props 값 전달, 랭킹/게임 모드 구분 렌더링 등 꼼꼼하게 해주신 것 같아서 코드 읽는데도 이해가 잘 갔던 것 같습니다 ㅎㅎ
리팩토링때는
- 중복 코드는 재사용 가능한 구조로 바꾸기
- theme.js
- 폴더 구조 개선
을 중점으로 하시면 좋을 것 같아요 ! 고생많으셨고 앞으로도 화이팅 해봅시당 💪💪💪
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 hours = date.getHours(); | ||
const minutes = date.getMinutes(); | ||
|
||
const timeNow = `${year}년 ${month}월 ${day}일 ${hours}시 ${minutes}분`; |
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.
게임 플레이 시간 저장부분에서, 저는 toLocaleString()
을 사용해서 간단하게 저장했습니다! 한번 찾아보시는 것도 좋을 것 같아요 :)
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 [timer, resetTimer] = useTimer(isTimerActive); | ||
|
||
const [isRankingView, setIsRankingView] = useState(false); | ||
const [isGameView, setIsGameView] = useState(true); |
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.
게임모드와 랭킹모드 둘 다 선언이 되어있는는데, 게임 모드일때는 랭킹 모드가 아니고, 랭킹 모드일때는 게임모드가 아닌 관계라서, 저는 랭킹모드 하나만 선언해서 RankingMode일때는 게임 화면이 렌더링이 되게 설정했습니다! 이 부분 좀 더 간단하게 관리할 수 있을 것 같아요 : )
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는 적게 사용하면 좋아요!
특히 isRankingView <-> isGameView 는 서로 반대 관계이고,
isGameStarted ~= isTimerActive 는 서로 비슷한 관계이니까
하나의 state로 표현할 수 있을 것 같아요! 그럼 state가 4개에서 2개로 줄어들겠죠?
{isRankingView && ( | ||
<Ranking updateRanking={updateRanking} /> | ||
)} | ||
</> |
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.
위에서도 게임과 랭킹모드가 상호 배타적인 관계라고 말씀 드렸었는데, 예를 들어서
{isRankingMode ? () : () } 이런식으로 조건부 렌더링하면 좀 더 간결해 질 수 도 있을 것 같아요
initializeGame(); // 게임 재초기화 | ||
} | ||
} | ||
}; |
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.
저는 카드를 섞는 부분, 카드를 선택했을 때 실행되는 함수같은건 utils 폴더에 gameLogic.js 파일로 분리해서 관리했습니다! 그렇게 하면 훨씬 컴포넌트 내의 코드가 더 깔끔해지더라구요, 지금은 레벨이 하나지만 후에 레벨을 여러 선택하는 기능까지 추가되면 로직과 UI를 분리해보시는 것도 좋을 것 같아요 :)
` | ||
const TableRow = styled.tr` | ||
background-color: #fff; | ||
&:nth-child(even) { |
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 ===0 이면 짝수, 홀수로 해서 처리하려다가 말았는데 nth-child 라는 아주 간단한 방법으로 할 수 있었네요.. 알아갑니다 !!! ㅎㅎ
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.
timeDisplay가 실제로 사용되는 부분이 있나요?
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.
components 폴더는 보통 UI와 관련된 컴포넌트들을 주고, timer과 같은 커스텀 훅은 UI와 관련된 부분이 아니라 로직 중심이기 때문에 UI 컴포넌트들과는 별도로 hooks 폴더나 utils 폴더에 위치시키는 게 일반적인 것 같습니다!
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.
폴더 구조에 따라 다를 수 있긴 한데, timer.jsx를 components에 위치시키는 건 확실히 일반적이진 않은 것 같아요 ! hook을 위한 폴더 안에 넣어두곤 하니 참고 부탁드립니다~ 그리고 이런 훅들은 태그를 반환하지 않으므로 .jsx가 아닌 .js를 써서 더욱 명확히 하곤 합니다 !
@@ -0,0 +1,13 @@ | |||
<!doctype html> | |||
<html lang="en"> |
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.
ko로 설정하기!
<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.
페이지의 이름을 별도로 (1 to 50) 설정해주면 좋을 것 같아요
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.
안 사용하는 파일은 가능하면 제거해주세요~!
const [timer, resetTimer] = useTimer(isTimerActive); | ||
|
||
const [isRankingView, setIsRankingView] = useState(false); | ||
const [isGameView, setIsGameView] = useState(true); |
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는 적게 사용하면 좋아요!
특히 isRankingView <-> isGameView 는 서로 반대 관계이고,
isGameStarted ~= isTimerActive 는 서로 비슷한 관계이니까
하나의 state로 표현할 수 있을 것 같아요! 그럼 state가 4개에서 2개로 줄어들겠죠?
const hours = date.getHours(); | ||
const minutes = date.getMinutes(); | ||
|
||
const timeNow = `${year}년 ${month}월 ${day}일 ${hours}시 ${minutes}분`; |
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 handleLevelChange = (newLevel) => { | ||
setGameLevel(newLevel); | ||
setIsGameStarted(false); | ||
resetTimer(); | ||
setIsTimerActive(false); | ||
}; | ||
|
||
const handleGameButtonClick = () => { | ||
setIsGameView(true); | ||
setIsRankingView(false); | ||
} | ||
|
||
const handleRankingButtonClick = () => { | ||
setIsGameView(false); | ||
setIsGameStarted(false); | ||
setIsTimerActive(false); | ||
resetTimer(); | ||
setIsRankingView(true); | ||
}; | ||
|
||
const handleResetRanking = () => { | ||
localStorage.removeItem("gameData"); | ||
setUpdateRanking(prev => !prev); | ||
}; |
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.
이 부분, 완전히 중복되는 코드는 아니긴 하지만 일정 부분은 계속 반복되지 않나요?
그럴 때는 초기화를 위한 함수를 따로 만들어보는 건 어떤가요? 이게 위에서 첨언한 함수의 재사용과도 연관이 있어요!
여유가 되면 한번 고민해보는 것도 좋을 것 같습니다 !
import { useState, useEffect } from "react"; | ||
import useTimer from "./timer.jsx"; // 타이머 기능의 커스텀 훅 | ||
|
||
const Game = ({ isGameStarted, onGameStart, onGameEnd, setTimer, gameLevel }) => { |
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.
사용하지 않는 인자는 없애주세요! 나중에 유지/보수할 때 힘들어집니다~~
{onResetRanking && ( | ||
<ResetButton onClick={onResetRanking}>초기화</ResetButton> | ||
)} | ||
{!onResetRanking && ( | ||
<> | ||
<GameStage value={gameLevel} onChange={handleLevelChange}> | ||
<LevelOne value={1}>Level 1</LevelOne> | ||
<LevelTwo value={2}>Level 2</LevelTwo> | ||
<LevelThree value={3}>Level 3</LevelThree> | ||
</GameStage> | ||
<StopWatch>소요 시간: {timer.toFixed(2)}초</StopWatch> | ||
</> )} |
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.
이렇게 해도 되긴 하지만, 삼항 연산자를 이용해서 렌더링해봅시다!
onResetRanking ? <> : <> 방식으로 하면 훨씬 가독성도 좋고, 불필요한 코드의 양도 줄어들 것 같아요!
const GameRanking = styled.button` | ||
background-color: #ECDFCC; | ||
border-radius: 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.
공통 스타일까지는 아직 이른 것 같아서 말씀드리지 않을까 했는데, 희선님이 잘 지적해주셨네요! 나중에는 계속 반복되는 스타일도 하나로 만든 뒤, 자식 스타일들을 만드는 방법이 있으니 참고해두었다가 추후 여유가 될 때 한번 시도해보는 것도 좋을 것 같습니다 !
margin-left: auto; | ||
background-color: #ECDFCC; | ||
border-radius: 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.
이번 4주차 과제에서 theme 사용해봐야하니까 희선님이 언급해주신 것 꼭 살펴보세요 !!
굿굿
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.
폴더 구조에 따라 다를 수 있긴 한데, timer.jsx를 components에 위치시키는 건 확실히 일반적이진 않은 것 같아요 ! hook을 위한 폴더 안에 넣어두곤 하니 참고 부탁드립니다~ 그리고 이런 훅들은 태그를 반환하지 않으므로 .jsx가 아닌 .js를 써서 더욱 명확히 하곤 합니다 !
</tbody> | ||
</Table> | ||
); | ||
} |
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.
그리고 00:00과 같은 형식으로 포맷팅하려면, .padStart 와 같은 함수를 사용하곤 하니 한번 찾아보는 것도 좋을 것 같습니다 !
https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/String/padStart
✨ 구현 기능 명세
💡 기본 과제
18까지 클릭해야한다면, 처음에는 19까지의 숫자가 랜덤으로 보여짐현재 시각
,게임의 레벨
,플레이 시간
3개의 정보를 localStorage에 저장 (랭킹에서 사용)🔥 심화 과제
Level 1:
3 x 3
, Level 2:4 x 4
, Level 3:5 x 5
createPortal
공유과제
제목: 리액트 Hooks - useState, useEffect
링크 첨부 : https://wave-web.tistory.com/83
❗️ 내가 새로 알게 된 점
[]
을 넣어 주면, 컴포넌트가 처음 렌더링될 때 한 번만 실행된다.❓ 구현 과정에서의 어려웠던/고민했던 부분
버튼 안에 텍스트 정렬이 중앙으로 안되는 문제가 생겼다.
버튼 크기를 padding으로 조정해서 그랬다. flex 속성을 줘서 가로축, 세로축 정렬을 하니까 해결되었다.
레벨에 따라 게임이 변화하는 부분은 더 생각해보아야 할 것 같다.
🥲 소요 시간
🖼️ 구현 결과물
https://www.youtube.com/watch?v=7bBBZkiCc3Y