자바스크립트

[자바스크립트 프로젝트] 쇼핑몰 구현하기 - [장바구니,navbar,카테고리]

하림회사 2023. 12. 4. 20:01

팀원들과 함께 쇼핑몰을 구현하기로하였습니다.

처음에는 리액트로 하려고 했으나 기본적인 개념을 이해해야한다고 생각하여

자바스크립트로 진행을 했습니다.

 

- vite를 이용하여 프로젝트 생성

-효율적인 프로젝스 시간을 위해서 디자인같은경우는 클론으로 하여 제작

백엔드를 담당한 팀원들과 폴더구조를 상의해서 만들고 제작하기로 하였습니다.

 

 

navbar / slide

 

로고 및 메뉴 / navbar.js

코드

더보기
const header = document.querySelector(".header");
const getItemNav = JSON.parse(localStorage.getItem("cart")) || [];
//카트 숫자넘버 그려지기전에 넣어둬야 작동한다.
let cart_num = getItemNav.length;
// 네비 메뉴
const menu = [
  { text: "상 품", link: "/product" },
  { text: "주문 하기", link: "/cart" },
  //{ text: "고객 센터", link: "/customer-service" }
];
// 메뉴 아이템을 생성하는 함수
const menu_li = () => {
  const menuItems = menu.map(
    (item) =>
      `<li><a href="${item.link}" onclick="route()">${item.text}</a></li>`
  );
  return menuItems.join(""); // 배열을 문자열로 결합하여 반환
};

header.innerHTML = `
    <div class="header_section">
        <div>
            <ul>
              <li class="main_login_btn logout"><a href="/login">로그인</a></li>
              <li class="main_user_join"><a href="/register">회원가입</a></li>
            </ul>
        </div>
        <div class="main_top_logo"><span style="font-size:40px">Hwasahae</span></div>
        <div class="header_right_area">
        <ul>
            <li class="search_open">
                <form action="/" method="post" name="/">
                    <fieldset>
                    <input name="search" placeholder="비타민 존맛탱"  onkeydown="함수명대체할것." value="" class="MS_search_word">
                    <a href="javascript:search_submit();" class="search_btn"></a>
                    </fieldset>
                </form>
            </li>
            <li class="top_myp"></li>
            <li class="top_cart">
            <span id="user_basket_quantity" class="user_basket_quantity">${cart_num}</span>
            </li>
        </ul>
        </div>
    </div>
    <nav>
        <ul>
            ${menu_li()}
        </ul>
    </nav>
    `;

//로그인 버튼 => 로그아웃 버튼
const userData = localStorage.getItem("userInfo");
const loginLogoutButton = document.querySelector(".main_login_btn");

window.onload = function () {
  if (loginLogoutButton) {
    if (userData) {
      loginLogoutButton.innerHTML = `<li class="main_login_btn logout"><a href="/login">로그아웃</a></li>`;
    } else {
      loginLogoutButton.innerHTML = `<li class="main_login_btn logout"><a href="/login">로그인</a></li>`;
    }
  }
};

const logoutEvent = () => {
  loginLogoutButton.addEventListener("click", async (e) => {
    e.preventDefault();
    const logout_URL = "http://localhost:3000/logout";

    fetch(logout_URL, {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
      },
    }).then((res) => {
      console.log(res);
      alert("로그아웃 되었습니다");
      localStorage.removeItem("userInfo");
      location.href = "/login";
    });
  });
};

if (localStorage.getItem("userInfo")) {
  logoutEvent();
}

//메인로고 넣기
const main_log = document.querySelector(".main_top_logo");
// const mainImg = new Image();
// //메인로고 2023.11.11 김나영이 바꿈
// // mainImg.src = "http://skincure.co.kr/design/skincure/0759ansome/top_logo.gif";
// mainImg.src =
//   "https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdna%2FqEkX2%2Fbtsz61YM6Nk%2FAAAAAAAAAAAAAAAAAAAAAHmsv_KZ-7gLLBR1zTV8ZqT7Ya9NwTytMGB2Rss9fily%2Fimg.jpg%3Fcredential%3DyqXZFxpELC7KVnFOS48ylbz2pIh7yKj8%26expires%3D1753973999%26allow_ip%3D%26allow_referer%3D%26signature%3DyYcp%252Fqm8eMEvsD7acnerhXzubJc%253D";
// main_log.appendChild(mainImg);

main_log.addEventListener("click", () => {
  window.location.href = "/index.html"; // 메인 페이지의 URL을 여기에 지정
});
//서치 돋보기 버튼
const search_btn = document.querySelector(".search_btn");
const search_btn_img = new Image();
search_btn_img.src =
  "http://skincure.co.kr/design/skincure/0759ansome/btn_search.png";
search_btn.appendChild(search_btn_img);

search_btn.addEventListener("click", () => {
  window.location.href = "/index.html"; //추후 돋보기 룅크변경
});

//사람 아이콘
const top_myp = document.querySelector(".top_myp");
const mypImg = new Image();
mypImg.src = "http://skincure.co.kr/design/skincure/0759ansome/btn_mypage2.gif";
top_myp.appendChild(mypImg);

// top_myp.addEventListener("click", () => {
//   window.location.href = "/mypage"; //추후 룅크변경
// });

//userData위에 getItem으로 가져옴 const userData = localStorage.getItem("userInfo");
//관리자 여부에 따른 네브바 처리 => 관리자로 로그인 했을 경우 마이페이지 링크가 관리자 페이지로, 구매자가 로그인 했을 경우 구매자페이지로 이동
if (userData) {
  const userInfo = JSON.parse(userData);
  const isRole = userInfo.role; //role이 구매자인지 관리자인지

  if (isRole === "관리자") {
    // 마이페이지 아이콘을 관리자 페이지로 연결
    top_myp.addEventListener("click", () => {
      window.location.href = "/admin"; //추후 룅크변경
    });
  } else {
    // 구매자인 경우 마이페이지로 연결
    top_myp.addEventListener("click", () => {
      window.location.href = "/mypage"; //추후 룅크변경
    });
  }
}

//카트 아이콘
const top_cart = document.querySelector(".top_cart");
const cartImg = new Image();
cartImg.src = "http://skincure.co.kr/design/skincure/0759ansome/btn_cart2.gif";
top_cart.appendChild(cartImg);

top_cart.addEventListener("click", () => {
  window.location.href = "/cart"; //추후 룅크변경
});

// 위에는 헤더바 아래는 푸터바
const footer = document.querySelector("footer");
footer.innerHTML = `
<div class="ft_sec01">
    <div class="bt_inner">
        <ul class="fleft">
            <li><a href="/shop/page.html?id=1">회사소개</a><span class="bt_bar">|</span></li>
            <li><a href="/html/info.html">이용가이드</a><span class="bt_bar">|</span></li>
            <li><a href="javascript:bottom_privacy();" style="color:#ac1818; font-weight:700;">개인정보처리방침</a><span class="bt_bar">|</span></li>
            <li><a href="javascript:view_join_terms();">이용약관</a></li>
        </ul>
        <ul class="fright">
            <li><a href="https://www.skincure.co.kr">홈으로</a><span class="bt_bar">|</span></li>
            <li><a href="#">위로가기</a><span class="bt_bar">|</span></li>
            <li><div class="familySite">
        <select name="number" onchange="window.open(this.value)">
            <option selected="selected">Family Site</option>
            <option value="http://eskincure.co.kr">스킨큐어 기업 홈페이지</option>
            <option value="http://eskincure.com">스킨큐어 영문 홈페이지</option>
            <option value="http://en.skincure.co.kr/">스킨큐어 영문 쇼핑몰</option>
            <option value="http://www.skincure.kr">B2B 도매홈페이지</option>
        </select>
                </div></li>
        </ul>
    </div>
</div>
<div class="cboth ft_sec02">
		<div class="bt_inner">
			<div class="fleft cs_area">
				<div class="tit_cs">고객센터</div>
				<div class="cs_sec"><!-- CUSTOMER CENTER : 정보 -->
					<div class="fleft ft_phone">1544-5439</div>
					<div class="fleft cs_info">운영시간: 월~금 AM 09:00 - PM 5:30  /  점심시간: PM 12:20 - PM 1:20<br><span class="bk">휴무: 토,일,공휴일</span></div>
				</div>
			</div>

			<div class="fleft bank_area">
				<div class="tit_bank">무통장 입금 계좌</div>
				<div class="bk_sec"><!-- BANK ACCOUNT : 정보 -->
					<div class="fleft bank_info">국민은행 : 290301-04-006396</div>
				</div>
			</div>

			<div class="fleft return_area">
				<div class="tit_return">반품/교환 주소 안내</div>
				<div class="rt_sec"><!-- 반품주소안내 -->
					<div class="fleft rt_info">경기도 용인시 수지구 신수로 767, A동 10층 1007호(동천동, 분당 수지 U-TOWER)<br><span>자세한 사항은 문의게시판 혹은 공지사항을 참고해주세요</span></div>
				</div>
			</div>
		</div>
	</div>
    <div class="cboth ft_sec03">
		<div class="bt_inner">
			<div class="fleft com_info">
				상호명 : 스킨큐어(주)  /  대표이사 : 김명옥  /  경기도 용인시 수지구 신수로 767, A동 10층 1007호(동천동, 분당수지 U-TOWER)  /  1544-5439  /  FAX : 031-719-5202 <br>
				사업자등록번호 : 123-86-09894 <a href="https://www.ftc.go.kr/www/biz/bizCommList.do?key=5375&amp;searchCnd=wrkr_no&amp;searchKrwd=1238609894" target="_blank">[사업자정보확인]</a>  /  개인정보 책임자 : 김정기(<a href="mailto:msd02@skincure.co.kr">msd02@skincure.co.kr</a>)  /  통신판매업신고 : 제 2016-용인수지-0385 호			</div>
			<div class="cboth copyright">COPYRIGHT BY 스킨큐어(주) ALL RIGHTS RESERVED. <!--Hosting by (주)코리아센터닷컴--></div>
			<span class="escrow"><div class="ft-escrow">                <a href="http://www.skincure.co.kr" onclick="window.open('https://okbfex.kbstar.com/quics?e2eType=10&amp;page=C021590&amp;cc=b034066%3Ab035526&amp;mHValue=54e0922d4df6a7693fb792989984b118201609091343851 ', 'escrow', 'height=670,width=630'); return false;">
            <!-- 하단 에스크로 배너 링크수 삭제-->
                </a><!-- 하단 에스크로 배너 링크수정 -->
		</div>
	</span></div>
    </div>
    `;

 

다른페이지에서 공통으로 사용하기 위해서 템플릿화하여 js에서 html 을 만드는 형식으로 제작하였습니다.

 

 

장바구니 / cart.js

 

코드

더보기
const getItems = JSON.parse(localStorage.getItem("cart")) || [];
console.log(getItems);

let totalPrice = 0; //총가격 초기

// 각 fetch 요청을 저장할 배열 만들기.
const fetchPromises = getItems.map(async (item) => {
  const response = await fetch(`http://localhost:3000/products/${item.id}`);
  return await response.json();
});
console.log(fetchPromises);

// fetch 요청으로 위에서 만든 리절트쪽에 있는 데이터를 프로미스 올로 사용.
Promise.all(fetchPromises).then((productDataArray) => {
  getItems.forEach((item, index) => {

    const data = productDataArray[index]; //각 배열순서에맞는 데이터를 담는다.
    console.log(data.images[0]);

    // 각 상품의 가격을 화면에 표시되는 가격으로 계산하여 더합니다 //처음화면 진입때 더하기함
    let item_su = item.count || 1; //카운트 값이 있으면 넣고 아니면 1을넣느다.
    totalPrice += data.price * item_su;

    //총 상품 가격
    let total_price = () => {
      const total_num = data.price * item_su;
      return total_num.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
    };

    const MapItem = `
        <tr data-index="${index}">
          <td><img src="${data.images[0]}" alt="상품 이미지" style="width: 100px; height: 100px;"></td>
          <td><span class="product_name">${data.name}</span></td>
          <td>
            <button class="minus" data-index="${index}">-</button>
            <input class="item_many" type="text" value="${item_su}" data-index="${index}">
            <button class="plus" data-index="${index}">+</button>
          </td>
          <td><span class="item_total">${total_price()}</span>원</td>
          <td><button class="deleteBtn" data-index="${index}">삭제</button></td>
        </tr>
      `;

    const pageSleeted = document.querySelector("#productTable");
    pageSleeted.innerHTML += MapItem; //html

    // 장바구니 - 제품수량 증가 및 감소
    const plusBtns = document.querySelectorAll(".plus");
    const minusBtns = document.querySelectorAll(".minus");
    const deleteBtns = document.querySelectorAll(".deleteBtn");
    const item_many = document.querySelectorAll(".item_many");
    const item_total = document.querySelectorAll(".item_total");

    plusBtns.forEach((plusBtn, index) => {
      plusBtn.addEventListener("click", (e) => {
        e.preventDefault();
        handlePlusClick(index);
      });
    });
    minusBtns.forEach((minusBtn, index) => {
      minusBtn.addEventListener("click", (e) => {
        e.preventDefault();
        handleMinusClick(index);
      });
    });
    deleteBtns.forEach((deleteBtn, index) => {
      deleteBtn.addEventListener("click", (e) => {
        e.preventDefault();
        handleDeleteClick(index);
      });
    });

    // 삭제 버튼
    const handleDeleteClick = (index) => {
      // 해당 인덱스의 상품 삭제
      getItems.splice(index, 1);

      // 로컬 스토리지에서도 삭제
      localStorage.setItem("cart", JSON.stringify(getItems));

      // 해당 행 삭제
      const tableRow = document.querySelector(
        `#productTable tr[data-index="${index}"]`
      );
      tableRow.parentNode.removeChild(tableRow);
      location.reload();
    };

    // 플러스 버튼
    const handlePlusClick = (index) => {
      // 카운트 증가
      getItems[index].count = (getItems[index].count || 1) + 1;

      // UI 업데이트
      item_su = getItems[index].count;
      item_many[index].value = item_su;
      totalPrice += data.price;
      item_total[index].innerHTML = total_price();
      console.log(item_many[index].value);

      // 로컬 스토리지 업데이트
      const cartItems = JSON.parse(localStorage.getItem("cart")) || [];

      if (cartItems[index]) {
        // 로컬 스토리지의 카운트 업데이트
        cartItems[index].count = getItems[index].count;
      } else {
        // 로컬 스토리지에 아이템이 없으면 새로 추가
        cartItems[index] = { count: getItems[index].count };
      }

      // 업데이트된 데이터를 로컬 스토리지에 저장
      localStorage.setItem("cart", JSON.stringify(cartItems));
      totalElement();
    };

    // 마이너스 버튼
    const handleMinusClick = (index) => {
      getItems[index].count = (getItems[index].count || 1) - 1;

      // 수량이 1보다 작으면 최소값을 1로 설정
      if (getItems[index].count < 1) {
        getItems[index].count = 1;
      }
      item_su = getItems[index].count;
      totalPrice -= data.price;
      item_many[index].value = item_su;
      item_total[index].innerHTML = total_price();
      console.log(item_many[index].value); //콘솔 확인용

      // 수량 업데이트용 로컬 스토리지에서 데이터 가져오기
      const cartItems = JSON.parse(localStorage.getItem("cart")) || [];
      if (cartItems[index]) {
        cartItems[index].count = item_many[index].value;
        // 로컬 스토리지 업데이트된 데이터 저장
        localStorage.setItem("cart", JSON.stringify(cartItems));
      }
      totalElement();
    };

    const totalElement = () => {
      const totalBox = document.getElementById("totalPrice");
      totalBox.innerHTML = `총 가격: ${totalPrice
        .toString()
        .replace(/\B(?=(\d{3})+(?!\d))/g, ",")}원`;
    }; // 총 가격을 표시할 위치에 id="totalPrice"인 요소를 추가
    totalElement();

    const paymentBtn = document.querySelector("#payment_btn");
    paymentBtn.addEventListener("click", () => {
      const totalMani = totalPrice
        .toString()
        .replace(/\B(?=(\d{3})+(?!\d))/g, ",");
      localStorage.setItem("totalPrice", totalMani);
    });

    //장바구니 지우기
    const allDeleted = document.querySelector("#cartDelete");
    allDeleted.addEventListener("click", () => {
      window.localStorage.removeItem("cart");
      location.reload();
    });
  });
});

 

로컬스토리지를 이용한 장바구니 제작.

직면한 문제 - 각각 상품에대한 정보를 index 순서로 데이터를 각각 저장해서 사용하는 방식이었으나 새로고침을하면 로딩순서에 따라서

순서가 무작위로 섞이면서 바뀌는 현상이 나타났다.

해결법 - Promise.all

프로미스 올을 이용하여 각 아이템의 데이터를 전부 받고나서 아이템이 정렬되도록하여 입력된 순서대로 로딩하여 해결.

추가사항 

제품을 개별로 한개씩 삭제시 index로 제품 정렬로 인해 새로고침을 실행.

 

 

제품 상세 페이지. / itemPage.js

코드

더보기
const urlParams = new URL(location.href).searchParams;
const itemId = urlParams.get('id');
console.log(itemId)


fetch(`http://localhost:3000/products/${itemId}`)
.then((response) => response.json()).then((data) =>{
        //상품 이미지 추가.
        const img_box = document.querySelector('.img_box')
        const imgBox = new Image();
        imgBox.src = `${data.images}`
        img_box.appendChild(imgBox)


        //구매 관련 폼
        const info = document.querySelector('.info')
        const item_date = data;
        console.log(item_date)

        //상품 갯수
        let item_su = 1

        //토탈금액 물픔금액 * 갯수
        let total_price = () =>{
            const total_num = item_date.price * item_su
            return total_num.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",")
        }

    info.innerHTML = `
    <h3 class="tit-prd">${item_date.name}</h3> 
    <ul class="itemInfo">
        <li>
            <span class="tb-left">판매가격</span>
            <div class="tb-left tb-left-in"><span class="pricevalue" id="price">${item_date.price.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",")}</span>원</div>
        </li>
        <li>
            <span class="tb-left">소비자가격</span>
            <div class="tb-left tb-left-in"><strike>${item_date.price.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",")}원</strike></div>
        </li>
        <li>
            <span class="tb-left">적립금</span>
            <div class="tb-left tb-left-in reserves"><span class="pricevalue">1</span>%</div>
        </li>
        <li>
            <span class="tb-left">용량 및 중량</span>
            <div class="tb-left tb-left-in reserves"><span class="pricevalue">200</span>ml</div>
        </li>
    </ul>
    <div class="option_add_area">
        <ul>
            <li>${item_date.description}</li>
            <li>
            <input type="text" class="item_many" id="MS_amount_basic_0" name="amount[]" value=${item_su} onfocusout="set_amount(this, 'basic');" size="4" style="text-align: right; float: left;" class="basic_option" maxlength="" data-gtm-form-interact-field-id="0">
            <span class="plus area_btn">+</span>
            <span class="minus area_btn">-</span>
            <strong class="MK_price"><span id="MK_p_price_basic_0">${item_date.price.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",")}</span>원</strong>
            </li>
        </ul>
    </div>
    <div>
    <ul id="MK_innerOptTotal">
        <li>
        <p class="totalLeft"><span class="MK_txt-total">총 상품 금액</span></p>
        <p class="totalRight">
            <strong class="MK_total" id="MK_p_total"></strong>
            <span class="MK_txt-won">원</span>
        </p>
        </li>
    </ul>
    </div>
    <div class="cboth prd-btns">
    <a id="buyBtn" href="/payment" class="buy move">BUY NOW</a>
    <a id="cartBtn" href="/cart" class="basket move">CART</a>
    </div>
`

// 바로구매 / 장바구니 로컬스토리지 넣기.
const buyBtn = document.querySelector('#buyBtn')
const cartBtn = document.querySelector('#cartBtn')
const storedCart = JSON.parse(window.localStorage.getItem('cart') || '[]'); // 가져오기
const userInfo = window.localStorage.getItem('userInfo'); // 로그인 확인용.

//바로결제
buyBtn.addEventListener('click',(e)=>{
    if(!userInfo){
        e.preventDefault()
        window.location.href ="/login"
    }
    const cartList = []
    if (cartList.some(item => item.id === itemId)) {
        return;
    }else{
        cartList.push({
            id:itemId,
            price:item_date.price,
            total_price: total_price(),
            count:item_su
        });
        window.localStorage.setItem('buyItem', JSON.stringify(cartList))
    }
})

//장바구니로
cartBtn.addEventListener('click', () => {
    const cartList = storedCart;
    const itemIndex = cartList.findIndex(item => item.id === itemId);

    if (itemIndex !== -1) {
        // 이미 장바구니에 있는 경우 count를 갱신합니다.
        cartList[itemIndex].count = item_su;
        window.localStorage.setItem('cart', JSON.stringify(cartList));
        alert("제품의 수량을 업데이트했습니다.");
    } else {
        // 장바구니에 없는 경우 새로운 아이템을 추가합니다.
        cartList.push({
            id: itemId,
            price: total_price().replace(/,/g, ''),
            count: item_su
        });
        alert("장바구니에 넣었습니다.");
        window.localStorage.setItem('cart', JSON.stringify(cartList));
    }
});


//제품수량 증가 및 감소
const plus = document.querySelector('.plus')
const minus = document.querySelector('.minus')
const item_many = document.querySelector('.item_many')
const item_total = document.querySelector('#MK_p_total') 
item_total.innerHTML = item_date.price.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",")

plus.addEventListener('click', () => {
        item_su = item_su+1
        item_many.value = item_su
        item_total.innerHTML = total_price()
})
minus.addEventListener('click', () => {
    if(item_many.value == 1){
        item_many.value = 1
        return alert("최소수량 입니다")}
        
    item_su = item_su-1
    item_many.value = item_su
    item_total.innerHTML = total_price()
})

//디테일 이미지. url
const detail_div = document.querySelector('.detail_img') 
const detail_text = document.querySelector('.item_detail>div:first-child')
//위에는 선택자

const detail_img = new Image();
detail_img.src = 'http://sandawha.com/sandawha/detail/Sandawha-Natural-Mild-Cleansing-Oil_01.jpg'
detail_div.appendChild(detail_img)

//아래는 상세페이미지가 
detail_img.onload = function() { // 이미지로딩 성공했을때
    detail_text.style.width = detail_div.offsetWidth + "px";
}
detail_img.onerror = function() {
    // 이미지 로딩 중 오류가 발생한 경우 실행할 코드를 여기에 작성합니다.
    alert("이미지 로드 오류")
};

  });

 

제품 클릭시 url에 제품의 id 값을 Params 를 이용한 상세페이지 정보를 불러오게 제작.

데이터를 불러와서 아이템에대한 구매 정보를 로컬스토리지에 저장하여 구매페이지or장바구니 페이지로 이동.

 

카테고리 아이템 정렬 / product.js

코드

더보기
const categoryNav = document.querySelector('.list')

let linkName = []
// 카테고리 데이터로 불러와서 만들기.
async function fetchData() {
  try {
    const storedCart = JSON.parse(window.localStorage.getItem("cart") || "[]"); // 가져오기
    const response = await fetch('http://localhost:3000/category');
    const data = await response.json();
    console.log(data);

    const category_item_nav = data.map((item, index) => `
      <li><a href="" class="linkName" data-name="${item.name}" key={index}>${item.name}</a></li>
    `).join('');
    categoryNav.innerHTML = category_item_nav;
    const linkBtns = document.querySelectorAll('.linkName');

    linkBtns.forEach(linkBtn => {
      linkBtn.addEventListener('click', (e) => {
        e.preventDefault();
        const dataNameValue = linkBtn.getAttribute('data-name');
 
        linkName = []
        linkName.push(dataNameValue);

        listHtml(linkName)
        cartB()
      });
    });
    const cartB = () =>{
      setTimeout(()=>{
        const cartBtns = document.querySelectorAll(".cartDateBtnMd"); //버튼클래스
        const cartList = storedCart; //장바구니 리스트
        cartBtns.forEach((cartBtn) => {
          const dataValue = cartBtn.dataset.value; //상품의 아이디값
          const dataPrice = cartBtn.getAttribute("data-another"); //상품의 가격 데이터
        
          cartBtn.addEventListener("click", (e) => {
            if (cartList.some((item) => item.id === dataValue)) {
              //장바구니 스토리지 중복확인.
              alert("이미 장바구니에 있습니다.");
              return;
            } else {
              cartList.push({
                id: dataValue,
                price: dataPrice,
                count: 1,
              }); //상품 아이디를 배열에 넣음
              window.localStorage.setItem("cart", JSON.stringify(cartList)); // 스토리지에 장바구니 아이템넣음.
              alert("장바구니에 넣었습니다.");
            }
          });
        });
      
      },100)
    }
    cartB()
    


  } catch (error) {
    console.error('Error fetching data:', error);
  }
}

fetchData().then(() => {
  // fetchData가 완료된 후 listHtml 호출
  listHtml(linkName);
});




// const koreanText = '스킨'; // 여기에 실제로 사용하려는 한글 텍스트를 넣어주세요
// fetch(`http://localhost:3000/category/products/${koreanText}`).then((response) => response.json()).then((data)=>console.log(data))

const listHtml = async (link) => {
  console.log(link);
  const encodedLink = encodeURIComponent(link);
  let notLink;

  if (link) {
    notLink = fetch(`http://localhost:3000/category/products/${encodedLink}`);
  } else {
    notLink = fetch(`http://localhost:3000/category/products/스킨`);
  }

  const response = await notLink;
  const items = await response.json();
  console.log(items);
  // 상품 템플릿화
  function createProductTemplate(imageSrc, title, price, id) {
    const productTemplate = `
          <div class="product">
            <div class="product-image">
              <a href="/item?id=${id}">
                <img src="${imageSrc}" alt="상품 섬네일">
              </a>
              <div class="info_icon">
                <span class="cartDateBtnMd" data-value="${id}" data-another="${price}"><img src="http://skincure.co.kr/design/skincure/0759ansome/icon_prd04.gif" alt="미리보기"></span>
              </div>
            </div>
            <div class="product-info">
              <div class="product-title">${title}</div>
              <div class="product-price">
                <span>${price} 원</span>
              </div>
            </div>
          </div>
        `;

        
    return productTemplate;
  }
  // 동적 상품 추가
  const productContainer = document.querySelector('.product-container');
  // 반복해서 상품을 추가
  productContainer.innerHTML = '';
  items.forEach((item) => {
    const productElement = createProductTemplate(item.images[0], item.name, item.price, item._id);
    productContainer.innerHTML += productElement;
  });
};

listHtml();


document.addEventListener("DOMContentLoaded", function() {
    const liElements = document.querySelectorAll("#productClass .cate-wrap .class-list ul li");
  
    liElements.forEach(function(li) {
      li.addEventListener("mouseover", function() {
        // 마우스 호버 시 배경색과 테두리 색 변경
        li.classList.add("active");
      });
  
      li.addEventListener("mouseout", function() {
        // 마우스가 빠져나가면 배경색과 테두리 색을 제거하여 원래대로 돌립니다.
        li.classList.remove("active");
      });
    });
  });

 

카테고리 클릭에따라 data-name 의 어트리뷰트 이름을 가져와서 

데이터를 다시 카테고리에 맞는 아이템으로 다시 로딩을 시킨다.

문제점 - 클릭시 데이터 로딩보다 view그려지는속도가 빨라서 오류가있었는데

해결 - async/await 를 이용하여 로딩이 될때까지 기다리게하여 해결하였다.