본문 바로가기
개발이야기/Etc.

디데이 카운트다운 구현하기(feat. jQuery & moment.js)

by hyung12 2019. 4. 18.
반응형

디데이 카운트다운을 구현하기에 도전했다.(feat. jQuery & moment.js)

 

 

 

 


 

 

처음에는 어떻게 해야 할지 몰라서 구글링을 열심히 해서 내가 원하던 기능을 구현해보았다.

 

[HTML]

<h4 class="title">D-Day Count</h4>
<div id="countDay">
  <span class="day"></span>
  <span class="time"></span>
  <span class="minute"></span>
  <span class="second"></span>
  <span class="endday"></span>
</div>

 

[JS]

function CountDownTimer(dday) {
  var countDownDate = new Date(dday).getTime();
  var x = setInterval(function() {
    var now = new Date().getTime();
    var distance = countDownDate - now;
    var days = Math.floor(distance / (1000 * 60 * 60 * 24));
    var hours = Math.floor((distance % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
    var minutes = Math.floor((distance % (1000 * 60 * 60)) / (1000 * 60));
    var seconds = Math.floor((distance % (1000 * 60)) / 1000);

    function showRemaining() {
      if(days < 0) {
        days = 0;
        hours = 0;
        minutes = 0;
        seconds = 0;
        clearInterval(x);
      }
      $('#countDay').find('.day').html(days);
      $('#countDay').find('.time').html(hours);
      $('#countDay').find('.minute').html(minutes);
      $('#countDay').find('.second').html(seconds);
    }
  showRemaining();
  }, 1000);
}
CountDownTimer('2020/04/18 23:59:59');

 

이렇게 하니 정말 내가 원하는 날짜를 기준으로 일, 시간, 분, 초가 1초씩 카운트다운이 되고 있었다.

 

 

 

 


 

 

다음으로는 같은 HTMl 코드에 카운트다운에 필요한 함수 setInterval()과 clearInterval()을 기준으로 나눠서 다시 짜보았다.

 

[JS]

function CountDownTimer(dday, show) {
  var countDownDate = new Date(dday).getTime();
  var handle = null

  return {
    start: function() {
      this.handle = setInterval(function() {
        var now = new Date().getTime();
        var distance = countDownDate - now;
        var days = Math.floor(distance / (1000 * 60 * 60 * 24));
        var hours = Math.floor((distance % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
        var minutes = Math.floor((distance % (1000 * 60 * 60)) / (1000 * 60));
        var seconds = Math.floor((distance % (1000 * 60)) / 1000);
        if(days < 0) {
          days = 0;
          hours = 0;
          minutes = 0;
          seconds = 0;
          this.stop();
        }
        show({
          days: days,
          hours: hours,
          minutes: minutes,
          seconds: seconds,
        })
      }, 1000);
    },
    stop: function() {
      clearInterval(this.handle)
    }
  }
}

var count = CountDownTimer('2020/04/18 23:59:59', function(data) {
  var countDayId = $('#countDay');
  var dayEl = countDayId.find('.day');
  var timeEl = countDayId.find('.time');
  var minuteEl = countDayId.find('.minute');
  var secondEl = countDayId.find('.second');

  dayEl.html(data.days);
  timeEl.html(data.hours);
  minuteEl.html(data.minutes);
  secondEl.html(data.seconds);
})

count.start()
console.log(count)

 

return 할 값에 start와 stop으로 나눴다.

그래서 start에는 setInterval()에 대한 내용을 넣고 stop에는 clearInterval()에 대한 내용을 넣었다.

그리하여 얕은 지식으로 알고 있던 setInterval()과 clearInterval()에 대해 다시 한번 공부하게 됐다.


 setInterval() 

지정된 간격(밀리 초)으로 함수를 호출하며 clearInterval() 메서드가 호출되거나 윈도우가 닫힐 때까지 함수를 계속 호출한다.

간단히 말해 setInterval() 메서드는 일정시간마다 함수를 실행한다는 것이다.

setInterval(function(){ alert("Hello"); }, 3000); // 3초마다 "Hello" 알림

 

 clearInterval() 

setInterval() 메서드로 설정된 타이머를 지우며 setInterval() 메서드에 의해 반환된 ID 값은 clearInterval() 메서드의 매개 변수로 사용된다. clearInterval() 메서드를 사용하려면 간격 메서드를 만들 때 변수를 사용해야 한다.

myVar = setInterval("javascript function", milliseconds); // 실행
clearInterval(myVar); // 실행 중지

간단히 말해 clearInterval() 메서드는 일정 시간마다 실행되는 setInterval() 메서드를 멈추게 한다.


 

 

 

 


 

 

이번에는 moment.js를 사용하여 매주 일요일마다 디데이 카운트다운이 되도록 구현해봤다.

moment.js 홈페이지 바로가기

 

[JS]

<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.24.0/moment.min.js"></script>
<script>
  var TARGET_DAY = 'Sunday'

  function displayCallback(data) {
    var daysL = data.days+'';
    var hoursL = data.hours+'';
    var minutesL = data.minutes+'';
    var secondsL = data.seconds+'';
    var countDayClass = $('.js_countDay');
    var dayEl = countDayClass.find('.day');
    var timeEl = countDayClass.find('.time');
    var minuteEl = countDayClass.find('.minute');
    var secondEl = countDayClass.find('.second');

    if (daysL.length < 2 ) $(dayEl).html(data.days);
    else $(dayEl).html(data.days);
    if (hoursL.length < 2 ) $(timeEl).html('0'+data.hours);
    else $(timeEl).html(data.hours);
    if (minutesL.length < 2 ) $(minuteEl).html('0'+data.minutes);
    else $(minuteEl).html(data.minutes);
    if (secondsL.length < 2 ) $(secondEl).html('0'+data.seconds);
    else $(secondEl).html(data.seconds);
  }


  function countDownTimer(dday, show) {
    var countDownDate = new Date(dday).getTime();
    var handle = null;

    return {
      start: function() {
        var that = this
        function timer () {
          var now = new Date().getTime();
          var distance = countDownDate - now;
          var days = Math.floor(distance / (1000 * 60 * 60 * 24));
          var hours = Math.floor((distance % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
          var minutes = Math.floor((distance % (1000 * 60 * 60)) / (1000 * 60));
          var seconds = Math.floor((distance % (1000 * 60)) / 1000);

          if(seconds < 0) {
            that.stop();
            var count = countDownTimer(makeTime(TARGET_DAY).format('YYYY/MM/DD HH:mm:ss'), displayCallback);
            count.start();
            return false;
          }

          show({
            days: days,
            hours: hours,
            minutes: minutes,
            seconds: seconds,
          })
        }
        this.handle = setInterval(timer, 1000);
      },
      stop: function() {
        clearInterval(this.handle);
      }
    }
  }

  function makeTime(targetDay) {
    var nextWeekDay = moment(moment().add(1, 'weeks').day(targetDay).format('YYYY/MM/DD') + ' 23:59:59');
    var thisWeekDay = moment(moment().day(targetDay).format('YYYY/MM/DD') + ' 23:59:59');
    var today = moment(new Date());

    if (moment.duration(today.diff(thisWeekDay)).asDays() > 0) {
      return nextWeekDay;
    } else {
      return thisWeekDay;
    }
  }

  var count = countDownTimer(makeTime(TARGET_DAY).format('YYYY/MM/DD HH:mm:ss'), displayCallback)
  count.start();
</script>

 

확실히 코드가.... 길어졌다....

먼저를 카운트다운 기준으로 삼을 요일을 변수 TARGET_DAY에 담았다.

그리고 displayCallback() 함수에는 화면에 어떻게 보여줄지에 대한 내용을, countDownTimer() 함수에는 카운트다운 기능에 대한 내용을, 마지막 makeTime() 함수에는 매주 일요일마다 카운트다운이 실행되게 하는 코드를 담았다.

그리고 어떻게 포맷으로 보여질 지를 count 변수에 담아 count.start();로 실행시키니 매주 일요일마다 카운트다운이 되었다.

 

 

 

 


 

 

하지만 이 코드는 view와 data가 명확하게 나눠져 있지 않다는 얘기를 들었다.....

그래서 어떻게 해야 view와 data를 명확하게 구분할 수 있는 코드로 변경할 수 있을지에 대해 알아봤다.

 

[JS]

function calculateEndDate(now) {
  // 일주일 순회
  for (var i = 1; i <= 7; ++i) {
    var tomorrow = moment(now).add(i, 'days');
    if (tomorrow.day() === 0 /* 일요일 */) {
      return tomorrow.startOf('day').valueOf();
    }
  }
            
  throw new Error('Cannot find Wednesday');
}

function renderCountdown(distance) {
  var days = Math.floor(distance / (1000 * 60 * 60 * 24));
  var hours = Math.floor((distance % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
  var minutes = Math.floor((distance % (1000 * 60 * 60)) / (1000 * 60));
  var seconds = Math.floor((distance % (1000 * 60)) / 1000);

  var daysL = days+'';
  var hoursL = hours+'';
  var minutesL = minutes+'';
  var secondsL = seconds+'';
  var countDayClass = $('.js_countDay');
  var dayEl = countDayClass.find('.day');
  var timeEl = countDayClass.find('.time');
  var minuteEl = countDayClass.find('.minute');
  var secondEl = countDayClass.find('.second');

  if (daysL.length < 2 ) $(dayEl).html(days);
  else $(dayEl).html(days);
  if (hoursL.length < 2 ) $(timeEl).html('0'+hours);
  else $(timeEl).html(hours);
  if (minutesL.length < 2 ) $(minuteEl).html('0'+minutes);
  else $(minuteEl).html(minutes);
  if (secondsL.length < 2 ) $(secondEl).html('0'+seconds);
  else $(secondEl).html(seconds);
}

setInterval(function () {
  var now = Date.now();
  var endDate = calculateEndDate(now);

  renderCountdown(endDate - now);
}, 1000);

 

이 코드를 나 혼자 이렇게 만들어봐라고 했다면 지금의 나는 이렇게 할 생각조차도 못 했을 것이다. 스크립트 코드를 짤 때 어렵게 생각할 필요가 없다고 하시면서 view 단에 보여줄 코드와 data를 컨트롤할 수 있는 코드 이렇게 두 가지로 분리하여 생각하면 코드가 간결해지면서 보기 좋은 코드가 만들어질 것이라는 좋은 말씀을 해주신 분이 계셨다.

처음에 혼자서 디데이 카운트다운 기능을 구현해보겠다고 했을 때 이걸 내가 어찌 만들어?라는 생각을 했었는데 그분의 도움으로 이렇게 멋진 코드를 짠 듯하다. 또한 그분의 조언으로 인해 스크립트 코드 짜는 것에 대한 두려움(?)이 조금은 사라지는 듯했다.(덤으로 이제 스크립트 코드 짜는 것에 대한 흥미도 조금씩 늘어가는 듯~)

자바스크립트 공부를 열심히 해서 나도 저렇게 깔끔 간결한 코드를 혼자서 거뜬히 만들 수 있는 날이 왔으면 좋겠다는 마음으로 디데이 카운트다운 기능을 구현했던 과정을 정리해본다.

반응형