Notice
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | ||||||
2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 |
Tags
- darknode
- Typescript
- DOM
- javascript
- 웹사이트
- calender
- stopwatch
- todolist
- CSS
- mini_project
- 브라우저
- Calculator
- localStorage
- html
- React
- Project
- Timer
Archives
- Today
- Total
이야기 정리
eval() 없이 계산기 만들기 본문
eval 함수는 왜 사용하면 안될까?
계산기를 만들기 위해 코드들을 참고하다보면 자주보는 코드가 있다. 바로 eval 함수다. eval 함수는 전달된 문자열을 JS코드로 해석해 해석된 코드를 실행한다. 얼핏 편리해보이는 이 함수를 어째서 사용해선 안될까? 이유는 크게 3가지로 나눌 수 있다.
- 성능 : 어떤 문자열이 함수에 전달될지 예측하기 어렵다.
- 보안 : 텍스트 필드에 악성 코드를 입력하면, eval 함수로 실행되어 보안에 위험이 생긴다.
- 디버깅 : eval 함수에 전달되는 값은 문자열이기 때문에 ‘,’를 제대로 명시 하지 않을 시 문제가 발생한다.
참고 : https://developer-talk.tistory.com/271
위와 같은 이유로 eval함수를 사용하지 않고, 간단한 계산기를 만들었다.
See the Pen calculator by beren-105 (@beren-105) on CodePen.
필요한 기능
- 기본 사칙연산 기능
- AC를 누를 시 초기화된다.
- 화면에 누른 숫자들이 보이고, =를 클릭 시 최종값을 보여준다.
- 연산자가 연속되어선 안된다.
- 키보드를 누를 시, 마우스로 클릭한 것과 동일한 이벤트가 발생해야한다.
- 발생한 문제
- eval 함수의 대체방안
- if문을 여러개 사용하며 코드가 복잡해진다.
- 맨 처음에 연산자가 올 수 없게 하면 -값을 입력할 수 없다.
- 이미 버튼 이벤트를 만들었는데, 키보드를 눌렀을 때의 이벤트를 처음부터 끝까지 아예 다시 만들어야하나? 버튼 이벤트를 활용하는 방법은 없을까?
- 해결방안
1. eval 함수의 대체방안
- eval 함수의 경우 다음과 같은 우회 함수를 사용하였다.
let result = (new Function ('return '+ input.value))
answer.value = result()
2. if문 남용으로 인한 코드의 복잡함
- if문 대신 switch문을 사용해 코드를 보기 쉽게 변경했다.
function btnClick(e) {
const list = e.target.className
switch (list) {
case 'num' :
numberClick(e.target)
break;
case 'operator' :
if (state === true) {
operatorClick(e.target)
break;
} else {break}
case 'operator minus' :
if (index === 0) {
index = 1
operatorClick(e.target)
break;
} else if (state === true) {
operatorClick(e.target)
break;
} else {break}
case 'equal' :
if (state === true) {
let result = (new Function ('return '+ input.value))
answer.value = result()
break;
} else {break}
case 'reset' :
state = true
input.value = input.value.substring(0, input.value.length - 1)
break;
case 'all-reset' :
allReset()
break;
}
}
3. 맨 처음 -값을 입력할 수 없는 문제
- 기존 - 역시 다른 +, /, * 연산자와 함께 두었으나, -를 따로 나누고, 함수만 재사용했다.
// +, /, * 연산자
case 'operator' :
if (state === true) {
operatorClick(e.target)
break;
} else {break}
// - 연산자
case 'operator minus' :
if (index === 0) {
index = 1
operatorClick(e.target)
break;
} else if (state === true) {
operatorClick(e.target)
break;
} else {break}
4. 이미 버튼 클릭 이벤트를 만들었는데, 똑같은 내용을 키보드 이벤트에 또 넣어야하나?
- 키보드 이벤트를 만들 때 가장 많은 고민을 한 부분이다. 버튼 이벤트와 키보드 이벤트는 별개로 만들어야한다. 하지만 버튼 이벤트의 내용을 그대로 또 키보드 이벤트에 복사해 붙여넣는 것은 코드만 길어질 뿐이다.
- 때문에 키보드에 연산자를 계산하는 함수를 다시 만드는 것이 아닌, 키보드를 누르면 버튼이 클릭되게 만들었다.
window.addEventListener('keydown', keyclick)
function keyclick(e) {
var keyNumber = e.which;
if (keyNumber > 47 && keyNumber < 58 ||
keyNumber > 96 && keyNumber < 105 ||
keyNumber === 189 ||
keyNumber === 187 ||
keyNumber === 191 ||
keyNumber === 111 ||
keyNumber === 107
) {
buttons.forEach((btn) => {
if (btn.dataset.value == e.key)
btn.click()
})
} else if (keyNumber === 13) {
buttons[17].click()
} else if (keyNumber === 8) {
buttons[2].click()
} else if (keyNumber === 27) {
buttons[1].click()
}
}
위 문제점들을 해결하고 완성된 최종 코드다.
const buttons = document.querySelectorAll('button')
const input = document.querySelector('.input')
const answer = document.querySelector('.input-final')
let state = false
let index = 0
buttons.forEach((button) => {
button.addEventListener('click', btnClick)
})
function btnClick(e) {
const list = e.target.className
switch (list) {
case 'num' :
numberClick(e.target)
break;
case 'operator' :
if (state === true) {
operatorClick(e.target)
break;
} else {break}
case 'operator minus' :
if (index === 0) {
index = 1
operatorClick(e.target)
break;
} else if (state === true) {
operatorClick(e.target)
break;
} else {break}
case 'equal' :
if (state === true) {
let result = (new Function ('return '+ input.value))
answer.value = result()
break;
} else {break}
case 'reset' :
state = true
input.value = input.value.substring(0, input.value.length - 1)
break;
case 'all-reset' :
allReset()
break;
}
}
function numberClick(e) {
state = true
index = 1
input.value += e.dataset.value
}
function operatorClick(e) {
state = false
input.value += e.dataset.value
}
function allReset() {
input.value = ''
answer.value = '0'
index = 0
}
window.addEventListener('keydown', keyclick)
function keyclick(e) {
var keyNumber = e.which;
if (keyNumber > 47 && keyNumber < 58 ||
keyNumber > 96 && keyNumber < 105 ||
keyNumber === 189 ||
keyNumber === 187 ||
keyNumber === 191 ||
keyNumber === 111 ||
keyNumber === 107
) {
buttons.forEach((btn) => {
if (btn.dataset.value == e.key)
btn.click()
})
} else if (keyNumber === 13) {
buttons[17].click()
} else if (keyNumber === 8) {
buttons[2].click()
} else if (keyNumber === 27) {
buttons[1].click()
}
}
'Project > JavaScript project' 카테고리의 다른 글
자바스크립트로 두더지잡기 게임 만들기 - clearInterval() (0) | 2023.02.16 |
---|---|
2-2. Stop watch와 Timer - 타이머 만들기 (0) | 2023.01.27 |
2. Stop watch와 Timer - 스톱워치 만들기 (0) | 2023.01.18 |
1-2. To do list - 클릭 이벤트, 일정 삭제, 일정 수정하기 (0) | 2023.01.13 |
1. To do list - 날짜 다루기, 일정 추가하기 (0) | 2023.01.12 |
Comments