본문으로 건너뛰기

2. 도메인 ID 규칙

PlantPulse의 모든 마스터 데이터는 서버측 정규식 검증을 따릅니다. 서버가 정규식 매칭을 강제하며, 위반하면 E1002 (VALIDATION_FAILED)로 거절됩니다. 정확한 ID 설계는 확장 가능한 IoT 시스템 구축의 출발점입니다.

📖 플랫폼 전반의 컨벤션 요약은 도메인 ID 네이밍 컨벤션 페이지를 참고하세요.

2.1 한눈에 보는 ID 규칙 (서버 정규식 기준)​

새 ID 는 «접두 풀네임 + 번호»입니다. 플랫폼이 스스로 발급할 때는 접두 뒤에 여섯 자리 번호를 붙입니다 (SITE_000001, TAG_000001). 도메인마다 시퀀스가 따로이고, 999999 를 넘으면 자릿수가 늡니다(ORDER_1000000).

도메인정식 접두옛 접두(계속 유효)발급 예
기업 (Enterprise)ENTERPRISE_—ENTERPRISE_000001
사이트 (Site)SITE_—SITE_000001
구역 (Area)AREA_ASSET_AREA_000001
라인 (Line)LINE_ASSET_LINE_000001
설비 (Equipment)EQUIPMENT_ASSET_EQUIPMENT_000001
연결 (OPC)OPC_—OPC_000001
태그 (Tag)TAG_—TAG_000001
엣지 (Edge)EDGE_—발급하지 않음 — EDGE_LINE1_01 처럼 직접 짓습니다
알람 설정 (AlarmConfig)ALARM_CONFIG_다섯 자리 번호, ALARM_CONFIG_TAG_…ALARM_CONFIG_000001
직원 (Employee)EMPLOYEE_EMP_EMPLOYEE_000001
고객 (Customer)CUSTOMER_—CUSTOMER_000001
제품 (Product)PRODUCT_—PRODUCT_000001
플로우 (Flow)FLOW_—FLOW_000001
워크오더 (Order)ORDER_ORD_ORDER_000001
캘린더 (Calendar)CALENDAR_CAL_CALENDAR_000001
교대 예외 (Shift override)SHIFT_OVERRIDE_OV_SHIFT_OVERRIDE_000001
공정 세그먼트 (Process segment)PROCESS_SEGMENT_SEG_PROCESS_SEGMENT_000001
설비 관계 (Asset relation)ASSET_RELATION_REL_ASSET_RELATION_000001
설비 명령 (Asset command)ASSET_COMMAND_—ASSET_COMMAND_000001
PLC 명령 영역 (Command area)COMMAND_AREA_CMDAREA_COMMAND_AREA_000001
이미지 (Image)IMAGE_IMG_IMAGE_000001
정비 오더 (Maintenance order)MAINTENANCE_ORDER_숫자만MAINTENANCE_ORDER_000001
로그인 계정 (User)없음—발급하지 않음 — ^[A-Z][A-Z0-9_]*$

접두 뒤에는 대문자·숫자·_ 만 옵니다([A-Z0-9_]+). 정비 오더·알람 설정의 새 ID 는 여섯 자리 이상의 번호만 받습니다.

층은 ID 가 아니라 asset_type 으로 판단합니다

옛 ID 는 다시 키를 매기지 않으므로 앞으로도 계속 나옵니다. ASSET_ 하나가 구역·라인·설비에 두루 쓰였기 때문에, 접두를 보고 층을 추측하면 틀립니다. 층은 언제나 asset_type(E · S · A · L · M)을 읽으십시오.

접두로 도메인을 가를 때는 가장 긴 접두가 주인입니다 — ASSET_RELATION_… 과 ASSET_COMMAND_… 은 ASSET_ 이 아니고, SHIFT_PATTERN_… 은 SHIFT_ 가 아닙니다.

ID 를 직접 주면 그대로 저장됩니다

ID 를 주고 만들면 형식 검사만 통과하면 준 값이 그대로 저장됩니다. 번호를 발급하는 것은 ID 를 비워 둔 경우입니다. V5 에서 비워 둘 수 있는 것은 구역·라인·설비(asset_id)와 캘린더(calendar_id)이고, POST /api/v5/site 는 site_id 를 반드시 받습니다. 옛 접두로 새로 만드는 것도 구역·라인·설비는 받습니다.

번호는 연속이 아닙니다 — 거절된 생성이 번호를 쓰고 지나가므로 빈 번호가 생기는 것이 정상입니다.

⚠️ 검증 강제: 서버는 위 규칙을 검사합니다. 위반하면 HTTP 422, 코드 E1002 로 거절됩니다. 같은 ID 가 이미 있으면 HTTP 409 입니다.

⚠️ 풀네임이 정답입니다: CUST_, PROD_ 는 어느 시절에도 유효한 접두가 아니므로 거부됩니다. EMP_, ORD_, CAL_ 은 이미 있는 ID 를 위한 옛 접두이고, 새로 지을 때는 풀네임을 쓰십시오.

⚠️ opc_id 는 OPC_ 하나뿐입니다: 엣지 출처든 외부 API 채널이든 OPC_ 로 시작해야 합니다. EDGE_·TEST_·API_ 는 거부됩니다 — 엣지 출처는 OPC_EDGE_00303 처럼 OPC_ 뒤에 붙입니다. edge_id 는 별개 도메인이고 EDGE_ 만 받습니다.

2.2 Site ID​

정규식: ^SITE_[A-Z0-9_]+$
허용: SITE_… 만

가상·집계용 사이트도 접두는 같습니다 — SITE_V_… 처럼 접두 안에서 이름을 짓습니다. 별도 패턴이 아니라 이름 관례입니다.

예시​

import plantpulse.api.v5.dto.request.SiteRequestV5;
import plantpulse.api.v5.dto.response.SiteResponseV5;

SiteRequestV5 req = new SiteRequestV5();
req.setSite_id("SITE_DJ"); // 대전공장
req.setSite_name("대전 1공장");
req.setLat("36.3504");
req.setLng("127.3845");

SiteResponseV5 created = client.site().create(req);
if (created == null) {
System.err.println("등록 실패 — site_id 정규식 확인 필요");
}

// 가상 사이트 (테스트·집계용) — 접두는 SITE_ 그대로
SiteRequestV5 vsite = new SiteRequestV5();
vsite.setSite_id("SITE_V_AGG_01");
vsite.setSite_name("집계 가상 사이트");
client.site().create(vsite);

❌ 잘못된 예 (모두 V5에서 null 반환)​

req.setSite_id("DJ_FACTORY"); // ❌ SITE_ 누락 → E1002
req.setSite_id("site_00001"); // ❌ 소문자 → E1002
req.setSite_id("00001"); // ❌ 접두사 없음 → E1002
req.setSite_id("VSITE_01"); // ❌ V 변형 접두는 없다 → E1002

2.3 OPC ID​

정규식: ^OPC_[A-Z0-9_]+$
허용: OPC_… 만

OPC ID는 데이터 수집의 물리·논리적 출처를 나타냅니다. 같은 OPC 타입 안에서도 의미 분리가 필요할 때는 _ 로 계층을 표현합니다.

OPC Type 별 권장 패턴​

OPC Type용도권장 ID 접두
OPCOPC UA / OPC DA 서버OPC_<UA서버이름>_<번호>
PLCSiemens, Mitsubishi, Allen-Bradley 등OPC_PLC_<라인>_<번호>
MODBUSModbus TCP/RTU 디바이스OPC_MB_<장비>_<번호>
DATABASE외부 RDBMS 폴링OPC_DB_<시스템>
FILEREST API/파일 임포트OPC_FILE_<시스템>_<번호>
VIRTUAL테스트·시뮬레이션OPC_TEST_VIRTUAL_<번호>
Edge Gateway엣지 게이트웨이 (별도 edge_id 도메인 — ^EDGE_[A-Z0-9_]+$)EDGE_<위치>_<번호>

⚠️ 외부 API/파일 채널이든 테스트용이든 OPC 도메인에서는 OPC_FILE_… · OPC_TEST_… 형태를 사용하세요.

예시​

import plantpulse.api.v5.dto.request.OPCRequestV5;

OPCRequestV5 req = new OPCRequestV5();
req.setOpc_id("OPC_PLC_L1_001");
req.setOpc_name("Line1 메인 PLC");
req.setOpc_protocol("S7"); // 프로토콜 코드 — 소스 유형(PLC)은 여기서 파생
req.setSite_id("SITE_DJ");
req.setOpc_source_ip("192.168.10.50");
req.setOpc_source_port(102);
client.opc().create(req);

// ERP 연동 — REST API 로 들어오는 연결
OPCRequestV5 erp = new OPCRequestV5();
erp.setOpc_id("OPC_API_ERP_001");
erp.setOpc_protocol("API");
erp.setSite_id("SITE_DJ");
client.opc().create(erp);

// 테스트·시뮬레이션
OPCRequestV5 testOpc = new OPCRequestV5();
testOpc.setOpc_id("OPC_TEST_001"); // TEST_ 로 시작하면 E1002
testOpc.setOpc_protocol("API");
testOpc.setSite_id("SITE_DJ");
client.opc().create(testOpc);

// 엣지 자기감시 연결 OPC_SYSTEM_<edge_id> 는 엣지가 sync 로 선언한다 — 직접 만들지 않는다.

2.4 Edge ID​

정규식: ^EDGE_[A-Z0-9_]+$

edge_id는 엣지 게이트웨이 디바이스 식별자로 opc_id 와 다른 도메인입니다.

req.setEdge_id("EDGE_LINE1_01");
// req.setEdge_id("OPC_EDGE_00303"); // ❌ OPC_ 는 edge_id 의 접두사가 아니다
// req.setEdge_id("TEST_EDGE_01"); // ❌ TEST_ 는 어느 도메인에서도 접두사가 아니다

2.5 Asset ID — 계층 구조의 핵심​

설비 계층은 기업 → 사이트 → 구역 → 라인 → 설비 의 다섯 층입니다(ISA-95). 다섯 층이 모두 자산 트리의 노드이고, 노드의 층은 asset_type 이 말합니다.

asset_type층정규식
E기업 (Enterprise)^ENTERPRISE_[A-Z0-9_]+$
S사이트 (Site)^SITE_[A-Z0-9_]+$
A구역 (Area)^(AREA_[A-Z0-9_]+|ASSET_[A-Z0-9_]+)$
L라인 (Line)^(LINE_[A-Z0-9_]+|ASSET_[A-Z0-9_]+)$
M설비 (Equipment, Machine)^(EQUIPMENT_[A-Z0-9_]+|ASSET_[A-Z0-9_]+)$

💡 EQUIPMENT 코드가 M인 이유: Equipment = Machine. 검색·필터링에서 짧고 명확한 한 글자 코드를 사용합니다.

기업은 설치마다 하나뿐입니다. 둘째를 만들거나 하나뿐인 기업을 지우면 409(enterprise.only_one)로 거절됩니다 — Site 서비스 를 보십시오.

노드의 층은 바꿀 수 없습니다. 구역을 라인으로 바꾸려는 수정은 거절되므로, 층이 틀렸다면 옮기는 것이 아니라 새로 만드십시오.

표준 ID 패턴 예시​

대전공장(SITE_DJ)의 구역 하나, 그 안의 라인 하나, 그 위의 설비 하나 — ID 를 비워 두고 만들면 이렇게 됩니다.

AREA_000001 ← 구역
└ LINE_000001 ← 라인
└ EQUIPMENT_000001 ← 설비

예시 — 자산 트리 생성​

import plantpulse.api.v5.dto.request.AssetRequestV5;
import plantpulse.api.v5.dto.response.AssetResponseV5;

// 1) 사이트 직속 Area
AssetRequestV5 area = new AssetRequestV5();
area.setSite_id("SITE_DJ");
area.setParent_asset_id("SITE_DJ"); // 부모는 사이트 ID
area.setAsset_name("조립구역");
area.setAsset_type("A");
client.asset().create(area);

// asset_id 를 비워 두면 플랫폼이 발급합니다 — 응답에서 AREA_000001 을 받습니다

// 2) Area 산하 Line
AssetRequestV5 line = new AssetRequestV5();
line.setSite_id("SITE_DJ");
line.setParent_asset_id("AREA_000001"); // 부모는 Area
line.setAsset_name("1라인");
line.setAsset_type("L");
client.asset().create(line);

// 3) Line 산하 Equipment
AssetRequestV5 equip = new AssetRequestV5();
equip.setSite_id("SITE_DJ");
equip.setParent_asset_id("LINE_000001"); // 부모는 Line
equip.setAsset_name("CNC #1");
equip.setAsset_type("M");
equip.setAsset_class_code("CNC"); // 설비 분류 — 기준정보에 있는 코드
client.asset().create(equip);

parent_asset_id 규칙​

자식 타입parent_asset_id 값
구역 (A)사이트 ID (예: SITE_DJ)
라인 (L)구역의 asset_id (예: AREA_000001)
설비 (M)라인의 asset_id (예: LINE_000001)

부모는 바로 위 층이어야 합니다. 층을 건너뛰거나 다른 사이트의 노드를 부모로 주면 거절됩니다.

2.6 Tag ID​

정규식: ^TAG_[A-Z0-9_]+$
허용: TAG_… 만 (가상 태그도 TAG_ 로 시작한다)

Tag는 데이터 포인트(센서 값, 카운터, 상태 등)의 정의입니다. OPC와 1:1, Asset과는 0:N (선택적 연결) 관계입니다.

권장 패턴​

시나리오패턴예시
OPC 연결 태그TAG_<OPC ID 일부>_<채널번호>TAG_EDGE_00303_90007
자체 정의 태그TAG_<도메인>_<번호>TAG_PROD_COUNT_001
Virtual TagTAG_V_<용도>_<번호>TAG_V_OEE_LINE1

💡 Virtual Tag: 계산식·집계 태그도 접두사는 TAG_ 하나입니다. 구분이 필요하면 관례로 TAG_V_ 를 씁니다(별도 패턴이 아니라 이름 관례). 태그가 «계산된 값인가» 는 태그의 계산 설정(use_calculation)이 답합니다. 만드는 법은 가상 태그에 있습니다.

예시​

import plantpulse.api.v5.dto.request.TagRequestV5;

TagRequestV5 req = new TagRequestV5();
req.setTag_id("TAG_EDGE_00303_90007");
req.setTag_name("Spindle RPM");
req.setOpc_id("OPC_EDGE_00303");
req.setSite_id("SITE_DJ");
req.setLinked_asset_id("EQUIPMENT_000001");
req.setJava_type("Double");
req.setUnit("RPM");
req.setDescription("Spindle 회전수 (RPM)");
client.tag().create(req);

// Virtual Tag — 라인별 OEE 집계
TagRequestV5 vtag = new TagRequestV5();
vtag.setTag_id("TAG_V_OEE_LINE1");
vtag.setTag_name("Line1 OEE");
vtag.setSite_id("SITE_DJ");
client.tag().create(vtag);

tag_id vs tag_name vs alias_name​

필드V5에서 변경 가능한가용도
tag_id변경 불가 (재생성 권장)시스템 내부 고유 키. 불변
tag_nameupdate() 또는 patchBasic()사용자에게 보이는 이름
alias_namepatchMetadata()외부 시스템과 매핑하는 별칭 (예: HMI 태그명)

가능하면 tag_id는 불변으로 두고, 표시 이름은 tag_name / alias_name을 사용하시는 것을 권장합니다. tag_id 변경은 외부 시계열 데이터와의 정합성 이슈가 발생할 수 있습니다.

2.7 AlarmConfig / Employee / Customer / Product / Flow​

이 도메인들도 모두 서버 검증이 강제됩니다. 정확한 전체 접두사를 사용하세요. 알람 설정 ID 는 번호 꼴만 받습니다(ALARM_CONFIG_000001).

// Alarm Config
AlarmConfigRequestV5 alarm = new AlarmConfigRequestV5();
alarm.setAlarm_config_id("ALARM_CONFIG_000001");
alarm.setSite_id("SITE_DJ");

// Employee
EmployeeRequestV5 emp = new EmployeeRequestV5();
emp.setEmployee_id("EMPLOYEE_E12345");
emp.setRole_code("OPERATOR"); // OPERATOR, SUPERVISOR 등

// Customer — CUSTOMER_ (전체 단어, CUST_ 아님!)
CustomerRequestV5 cust = new CustomerRequestV5();
cust.setCustomer_id("CUSTOMER_001");
cust.setExternal_customer_id("ERP_C001");

// Product — PRODUCT_ (전체 단어, PROD_ 아님!)
ProductRequestV5 prod = new ProductRequestV5();
prod.setProduct_id("PRODUCT_A001");
prod.setProduct_code("SKU-A001");

// Flow
FlowRequestV5 flow = new FlowRequestV5();
flow.setFlow_id("FLOW_OEE_DAILY");
flow.setSite_id("SITE_DJ");

❌ 자주 발생하는 거부 사례​

cust.setCustomer_id("CUST_001"); // ❌ CUST_ 아님 → E1002
prod.setProduct_id("PROD_A001"); // ❌ PROD_ 아님 → E1002
emp.setEmployee_id("E12345"); // ❌ EMPLOYEE_ 누락 → E1002
flow.setFlow_id("flow_oee_daily"); // ❌ 소문자 → E1002

외부 시스템 이름 (external_system)​

고객 · 제품 · 직원의 external_system 은 1~50자의 대문자 · 숫자 · _ · : 만 받습니다(^[A-Z0-9_:]{1,50}$). : 는 그 시스템 안에서 번호를 유일하게 만드는 범위를 붙일 때 씁니다 — 예: SAP PM 의 클라이언트별 SAP_PM:100. 플랫폼은 범위를 해석하지 않고 external_system + external_id 쌍을 그대로 키로 씁니다. SAP-PM 처럼 하이픈이 들어가면 거절되고, 거절 문구가 이 허용 문자를 알려 줍니다.

2.8 User ID (로그인 계정)​

정규식: ^[A-Z][A-Z0-9_]*$
규칙: 대문자로 시작, 이후 [A-Z0-9_]만 허용

다른 도메인과 달리 고정 접두사가 없고, 첫 글자가 대문자여야 합니다.

✅ ADMIN
✅ OPERATOR_01
✅ KOPENS_USER
❌ admin → 소문자 시작
❌ 1USER → 숫자 시작
❌ _ADMIN → _ 시작

2.9 Calendar / Order​

두 도메인도 서버가 접두를 검사합니다. 캘린더는 ^(CALENDAR_[A-Z0-9_]+|CAL_[A-Z0-9_]+)$, 워크오더는 ^(ORDER_[A-Z0-9_]+|ORD_[A-Z0-9_]+)$ 입니다.

Calendar​

import plantpulse.api.v5.dto.request.CalendarRequestV5;

CalendarRequestV5 cal = new CalendarRequestV5();
// calendar_id 를 비워 두면 플랫폼이 CALENDAR_000001 꼴로 발급합니다
cal.setSite_id("SITE_DJ");
cal.setAsset_id("EQUIPMENT_000001");
cal.setTarget_type("MTN"); // 점검 일정
target_type의미
MTN정기 점검 (Maintenance)
HOLIDAY휴일/비조업
INSPECTION검사
MEETING회의

Order​

import plantpulse.api.v5.dto.Quantity;
import plantpulse.api.v5.dto.request.OrderRequestV5;

OrderRequestV5 order = new OrderRequestV5();
order.setOrder_id("ORDER_000001");
order.setSite_id("SITE_DJ");
order.setAsset_id("EQUIPMENT_000001");
order.setCustomer_id("CUSTOMER_001"); // CUSTOMER_ 주의!
order.setProduct_id("PRODUCT_A001"); // PRODUCT_ 주의!
order.setEmployee_id("EMPLOYEE_E12345");
order.setPlanned_quantity(Quantity.of("500", "EA")); // ISA-95 Quantity — 값 + UN/ECE Rec 20 단위

작업지시 상태(ISA-88): WAIT → START → END / ABORTED (자세한 내용은 Order 서비스)

2.10 ID 설계 모범 사례​

권장 사항​

  1. 불변성 유지 — 한번 부여한 ID는 변경하지 마세요. 변경이 필요하면 새 ID로 마이그레이션하시는 것을 권장합니다.
  2. ID 에 뜻을 싣지 마세요 — 층은 asset_type, 위치는 부모·경로가 말합니다. 꼭 직접 짓는다면 접두 풀네임을 지키세요.
  3. 자동 ID 를 쓰세요 — ID 를 비워 두면 플랫폼이 접두 + 여섯 자리로 발급합니다.
  4. 대문자·언더스코어 고정 — 시스템 전반에서 대소문자 혼용은 피하고 [A-Z0-9_] 패턴을 유지합니다.
  5. 외부 시스템 ID는 별도 필드 — ERP/MES 원본 ID는 external_*_id 필드에 보관하고, PlantPulse ID는 자체 체계를 유지하세요.

피해야 할 패턴​

  • ❌ 한글, 공백, 특수문자 사용 — LINE_라인1, OPC 001
  • ❌ 너무 짧거나 무의미한 ID — S1, A, T01
  • ❌ 접두사 누락 또는 잘못된 약어 — CUST_001 (정답: CUSTOMER_001), PROD_A001 (정답: PRODUCT_A001)
  • ❌ 의미가 ID에 박혀있어 변경 불가 — LINE_DJ_OLD_BROKEN_LINE

2.11 검증 실패 응답 확인​

V5는 검증 실패 시 메서드가 null (또는 false)을 반환합니다. 자세한 에러는 envelope에서 확인할 수 있습니다.

import plantpulse.api.v5.service.BaseServiceV5;
import plantpulse.json.JSONObject;

// V5 서비스 내부는 envelope를 외부에 노출하지 않으나,
// 디버그 모드를 켜면 로그에 출력됩니다.
client = new APIClient_V5(proto, host, port, user, token, true); // debug=true

응답 envelope 예시:

{
"_status": "ERROR",
"_code": "E1002",
"_message": "validation failed: site_id must match ^SITE_[A-Z0-9_]+$",
"_http_status": 422
}

자세한 에러 코드는 응답 형식 및 에러 처리 참고.

2.12 ID 를 미리 받기 — POST /api/v5/domain-ids​

객체를 만들기 전에 ID 를 알아야 하는 시뮬레이터 · 외부 시스템이 씁니다.

요청 {"domain": "EQUIPMENT", "count": 3} → 응답 {"domain": "EQUIPMENT", "ids": ["EQUIPMENT_000123", ...]}

칸값
domain도메인 이름(대소문자 무관). 시퀀스가 있는 것만 — ASSET · EDGE · SHIFT · SHIFT_PATTERN · HOLIDAY · BLOB 은 안 됩니다
count1~1000, 기본 1

ADMIN 또는 API 토큰이 필요합니다. 행을 만들지 않고 번호만 가져가므로 안 쓴 번호는 비는 것이 정상입니다. 다시 부르면 새 번호가 나옵니다(같은 Idempotency-Key 를 보내면 그렇지 않습니다).

응답뜻
400 domain_id.domain_requireddomain 없음
404 domain_id.unknown없는 도메인이거나 시퀀스가 없음
422 domain_id.count_invalidcount 가 1~1000 밖

콘솔 등록 화면의 ID 칸​

등록 화면의 ID 칸 오른쪽에 생성 아이콘(마우스를 올리면 «생성»)이 있습니다. 누르면 새 번호가 채워집니다. 직접 입력하면 대문자로 바뀌고, 칸을 벗어날 때 형식과 중복을 검사해 문제를 칸 아래에 표시합니다. 수정 화면에서는 ID 칸이 잠기고 아이콘이 없습니다. 발급 시퀀스가 없는 도메인(엣지 · 근무조 · 근무 패턴 · 휴일 · 오브젝트)에도 아이콘이 없습니다.

콘솔에서는 ID 칸이 필수입니다. 생성 을 누르거나 직접 입력합니다. 비워 두면 발급되는 칸은 공정 구간뿐입니다. 생성과 중복 검사는 ADMIN · API 역할만 쓸 수 있습니다.

다음 단계​