-
Notifications
You must be signed in to change notification settings - Fork 6
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,4주차 과제 제출 #20
base: main
Are you sure you want to change the base?
Changes from all commits
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,34 @@ | ||
import { BrowserRouter, Routes, Route } from 'react-router-dom' | ||
import { RecoilRoot, atom, selector, useRecoilState, useRecoilValue,} from 'recoil'; | ||
import Home from './home/Home'; | ||
import Category from './home/Category'; | ||
import Product from './product/Product'; | ||
import Upload from './upload/Upload'; | ||
import Welcome from './login/Welcome'; | ||
import Login from './login/Login'; | ||
import SignUp from './login/SignUp'; | ||
|
||
|
||
function App() { | ||
return ( | ||
<RecoilRoot> | ||
<BrowserRouter> | ||
<Routes> | ||
<Route path="/" element={<Home />} /> | ||
<Route path="/welcome" element={<Welcome />} /> | ||
<Route path="/login" element={<Login />} /> | ||
<Route path="/sign-up" element={<SignUp />} /> | ||
<Route path={"/product/:product_id"} element={<Product />} /> | ||
<Route path={"/upload"} element={<Upload />} /> | ||
<Route path={"/select-category"} element={<Category />} /> | ||
<Route path="*" element={<h1> 존재하지 않는 URL입니다.</h1>} /> | ||
</Routes> | ||
</BrowserRouter> | ||
</RecoilRoot> | ||
); | ||
} | ||
|
||
export default App; | ||
|
||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
import { render, screen } from '@testing-library/react'; | ||
import App from './App'; | ||
|
||
test('renders learn react link', () => { | ||
render(<App />); | ||
const linkElement = screen.getByText(/learn react/i); | ||
expect(linkElement).toBeInTheDocument(); | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
|
||
![image](https://user-images.githubusercontent.com/101636590/219128383-2fdaa0bd-1273-4594-988a-c72e9416a45a.png) | ||
|
||
-local storage로 작성중인 글 내용 저장 구현 | ||
|
||
-recoil로 전역 변수 관리 | ||
|
||
-카테고리 검색 기능 추가 | ||
|
||
-useForm과 yup으로 회원가입, 로그인 구현 | ||
|
||
-styled components 적용 | ||
|
||
|
||
저번주에 제출못한 3주차 과제와 이번 4주차 과제입니다 | ||
|
||
회원가입 파트는 너무 어려워서 가은님이 작성한 코드를 이해하는 방향으로 공부했습니다 ㅎㅎ; | ||
|
||
대신이라하긴 뭐하지만 주어진 api 대신 직접 spring api 구현에서 연결하였습니다! | ||
|
||
|
||
|
||
|
||
https://khs20010327.tistory.com/142 | ||
|
||
https://khs20010327.tistory.com/143 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
|
||
import { atom, selector } from "recoil"; | ||
import { recoilPersist } from 'recoil-persist'; | ||
import axios from 'axios'; | ||
|
||
/* | ||
export const productData = selector({ | ||
key: 'productData', | ||
get: async ({ get }) => { | ||
const response = await axios.get('/products'); | ||
return response.data; | ||
}, | ||
}); | ||
*/ | ||
|
||
|
||
const product = [ | ||
{ pid: 0, pname: "닌텐도 스위치", price: 360000, heart: 6, seller: "이예일", category: "게임/취미"}, | ||
{ pid: 1, pname: "노트북", price: 1330000, heart: 0, seller: "이예일", category: "디지털기기" }, | ||
{ pid: 2, pname: "스마트폰", price: 770000, heart: 2, seller: "이예이", category: "디지털기기" }, | ||
{ pid: 3, pname: "오디오 스피커", price: 120000, heart: 3, seller: "이예이", category: "디지털기기" }, | ||
{ pid: 4, pname: "플레이스테이션", price: 330000, heart: 6, seller: "이예이", category: "게임/취미" }, | ||
{ pid: 5, pname: "보조배터리", price: 55000, heart: 11, seller: "이예이", category: "디지털기기" }, | ||
{ pid: 6, pname: "컴퓨터 모니터", price: 190000, heart: 8, seller: "이예삼", category: "디지털기기" }, | ||
{ pid: 7, pname: "헤드폰", price: 170000, heart: 0, seller: "이예사", category: "디지털기기" } | ||
]; | ||
|
||
export const productData = atom({ | ||
key: 'productData', | ||
default: product | ||
}) | ||
|
||
const categories = [ | ||
"디지털기기", "생활가전", "가구/인테리어", | ||
"유아동", "생활/가공식품", "유아도서", | ||
"여성의류", "남성패션/잡화", "게임/취미", | ||
"뷰티/미용", "반려동물용품", "도서/티켓/음반", | ||
"식물", "기타 중고물품", "중고차" | ||
]; | ||
|
||
export const categoryData = atom({ | ||
key: 'categoryData', | ||
default: categories | ||
}) | ||
|
||
export const categoryFilter = atom({ | ||
key: 'categoryFilter', | ||
default: '전체' | ||
}) | ||
|
||
|
||
/* | ||
let a = {"cid":2,"cname":"이예이","email":"abc","password":"1234","phone":"1234567","nickname":"일단은예일쓰"}; | ||
(() => { | ||
fetch("new?customer="+JSON.stringify(a), {method: "POST"}) | ||
.then((response) => { | ||
return response.json(); | ||
}) | ||
.then((data) => { | ||
pd_chg(data); | ||
}); | ||
})(); | ||
*/ | ||
|
||
const { persistAtom } = recoilPersist(); | ||
|
||
export const LoginStateAtom = atom({ | ||
key: 'LoginState', | ||
default: { | ||
state: false, | ||
accessToken: '', | ||
refreshToken: '', | ||
nickName: '', | ||
userId: '', | ||
}, | ||
effects_UNSTABLE: [persistAtom], | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
import { useNavigate } from 'react-router-dom' | ||
import styled from "styled-components"; | ||
|
||
function BackButton(){ | ||
const navigate = useNavigate(); | ||
return( | ||
<BackButtonStyle> | ||
<img alt="뒤로가기" src="/ui/back_button.png" | ||
onClick={() => {navigate(-1)}} /> | ||
</BackButtonStyle> | ||
); | ||
} | ||
export default BackButton; | ||
|
||
const BackButtonStyle = styled.div | ||
` | ||
img{ | ||
width: 50px; | ||
height: 50px; | ||
cursor: pointer; | ||
padding-left: 20px; | ||
paddint-top: 4px; | ||
} | ||
` |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
|
||
import styled from "styled-components"; | ||
import BackButton from './BackButton' | ||
|
||
function Header2({title}){ | ||
return( | ||
<Header2Style> | ||
<BackButton /> | ||
<h1> {title} </h1> | ||
<h2> 완료 </h2> | ||
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. h2는 버튼 기능을 하지 않습니다! 역할에 맞는 태그를 사용하면 좋을 것 같아요. 그리고 이벤트 리스너가 등록되어있지 않은데 아직 이부분 구현이 안된 걸까요? |
||
</Header2Style> | ||
); | ||
} | ||
export default Header2; | ||
|
||
const Header2Style = styled.div | ||
` | ||
position: fixed; | ||
top: 0; | ||
width: 100%; | ||
height: 80px; | ||
display: flex; | ||
justify-content: space-between; | ||
align-items: center; | ||
border-bottom: 1px solid gray; | ||
background-color: white; | ||
h1{ padding-bottom: 2px; } | ||
h2{ padding-right: 30px; visibility:hidden} | ||
` |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
import styled from "styled-components"; | ||
import BackButton from './BackButton' | ||
|
||
function Header3({title}){ | ||
return( | ||
<Header3Style> | ||
<BackButton /> | ||
<h1> {title} </h1> | ||
<h2> 완료 </h2> | ||
</Header3Style> | ||
); | ||
} | ||
export default Header3; | ||
|
||
const Header3Style = styled.div | ||
` | ||
position: fixed; | ||
top: 0; | ||
width: 100%; | ||
height: 80px; | ||
display: flex; | ||
justify-content: space-between; | ||
align-items: center; | ||
border-bottom: 1px solid gray; | ||
background-color: white; | ||
h1{ padding-bottom: 2px; } | ||
h2{ padding-right: 30px; color: orange; cursor: pointer; } | ||
` |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
import DefaultHeader from '../component/Header2'; | ||
import styled from 'styled-components'; | ||
import { useNavigate } from 'react-router-dom'; | ||
import { useSetRecoilState, useRecoilValue } from "recoil"; | ||
import { categoryData, categoryFilter } from "../Recoil"; | ||
|
||
|
||
|
||
function Category() { | ||
const navigate = useNavigate(); | ||
const setCategory = useSetRecoilState(categoryFilter); | ||
const CATEGORY = useRecoilValue(categoryData); | ||
return ( | ||
<> | ||
<DefaultHeader title="중고거래 카테고리" text=" "/> | ||
<SelectedCategoryStyle> | ||
{ | ||
CATEGORY.map((category, idx) => | ||
<button key={idx} onClick={() => { setCategory(category); navigate('/'); }}> | ||
{category} | ||
</button> | ||
) | ||
} | ||
</SelectedCategoryStyle> | ||
</> | ||
); | ||
} | ||
|
||
export default Category; | ||
|
||
|
||
const SelectedCategoryStyle = styled.div` | ||
margin-top: 70px; | ||
display: grid; | ||
grid-template-columns: 1fr 1fr 1fr; | ||
padding: 30px; | ||
text-align: center; | ||
|
||
button { | ||
height: 100px; | ||
border: none; | ||
background: white; | ||
|
||
&:hover { | ||
background-color: lightgray; | ||
transition: 0.2s; | ||
} | ||
} | ||
` | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
import { useState } from 'react'; | ||
import styled from "styled-components"; | ||
|
||
|
||
function HeartButton({heart}){ | ||
const [url, setUrl] = useState("/ui/heart_button.png"); | ||
return( | ||
<HeartStyle onClick={()=>{setUrl("/ui/red_heart.png")}}> | ||
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. 여기서 하트를 다시 눌렀을 때 취소가 되게 하려면 떻게 하는 게 좋을까요? |
||
<img alt="하트 버튼" src={url} /> <h2>{heart}</h2> | ||
</HeartStyle> | ||
); | ||
} | ||
|
||
export default HeartButton; | ||
|
||
const HeartStyle = styled.div | ||
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. 컴포넌트 이름에 style은 빼시고 역할 기반으로 작성해주세요. 또한 |
||
` | ||
img{ | ||
position: absolute; | ||
left: 330px; | ||
top:50%; | ||
width: 25px; | ||
height: 25px; | ||
} | ||
h2{ | ||
position: absolute; | ||
left: 370px; | ||
top: 27%; | ||
} | ||
` |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
import styled from "styled-components"; | ||
import HomeHeader from './HomeHeader' | ||
import ProductLists from './ProductLists' | ||
import HomeFooter from './HomeFooter' | ||
import PlusButton from './PlusButton' | ||
import { useRecoilValue } from "recoil"; | ||
import { categoryFilter, productData } from "../Recoil"; | ||
|
||
function Home() { | ||
|
||
const products = useRecoilValue(productData); | ||
const category = useRecoilValue(categoryFilter); | ||
|
||
return ( | ||
<HomeStyle> | ||
<HomeHeader/> | ||
{products.filter((p)=>p.category === category || category=="전체").map(p => <ProductLists key={p.pid} p = {p} />)} | ||
<HomeFooter/> | ||
<PlusButton/> | ||
</HomeStyle> | ||
); | ||
} | ||
|
||
export default Home; | ||
|
||
|
||
const HomeStyle = styled.div | ||
` | ||
padding-top: 70px; | ||
padding-bottom: 110px; | ||
` | ||
|
||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
import styled from "styled-components"; | ||
|
||
function HomeFooter() { | ||
return ( | ||
<HomeFooterSytle> | ||
<HomeButtonSytle> | ||
<img alt="홈 버튼" src="/ui/home_button.png"/> | ||
<h2>홈</h2> | ||
</HomeButtonSytle> | ||
<MyButtonSytle> | ||
<img alt="마이 버튼" src="/ui/my_button.png"/> | ||
<h2>나의 당근</h2> | ||
</MyButtonSytle> | ||
</HomeFooterSytle> | ||
); | ||
} | ||
|
||
export default HomeFooter; | ||
|
||
const HomeFooterSytle = styled.div` | ||
text-align: center; | ||
position: fixed; | ||
bottom: 0; | ||
width: 100%; | ||
height: 110px; | ||
|
||
display: flex; | ||
justify-content: space-around; | ||
|
||
border-top: 2px solid gray; | ||
background-color: white; | ||
|
||
h2{ margin: 0; } | ||
img{ width: 70px; height: 70px;} | ||
|
||
` | ||
const HomeButtonSytle = styled.div` cursor: pointer;` | ||
const MyButtonSytle = styled.div`cursor: pointer;` | ||
|
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.
Header2, Header3는 이 컴포넌트가 각각 무슨 역할을 위해 분리가 되었는지 명확하지 않은 네이밍인 것 같아요. 또 두 컴포넌트 간의 차이가 CSS 빼고 없는 것 같아서 CSS를 props 조건에 따라 바꿔주고, 두 컴포넌트를 하나로 합치는 게 낫지 않을까 싶습니다