바닐라 자바스크립트로 만드는 계산기

Duboo·2022년 3월 22일
1
post-thumbnail

순수 자바스크립트를 이용해서 만드는 계산기입니다.
깃헙 전체 코드 확인


진행 과제

위 그림에서 동작과정과 같이 입력, 계산 과정, 임시 결과를 출력해주는 계산기를 만들기 위해 필요한 과제

  • 입력창(결과창), 임시 결과창, 계산 과정창
  • . 중복 체크
  • 숫자와 기호 비교
  • AC 버튼 [전체 초기화]
  • 각 기호별 계산
  • +@ 키보드 입력

html

<div class="wrap">
    <div class="calc">
		<div class="display-container">
            <div class="display-1">0</div>
            <div class="display-2">0</div>
            <div class="temp-result">0</div>
        </div>
        <button class="button all-clear">AC</button>
        <button class="button operation">&#177</button>
        <button class="button operation">%</button>
        <button class="button operation">&#247</button>
        <button class="button number">7</button>
        <button class="button number">8</button>
        .
        .
        . 
    </div>
</div>

html에서 중요한 부분은 display-container 부분입니다.
위 과제의 입력창(결과창), 임시 결과창, 계산 과정창을 출력하기 위해 3개의 출력창이 필요합니다.

html tip

  • 숫자와 기호를 다른 클레스명으로 묶어서 사용하면 js 코드에서 편하게 할 수 있습니다.

  • 기호의 경우 아이콘을 사용하지 않고 유니코드를 사용하면 편합니다.
    유니코드 👈


css

스타일은 본인의 취향에 맞게 적용합니다.

저는 아이폰 계산기 디자인을 참고했습니다.

css tip

  • css팁은 계산기 전체를 감싸고있는 .calcdisplay: grid 속성을 주면 정렬을 편하게 할 수 있습니다.
  • grid의 자식에게 grid-column: span 4 속성 값을 적용하면 한줄 전체를 사용하게 만들 수 있습니다.
    • 같은 방법으로 숫자 0 의 경우 2칸을 차지grid-column: span 2하게 만들 수 있습니다.

EXAMPLE 👇

.calc {
    display: grid;
    grid-template-columns: repeat(4, 1fr);
}

.display-container {
    grid-column: span 4;
}

javascript

가장 먼저 필요한 태그를 불러옵니다.

DOM

const display1El = document.querySelector(".display-1");
const display2El = document.querySelector(".display-2");
const tempResultEl = document.querySelector(".temp-result");
const numbersEl = document.querySelectorAll(".number");
const operationEl = document.querySelectorAll(".operation");
const equalEl = document.querySelector(".equal");
const clearAllEl = document.querySelector(".all-clear");

출력에 필요한 태그 3가지와 숫자가 들어있는 태그들 기호가 들어있는 태그들 그리고 특별한 기능을 하는 태그 두가지를 불러옵니다.

VARIABLES

다음은 필요한 변수를 전역에 선언합니다.

// 출력할 값을 담을 변수
let display1Num = "";
let display2Num = "";
let result = null;

// 기호를 담을 변수
let lastOperation = "";

// . 중복 체크를 위한 변수
let haveDot = false;

FUNCTIONS

가장 먼저 .의 중복 체크와 클릭한 숫자를 변수에 넣고 변수 값을 출력하는 부분입니다.

// number click Event
numbersEl.forEach(number => {
  number.addEventListener("click", (e) => {
    // .의 중복 체크 부분
    if(e.target.innerText === "." && !haveDot) {
      haveDot = true;
    } else if(e.target.innerText === "." && haveDot) {
      return;
    }
    // 클릭한 숫자값 변수에 넣고 변수 값을 출력하는 부분
    display2Num += e.target.innerText;
    display2El.innerText = display2Num;
  })
})

먼저 .의 중복체크가 필요하며 체크가 끝난 값을 출력하는 과정입니다.


다음은 기호 클릭시 일어날 수 있는 경우를 체크합니다.

// operation click Event
operationEl.forEach(operation => {
  operation.addEventListener("click", (e) => {
    // 기호 앞에 아무값도 없을 경우 아무것도 실행할 수 없음
    if(!display2Num) {
      return;
    }
    // 기호 다음 두번째 값은 .을 다시 사용할 수 있어야함
    haveDot = false;
    // 클릭한 기호를 변수에 넣음
    const operationSymbol = e.target.innerText;
    // 첫번째 값과 두번째 값 그리고 기호가 모두 있을 때
    if(display1Num && display2Num && lastOperation) {
      // 계산 실행
      mathOperation();
    } else {
      // 하나라도 없을 경우 입력값을 임수 출력 결과창에 사용할 변수에 숫자로 변환 후 담아둠
      result = parseFloat(display2Num);
    }
    clearVar(operationSymbol);
    lastOperation = operationSymbol;
  })
})

먼저 첫번째 값 즉, 기호 앞에 아무값도 없을 경우 아무것도 실행할 수 없습니다.
[다만 이 경우에 첫번째 값에 음수를 넣을 수 없는 오류가 있습니다.]

// 첫번째 입력 숫자와 기호를 합쳐 출력하는 함수 및 임시 결과창에 값 출력
function clearVar(symbol = "") {
  display1Num += `${display2Num} ${symbol} `;
  display1El.innerText = display1Num;
  display2El.innerText = "";
  display2Num = "";
  // 임시 결과창에 저장한 값 출력
  tempResultEl.innerText = result;
}

위 코드를 Arrow functions(화살표 함수)로 변경해도 상관없습니다.

Template Literals(템플릿 리터럴) 👉 ${display2Num} ${symbol}
문자열을 연결하기 위해 + 연산자를 사용하기 보단 es6의 템플릿 리터널을 사용하는 것이 더 깔끔하고 보기 좋은 코드를 만들 수 있습니다.


Default parameters(기본 매개 변수) 👉 clearVar(symbol = "")
매개 변수를 사용하지 않을 경우 미리 기본값을 정의할 수 있으며, 누락된 매개 변수를 사용해도 함수를 실행할 때 오류를 반환하지 않습니다.


// 모든 값이 있을 경우 각 기호별로 계산 및 임시 결과창에 계산 결과 출력
function mathOperation() {
  if(lastOperation === "×") {
      result = parseFloat(result) * parseFloat(display2Num);
  } else if(lastOperation === "+") {
      result = parseFloat(result) + parseFloat(display2Num);
  } else if(lastOperation === "−") {
      result = parseFloat(result) - parseFloat(display2Num);
  } else if(lastOperation === "÷") {
      result = parseFloat(result) / parseFloat(display2Num);
  } else if(lastOperation === "%") {
      result = parseFloat(result) % parseFloat(display2Num);
  }
}

각 기호별 계산을 진행합니다.
[±의 계산은 따로 정의하지 않았습니다.]


특수 기호 클릭시 발생하는 이벤트를 정의합니다.

// equalEl click Event
equalEl.addEventListener("click", (e) => {
  // 첫번째 값 혹은 두번째 값이 없을 경우는 계산 불가하여 아무것도 하지 않음
  if(!display1Num || !display2Num) {
    return;
  }
  // 둘다 값이 있을 경우
  haveDot = false;
  mathOperation();
  clearVar();
  display2El.innerText = result;
  tempResultEl.innerText = "";
  display2Num = result;
  display1Num = "";
})
// all clear(AC) click Event
clearAllEl.addEventListener("click", (e) => {
  // 모든 값 초기화
  display1El.innerText = "0";
  display2El.innerText = "0";
  display1Num = "";
  display2Num = "";
  result = "";
  tempResultEl.innerText = "0";
  haveDot = false;
})

+@ window keydown Event

키보드를 이용해 조작할 수 있는 방법입니다.

window.addEventListener("keydown", (e) => {
  // 누른 키의 값을 받아와 각 종류 별 매칭되는 클릭과 연결
  if(
      e.key === "0" ||
      e.key === "1" ||
      e.key === "2" ||
      e.key === "3" ||
      e.key === "4" ||
      e.key === "5" ||
      e.key === "6" ||
      e.key === "7" ||
      e.key === "8" ||
      e.key === "9" ||
      e.key === "."
  ) {
      clickButtonEl(e.key);
  } else if(
      e.key === "+" ||
      e.key === "/" ||
      e.key === "%"
  ) {
      clickOperation(e.key);
  } else if(e.key === "*") {
      clickOperation("×");
  } else if(e.key === "-") {
      clickOperation("−");
  } else if(e.key === "?") {
      clickOperation("÷");
  } else if(e.key === "Enter" || e.key === "=") {
      ClickEqual();
  }
});

/의 경우 키입력이 안되는 오류가 있어 동일한 키인 ?로 대채했습니다.

// 숫자 클릭
function clickButtonEl(key) {
  numbersEl.forEach(button => {
      if(button.innerText === key) {
          button.click();
      }
  })
};
// 기호 클릭
function clickOperation(key) {
  operationEl.forEach(button => {
      if(button.innerText === key) {
          button.click();
      }
  })
};
function ClickEqual() {
  equalEl.click();
};

keydown 이벤트로 일어나는 키입력과 클릭을 연동시켜서 작동하는 방식입니다.

profile
둡둡

0개의 댓글