테이블을 수정하지 않고 엔터티에 Extended Fields를 추가하는 방법

발행: (2025년 12월 19일 오후 08:57 GMT+9)
9 min read
원문: Dev.to

I’m happy to help translate the article, but I need the actual text you’d like translated. Could you please paste the content (or the portion you want translated) here? I’ll keep the source line, formatting, code blocks, and URLs exactly as they are.

비디오 데모

Nop 플랫폼에 내장된 NopOrm 엔진을 기반으로, 어떤 엔티티에도 확장 속성을 추가하는 것이 매우 편리합니다.
이 속성들은 조회와 정렬을 지원하며, 사용 측면에서 엔티티의 내장 속성과 완전히 일관됩니다.

확장 필드 활성화 방법

  1. use‑ext‑field 태그를 Excel 데이터 모델의 데이터 테이블에 추가합니다.
  2. 시스템이 전역 확장‑필드 지원을 활성화하고 값을 nop_sys_ext_field 테이블에 저장합니다.

nop_sys_ext_field 테이블 구조

컬럼명타입
entity_nameVARCHAR
entity_idVARCHAR
field_nameVARCHAR
field_typeINTEGER
decimal_valueDECIMAL
date_valueDATE
timestamp_valueTIMESTAMP
string_valueVARCHAR

실제로 값을 저장하는 컬럼은 field_type에 따라 달라집니다 (예: decimal_value, date_value 등).

생성된 ORM 매핑

컴파일 중에 “ 태그가 use‑ext‑field 설정을 읽어 자동으로 다음 매핑을 생성합니다:

<!-- ORM mapping generated by the compiler -->

일대다 관계에서 keyProp이 설정되면 해당 속성을 컬렉션의 고유 식별자로 표시합니다. IOrmEntitySet 컬렉션은 이 키를 통해 항목에 직접 접근할 수 있는 확장 메서드 prop_get / prop_set을 제공합니다.

Java에서 확장 필드 접근하기

// read
IOrmKeyValueTable field = (IOrmKeyValueTable) entity.getExtFields()
                                                   .prop_get("fldA");

// write
entity.getExtFields().prop_set("fldA", value);

XScript / EQL에서 확장 필드 접근하기

extFields.fldA.string   // equivalent to entity.getExtFields().prop_get("fldA").getString()

fldAkeyProp에 따라 일대다 컬렉션에서 고유 레코드를 찾아 해결됩니다.


별칭을 사용하여 더 간단히 접근하기

별칭을 정의하면 확장 필드를 일반 속성처럼 사용할 수 있습니다.

<!-- Alias configuration XML -->

별칭 구성을 추가한 후:

  • extFldAextFldB는 엔터티의 일반 속성이 됩니다.
  • Java에서는 entity.prop_get("extFldA")를 사용하거나, 코드가 생성된 경우 편리한 메서드를 사용할 수 있습니다:
entity.getExtFldA();          // getter
entity.setExtFldA(value);    // setter

getter/setter가 생성되면 해당 필드에 대해 더 이상 entity.prop_get을 사용할 수 없습니다. prop_get은 엔터티에 존재하지 않는 속성에만 사용되기 때문입니다.

내장 필드와 확장 필드를 모두 일관되게 가져오려면 다음을 호출하면 됩니다:

entity.orm_propValueByName(name);

또는 BeanTool.getProperty(entity, propName)와 같은 리플렉션 유틸리티를 사용할 수 있습니다.

확장 필드를 활용한 EQL 쿼리

확장 필드는 필터링, 정렬, 프로젝션 등에서 내장 필드와 동일하게 사용할 수 있습니다:

select o.extFldA
from MyEntity o
where o.extFldA = '123'
order by o.extFldA

GraphQL 노출

xmeta 파일에 속성 정의를 추가하여 GraphQL이 확장된 필드를 조회할 수 있도록 합니다:

<!-- xmeta configuration for GraphQL -->

로컬‑Ext 테이블 (성능 최적화)

모든 확장 필드는 기본적으로 nop_sys_ext_field에 저장되며, 이로 인해 테이블이 커지고 성능이 저하될 수 있습니다.
이를 완화하려면 엔티티 테이블에 local‑ext 태그를 추가하십시오. 시스템은 해당 엔티티에 대해 쌍을 이루는 확장 필드 테이블을 생성하며, 일반적으로 다음과 같이 명명됩니다:

original_table_name + '_ext'

예시: nop_sys_notice_template_ext

로컬‑ext 테이블은 nop_sys_ext_field와 동일한 구조를 가지지만 예외적으로 entityName 컬럼을 포함하지 않습니다 (엔티티 이름으로 필터링할 필요가 없습니다).

참고: 많은 로우코드 플랫폼은 모든 데이터를 단일 수직 테이블에 저장하고, 하드코딩된 수직‑수평 변환이 필요합니다. Nop의 선택적 로컬‑ext 테이블 접근 방식은 이러한 복잡성을 피합니다.

예시: 단위 테스트 TestExtFields

<!-- Unit test configuration XML -->

모든 to‑many 관계는 컬렉션 내 레코드를 고유하게 식별하기 위해 keyProp 속성으로 구성할 수 있습니다.

ext.fldA.string 별칭은 다음과 동일합니다:

((IOrmEntitySet) entity.getExt()).prop_get("fldA").getString();

요약

  • Extended fieldsuse‑ext‑field를 통해 추가되며 nop_sys_ext_field에 저장됩니다.
  • ORM 생성기는 keyProp을 가진 to‑many 관계(extFields)를 생성합니다.
  • 접근은 prop_get/prop_set, 생성된 getter/setter, 혹은 통합된 orm_propValueByName을 통해 가능합니다.
  • Aliases는 확장 필드를 일반 속성처럼 만들어 Java, XScript, EQL, GraphQL에서 사용할 수 있게 합니다.
  • 대규모 사용을 위해서는 local‑ext를 추가하여 엔티티당 전용 테이블을 만들면 성능이 향상됩니다.

확장 필드 접근 및 코드 생성

명명 단축키

위 구성에서 extFldAext.fldA.string와 동일합니다.
이는 확장 필드에 접근하기 위해 필요한 복잡한 경로에 대한 간단한 이름을 제공합니다.

코드 생성 플래그

notGenCode가 표시된 경우, 코드 생성 시 해당 속성에 대한 Java의 getter/setter 메서드가 생성되지 않습니다.
값은 다음과 같은 메서드를 통해 얻어야 합니다:

entity.prop_get("extFldB");

통일된 접근 구문

XScript 또는 XPL 템플릿 언어에서는 확장 속성과 일반 속성에 대한 접근 구문이 동일합니다. 값을 직접 할당할 수 있습니다:

entity.extFldB = true;

EQL 자동 인식

EQL 쿼리 언어에서 keyProp은 구조 변환을 위해 자동으로 인식됩니다.


EQL 변환

간단한 속성 접근

select o.children.myKey.value
from MyEntity o

변환 결과

select u.value
from MyEntity o
left join Children u
  on o.id = u.parent_id
 and u.key = 'myKey';

컬렉션 평탄화

컬렉션에 고유 식별자가 있는 한, 언제든지 고유한 접근 경로를 가진 연관 속성으로 평탄화할 수 있습니다. ORM 엔진의 EQL 언어는 o.a.b.c와 같은 중첩 속성을 적절한 테이블‑조인 쿼리로 변환하는 작업을 처리합니다.

다중 키 필터

select o.name
from MyEntity o
where o.children.myKey1.intValue = 3
  and o.children.myKey2.strValue like 'a%';

변환 결과

select o.name
from MyEntity o
left join Children u1
  on o.sid = u1.parent_id
 and u1.key = 'myKey1'
left join Children u2
  on o.sid = u2.parent_id
 and u2.key = 'myKey2'
where u1.intValue = 3
  and u2.strValue like 'a%';

일대다 → 일대일 (키 필터 사용)

select o.key1,
       o.children.myKey1.value,
       o.children.myKey2.value
from MyEntity o;

변환 결과

select o.key1,
       u1.value,
       u2.value
from MyEntity o
left join Children u1
  on o.sid = u1.parent_id
 and u1.key = 'myKey1'
left join Children u2
  on o.sid = u2.parent_id
 and u2.key = 'myKey2';

결정적 변환 규칙

정의된 규칙에 따르면 o.children.myKey에서 관련 테이블을 추출하는 것은 수학적 수준에서 결정적 로컬 변환에 해당합니다. ORM 엔진은 이 규칙을 자동으로 적용하여 속성‑경로 표현식을 해당 SQL 조인으로 변환합니다.

Back to Blog

관련 글

더 보기 »