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

[염정훈] Sprint6 #176

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
/node_modules
/.pnp
.pnp.js
.env

# testing
/coverage
Expand Down
23 changes: 23 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
"@testing-library/user-event": "^13.5.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-native-dotenv": "^3.4.11",
"react-router-dom": "^6.23.1",
"react-scripts": "5.0.1",
"web-vitals": "^2.1.4"
Expand Down
Binary file added public/images/i-close.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 public/images/i-plus.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 public/images/i-profile.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion public/index.html
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<!DOCTYPE html>
<html lang="en">
<html lang="ko">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
Expand Down
9 changes: 4 additions & 5 deletions src/api.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
export async function getProducts({ pageSize, order }) {
const query = `pageSize=${pageSize}&orderBy=${order}`;
const response = await fetch(
`https://panda-market-api.vercel.app/products?page=1&${query}`
);
export async function getProducts({ page, pageSize, order }) {
const apiUrl = process.env.REACT_APP_API_URL;
const query = `page=${page}&pageSize=${pageSize}&orderBy=${order}`;
Copy link
Collaborator

Choose a reason for hiding this comment

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

닷엔브 활용해주신거 좋네요. 추가로 보통 baseURL이라는 이름도 많이 사용합니다!

const response = await fetch(`${apiUrl}.vercel.app/products?${query}`);
const body = await response.json();
return body;
}
157 changes: 157 additions & 0 deletions src/components/AddItem.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
import { useEffect, useRef, useState } from "react";

function AddItem() {
const [formData, setFormDate] = useState({
name: "",
info: "",
price: "",
tag: "",
});
Comment on lines +4 to +9
Copy link
Collaborator

Choose a reason for hiding this comment

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

폼으로 관련이 있는 상태끼리 객체로 관리 하시는 점 좋네요.

const [addItemBtn, setAddItemBtn] = useState("");
const [btnDisabled, setBtnDisabled] = useState(true);
const [inputFileUrl, setInputFileUrl] = useState(null);
const inputFileImg = useRef(null);
const imgAccept = ".jpg, .jpeg, .png";

const inputChange = (e) => {
// input value 상태 변경
const { name, value } = e.target;
setFormDate({
...formData,
[name]: value,
});
};

const inputFileChange = () => {
const inputFile = inputFileImg.current.files[0];
console.log(inputFile);

if (inputFile) {
const fileUrl = URL.createObjectURL(inputFile);
setInputFileUrl(fileUrl);
}
};

const addImgDelete = () => {
setInputFileUrl(null);
};

const validAddItem = () => {
// 유효성 검사
if (
formData.name !== "" &&
formData.info !== "" &&
formData.price !== "" &&
formData.tag !== ""
) {
setAddItemBtn("on");
setBtnDisabled(false);
} else {
setAddItemBtn("");
setBtnDisabled(true);
}
};
Comment on lines +39 to +53
Copy link
Collaborator

Choose a reason for hiding this comment

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

현재 방식과 큰 차이는 없지만, 조금 더 역할을 구분해주시는 방법도 추천드립니다. 예를 들어서 유효성 검사하는 함수 내부에서는

formData.name !== "" &&
formData.info !== "" &&
formData.price !== "" &&
formData.tag !== ""

이 로직에 대한 결과만 반환하고 useEffect 내부에서 이 함수의 결과를 보고 setState가 일어나는 방식으로요!


useEffect(() => {
// input 데이터 변경 될때마다 유효성 검사 실행
validAddItem();
}, [formData]);
Comment on lines +55 to +58
Copy link
Collaborator

Choose a reason for hiding this comment

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

상태에 따라서 매번 유효성 검사해주시는 로직을 useEffect를 활용해서 잘 작성해주셨네요!


return (
<section className="add-item-wrap max-wrap">
<form method="post">
<div className="add-item-title">
<h2>상품 등록하기</h2>
<button type="button" disabled={btnDisabled} className={addItemBtn}>
등록
</button>
</div>
<div className="add-input">
<h3 className="add-input-title">상품 이미지</h3>
<div className="add-img-list">
<label htmlFor="add-input-img" className="add-img-area">
<img src="/images/i-plus.png" alt="플러스 아이콘" />
이미지 등록
</label>
<input
id="add-input-img"
type="file"
ref={inputFileImg}
accept={imgAccept}
onChange={inputFileChange}
/>
{inputFileUrl ? (
<div className="add-img-item">
<img src={inputFileUrl} alt="상품 등록 이미지" />
<button type="button" onClick={addImgDelete}></button>
</div>
) : (
""
)}
</div>
</div>
<div className="add-input">
<label htmlFor="add-input-name" className="add-input-title">
상풍명
</label>
<input
id="add-input-name"
type="text"
name="name"
placeholder="상품명을 입력해주세요."
value={formData.name}
onChange={inputChange}
/>
</div>
<div className="add-input">
<label htmlFor="add-input-info" className="add-input-title">
상품 소개
</label>
<textarea
id="add-input-info"
name="info"
placeholder="상품 소개를 입력해주세요."
value={formData.info}
onChange={inputChange}
></textarea>
</div>
<div className="add-input">
<label htmlFor="add-input-price" className="add-input-title">
판매가격
</label>
<input
id="add-input-price"
type="number"
name="price"
placeholder="판매 가격을 입력해주세요."
value={formData.price === NaN ? null : formData.price}
onChange={inputChange}
/>
</div>
<div className="add-input">
<label htmlFor="add-input-tag" className="add-input-title">
태그
</label>
<input
id="add-input-tag"
type="text"
name="tag"
placeholder="태그를 입력해주세요."
value={formData.tag}
onChange={inputChange}
/>
<ul className="tag-list">
<li>
티셔츠 <button type="button"></button>
</li>
<li>
상의 <button type="button"></button>
</li>
</ul>
</div>
</form>
</section>
);
}

export default AddItem;
74 changes: 48 additions & 26 deletions src/components/AllProduct.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { Link } from "react-router-dom";
import { getProducts } from "../api.js";
import { useEffect, useState } from "react";

Expand All @@ -15,50 +16,70 @@ const responsivePageSize = () => {
}
};

const recent = "recent";
const favorite = "favorite";
Comment on lines +19 to +20
Copy link
Collaborator

Choose a reason for hiding this comment

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

보통 상수로 관리할때 상수라는 점을 표현하기 위해 변수명으로 대문자를 많이 사용합니다. RECENT 이런식으로요!


function AllProduct() {
const [products, setProducts] = useState([]);
const [page, setPage] = useState(1);
const [pageSize, setPageSize] = useState(10);
const [order, setOrder] = useState("recent");
const [order, setOrder] = useState(recent);
const [dropArrow, setDropArrow] = useState("");
const [dropDisplay, setDropDisplay] = useState("none");
const [orderTxt, setOrderTxt] = useState("최신순");
const [pagesNum, setPagesNum] = useState([1, 2]);

const handleLoad = async (options) => {
let { list } = await getProducts(options);
setProducts(list);
// console.log(list);
};

const handleDropClick = () => {
dropArrow === "" ? setDropArrow("on") : setDropArrow("");
dropDisplay === "none" ? setDropDisplay("block") : setDropDisplay("none");
setDropArrow(dropArrow === "" ? "on" : "");
setDropDisplay(dropDisplay === "none" ? "block" : "none");
};

const handleNewsOrder = (e) => {
const menuTxt = e.target.textContent;
setOrderTxt(menuTxt);
setDropArrow("");
setDropDisplay("none");
setOrder("recent");
setOrder(recent);
};

const handleBestOrder = (e) => {
const menuTxt = e.target.textContent;
setOrderTxt(menuTxt);
setDropArrow("");
setDropDisplay("none");
setOrder("favorite");
setOrder(favorite);
};

const pageNumClick = (page) => {
setPage(page);
};

const prevPageBtn = () => {
if (page !== 1) {
setPage(page - 1);
}
};

const nextPageBtn = () => {
if (page !== pagesNum.length) {
setPage(page + 1);
}
};

useEffect(() => {
handleLoad({ pageSize, order });
handleLoad({ page, pageSize, order });

const handleResize = () => {
setPageSize(responsivePageSize());
};

window.addEventListener("resize", handleResize);
}, [order, pageSize]);
}, [page, order, pageSize]);

return (
<>
Expand All @@ -67,7 +88,9 @@ function AllProduct() {
<h2>판매중인 상품</h2>
<div className="product-info-menu">
<input type="search" placeholder="검색할 상품을 입력해주세요." />
<button type="button">상품 등록하기</button>
<Link to="/AddItem" className="item-add-btn">
상품 등록하기
</Link>
<div className="drop-menu">
<p className={dropArrow} onClick={handleDropClick}>
{orderTxt}
Expand Down Expand Up @@ -118,27 +141,26 @@ function AllProduct() {
<div className="pageNav">
<ul>
<li>
<button type="button">
<button type="button" onClick={prevPageBtn}>
<img src="/images/i-arrow-left.png" alt="왼쪽 화살표" />
</button>
</li>
<li className="on">
<button type="button">1</button>
</li>
<li>
<button type="button">2</button>
</li>
<li>
<button type="button">3</button>
</li>
<li>
<button type="button">4</button>
</li>
<li>
<button type="button">5</button>
</li>
{pagesNum.map((pageNum) => {
return (
<li key={pageNum} className={pageNum === page ? "on" : ""}>
<button
type="button"
onClick={() => {
pageNumClick(pageNum);
}}
>
{pageNum}
</button>
</li>
);
})}
<li>
<button type="button">
<button type="button" onClick={nextPageBtn}>
<img src="/images/i-arrow-right.png" alt="왼쪽 화살표" />
</button>
</li>
Expand Down
Loading
Loading