고난과 역경을 거친 자판기 JS 구현하기

blueprint·2022년 6월 22일
0

이전에도 나는 JS를 독학하다 한계치에 다다른 후 포기한 경험이 있는 사람으로써 이대로 가다가는 또 아무것도 머리에 넣지 못할 것 같아 실패한 경험을 쓰기로 했다.

두 달 전에 HTML/CSS 작업은 완료해 둔 상태의 밴딩머신이 동작할 수 있도록 해 주어야 하는데...... 마음먹은 대로 움직이지를 않아 멘토님의 도움과 여기저기서 얻은 정보로 수정해 보았다.

💡 요구사항 명세

  1. 판매할 음료에 대한 데이터는 따로 분리되어 있어야 합니다. (혹은 API로 받아야 합니다.)
  2. 돈의 입금과 음료의 선택 시점은 자유롭지만 돈이 모자라면 음료가 나와서는 안 됩니다.
  3. 거스름돈이 나와야 합니다.
  4. 버튼을 누르면 상품이 1개씩 추가됩니다. (일반적인 자판기와 동일)

💣 직면한 문제들

1. 데이터 객체 분리 후 연결하기

  • 기존에 구현한 방식
    : drink[name]의 형식으로 데이터에 접근해 음료의 이름을 읽어 오려고 시도함

  • 문제점
    : name을 선언하지 않았기 때문에 값을 읽어 올 수 없음

  • 해결
    : button 동작 시 해당 음료의 정보를 데이터 객체에서 가져올 수 있도록 하기 위해 dataset을 사용

💡 dataset 사용법

  1. HTML 태그에 접두어(data)가 붙은 속성을 추가 후 값을 지정한다.
  2. JS에서 요소를 선택 후 dataset 객체에서 속성을 읽도록 한다. 접두어(data)는 생략해 사용한다.

HTML

<button class="btn-cola btn-original" data-name="Original_Cola" data-price="1000">

JS

  • drink의 이름(name)이 data-name과 일치하는 객체의 count 값이 0일 때 alert 띄움
  • 여기서 drink는 데이터를 담고 있는 변수
if (drink[name].count == 0) {
    alert("품절된 음료입니다!");
}

2. 입금 버튼(depositBtn)을 누르면 입력한 금액이 잔액으로 뜨면서 동시에 소지금도 차감되도록 하기

  • 기존에 구현한 방식
    : 자판기 최초 동작 시 insertAdjacentHTML를 통해 입금한 금액을 띄우는 태그를 추가하도록 함

  • 문제점
    : 입금액을 입력 후 입금 버튼을 눌러도 잔액 칸에 금액이 뜨지 않음

  • 이유

    • balance가 담고 있는 요소가 span.balance인데, 여러 번의 수정 과정에서 클래스명 balance를 작성하지 않음
    • myMoney는 HTML 요소가 아닌 50000이라는 숫자를 담고 있는 변수이므로 textContent가 동작하지 않는 것이 당연함
  • 해결

    • 요소와 연산을 위한 변수를 분리해 작업
    • moneyDiv 뒤에 insertAdjacentHTML을 이용해 span 태그 삽입
    • 삽입된 span 태그를 담는 변수 moneySpan 선언
      : 두 번째 동작부터는 moneySpan을 선택해 숫자만 바꿔 주도록 할 것이기 때문

수정 전 코드

const depositBtn = document.querySelector(".btn-deposit"); // 입금 버튼

let myMoney = 50000; // 소지금
let balance = document.querySelector(".balance"); // 잔액
let count = 0; // 자판기 작동 횟수

// 소지금 띄우기
let moneyDiv = document.querySelector(".my-money");
let moneySpan = moneyDiv.insertAdjacentHTML(
  "beforeend",
  `<span class="current">${myMoney}</span>`
);
myMoney.textContent = comma(myMoney);

수정 후 코드

const depositBtn = document.querySelector(".btn-deposit"); // 입금 버튼
const balance = document.querySelector(".balance"); // 잔액 요소

let myMoney = 50000; // 기본 소지금
let balanceValue = 0; // 현재 잔액

// 소지금 띄우기
let moneyDiv = document.querySelector(".my-money");
moneyDiv.insertAdjacentHTML(
  "beforeend",
  `<span class="current">${comma(myMoney)}</span>`
);
const moneySpan = document.querySelector(".current"); // 현재 소지금 요소

// 입금 버튼 동작 로직
    // 현재 소지금과 잔액 값 띄우기
    balance.textContent = comma(balanceValue);
    moneySpan.textContent = comma(myMoney);

3. 최초 동작 시에만 insertAdjacentHTML로 태그 추가되도록 하고 이후부터는 입금한 값만 바뀌도록 하기

  • 기존에 구현한 방식
    : count 변수 이용해 실행 횟수를 세도록 한 후 자판기 최초 동작 시에만 insertAdjacentHTML를 이용해 잔액을 띄우도록 함

  • 문제점
    : 상당히 복잡한 코드, 깔끔하지 않고 동작하지도 않음

  • 해결

    • 위에서 요소와 연산에 쓰일 변수를 분리한 작업으로 해결 가능
    • moneySpan이라는 텍스트가 들어갈 곳은 insertAdjacentHTML로 추가해 뒀으니 누를 때마다 입금액이 누적되도록 textContent로 넣어 주기만 하면 됨
    • input 요소에서 입금액 입력 후 입금 버튼 클릭 시 칸이 비워질 수 있도록 초기화 해 주는 기능 추가
// 소지금 띄우기
let moneyDiv = document.querySelector(".my-money");
moneyDiv.insertAdjacentHTML(
  "beforeend",
  `<span class="current">${comma(myMoney)}</span>`
);
const moneySpan = document.querySelector(".current");

depositBtn.addEventListener("click", () => {
  let inpDeposit = parseInt(document.querySelector(".inp-deposit").value);

  let check = /^[0-9]+$/; // 숫자인지 확인
  if (check.test(inpDeposit)) {
    myMoney -= inpDeposit;
    balanceValue += inpDeposit;

    alert(`${inpDeposit} 원이 입금되었습니다.`);

    // 잔액 띄우기
    let balanceDiv = document.querySelector(".box-balance");
    balanceDiv.insertAdjacentHTML(
      "beforeend",
      `<span class="balance">${balance}</span>`
    );

    totalMoney = `${myMoney}`;
  } else if (check.test(inpDeposit) && count != 0) {
    myMoney -= inpDeposit;
    // balance += inpDeposit
    balance = `${balance - inpDeposit}`;
  } else {
    alert("숫자로만 입력해 주세요.");
  }
  count++;
});

수정 후 코드

depositBtn.addEventListener("click", () => {
  let inpDeposit = parseInt(document.querySelector(".inp-deposit").value); // 입금액 읽어 오기
  let check = /^[0-9]+$/; // 숫자인지 확인

  if (check.test(inpDeposit)) {
    // 소지금과 잔액 계산
    myMoney -= inpDeposit;
    balanceValue += inpDeposit;
    // 현재 소지금과 잔액 값 띄우기
    balance.textContent = comma(balanceValue);
    moneySpan.textContent = comma(myMoney);
  } else {
    alert("숫자로만 입력해 주세요.");
  }
  document.querySelector(".inp-deposit").value = "";
});

4. 버튼을 이용해 데이터 객체에 접근하기

  • 기존에 구현한 방식
    : querySelector를 이용해 .btn-cola를 읽어 와 데이터에 접근 시도

  • 문제점

    • .btn-cola 클래스를 가지는 버튼이 여러 개이기 때문에 첫 번째 요소만 반환하게 됨
    • 각자 다른 데이터 객체에 접근하도록 설정하는 방법을 찾지 못했음
  • 해결 방법 1
    : querySelectorAll을 이용해 모든 button.btn-cola를 가지고 온 후 forEach로 순회

const colaBtns = document.querySelectorAll(".btn-cola");

colaBtns.forEach(button => {
  button.addEventListener("click", () => {
    // 동작
  });
})
  • 해결 방법 2 (이 방법으로 해결)
    : button.btn-cola들의 부모 요소인 ul.colaList를 가지고 와 이벤트 부여 후 자식 요소들에게 이벤트 위임
const colaList = document.querySelector(".colaList"); // 콜라 리스트

colaList.addEventListener("click", (e) => {
  if(e.target.className == "btn-cola") {
    // 동작
  }
})

더 추가 예정

0개의 댓글

관련 채용 정보

Powered by GraphCDN, the GraphQL CDN