목차
룰 엔진 (Rule Engine) 은 입력 데이터에 대해 규칙을 실행하고 조건이 맞으면 해당 작업을 실행하는 전문 시스템 프로그램입니다.
소프트웨어 개발에서 데이터를 필터링하고 처리하기 위해 여러 규칙이나 조건을 적용해야 하는 상황이 자주 발생합니다. 전통적인 if와 else 조건문을 사용하여 이러한 규칙을 관리하면 복잡하고 유지 보수가 어려워질 수 있습니다.
룰 엔진 (Rule Engine)을 사용하면 이러한 규칙을 보다 유연하고 체계적으로 정의하고 실행할 수 있습니다.
이 글에서는 Java 함수형 프로그래밍 원칙을 사용하여 Spring Boot 프로젝트에서 간단한 룰 엔진 (Rule Engine)을 구축하는 방법을 살펴보겠습니다.
룰 엔진 (Rule Engine) 이란?
룰 엔진 (Rule Engine)은 여러 규칙을 관리하고 객체 집합에 이를 적용하는 역할을 합니다. 규칙에 따라 객체를 필터링하고 필터링된 결과를 반환합니다.
룰 엔진 (Rule Engine) 만들기 단계별 가이드
먼저, Spring Initializr를 사용하여 간단한 스프링 부트 애플리케이션을 생성합니다. 이 애플리케이션에서 사용자 목록을 반환하는 REST 엔드포인트를 만들겠습니다.
1. UserController.java
클래스 생성
package com.example.spring_boot_rule_engine_demo.controller;
import com.example.spring_boot_rule_engine_demo.model.User;
import com.example.spring_boot_rule_engine_demo.engine.RuleEngine;
import com.example.spring_boot_rule_engine_demo.rule.Rule;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.GetMapping;
import java.util.List;
@RestController
@RequestMapping("/user")
public class UserController {
@GetMapping
public List<User> getAllUser() {
return userData();
}
private List<User> userData() {
return List.of(
new User("sumit", 33),
new User("agam", 13),
new User("mohit", 23),
new User("kailash", 43)
);
}
}
여기서 나이가 30세 이상이고 이름이 'sum'으로 시작하는 사용자를 가져와야 한다고 생각해 봅시다.
이럴 경우, 두 가지 규칙을 만들고, 클라이언트에게 응답을 반환하기 전에 적용해야 합니다.
2. Rule (규칙) 정의
먼저 Rule
이라는 enum을 사용하여 규칙을 정의하겠습니다.
enum의 각 규칙은 우리가 평가하고자 하는 특정 조건이나 기준을 나타냅니다.
package com.example.spring_boot_rule_engine_demo.rule;
import com.example.spring_boot_rule_engine_demo.model.User;
import java.util.function.Predicate;
public enum Rule implements TestRule {
AGE_GREATER_THAN_30(user -> user.getAge() > 30),
NAME_STARTS_WITH_SUM(user -> user.getName().startsWith("sum"));
private final Predicate<User> predicate;
Rule(Predicate<User> predicate) {
this.predicate = predicate;
}
@Override
public Predicate<User> getPredicate() {
return predicate;
}
}
3. TestRule
인터페이스 생성
구현 없이 메서드를 정의하기 위해 TestRule
인터페이스를 만들겠습니다.
package com.example.spring_boot_rule_engine_demo.rule;
import java.util.function.Predicate;
public interface TestRule {
<T> Predicate<T> getPredicate();
}
4. Rule Engine (룰 엔진) 클래스 생성
이 클래스는 규칙의 관리와 실행을 담당합니다. 규칙 목록을 유지하며, 새로운 규칙을 추가하고 이러한 규칙을 기반으로 객체를 필터링하는 메서드를 제공합니다.RuleEngine
클래스의 filter
메서드에서 객체 목록을 반복하며 Java 8 Stream API의 allMatch
메서드를 사용해 각 규칙을 적용합니다. 이 메서드는 주어진 객체가 모든 규칙을 통과하는지 확인합니다. 모든 규칙을 통과하면 해당 객체를 필터링된 목록에 추가합니다.
package com.example.spring_boot_rule_engine_demo.engine;
import com.example.spring_boot_rule_engine_demo.rule.TestRule;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Predicate;
import java.util.stream.Collectors;
public class RuleEngine<T> {
private final List<TestRule> rules;
public RuleEngine() {
this.rules = new ArrayList<>();
}
public void addRule(TestRule rule) {
rules.add(rule);
}
public List<T> filter(List<T> items) {
return items.stream()
.filter(item -> rules.stream()
.map(TestRule::getPredicate)
.allMatch(predicate -> predicate.test(item)))
.collect(Collectors.toList());
}
}
filter메서드는 규칙 목록에 저장된 규칙을 기반으로 항목 목록을 필터링합니다:
1. 입력으로 T 타입의 항목 목록을 받습니다.
2. items.stream()을 사용해 항목 목록에서 스트림을 생성합니다.
3. filter 메서드는 각 항목에 대해 해당 항목이 모든 규칙을 만족하는지 확인합니다:
rules.stream()은 규칙 목록에서 스트림을 생성합니다.
map(TestRule::getPredicate)는 각 규칙의 조건을 가져옵니다.
allMatch(predicate -> predicate.test(item))는 항목이 모든 규칙을 만족하는지 확인합니다.
4. 모든 규칙을 만족하는 항목은 필터링된 목록에 추가됩니다.
5. 마지막으로, collect(Collectors.toList())를 사용해 필터링된 항목을 새로운 목록으로 수집하여 반환합니다.
5. User
클래스 생성
package com.example.spring_boot_rule_engine_demo.model;
public class User {
private String name;
private int age;
public User(String name, int age) {
this.name = name;
this.age = age;
}
public User() {}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
6. Rule Engine (룰 엔진) 사용하기
이제 UserController.java
클래스에서 룰 엔진 (Rule Engine) 을 사용하여 필터링된 사용자 목록을 반환하도록 수정합니다.
먼저, 우리는 사용자 목록을 생성하고, AGE_GREATER_THAN_30과 NAME_STARTS_WITH_SUM 두 가지 규칙으로 RuleEngine을 초기화합니다. 그런 다음 Rule Engine 클래스의 filter 메서드를 호출하여 사용자 객체 목록을 전달합니다. Rule Engine 클래스는 각 사용자에게 규칙을 적용하고 필터링된 목록을 반환합니다.
마지막으로, 필터링된 목록을 반복하고 지정된 규칙을 충족하는 사용자 목록을 반환합니다.
package com.example.spring_boot_rule_engine_demo.controller;
import com.example.spring_boot_rule_engine_demo.engine.RuleEngine;
import com.example.spring_boot_rule_engine_demo.model.User;
import com.example.spring_boot_rule_engine_demo.rule.Rule;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.GetMapping;
import java.util.List;
@RestController
@RequestMapping("/user")
public class UserController {
@GetMapping
public List<User> getAllUser() {
return getFilteredUsers();
}
private List<User> getFilteredUsers() {
RuleEngine<User> ruleEngine = new RuleEngine<>();
ruleEngine.addRule(Rule.AGE_GREATER_THAN_30);
ruleEngine.addRule(Rule.NAME_STARTS_WITH_SUM);
return ruleEngine.filter(userData());
}
private List<User> userData() {
return List.of(
new User("sumit", 33),
new User("agam", 13),
new User("mohit", 23),
new User("kailash", 43)
);
}
}
이제 규칙을 적용한 후 필터링된 사용자 목록을 응답으로 받을 수 있습니다.
읽어주셔서 감사합니다! 😊
개발 관련 궁금증이나 고민이 있으신가요?
아래 링크를 통해 저에게 바로 문의해 주세요! 쉽고 빠르게 도움 드리겠습니다.
'Development > Code' 카테고리의 다른 글
알아두면 좋은 Spring Boot 기능 10가지 (0) | 2024.06.28 |
---|---|
Spring Boot의 성능을 향상시키는 10가지 방법 (0) | 2024.06.28 |
Spring Boot 3.3의 고급 기능 : 종합 가이드 (0) | 2024.06.26 |
Spring State Machine을 어떻게 활용할 수 있을까요? (사용 방법 및 예제) (0) | 2024.06.26 |
자바 직렬화 사용을 피해야 하는 이유 (0) | 2024.06.25 |