본문 바로가기
dev/javascript

[javascript] 자바스크립트 자판기 만들기

by 최연탄 2022. 9. 28.
728x90
반응형

자바스크립트 자판기는 브라우저에서 구동되는 프로그램이기 때문에 c언어나 자바, 파이썬과는 다르게 화면에 UI를 그리고 변경하기가 쉽습니다. 이번 포스트에서는 사용자의 클릭 이벤트를 받아 음료를 얻을 수 있는 자판기를 만들어 보겠습니다.

전체 소스코드

index.html

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>JavaScript - Vending Machine</title>
  <link rel="stylesheet" href="style.css">
</head>

<body>
  <div class="container vertical">
    <h1>자판기</h1>

    <div id="machine-base" class="panel vertical">
      <div id="shelf" class="panel"></div>

      <div class="panel">
        <div id="current-money" class="panel"></div>
        <div id="insert-money" class="button">돈을 넣으세요</div>
      </div>
    </div>

    <div id="money-base" class="popup hide">
      <div class="panel"></div>
    </div>

    <div id="result-base" class="popup hide">
      <div class="panel button"></div>
    </div>
  </div>

  <script src="index.js"></script>
</body>

</html>

style.css

* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

h1 {
  color: #323330;
}

div {
  display: flex;
  align-items: center;
  justify-content: center;
  margin: 10px;
}

.hide {
  display: none;
}

.container {
  height: 100vh;
}

.panel {
  padding: 20px;
  border: 1px solid #000;
  border-radius: 10px;
  background-color: #fff;
}

.vertical {
  flex-direction: column;
}

.popup {
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  margin: 0;
  background-color: rgba(0, 0, 0, 0.5);
}

.popup>.panel {
  padding: 30px;
}

.button {
  padding: 10px 20px;
  border: 1px solid #000;
  border-radius: 10px;
  cursor: pointer;
}

index.js

const items = [
  { name: 'Coke', price: '1500' },
  { name: 'Coffee', price: '3000' },
  { name: 'Tea', price: '2600' },
  { name: 'Water', price: '300' },
];

const moneyList = [100, 500, 1000, 5000];

let currentMoney = 0;

const init = () => {
  addItemToShelf();
  addMoneyToPopup();

  document.querySelector('#insert-money').addEventListener('click', onClickInsert);
  document.querySelector('#result-base>.panel').addEventListener('click', hideAllPopup);

  render();
}

const render = () => {
  document.querySelector('#current-money').innerHTML = currentMoney.toString();
};

const addItemToShelf = () => {
  const shelfElement = document.querySelector('#shelf');

  items.forEach((item, index) => {
    const itemElement = createElement(`${item.name}<br>${item.price}`, index, onClickItem);

    shelfElement.appendChild(itemElement);
  });
};

const addMoneyToPopup = () => {
  const baseElement = document.querySelector('#money-base>.panel');

  moneyList.forEach((item, index) => {
    const itemElement = createElement(item, index, onClickMoney);

    baseElement.appendChild(itemElement);
  });
};

const createElement = (name, index, callback) => {
  const element = document.createElement('div');

  element.innerHTML = name;
  element.classList.add('button');
  element.setAttribute('data-index', index);
  element.addEventListener('click', callback);

  return element;
};

const showResultPopup = (text) => {
  const resultBaseElement = document.querySelector('#result-base');
  const resultPanelElement = document.querySelector('#result-base>.panel');

  resultPanelElement.innerHTML = text;
  showElement(resultBaseElement);
};

const hideAllPopup = () => document.querySelectorAll('.popup').forEach((element) => hideElement(element));

const showElement = (element) => element.classList.remove('hide');

const hideElement = (element) => element.classList.add('hide');

const onClickItem = (event) => {
  const targetElement = event.target;
  const itemIndex = targetElement.getAttribute('data-index');
  const item = items[itemIndex];

  if (item.price <= currentMoney) {
    currentMoney -= item.price;

    showResultPopup(`${item.name}을(를) 획득했다!`);
    render();
  } else {
    showResultPopup(`돈이 부족하다!`);
  }
};

const onClickInsert = (event) => {
  hideAllPopup();

  showElement(document.querySelector('#money-base'));
};

const onClickMoney = (event) => {
  const targetElement = event.target;
  const itemIndex = targetElement.getAttribute('data-index');
  const item = moneyList[itemIndex];

  currentMoney += item;

  render();

  hideElement(document.querySelector('#money-base'));
};

init();

위의 코드를 입력한 후 index.html을 실행하면 다음과 같은 화면을 볼 수 있습니다.

설명

먼저 index.html에 화면에 보여줄 자판기의 모양을 잡아줍니다. 처음 자판기 화면에는 물건 목록, 돈 넣은 금액, 돈 넣기와 같이 크게 세가지 항목을 보여주도록 했습니다. 또한 돈을 넣을 팝업과 결과를 보여줄 화면을 미리 정의해 놓고 첫 화면에는 일단 보이지 않게 숨겼습니다.

style.css에는 자판기에서 여러 항목들이 구분지어지도록 단순한 설정만 했습니다. 여기서 더 예쁘게 꾸미고 싶다면 각각의 클래스 마다 색이나 간격을 수정할 수 있습니다.

마지막으로 index.js에 자판기 구동의 핵심 코드가 들어있습니다. 첫 부분에는 자판기 프로그램에서 사용할 물건 목록과 넣을 수 있는 돈의 목록과 같은 데이터를 정의합니다.

const items = [
  { name: 'Coke', price: '1500' },
  { name: 'Coffee', price: '3000' },
  { name: 'Tea', price: '2600' },
  { name: 'Water', price: '300' },
];

const moneyList = [100, 500, 1000, 5000];

그리고 자판기를 사용함에 따라 돈을 넣고 물건을 샀을 때의 금액을 기억할 변수를 선언합니다.

let currentMoney = 0;

이제 본격적인 함수를 작성할 시간입니다. 먼저 자판기 프로그램을 초기화 하는 코드를 넣습니다. 이 초기화에는 물건 목록 데이터를 읽어서 화면에 물건을 진열하고 돈 넣기를 눌렀을 때 얼마를 넣을지 확인하는 화면도 만듭니다. 그 다음 돈 넣기 버튼을 눌렀을 때 실행될 코드를 정의하고 결과 화면이 나온 경우 글씨를 누르면 결과 화면이 사라지도록 클릭 이벤트를 추가합니다. 마지막으로 화면에 변경사항을 다시 그리도록 render() 함수를 실행합니다.

const init = () => {
  addItemToShelf();
  addMoneyToPopup();

  document.querySelector('#insert-money').addEventListener('click', onClickInsert);
  document.querySelector('#result-base>.panel').addEventListener('click', hideAllPopup);

  render();
}

 

거창한 이름의 화면 그리기 함수인 render()는 사실 자판기 프로그램에서는 현재 금액이 얼마나 남았는지만 표시하는 용도로 쓰입니다.

const render = () => {
  document.querySelector('#current-money').innerHTML = currentMoney.toString();
};

위 이미지 처럼 넣을 돈을 선택하면 render() 함수를 실행하여 자판기에 넣은 돈의 액수를 다음 이미지 처럼 갱신하게 됩니다.

이후의 소스코드는 앞서 설명한 기능이 동작하도록 dom을 조작하는 코드입니다. dom 조작은 넘어가고 자판기 화면에서 돈 넣기 버튼을 누르면 실행될 코드가 다음 예제입니다.

const onClickInsert = (event) => {
  hideAllPopup();

  showElement(document.querySelector('#money-base'));
};

위의 코드는 이미 켜져있을 지 모르는 팝업을 모두 숨기고 돈 선택 팝업을 보이게 합니다.

이 팝업은 코드의 맨 처음에 정의했던 moneyList의 내용을 토대로 만들어진 화면으로 moneyList의 값이 바뀐다면 이 화면도 바뀌게 됩니다. 여기서 액수를 클릭하면 실행될 코드는 다음과 같습니다.

const onClickMoney = (event) => {
  const targetElement = event.target;
  const itemIndex = targetElement.getAttribute('data-index');
  const item = moneyList[itemIndex];

  currentMoney += item;

  render();

  hideElement(document.querySelector('#money-base'));
};

액수를 클릭하면 currentMoney에 선택한 금액만큼 추가하고 자판기 화면을 갱신하고 팝업을 숨기도록 합니다.

이제 자판기에서 음료를 누른 경우 실행될 코드를 정의 해야합니다.

const onClickItem = (event) => {
  const targetElement = event.target;
  const itemIndex = targetElement.getAttribute('data-index');
  const item = items[itemIndex];

  if (item.price <= currentMoney) {
    currentMoney -= item.price;

    showResultPopup(`${item.name}을(를) 획득했다!`);
    render();
  } else {
    showResultPopup(`돈이 부족하다!`);
  }
};

음료 버튼을 누르면 먼저 눌린 항목이 몇 번째 항목인지 확인하여 어떤 항목이 선택되었는지 판단하고 현재 자판기에 넣은 돈의 액수인 currentMoney와 음료의 가격을 비교하여 음료가 더 비싸면 화면에 "돈이 부족하다!"라는 메시지를 보여주게 됩니다.

반면 자판기에 충분한 액수를 넣었다면 currentMoney에서 음료 가격을 차감한 후 아이템을 얻었다는 메시지를 보여주고 render() 함수로 자판기 화면을 갱신하게 됩니다.

관련 글

자바스크립트 버튼 클릭

자바스크립트 forEach 사용 방법

forEach 루프 도는 방법

DOM createElement 사용 방법

classList 사용 방법

querySelector 사용 방법

 

반응형

댓글