- 6차 라운드: 태그 네임스페이싱(Ref로 충분), Store가 Store를 담지 않음 확정 - Modifier 메커니즘 전체 확정(정적 merge, immutable+clone 체이닝, State 필드 지원, "관측해야 실체화된다" 전역 원칙) — base/modifier-plan.md 신설 - 컴포넌트화 논의 시작(research/component-composition-plan.md) — 컴포넌트= 플레인 함수, State/Source 읽기·쓰기 경계, StoreSource 프록시까지 수렴, modifier/Ref의 컴포넌트 경계 통과 방식은 열린 채로 남김 - .claude/ 코퍼스 전체(약 15개 문서)를 서브에이전트로 감사해 여러 라운드에 걸쳐 쌓인 모순/중복/stale 마커/끊긴 참조 다수 수정 - purity-and-effects-plan.md를 research/에서 base/로 승격 - CLAUDE.md: 라운드별 인수인계 메모 3개를 하나로 통합, 오래된 "더 이상 열린 질문 없음" 모순 제거 - question.md: 시간순도 우선순위순도 아니던 구조를 "지금 열려있는 것" 중심 으로 재정리, 용어 정리 제안(State/DI/PerInstanceState 등 우선순위) 추가 Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
7.4 KiB
Slot — 뮤터블 자식 배열, 엄격한 단일 마운트 소유권 (base로 승격됨)
상태: base — 설계 방향(소유권 귀속, 재마운트 시 throw, retract=폐기)과
소스 트리 상 패키지 경계까지 확정되어 research/에서 승격됨(base/ architecture.md의 "구현 착수: 소스 트리 구조 확정" 절 참고). 원본:
.claude/initreq/raw-userinput.md "slot을 구현하도록 하기로 했음" 절. Fusion의
Children SpecialKey와 Vide의 mount 무가드 비교는 base/comparison-fusion-vide.md
참고 — 결론: 두 라이브러리 어디에도 이런 엄격한 단일 마운트 가드가 없음,
quad의 진짜 개선점.
base/roblox 패키지 경계 (2026-08-04, 5차 라운드 확정)
Slot의 add/remove/clear 재조정 로직(추상 자식 참조 기준 — "이 자리에 뭐가
있어야 하는가"를 결정하는 순수 로직)은 quad-base/src/Dispatch/Slot.luau가
소유. 실제 트리 조작(Instance Parent 설정/Destroy)은 quad-roblox/src/ Handlers/Slot.luau가 그 위에서 적용/해제만 담당 — 다른 모든 인터페이스/구현
분리와 동일한 패턴(base/architecture.md의 소스 트리 참고). Slot 자체는
당연히 Instance들을 담게 될 것으로 취급.
추가로 필요해진 핸들러: Slot과는 별개로, k가 number이고 v가 이미
만들어진 Instance인 경우(중첩 인스턴스를 자식으로 직접 넣는 경우, 예:
Frame { Frame {} })를 위한 핸들러도 필요 — quad-roblox/src/Handlers/ InstanceChild.luau. Slot은 "뮤터블 배열"을 다루고 이 핸들러는 "정적으로
하나 박아넣는" 더 단순한 경우라 별개로 둠.
개념
add/remove/clear/get/set 등 뮤터블 연산을 지원하는 메타 배열. 실제
바인드가 일어나면 child로 풀리고, 이 메타 배열에 CRUD를 하면 실제 children이
적절히 제어됨.
핵심 제약: 소유권 귀속과 단일 마운트
Slot에 들어간 요소는 ownership이 귀속되며 다른 곳에 마운트할 수 없게 된다.
isMounted를 관리해서, 한 인스턴스에 대한 다중 마운팅이 라이브러리 차원에서
절대 일어나지 않도록 강제하는 게 v1 대비 핵심 디자인 변화. v1의 mount()는
별다른 강제를 안 했지만(base/quad-v1-architecture.md의 mount.lua 분석 참고 —
실제로는 부모/자식 부기까지 했지만 다중 마운트 방지는 없었음), v2는 mount
함수 자체가 이 강제를 담당.
Fusion의 Children SpecialKey는 이걸 "특정 SpecialKey 하나의 내부 부기"로만
구현했고(재사용 가능한 1급 프리미티브가 아님), Vide는 아예 이 개념이 없어서
같은 target에 두 번 mount()하면 조용히 두 개의 독립 루트가 생김 — 둘 다
반면교사.
여럿 존재 가능, 부모가 실제 데이터 테이블만 다루면 됨
Slot은 하나의 instance 안에 여럿 존재할 수 있다. 전부 하나의 children으로
들어가지만, 실제 렌더된 instance에서 GetChildren()을 직접 하지 않고도 부모가
생성한 "실제 slot 데이터 테이블"만 다루면 되게 해서 추상화 수준을 낮은 직접
바인딩에서 한 단계 떼어냄(간접화를 통한 추상화).
마운트된 Slot의 재마운트는 즉시 throw (확정)
사용자 확인 완료: 이미 사용된(마운트된) slot을 재마운트하려 하면 즉시
error()로 중단 — warn+no-op 아님. 개발 중 바로 잡아낼 수 있게 강하게
실패하는 쪽 선택. 마운트되는 순간 slot의 실제 대상은 고정된다 — 따라서
글로벌 스코프에서 slot을 쓰는 건 그다지 좋지 않을 수 있음(재사용/재마운트가
막히므로).
클래스가 슬롯을 받는 방법
"네이밍된 슬롯"이 필요한가에 대한 사용자 자문: 그냥 슬롯 바인드 테이블을 값으로 넘기면 되는 것 아닌가 — 결국 array처럼 구현된 Store라고 생각하는 게 편하다는 방향. 기울어진 결론: 별도 "Named Slot" 개념 없이, store나 파라미터로 넘기고 그게 그냥 ref처럼 바인드되는 모양.
상태 확인(2026-08-04 문서 정리 시점): 이 방향은 아래 "열린 질문" 절이나
.claude/question.md의 확정 목록 어디에도 명시적으로 흡수된 흔적이 없음 —
아직 정식 확정 절차(AskUserQuestion 등)를 거치지 않은 것으로 보임. 열린
질문으로 유지, .claude/question.md에도 반영 필요.
Slot과 Store 바인드의 관계 (retract 순서)
Slot이 store 바인드로 들어오는 경우, pluggable 처리기에 retract(구 cleanup,
base/lifecycle-pattern.md 참고) 핸들러가 필요함 — 한번 넘어간 slot 요소가
나중에 retract되면 삭제되는지, 아니면 "부모의 소유이니 부모가 처리"해야
하는지 검토 필요. 기울어진 결론(잠정안, 이후 정정됨): 부모가 정리 정도만
미리 수행하고 다시 process하면 되므로, 부모에게 위임(자식 slot 요소 자체가
스스로 정리를 실행하는 게 아니라).
정정(2026-08-04 검증 라운드): 위 "부모 위임" 잠정안은 이후 폐기 쪽으로 정정됨 — 아래 "확정" 절과
.claude/question.md("Slot의retract동작이 '부모 위임' 잠정안에서 '폐기(옮기지 않음)'로 확정") 참고. 이 문단은 검토 과정의 히스토리로만 남겨둠, 현재 유효한 동작 아님.
이건 base/bind-system-plan.md의 "Store 바인드는 재실행 래핑" 확정
모델과 맞물림 — slot이 store 값으로 오면, store 바인드 핸들러가 이전 slot
상태를 retract하고 새 slot 상태로 다시 process하는 사이클을 돈다는 뜻.
Slot 핸들러 자신이 감시 중인 값(배열/스토어)이 바뀔 때 child를 갱신하는
추적(구독)도 base/bind-system-plan.md가 말하는 "process 함수가 다른 값
변경을 추적해도 됨" 범위에 속하고, retract 시점엔 그 추적만 풀면 됨 —
Destroy 시점엔 retract가 호출되지 않는다는 원칙(base/lifecycle-pattern.md)도
동일하게 적용.
확정(2026-08-04 검증 라운드): retract되는 slot은 옮겨지지 않고 그냥 폐기된다.
Slot은 바인딩되는 순간 그 안의 요소를 전부 own해버리는 데이터형 — 새 slot
상태로 교체될 때 이전 slot의 내용을 다른 곳으로 옮기는 경로는 없음, 그냥
버림. React의 portal(<></>)류로 나중에 옮길 수 있게 하는 것도 검토됐으나
이번 마일스톤에서는 오버엔지니어링으로 판단, 하지 않음 — 필요성이 명확해지면
그때 별도로 다시 논의.
자식으로 넘기는 클래스 스토어
자식에게 내려주는 클래스 스토어는 부모 쪽에서 미리 만들어서 내려보내는 게
편할 것 같다는 방향 — store<<ChildClass.Props>> 형태로 구성된 스토어를 만들면
됨(타입 표기는 러프한 스케치, 실제 문법은 tbox의 명시적 제네릭 적용 패턴
f<<T>>(...) — .claude/initreq/tbox/CLAUDE.md:40-41 — 참고해서 확정할 것).
열린 질문 (.claude/question.md에도 취합)
- 재마운트 에러 처리(throw), retract 시 폐기(옮기지 않음) 둘 다 확정. 남은 건 실제 구현 단계에서 이 "폐기" 동작이 실사용에서 불편하지 않은지 재검증하는 정도 — 설계 방향 자체는 더 이상 열려있지 않음.