자바스크립트 setInterval 중복 실행

안녕하세요 타이머 기능을 구현하려고 하는 코린이입니다.
구글이랑 검색해보면서 타이머 구현을 했는데, 여러 번 클릭을 하게 되면 중복 실행이 되서
시간이 몇 배로 떨어지더라구요. 어떻게 해야할 지 전혀 감이 안 와서 조언을 얻고자 질문 남깁니다.
제가 해본 방법은

  1. 함수로도 만들어보고 setTimeout이나 while를 통해 참 거짓 방법으로도 해봤는데 안되구요.

  2. 다른 분 글을 보면 중복 실행을 막기 위해 실행 위에 다시 한번 clearInterval를 준다는데 막상 해보면 아무래도 timer 코드는 아래에 있어서 오류가 뜨구요.

  3. 또 해당코드 끝나는 줄에 clearInterval를 주면 한 번 실행하고 나서 안하구요. clearInterval이 나오면 끝나는 걸 아는데 다른 분 글을 보면 코드가 길어질까봐 중간은 생략하고 마지막에 clearInterval 를 해놓으셨더라구요. 그리고 타이머를 할 때 버튼 눌러서 멈추고 종료하는게 아니라 타이머는 0초가 될때까지 자동으로 계속 나오는 기능입니다.

현재 코드는 다음과 같습니다.
`let timer = setInterval(function(){
time -= 1;

    if(time==0){
        console.log("종료");

        clearInterval(timer);
    }
    console.log(time);
    },1000);
`

감사합니다.

  1. 언급하신 클릭의 대상이 무엇인지, 그 대상은 어떻게 작동하는지 알 수 있으면 좋겠습니다.
    – 버튼이라 하신 거로 보아, 버튼이 클릭되면 어떤 코드를 실행한다는 둥 설명해 주시면 좋겠습니다.
  2. 타이머가 어떤 식으로 작동해야 하는지 알 수 있으면 좋겠습니다.
    – 버튼이 눌리면 타이머가 어떻게 작동한다는 둥 설명해 주시면 좋겠습니다.
  3. 언급하신 “클릭이 여러 번 이뤄졌을 때” 타이머가 어떻게 작동해야 하는지 알 수 있으면 좋겠습니다.
    – 버튼이 여러 번 눌렸을 때 기존에 실행되던 타이머는 어떻게 작동해야 하는지 알 수 있으면 좋겠습니다.

안녕하세요 먼저 댓글달아주셔서 감사합니다.
간단하게 코드 작성했습니다.

HTML

<button class="start_btn">start</button>
    <span class="time_text">3</span>

Js

const start_btn = document.querySelector(".start_btn");
        let time_text = document.querySelector(".time_text");
        start_btn.addEventListener("click",function(){
            time = 30;

        let timer = setInterval(function(){
            console.log(time);
            
            if(time>=1){
                time = time - 1;    
            }
            if(time==0){
                console.log("종료");
        
                clearInterval(timer);
            }
            time_text.textContent = time;
            },1000);
            });

1.먼저 start 버튼을 클릭을 하게 되면 실행을 하게 되는 것입니다.
2.버튼을 클릭하면 setInterval 함수를 통해 동작이 실행하여 time 을 1초 씩 줄이는 것입니다.
1보다 크거나 같아야 딱 1에서 멈추더라구요 제가 잘못한 것일 수도 있겠지만요…
3. 여러 번 클릭 했을 경우 중복으로 실행이 됩니다. 예를 들어 3번을 연속으로 클릭시에 세번 연속 실행이 되서 한 번에 3번이 실행되어 3초가 빠르게 줄어버리는 것이죠.

기존에 생각 한 것은 여러번 클릭 해도 정상적으로 1초에 1씩 줄어들게 만들고 싶었습니다.
예를 들어 100번을 클릭해도 시작 시간이 30초라면 30초가 나와서 1씩 줄어들게 하는 것이죠.

감사합니다.

만들고자 하는 것에 대해 주어진 정보는 충분해 보이니, 차근차근 만들 수 있겠습니다.

  1. 타이머가 중복으로 만들어지는 게 문제

버튼을 클릭하면, 버튼 요소에 등록된 리스너가 타이머를 생성하고, 그 타이머가 HTML 텍스트를 변화시키는 것입니다. 여기서 중요한 것은, 진행되는 타이머는 유일해야 한다는 것입니다. 하지만 기존의 코드는 타이머를 클릭하는 대로 우후죽순 만들고, 표시되는 텍스트는 유일하기 때문에 여러 타이머가 동시에 한 텍스트를 건드니 타이머가 원하는 대로 작동하지 않는 것입니다.

  1. 타이머를 하나만 생성하도록 관리하는 방법

만드는 데엔 여러 방식이 있겠지만, 막무가내로 일단 가장 간단한 방식으로 만들겠습니다.
염두에 둬야 하는 것은 아래와 같습니다.

  • 페이지 내에서 작동하는 타이머는 유일하다.
  • 버튼을 클릭했을 때 작동하던 타이머가 있다면 그 타이머를 제거하고 새로운 타이머로 교체한다.
  • 타이머가 종료되면 종료된 타이머를 제거한다.

세세한 코드 스타일은 다를 수 있겠지만, 최대한 기존의 코드를 활용하겠습니다.

const INTERVAL = 1000;
const START_COUNTDOWN = 30;
const startButton = document.querySelector(".start_btn");
const timerText = document.querySelector(".time_text");

let currentTimer = null; // 실행되는 타이머가 할당될 변수
let currentCountdown = START_COUNTDOWN; // 타이머가 표시할 카운트다운

currentTimer에 실행되는 타이머가 없으면 null 상태로 만들고, 반대로 currentTimernull이 아니라면 진행되는 타이머가 존재한다는 의미로 활용합니다.

이 흐름에 맞춰서 이벤트 리스너도 조정합니다.

startButton.addEventListener("click", () => {
  if (currentTimer === null) {
    // 타이머가 존재하지 않는 경우
  } else {
    // 기존에 작동하는 타이머가 존재하는 경우
  }
});

이벤트 리스너 내부의 콜백 함수는 다음처럼 정리할 수 있습니다.

  • 작동하는 타이머가 존재하지 않는다면
    • 새로운 타이머를 만들어 currentTimer에 할당한다.
  • 이미 작동하는 타이머가 존재한다면
    • 기존의 타이머를 제거한다.
    • 새로운 타이머를 만들어 currentTimer에 할당한다.

새로운 타이머를 만드는 기능이 중복되므로 함수로 따로 생성하겠습니다.

function createTimer() {
  // setInterval은 바로 시작하지 않기 때문에 클릭 직후의 상황을 미리 설정해 둠
  currentCountdown = START_COUNTDOWN;
  timerText.innerHTML = `${currentCountdown}`;
  currentTimer = setInterval(() => {
    currentCountdown -= 1;
    timerText.innerHTML = `${currentCoundown}`;
    if (currentCountdown === 0) {
      clearInterval(currentTimer);
      currentTimer = null;
      console.log("종료");
    }
  }, INTERVAL);
}

콜백 함수는 아래처럼 채워집니다.

startButton.addEventListener("click", () => {
  if (currentTimer === null) {
    createTimer();
  } else {
    // 기존에 작동하는 타이머가 존재하는 경우
  }
});

screenshot1
테스트 결과, 타이머가 정상적으로 종료되는 것을 확인했습니다.
이제 남은 부분을 채우면 됩니다.

startButton.addEventListener("click", () => {
  if (currentTimer === null) {
    createTimer();
  } else {
    clearInterval(currentTimer);
    console.log("타이머 제거됨");
    createTimer();
  }
});

screenshot2
기존에 작동되는 타이머가 있으면 취소가 되고 새로 타이머가 생성돼 할당되는 것을 확인할 수 있었습니다.

전체 코드는 여기에서 확인하실 수 있습니다.

1개의 좋아요

자세한 설명 감사합니다!
아무리 해도 안풀렸는데 답답함이 해소되었네요 감사합니다.

1개의 좋아요