index.html의 ProductListPage Class 마크업구조를 확인하면 다음과같이 Product li 요소에 product데이터가 매핑되는 구조입니다.
App.js에서 데이터를 할당하고, ProductListPage.js를 작성하여 상품 목록을 렌더링합니다.
App.js
import { request } from "./api/api.js";
export default function App($app) {
...
this.init = async () => {
...
let componentData = [];
switch (routeComponent.path) {
case "/web/":
componentData = await request();
break;
...
}
new routeComponent.component({
$app,
initialState: componentData,
});
};
this.init();
}
ProductListPage.js
export default function ProductListPage({ $app, initialState }) {
...
this.render = () => {
this.$target.innerHTML = `
<h1>상품목록</h1>
<ul>
${this.state
.map((product) => {
return `
<li class="Product" data-product-id=${product.id}>
<img src="${product.imageUrl}"/>
<div class="Product__info">
<div>${product.name}</div>
<div>${product.price.toLocaleString()}원~</div>
</div>
</li>
`;
})
.join("")}
</ul>
`;
};
}
요구사항에서 숫자 3자리수마다 콤마 작성을 원했습니다. 다음 두 방법을 통해 해결 가능하며, 더 사용성 있는 toLocaleString 함수를 사용했습니다.
Regular Expression
number.toString().replace(/\B(?<!\.\d*)(?=(\d{3})+(?!\d))/g, ",");
파라미터가 아무것도 전달되지 않으면 local의 language format에 맞는 문자열을 default로 변경해줍니다. 자세한 예시와 구문은 위 MDN을 통해 확인해주세요.
number.toLocaleString(locales, options)
상품을 클릭하는 경우, 상품의 productId를 함께 history.pushState()로 넘겨주어 라우팅을 진행합니다.
ProductListPage.js
export default function ProductListPage({ $app, initialState, onClick }) {
...
this.onClick = onClick;
this.$target.addEventListener("click", (e) => {
const $liItem = e.target.closest(".Product");
if ($liItem) {
const { productId } = $liItem.dataset;
this.onClick({ productId }, null, `/web/products/${productId}`);
}
});
}
이벤트 위임과 dataset을 활용해서 productId를 찾고 onClick이벤트를 할당합니다.