ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Node.js로 사칙연산 계산기를 만들어봐요 - 3편(계산기 완성하기)
    프로그래밍 언어/Node.js(노드) 2018. 11. 11. 18:08


    2편에 이어서 이제 핵심인 계산기 화면과 계산기 로직을 통해 계산기를 완성해보겠습니다!


    팝업창으로 호출한 calculator/calculatorPop url을 받기위해서 calculator 모듈을 만들겠습니다.


    router 폴더 밑에 아래처럼 폴더와 js파일을 만들어주세요.


    calculator.js에 아래의 코드를 작성해주세요.

    var express = require('express')
    var router = express.Router()

    router.get('/calculatorPop', function(req,res){
    res.render('calculator.ejs')
    })

    module.exports = router;


    calculatorPop이란 호출이 들어오면(get으로 받습니다) calculator.ejs로 보냅니다.


    이때 res.render 메서드를 사용합니다.


    마지막에 외부에서도 사용 가능하도록 module.exports 잊지 마시구요!


    ejs를 템플릿 엔진으로 사용한다고 했었던 것 기억나시나요 ? views 폴더를 만들어두면 자동으로 views 아래 ejs파일들을 읽습니다.


    아래의 경로로 calculator.ejs를 만들어주세요.




    calculator.ejs 화면을 먼저 만들어볼게요.

    <!DOCTYPE html>
    <html>
    <head>
    <meta charset="utf-8" />
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css">
    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.1.3/js/bootstrap.min.js"></script>
    <title>계산기</title>

    </head>
    <body>
    <div class="container bg-danger" style="height:100%">
    <div class="form-group">
    <input class="form-control" type="text" id="textBox" style="text-align:right; width:100%"
    placeholder="0" readonly/>
    </div>
    <div class="form-group" style="text-align:center">
    <button class="btn btn-primary" style="width:23%" value="7" onclick="typing('7')">7</button>
    <button class="btn btn-primary" style="width:23%" value="8" onclick="typing('8')">8</button>
    <button class="btn btn-primary" style="width:23%" value="9" onclick="typing('9')">9</button>
    <button class="btn btn-info" style="width:23%" value="+" onclick="typing('+')">+</button>
    </div>
    <div class="form-group" style="text-align:center">
    <button class="btn btn-primary" style="width:23%" value="4" onclick="typing('4')">4</button>
    <button class="btn btn-primary" style="width:23%" value="5" onclick="typing('5')">5</button>
    <button class="btn btn-primary" style="width:23%" value="6" onclick="typing('6')">6</button>
    <button class="btn btn-info" style="width:23%" value="-" onclick="typing('-')">-</button>
    </div>
    <div class="form-group" style="text-align:center">
    <button class="btn btn-primary" style="width:23%" value="1" onclick="typing('1')">1</button>
    <button class="btn btn-primary" style="width:23%" value="2" onclick="typing('2')">2</button>
    <button class="btn btn-primary" style="width:23%" value="3" onclick="typing('3')">3</button>
    <button class="btn btn-info" style="width:23%" value="*" onclick="typing('*')">X</button>
    </div>
    <div class="form-group" style="text-align:center">
    <button class="btn btn-primary" style="width:23%" value="0" onclick="typing('0')">0</button>
    <button class="btn btn-light" style="width:23%" id="clear">C</button>
    <button class="btn btn-warning" style="width:23%" id="calculation" value="=">=</button>
    <button class="btn btn-info" style="width:23%" value="/" onclick="typing('/')">/</button>
    </div>
    </div>
    </body>
    </html>


    부트스트랩을 이용해 화면을 구성했고, 버튼을 눌릴 때마다 typing이라는 함수를 타도록 만들었습니다.


    input 화면은 버튼을 통해서만 계산할 수 있도록 했습니다. 


    main.html에서 계산기 버튼을 눌리면 디자인된 계산기 화면을 볼 수 있습니다.



    이제 계산기에 로직을 추가해보겠습니다.


    <!DOCTYPE html>
    <html>
    <head>
    <meta charset="utf-8" />
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css">
    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.1.3/js/bootstrap.min.js"></script>
    <title>계산기</title>

    </head>
    <body>
    <div class="container bg-danger" style="height:100%">
    <div class="form-group">
    <input class="form-control" type="text" id="textBox" style="text-align:right; width:100%"
    placeholder="0" readonly/>
    </div>
    <div class="form-group" style="text-align:center">
    <button class="btn btn-primary" style="width:23%" value="7" onclick="typing('7')">7</button>
    <button class="btn btn-primary" style="width:23%" value="8" onclick="typing('8')">8</button>
    <button class="btn btn-primary" style="width:23%" value="9" onclick="typing('9')">9</button>
    <button class="btn btn-info" style="width:23%" value="+" onclick="typing('+')">+</button>
    </div>
    <div class="form-group" style="text-align:center">
    <button class="btn btn-primary" style="width:23%" value="4" onclick="typing('4')">4</button>
    <button class="btn btn-primary" style="width:23%" value="5" onclick="typing('5')">5</button>
    <button class="btn btn-primary" style="width:23%" value="6" onclick="typing('6')">6</button>
    <button class="btn btn-info" style="width:23%" value="-" onclick="typing('-')">-</button>
    </div>
    <div class="form-group" style="text-align:center">
    <button class="btn btn-primary" style="width:23%" value="1" onclick="typing('1')">1</button>
    <button class="btn btn-primary" style="width:23%" value="2" onclick="typing('2')">2</button>
    <button class="btn btn-primary" style="width:23%" value="3" onclick="typing('3')">3</button>
    <button class="btn btn-info" style="width:23%" value="*" onclick="typing('*')">X</button>
    </div>
    <div class="form-group" style="text-align:center">
    <button class="btn btn-primary" style="width:23%" value="0" onclick="typing('0')">0</button>
    <button class="btn btn-light" style="width:23%" id="clear">C</button>
    <button class="btn btn-warning" style="width:23%" id="calculation" value="=">=</button>
    <button class="btn btn-info" style="width:23%" value="/" onclick="typing('/')">/</button>
    </div>
    </div>
    <script>
    function typing(inputValue){
    var originalText = $('#textBox').val();
    if(inputValue == '+' || inputValue == '-' || inputValue == '*' || inputValue == '/'){
    // 연속된 기호를 못 붙이기 위함
    var isNumber = parseInt(originalText.charAt(originalText.length-1));
    if(isNaN(isNumber)){
    return;
    }
    }
    $('#textBox').val(originalText+inputValue);
    }
    </script>
    </body>
    </html>


    기존의 inputText에서 값을 가져와서 덧붙여주는 역할을 수행합니다. 연속된 기호를 못 붙이기 위함이라는 뜻이

    12++13이나 14*+12 등이 될 수 없도록 연산 기호들이 연속으로 올 수 없도록 합니다.  그게 아니라면 연속된 텍스트를 입력할 수 있습니다.



    부가적인 기능을 만들어볼게요. 'C'를 눌리면 초기화하고 백스페이스를 눌리면 입력된 값이 지워지도록 해보겠습니다.

    <script>

    $(document).ready(function(){
    $('#clear').on("click", function(){
    $('#textBox').val('');
    })

    $(document).keydown(function(event){
    if(event.keyCode == '8'){
    var originText = $('#textBox').val();
    $('#textBox').val(originText.substring(0, originText.length-1));
    }

    })
    })
    function typing(inputValue){
    var originalText = $('#textBox').val();
    if(inputValue == '+' || inputValue == '-' || inputValue == '*' || inputValue == '/'){
    // 연속된 기호를 못 붙이기 위함
    var isNumber = parseInt(originalText.charAt(originalText.length-1));
    if(isNaN(isNumber)){
    return;
    }
    }
    $('#textBox').val(originalText+inputValue);
    }
    </script>


    jQuery를 사용하니까 훨씬 이벤트 만들기가 편한데요. id가 clear라는 버튼을 눌리면 textBox의 값이 ''로 초기화됩니다.


    $(document).keydown은 키패드 이벤트인데요. 백스페이스는 KeyCode가 8입니다. 그래서 백스페이스를 눌렀을 경우,


    텍스트박스의 값이 뒷자리부터 하나씩 지워집니다.



    이제 '='을 눌렀을 때 Node 서버에서 입력된 값들을 계산 할거에요. 계산 결과는 비동기로 보여주는게 더 깔끔할 것 같아서 ajax로 처리했습니다.


    $(document).ready(function(){

    $('#calculation').on("click", function(){
    var parameter = $('#textBox').val();

    $.ajax({
    type: 'POST',
    url: '/calculator/calculatorPop',
    data: {inputString : parameter},
    success: function(result){
    if(result.calculationOutput == '0'){
    $('#textBox').val('');
    }else{
    $('#textBox').val(result.calculationOutput);
    }
    },
    error: function(err){
    console.log('ajax error')
    }
    })
    })

    $('#clear').on("click", function(){
    $('#textBox').val('');
    })

    $(document).keydown(function(event){
    if(event.keyCode == '8'){
    var originText = $('#textBox').val();
    $('#textBox').val(originText.substring(0, originText.length-1));
    }

    })


    ajax로 inputString이라는 key값으로 textBox값을 담아 파라미터로 던집니다. url은 같은 url로 던지지만, http 메서드가 달라요.


    POST 방식으로 값을 던집니다. 값을 가공할거니까요?! 서버에서 calculationOutput으로 결과값을 담아 던질건데, 그 값이 0이라면 초기화 시켜버리고, 다른 계산된 값이라면 결과 값을 textBox에 값을 넣어주는 거죠.




    자 그럼 server에서 해당 url을 만들고 파라미터를 받고 결과 값을 리턴해줄 수 있는 메서드를 만들어보겠습니다.


    다시 calculator.js로 갑니다. 설명은 주석으로 대신합니다. post 방식으로 받기 위해 router.post를 사용합니다.


    var express = require('express')
    var router = express.Router()

    router.get('/calculatorPop', function(req,res){
    res.render('calculator.ejs')
    })

    router.post('/calculatorPop', function(req, res){
    var inputText = req.body.inputString; // 넘어온 파라미터 텍스트
    if(isNumber(inputText.charAt(inputText.length-1))== 'false'){ // 마지막에 숫자가 아닌 부호가 들어오면 잘라줌
    inputText = inputText.substring(0, inputText.length-1);
    }
    var appendNumber = ''; // 자릿수를 문자열로 조합해 완전한 수로 만들어줌
    var calArray = new Array(); // 숫자와 기호로 이루어진 배열
    var arrayCount = 0; // calArray의 인덱스

    for(var i=0; i<inputText.length; i++){
    var oneCharater = inputText.charAt(i);
    if(isNumber(oneCharater) == 'true' || (i== 0 && oneCharater == '-')){ // 입력된 값이 숫자라면 더해줌
    // 아니면 첫번째 숫자 결과값이 음수일 경우,
    }else{
    calArray[arrayCount++] = appendNumber; // 연산 기호가 나왔다면 앞의 appendNumber을 대입
    calArray[arrayCount++] = oneCharater; // 연산 기호를 담아줌
    appendNumber = ''; // 초기화
    }
    }

    calArray[arrayCount] = appendNumber; // 연산 기호 다음, 마지막으로 들어온 appendNumber을 대입
    arrayCount = 1; // 연산 기호가 있는 인덱스를 가리킴
    var calculationOutput = 0; // 리턴될 결과값

    /** arrayCount는 연산 기호가 있는 위치를 가리킨다. 연산 기호가 배열의 마지막 값에 들어갈 수 없으므로
    * 배열의 길이보다 -1의 위치가 마지막 연산 기호가 있을 수 있는 위치이다.
    * ex) 1+3+4는 가능하지만 1+4+5+ 는 안됨.
    *
    * 숫자가 올 수 있는 배열의 인덱스는 0,2,4,6으로 온다. 345*9+123을 보면 알 수 있다.
    * 똑같이 연산 기호는 1,3,5 홀수의 위치만 올 수 있다.
    */
    while(arrayCount < calArray.length){
    switch(calArray[arrayCount]){
    case '+':
    if(arrayCount == 1){ // 첫 연산 기호일 경우 앞의 숫자와 뒤에 오는 숫자를 같이 계산해준다.
    calculationOutput = parseInt(calArray[arrayCount-1])+parseInt(calArray[arrayCount+1]);
    }else{ // 첫 연산기호가 아닐 경우는 이제까지 계산된 결과에 대해서 연산을 수행하면 된다.
    calculationOutput += parseInt(calArray[arrayCount+1]);
    }
    break;
    case '-':
    if(arrayCount == 1){
    calculationOutput = parseInt(calArray[arrayCount-1])-parseInt(calArray[arrayCount+1]);
    }else{
    calculationOutput -= parseInt(calArray[arrayCount+1]);
    }
    break;
    case '*':
    if(arrayCount == 1){
    calculationOutput = parseInt(calArray[arrayCount-1])*parseInt(calArray[arrayCount+1]);
    }else{
    calculationOutput *= parseInt(calArray[arrayCount+1]);
    }
    break;
    case '/':
    if(arrayCount == 1){
    calculationOutput = parseInt(calArray[arrayCount-1])/parseInt(calArray[arrayCount+1]);
    }else{
    calculationOutput /= parseInt(calArray[arrayCount+1]);
    }
    break;
    }
    arrayCount += 2; // +2를 통해서 숫자는 건너뛰고 연산 기호만 비교할 수 있다.
    }

    var result = {};
    result.calculationOutput = calculationOutput
    res.json(result); // json형태로 결과 값을 리턴한다.

    })

    /** 입력된 값이 숫자인지 문자인지 판별 */
    function isNumber(numOrString){
    var output = parseInt(numOrString)

    if(isNaN(output)){ // 숫자가 아니면 parseInt를 수행할 경우 NaN 결과 값을 내뱉는다.
    return 'false';
    }
    return 'true';
    }

    module.exports = router;



    그럼 결과 값을 return해주는 모습을 볼 수 있습니다.


          -> 



    그런데 문제는 결과값이 한 번 나오고 숫자를 입력하면 결과값에 합쳐진다는 것이죠 ㅠㅠ




    위의 결과 값에 3을 눌렸더니 17 뒤에 텍스트가 붙으면서 173이 되네요 ㅠㅠ?? 이러면 안되겠죠. 


    결과를 한 번 받아봤다면, 숫자를 눌릴 경우 초기화되면서 새로운 값이 들어가고, 연산 기호를 눌리면 기존이 결과값에 계산이 되도록 만들어볼게요~!


    전 flag를 이용했습니다.


    <script>
    var resetFlag = 'true' // =을 클릭하고 새로할 때는 새로운 값이 입력되게 하려고 'false'면 리셋을 시켜줘야함
    $(document).ready(function(){

    $('#calculation').on("click", function(){
    var parameter = $('#textBox').val();

    $.ajax({
    type: 'POST',
    url: '/calculator/calculatorPop',
    data: {inputString : parameter},
    success: function(result){
    if(result.calculationOutput == '0'){
    $('#textBox').val('');
    }else{
    $('#textBox').val(result.calculationOutput);
    resetFlag = 'false';
    }
    },
    error: function(err){
    console.log('ajax error')
    }
    })
    })

    $('#clear').on("click", function(){
    $('#textBox').val('');
    })

    $(document).keydown(function(event){
    if(event.keyCode == '8'){
    var originText = $('#textBox').val();
    $('#textBox').val(originText.substring(0, originText.length-1));
    }

    })
    })
    function typing(inputValue){
    var originalText = $('#textBox').val();
    if(inputValue == '+' || inputValue == '-' || inputValue == '*' || inputValue == '/'){
    var isNumber = parseInt(originalText.charAt(originalText.length-1));
    if(isNaN(isNumber)){
    return;
    }
    }
    if(resetFlag== 'false'){ // '='을 클릭하고 값을 리셋해주기 위해서
    resetFlag = 'true';

    if(!isNaN(parseInt(inputValue))){ // 숫자의 경우 리셋, 기호가 들어오면 계산 진행
    $('#textBox').val(inputValue);
    return;
    }
    }
    $('#textBox').val(originalText+inputValue);
    }
    </script>



    전역변수로 사용할 수 있게 script 밑에 resetFlag라는 변수를 만들었습니다. resetFlag가 'false'라면 값을 리셋을 해주어야한다는 표시입니다.


    ajax 결과를 받아왔을 때 resetFlag를 false로 만듭니다. 한 번 연산이 수행되었다는 뜻이죠.


    연산이 수행되었고 새로운 버튼을 눌렸을 때 resetFlag=='false'를 타겠죠?


    그럼 다음 연산을 위해 flag값은 다시 true로 돌려놓습니다. 그리고 새로 입력된 값이 숫자면 초기화 시켜서 새로운 계산이 될 수 있도록 만듭니다.


    하지만 연산 기호가 들어오면 진행되었던 결과 값에 계속해서 연산을 수행할 수 있도록 만듭니다. 


    그럼 아까와 똑같은 방식으로 진행해본 사진입니다.


          -> 


    -> 





    이 계산기의 단점은 textBox에 +, *, /, - 연산 표시가 모두 되지만 앞에서 부터 쭉 연산을 수행하기 때문에 곱셈이나 나눗셈이 문자열 뒤에 들어왔다고 해서 먼저 계산되지는 않아요. 


    Node 공부를 하기 위해서였기 때문에 개선을 고려하고 있진 않습니다. Node로 서버와 클라이언트단을 모두 공부해볼 수 있던 기회였던 것 같습니다!! 


    포스팅 작성하는데도 여간 시간이 많이 드는게 아니지만 좋은 복습이 된 것 같네요. 


    완성된 코드는 이쪽으로 들어오시면 확인하실 수 있습니다. 감사합니다.






Designed by Tistory.