반응형
Notice
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
31 |
Tags
- 머신러닝
- 빅데이터
- 웹개발
- 컴퓨터과학
- 데이터베이스
- 인공지능
- 파이썬
- 보안
- 소프트웨어
- 소프트웨어공학
- 사이버보안
- 프로그래밍언어
- 클라우드컴퓨팅
- 컴퓨터비전
- 컴퓨터공학
- 디자인패턴
- 딥러닝
- 데이터구조
- 데이터분석
- 자료구조
- springboot
- 버전관리
- 프로그래밍
- 네트워크
- 자바스크립트
- Yes
- 데이터과학
- 네트워크보안
- 알고리즘
- I'm Sorry
Archives
- Today
- Total
스택큐힙리스트
Interpreter 패턴: 미니 DSL로 “규칙을 읽는” 코드 만들기 본문
반응형
복잡한 if-else 정글 대신, 사람이 읽는 문장 같은 규칙을 그대로 코드가 해석(interpret)해 실행하면 얼마나 깔끔할까요?
Interpreter 패턴은 아주 단순한 문법(Grammar)을 클래스 구조로 표현하고, 그 트리를 순회하며 의미를 평가하는 방식입니다. 검색 필터, 권한 규칙, 프로모션 조건, 피처 플래그 같은 반복적으로 해석해야 하는 도메인 문장에 제격이에요.
언제 쓰나? (한눈에)
- 도메인 규칙을 문장처럼 표현하고 싶을 때 (ex. “가격 > 1만 AND 태그=세일”).
- 같은 규칙을 여러 데이터에 반복 적용해야 할 때.
- 문법이 작고 안정적일 때(중요!). 커지면 파서/컴파일러 영역이라 다른 접근이 낫습니다.
큰 그림: 구성 요소
- Expression 계층: 규칙을 트리로 표현하는 인터페이스/클래스들
- TerminalExpression : 숫자·문자·단일 속성 비교 같은 원자 규칙
- NonTerminalExpression : AND/OR/NOT 같은 조합 규칙
- Context: 해석 대상(예: 상품, 사용자, 요청 등)
- interpret(context): 트리를 따라 내려가며 참/거짓 또는 값을 계산
실무에선 Composite로 트리를 만들고, 필요하면 Visitor로 최적화·로깅·출력 같은 부가 작업을 붙입니다.
Kotlin 미니 예제 – 상품 필터 DSL
“가격이 1만 원 초과이고, 태그에 ‘세일’ 또는 ‘신상’이 포함” 규칙을 해석하는 예제입니다.
// --- 도메인 ---
data class Product(val price: Int, val tags: Set<String>)
// --- Expression ---
fun interface Expr { fun interpret(p: Product): Boolean }
// Terminal
class PriceGt(private val min: Int) : Expr {
override fun interpret(p: Product) = p.price > min
}
class HasTag(private val tag: String) : Expr {
override fun interpret(p: Product) = tag in p.tags
}
// NonTerminal
class And(private val left: Expr, private val right: Expr) : Expr {
override fun interpret(p: Product) = left.interpret(p) && right.interpret(p)
}
class Or(private val left: Expr, private val right: Expr) : Expr {
override fun interpret(p: Product) = left.interpret(p) || right.interpret(p)
}
class Not(private val expr: Expr) : Expr {
override fun interpret(p: Product) = !expr.interpret(p)
}
// --- DSL 헬퍼 ---
infix fun Expr.and(other: Expr) = And(this, other)
infix fun Expr.or(other: Expr) = Or(this, other)
fun not(e: Expr) = Not(e)
// --- 규칙 트리 (Grammar를 코드로) ---
val rule: Expr =
PriceGt(10_000) and (HasTag("세일") or HasTag("신상"))
// --- 사용 ---
val a = Product(price = 9000, tags = setOf("세일"))
val b = Product(price = 12000, tags = setOf("여름", "신상"))
println(rule.interpret(a)) // false (가격 조건 탈락)
println(rule.interpret(b)) // true
핵심은 규칙을 데이터처럼 조립한다는 점이에요. 이제 “세일은 아니지만 ‘특가’ 태그면 OK” 같은 요구도 트리만 바꾸면 바로 반영됩니다.
실전 팁
- 문법을 욕심내지 말 것: 비교·AND/OR/NOT 정도로 시작하세요. 커지면 파싱·성능이 급격히 어려워집니다.
- 캐시/메모이제이션: 같은 규칙을 많은 데이터에 적용하면 서브트리 결과 캐시가 체감 성능을 올려줍니다.
- 빌더/DSL로 표현력 업그레이드: 위처럼 infix/헬퍼 함수로 “읽히는” 코드를 만드세요.
- 검증 단계 추가: 배포 전 규칙 트리를 정적 검사(불가능한 조합, 항상 true 등)해 장애를 줄이세요.
- 한계를 인정하기: 문법이 커지기 시작하면 파서(ANTLR), 스펙/룰 엔진(SPEL, MVEL, Drools), Kotlin DSL, 혹은 Query 언어로 전환을 고려하는 게 장기적으로 이득입니다.
장단점 정리
- 장점
- 규칙을 데이터처럼 조립 → 변경·테스트 용이
- 도메인 담당자와 공유 가능한 표현(의사 언어)
- 단점
- 문법이 커지면 성능·가독성·테스트 비용 폭증
- 트리 생성(파싱)까지 자체 구현하면 유지보수 난도↑
한 줄 요약
Interpreter 패턴은 “작은 문법을 가진 규칙 엔진”에 최적—트리로 규칙을 만들고 해석해, 변화 많은 비즈니스 룰을 안전하게 운용할 수 있습니다.
반응형
'개발' 카테고리의 다른 글
MVVM 패턴, 뷰모델로 화면 로직을 ‘깨끗하게’ 분리하는 법 (1) | 2025.08.11 |
---|---|
MVC 패턴, 지금까지 가장 많이 쓰인 “기본기”부터 다지자 (2) | 2025.08.09 |
커맨드 vs 메멘토: 언제 어떤 패턴이 덜 아픈가 (4) | 2025.08.08 |
메멘토(Memento) 패턴 완전 분석: “되돌리기(Undo)”를 가장 깔끔하게 (2) | 2025.08.08 |
Visitor 패턴: “구조는 그대로, 기능은 덧붙여” (3) | 2025.07.30 |
Comments