Validation
Q-Framework validation is declaration-based. Instead of writing validation logic manually, you declare rules with annotations and the framework handles everything automatically.
Required Fields
Required rules are declared inside @QfCreateAttribute or @QfUpdateAttribute using requiredOn:
// Always required
@QfCreateAttribute(requiredOn = @QfRequiredOn(always = true))
@QfUpdateAttribute(requiredOn = @QfRequiredOn(always = true))
@QfDisplayLabel(text = "Name")
private String name;
// Required conditionally
@QfCreateAttribute(
requiredOn = @QfRequiredOn(
conditions = @QfConditionExpr(expr = "memberType == 'BUSINESS'")
)
)
@QfDisplayLabel(text = "Business Registration Number")
private String businessNumber;@QfRequiredOn is an element annotation (not standalone) used inside @QfCreateAttribute / @QfUpdateAttribute:
| Form | Description |
|---|---|
@QfRequiredOn(always = true) | Always required |
@QfRequiredOn(conditions = ...) | Required when condition matches |
@QfRequiredOn (default) | Not required |
@QfValidationRule
@QfValidationRule is the single validation annotation. It is repeatable — multiple rules can be stacked on one field.
@QfEntity(name = @QfI18n(defaultMessage = "Member", texts = {}))
public class MemberEntity {
// Login ID — custom regex pattern
@QfListAttribute(sortable = true)
@QfCreateAttribute(requiredOn = @QfRequiredOn(always = true))
@QfValidationRule(
rule = QfValidationRule.Rule.login_id // uses configured login-ID regex
)
@QfDisplayLabel(text = "Login ID")
private String loginId;
// Email — control type declares format; crypto for encryption
@QfDetailAttribute
@QfCreateAttribute
@QfCrypto
@QfControlType(QfControlType.Type.email)
@QfDisplayLabel(text = "Email")
private String email;
// Phone — custom regex
@QfDetailAttribute
@QfCreateAttribute
@QfUpdateAttribute
@QfCrypto
@QfValidationRule(
rule = QfValidationRule.Rule.regex,
params = "^(?:\\+82[-\\s]?)?(?:0?1[0-9])[-\\s]?\\d{3,4}[-\\s]?\\d{4}$",
invalidValueMessageKey = "validation.message.phone.invalid"
)
@QfDisplayLabel(text = "Phone")
private String phone;
// Password — uses configured password policy
@QfCreateAttribute(requiredOn = @QfRequiredOn(always = true))
@QfCrypto
@QfValidationRule(rule = QfValidationRule.Rule.user_pwd)
@QfDisplayLabel(text = "Password")
private String password;
}Validation Rule Types
| Rule | Description | params |
|---|---|---|
Rule.regex | Custom regular expression | params[0] = pattern string |
Rule.unique | Server-side uniqueness check | params[0] = check URL (optional) |
Rule.login_id | Login ID format (from config) | none |
Rule.user_pwd | Password policy (from config) | none |
Validation Error Message
Provide either a message key or inline i18n messages:
// By message key (resolved from message resource)
@QfValidationRule(
rule = QfValidationRule.Rule.regex,
params = "^[A-Za-z0-9_]+$",
invalidValueMessageKey = "validation.message.loginId.invalid"
)
// By inline message
@QfValidationRule(
rule = QfValidationRule.Rule.regex,
params = "^[A-Za-z0-9_]+$",
invalidValueMessages = @QfI18n(
defaultMessage = "Only alphanumeric and underscore allowed",
texts = { @QfI18nText(locale = "ko", message = "영문, 숫자, 밑줄(_)만 허용됩니다") }
)
)Control Type as Validation Signal
@QfControlType declares how the field is rendered, and the framework uses the type to infer client-side validation behavior:
@QfControlType(QfControlType.Type.email) // email format validation on client
@QfDisplayLabel(text = "Email")
private String email;Common control types relevant to validation:
| Type | Effect |
|---|---|
email | Email format check |
text | Plain text (no format constraint) |
number | Numeric input |
date | Date picker |
textarea | Multi-line text |
Compile-Time Fail-Fast
Q-Framework detects invalid annotation combinations at compile time.
Encrypted + Search — Compile Error
@QfCrypto
@QfSearch // compile error — encrypted fields cannot be searched server-side
private String email;ERROR: @QfCrypto and @QfSearch cannot be used together.
Encrypted fields do not support server-side search.
→ MemberEntity.emailAI-Generated Code
Q-Framework's compile-time validation applies equally to AI-generated code. Even if AI produces an invalid annotation combination, the build will be rejected.
Runtime Validation Error Response
When validation fails, Q-Framework returns a standardized error response.
{
"success": false,
"code": "Q_RUNTIME_000002",
"message": "Input validation failed.",
"errors": [
{
"field": "loginId",
"message": "Only alphanumeric and underscore allowed",
"rejectedValue": "invalid user!"
}
]
}Custom Validation with @QfOn Hooks
Complex business rules that cannot be expressed with annotations are handled via @QfOn hooks.
@QfOn(
phase = QfOnPhase.BEFORE,
layer = QfOnLayer.DOMAIN,
op = QfOnOp.CREATE
)
public void validateUniqueLoginId(/* hook parameters */) {
// Duplicate login ID check
if (memberRepository.existsByLoginId(loginId)) {
throw new QfManagedException(MemberResultCode.DUPLICATE_LOGIN_ID);
}
}INFO
Complex validations that cannot be expressed with annotations are handled in @QfOn hooks. The hook contains logic, but the annotation declarations remain the SSOT for structural rules.
Next Steps
- Capability / Access Control — Declare access control
- Entity Definition — Full entity configuration