본문 바로가기

코드스테이츠

2021년 4월 13일 코드스테이츠 DAY-9(계산기)(calculator)#2

반응형
 

2021년 4월 12일 코드스테이츠 DAY-8(CSS + 계산기)

CSS 기초(Chapter) querySelector 과제 제출(Pair) 계산기(Pair) 계산기 : HTML/CSS/JS를 활용하여 Pair와 함께 계산기를 만들어 내는 것. 1. Bare Minimum Requirements(필수 과제)  Step1 - CSS 마음껏 수정하..

bedeveloper.tistory.com

 

 

계산기(Pair)

 

Sprint Review(Zoom)

 

Pair Review(Survey)

 

Sprint Feedback(Survey)

 

 

 


계산기 : 어제(4월 12일) 필수과제인 Bare Minimum Requirements를 완료하였기에, 오늘은 Advanced challenge과 Nightmare를 도전하였다.(Javascript로 기능을 조작) 또한 CSS와 Javascript로 약간의 재미적 요소를 넣어보았다.

 


1. Advanced challenge

Step1 - 숫자를 클릭하여 화면에 입력하기

 

  • 어제의 이상한 계산기와는 달리 일반적인 계산기를 만들어 보는 것이다.
  • 숫자 입력시 catenatioin이 되도록 구현한다. 
  • 연산자(operator)를 눌러도 첫번째 입력한 수는 화면에 그대로 출력된다.
  • textContent를 활용한다.

Step2 - 연산자 버튼을 클릭하여 계산을 준비하기

 

  • 변수 previousKey를 이용하여 이전에 클릭한 버튼이 숫자인지, 연산자인지 구분하여 코드를 작성.
  • 연산자(operator)를 입력 후 두번째 수를 입력하면 두번째 수는 화면에 나타나고 첫번째 수는 변수에 담긴다. 
  • 입력된 연산자는 임의의 변수에 담긴다.

Step3 - Enter로 계산하고, AC로 초기화하기

 

 

  • enter가 클릭되면 calculate함수에 의한 결과값이 계산기의 화면에 출력된다.

 

 


Step1- 숫자를 클릭하여 화면에 입력하기(단, 본인의 코드에는 night mare단계가 포함되어 있다.)

const display = document.querySelector('.calculator__display--for-advanced');
let firstNum, operatorForAdvanced, previousKey, previousNum;

buttons.addEventListener('click', function (event) {

  const target = event.target;
  const action = target.classList[0];
  const buttonContent = target.textContent;
  
  if (target.matches('button')) {
      if (action === 'number') {
      if(operatorForAdvanced !== undefined){ //연산자가 한번 선언되어 있다면
        if(display.textContent === firstNum){ // 화면에 firstNum이 있다면(두번째 숫자의 첫번째가 나올 차례이기 때문에 화면이 다른 수로 채워져야한다.)
          display.textContent = buttonContent; //화면에는 방금 누른 버튼이 출력되어라.
        }else{
          display.textContent = display.textContent + buttonContent; //두번째 수의 첫번째 혹은 여러수가 입력되었을때는 그 뒤에 방금 누른 버튼의 숫자(textContent)가 붙어라.
        }
      }else if(operatorForAdvanced === undefined && display.textContent === '0') { //연산자가 선언되지 않았고 화면에 0이라면(초기상태라면 즉 firstNum이 입력되야한다면)
        //console.log('숫자 ' + buttonContent + ' 버튼');
        display.textContent = buttonContent; // 화면에 나타날 텍스트를 버튼의 텍스트로 변경
      }else if(operatorForAdvanced === undefined && display.textContent !== '0') { //firstNum의 첫번째 혹은 여러수가 입력되었을때는 
        display.textContent = display.textContent + buttonContent; //그 뒤에 방금 누른 버튼의 숫자(textContent)가 붙어라.
      }
    }
  }

Step2  - 연산자 버튼을 클릭하여 계산을 준비하기(단, 본인의 코드에는 night mare단계가 포함되어 있다.)

const display = document.querySelector('.calculator__display--for-advanced');
let firstNum, operatorForAdvanced, previousKey, previousNum;

buttons.addEventListener('click', function (event) {

  const target = event.target;
  const action = target.classList[0];
  const buttonContent = target.textContent;
  
  if (target.matches('button')) {
    if (action === 'operator') { //operator(연산자 버튼이 눌리면)
      operatorForAdvanced = buttonContent; //calculate 버튼(enter)을 누를 시 계산을 위하여 현재 입력한 연산자를 변수에 할당.  
      firstNum = display.textContent; calculate 버튼(enter)을 누를 시 계산을 위하여 현재 화면에 있는 수를 변수에 할당.
      
      //아래 코드는 나이트메어를 위해 치다가 시간이 만료되어 완성하지 못한 코드입니다. 아래에서 추가로 다룰예정
      // if(operatorForAdvanced !== undefined){//두번 혹은 그 이상이라는 거지
      //   previousNum = calculate(result, operatorForAdvanced, display.textContent)//100 + 200에다가 +를 한 번 더 눌렀는 상황
      //   console.log(previousNum);
      // } 
      //위 코드는 나이트메어를 위해 치다가 시간이 만료되어 완성하지 못한 코드입니다. 아래에서 추가로 다룰예정
    
    }
  }

Step3- Enter로 계산하고, AC로 초기화하기(단, 본인의 코드에는 night mare단계가 포함되어 있다.)

const display = document.querySelector('.calculator__display--for-advanced');
let firstNum, operatorForAdvanced, previousKey, previousNum;

buttons.addEventListener('click', function (event) {

  const target = event.target;
  const action = target.classList[0];
  const buttonContent = target.textContent;
  
  if (target.matches('button')) {
    if (action === 'clear') {
      display.textContent = '0';
      //화면을 초기화한다.
      operatorForAdvanced = undefined;
      //연산자가 입력되어 할당된 operatorForAdvanced라는 변수를 undefined로 바꾸어 준다.
      
      //! 적다보니 발견한것은 firstNum을 초기화해줘야하는데...빼먹었다. ^0^ 그런데 advanced challenge의 테스트 케이스를 통과하였다....이상하다..
    }
    if (action === 'calculate') {
      display.textContent = calculate(firstNum, operatorForAdvanced, display.textContent)
    }//입력된 값들을 바탕으로 calculate함수를 실행해주고 결과를 리턴하여 화면으로 출력해준다. 
  }

 

위 코드의 결과는 아래와 같다.

 

 

Step3의 코드의 주석에서 말했듯이 빼먹은 부분이 있었지만 다행히(?)도 advanced는 통과할 수 있었으나....

 

더 큰 산이 뒤에 있었으니.....nightmare.....

 

애초에 nightmare에는 설명이 없었다. 

 

그러나 테스트케이스를 통과하기 위하여 하나하나씩 도전해가기 시작했다.

 

많은 시간을 pair와 조건문에 조건문을 넣고 그안에 또 조건문을 넣는 미친듯한 삽질을 했다. 

 

그 결과 코드는 난잡하였으나 꽤나 많은 부분을 해결했는데 딱 두가지 부분이 막혀 더이상 진행을 못하였고

 

nightmare는 해결을 못한 채 CSS를 부랴부랴 수정하여 제출하였다.

 

reference를 받고 느낀것은 엄청난 감탄이었다. 간결하면서도 정확한 코드!

 

자, 아래 나이트메어 테스트케이스 결과를 보고 부족한 점은 무엇이었으며 어떻게 고치면 될지 알아보자 그리고 해결해보자

 

 

부족한 점

  • calculate 버튼 누르기 전의 값이 무엇이냐에 따라 도출되는 값을 정의하지 못했음.
  • 현재 클릭한 버튼이 어떤종류인지 결정하는 previousKey(이것으로 calculate가 연속으로 입력될 때 그리고 숫자 이후 입력될 때를 나누어서 상세하게 값을 도출해 낼 수 있음), n1 + operator + n2 + (enter할 차례에서) 현재 화면에 떠있는 값을 정의하는 previousNum(이것으로 enter가 한번만 눌러질때, 연속으로 눌러질때를 나누어서 상세하게 값을 도출해 낼 수 있음)을  정의하지 않아서 수없이 많은 중첩 조건문을 만들었어야 했음.
  • 위 clear버튼을 눌렀을 경우에 리셋되야하는 값들이 추가되어야한다.(위 코드에서 말했듯이 firstNum은 물론이고 previousNum과 previousKey도 포함하여....)

 

위 부족한 점들을 고치기 전에 우선 레퍼런스와 레퍼런스에 대한 나의 리뷰를 보자.

 

function calculate(n1, operator, n2) {
  let result = 0;
  if (operator === '+') {
    result = Number(n1) + Number(n2);
  }
  if (operator === '-') {
    result = Number(n1) - Number(n2);
  }
  if (operator === '*') {
    result = Number(n1) * Number(n2);
  }
  if (operator === '/') {
    result = Number(n1) / Number(n2);
  }
  return String(result);
}

const display = document.querySelector('.calculator__display--for-advanced'); // calculator__display 엘리먼트와, 그 자식 엘리먼트의 정보를 모두 담고 있습니다.
let firstNum, operatorForAdvanced, previousKey, previousNum;

buttons.addEventListener('click', function (event) {

  const target = event.target; 
  const action = target.classList[0];
  const buttonContent = target.textContent;
  const buttonContainerArray = buttons.children;

  if (target.matches('button')) {
    for (let i = 0; i < buttonContainerArray.length; i++) {
      const childrenArray = buttonContainerArray[i].children;
      for (let j = 0; j < childrenArray.length; j++) {
        childrenArray[j].classList.remove('isPressed');
      }
    }

    if (action === 'number') {
      if (display.textContent === '0' || previousKey === 'operator' || previousKey === 'calculate') { 
        display.textContent = buttonContent;//직전에 누른 버튼이 연산자 이거나 계산키, 그리고 아무것도 입력되지않은 0일 경우에 지금 누른 숫자의 버튼이 처음 디스플레이에 찍히도록 한다. 
      } else {
        display.textContent = display.textContent + buttonContent; //class가 number에 해당하는 버튼을 눌렀을 때 기존에 숫자가 있는 경우는 그 뒤에 붙여서 concatenation한다.
      }
      previousKey = 'number'; //방금 누른 키가 number이므로 previousKey로 셋팅한다. 
    }

    if (action === 'operator') {
      target.classList.add('isPressed');
      if (firstNum && operatorForAdvanced && previousKey !== 'operator' && previousKey !== 'calculate') {
        //첫번째 누른 숫자(operator 이전에 누른 숫자)와 연산자가 있으면 그리고 연산자를 연속으로 누른게아니라면 또한 결과 계산(enter) 후 연산자를 누른게 아니라면(그러니까 연산자 이후 number를 다시 눌렀다면)
        display.textContent = calculate(firstNum, operatorForAdvanced, display.textContent);
        //operator키를 누를 시 화면에 나타나는 값은 calculate함수에 의해 계산되는 문자열 타입의 리턴 값
      }
      firstNum = display.textContent; //처음 연산자를 누르면 firstNum은 연산자가 입려되기 전 화면에 있던 값이다.

      //operator 이후에 다시 숫자를 넣으면 이전에 계산한 값들이 날아가고 display.textContext가 다시 정의 되므로 그전에 firstNum에 operator버튼을 눌러 계산한 값을 넣어준다.
      //그리하여 다시 숫자 버튼이 눌러진 이후 연산자 버튼을 눌렀을 때 110번째 줄에 firstNum으로 들어가 현재화면의 있는 값과 다시 calculate함수에 들어가 값을 리턴하기를 반복할 것이다.

      operatorForAdvanced = buttonContent; //operatorForAdvanced는 operator버튼을 처음 눌렀을 때 button의 textContent.
      previousKey = 'operator';//방금 누른 키가 operator이므로 previousKey로 셋팅한다. 
    }

    if (action === 'decimal') {
      if (!display.textContent.includes('.') && previousKey !== 'operator') {
        //소숫점은 숫자스트링에 하나만 들어갈 수 있기 때문에 화면에 '.'가 없다면 그리고 number버튼 혹은 calculate(enter)버튼을 눌렀다면(operator를 누른 직후가 아니라면)
        display.textContent = display.textContent + '.';
        //기존의 있던 숫자(이 숫자에는 .이 포함되어있지 않다.)뒤에 '.'을 붙여준다. 
      } else if (previousKey === 'operator') {
        //연산자 버튼을 누른 후 '.'을 찍는다면 
        display.textContent = '0.'; 
        //화면에는 '0.'이 표시된다.
      }
      previousKey = 'decimal';//방금 누른 키가 decimal이므로 previousKey로 셋팅한다. 
    }

    if (action === 'clear') {
      firstNum = undefined; 
      operatorForAdvanced = undefined;
      previousNum = undefined;
      previousKey = 'clear';
      //어떠한 수나 연산자 기타 다른 버튼이 눌렸을 시 정의되어지는 값들을 초기화
      display.textContent = '0';//방금 누른 키가 AC이므로 초기의 상태로 만들어준다. 
    }

    if (action === 'calculate') {
      if (firstNum) { //calculate(enter)버튼을 누른다는 것은 firstNum과 operator를 누른 후 firstNum과 연산해야하는 다른 수가 있다는 것을 가정하기에 firstNum이 있다면을 가정했다.
        if (previousKey === 'calculate') { 
          //enter를 연속으로 누른 경우
          display.textContent = calculate(display.textContent, operatorForAdvanced, previousNum);
          //(n1 + operator + n2의 결과값) + operatorForAdvanced(직전에 계산한 연산자) + n2의 결과를 화면에 나타낸다.
        } else {
          //enter가 아닌 수를 이전에 누른 경우 n1 + operator + n2(숫자) + (enter할 차례)
          previousNum = display.textContent;
          //n2가 previousNum
          display.textContent = calculate(firstNum, operatorForAdvanced, display.textContent);
          //화면에 출력되는 값은 n1 + operator + n2의 결과값이다. 
        }
      }
      previousKey = 'calculate';//방금 누른 키가 calculate이므로 previousKey로 셋팅한다. 
    }
  }
});

아아 얼마나 아름답고 효율적인 코드인가! 

 

하지만 나의 코드가 부족할 지언정 위에 코드를 그대로 뺏기고 싶지는 않다. 물론 reference 코드도 공부 하며 나의 코드에 양분이 될 녀석들을

 

뽑아내기는 할 것이다. 

 

나의 코드를 수정하여 오늘 nightmare를 완성시키기에는 많은 시간이 소요되므로 이번주 주말까지 완성 시켜 수정하도록 하겠다.

 

글을 마치기 전 오늘 제출한 계산기를 보고 고치고 싶은 점을 적어봄으로써 마무리하겠다. 

 

 

계산기(calculator)

고치고 싶은 점

  • 커서와 손이 떨어져있다. (CSS에 transform translate(-50%, -50%)를 추가하겠다.)
  • 커서를 없애고 싶다.
  • 손그림이 화면에서 나갈 때 스크롤창이 생긴다.(overflow : hidden 하자.)
  • 클릭할 때 손 모양이 바뀌게 해보자

나의 코드를 바탕으로 nightmare와 위 고치고 싶은 점을 해결해서 주말에 포스팅하겠다!

반응형