-
Notifications
You must be signed in to change notification settings - Fork 0
[3주차 기본/심화/공유 과제] 1 to 50 게임 구현 #4
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
base: main
Are you sure you want to change the base?
Changes from all commits
306d28f
a518638
14cb346
8b1ad6f
7f3e321
c806027
1e00390
3bb1fef
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,24 @@ | ||
| # Logs | ||
| logs | ||
| *.log | ||
| npm-debug.log* | ||
| yarn-debug.log* | ||
| yarn-error.log* | ||
| pnpm-debug.log* | ||
| lerna-debug.log* | ||
|
|
||
| node_modules | ||
| dist | ||
| dist-ssr | ||
| *.local | ||
|
|
||
| # Editor directories and files | ||
| .vscode/* | ||
| !.vscode/extensions.json | ||
| .idea | ||
| .DS_Store | ||
| *.suo | ||
| *.ntvs* | ||
| *.njsproj | ||
| *.sln | ||
| *.sw? |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| # React + Vite | ||
|
|
||
| This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules. | ||
|
|
||
| Currently, two official plugins are available: | ||
|
|
||
| - [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh | ||
| - [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,38 @@ | ||
| import js from '@eslint/js' | ||
| import globals from 'globals' | ||
| import react from 'eslint-plugin-react' | ||
| import reactHooks from 'eslint-plugin-react-hooks' | ||
| import reactRefresh from 'eslint-plugin-react-refresh' | ||
|
|
||
| export default [ | ||
| { ignores: ['dist'] }, | ||
| { | ||
| files: ['**/*.{js,jsx}'], | ||
| languageOptions: { | ||
| ecmaVersion: 2020, | ||
| globals: globals.browser, | ||
| parserOptions: { | ||
| ecmaVersion: 'latest', | ||
| ecmaFeatures: { jsx: true }, | ||
| sourceType: 'module', | ||
| }, | ||
| }, | ||
| settings: { react: { version: '18.3' } }, | ||
| plugins: { | ||
| react, | ||
| 'react-hooks': reactHooks, | ||
| 'react-refresh': reactRefresh, | ||
| }, | ||
| rules: { | ||
| ...js.configs.recommended.rules, | ||
| ...react.configs.recommended.rules, | ||
| ...react.configs['jsx-runtime'].rules, | ||
| ...reactHooks.configs.recommended.rules, | ||
| 'react/jsx-no-target-blank': 'off', | ||
| 'react-refresh/only-export-components': [ | ||
| 'warn', | ||
| { allowConstantExport: true }, | ||
| ], | ||
| }, | ||
| }, | ||
| ] |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| <!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> | ||
| </head> | ||
| <body> | ||
| <div id = "modal"></div> | ||
| <div id = "root"></div> | ||
| <script type="module" src="/src/main.jsx"></script> | ||
| </body> | ||
| </html> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,31 @@ | ||
| { | ||
| "name": "assignment3", | ||
| "private": true, | ||
| "version": "0.0.0", | ||
| "type": "module", | ||
| "scripts": { | ||
| "dev": "vite", | ||
| "build": "vite build", | ||
| "lint": "eslint .", | ||
| "preview": "vite preview" | ||
| }, | ||
| "dependencies": { | ||
| "@emotion/react": "^11.13.3", | ||
| "@emotion/styled": "^11.13.0", | ||
| "lodash": "^4.17.21", | ||
| "react": "^18.3.1", | ||
| "react-dom": "^18.3.1" | ||
| }, | ||
| "devDependencies": { | ||
| "@eslint/js": "^9.13.0", | ||
| "@types/react": "^18.3.12", | ||
| "@types/react-dom": "^18.3.1", | ||
| "@vitejs/plugin-react": "^4.3.3", | ||
| "eslint": "^9.13.0", | ||
| "eslint-plugin-react": "^7.37.2", | ||
| "eslint-plugin-react-hooks": "^5.0.0", | ||
| "eslint-plugin-react-refresh": "^0.4.14", | ||
| "globals": "^15.11.0", | ||
| "vite": "^5.4.10" | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| import { Global, ThemeProvider} from '@emotion/react'; | ||
| import Home from './Home' | ||
| import global from './styles/global' | ||
| import {Theme} from './styles/theme' | ||
|
|
||
| function App() { | ||
|
|
||
| return ( | ||
| <> | ||
| <ThemeProvider theme={Theme}> | ||
| <Global styles={global} /> | ||
| <Home /> | ||
| </ThemeProvider> | ||
| </> | ||
| ) | ||
| } | ||
|
|
||
| export default App; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,48 @@ | ||
| import styled from "@emotion/styled"; | ||
| import { useState } from 'react'; | ||
| import Header from './components/Header'; | ||
| import Game from './components/Game'; | ||
| import Rank from './components/Rank' | ||
|
|
||
| const Home = () =>{ | ||
| const [menu, setMenu] = useState("game"); | ||
| const [level, setLevel] = useState(1); | ||
| const [timer, setTimer] = useState(0); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. timer 값을 부모 컴포넌트에서 관리함에 따라 게임 컴포넌트나 다른 헤더 내부 요소들이 무지막지하게 리렌더링 되고 있습니다...!🥹
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 코드 설계할때 고려하지 못했던 부분인데 정말로 불필요한 렌더링이 많이 되는 것 같아요 반영해서 수정해볼게요 좋은 리뷰 감사합니다~~ |
||
|
|
||
| const handleMenuChange = (menu) => { | ||
| setMenu(menu); | ||
| } | ||
| const handleLevelSelect = (level) => { | ||
| setLevel(level); | ||
| }; | ||
| const handleTimerChange = (timer) => { | ||
| setTimer(timer); | ||
| }; | ||
|
Comment on lines
+12
to
+20
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. setState 자체를 props로 전달하지 않고 함수 만들어서 전달하는 방식 좋네요.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 오호 몰랐는데 자체를 넘기지 않는게 좋갰네요! 앗싸 새로운거 배워갑니다! |
||
|
|
||
| return ( | ||
| <> | ||
| <Header | ||
| menu={menu} | ||
| timer={timer} | ||
| handleMenuChange={handleMenuChange} | ||
| handleLevelSelect={handleLevelSelect} | ||
| /> | ||
| <Main> | ||
| {menu === "game" ? ( | ||
| <Game level={level} timer={timer} handleTimerChange={handleTimerChange}/> | ||
| ):( | ||
| <Rank/> | ||
| )} | ||
| </Main> | ||
| </> | ||
| ); | ||
| }; | ||
|
|
||
| const Main = styled.main` | ||
| width: 100%; | ||
| height: 100%; | ||
| margin: 0 auto; | ||
| padding-top: 2rem; | ||
| `; | ||
|
|
||
| export default Home; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,111 @@ | ||
| import { useState, useEffect } from 'react'; | ||
|
|
||
| // 숫자를 섞어주는 함수 | ||
| const randomArray = (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]]; | ||
| } | ||
| return array; | ||
| }; | ||
|
|
||
| // 숫자를 생성해서 랜덤으로 배열에 넣는 함수 | ||
| const createNums = (min, max) => { | ||
| const nums = []; | ||
| for (let i = min; i <= max; i++) { | ||
| nums.push(i); | ||
| } | ||
| return randomArray(nums); | ||
| }; | ||
|
|
||
| // 결과 저장 및 local 저장소에서 넣고 빼기 | ||
| const saveResult = (time, level) => { | ||
| const result = { | ||
| timestamp: new Date().toISOString(), | ||
| time, | ||
| level, | ||
| }; | ||
| const savedResults = JSON.parse(localStorage.getItem('results')) || []; | ||
| savedResults.push(result); | ||
| localStorage.setItem('results', JSON.stringify(savedResults)); | ||
| }; | ||
|
|
||
| // 게임 알고리즘 | ||
| const gameAlgorith = (level, timer, handleTimeChange) => { | ||
| const rowNum = level + 2; | ||
| const boardSize = rowNum * rowNum; | ||
| const maxNum = boardSize * 2; | ||
| const [currentNum, setCurrentNum] = useState(1); | ||
| const [timeId, setTimeId] = useState(null); | ||
| const [isFinish, setIsFinish] = useState(false); | ||
| const [beforeNums, setBeforeNums] = useState(createNums(1, boardSize)); | ||
| const [afterNums, setAfterNums] = useState(createNums(boardSize + 1, maxNum)); | ||
|
Comment on lines
+41
to
+42
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. p2: 상태관리 변수이름이 추상적인 것 같습니다! 조금 더 직관적인 이름으로 바꾸거나 각주를 추가하는 방법 추천 드립니다!
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 넵 직관적인 이름이나 각주 달아보도록 하겠습니다!! |
||
|
|
||
| const checkNumberClick = (number) => { | ||
| // 게임을 시작 할때 타이머를 시작시키는 로직 | ||
| if (currentNum === 1) { | ||
| const id = setInterval(() => { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. p1: 요기 타이머 관련 변수를
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 사실 큰 이유가 없었는데 더 직관적인 변수명을 사용하는게 좋을 것 같아요! 반영해서 수정하겠습니다 ㅎㅎ |
||
| handleTimeChange((previousTime) => { | ||
| const updatedTime = (previousTime + 0.01).toFixed(2); | ||
| return parseFloat(updatedTime); | ||
| }); | ||
| }, 10); | ||
| setTimeId(id); | ||
| } | ||
|
Comment on lines
+46
to
+54
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. p1: 보니까
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 넵 말씀해주신대로 클릭한 number가 1일때 타이머가 시작되는게 맞는 것 같아요!! 반영하도록 하겠습니다
youtheyeon marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| if (currentNum <= boardSize) { | ||
| const newNum = afterNums.pop(); | ||
| const updateBoard = beforeNums.map((num) => | ||
| num === number ? newNum : num | ||
| ); | ||
| setBeforeNums(updateBoard); | ||
| } | ||
| // 카드를 없애는 로직 | ||
| else if (currentNum > boardSize && currentNum < maxNum) { | ||
| const updatedBoard = beforeNums.map((num) => | ||
| num === number ? null : num | ||
| ); | ||
| setBeforeNums(updatedBoard); | ||
| } | ||
|
Comment on lines
+56
to
+69
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. p1: 둘다 if 조건에 내가 누른 카드의 수가, 눌러야 할 숫자인지 판별하는 조건이 없습니다! 추가해주세욥
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 넵 반영해서 수정하도록 하겠습니다! |
||
|
|
||
| setCurrentNum(currentNum + 1); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. p1: 카드를 클릭할때마다
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 넵 이 부분을 고려해야 알맞은 카드만 선택이 가능할 것 같아요 좋은 리뷰 감사합니다! |
||
|
|
||
| if (number === maxNum) { | ||
| clearInterval(timeId); | ||
| saveResult(timer, level); | ||
| setIsFinish(true); | ||
| return; | ||
| } | ||
|
Comment on lines
+73
to
+78
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. p1:
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 헉 정말 고려하지 못했던 부분이네요 좋은 리뷰 감사합니다!! 반영하도록 하겠습니다 |
||
| }; | ||
|
|
||
| const closeModal = () => { | ||
| setBeforeNums(createNums(1, boardSize)); | ||
| setAfterNums(createNums(boardSize + 1, maxNum)); | ||
| setIsFinish(false); | ||
| setCurrentNum(1); | ||
| handleTimeChange(0); | ||
| }; | ||
|
|
||
| useEffect(() => { | ||
| setBeforeNums(createNums(1, boardSize)); | ||
| setAfterNums(createNums(boardSize + 1, maxNum)); | ||
| setIsFinish(false); | ||
| clearInterval(timeId); | ||
| setCurrentNum(1); | ||
| handleTimeChange(0); | ||
| return () => clearInterval(timeId); | ||
| }, [level]); //level이 변경될 때 마다 렌더링이 되도록 | ||
|
Comment on lines
+81
to
+97
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. closeModal과 useEffect에서 동일한 함수들이 호출되고 있는 것 같은데 중복되는 함수들은 하나의 함수로 묶어서 호출하는 건 어떨까요?
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 오호 저도 비슷하게 남겼는데 공감!
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 동의합니다!! 말씀해주신대로 수정해볼게요
Comment on lines
+81
to
+97
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. p3: setBeforeNums(createNums(1, boardSize));
setAfterNums(createNums(boardSize + 1, maxNum));
setIsFinish(false);
setCurrentNum(1);
handleTimeChange(0);
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 넵 중복되는 부분 함수로 만들어서 적용해보겠습니다! |
||
|
|
||
| return { | ||
| rowNum, | ||
| maxNum, | ||
| beforeNums, | ||
| currentNum, | ||
| isFinish, | ||
| closeModal, | ||
| createNums, | ||
| checkNumberClick, | ||
| }; | ||
| }; | ||
|
|
||
| export default gameAlgorith; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,22 @@ | ||
| const formatDate = (timestamp) => { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 함수 따로 파일로 분리해서 사용하는 방법 좋네요...👍
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 완전!! 로직분리까지 최고 🙌
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 감사합니다 ㅎㅎ |
||
| const dateObj = new Date(timestamp); | ||
|
|
||
| let [year, month, day, hour, minute, second] = [ | ||
| dateObj.getFullYear(), | ||
| dateObj.getMonth() + 1, | ||
| dateObj.getDate(), | ||
| dateObj.getHours(), | ||
| dateObj.getMinutes(), | ||
| dateObj.getSeconds(), | ||
| ]; | ||
|
|
||
| const period = hour >= 12 ? '오후' : '오전'; | ||
| hour = hour % 12 || 12; | ||
|
Comment on lines
+13
to
+14
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. p5: 오호 오전 오후까지 고려하셨다니 세심한 생각 한수 배웠습니다 !!
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 감사합니다 ㅎㅎ |
||
|
|
||
| const pad = (num) => num.toString().padStart(2, '0'); | ||
| const formattedDate = `${year}.${pad(month)}.${pad(day)}`; | ||
| const formattedTime = `${period} ${pad(hour)}시 ${pad(minute)}분 ${pad(second)}초`; | ||
| return `${formattedDate} ${formattedTime}`; | ||
| }; | ||
|
|
||
| export default formatDate; | ||
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.
ThemeProvider까지 사용하시다니... 꼼꼼함 최고네여👏
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.
세미나 내용에 있었어서 한 번 적용해보고 싶었습니다 ㅎㅎ 감사합니다!