포와인드

html 사칙연산 계산기 만들기

도준영 2025. 3. 12. 10:51

html로 간단한 사칙연산 계산기를 만들어 보자.

 

목표 : html로 윈도우 기본 어플인 표준 계산기와 비슷하게 만들어 보기.

 

연산 부분은 따로 구현하지 않고 그냥 eval() 함수를 사용하였음.

 

세부 기능의 표현 방식은 최대한 윈도우 계산기와 동일하게 만듬.

 

 

1차 리팩토링 

- 숫자 입력 방식을 개별 입력에서  함수화하여 통일 (0 제외)

- 키보드 입력 기능 추가

 

변경 후 오류 확인

표시창에 천단위 콤마 (,) 삽입 후 읽어올 때 콤마 삭제하여 읽어오는 과정에서 오류 발생 

연산 완료 후 부호 클릭시 subarea 변경 안됨 + 연산 오류 

 

 

2차 리팩토링

- ( - -) 연산 예외처리

- 0으로 나눌시 "0으로 나눌 수 없습니다."예외처리

- 0을 0으로 나눌 시 "정의되지 않은 값입니다." 예외처리

- 0에서 토글 버튼 누를 시 동작하지 않도록 예외처리

...

 

확인된 오류 사항

백스페이스 키 입력값이 안 읽혀와 \ 자리에 넣음

엔터키 입력시 계산(=)기능만 적용되어야 하는데 클린(C)기능까지 적용됨

스페이스 키 입력시 계산(=)기능만 적용되어야 하는데 0 키 입력까지 적용됨

 

3차 리팩토링 (최종본)

 

디자인 및 css 수정

기능 버그 수정

주석 추가

 

 

<!DOCTYPE html>
<html lang="ko">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<style>
body {
    display: flex;
    justify-content: center;
    align-items: center;
    min-height: 100vh;
    margin: 0;
    background-color: #f3f3f3;
    font-family: Arial, sans-serif;
}

.wrapper {
    background: #ffffff;
    padding: 20px;
    border-radius: 10px;
    box-shadow: 0px 4px 10px rgba(0, 0, 0, 0.2);
    width: 320px; 
}

.subarea, .calcarea {
    width: 100%;
    text-align: right;
    border: none;
    background: none;
    color: #555;
    font-family: inherit;
}

.subarea {
    height: 30px;
    font-size: 16px;
    margin-bottom: 10px;
}

.calcarea {
    height: 60px;
    font-size: 36px;
    color: #000;
    font-weight: bold;
    margin-bottom: 15px;
}

.button-wrapper {
    display: grid;
    grid-template-columns: repeat(4, 1fr); 
    gap: 10px;
}

.button-wrapper .button-0 {
    grid-column: span 2; 
}

.number {
    width: 100%;
    height: 50px;
    font-size: 18px;
    border: 1px solid #ddd;
    background-color: #f9f9f9;
    color: #000;
    border-radius: 5px;
    cursor: pointer;
    transition: background-color 0.3s ease, transform 0.2s ease;
}

.number:hover {
    background-color: #e6e6e6;
}

.number:active {
    background-color: #dcdcdc;
    transform: scale(0.98);
}

#calculate {
    background-color: #007BFF;
    color: #fff;
    font-weight: bold;
    border: none;
}

#calculate:hover {
    background-color: #0056b3;
}

#calculate:active {
    background-color: #004494;
    transform: scale(0.98);
}


</style>
<title>Calculator</title>
</head>
<body onkeypress="keydown(event)">
	<div class ="wrapper">

		<div>
			<input type ="text" class="subarea" id="subarea" readonly >
		</div>

		<br>

		<div>
			<input type ="text" class="calcarea" id="calcarea" value = '0' readonly >
		</div>
	<br>

		<div class="button-wrapper">
			<div class="button"><input class="number" type="button" value="C" id="clearcalc" onclick="clearbutton()"></div>
			<div class="button"><input class="number" type="button" value="+/-" onclick="toggle()"></div>
			<div class="button"><input class="number" type="button" value="<-" onclick="backspace()"></div>
			<div class="button"><input class="number" type="button" value="÷" onclick="inputsym('/')"></div>

			<div class="button"><input class="number" type="button" value="7" onclick="inputNumber(7)"></div>
			<div class="button"><input class="number" type="button" value="8" onclick="inputNumber(8)"></div>
			<div class="button"><input class="number" type="button" value="9" onclick="inputNumber(9)"></div>
			<div class="button"><input class="number" type="button" value="×" onclick="inputsym('*')"></div>

			<div class="button"><input class="number" type="button" value="4" onclick="inputNumber(4)"></div>
			<div class="button"><input class="number" type="button" value="5" onclick="inputNumber(5)"></div>
			<div class="button"><input class="number" type="button" value="6" onclick="inputNumber(6)"></div>
			<div class="button"><input class="number" type="button" value="-" onclick="inputsym('-')"></div>

			<div class="button"><input class="number" type="button" value="1" onclick="inputNumber(1)"></div>
			<div class="button"><input class="number" type="button" value="2" onclick="inputNumber(2)"></div>
			<div class="button"><input class="number" type="button" value="3" onclick="inputNumber(3)"></div>
			<div class="button"><input class="number" type="button" value="+" onclick="inputsym('+')"></div>

			<div class="button-0"><input class="number" type="button" value="0" onclick="input0()"></div>
			<div class="button"><input class="number" type="button" value="." onclick="inputdot()"></div>
			<div class="button"><input class="number" type="button" value="=" id="calculate" onclick="calculate()"></div>
		</div>
	</div>
</body>
</html>
<script>

// 전역 변수 목록
var completecalc = true;
var symbolcheck = false;
var firstzerocheck = true;
var dot = false;
var left = 0;
var right = 0;
var lastsym = '';
var input = 0;
var result = 0;
var symclac = false;
var lastkey = null;
var lastkeyCode = 0;
var lastinput = 0;


// 표기창에 16자 제한 거는 함수
function calcarealength() {
	document.getElementById('subarea').value = document.getElementById('subarea').value.substring(0, 16);
	var val = document.getElementById('calcarea').value.replace(/,/g, "").substring(0, 16);
	if(dot == false && val >= 0){
		document.getElementById('calcarea').value = Number(val).toLocaleString('ko-KR', { maximumFractionDigits: 16 });
	}else{
		document.getElementById('calcarea').value = val
	}
}

// × ÷ 를 * / 로 변환시키는 함수
function symbolconv(){
	
}


// 키보드 입력 함수
function keydown(event){
	var key = event.key;
	var keyCode = event.keyCode;
	var code = event.code;
    if(key == '0'){
    	input0();   
	}else if(key == '1'){
		inputNumber(1);
	}else if(key == '2'){
		inputNumber(2);
	}else if(key == '3'){
		inputNumber(3);
	}else if(key == '4'){
		inputNumber(4);
	}else if(key == '5'){
		inputNumber(5);
	}else if(key == '6'){
		inputNumber(6);
	}else if(key == '7'){
		inputNumber(7);
	}else if(key == '8'){
		inputNumber(8);
	}else if(key == '9'){
		inputNumber(9);
	}else if(keyCode == 92){
		backspace();
		lastkeyCode = 92;
		return;
	}else if(key == '='){
		calculate();
	}else if(key == 'Enter'){
		calculate();
	}else if (key == '+'){
		inputsym('+');
	}else if (key == '-'){
		inputsym('-');
	}else if (key == '*'){
		inputsym('*');
	}else if (key == '/'){
		inputsym('/');
	}else if(key == '.'){
		inputdot();
	}else if(key == ' '){
		event.preventDefault();
		if (lastKey != null) {
			if(lastkeyCode == 92){
				backspace();
				return;
			}else {
	            processKey(lastKey); 
				return;
			}
		}else{
	        return;
		}
	}
	else{
		console.log(key);
	}
	lastKey = key;
	lastkeyCode = 0;
}

// 재입력 기능 (Spacebar)
function processKey(key) {
    if (key == '0') {
        input0();   
    } else if (!isNaN(key)) { 
        inputNumber(Number(key));
    } else if (key === '=') {
        calculate();
    } else if (key === 'Enter') {
        calculate();
    } else if (key === '+' || key === '-' || key === '*' || key === '/') {
        inputsym(key);
    } else {
        console.log("입력된 키:", key);
    }
}


// 0인지 판별하는 상태 변수
function firstzero(){
	firstzerocheck = false;
}


// C 버튼 기능 - 초기화
function clearbutton(){
	clearsub();
	clearcalc();
	symbolcheck = false;
	firstzerocheck = true;
	left = 0;
	right = 0;
	input = 0;
	result = 0;
	lastsym = '';
	document.getElementById('calcarea').value='0';
	console.log('클리어 기능 실행');
}


// 계산 에리어 초기화
function clearcalc(){ 
	document.getElementById('calcarea').value='';
	completecalc = false;
	dot = false;
}

// 서브 에리어 초기화
function clearsub(){
	document.getElementById('subarea').value='';
}

// 숫자 입력시 부호 입력 상탯값 변경
function inputnum(){
	symbolcheck = false;
}

// 부호 입력시 부호 입력 상탯값 변경
function inputsymbol(){
	symbolcheck = true;
	dot = false;
}

// 기호 입력 기능
function inputsym(symbol){
	
	if(symbol == '*'){
		symbol = '×';
	}

	if(symbol == '/'){
		symbol = '÷';
	}

	// 계산이 완료된 상황일 경우
	if(completecalc == true){
		left = document.getElementById('calcarea').value.replace(/,/g, "");
		document.getElementById('subarea').value=left+symbol;
		inputsymbol();
		lastsym = symbol;
		completecalc = false;
	}
	// 계산 미완료 상태
	else{
		// 기호 미입력 상태일 경우
		if(symbolcheck == false){
			// subarea에 값이 없을 때 현재값 + 입력한 기호 를 subarea에 넣는다.
			if(document.getElementById('subarea').value == ''){
				var nowvalue = document.getElementById('calcarea').value.replace(/,/g, "");
				document.getElementById('subarea').value+=nowvalue+symbol;
				inputsymbol();
				lastsym = symbol;
				completecalc = false;
			}
			// 계산 기능 호출 후 기호 넣기 ex): 48-35 인 상태에서 + 입력시 : 13+
			else{
				symclac = true;
				lastsym = symbol;
				calculate();
				inputsymbol();
				completecalc = false;
			}
		}else if(dot == true && document.getElementById('subarea').value == ''){
			var nowvalue = document.getElementById('calcarea').value.replace(/,/g, "");
			// 소수 값이 0일때(0.0000000~) 0으로 치환
			if(nowvalue == '0' || nowvalue == 0){
				document.getElementById('subarea').value = '0'+symbol;
				document.getElementById('calcarea').value = '0';
 			}
			// 0.123456 -> 서브area에 0.123456- 로 넣어주기
			else{
				document.getElementById('subarea').value+=nowvalue+symbol;
			}
			inputsymbol();
			lastsym = symbol;
			dot = false;
			completecalc = false;
		}
		// 부호 스왑
		else{
			var tmp = document.getElementById('subarea').value.slice(0, -1);
			document.getElementById('subarea').value = tmp+symbol;
			completecalc = false;
			lastsym = symbol;
			completecalc = false;
		}
	}
}

// 소숫점 입력 기능
function inputdot(){	
	// 이미 소숫점을 입력한 상태일 때 => 입력 무시
	if(dot == true){
		return;
	}
	var nowvalue = document.getElementById('calcarea').value.replace(/,/g, "");
	// 계산 완료된 상태일 때 => 0. 
	if(completecalc == true){
		clearcalc();
		document.getElementById('calcarea').value='0.';
		inputsymbol();
		firstzerocheck = true;
		dot = true;
		return;
	}
	// 현재 값이 0일 때
	if(nowvalue == 0){
		document.getElementById('calcarea').value='0.';
		inputsymbol();
		firstzerocheck = true;
		dot = true;
		return;
	}
	// 일반적인 경우 +.
	if(symbolcheck == false){
		document.getElementById('calcarea').value+='.';
		firstzerocheck = false;
		dot = true;
	}
}

// 숫자 입력 함수
function inputNumber(num) {

	// 0 입력은 별도 처리
	if(num == 0 || num == "0"){
		input0();	
		return;
	}

	// 소숫점 아래 값 입력일 때
	if(dot == true){
		document.getElementById('calcarea').value+=num;
		calcarealength();
	}
	// 계산이 완료된 상태일 때
	else if(completecalc == true){
		clearsub();
		clearcalc();
		document.getElementById('calcarea').value=num;
		calcarealength();
		inputnum();
	}
	// 첫번째 값이 0일 때
	else if(firstzerocheck == true){
		document.getElementById('calcarea').value=num;
		inputnum();
		firstzero();
	}
	// 마지막으로 기호가 입력된 상태인 경우
	else if(symbolcheck == true){
		document.getElementById('calcarea').value=num;
		inputnum();
		firstzero();
	}
	// 일반적인 경우 + num
	else{
		var tmp = document.getElementById('calcarea').value.replace(/,/g, "");
		tmp +=num;
		document.getElementById('calcarea').value = Number(tmp).toLocaleString();
		calcarealength();
		inputnum();
		firstzero();
	}completecalc = false;
	firstzerocheck = false;
}

// 0 입력은 별도 관리
function input0(){
	// 계산이 완료된 상태일 때 -> 결과 칸 초기화 후 calcarea에 0
	if(completecalc == true){
		clearsub();
		clearcalc();
		var tmp = document.getElementById('calcarea').value.replace(/,/g, "");
		tmp +='0';
		document.getElementById('calcarea').value = Number(tmp).toLocaleString();
		firstzerocheck = true;
	}
	// calcarea 가 비어 있을 때 0 입력
	else if(document.getElementById('calcarea').value ==''){
		var tmp = document.getElementById('calcarea').value.replace(/,/g, "");
		tmp +='0';
		document.getElementById('calcarea').value = Number(tmp).toLocaleString();
		firstzerocheck = true;
	}
	// 부호가 들어온 상태일 때 0 입력
	else if(symbolcheck == true && dot == false){
		document.getElementById('calcarea').value='0'
		inputnum();
		firstzero();
	}
	else{
		var nowvalue = document.getElementById('calcarea').value;
		var last = nowvalue.charAt(nowvalue.length - 1);
		// 기존 값이 있을 떄 (0이 아닐 때) 뒤에 0 붙임
		if(firstzerocheck == false){
			var tmp = document.getElementById('calcarea').value.replace(/,/g, "");
			tmp +='0';
			document.getElementById('calcarea').value = Number(tmp).toLocaleString();
			calcarealength();
		}
		// 소숫점 단위 입력 중일때 0 붙여줌
		else if(dot == true){
			var tmp = document.getElementById('calcarea').value.replace(/,/g, "");
			tmp +='0';
			document.getElementById('calcarea').value = tmp;
			calcarealength();
		}
		// 값이 0일 때는 그대로 0 (00 X)
		else{
			return;
		}

	}completecalc = false;

}

// 토글 기능
function toggle(){
	completecalc = false;

	var nowvalue = Number(document.getElementById('calcarea').value.replace(/,/g, ""));
	// 0일때 입력 무시
	if(document.getElementById('calcarea').value == 0){

	}else{
		try{
			// 0보다 클 때 -1 곱해주기
			if(nowvalue > 0){
				nowvalue = nowvalue*-1;
				document.getElementById('calcarea').value = nowvalue.toLocaleString();
				console.log("+/- 토글 기능 실행");
			}
			// 0보다 작을 때 Math 함수로 절댓값 변환
			else{
				nowvalue = Math.abs(nowvalue);
				document.getElementById('calcarea').value = nowvalue.toLocaleString();
				console.log("+/- 토글 기능 실행");
			}
		}catch{
			console.log("토글 기능 미수행");
		}
	}		
}


// 백스페이스 기능 
function backspace(){
	// 계산 완료 상태인 경우 아무 기능도 수행되지 않음
	if(completecalc == false){	
		var nowvalue = document.getElementById('calcarea').value.replace(/,/g, "");
		var deletevalue = nowvalue.charAt(nowvalue.length - 1); 
		nowvalue = nowvalue.slice(0, -1);
		var calcareavalue = Number(nowvalue).toLocaleString('ko-KR', {maximumFractionDigits: 12});
		var last = nowvalue.charAt(nowvalue.length - 1);
		// 소숫점 판별
		if(deletevalue == '.'){
			dot = false;
			if(nowvalue == '0'){
				firstzerocheck = true;			
			}
			document.getElementById('calcarea').value = calcareavalue;
		}
		// 소숫점 앞까지만 지운 경우
		else if(last == '.'){
			document.getElementById('calcarea').value = calcareavalue + '.';
		}
		// 다 지운 경우
		else if(nowvalue == ''){
			document.getElementById('calcarea').value = '';			
		}
		// 부호 앞까지만 지운 경우 부호 판별 변수 true 만들어주기
		else if(isNaN(last) == true){
			inputsymbol();
			document.getElementById('calcarea').value = calcareavalue;
		}
		// 그 외의 경우 = 숫자 
		else{
			inputnum();
			document.getElementById('calcarea').value = calcareavalue;
		}
	}else{
		document.getElementById('subarea').value = '';
	}
	
	console.log("백스페이스 기능 실행");
}

// 계산 기능 
function calculate(){

	right = document.getElementById('calcarea').value.replace(/,/g, "");
	left = document.getElementById('subarea').value.slice(0,-1);
	// = 기능 연속 입력 
	if(completecalc == true){
		var sum = right+lastsym+lastinput;
		result = eval(sum.replace(/×/g, '*').replace(/÷/g, '/'));
		document.getElementById('calcarea').value = result.toLocaleString();
		document.getElementById('subarea').value = sum + '=';
		calcarealength();
		console.log('연속 계산');
		completecalc = true;
		firstzerocheck = false;
		dot = false;
		symbolcheck = true;
		
		return;
	}

	// = 입력 없이 부호 입력으로 진행되는 계산 -> ex): 19 + 10 *   
	if(symclac == true){
		left = document.getElementById('subarea').value.slice(0,-1);
		var sum = document.getElementById('subarea').value
		right = document.getElementById('calcarea').value.replace(/,/g, "");
		var value = sum.concat(right);
		result = eval(value.replace(/×/g, '*').replace(/÷/g, '/'));
		document.getElementById('calcarea').value = result.toLocaleString();
		document.getElementById('subarea').value = result+lastsym;
		symclac = false;
		calcarealength();
		console.log('부호로 계산');
		completecalc = true;
		firstzerocheck = false;
		dot = false;
		symbolcheck = true;
		
		right = lastinput;
		return;
	}
	
	// -를 빼기(-) 할 떄 
	if(right<0 && lastsym == '-'){
		var sum = left+'+';
		var concat = sum.concat(Math.abs(right));
		var sub = left + '-' + right;
		result = eval(concat.replace(/×/g, '*').replace(/÷/g, '/'));
		document.getElementById('subarea').value = sub + '=';
		document.getElementById('calcarea').value = result;
		calcarealength();
		console.log('-로 빼기');
		completecalc = true;
		firstzerocheck = false;
		dot = false;
		symbolcheck = true;
		
		right = lastinput;
		return;
	}
	
	// 0을 0으로 나누는 경우
	if(document.getElementById('subarea').value == "0/" && Number(document.getElementById('calcarea').value) == 0){
		document.getElementById('calcarea').value = "정의되지 않은 결과입니다.";
		console.log('0을 0으로 나누기');
		completecalc = true;
		firstzerocheck = false;
		dot = false;
		symbolcheck = true;
		
		return;
	}
	// 0으로 나누는 경우
	if(lastsym == '/' && document.getElementById('calcarea').value == '0'){
		document.getElementById('calcarea').value = "0으로 나눌 수 없습니다.";
		console.log('0으로 나누기');
		completecalc = true;
		firstzerocheck = false;
		dot = false;
		symbolcheck = true;
		
		right = lastinput;
		return;
	}
	// 입력한 부호 없이 그냥 = 했을 때 subarea 에 calcarea 값 + = ex): 10 + enter => subarea = "10="
	if(lastsym == ''){
		document.getElementById('subarea').value = document.getElementById('calcarea').value.replace(/,/g, "")+"=";
		calcarealength();
		console.log('부호 없는 계산 입력');
		completecalc = true;
		firstzerocheck = false;
		dot = false;
		symbolcheck = true;
		
		right = lastinput;
		return;
	}

	// 일반적인 계산 기능 실행
	var sum = left + lastsym + right;
	result = eval(sum.replace(/×/g, '*').replace(/÷/g, '/'));
	document.getElementById('subarea').value = sum + '=';
	document.getElementById('calcarea').value = result;
	calcarealength();
	console.log('일반 계산 기능 실행');
	completecalc = true;
	firstzerocheck = false;
	dot = false;
	symbolcheck = true;
	
	lastinput = right;
}


</script>