콘텐츠로 이동
공식 웹사이트로 돌아가기

프런트엔드 실시간 전송 및 직접 제어

아키텍처 개요의 “실행”에는 데이터 포인트 쓰기, 알람 및 MQTT 발행뿐 아니라 프런트엔드 화면에 대한 직접 서비스도 포함됩니다. Edge-EMS Builder는 두 WebSocket 채널을 제공하여 사용자 정의 프런트엔드가 실시간 데이터를 수신하고 장치에 제어 명령을 직접 전달할 수 있으므로 별도의 중간 계층을 개발할 필요가 없습니다.

채널 경로 인증 방식 용도
프런트엔드 전송 채널 ws://<host>:<port>/front 로그인 Token(사용자 이름과 암호로 로그인하여 발급) 사용자 정의 프런트엔드용으로 역할 권한을 검증하며 구독 전송 및 직접 쓰기 지원
데이터 읽기/쓰기 채널 ws://<host>:<port>/data/rw 정적 data-rw-token(ems.yaml에서 설정) 백엔드 통합 시스템용으로 로그인에 의존하지 않는 낮은 지연의 읽기/쓰기 제공, 시스템 간 통합에 적합

두 채널의 메시지 형식은 완전히 같으며 인증 방식과 권한 검증 수준만 다릅니다. 이 페이지는 /front를 중심으로 설명하며, /data/rw에도 같은 사용 방식을 적용할 수 있습니다.

연결 시 Query 매개변수로 전달합니다.

ws://<host>:<port>/front?socketId=<사용자_정의_연결_ID>&token=<로그인_토큰>
ws://<host>:<port>/data/rw?socketId=<사용자_정의_연결_ID>&token=<data-rw-token>

요청 프레임(프런트엔드 → Edge-EMS Builder)의 공통 구조:

{
"topic": "read | write | clear",
"messageId": "응답을 매칭하는 데 사용하는 사용자 정의 고유 ID",
"keep": true,
"content": [
{ "dataKey": "pcsActivePower" }
]
}

응답 프레임(Edge-EMS Builder → 프런트엔드)은 messageId로 요청과 일대일 대응합니다.

{
"code": "200",
"message": "Processed successfully",
"topic": "read",
"messageId": "...",
"time": "2026-07-23T10:00:00+08:00",
"data": { "...": "..." }
}

3. 실시간 전송 수신 방법(구독 = read + keep)

섹션 제목: “3. 실시간 전송 수신 방법(구독 = read + keep)”

Edge-EMS Builder의 전송 모델은 “특정 Topic에 대한 발행/구독”이 아니라 각 연결이 자체 “전송 설정”을 선언하는 방식입니다.

  1. 프런트엔드가 topic: "read"를 보내고 content에 관심 있는 dataKey를 나열합니다(또는 groupCode로 데이터 그룹 전체 지정). keep는 true로 설정합니다.
  2. Edge-EMS Builder는 수신한 content를 해당 연결(socketId)의 전송 설정으로 저장하고 현재 값을 즉시 한 번 반환합니다.
  3. 이후 백그라운드 작업이 1초마다 연결된 모든 프런트엔드 소켓을 순회하여 각 전송 설정의 최신 값을 능동적으로 보냅니다. 프런트엔드가 반복 요청으로 폴링할 필요가 없습니다.
  4. 지속 전송이 필요하지 않으면 keep를 false로 설정하거나 생략합니다. 이 경우 Edge-EMS Builder는 30s 동안만 전송합니다.
  5. 연결의 전송 설정을 취소하려면 topic: "clear"를 보냅니다.
// 구독: pcsActivePower와 bmsSOC의 최신 값을 지속적으로 모니터링합니다.
{
"topic": "read",
"messageId": "sub-001",
"keep": true,
"content": [
{ "dataKey": "pcsActivePower" },
{ "dataKey": "bmsSOC" }
]
}
// 또는 그룹별로 구독합니다(하위 그룹을 포함하여 그룹 내 모든 데이터 포인트를 모니터링).
{
"topic": "read",
"messageId": "sub-002",
"keep": true,
"content": [
{ "groupCode": "pcs-group-01" }
]
}

같은 연결에서 read + keep:true를 여러 번 보내 전송 설정을 추가/갱신할 수 있습니다. dataKey와 groupCode 중 하나 이상을 입력해야 합니다.

프런트엔드가 topic: "write"를 보내고 content에 쓸 dataKey와 dataValue를 지정합니다. 데이터 유형은 지정할 필요가 없습니다. Edge-EMS Builder가 dataKey를 기준으로 원본 데이터(Collection), 집계 데이터(Aggregation) 또는 시간대별 데이터(Customization)를 자동 식별하고 해당 쓰기 로직으로 전달합니다.

{
"topic": "write",
"messageId": "ctrl-001",
"content": [
{ "dataKey": "pcsActivePower", "dataValue": "300" }
]
}

시간대별 데이터(Customization)를 쓸 때는 유효 구간을 함께 지정할 수 있습니다.

{
"topic": "write",
"messageId": "ctrl-002",
"resetCustomValue": true,
"content": [
{
"dataKey": "import_price",
"dataValue": "0.85",
"availableDate": "2026-06-25T00:00:00+08:00",
"expiredDate": "2026-06-25T08:00:00+08:00"
}
]
}
  • resetCustomValue: true는 해당 시간대별 데이터의 기존 값을 먼저 비우고 이번 내용을 기록한다는 의미입니다. “전체 계획 교체” 시 새 시간대와 기존 시간대의 중복 충돌을 방지합니다.
  • 다른 공유 장치의 데이터 포인트에 쓰려면 해당 content 요소에 deviceCode 필드를 추가합니다. Edge-EMS Builder가 공유 데이터 쓰기로 식별하여 대상 장치로 전달합니다.
  • /data/rw에 정적 data-rw-token로 연결하면 역할을 검증하지 않고 바로 허용하므로 백엔드 시스템 간 통합에 적합합니다.
  • /front에 로그인 Token으로 연결하면 계정 역할이 슈퍼 관리자, 관리자 또는 운영자인지 확인합니다. 관찰자는 쓸 수 없습니다. 페이지의 권한 규칙과 동일하므로 “프런트엔드 권한을 우회하는” 쓰기 경로는 없습니다.

DAILY_REPEAT는 availableTime / expiredTime(예: 08:00:00~12:00:00)를 사용하며 전체 유효 날짜는 시간대별 데이터 포인트에 설정합니다. 전체 날짜·시간 구간은 DAILY_CHANGE에만 사용합니다. 두 매개변수 유형을 혼용할 수 없으며 자정을 넘는 시간대는 나누어야 합니다.

/front는 Ping/Pong 하트비트로 연결을 확인합니다. 클라이언트는 종료 code/message를 처리하고 재연결 후 구독을 다시 설정해야 합니다. 쓰기 요청을 구독과 함께 자동 재전송하지 마세요.

장치 쓰기는 CommBridge 응답 또는 제한 시간 초과 결과를 기준으로 판단해야 하며, 전송 성공만으로 실행을 확정해서는 안 됩니다. 장치 제어 로그로 처리 중, 성공 및 실패를 확인할 수 있습니다. 수집 데이터 쓰기 계수는 CommBridge가 역산하므로 호출자가 중복 환산하지 않습니다.

유지보수 잠금 중 보호되는 제어 쓰기는 200162를 반환합니다. 재시도를 위해 잠금을 자동 해제해서는 안 됩니다.

const ws = new WebSocket(`ws://<host>:<port>/front?socketId=web-ui-01&token=${loginToken}`);
ws.onopen = () => {
// 실시간 데이터 구독: PCS 유효 전력과 SOC를 모니터링합니다.
ws.send(JSON.stringify({
topic: 'read',
messageId: 'sub-' + Date.now(),
keep: true,
content: [
{ dataKey: 'pcsActivePower' },
{ dataKey: 'bmsSOC' }
]
}));
};
ws.onmessage = (event) => {
const resp = JSON.parse(event.data);
if (resp.topic === 'read') {
// 매초 최신 값을 수신하여 화면의 대시보드/차트를 갱신합니다.
renderDashboard(resp.data);
}
if (resp.topic === 'write') {
// 쓰기 결과 피드백: resp.successContent / resp.errorContent
showWriteResult(resp);
}
};
// 사용자가 화면에서 "전력을 300kW로 설정" 버튼을 클릭할 때:
function setPower(value) {
ws.send(JSON.stringify({
topic: 'write',
messageId: 'ctrl-' + Date.now(),
content: [{ dataKey: 'pcsActivePower', dataValue: String(value) }]
}));
}

이 코드는 “페이지 로드 시 실시간 데이터 구독 → 매초 화면 자동 갱신 → 사용자 조작으로 제어 명령 직접 전달 → 쓰기 결과 피드백 수신”의 일반적인 프런트엔드 상호작용 폐루프 전체를 구현합니다. 별도의 백엔드 중계 서비스는 필요하지 않습니다.

  • /front 또는 /data/rw로 기록한 데이터는 트리거 구성과 표현식 엔진이 읽는 동일한 데이터입니다. 프런트엔드 제어의 최종 결과를 장치에서 다시 수집하면 집계 표현식과 트리거 판단에 계속 사용할 수 있습니다. 데이터 모델을 참조하세요.
  • 사용자 정의 화면뿐 아니라 클라우드 대시보드에도 데이터를 보내야 한다면 두 경로를 함께 사용할 수 있습니다. /front로 자체 프런트엔드에 서비스를 제공하면서 MQTT 클라우드와 엣지 협업으로 같은 데이터를 클라우드에 보고할 수 있습니다.
  • aems-write-data AI 설정 Skill은 /data/rw 및 /front 호출 방식을 캡슐화하여 자연어로 AI Agent에게 쓰기 명령 디버깅을 요청할 수 있습니다.
  • 전체 HTTP/WebSocket 인터페이스 목록은 Edge-EMS Builder 기술 지원에 《Edge-EMS Builder HTTP 인터페이스 문서》를 요청하여 확인할 수 있습니다.
  • 기록한 데이터를 표현식 엔진과 트리거 구성이 사용하는 방법: 표현식 엔진, 트리거 구성
  • 장치 간 쓰기에 필요한 공유 대상 메커니즘: 장치 협업 및 공유 대상