-
Notifications
You must be signed in to change notification settings - Fork 20
[한동형] Sprint 5 #45
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
[한동형] Sprint 5 #45
The head ref may contain hidden characters: "React-\uD55C\uB3D9\uD615-sprint5"
Conversation
[Fix] delete merged branch github action
…-sprint1 [한동형] Sprint 1
|
스프리트 미션 하시느라 수고 많으셨어요. |
|
크으 커밋 메시지 너무 너무 깔끔해요 ! 👍👍👍👍 |
| const GlobalStyle = createGlobalStyle` | ||
| ${reset} | ||
|
|
||
| *{ | ||
| text-decoration: none; | ||
| list-style: none; | ||
| box-sizing: border-box; | ||
| } | ||
| @font-face { | ||
| font-family: 'Pretendard-Regular'; | ||
| src: url('https://fastly.jsdelivr.net/gh/Project-Noonnu/[email protected]/Pretendard-Regular.woff') format('woff'); | ||
| font-weight: 400; | ||
| font-style: normal; | ||
| } | ||
| @font-face { | ||
| font-family: 'ROKAF_Sans_Bold'; | ||
| src: url('./fonts/ROKAF_Sans_Bold.ttf') format('woff2'); | ||
| font-weight: 700; | ||
| font-size: normal; | ||
| } | ||
| body{ | ||
| font-family: Pretendard-Regular,ROKAF_Sans_Bold; | ||
| } | ||
| `; |
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.
오호 벌써 styled-components를..? 🫢🫢
| export const getItems = async (url) => { | ||
| const response = await fetch(`${baseUrl}${url}`); | ||
| const body = response.json(); | ||
| return body; | ||
| }; |
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.
굿굿 ! fetch 함수를 따로 레이어를 나누셨군요 ! 👍
관심사를 잘 분리하셨습니다 ! 훌륭해요 ! 👍👍
| @@ -0,0 +1,7 @@ | |||
| const baseUrl = "https://panda-market-api.vercel.app"; | |||
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.
base URL은 환경 변수에 저장하시는게 좋습니다!
환경 변수(Environment Variable):
process.env에 내장되며 앱이 실행될 때 적용할 수 있는 값입니다!
다음과 같이 적용할 수 있습니다:
// .env.development
REACT_APP_BASE_URL="http://localhost:3000"
// .env.production
REACT_APP_BASE_URL="http://myapi.com"
// 사용시
<a href={`${process.env.REACT_APP_BASE_URL}/myroute`}>URL</a>
왜 환경 변수에 저장해야 하나요?
개발(dev), 테스트(test), 실제 사용(prod) 등 다양한 환경에서 앱을 운영하게 되는 경우, 각 환경에 따라 다른 base URL을 사용해야 할 수 있습니다. 만약 코드 내에 하드코딩되어 있다면, 각 환경에 맞춰 앱을 배포할 때마다 코드를 변경해야 하며, 이는 매우 번거로운 작업이 됩니다. 하지만, 환경 변수를 .env.production, .env.development, .env.test와 같이 설정해두었다면, 코드에서는 단지 다음과 같이 적용하기만 하면 됩니다.
const apiUrl = `${process.env.REACT_APP_BASE_URL}/api`;
이러한 방식으로 환경 변수를 사용하면, 배포 환경에 따라 쉽게 URL을 변경할 수 있으며, 코드의 가독성과 유지보수성도 개선됩니다.
실제 코드 응용과 관련해서는 다음 한글 아티클을 참고해보세요! => 보러가기
| const AddProductButton = () => { | ||
| const navigate = useNavigate(); | ||
|
|
||
| const handleMoveAddProduct = () => { | ||
| navigate("/additem"); | ||
| }; | ||
|
|
||
| return ( | ||
| <AddProductButtonStyle onClick={handleMoveAddProduct}> | ||
| 상품 등록하기 | ||
| </AddProductButtonStyle> | ||
| ); | ||
| }; | ||
|
|
||
| export default AddProductButton; |
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.
(제안) 해당 컴포넌트는 Link로 작성할 수 있어요.
현재 해당 버튼 컴포넌트는 페이지 이동이 목적으로 보여요.
다음과 같이 작성되면 어떨까요?:
const AddProductButton = () => {
return (
<Link to="/additem">
<AddProductButtonStyle>
상품 등록하기
</AddProductButtonStyle>
</Link>
);
};
현재 하이퍼링크의 접근성을 활용하지 못하고 있기에 제안드립니다 😊
| const ProductsListWrapper = styled.ul` | ||
| display: flex; | ||
| width: ${({ browserWidth }) => { | ||
| if (767 < browserWidth && browserWidth < 1200) { | ||
| return "696px"; | ||
| } | ||
| if (374 < browserWidth && browserWidth < 768) { | ||
| return "344px"; | ||
| } | ||
| return "1200px"; | ||
| }}; | ||
| flex-wrap: wrap; | ||
| gap: ${({ browserWidth }) => { | ||
| if (374 < browserWidth && browserWidth < 1200) { | ||
| if (browserWidth < 767) { | ||
| return "8px"; | ||
| } | ||
| return "10px"; | ||
| } | ||
|
|
||
| return "23px"; | ||
| }}; | ||
| `; |
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.
(제안)browserWidth를 제거하고 미디어 쿼리로 변경하실 수 있습니다 !
const ProductsListWrapper = styled.ul`
display: flex;
flex-wrap: wrap;
gap: 23px;
max-width: 1200px;
@media (max-width: 1200px) {
max-width: 696px;
gap: 10px;
}
@media (max-width: 768px) {
max-width: 344px;
gap: 8px;
}
`;이렇게하면 props 변경에 따른 리렌더링을 방지할 수 있으며 props-drilling을 방지할 수 있습니다 !
props-drilling: 컴포넌트 트리에서 데이터를 하위 컴포넌트로 전달하기 위해 중간 컴포넌트를 통해 프로퍼티를 내려주는 것을 의미합니다.
| const [isOpen, setIsOpen] = useState(false); | ||
| const [selectedValue, setSelectedValue] = useState("최신순"); | ||
|
|
||
| const handleToggle = () => setIsOpen((prev) => !prev); | ||
| const handleSelect = (optionValue, optionLabel) => { | ||
| setSelectedValue(optionLabel); | ||
| setIsOpen(false); | ||
| onSortOrderChange(optionValue); | ||
| }; |
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.
상태 관리가 명확하네요 !
컴포넌트에 필요한 상태들만 컴팩트하게 정의하시고 핸들링하고 계십니다 ! 👍👍
| const Layout = () => { | ||
| return ( | ||
| <LayoutStyle> | ||
| <Header /> | ||
| <Main> | ||
| <Outlet /> | ||
| </Main> | ||
| </LayoutStyle> | ||
| ); | ||
| }; |
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.
크으 Layout도 정의하셨군요 👍👍
이렇게 되면 전역적인 레이아웃을 유지보수하기에 용이하겠군요 👍
| import DropdownProduct from "../components/DropdownProduct"; | ||
|
|
||
| const Items = () => { | ||
| const [items, setItems] = useState([]); | ||
| const [keyword, setKeyword] = useState(""); | ||
| const [sortOrder, setSortOrder] = useState("orderByLatest"); | ||
| const [browserWidth, setBrowserWidth] = useState(window.innerWidth); | ||
| const ONE_ITEM = 1; | ||
| const TWO_ITEMS = 2; | ||
| const FOUR_ITEMS = 4; |
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.
(제안) 컴포넌트와 무관한 변수는 컴포넌트 밖에 정의하시는게 좋습니다 !
| import DropdownProduct from "../components/DropdownProduct"; | |
| const Items = () => { | |
| const [items, setItems] = useState([]); | |
| const [keyword, setKeyword] = useState(""); | |
| const [sortOrder, setSortOrder] = useState("orderByLatest"); | |
| const [browserWidth, setBrowserWidth] = useState(window.innerWidth); | |
| const ONE_ITEM = 1; | |
| const TWO_ITEMS = 2; | |
| const FOUR_ITEMS = 4; | |
| import DropdownProduct from "../components/DropdownProduct"; | |
| const ONE_ITEM = 1; | |
| const TWO_ITEMS = 2; | |
| const FOUR_ITEMS = 4; | |
| const Items = () => { | |
| const [items, setItems] = useState([]); | |
| const [keyword, setKeyword] = useState(""); | |
| const [sortOrder, setSortOrder] = useState("orderByLatest"); | |
| const [browserWidth, setBrowserWidth] = useState(window.innerWidth); |
이렇게 하면 리렌더링에 따른 불필요한 재선언을 방지할 수 있으며 컴포넌트와 무관한 로직이 분리될 수 있기에 가독성이 향상될 수 있습니다 😊😊
| const filteredAndSortedItems = useMemo(() => { | ||
| let filtered = items.filter((item) => item.name.includes(keyword)); | ||
| if (sortOrder === "orderByFavoriteCount") { | ||
| filtered = filtered.sort( | ||
| (a, b) => b["favoriteCount"] - a["favoriteCount"] | ||
| ); | ||
| } else { | ||
| filtered = filtered.sort( | ||
| (a, b) => new Date(b["createdAt"]) - new Date(a["createdAt"]) | ||
| ); | ||
| } | ||
| return filtered; | ||
| }, [items, keyword, sortOrder]); |
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.
크으 메모이제이션까지 잘 활용하셨군요 👍👍
정렬과 같은 고비용 로직을 메모이제이션을 통하여 성능을 향상시키셨군요 👍
|
수고하셨습니다 !훌륭합니다 ! 라이브러리도 잘 활용하시고 리액트의 임베드 기능들을 속속히 잘 사용하고 계시네요 👍👍 |
요구사항
기본
Desktop : 4개 보이기
Tablet : 2개 보이기
Mobile : 1개 보이기
Desktop : 12개 보이기
Tablet : 6개 보이기
Mobile : 4개 보이기
심화
주요 변경사항
스크린샷
멘토에게