Skip to content
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주차 과제]  🌼 쵸키 푸키를 찾아라 💖 #8

Open
wants to merge 54 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
e07952f
init: 초기 환경 구축
Arooming Apr 26, 2023
3018ee5
feat: css 전역변수 생성
Arooming Apr 26, 2023
15ed4e3
style: title 변경
Arooming Apr 26, 2023
32bcda2
chore: style-components 설치
Arooming Apr 26, 2023
5778688
feat: Header 생성
Arooming Apr 26, 2023
ffc9b24
chore: 설치 및 업데이트
Arooming Apr 26, 2023
98e9381
style: 색상 및 폰트 수정
Arooming Apr 26, 2023
91aefe4
feat: 난이도 선택 버튼 추가
Arooming Apr 26, 2023
7db65d4
style: GlobalStyle 파일로 분리
Arooming Apr 27, 2023
311e61f
style: 카드 이미지 추가
Arooming Apr 27, 2023
10bf217
feat: 선택된 난이도에 따라 다른 페이지 렌더링
Arooming Apr 27, 2023
b1961c3
style: 전역 스타일 추가
Arooming Apr 27, 2023
1affc68
chore: 카드 이미지 리스트 생성
Arooming Apr 27, 2023
24770bb
feat: 난이도 별 페이지 생성
Arooming Apr 27, 2023
5ffbda0
feat: 카드 섞기
Arooming Apr 27, 2023
a4f866b
remove: 난이도 별 페이지 삭제
Arooming Apr 28, 2023
eb59ead
feat: 난이도 별 이미지 전달
Arooming Apr 28, 2023
056989e
remove: 안 쓰는 파일 삭제
Arooming Apr 28, 2023
b38baa2
chore: 필요없는 주석 삭제
Arooming Apr 28, 2023
450054d
feat: 난이도 별 카드 불러오기 동적 구현
Arooming Apr 28, 2023
c9bbefa
chore: prop-types 설치
Arooming May 2, 2023
df4a1ba
style: 카드 뒷면 사진
Arooming May 2, 2023
1f987eb
feat: 카드 생성 컴포넌트 분리 및 prop-types 지정
Arooming May 2, 2023
c95df59
feat: 카드 생성 부분 컴포넌트로 분리
Arooming May 2, 2023
8679a34
feat: reset 기능 추가
Arooming May 2, 2023
0c4064d
feat: 선택된 카드의 동일함 여부에 따라 콘솔 출력
Arooming May 2, 2023
66ec068
fix: 카드 선택 시 모든 카드 위치가 재 정렬되는 오류 해결
Arooming May 2, 2023
481bca6
style: reset 버튼 position 변경
Arooming May 2, 2023
2509faa
feat: 카드 뒤집힌 상태로 있다가 선택하면 앞면 보임
Arooming May 2, 2023
7588080
fix: 하나의 카드 선택 시 동일한 주소의 이미지를 가진 카드까지 공개되는 오류 해결
Arooming May 2, 2023
e7caef2
chore: console 삭제 및 지연 시간 추가
Arooming May 2, 2023
96ac8ca
style: 카드 뒤집힐 때 애니매이션 효과나도록 수정
Arooming May 2, 2023
6644764
style: reset 버튼에 z-index 추가
Arooming May 2, 2023
87c945a
feat: 전체 카드 쌍의 수와 맞춘 카드 쌍의 수 동적으로 출력
Arooming May 2, 2023
27991e3
feat: 정답 시 텍스트에 애니메이션 적용
Arooming May 2, 2023
364e05f
fix: 정답 수가 0 이상일 경우에만 애니메이션 적용되도록 수정
Arooming May 3, 2023
621d0ed
feat: 게임 도중 reset 클릭, 난이도 변경 시 정답 수 초기화
Arooming May 3, 2023
000920d
style: 색상 추가
Arooming May 3, 2023
4556399
feat: 모달 추가할 새로운 DOM 생성
Arooming May 3, 2023
314e787
feat: portal 활용해서 모달 생성
Arooming May 3, 2023
5442735
1주차 생각과제
Arooming May 4, 2023
5694a0b
remove: 생각과제 삭제
Arooming May 4, 2023
e474728
feat: 난이도 별 카드 이미지 랜덤 지정
Arooming May 4, 2023
292a49a
feat: 2개 카드 선택 시, 다른 카드 선택 불가
Arooming May 4, 2023
bb8c04c
fix: 오타 수정
Arooming May 5, 2023
ffd5dfc
chore: 콘솔 삭제
Arooming May 5, 2023
a3f469a
chore: timeout 시간 늘림
Arooming May 5, 2023
5cd57a2
style: 카드 크기 수정
Arooming May 5, 2023
5452b9b
Merge branch 'main' into week3_findCard
Arooming Jun 9, 2023
c0a3c5b
refactor: theme에 font 추가
Arooming Jun 9, 2023
aa3abe6
refactor: 중복 코드 삭제
Arooming Jun 9, 2023
fa9c773
refactor: 연산자 수정
Arooming Jun 9, 2023
a7627e8
refactor: 코드리뷰 반영
Arooming Jun 9, 2023
f1a28d8
refactor: 직관적인 변수 명으로 수정
Arooming Jun 9, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions week3/.eslintrc.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
module.exports = {
env: { browser: true, es2020: true },
extends: [
'eslint:recommended',
'plugin:react/recommended',
'plugin:react/jsx-runtime',
'plugin:react-hooks/recommended',
],
parserOptions: { ecmaVersion: 'latest', sourceType: 'module' },
settings: { react: { version: '18.2' } },
plugins: ['react-refresh'],
rules: {
'react-refresh/only-export-components': 'warn',
},
}
24 changes: 24 additions & 0 deletions week3/.gitignore
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?
18 changes: 18 additions & 0 deletions week3/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link
rel="icon"
type="image/svg+xml"
href="https://item.kakaocdn.net/do/cc744df91dd6ecf377d4584e79dd0e31b3a18fdf58bc66ec3f4b6084b7d0b570"
/>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>쵸키푸키를 찾아랏🐾</title>
</head>
<body>
<div id="root"></div>
<div id="modal"></div>
<script type="module" src="/src/main.jsx"></script>
</body>
</html>
28 changes: 28 additions & 0 deletions week3/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
{
"name": "week3",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build",
"lint": "eslint src --ext js,jsx --report-unused-disable-directives --max-warnings 0",
"preview": "vite preview"
},
"dependencies": {
"prop-types": "^15.8.1",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"style-components": "^0.1.0"
},
"devDependencies": {
"@types/react": "^18.0.28",
"@types/react-dom": "^18.0.11",
"@vitejs/plugin-react": "^4.0.0",
"eslint": "^8.38.0",
"eslint-plugin-react": "^7.32.2",
"eslint-plugin-react-hooks": "^4.6.0",
"eslint-plugin-react-refresh": "^0.3.4",
"vite": "^4.3.2"
}
}
1 change: 1 addition & 0 deletions week3/public/vite.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
17 changes: 17 additions & 0 deletions week3/src/App.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { ThemeProvider } from "styled-components";
import theme from "./styles/theme";
import { GlobalStyle } from "./styles/GlobalStyle";
import Header from "./components/Header";
import Button from "./components/Button";

function App() {
return (
<ThemeProvider theme={theme}>
<GlobalStyle />
<Header />
<Button />
</ThemeProvider>
);
}

export default App;
Binary file added week3/src/assets/cover.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added week3/src/assets/img_1.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added week3/src/assets/img_2.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added week3/src/assets/img_3.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added week3/src/assets/img_4.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added week3/src/assets/img_5.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added week3/src/assets/img_6.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added week3/src/assets/img_7.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added week3/src/assets/img_8.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added week3/src/assets/img_9.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
121 changes: 121 additions & 0 deletions week3/src/components/Button.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
import { useEffect, useState } from "react";
import styled from "styled-components";
import {
easyCardList,
normalCardList,
hardCardList,
} from "../constants/cardImgList";
import CommonPage from "./CommonPage";

const Button = () => {
const levelData = ["EASY", "NORMAL", "HARD"];
// 기본 난이도 'EASY'로 설정
const [level, setLevel] = useState(0);
const [cardList, setCardList] = useState(easyCardList);
const [shuffle, setShuffle] = useState(0);

useEffect(() => {
switch (level) {
case 0:
setCardList(easyCardList);
break;

case 1:
setCardList(normalCardList);
break;

case 2:
setCardList(hardCardList);
break;

default:
break;
}
}, [level, shuffle]);

return (
<>
<ResetButton
type="button"
onClick={() => {
setShuffle((prev) => prev + 1);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

왜 리셋버튼을 누르면 setShuffle에 +1을 하는거야??!

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

나두 궁금!!!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

아! 이거 reset 버튼이 클릭될 때마다 위에 페이지들을 다시 렌더링시켜서 카드 배열을 변하게 해주려고 shuffle을 생성했어! 그래서 버튼이 클릭될 때마다 shuffle 값에 변화를 줘서 useEffect가 실행될 수 있게 구현해봤어..!!

}}
>
RESET
</ResetButton>

<LevelBtnContainer>
{levelData.map((data, idx) => {
return (
<LevelButton
key={idx}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

키값 잊지않구 준거 칭찬해~!

type="button"
isClick={idx === level}
onClick={() => {
setLevel(idx);
}}
>
{data}
</LevelButton>
);
})}
</LevelBtnContainer>

{<CommonPage cardList={cardList} />}
</>
);
};

export default Button;

const ResetButton = styled.button`
display: flex;
justify-content: center;
position: absolute;
z-index: 1;
top: 2rem;
right: 0;
width: 7rem;
margin: 1rem;
padding: 1.5rem 1rem;
border: 0;
border-radius: 1rem;
box-shadow: 0.3rem 0.3rem 0.3rem ${({ theme }) => theme.colors.lightGreen};
background-color: ${({ theme }) => theme.colors.darkGreen};
color: ${({ theme }) => theme.colors.white};

&:hover {
background-color: ${({ theme }) => theme.colors.white};
color: ${({ theme }) => theme.colors.darkGreen};
}

font-family: ${({ theme }) => theme.font.buttonFont};
font-size: 1.5rem;
`;

const LevelBtnContainer = styled.div`
display: flex;
justify-content: center;
align-items: center;
position: absolute;
top: 13rem;
left: 0;
right: 0;
`;

const LevelButton = styled.button`
margin: 0 1rem;
padding: 1rem 1.5rem;
box-shadow: 0.3rem 0.3rem 0.3rem ${({ theme }) => theme.colors.purple};
border: 0;
border-radius: 1rem;

background-color: ${({ theme, isClick }) =>
isClick ? theme.colors.purple : theme.colors.lightPink};

color: ${({ theme, isClick }) =>
isClick ? theme.colors.lightPink : theme.colors.purple};

font-family: ${({ theme }) => theme.font.buttonFont};
font-size: 1.3rem;
`;
105 changes: 105 additions & 0 deletions week3/src/components/CommonPage.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
import styled from "styled-components";
import { useEffect, useState, useMemo } from "react";
import SingleCard from "./SingleCard";
import Header from "./Header";
import Modal from "./Modal";
import ModalPortal from "./ModalPortal";

const CommonPage = (cardList) => {
const [choiceOne, setChoiceOne] = useState(null);
const [choiceTwo, setChoiceTwo] = useState(null);
Comment on lines +9 to +10
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

각각 state로 만들지 않고 요런식으로 배열로 관리하는건 오때?!

Suggested change
const [choiceOne, setChoiceOne] = useState(null);
const [choiceTwo, setChoiceTwo] = useState(null);
const [choices, setChoices] = useState([]);

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

옹 참고해보께!

const [counter, setCounter] = useState(0);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

점수를 나타내는거면 좀 더 직관적으로 score라고 변수명 지어도 좋을거같아!! ㅎㅎ

const [modalOn, setModalOn] = useState(false);
const [disabled, setDisabled] = useState(false);

const flippedCard = document.getElementsByClassName("flipped");

// useMemo() 활용하여 cardList가 변경되지 않을 경우, 이전 값을 재사용하도록 구현
const copiedCardList = useMemo(() => {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

오 useMemo 사용햇구나 싱기...!

Copy link
Contributor Author

@Arooming Arooming May 10, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

웅 useMemo에 dependency로는 cardList를 줘서 리스트가 변하지 않을 경우에는 카드 배열이 변하지 않도록 했어..!! 안그러면 카드를 하나 클릭할 때마다 계속 카드 배열이 변하더라구 흑흑

setCounter(0);
// JSON.parse(JSON.stringify(obj)): 깊은 복사
const copied = JSON.parse(
JSON.stringify(
// Object.keys(): 객체를 문자열 배열로 변환
Object.keys(cardList)
.map((item) => cardList[item])
// flat(): 하나의 배열로 만들고자 사용
.flat()
)
);
// 카드 선택을 하다가 중간에 reset 버튼이나 레벨 선택을 다시 하면 이전에 저장되어 있던 카드 선택 정보 모두 삭제
copied.map((it) => (it.matched = false));
Comment on lines +20 to +31
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

복사한 두 카드가 원본 객체를 참조하기 때문에 JSON.parse(JSON.stringify(obj))로 깊은 복제를 해서 새로운 개체로 만든거징??
나도 두 개가 동시에 뒤집혔었는데 그래서 나는 카드에 answer라는 프로퍼티를 따로 추가해줘서 이걸로 매칭 여부를 판별하고, id를 통해서 뒤집혔는지 여부를 판별했어!!


return copied.sort(() => Math.random() - 0.5);
}, [cardList]);

// 값이 null이 아니면 이미 해당 값은 선택되어 있다는 것
const handleChoice = (card) => {
choiceOne ? setChoiceTwo(card) : setChoiceOne(card);
};

useEffect(() => {
if (choiceOne && choiceTwo) {
// 두 개의 카드가 같은지 확인하는 동안 다른 카드 선택 불가
setDisabled(true);

if (choiceOne.name === choiceTwo.name) {
setChoiceOne((choiceOne.matched = true));
setChoiceTwo((choiceTwo.matched = true));
setCounter((prev) => prev + 1);
resetTurn();

flippedCard.length === copiedCardList.length
? setModalOn(true)
: setModalOn(false);
Comment on lines +52 to +54
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
flippedCard.length === copiedCardList.length
? setModalOn(true)
: setModalOn(false);
setModalOn(flippedCard.length === copiedCardList.length);

요렇게 하면 좀 더 간결하게 쓸 수 있겠당!ㅎㅎ

} else {
setTimeout(() => resetTurn(), 1000);
}
}
}, [choiceOne, choiceTwo, flippedCard, copiedCardList]);

// 두 개의 카드가 선택된 후, 각 카드 정보 초기화
const resetTurn = () => {
setChoiceOne(null);
setChoiceTwo(null);

// 검사가 끝나면 다른 카드 선택 가능
setDisabled(false);
};

return (
<>
<Header counter={counter} length={copiedCardList.length / 2} />

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

요기 현지가 위에 counter score로 바꾸는거 말한거처럼 length 도 �totalScore 같은걸로 바꿔주면 더 직관적일거 같아!!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

죠아!

<CardContainer>
{copiedCardList.map((data, idx) => {
return (
<SingleCard
key={idx}
data={data}
handleChoice={handleChoice}
flipped={data === choiceOne || data === choiceTwo || data.matched}
disabled={disabled}
/>
);
})}
</CardContainer>

{modalOn && (
<ModalPortal>
<Modal onClose={() => setModalOn(false)} />
</ModalPortal>
)}
</>
);
};

export default CommonPage;

const CardContainer = styled.div`
display: flex;
flex-wrap: wrap;
justify-content: center;
align-items: center;

margin: 20rem 7rem 0rem;
`;
Loading