[JS] 계산기 만들기

2020. 6. 14. 02:36Javascript/응용

1. 완성본

완성본 영상은 다음과 같다. 간단하게 사칙연산(더하기, 빼기, 곱하기, 나누기)만을 구현했다.

 

[영상] 완성본 영상

 

 

2. HTML 구조

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <link rel="stylesheet" href="styles.css" />
  </head>
  <body>
    <div class="calc-wrapper">
      <form id="calc">
        <div class="calc__column">
          <div class="calc__display" id="calc-display">0</div>
        </div>
        <div class="calc__column">
          <input type="button" id="num9" class="num" value="9" />
          <input type="button" id="num8" class="num" value="8" />
          <input type="button" id="num7" class="num" value="7" />
          <input type="button" id="clear" value="C" />
        </div>
        <div class="calc__column">
          <input type="button" id="num4" class="num" value="4" />
          <input type="button" id="num5" class="num" value="5" />
          <input type="button" id="num6" class="num" value="6" />
          <input type="button" id="plus" value="+" />
        </div>
        <div class="calc__column">
          <input type="button" id="num1" class="num" value="1" />
          <input type="button" id="num2" class="num" value="2" />
          <input type="button" id="num3" class="num" value="3" />
          <input type="button" id="minus" value="-" />
        </div>
        <div class="calc__column">
          <input type="button" id="times" value="×" />
          <input type="button" id="num0" value="0" />
          <input type="button" id="divide" value="÷" />
          <input type="submit" id="result" value="=" />
        </div>
      </form>
    </div>
    <script src="index.js"></script>
  </body>
</html>

 

 

3. CSS

* {
  box-sizing: border-box;
}

html,
body {
  height: 100%;
  padding: 0;
  margin: 0;
}

.calc-wrapper {
  width: 250px;
  height: 300px;
  position: relative;
  margin: 0 auto;
  margin-top: 50px;
  background-color: #f9f9f9;
  border: 1px solid #dfe1e5;
  border-radius: 20px;
  padding: 10px;
  display: flex;
  flex-direction: column;
}

.calc-wrapper form {
  position: relative;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  margin: auto;
}

.calc__column:first-child {
  margin-bottom: 10px;
}

.calc__column:nth-child(n + 2) {
  display: grid;
  grid-template-columns: 55px 55px 55px 55px;
  grid-gap: 2px;
  margin-bottom: 10px;
  padding: 1px;
}

.calc__column:last-child {
  margin-bottom: 0;
}

.calc__column .calc__display {
  height: 50px;
  padding: 10px;
  text-align: right;
  font-family: Consolas;
  font-size: 25px;
  background-color: #ffffff;
  border: 1px solid #dfe1e5;
  border-radius: 5px;
}

.calc__column input {
  height: 40px;
  background-color: #dfe1e5;
  border: none;
}

.calc__column .num {
  background-color: #f1f3f4;
}

.calc__column input:focus {
  outline: none;
}

.calc__column input:hover {
  border-radius: 5px;
  background-color: rgba(0, 0, 0, 0.15);
}

.calc__column input:active {
  border-radius: 5px;
  border: 1px solid rgba(0, 0, 0, 0.52);
}

 

 

4. 자바스크립트

 프로그램 코드는 크게 어렵지 않다. 계산 방식은 후위 표기법을 이용했다.

 $$ 3 + 2 \times 4 $$

 

 를 예로 들어보자.

 

 후위 표기법이란 컴퓨터가 계산하기 쉬운 수식을 말한다. 후위 표기법을 사용하기 위해서는 스택 자료구조가 필요하다. 우리가 사용하는 위 식은 보통 중위 표기법이라고한다. 일단 이렇게만 알아두자. 이제 위 식을 컴퓨터의 계산방식을 이용해서 답을 구하는 과정을 설명하도록 하겠다.

 

 

 [그림 1]을 보자. [그림 1]에는 숫자를 담는 스택연산자를 담는 스택이 있다. 먼저, 첫 번째 요소 3을 빼서 숫자를 담는 스택에 넣는다.

 

[그림 1] 첫 번째 과정

 

 

그 다음단계로 '+' 연산자연산자를 넣는 스택에 넣는다.

 

[그림 2] 두 번째 과정

 

 세 번째 단계 역시 2를 숫자를 넣는 스택에 넣는다.

 

[그림 3] 세 번째 과정

 

그 다음 단계에는 곱하기 연산자를 만나게 된다. 사칙 연산 중 곱하기, 나머지 연산자는 어느 위치에 있더라도 더하기, 빼기 연산보다 먼저 계산해야한다. 그런 이유로

$$ 2 \times 4$$

 

를 계산하기 위해서는 다음과 같은 과정이 필요하다.

 

  1.  숫자를 넣는 스택에서 2를 꺼낸다.

  2.  곱하기 연산 다음 숫자 4를 배열에서 빼낸다.

  3.  2와 4를 곱해서 숫자를 넣는 스택에 넣는다.

 

[그림 4] 네 번째 과정

 

 마지막 단계연산자 스택에 있는 + 값을 꺼낸다. 꺼낸 값은 더하기 기호이므로 숫자 스택에 있는 8, 3 두 수를 더해서 숫자를 넣는 스택에 넣는다. 그러면 숫자를 넣는 스택에는 최종적으로 계산 결과 데이터만이 들어있게 된다.

 

[그림 5] 마지막 단계

 

 따라서 프로그램 코드는 다음과 같다.

const form = document.querySelector("form");
const btn = form.querySelectorAll("input");
const display = form.querySelector("#calc-display");

let equation = [];
let num = "";

const numStack = [];
const opStack = [];

function handleDisplay(value) {
  // 숫자 입력 또는 계산 결과 출력할 곳
  value = value.substr(0, 14);
  console.log(value);
  if (!isNaN(Number(value))) {
    if (num.length <= 14) {
      num += value;
      display.innerText = num;
    }
  } else {
    equation.push(Number(display.innerText));
    num = "";
    equation.push(value);
    display.innerText = "0";
  }
}

function handleClear(event) {
  equation = [];
  num = "";
  display.innerText = "0";
}

function handleInput(event) {
  const value = event.target.value;
  handleDisplay(value);
}

function calculate() {
  //중위 표기법을 후위 표기법으로 변환
  //inorderfix -> postorderfix
  while (equation.length > 0) {
    const item = equation.shift();
    console.log("item", item);
    if (!isNaN(Number(item))) {
      //숫자 일때
      numStack.unshift(item);
    } else {
      //숫자가 아닐 때
      //곱하기나 나눗셈을 만났을 때!
      if (item === "×") {
        const num1 = numStack.shift();
        const num2 = equation.shift();
        numStack.unshift(num1 * num2);
      } else if (item === "÷") {
        const num1 = numStack.shift();
        const num2 = equation.shift();
        numStack.unshift(num1 / num2);
      } else {
        opStack.unshift(item);
      }
    }
  }

  // 계산
  while (opStack.length) {
    const op = opStack.shift();
    const num2 = numStack.shift();
    const num1 = numStack.shift();

    if (op === "+") numStack.unshift(num1 + num2);
    else if (op === "-") numStack.unshift(num1 - num2);
  }

  //출력!!
  console.log("정답", numStack);
  handleDisplay("" + numStack.shift());
}

function handleSubmit(event) {
  event.preventDefault();
  equation.push(Number(num));

  num = "";
  display.innerText = "";

  calculate();
}

function init() {
  for (let i = 0; i < btn.length - 1; i++) {
    if (i === 3) btn[i].addEventListener("click", handleClear);
    else btn[i].addEventListener("click", handleInput);
  }
  form.addEventListener("submit", handleSubmit);
}

init();