🖐 주제
스포츠 웨어 쇼핑몰 브랜드 안다르 홈페이지 클론코딩
🖐 기간
10월 1일(금)~ 10월 15일(금): front-end 1주, back-end 1주
🖐 인원
full-stack 5명
🖐 github repository
모른다르: Back-end
모른다르: front-end
👏 Front-End
👏 Back-End:
안다르 홈페이지는 다양한 API와 화면구현 기능을 시도해볼 수 있고, 프론트 엔드 - 백엔드의 역할 분담이 적절하게 나누어져 있었다.
역할은 페이지 별 기능 분담을 했고, 같은 페이지로 front-end, back-end 역할을 할 수가 없었기 때문에, [유저]-[상품]-[메인] 큰 카테고리 세개로 구분해놓고 빠르게 나눴다. 먼저 기능들은 다음과 같다.
Front-end
1) 메인 페이지 - footer, navbar, carousel
2) 회원가입 / 로그인 페이지
3) 장바구니 페이지
4) 상품 리스트 페이지
5) 상품 상세 페이지
Back-end
1) 회원가입 API
2) 로그인 API(+user check middleware)
3) nav bar 리스트 요청 API
4) 필터, 정렬조건 API
5) 제품 상세 API
6) 장바구니 API)
나는 상품 리스트 페이지와 회원가입 API를 맡았다.
높은 가격 / 낮은 가격 sorting에 따라서 다른 API를 호출하도록 ComponentDidUpdate 함수를 이용했다.
//내가 작성한 코드
componentDidUpdate(prevProps) {
const { search: currentQuery } = this.props.location;
const { search: prevQuery } = prevProps.location;
if (currentQuery !== prevQuery) {
fetch(`${API_URL}/products${currentQuery}`)
.then(res => res.json())
.then(res => {
this.setState({ productList: res.products });
});
}
}
👉(직면한 문제) ComponentDidUpdate()에서 setState() 함수를 이용할 때, 무한 렌더되는 현상이 나타났다. 그래서, React 공식문서를 활용해서 조건문으로 해결했다.

👆(해결) 현재의 this.props.location.search 객체와 CDU로 인하여 상태변경이 될 객체 값이 다를 때, 라는 조건문을 주었다.
const { search: currentQuery } = this.props.location;
const { search: prevQuery } = prevProps.location;
if (currentQuery !== prevQuery) {...}
높은 가격 / 낮은 가격을 클릭 할 때 마다 다른 API를 호출할 수 있도록 했다.
//내가 작성한 코드
import React from 'react';
import { withRouter } from 'react-router';
import { Link } from 'react-router-dom';
import './ProductSort.scss';
class ProductSort extends React.Component {
render() {
const { search } = this.props.location;
const searchArray = search.split('&');
const priceValue =
searchArray.length !== 1 ? searchArray[1].split('=')[1] : '';
return (
<section className="productSort">
<span className="productSortList">
상품정렬
<img src="https://andar.co.kr/common/PC/arrow_down.png" alt="arrow" />
</span>
<div className="dropDown">
<Link
to={{
pathname: '/productlist',
search:
searchArray.length === 1
? search + '&price=DESC'
: priceValue !== 'DESC'
? searchArray[0] + '&price=DESC'
: search,
}}
>
높은가격
</Link>
<Link
to={{
pathname: '/productlist',
search:
searchArray.length === 1
? search + '&price=ASC'
: priceValue !== 'ASC'
? searchArray[0] + '&price=ASC'
: search,
}}
>
낮은가격
</Link>
</div>
</section>
);
}
}
export default withRouter(ProductSort);
👉(직면한 문제) 처음에는 Link 태그 안에{productlist/${search}&'price=DESC} /> 와 같이 URL 쿼리를 작성했으나, 높은 가격을 클릭 했을 때의 URL(http://localhost8000/typeNum=1&'price=DESC') 에서 다른 API를 불러올 때 다음과 같이 원하지 않는 URL 쿼리 값이 계속 추가 됐다.
=>URL(http://localhost8000/typeNum=1&'price=DESC'/'price=ASC'/'price=ASC')
👆(해결) 삼항 연산자 조건문을 활용하여 해결
const { search } = this.props.location;
// search 객체의 URL 쿼리를 &을 기준으로 배열로 나눈다.
const searchArray = search.split('&');
// priveValue = 'DESC' 또는 'ASC'
const priceValue =
searchArray.length !== 1 ? searchArray[1].split('=')[1] : '';
<Link
to={{
pathname: '/productlist',
search:
searchArray.length === 1 // &이 포함되어 있지 않을 때, 즉 메인 또는 리스트 페이지 등에서 접속 할 때
? search + '&price=DESC' // http://localhost:8000/typeNum=1(search)'&price=DESC'
: priceValue !== 'DESC'
// 또는 priceValue가 ASC 등 &을 포함한 URL을 호출한 경우에,
? searchArray[0] + '&price=DESC'
// search값 까지만 빼오고 원하는 URL 쿼리를 붙인다.
: search,
}}
>
높은가격
</Link>
<Link
to={{
pathname: '/productlist',
search:
searchArray.length === 1
? search + '&price=ASC'
: priceValue !== 'ASC'
? searchArray[0] + '&price=ASC'
: search,
}}
>
낮은가격
</Link>
리스트 페이지의 일부분을 메인 페이지에 적용시키기 위해서 많은 단위를 component로 쪼개어 재활용성이 가능하게 만들었다. URL을 통해서 다른 API를 호출 할 때, 삼항 연산자를 이용했고 또한, 메인 페이지와 리스트 페이지에서 받아오는 정보도 다른데 이것도 마찬가지로 똑같은 방법을 이용했다.
class ProductList extends React.Component {
constructor() {
super();
this.state = {
productList: [],
};
}
componentDidMount() {
const { search } = this.props.location;
fetch(`${API_URL}/products${search}`)
.then(res => res.json())
.then(res => {
this.setState({ productList: res.products });
});
}
componentDidUpdate(prevProps) {
const { search: currentQuery } = this.props.location;
const { search: prevQuery } = prevProps.location;
if (currentQuery !== prevQuery) {
fetch(`${API_URL}/products${currentQuery}`)
.then(res => res.json())
.then(res => {
this.setState({ productList: res.products });
});
}
}
render() {
const { productList } = this.state;
const { search } = this.props.location;
return (
<div className="productList">
{search && <ListCategory />}
<main className="productMain">
{search && <ListMenu productCount={productList.length} />}
<ListMain productList={productList} />
</main>
{search && <ListPagiNation />}
</div>
);
}

Event: onMouseOver시 바뀌는 불리언 값의 state를 변경되는 className에 css를 다르게 주어서 해결
👉(직면한 문제) 사실 이 부분은 webucks 클론 실습때 해본 적이 있어서 접근하는데 어렵지 않았고, 코딩을 하는데도 오랜시간이 걸리지 않았다. 다만, 나에게 있어서 가장 큰 고민은 예상과 달리 Event를 주었을 때 re-rendering이 되어서 fetch된 데이터의 모든 함수들이 적용 된다는 점이었다.
👆(해결)
React의 LifeCycle을 잘 이해하지 못했기 때문에 생긴 문제다.!
[LifeCycle 알아보기]

먼저 렌더가 되고 난 이후 CDM->fetch 후에 재렌더링이 된다. Event를 발생시킬 때도 마찬가지이다. 재렌더링이 되길 원하지 않는다면 자식 Component를 생성시키면 부모 constructor-> render 후에 즉시, 자식 render -> CDM(->fetch) 또는 Event 발생
순서이기 때문에 re-rendering이 일어나지 않는다. 즉, 내가 원하는 이벤트만 발생시키기 때문에 다시 re-rendering이 되어서 map함수의 적용을 받지 않아 모든 이미지들에게 적용이 되지 않는다는 것이다.

// 내가 작성한 코드
listMain.js
class ListMain extends React.Component {
render() {
const { productList } = this.props;
return (
<div className="listMain">
{productList.map(product => {
return <ProductCard key={product.id} {...product} />;
})}
</div>
);
}
}
// 내가 작성한 코드
productCard.js
class ProductCard extends React.Component {
constructor() {
super();
this.state = {
isMouseOver: true,
};
}
handleMouseHover = () => {
const { isMouseOver } = this.state;
this.setState({ isMouseOver: !isMouseOver });
};
render() {
const { isMouseOver } = this.state;
const {
id,
imageUrlList,
name,
originPrice,
discountedPrice,
colorAmount,
} = this.props;
return (
<div className="productCard">
<ul className="productCardList">
<li key={id}>
<Link
to={`products/${id}`}
onMouseOver={this.handleMouseHover}
onMouseOut={this.handleMouseHover}
>
<img
className={isMouseOver ? 'mouseUp' : 'mouseDown'}
src={imageUrlList[0]}
alt="MRDR JH 트레이닝 바지"
/>
<img
className={isMouseOver ? 'mouseDown' : 'mouseUp'}
src={imageUrlList[1]}
alt="Dr.HANS 크루 바지"
/>
</Link>
</li>
<li className="productTitle">
<span>{name}</span>
</li>
<li className="productPrice">
<span className="discountedPrice">
{discountedPrice.toLocaleString()}원
</span>
{discountedPrice !== originPrice && (
<span className="originPrice">
{originPrice.toLocaleString()}원
</span>
)}
</li>
<li className="productColorAmount">{colorAmount} 컬러</li>
</ul>
</div>
);
}
}
