-
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주차 기본/심화/공유 과제] React 연습 #6
base: main
Are you sure you want to change the base?
Conversation
@@ -0,0 +1,42 @@ | |||
#root { |
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.css, index.css 파일은 삭제해서 스타일을 초기화 시켜주고, 시작하는게 좋습니다! 추가적인 스타일을 먹였을때 예상하지 못한대로 동작할 수 있기 때문이예요!
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.
오호.. 저도 처음에 이 파일이 있는지 모르고 원하는 바와 다르게 화면이 나와서 root를 어디서 설정하고 있는지 한참 찾았거든요..
삭제한다는 생각은 못하고 여기서 수정을 해줬는데 앞으로는 삭제하고 구성해나가야겠어요!!
@@ -0,0 +1,68 @@ | |||
:root { |
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.css 와 마찬가지로 삭제하고 시작하면 좋습니다!
@@ -0,0 +1,14 @@ | |||
<!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.
제목도 설정하면 좀 더 좋을거 같아요~
return ( | ||
<> | ||
<GlobalStyle /> | ||
{/* <ProfileCard /> */} |
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.
사용하지 않는 주석들은 삭제하기!
|
||
return ( | ||
<S.PageContainer> | ||
<Header time={time} activeButton={activeButton} setActiveButton={setActiveButton} /> |
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.
여기서 Header 컴포넌트로 setActiveButton이라는 setter함수 자체를 보내주고 있는데 이런식으로 자식 요소에게 부모의 state 값을 즉각적으로 변경할 수 있는 setter 함수를 위임하는것이 좋지 않다고 합니다. 이는 자식 컴포넌트에게 외부 데이터 변경의 주도권을 넘겨주는 것과 같은 작동방식이기 때문에 handleActiveButtonChange 함수를 만들고 내부에서 setActiceButton으로 값을 관리하고, Header 컴포넌트에게는 이 함수를 넘겨주는것이 좀 더 건강한 로직이 될 수 있어 보여요!
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.
아.. 큰일났다...😱
setTime((prevTime) => parseFloat((prevTime + 0.01).toFixed(2))); | ||
}, 10); | ||
} | ||
return () => clearInterval(timer); |
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.
클린업 함수로 clearInterval 좋네요!!
import React, { useState } from 'react'; | ||
import * as S from "./styled"; | ||
|
||
function Header({ activeButton, setActiveButton, time, resetTimer }) { |
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.
왜 화살표 함수로 컴포넌트를 생성하지 않고 function으로 했는지 알 수 있을까요??
@@ -0,0 +1,177 @@ | |||
import styled from '@emotion/styled'; | |||
|
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.
하나의 스타일 파일에 header, modal 등등 games 폴더에 있는 모든 컴포넌트들의 스타일을 정의하다보니 가독성이 떨어지고 어떤 스타일을 먹였는지 이름을 찾기가 조금 어려운 단점이 있는것 같아요. 하나의 컴포넌트와 그에 적용되는 스타일 파일은 하나씩 만들어서 같은 폴더에 넣어주면 좀 더 가독성도 좋아지고, 폴더구조도 깔끔해 질 것 같습니다!
import styled from '@emotion/styled'; | ||
|
||
//header.jsx | ||
export const Header = styled.div` |
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.
Header컴포넌트의 가장 바깥 레이아웃이 되는 이 스타일에는 div대신 header 태그를 사용하면 시맨틱 태그의 장점을 살릴 수 있을것 같아요!
export const ButtonGame = styled.button` | ||
padding: 0.5rem 1rem; | ||
font-size: 1rem; | ||
cursor: pointer; | ||
border-radius: 0.5rem; | ||
border:0; | ||
width: 5rem; | ||
background-color: ${({ isActive }) => (isActive ? 'beige' : 'pink')}; | ||
|
||
&:focus { | ||
outline: none; | ||
} | ||
`; | ||
|
||
export const ButtonRanking = styled.button` | ||
padding: 0.5rem 1rem; | ||
font-size: 1rem; | ||
border-radius: 0.5rem; | ||
cursor: pointer; | ||
width: 5rem; | ||
background-color: ${({ isActive }) => (isActive ? 'beige' : 'pink')}; | ||
|
||
&:focus { | ||
outline: none; | ||
} | ||
`; |
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.
이 두개의 버튼 스타일은 하나만 만들고 게임, 랭킹에 공통으로 줘도 괜찮을거 같아요!
} | ||
`; | ||
|
||
export const Dropdown = styled.div` |
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.
이걸 styled.select`` 로 줘도 될 것 같아요!
const [currentLevel, setCurrentLevel] = useState(1); | ||
|
||
const startCards = () => { | ||
const initialNumbers = Array.from({ length: 9 }, (_, i) => i + 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.
Level2,3을 구현할때 level의 value값을 prop으로 받아서 +2를 해주면 카드 배열의 한 줄 개수를 알 수 있고, 이를 제곱하면 카드의 총 개수를 알 수 있으며, 여기서 *2 해주면 필요한 총 숫자 개수까지 알 수 있을 것 같아요. 이걸 변수로 설정해서 length: 9 이 부분을 동적으로 설정해줄 수 있겠네요!
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.
threeGrid, createNewCard 함수 등 Level1의 개수와 관련된 부분을 모두 이런 방식으로 수정해주면 될 것 같습니다!
do { | ||
randomAfterNumber = Math.floor(Math.random() * 9) + 10; | ||
} while (usedNumbers.has(randomAfterNumber)); |
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.
do while 문을 실제로 쓴건 처음보네요. while문과의 차이가 무조건 do {} 부분을 한번 실행하고 반복을 결정한다고 하는데 의도하신 부분일까요?
const currentTime = new Date().toLocaleString(); | ||
const existingData = JSON.parse(localStorage.getItem('gameData')) || []; | ||
existingData.push({ | ||
currentTime, | ||
level: currentLevel, | ||
playTime: time.toFixed(2) | ||
}); | ||
localStorage.setItem('gameData', JSON.stringify(existingData)); |
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.
모달을 닫는 함수에 있어서 로컬스토리지에 시간 같은 데이터를 저장하는 로직은 필요한 로직이지만 약간 분리시켜주는게 좋을 것 같아요. util함수로 분리하면 코드도 훨씬 깔끔해지고, 도메인을 분리할 수 있을 것 같습니다!
display: flex; | ||
align-items: center; | ||
justify-content: center; | ||
z-index: 1000; |
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.
z-index의 값을 1000까지 안줘도 괜찮습니다. 나중에 z-index로 관리해야할 요소들이 많아질수록 쌓임 맥락이 점점 헷갈리기 때문에 예를들면 모달 백드롭 부분을 z-index: 1, 모달 컨텐츠 요소를 z-index: 2로 설정해주는것이 좋을 것 같습니다!
padding: 20px; | ||
border-radius: 8px; | ||
text-align: center; | ||
max-width: 400px; |
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로 하신 이유가 있으신가요? 없다면 통일하면 좋을 것 같아요!
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부터 9까지 클릭한 이후 10부터 18 까지의 숫자들은 클릭했을때 카드 자체가 없어져야 하는데 이건 어떻게 하면 좋을지도 생각해보면 좋겠네요! |
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.
이번 과제를 하면서 제가 효율적이지 않은 돌아가기만 하는 코드를 짜고있는것 같다는 생각은 들었지만,,
민이가 구현한 코드를 살펴보면서 정말정말 수정해야 할 부분이 많다는게 느껴지네요...
리액트 감을 익히는데 많은 도움이 되었숩니다~~~
이번주도 수고하셨어용
@@ -0,0 +1,42 @@ | |||
#root { |
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.
오호.. 저도 처음에 이 파일이 있는지 모르고 원하는 바와 다르게 화면이 나와서 root를 어디서 설정하고 있는지 한참 찾았거든요..
삭제한다는 생각은 못하고 여기서 수정을 해줬는데 앞으로는 삭제하고 구성해나가야겠어요!!
|
||
return ( | ||
<S.PageContainer> | ||
<Header time={time} activeButton={activeButton} setActiveButton={setActiveButton} /> |
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.
스타일을 이렇게 따로 파일을 떼서 만들어주는것도 좋네요..!!👍👍
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.css 같은 기본 설정 파일을 제거하고나서도, 각 요소에서 따로 스타일을 지정해주기보다 전체적인 기본 틀은 global로 설정해주는게 좋나요???
</tbody> | ||
</S.Table> | ||
) : ( | ||
<S.NoDataMessage>저장된 게임 정보가 없어요🥲</S.NoDataMessage> |
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 newIntervalId = setInterval(() => setTime(prev => prev + 0.01), 10); | ||
setIntervalId(newIntervalId); | ||
startTimer(); | ||
} |
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.
setIntervalId로 타이머의 ID값을 저장해서 사용하는 방식이 생각지 못한 부분이었어요!!
한가지 궁금한 점은
pages/games/game.jsx에서 타이머 기능을 정의할 때 setInterval(() => setTime(prev => prev + 0.01), 10)을 해주는 부분이 있는데, 여기서도 setInterval()을 반복해서 써줘야하나요???
✨ 구현 기능 명세
💡 기본 과제
18까지 클릭해야한다면, 처음에는 19까지의 숫자가 랜덤으로 보여짐현재 시각
,게임의 레벨
,플레이 시간
3개의 정보를 localStorage에 저장 (랭킹에서 사용)🔥 심화 과제
Level 1:
3 x 3
, Level 2:4 x 4
, Level 3:5 x 5
createPortal
공유과제
제목: React에서의 '상태관리' 😎
링크 첨부 : https://wave-web.tistory.com/91
❗️ 내가 새로 알게 된 점
const mixNumbers = initialNumbers.sort(() => Math.random() - 0.5);
예를 들어보자면 initialNumbers가 [1, 2, 3, 4, 5, 6, 7, 8, 9]일 때, 만약 Math.random()이 밑에처럼 값을 반환했으면
1: Math.random() → 0.3 (0.3 - 0.5 = -0.2) → 순서 유지
2: Math.random() → 0.7 (0.7 - 0.5 = 0.2) → 2가 3 앞에
3: Math.random() → 0.1 (0.1 - 0.5 = -0.4) → 1이 4 앞에
...
이렇게 반복적으로 요소들이 비교되면서 무작위로 섞이게 되는 로직이라고 합니다..!
그런데 뭔가 더 쉽고 간단한 random방법이 있을 것 같아요..😢
일반적으로 React 컴포넌트는 부모 컴포넌트의 DOM 트리 구조에 따라 렌더링되는데,
createPortal을 사용하면, 특정 컴포넌트를 부모 컴포넌트의 DOM 트리와는 별도로 다른 위치에 렌더링할 수 있고 부모 요소의 스타일이나 레이아웃과 무관하게 Modal 같은 요소를 구현할 수 있다고 합니당 !
❓ 구현 과정에서의 어려웠던/고민했던 부분
🥲 소요 시간
7d
🖼️ 구현 결과물
3.1.mp4
3.2.mp4