JexLang: 한 번 작성, 어디서든 검증 — 플랫폼에 구애받지 않는 표현 언어

발행: (2025년 12월 14일 오전 03:51 GMT+9)
9 min read
원문: Dev.to

Source: Dev.to

🎯 우리 모두가 직면한 문제

이 상황을 상상해 보세요: React 웹 앱, Android 모바일 앱, Spring Boot 백엔드, 그리고 어쩌면 iOS 앱까지 포함한 멀티‑플랫폼 애플리케이션을 만들고 있습니다. 모든 것이 관리 가능한 것처럼 보이다가도 익숙한 장벽—검증 로직에 부딪히게 됩니다.

프로덕트 매니저가 들어와서 말합니다:

“우리는 사용자가 18세 이상인지, 이메일이 회사 도메인과 일치하는지, 그리고 프리미엄 플랜을 선택하면 유효한 신용카드가 있는지 검증해야 합니다.”

간단하죠? 이제 이를 여러 플랫폼에 걸쳐 곱해 보세요:

// JavaScript (Web/React Native/NodeJS)
if (
  user.age >= 18 &&
  user.email.endsWith('@company.com') &&
  (user.plan !== 'Premium' || user.hasValidCard)
) {
  // valid
}
// Java (Backend/Android)
if (
  user.getAge() >= 18 &&
  user.getEmail().endsWith("@company.com") &&
  (!user.getPlan().equals("Premium") || user.hasValidCard())
) {
  // valid
}
// Swift (iOS)
if user.age >= 18 &&
   user.email.hasSuffix("@company.com") &&
   (user.plan != "Premium" || user.hasValidCard) {
  // valid
}

이제 50개 이상의 검증 규칙이 애플리케이션 전역에 있다고 상상해 보세요. 각 플랫폼마다 구현이 따로 있고, 각각 테스트가 필요하며, 미묘한 버그가 숨어 있을 수 있습니다.

검증 지옥에 오신 것을 환영합니다. 🔥

😫 실제 고통 포인트

1. 코드 중복 악몽

각 검증 규칙이 여러 곳에 존재합니다. 비즈니스 규칙이 바뀔 때마다 3‑4개의 다른 레포지토리에서 코드를 업데이트해야 합니다. 하나라도 놓치면 디버깅 세션을 괴롭히는 일관성 없는 동작이 발생합니다.

2. 미묘한 플랫폼 차이

각 언어는 약간씩 다르게 동작합니다:

  • 문자열 비교: === vs .equals() vs ==
  • Null 처리: undefined vs null vs Optional<>
  • 타입 강제 변환: JavaScript의 특이한 "5" + 3 = "53" vs Java의 엄격한 타입

이 차이점 때문에 특정 플랫폼에서만 나타나는 버그가 생깁니다—가장 골치 아픈 버그 유형이죠.

3. 하드코딩 로직 = 배포 고통

검증 규칙을 바꾸려면 코드 변경, PR 리뷰, 테스트, 스테이징, 배포—플랫폼마다 모두 진행해야 합니다. 검증 로직을 데이터베이스나 설정에 두면 어떨까요? 비개발자도 코드를 건드리지 않고 간단한 규칙을 수정할 수 있다면요?

4. 테스트 오버헤드

50개의 검증 규칙 × 4개 플랫폼 = 최소 200개의 테스트 케이스. 그리고 모두가 동일하게 동작하도록 보장해야 합니다. 행운을 빕니다.

💡 더 나은 방법이 있다면?

검증 로직을 한 번만 작성하고, 간단하고 익숙한 문법으로 표현한다면 어떨까요:

user.age >= 18 && endsWith(user.email, "@company.com") && (user.plan != "Premium" || user.hasValidCard)

…그리고 다음 환경에서도 동일하게 실행됩니다:

  • ✅ JavaScript/TypeScript (브라우저 & Node.js)
  • ✅ Java (Android & Backend)
  • 🚧 Swift (iOS) — 곧 지원!

플랫폼별 특이점이 없습니다. 코드 중복도 없습니다. 배포 오버헤드도 없습니다.

바로 이것이 제가 JexLang을 만든 이유입니다.

🚀 JexLang 소개

JexLang은 JavaScript와 같은 문법을 갖춘 가볍고 플랫폼에 구애받지 않는 표현식 언어입니다. 한 가지 문제를 탁월하게 해결합니다: 로직을 한 번 작성하고, 어디서든 일관된 동작으로 실행.

왜 JavaScript‑같은 문법인가?

대부분의 개발자는 JavaScript 문법에 익숙합니다. 비록 주로 Java나 Swift를 사용하더라도 어느 순간 JavaScript을 접해본 적이 있을 겁니다. 익숙한 문법을 사용함으로써 JexLang은 사실상 학습 곡선이 거의 없습니다.

user.age >= 18 && user.active === true

이 코드를 쓸 수 있다면 이미 JexLang을 알고 있는 겁니다.

🛠️ JexLang 작동 방식

JexLang은 세 가지 핵심 구성 요소로 이루어집니다:

1. Lexer (Tokenizer)

표현식을 토큰으로 분해합니다:

"user.age >= 18" → [IDENTIFIER:user, DOT, IDENTIFIER:age, GTE, NUMBER:18]

2. Parser

토큰을 추상 구문 트리(AST)로 변환합니다:

{
  "type": "BinaryExpression",
  "left": {
    "type": "MemberExpression",
    "object": "user",
    "property": "age"
  },
  "operator": ">=",
  "right": {
    "type": "NumericLiteral",
    "value": 18
  }
}

3. Interpreter

AST를 컨텍스트 데이터와 대조해 평가하고 결과를 반환합니다.

핵심은? 동일한 AST 구조와 평가 로직이 각 언어마다 동일하게 구현된다는 점—동일한 파서 규칙, 동일한 연산자 우선순위, 동일한 타입 강제 변환, 동일한 결과.

📖 실제 활용 사례

1. 동적 폼 검증

검증 규칙을 백엔드에 저장하고 모든 플랫폼에서 공유합니다:

{
  "fields": [
    {
      "name": "email",
      "validations": [
        { "rule": "required(email)", "message": "Email is required" },
        { "rule": "contains(email, '@')", "message": "Please enter a valid email" }
      ]
    },
    {
      "name": "age",
      "validations": [
        { "rule": "age >= 18", "message": "You must be 18 or older" }
      ]
    }
  ]
}

React 앱, Android 앱, 백엔드가 이 규칙들을 받아 JexLang으로 평가합니다. 단일 진실 원천이 됩니다.

2. 비즈니스 규칙 엔진

코드 배포 없이 비즈니스 로직을 구성합니다:

// 데이터베이스에 저장된 규칙
const discountRules = [
  { condition: "customer.tier === 'Gold' && cart.total > 100", discount: 20 },
  { condition: "customer.tier === 'Silver' && cart.total > 200", discount: 10 },
  { condition: "cart.items.length > 5", discount: 5 }
];

// 런타임에 평가
const applicableRule = discountRules.find(rule =>
  jexlang.evaluate(rule.condition, { customer, cart })
);

마케팅 팀이 골드 티어 기준을 100에서 150으로 바꾸고 싶다면? 데이터베이스만 업데이트하면 됩니다—배포가 필요 없습니다.

3. 조건부 UI 렌더링

구성 가능한 규칙에 따라 UI 요소를 표시/숨김합니다:

{
  "component": "PremiumBanner",
  "showWhen": "user.subscription === 'free' && user.daysActive > 30"
}

4. 워크플로 자동화

조건을 사용자가 직접 설정할 수 있는 워크플로 엔진을 구축합니다:

IF order.total > 1000 AND customer.verified === true
  → 우선 순위 처리로 라우팅

IF order.items.length > 10
  → 대량 처리 적용

IF contains(order.shippingAddress.country, 'EU')
  → VAT 계산 적용

5. 복잡한 로직을 가진 피처 플래그

단순 불리언 플래그를 넘어서는 기능 플래그:

const featureRules = {
  newCheckout: "user.region === 'US' && user.accountAge > 30 && !user.hasActiveIssues",
  betaFeatures: "user.role === 'developer' || contains(user.email, '@internal.com')"
};

⚡ 빠른 예제

기본 표현식

// 산술
jexlang.evaluate("10 + 5 * 2"); // 20

// 비교
jexlang.evaluate("100 >= 50"); // true

// 논리 연산자
jexlang.evaluate("true && false || true"); // true

// 문자열 연산
jexlang.evaluate("'Hello' + ' ' + 'World'"); // "Hello World"

컨텍스트와 함께 사용하기

const context = {
  user: {
    name: "John Doe",
    age: 28,
    email: "john@company.com",
    roles: ["admin", "editor"]
  },
  settings: {
    maxUploadSize: 100,
    allowedFormats: ["jpg", "png", "pdf"]
  }
};

// 중첩 속성 접근
jexlang.evaluate("user.name", context); // "John Doe"
jexlang.evaluate("settings.maxUploadSize > 50", context); // true
Back to Blog

관련 글

더 보기 »

Universal & Deep Links: 2026 완전 가이드

딥 링크는 이론적으로는 간단해 보입니다: 사용자가 링크를 탭하면 앱이 열리고, 필요한 정확한 화면으로 이동합니다. 실제로는 Univ...를 구현해 본 사람이라면 누구나 알 수 있습니다.