본문으로 건너뛰기
Paul's Dev Notes

ServiceNow CMDB Class Hierarchy 와 sys_class_path — Table Extension, Over-/Under-classification 균형 (CMDB Deep Dive #2)

cmdb_ci 의 table extension 메커니즘, sys_class_name 의 dot-walking 영향, sys_class_path 의 자식 매칭 query 패턴, Custom CI class 신설 vs 기존 활용의 결정 기준. CMDB Deep Dive 시리즈 #2.

· note cmdb
검증 인스턴스: OOTB Australia · 검증일 2026-05-24

개요

CMDB Deep Dive #1 에서 over-classification(불필요한 Custom class 폭증으로 schema 복잡도와 GlideRecord join 비용이 증가하는 문제)과 under-classification(모든 CI 를 상위 generic class 에 몰아넣어 Identification Rule 동작이 저하되는 문제)을 소개했다. 그 균형점을 잡으려면 먼저 class hierarchy 의 데이터 모델 토대를 정밀하게 이해해야 한다 — 본 글은 그 토대를 다룬다. 핵심 세 축은 table extension 메커니즘, sys_class_name 의 query 영향, sys_class_path 의 자식 매칭 패턴이다.

두 가지 메커니즘이 class hierarchy 의 query 동작을 결정한다. 첫째, ServiceNow 의 table extension(테이블 확장) — 자식 class 는 부모 테이블을 확장하므로, 부모 테이블에 GlideRecord 쿼리를 실행하면 모든 자식 class 의 CI 도 결과에 자동 포함된다. 둘째, sys_class_path 의 STARTSWITH 패턴 — 각 class 는 자신의 상속 경로를 인코딩한 path 값을 가지며, 하위 class 의 path 는 항상 부모 class 의 path 로 시작한다. 이 특성을 이용해 단일 쿼리로 특정 class 의 모든 하위 트리를 매칭할 수 있다.

Custom CI class 신설 여부는 “데이터 모델 차이”와 “라이프사이클 분리” 기준으로 판단한다. 이 기준은 §4 에서 4가지 관점으로 상세히 다룬다. over-/under-classification 균형 판단의 핵심 결정 도구다.

OOTB(Out-of-the-Box, 기본 제공) Australia 기준입니다. 인스턴스 버전·플러그인 구성에 따라 동작이 달라질 수 있습니다.


§1 — Table Extension 메커니즘

ServiceNow CMDB 의 class hierarchy 는 단순한 분류 체계가 아니라 실제 데이터베이스 테이블 상속 구조다. cmdb 가 최상위 base table 이고, 그 위에 cmdb_ci 가 extension 테이블로 존재한다. 구체 class 는 이 cmdb_ci 를 출발점으로 깊어진다. OOTB 표준 계층의 예를 들면: cmdb_ci_hardwarecmdb_ci_computercmdb_ci_servercmdb_ci_win_server 같은 깊이로 이어진다. 각 단계는 실제 DB 테이블이 생성된다.

“extends” 관계는 세 가지 영향을 가진다. Schema: 자식 class 는 부모 테이블의 모든 필드를 그대로 상속한다. cmdb_ci_servercmdb_ciname, serial_number, ip_address 등을 별도 정의 없이 사용할 수 있다. 자식 class 는 거기에 자신만의 추가 필드를 더한다 — cmdb_ci_win_server 라면 Windows 전용 필드가 추가된다. Data: 자식 class 의 CI 레코드는 부모 테이블과 자식 테이블에 분산되어 저장된다 — 공통 필드는 부모 테이블에, 자식 전용 필드는 자식 테이블에. Query: 부모 테이블에 GlideRecord 쿼리를 실행하면 자식 class 의 CI 도 자동으로 결과에 포함된다. 이 동작이 §2 에서 다루는 핵심 패턴이다.

GlideTableHierarchy API 는 이 계층 구조를 코드로 탐색하는 수단을 제공한다. getRoot() 는 최상위 base table 명을 반환하고, getBase() 는 직계 부모 테이블 명을 반환한다. class 계층을 동적으로 탐색해야 하는 케이스 — 예를 들어 특정 class 의 모든 하위 class 목록을 런타임에 조회해야 할 때 — 에 유용하다. 다만 이 API 의 구체 동작은 인스턴스 버전에 따라 차이가 있을 수 있으므로 공식 문서 확인을 권장한다.

cmdb_cicmdb_ci_hardwarecmdb_ci_computercmdb_ci_servercmdb_ci_win_serverCustom class 예시

§2 — sys_class_name 의 의미와 parent query 자동 포함

sys_class_name 은 CI 레코드가 실제로 어느 class 에 속하는지를 저장하는 필드다. cmdb_ci_server 레코드라면 sys_class_name = 'cmdb_ci_server', cmdb_ci_win_server 레코드라면 sys_class_name = 'cmdb_ci_win_server' 가 된다. 이 필드는 class 계층 어디서 실제 레코드가 생성됐는지를 정확하게 가리킨다.

부모 테이블에 GlideRecord 쿼리를 실행하면 자식 class 의 CI 도 결과에 자동으로 포함된다. 예를 들어 new GlideRecord('cmdb_ci') 로 쿼리하면, cmdb_ci_server, cmdb_ci_computer, cmdb_ci_win_server 등 모든 하위 class 의 CI 가 결과에 들어온다. 이 동작은 의도적으로 활용할 수도 있고, 의도치 않은 결과를 낳을 수도 있다. 예를 들어 Incident 의 Affected CI 필드에 연결된 CI 가 cmdb_ci_computer class 인데, cmdb_ci 테이블을 쿼리하면 그 CI 도 결과에 포함된다 — 원하는 동작일 수도, 필터가 빠진 실수일 수도 있다.

sys_class_name 으로 결과를 좁히는 것이 가장 직접적인 필터 방법이다. 단일 class 만 조회하고 싶다면 addQuery('sys_class_name', 'cmdb_ci_server') 처럼 정확한 class 명을 조건으로 추가하면 된다. 반면 특정 class 의 하위 트리 전체를 한꺼번에 조회하고 싶다면 sys_class_path STARTSWITH 패턴을 활용하는 것이 더 효율적이다 — 이는 §3 에서 다룬다.

dot-walking 시 자식 class 의 추가 필드에 접근하는 방식도 주의가 필요하다. 부모 테이블(cmdb_ci)을 통해 쿼리하면 공통 필드는 바로 접근되지만, cmdb_ci_win_server 전용 필드는 해당 레코드가 실제로 cmdb_ci_win_server class 여야 존재한다. class 를 잘못 판단하고 자식 전용 필드에 dot-walk 하면 빈 값이 반환될 수 있다. 인스턴스 구성에 따라 동작 차이가 있을 수 있으므로 직접 검증을 권장한다.

아래는 세 가지 query 패턴을 비교한 코드 예시다.

// 패턴 1: 직접 class 테이블 쿼리 — 해당 class 레코드만 반환
var gr1 = new GlideRecord('cmdb_ci_server');
gr1.addQuery('operational_status', 1);
gr1.query();

// 패턴 2: 부모 테이블 + sys_class_name 필터 — 단일 class 지정
var gr2 = new GlideRecord('cmdb_ci');
gr2.addQuery('sys_class_name', 'cmdb_ci_server');
gr2.addQuery('operational_status', 1);
gr2.query();

// 패턴 3: sys_class_path STARTSWITH — 자식 트리 전체 매칭
// cmdb_ci_server 의 sys_class_path 예: '/!!/!E/!9/!!'
// (정확한 값은 인스턴스에서 확인 필요)
var gr3 = new GlideRecord('cmdb_ci');
gr3.addQuery('sys_class_path', 'STARTSWITH', '/!!/!E/!9');
gr3.addQuery('operational_status', 1);
gr3.query();

§3 — sys_class_path 의 포맷과 자식 매칭

sys_class_path 는 각 class 의 상속 경로를 인코딩한 문자열 필드다. 2자리 문자열들을 / 로 구분하여 이어 붙인 형태로, 예를 들어 cmdb_ci_computer 의 경우 /!!/!E/!! 같은 값을 가진다. 각 2자리 세그먼트가 계층의 한 단계를 나타내며, 세그먼트가 늘어날수록 계층이 깊어진다. 정확한 인코딩 방식과 세그먼트 값은 인스턴스 및 버전에 따라 차이가 있을 수 있으므로, 실제 사용 전 본인 인스턴스에서 sys_db_object 테이블의 해당 class 레코드에서 직접 확인하는 것을 권장한다.

STARTSWITH 쿼리 패턴이 이 필드의 핵심 활용법이다. 하위 class 의 sys_class_path 는 항상 부모 class 의 path 로 시작한다. cmdb_ci_server 의 path 가 /!!/!E/!9/!! 라면, cmdb_ci_win_server 의 path 는 /!!/!E/!9/!!/ 로 시작하는 더 긴 문자열이다. 따라서 부모 path 를 STARTSWITH 조건으로 걸면 단일 쿼리로 해당 class 의 모든 하위 트리를 한 번에 매칭할 수 있다. Reference Qualifier 의 재귀 GlideRecord 함정(cycle 7 글 참조)을 피하면서도 트리 전체를 조회하는 효율적인 방법이다.

sys_class_namesys_class_path 중 어느 것을 사용할지는 목적에 따라 결정한다. 단일 class 의 CI 만 조회하거나 정확한 class 를 확인해야 할 때는 sys_class_name 이 적합하다. 특정 class 를 포함한 하위 트리 전체를 한 번에 쿼리해야 할 때는 sys_class_path STARTSWITH 가 더 효율적이다.

패턴 1 — 직접 class 테이블

new GlideRecord(‘cmdb_ci_server’) 로 쿼리. 해당 class 레코드만 반환되며, 공통 필드와 자식 전용 필드 모두 직접 접근 가능. 단일 class 대상일 때 가장 명확한 방법.

패턴 2 — sys_class_name 필터

부모 테이블 cmdb_ci 에서 sys_class_name 조건 추가. 특정 class 를 정확히 지정하거나 IN 조건으로 복수 class 를 나열할 때 유용. 하위 트리 전체는 아니고 명시한 class 만 매칭.

패턴 3 — sys_class_path STARTSWITH

부모 class 의 path 를 STARTSWITH 조건으로 걸면 하위 트리 전체를 단일 쿼리로 매칭. 새 하위 class 가 추가돼도 쿼리 수정 불필요. 단, path 값은 인스턴스에서 직접 확인 필요.


§4 — Custom CI class 신설 결정 기준

CMDB CI Class Manager 에서 Custom CI class 를 신설할 때 적용할 4가지 기준을 제시한다. 이 기준을 순서대로 검토하면 over-/under-classification 균형점을 잡는 데 도움이 된다. Custom class 생성과 CMDB CI Class Manager 운영에는 admin 또는 sn_cmdb_admin role 이 필요하다.

“데이터 모델 차이” 기준: 신설 class 가 부모 class 와 의미 있게 다른 추가 필드 세트를 가져야 하는가. 추가 필드가 없거나 기존 필드에 값만 다르게 채우는 수준이라면 별도 class 가 아니라 기존 class 의 type 필드나 attribute 로 구분하는 편이 낫다. Custom class 생성은 DB 테이블 생성을 수반하므로, 필드 구분 목적만으로 class 를 늘리면 over-classification 으로 이어진다.

“라이프사이클 분리” 기준: 해당 자산의 install, move, add, change, dispose(IMACD) 단계를 부모 class 와 별도로 추적할 실질적 필요가 있는가. 라이프사이클 흐름이 부모 class 와 동일하다면 별도 class 의 실익이 크지 않다. 반면 특정 자산 유형이 고유한 배포·폐기 절차를 가지고 그것을 CMDB 에서 별도 추적해야 한다면 class 분리가 정당화된다.

“Discovery Identification Rule 분기” 기준: Discovery 가 해당 자산을 기존 부모 class 의 Identification Rule 로 정확하게 식별할 수 있는가. 만약 부모 class 의 rule 만으로는 충분히 구분이 안 되고 오류나 중복 CI 가 반복 발생한다면, 별도 class 와 전용 Identification Rule 이 근본 해결책이 될 수 있다. Discovery·IRE 와의 관계는 #4 글에서 깊이 다룬다.

“Service Mapping dedicated class” 기준: Service Mapping 토폴로지에서 해당 자산을 독립 노드로 표시할 가치가 있는가. Service Mapping 패턴이 특정 자산 유형을 별도 노드로 구분해 토폴로지에 표시해야 한다면, 전용 class 가 권장된다. 반면 부모 class 로 충분히 표현되는 자산은 class 신설 없이 처리하는 쪽이 schema 를 단순하게 유지한다.

과거 #1 글에서 언급한 under-classification 함정 — “모든 CI 를 cmdb_ci_server 에 몰아넣기” — 도 이 기준으로 점검할 수 있다. cmdb_ci_server 에 물리 서버와 가상 머신을 모두 몰아넣으면 Discovery Identification Rule 분기가 필요한 자산을 구분하지 못해 중복 CI 문제로 이어질 수 있다.


§5 — 함정·트레이드오프

Custom class 깊이 증가 — GlideRecord join 비용

class 계층이 깊어질수록 GlideRecord 쿼리가 더 많은 테이블을 join 해야 한다. 특히 공통 필드와 자식 전용 필드를 함께 참조하는 쿼리는 join 깊이에 비례해 비용이 증가한다. over-classification 이 단순히 schema 복잡도 문제가 아닌 이유.

Class 이전(re-classification) — 데이터 마이그레이션 비용

잘못 분류된 CI 를 다른 class 로 이전하려면 레코드 재생성에 가까운 작업이 필요하다. 수천 개 CI 가 잘못된 class 에 쌓인 상태라면 이전 비용은 매우 크다. 초기 class 설계가 중요한 근본 이유다.

parent table query 의 자식 자동 포함 — 의도 vs 실수

cmdb_ci 테이블을 쿼리하면 모든 하위 class CI 가 결과에 포함된다. 영향 분석이나 Health check 목적에서는 의도한 동작이지만, 좁은 범위를 조회할 의도였다면 sys_class_name 필터를 빠뜨린 실수가 된다. 특히 대량 데이터 처리 스크립트에서 이 차이는 성능에 직결된다.

Extensible flag OFF — 가드 vs 막힘 트레이드오프

CMDB CI Class Manager 에서 Extensible flag 를 OFF 로 설정하면 다른 사용자가 해당 class 를 부모로 하는 자식 class 를 만들 수 없다. 무분별한 class 신설을 막는 가드 역할을 하지만, 정당한 확장도 막히므로 class 거버넌스 정책과 함께 운용해야 한다.


§6 — 다음 글 예고

다음 글 #3 는 CI Relationship 유형과 Reference Field 의 차이를 다룬다. CMDB 에서 CI 간의 관계를 표현하는 방법은 두 가지 — cmdb_rel_ci 테이블의 CI Relationship 과 CI 레코드 내 Reference 필드 — 인데, 언제 어느 것을 선택해야 하는지의 판단 기준, 그리고 그 선택이 Service Mapping 토폴로지 표현과 CMDB Health Compliance score 에 어떻게 영향을 미치는지를 다룰 예정이다.

본 #2 글의 핵심은 세 가지다. Table extension 으로 자식 class 는 부모 필드를 자동 상속하고 부모 쿼리 결과에 자동 포함된다. sys_class_path 의 STARTSWITH 패턴으로 재귀 쿼리 없이 하위 트리를 단일 쿼리로 매칭할 수 있다. Custom class 신설은 데이터 모델 차이·라이프사이클 분리·Discovery Rule 분기·Service Mapping 노드 가치라는 4가지 기준으로 판단하면, over-/under-classification 균형을 잡는 실질적인 결정 도구가 된다.


학습 허브

이 글은 ServiceNow CMDB 완전 정리 학습 허브의 2단계(데이터 구조 — 클래스 계층)다. CMDB 의 의미·역할부터 CI Relationship·IRE·CSDM 까지 전체 경로를 허브에서 순서대로 따라갈 수 있다.


참조