주어진 더미 데이터 API를 활용하여 쇼핑몰을 구현하는 프로젝트이다.
구조
1-1. Header
1-2. Product List
1-3. Bookmark List
1-4. Footer
1-5. Menu Bar
2-1. Category
2-2. Product List
3-1 . Category
3-2. Bookmark List
4-1. Toast
4-2. Bookmark
크게 위와 같이 구조를 잡고 프로젝트를 진행하였다.
import Header from "./components/Header";
import './App.css';
import Footer from "./components/Footer";
import MainPage from "./pages/MainPage";
import ProductList from "./pages/ProductList";
import BookmarkList from "./pages/BookmarkList";
import { Routes, Route } from 'react-router-dom';
import { useState } from "react";
import { ToastContainer } from "react-toastify";
import "react-toastify/dist/ReactToastify.css";
function App() {
const bookmarkRender = JSON.parse(localStorage.getItem("bookmark"));
const [bookmarkState, setBookmarkState] = useState(bookmarkRender);
return (
<div className="App">
<Header />
<main>
<Routes>
<Route path="/" element={<MainPage
bookmarkState={bookmarkState}
setBookmarkState={setBookmarkState}
/>} />
<Route path="/product/list" element={<ProductList
bookmarkState={bookmarkState}
setBookmarkState={setBookmarkState}
/>} />
<Route path="/bookmark" element={<BookmarkList
bookmarkState={bookmarkState}
setBookmarkState={setBookmarkState}
/>} />
</Routes>
</main>
<Footer />
<ToastContainer position="bottom-right" />
</div>
);
}
export default App;
Other 기능 중 Toast 기능은 App의 제일 위에 올려져야 한 번만 실행되므로 App.js에 삽입하였다.
Pages(Main, Product, Bookmark)
import React, { useState, useEffect } from "react";
import MainListItems from "../components/MainListItems";
import MainBookmarkItems from "../components/MainBookmarkItems";
import axios from "axios";
const MainPage = () => {
const [bookmarkState, setBookmarkState] = useState([]);
const [itemList, setItemList] = useState([]);
const url = "http://cozshopping.codestates-seb.link/api/v1/products?count=4";
useEffect(() => {
axios.get(url).then(res => {
setItemList(res.data);
});
}, []);
useEffect(() => {
const storedBookmarks = JSON.parse(localStorage.getItem("bookmark")) || [];
setBookmarkState(storedBookmarks);
}, []);
return (
<main>
<h2>상품 리스트</h2>
<MainListItems
itemList={itemList}
bookmarkState={bookmarkState}
setBookmarkState={setBookmarkState}
/>
<h2>북마크 리스트</h2>
<MainBookmarkItems
bookmarkState={bookmarkState}
setBookmarkState={setBookmarkState}
/>
</main>
);
};
export default MainPage;
import React, { useState, useEffect } from "react";
import axios from "axios";
import ItemList from "../components/ItemList";
import "./ProductList.css"
import all from "./all.png";
import product from "./product.png";
import category from "./category.png";
import exhibition from "./exhibition.png";
import brand from "./brand.png";
const ProductList = ({ item, bookmarkState, setBookmarkState, isBookmarked }) => {
const [itemListPage, setItemListPage] = useState([]);
const [selectedType, setSelectedType] = useState("All");
const url = "http://cozshopping.codestates-seb.link/api/v1/products";
const handleIsBookmarked = item => {
if (bookmarkState) {
return bookmarkState.some(x => x.id === item.id);
} else {
return false;
}
};
const handleSelectCategory = type => {
setSelectedType(type);
};
useEffect(() => {
axios.get(url).then(res => {
setItemListPage(res.data);
});
}, []);
const choose = [
{ img: all, name: "전체", type: "All" },
{ img: product, name: "상품", type: "Product" },
{ img: category, name: "카테고리", type: "Category" },
{ img: exhibition, name: "기획전", type: "Exhibition" },
{ img: brand, name: "브랜드", type: "Brand" },
];
return (
<div className="list">
<div className="choose">
{choose.map(category => (
<div
key={category.name}
onClick={() => handleSelectCategory(category.type)}
>
<img src={category.img} alt={category.name} className="category" />
<span>
{category.name}
</span>
</div>
))}
</div>
<ul className="itemList">
{
itemListPage.filter(item => selectedType === "All" ? true : item.type === selectedType).map(item => {
return (
<ItemList
key={item.id}
item={item}
isBookmarked={handleIsBookmarked(item)}
bookmarkState={bookmarkState}
setBookmarkState={setBookmarkState}
/>
)
})
}
</ul>
</div>
)
};
export default ProductList;
import React, { useState, useEffect } from "react";
import axios from "axios";
import ItemList from "../components/ItemList";
import "./ProductList.css"
import all from "./all.png";
import product from "./product.png";
import category from "./category.png";
import exhibition from "./exhibition.png";
import brand from "./brand.png";
const BookmarkList = ({ item, bookmarkState, setBookmarkState }) => {
const [itemListPage, setItemListPage] = useState([]);
const [selectedType, setSelectedType] = useState("All");
const url = "http://cozshopping.codestates-seb.link/api/v1/products";
const handleIsBookmarked = item => {
if (bookmarkState) {
return bookmarkState.some(x => x.id === item.id);
} else {
return false;
}
};
const handleSelectCategory = type => {
setSelectedType(type);
};
useEffect(() => {
axios.get(url).then(res => {
setItemListPage(res.data);
});
}, []);
const choose = [
{ img: all, name: "전체", type: "All" },
{ img: product, name: "상품", type: "Product" },
{ img: category, name: "카테고리", type: "Category" },
{ img: exhibition, name: "기획전", type: "Exhibition" },
{ img: brand, name: "브랜드", type: "Brand" },
];
return (
<div className="list">
<div className="choose">
{choose.map(category => (
<div
key={category.name}
onClick={() => handleSelectCategory(category.type)}
>
<img src={category.img} alt={category.name} className="category" />
<span>
{category.name}
</span>
</div>
))}
</div>
<ul className="itemList">
{
bookmarkState.filter(item => selectedType === "All" ? true : item.type === selectedType).map(item => {
return (
<ItemList
key={item.id}
item={item}
isBookmarked={handleIsBookmarked(item)}
bookmarkState={bookmarkState}
setBookmarkState={setBookmarkState}
/>
);
})}
</ul>
</div>
)
};
export default BookmarkList;
상품리스트 화면과 북마크리스트 화면은 구조가 완전히 동일하다. 코드에서도 알수있듯이 거의 90% 흡사하게 코드가 짜여있다. 다만 북마크리스트는 말 그대로 북마크 버튼이 활성화가 되어있는 상품들만 들여와야 하기에, itemListPage가 아닌 bookmarkState를 이용하여 필터링을 해주고 있는 것을 확인할 수 있다.
3개의 페이지가 결과물로 드러나 있는 화면이다. 다음 게시글에선 Pages를 제외한 각각의 Component 코드를 살펴보도록 하자.