본문 바로가기
기타 이야기/공부 이야기

공부 과정 기록06_JS 활용 페이지 만들어보기(01)

by chan00 2022. 8. 17.

저번에 w3school에서 JavaScript 기본 문서 내용들까지 봤었습니다.

생활코딩, w3school로 어느 정도 문법을 알고 있다고 판단하여 JavaScript 공부용을 위한 예제 페이지를

2개 만들었습니다.

그 중에 첫 번째 페이지를 소개해 드리겠습니다.

 

 

처음 페이지를 열면 위와 같이 계산기 모양으로 배치되어 있는 버튼들이 나옵니다.

(보다싶이 CSS 디자인도 추가한 상태입니다.)

 

JavaScript로 어떻게 계산기가 동작하는지 알아보기 전, HTML과 CSS 코드를 보고 구성과 디자인을 어떻게 하였는지

보겠습니다.

 

 

<!DOCTYPE html>
<html lang="en" dir="ltr">
  <head>
    <meta charset="utf-8">
    <title>계산기 구현</title>
    <link rel="stylesheet" href="./guiCalcCss.css">
  </head>
  <body>
    <div class="container">
      <input type="text" id="ipnum" placeholder="여기에 계산식이 입력됩니다.">
      <button type="button" name="button" onclick="inputText(7)" id="seven">7</button>
      <button type="button" name="button" onclick="inputText(8)" id="eight">8</button>
      <button type="button" name="button" onclick="inputText(9)" id="nine">9</button>
      <button type="button" name="button" onclick="inputOper(' + ')" id="add">+</button>
      <button type="button" name="button" onclick="inputText(4)" id="four">4</button>
      <button type="button" name="button" onclick="inputText(5)" id="five">5</button>
      <button type="button" name="button" onclick="inputText(6)" id="six">6</button>
      <button type="button" name="button" onclick="inputOper(' - ')" id="sub">-</button>
      <button type="button" name="button" onclick="inputText(1)" id="one">1</button>
      <button type="button" name="button" onclick="inputText(2)" id="two">2</button>
      <button type="button" name="button" onclick="inputText(3)" id="three">3</button>
      <button type="button" name="button" onclick="inputOper(' * ')" id="multiple">*</button>
      <button type="button" name="button" onclick="subText()" id="subonetext">X</button>
      <button type="button" name="button" onclick="inputText(0)" id="zero">0</button>
      <button type="button" name="button" onclick="inputText('.')" id="decimal">.</button>
      <button type="button" name="button" onclick="inputOper(' / ')" id="divn">/</button>
      <button type="button" name="button" onclick="initText()" id="initbtn">C</button>
      <button type="button" name="button" onclick="inputOper(' % ')" id="remainbtn">%</button>
      <button type="button" name="button" onclick="calcNum()" id="result">=</button>
    </div>

    <script src="./guiCalcJs.js"></script>
  </body>
</html>

HTML 코드입니다.

 

구성을 보면 div container 태그 안에 button 태그들이 쭉 배치되어 있는데, onclick 속성은 JavaScript 쪽에서 사용하니

나중에 보고, 여기서는 버튼의 배치와 id값을 잘 보고 넘어가면 됩니다.

 

 

* {
  box-sizing: border-box;
}

.container {
  background-color: gray;
  border: 1px solid black;
  width: 450px;
  height: 500px;
  font-size: 0.5rem;
  display: grid;
  grid-template-areas:
  "inputtext inputtext inputtext inputtext"
  "sevennum eightnum ninenum addoper"
  "fournum fivenum sixnum suboper"
  "onenum twonum threenum multioper"
  "subtext zeronum decimaltext divoper"
  "initbtn resulttext resulttext remainoper";
}

.container input {
  grid-area: inputtext;
  font-size: 1rem;
}

.container * {
  border: 1px solid black;
  border-radius: 10px;
  background-color: white;
  margin: 3px;
}

.container button {
  box-shadow : 3px 3px 3px black;
  transition-duration: 0.3s;
}

.container button:active {
  margin-left: 5px;
  margin-top: 5px;
  box-shadow: none;
}

.container #one {grid-area: onenum;}
.container #two {grid-area: twonum;}
.container #three {grid-area: threenum;}
.container #four {grid-area: fournum;}
.container #five {grid-area: fivenum;}
.container #six {grid-area: sixnum;}
.container #seven {grid-area: sevennum;}
.container #eight {grid-area: eightnum;}
.container #nine {grid-area: ninenum;}
.container #zero {grid-area: zeronum;}
.container #add {grid-area: addoper;}
.container #sub {grid-area: suboper;}
.container #multiple {grid-area: multioper;}
.container #divn {grid-area: divoper;}
.container #subonetext {grid-area: subtext;}
.container #decimal {grid-area: decimaltext;}
.container #result {grid-area: resulttext;}
.container #initbtn {grid-area: initbtn;}
.container #remainbtn {grid-area: remainoper;}

css를 보면 grid-template-areas라는 속성이 있습니다.

이 속성이 페이지에서 봤을 때 계산기 버튼을 배치되게 하는 역할을 하는데, HTML 코드에서 div container 안의

모든 태그들에게 id 값을 줬었습니다.

 

그리고 css 코드 아래쪽을 보면 해당 id 값을 사용하여 모든 태그들에게 grid-area라는 속성값으로 이름을 붙여 주는

것을 볼 수 있습니다.

그 이름들이 어떤 HTML 버튼인지 기억한 상태로 grid-template-areas 속성값을 본다면 어떤 속성인지 조금

감이 오실 수 있을 것입니다.

 

grid-template-areas 값을 보면 가로 4줄에 세로 6줄로 나눠져 있고, 어떤 위치는 중복되는 값들이 적혀 있습니다.

이 배치 그대로 페이지에서 보이는 계산기의 버튼 배치와 비교해 본다면, grid-area로 버튼들에게 지어준 이름으로

grid-template-areas에 이름을 사용하여 원하는 대로 계산기 모양으로 배치했다는 것을 알 수 있습니다.

 

https://studiomeal.com/archives/533

 

이번에야말로 CSS Grid를 익혀보자

이 포스트에는 실제 코드가 적용된 부분들이 있으므로, 해당 기능을 잘 지원하는 최신 웹 브라우저로 보시는게 좋습니다. (대충 인터넷 익스플로러로만 안보면 된다는 이야기) 이 튜토리얼은 “

studiomeal.com

 

(저는 위 페이지의 글을 보고 grid에 대해 감을 잡았었습니다.)

 

사실 이 페이지의 javascript 코드가 좀 길기 때문에 html과 css의 설명을 좀 빨리 뛰어넘은 감이 있는데,

이제 javascript의 코드를 보겠습니다.

 

 

const ipnumplace = document.getElementById("ipnum");

// ***기본 기능 함수들***

//input 영역의 값을 반환(input.value)
function getIpnumValue() {
  return ipnumplace.value;
}

//input 영역의 문자열을 ' ' 문자를 기준으로 나눠 배열로 바뀐 값을 반환
function getIpnumArr() {
  const ipstr = getIpnumValue();
  return ipstr.split(' ');
}

// ***기본 기능 함수들***

// ***button onclick 함수들***

//숫자 혹은 소수점(.) 버튼을 눌렀을 때 input 영역에 텍스트를 추가하기 위한 함수
function inputText(ipn) {
  const iparr = getIpnumArr();
  let iplastarr = iparr[iparr.length - 1];
  let noexisdec = true;

  //매개변수가 소수점이고, 마지막으로 입력된 값이 연산자가 아닐 때(소수점이 추가될 수 있는 상황을 가정한다.)
  if((ipn === '.') && !(iplastarr === '+' || iplastarr === '-' || iplastarr === '*' || iplastarr === '/' ||
  iplastarr === '%')) {
    //for문을 돌며 현재 입력되어 있는 숫자에 이미 소수점이 입력되었는지 확인한다.
    for(let i = 0; i < iplastarr.length; i++) {
      if(iplastarr[i] === '.') {
        noexisdec = false;
      }
    }
    //noexisdec 변수값이 true라면 현재 입력된 숫자에 소수점이 없다는 뜻이니 소수점을 추가해도 된다.
    if(noexisdec && iplastarr.length !== 0) {
      ipnumplace.value += ipn;
    }
  } else {
    ipnumplace.value += ipn;
  }
}

//연산자 버튼을 눌렀을 때 연산자 텍스트를 input 영역에 추가하는 함수
function inputOper(ipoper) {
  const ipstr = getIpnumValue();
  const iparr = getIpnumArr();
  const iplastarr = iparr[iparr.length - 1];

  if(ipstr[ipstr.length - 1] !== ' ' && iplastarr[iplastarr.length - 1] !== '.') {
    ipnumplace.value += ipoper;
  }
}

//지우기 버튼(X)을 눌렀을 때 실행되는 함수
function subText() {
  const ipstr = getIpnumValue();

  if((ipstr[ipstr.length - 1] >= '0' && ipstr[ipstr.length - 1] <= '9') || (ipstr[ipstr.length - 1] === '.')) {
    ipnumplace.value = ipstr.slice(0, ipstr.length - 1);
  } else {
    ipnumplace.value = ipstr.slice(0, ipstr.length - 3);
  }
}

//input 영역에 입력되어 있는 값을 초기화하는 함수(C 버튼)
function initText() {
  ipnumplace.value = '';
}

//계산하기 버튼(=)을 눌렀을 때 호출되는 함수로, 지금까지 입력한 값을 계산한다.
function calcNum() {
  const iparr = getIpnumArr();
  let result = Number(iparr[0]);
  const resinp = document.getElementById("ipnum");

  //계산하기 버튼(=)을 누르기 전 마지막 값이 연산자(+, -, *, /, %)로 끝나면 정상적인 계산이 되지 않기 때문에,
  //그런 상황을 막기 위한 if 조건문이다.
  if(iparr[iparr.length - 1] !== '') {
    for(let i = 0; i < iparr.length; i++) {
      switch(iparr[i]) {
        case '+':
          result += Number(iparr[i + 1]);
          break;
        case '-':
          result -= Number(iparr[i + 1]);
          break;
        case '*':
          result *= Number(iparr[i + 1]);
          break;
        case '/':
          result /= Number(iparr[i + 1]);
          break;
        case '%':
          result %= Number(iparr[i + 1]);
          break;
      }
    }
    resinp.value = result;
  }
}

// ***button onclick 함수들***

전체 코드입니다.

(참고로 설명할 때 어느 정도 JavaScript의 기본 문법을 알고 계신다 생각하고, 어떤 구조로 계산기가 돌아가게 했는지

설명 드리겠습니다.)

 

함수 단위로 끊어서 설명 드리겠습니다.

 

const ipnumplace = document.getElementById("ipnum");

// ***기본 기능 함수들***

//input 영역의 값을 반환(input.value)
function getIpnumValue() {
  return ipnumplace.value;
}

//input 영역의 문자열을 ' ' 문자를 기준으로 나눠 배열로 바뀐 값을 반환
function getIpnumArr() {
  const ipstr = getIpnumValue();
  return ipstr.split(' ');
}

// ***기본 기능 함수들***

아래의 버튼 이벤트 함수들을 보기 전, 변수 값과 그 외의 함수들입니다.

 

변수 ipnumplace라는 변수는 getElementById로 버튼을 눌렀을 때 계산식이 입력될 input 태그 요소를 가져와 담을

변수입니다.

그렇기 때문에 값이 변하지 않도록 const로 선언을 하였습니다.

 

그 밑의 getIpnumValue 함수는 단순히 input 태그에 현재 어떤 값이 들었는지(value) 반환하는 함수이고,

getIpnumArr 함수는 input 태그에 계산식이 입력되어 있다면 공백을 기준으로 숫자값과 연산자가 구분되어

입력되어 있을 것이기에 그 값들을 배열로 나눠 반환하기 위해 split(' ') 함수를 사용한 함수입니다.

 

 

//숫자 혹은 소수점(.) 버튼을 눌렀을 때 input 영역에 텍스트를 추가하기 위한 함수
function inputText(ipn) {
  const iparr = getIpnumArr();
  let iplastarr = iparr[iparr.length - 1];
  let noexisdec = true;

  //매개변수가 소수점이고, 마지막으로 입력된 값이 연산자가 아닐 때(소수점이 추가될 수 있는 상황을 가정한다.)
  if((ipn === '.') && !(iplastarr === '+' || iplastarr === '-' || iplastarr === '*' || iplastarr === '/' ||
  iplastarr === '%')) {
    //for문을 돌며 현재 입력되어 있는 숫자에 이미 소수점이 입력되었는지 확인한다.
    for(let i = 0; i < iplastarr.length; i++) {
      if(iplastarr[i] === '.') {
        noexisdec = false;
      }
    }
    //noexisdec 변수값이 true라면 현재 입력된 숫자에 소수점이 없다는 뜻이니 소수점을 추가해도 된다.
    if(noexisdec && iplastarr.length !== 0) {
      ipnumplace.value += ipn;
    }
  } else {
    ipnumplace.value += ipn;
  }
}

이 함수는 계산기 버튼 중 숫자 혹은 소수점 버튼을 눌렀을 때 호출되는 이벤트 함수입니다.

 

변수부터 설명드리면 iparr 변수를 선언하여 위에서 설명드렸던 getIpnumArr 함수로 input 태그 안의 값들을 배열 형식으로

받아옵니다.

그리고 현재 소수점을 입력해도 되는 상황인지 판단하기 위해 iplastarr 이라는 변수를 선언하여 input 값의 마지막 요소를

넣어 줍니다.

noexisdec 변수는 boolean 변수로 현재 소수점이 입력될 수 있는 숫자인지, 아니면 숫자에 이미 소수점이 입력되어

있는지 판단하는 역할의 변수입니다.

(예를 들어 3456.234이라는 숫자는 소수점이 이미 찍혀 있어 소수점 버튼을 눌러도 소수점이 추가되면 안 됩니다.)

 

그리고 아래 if~else 조건식은 현재 입력한 버튼이 숫자 버튼인지 소수점 버튼인지(ipn 매개변수로 판단) 검사하고,

소수점일 경우에는 && 연산자로 현재 input 영역에 마지막으로 입력된 값이 연산자인지 아닌지를 검사합니다.

(숫자 시작을 .345 이렇게 시작하게 하고 싶지 않기 때문.)

 

이 조건을 모두 만족하였다면 소수점 버튼을 누른 상태이고, 마지막 값이 연산자가 아닌 숫자라는 것까지 알았으니

위에서 설명했던 noexisdec 변수를 활용해 소수점이 찍힌 숫자인지 아닌지 검사하기 위해 for 반복문을 돌립니다.

 

그렇게 반복문을 돌린 결과 noexisdec이 false로 바뀌지 않고 여전히 true라면 소수점이 찍혀도 되는 상황이라는

것으로 ipnumplace 변수의 value 값에 소수점(ipn)을 더해 줍니다.

(ipnumplace는 getElementById로 input 태그를 가져 온 상태)

 

그리고 가장 위쪽의 if 조건식을 만족하지 않는 else 문은 숫자 버튼을 누른 상황으로, input 영역에 해당

숫자 값을 추가해 줍니다.

 

 

//연산자 버튼을 눌렀을 때 연산자 텍스트를 input 영역에 추가하는 함수
function inputOper(ipoper) {
  const ipstr = getIpnumValue();
  const iparr = getIpnumArr();
  const iplastarr = iparr[iparr.length - 1];

  if(ipstr[ipstr.length - 1] !== ' ' && iplastarr[iplastarr.length - 1] !== '.') {
    ipnumplace.value += ipoper;
  }
}

이 함수는 연산자 버튼에 대한 이벤트 함수입니다.

 

이 함수에서의 조건식은 배열의 이전 값이 공백이거나(연산자를 연속해서 입력하는 상황 방지),

이전 값이 소수점이라면(0. + 4. * 2. 이런 식의 계산식을 방지하기 위해) 조건식을 만족하지 않고 조건식을

만족할 때만 연산자를 input 영역에 추가하는 함수입니다.

 

 

//지우기 버튼(X)을 눌렀을 때 실행되는 함수
function subText() {
  const ipstr = getIpnumValue();

  if((ipstr[ipstr.length - 1] >= '0' && ipstr[ipstr.length - 1] <= '9') || (ipstr[ipstr.length - 1] === '.')) {
    ipnumplace.value = ipstr.slice(0, ipstr.length - 1);
  } else {
    ipnumplace.value = ipstr.slice(0, ipstr.length - 3);
  }
}

위에서도 말했듯이 숫자와 연산자 사이에는 공백으로 구분되게 입력 값을 넣은 상태입니다.

이 함수는 backspace 역할의 버튼 이벤트 함수로, if~else 조건식은 현재 input의 마지막 값이 숫자 또는 소수점이라면

한 칸만 지우게 하고 아니라면(연산자인 상황) 연산자는 앞뒤로 공백이 있으니 3칸을 지우게 하는 기능의 코드입니다.

 

 

//input 영역에 입력되어 있는 값을 초기화하는 함수(C 버튼)
function initText() {
  ipnumplace.value = '';
}

이건 계산기에도 있는 모두 지우는 버튼으로(C 버튼), 말 그대로 input 영역의 값(value)을 '' 값으로 넣어 줌으로써

모든 값을 지우게 했습니다.

 

 

//계산하기 버튼(=)을 눌렀을 때 호출되는 함수로, 지금까지 입력한 값을 계산한다.
function calcNum() {
  const iparr = getIpnumArr();
  let result = Number(iparr[0]);
  const resinp = document.getElementById("ipnum");

  //계산하기 버튼(=)을 누르기 전 마지막 값이 연산자(+, -, *, /, %)로 끝나면 정상적인 계산이 되지 않기 때문에,
  //그런 상황을 막기 위한 if 조건문이다.
  if(iparr[iparr.length - 1] !== '') {
    for(let i = 0; i < iparr.length; i++) {
      switch(iparr[i]) {
        case '+':
          result += Number(iparr[i + 1]);
          break;
        case '-':
          result -= Number(iparr[i + 1]);
          break;
        case '*':
          result *= Number(iparr[i + 1]);
          break;
        case '/':
          result /= Number(iparr[i + 1]);
          break;
        case '%':
          result %= Number(iparr[i + 1]);
          break;
      }
    }
    resinp.value = result;
  }
}

이 함수는 최종 계산 버튼을 눌렀을 때 호출되는 이벤트 함수로, 계산하는 방식은 결과 변수에 처음 숫자값을 넣고

뒤의 연산자에 따라 결과값 변수를 더하거나, 빼거나 해서 최종 결과값만을 input 영역에 표시하도록 하는 기능입니다.

 

아래의 if 조건식은 input 영역의 값이 계산 버튼을 눌렀을 때 숫자로 끝나 있어야 정상적으로 계산을 할 수 있기 때문에

그 외의 상황을 막고자 조건식을 쓴 것으로, 해당 조건을 만족했을 때 input 배열값을 for 반복문으로 돌며

switch case문을 활용해 연산자 상황에 따라 결과값을 연산하도록 했습니다.

 

그리고 마지막에 input 영역에 결과값 변수(result)를 = 연산자로 넣어 줌으로써 계산 결과값만이 input 영역에

표시되도록 한 것입니다.

 

 

 

이렇게 JavaScript를 사용하여 계산기 모양을 흉내내 봤습니다.

저도 모르는 함수(slice, split 사용법 등....)들을 구글링 해 가면서 만들었기 때문에 설명에 미숙한 부분이 있었을 수

있습니다.

그래도 위의 설명들이 여러분의 공부에 조금이나마 도움이 되었길 바라며 글을 마치겠습니다.

댓글