dhtmlxScheduler와 PHP
이 튜토리얼은 프레임워크에 의존하지 않고 PHP로 Scheduler를 구축하는 데 필요한 모든 필수 정보를 제공합니다.
설정에는 데이터 저장을 위해 MySQL을 사용하며, 데이터베이스 접근을 위해 PDO 인터페이스를 활용합니다. 이 튜토리얼을 따라하려면 PHP 5.4 이상과 PDO_MYSQL 확장 기능이 활성화되어 있어야 하며, MySQL 또는 MariaDB가 필요합니다.
서버 사이드 통합을 다른 플랫폼이나 프레임워크에서 진행하고 싶다면, 다음과 같은 튜토리얼도 참고할 수 있습니다:
- "dhtmlxScheduler와 ASP.NET Core"
- "dhtmlxScheduler와 ASP.NET MVC"
- "dhtmlxScheduler와 Node.js"
- "dhtmlxScheduler와 PHP:Slim"
- "dhtmlxScheduler와 PHP:Laravel 연동하기"
- "dhtmlxScheduler와 SalesForce LWC 통합하기"
- "dhtmlxScheduler와 Ruby on Rails 연동하기"
- "dhtmlxScheduler와 dhtmlxConnector 연동하기"
또한 GitHub의 전체 데모를 확인 하고, 단계별 안내를 따라 애플리케이션을 빌드할 수 있습니다.
전체 소스 코드는 GitHub에서 확인할 수 있습니다.
1단계. 프로젝트 생성
애플리케이션을 위한 새 디렉토리를 만들어 시작하세요.
빈 폴더를 생성하고 이름을 scheduler-howto-php-plain으로 지정합니다.
2단계. 페이지에 Scheduler 추가
다음으로, 스케줄러를 호스팅할 페이지를 만듭니다.
scheduler-howto-php-plain 폴더 내에 index.html 파일을 만들고 아래 내용을 추가하세요.
<!doctype html>
<html>
<head>
<title> Getting started with dhtmlxScheduler</title>
<meta charSet="utf-8"/>
<script src="https://cdn.dhtmlx.com/scheduler/edge/dhtmlxscheduler.js"></script>
<link href="https://cdn.dhtmlx.com/scheduler/edge/dhtmlxscheduler.css"
rel="stylesheet" type="text/css" charSet="utf-8">
<style>
html, body{
margin:0px;
padding:0px;
height:100%;
overflow:hidden;
}
</style>
</head>
<body>
<div id="scheduler_here" className="dhx_cal_container">
<div className="dhx_cal_navline">
<div className="dhx_cal_prev_button"> </div>
<div className="dhx_cal_next_button"> </div>
<div className="dhx_cal_today_button"></div>
<div className="dhx_cal_date"></div>
<div className="dhx_cal_tab" name="day_tab"></div>
<div className="dhx_cal_tab" name="week_tab"></div>
<div className="dhx_cal_tab" name="month_tab"></div>
</div>
<div className="dhx_cal_header"></div>
<div className="dhx_cal_data"></div>
</div>
<script>
scheduler.init('scheduler_here', new Date(2019,0,20), "week");
scheduler.load("data/api.php");
</script>
</body>
</html>
앱을 실행하면, 스케줄러가 페이지에 표시됩니다:

3단계. 데이터베이스 준비
이 시점에서 스케줄러는 비어 있습니다. 다음 단계는 데이터베이스를 설 정하고 애플리케이션과 연결하는 것입니다.
데이터베이스 생성
선호하는 MySQL 클라이언트(예: phpMyAdmin)나 커맨드 라인을 통해 데이터베이스를 만들 수 있습니다. 아래 SQL을 활용하여 새 데이터베이스와 캘린더 이벤트용 테이블을 생성하세요:
CREATE DATABASE IF NOT EXISTS `scheduler_howto_php`;
USE `scheduler_howto_php`;
DROP TABLE IF EXISTS `events`;
CREATE TABLE `events` (
`id` int(11) AUTO_INCREMENT,
`start_date` datetime NOT NULL,
`end_date` datetime NOT NULL,
`text` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) DEFAULT CHARSET="utf8;"
MySQL 콘솔을 사용할 경우, 위 SQL을 dump.sql 파일로 저장한 뒤 아래처럼 실행하세요:
$ mysql -uuser -ppass scheduler < mysql_dump.sql
4단계. 데이터 불러오기
데이터베이스가 준비되었으니, 이제 스케줄러에 데이터를 불러올 차례입니다.
프로젝트 디렉토리 내에 data라는 새 폴더를 만드세요.
먼저, 데이터베이스 연결 정보를 설정 파일 data/config.php에 정의합니다:
<?php
$dsn = "mysql:host=localhost;dbname=scheduler_howto_php";
$username = "root";
$password = "";
$options = array(
PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES 'utf8'",
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
);
"localhost", "scheduler_howto_php", "root", "" 부분을 실제 데이터베이스 설정에 맞게 수정하세요.
다음으로, 클라이언트가 데이터베이스에서 데이터를 불러오고 변경사항을 저장할 때 호출할 PHP 스크립트를 추가합니다.
data 폴더 내에 api.php 파일을 만들고, 데이터베이스 연결을 여는 코드로 시작하세요:
<?php
require_once("config.php");
$db = new PDO($dsn, $username, $password, $options);
그 다음, 데이터베이스에서 스케줄러 이벤트를 가져오는 함수를 구현합니다:
function read($db, $requestParams){
$queryParams = [];
$queryText = "SELECT * FROM `events`";
$query = $db->prepare($queryText);
$query->execute($queryParams);
$events = $query->fetchAll();
return $events;
}
이후, 들어오는 요청에 응답하는 리퀘스트 핸들러를 만듭니다:
switch ($_SERVER["REQUEST_METHOD"]) {
case "GET":
$result = read($db, $_GET);
break;
case "POST":
// we'll implement this later
break;
default:
throw new Exception("Unexpected Method");
break;
}
header("Content-Type: application/json");
echo json_encode($result);
데이터베이스에 이벤트를 추가하면, 이제 스케줄러에 해당 이벤트들이 표시됩니다.
동적 로딩(Dynamic loading)
현재는 스케줄러가 데이터베이스의 모든 이벤트 레코드를 한 번에 불러옵니다. 데이터가 적은 경우에는 괜찮지만, 예약 시스템 등에서 오래된 기록이 쌓이면 데이터 전송량이 급격히 늘어날 수 있습니다. 몇 달간 사용하면, 페이지를 불러올 때마다 수 메가바이트의 이벤트를 요청하게 될 수도 있습니다.
이런 문제는 동적 로딩을 활성화하여 방지할 수 있습니다. 스케줄러가 현재 표시되는 날짜 범위를 파라미터로 서버에 전송하면, 서버는 해당 범위 내의 이벤트만 반환합니다. 사용자가 다른 날짜 범위로 이동할 때마다, 스케줄러는 해당 기간에 해당하는 데이터만 불러옵니다.
클라이언트 측에서 동적 로딩을 활성화하려면 setLoadMode 옵션을 "day", "week", "month" 중 하나로 설정하세요. 예를 들어, 아래 코드를 클라이언트 코드에 추가합니다:
scheduler.init("scheduler_here", new Date(2019, 0, 20), "week");
scheduler.setLoadMode("day"); /*!*/
// load data from the backend
scheduler.load("data/api.php");
서버 측에서는, read 함수를 다음과 같이 수정하여 처리할 수 있습니다:
function read($db, $requestParams){
$queryParams = [];
$queryText = "SELECT * FROM `events`";
// handle dynamic loading
if (isset($requestParams["from"]) && isset($requestParams["to"])) { /*!*/
$queryText .= " WHERE `end_date`>=? AND `start_date` < ?;"; /*!*/
$queryParams = [$requestParams["from"], $requestParams["to"]]; /*!*/
} /*!*/
$query = $db->prepare($queryText);
$query->execute($queryParams);
$events = $query->fetchAll();
return $events;
}
5단계. 변경사항 저장
백엔드 핸들러 구현
지금까지는 스케줄러가 백엔드에서 데이터를 읽어올 수 있었습니다. 다음 단계는 변경사항을 데이터베이스에 저장할 수 있게 하는 것입니다.
클라이언트는 JSON 모드로 동작하며, 이벤트 액션을 수행하기 위해 POST 요청을 보냅니다. 요청 포맷 및 라우트에 대한 자세한 내용은 Server-Side Integration에서 확인할 수 있습니다.
데이터베이스에서 이벤트를 생성, 수정, 삭제하는 함수를 추가하세요.
data/api.php에 아래 함수를 추가합니다:
// create a new event
function create($db, $event){
$queryText = "INSERT INTO `events` SET
`start_date`=?,
`end_date`=?,
`text`=?";
$queryParams = [
$event["start_date"],
$event["end_date"],
$event["text"]
];
$query = $db->prepare($queryText);
$query->execute($queryParams);
return $db->lastInsertId();
}
// update an event
function update($db, $event, $id){
$queryText = "UPDATE `events` SET
`start_date`=?,
`end_date`=?,
`text`=?
WHERE `id`=?";
$queryParams = [
$event["start_date"],
$event["end_date"],
$event["text"],
$id
];
$query = $db->prepare($queryText);
$query->execute($queryParams);
}
// delete an event
function delete($db, $id){
$queryText = "DELETE FROM `events` WHERE `id`=? ;";
$query = $db->prepare($queryText);
$query->execute([$id]);
}
그런 다음, POST 요청 핸들러를 아래와 같이 업데이트하세요:
$db = new PDO($dsn, $username, $password, $options);
switch ($_SERVER["REQUEST_METHOD"]) {
case "GET":
$result = read($db, $_GET);
break;
case "POST": /*!*/
$requestPayload = json_decode(file_get_contents("php://input")); /*!*/
$id = $requestPayload->id; /*!*/
$action = $requestPayload->action; /*!*/
$body = (array) $requestPayload->data; /*!*/
$result = [ /*!*/
"action" => $action /*!*/
]; /*!*/
if ($action == "inserted") {; /*!*/
$databaseId = create($db, $body); /*!*/
$result["tid"] = $databaseId; /*!*/
} elseif($action == "updated") { /*!*/
update($db, $body, $id); /*!*/
} elseif($action == "deleted") { /*!*/
delete($db, $id); /*!*/
} /*!*/
break; /*!*/
default:
throw new Exception("Unexpected Method");
break;
}
header("Content-Type: application/json");
echo json_encode($result);
새 이벤트가 생성되면, 해당 데이터베이스 ID가 응답의 tid 속성으로 클라이언트에 반환됩니다. 응답 JSON에는 필요에 따라 추가 속성을 포함할 수 있으며, 이는 클라이언트 측 핸들러에서 접근할 수 있습니다.
클라이언트 측 데이터 저장 활성화
다음으로, 방금 생성한 API와 연동할 수 있도록 클라이언트 측을 설정합니다:
scheduler.init("scheduler_here", new Date(2019, 0, 20), "week");
scheduler.setLoadMode("day");
// 백엔드에서 데이터 불러오기
scheduler.load("data/api.php"); /*!*/
// 백엔드로 업데이트 전송
var dp = scheduler.createDataProcessor({ /*!*/
url: "data/api.php", /*!*/
mode: "JSON" /*!*/
}); /*!*/
이제 애플리케이션을 재시작하면, 스케줄러에서 이벤트를 생성, 삭제, 수정할 수 있으며, 모든 변경 사항이 페이지를 새로고침해도 유지됩니다.

이 단계에서, 이벤트를 MySQL 데이터베이스에 저장하는 기본적인 스케줄러가 완성되었습니다.
반복 이벤트
"매일 반복"과 같은 반복 이벤트를 활성화하려면, 스케줄러 페이지에 적절한 확장 기능을 추가해야 합니다:
...
<body>
...
<script>
scheduler.plugins({
recurring: true /*!*/
});
scheduler.init('scheduler_here', new Date(2019,0,20), "week");
...
</script>
</body>
반복 이벤트 정보를 저장하려면 "events" 테이블에 추가 컬럼이 필요합니다. 반복 이벤트를 지원하는 테이블을 생성하는 SQL 쿼리는 다음과 같습니다:
CREATE DATABASE IF NOT EXISTS `scheduler_howto_php`;
USE `scheduler_howto_php`;
DROP TABLE IF EXISTS `events`;
CREATE TABLE `events` (
`id` int(11) AUTO_INCREMENT,
`start_date` datetime NOT NULL,
`end_date` datetime NOT NULL,
`text` varchar(255) DEFAULT NULL,
`event_pid` int(11) DEFAULT 0,
`event_length` bigint(20) unsigned DEFAULT 0,
`rec_type` varchar(25) DEFAULT '',
PRIMARY KEY (`id`)
) DEFAULT CHARSET="utf8;"
또는, 이전 단계의 기존 events 테이블을 다음과 같이 업데이트할 수 있습니다:
ALTER TABLE `events` ADD COLUMN `event_pid` int(11) DEFAULT '0';
ALTER TABLE `events` ADD COLUMN `event_length` bigint(20) unsigned DEFAULT '0';
ALTER TABLE `events` ADD COLUMN `rec_type` varchar(25) DEFAULT '';