[한장희] sprint6#131
Merged
kiJu2 merged 2 commits intocodeit-bootcamp-frontend:React-한장희from Sep 20, 2025
Hidden character warning
The head ref may contain hidden characters: "React-\ud55c\uc7a5\ud76c-sprint5"
Merged
Conversation
Collaborator
|
스프리트 미션 하시느라 수고 많으셨어요. |
kiJu2
approved these changes
Sep 20, 2025
Comment on lines
+10
to
+14
| <ProductGrid | ||
| products={products} | ||
| gridSize={responsiveValues.bestPicContainerSize} | ||
| picSize={responsiveValues.bestPicSize} | ||
| /> |
Collaborator
There was a problem hiding this comment.
(참고만 해도 됩니다 😉)ProductGrid에 gridSize, picSize가 드릴링이 되는 것 같아서 한 번 리팩토링 해봤습니다 !
diff --git a/vite-project/src/components/BestProductSection/BestProducts.jsx b/vite-project/src/components/BestProductSection/BestProducts.jsx
index 82f5c22..468fddc 100644
--- a/vite-project/src/components/BestProductSection/BestProducts.jsx
+++ b/vite-project/src/components/BestProductSection/BestProducts.jsx
@@ -1,17 +1,30 @@
-import { useResponsivePage } from "../../hooks/useResponsivePage";
import ProductGrid from "../common/ProductGrid";
+import ProductCard from "../common/ProductCard";
+import { useResponsivePage } from "../../hooks/useResponsivePage";
+import { DEVICE_STYLES } from "../common/DeviceClasses";
const BestProducts = ({ products }) => {
- const responsiveValues = useResponsivePage();
+ const { device } = useResponsivePage();
+
+ const containerClass = DEVICE_STYLES[device].best.container;
+ const pictureClass = DEVICE_STYLES[device].best.picture;
return (
<div className="flex flex-col gap-3 ">
<h2 className="text-xl font-bold ">베스트 상품</h2>
- <ProductGrid
- products={products}
- gridSize={responsiveValues.bestPicContainerSize}
- picSize={responsiveValues.bestPicSize}
- />
+ <ProductGrid>
+ {products.map((product) => (
+ <ProductCard
+ key={product.id}
+ className={containerClass}
+ name={product.name}
+ price={product.price}
+ favoriteCount={product.favoriteCount}
+ images={product.images}
+ picSizeClass={pictureClass}
+ />
+ ))}
+ </ProductGrid>
</div>
);
};diff --git a/vite-project/src/components/ProductListSection/Container.jsx b/vite-project/src/components/ProductListSection/Container.jsx
index 0d02c77..905a802 100644
--- a/vite-project/src/components/ProductListSection/Container.jsx
+++ b/vite-project/src/components/ProductListSection/Container.jsx
@@ -2,12 +2,14 @@ import { useState } from "react";
import useGetProducts from "../../hooks/useGetProducts";
import usePagination from "../../hooks/usePagination";
import ProductGrid from "../common/ProductGrid";
+import ProductCard from "../common/ProductCard";
import Pagination from "../ProductListSection/Pagination";
import searchIcon from "../../assets/searchIcon.svg";
import Dropdown from "./Dropdown";
-import { useResponsivePage } from "../../hooks/useResponsivePage";
import { Link } from "react-router-dom";
import { SORT_OPTIONS } from "../../constant/SORT_OPTIONS";
+import { useResponsivePage } from "../../hooks/useResponsivePage";
+import { DEVICE_STYLES } from "../common/DeviceClasses";
const Container = ({ pageSize, isMobile }) => {
const [orderBy, setOrderBy] = useState(SORT_OPTIONS[0].value);
@@ -17,7 +19,10 @@ const Container = ({ pageSize, isMobile }) => {
orderBy,
currentPage,
});
- const ResponsiveValues = useResponsivePage();
+ const { device } = useResponsivePage();
+
+ const containerClass = DEVICE_STYLES[device].normal.container;
+ const pictureClass = DEVICE_STYLES[device].normal.picture;
const handleSelect = (val) => {
console.log("$$", val);
@@ -92,11 +97,19 @@ const Container = ({ pageSize, isMobile }) => {
</div>
)}
- <ProductGrid
- products={products}
- picSize={ResponsiveValues.normalPicSize}
- gridSize={ResponsiveValues.normalPicContainerSize}
- />
+ <ProductGrid>
+ {products.map((product) => (
+ <ProductCard
+ key={product.id}
+ className={containerClass}
+ name={product.name}
+ price={product.price}
+ favoriteCount={product.favoriteCount}
+ images={product.images}
+ picSizeClass={pictureClass}
+ />
+ ))}
+ </ProductGrid>
<Pagination
currentPage={currentPage}
setCurrentPage={setCurrentPage}diff --git a/vite-project/src/components/common/ProductGrid.jsx b/vite-project/src/components/common/ProductGrid.jsx
index 3b5835d..871a009 100644
--- a/vite-project/src/components/common/ProductGrid.jsx
+++ b/vite-project/src/components/common/ProductGrid.jsx
@@ -1,30 +1,7 @@
-import ProductCard from "./ProductCard";
-import { GRID_SIZES } from "./ProductGridClasses";
-import { PIC_SIZES } from "./ProductGridClasses";
-
-const ProductGrid = ({
- gridSize = "grid224",
- picSize = "normalPicSize",
- products,
-}) => {
- const sizeClass = GRID_SIZES[gridSize] || "";
- const picSizeClass = PIC_SIZES[picSize] || "";
-
+const ProductGrid = ({ children }) => {
return (
<div className="flex flex-wrap justify-center gap-5">
- {products.map((product) => {
- return (
- <ProductCard
- key={product.id}
- className={`${sizeClass}`}
- name={product.name}
- price={product.price}
- favoriteCount={product.favoriteCount}
- images={product.images}
- picSizeClass={picSizeClass}
- />
- );
- })}
+ {children}
</div>
);
};diff --git a/vite-project/src/components/common/ProductGridClasses.js b/vite-project/src/components/common/ProductGridClasses.js
deleted file mode 100644
index 8ad09e3..0000000
--- a/vite-project/src/components/common/ProductGridClasses.js
+++ /dev/null
@@ -1,13 +0,0 @@
-export const GRID_SIZES = {
- grid168: "w-[168px]",
- grid224: "w-56 h-72",
- grid228: "w-72 h-[378px]",
- grid343: "w-[343px] h-[434px]",
-};
-
-export const PIC_SIZES = {
- normalPicSize: "w-56 h-56 ",
- normalPicMobile: "w-[168px] h-[168px]",
- bestPicSize: "w-[282px] h-[282px]",
- bestPicTabletMobile: "w-[343px] h-[343px]",
-};
diff --git a/vite-project/src/hooks/useResponsivePage.js b/vite-project/src/hooks/useResponsivePage.js
index edf831d..d4ca7ed 100644
--- a/vite-project/src/hooks/useResponsivePage.js
+++ b/vite-project/src/hooks/useResponsivePage.js
@@ -17,6 +17,6 @@ export const useResponsivePage = () => {
return () => window.removeEventListener("resize", onResize);
}, []);
- const values = useMemo(() => CONFIG[device], [device]);
+ const values = useMemo(() => ({ ...CONFIG[device], device }), [device]);
return values;
};핵심은 ProductGrid의 역할이 프롭스를 전달만 해주는 것 같아서요 !
일관적인 스타일 용도로만 사용하도록 바꿔봤습니다 !
참고만 하시고 사용하지 않으셔도 됩니다 😉
Comment on lines
+3
to
+40
| const Input = ({ | ||
| inputTypeStyle = "basic", | ||
| as, | ||
| type, | ||
| id, | ||
| imgUrl, | ||
| onChange, | ||
| onClick, | ||
| onKeyDown, | ||
| ...props | ||
| }) => { | ||
| const styleClass = INPUT_TYPE_STYLE[inputTypeStyle] || ""; | ||
| const Component = as || "input"; | ||
|
|
||
| return ( | ||
| <div> | ||
| {type === "file" ? ( | ||
| <div className="flex gap-8"> | ||
| <div className="flex flex-col gap-4"> | ||
| <label className={`${styleClass} bg-gray-100 cursor-pointer`}> | ||
| <img src={props.src} alt={props.alt} /> | ||
| <Component | ||
| accept="image/*" | ||
| type={type} | ||
| {...props} | ||
| id={id} | ||
| className="hidden" | ||
| onChange={onChange} | ||
| /> | ||
| </label> | ||
| {imgUrl && ( | ||
| <p className="text-red-400"> | ||
| *이미지 등록은 최대 1개까지 가능합니다 | ||
| </p> | ||
| )} | ||
| </div> | ||
| {imgUrl && ( | ||
| <div className="relative"> |
Collaborator
There was a problem hiding this comment.
오호. input 외에 다른 컴포넌트로 사용될 수 있도록 해놓으셨군요.
확장성을 고려하신 것 같군요? 장희님을 보면 참 도전적이고 학습에 열려있는 수강생이란 느낌이 많이 들어요 ! 리스펙합니다 🥺
다만, 하나의 컴포넌트에서 많은 역할을 수행하려다 보니 조금 복잡해진 느낌이 없잖아 있군요 !
다음과 같이 분리해보는 것도 고려해보실 수 있을 것 같습니다 😊:
const InputText = ({ styleClass, ...props }) => (
<input className={`${styleClass} bg-gray-100`} {...props} />
);
const InputFile = ({ styleClass, imgUrl, onDelete, ...props }) => (
<div className="flex gap-8">
<div className="flex flex-col gap-4">
<label className={`${styleClass} bg-gray-100 cursor-pointer`}>
<input type="file" className="hidden" {...props} />
</label>
{imgUrl && <p className="text-red-400">*이미지 등록은 최대 1개</p>}
</div>
{imgUrl && (
<div className="relative">
<img src={imgUrl} alt="preview" />
<img src="/ic_X.svg" alt="삭제" onClick={onDelete} />
</div>
)}
</div>
);
export default function Input({ type, ...props }) {
const styleClass = INPUT_TYPE_STYLE[props.inputTypeStyle] || "";
if (type === "file") return <InputFile styleClass={styleClass} {...props} />;
return <InputText styleClass={styleClass} type={type} {...props} />;
}| return ( | ||
| <div> | ||
| <Header /> | ||
| <main className="max-w-screen-xl px-4 mt-6 md:px-6 md:mx-auto"> |
Comment on lines
+41
to
+51
| const handleHashTagEnter = (e) => { | ||
| if (e.key === "Enter") { | ||
| e.preventDefault(); | ||
| const newTag = e.target.value.trim(); | ||
| if (newTag && !hashtags.includes(newTag)) { | ||
| setHashtags((prev) => [...prev, newTag]); | ||
| e.target.value = ""; | ||
| handleInputChange(); | ||
| } | ||
| } | ||
| }; |
Collaborator
There was a problem hiding this comment.
사용성까지 고려한 꼼꼼함.. ✨✨
엔터까지 고려하셨군요 ! 🥺
Collaborator
|
크으.. 장희님 수고하셨습니다. 그리고.. 번아웃.. 이시라면서요 ㄷㄷ... 무튼, 이번 미션 정말 수고 많으셨습니다 장희님 💪💪 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
체크리스트 [기본]
체크리스트 [심화]