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주차 기본/심화/생각 과제 ] 나메추 ✈️ #6

Open
wants to merge 27 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
d2b95e9
init: 초기 세팅
nayujin-dev Nov 7, 2023
12fc93f
feat: 제목 컴포넌트 초기 구현
nayujin-dev Nov 7, 2023
6b8502e
feat: 전반적인 레이아웃 설정
nayujin-dev Nov 7, 2023
025fc19
feat: 추천 종류 선택
nayujin-dev Nov 8, 2023
14cdce7
fix: 컴포넌트 분리, 디자인 요소 분리
nayujin-dev Nov 8, 2023
25a5428
fix: 테스트 종류 전후, 취향 테스트 컴포넌트 분리
nayujin-dev Nov 8, 2023
7eaee84
feat: 답변 호버, 클릭시 스타일 변화 구현
nayujin-dev Nov 8, 2023
1625ad8
fix: 테스트 종류 선택 박스 디자인 수정
nayujin-dev Nov 9, 2023
14e7edf
feat: '다음으로' 버튼 비활성화 구현
nayujin-dev Nov 9, 2023
1db2a12
feat: 이전으로, 다음으로 버튼 눌렀을 때 이동 구현
nayujin-dev Nov 9, 2023
74704c5
feat: 이전으로 다음으로 버튼 hover 스타일 구현
nayujin-dev Nov 9, 2023
8b700e9
feat: 결과 페이지 레이아웃 구현
nayujin-dev Nov 9, 2023
dded72d
feat: 선택지에 따른 결과 계산
nayujin-dev Nov 9, 2023
1b53d1c
feat: 계산된 결과 보여주기
nayujin-dev Nov 9, 2023
f9f2da5
feat: 랜덤선택 카운트다운 구현
nayujin-dev Nov 10, 2023
20cc63c
feat: 랜덤으로 결과 보여주기
nayujin-dev Nov 10, 2023
47bccb9
feat: 다시하기 버튼 구현
nayujin-dev Nov 10, 2023
8e7065e
feat: 카운트다운 애니메이션 효과
nayujin-dev Nov 10, 2023
e4c91f5
feat: 처음으로 버튼 구현
nayujin-dev Nov 10, 2023
85af4ec
feat: 단계 노출 구현
nayujin-dev Nov 10, 2023
ca5a099
feat: 이전으로 돌아가도 답변 유지
nayujin-dev Nov 10, 2023
365252c
feat: useMemo로 최적화
nayujin-dev Nov 10, 2023
704f5ae
feat: globalStyle, theme 적용
nayujin-dev Nov 10, 2023
73df96b
feat: 생각과제
nayujin-dev Nov 10, 2023
b0f32b1
fix: css 애니메이션 theme 적용 안되어서 수정
nayujin-dev Nov 10, 2023
f78b5c5
fix: 카테고리 선택 hover css
nayujin-dev Nov 10, 2023
d4d0f3f
fix: 불필요한 파일 삭제
nayujin-dev Nov 13, 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
File renamed without changes.
42 changes: 42 additions & 0 deletions assignment/week03/thinking.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
## 🖤 리액트에 대하여

#### 컴포넌트는 어떤 기준과 방법으로 분리하는 것이 좋을까?
- `재사용성`과 `복잡성`
- 코드가 재사용될 가능성이 있다면 컴포넌트 분리를 고민해보자
- 코드가 복잡하다면, 가독성을 개선하고 유지보수 할 수 있도록 컴포넌트 분리를 고민해보자
- `Presentational Components` vs `Container Components`
- 과거 가장 유명한 기준 중 하나. 현재는 이 패턴 사용 지양
- 로직과 view를 분리하기 위해 등장한 개념
- 그를 분리하여 좀 더 재사용이 가능한 형태로 강제함
- 표현(마크업)과 로직을 분리해 복잡도를 낮춤.


#### 좋은 상태 관리란 무엇일까?
- 상태관리의 중요성: 불필요한 리렌더링 및 유지보수 개선
- 좋은 상태관리란?
- 무분별한 전역상태관리는 지양
- 상태 코드는 연관 컴포넌트들과 최대한 가까이 배치 지향
- 서버의 데이터를 전역 상태에 저장할때는, 명확한 전략을 갖도록 하자

#### 렌더링을 효과적으로 관리하는 방법은 무엇이 있을까?
- 적절한 위치에 state를 선언한다
- 객체 타입의 state 변형하지 않기
- 컴포넌트 맴핑시 key 값 명시
- shouldComponentUpdate() 메소드 활용
- React.memo()를 통한 메모이제이션
- useMemo(), useCallback() 활용


#### Props Drilling이란 무엇이고 이를 어떻게 해결할 수 있는가?
- `Props Drilling`: 컴포넌트 트리에서 데이터를 하위 컴포넌트로 전달하기 위해, 중간 컴포넌트를 통해 프로퍼티를 내려주는 것
- 이때, 중간 컴포넌트는 해당 값을 `직접 사용하지 않는 경우에도`, 하위에 전달하기 위해 프로퍼티를 받아서 전달해야한다는 불편함이 존재.
- 그 이외의 단점
1. 프로퍼티 데이터 형식 변경의 불편함
2. 누락된 프로퍼티 인지의 어려움
3. 프로퍼티 이름 변경 추적의 어려움
- 해결책
1. Context API 활용
2. Redux 또는 다른 상태 관리 라이브러리 사용
3. Custom Hooks
4. Render Props 패턴과 Children props 활용

20 changes: 20 additions & 0 deletions assignment/week03/travel-recommendation/.eslintrc.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
module.exports = {
root: true,
env: { browser: true, es2020: true },
extends: [
'eslint:recommended',
'plugin:react/recommended',
'plugin:react/jsx-runtime',
'plugin:react-hooks/recommended',
],
ignorePatterns: ['dist', '.eslintrc.cjs'],
parserOptions: { ecmaVersion: 'latest', sourceType: 'module' },
settings: { react: { version: '18.2' } },
plugins: ['react-refresh'],
rules: {
'react-refresh/only-export-components': [
'warn',
{ allowConstantExport: true },
],
},
}
24 changes: 24 additions & 0 deletions assignment/week03/travel-recommendation/.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?
8 changes: 8 additions & 0 deletions assignment/week03/travel-recommendation/README.md
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
14 changes: 14 additions & 0 deletions assignment/week03/travel-recommendation/index.html
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" />
<link rel="stylesheet" href="src/index.css"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite + React</title>
Comment on lines +2 to +8

Choose a reason for hiding this comment

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

요기 lang , title 설정도 바꿔주는거 잊지 말깅!

</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.jsx"></script>
</body>
</html>
27 changes: 27 additions & 0 deletions assignment/week03/travel-recommendation/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
{
"name": "travel-recommendation",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build",
"lint": "eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0",
"preview": "vite preview"
},
"dependencies": {
"react": "^18.2.0",
"react-dom": "^18.2.0",
"styled-components": "^6.1.0"
},
"devDependencies": {
"@types/react": "^18.2.15",
"@types/react-dom": "^18.2.7",
"@vitejs/plugin-react": "^4.0.3",
"eslint": "^8.45.0",
"eslint-plugin-react": "^7.32.2",
"eslint-plugin-react-hooks": "^4.6.0",
"eslint-plugin-react-refresh": "^0.4.3",
"vite": "^4.4.5"
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions assignment/week03/travel-recommendation/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.
49 changes: 49 additions & 0 deletions assignment/week03/travel-recommendation/src/App.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { useState } from 'react'
import Header from './components/Header'
import styled, {ThemeProvider} from 'styled-components'
import GlobalStyle from './styles/GlobalStyle';
import theme from './styles/theme';
import BeforeStart from './components/BeforeStart';
import AfterStart from './components/AfterStart';
function App() {
const [category,setCategory]=useState(-1);
const [isStarted,setIsStarted]=useState(false);
const [isHome,setIsHome]=useState(true);

return (
<ThemeProvider theme={theme}>
<GlobalStyle/>
<Header isHome={isHome} setIsStarted={setIsStarted} setIsHome={setIsHome} setCategory={setCategory}/>
<Container>
{isStarted?(
<AfterStart
setIsStarted={setIsStarted}
category={category}
/>
):(
<BeforeStart
isStarted={isStarted}
setIsStarted={setIsStarted}
category={category}
setCategory={setCategory}
setIsHome={setIsHome}
/>
)}
</Container>
Comment on lines +14 to +32

Choose a reason for hiding this comment

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

수연이 리뷰에도 남겼던건데..!

나는 개인적으로 app.jsx에서는 프로젝트 전반에 필요한 설정들 (router, theme, globalStyle, 전역 적용 스타일.. 등)만 써주고 나머지는 하위 컴포넌트로 한 번 더 감싸서 모듈화 해주는편이야!
나중에 페이지랑 컴포넌트들이 더 많아지게 될 때 이렇게 해줘야 가독성이 좋고 나중에 코드 다시 볼 때도 이해하기 쉽더라고!

이 부분 한 번 고민해보고, Header, Container와 같은 컴포넌트를 MainContainer와 같이 하나의 컴포넌트로 만들어서 App.jsx에서는 MainContainer만 import 할 수 있도록 해봐도 좋을것 같아~

</ThemeProvider>

)
}

export default App

const Container=styled.div`
display: flex;
position: relative;
flex-direction: column;
height: 50rem;
justify-content: center;
align-items: center;
background-color: pink;
margin: 5rem 7rem;
`
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import React, { useState } from 'react';
import Random from './Random/Random';
import NonRandom from './NonRandom/NonRandom';
import Result from './Result/Result';

const AfterStart = ({category, setIsStarted}) => {
const [id,setId]=useState(0);
const [calculator,setCalculator]=useState([0,0,0]);
return (
<>
{category==='랜덤 추천'?(
<Random setIsStarted={setIsStarted}/>
):(
id < 3
? <NonRandom
id={id} setId={setId}
setIsStarted={setIsStarted}
calculator={calculator} setCalculator={setCalculator}/>
: <Result calculator={calculator} setIsStarted={setIsStarted}/>
Comment on lines +14 to +19

Choose a reason for hiding this comment

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

나는 가독성 측면에서 삼항 연산자를 중첩해서 사용하는걸 지양하는 편이야!
조건문이 여러개이면 if-else if문을 활용하는 편이구,
컴포넌트 return 하는 부분에서 조건문이 복잡하게 걸려 있으면 보기 어려운것 같아 조건이 복잡한 경우에는 따로 render()함수를 만들어 놓고 사용하는 편이얌 !!

요기 중첩된 삼항연산자 로직에 대해서 한 번 생각해보고 더 나은 방향으로 개선해보면 좋을것 같아서 의견 남겨용

)}
</>
);
};

export default AfterStart;
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import React from 'react';
import { Btn, BtnBox, MainBox, TypeofRecommand,Description } from '../styles/Design';

const BeforeStart = ({isStarted,setIsStarted, category, setCategory, setIsHome}) => {
const categoryList=['취향대로 추천', '랜덤 추천'];

Choose a reason for hiding this comment

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

요건 상수데이터니까 대문자로 표기해주면 더 좋을것 같아!
CATEGORY_LIST 일케 ~

const onTypeClick=(category)=>{
category==='취향대로 추천'?setCategory('취향대로 추천'):setCategory('랜덤 추천');
setIsHome(false);
}
const onBtnClick=()=>{
setIsStarted(true);
}

return (
<>
<Description>원하는 추천 방식을 골라줘!</Description>
<MainBox>
{category==-1 ? (
categoryList.map((type,index)=>
<TypeofRecommand key={index} onClick={(e)=>onTypeClick(type,e)}>
{type}
</TypeofRecommand>)
):(
<TypeofRecommand selected={true}>
{category}
</TypeofRecommand>
)}
</MainBox>
<BtnBox>
{category!==-1 &&<Btn onClick={onBtnClick}>Start!</Btn>}
</BtnBox>
</>
);
};

export default BeforeStart;


29 changes: 29 additions & 0 deletions assignment/week03/travel-recommendation/src/components/Header.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import React from 'react';
import styled from 'styled-components';
import { GotoHomeBtn } from '../styles/Design';
import { temporaryAnswer } from './NonRandom/QuestionList';

const Header = ({isHome, setIsHome, setIsStarted, setCategory}) => {

Choose a reason for hiding this comment

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

구조분해할당 굿!!!! 짱이당!!!

const onClickHomeBtn=()=>{
setIsStarted(false);
setIsHome(true);
setCategory(-1);

temporaryAnswer.map((eachQ,qIdx) => temporaryAnswer[qIdx]=eachQ.map(() => false));
}
return (
<Container>
여행지 추천
{isHome||<GotoHomeBtn onClick={onClickHomeBtn}>처음으로</GotoHomeBtn>}
</Container>
);
};

export default Header;

const Container=styled.div`
padding: 1rem;
background-color: pink;
font-size:5rem;

`
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import React, { useEffect, useState } from 'react';
import { BtnBox, MainBox,Description,AnswerBox, Btn, LevelBox } from '../../styles/Design';
import { questionList, temporaryAnswer } from './QuestionList';

const NonRandom = ({id, setId, setIsStarted, calculator, setCalculator}) => {
const [nextBtnContent, setNextBtnContent]=useState('다음으로');
const [loaded,isLoaded]=useState(false);

useEffect(()=>{
id>=2 ? setNextBtnContent("결과보기") : setNextBtnContent("다음으로");
Copy link
Member

Choose a reason for hiding this comment

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

이렇게 단계마다 다른 부분들 때문에 매 단계를 다른 컴포넌트로 만들었는데 이렇게 하면 한 컴포안에서 바꿀 수 있군...!!!

isLoaded(true);
},[id]);

const onAnswerClick=(idx)=>{
const newTemporary = temporaryAnswer[id].map((each,i)=>idx===i ? true : false);

Choose a reason for hiding this comment

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

Suggested change
const newTemporary = temporaryAnswer[id].map((each,i)=>idx===i ? true : false);
const newTemporary = temporaryAnswer[id].map((each,i)=>return idx === i);

-> 요거는 이런식으루 해도 될것 같당!

temporaryAnswer[id]=newTemporary;
calculator[id]=idx;
setCalculator([...calculator]);
}

const onNextClick=()=>{
setId(id+1);
}
const onBackClick=()=>{
id-1>=0
? setId(id-1)
: (setIsStarted(false), setId(0))
Copy link
Member

Choose a reason for hiding this comment

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

setIsStarted(false) 해줘서 첫 단계에서 이전으로 누르면 추천방식고르는 페이지로 돌아가는건가???

}
return (
<>
<Description>{questionList[id].question}</Description>
<LevelBox>
{id+1} / 3

Choose a reason for hiding this comment

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

여기서 '3'도 상수로 바꿔주는건 어떨까용 ?!
지금은 3단계로 이루어져 있지만 나중에 단계가 추가되거나, 제거될수도 있는데 그때마다 숫자 값을 찾아서 변경하기엔 번거로울수 있습니댱
그래서 저는 TOTAL_STAGE 라는 상수를 하나 선언해놓고

Suggested change
{id+1} / 3
{id+1} / {TOTAL_STAGE}

이런식으로 활용했어요!!

사소하지만 magic number 하드코딩은 지양하는 습관을 들이는게 좋다구 생각합니당!

Copy link
Member

Choose a reason for hiding this comment

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

단계 받아와서 하는 방법..!! 내거 하드코딩 잔친데 언니거엔 하드코딩이 없당👍🏻👍🏻

</LevelBox>
<MainBox>
{loaded && questionList[id].answer.map((each, idx)=>
Copy link
Member

Choose a reason for hiding this comment

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

언니 코드 보면서 map 사용하는 느낌을 많이 익힌거같아! map사용이 안익숙해서 어떻게 써야할지 잘몰랐거덩

<AnswerBox
Comment on lines +36 to +37

Choose a reason for hiding this comment

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

논리 연산자 엄청 잘 활용한다! !👍

key={idx}
onClick={(e)=>onAnswerClick(idx,e)}
btncolor={temporaryAnswer[id][idx]}
>
{each}
</AnswerBox>
)}
</MainBox>
<BtnBox>
<Btn onClick={onBackClick}>이전으로</Btn>
{temporaryAnswer[id].some((element)=> element==true)
Copy link
Member

Choose a reason for hiding this comment

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

some메서드 좋다!!

? <Btn onClick={onNextClick}>{nextBtnContent}</Btn>
: <Btn disabled>{nextBtnContent}</Btn>}
</BtnBox>
</>

);
};

export default NonRandom;

Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
export const questionList=[{

Choose a reason for hiding this comment

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

상수는 대문자로 바꿔주장!! QUESTION_LIST 이렇게 하면 더 명확하게 상수임을 알수 있을것 같아!

Copy link
Member

Choose a reason for hiding this comment

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

이렇게 질문도 나눠서 해주니까 더 좋다!! 그리고 파일도 관련된거끼리 묶어두니까 훨씬 코드 이해하기 쉽구.. 리뷰반영할 때 나도 갈아엎어야겠어 ㅎㅎㅎㅎ

id:1,
question:'어느 지역을 가고 싶어?',
answer:['유럽','북미','아시아']
},{
id:2,
question:'어떤 컨셉의 여행을 좋아해?',
answer:['힐링/휴양','관광/활동']
},{
id:3,
question:'누구랑 함께 가?',
answer:['혼자','친구/연인','가족']
}];

export const temporaryAnswer = [
[false,false,false],
[false,false],
[false,false,false],
]
Loading