Ontology & Knowledge Graph

GRAPH TYPE – 스키마 강제, 이제 더 쉬워졌습니다 (미리보기)

hsystems 2026. 6. 30. 09:01
728x90
반응형

Neo4j 데이터베이스는 스키마 선택 사항으로 유명하죠. 개발자들은 덕분에 빠르게 시작하고 실행할 수 있는 유연성을 좋아해요. 하지만 결국 프로덕션에 들어갈 시간이 오면... 규칙이 필요하답니다. 구조가 필요한 거죠. 필수 속성 없이는 엔터티가 존재하지 않도록 하고, 적용된 데이터 모델에 따라 속성이 항상 검증되도록 하는 등 데이터 품질을 강화해야 해요.

수년 동안 이 구조를 시행하는 것은 많은 제약을 의미했어요. 한 번에 하나의 특정 규칙으로 스키마를 단편화해야 했죠.

Neo4j 2026.02에 소개될 Graph Types 미리보기 기능은 스키마를 전체적으로 정의하는 방법이에요. 수십 개의 격리된 규칙을 관리하는 대신 이제 `Node` 정의, 다중 `Label` 의미 및 `Relationship` 연결과 같은 데이터베이스의 데이터 모델을 모두 읽을 수 있는 단일 구조로 선언할 수 있어요.

참고: Enterprise Edition, Infinigraph Edition 및 Neo4j Aura의 모든 계층에서 Cypher 25의 미리 보기 기능으로 Neo4j v2026.02에서 사용할 수 있어요. 미리 보기 기능은 평가 및 피드백을 위한 것이며 프로덕션 용도로는 지원되지 않아요. 구문과 기능은 미리 보기와 GA 간에 변경될 수 있다는 점 잊지 마세요. 전체 문서는 곧 공개될 예정이랍니다.

문제: 단편적인 제약 조건 관리

Graph Type 이전에는 엄격한 데이터 모델을 시행하려면 개별 제약 조건을 하나로 묶어야 했어요. 이 접근 방식은 강력하지만 모델이 성장함에 따라 번거롭고 장황해질 수 있죠.

기존 방식(개별 제약 조건) 이전에는 이를 적용하려면 모든 단일 규칙에 대해 별도의 명령을 실행해야 했어요. 즉, 엔터티를 하고 하는 거죠.

이 방법도 작동은 하지만 스키마 논리를 데이터베이스 메타데이터에 분산시켜요.

새로운 방식: Graph Types

수십 개의 격리된 규칙을 관리하는 대신 Graph Type을 사용하면 유니버스의 "강제 데이터 모델"을 간단히 선언할 수 있어요. 데이터가 `Node`의 모양, 해당 속성, 연결 방식을 설명하면 데이터베이스는 이를 시행하는 데 필요한 제약 조건을 자동으로 생성해 준답니다.

현재 미리보기에서는 다음을 지원해요. Graph Types 열기 즉, 스키마는 사용자가 정의한 내용을 검증하지만 애플리케이션 요구 사항에 따라 추가 속성이나 `Label`을 추가할 수 있다는 의미에요.

이 강제 데이터 모델의 수명 주기를 관리하기 위해 4가지 핵심 명령어를 사용해요. 즉, 생성을 위한 SET, 확장을 위한 ADD, 수정을 위한 ALTER, 일부 제거를 위한 DROP이에요.

SET, ADD, ALTER, DROP을 사용하여 강제 데이터 모델의 수명 주기를 관리하는 방법은 다음과 같아요.

1장: 제임스 T. 커크(James T. Kirk)와 엔터프라이즈(SET)

SET 명령어는 기준선을 정의해요. 데이터베이스를 초기화하거나 전체 스키마 재설정을 수행할 때 유용하죠. 2025.02에 생성된 데이터베이스가 있는 경우 빈 데이터 모델(Graph Type)을 갖는 것으로 간주되며 Graph Type은 필요에 따라 변경될 수 있어요.

우리는 깨끗한 상태에서 시작할 거예요. 함대의 핵심을 정의해야겠죠?

  • 승무원: 모든 승무원은 자신이 `Person`이라는 것을 암시해요. 그들은 *반드시* ID(`Key`)와 이름이 있어야 해요.
  • 배: 모든 선박은 *반드시* RegistryCode(`Key`)와 클래스가 있어야 해요.
  • 과제: 승무원은 선박에 배정돼요.

ALTER CURRENT GRAPH TYPE SET을 사용하여 이 기본 구조를 선언해 봅시다.

ALTER CURRENT GRAPH TYPE SET {
  // 승무원을 정의합니다.
// '=> :Person' 구문은 모든 Crew Node도 Person이어야 함을 강제합니다.
  (c:Crew => :Person {
    id   :: STRING,
    name :: STRING NOT NULL
  }) REQUIRE c.id IS KEY,

// 우주선을 정의합니다.
  (s:Ship => {
    registryCode :: STRING,
    class        :: STRING NOT NULL
  }) REQUIRE s.registryCode IS KEY,

// 할당 Relationship을 정의합니다.
// 이는 승무원을 선박에만 연결하도록 'ASSIGNED_TO'를 명시적으로 제한합니다.
  (:Crew)-[:ASSIGNED_TO => { since :: DATE }]->(:Ship)
}

새로운 기능: Graph Type은 Neo4j에서 새로운 스키마 기능을 선보여요.

  • `Relationship`의 시작 및 끝 `Node`에 `Label`을 설정할 수 있어요.
  • 하나의 `Label` 세트가 있는 `Node`에는 다른 `Label` 세트가 필요할 수 있어요.

영향: 이 명령을 실행하면 데이터베이스가 자동으로 생성됩니다. 12가지 제약 조건 속성 유형, `Node` `Key`, `Relationship`에 대한 소스/대상 유효성 검사를 포함한 데이터를 보호하는 거죠.

적용된 스키마를 테스트해 볼까요?

// ✅ 성공: 커크가 지휘권을 잡다
CREATE (enterprise:Ship {registryCode: "NCC-1701", class: "Constitution"});
CREATE (kirk:Crew:Person {id: "SC-937–0176", name: "James T. Kirk"});
MATCH (c:Crew {id: "SC-937–0176"}), (s:Ship {registryCode: "NCC-1701"})
CREATE (c)-[:ASSIGNED_TO {since: date("2265–01–01")}]->(s);

// ❌ 실패: 이상 현상
// 실패 라벨 의미: :Crew로 생성되었지만 :Person이 누락되었습니다.
CREATE (:Crew {id: "SC-000", name: "Unknown"});

// ❌ 실패: 관계 위반
// 실패 Source/Target: 선박은 다른 선박에 ASSIGNED_TO될 수 없습니다.
MATCH (s:Ship {registryCode: "NCC-1701"})
CREATE (s)-[:ASSIGNED_TO {since: date()}]->(s);

2장: 첫 번째 접촉(ADD)

우주는 팽창하고 있어요. 우리는 행성을 발견했죠. 기존 차량 기록을 방해하지 않고 레지스트리에 추가해야 해요.

행성은 복잡한 개체에요. 둘 다 CelestialBody 이면서 Location 이기도 하죠 (다중 라벨 의미).

ALTER CURRENT GRAPH TYPE ADD {
// Define Planet.
// Enforces that every Planet implies BOTH CelestialBody AND Location.
(p:Planet => :CelestialBody&Location {
name :: STRING,
coordinates :: POINT NOT NULL
}) REQUIRE p.name IS UNIQUE,
// Ships traverse to Planets.
(:Ship)-[:BOLDLY_GOES_TO =>]->(:Planet)
}

영향: 이 단일 명령을 실행하면 데이터베이스가 자동으로 추가 생성을 생성해요. 8가지 Constraint로 여러분의 데이터를 보호하기 위해서죠.

데이터:

// ✅ SUCCESS: Vulcan 차트 작성
// 모든 암시적 라벨(:CelestialBody:Location)을 추가해야 해요. 그렇지 않으면 스키마가 이를 거부할 거예요.
CREATE (vulcan:Planet:CelestialBody:Location {
name: "Vulcan",
coordinates: point({x: 2.3, y: 4.5, z: 1.1})
});
// 우주선이 행성으로 이동합니다.
MATCH (s:Ship {registryCode: "NCC-1701"}), (p:Planet {name: "Vulcan"})
CREATE (s)-[:BOLDLY_GOES_TO]->(p);

// ❌ FAILURE: Bad Coordinates
// Fails Property Type: 'coordinates' must be a POINT, not a String.
CREATE (:Planet:CelestialBody:Location {
name: "Earth",
coordinates: "Sector 001"
});

제3장: 관료주의(ALTER)

함대 사령부가 메모를 보냈어요. 분명히 승무원의 이름을 추적하는 것만으로는 충분하지 않대요. 이제 우리는 그들의 순위를 추적해야 한대요. Integer (등급)로 말이죠.

우리는 다음의 정의를 업데이트해야 해요. Crew.

⚠️ 중요한 점: ALTER를 사용할 때 다음을 제공해야 해요. 새로운 완전한 정의를 해당 요소에 대해서요. 기존에 있었던 속성이나 암시적 라벨을 생략하면 스키마가 해당 속성 적용을 중지할 거예요.

ALTER CURRENT GRAPH TYPE ALTER {
// Crew를 재정의하여 '순위'를 추가합니다.
// 강제로 유지하려면 'id'와 'name'을 반복해야 합니다.
(c:Crew => :Person {
id :: STRING,
name :: STRING NOT NULL,
rank :: INTEGER // 새로운 속성 및 속성 유형 적용
})
}

영향: 이 명령을 실행하면 데이터베이스가 자동으로 하나의 추가 생성을 생성해요. Constraint로 여러분의 데이터를 보호하기 위해서죠.

마이그레이션:

// ✅ 성공:
// 1. 기존 데이터 수정(Kirk에는 순위가 필요합니다!)
MATCH (c:Crew {id: "SC-937–0176"})
SET c.rank = 6; // Captain's Grade

// 2. 스팍 생성
CREATE (:Crew:Person {
id: "SC-937–0177",
name: "Spock",
rank: 5 // 지휘관 등급
});

// ❌ 실패: 잘못된 데이터 유형
// HR에는 문자열 코드가 아닌 정수 순위가 필요합니다.
CREATE (:Crew:Person {
id: "SC-000–000",
name: "Red Shirt",
rank: "Ensign"
});

4장: 감사(SHOW)

연맹 감사관이 도착했어요. 그들은 스키마를 보고 싶어해요. 15가지의 다양한 Constraint를 찾아다닐 필요가 없어요. 현재 시행되는 데이터 모델을 보여주기만 하면 돼요.

SHOW CURRENT GRAPH TYPE
"{
(:`Crew` => :`Person` {`id` :: STRING, `name` :: STRING NOT NULL, `rank` :: INTEGER}),
(:`Planet` => :`CelestialBody`&`Location` {`coordinates` :: POINT NOT NULL, `name` :: STRING}),
(:`Ship` => {`class` :: STRING NOT NULL, `registryCode` :: STRING}),
(:`Crew` =>)-[:`ASSIGNED_TO` => {`since` :: DATE}]->(:`Ship` =>),
(:`Ship` =>)-[:`BOLDLY_GOES_TO` =>]->(:`Planet` =>),
CONSTRAINT `constraint_9b6f6d22` FOR (`n`:`Crew` =>) REQUIRE (`n`.`id`) IS KEY,
CONSTRAINT `constraint_fb3b8b63` FOR (`n`:`Planet` =>) REQUIRE (`n`.`name`) IS UNIQUE,
CONSTRAINT `constraint_23f2a288` FOR (`n`:`Ship` =>) REQUIRE (`n`.`registryCode`) IS KEY
}"

그러면 전체 데이터 모델에 대해 사람이 읽을 수 있는 단일 사양 문자열이 반환돼요.

5장: 규제 완화(삭제)

BOLDLY_GOES_TO 관계가 너무 제한적인 것 같다는 의견이 있었어요. 그래서 연방에서 이제는 배가 다음 지역으로 횡단하는 것을 허용하려고 해요. any 행성뿐만 아니라 Node (우주 정거장, 소행성, 이상 현상)도 포함해서요.

그래서 이 관계에 대한 스키마 적용을 중단하기로 결정했답니다.

ALTER CURRENT GRAPH TYPE DROP {
// Drop the strict relationship definition.
// The data remains, but the rules are gone.
()-[:BOLDLY_GOES_TO =>]->()
}

영향: 이 명령을 실행하면 데이터베이스가 자동으로 2개의 제약 조건(Constraint)을 제거해요.

이제 BOLDLY_GOES_TO는 규제되지 않아요. ShipShip에 연결하거나, Crew를 에 연결할 수도 있어요. 이 관계 유형을 사용하면 데이터베이스가 사용자를 막지 않죠.

요약: 그래프 유형의 전략적 이점

Graph Type을 도입한다는 건, 격리된 규칙 관리에서 통합 스키마 전략 구현으로의 전환을 의미해요.

  • 통합 스키마 정의: 수많은 단절된 제약 조건을 유지하는 대신, GRAPH TYPE을 사용하면 일관성 있는 단일 사양으로 전체 데이터 모델을 선언할 수 있어요. 이렇게 하면 스키마 관리가 중앙 집중화되어서 가독성과 유지 관리성이 향상되죠.
  • 고급 데이터 무결성: 이 기능은 기존 속성 제약 조건을 뛰어넘는 유효성 검사 기능을 도입해요. 특히 관계 요소 유형이 연결 및 Node 레이블 의미에 대해 유효한 소스 및 대상 Node를 엄격하게 정의하도록 강제하여 특정 레이블(예: Crew)이 자동으로 상위 레이블(예: Person)이 필요하도록 보장하죠.
  • 유연한 거버넌스: 개방형 GRAPH TYPE을 활용함으로써 시스템은 그래프의 유연한 스키마 특성을 희생하지 않고 정의된 엔터티에 대한 엄격한 유효성 검사 계층을 설정해요. 이렇게 하면 데이터 모델의 임시 발전을 허용하는 동시에 핵심 데이터 품질을 보장할 수 있답니다.

피드백

나만의 세상을 만들 준비 되셨나요? GRAPH TYPE은 Neo4j 2026.02의 공개 미리보기 및Neo4j 아우라graphtype@neo4j.com. 여러분의 사용 사례에 대한 피드백은 다음 릴리스가 여러분의 요구 사항을 충족하는지 확인하는 데 큰 도움이 된답니다.

그래프 유형 문서는 여기에서 찾을 수 있어요.


  • 데이터 모델링
  • GQL

에이치시스템즈LogTree는 Neo4j 기반 GraphRAG 플랫폼으로, 데이터를 자동으로 지식그래프화하고 자연어 질의로 즉시 답을 제공합니다.

👉 에이치시스템즈 홈페이지

728x90
반응형