왜 setTimeout이 Node.js에서 객체를 반환하는가 (그리고 setInterval이 앱을 깨뜨릴 수 있는 이유)

발행: (2026년 1월 4일 오전 06:52 GMT+9)
8 min read
원문: Dev.to

I’m happy to translate the article for you, but I’ll need the full text you’d like translated. Could you please paste the content (excluding the source line you already provided) here? Once I have the article, I’ll translate it into Korean while preserving the original formatting, markdown syntax, and any code blocks or URLs.

Browser Timers vs Node.js Timers

브라우저에서 타이머는 Web API의 일부입니다. setTimeout을 호출하면 숫자 식별자를 반환합니다:

const id = setTimeout(fn, 1000);

그 숫자는 단순히 조회 키일 뿐입니다. 브라우저는 내부 타이머 테이블을 유지하고 필요할 경우 해당 번호를 사용해 타이머를 취소합니다. 브라우저는 이미 UI 이벤트 루프를 실행하고 있기 때문에, 타이머가 환경이 살아 있는지 여부에 영향을 주지 않습니다.

Node.js는 언제 종료할지 결정해야 하는 프로세스

Node.js는 일반적으로 서버나 CLI 프로세스로 실행됩니다. 브라우저와 달리 지속적으로 다음과 같은 중요한 질문에 답해야 합니다:

“계속 살아 있어야 할 작업이 남아 있나요?”

이를 위해 Node는 활성 리소스를 추적합니다. 예를 들어:

  • 열린 서버
  • 열린 소켓
  • 파일 디스크립터
  • 타이머

활성 리소스가 하나도 남지 않으면 Node는 종료됩니다. 이것이 Node 타이머가 단순히 숫자가 아닌 이유의 핵심입니다.

setTimeout이 Node.js에서 객체를 반환하는가

Node.js에서 setTimeout타이머 핸들 객체(NodeJS.Timeout 타입) 를 반환합니다. 이 객체는 이벤트 루프에 실제로 등록된 리소스를 나타내며 다음을 할 수 있습니다:

  • 라이프사이클 추적에 참여한다
  • 프로세스가 살아있는지를 결정하는 데 영향을 준다
  • 해당 동작을 변경하는 메서드를 제공한다

숫자 ID는 이러한 일을 할 수 없습니다.

Source:

ref()unref()의 의미

기본 동작: 타이머는 “ref’d” 상태

기본적으로 setTimeout이나 setInterval로 만든 모든 타이머는 ref’d 상태입니다. 개념적으로 이는 다음을 의미합니다:

“이 타이머는 중요합니다. 작업이 끝날 때까지 프로세스가 종료되지 않도록 합니다.”

setTimeout(() => {
  console.log("done");
}, 10_000);

ref()를 명시적으로 호출할 필요는 없습니다. 이 동작이 이미 기본값이기 때문입니다.

unref(): 타이머를 선택 사항으로 만들기

unref()를 호출하면 타이머의 역할이 바뀝니다:

const t = setTimeout(task, 10_000);
t.unref();

이제 타이머는 다음과 같이 말합니다: “내가 유일한 남은 것이면, 나를 기다리지 마세요.” 다른 모든 리소스가 사라지면 Node는 즉시 종료되고, 타이머는 실행되지 않을 수도 있습니다. 이는 의도된 동작이며 백그라운드 작업이나 최선 노력(​best‑effort) 작업에 유용합니다.

ref(): unref()를 되돌리기

ref() 메서드는 오직 unref()를 되돌리기 위해 존재합니다:

t.ref();

보통 라이브러리, 재사용 가능한 인프라 코드, 혹은 타이머의 중요도가 동적으로 변하는 상황에서만 ref()를 호출합니다. 일반 애플리케이션 코드에서는 수동으로 ref()를 호출할 필요가 거의 없습니다.

setIntervalsetTimeout보다 위험한 이유

핵심 차이는 간단하지만 중요합니다:

  • setTimeout은 한 번 실행되고 스스로 정리합니다.
  • setInterval은 명시적으로 중지하지 않는 한 계속 실행됩니다.

라이프사이클에 미치는 영향

setTimeout:

  • 타이머를 등록하고
  • 한 번 실행한 뒤
  • 자신을 해제하며
  • 프로세스가 살아있는 것을 멈춥니다

setInterval:

  • 타이머를 등록하고
  • 반복적으로 실행하며
  • 절대 해제되지 않고
  • 프로세스를 무기한 살아 있게 합니다

setTimeout을 잊어버리면 문제는 스스로 종료됩니다. setInterval을 잊어버리면 프로세스가 절대 종료되지 않을 수 있습니다.

조용히 발생하는 프로덕션 버그

const interval = setInterval(() => {
  collectMetrics();
}, 60_000);

종료 과정에서 HTTP 서버와 데이터베이스 연결이 닫히고 모든 실제 작업이 끝났음에도, 인터벌이 아직 ref 상태이기 때문에 프로세스가 계속 대기합니다. 이를 방지하려면 인터벌을 clear하거나 unref해야 합니다:

clearInterval(interval);

또는

interval.unref();

겹치는 실행: 또 다른 setInterval 위험

setInterval은 이전 실행이 끝났는지 여부를 신경 쓰지 않습니다:

setInterval(async () => {
  await slowTask(); // 10 초 소요
}, 5_000);

이로 인해 실행이 겹치면서 레이스 컨디션, 무제한 동시성, 메모리 증가, 데이터베이스 과부하가 발생할 수 있습니다.

재귀 setTimeout이 더 안전한 이유

보다 안전한 일반적인 패턴은 다음과 같습니다:

async function loop() {
  await slowTask();
  setTimeout(loop, 5_000);
}

loop();

이 패턴은 다음을 보장합니다:

  • 겹침 없음
  • 예측 가능한 간격
  • 자연스러운 정리
  • 더 안전한 종료 동작

이 방식이 Node.js의 라이프사이클 모델에 더 잘 맞습니다.

모범 사례 요약

  • Node 타이머는 실제 이벤트 루프 자원이므로 객체를 반환합니다.
  • 타이머는 기본적으로 ref()됩니다.
  • 백그라운드 작업이나 최선 노력 작업에는 unref()를 사용하세요.
  • setInterval은 그 수명 주기를 완전히 제어할 수 없는 경우 사용을 피하세요.
  • 반복적인 비동기 작업에는 재귀적인 setTimeout을 선호하세요.
  • 서버 코드에서는 항상 종료 동작을 고려하세요.

질문이 있나요? 댓글에 남겨 주세요!

Back to Blog

관련 글

더 보기 »

JavaScript에서 비동기

🔴 동기식 일반적으로 하나의 작업이 끝난 뒤에야 다음 작업이 시작됩니다. javascript console.log'One'; console.log'Two'; console.log'Three'; 👉 출력 One Two...

JSBooks: 최고의 JavaScript 책 선별 목록

소개 오늘날 JavaScript를 배우는 것은 압도적으로 느껴질 수 있습니다. 수천 권의 책, 강좌, 튜토리얼이 있으며, 어떤 것이 실제로 도움이 되는지 알기 어렵습니다.

NodeJS 101 — 파트 2 MySQL

🚀 JavaScript Node.js Express를 사용한 API 만들기. Node.js, Express, Sequelize 및 MySQL을 활용한 종합적인 RESTful API 개발 가이드!