웹 프론트

[Web] 계산기 만들기 w/ html, css, js

민석삼 2025. 2. 22. 02:56

2학년 1학기 때 인터넷 프로그래밍을 들은 후 처음 잡아본 웹이다. 기억을 되살리는 겸 간단히 프로젝트를 진행해보았다. 생각보다 기억나지 않는 게 많아서 구글링에 의지하며 만들어보았다.

 

완성 모습

HTML

먼저 html이다. 뭘 어떻게 해야할 지 모르겠어서 기본적인 틀을 잡았다.

calculator 클래스로 전체 틀을 묶고, display 칸을 만들었다. 

<div class="calculator">
        <div class="display">
            <input type="text" id="display" value="0">
        </div>

class display는 표시되는 입력 칸 전체(컨테이너)를, id display는 입력되는 텍스트 부분만을 의미한다.

타입을 text로 지정하여 사용자가 텍스트를 입력할 수 있는 필드로 지정해주었다. 기본값은 0으로 설정했다.

<div class="buttons">
            <button class="operator">C</button>
            <button class="operator">AC</button>
            <button class="operator">%</button>
            <button class="operator"><</button>
        </div>
        <div class="buttons">
            <button class="number">7</button>
            <button class="number">8</button>
            <button class="number">9</button>
            <button class="operator">/</button>
        </div>
        <div class="buttons">
            <button class="number">4</button>
            <button class="number">5</button>
            <button class="number">6</button>
            <button class="operator">*</button>
        </div>
        <div class="buttons">
            <button class="number">1</button>
            <button class="number">2</button>
            <button class="number">3</button>
            <button class="operator">-</button>
        </div>
        <div class="buttons">
            <button class="number">0</button>
            <button class="decimal">.</button>
            <button id="equals">=</button>
            <button class="operator">+</button>
        </div>

button은 크게 연산자(operator)와 숫자(number)로 구분했다. "."과 "="만 따로 decimal과 equals로 구분하였다.

 

다음은 html 전체 코드이다.

<div class="calculator">
        <div class="display">
            <input type="text" id="display" value="0">
        </div>
        <div class="buttons">
            <button class="operator">C</button>
            <button class="operator">AC</button>
            <button class="operator">%</button>
            <button class="operator"><</button>
        </div>
        <div class="buttons">
            <button class="number">7</button>
            <button class="number">8</button>
            <button class="number">9</button>
            <button class="operator">/</button>
        </div>
        <div class="buttons">
            <button class="number">4</button>
            <button class="number">5</button>
            <button class="number">6</button>
            <button class="operator">*</button>
        </div>
        <div class="buttons">
            <button class="number">1</button>
            <button class="number">2</button>
            <button class="number">3</button>
            <button class="operator">-</button>
        </div>
        <div class="buttons">
            <button class="number">0</button>
            <button class="decimal">.</button>
            <button id="equals">=</button>
            <button class="operator">+</button>
        </div>
    </div>

 

CSS

* {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
    user-select: none;
}

모든 요소에 대하여 html의 기본값의 여백을 없애고 원하는 대로 여백을 주기 위하여 margin과 padding을 0으로 설정했다.

box-sizing는 각 요소의 크기 계산 방식을 정하는 값이다. 기본값은 content-box로, 이 경우 width와 height가 요소의 콘텐츠 영역 크기만을 기준으로 계산된다. 이를 border-box로 설정하여, width와 height가 요소의 전체 크기(padding과 border를 포함한 크기)로 계산되도록 해주었다.

user-select를 none으로 설정하여 사용자가 텍스트를 마우스로 드래그하거나 복사하지 못하도록 하였다.

.calculator {
    width: 300px;
    height: 500px;
    padding: 20px;
    background-color: #f4faf5;
    border: 1px solid #cecfce;
    border-radius: 10px;
    display: flex;
    flex-direction: column;
}

width, height로 크기 지정, padding으로 여백 지정, background-color로 계산기의 색을 정했다.

border로 테두리의 두께, 모양, 색을 정하고, border-radius로 모서리를 약간 둥글게 해주었다.

display: flex로 calculator를 플렉스 컨테이너로 만들어, 내부의 자식 요소들이 calculator의 크기에 따라 자동으로 크기, 위치를 조정하도록 해주었다.

flex-direction: column로 지정하여, calculator의 자식 요소들이 세로로 쌓이게 만들었다. flex-direction의 기본값은 가로 배치(row)이다.

.display {
    text-align: center;
    height: 50px;
    margin: 10px;
}

display의 컨테이너를 계산기 내에서 가운데 정렬 하고, 높이와 여백을 지정했다.

.display input {
    width: 250px;
    text-align: right;
    padding: 10px;
    border: none;
    font-size: 2rem;
    background-color: #f4faf5;
    color: black;
}

display의 텍스트 입력부의 넓이를 지정하고, 실제 계산기처럼 오른쪽 정렬했다.

padding으로 여백을 지정하고, border: none으로 테두리를 없도록 설정했다.

font size와 background color, 그리고 글자 색인 color를 지정했다.

.buttons {
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    gap: 10px;
    margin-top: 15px;
    height: 100%;
}

버튼 전체 부분을 의미하는 buttons 클래스에 대해서,

display: grid;와 grid-template-columns: repeat(4, 1fr);를 통해 정해진 범위(계산기) 내에서 버튼을 grid 형태로 정렬하고, 각 버튼이 동일한 크기로 4등분되어 배치되게 했다.

각각 display: grid는 buttons 클래스를 그리드 컨테이너로 만들어, 그 내부 자식 요소들인 button을 그리드 항목으로 배치하게 해주는 코드이고, grid-template-columns: repeat(4, 1fr)는 그리드의 열을 4개로 설정하고, 각 열의 크기를 1fr로 지정한 것이다. 여기서 1fr은 가용 공간을 균등하게 나누겠다는 의미로, 4개의 열이 동일한 너비를 가지게 한다.

button {
    width: 100%;
    height: 100%;
    background-color: #eaf5eb;
    border-radius: 50%;
    border: 1px solid #cecfce;
}

각 버튼에 대한 설정이다.

width와 height는 가용 범위 내의 최대 크기를 사용하도록 했고(100%), background color를 지정해주었다.

border-radius를 50%로 주어 버튼이 원형이 되도록 했고, border 설정으로 테두리 굵기, 모양, 색을 지정했다.

더보기

 

html에서 css에서
id (id: button) #으로 호출 (#button)
class (class: button) .으로 호출 (.button)
태그 (<button>) 그냥 호출 (button)
button:hover {
    border-color: #9c9d9c;
}

button 태그에 hover를 주어, 사용자가 마우스를 올리면 border color(테두리 색)이 약간 더 진한 색으로 변하게 하였다.

#equals {
    background-color: #bbe2c1;
}

마지막으로 등호만 다른 버튼들과 차별되는 색을 주었다.

 

다음은 CSS 전체 코드이다.

* {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
    user-select: none;
}

.calculator {
    width: 300px;
    height: 500px;
    padding: 20px;
    background-color: #f4faf5;
    border: 1px solid #cecfce;
    border-radius: 10px;
    display: flex;
    flex-direction: column;
    /*display margin 0 auto;*/
}

.display {
    text-align: center;
    height: 50px;
    margin: 10px;
}

.display input {
    width: 250px;
    text-align: right;
    padding: 10px;
    border: none;
    font-size: 2rem;
    background-color: #f4faf5;
    color: black;
}

.buttons {
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    gap: 10px;
    margin-top: 15px;
    height: 100%;
}

button {
    width: 100%;
    height: 100%;
    background-color: #eaf5eb;
    border-radius: 50%;
    border: 1px solid #cecfce;
}

button:hover {
    border-color: #9c9d9c;
}

#equals {
    background-color: #bbe2c1;
}

 

JS

const display = document.getElementById("display");
const buttons = document.querySelectorAll("button");
const operators = ['+', '-', '*', '/', '%'];

가장 먼저 document(html 파일)에서 display와 button 항목을 찾아와 변수에 지정해주었다. 

(document는 현재 로드된 HTML 문서의 DOM(Document Object Model)을 참조한다.)

operators는 따로 지정하여 밑에서 이벤트 발생 시 연산자라면~을 판별하는 조건문에 사용할 예정이다.

buttons.forEach(button => {
    button.addEventListener("click", function() {
        const value = button.textContent;

        if (value === "=") {
            display.value = eval(display.value);
        } else if (value === "AC") {
            display.value = "0";
        } else if (value === "C") {
            display.value = "0";
        } else if (value === "<") {
            display.value = display.value.slice(0, -1);
        } else if (operators.includes(value)) {
            display.value += value;
        } else {
            if (display.value === "0") {
                display.value = value;
            } else {
                display.value += value;
            }
        }
    });
});

button이 click되는 이벤트이다.

button.forEach(button => { : 모든 버튼에 대하여 반복 작업을 수행한다.

button.addEventListener("click", function() { : 버튼에 대하여 "click"이 일어나면 해당 함수를 실행
const value = button.textContent; : value에 현재 클릭된 버튼의 text 내용이 들어간다.

다음 조건문을 보기 전에, display.value는 현재 display에 있는 모든 텍스트를 의미한다.

        if (value === "=") {
            display.value = eval(display.value);

=이 눌리면 현재 display에 있는 텍스트를 계산(eval 함수)하여 display.value에 넣는다. 즉, display에 있는 값이 계산된 값으로 바뀐다.
        } else if (value === "AC") {
            display.value = "0";

        } else if (value === "C") {
            display.value = "0";

AC와 C가 눌리면 display에 있는 값을 0으로 초기화한다.
        } else if (value === "<") {
            display.value = display.value.slice(0, -1);

<가 눌리면 현재 display에 있는 텍스트에서 하나를 지운다. (ex: 123 -> 12)
        } else if (operators.includes(value)) {
            display.value += value;

operators에 있는 값들 중 하나가 눌렸다면 (연산자가 눌렸다면) display에 있는 텍스트의 끝에 해당 텍스트를 추가한다.
        } else {
            if (display.value === "0") {
                display.value = value;
            } else {
                display.value += value;
            }

그밖의 경우, 즉 숫자가 눌리는 경우에는,

실제 계산기에서 0이 display에 있고 다른 숫자(ex: 3)가 눌리면 03이 되는게 아니라 0이 되므로,

현재 display에 0이 있고 다른 숫자가 눌린다면 현재 눌린 값을 0 대신 써주도록 하고, 그게 아니라면 operator처럼 현재 텍스트의 끝에 해당 텍스트를 추가한다.

 

다음은 키보드가 눌리는 이벤트이다.

 

document.addEventListener("keydown", function(e) {
    const value = e.key;
    
    if (value === "Enter") {
        display.value = eval(display.value);
    } else if (value == "Backspace") {
        display.value = display.value.slice(0, -1);
    } else if (value >= "0" && value <= "9") {
        if (display.value === "0") {
            display.value = value;
        } else {
            display.value += value;
        }
    } else if(operators.includes(value)) {
        display.value += value;
    }
});

document.addEventListener("keydown", function(e) { : keydown 이벤트, 즉 키보드가 눌리는 이벤트가 발생한다면 해당 함수를 실행한다.

const value = e.key; value라는 변수에 현재 눌린 키를 넣어준다.     

    if (value === "Enter") {
        display.value = eval(display.value);

Enter가 눌리면 "=" 버튼이 눌린 이벤트와 마찬가지로 현재 display에 있는 텍스트를 연산하여 display에 업데이트한다.
    } else if (value == "Backspace") {
        display.value = display.value.slice(0, -1);

Backspace가 눌렸다면 현재 display에 있는 텍스트에서 하나를 지운다. (ex: 123 -> 12)
    } else if (value >= "0" && value <= "9") {
        if (display.value === "0") {
            display.value = value;
        } else {
            display.value += value;
        }

숫자가 들어온다면, 현재 display에 있는 값이 0이라면 현재 눌린 값으로 display값을 업데이트 한다. 아니라면 현재 텍스트에 지금 눌린 텍스트를 추가한다.

    } else if(operators.includes(value)) {
        display.value += value;
    }

현재 눌린 값이 operators중 하나라면, 즉 연산자가 눌렸다면 현재 텍스트에 지금 눌린 텍스트를 추가한다.
다음은 JS 코드 전체이다.

const display = document.getElementById("display");
const buttons = document.querySelectorAll("button");
const operators = ['+', '-', '*', '/', '%'];

buttons.forEach(button => {
    button.addEventListener("click", function() {
        const value = button.textContent;

        if (value === "=") {
            display.value = eval(display.value);
        } else if (value === "AC") {
            display.value = "0";
        } else if (value === "C") {
            display.value = "0";
        } else if (value === "<") {
            display.value = display.value.slice(0, -1);
        } else if (operators.includes(value)) {
            display.value += value;
        } else {
            if (display.value === "0") {
                display.value = value;
            } else {
                display.value += value;
            }
        }
    });
}); 

document.addEventListener("keydown", function(e) {
    const value = e.key;
    
    if (value === "Enter") {
        display.value = eval(display.value);
    } else if (value == "Backspace") {
        display.value = display.value.slice(0, -1);
    } else if (value >= "0" && value <= "9") {
        if (display.value === "0") {
            display.value = value;
        } else {
            display.value += value;
        }
    } else if(operators.includes(value)) {
        display.value += value;
    }
});

 

아쉬운 부분이자 앞으로 보완할 부분

1. 연산자가 연속으로 들어오면 실제 계산기에서는 새로운 연산자로 계속 업데이트하는데, 내 계산기에서는 +-이런식으로 텍스트에 계속 추가된다.

2. 실제 계산기처럼 맨 앞에 -가 들어오면 음수로 시작 할 수 있게 하는 기능

3. =로 연산을 끝내고 나서 새로운 입력을 시도하면 실제 계산기에서는 현재 계산 값이 초기화되고 새로 입력이 진행되는데, 내 계산기에서는 계산이 완료된 후의 텍스트에 추가 입력이 뒤에 추가된다.

 

 

인터넷 프로그래밍을 복기하는 의미에서 시작한 프로젝트로 이정도로 간단하게 마무리하고, 추후 공부가 더 된 후에 보완하도록 하겠다.