오늘은 저번주에 만들었던 계산기에 기능을 추가하는 작업을 했다.
아직 개념을 제대로 배우지 않은 이벤트 리스너를 사용했고, 그동안 공부했던 것들에 비해 높은 난이도를 가진 작업이어서 코딩을 처음 배우는 사람들에게는 매우 어려울 것 같다고 생각했다.
실제로 이번에 나와 페어 프로그래밍을 하게 되신 분은 아예 깃허브도 처음 사용해 보시는 분이라 파일을 다운받는 것 까지 하나하나 알려 드려야 했었다.
계산기 기능은 크게 세가지 파트로 나뉘었었다.
기본적인 기능을 넣고 변수, 함수에 대해 학습하는 Bare Minimum test
심화된 기능을 넣는 Advanced test,
그리고 초심자는 따라하기 매우 어려운 난이도의 기능을 넣는 Nightmare test까지.
각 파트에서 요구하는 기능 구현에 대해 차례대로 설명하도록 하겠다.
const calculator = document.querySelector('.calculator');
// calculator 엘리먼트와, 그 자식 엘리먼트의 정보를 모두 담고 있습니다.
const buttons = calculator.querySelector('.calculator__buttons');
// calculator__keys 엘리먼트와, 그 자식 엘리먼트의 정보를 모두 담고 있습니다.
const firstOperend = document.querySelector('.calculator__operend--left');
// calculator__operend--left 엘리먼트와, 그 자식 엘리먼트의 정보를 모두 담고 있습니다.
const operator = document.querySelector('.calculator__operator');
// calculator__operator 엘리먼트와, 그 자식 엘리먼트의 정보를 모두 담고 있습니다.
const secondOperend = document.querySelector('.calculator__operend--right');
// calculator__operend--right 엘리먼트와, 그 자식 엘리먼트의 정보를 모두 담고 있습니다.
const calculatedResult = document.querySelector('.calculator__result');
// calculator__result 엘리먼트와, 그 자식 엘리먼트의 정보를 모두 담고 있습니다.
function calculate(n1, operator, n2) {
let result = 0;
// TODO : n1과 n2를 operator에 따라 계산하는 함수를 만드세요.
// ex) 입력값이 n1 : '1', operator : '+', n2 : '2' 인 경우, 3이 리턴됩니다.
return String(result);
}
//Bare Minumum Test
buttons.addEventListener('click', function (event) {
// 버튼을 눌렀을 때 작동하는 함수입니다.
const target = event.target; // 클릭된 HTML 엘리먼트의 정보가 저장되어 있습니다.
const action = target.classList[0]; // 클릭된 HTML 엘리먼트에 클레스 정보를 가져옵니다.
const buttonContent = target.textContent; // 클릭된 HTML 엘리먼트의 텍스트 정보를 가져옵니다.
// ! 위 코드(Line 19 - 21)는 수정하지 마세요.
if (target.matches('button')) {
// TODO : 계산기가 작동할 수 있도록 아래 코드를 수정하세요.
//작성되어 있는 조건문과 console.log를 활용하시면 쉽게 문제를 풀 수 있습니다.
// 클릭된 HTML 엘리먼트가 button이면
if (action === 'number') {
// 그리고 버튼의 클레스가 number이면
// 아래 코드가 작동됩니다.
console.log('숫자 ' + buttonContent + ' 버튼');
}
if (action === 'operator') {
console.log('연산자 ' + buttonContent + ' 버튼');
}
if (action === 'decimal') {
console.log('소수점 버튼');
}
if (action === 'clear') {
console.log('초기화 버튼');
}
if (action === 'calculate') {
console.log('계산 버튼');
}
}
});
코드는 기본적으로 이러한 모양을 가지고 있었다.
이 아래의 내용을 채우는 것이 우리의 목표.
1. Bare Minimum test
1. 화면의 첫번째 칸에 숫자 나타내기
2. 화면의 두번째 칸에 숫자 나타내기
첫번째 칸을 나타내는 변수인 firstOperend와 두번째 변수를 나타내는 변수인 secondOperend는 각각 초기값이 0이다.
firstOperened는 비교적 간단하게 구현할 수 있다.
디스플레이에 나와있는 수가 firstOperend이고, 이를 방금 누른 버튼 안에 들어있는 수인 buttonContent로 바꿔주기만 하면 된다.
secondOperend는 조금 헷갈릴 수 있는데, 간단한 의사코드 만으로 쉽게 구현해 낼 수 있다.
- 만약 firstOperend가 0(초기값)이라면
- firstOperend의 값을 누른 버튼의 값으로 바꿔준다
- 만약 firstOperend가 0이 아니라면
- secondOperend의 값을 누른 버튼의 값으로 바꿔준다
if (action === 'number') {
// 그리고 버튼의 클레스가 number이면
// 아래 코드가 작동됩니다.
console.log('숫자 ' + buttonContent + ' 버튼');
previousKey = 'number'
if(firstOperend.textContent === '0'){
firstOperend.textContent = buttonContent
}
else if(firstOperend.textContent !== '0'){
secondOperend.textContent = buttonContent
}
}
어쨌거나 firstOperend의 값을 저장해 준 순간부터 firstOperend !== '0'이라는 조건을 만족하기 때문에, 그 이후로는 secondOperend만 주구장창 바뀌게 된다.
3. 화면에 출력된 숫자와 연산자로 계산하기
두 화면에 숫자를 띄웠으니, 이제 연산자를 띄우고 계산을 해야 한다.
맨 위에 있는 calculate 함수를 채워넣어 두 수를 계산하는 함수를 만들고, 결과창의 함수의 결과값을 띄우면 된다.
마찬가지로 연산자도 연산자 칸을 나타내는 변수인 operator를 방금 누른 버튼으로 바꿔 주면 된다.
function calculate(n1, operator, n2) {
let result = 0;
// TODO : n1과 n2를 operator에 따라 계산하는 함수를 만드세요.
// ex) 입력값이 n1 : '1', operator : '+', n2 : '2' 인 경우, 3이 리턴됩니다.
if(operator === '+'){
result = Number(n1) + Number(n2)
} else if(operator === '-'){
result = Number(n1) - Number(n2)
} else if(operator === '*'){
result = Number(n1) * Number(n2)
} else if(operator === '/'){
result = Number(n1) / Number(n2)
}
return String(result);
}
calculate 함수는 조건문을 이용하여 매개변수들을 연산하는 함수를 만든다.
if (action === 'calculate') {
console.log('계산 버튼');
calculatedResult.textContent =
calculate(firstOperend.textContent, operator.textContent, secondOperend.textContent)
previousKey = 'calculate'
}
그리고 그렇게 만든 함수는 각 디스플레이에 저장된 수를 전달인자로 삼아 계산을 하게 된다.
4. 화면상의 값을 초기화하기
AC 버튼을 누르면 모든 값들을 초기화 시킨다.
지금까지 바꾸었던 모든 변수들과 디스플레이의 숫자들을 초기 상태로 돌려 놓으면 된다,
if (action === 'clear') {
console.log('초기화 버튼');
firstOperend.textContent = '0'
operator.textContent = '+'
secondOperend.textContent = '0'
calculatedResult.textContent = '0'
previousKey = 'clear'
}
2. Advanced, Nightmare test
디스플레이가 우리가 흔히 아는대로 하나로 통일되었고, 연산자를 누르기 전까지 숫자를 계속해서 붙인다.
여기부터는 구현이 어려웠던 부분 위주로 가야겠다.
1. 숫자 이어 붙이기
말 그대로 버튼을 클릭하면 누른 버튼의 숫자가 계속해서 이어지게 만들어야 했다.
- 초기값은 0. 아무 버튼이나 누르면 해당 버튼이 디스플레이에 띄워짐
- 디스플레이에 띄워진 숫자를 firstNum이라는 변수에 저장
- 디스플레이에 0이 아닌 숫자가 띄워져 있는 경우 디스플레이에 누른 숫자를 이어붙임
- 디스플레이에 띄워진 숫자를 다시 firstNum에 저장
이때 아직 '연산자 버튼이 눌리지 않았다는' 것을 전제로 한다.
if(display.textContent === '0' && operatorForAdvanced === undefined){
//초기화면(디스플레이 숫자가 0)이고, 연산자가 눌리지 않았을 때
display.textContent = buttonContent
firstNum = display.textContent
//처음 누른 버튼을 디스플레이에 띄우고, firstNum에 저장
}else if(display.textContent !== '0' && operatorForAdvanced === undefined){
//아무 숫자를 누르고, 연산자가 눌리지 않았을 때
display.textContent += buttonContent
firstNum = display.textContent
//디스플레이에는 누르는 버튼을 계속 이어 붙이고, firstNum에 해당 숫자를 저장
}
2. 연산자를 누른 후 두번째 숫자 저장하기
- 첫번째 숫자를 모두 입력한 후, 연산자 버튼을 눌러 첫번째 수의 입력을 종료
- 연산자 버튼을 누르면 직전에 입력한 키(previousKey)를 'operator'로 변경
- 연산자를 저장하는 변수 operatorForAdvance에 사용한 연산자를 저장
- 연산자 변수에 연산자가 저장된 상태에서, 직전 누른 버튼이 연산자였다면 다시 숫자를 입력 시작
- previousNum 변수에 처음 누른 버튼의 수를 저장한 후 직전 누른 버튼인 previousKey를 number로 바꿈
- 직전 누른 버튼이 연산자가 아니었다면 디스플레이에 나와있는 숫자를 다시 previousNum 변수에 이어붙임
if(display.textContent !== '0' && operatorForAdvanced !== undefined){
//아무 숫자를 누르고, 연산자를 눌렀을 때
if(previousKey === 'operator'){
//1. 직전 누른 버튼이 연산자인 경우
display.textContent = buttonContent
previousNum = display.textContent
previousKey = 'number'
//처음 누른 버튼을 디스플레이에 띄우고, previousNum에 저장. previousKey는 number로 변경
} else if(previousKey !== 'operator' && previousKey !== 'demical'){
//2. 직전 누른 버튼이 연산자나 소수점이 아닌 경우
display.textContent += buttonContent
previousNum = display.textContent
//디스플레이에는 누르는 버튼을 계속 이어 붙이고, previousKey에 해당 숫자를 저장
}
}
직전 누른 버튼이 무엇인가에 따라 입력의 형태가 바뀌는 형식을 사용하였다.
연산자를 누르면 직전 버튼(previousKey)가 operator로 바뀌어 새로운 수를 입력 가능하게 한다.
그 뒤 다시 숫자 버튼을 누르면 previousKey가 number로 바뀌어 숫자를 이어 붙일 수 있게 된다
3. 소수점을 여러번 눌러도 한번만 적용되게 하기
이걸 구현하는데 1시간 정도 소요한 것 같다.
- 소수점 버튼을 누를 때 직전에 누른 버튼이 무엇인가에 따라 소수점을 어떻게 추가하는지가 달라진다
- 직전에 누른 버튼이 숫자 버튼이고, 소수점을 누르지 않은 상태라면, 숫자에 소수점을 추가하고 previousKey를 소수점(decimal)로 바꾼다
- 직전에 누른 버튼이 숫자 버튼이고, 소수점을 누른 상태라면 누른 숫자를 소수점 뒤에 이어붙이고 previousKey를 number로 바꾼다
- 직전에 누른 버튼이 소수점 버튼인 상태에서 소수점을 누르면 아무것도 추가하지 않는다
if(previousKey === 'number'){
//1. 직전에 누른 버튼이 연산자가 아닐 때
display.textContent += buttonContent
previousKey = 'number'
//디스플레이에는 누르는 버튼을 계속 이어 붙임
} else if(previousKey === 'decimal'){
//2. 직전 누른 버튼이 소수점일때
display.textContent += ''
//아무것도 추가하지 않음
} else {
//3. 직전 누른 버튼이 연산자, 소수점 아무것도 아닐 때(숫자일 때)
display.textContent += buttonContent
previousKey = 'decimal'
//소수점을 추가
}
4. enter를 입력하지 않고 계속 연산자를 입력할 때(2개 이상의 연산자 계산)
사실 복잡해 보이지만 그렇게 엄청 복잡한 문제는 아니었던 것 같다.
- firstNum과 previousNum, operatorForAdvance에는 이미 각 값들이 저장되어 있는 상태
- 연산자를 누르면, firstNum과 previousNum을 operatorForAdvance로 연산한 결과를 다시 firstNum에 저장
- 연산자를 눌렀으므로, 그 뒤에 누르는 숫자는 새로운 previousNum이 됨
- operatorForAdvance에 새로 누른 연산자를 저장
- 다음 연산자를 누를 때, 이미 저장되어 있던(한번 전에 누른 연산자)로 계산한 결과를 다시 firstNum에 저장
if(previousKey === 'number' && previousNum !== undefined){
//연산을 한 후 계산 버튼이 아니라 다른 연산자를 누르면
firstNum = calculate(firstNum, operatorForAdvanced, previousNum)
//해당 연산 결과에 이어서 연산
}
operatorForAdvanced = buttonContent
//누른 연산자 버튼을 operatorForAdvanced 변수에 저장
previousKey = 'operator'
//previousKey를 operator로 변경 => number 버튼에서 직전 누른 버튼을 판단할 때 사용
정말 오랜만에 고민하고 시행착오를 겪으며 코딩을 했던 날인 것 같다.
나는 안되면 일명 '샷건'을 치는 편인데.,,.,.다행히 집에 아무도 없어서 나의 괴성을 듣지는 못한 것 같다!
앞으로 이렇게 코딩 할 일이 많을텐데,.,.,.좀더 공부를 열심히 해야겠다는 생각이 들었다.
// //Bare Minumum Test
buttons.addEventListener('click', function (event) {
// 버튼을 눌렀을 때 작동하는 함수입니다.
const target = event.target;
// 클릭된 HTML 엘리먼트의 정보가 저장되어 있습니다.
const action = target.classList[0];
// 클릭된 HTML 엘리먼트에 클레스 정보를 가져옵니다.
const buttonContent = target.textContent;
// 클릭된 HTML 엘리먼트의 텍스트 정보를 가져옵니다.
// ! 위 코드(Line 19 - 21)는 수정하지 마세요.
if (target.matches('button')) {
// TODO : 계산기가 작동할 수 있도록 아래 코드를 수정하세요.
//작성되어 있는 조건문과 console.log를 활용하시면 쉽게 문제를 풀 수 있습니다.
// 클릭된 HTML 엘리먼트가 button이면
if (action === 'number') {
// 그리고 버튼의 클레스가 number이면
// 아래 코드가 작동됩니다.
console.log('숫자 ' + buttonContent + ' 버튼');
previousKey = 'number'
if(firstOperend.textContent === '0'){
firstOperend.textContent = buttonContent
}
else if(firstOperend.textContent !== '0'){
secondOperend.textContent = buttonContent
}
}
if (action === 'operator') {
console.log('연산자 ' + buttonContent + ' 버튼');
operator.textContent = buttonContent
previousKey = 'operator'
}
if (action === 'decimal') {
console.log('소수점 버튼');
previousKey = 'decimal'
}
if (action === 'clear') {
console.log('초기화 버튼');
firstOperend.textContent = '0'
operator.textContent = '+'
secondOperend.textContent = '0'
calculatedResult.textContent = '0'
previousKey = 'clear'
}
if (action === 'calculate') {
console.log('계산 버튼');
calculatedResult.textContent =
//calculate(firstOperend.textContent, operator.textContent, secondOperend.textContent)
previousKey = 'calculate'
}
}
});
//Advanced Challenge test, Nightmare test
const display = document.querySelector('.calculator__display--for-advanced');
// calculator__display 엘리먼트와, 그 자식 엘리먼트의 정보를 모두 담고 있습니다.
let firstNum, operatorForAdvanced, previousKey, previousNum;
buttons.addEventListener('click', function (event) {
// 버튼을 눌렀을 때 작동하는 함수입니다.
const target = event.target; // 클릭된 HTML 엘리먼트의 정보가 저장되어 있습니다.
const action = target.classList[0]; // 클릭된 HTML 엘리먼트에 클레스 정보를 가져옵니다.
const buttonContent = target.textContent; // 클릭된 HTML 엘리먼트의 텍스트 정보를 가져옵니다.
// ! 위 코드는 수정하지 마세요.
// ! 여기서부터 Advanced Challenge & Nightmare 과제룰 풀어주세요.
if (target.matches('button')) {
if (action === 'number') {
console.log('누른 숫자 ' + buttonContent + ' 버튼');
if(display.textContent === '0' && operatorForAdvanced === undefined){
//초기화면(디스플레이 숫자가 0)이고, 연산자가 눌리지 않았을 때
display.textContent = buttonContent
firstNum = display.textContent
//처음 누른 버튼을 디스플레이에 띄우고, firstNum에 저장
}else if(display.textContent !== '0' && operatorForAdvanced === undefined){
//아무 숫자를 누르고, 연산자가 눌리지 않았을 때
display.textContent += buttonContent
firstNum = display.textContent
//디스플레이에는 누르는 버튼을 계속 이어 붙이고, firstNum에 해당 숫자를 저장
}else if(display.textContent !== '0' && operatorForAdvanced !== undefined){
//아무 숫자를 누르고, 연산자를 눌렀을 때
if(previousKey === 'operator'){
//1. 직전 누른 버튼이 연산자인 경우
display.textContent = buttonContent
previousNum = display.textContent
previousKey = 'number'
//처음 누른 버튼을 디스플레이에 띄우고, previousNum에 저장.
//previousKey는 number로 변경
} else if(previousKey !== 'operator' && previousKey !== 'demical'){
//2. 직전 누른 버튼이 연산자나 소수점이 아닌 경우
display.textContent += buttonContent
previousNum = display.textContent
//디스플레이에는 누르는 버튼을 계속 이어 붙이고, previousKey에 해당 숫자를 저장
}
}
}
if (action === 'operator') {
if(previousKey === 'number' && previousNum !== undefined){
//연산을 한 후 계산 버튼이 아니라 다른 연산자를 누르면
firstNum = calculate(firstNum, operatorForAdvanced, previousNum)
//해당 연산 결과에 이어서 연산
}
operatorForAdvanced = buttonContent
//누른 연산자 버튼을 operatorForAdvanced 변수에 저장
previousKey = 'operator'
//previousKey를 operator로 변경 => number 버튼에서 직전 누른 버튼을 판단할 때 사용
}
if (action === 'decimal') {
if(previousKey === 'operator'){
//직전에 누른 버튼이 연산자일 때
display.textContent = '0.'
previousNum = display.textContent
previousKey = 'decimal'
//정수 자리 숫자가 없으므로 디스플레이와 previousNum에 0.을 추가하고
//previousKey에 decimal을 저장
} else if(previousKey === 'number'){
//1. 직전에 누른 버튼이 연산자가 아닐 때
display.textContent += buttonContent
previousKey = 'number'
//디스플레이에는 누르는 버튼을 계속 이어 붙임
} else if(previousKey === 'decimal'){
//2. 직전 누른 버튼이 소수점일때
display.textContent += ''
//아무것도 추가하지 않음
} else {
//3. 직전 누른 버튼이 연산자, 소수점 아무것도 아닐 때(숫자일 때)
display.textContent += buttonContent
previousKey = 'decimal'
//소수점을 추가
}
}
if (action === 'clear') {
display.textContent = '0'
firstNum = undefined
previousNum = undefined
operatorForAdvanced = undefined
previousKey = undefined
//디스플레이와 모든 변수를 초기화
}
if (action === 'calculate') {
if(operatorForAdvanced !== undefined &&
previousKey === 'number' || previousKey === 'decimal'){
//연산자를 누른 상태에서 직전에 누른 버튼이 숫자나 소수점일 때
display.textContent = calculate(firstNum, operatorForAdvanced, previousNum)
firstNum = calculate(firstNum, operatorForAdvanced, previousNum)
//계산 결과를 내보내고, 계산 결과를 firstNum 자리에 넣음
} else if(operatorForAdvanced !== undefined && previousKey === 'operator'){
//연산자를 누른 상태에서 직전에 누른 버튼이 연산자일 때
previousNum = firstNum
display.textContent = calculate(firstNum, operatorForAdvanced, previousNum)
//previousNum이 없으므로 previousNum 자리에 firstNum을 넣어 계산
}
}
console.log('firstNum = ' + firstNum)
console.log('operatorForAdvanced = ' + operatorForAdvanced)
console.log('previousNum = ' + previousNum)
console.log('previousKey = ' + previousKey)
console.log("===============================================")
}
});
위는 Bare Minimum test의 전체 코드
아래는 Advanced, Nightmare test의 전체 코드이다.
'Frontend Study' 카테고리의 다른 글
[FE Bootcamp] 11일차_배열 (0) | 2023.02.28 |
---|---|
[FE Bootcamp] 10일차_CLI와 Linux 기초 (0) | 2023.02.27 |
[FE Bootcamp] 8일차_조건문, 반복문 문제풀이 (0) | 2023.02.22 |
[FE Bootcamp] 7일차_반복문, 조건문, 함수 (0) | 2023.02.21 |
[FE Bootcamp] 6일차_자바스크립트 기초 (0) | 2023.02.20 |