코드스테이츠 부트캠프

계산기 구현하기

als982001 2023. 1. 1. 22:34

계산기 구현하기

계산기 구현 - User flow에 따라 기능 구현하기 개요

기본적인 계산기의 기능을 구현하는 것이 과제인데, 특히 신경써서 구현해야 할 부분은 아래와 같다.

  • . 버튼을 활용하여 소수를 입력받기
  • . 버튼을 연속적으로 눌러도 .은 처음 단 한 번만 입력되어야 함
  • 정수 부분 없이 . 버튼과 숫자를 눌러 작동시키는 경우는 소수가 나타나야 함 (.5 === 0.5)
  • Enter 버튼을 여러 번 클릭했을 때, 변수 previousNum을 활용하여 이전 숫자를 계속 더할 수 있어야 함
  • 연산자 버튼을 누르기 전 숫자 버튼을 누른 후, 실수로 Enter를 여러 번 눌러도 정상 작동해야 함
  • 연산자 버튼을 연속적으로 눌러도 계산 값에 영향이 없어야 함

 

CSS에서 신경써야 할 부분은 다음과 같다.

  • 연산자 버튼을 눌렀을 때 적절한 클래스를 추가하여 연산자 버튼의 색 변경

계산기 구현 js파일

const calculator = document.querySelector(".calculator"); // calculator 엘리먼트와, 그 자식 엘리먼트의 정보를 모두 담고 있습니다.
const buttons = calculator.querySelector(".calculator__buttons"); // calculator__keys 엘리먼트와, 그 자식 엘리먼트의 정보를 모두 담고 있습니다.
const firstOperend = document.querySelector(".calculator__operend--left"); // calculator__operend--left 엘리먼트와, 그 자식 엘리먼트의 정보를 모두 담고 있습니다.
const operator = document.querySelector(".calculator__operator"); // calculator__operator 엘리먼트와, 그 자식 엘리먼트의 정보를 모두 담고 있습니다.
const secondOperend = document.querySelector(".calculator__operend--right"); // calculator__operend--right 엘리먼트와, 그 자식 엘리먼트의 정보를 모두 담고 있습니다.
const calculatedResult = document.querySelector(".calculator__result"); // calculator__result 엘리먼트와, 그 자식 엘리먼트의 정보를 모두 담고 있습니다.
// press용들
const clearBtn = document.getElementsByClassName("clear");
const calculateBtn = document.getElementsByClassName("calculate");
const numberBtns = document.getElementsByClassName("number");
const operatorBtns = document.getElementsByClassName("operator");
const removePressedClass = () => {
const beforePressedBtn = document.getElementsByClassName("pressed");
for (let i = 0; i < beforePressedBtn.length; ++i) {
beforePressedBtn[i].classList.remove("pressed");
}
};
function calculate(n1, operator, n2) {
let result = 0;
// TODO : n1과 n2를 operator에 따라 계산하는 함수를 만드세요.
// ex) 입력값이 n1 : '1', operator : '+', n2 : '2' 인 경우, 3이 리턴됩니다.
console.log(`${parseFloat(n1)} ${parseFloat(n2)}`);
if (operator === "+") {
result = parseFloat(n1) + parseFloat(n2);
} else if (operator === "-") {
result = parseFloat(n1) - parseFloat(n2);
} else if (operator === "*") {
result = parseFloat(n1) * parseFloat(n2);
} else if (operator === "/") {
result = parseFloat(n1) / parseFloat(n2);
}
console.log(result);
return String(result);
}
buttons.addEventListener("click", function (event) {
// 버튼을 눌렀을 때 작동하는 함수입니다.
const target = event.target; // 클릭된 HTML 엘리먼트의 정보가 저장되어 있습니다.
const action = target.classList[0]; // 클릭된 HTML 엘리먼트에 클레스 정보를 가져옵니다.
const buttonContent = target.textContent; // 클릭된 HTML 엘리먼트의 텍스트 정보를 가져옵니다.
// ! 위 코드(Line 19 - 21)는 수정하지 마세요.
if (target.matches("button")) {
// TODO : 계산기가 작동할 수 있도록 아래 코드를 수정하세요. 작성되어 있는 조건문과 console.log를 활용하시면 쉽게 문제를 풀 수 있습니다.
// 클릭된 HTML 엘리먼트가 button이면
if (action === "number") {
// 그리고 버튼의 클레스가 number이면
// 아래 코드가 작동됩니다.
console.log("숫자 " + buttonContent + " 버튼");
if (firstOperend.textContent === "0") {
firstOperend.textContent = buttonContent;
} else {
secondOperend.textContent = buttonContent;
}
}
if (action === "operator") {
console.log("연산자 " + buttonContent + " 버튼");
operator.textContent = buttonContent;
}
if (action === "decimal") {
// console.log('소수점 버튼');
}
if (action === "clear") {
console.log("초기화 버튼");
firstOperend.textContent = "0";
secondOperend.textContent = "0";
operator.textContent = "+";
calculatedResult.textContent = "0";
}
if (action === "calculate") {
console.log("계산 버튼");
calculatedResult.textContent = calculate(
firstOperend.textContent,
operator.textContent,
secondOperend.textContent
);
}
}
});
// ! Advanced Challenge test와 Nightmare test를 위해서는 아래 주석을 해제하세요.
const display = document.querySelector(".calculator__display--for-advanced"); // calculator__display 엘리먼트와, 그 자식 엘리먼트의 정보를 모두 담고 있습니다.
let firstNum = "",
operatorForAdvanced = "",
previousKey = "",
previousNum = "";
buttons.addEventListener("click", function (event) {
// 버튼을 눌렀을 때 작동하는 함수입니다.
const target = event.target; // 클릭된 HTML 엘리먼트의 정보가 저장되어 있습니다.
const action = target.classList[0]; // 클릭된 HTML 엘리먼트에 클레스 정보를 가져옵니다.
const buttonContent = target.textContent; // 클릭된 HTML 엘리먼트의 텍스트 정보를 가져옵니다.
// ! 위 코드는 수정하지 마세요.
// ! 여기서부터 Advanced Challenge & Nightmare 과제룰 풀어주세요.
if (target.matches("button")) {
if (action === "number") {
// 숫자를 눌렀을 경우의 상황
if (
display.textContent === "0" ||
previousKey === "operator" ||
previousKey === "calculate"
) {
// 1. 만약 숫자를 처음 누르거나, 연산자 다음에 누르거나, 계산이 끝난 후 누르는 경우
// 디스플에이에 누른 숫자를 띄운다.
console.log("숫자 " + buttonContent + " 버튼");
display.textContent = buttonContent;
previousKey = "number";
/* if (previousKey === "operator") {
for (let i = 0; i < operatorBtns.length; ++i) {
operatorBtns[i].classList.remove("pressed");
}
} else if (previousKey === "calculate") {
for (let i = 0; i < calculateBtn.length; ++i) {
calculateBtn[i].classList.remove("pressed");
}
} */
} else {
// 2. 이미 숫자가 입력되어 있는 경우
// 이미 적힌 숫자 뒤에 누른 숫자를 덧붙힌다.
console.log("숫자 " + buttonContent + " 버튼");
display.textContent += buttonContent;
/* for (let i = 0; i < numberBtns.length; ++i) {
numberBtns[i].classList.remove("pressed");
} */
}
removePressedClass();
target.classList.add("pressed");
// 3. 그 외의 경우는 고려하지 않아도 된다.
}
if (action === "operator") {
console.log(`연산자 ${buttonContent} 버튼`);
// 연산자를 누르는 경우들
if (previousKey === "number" && operatorForAdvanced !== "") {
// 1. 연산자 입력 전에 숫자를 입력했으면서 이 연산자가 첫 연산자가 아닌 경우
// 이 때는 'Enter 버튼을 여러 번 클릭했을 때, 변수 previousNum 를 활용하여 이전 숫자를 계속 더할 수 있어야 합니다.'
// 위의 문장을 실행한다.
firstNum = calculate(
firstNum,
operatorForAdvanced,
display.textContent
);
} else if (previousKey !== "operator") {
// 2. 전에 입력한 키가 연산자 키가 아니라면
// firstNum에 디스플레이에 입력된 수를 저장한다.
firstNum = display.textContent;
}
// 입력된 연산자를 저장한 후, previousKey에 연산자를 눌렀음을 기록한다.
operatorForAdvanced = buttonContent;
previousKey = "operator";
removePressedClass();
target.classList.add("pressed");
}
if (action === "decimal") {
// 점 키를 눌렀을 경우, 고려해야할 상황
if (previousKey !== "decimal") {
// 1. 만약 이전에 입력한 키가 소수점이 아니라면
// 이전에 입력한 것이 연산자가 아닐 경우는
// 디스플레이에 있는 숫자에 점을 붙여줘야 한다.
if (previousKey === "operator") {
// 연산자가 입력된 후 점 키를 누르는 경우는
// 새로운 소수로 시작한다는 의미이므로 (예시로 0.1234... 등)
// 디스플레이를 비워준다.
display.textContent = "";
}
// 디스플레이에 표시된 숫자 뒤에 소수점을 찍어준 후 점 키를 눌렀음을 기록한다.
display.textContent += ".";
previousKey = "decimal";
}
// 2. 만약 바로 직전에 소수점이 입력된 경우라면 그냥 패스한다.
removePressedClass();
target.classList.add("pressed");
}
if (action === "clear") {
// 만약 clear 키를 누른 경우라면 모든 변수들을 초기화해준다.
display.textContent = "0";
firstNum = "";
operatorForAdvanced = "";
previousNum = "";
previousKey = "clear";
removePressedClass();
target.classList.add("pressed");
}
if (action === "calculate") {
// 계산 키를 누른 경우
// 우선적으로 고려해야 할 점은 직전에 누른 키이다.
if (previousKey === "calculate") {
// 1. 직전에 계산 키를 누른 경우
// Enter 버튼을 여러 번 클릭했을 때, 변수 previousNum 를 활용하여 이전 숫자를 계속 더할 수 있어야 합니다.
// 위 조건을 실행한다.
display.textContent = calculate(
display.textContent,
operatorForAdvanced,
previousNum
);
} else {
// 2. 위의 경우가 아니라면 저장된 firstNum을 이용해 계산한 후,
// 디스플레이에 결과를 표시한다.
if (firstNum != "") {
previousNum = display.textContent;
display.textContent = calculate(
firstNum,
operatorForAdvanced,
display.textContent
);
previousKey = "calculate";
}
}
removePressedClass();
target.classList.add("pressed");
}
}
console.log("First Num = ", firstNum);
console.log("Operator For Advanced = ", operatorForAdvanced);
console.log("Previous Key = ", previousKey);
console.log("Previous Num = ", previousNum);
console.log("=========================");
});

스크립트 파일은 무슨 버튼을 눌렀느냐에 따라 진행이 달라진다. 우선 숫자 버튼이다. 숫자 버튼을 눌렀을 경우 고려해야 할 경우는 크게 두 가지이다. 우선 숫자를 처음 누르거나, 연산자 다음 누르거나, 계산이 다 끝난 후 누른 경우이다. 이 경우는 디스플레이의 숫자를 방금 누른 숫자로 바꿔준 후, 숫자 버튼을 눌렀다는 사실을 기록하면 된다. 그리고 이미 디스플레이에 숫자가 있는 경우인데 이는 그저 디스플레이의 숫자 뒤에 누른 숫자를 덧붙이면 된다.

그 다음은 연산자 버튼이다. 연산자 버튼의 경우, 우선 연산자 입력 전에 숫자를 입력했으면서 누른 연산자가 첫 연산자가 아닌 경우를 고려한다. 만약, 그렇다면 firstNum 변수에는 이미 기록되어 있던 숫자와 이전의 연산자를 통해 계산을 진행한다. 혹은 이전에 누른 키가 연산자가 아닐 경우도 있다. 이 경우에는 디스플레이의 숫자가 연산자 기준의 왼쪽 수라는 뜻이므로 디스플레이의 숫자를 firstNum 이라는 변수에 기록한다. 미리 고려해야 할 사항들을 체크한 후, 누른 연산자 버튼을 저장한다.

그 다음은 소수점 버튼이다. 만약 이전에 누른 키가 소수점이 아니라 연산자라면 디스플레이를 비운다. 그 후, 디스플레이에 .을 추가한 후 소수점 버튼을 눌렀음을 기록한다. 만약, 이전에 누른 버튼이 소수점 버튼이라면 아무 일도 일어나지 않게 한다.

그리고 clear 버튼의 경우, 다른 것을 고료할 필요 없이 모든 값들을 초기화시킨다.

마지막으로 calcultae 버튼이다. 전에 누른 버튼이 똑같이 calculate 버튼이라면 전술하였던 조건을 충족시키기 위하여 디스플레이에 이전에 활용했던 값들을 이용해 계산한 값을 띄운다. 만약 그렇지 않을 경우, 연산자 기준 왼쪽 수가 있을 경우는 계산을 진행한다.

그리고 각 버튼들이 눌렸을 경우, html 코드 내의 모든 pressed 클래스를 지우고 눌린 버튼에 pressed 클래스를 추가한다.

 

계산기 구현 - CSS

/* TODO : CSS파일을 편집하여 원하는 스타일의 계산기를 만들어보세요 :) */
/* global */
* {
margin: 0;
padding: 0;
border: 0px;
box-sizing: border-box;
font-family: "Righteous", cursive;
color: #000;
}
body {
background-image: url("./data/codestates-motif.png");
}
/*
Calculator styles
*/
.container {
height: 100vh;
display: flex;
justify-content: center;
align-items: center;
}
.calculator {
background-color: #4000c7;
width: 350px;
height: 500px;
border-radius: 10px;
padding: 30px 20px;
box-shadow: 7px 7px 5px #243763, inset 7px 7px 20px #82aae3;
}
.calculator__display--bare {
display: none;
background-color: #ffffff;
text-align: center;
vertical-align: middle;
height: 100px;
width: 100%;
border-radius: 10px;
font-size: 20px;
padding: 25px 15px;
overflow: hidden;
overflow-wrap: break-word;
}
.calculator__display--bare > span {
display: inline-block;
text-align: center;
background-color: #f3f0fc;
margin: 5px;
width: 40px;
height: 45px;
border-radius: 10px;
font-size: 20px;
padding: 10px 5px;
}
.calculator__display--for-advanced {
background-color: #ffffff;
height: 100px;
width: 100%;
border-radius: 10px;
font-size: 20px;
text-align: right;
vertical-align: middle;
padding: 25px 15px;
overflow: hidden;
overflow-wrap: break-word;
box-shadow: inset 3px 3px 5px black;
}
.calculator__buttons {
background-color: #ffffff;
width: 100%;
height: 330px;
margin-top: 10px;
padding: 10px;
border-radius: 10px;
}
.clear__and__enter {
height: 50px;
margin: 10px;
background-color: #f3f0fc;
display: flex;
}
.clear__and__enter > button {
border-radius: 10px;
/* width: 50px;
height: 40px; */
flex: 1 0 0;
margin: 0px 5px;
background-color: #00da75;
cursor: pointer;
outline: none;
}
.button__row {
height: 50px;
margin: 10px;
background-color: #f3f0fc;
display: flex;
}
.button__row > button {
width: 50px;
height: 40px;
border-radius: 10px;
cursor: pointer;
outline: none;
background-color: #ebebeb;
flex: 1 0 0;
margin: 5px;
max-width: 25%;
}
.button__row > .operator {
color: #ffffff;
background-color: #313132;
}
.button__row > .double {
width: 115px;
flex: 2 0 0;
max-width: 50%;
}
.button__row > .isPressed {
background-color: #00da75;
}
.logo {
position: fixed;
width: 204px;
padding: 30px;
bottom: 0px;
right: 0px;
}
button {
box-shadow: 0 5px 5px gray;
transition-duration: 0.3s;
}
button {
font-size: 30px;
display: flex;
justify-content: center;
align-items: center;
}
button:hover {
box-shadow: 0 1px 1px gray;
}
.operator:active,
button:active {
box-shadow: none;
}
.clear__and__enter .pressed,
.button__row .pressed {
scale: 1.2;
background-color: #ada2ff;
}

계산기 목업의 css와 다른 점은 pressed 클래스가 있을 때는 색과 크기를 다르게 해준다는 것이다.