---
term: '예약된 Cloud Code (크론 작업)'
seoTitle: '크론 작업과 예약된 Cloud Code: 문법, DST, 안정성'
headline: '예약된 Cloud Code(크론 작업)란?'
slug: scheduled-cloud-code-cron-jobs
category: backend-compute
shortDefinition: '크론 작업은 정해진 시간 일정에 따라 자동으로 실행되는 작업이며, 예약된 Cloud Code는 이 개념을 서버리스 함수에 적용한 것입니다.'
relatedTerms:
  - background-jobs-task-schedulers
  - cloud-code-serverless-functions
  - database-triggers-beforesave-aftersave
  - serverless-architecture
contrastsWith:
  - background-jobs-task-schedulers
aboutTerms:
  - 'Cron Expression'
  - 'Crontab'
  - 'Misfire Policy'
faq:
  - question: '크론 작업이란 무엇입니까?'
    answer: '정해진 시각이나 간격에 자동으로 실행되도록 예약된 작업입니다. 고전적으로는 Unix 시스템에서 cron 데몬이 crontab을 읽어 실행하는 작업을 가리키며, 넓게는 어떤 플랫폼에서든 시간을 기준으로 트리거되는 작업을 뜻합니다. 이름은 그리스어로 시간을 뜻하는 chronos에서 왔습니다.'
  - question: 'cron 표현식의 다섯 필드는 무엇을 의미합니까?'
    answer: '분(0–59), 시(0–23), 일(1–31), 월(1–12), 요일(0–7, 0과 7 모두 일요일)이며, 그 뒤에 명령이 옵니다. 별표는 모든 값을 뜻하고, 쉼표는 목록, 하이픈은 범위, 슬래시는 간격을 나타냅니다.'
  - question: '예약된 시각에 머신이 꺼져 있으면 어떻게 됩니까?'
    answer: '클래식 cron은 그 실행을 그냥 건너뜁니다. 따라잡기(catch-up)는 없습니다. 그래서 간헐적으로 켜지는 머신을 위한 anacron이 있고, systemd 타이머는 영속성 옵션을 제공하며, 현대적인 스케줄러는 따라잡기 정책을 뜻밖의 동작이 아닌 명시적 설정으로 다룹니다.'
  - question: 'cron은 일광 절약 시간(DST)을 어떻게 처리합니까?'
    answer: '기본적으로는 서툴게 처리합니다. 매뉴얼에 따르면 봄철에 "사라지는 한 시간"에 예약된 작업은 실행되지 않고, 가을에 시각이 되돌아가며 두 번 나타나는 시각의 작업은 두 번 실행됩니다. 일반적인 권고는 UTC로 예약하고, 중요한 작업은 현지 시각 00:00–03:00 구간에 두지 않는 것입니다.'
  - question: '다음 실행이 시작될 때 이전 작업이 아직 실행 중이면 어떻게 됩니까?'
    answer: '클래식 cron은 아무렇지 않게 두 번째 사본을, 이어서 세 번째 사본을 시작합니다. 실행이 겹치는 상황에는 명시적인 답이 필요합니다. 늦게 시작한 실행을 건너뛰게 하는 락 파일을 쓰거나, 현대적인 스케줄러가 정형화한 정책, 즉 실행 중인 인스턴스를 허용(allow), 금지(forbid), 교체(replace)하는 정책을 써야 합니다.'
  - question: '크론 작업이 실패하면 어떻게 알 수 있습니까?'
    answer: '기본적으로는 알 수 없습니다. 출력은 아무도 읽지 않는 로컬 메일로 가고, 한 번도 실행되지 않은 스케줄은 아무 오류도 남기지 않습니다. 실행된 것이 없기 때문입니다. 이를 해결하는 패턴은 하트비트 모니터링입니다. 작업이 성공하면 URL에 핑을 보내고, 핑이 오지 않으면 알림이 울립니다.'
  - question: 'cron으로 1분보다 짧은 간격으로 작업을 실행할 수 있습니까?'
    answer: '아니요. 1분이 클래식 cron의 하한선입니다. 데몬은 1분마다 깨어나 테이블을 훑고 작업을 실행합니다. 초 단위 정밀도가 필요하면 다른 스케줄러가 필요합니다. Quartz 방식의 여섯 필드 표현식, 서비스 내부의 루프, 또는 시계가 아닌 이벤트 큐를 써야 합니다.'
  - question: '크론 작업을 실행하려면 서버가 필요합니까?'
    answer: '이제는 필요하지 않습니다. 서버리스 플랫폼은 함수에 스케줄을 직접 연결합니다. 플랫폼이 시계를 소유하고, 작업을 실행하고, 로그와 상태를 기록하고, 정책에 따라 재시도합니다. 스케줄이 계속 살려 두어야 하는 머신 위의 파일이 아니라 설정이 되는 셈입니다.'
codeLanguages: [javascript, dart, swift, kotlin]
externalAuthorities:
  - name: 'crontab(5) — Linux manual page'
    url: 'https://man7.org/linux/man-pages/man5/crontab.5.html'
  - name: 'cron — Wikipedia (history and implementations)'
    url: 'https://en.wikipedia.org/wiki/Cron'
  - name: 'Quartz Scheduler — CronTrigger and misfire policies'
    url: 'https://www.quartz-scheduler.org/documentation/quartz-2.3.0/tutorials/tutorial-lesson-06.html'
  - name: 'Kubernetes — CronJob concepts'
    url: 'https://kubernetes.io/docs/concepts/workloads/controllers/cron-jobs/'
cta:
  title: '서버 없는 스케줄'
  text: '코드로 Cloud Job을 정의하고, Back4app 대시보드에서 시작 시각과 반복 주기를 지정해 예약하면 됩니다. crontab은 필요 없고, 상태와 로그는 플랫폼이 보관하는 곳에서 바로 확인합니다. 시계는 저희가 맡고, 로직은 여러분의 몫입니다.'
  linkText: '무료로 시작하기'
  linkUrl: 'https://www.back4app.com/signup'
author: 'Back4app Engineering'
publishedDate: '2026-10-31'
translationKey: scheduled-cloud-code-cron-jobs
---

**크론 작업은 정해진 시간 일정에 따라 자동으로 실행되는 작업이며, 예약된 Cloud Code는 이 개념을 서버리스 함수에 적용한 것입니다.** 계보는 Version 7 Unix에서 [Vixie cron](https://en.wikipedia.org/wiki/Cron)(1987)을 거쳐 오늘날의 플랫폼 스케줄러까지 이어지며, 모델 자체는 거의 바뀌지 않았습니다. 데몬이 1분마다 깨어나 *스케줄 + 명령* 줄로 이루어진 테이블, 즉 **crontab**을 읽고 일치하는 것을 실행합니다. 바뀐 것은 모델을 둘러싼 모든 것입니다. 시계가 어디에 있는지, 실행이 실패하면 어떻게 되는지, 그리고 누군가 그 사실을 알아차리는지입니다.

## 핵심 요약

| 질문 | 답변 |
| --- | --- |
| 문법 | 다섯 필드 – 분 · 시 · 일 · 월 · 요일 |
| 하한선 | 1분 단위 정밀도. 초 단위는 다른 스케줄러가 필요 |
| 함정 | DST로 인한 누락과 중복 실행 · 일/요일 필드의 OR 규칙 |
| 안정성 공백 | 클래식 cron: 재시도 없음, 따라잡기 없음, 클러스터 없음, 조용한 실패 |
| 현대적인 형태 | 함수에 붙는 플랫폼 설정으로서의 스케줄 – 시계, 로그, 재시도를 관리형으로 제공 |

## 한 화면으로 보는 cron

```text
┌ 분 (0–59)       ┌ 시 (0–23)     ┌ 일 (1–31)              ┌ 월      ┌ 요일 (0–7)
*                 *               *                        *         *        명령

0 2 * * *        매일 02:00              */15 * * * *   15분마다
0 9 * * 1-5      평일 09:00              0 6,18 * * *   06:00, 18:00
0 0 1,15 * *     매월 1일과 15일         @daily         = 0 0 * * *
@reboot          데몬 시작 시 1회        @hourly        = 0 * * * *

crontab -e   테이블 편집          crontab -l   목록 보기
crontab -ri  삭제 (확인 절차 포함, -r만 쓰면 묻지 않고 전부 지움)
```

현대적인 대응물은 이렇습니다. 작업은 코드에, 스케줄은 대시보드에 두고, 클라이언트는 결과만 읽습니다:

**JavaScript:**

```javascript
// JavaScript — Cloud Code (cloud/main.js)
// A scheduled job: defined in code, scheduled in the dashboard
Parse.Cloud.job('nightlyReport', async (request) => {
  const since = new Date(Date.now() - 24 * 60 * 60 * 1000);

  // Idempotent by date key: a rerun overwrites tonight's report, not duplicates
  const existing = await new Parse.Query('DailyReport')
    .equalTo('runDate', dateKey(new Date()))
    .first({ useMasterKey: true });
  const report = existing ?? new Parse.Object('DailyReport');

  report.set('runDate', dateKey(new Date()));
  report.set('summary', await summarizeOrdersSince(since));
  await report.save(null, { useMasterKey: true });
  request.message('Report written'); // shows in the dashboard's job status
});
```

**Flutter:**

```dart
// Flutter / Dart — Back4app Flutter SDK
// Clients consume what the schedule produces — no crontab in sight
final query = QueryBuilder<ParseObject>(ParseObject('DailyReport'))
  ..orderByDescending('runDate')
  ..setLimit(1);
final latest = (await query.query()).results?.first as ParseObject?;
print(latest?.get<String>('summary'));
// The report exists because a scheduled Cloud Job ran at 02:00 UTC —
// defined in code, scheduled in the dashboard, logged by the platform.
```

**Swift:**

```swift
// iOS / Swift — Back4app Swift SDK
// Clients consume what the schedule produces — no crontab in sight
let latest = try await DailyReport.query()
    .order([.descending("runDate")])
    .first()
print(latest.summary ?? "")
// The report exists because a scheduled Cloud Job ran at 02:00 UTC —
// defined in code, scheduled in the dashboard, logged by the platform.
```

**Kotlin:**

```kotlin
// Android / Kotlin — Back4app Android SDK
// Clients consume what the schedule produces — no crontab in sight
val query = ParseQuery.getQuery<ParseObject>("DailyReport")
query.orderByDescending("runDate")
query.limit = 1
val latest = query.find().firstOrNull()
println(latest?.getString("summary"))
// The report exists because a scheduled Cloud Job ran at 02:00 UTC —
// defined in code, scheduled in the dashboard, logged by the platform.
```

## 매뉴얼 페이지가 알려 주는 함정

[crontab(5)](https://man7.org/linux/man-pages/man5/crontab.5.html)에는 거의 모든 사람을 놀라게 하는 네 가지 의미론이 있습니다. **OR 규칙:** 일 필드와 요일 필드가 *둘 다* 제한되어 있으면 *어느 한쪽*만 일치해도 작업이 실행됩니다. `0 0 13 * 5`는 13일의 금요일에만이 아니라 매월 13일*과* 매주 금요일에 실행됩니다. **DST, 매뉴얼 그대로:** 봄에 시각을 앞당길 때 "사라지는 한 시간"에 예약된 작업은 *실행되지 않고*, 가을에 시각을 되돌릴 때 두 번 나타나는 시각의 작업은 *두 번 실행됩니다*. UTC로 예약하고, 중요한 작업은 현지 시각 새벽 시간대에서 빼 두어야 합니다. **간격은 필드 경계에서 초기화됩니다:** 분 필드의 `*/90`은 "90분마다"를 뜻할 수 없습니다. 카운터가 매시 초기화되기 때문입니다. 이런 불규칙한 간격에는 제대로 된 스케줄러가 필요합니다. **1분 하한선:** 데몬은 1분에 한 번 테이블을 훑습니다. 그보다 세밀한 것은 다른 도구의 일입니다.

## 안정성 공백

클래식 cron은 스케줄러이지 안정성 시스템이 아니며, 그 공백에는 분명한 형태가 있습니다. **재시도 없음**(실패한 실행은 그대로 실패입니다), **따라잡기 없음**(02:00에 잠들어 있던 머신은 실행을 건너뜁니다. `anacron`과 systemd 타이머의 영속성 옵션은 바로 이 때문에 존재합니다), **클러스터 대응 없음**(같은 crontab을 가진 서버 두 대는 모든 작업을 두 번 실행하고, 서버 한 대는 단일 장애점입니다), **조용한 실패**(출력은 아무도 읽지 않는 로컬 계정으로 메일 발송됩니다). 현대적인 스케줄러는 각 공백에 이름 붙은 정책으로 답합니다. **미스파이어 정책**([Quartz](https://www.quartz-scheduler.org/documentation/quartz-2.3.0/tutorials/tutorial-lesson-06.html)의 즉시 실행 vs. 건너뛰기), **동시 실행 정책**(겹치는 실행에 대한 [Kubernetes](https://kubernetes.io/docs/concepts/workloads/controllers/cron-jobs/)의 allow/forbid/replace), 그리고 기한 기반의 건너뛰기 의미론입니다. 동시에 이들 스케줄러는 분산 스케줄링이 *대략* 한 번(approximately once)이라는 점, 즉 실행이 가끔 중복되거나 아예 일어나지 않을 수 있다는 점을 솔직하게 문서화합니다. 여기서 두 세계가 공유하는 원칙이 나옵니다. **예약 작업은 멱등해야 합니다.** 작업을 해당 주기에 키로 묶으면(코드 탭의 리포트는 날짜를 키로 씁니다) 재실행이 중복을 만들지 않고 덮어쓰며, 놓친 실행은 늦게라도 실행해서 복구할 수 있습니다.

```mermaid
flowchart LR
  accTitle: 모니터링이 붙은 예약 서버리스 함수
  accDescr: 플랫폼 대시보드에서 설정한 스케줄이 지정된 반복 주기에 따라 클라우드 작업을 실행합니다. 작업은 플랫폼이 로그와 상태를 기록하는 가운데 실행되어 멱등한 결과를 데이터베이스에 쓰고, 성공하면 하트비트 모니터에 핑을 보냅니다. 따라서 놓치거나 실패한 실행은 핑의 부재로 감지됩니다.
  D["대시보드 스케줄<br/>매일 02:00 UTC"] --> C["플랫폼 시계가 실행"]
  C --> J["Cloud Job 실행<br/>상태 + 로그 기록"]
  J -->|"멱등한 쓰기<br/>날짜를 키로"| DB[("데이터베이스")]
  J -->|"성공 시"| HB["하트비트 핑"]
  HB -.->|"유예 기간이 지나도<br/>핑 없음 → 알림"| M["모니터"]
```

## 클래식 cron vs. systemd 타이머 vs. 분산 스케줄러 vs. 예약 함수

| | 클래식 cron | systemd 타이머 | 분산 (Quartz/K8s 방식) | 예약 클라우드 함수 |
| --- | --- | --- | --- | --- |
| 위치 | 한 머신의 crontab | 한 머신, 유닛 파일 | 클러스터 | [함수](/glossary/ko/cloud-code-serverless-functions/)에 붙는 플랫폼 설정 |
| 따라잡기 | 없음 (anacron으로 덧붙임) | `Persistent=true` | 미스파이어 정책 | 플랫폼 정책 |
| 실행 겹침 | 사본을 하나 더 시작 | 유닛 단위로 직렬화 | allow / forbid / replace | 플랫폼 정책 |
| 재시도 | 없음 | 서비스 재시작 규칙 | 설정 가능 | 기본 제공 |
| 실패 가시성 | 읽히지 않는 로컬 메일 | journald | 작업 상태 객체 | 대시보드 상태 + 로그 |
| 살려 두어야 할 서버 | 있음 – 그리고 그것이 단일 장애점 | 있음 | 클러스터 | **없음** |

## 스케줄 감시하기

예약 작업의 실패는 컴퓨팅에서 가장 조용한 실패입니다. 한 번도 실행되지 않은 작업은 예외도, 로그 한 줄도, 이메일도 *아무것도* 남기지 않습니다. 실행된 것이 없기 때문입니다. 이를 해결하는 패턴은 알림을 뒤집습니다. **하트비트 모니터링**에서는 작업이 성공적으로 끝나면 URL에 핑을 보내고, 모니터는 유예 기간 안에 핑이 오기를 기대하며, 핑의 *부재*가 알림을 일으킵니다. 여기에 클래식 cron에는 없던 기본 위생을 더해야 합니다. 실행 시간을 시간 흐름에 따라 추적하고(지난달 4분 걸리던 리포트가 오늘 밤 40분 걸린다면 무언가를 알려 주는 것입니다), 출력을 로컬 메일이 아닌 실제 로그로 수집하고, 스케줄 목록을 문서화합니다. 다섯 대의 서버에 흩어진 crontab이야말로 조직이 실행하는 줄도 잊고 있던 작업을 뒤늦게 발견하는 경로이기 때문입니다.

## 일반적인 사용 사례

- **리포트와 다이제스트** – 코드 탭이 보여 주는 야간 집계입니다. 집계하고, 쓰고, 알립니다.
- **정리와 만료** – TTL 정리, 고아 데이터 제거, 세션과 토큰 만료 처리.
- **데이터 동기화** – [웹훅](/glossary/ko/webhooks/)을 제공하지 않는 서드파티 시스템에서 주기적으로 데이터를 가져옵니다.
- **리마인더와 재참여 유도** – 체험 기간 종료, 장바구니 이탈, 갱신 안내 같은 시간 기반 발송.
- **상태 점검과 정합성 대조** – 이벤트 기반 경로가 놓친 것을 잡아내는 예약 감사.

## 어떤 스케줄러를 선택해야 할까? 결정 매트릭스

| 상황 | 선택 |
| --- | --- |
| 이미 운영 중인 Linux 서버 한 대 | 클래식 cron – 락과 하트비트를 함께 |
| 노트북급이거나 간헐적으로 켜지는 머신 | 영속성 옵션을 켠 anacron / systemd 타이머 |
| 어차피 운영하는 클러스터 | 클러스터 고유의 예약 작업 컨트롤러 |
| BaaS 위의 앱 백엔드 | 예약된 Cloud Job – 서버 없음, 로그 포함 |
| 1분 미만이거나 불규칙한 간격 | crontab 산술이 아닌 제대로 된 스케줄러나 큐 |
| 예약 작업으로 잘못 분류된 이벤트형 작업 | [작업 큐](/glossary/ko/background-jobs-task-schedulers/) – cron은 큐에 넣기만 함 |

## 한계와 트레이드오프

- **시간 트리거는 언제를 말할 뿐, 할 일이 있는지는 말하지 않습니다.** 스케줄은 처리할 작업이 있든 없든 실행됩니다. 불규칙하게 도착하는 작업에는 이벤트 기반 [트리거](/glossary/ko/database-triggers-beforesave-aftersave/)와 큐가 맞습니다.
- **대략 한 번이 솔직한 계약입니다.** 관리형 스케줄러조차 가끔 중복 실행하거나 건너뜁니다. 멱등성은 어디서나 작업의 책임입니다.
- **시간대는 정책 결정입니다.** UTC 스케줄은 DST에도 흔들리지 않고, 현지 시각 스케줄은 사람에게 맞춰집니다. 의도적으로 고르고, 분명하게 문서화해야 합니다.
- **빈도에는 하한선과 비용이 있습니다.** 클래식 cron은 1분, 다른 곳은 플랫폼마다 하한선이 다릅니다. cron으로 고빈도 폴링을 하는 것은 대개 시계를 찬 큐에 불과합니다.
- **스케줄은 조용히 쌓입니다.** "임시" 야간 작업은 목록으로 정리되기 전까지 모두 영구적입니다. 이를 한눈에 보여 주는 대시보드는 과소평가된 기능입니다.

## Back4app에서의 예약된 Cloud Code

Back4app은 관리형 데이터베이스, 자동 생성되는 REST 및 GraphQL API, 인증, 파일 스토리지, Cloud Code 서버리스 함수를 결합한 오픈소스 Backend as a Service(BaaS) 플랫폼입니다. 이곳의 스케줄링은 비교표의 현대적인 열을 구체화한 것입니다. [Cloud Code](/glossary/ko/cloud-code-serverless-functions/)에 `Parse.Cloud.job`을 정의하고(코드 탭의 야간 리포트로, 날짜 키로 멱등하며, 사용자 전체를 집계하기 위해 마스터 키로 접근합니다), 대시보드의 Background Jobs 패널에서 예약합니다. 작업 이름, 시작 시각, 반복 주기만 지정하면 되며, crontab 문법도, 그 crontab을 담을 서버도 없습니다. 실행, 상태, 오류는 서버 로그와 작업 패널에 남으므로 실패 가시성 열의 답은 기본으로 제공됩니다. 이 글의 마지막에서 다룬 원칙은 설계상 여전히 여러분의 몫입니다. 작업을 주기에 키로 묶고, 멱등하게 유지하고, 하트비트로 플랫폼의 시계와 여러분의 로직이 오늘 밤에도 여느 밤처럼 맞아떨어졌는지 확인하는 것입니다.
