-
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주차 기본/심화/생각 과제 ] 나메추 ✈️ #6
base: main
Are you sure you want to change the base?
Changes from all commits
d2b95e9
12fc93f
6b8502e
025fc19
14cdce7
25a5428
7eaee84
1625ad8
14e7edf
1db2a12
74704c5
8b700e9
dded72d
1b53d1c
f9f2da5
20cc63c
47bccb9
8e7065e
e4c91f5
85af4ec
ca5a099
365252c
704f5ae
73df96b
b0f32b1
f78b5c5
d4d0f3f
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,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 활용 | ||
|
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 }, | ||
], | ||
}, | ||
} |
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,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> | ||
</head> | ||
<body> | ||
<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,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" | ||
} | ||
} |
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
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. 수연이 리뷰에도 남겼던건데..! 나는 개인적으로 app.jsx에서는 프로젝트 전반에 필요한 설정들 (router, theme, globalStyle, 전역 적용 스타일.. 등)만 써주고 나머지는 하위 컴포넌트로 한 번 더 감싸서 모듈화 해주는편이야! 이 부분 한 번 고민해보고, Header, Container와 같은 컴포넌트를 |
||
</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; | ||
` |
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
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. 나는 가독성 측면에서 삼항 연산자를 중첩해서 사용하는걸 지양하는 편이야! 요기 중첩된 삼항연산자 로직에 대해서 한 번 생각해보고 더 나은 방향으로 개선해보면 좋을것 같아서 의견 남겨용 |
||
)} | ||
</> | ||
); | ||
}; | ||
|
||
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=['취향대로 추천', '랜덤 추천']; | ||
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 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; | ||
|
||
|
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}) => { | ||
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 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("다음으로"); | ||||||
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. 이렇게 단계마다 다른 부분들 때문에 매 단계를 다른 컴포넌트로 만들었는데 이렇게 하면 한 컴포안에서 바꿀 수 있군...!!! |
||||||
isLoaded(true); | ||||||
},[id]); | ||||||
|
||||||
const onAnswerClick=(idx)=>{ | ||||||
const newTemporary = temporaryAnswer[id].map((each,i)=>idx===i ? true : false); | ||||||
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.
Suggested change
-> 요거는 이런식으루 해도 될것 같당! |
||||||
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)) | ||||||
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 ( | ||||||
<> | ||||||
<Description>{questionList[id].question}</Description> | ||||||
<LevelBox> | ||||||
{id+1} / 3 | ||||||
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. 여기서 '3'도 상수로 바꿔주는건 어떨까용 ?!
Suggested change
이런식으로 활용했어요!! 사소하지만 magic number 하드코딩은 지양하는 습관을 들이는게 좋다구 생각합니당! 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. 단계 받아와서 하는 방법..!! 내거 하드코딩 잔친데 언니거엔 하드코딩이 없당👍🏻👍🏻 |
||||||
</LevelBox> | ||||||
<MainBox> | ||||||
{loaded && questionList[id].answer.map((each, idx)=> | ||||||
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. 언니 코드 보면서 map 사용하는 느낌을 많이 익힌거같아! map사용이 안익숙해서 어떻게 써야할지 잘몰랐거덩 |
||||||
<AnswerBox | ||||||
Comment on lines
+36
to
+37
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. 논리 연산자 엄청 잘 활용한다! !👍 |
||||||
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) | ||||||
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. 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=[{ | ||
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. 상수는 대문자로 바꿔주장!! 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. 이렇게 질문도 나눠서 해주니까 더 좋다!! 그리고 파일도 관련된거끼리 묶어두니까 훨씬 코드 이해하기 쉽구.. 리뷰반영할 때 나도 갈아엎어야겠어 ㅎㅎㅎㅎ |
||
id:1, | ||
question:'어느 지역을 가고 싶어?', | ||
answer:['유럽','북미','아시아'] | ||
},{ | ||
id:2, | ||
question:'어떤 컨셉의 여행을 좋아해?', | ||
answer:['힐링/휴양','관광/활동'] | ||
},{ | ||
id:3, | ||
question:'누구랑 함께 가?', | ||
answer:['혼자','친구/연인','가족'] | ||
}]; | ||
|
||
export const temporaryAnswer = [ | ||
[false,false,false], | ||
[false,false], | ||
[false,false,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.
요기 lang , title 설정도 바꿔주는거 잊지 말깅!