--- title: "pg_cron 사용" slug: "clouddbforpostgresql-pg-cron-use" updated: 2026-08-20T09:03:30Z published: 2026-08-20T09:03:30Z canonical: "guide-gov.ncloud-docs.com/clouddbforpostgresql-pg-cron-use" --- > ## Documentation Index > Fetch the complete documentation index at: https://guide-gov.ncloud-docs.com/llms.txt > Use this file to discover all available pages before exploring further. # pg_cron 사용

VPC 환경에서 이용 가능합니다.

Cloud DB for PostgreSQL은 pg_cron extension을 제공합니다. pg_cron은 PostgreSQL 내부에서 cron 표현식 기반으로 SQL 작업을 예약 실행하는 extension입니다. ## 콘솔을 통한 extension 설치 Cloud DB for PostgreSQL에서는 superuser 권한을 제공하지 않으므로 콘솔을 통해 superuser 권한이 필요한 extension을 설치할 수 있습니다. 자세한 내용은 아래 가이드를 참조해 주십시오. * [Extension 설치](/docs/clouddbforpostgresql-extension) pg_cron을 설치할 때는 작업을 예약할 대상 데이터베이스를 선택해 주십시오. 설치 과정에서 DB Server가 재시작되며, HA(고가용성) 구성에서는 Failover 방식으로 서버가 순차적으로 재시작됩니다. :::(Warning) (주의) - pg_cron은 PostgreSQL 클러스터당 1개의 데이터베이스에만 설치할 수 있습니다. 이미 설치된 경우 다른 데이터베이스에 추가로 설치할 수 없습니다. - 설치 시 선택한 데이터베이스의 DB owner 계정에만 pg_cron 사용 권한이 부여됩니다. 다른 DB 사용자에게는 권한이 부여되지 않습니다. 따라서 pg_cron은 해당 데이터베이스의 DB owner 계정으로 접속해야만 사용할 수 있습니다. - DB owner 계정에는 권한을 다시 부여할 수 있는 권한(GRANT OPTION)이 없으므로, DB owner 계정이 다른 DB 사용자에게 cron 스키마에 대한 권한을 부여(GRANT)할 수 없습니다. ::: ## pg_cron 개념 pg_cron을 설치하면 대상 데이터베이스에 `cron` 스키마가 생성되며, 다음 구성 요소를 통해 작업을 예약하고 관리합니다. Cloud DB for PostgreSQL은 superuser 권한을 제공하지 않으므로 아래 표의 DB owner 계정 사용 항목에 따라 일부 함수는 사용할 수 없습니다. | 구분 | 이름 | 설명 | DB owner 계정 사용 | |------|------|------|------| | 테이블 | cron.job | 예약된 작업 정의 | 조회 (본인 작업) | | 테이블 | cron.job_run_details | 작업 실행 이력 | 조회·삭제 (본인 이력) | | 함수 | cron.schedule | 작업 예약 | 사용 가능 | | 함수 | cron.unschedule | 작업 삭제 | 사용 가능 | | 함수 | cron.schedule_in_database | 다른 데이터베이스의 작업 예약 | 지원하지 않음 | | 함수 | cron.alter_job | 예약된 작업 변경 | 지원하지 않음 | 예약된 작업은 작업을 등록한 사용자의 권한으로 실행됩니다. 등록한 사용자가 접근할 수 없는 테이블에 대해서는 작업이 실패합니다. :::(Info) (참고) - Cloud DB for PostgreSQL은 superuser 권한을 제공하지 않으므로, DB owner 계정으로는 `cron.schedule`(작업 예약), `cron.unschedule`(작업 삭제)과 `cron.job`·`cron.job_run_details` 조회를 사용할 수 있습니다. - `cron.schedule_in_database`(다른 데이터베이스의 작업 예약)와 `cron.alter_job`(예약된 작업 변경)은 DB owner 계정에 제공되지 않습니다. 호출하면 `permission denied for function` 오류가 발생합니다. 두 함수는 `username` 인자로 다른 계정의 권한으로 작업을 실행할 수 있어 보안을 위해 제한됩니다. - 예약된 작업의 스케줄이나 명령을 변경하려면 `cron.unschedule`로 삭제한 후 다시 예약합니다. - pg_cron 작업은 Primary 서버에서만 실행됩니다. Failover가 발생하면 새 Primary 서버에서 예약된 작업이 자동으로 이어서 실행됩니다. ::: ## 작업 예약 및 관리 설치 시 선택한 데이터베이스에 DB owner 계정으로 접속한 후 작업을 예약합니다. :::(Info) (참고) - Cloud DB for PostgreSQL은 보안을 위해 신규 데이터베이스의 `public` 스키마 사용을 권장하지 않습니다. 따라서 별도 스키마를 생성하여 사용합니다. - 예약 작업의 SQL에는 스키마를 포함한 이름(`sales.orders`)을 사용하는 것을 권장합니다. 작업은 `search_path` 설정과 무관하게 동작해야 안전합니다. ::: ### 작업 예약 `cron.schedule(job_name, schedule, command)` 형식으로 예약합니다. ```sql -- 매일 03:00에 sales.orders 테이블의 VACUUM ANALYZE 실행 SELECT cron.schedule('nightly-vacuum-orders', '0 3 * * *', 'VACUUM ANALYZE sales.orders'); -- 매주 일요일 05:00에 90일 이전 주문 삭제 SELECT cron.schedule('purge-old-orders', '0 5 * * 0', $$DELETE FROM sales.orders WHERE ordered_at < now() - interval '90 days'$$); ``` :::(Info) (참고) - 스케줄은 표준 cron 표현식(분 시 일 월 요일)을 사용합니다. `'10 seconds'`와 같이 초 단위 간격도 지정할 수 있습니다. - pg_cron은 예약된 작업을 백그라운드 워커로 실행합니다(`cron.use_background_workers`가 `on`으로 설정됩니다). 동시에 실행할 수 있는 작업 수는 서버의 백그라운드 워커 수로 제한됩니다. - 여러 작업의 실행 시각이 겹치면 동시 실행 한계에 도달할 수 있으므로, 무거운 작업이나 다수의 작업은 실행 시각을 분산하는 것을 권장합니다. - 동일한 작업의 이전 실행이 끝나지 않은 경우, 다음 실행은 중복 실행되지 않고 이전 실행이 끝날 때까지 대기합니다. ::: ### 작업 조회 및 변경 예약된 작업은 `cron.job`에서 조회합니다. 스케줄이나 명령을 변경할 때는 `cron.alter_job`을 사용할 수 없으므로, `cron.unschedule`로 삭제한 후 같은 이름으로 다시 예약합니다. ```sql -- 예약된 작업 조회 (본인이 등록한 작업만 표시) SELECT jobid, jobname, schedule, command, active FROM cron.job; -- 스케줄 변경: 기존 작업을 삭제한 후 새 스케줄로 다시 예약합니다. SELECT cron.unschedule('purge-old-orders'); SELECT cron.schedule('purge-old-orders', '0 6 * * 0', $$DELETE FROM sales.orders WHERE ordered_at < now() - interval '90 days'$$); -- 작업 삭제 SELECT cron.unschedule('nightly-vacuum-orders'); ``` ## 사용 예제: 일별 매출 집계 적재 매일 새벽에 전날 주문을 집계하여 요약 테이블에 적재하는 예제입니다. 아래 예제는 스키마 및 테이블 생성부터 검증까지 그대로 실행할 수 있습니다. 이 예제에서는 pg_cron을 설치한 데이터베이스를 `maindb`, 해당 데이터베이스의 DB owner를 `mainowner`로 가정합니다. 모든 작업은 `maindb`에 `mainowner` 계정으로 접속한 후 등록합니다. ```sql -- maindb 에 mainowner 계정으로 접속합니다. \c maindb mainowner ``` ```sql -- 1. 사용할 스키마를 생성합니다. (Cloud DB for PostgreSQL은 public 스키마 사용을 권장하지 않으므로 별도 스키마를 사용합니다.) CREATE SCHEMA sales; -- 2. 원본 주문 테이블을 생성하고 샘플 데이터를 입력합니다. (운영 중인 테이블이 있다면 생략합니다.) CREATE TABLE sales.orders ( order_id bigserial PRIMARY KEY, amount numeric(12,2) NOT NULL, ordered_at timestamptz NOT NULL DEFAULT now() ); INSERT INTO sales.orders (amount, ordered_at) VALUES (15000, current_date - 1 + time '09:30'), -- 어제 주문 (32000, current_date - 1 + time '13:10'), -- 어제 주문 ( 8900, current_date - 1 + time '20:05'), -- 어제 주문 (47000, current_date + time '08:00'); -- 오늘 주문 (어제 집계에는 미포함) -- 3. 집계 결과를 저장할 요약 테이블을 생성합니다. CREATE TABLE sales.daily_sales_summary ( sales_date date PRIMARY KEY, order_cnt integer NOT NULL, total_amount numeric(14,2) NOT NULL ); -- 4. 먼저 동작을 빠르게 검증하기 위해 매분 실행으로 작업을 예약합니다. -- 스키마를 포함한 이름을 사용하며, 같은 날짜로 다시 실행되어도 중복되지 않도록 ON CONFLICT 로 갱신합니다. SELECT cron.schedule( 'rollup-daily-sales', '* * * * *', $$INSERT INTO sales.daily_sales_summary (sales_date, order_cnt, total_amount) SELECT current_date - 1, count(*), coalesce(sum(amount), 0) FROM sales.orders WHERE ordered_at >= current_date - 1 AND ordered_at < current_date ON CONFLICT (sales_date) DO UPDATE SET order_cnt = EXCLUDED.order_cnt, total_amount = EXCLUDED.total_amount$$ ); -- 5. 1~2분 정도 기다린 후 적재 결과와 작업 실행 이력을 확인합니다. SELECT sales_date, order_cnt, total_amount FROM sales.daily_sales_summary ORDER BY sales_date DESC LIMIT 10; SELECT jobid, status, return_message, start_time FROM cron.job_run_details WHERE jobid = (SELECT jobid FROM cron.job WHERE jobname = 'rollup-daily-sales') ORDER BY start_time DESC LIMIT 5; -- 6. 정상 동작을 확인했으면 테스트 작업을 삭제하고 운영 스케줄(매일 00:05)로 다시 예약합니다. -- (cron.alter_job 은 사용할 수 없으므로 unschedule 후 재예약합니다.) SELECT cron.unschedule('rollup-daily-sales'); SELECT cron.schedule( 'rollup-daily-sales', '5 0 * * *', $$INSERT INTO sales.daily_sales_summary (sales_date, order_cnt, total_amount) SELECT current_date - 1, count(*), coalesce(sum(amount), 0) FROM sales.orders WHERE ordered_at >= current_date - 1 AND ordered_at < current_date ON CONFLICT (sales_date) DO UPDATE SET order_cnt = EXCLUDED.order_cnt, total_amount = EXCLUDED.total_amount$$ ); ``` :::(Info) (참고) - 집계 기준일(`current_date`)은 세션 및 서버 타임존에 따라 결정됩니다. 타임존에 따라 집계 대상 날짜가 달라질 수 있으므로, 의도한 기간이 집계되는지 확인합니다. - 테스트 시에는 매분(`* * * * *`) 실행으로 동작을 빠르게 확인한 후, `cron.unschedule` 후 운영 스케줄로 다시 예약합니다. (`cron.alter_job`은 DB owner 계정으로 사용할 수 없습니다.) ::: ## 실행 이력 모니터링 `cron.job_run_details`에서 작업 실행 이력을 확인할 수 있습니다. 본인이 등록한 작업의 이력만 조회됩니다. ```sql -- 최근 실행 이력 조회 SELECT jobid, jobname, status, return_message, start_time, end_time FROM cron.job_run_details ORDER BY start_time DESC LIMIT 20; ``` `cron.job_run_details`는 자동으로 정리되지 않으므로, 다음과 같이 정리 작업을 예약하는 것을 권장합니다. ```sql -- 매주 일요일 05:00에 30일 이전 실행 이력 삭제 SELECT cron.schedule('cleanup-cron-history', '0 5 * * 0', $$DELETE FROM cron.job_run_details WHERE end_time < now() - interval '30 days'$$); ``` :::(Info) (참고) - `cron.job_run_details`는 작업 실행마다 누적되므로, 정리 작업을 예약하지 않으면 테이블 크기가 계속 증가합니다. :::