제목의 글을 설명하기에 앞서 정보전달의 목적도 있지만,
제가 잊지않기 위해서 공부 및 정리하며 쓰는 글이라는 사실을 미리 고지합니다.
혹시라도 오입력된 정보가 있다면, 댓글 남겨주세요!
오늘의 Vue.js 실습 목표는 "계산기 만들어보기!" 입니다.
계산기 기능을 구현하기 위해서는 뷰 인스턴스 상태(Vue Instance State) 개념을 필수적으로 공부하고 구현해야 합니다.본 게시글에서 다루는 사항은 계산기를 구현하는 과정입니다.
계산기 구현하기
본 실습을 진행하면 기본적인 HTML과 CSS, 나아가 뷰의 인스턴스 상태(Vue Instance State) 중에서도 기본적인 data, methods에 대해 공부해볼 수 있습니다.
사전 준비
아래 사항에 대해서 사전 준비가 완료되지 않으신 분들은 아래 링크를 참조하여 사전 준비를 진행합니다.
- Vue 프로젝트 생성 (참고: https://logs-jejustone.tistory.com/5)
혹시라도 아래 개념이 잘 기억나지 않으시는 분들은 관련 링크를 참조하시기 바랍니다.
- 뷰 인스턴스 상태(Vue Instance State): https://logs-jejustone.tistory.com/27
- 뷰 라우터(Vue Router) - 네스티드 라우터: https://logs-jejustone.tistory.com/20
설계/기획
다양한 계산기 종류가 있으나 저의 경우 가계부를 작성할 때 계산기를 많이 이용합니다.
그래서 주로 이용하는 계산기의 동작 방식을 착안하여 개발할 계산기를 기획하였습니다.
Layout

기능
- Clear 기능은 'Backspace(←)', 'Clear Entry(CE)', 'Clear All(AC)'로 총 3가지를 제공합니다.
- ←: Backspace 기능
- 입력 중인 필드에서 가장 마지막에 입력한 글자 1개를 삭제합니다.
- 지울 글자가 없는 경우 입력 중인 필드에는 '0'으로 표시합니다.
- CE: Clear Entry의 약자로 입력 중인 필드의 값 전체를 삭제합니다.
- AC: Clear All의 약자로 입력 중인 필드와 입력 결과가 표시되는 필드 값 전체를 삭제합니다.
- ←: Backspace 기능
- 연산 결과는 '=' 버튼이 클릭된 경우에만 진행합니다.
- 숫자 버튼은 0~9까지 총 10개와 더불어 돈과 관련된 입력이 편리하도록 000으로 총 11개를 제공합니다.
- 0, 000: 입력 중인 필드에서 1~9까지의 숫자가 입력되지 않은 경우, 버튼을 클릭해도 '0'으로 표시합니다.
- 연산 기호는 곱셈(*), 나눗셈(/), 뺄셈(-), 덧셈(+)으로 총 4가지를 제공합니다.
- 소숫점 기호로 '.'을 제공하며, 입력 중인 필드의 마지막 숫자에 대해서 소숫점을 적용합니다.
결과 이미지
구현하기에 앞서 본 글을 따라하시면 아래와 같은 계산기를 확인하실 수 있습니다.


소스 코드
src\views\Practice\PracticeCalculator.vue
<template>

template 영역은 크게 입력 필드와 입력된 결과가 표시되는 필드로 2가지 영역으로 구분되었습니다.
입력된 결과가 표시되는 필드는 expgrp로 명명하고 영역을 분리할 수 있도록 <div> 태그로 선언하였습니다.
<div id="expgrp">
<div id="numexp_result">
{{ strResult }}
</div>
<div id="numexp_input">
{{ strInput }}
</div>
</div>
입력 결과가 표시되는 필드는 2개 세부 영역으로 구분되며, 두 개 영역은 결과 표시 필드와 입력 중인 필드입니다.
<div id="btngrp">
<div class="btnrow">
<button v-on:click="clickBackspaceBtn">←</button>
<button v-on:click="clickClearBtn('CE')">CE</button>
<button v-on:click="clickClearBtn('AC')">AC</button>
<button v-on:click="clickCalculateBtn('*')">X</button>
</div>
<div class="btnrow">
<button v-on:click="clickNumberBtn('7')">7</button>
<button v-on:click="clickNumberBtn('8')">8</button>
<button v-on:click="clickNumberBtn('9')">9</button>
<button v-on:click="clickCalculateBtn('/')">/</button>
</div>
<div class="btnrow">
<button v-on:click="clickNumberBtn('4')">4</button>
<button v-on:click="clickNumberBtn('5')">5</button>
<button v-on:click="clickNumberBtn('6')">6</button>
<button v-on:click="clickCalculateBtn('-')">-</button>
</div>
<div class="btnrow">
<button v-on:click="clickNumberBtn('1')">1</button>
<button v-on:click="clickNumberBtn('2')">2</button>
<button v-on:click="clickNumberBtn('3')">3</button>
<button v-on:click="clickCalculateBtn('+')">+</button>
</div>
<div class="btnrow">
<button v-on:click="clickNumberBtn('000')">000</button>
<button v-on:click="clickNumberBtn('0')">0</button>
<button v-on:click="clickCalculateBtn('.')">.</button>
<button v-on:click="clickCalculateBtn('=')">=</button>
</div>
</div>
입력 필드는 btngrp로 명명하고 영역을 분리할 수 있도록 역시나 <div> 태그로 선언하였습니다.
세부적으로 총 4개 행으로 영역이 구분되어 있으며 한 행에 4개의 버튼이 표시되도록 구성하였습니다.
각 버튼은 'v-on:click'라는 뷰 이벤트 디렉티브(Vue Event Directive)로 버튼 클릭시 호출될 이벤트 함수를 정의하였습니다.
총 20개의 버튼은 20개의 함수를 호출하는 것이 아니라는 점이 핵심입니다.
아래는 template 영역의 전체 코드입니다.
<template>
<div id="wrapCalculator">
<div id="calgrp">
<div id="expgrp">
<div id="numexp_result">
{{ strResult }}
</div>
<div id="numexp_input">
{{ strInput }}
</div>
</div>
<div id="btngrp">
<div class="btnrow">
<button v-on:click="clickBackspaceBtn">←</button>
<button v-on:click="clickClearBtn('CE')">CE</button>
<button v-on:click="clickClearBtn('AC')">AC</button>
<button v-on:click="clickCalculateBtn('*')">X</button>
</div>
<div class="btnrow">
<button v-on:click="clickNumberBtn('7')">7</button>
<button v-on:click="clickNumberBtn('8')">8</button>
<button v-on:click="clickNumberBtn('9')">9</button>
<button v-on:click="clickCalculateBtn('/')">/</button>
</div>
<div class="btnrow">
<button v-on:click="clickNumberBtn('4')">4</button>
<button v-on:click="clickNumberBtn('5')">5</button>
<button v-on:click="clickNumberBtn('6')">6</button>
<button v-on:click="clickCalculateBtn('-')">-</button>
</div>
<div class="btnrow">
<button v-on:click="clickNumberBtn('1')">1</button>
<button v-on:click="clickNumberBtn('2')">2</button>
<button v-on:click="clickNumberBtn('3')">3</button>
<button v-on:click="clickCalculateBtn('+')">+</button>
</div>
<div class="btnrow">
<button v-on:click="clickNumberBtn('000')">000</button>
<button v-on:click="clickNumberBtn('0')">0</button>
<button v-on:click="clickCalculateBtn('.')">.</button>
<button v-on:click="clickCalculateBtn('=')">=</button>
</div>
</div>
</div>
</div>
</template>
<script> 영역
script 영역은 계산기에서 사용할 변수 및 함수를 정의하는 영역입니다.
다양한 뷰 인스턴스 상태(Vue Instance State) 중에서도 data와 methods를 사용하였습니다.
data 영역에서는 입력된 결과가 표시되는 필드에서 사용할 3가지 변수가 선언되어 있습니다.
data: function() {
return {
strResult: '0',
strInput: '0',
strLastInput: '0',
};
},
- strResult: 연산 결과에 해당하는 수식을 지니기 위한 변수
- strInput: 연산 결과를 입력하는 수식을 지니기 위한 변수
- strLastInput: 연산 결과를 입력하는 수식의 마지막 입력 글자를 지니는 변수
method 영역에서는 <template>에서 'v-on:click'로 호출될 함수가 정의된 영역으로 data 영역에서 선언된 변수를 이용합니다.
methods: {
printLog: function (btnName) {
console.log('Press the ' + btnName + ' button');
},
// Calculator Button
clickNumberBtn: function (strNum) {
this.printLog(strNum);
this.strLastInput = strNum;
if (this.strInput == '0') {
if (strNum != '000' &&
strNum != '0') {
this.strInput = strNum;
}
}
else {
this.strInput += strNum;
}
},
clickCalculateBtn: function (strSymbol) {
this.printLog(strSymbol);
if (strSymbol == '=') {
// 연산 수식
this.strResult = this.strInput + ' ' + strSymbol;
// 연산 수행 및 결과
const elements = this.strResult.split(' ');
let numResult = parseFloat(elements[0]);
for (let i = 1; i < elements.length; i += 2) {
console.log('numResult:' + numResult);
console.log('elements[i]: ' + elements[i]);
console.log('elements[i+1]: ' + elements[i + 1]);
if (elements[i] == '*') {
numResult *= parseFloat(elements[i + 1]);
}
else if (elements[i] == '/') {
numResult /= parseFloat(elements[i + 1]);
}
else if (elements[i] == '-') {
numResult -= parseFloat(elements[i + 1]);
}
else if (elements[i] == '+') {
numResult += parseFloat(elements[i + 1]);
}
}
console.log('numResult:' + numResult);
this.strInput = numResult.toString();
this.strLastInput = this.strInput;
}
else if (strSymbol == '.') {
this.strInput += strSymbol;
}
else if (this.strLastInput != '*' &&
this.strLastInput != '/' &&
this.strLastInput != '-' &&
this.strLastInput != '+') {
this.strLastInput = strSymbol;
this.strInput += ' ' + strSymbol + ' ';
}
else {
alert('Press number button!!!');
}
},
// Clear Button
clickBackspaceBtn: function () {
this.printLog('Backspace Button');
this.strInput = this.strInput.substring(0, this.strInput.length - 1);
if (this.strInput.length == 0) {
this.strInput = '0';
}
},
clickClearBtn: function(strSymbol) {
this.printLog('Clear ' + strSymbol + ' Button');
this.strInput = '0';
if (strSymbol == 'AC') {
this.strResult = '0';
this.strLastInput = '0';
}
}
}
계산기의 20개 버튼에 대해서 총 5개의 함수를 구현하였습니다. 5개의 함수를 구현할 때는 위 설계/기획 단계에서 언급한 예외 조건들 놓치지 않고 분기처리를 하여 구현합니다.
- printLog: F12(개발자모드)의 콘솔 영역에서 표시될 메세지를 구현하는 함수
- clickNumberBtn: 숫자 버튼과 관련된 로직을 구현하는 함수
- clickCalculateBtn: 연산 기호 버튼과 관련된 로직을 구현하는 함수
- clickBackspaceBtn: 입력 중인 필드에서 마지막으로 입력한 한 글자를 삭제하는 로직을 구현하는 함수
- clickClearBtn: 입력된 결과가 표시되는 필드의 AC와 CE 버튼에 대한 로직을 구현하는 함수
아래는 script 영역의 전체 코드입니다.
<script>
export default {
data: function() {
return {
strResult: '0',
strInput: '0',
strLastInput: '0',
};
},
methods: {
printLog: function (btnName) {
console.log('Press the ' + btnName + ' button');
},
// Calculator Button
clickNumberBtn: function (strNum) {
this.printLog(strNum);
this.strLastInput = strNum;
if (this.strInput == '0') {
if (strNum != '000' &&
strNum != '0') {
this.strInput = strNum;
}
}
else {
this.strInput += strNum;
}
},
clickCalculateBtn: function (strSymbol) {
this.printLog(strSymbol);
if (strSymbol == '=') {
// 연산 수식
this.strResult = this.strInput + ' ' + strSymbol;
// 연산 수행 및 결과
const elements = this.strResult.split(' ');
let numResult = parseFloat(elements[0]);
for (let i = 1; i < elements.length; i += 2) {
console.log('numResult:' + numResult);
console.log('elements[i]: ' + elements[i]);
console.log('elements[i+1]: ' + elements[i + 1]);
if (elements[i] == '*') {
numResult *= parseFloat(elements[i + 1]);
}
else if (elements[i] == '/') {
numResult /= parseFloat(elements[i + 1]);
}
else if (elements[i] == '-') {
numResult -= parseFloat(elements[i + 1]);
}
else if (elements[i] == '+') {
numResult += parseFloat(elements[i + 1]);
}
}
console.log('numResult:' + numResult);
this.strInput = numResult.toString();
this.strLastInput = this.strInput;
}
else if (strSymbol == '.') {
this.strInput += strSymbol;
}
else if (this.strLastInput != '*' &&
this.strLastInput != '/' &&
this.strLastInput != '-' &&
this.strLastInput != '+') {
this.strLastInput = strSymbol;
this.strInput += ' ' + strSymbol + ' ';
}
else {
alert('Press number button!!!');
}
},
// Clear Button
clickBackspaceBtn: function () {
this.printLog('Backspace Button');
this.strInput = this.strInput.substring(0, this.strInput.length - 1);
if (this.strInput.length == 0) {
this.strInput = '0';
}
},
clickClearBtn: function(strSymbol) {
this.printLog('Clear ' + strSymbol + ' Button');
this.strInput = '0';
if (strSymbol == 'AC') {
this.strResult = '0';
this.strLastInput = '0';
}
}
}
}
</script>
<style> 영역
style 영역은 template 영역에서 정의한 Layout에 CSS로 스타일 효과를 주는 영역입니다.
template 영역에서 사용한 id와 class에 대해서 기획/설계에서 정의한 이미지를 참고하여 구현하였습니다.
CSS는 자유롭게 꾸밀 수 있기 때문에 별다른 설명없이 코드만 첨부합니다.
<style scoped>
#wrapCalculator {
padding: 5px;
width: 260px;
}
#wrapCalculator h2 {
padding: 5px;
text-align: center;
background-color: white;
}
#calgrp {
padding: 5px;
background-color: white;
border: 2px dotted black;
}
/* STYLE: Expression group of the Calculator */
#expgrp {
padding: 5px;
background-color: lightcoral;
text-align: right;
color: white;
}
#numexp_result {
margin: 5px 5px 0px 5px;
font-size: x-small;
}
#numexp_input {
margin: 0px 5px 0px 5px;
font-size: x-large;
}
/* STYLE: Button group of the calculator */
.btnrow {
margin: 5px;
border: 2px solid white;
}
#btngrp button {
margin: 5px;
padding: 10px;
background-color: lightcoral;
border-color: white;
text-align: center;
font-size: medium;
width: 45px;
height: 45px;
}
</style>
References
- HTML Tutorial: https://www.w3schools.com/html/default.asp
HTML Tutorial
www.w3schools.com
- CSS Tutorial: https://www.w3schools.com/css/default.asp
CSS Tutorial
www.w3schools.com
- Vue.js 공식 사이트: https://vuejs.org/guide/quick-start.html
Quick Start | Vue.js
vuejs.org

'Programming > Vue' 카테고리의 다른 글
뷰(Vue) 프로젝트 구조(폴더, 디렉토리) - views와 components 차이 (0) | 2022.12.16 |
---|---|
[실습] 뷰(Vue)로 계산기 구현하기 - 웹(WEB)에 계산기 구현하기 (0) | 2022.12.15 |
뷰(Vue)로 웹 어플리케이션의 메뉴/사이드바 라우팅 구현하기 (0) | 2022.12.13 |
[실습] 뷰(Vue)로 웹 어플리케이션 레이아웃(Web layout) 구현하기 (0) | 2022.12.12 |
뷰(Vue) 프로젝트 기본 파일(main.js / index.html / App.vue) 구조 및 동작 흐름도 (Workflow) (0) | 2022.12.08 |
댓글