쿼리스트링을 활용하여 백엔드에서 필요한 데이터를 fetch 해온 두가지 코드를 리뷰해보자.
백엔드에서 상품 가격순으로 ordering 하는 함수를 빨리 구현해주셔서 전체 페이지에서 추가적인 기능을 구현할 수 있었다. query string 을 사용해 본 첫번째 기능이었는데, 너무 간단해서 신기했다!
무한스크롤을 구현하기 위해 hoc.js 파일을 만들어 메인의 각 탭에 함수 컴포넌트로 적용하고 있었기 때문에 데이터를 받아오는 hoc.js 에서 쿼리 스트링을 받아 자식 컴포넌트인 Allitem.js 에게 props를 넘겨줘야 했다.
순서를 간단하게 정리해보면 다음과 같다.
changeOrder(인자)
함수가 실행된다.Sorting.js
const { changeOrder } = this.props;
...
<ul className="sortType">
...
<li
className="selectModalItem"
onClick={() => changeOrder("?ordering=-price")}
>
<p className="selectModalOption orderLowPrice">낮은 가격순</p>
</li>
<li
className="selectModalItem"
onClick={() => changeOrder("?ordering=price")}
>
<p className="selectModalOption orderHighPrice">
높은 가격순
</p>
</li>
</ul>
가장 최하위 자식 컴포넌트인 Sorting.js 에서 모달창에서 원하는 순서를 클릭시 changeOrder 함수가 실행된다. 해당 함수는 hoc.js 에서 Allitem.js에게, Alltiem.js에서 Sorting.js 에게 props로 넘겨준 함수이다.
changeOrder 함수의 인자로 쿼리스트링의 뒤에 붙는 ordering 옵션을 넘겨준다.
hoc.js
changeOrder = (option = "") => {
this.setState(
{
productList: [],
items: 10,
preItems: 0,
totalCount: 0,
},
() => {
const { preItems, items, productList } = this.state;
fetch(URL + path + option)
.then((res) => res.json())
.then((res) => {
const result = res.data_list.slice(preItems, items);
this.setState({
productList: [...productList, ...result],
totalCount: result[0].total_count,
});
});
}
);
};
...
render() {
const { productList, totalCount } = this.state;
return (
<InputComponent
productList={productList}
totalCount={totalCount}
changeOrder={(option) => this.changeOrder(option)}
/>
);
}
CDM에 있는 getData()
함수와 유사하지만 fetch하는 URL
뒤에 path
와 option
이 추가 된다는 차이가 있다. 해당 옵션은 Sorting 에서 선택한 버튼에 따라 달라진다.
Allitem.js
import { withInfiniteScroll } from "../hoc";
import "./Allitem.scss";
class Allitem extends React.Component {
render() {
const { productList, totalCount, changeOrder } = this.props;
return (
<article className="Allitem">
<div className="topInfo">
<div className="totalItems">
<p className="totalNum">
총 <span className="pointSpan">{totalCount}</span> 개의 상품이
조회되었습니다.
</p>
</div>
<Sorting changeOrder={changeOrder} />
</div>
<List productList={productList} />
</article>
);
}
}
export default withInfiniteScroll(Allitem, "products");
Allitem.js 에서는 hoc.js 에서 새로 받아온 (새로운 순서로 받아온) productList를 List 컴포넌트에 넘겨주게 되고, Sorting에게는 hoc.js에서 props로 받아온 changeOrder()
함수를 그대로 넘겨주기만 한다. 참 쉽죠?
상세페이지의 레이아웃과 scss 작업은 형우님이 거의 대부분을 해주셨고, 나는 마지막에 query string을 붙이는 작업만 조금 도와드렸다. ordering 옵션보다 하나의 컴포넌트로 모든 상품의 상세페이지가 나오는게 너무너무너무 놀라웠다. 그래서 꼭 기록으로 남기고 싶었다!
리스트에 노출되는 카드 컴포넌트에 onClick 이벤트로 history.push 방식으로 디테일 컴포넌트 라우트로 연결되도록 하였고, 디테일 컴포넌트에서 componentDidMount 될 때 쿼리스트링으로 필요한 데이터를 받아오도록 만들었다.
Routes.js
<Route exact path="/details" component={Details} />
<Route exact path="/details/:id" component={Details} />
라우트 파일에 파라미터가 붙은 Detail 컴포넌트를 추가했다.
Card.js
<li className="Card">
<div>
<div
onClick={() => this.props.history.push(`/details/${id}`)}
className="thumnailWrap"
>
...
카드 컴포넌트에 onClick 이벤트 발생 시 history.push로 해당 페이지로 전환되도록 했다. id 값은 제품의 id 값이다.
Detail.js
class Details extends React.Component {
constructor() {
super();
this.state = {
productDetails: [],
};
}
s;
componentDidMount() {
fetch(`${URL}products/${this.props.match.params.id}`)
.then((res) => res.json())
.then((res) => {
this.setState({
productDetails: res.data_list,
});
});
}
render() {
return (
<div className="Details">
<Nav />
<ImgSlider productInfo={this.state.productDetails} />
<Title productInfo={this.state.productDetails} />
<ProductDetails productInfo={this.state.productDetails} />
<Shipping />
<LiveRfq />
<ReviewComment productInfo={this.state.productDetails} />
<Suggestion />
<PurchaseBar productInfo={this.state.productDetails} />
<TopBtn />
<Footer />
</div>
);
}
}
export default Details;
형우님께서 넘나 고생하며 만들어주신 디테일 컴포넌트! 컴포넌트가 마운트 될 때 어떤 라우트에 매칭이 되어있는지에 대한 정보가 있는 match
props를 사용해서 해당 아이디의 상세페이지 컴포넌트를 렌더하게 된다.