Constraint와 OCL로 모델 제약조건을 검증하는 방법
UML·Ecore 모델의 Constraint를 OCL로 정형화하고, 설계부터 빌드와 런타임까지 일관되게 검증하는 연계 방법을 정리한다.
2026-08-14 · 최초 발행 2025-10-31
모델 규칙을 선언적으로 다루는 이유
모델 구동 개발(Model-Driven Engineering)에서는 모델 요소가 만족해야 할 규칙을 설계 단계에서 명확히 표현해야 한다. Constraint는 불변식, 선행조건, 후행조건, 파생식으로 구성되는 규칙 집합이며, UML의 Constraint나 Ecore의 Annotation/Validator 같은 메타모델 수준에서 표현할 수 있다.
문제는 같은 규칙이 모델, 코드, 데이터베이스에 각각 구현될 때다. 계층마다 규칙이 달라지지 않도록 관리하려면 규칙의 표현과 검증 흐름을 연결할 기준점이 필요하다. OCL(Object Constraint Language)은 이 역할을 맡는다. 이 글은 OMG OCL 2.4 사양을 기준으로 하며, 사양과 도구 호환성은 최신 정보를 확인할 필요가 있다.
OCL은 부작용 없이 식을 작성하는 선언적 언어다. 정적 타입, 널 안전, 컬렉션 연산을 제공하며 context에 규칙을 붙여 inv, derive, pre, post 구조를 표현할 수 있다. 파서, 타입체커, 평가기로 이어지는 구성이 가능하므로 모델 검증을 자동화하기에도 적합하다.
모델과 OCL 엔진을 연결하는 흐름
Constraint–OCL 연계는 모델 요소를 컨텍스트로 삼아 OCL 식을 매핑하고, 파싱과 평가 결과를 보고하는 구조다. Eclipse OCL, EMF Validation, Papyrus 같은 설계 도구는 물론 CI/CD, 코드 생성기, DB 스키마 생성기와도 연결할 수 있다.
검증은 한 단계에만 두지 않는다. 설계 시점에는 정적 검증을 수행하고, 빌드에서는 배치 검증으로 품질 게이트를 둔다. 실행 시점에는 런타임 가드 체크를 추가해 다층 방어선으로 운영한다.
입력에는 모델 리포지토리 스냅샷과 규칙 버전 식별자가 포함된다. 파싱 또는 타입체킹에 실패하면 즉시 중단하고, 평가에서 실패하면 위반 규칙 목록을 수집한다. 통과한 변경은 빌드와 배포를 진행하고, 실패한 변경은 트랜잭션 롤백 또는 머지 거부로 처리할 수 있다. 이때 모델 편집 도메인 단위의 락 또는 변경 세트 기반 스냅샷으로 일관성을 확보해야 한다.
타입 정합성과 평가 비용을 함께 관리하기
UML/Ecore 타입 시스템과 OCL 타입 규칙은 일치해야 한다. Association, Multiplicity, Derived 특성도 OCL 내비게이션과 컬렉션 연산에서 보존되어야 한다.
자동화 파이프라인은 파싱, 타입 체크, 평가, 결과 집계와 보고 순서로 구성한다. 대형 모델에서는 증분 평가, 캐싱, 사전 컴파일을 적용할 수 있다. 부분 평가와 병렬 처리, 컬렉션 연산 최적화도 필요하며, 특히 비용이 큰 규칙은 프로파일링과 리팩터링 대상이 된다.
규칙 수가 늘어날수록 복잡도 제한도 필요하다. 컬렉션 크기를 제약하고 선택과 프로젝션을 최소화하며 공통 서브식을 캐싱한다. 비용이 높은 규칙은 야간 배치 검증으로 옮기는 방법도 있다.
주문 모델에 제약을 부착하는 예시
다음 예시는 Customer, Order, OrderLine, Product 클래스를 가정한다. 주문 합계, 주문 항목, 고객 신용 한도, 연산 계약 조건을 OCL로 표현한다.
-- Order 합계 파생식 및 무결성
context Order
inv hasCustomer: not self.customer.oclIsUndefined()
inv positiveTotal: self.total >= 0
inv totalEqualsSum:
self.total = self.lines->collect(l | l.qty * l.price)->sum()
-- 파생 속성 정의 (Ecore의 derived 설정과 연계 가능)
context Order::total : Real
derive:
self.lines->collect(l | l.qty * l.price)->sum()
-- OrderLine 제약
context OrderLine
inv qtyPositive: self.qty > 0
inv priceNonNegative: self.price >= 0
-- 고객 신용 한도 제약 (제출된 주문만 합산)
context Customer
inv withinCredit:
self.orders->select(o | o.status = OrderStatus::Submitted)
->collect(o | o.total)->sum() <= self.creditLimit
-- 연산 계약 조건
context Order::addLine(qty: Integer, price: Real)
pre validArgs: qty > 0 and price >= 0
post increased: self.lines->size() = self.lines@pre->size() + 1
실행 환경은 Eclipse Modeling Tools 2024-06+, EMF 2.34+, Eclipse OCL 6.17+ 설치를 전제로 한다. OCL 콘솔 또는 EMF Validation 연동을 사용하며, 모델을 로드한 뒤 OCL 파일을 첨부하거나 모델 안의 OpaqueExpression/Annotation에 규칙을 임베드한다. 이후 OCL Console Evaluate 또는 Validate Model을 실행한다.
빌드 단계에서는 Gradle/Maven 스크립트에 Eclipse OCL Headless 평가 태스크를 구성하고, 검증 실패 시 빌드를 중단한다. 코드 생성 과정에서는 어노테이션 기반 런타임 가드를 삽입하고 테스트 프레임워크와 자동화 흐름을 연결할 수 있다.
검증 책임을 배치하는 선택
| 접근 방식 | 성능(런타임) | 일관성(레이어 간) | 안정성(부작용) | 확장성(규칙 수 증가) | 운영 편의 |
|---|---|---|---|---|---|
| OCL(모델 검증) | 중 | 높음 | 높음(순수 함수) | 중~높음(증분·캐싱) | 높음 |
| DB 제약(CHECK/PK) | 높음(엔진 최적) | 중(모델과 괴리) | 높음 | 중(복잡식 한계) | 중 |
| 코드 검증(Assert) | 중~높음(케이스별) | 낮음(중복 위험) | 중(부작용 가능) | 높음(복잡도 흡수) | 중 |
OCL은 설계와 테스트 단계의 단일 진실 원천으로 두고, 반드시 보장해야 하는 무결성은 DB와 코드에 선택적으로 중복 배치하는 방식이 적합하다. OCL을 규범적 소스로 관리하고 코드·DB로 생성하거나 동기화하면 일관성을 얻을 수 있지만, 생성과 동기화의 복잡성은 늘어난다.
외부 I/O, 상태 변경, 시간 의존 로직은 OCL에 맞지 않는다. 이런 영역은 주변 시스템 어댑터 또는 테스트 픽스처로 보완해야 한다.
추적성과 변경 통제를 운영에 포함하기
요구사항, 설계 모델, OCL 규칙, 테스트 케이스 사이의 추적 링크를 관리하면 규칙 변경의 영향도를 파악할 수 있다. 규칙 변경에는 영향 분석과 승인 워크플로우를 두고, 규정 준수 영역에서는 감사 로그와 규칙 버전 태깅을 필수로 관리한다.
CI 단계에서 OCL을 일괄 평가하면 도메인 모델의 유효성을 선제적으로 검증할 수 있다. API 계약은 코드 생성 시 pre와 post 조건을 어서션 또는 예외로 변환해 서비스 계층에서 검증한다. 데이터 무결성은 OCL의 체크 가능한 부분집합을 SQL CHECK/Trigger로 매핑해 이중 방어선을 구성할 수 있다. 안전·금융 규제 영역에서는 규정 기반 규칙을 OCL로 정형화하고 변경 감사 및 승인 프로세스와 연결한다.
설계 단계의 위반을 다수 제거하면 테스트·통합 단계 결함이 20~40% 감소할 수 있다(환경 의존). 모델·코드·DB 사이의 규칙 편차를 줄이고, 단일 소스를 수정해 변경 비용을 낮추며, 규칙 가시성과 추적성을 높여 감사·컴플라이언스 대응 시간을 단축할 수 있다.