ProEssentialsJS v11 워크스루

npm 설치부터 실시간으로 동작하는 대화형 차트까지 - 실제로 만드는 순서대로, 한 단계씩

JavaScript chart walkthrough
WebAssembly chart tutorial
npm install javascript chart
javascript chart example code
ProEssentialsJS chart
javascript real time chart tutorial
canvas2d chart getting started
javascript charting library walkthrough

ProEssentialsJS v11 Walkthrough
Your first chart, step by step

이 워크스루에서는 실제로 동작하는 차트를 처음부터 만듭니다. 설치, 데이터, 축, 상호작용, 실시간으로 움직이는 트레이스, 그리고 데이터 클릭을 받는 이벤트 핸들러까지 다룹니다. 약 10분이 걸리며, 원하지 않는다면 빌드 툴체인은 필요 없습니다.

데스크톱에서 ProEssentials를 이미 사용해 보셨다면 훑어보셔도 됩니다. JavaScript 속성 이름은 WinForms 속성 이름 그대로이며, 이벤트 이름도 마찬가지입니다. 10단계에서 C#과 JavaScript를 나란히 놓아, 바뀌는 부분이 얼마나 적은지 보여드립니다.

완성된 코드를 먼저 보고 싶으신가요? 라이브 예제 브라우저를 열어보세요 — 각 예제는 차트 옆에 자신의 소스를 함께 보여줍니다.

1) 설치

들어오는 길은 두 가지이며, 작성하는 페이지는 어느 쪽이든 동일합니다. 다른 것은 경로뿐입니다.

스타터 저장소를 클론하기 — 화면에 차트를 띄우는 가장 빠른 길입니다. 라이브러리가 페이지 옆에 커밋되어 있으므로 해석할 것도, 설치할 것도 없습니다.

git clone https://github.com/GigasoftInc/proessentials-js-starter
npm start

이것이 전체 절차입니다. 이 패키지는 의존성이 없으며, 정의된 하나의 스크립트가 작은 정적 서버를 실행합니다. 이유는 아래에 있습니다.

또는 npm에서, 여러분의 프로젝트에서:

npm install proessentials

어느 쪽이든 페이지는 스크립트 태그 두 개입니다. 첫 번째는 엔진으로, 클래식 스크립트입니다. 두 번째는 여러분의 코드로, 모듈로 불러옵니다.

<script src="proessentials.iife.js"></script>
<script type="module" src="app.js"></script>

app.js 안에서 원하는 차트의 속성 표면을 import합니다. 이는 여러분의 JavaScript 안에서 이루어지는 import이지, 또 하나의 include가 아닙니다.

import { attachApi, Enums as E } from './pe-api-graph.js';

태그 하나로는 불가능하며, 이는 패키징의 결함이 아닙니다. 속성 표면은 진짜 ES 모듈이고, 클래식 스크립트는 모듈을 담을 수 없기 때문입니다. 두 형식을 모두 제공하는 차팅 라이브러리도 같은 지점에서 나뉩니다. npm에서 설치하면 두 경로는 대신 node_modules/proessentials/dist/를 가리킵니다 — 맨 앞의 ./를 반드시 유지하세요. 맨 앞의 /도 유효한 URL이라 브라우저에서는 완벽하게 동작하지만, 에디터의 자동 완성만 조용히 사라집니다.

그 밖에 설정할 것은 없습니다. 번들러도, import map도, jsconfig.json도, 에셋 복사도 필요 없습니다. 라이브러리는 자신의 WebAssembly 바이너리와 내장 비트맵을 페이지가 아니라 자신의 스크립트 URL을 기준으로 해석하므로, 라이브러리가 어디에 있든 찾아냅니다. 에디터 자동 완성이 되는 이유도 같습니다. TypeScript 정의가 그것이 설명하는 모듈 바로 옆에 있기 때문입니다.

페이지는 서버로 제공하세요. 디스크에서 바로 열지 마세요. file:// URL에는 오리진이 없고, 브라우저는 오리진 없는 페이지를 위해 WebAssembly 모듈을 가져오지 않습니다 — 그래서 페이지는 빈 화면으로 남지만, 페이지 자체에는 아무 문제가 없습니다. 정적 서버라면 무엇이든 되고, 위의 npm start도 그중 하나입니다. 디스크에서 열리는 라이브러리를 쓰다 오신 분들이 여기서 걸립니다. 모듈도 없고 가져올 것도 없는 평범한 스크립트는 그냥 열리기 때문입니다. 이쪽은 2.9 MB 엔진을 가져와야 하고, 시도하면 그렇다고 알려줍니다.

이 모든 것을 실행하는 데 라이선스 키는 필요 없습니다. 활성화도, 도메인 등록도, 외부 통신도 없습니다.

2) 그릴 자리를 마련하기

요소 하나면 됩니다. 차트가 그 크기에 스스로를 맞춥니다.

<div id="chart" style="width:800px; height:520px"></div>

3) 차트 만들기

엔진은 WebAssembly이므로 한 번, 비동기로 로드됩니다. 그 이후는 전부 동기적인 속성 설정입니다.

// 1. The engine. Once per page, however many charts you go on to make //
const m = await ProEssentials();

// 2. The control. This is the WinForms designer step: dropping a Pego
// on the form. autoResize is the web Dock = Fill //
const ctl = new PeControl(document.getElementById('chart'), {
  module: m,
  kind: 'graph',
  autoResize: true,
});

// 3. The property tree //
const Pego1 = ctl.attach(attachApi);

객체가 셋이고, 각자 역할이 있습니다. m은 엔진이며 이후로는 건드릴 일이 없습니다. ctl은 컨트롤로, 캔버스와 메뉴, 스크롤바, 이벤트를 소유합니다. Pego1은 속성 표면이며, 이 워크스루의 나머지는 모두 여기서 이루어집니다.

Pego1일까요? 모든 플랫폼의 모든 ProEssentials 예제에서 30년 동안 이 객체를 그렇게 불러왔기 때문입니다. 원하는 이름을 붙이셔도 되지만, 이 이름을 유지하면 저희 예제와 AI 어시스턴트의 코드를 그대로 프로젝트에 붙여 넣을 수 있습니다.

4) 데이터 전달하기 — 그리고 순서에 유의하기

두 번 읽을 가치가 있는 단계는 여기 하나입니다. ProEssentials는 속성을 정해진 순서로 받습니다.

Subsets, 그다음 Points, 그다음 데이터, 그다음 나머지 전부, 그리고 마지막에 render().

이유는 모든 데이터 쓰기가 Subsets × Points에 대해 경계 검사를 받기 때문입니다. 차트에 크기를 알려주기 전에 데이터를 설정하면 쓰기가 거부됩니다 — 그런데도 차트는 기본 데이터로 그려지고, 겉보기에는 전혀 문제가 없어 보입니다. 모든 플랫폼을 통틀어 가장 흔한 실수이며, 한 번 알고 나면 피하기 쉽습니다.

// 1. Shape first. Subsets = rows, Points = columns //
Pego1.PeData.Subsets = 2;
Pego1.PeData.Points = 6;

// 2. Then the data //
Pego1.PeData.Y[0][0] = 10; Pego1.PeData.Y[0][1] = 30;
Pego1.PeData.Y[0][2] = 20; Pego1.PeData.Y[0][3] = 40;
Pego1.PeData.Y[0][4] = 30; Pego1.PeData.Y[0][5] = 50;
Pego1.PeData.Y[1][0] = 15; Pego1.PeData.Y[1][1] = 63;
Pego1.PeData.Y[1][2] = 74; Pego1.PeData.Y[1][3] = 54;
Pego1.PeData.Y[1][4] = 25; Pego1.PeData.Y[1][5] = 34;

인덱싱 표기에 유의하세요. C#은 PeData.Y[s, p]로 쓰지만, JavaScript에는 2차원 인덱서가 없으므로 PeData.Y[s][p]가 됩니다. API 전체에서 형태가 다른 곳은 여기뿐입니다.

5) 제목, 레이블, 축

Pego1.PeString.MainTitle = "Units Sold per Month";
Pego1.PeString.SubTitle = "";
Pego1.PeString.YAxisLabel = "Units Sold";

Pego1.PeString.PointLabels[0] = "Jan";
Pego1.PeString.PointLabels[1] = "Feb";
Pego1.PeString.PointLabels[2] = "Mar";
Pego1.PeString.PointLabels[3] = "Apr";
Pego1.PeString.PointLabels[4] = "May";
Pego1.PeString.PointLabels[5] = "Jun";

Pego1.PeString.SubsetLabels[0] = "Texas";
Pego1.PeString.SubsetLabels[1] = "Florida";

축 스케일링은 기본적으로 자동입니다. 고정해야 할 때 — 실시간 차트에서는 거의 항상 그렇습니다. 자동 스케일링 축은 프레임마다 다시 맞춰져 트레이스가 숨 쉬는 것처럼 보이기 때문입니다 — 수동 제어로 전환하세요.

Pego1.PeGrid.Configure.ManualScaleControlY = E.ManualScaleControl.MinMax;
Pego1.PeGrid.Configure.ManualMinY = 0;
Pego1.PeGrid.Configure.ManualMaxY = 100;

Graph 객체는 Y축만 스케일링합니다. 점을 왼쪽에서 오른쪽으로 일정 간격으로 배치하므로 고정할 X 스케일도, ManualMinX도 없습니다. X 값 자체가 의미를 가진다면, 예를 들어 타임스탬프나 불규칙한 샘플 간격이라면 그것은 Scientific Graph이며, 같은 이름으로 X 쪽 전체가 갖춰져 있습니다. API는 처음부터 끝까지 동일합니다. 둘 다 예제 브라우저에서 확인하실 수 있습니다.

6) 그리는 방식 선택하기

열거형은 E 아래에 있으며, .NET 열거형과 같은 이름을 갖습니다.

Pego1.PePlot.Method = E.GraphPlottingMethod.Bar;
Pego1.PeGrid.LineControl = E.GridLineControl.Both;
Pego1.PeGrid.Style = E.GridStyle.Dot;
Pego1.PePlot.Option.BarGlassEffect = true;
Pego1.PePlot.DataShadows = E.DataShadows.Shadows;

7) 상호작용 가능하게 만들기

확대, 스크롤바, 우클릭 메뉴, 추적 커서는 모두 내장되어 있습니다. 여러분이 작성하는 코드가 아니라 속성입니다.

// Left-drag draws a zoom box; z or the popup menu undoes it //
Pego1.PeUserInterface.Allow.Zooming = E.AllowZooming.HorzAndVert;

// Follows the data under the pointer and shows the value //
Pego1.PeUserInterface.Cursor.PromptTracking = true;

// Middle-button drag to pan //
Pego1.PeUserInterface.Scrollbar.MouseDraggingX = true;
Pego1.PeUserInterface.Scrollbar.MouseDraggingY = true;

// This enables data hot spots. Step 9 writes the handler //
Pego1.PeUserInterface.HotSpot.Data = true;

8) 그리기 — 그리고 계속 움직이게 하기

호출은 한 번, 모든 속성을 설정한 뒤 마지막에 합니다.

ctl.render(); // last, always //

실시간에는 두 가지 형태가 있으며, 어느 쪽이 필요한지 알아두는 것이 좋습니다.

추가 — 길어지는 트레이스에 새 샘플을 덧붙입니다. 비용이 적고, 스트립 차트에서는 보통 이쪽을 택합니다.

교체 — 프레임마다 완전히 새로운 데이터셋을 차트에 넘깁니다. 오실로스코프가 스윕할 때마다 하는 일이고, 그리기 전에 데이터를 필터링하거나 다시 계산하는 모든 애플리케이션이 하는 일입니다. 이쪽이 더 어려운 경우이고, 엔진이 바로 이를 위해 만들어졌습니다. 아래 예제는 100,000점짜리 Subset 네 개 — 합계 400,000점 — 을 매 프레임 교체합니다.

// requestAnimationFrame, not setInterval -- ask for what the display
// will take and never more, or the callbacks queue up //
const tick = () => {
  Pego1.PeData.Y.load(block, 4, 100000); // values, subsets, points //
  ctl.render();
  requestAnimationFrame(tick);
};
requestAnimationFrame(tick);

들일 만한 습관이 두 가지 있습니다. 축은 자동 스케일링에 맡기지 말고 고정하세요. 그러지 않으면 차트가 프레임마다 다시 맞춰져 트레이스가 숨 쉬는 것처럼 보입니다. 그리고 점을 하나씩 대입하는 대신 load()로 형식화 배열 전체를 엔진에 넘기세요 — 경계를 한 번만 넘습니다.

X 계열이 실제로 존재하는 Scientific Graph에는 세 번째가 있습니다. Y만 바뀔 때 PeData.ReuseDataX = true로 두면, 움직이지 않은 X 계열을 엔진이 다시 만들지 않습니다. Graph 객체에는 X 계열이 없으므로 여기서는 해당하지 않습니다.

9) 데이터 점 클릭에 응답하기

7단계에서 데이터 핫스팟을 켰습니다. 이것이 그 핸들러입니다.

ctl.PeDataHotSpot.add((sender, ev) => {
  alert("Subset " + ev.subset + ", Point " + ev.point +
      " with a value of " + Pego1.PeData.Y[ev.subset][ev.point]);
});

막대를 클릭해 보세요. 엔진은 마우스 다운 시점에 히트 테스트를 수행하고, 핫스팟 레코드를 채운 뒤, 해당 이벤트를 발생시킵니다. ev.subsetev.point는 이미 디코딩되어 있습니다. 클릭 리스너를 붙일 필요도, 타입 코드를 해석할 필요도 없습니다.

.add는 C#의 +=이고, .remove-=입니다. JavaScript에는 오버로드할 연산자가 없으므로 이벤트는 메서드입니다. .NET 원본과 마찬가지로 멀티캐스트여서, 두 번째 .add는 첫 번째를 대체하지 않고 핸들러를 하나 더 추가합니다. 각 핸들에는 .count.clear()도 있습니다.

Graph는 37개의 이벤트를 가지며, .NET과 같은 철자를 씁니다 — PeSubsetHotSpot, PeGraphHotSpot, PeMainTitle, PeZoomIn, PeCursorMoved 등입니다. 특정 컨트롤이 발생시킬 수 없는 이벤트는 죽은 채 남아 있지 않고 아예 없습니다. 구독할 핸들 자체가 없으므로, 결코 발생할 수 없는 구독은 런타임의 침묵이 아니라 그 줄에서의 TypeError가 됩니다.

받는 쪽에 유의하세요. 속성은 Pego1에, 이벤트는 컨트롤 자체인 ctl에 있습니다. WinForms에서는 둘 다 pego1에 있습니다. 웹에서는 이벤트를 컨트롤에 두어, 속성 표면을 한 번도 바인딩하지 않는 페이지도 이벤트를 받을 수 있게 했습니다 — 클릭만 하는 차트라면 attach()가 아예 필요 없습니다.

10) 데스크톱 컨트롤을 이미 아신다면

같은 차트를 두 번. 이것이 이 API에 대한 주장 전부이며, 설명하기보다 보여주는 편이 빠릅니다.

C# — WinForms, WPF 또는 WinUI:

pego1.PeData.Subsets = 2;
pego1.PeData.Points = 6;
pego1.PeData.Y[0, 0] = 10;
pego1.PeString.MainTitle = "Units Sold per Month";
pego1.PePlot.Method = GraphPlottingMethod.Bar;
pego1.PeUserInterface.HotSpot.Data = true;
pego1.PeDataHotSpot += OnDataHotSpot;
pego1.PeFunction.ReinitializeResetImage();

JavaScript — 브라우저:

Pego1.PeData.Subsets = 2;
Pego1.PeData.Points = 6;
Pego1.PeData.Y[0][0] = 10;
Pego1.PeString.MainTitle = "Units Sold per Month";
Pego1.PePlot.Method = E.GraphPlottingMethod.Bar;
Pego1.PeUserInterface.HotSpot.Data = true;
ctl.PeDataHotSpot.add(onDataHotSpot);
ctl.render();

같은 속성 이름, 같은 순서, 그 아래 같은 엔진. 차이는 다섯 가지이고, 그것이 전부입니다. 2차원 인덱서, 열거형 접두사, += 대신 .add, 이벤트를 받는 쪽, 그리고 마지막 호출입니다. ProEssentials에 대해 아시는 모든 것이 그대로 이어집니다. 저희 AI 어시스턴트가 아는 것도 마찬가지입니다 — 같은 속성 모델에서 답하기 때문입니다.

기존 데스크톱 코드를 옮기고 계신가요? 마지막 줄은 그대로 동작합니다. Pego1.PeFunction.ReinitializeResetImage()는 JavaScript 컨트롤에도 있으며, Reinitialize, Reset, GetRectGraph를 비롯한 PeFunction의 나머지도 마찬가지입니다. 이는 render()와 같은 호출이며, 새 브라우저 코드에서는 짧은 쪽 철자를 씁니다.

다음으로 볼 것
  • 라이브 예제 브라우저 — 데스크톱 데모와 같은 예제 모음을, 각각 소스와 함께.
  • PE-Query — 필요한 속성을 물어보면, 문서를 검색한 결과가 아니라 컴파일된 엔진에 대해 검증된 답을 돌려줍니다.
  • API 탐색기 — 모든 컨트롤의 모든 속성.
  • 비교 자료 — Highcharts, SciChart.js, LightningChart JS 등과의 비교.
  • 문의하기 — 지원은 무료이고 무제한이며, 엔진을 만든 사람이 직접 답변합니다.