본문으로 건너뛰기

Salesforce LWC와 함께하는 dhtmlxGantt

이 튜토리얼은 Salesforce Lightning Web Component에 dhtmlxGantt를 추가하는 방법을 설명합니다.

Salesforce LWC와의 DHTMLX 컴포넌트 통합 데모를 онлайн에서 확인해 보세요(로그인: user, 비밀번호: demo). 데모의 소스 코드는 [GitHub]에 제공됩니다.

다른 기술을 사용하는 경우 아래에 있는 사용 가능한 통합Variants 목록을 확인하십시오:

우리는 Lightning Web Component를 만들고 조직에 업로드하기 위해 Salesforce CLI를 사용할 것입니다.
또한 개발 조직과 함께 작업하기 위해 Visual Studio Code에 Salesforce Extension Pack"를 설치할 수 있습니다.

노트

이 튜토리얼에서 생성된 데모의 전체 소스 코드는 GitHub에서 확인할 수 있습니다.

Salesforce LWC로 Gantt 차트를 만드는 방법을 보여주는 비디오 가이드를 확인해 볼 수 있습니다.

사전 준비

필요하시면 Salesforce CLI를 설치하세요. 설치 가이드는 이 기사에서 확인할 수 있습니다.

1단계. 프로젝트 생성

무료 개발자 계정이 없다면 가입하여 무료 계정을 만들어 주세요. 설치 가이드는 이 기사를 참조하십시오.

왼쪽의 검색 창에서 Dev Hub를 찾아 선택합니다:

sf_devhub

새 설정 창에서 Dev Hub 활성화를 선택합니다:

sf_enabledh

Salesforce DX 프로젝트의 기본 디렉터리를 만들겠습니다:

$ mkdir ~/salesforce

CLI를 통해 Salesforce DX 프로젝트를 생성합니다:

$ cd ~/salesforce
$ sfdx project generate -n gantt-salesforce-app
target dir = C:UsersUsersalesforce
create gantt-salesforce-appconfigproject-scratch-def.json
create gantt-salesforce-appREADME.md
create gantt-salesforce-appsfdx-project.json
create gantt-salesforce-app.huskypre-commit
create gantt-salesforce-app.vscodeextensions.json
create gantt-salesforce-app.vscodelaunch.json
create gantt-salesforce-app.vscodesettings.json
create gantt-salesforce-appforce-appmaindefaultlwc.eslintrc.json
create gantt-salesforce-appforce-appmaindefaultaura.eslintrc.json
create gantt-salesforce-appscriptssoqlaccount.soql
create gantt-salesforce-appscriptsapexhello.apex
create gantt-salesforce-app.eslintignore
create gantt-salesforce-app.forceignore
create gantt-salesforce-app.gitignore
create gantt-salesforce-app.prettierignore
create gantt-salesforce-app.prettierrc
create gantt-salesforce-appjest.config.js
create gantt-salesforce-apppackage.json

생성된 프로젝트로 이동합니다:

$ cd gantt-salesforce-app

2단계. 권한 부여

Web Server Flow를 사용하여 Org에 로그인합니다:

$ sfdx org login web -d

Successfully authorized ... with org ID ...

프로젝트 구성 파일(sfdx-project.json)을 업데이트합니다. "sfdcLoginUrl" 매개변수를 자신의 "My Domain URL"로 설정합니다. 조직의 "My Domain URL"은 "My Domain" 설정 페이지에서 확인할 수 있습니다. 예를 들어:

sf_mydomain

gantt-salesforce-app/sfdx-project.json
"sfdcLoginUrl" : "https://xbs2-dev-ed.my.salesforce.com"

Scratch Org를 생성합니다:

$ sfdx org create scratch -f config/project-scratch-def.json -d

Creating Scratch Org...
RequestId: 2SR5j0000006JhCGAU
(https://xbsoftware2-dev-ed.my.salesforce.com/2SR5j0000006JhCGAU)
OrgId: 00DH40000000s0D
Username: test-tc0telfqhudt@example.com
Prepare Request
Send Request
Wait For Org
Available
Authenticate
Deploy Settings
Done

Your scratch org is ready.

3단계. Salesforce에 Gantt 추가

라이브러리를 사용하려면 Salesforce에 Static Resource로 업로드해야 합니다. 따라서 scratch org를 엽니다:

$ sfdx org open

이제 "Static Resources" 탭을 열고 "New" 버튼을 누릅니다

sf_static_resources

의미 있는 이름을 지정합니다(예: "dhtmlxgantt7111"), 라이브러리 자체가 들어 있는 ZIP 아카이브를 선택합니다(아카이브에는 dhtmlxgantt.jsdhtmlxgantt.css 파일이 포함되어야 합니다). 성능 향상을 위해 "Public" 캐시 제어를 선택합니다. "Save" 버튼을 누릅니다.

sf_gantt_file

이제 Salesforce 내부에 dhtmlxGantt가 있습니다.

sf_gantt_in_sf

4단계. 데이터 모델 생성

핵심 dhtmlxGantt 엔티티는 Tasks와 Links입니다. dhtmlxGantt 엔티티의 모든 속성을 Salesforce 내부의 일반 JSON으로 저장하는 것이 좋습니다. Tasks와 Links 객체를 생성해 보겠습니다. Object Manager를 열고 "Create"를 선택한 다음 "Custom Object"를 선택합니다:

sf_object_manager

Task 객체

작업 객체의 이름을 지정합니다. 예를 들어 GanttTask/GanttTasks로 설정합니다.

sf_task_object

노트

레코드 이름은 객체 이름과 일치해야 합니다. 예: Object Name: GanttTask => Record Name: GanttTask Name

"Save" 버튼을 누릅니다.

객체가 생성되면 "Fields & Relationships" 탭을 열고 "New" 버튼을 누릅니다.

sf_fields

  • Duration

데이터 유형으로 "Number"를 선택하고 "Next" 버튼을 누릅니다.

sf_data_type

이를 "Duration"으로 명명합니다. 이 컬럼은 JSON으로 직렬화된 Task 속성을 저장합니다. 여러 설정을 기본값으로 두고 "다음" 버튼을 누르다가 "Save & New" 버튼이 활성화되면 클릭합니다.

sf_new_field

다음 버튼을 눌러 기본 설정을 수락하고 "Save & New" 버튼이 활성화될 때까지 계속합니다.

  • Parent

"Parent" 필드를 만듭니다. 데이터 유형으로 "Text"를 선택합니다.

sf_parent

다음 버튼을 눌러 기본 설정을 수락합니다.

  • Progress

"Progress" 필드를 만듭니다. 데이터 유형으로 "Number"를 선택합니다.

sf_progress

다음 버튼을 눌러 기본 설정을 수락합니다.

  • Start date

"Start Date" 필드를 만듭니다. 데이터 유형으로 "Date/Time"을 선택합니다.

sf_start_date

다음 버튼을 눌러 기본 설정을 수락합니다. 마지막으로 "Save" 버튼이 활성화되면 저장합니다.

마지막으로 아래와 같이 보일 것입니다:

sf_gantttask

Object Manager를 열고 "Create"를 선택한 다음 "Custom Object"를 선택합니다:

링크 객체의 이름을 지정합니다. 예를 들어 GanttLink/GanttLinks로 설정합니다.

sf_link_object

노트

레코드 이름은 객체 이름과 일치해야 합니다. 예: Object Name: GanttLink => Record Name: GanttLink Name

다음으로 필요한 필드를 생성합니다.

  • Source

"Source" 필드를 만듭니다. 데이터 유형으로 "Text"를 선택합니다.

sf_source

다음 버튼을 눌러 기본 설정을 수락합니다.

  • Target

"Target" 필드를 만듭니다. 데이터 유형으로 "Text"를 선택합니다.

sf_target

다음 버튼을 눌러 기본 설정을 수락합니다.

  • Type

"Type" 필드를 만듭니다. 데이터 유형으로 "Text"를 선택합니다.

sf_type

다음 버튼을 눌러 기본 설정을 수락합니다.

마지막으로 아래와 같이 보일 것입니다:

sf_ganttlink

5단계. Lightning Web Component 만들기

Lightning Web Component를 만들려면 다음 명령을 실행합니다:

$ sfdx lightning generate component --type lwc -n gantt -d force-app/main/default/lwc

target dir =
C:UsersUsersourcesalesforcegantt-salesforce-appforce-appmaindefaultlwc
create force-appmaindefaultlwcganttgantt.js
create force-appmaindefaultlwcganttgantt.html
create force-appmaindefaultlwcganttgantt.js-meta.xml

컴포넌트 정의를 gantt.js-meta.xml에서 Lightning App Builder에 노출되도록 변경합니다:

force-app/main/default/lwc/gantt/gantt.js-meta.xml
<?xml version="1.0" encoding="UTF-8"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
<apiVersion>54.0</apiVersion>
<isExposed>true</isExposed>
<targets>
<target>lightning__AppPage</target>
</targets>
<targetConfigs>
<targetConfig targets="lightning__AppPage">
<property name="height" label="Height" type="Integer" default="800" />
</targetConfig>
</targetConfigs>
</LightningComponentBundle>

Open gantt.html and add the following code into it:

force-app/main/default/lwc/gantt/gantt.html
<template>
<div class="thegantt" lwc:dom="manual" style='width: 100%;'></div>
</template>

Open gantt.js and add the following code into it:

force-app/main/default/lwc/gantt/gantt.js
/* eslint-disable guard-for-in */
/* eslint-disable no-undef */
import { LightningElement, api } from "lwc";
import { ShowToastEvent } from "lightning/platformShowToastEvent";
import { loadStyle, loadScript } from "lightning/platformResourceLoader";
import { createRecord, updateRecord, deleteRecord } from "lightning/uiRecordApi";

// Static resources
import GanttFiles from "@salesforce/resourceUrl/dhtmlxgantt7111";

// Controllers
import getTasks from "@salesforce/apex/GanttData.getTasks";

function unwrap(fromSF) {
const data = fromSF.tasks.map((a) => ({
id: a.Id,
text: a.Name,
start_date: a.Start_Date__c,
duration: a.Duration__c,
parent: a.Parent__c,
progress: a.Progress__c,
type: a.Task_Type__c,
}));
const links = fromSF.links.map((a) => ({
id: a.Id,
source: a.Source__c,
target: a.Target__c,
type: a.Type__c
}));
return { data, links};
}

export default class GanttView extends LightningElement {
static delegatesFocus = true;

@api height;
ganttInitialized = false;

renderedCallback() {
if (this.ganttInitialized) {
return;
}
this.ganttInitialized = true;

Promise.all([
loadScript(this, GanttFiles + "/dhtmlxgantt.js"),
loadStyle(this, GanttFiles + "/dhtmlxgantt.css")
])
.then(() => {
this.initializeUI();
})
.catch((error) => {
this.dispatchEvent(
new ShowToastEvent({
title: "Error loading Gantt",
message: error.message,
variant: "error"
})
);
});
}

initializeUI() {
const root = this.template.querySelector(".thegantt");
root.style.height = this.height + "px";

// Enterprise 또는 Ultimate 버전을 사용하는 경우 주석 해제
//const gantt = window.Gantt.getGanttInstance();
gantt.templates.parse_date = (date) => new Date(date);
gantt.templates.format_date = (date) => date.toISOString();

gantt.init(root);
getTasks().then((d) => {
const chartData = unwrap(d);
gantt.parse({
tasks: chartData.data,
links: chartData.links
});
});

///↓↓↓ 백엔드 SF로의 변경 저장 ↓↓↓
gantt.createDataProcessor({
task: {
create: (data) => {
console.log("createTask",data);
const insert = {
apiName: "GanttTask__c",
fields: {
Name: data.text,
Start_Date__c: data.start_date,
Duration__c: data.duration,
Parent__c: String(data.parent),
Progress__c: data.progress
}
};
gantt.config.readonly = true; // 저장이 완료될 때까지 변경 비활성화
// until saving is complete
return createRecord(insert).then((res) => {
gantt.config.readonly = false;
return { tid: res.id, ...res };
});
},
update: (data, id) => {
console.log("updateTask",data);
const update = {
fields: {
Id: id,
Name: data.text,
Start_Date__c: data.start_date,
Duration__c: data.duration,
Parent__c: String(data.parent),
Progress__c: data.progress
}
};
return updateRecord(update).then(() => ({}));
},
delete: (id) => {
return deleteRecord(id).then(() => ({}));
}
},
link: {
create: (data) => {
const insert = {
apiName: "GanttLink__c",
fields: {
Source__c: data.source,
Target__c: data.target,
Type__c: data.type
}
};
return createRecord(insert).then((res) => {
return { tid: res.id };
});
},
update: (data, id) => {
const update = {
apiName: "GanttLink__c",
fields: {
Id: id,
Source__c: data.source,
Target__c: data.target,
Type__c: data.type
}
};
return updateRecord(update).then(() => ({}));
},
delete: (id) => {
return deleteRecord(id).then(() => ({}));
}
}
});
}
}

6단계. Apex 클래스 생성

다음 단계는 Lightning 컴포넌트와 데이터 모델 간의 상호 작용을 가능하게 하는 클래스를 만드는 것입니다.

$ sfdx apex generate class -n GanttData -d force-app/main/default/classes

target dir =
C:UsersUsersalesforcegantt-salesforce-appforce-appmaindefaultclasses
create force-appmaindefaultclassesGanttData.cls
create force-appmaindefaultclassesGanttData.cls-meta.xml

생성 후 GanttData.cls를 열고 다음 코드를 추가합니다:

force-app/main/default/classes/GanttData.cls
public with sharing class GanttData {

@RemoteAction
@AuraEnabled(cacheable="true)"
public static Map<String, Object> getTasks() {

// SOQL로 레코드를 가져옵니다
List<GanttTask__c> Tasks = new List<GanttTask__c>();
Tasks = [SELECT Id, Name, Start_Date__c, Duration__c,
Parent__c FROM GanttTask__c];

List<GanttLink__c> Links = new List<GanttLink__c>();
Links = [SELECT Id, Type__c, Source__c, Target__c FROM GanttLink__c];

Map<String, Object> result = new Map<String, Object>{
'tasks' => Tasks, 'links' => Links };
return result;
}
}

Scratch Org에서 프로젝트로 소스를 가져오기

$ sfdx project retrieve start

그런 다음 Scratch Org로 소스를 푸시합니다

$ sfdx project deploy start

7단계. Lightning 페이지 생성

"Lightning App Builder"를 열고 새 Lightning 페이지를 만듭니다.

sf_lightning_app

"App Page"를 선택한 후 페이지 이름과 레이아웃을 지정합니다.

sf_new_page

sf_page_name

sf_page_layout

새 페이지에서 Gantt 커스텀 컴포넌트가 보이는지 확인합니다. 어떤 영역에든 추가하고 저장합니다.

sf_gantt

페이지를 활성화합니다.

sf_saved_page

변경 사항을 저장합니다.

sf_activate_gantt

sf_add_page_to_nm

sf_gantt_page

앱 실행 페이지를 열면, 앱 런처에서 클릭하고 Gantt로 검색하면 액세스할 수 있습니다.

sf_app_launcher

모든 것이 원활하게 작동했다면 Lightning 페이지에서 간단한 간트 데모가 실행되는 것을 볼 수 있습니다.

sf_final_page

애플리케이션 보안

Gantt는 SQL 인젝션이나 XSS, CSRF 공격 등 다양한 위협으로부터 애플리케이션을 방지하는 기능을 제공하지 않습니다. 애플리케이션의 안전성을 유지하는 책임은 이를 구현하는 개발자에게 있습니다. 해당 문서의 상세 내용은 [해당 기사]를 참조하십시오. Salesforce는 데이터와 애플리케이션을 보호하기 위해 보안으로 구축되어 있습니다. 조직의 구조와 필요에 맞게 보안 체계를 직접 구현할 수도 있습니다. 자세한 내용은 Salesforce 보안 가이드를 참고하시고, 여기에서 보안을 유지하는 데 필요한 내용을 확인할 수 있습니다.

문제 해결

위의 단계를 따라 Salesforce와의 Gantt 통합을 구현했는데 페이지에서 Gantt의 작업과 연결이 렌더링되지 않는 경우, [리소스 문제 해결 Backend Integration Issues 가이드] 문서를 참고하십시오. 문제의 근본 원인을 식별하는 방법을 설명합니다.

다음 단계

이제 완전히 작동하는 간트를 가지게 되었습니다. 전체 코드를 [GitHub]에서 확인하거나 클론하거나 다운로드하여 프로젝트에 사용할 수 있습니다.

또한 [gantt의 다양한 기능에 대한 가이드]를 확인하거나 [다른 백엔드 프레임워크와의 Gantt 통합 튜토리얼]을 참조해 보세요.

Need help?
Got a question about the documentation? Reach out to our technical support team for help and guidance. For custom component solutions, visit the Services page.