본문 바로가기
JavaScript/드림코딩

드림코딩_자바스크립트_쇼핑몰 미니게임 클론코딩

by junvely 2022. 4. 2.

[ 쇼핑몰 미니게임 클론코딩 ]

 

1. HTML / CSS 정적인 페이지 완성시키기

 

  • ellie와 내 코드를 비교한 결과 및 보완해야 할 점

- 일정한 수치값을 변수로 사용하여(통일성 있게) 재사용 한다.

- 각각의 태그별 class를 거의 대부분에 사용하여 css를 적용시킨다.

- 가로 세로정렬 모두 flex를 사용해 더 간편하게 정렬하였다.

- 쇼핑 리스트 ul>li 하나를 먼저 구현해 놓고 추후 JS에서 동적으로 변경하였다.

- section.class 사용하여 섹션을 구분한다. (div_con사용x)

- div_con 감싸지 않고 바로 tag 사용(ex, 바로 ul>li사용, 간단한 프로젝트라 그런 것 같다.)

 

 

2. data폴더 안에 data.json 파일 생성하여 data 보관하기

  • JS에서 배열을 이용해도 상관 없으나 JS는 어플리케이션의 비즈니스 로직을 담당하는 곳이고 비즈니스 로직은 코드가 들어있는 곳이다.
  • 코드와 데이터를 섞어서 보관하는 것은 좋지 않다. 데이터는 데이터를 보관할 수 있는 특별한 장소에 보관하는 것이 좋다. > file, DB, back-end가 될 수 도 있으나 백엔드는 현재 존재하지 않기 때문에 데이터 파일에 보관할 것 > 데이터 폴더 안에 JSON파일을 만들어 필요한 데이터를 보관한다. > JSON

 

  • data.json > 으로 data폴더 안에 파일 만들기
data폴더 안에 data.json파일
// data.json 
// 'items'라는 배열 안에는 각각의 item이라는 오브젝트들이 들어있다. (key와 value로 구성)
{
  "items": [
    {
      "type": "tshirt",
      "gender": "female",
      "size": "large",
      "color": "pink",
      "image": "../img/pink_t.png"
    },
    {
      "type": "tshirt",
      "gender": "male",
      "size": "large",
      "color": "blue",
      "image": "../img/blue_t.png"
    },
    {
      "type": "tshirt",
      "gender": "female",
      "size": "small",
      "color": "yellow",
      "image": "../img/yellow_t.png"
    },
    ]
    }
 
- JSON > JS 오브젝트와 굉장히 유사하다.(오브젝트 안에 KEY와 VALUE로 구분)
- 'items'라는 배열 안에는 각각의 item이라는 오브젝트들이 들어있다. (key와 value로 구성)
- data.json이라는 파일 안에는 'items'라는 배열 안에 데이터가 들어있고, 'items' 안에는 여러개의 item(object)들이 들어있다.
- 이것은 데이터 폴더 안에 data.json으로 있는데 콘솔창 network 창에 보면 data.json은 다운로드가 되지 않는다. > html에서는 아직 링크 x 아직 사용하지 않고 있기 때문
- js에서 json파일을 동적으로 데이터를 html에 추가해 주도록 한다.
 
 
 

3. fetch()를 사용하여 data.json의 데이터를 읽어와 'items'를 전달한다.

  • 데이터를 읽어오는데 시간이 많이 걸리기 때문에 > 비동기적으로 프로미스를 리턴하도록 만든다. > 성공시 'items'를 받아오고, 에러시 적절한 문구 사용자에게 보여주도록 한다.
  • 데이터 받아오면 response라는 Object를 전달 받는다.  response > body > ReadableStream에 우리가 원하는 데이터 들어있다. response.json() API 이용하여 response의 body를 json object으로 변환한다.

console.log(response)의 body 안에 ReadableStream에 데이터가 들어있다.

function loadItems() {
  // loadItems() > fetch를 통해 데이터를 받아오고, 받아온 데이터가 성공적이면 json으로 변환하고, json안에있는 items를 반환
  return (
    fetch("data/data.json") // 해당하는 파일의 경로나 url작성하면 간단하게 데이터를 네트워크를 통해 가져올 수 있음
      .then((response) => response.json()) // 데이터 받아오면 response라는 오브젝트 전달 받음 // response > body > ReadableStream에 우리가 원하는 데이터 들어있다.
      // response.json() API 이용하여 response의 body를 json object으로 변환
      .then((json) => json.items) // 출력 > json으로 변환됨 json은 배열 object자체기 때문에 json의 items만 전달;
  );
}

// main
loadItems()
  .then((items) => {
    console.log(items);
    // displayItems(items); // items를 html에 보여주고, 받아온 아이템즈 함수에 전달
    // setEventListener(items); // 버튼 누르면 적절하게 필터링해야 하기때문에 이벤트 리스너 추가
  })
  .catch(console.log);

 

 

4. displayItems() 함수를 정의하고 loadItems() 안에  호출하여 'items' 데이터를 통해 html의 li요소로 변환해 html을 업데이트시킨다.

// displayItems() > items 데이터를 html 요소로 변환하여 페이지에 표기되도록 한다.
function displayItems(items) {
  const container = document.querySelector(".items"); // ul.items
  container.innerHTML = items.map((item) => createHTMLString(item));
  // items(배열) 안에는 item Object 들이 들어있는데 이것을 html 요소로 변환해 준다. 각각의 item들을 html li태그로 변환하여 배열로 만들어 준다 > 한가지의 배열 형태에서 다른 형태의 배열로 변환(mapping)> map()
  // map() > 배열안에 들어있는 요소들에 지정된 콜백함수를 호출하여 함수에서 return된 값으로 가공하여 mapping하여 만들어 주는 것
  // join('') > 단순히 문자열의 배열을 > li가 반복되는 배열 > 한가지의 문자열로 병합한다. > ['<li></li>',<li></li>, ...] > ['<li></li><li></li>...'] > ,사라짐 이어서 그대로 출력
// 결론 : items의 데이터를 .map(item) > 문자로 변환하는 콜백함수(createHTMLString) 실행하여 배열로 다시 만들어주고 > 다시 만들어진 문자열 배열['','',...]을 .jpin('')으로 하나의 문자열['...']로 이어주어 innerHTML시킨다.
}

function createHTMLString(item) {
  // String 템플릿의 장점 : 문자열과 변수나 사용하고자하는 코드를 바로 중간에 섞어서 쓸 수 있음 > item의 이미지 경로는 item.iage , type, gender 등 섞어서 사용 가능하다.
  return `
    <li class="item">
        <img src="${item.image}" alt="${item.type}" class="item_thumbnail" />
        <span class="item_description">${item.gender}, ${item.size}</span>
    </li>
    `;
}

// main
loadItems()
  .then((items) => {
    console.log(items);
    displayItems(items); // items를 html에 보여주고, 받아온 아이템즈 함수에 전달
    // setEventListener(items); // 버튼 누르면 적절하게 필터링해야 하기때문에 이벤트 리스너 추가
  })
  .catch(console.log);

- displayItems(items) > items의 데이터를 .map(item) API  > 문자로 변환하는 콜백함수(createHTMLString) 실행하여 배열로 다시 만들어주고 > 다시 만들어진 문자열 배열['','',...]을 .jpin('')으로 하나의 문자열['...']로 이어주어 innerHTML시킨다(추가x새로 업데이트 > 기존의 자식 등 전부 없애고 새로 업데이트 한다).

 

 

5. setEventListener()함수를 정의하고 loadItems() 안에  호출하여 logo와 buttons(이벤트 위임) click시 필터링하는 이벤트 함수가 호출되도록 한다.

function setEventListener(items) {
  const logo = document.querySelector(".logo");
  const buttons = document.querySelector(".buttons");
  // 이벤트 위임 > 하나하나의 이벤트 리스너를 반복하는 것보다 버튼들이 들어있는 컨테이너에 이벤트를 등록해서 한 곳에서만 핸들링할 수 있도록 만든다 > 효율적
  logo.addEventListener("click", () => displayItems(items));
  buttons.addEventListener("click", (event) => onButtonClick(event, items)); // 버튼이 클릭되면 이벤트가 발생하고 이벤트 발생한 아이와, items을 전달
}

loadItems()
  .then((items) => {
    console.log(items);
    displayItems(items); 
    setEventListener(items); // 버튼 누르면 적절하게 필터링해야 하기때문에 이벤트 리스너 추가
  })
  .catch(console.log);

 

 

6. 클릭하는 event Object 안에 우리가 원하는 정보를 넣어 유용하게 사용하기 > html 태그에  'data-key = "type", 'data-value = "tshirt" 추가 > event.target 속성들로 이용이 가능해 진다.

<section class="buttons">
      <button class="btn">
        <img
          src="img/blue_t.png"
          alt="shirts_btn"
          class="imgBtn"
          data-key="type" // 데이터 키
          data-value="tshirt" // 데이터 값
        />
      </button>
      <button class="btn">
        <img
          src="img/blue_p.png"
          alt="pants_btn"
          class="imgBtn"
          data-key="type"
          data-value="pants"
        />
      </button>
      <button class="btn">
        <img
          src="img/blue_s.png"
          alt="skirt_btn"
          class="imgBtn"
          data-key="type"
          data-value="skirt"
        />
      </button>
      <button class="btn colorBtn blue" data-key="color" data-value="blue">
        Blue
      </button>
      <button class="btn colorBtn yellow" data-key="color" data-value="yellow">
        Yellow
      </button>
      <button class="btn colorBtn pink" data-key="color" data-value="pink">
        Pink
      </button>
    </section>

 

 

7. onButtonClick(event,items) 함수 정의하여 html에서 작성한 것들이 추가된 event 인자를 받아오고 event.target.dataset.key 확인하기

// 이벤트를 처리하는 함수는 on이벤트이름
function onButtonClick(event, items) {
  console.log(event.target.dataset.key); // html에서 지정한 의미있는 데이터 정보 가져오기
  console.log(event.target.dataset.value);
}

function setEventListener(items) {
  const logo = document.querySelector(".logo");
  const buttons = document.querySelector(".buttons");
  // 이벤트 위임 > 하나하나의 이벤트 리스너를 반복하는 것보다 버튼들이 들어있는 컨테이너에 이벤트를 등록해서 한 곳에서만 핸들링할 수 있도록 만든다 > 효율적
  logo.addEventListener("click", () => displayItems(items));
  buttons.addEventListener("click", (event) => onButtonClick(event, items)); // 버튼이 클릭되면 이벤트가 발생하고 이벤트 발생한 아이와, items을 전달
}

- 버튼들을 클릭하면 각각에 지정했던 key, vlaue 정보가 console.log 된다.

event 인자로 받아와 각 btn들의 data-key , data-value 정보들 가져오기

 

 

8. onButtonClick(event,items)에 key와 vlaue를 변수로 지정하고, 'items'의 데이터들을 필터링 하여 우리가 클릭한 버튼이 가지고 있는 지정된 정보(key,value)가 item의 데이터 안에 있는 경우만 추출하여 displayItems()에 인자로 보내 html에 띄우도록 하기

function onButtonClick(event, items) {
  const dataset = event.target.dataset;
  const key = dataset.key;
  const value = dataset.value;

  if (key == null || value == null) {
    return; // 해당하지 않는 경우 함수를 빨리 끝내버림
  }

  //   const filtered = items.filter((item) => item[key] === value);
  //   console.log(filtered);
  displayItems(items.filter((item) => item[key] === value));
  // item 안에 있는 key값이 우리가 지정한 value와 같은 아이들만 골라서 displayItems로 전달하기
  //   displayItems(filtered); // 해당하는 키와 값이 존재하는 경우에만 데이타가 보여질 수 있도록 함
}

 

 

9. 이와같이 코드를 작성하면 매번 버튼을 클릭할 때 마다 html이 새로 업데이트되어야 하는 문제가 있다.

이를 보완하기 위해 기존의 리스트들을 유지한 상태에서, 클릭 시 해당하는 정보를 가진 item만 .invisible 클래스를 추가, 삭제 하도록 만들 수 있다.

function onButtonClick(event, items) {
  const target = event.target;
  const key = target.dataset.key;
  const value = target.dataset.value;

  if (key == null || value == null) {
    return; // 해당하지 않을 경우 함수를 빨리 끝내버림
  }

  updateItems(items, key, value);
}

// item의 key와 value가 일치하는 것만 display:none 클래스를 삭제 , 추가
function updateItems(items, key, value) {
  items.forEach((item) => {
    if (item.dataset[key] === value) {
      item.classList.remove("invisible");
    } else {
      item.classList.add("invisible");
    }
  });
}

 

 

 

*본 포스팅은 드림코딩 '쇼핑몰 미니게임 클론코딩' 강의를 정리한 내용입니다.