.me 소개 - 역방향 의존성 인덱싱 (Inverted Dependency Indexing)
요약
.me는 경로 중심의 의미론적 커널로, 역방향 의존성 인덱싱(Inverted Dependency Indexing)을 통해 반응형 의존성을 효율적으로 추적합니다. 전체 트리를 스캔하는 대신 역방향 매핑을 사용하여 데이터 변이 시 파생된 값을 즉각적으로 업데이트합니다.
핵심 포인트
- 역방향 의존성 인덱싱을 통한 효율적인 데이터 업데이트
- 전체 트리 스캔 없이 로컬 업데이트 비용 최소화
- 의미론적 경로 기반의 반응형 의존성 그래프 구축
- 파생된 값 등록 시 참조를 추출하여 역방향 매핑 생성
description: .me가 리프(leaf)에서 파생된 의미론적 경로(semantic paths)로 어떻게 반응형 의존성(reactive dependencies)을 추적하는지에 대하여.
.me 소개 - 역방향 의존성 인덱싱 (Inverted Dependency Indexing)
.me는 경로(paths)를 중심으로 구축된 의미론적 커널(semantic kernel)입니다.
다음과 같이 작성할 수 있습니다:
me.order.price(100);
me.order.quantity(5);
me.order["="]("total", "price * quantity");
그리고 다음과 같이 읽을 수 있습니다:
me("order.total"); // 500
겉보기에는 자유 형식의 객체(free-form object)처럼 보입니다.
하지만 그 내부에는 반응형 의존성 그래프(reactive dependency graph)가 존재합니다.
중요한 부분은 .me가 전체 트리를 스캔하지 않고 어떻게 파생된 값(derived values)을 업데이트하느냐 하는 점입니다.
그 메커니즘이 바로 **역방향 의존성 인덱싱 (Inverted Dependency Indexing)**입니다.
문제점 (The Problem)
일반적인 트리에서는 데이터가 루트(root)에서 리프(leaf)로 흐릅니다:
order
price
quantity
...
단순한 반응형 시스템(naive reactive system)은 다음과 같은 질문을 던짐으로써 변이(mutation)를 처리할 수 있습니다:
무엇이 변했는가?
그것에 의존하는 것은 무엇인가?
트리를 스캔해야 하는가?
...
그래프가 커질수록 이는 비용이 많이 드는 작업이 됩니다.
시스템에 N개의 경로가 있다면, 전역 스캔(global scan)으로 인해 단 한 번의 로컬 업데이트 비용이 전체 그래프 크기에 의존하게 됩니다.
.me는 의존성을 반대 방향으로 인덱싱함으로써 이를 방지합니다.
역전 (The Inversion)
파생된 값이 선언될 때:
me.order["="]("total", "price * quantity");
.me는 다음과 같이 기록합니다:
order.total은 다음을 의존함:
order.price
order.quantity
하지만 동시에 역방향 매핑(reverse mapping)도 구축합니다:
order.price -> order.total
order.quantity -> order.total
이 역방향 매핑이 바로 역방향 의존성 인덱스(inverted dependency index)입니다.
개념적으로는 다음과 같습니다:
이제 쓰기(write) 작업 시 그래프를 검색할 필요가 없습니다.
다음과 같은 상황이 발생하면:
me.order.price(200);
커널은 직접적으로 물을 수 있습니다:
그리고 정답은 이미 인덱싱되어 있습니다:
order.total
등록 시점 (Registration Time)
작업은 파생(derivation)이 등록될 때 시작됩니다.
.me는 표현식(expression)에서 참조(references)를 추출합니다:
"price * quantity"
그런 다음 현재 경로를 기준으로 이를 해결(resolve)합니다:
price -> order.price
quantity -> order.quantity
그리고 파생(derivation) 대상을 저장합니다:
order.total
파생 기록에는 다음이 포함됩니다:
expression
evaluation scope
resolved refs
...
역방향 인덱스에는 다음이 포함됩니다:
ref path -> dependent target paths
이것이 핵심적인 변화입니다.
커널은 변경(mutation) 시간이 될 때까지 의존성 구조를 발견하지 않습니다. 파생이 선언될 때 구조를 구축합니다.
변경 시간 (Mutation Time)
값이 변경될 때:
me.order.price(200);
커널은 무효화 경로(invalidation path)를 호출합니다.
Eager 모드에서는 알고리즘이 다음과 같습니다:
1. 변경된 경로에서 시작
3. 해당 대상을 재계산
...
따라서 다른 파생이 order.total에 의존한다면, 위로 계속 전파될 수 있습니다:
me.order["="]("total", "price * quantity");
me.order["="]("withTax", "total * 1.16");
가격 변경은 다음을 영향을 미칩니다:
order.price
-> order.total
-> order.withTax
커널은 그 체인(chain)을 직접 따라갑니다.
관련 없는 경로를 스캔하지 않습니다.
O(k), O(N)이 아님
실제 복잡도는 영향을 받는 의존성 경계(dependency frontier)인 k에 기반합니다.
N = 전체 그래프 크기
k = 이 변경으로 인해 영향을 받는 파생 노드 수
만약 그래프가 백만 개의 경로를 가지고 있지만, 오직 세 개의 파생 노드만이 변경된 값에 의존한다면, 변경은 전체 백만 개가 아니라 그 세 개의 파생 노드를 건드려야 합니다.
이것이 역방향 인덱싱의 요점입니다:
cost(update) ~= O(k)
아니라:
cost(update) ~= O(N)
Eager vs Lazy
.me는 두 가지 재계산 모드를 지원합니다.
write source
-> recompute now
Lazy 모드에서는 변경이 참조 버전만 올립니다.
write source
-> mark dependency version changed
-> read 시점에 재계산
런타임 제어는 리플렉티브 평면(reflective plane) 아래에 존재합니다:
me["!"]
.runtime.setRecomputeMode("lazy");
그런 다음, 파생 값이 읽힐 때:
me("order.total");
커널은 의존성 버전이 오래되었는지(stale) 확인합니다. 만약 오래되었다면, 값을 반환하기 전에 대상을 재계산합니다.
eager 모드는 작업을 쓰기(writes)로 밀어냅니다.
lazy 모드는 작업을 읽기(reads)로 끌어옵니다.
설명 가능성 (Explainability)
재계산이 웨이브(wave)로 추적되기 때문에, .me는 무엇이 발생했는지 설명할 수 있습니다.
me["!"].explain("order.total");
파생된 경로(derived path)는 다음을 보고할 수 있습니다:
expression
dependency inputs
last recompute wave
...
예를 들어:
order.total
expression: price * quantity
depends on:
...
이것은 반응형 시스템(reactive system)이 블랙박스여서는 안 되기 때문에 중요합니다. 값이 변경되었다면, 커널은 인과관계의 경로를 보여줄 수 있어야 합니다.
비밀 범위 (Secret Scopes)
동일한 모델이 비밀 브랜치(secret branches) 전반에 걸쳐 작동할 수 있습니다.
.me는 경로 기반의 비밀 범위를 지원합니다:
me.wallet["_"]("wallet-key");
me.wallet.balance(12480);
루트는 여전히 은밀하게 유지됩니다:
me("wallet"); // undefined
하지만 내부의 파생 로직은 커널의 규칙 하에 암호화된 브랜치를 계속 사용할 수 있습니다.
explain()이 비밀 입력(secret input)을 가로지르면, 값을 노출하는 대신 마스킹합니다:
origin: "stealth"
value: "●●●●"
따라서 의존성 추적과 구조적 침묵은 공존할 수 있습니다.
의존성 그래프는 여전히 작동하지만, 공개적인 검사는 개인 정보를 유출하지 않습니다.
정신 모델 (The Mental Model)
일반적인 인덱스에서는:
path -> value
역방향 의존성 인덱스(inverted dependency index)에서는:
source path -> derived targets
따라서 .me는 두 가지 아이디어를 모두 유지합니다:
semantic path -> current value
첫 번째는 커널이 의미(meaning)를 읽게 하고.
두 번째는 커널이 의미를 효율적으로 업데이트하게 합니다.
중요성 (Why It Matters)
.me는 자유 형식 경로(free-form paths)로 시작합니다:
me.whatever.you.want("x");
하지만 일단 경로가 다른 경로에서 파생될 수 있게 되면, 런타임은 일관성을 유지할 방법을 필요로 합니다.
역방향 의존성 인덱싱(Inverted Dependency Indexing)이 바로 그 메커니즘입니다.
이는 .me가 모든 변경을 전체 그래프 이벤트로 취급하지 않으면서 살아있는 의미론적 그래프(living semantic graph)처럼 작동하도록 합니다.
설계는 간단합니다:
declare meaning freely
record dependencies when formulas are created
...
이것이 .me 반응성의 핵심입니다:
외부에는 자유 형식의 의미론 (free-form semantics)을 제공하고
내부에는 인덱싱된 의존성 인과관계 (indexed dependency causality)를 유지합니다
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기