Js 계산기

원도훈·2024년 9월 26일
1
post-thumbnail

웹 개발 실력을 향상시키기 위해 작은 프로젝트를 진행하고 싶었습니다. 그래서 기본적이지만 중요한 기능을 담은 계산기를 만들기로 결정했습니다.
이 프로젝트에서는 순수 HTML, CSS, JavaScript를 사용하여 계산기를 구현하였습니다.

1. 폴더 및 파일 생성

calculator 폴더를 생성합니다.
calculator 폴더 내부에 caculator.html, caculator.css, calculator.js 파일을 생성합니다.

  1. body 요소 내부에 계산기 컨테이너를 만듭니다.
  2. flex를 이용하여 컨테이너가 화면의 중간에 위치하도록 합니다.
  3. 컨테이너 내부에 2개의 영역을 생성합니다. (display, buttons)
  4. 각 영역을 시각적으로 확인할 수 있도록 border 속성을 추가합니다.
  5. displaybuttons를 flex를 사용하여 적절하게 배치합니다.
  6. 계산기 컨테이너의 내부 여백을 적절하게 설정합니다.
  7. 계산기 상단에 버튼을 3개 추가합니다.
  8. calculator.html
    1. buttons 내부에 계산기에 필요한 버튼을 추가합니다.
      1. 모든 버튼은 button class를 가지고 있어야 합니다.
      2. 숫자 버튼에는 number class를 추가합니다.
      3. 연산기호 버튼(+, -, *, /)에는 operator class를 추가합니다.
      4. 기능 버튼(C, ±, %)에는 function class를 추가합니다.
      5. 숫자 0은 다른 버튼에 비해 두 배의 영역을 가지고 있으므로, zero class를 추가합니다.
  9. calculator.css
    1. display 영역을 스타일링합니다.
      1. 텍스트를 오른쪽으로 정렬합니다.
      2. 콘텐츠와 테두리 사이에 padding을 지정합니다.
    2. buttons 영역과 버튼들을 flexbox를 사용하여 정렬합니다.

HTML

<!DOCTYPE html>
<html lang="ko">
<head>
  <meta charset="UTF-8">
  <title>계산기 만들기</title>
  <link rel="stylesheet" href="calculator.css">
</head>
<body>
<div class="calculator-container">
  <!-- 상단 버튼 영역 -->
  <div class="window-buttons">
    <span class="close"></span>
    <span class="minimize"></span>
    <span class="maximize"></span>
  </div>
  <!-- 디스플레이 영역 -->
  <div class="display">
    <div class="input">0</div>
  </div>
  <!-- 버튼 영역 -->
  <div class="buttons">
    <button class="button function">C</button>
    <button class="button function">±</button>
    <button class="button function">%</button>
    <button class="button operator">÷</button>
    <button class="button number">7</button>
    <button class="button number">8</button>
    <button class="button number">9</button>
    <button class="button operator">×</button>
    <button class="button number">4</button>
    <button class="button number">5</button>
    <button class="button number">6</button>
    <button class="button operator"></button>
    <button class="button number">1</button>
    <button class="button number">2</button>
    <button class="button number">3</button>
    <button class="button operator">+</button>
    <button class="button number zero">0</button>
    <button class="button number">.</button>
    <button class="button operator">=</button>
  </div>
</div>
<script src="calculator.js"></script>
</body>
</html>

CSS

* {
    box-sizing: border-box;
}

body {
    margin: 0;
    padding: 0;
    background-color: #333;
    display: flex;
    justify-content: center;
    align-items: center;
    height: 100vh;
}

.calculator-container {
    width: 320px;
    background-color: #000;
    border-radius: 20px;
    overflow: hidden;
    position: relative;
}

.window-buttons {
    position: absolute;
    top: 10px;
    left: 15px;
    display: flex;
    gap: 8px;
    cursor: pointer;
}

.window-buttons span {
    width: 12px;
    height: 12px;
    border-radius: 50%;
    display: inline-block;
}

.window-buttons .close {
    background-color: #ff5f57;
}

.window-buttons .minimize {
    background-color: #ffbd2e;
}

.window-buttons .maximize {
    background-color: #28c840;
}

.display {
    color: #fff;
    font-size: 48px;
    text-align: right;
    padding: 20px;
    min-height: 80px;
    display: flex;
    align-items: center;
    justify-content: flex-end;
}

.display .input {
    display: block;
    word-wrap: break-word;
}

.buttons {
    display: flex;
    flex-wrap: wrap;
}

.button {
    width: 25%;
    height: 60px;
    font-size: 24px;
    border: 1px solid #555;
    background-color: #333;
    color: #fff;
    cursor: pointer;
    outline: none;
}

.zero {
    width: 50%;
}

.operator {
    background-color: #ff9500;
    color: #fff;
}

.function {
    background-color: #a5a5a5;
    color: #000;
}

.button:hover {
    filter: brightness(1.2);
}

.button:active {
    filter: brightness(0.8);
}
  1. 모든 버튼 요소를 선택합니다.
  2. 각 버튼에 클릭 이벤트 리스너를 추가합니다.
  3. 버튼이 클릭되었을 때, 해당 버튼의 값을 콘솔에 출력합니다.
  4. script.js
    1. 모든 버튼 요소와 디스플레이 요소를 선택합니다.
    2. 각 버튼에 클릭 이벤트 리스너를 추가합니다.
    3. 버튼이 클릭되었을 때, 클래스가 number인 경우 디스플레이에 값을 표시합니다.
    4. 디스플레이가 0일 때는 클릭한 숫자로 바뀌어야 합니다.
    5. 디스플레이가 0이 아닐 때는 클릭한 숫자가 뒤에 추가되어야 합니다.
  • 소수점(.) 버튼을 클릭하면 디스플레이에 소수점을 추가하세요. (이미 소수점이 있는 경우 추가되지 않도록)

  • C 버튼을 클릭하면 디스플레이를 0으로 초기화하세요.

  • 디스플레이에 숫자를 입력한 다음 연산기호를 누르면 디스플레이에 있는 숫자를 firstOperand로 저장하고 연산기호를 기억합니다.

  • 연산기호를 누른 후 디스플레이에 다른 숫자를 입력하면 새로운 숫자가 디스플레이에 입력되도록 합니다.

  1. firstOperand, operator 변수를 선언합니다.
    • firstOperand: 첫 번째 피연산자를 저장할 변수입니다.
    • operator: 연산자를 저장할 변수입니다.
  2. 연산기호 버튼이 클릭되면 현재 디스플레이 값을 firstOperand로 저장하고, 연산기호를 기억합니다.
    • 첫 번째 피연산자가 null이면 현재 디스플레이 값을 firstOperand로 저장합니다.
    • operator 변수에 클릭한 연산기호를 값으로 할당합니다.
    • firstOperandoperator를 console에 출력합니다.
  3. 연산기호 버튼이 클릭된 후 디스플레이에 다른 숫자를 입력하면 새로운 숫자가 디스플레이에 입력되도록 합니다.
    • 연산기호 버튼이 클릭된 후 두 번째 숫자를 입력하면 디스플레이의 값이 새로 입력한 숫자로 바뀝니다.
  4. = 버튼을 눌러서 계산이 완료된 후, 새로운 숫자를 입력하고 연산자 버튼을 누릅니다.
  5. 현재 디스플레이 값을 firstOperand로 저장하고, 연산자를 operator로 저장합니다.
  6. 새로운 숫자를 입력하고 다시 연산자 버튼을 누르면, 현재 디스플레이 값을 secondOperand로 저장합니다. 다시 연산자를 누르면, 이전에 입력한 secondOperandoperator로 계산이 됩니다.
  7. 계산 결과를 디스플레이에 표시하고, firstOperand를 결과 값으로 업데이트합니다.
// 모든 버튼 요소와 디스플레이 요소를 선택합니다.
const buttons = document.querySelectorAll('.button');
const display = document.querySelector('.input');

// 피연산자와 연산자를 저장할 변수를 선언합니다.
let firstOperand = null;
let operator = null;
let waitingForSecondOperand = false;

// 초기 디스플레이 값을 설정합니다.
display.textContent = '0';

// 각 버튼에 클릭 이벤트 리스너를 추가합니다.
buttons.forEach((button) => {
    button.addEventListener('click', () => {
        const buttonText = button.textContent;

        // 숫자 버튼을 클릭한 경우
        if (button.classList.contains('number')) {
            inputDigit(buttonText);
        }

        // 연산자 버튼을 클릭한 경우
        else if (button.classList.contains('operator')) {
            handleOperator(buttonText);
        }

        // 소수점 버튼을 클릭한 경우
        else if (buttonText === '.') {
            inputDecimal(buttonText);
        }

        // 'C' 버튼을 클릭한 경우
        else if (buttonText === 'C') {
            resetCalculator();
        }

        // 그 외의 버튼들 처리 (±, %)
        else if (buttonText === '±') {
            toggleSign();
        } else if (buttonText === '%') {
            inputPercent();
        }
    });
});

// 숫자를 입력하는 함수
function inputDigit(digit) {
    if (waitingForSecondOperand) {
        display.textContent = digit;
        waitingForSecondOperand = false;
    } else {
        if (display.textContent === '0') {
            display.textContent = digit;
        } else {
            display.textContent += digit;
        }
    }
    updateFontSize();
}

// 소수점을 입력하는 함수
function inputDecimal(dot) {
    if (waitingForSecondOperand) {
        display.textContent = '0.';
        waitingForSecondOperand = false;
        return;
    }
    if (!display.textContent.includes(dot)) {
        display.textContent += dot;
    }
    updateFontSize();
}

// 연산자를 처리하는 함수
function handleOperator(nextOperator) {
    const inputValue = parseFloat(display.textContent);

    if (operator && waitingForSecondOperand) {
        operator = nextOperator === '=' ? null : nextOperator;
        console.log(`Operator changed to ${operator}`);
        return;
    }

    if (firstOperand === null) {
        firstOperand = inputValue;
    } else if (operator) {
        const result = calculate(firstOperand, inputValue, operator);
        display.textContent = formatResult(result);
        firstOperand = result;
    }

    waitingForSecondOperand = true;
    operator = nextOperator === '=' ? null : nextOperator;
    console.log(`First Operand: ${firstOperand}, Operator: ${operator}`);
    updateFontSize();
}

// 계산을 수행하는 함수
function calculate(firstOperand, secondOperand, operator) {
    let result;
    if (operator === '+') {
        result = firstOperand + secondOperand;
    } else if (operator === '−') {
        result = firstOperand - secondOperand;
    } else if (operator === '×') {
        result = firstOperand * secondOperand;
    } else if (operator === '÷') {
        if (secondOperand === 0) {
            alert('0으로 나눌 수 없습니다.');
            return 0;
        }
        result = firstOperand / secondOperand;
    } else {
        return secondOperand;
    }

    // 부동소수점 오류를 최소화하기 위해 결과를 반올림합니다.
    result = parseFloat(result.toFixed(10));

    return result;
}

// 결과를 포맷팅하는 함수
function formatResult(result) {
    if (result.toString().length > 12) {
        // 지수 표기법으로 변환
        return result.toExponential(5);
    } else {
        return result.toString();
    }
}

// 계산기를 초기화하는 함수
function resetCalculator() {
    display.textContent = '0';
    firstOperand = null;
    operator = null;
    waitingForSecondOperand = false;
    updateFontSize();
}

// 부호를 변경하는 함수
function toggleSign() {
    const currentValue = parseFloat(display.textContent);
    if (currentValue === 0) return;
    display.textContent = (-currentValue).toString();
    updateFontSize();
}

// 퍼센트를 계산하는 함수
function inputPercent() {
    const currentValue = parseFloat(display.textContent);
    const newValue = currentValue / 100;
    display.textContent = newValue.toString();
    updateFontSize();
}

// 디스플레이 글자 크기를 조정하는 함수
function updateFontSize() {
    const length = display.textContent.length;
    if (length > 10) {
        display.style.fontSize = '30px';
    } else if (length > 7) {
        display.style.fontSize = '40px';
    } else {
        display.style.fontSize = '48px';
    }
}

마무리로 테스트 실행하고 끝~

profile
개발

0개의 댓글