barcode

자바스크립트로 계산기를 만들어보자

Coding/JavaScript

내 33년평생 이거보다 (삐-) 언어는 없을것이다... 어떻게 중괄호 위치 하나 삐꾸났다고 버그가 아오

 

참고로 ㄹㅇ 사칙연산만 되고 일일이 AC를 눌러줘야 하는 매우 불편한 계산기입니다… 근데 솔직히 삼각함수 지수 로그 이딴거 계산할거면 여기를 찾을 게 아니고 울프램알파를 가셔야죠…


Reference

https://velog.io/@jhyun_k/js-calculator

 

JS 로 계산기 만들기

자바스크립트 연습 겸 하여 사칙연산 계산기를 만들어보았다. 처음에는 간단할 거라 생각했지만 생각보다 고려해야할 점이 많았다. 복잡한 계산은 아니지만 사칙연산 기능은 문제없이 작동된

velog.io


일단 HTML이나 CSS는 그냥 계산기 구색만 맞추고 색깔 그렇게 신경 못 썼다. 왜? 분명 자바스크립트에서 뭔가 안될거같아서... 그리고 진짜 뭐가 안돼서 고생함 ㅠ

 

<html>
    <head>
        <title>사칙연산 계산기</title>
        <link href="style.css" rel="stylesheet">
    </head>
    <body>
        <h1>이다 이것 계산기</h1>
        <div class="wrap">
            <div class="resultDisplay">
                <div class="display"></div>
            </div>
            <div class="keypad">
                <button class="ac">AC</button>
                <button class="operator" id="plus"">+</button>
                <button class="operator" id="minus">-</button>
                <button class="operator" id="multiple">X</button>
                <button class="numBtn">7</button>
                <button class="numBtn">8</button>
                <button class="numBtn">9</button>
                <button class="operator" id="divide">/</button>
                <button class="numBtn">4</button>
                <button class="numBtn">5</button>
                <button class="numBtn">6</button>
                <button class="result" id="result">=</button>
                <button class="numBtn">1</button>
                <button class="numBtn">2</button>
                <button class="numBtn">3</button>
                <button class="numBtn">0</button>
            </div>
        </div>
        <script src="script.js"></script>
    </body>
</html>

상여자는 클래스 외에는 붙이지 않는다. 어차피 ID는 JS에서 갖고오지도 않음 나 편하라고 붙인거지… 아무튼 버튼은 하나하나 정성스럽게… 만들어줍니다. …놋북이 작아서 그런지 오래돼서 그런지 키 안눌려서 오타남… 아무튼 만들어주세요.

 

@font-face {
    font-family: 'DungGeunMo';
    src: url('https://cdn.jsdelivr.net/gh/projectnoonnu/noonfonts_six@1.2/DungGeunMo.woff') format('woff');
    font-weight: normal;
    font-style: normal;
}

:root {
    --main-color: #331B3F;
    --sub-color: #ACC7B4;
    --white: #ffffff;
    --black: #000000;
}

html {
    margin: 0;
    padding: 0;
}

body {
    font-family: 'DungGeunMo';
    font-weight: 700;
}

h1 {
    text-align: center;
    color: var(--main-color);
}

.wrap {
    width: 480px;
    margin: 0 auto;
    background-color: var(--sub-color);
    border: 1px solid transparent;
    border-radius: 10px;
}

.resultDisplay {
    display: flex;
    flex-direction: column;
    height: 50px;
    font-size: 30px;
    border-radius: 10px;
    background-color: var(--white);
    padding: 10px;
    margin: 10px 10px 0 10px;
    border: 1px solid var(--main-color);
}

.display {
    height: 20px;
    text-align: right;
}

.keypad {
    display: grid;
    padding: 10px;
    height: 480px;;
    gap: 15px;
    grid-template-columns: repeat(4, 1fr);
}

button {
    font-family: 'DungGeunMo';
    border: none;
    background-color: var(--main-color);
    color: var(--white);
    font-size: 24pt;
    border-radius: 10px;
}

.operator, .ac, .result {
    background-color: var(--black);
}

CSS도 사실 계산기 모양 만들려고 한거다. grid는 계산기 버튼이 네모네모 그리드이기 때문에 그리드 준 거다. 연산자랑 =, AC버튼은 색이 다른데 별로 차이는 안 난다. 그래도 눈뽕 오는 조합은 아니니까 넘어가자.

 

const buttons = document.querySelectorAll("button");
const operators = document.querySelectorAll(".operator");
const display = document.querySelector(".display");
const numBtn = document.querySelectorAll(".numBtn");
// 순서대로 1. 모든 버튼'들'. 2. 더하기 빼기 곱하기 나누기 =, 3. 결과창, 4. 숫자 버튼

자바스크립트는 DOM을 조작하기 위해서 일단 다 갖고오는 게 국룰이다.

 

let operatorOn = '';
let prevNum = '';
let nextNum = '';
//순서대로 연산자, 앞의 숫자, 뒤의 숫자

let calculate = (n1, operator, n2) => {
    let result = 0;
    switch (operator) {
        case '+':
            result = prevNum + nextNum;
            break;
        case '-':
            result = prevNum - nextNum;
            break;
        case 'X':
            result = prevNum * nextNum;
            break;
        case '/':
            result = prevNum / nextNum;
            break;
        default:
            break;
    }
    return String(result);
}
//입력받은 값을 연산자에 따라 계산한다

생각해봅시다. 계산이라는 건 연산자를 사이에 두고 앞에 숫자가 있고 뒤에 숫자가 있잖아요? 예를 들어서 2+2면 2 더하기 2 이런 식으로. 이거는 숫자 두 개(버튼 누르면 된다)랑 연산자가 정해지면 계산해서 결과 내놔라 이런 얘기다. 참고로 밑에서 한번 더 서술하겠지만 따로 0으로 나누는것에 대해 대비를 하지 않아도 된다.

 

let calculator = () => {
    let isFirstDigit = true;

    buttons.forEach((item) => {
        item.addEventListener('click', (e) => {
            let action = e.target.classList[0];
            let click = e.target.innerHTML;

            if (action === 'operator') {
                operatorOn = click;
                prevNum = display.textContent;
                display.textContent = '';
                isFirstDigit = true;
            }
            if (action === 'numBtn') {
                if (isFirstDigit && click === '0') {
                    return;
                }

                if (display.textContent === '' && operatorOn === '') {
                    display.textContent = click;
                    prevNum = display.textContent;
                } else if (display.textContent !== '' && operatorOn === '') {
                    display.textContent = display.textContent + click;
                    prevNum = display.textContent;
                } else if (display.textContent === '' && operatorOn !== '') {
                    display.textContent = click;
                    nextNum = display.textContent;
                } else if (display.textContent !== '' && operatorOn !== '') {
                    display.textContent = display.textContent + click;
                    nextNum = display.textContent;
                }
                isFirstDigit = false;
            }
            if (action === 'result') {
                display.textContent = calculate(prevNum, operatorOn, nextNum);
                isFirstDigit = true;
            }

            if (action === 'ac') {
                display.textContent = '';
                prevNum = '';
                operatorOn = '';
                nextNum = '';
                isFirstDigit = true;
            }
        });
    });
};

내 여기서 개고생한것이다… ㅡㅡ 여기가 바로 계산기 버튼을 누르는 것에 반응하는 부분이다. 그러니까 봐봐요? 2+2면 2 누르고, + 누르고, 2를 또 누를 거 아닙니까. 바로 그거다. 앞이랑 뒤를 어떻게 나누는지 컴퓨터한테 가르쳐주는거다.

 

buttons.forEach((item) => {
    item.addEventListener('click', (e) => {
        let action = e.target.classList[0];
        let click = e.target.innerHTML;

ForEach가 왜 있냐면 맨 위에서 가져올 때 숫자 표시할 창을 뺀 다른 요소들을 전부 document.querySelectorAll로 가져왔다. 그러면 이거 배열이라 반복문이 있어야 안에 있는 걸 꺼내올 수 있다. 오케이? 그니까 쉽게 말하자면 이 두 줄을 통해 당신이 누른 버튼의 클래스와 내용물을 가져오는거다.

 

if (action === 'operator') {
	operatorOn = click;
	prevNum = display.textContent;
	display.textContent = '';
	isFirstDigit = true;
}

정리하니까 탭이 가출했는데 아무튼… action에서 클래스를 가져왔는데 이게 operator, 그러니까 연산자면 앞에 숫자가 끝난거니까 끊고 뒤에꺼 가져와라 이 얘기. 여기 있는 isFirstDigit이라는 변수는 숫자가 0으로 시작할 때, 그러니까 0020, 003같은 애들을 20이랑 3으로 인식하게 해 준다. 뒤에 숫자를 입력할때도 저게 그대로 적용되기때문에 0이 아닌 다른 숫자로 시작할때까지 걍 입력중이라 0으로 나누는 것에 대해 굳이 처리할 필요가 없는 것.

 

if (action === 'numBtn') {
	if (isFirstDigit && click === '0') {
		return;
	}

	if (display.textContent === '' && operatorOn === '') {
		display.textContent = click;
		prevNum = display.textContent;
	} else if (display.textContent !== '' && operatorOn === '') {
		display.textContent = display.textContent + click;
		prevNum = display.textContent;
	} else if (display.textContent === '' && operatorOn !== '') {
		display.textContent = click;
		nextNum = display.textContent;
	} else if (display.textContent !== '' && operatorOn !== '') {
		display.textContent = display.textContent + click;
		nextNum = display.textContent;
	}
	isFirstDigit = false;
}

위에 설명했던 첫 자리수가 0이면 무시하는 기능, 그러니까 0001을 1로 인식하는 기능이 위에 네 줄이다. 아래는 쉽게 설명하자면 분기점에 따라 앞의 수인지 뒤에 수인지를 나누는거라고 보시면 된다. display는 버튼 입력하면 숫자가 보이는 부분을 의미한다. 일단 operatorOn이 공백이 아니라는 건 일단 연산자를 입력했다는 얘기이기 때문에 operatorOn이 공백인 상태에서는 앞의 숫자가 되고, operatorOn이 공백이 아닐 때는 뒤의 숫자가 된다. 왼쪽 조건은 말 그대로 이미 입력된 숫자가 있으면 거기에 갖다 붙이라는 얘기. 2를 누르고 5를 누르면 25로 입력된다.

 

if (action === 'result') {
	display.textContent = calculate(prevNum, operatorOn, nextNum);
	isFirstDigit = true;
}

if (action === 'ac') {
	display.textContent = '';
	prevNum = '';
	operatorOn = '';
	nextNum = '';
	isFirstDigit = true;
}

그러고보니 함수가 둘다 김람다씨네… 아무튼. 우리 여기서 한번 생각해봅시다. operator는 따로 가져왔고, 숫자도 따로 가져왔는데 결과 보는거랑 AC버튼은 왜 안 가져왔는지 궁금했던 사람 있을거다. 일단 내가 그랬음. 어차피 위에서 버튼 전체를 가져왔기때문에 굳이 변수로 따로 가져오지는 않았다. 그리고 버튼들에서 결과 보기를 누르면 위에 만들었던 앞에꺼 연산자 뒤에꺼 계산하는 기능과 AC를 누르면 초기화하는 기능이다.

 

const buttons = document.querySelectorAll("button");
const operators = document.querySelectorAll(".operator");
const display = document.querySelector(".display");
const numBtn = document.querySelectorAll(".numBtn");
// 순서대로 1. 모든 버튼'들'. 2. 더하기 빼기 곱하기 나누기 =, 3. 결과창, 4. 숫자 버튼

let operatorOn = '';
let prevNum = '';
let nextNum = '';
//순서대로 연산자, 앞의 숫자, 뒤의 숫자

let calculate = (n1, operator, n2) => {
    let result = 0;
    switch (operator) {
        case '+':
            result = prevNum + nextNum;
            break;
        case '-':
            result = prevNum - nextNum;
            break;
        case 'X':
            result = prevNum * nextNum;
            break;
        case '/':
            result = prevNum / nextNum;
            break;
        default:
            break;
    }
    return String(result);
}
//입력받은 값을 연산자에 따라 계산한다

let calculator = () => {
    let isFirstDigit = true;

    buttons.forEach((item) => {
        item.addEventListener('click', (e) => {
            let action = e.target.classList[0];
            let click = e.target.innerHTML;

            if (action === 'operator') {
                operatorOn = click;
                prevNum = display.textContent;
                display.textContent = '';
                isFirstDigit = true;
            }
            if (action === 'numBtn') {
                if (isFirstDigit && click === '0') {
                    return;
                }

                if (display.textContent === '' && operatorOn === '') {
                    display.textContent = click;
                    prevNum = display.textContent;
                } else if (display.textContent !== '' && operatorOn === '') {
                    display.textContent = display.textContent + click;
                    prevNum = display.textContent;
                } else if (display.textContent === '' && operatorOn !== '') {
                    display.textContent = click;
                    nextNum = display.textContent;
                } else if (display.textContent !== '' && operatorOn !== '') {
                    display.textContent = display.textContent + click;
                    nextNum = display.textContent;
                }
                isFirstDigit = false;
            }
            if (action === 'result') {
                display.textContent = calculate(prevNum, operatorOn, nextNum);
                isFirstDigit = true;
            }

            if (action === 'ac') {
                display.textContent = '';
                prevNum = '';
                operatorOn = '';
                nextNum = '';
                isFirstDigit = true;
            }
        });
    });
};

calculator();

사실 전체 코드에는 맨 밑에 한줄이 더 있는데 설명할 때 굳이 언급할 필요는 없어보여서 걍 언급 안했다. 그죠 함수 짰으면 불러야 일하죠.

 

그래서 조촐하지만 계산기 완성. 고급 기능 필요하시면 위에도 썼지만 울프램알파 쓰십쇼.