[TIL] 내배캠 사전캠프 6일차

yeols·2023년 9월 4일
0

[TIL]

목록 보기
5/72

뭔가 하다보니 사전캠프 2주차가 시작되었다
마냥 이론만 공부하자니 머리가 지끈거려 혼자 토이 프로젝트를 해보기로 했다.

javacript로 계산기 만들기

생긴모습은 이렇게 생겼다.

머리속의 전략

계산기를 퍼블리싱하며 어떻게 만들어야 할까 고민을 하면서 만들기 시작했다.
선입력 된 값을 가지고 있다가 연산자를 입력 받고 그 후에 후입력 되는 값과 연산을 하는 구조로 만들기로 정하고 시작했다.

우선 버튼이 되는 divclassbuttonoperator로 나누었다.
이유는 숫자 버튼이 입력되었을때, 연산자가 입력 되었을때 처리를 나누고 싶었기 때문이다.
숫자 버튼은 계속 입력되는 값을 이전 숫자와 붙여 줘야하고, 연산자 버튼은 입력을 받고 후입력 되는 숫자를 받아야 하기에 서로 나누어 처리했다.
그리고 숫자는 연산을 제외하고 전부 문자열로 다루었다.
문자열로 다룬 이유는 0.을 입력 받았을때 숫자로 다루면 javascript가 0으로 치환시키기 때문에 소수점을 입력할 수 가 없어서 문자열로 다루었다.


html

<!doctype html>
<html lang="ko">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Calculator</title>
    <!--fonts-->
    <link rel="preconnect" href="https://fonts.googleapis.com" />
    <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
    <link
      href="https://fonts.googleapis.com/css2?family=Gugi&display=swap"
      rel="stylesheet"
    />

    <link rel="stylesheet" href="css/styles.css" />
  </head>
  <body>
    <section class="app-container">
      <header class="app-header">
        <h1>사전 캠프 Calculator</h1>
      </header>
      <main class="app-main">
        <div class="div-result">
          <span class="result">0</span>
          <input type="hidden" id="current_type" value="0" />
        </div>
        <div class="button one"><span>1</span></div>
        <div class="button two"><span>2</span></div>
        <div class="button three"><span>3</span></div>
        <div class="button four"><span>4</span></div>
        <div class="button five"><span>5</span></div>
        <div class="button six"><span>6</span></div>
        <div class="button seven"><span>7</span></div>
        <div class="button eight"><span>8</span></div>
        <div class="button nine"><span>9</span></div>
        <div class="button zero"><span>0</span></div>
        <div class="operator add"><span>+</span></div>
        <div class="operator minus"><span>-</span></div>
        <div class="operator division"><span>/</span></div>
        <div class="operator multiply"><span>X</span></div>
        <div class="operator equal"><span>=</span></div>
        <div class="button dot"><span>.</span></div>
        <div class="operator clear"><span>clear</span></div>
      </main>
    </section>

    <script src="js/app.js"></script>
  </body>
</html>

css

/* Reset */
body {
  margin: unset;
  padding: unset;
}
h1,
h2,
h3,
h4,
h5 {
  margin: unset;
  font-size: unset;
  font-weight: unset;
}

/* Typography */
:root {
  font-size: 10px;
}

body {
  font-size: 1.6rem;
  line-height: 1.67;
}

/* Contents */
body {
  height: 100vh;
  display: flex;
  font-family: "Gugi", cursive;
}
.app-container {
  max-width: 60%;
  margin: auto;
  flex-grow: 1;
}

.app-header {
  background-color: rgba(0, 0, 0, 0.1);
  text-align: center;
  font-size: 5rem;
  font-weight: bold;
  padding: 20px 0;
  box-sizing: border-box;
  border-radius: 10px;
  box-shadow: 0 -1px rgba(0, 0, 0, 0.2);
}

.app-main {
  height: calc(100vh - 400px);
  font-size: 3rem;
  margin-top: 10px;
  display: grid;
  grid-gap: 10px;
  grid-template: repeat(7, 1fr) / repeat(4, 1fr);
  grid-template-areas:
    "result result result result"
    "result result result result"
    "clear clear division multiply"
    "seven eight nine minus"
    "four five six add"
    "one two three equal"
    "zero zero dot equal";
}
.div-result {
  grid-area: result;
}
.button.one {
  grid-area: one;
}
.button.two {
  grid-area: two;
}
.button.three {
  grid-area: three;
}
.button.four {
  grid-area: four;
}
.button.five {
  grid-area: five;
}
.button.six {
  grid-area: six;
}
.button.seven {
  grid-area: seven;
}
.button.eight {
  grid-area: eight;
}
.button.nine {
  grid-area: nine;
}
.button.zero {
  grid-area: zero;
}
.operator.add {
  grid-area: add;
}
.operator.minus {
  grid-area: minus;
}
.operator.division {
  grid-area: division;
}
.operator.multiply {
  grid-area: multiply;
}
.operator.equal {
  grid-area: equal;
}
.operator.dot {
  grid-area: dot;
}
.operator.clear {
  grid-area: clear;
}

.app-main > div {
  display: flex;
  justify-content: center;
  align-items: center;
  background-color: rgba(0, 0, 0, 0.2);
  border-radius: 10px;
  cursor: pointer;
  transition: background-color 0.5s ease-in;
}
.app-main > div:first-child {
  background-color: rgba(0, 0, 0, 0.1);
}

.app-main > div:not(:first-child):hover {
  background-color: rgba(0, 0, 0, 0.4);
}

.div-result {
  display: block;
  text-align: right;
  padding: 0 20px;
  font-size: 4rem;
}
.div-result span {
  width: 100%;
}

후.. 퍼블리싱도 조금 조금 뭐라도 만들어야 공부했던것이 기억나고 조금씩 발전하는것 같다.


javascript

// button
const numBtn = document.querySelectorAll(".button");
const operatorBtn = document.querySelectorAll(".operator");

// 결과 span
const resultSpan = document.querySelector(".result");

// 현재 상태 type
const currentType = document.querySelector("#current_type");

// 연산자 저장
let currentOperator = "";
let waitingOperator = "";

// 대기 값, 결과 값
let currentNumber = "0";
let resultNumber = "0";

//숫자 버튼 클릭 '.' 포함
numBtn.forEach((item) => {
  item.addEventListener("click", (event) => {
    if (currentType.value !== "0") {
      currentType.value = "0";
      printResult("");
    }

    // 현재 숫자
    currentNumber = String(currentNumber) + event.target.innerText;
    if (!currentNumber.includes(".")) {
      if (currentNumber.charAt("0") === "0")
        currentNumber = currentNumber.substring(1);
    }
    printResult(currentNumber);
  });
});

//연산자 버튼 클릭
operatorBtn.forEach((item) => {
  item.addEventListener("click", (event) => {
    waitingOperator = currentOperator;
    currentOperator = event.target.innerText;

    // currentOperator가 clear 이면 모든걸 초기화한다.
    if (currentOperator === "clear") {
      calculator(currentOperator, resultNumber, currentNumber);
      return;
    }

    // currentOperator가 = 이면 waitingOperator에 있는 연산을 하고 waitingOperator를 초기화
    if (currentOperator === "=") {
      calculator(waitingOperator, resultNumber, currentNumber);
      waitingOperator = "";
      return;
    }

    if (waitingOperator === "") {
      resultNumber = currentNumber;
      printResult(resultNumber);
      currentNumber = "0";
      currentType.value = "1";
    } else {
      calculator(waitingOperator, resultNumber, currentNumber);
    }
  });
});

// 결과 출력 함수
const printResult = (num) => {
  const option = {
    maximumFractionDigits: 6,
  };
  num = num === "" ? "0" : num;
  resultSpan.innerText = num
    .toString()
    .replace(/\B(?<!\.\d*)(?=(\d{3})+(?!\d))/g, ",");
};

// currentNum, type 초기화
const resetCurrentState = () => {
  currentType.value = "1";
  currentNumber = 0;
};

const delDot = (num) => {
  if (num.length > 0) {
    if (num.charAt(num.length - 1) === ".")
      return num.substring(0, num.length - 1);
    else return num;
  } else {
    return "0";
  }
};

const calculator = (operator, operatorNum1, operatorNum2) => {
  operatorNum1 = delDot(operatorNum1);
  operatorNum2 = delDot(operatorNum2);

  switch (operator) {
    case "+":
      resultNumber = String(
        parseFloat(operatorNum1) + parseFloat(operatorNum2)
      );
      printResult(resultNumber);
      resetCurrentState();
      break;
    case "-":
      resultNumber = String(
        parseFloat(operatorNum1) - parseFloat(operatorNum2)
      );
      printResult(resultNumber);
      resetCurrentState();
      break;
    case "X":
      resultNumber = String(
        parseFloat(operatorNum1) * parseFloat(operatorNum2)
      );
      printResult(resultNumber);
      resetCurrentState();
      break;
    case "/":
      resultNumber = String(
        parseFloat(operatorNum1) / parseFloat(operatorNum2)
      );
      printResult(resultNumber);
      resetCurrentState();
      break;
    case "=":
      printResult(resultNumber);
      break;
    case ".":
      currentNumber = operatorNum2 + ".";
      printResult(currentNumber);
      break;
    case "clear":
      resultNumber = "0";
      currentNumber = "0";
      currentOperator = "";
      waitingOperator = "";
      currentType.value = "0";
      printResult(0);
      break;
    default:
      resultNumber = operatorNum2;
      printResult(resultNumber);
      resetCurrentState();

      break;
  }
};

아직 완벽히 완료는 아니지만 부분 부분 뜯어보겠다.

// button
const numBtn = document.querySelectorAll(".button");
const operatorBtn = document.querySelectorAll(".operator");

// 결과 span
const resultSpan = document.querySelector(".result");

// 현재 상태 type
const currentType = document.querySelector("#current_type");

// 연산자 저장
let currentOperator = "";
let waitingOperator = "";

// 대기 값, 결과 값
let currentNumber = "0";
let resultNumber = "0";

우선 전역변수들이다.
입력되는 버튼 button, 결과값을 뿌려주는 span, 현재 상태를 저장하는 hidden input, 이전 연산자와 현재 연산자를 담을 변수, 현재 값과, 결과값을 담을 변수 들이 있다.

//숫자 버튼 클릭 '.' 포함
numBtn.forEach((item) => {
  item.addEventListener("click", (event) => {
    if (currentType.value !== "0") {
      currentType.value = "0";
      printResult("");
    }

    // 현재 숫자
    currentNumber = String(currentNumber) + event.target.innerText;
    if (!currentNumber.includes(".")) {
      if (currentNumber.charAt("0") === "0")
        currentNumber = currentNumber.substring(1);
    }
    printResult(currentNumber);
  });
});

숫자 버튼과 연산자 버튼은 querySelectorAll로 찾아오기 때문에 반복문을 돌면서 버튼에 addEventlistener를 달아준다.
그리고 클릭된 버튼이 무엇인지에 따라 숫자이면 currentType0이 아닐때(0이 아니면 연산자가 이 전에 입력되었다는걸 의미한다. 이 때 부터 후입력을 받게된다.) currentType를 초기화 하고, 선입력 받은 숫자도(span innerText)초기화 시킨다.

다음은 미리 입력받은 숫자를 현재 입력받은 숫자과 합쳐준다.
합쳐진 문자열(입력받은 숫자)에 .이 없는데 문자열 제일 앞에 0이 있으면 삭제 한다.
그리고 최종적으로 합쳐진 문자열을 span에 뿌려준다.
printResult()는 결과나 현재 숫자를 span에 뿌려주는 함수이다.

//연산자 버튼 클릭
operatorBtn.forEach((item) => {
  item.addEventListener("click", (event) => {
    waitingOperator = currentOperator;
    currentOperator = event.target.innerText;

    // currentOperator가 clear 이면 모든걸 초기화한다.
    if (currentOperator === "clear") {
      calculator(currentOperator, resultNumber, currentNumber);
      return;
    }

    // currentOperator가 = 이면 waitingOperator에 있는 연산을 하고 waitingOperator를 초기화
    if (currentOperator === "=") {
      calculator(waitingOperator, resultNumber, currentNumber);
      waitingOperator = "";
      return;
    }

    if (waitingOperator === "") {
      resultNumber = currentNumber;
      printResult(resultNumber);
      currentNumber = "0";
      currentType.value = "1";
    } else {
      calculator(waitingOperator, resultNumber, currentNumber);
    }
  });
});

연산자 버튼이 클릭되면 waitingOperator에 이전에 입력받은 연산자를 넣어주고
currentOperator에는 새로 입력받는 연산자를 넣는다.

연산자가 clear이면 모든 값들을 초기화한다.
연산자가 =이면 waitingOperator에 있는 연산자를 찾아 연산을하고 결과값을 뿌리고 연산자를 초기화한다.

if (waitingOperator === "") {
선입력된 연산자가 없을경우 아직 후속 연산자가 입력되기 전이기 때문에 resultNumber(이 부분에서는 선입력 된 값을 저장하는 용도로 사용)에 선입력된 값을 넘겨주고 선입력 된 값을 다시 뿌려준다.

} else {여기는 선입력된 연산자가 있고, 후속 연산자가 입력되었을때 비로소 계산을 시작한다.

const calculator = (operator, operatorNum1, operatorNum2) => {
  operatorNum1 = delDot(operatorNum1);
  operatorNum2 = delDot(operatorNum2);

  switch (operator) {
    case "+":
      resultNumber = String(
        parseFloat(operatorNum1) + parseFloat(operatorNum2)
      );
      printResult(resultNumber);
      resetCurrentState();
      break;
    case "-":
      resultNumber = String(
        parseFloat(operatorNum1) - parseFloat(operatorNum2)
      );
      printResult(resultNumber);
      resetCurrentState();
      break;
    case "X":
      resultNumber = String(
        parseFloat(operatorNum1) * parseFloat(operatorNum2)
      );
      printResult(resultNumber);
      resetCurrentState();
      break;
    case "/":
      resultNumber = String(
        parseFloat(operatorNum1) / parseFloat(operatorNum2)
      );
      printResult(resultNumber);
      resetCurrentState();
      break;
    case "=":
      printResult(resultNumber);
      break;
    case ".":
      currentNumber = operatorNum2 + ".";
      printResult(currentNumber);
      break;
    case "clear":
      resultNumber = "0";
      currentNumber = "0";
      currentOperator = "";
      waitingOperator = "";
      currentType.value = "0";
      printResult(0);
      break;
    default:
      resultNumber = operatorNum2;
      printResult(resultNumber);
      resetCurrentState();

      break;
  }
};

계산하는 함수이며
계산 전에 입력받은 숫자 끝에 .이 있다면 삭제하고 연산을 시작한다.


혼자서 이렇게 뭔가 만들어보니 정말 공부를 열심히 해야겠다고 또 또 다짐한다.
다른 사람이 만든것을 참고하지 않고 혼자 머리를 써 가며 만들었지만 역시나 코드가 지저분하고 더 좋은 방법이 있을거 같다는 생각이 든다.
나중에 좀 더 발전하면 다시 만들어 봐야겠다.
그래도 너무 재미있다..!!!!!!!!!!!!

profile
흠..

0개의 댓글

관련 채용 정보