-
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 게임 #6
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.
린아님 안녕하세요!! React가 이번이 처음이라고 하셨는데 너무 코드도 깔끔하고 폴더구조도 잘 나눠주셔서 깜짝 놀랐습니다🫢🫢 천재 개발자의 탄생을 제가 이렇게 직관할 수 있다니 영광입니다!!! 덕분에 많이 배우고 갑니다!!
진짜 너무 깔끔하게 작성해주셔서 코드리뷰할 부분이 딱히 없어서 어떤 부분을 해야하나 고민을 많이 한 것 같아요 ㅎㅎㅎㅋㅋ
이번 과제 하시느라 수고 많으셨구!! 앱잼 전까지 나머지 부분들 구현하시면 다시 찾아올게요 헤헤
너무 코드를 깔끔하게 작성하셔서 완성된 코드가 진짜 궁금합니다....꼭 완성하면 삐삐쳐주세요!!
이번 주차도 수고 많으셨습니다🤗🤗
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.
여기 파일명이 app.jsx
로 되어있는데요!
일반적으로 React 컴포넌트 파일 이름은 대문자로 시작해서 파스칼 케이스에 맞게 작성합니다!!
크게 문제가 되는 것은 아니지만, App.jsx
로 이름을 지어서 컴포넌트라는 것을 쉽게 알 수 있도록 하는 것을 추천드립니다 :)
const App = () => { | ||
const [selectedTab, setSelectedTab] = useState('game'); // 'game' 또는 'ranking' |
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 startGame = () => { | ||
setTime(0); // 타이머 초기화 | ||
setNextNumber(1); | ||
setIsGameStarted(false); | ||
setIsGameCompleted(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.
이 함수는 선언만 되어있고 호출은 안하고 있는데 선언하신 궁금합니다!!
상태들을 초기화하고 있는데 이미 위에서 처음에 초기화해준 것이랑 같기 때문에 작성하신 의도도 궁금합니다!ㅎㅎ
const App = () => { | ||
const [selectedTab, setSelectedTab] = useState('game'); // 'game' 또는 'ranking' | ||
const [isGameStarted, setIsGameStarted] = useState(false); | ||
const [isGameCompleted, setIsGameCompleted] = 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.
혹시 여기 isGameCompleted
상태는 무슨 역할을 하는 상태인가요?? 이 상태값을 사용하는 로직이 없는 것 같아서요!!
const [time, setTime] = useState(0); | ||
const [nextNumber, setNextNumber] = useState(1); | ||
const timerRef = useRef(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.
useRef를 사용해서 불필요한 리렌더링을 방지하는 코드 너무 좋은 것 같습니다!! 최고최고👍🏻👍🏻
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.
setInterval의 리렌더링만 방지하고 있어서 타이머의 상태가 바뀔 때 발생하는 리렌더링은 방지하지 못하고 있어요...! 이 부분도 방지하면 더 좋을 것 같습니다 :)
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 initializeBoard = () => { | ||
const initialNumbers = Array.from({ length: 9 }, (_, i) => i + 1); // 1 to 9 | ||
shuffleArray(initialNumbers); | ||
setNumbers(initialNumbers); | ||
}; | ||
|
||
const shuffleArray = (array) => { | ||
for (let i = array.length - 1; i > 0; i--) { | ||
const j = Math.floor(Math.random() * (i + 1)); | ||
[array[i], array[j]] = [array[j], array[i]]; | ||
} | ||
}; |
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.
shuffleArray에서 initialNumbers라는 배열의 값을 직접 변경해주면서 불변성이 지켜지지 않고 있습니다..!
React에서는 불변성을 지키는 것이 중요합니다.
물론 이 코드에서는 원본 배열을 다시 사용하지 않기 때문에 문제가 발생하지 않지만, 만약 원본 배열이 다른 곳에서도 사용되고 있다고 가정한다면 직접 값을 바꾸면 다른 곳에도 영향을 주기 때문에 예상치못한 문제가 발생할 수 있습니다.
참고하면 좋은 블로그 같이 첨부드립니다.
따라서 아래처럼 코드를 수정해서 불변성을 지키는 것은 어떨까요??ㅎㅎ
const initializeBoard = () => { | |
const initialNumbers = Array.from({ length: 9 }, (_, i) => i + 1); // 1 to 9 | |
shuffleArray(initialNumbers); | |
setNumbers(initialNumbers); | |
}; | |
const shuffleArray = (array) => { | |
for (let i = array.length - 1; i > 0; i--) { | |
const j = Math.floor(Math.random() * (i + 1)); | |
[array[i], array[j]] = [array[j], array[i]]; | |
} | |
}; | |
const initializeBoard = () => { | |
const initialNumbers = Array.from({ length: 9 }, (_, i) => i + 1); // 1 to 9 | |
const shuffledNumbers = shuffleArray(initialNumbers); | |
setNumbers(shuffledNumbers); | |
}; | |
const shuffleArray = (array) => { | |
const newArray = [...array]; // 원본 배열 복사 | |
for (let i = newArray.length - 1; i > 0; i--) { | |
const j = Math.floor(Math.random() * (i + 1)); | |
[newArray[i], newArray[j]] = [newArray[j], newArray[i]]; | |
} | |
return newArray; | |
}; |
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.
저도 리액트 첫 과제 했을때 불변성에 대해 코드리뷰 받았었는데, 온전히 이해하기 어려웠던 기억이 새록새록하네요,,!💜🫧
앞으로 계속 신경써야하는 부분이라 참고된 아티클 정독 추천합니다!!
border-radius: 5px; | ||
transition: background-color 0.2s; | ||
} |
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 loadRecords = () => { | ||
const storedRecords = Object.keys(localStorage) | ||
.filter((key) => key.startsWith('gameResult_')) | ||
.map((key) => JSON.parse(localStorage.getItem(key))) | ||
.sort((a, b) => parseFloat(a.playTime) - parseFloat(b.playTime)); // 플레이 시간 기준으로 오름차순 정렬 | ||
|
||
setRecords(storedRecords); | ||
}; |
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.
로컬스토리지에 저장할 때 gameResult_${Date.now()}
로 작성하신 부분이 인상깊었는데 이렇게 데이터를 저장하고 있었네요!! 신기하네요!!ㅎㅎ
그런데 로컬스토리지에 많은 양을 저장하고 있다면 모든 값들을 가져와서 필터링을 해야하니까 비효율적일 수도 있을 것 같습니다.
그렇지만 지금 과제에서는 이 값만 저장하고 있기 때문에 크게 문제가 되지는 않을 것 같네요!! 새롭게 배우고 갑니다 ㅎㅎ
<td colSpan="3" style={{ textAlign: 'center' }}> | ||
기록이 없습니다. |
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.
인라인으로 스타일을 지정하는 것은 스타일 우선순위때문에 유지보수도 힘들고 예측 불가능하기 때문에 지양해야 합니다!
그런데 React 환경에서는 더욱 지양해야하는데요!! 인라인으로 스타일을 지정하면 객체를 새롭게 만들게 됩니다. React가 리랜더링을 할 때, 인라인 스타일의 객체의 주소값을 비교하는데, 주소값이 동일하지 않기 때문에 성능이 저하된다고 합니다.
클래스를 지정해서 스타일을 주거나 아래 코드처럼 useMemo를 사용해서 불필요한 객체 생성을 막아주세요!!
const centerTextStyle = useMemo(() => ({
textAlign: "center",
}), []);
// ...
<td colSpan="3" style={centerTextStyle}>
기록이 없습니다.
</td>
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.
조장님이 이미 피드백을 너무 잘 주셔서 뒤늦게 한 저는 코멘트 할게 별로 없네요..ㅎㅎ
5조는 레전드 인걸까요..?
폴더구조 보고 정말 놀랐습니다! Tile단위까지 컴포넌트 분리하신거 보고 감동받았어요!!
저도 컴포넌트 조금 더 분리해봐야겠습니다..!
useRef
useEffect
까지.. 진짜 레전드 개발자의 탄생인데요!?!!? 기대됩니다 츄류륩
✨ 구현 기능 명세
게임 설명
1부터 N까지 숫자를 순서대로 빠르게 클릭하는 게임
Level에 따라서 클릭해야 하는 숫자 범위가 다름
Level 1: 3x3 판에서 진행. 1 ~ 18까지 클릭
Level 2: 4x4 판에서 진행. 1 ~ 32까지 클릭
Level 3: 5x5 판에서 진행. 1 ~ 50까지 클릭
숫자는 매번 랜덤으로 배치됨
처음에는 클릭해야하는 숫자중 앞에 절반이 화면에 랜덤으로 보여짐
1부터 클릭할 때마다, 뒷쪽 나머지 숫자가 랜덤으로 채워짐
전체
구현 명세
헤더
구현 명세
레벨 선택 기능은 심화과제. 하지만 Select 퍼블리싱은 기본 과제
게임
구현 명세
만약 level 1이라 1
18까지 클릭해야한다면, 처음에는 19까지의 숫자가 랜덤으로 보여짐타이머는 소수점 2번째 자리까지 측정.
현재 시각
,게임의 레벨
,플레이 시간
3개의 정보를 localStorage에 저장(랭킹에서 사용)
심화
Level 1:
3 x 3
, Level 2:4 x 4
, Level 3:5 x 5
랭킹
구현 명세
심화
높은 Level이 위쪽으로, 같은 레벨 중에선 플레이 시간이 짧은게 위쪽으로 정렬
❗️ 내가 새로 알게 된 점
❓ 구현 과정에서의 어려웠던/고민했던 부분
🥲 소요 시간
🖼️ 구현 결과물