엔티티 정의
@QfEntity 기본 선언
엔티티는 클래스에 @QfEntity 어노테이션을 선언하여 정의합니다.
java
@QfEntity(
appKey = "app", // 클라이언트 앱 키 (선택 사항; 단일 @QfClientApp이 선언된 경우 자동 사용)
name = @QfI18n(
defaultMessage = "Product",
texts = { @QfI18nText(locale = "ko", message = "상품") }
)
)
public class ProductEntity {
// 필드 선언...
}@QfEntity 주요 속성
| 속성 | 타입 | 필수 | 설명 | 기본값 |
|---|---|---|---|---|
appKey | String | 소속 클라이언트 앱 키 | "" (선언된 단일 앱 사용) | |
name | @QfI18n | 다국어 표시명 | @QfI18n(texts = {}) | |
autoHistoryEnabled | boolean | 자동 변경 이력 활성화 | false | |
treePolicy | @QfTreePolicy | 트리 구조 설정 | @QfTreePolicy (비활성) | |
organizationPolicy | @QfOrganizationPolicy | 조직 기반 데이터 필터링 | @QfOrganizationPolicy(enabled = false) | |
deletePolicy | @QfCrudPolicy | 삭제 작업 정책 | @QfCrudPolicy(enabled = false) | |
capabilityKey | String | 접근 제어용 Capability 키 | "" (클래스명에서 도출) | |
excelDownloadable | boolean | Excel 내보내기 활성화 | true | |
excelUploadable | boolean | Excel 가져오기 활성화 | true |
속성 선언
필드에 화면 및 동작 어노테이션을 직접 선언하여 관리합니다. @QfField 마커 어노테이션은 존재하지 않으며 — 각 어노테이션이 특정 기능을 제어합니다.
java
@QfEntity(
name = @QfI18n(defaultMessage = "Product", texts = {})
)
public class ProductEntity {
// 목록 + 상세 + 등록 + 수정
@QfListAttribute(sortable = true)
@QfDetailAttribute
@QfCreateAttribute(requiredOn = @QfRequiredOn(always = true))
@QfUpdateAttribute
@QfDisplayLabel(text = "Product Name")
private String name;
// 목록 + 상세 전용 (읽기 전용 가격)
@QfListAttribute
@QfDetailAttribute
@QfDisplayLabel(text = "Price")
private Integer price;
// 상세 + 등록 + 수정 (textarea 컨트롤)
@QfDetailAttribute
@QfCreateAttribute
@QfUpdateAttribute
@QfControlType(QfControlType.Type.textarea)
@QfDisplayLabel(text = "Description")
private String description;
}화면 노출 어노테이션
| 어노테이션 | 설명 |
|---|---|
@QfListAttribute | 목록 화면에 컬럼으로 표시 |
@QfDetailAttribute | 상세 조회 화면에 표시 |
@QfCreateAttribute | 등록 폼에 입력 필드로 표시 |
@QfUpdateAttribute | 수정 폼에 입력 필드로 표시 |
@QfSearch | 목록 화면에서 검색 조건으로 활성화 |
@QfDisplayLabel | 표시 레이블 설정 (i18n 조회 키 또는 리터럴 텍스트) |
필수 필드
필수 규칙은 requiredOn을 통해 @QfCreateAttribute 또는 @QfUpdateAttribute 안에 선언합니다:
java
// 항상 필수
@QfCreateAttribute(requiredOn = @QfRequiredOn(always = true))
@QfUpdateAttribute(requiredOn = @QfRequiredOn(always = true))
@QfDisplayLabel(text = "Name")
private String name;
// type == 'BUSINESS'인 경우에만 필수
@QfCreateAttribute(
requiredOn = @QfRequiredOn(
conditions = @QfConditionExpr(expr = "type == 'BUSINESS'")
)
)
@QfDisplayLabel(text = "Business Registration Number")
private String businessNumber;@QfCrypto 암호화
민감 데이터 필드에 @QfCrypto를 선언하면 저장/조회 시 자동으로 암호화/복호화가 처리됩니다.
java
@QfDetailAttribute
@QfCreateAttribute
@QfCrypto // 자동 암호화
@QfControlType(QfControlType.Type.email)
@QfDisplayLabel(text = "Email")
private String email;
@QfDetailAttribute
@QfCreateAttribute
@QfUpdateAttribute
@QfCrypto
@QfDisplayLabel(text = "Phone")
private String phone;암호화 필드 제약
@QfCrypto가 선언된 필드는 @QfSearch(검색)와 함께 사용할 수 없습니다. 암호화된 데이터는 서버 사이드 LIKE 검색이 불가하기 때문입니다. 컴파일 시점에 오류로 처리됩니다.
조직 정책
조직 기반 데이터 필터링은 엔티티의 organizationPolicy에서 설정하며, 조직 식별자를 보유하는 속성을 지정합니다.
java
@QfEntity(
name = @QfI18n(defaultMessage = "Order", texts = {}),
organizationPolicy = @QfOrganizationPolicy(
enabled = true,
attribute = "orgId" // 조직 ID를 담고 있는 필드명
)
)
public class OrderEntity {
private String orgId; // 조직 필터 대상 (organizationPolicy.attribute에 선언)
@QfListAttribute
@QfDisplayLabel(text = "Order Number")
private String orderNumber;
@QfListAttribute
@QfDisplayLabel(text = "Order Amount")
private Long amount;
}조직 정책이 활성화된 엔티티는:
- 조회 시 현재 사용자의 조직 범위에 해당하는 데이터만 자동 반환
- 등록 시 현재 사용자의 조직 ID 자동 설정
- 별도의 WHERE 절 작성 불필요
Capability 키
capabilityKey를 설정하여 엔티티를 Capability에 연결합니다:
java
@QfEntity(
name = @QfI18n(defaultMessage = "Product", texts = {}),
capabilityKey = "product-management" // @QfCapability의 key와 일치해야 함
)
public class ProductEntity {
// ...
}마스터-디테일 관계
masterRelation을 통해 엔티티가 마스터 엔티티의 디테일임을 선언합니다:
java
// 주문 항목 (Detail 엔티티)
@QfEntity(
name = @QfI18n(defaultMessage = "Order Item", texts = {}),
masterRelation = @QfMasterRelation(
enabled = true,
masterEntityFqcn = "com.example.OrderEntity", // 마스터 클래스의 전체 패키지명
masterKeyAttribute = "orderId", // 마스터 ID를 담는 이 엔티티의 속성
onMasterDelete = QfMasterRelation.OnMasterDelete.CASCADE_DELETE
)
)
public class OrderItemEntity {
private String orderId; // 마스터의 ID
@QfListAttribute
@QfCreateAttribute(requiredOn = @QfRequiredOn(always = true))
@QfDisplayLabel(text = "Product Name")
private String productName;
@QfListAttribute
@QfCreateAttribute(requiredOn = @QfRequiredOn(always = true))
@QfDisplayLabel(text = "Quantity")
private Integer quantity;
}onMasterDelete 옵션:
CASCADE_DELETE— 마스터 삭제 시 디테일 레코드 자동 삭제RESTRICT— 디테일이 존재하면 마스터 삭제 거부IGNORE— 디테일 레코드를 고아 상태로 유지
이력 관리 활성화
java
@QfEntity(
name = @QfI18n(defaultMessage = "Product", texts = {}),
autoHistoryEnabled = true // 자동 변경 이력 활성화
)
public class ProductEntity {
// ...
}autoHistoryEnabled = true로 설정하면:
- 모든 수정/삭제 작업의 변경 이력 자동 기록
- 이력 조회 엔드포인트 자동 생성
트리 구조 엔티티
treePolicy를 통해 트리 동작을 설정합니다:
java
@QfEntity(
name = @QfI18n(defaultMessage = "Category", texts = {}),
treePolicy = @QfTreePolicy
)
public class CategoryEntity {
@QfParent
private String parentId; // 부모 노드 ID
@QfTreeDepth
private Integer depth; // 깊이 레벨
@QfListAttribute(isTreeNodeTitle = true)
@QfDisplayLabel(text = "Category Name")
private String name;
}트리 정책이 설정되면 트리 조회 엔드포인트가 자동으로 생성됩니다.
전체 예시: 복합 엔티티
java
@QfEntity(
name = @QfI18n(
defaultMessage = "Employee",
texts = { @QfI18nText(locale = "ko", message = "직원") }
),
organizationPolicy = @QfOrganizationPolicy(
enabled = true,
attribute = "orgId"
),
autoHistoryEnabled = true,
deletePolicy = @QfCrudPolicy(enabled = true),
capabilityKey = "hr-management"
)
public class EmployeeEntity {
// 조직 범위 — 자동 필터링
private String orgId;
// 사원번호
@QfListAttribute(sortable = true)
@QfDetailAttribute
@QfCreateAttribute(requiredOn = @QfRequiredOn(always = true))
@QfSearch(type = QfSearch.Type.text)
@QfDisplayLabel(text = "Employee Number")
private String employeeNumber;
// 성명
@QfListAttribute(sortable = true)
@QfDetailAttribute
@QfCreateAttribute(requiredOn = @QfRequiredOn(always = true))
@QfUpdateAttribute(requiredOn = @QfRequiredOn(always = true))
@QfSearch(type = QfSearch.Type.text)
@QfDisplayLabel(text = "Full Name")
private String fullName;
// 주민등록번호 — 암호화, 검색 불가
@QfDetailAttribute
@QfCreateAttribute
@QfCrypto
@QfDisplayLabel(text = "SSN")
private String ssn;
// 연락처 — 암호화 + 정규식 검증
@QfDetailAttribute
@QfCreateAttribute
@QfUpdateAttribute
@QfCrypto
@QfValidationRule(
rule = QfValidationRule.Rule.regex,
params = "^(?:\\+82[-\\s]?)?(?:0?1[0-9])[-\\s]?\\d{3,4}[-\\s]?\\d{4}$"
)
@QfDisplayLabel(text = "Phone")
private String phone;
// 직급 — 코드 그룹
@QfListAttribute
@QfDetailAttribute
@QfCreateAttribute
@QfUpdateAttribute
@QfCodeGroup(code = "EMPLOYEE_RANK")
@QfDisplayLabel(text = "Rank")
private String rank;
// 입사일
@QfListAttribute
@QfDetailAttribute
@QfCreateAttribute
@QfDisplayLabel(text = "Join Date")
private LocalDate joinDate;
}다음 단계
- 검증 — 검증 규칙 선언 방법
- Capability / 권한 — 접근 제어 설정
- 조직 모델 — 조직 기반 데이터 격리