SSF SHOP을 모티브로한 의류 소개 사이트
👤 회원가입 및 로그인
👕 위코드 멘토님들 및 동기분들의 의류소개
✏️ 카테고리 별 브랜드 검색 및 필터링
🛒 장바구니
$ git clone https://github.com/sujeong-dev/39-1st-WCF-frontend.git
$ git pull origin main
$ npm install
$ npm start.
├── Router.js
├── components
│ ├── Footer
│ │ ├── Footer.js
│ │ └── Footer.scss
│ └── Nav
│ ├── MainCtgData.js
│ ├── Nav.js
│ ├── Nav.scss
│ └── SpecialCtgData.js
├── config.js
├── images
│ ├── cart
│ │ ├── IMG_8905.JPG
│ │ ├── bag.png
│ │ ├── basket-img.png
│ │ ├── bg-component.png
│ │ ├── btn-x.svg
│ │ └── btn_x_gray.svg
│ ├── icon
│ │ ├── btn-sprite.png
│ │ ├── heart-stripe.png
│ │ ├── plus-minus-stripe.png
│ │ ├── signin
│ │ │ ├── term-check-off.svg
│ │ │ ├── term-check-on.svg
│ │ │ ├── term-check.svg
│ │ │ ├── vaild-check-purple.svg
│ │ │ └── valid-check.svg
│ │ ├── sns-icons.png
│ │ └── star-spripe.png
│ └── productdetail
│ └── banner-1.jpg
├── index.js
├── pages
│ ├── Cart
│ │ ├── Cart.js
│ │ ├── Cart.scss
│ │ └── components
│ │ ├── CartEmpty.js
│ │ └── CartFilled.js
│ ├── Login
│ │ ├── Login.js
│ │ └── Login.scss
│ ├── Main
│ │ ├── HotBrand.js
│ │ ├── HotBrand.scss
│ │ ├── Main.js
│ │ ├── Main.scss
│ │ ├── Marketing.js
│ │ ├── Marketing.scss
│ │ ├── OUR_PICKS.js
│ │ ├── OurPicks.js
│ │ └── OurPicks.scss
│ ├── MyPage
│ │ ├── MyPage.js
│ │ └── MyPage.scss
│ ├── Payment
│ │ ├── Payment.js
│ │ └── Payment.scss
│ ├── ProductDetail
│ │ ├── CartConfirm
│ │ │ ├── CartConfirm.js
│ │ │ └── CartConfirm.scss
│ │ ├── ImgArea
│ │ │ └── ImgArea.js
│ │ ├── ProductDetail.js
│ │ └── ProductDetail.scss
│ ├── ProductList
│ │ ├── Brand
│ │ │ ├── Brand.js
│ │ │ └── Brand.scss
│ │ ├── Price
│ │ │ ├── Price.js
│ │ │ └── Price.scss
│ │ ├── Product
│ │ │ ├── Product.js
│ │ │ └── Product.scss
│ │ ├── ProductList.js
│ │ ├── ProductList.scss
│ │ └── Size
│ │ ├── Size.js
│ │ └── Size.scss
│ └── SignIn
│ ├── SignIn.js
│ ├── SignIn.scss
│ └── termData.js
└── styles
├── common.scss
├── reset.scss
└── variables.scss
어떻게하면 같은 자리에 선택한 탭(브랜드, 가격, 사이즈)별로 다른 내용을 보여주게끔 할 수 있을까?고민하다가 처음에는 state에 선택한 탭의 Id값을 저장해서 조건부연산자로 구현하였다.
그렇지만 조건이 3개나 있어 조건을 주기가 애매했고 가독성이 떨어지는 문제점이 발생했고 알아본 결과 객체를 사용한 조건부 렌더링으로 해결하였다.
👇 ProductList.js
const TABS = [
{
id: 0,
title: '브랜드',
content: <Brand />,
},
{
id: 1,
title: '가격',
content: <Price />,
},
{
id: 2,
title: '사이즈',
content: <Size />,
},
];
<div className="left-filter">
<ul className="filter-list">
{TABS.map(filter => {
const isCurrent = currentTab === filter.id;
return (
<li key={filter.id}>
<button
onClick={() =>
setCurrentTab(isCurrent ? '' : filter.id)
}
className={isCurrent ? 'current' : ''}
>
{filter.title}
</button>
</li>
);
})}
</ul>
</div>
{TABS.find(({ id }) => id === currentTab)?.content}
TAB객체를 활용하여 id, content를 매칭하여 조건부 렌더링 구현map을 돌려title을 불러와 필터링 버튼을 생성하고 click시 현재 tab의 id값을 설정한다.- 현재 tab의 id를
TAB객체에서 찾아 해당하는 id의 content인 컴포넌트를 불러온다.옵셔널체이닝을 통해TAB객체에서 현재 tab의 id가 없는 경우, 즉 tab이 다 닫혀있는 경우 content를 불러올 수 없기 때문에 예외처리를 해주었다.
** 현재 열려있는 tab을 한번 더 click시 현재 tab의 id와TAB객체의 id가 같은 지 판별하여 현재 tab의 id를 reset하여 toggle기능 구현
브랜드, 가격, 사이즈 querystring으로 담아 필터링 구현
👇 Brand.js
const [searchParams, setSearchParams] = useSearchParams();
//TODO: 브랜드 검색기능
const searchBrand = e => {
const filterBrand = BRAND.filter(brand =>
brand.name.includes(e.target.value)
);
setFilterList(filterBrand);
};
//TODO: querystring 생성
const prevQuery = searchParams.getAll('brandId');
const handleCheckbox = e => {
const { checked, value } = e.target;
if (checked) {
searchParams.append('brandId', value);
setSearchParams(searchParams);
} else {
searchParams.delete('brandId');
prevQuery
.filter(query => query !== value)
.forEach(query => searchParams.append('brandId', query));
setSearchParams(searchParams);
}
};
- checkbox에 check가 되었으면 searchparams에 append()를 통해 추가한다.
- check가 해제되었으면 해당 param key의 value들을 모두 삭제하고 이전에 모든 value들을 담아놓은
prevQuery변수에서 e.target.value인 것들을 제외한 value들을 다시 searchparams에 담는다.
리스트페이지에서 의류를 선택 시 상세페이지로 넘어가야하는데 그럼 그 많은 상세페이지를 다 만들어야하나 고민하다가 동적라우팅으로 해결하였습니다.
👇 Product.js
<li>
<Link to={`/product-detail/${id}`}>
<img src={imgurl} alt="상품이미지" />
<div className="like" />
<div className="info">
<span className="brand">{brand}</span>
<span className="name">{name}</span>
<span className="price">{Number(price).toLocaleString()}</span>
<span className="heart">
<i className="fa-regular fa-heart" />
<em>999+</em>
</span>
</div>
</Link>
</li>👇 Router.js
<BrowserRouter>
<Nav />
<Routes>
<Route path="/" element={<Main />} />
<Route path="/product-detail/:productId" element={<ProductDetail />} />
<Route path="/product-list" element={<ProductList />} />
</Routes>
<Footer />
</BrowserRouter>
Router.js의 path prop에:productId로 이름을 지정한다.- 상품리스트페이지에서 어떠한 품목을 선택해도 상품 상세페이지로 이동하게된다.
Daily Scrum
Weekly Sprint
GitBook
Postman
- Notion과 Trello를 사용하여 scrum, sprint 진행
- GitBook과 Postman을 활용하여 API 및 mockdata형식 공유
- 컴포넌트의 파일명은 기본적으로 Pascal case를 사용한다. (ex. H3.js, Button.js)
- 여러 컴포넌트에서 import 하여 사용되는(utils) 파일명은 Camel case를 사용한다. (ex. useInputs.js)
- 변수명은 기본적으로 Camel case를 사용한다. (ex. className)
- 가능한 명사를 사용한다.
- 배열과 같이 여러개의 원소를 담는 경우 복수형 명사를 사용한다.
- 컴포넌트명은 기본적으로 파일명과 동일하게한다.
- 컴포넌트 외에 함수는 변수명과 동일하게 Camel case를 사용한다.
- 제일 바깥에 있는 부모 태그는 “컴포넌트명 container” 로 지정한다.
- className은 kebab-case로 작성한다.(ex. login-container)
- 공용으로 사용하는(ex. 로고 이미지)이미지들은 ‘public/images’에 kebab-case로 작성한다.(ex. logo-image)
- 각자 컴포넌트에서 사용하는(ex. 메인페이지-사진들, 상품상세페이지-사진들)이미지들은 ‘src/images’에 컴포넌트명으로 폴더생성 후 kebab-case로 작성한다.(ex. main-image)
FrontEnd
구수정 |
박문영 |
오주형 |
정효원 |
- 박문영 : 상품 상세 페이지
- 오주형 : 로그인 및 회원가입 페이지, 장바구니 페이지
- 정효원 : 메인페이지(navbar, footer)
BackEnd
이영서 |
박상욱 |
- 박상욱 : DB 모델링, 장바구니 API



