From bc0a8b9f5f94bf9c2b042ff3c18284cff089646b Mon Sep 17 00:00:00 2001 From: qwreey Date: Thu, 6 Aug 2026 15:23:18 +0900 Subject: [PATCH] =?UTF-8?q?quad-debug/UI=20=EC=88=8F=ED=95=B8=EB=93=9C/Att?= =?UTF-8?q?ribute=20=ED=83=80=EC=9E=85=20=EB=85=BC=EC=9D=98(2026-08-06)=20?= =?UTF-8?q?=EA=B2=B0=EA=B3=BC=20=EB=B0=98=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - research/debug-tooling-plan.md 신설: 런타임 디버깅 플러그인 quad-debug 설계 — BindableEvent/Function이 Studio 플러그인↔Play 중 게임 경계를 넘는지 실측 검증 완료, 채널 위치/페이로드 제약/UUID 기반 on-demand compute/Element Inspector/Explorer-플러그인 트리 동기화까지 정리 - research/ui-shorthand-plan.md 신설: v1 Corner/PaddingAll/Scale 인라인 숏핸드 조사, quad-v2 포팅 확정(RoundSize만 네이티브 UICorner로 대체돼 불필요), quad-roblox 코어 직접 포함 원칙 확정 - research/documentation-plan.md 신설: UI 네이밍 컨벤션 + Store 부작용을 게임 시스템에서 쓰는 패턴 문서화 뼈대 - base/bind-system-plan.md: Attribute 특수 키 타입 파라미터화 신규 논의 추가 - base/modifier-plan.md, README.md, question.md, ROADMAP.md, CLAUDE.md: 위 신규 문서 색인/요약 반영 Co-Authored-By: Claude Sonnet 5 --- .claude/README.md | 3 + .claude/base/bind-system-plan.md | 39 ++ .claude/base/modifier-plan.md | 5 + .claude/question.md | 21 + .claude/research/debug-tooling-plan.md | 524 +++++++++++++++++++++++++ .claude/research/documentation-plan.md | 61 +++ .claude/research/ui-shorthand-plan.md | 102 +++++ CLAUDE.md | 99 ++++- ROADMAP.md | 5 + 9 files changed, 858 insertions(+), 1 deletion(-) create mode 100644 .claude/research/debug-tooling-plan.md create mode 100644 .claude/research/documentation-plan.md create mode 100644 .claude/research/ui-shorthand-plan.md diff --git a/.claude/README.md b/.claude/README.md index a7b72cb..b37aa38 100644 --- a/.claude/README.md +++ b/.claude/README.md @@ -42,6 +42,9 @@ | `tween-plan.md` | 트윈을 Store 밖 특수 bind key로 처리, 기본 오버라이드는 Cancel | 중 — 세부 옵션만 남음 | | `existing-instance-bind-plan.md` | 이미 생성된 인스턴스 재바인드 — 착수 안 하되 "미지원" 확정도 안 함, 열린 가능성 유지 | 하 — v2 초기 스코프 제외 | | `component-composition-plan.md` | 컴포넌트=플레인 함수, State/Source 읽기·쓰기 경계, `StoreSource` 프록시 — 핵심 골격 수렴, modifier/Ref가 컴포넌트 경계를 어떻게 통과하는지만 남음 | 상 — 사용자가 "가장 문제되는 부분"으로 직접 지목 | +| `debug-tooling-plan.md` | 실물 Instance→코드 위치 역추적 Studio 플러그인(`quad-debug`) — 채널 실현 가능성(BindableEvent/Function 크로스 컨텍스트)까지 실측 검증 완료, 세부 API 이름·구현만 남음 | 하 — 사용자가 "quad 개발 완료 전엔 착수 못 함"으로 직접 후순위 지정, base 설계 시 훅 확장 지점만 고려 | +| `documentation-plan.md` | UI 네이밍 컨벤션 문서 + Store 부작용을 게임 시스템에서 깔끔하게 쓰는 패턴 문서 — 뼈대만, `debug-tooling-plan.md` 논의에서 파생 | 하 — 착수 시점 미정, 뼈대만 기록해둔 상태 | +| `ui-shorthand-plan.md` | UICorner/UIPadding/UIScale 인라인 편의 키(v1 `Corner`/`PaddingAll`/`Scale` 선례) — 여전히 필요한 기능으로 재확정(RoundSize만 네이티브 UICorner로 대체돼 불필요), 메커니즘(Handler)·패키지 배치(quad-roblox 코어) 확정 | 하 — 결론 남, M10 전후 구현하면 됨 | ## 참고 diff --git a/.claude/base/bind-system-plan.md b/.claude/base/bind-system-plan.md index 7c31475..c70558f 100644 --- a/.claude/base/bind-system-plan.md +++ b/.claude/base/bind-system-plan.md @@ -506,6 +506,45 @@ Service` 기반으로 구현)로 두면 됨 — 별도 `On` 모듈/필드 접근 - **Store/State 전파 모델, 라이프사이클 — 둘 다 재검토 후 기존 확정 유지** (위 "Store/State/Source 온톨로지" 절의 "PA님 코드와의 교차검증" 참고). +## Attribute 특수 키 — 타입 파라미터화 (2026-08-06, 신규 논의) + +**상태**: 미확정, 사용자가 이번에 새로 제기 — 이전에 기록된 적 없음 +(`architecture.md` 4번 항목의 `[Attribute "Name"]`은 특수 DI 키의 존재만 +확정했을 뿐, 타입을 어떻게 표현할지는 다룬 적 없었음). + +**문제**: Roblox Attribute는 Instance/Tag와 달리 실제로 **타입이 있는 +값**(string/boolean/number/Color3/UDim/UDim2/Vector2/Vector3/CFrame/ +Instance 참조 등 제한된 프리미티브 집합, 테이블 등 복합 타입은 지원 안 +함)이라, 그냥 `[Attribute "name"] = value`로 두면 `value`의 타입을 Luau가 +좁혀줄 방법이 없음. 커스텀/복합 데이터(테이블 등)는 애초에 Attribute가 +지원을 안 하므로 Ref(직접 참조 획득) 쪽으로 빠지는 게 맞고, Attribute는 +프리미티브 전용으로 남기면 된다는 게 사용자 판단 — Value 오브젝트가 +역사적으로 Attribute의 대안(테이블/참조를 담는 용도)으로 나온 배경이지만, +지금은 Roblox Attribute가 Instance 참조 타입도 지원해서 `ObjectValue` +없이도 Ref 용도로 Attribute를 그대로 쓸 수 있다는 점을 사용자가 짚음 +(`research/debug-tooling-plan.md`의 "Value 오브젝트 기각, Attribute로 +확정" 결정과 같은 방향 — Instance 타입 지원까지 감안하면 그 결정의 근거가 +한층 더 탄탄해짐). + +**후보 두 가지**: +- `[Attribute<> "name"] = true` (리터럴 또는 store-bind 값) — + 제네릭 파라미터로 타입을 명시하는 제네릭 생성자 스타일. +- `[BooleanAttribute "name"] = true` — 타입별로 이름이 다른 정적 생성자 + 패밀리(`StringAttribute`/`NumberAttribute`/`Color3Attribute`/ + `InstanceAttribute` 등). + +**소견(확정 아님, 검토 필요)**: 이 선택은 이미 확정된 DI 인스턴스 생성 +패턴(위 "인스턴스 생성 / 이벤트 네이밍 인체공학" 절)과 구조적으로 똑같은 +문제 — 그때도 "제네릭 하나로 다 커버할지 vs 타입별 정적 필드로 나눌지" +고민이 있었고, 결론은 **둘 다**(`new(className)` 제네릭 +생성자 + 자주 쓰는 ~25개는 정적 필드로 미리 바인딩)였음. Attribute도 같은 +모양을 재사용하면 자연스러울 가능성 — `Attribute("name")` 제네릭을 +기본으로 두고, 실사용 빈도가 압도적으로 높을 `Boolean`/`Number`/`String`/ +`Instance` 정도만 `BooleanAttribute`/`NumberAttribute`/`StringAttribute`/ +`InstanceAttribute` 같은 지름길로 정적 바인딩하는 절충. 단 이건 사용자 +확인 전 소견일 뿐 — `.claude/question.md`에 반영, 다음 세션에서 사용자 +판단 필요. + ## 남은 열린 질문 (`.claude/question.md`에도 취합) 이 문서의 핵심 설계 질문은 2026-08-04 세 라운드(전파 모델/`:Compute`/State diff --git a/.claude/base/modifier-plan.md b/.claude/base/modifier-plan.md index e906af7..82274b7 100644 --- a/.claude/base/modifier-plan.md +++ b/.claude/base/modifier-plan.md @@ -126,6 +126,11 @@ PA님 방식인 문자열 키 + 런타임 리플렉션으로 감, `base/bind-sys "이벤트 바인딩 정정" 절 참고. Modifier는 이벤트가 아니라 Store/인스턴스 생성과 같은 카테고리라 dot-access 관습이 그대로 적용됨.) +`Modifier.Rounded(8)`가 실제로 어떻게 UICorner 자식을 만들어 붙이는지(v1의 +`Corner` 특수 프로퍼티 선례, 핸들러 배치 소견)는 +`research/ui-shorthand-plan.md` 참고 — 이 문서는 Modifier 값 자체의 +동작만 다루므로 분리. + ### 6. State/Pipe 쪽엔 영향 없음 — 이미 있던 결정의 재확인일 뿐 Modifier가 immutable해야 하는 이유(변환마다 clone)와 State가 이미 diff --git a/.claude/question.md b/.claude/question.md index 3fc79b1..2e1d142 100644 --- a/.claude/question.md +++ b/.claude/question.md @@ -52,6 +52,27 @@ Slot이 섞일 때 순서 보장" 절 참고. Roblox 단일 백엔드로는 급하지 않음 (LayoutOrder/ZIndex로 대부분 해결), 다른 백엔드(웹 DOM 등) 재사용성과 직결되는 문제라 Slot 코어 로직 구현 시점에 재검토. +- **`quad-debug` 세부 API 이름** — `research/debug-tooling-plan.md` 참고. + 채널 실현 가능성(BindableEvent/Function이 플러그인↔Play 중 게임 경계를 + 넘는지)까지 사용자가 Studio에서 직접 실측 검증 완료 — 기술적 불확실성은 + 다 해소됨, 남은 건 세부 API 이름과 "이벤트 함수가 self로 instance를 + 읽는 게 quad 관습"이라는 언급 확인(그 문서 "열린 질문" 절 참고)뿐. + 사용자가 "quad 개발 완료 전엔 착수 못 함"으로 직접 후순위 지정한 건 + 여전함 — base 설계(M2 Dispatch/M3 Source/M5 DI 생성자) 시점에 훅 확장 + 지점만 고려해두면 됨. +- **문서화 전략(UI 네이밍 컨벤션, Store 부작용을 게임 시스템에서 쓰는 + 패턴)** — `research/documentation-plan.md`(뼈대만). 정식 백로그 항목으로 + 올릴지, 착수 시점을 언제로 볼지 사용자 판단 필요. +- **Attribute 특수 키 타입 파라미터화** — `base/bind-system-plan.md` + "Attribute 특수 키" 절(2026-08-06 신규). `[Attribute<> "name"]` + 제네릭 스타일 vs `[BooleanAttribute "name"]` 타입별 정적 생성자 패밀리 + 중 뭘로 갈지 — 소견은 DI 인스턴스 생성 패턴처럼 "제네릭 하나 + 자주 + 쓰는 타입만 정적 지름길" 절충이지만 확정 아님, M10(Handlers/Attribute) + 착수 전 아무 때나 확인해도 됨. +- **UICorner/UIPadding/UIScale 인라인 편의 키 세부** — `research/ + ui-shorthand-plan.md`. 기능 필요 여부(여전히 필요로 재확정)·메커니즘 + (Handler)·패키지 배치(quad-roblox 코어)는 확정, 남은 건 이름 재검토 + (용어 정리 합류)와 `RoundSize`(이미지 라운드) 대체 방식뿐. ## 참고: 지금까지 확정된 것 (요약) diff --git a/.claude/research/debug-tooling-plan.md b/.claude/research/debug-tooling-plan.md new file mode 100644 index 0000000..c3dd794 --- /dev/null +++ b/.claude/research/debug-tooling-plan.md @@ -0,0 +1,524 @@ +# quad-debug — 런타임 디버깅/역추적 도구 계획 + +**상태**: research — 착수 전, 사용자와 계속 논의 필요. 사용자가 "quad 개발이 +어느 정도 끝날 때까지는 실제 구현에 못 들어갈 것"이라고 스스로 판단한 후순위 +항목이지만, **base 설계(디스패치 엔진/Source/DI 생성자) 시점에 훅 확장 +지점만 미리 고려해두면 나중에 훨씬 싸게 먹힌다**는 문제의식으로 지금 미리 +정리해둠. `ROADMAP.md` 백로그 항목("범용 렌더 디버깅 도구로서의 quad-mock")과 +목적이 다름 — 아래 "quad-mock 백로그와의 관계" 절 참고. + +**2026-08-06 세션 결론(핸드오버 요약)**: 설계를 막던 유일한 기술적 불확실성 +(플러그인이 Play 중인 게임과 실시간으로 통신 가능한가)이 사용자의 Studio +실측으로 **해소됨** — `BindableEvent`/`BindableFunction` 둘 다 Plugin↔Play +클라이언트 경계를 넘는다(아래 "데이터 채널" 절). 이후 그 위에서 채널 위치 +(quad 모듈 내부+CollectionService 태그), 페이로드 제약(순수 직렬화 값만), +UUID 기반 on-demand compute, Element Inspector, flash 범위 축소까지 +설계가 한 라운드 더 수렴함(아래 "핵심 설계 방향" 7/8번, React DevTools +절 4번). **남은 건 세부 API 이름과 구현 착수뿐** — 남은 열린 질문은 전부 +후순위/백로그 표시된 것들, 다음 세션에서 뭔가 막혀있지 않음. + +## 배경 — 팀원 피드백 원문 요지 + +Roblox 안의 대부분 렌더러는 "뭐가 어디서 어떻게 렌더링됐는지" 알기 힘들다. +react-lua는 방법이 있긴 하지만 쓰기 어렵고, Studio 안에서 플러그인처럼 바로 +볼 수 있으면 좋겠다는 요청. 구체적으로 원하는 것: + +- **실물 Frame → 생성한 코드 위치 역추적** (제일 핵심 요청) +- Explorer에서 선택한 인스턴스로부터 코드 위치를 보여주는 플러그인, 또는 + 플러그인 자체 트리뷰 +- 어떤 프로퍼티가 어떤 파이프(Store/State 체인)에 연결됐는지, 파이프라인이 + 어떻게 생겼는지 UI로 확인 +- 웹 devtools처럼 변경된 부분을 반짝이게(flash) 보여주기 +- 생성된 Source/Store 목록을 스크립트별로 보여주기 + +스토리북(`ui-labs`, `architecture.md` 9번 항목으로 이미 대체 확정된 것)과는 +다른 문제라는 점을 사용자가 명시적으로 구분함 — 스토리북은 컴포넌트 단위 +격리 테스트, 이건 **인게임 전체를 실행한 상태에서** 발생하는 실제 버그를 +찾는 용도. 실사용 인게임 버그는 이 방식이 훨씬 찾기 쉽다는 게 사용자 판단. + +## 스코프 확정 (사용자 확인) + +**1차 설계는 클라이언트 UI 한정.** quad는 거의 항상 LocalScript/클라이언트 +UI를 다루므로, 1차 통신 채널은 "플러그인 ↔ 같은 머신의 로컬 클라이언트" +BindableEvent 채널로 한정. 서버에서 생성되는 인스턴스(팀 테스트/멀티플레이 +시나리오, RemoteEvent 필요)까지 다루는 건 실제 필요성이 확인되면 그때 확장 — +지금 설계를 막지 않음. + +## 리서치 결과 요약 + +서브에이전트로 `.claude/initreq/` 전체(quad v1, fusion, vide, rbvm, tbox, +quad2-try, artworks)를 조사, 일반 지식으로 Roblox 엔진 제약도 확인: + +1. **참고할 기존 구현체가 없음** — react-lua/roact devtools 소스 자체가 이 + 레포에 없음(react-lua가 애초에 이 프로젝트의 참고 레포 목록에 없음). + `quad2-try/out/quad-debug/`라는 빈 디렉토리가 이미 예약되어 있었으나 + 파일 0개 — 이전 시도도 손댄 적 없는 영역. v1에도 재사용할 인프라 없음 + (`customWarn.lua` 정도, `debug.traceback` 출력만 하는 3줄). +2. **에러 발생 시점 스냅샷 방식은 있지만 상시 레지스트리는 없음** — Vide + (`src/graph.luau`)와 Fusion(`src/Logging/parseError.luau`)은 둘 다 + `xpcall`+`debug.traceback`/`debug.info`로 **에러 나는 순간에만** 스택을 + 찍음. Instance→생성 위치를 항상 기록해두는 상시 레지스트리를 유지하는 + 선례는 없음 — quad-debug가 여기까지 해낸다면 차별점. +3. **Roblox Luau의 `debug` 라이브러리는 제한적** — 표준 Lua/LuaJIT에 있는 + `debug.sethook`(라인/콜 단위 훅), `debug.getlocal`/`setlocal`/ + `getupvalue`/`setupvalue`가 Roblox엔 없음(보안/성능 이유로 제거). + `debug.info`/`debug.traceback`/`debug.profilebegin`류만 노출. **즉 엔진이 + 공짜로 주는 동적 트레이싱 방법은 없고, quad 코드 안에 직접 계측을 심는 + 것 외엔 방법이 없음** — 사용자가 우려한 그대로 확인됨. +4. **"no-op 기본값 → 나중에 실제 구현으로 교체" 패턴의 실사용 선례 발견** — + Fusion `src/External.luau`가 정확히 이 모양: 모듈 상단 upvalue + `currentProvider: ExternalProvider? = nil`을 두고 + `External.setExternalProvider(newProvider)`(31행)로 통째로 교체, 소비 + 함수(`logWarn` 등)는 매 호출 시 `if currentProvider then ... end`로만 + 분기(101행). `RobloxExternal.luau:51`의 `RobloxExternal.logWarn = warn`도 + 같은 기법의 정적 버전. **quad가 이미 쓰기로 확정한 패턴(base는 인터페이스, + 구현은 팩토리가 나중에 주입 — `RobloxFactory` 등)과 정확히 같은 모양** — + 새로 발명할 필요 없이 기존 설계 원칙을 트레이싱에도 그대로 적용하면 됨. +5. **프로덕션에서 완전 제거하고 싶다면**(당장 필요한 결정 아님) darklua의 + 전역 값 주입(`inject_global_value`) + dead-branch 제거, 또는 Rojo + `project.json`을 릴리즈용으로 따로 둬 디버그 전용 파일 자체를 트리에서 + 빼는 방법이 있음 — 일반 지식 수준으로만 확인, 실제 필요해지면 재조사. +6. **플러그인과 실행 중인 게임은 별도 Luau VM(별도 보안 컨텍스트)** — + `_G`/`shared`가 공유되지 않음. `require()`는 공식 문서(creator-docs + `scripting/module.md`)로 "클라이언트-서버 경계를 넘어 같은 ModuleScript를 + require하면 각 쪽이 **독립적인 참조**를 받는다"가 확인됨 — 플러그인 + 경계에도 같은 메커니즘(별도 require 캐시)이 적용될 가능성이 높으나 공식 + 문서가 플러그인 케이스를 명시하진 않음. 따라서 플러그인이 게임 내부의 + Store/Source 레지스트리에 직접 접근하는 경로는 없다고 봐야 함. + - **정정 이력(2026-08-06)**: 이전 초안이 `BindableEvent` 브릿지를 + "차별점"으로 단정했던 건 근거 부족이었음(공식 문서는 플러그인 언급 + 없음, DevForum엔 실패 사례도 있었음) — 이후 사용자가 Studio에서 직접 + 실측해 **BindableEvent Fire/Connect가 Plugin↔Play 중 클라이언트 + 경계를 실제로 넘는다는 걸 확인**(아래 "데이터 채널" 절 5번 참고). + 핵심 원인 추정("react-lua devtools가 쓰기 어렵다")은 여전히 "게임 + 쪽에 브릿지를 미리 심어야 하는 설정 부담" 쪽으로 유지 가능해짐 — + 채널 자체는 되는 게 확인됐으므로. + +## 핵심 설계 방향 + +### 1. "존재하는 State 목록"이 아니라 "무엇이 무엇에 연결됐는가" — 사용자 확정 + +`bind-system-plan.md`에 이미 있듯 State는 `store.key`로 접근할 때마다 +매번 새로 만들어지는 ephemeral 캐시 핸들이라 "지금 존재하는 State 목록"이라는 +개념 자체가 성립하지 않음. **사용자가 이 논의 중 직접 정정**: 값 목록을 +보여주는 대신, Frame을 선택했을 때 "어디에 어떻게 훅이 연결돼 있는지", "이 +Compute 함수가 어디서 생성됐는지"를 보여주는 **연결 그래프** 중심으로 UX를 +잡는 게 맞음. 이건 quad 온톨로지와도 자연히 맞아떨어짐 — 열거해야 할 진짜 +실체는 State가 아니라 **Source**(Store가 소유하는 유일한 진짜 값 지점)와 +**디스패치 이력**(무엇이 언제 어떤 값으로 `process`됐는가) 둘뿐. 파이프라인 +그래프는 이 디스패치 이력을 재구성해서 보여주는 것. + +### 2. 계측 지점 3곳 — no-op 훅 upvalue, Fusion `External.luau` 패턴 재사용 + +사용자가 요청한 "빈 함수 만들어두고 나중에 트레이스 뽑는 동적 계측"을 +그대로 적용. `if DEBUG then` 분기를 코드 전체에 뿌리지 않고, 아래 세 지점에 +**모듈 upvalue 형태의 no-op 기본 훅**만 심어두면 됨(위 리서치 4번 패턴): + +- **`Dispatch/init.luau`의 `process`/`retract` 스캔 루프** — 어차피 매 + 호출마다 우선순위 스캔이 도는 지점이라, 여기에 훅 호출 1개(no-op이면 + 사실상 함수 호출 오버헤드뿐, 무시 가능 수준) 추가. `(inst, k, v, handler, + timestamp)`를 훅에 넘기면 "무엇이 무엇을 바꿨는가" 이력의 원천이 됨. +- **`Source.luau` 생성자** — Source 인스턴스를 weak-keyed 전역 레지스트리에 + 등록하는 훅(기본 no-op). 켜졌을 때만 이 레지스트리가 존재하므로 GC-native + 원칙(`lifecycle-pattern.md`)과 충돌 없음 — 꺼져 있으면 레지스트리 자체가 + 안 만들어짐. +- **quad-roblox `DI/init.luau`의 제네릭 생성자(`new(className)`)** — 인스턴스 + 생성 순간 `debug.info(2, "sl")`로 caller의 script+line을 얻어 기록하는 + 훅(기본 no-op). Instance 생성은 프로퍼티 변경보다 훨씬 드물게 일어나므로 + (렌더 타임 1회), 여기서만 비교적 비싼 `debug.info` 호출을 해도 부담 적음. + +이 세 곳 모두 "인터페이스는 base가 정의, 실 구현은 quad-debug가 나중에 +주입"하는 기존 확정 원칙(`bind-system-plan.md` "base 유틸은 인터페이스, +실제 구현은 백엔드 팩토리가 주입")과 완전히 같은 모양 — quad-debug를 위해 +새 아키텍처 패턴을 만드는 게 아니라 기존 패턴을 재사용하는 것뿐. + +### 3. 표준 디버그 인터페이스 — 핸들러가 선택적으로 구현하는 5번째 훅 (사용자 제안) + +사용자 제안: "트윈이 뭐 땜에 일어나냐, quad 땜인지 아님 Ref로 밖에 나가진 +instance로 직접 트윈되어버리는지" 같은 걸 구분하고 싶음. 이건 디스패치 +이력만으론 부족함 — quad가 만든 Tween과 사용자 코드가 `Ref`로 얻은 raw +Instance에 직접 `TweenService:Create()`를 건 것을 구분하려면 **핸들러 자신만 +아는 맥락**이 필요. + +**제안**: `isHandlable`/`priority`/`process`/`retract` 4종 계약에 선택적 +5번째 훅을 추가 — `describe(inst, k, v): DebugInfo?`(가칭, 기본 미구현 += no-op과 동일 효과). quad-debug가 트레이스 이벤트를 기록할 때 해당 키를 +처리한 핸들러에게 `describe`가 있으면 호출해서 사람이 읽을 수 있는 부가 +정보(예: Tween 핸들러라면 "store-bind 유발" vs "직접 세팅"인지, 어떤 store +key에서 왔는지)를 이벤트에 덧붙임. `bind-system-plan.md`가 이미 "4종 계약은 +지금 확정이지만 실제 구현하며 부족한 지점이 보이면 그때 hook 추가(점진적 +확장)"라고 열어둔 것과 정확히 맞아떨어지는 케이스 — 새 원칙이 아니라 이미 +예견된 확장. + +### 4. 외부 변경 감지 — 보조 신호일 뿐, 핵심 채널로 쓸 수 없음(사용자 정정) + +위 3번의 한계: quad가 전혀 모르는 코드 경로(Ref로 얻은 raw Instance에 대한 +직접 조작)는 애초에 `process()`를 거치지 않으므로 quad-debug의 계측 +지점으로는 절대 안 잡힘. 처음 검토했던 방법: quad가 관리하는 인스턴스에 +대해 `inst:GetPropertyChangedSignal(prop)`(Roblox 엔진 자체가 모든 +인스턴스에 제공하는 범용 시그널)을 구독해두고, 변경 시점을 직전 quad +디스패치 이력과 타임스탬프로 대조해 "일치하는 트레이스가 없으면 외부 +변경"으로 표시하는 아이디어. + +**사용자 정정(2026-08-06)**: 이건 quad-debug의 핵심 가치와 맞지 않음 — +"이 프로퍼티가 바뀌었다"는 사실 자체는 `PropertyChangedSignal`로 누구나 +알 수 있고, quad-debug가 진짜 필요한 이유는 **그 변경이 quad의 어떤 +Store/파이프라인/handler에서 왔는지, 코드 몇 번째 줄에서 유발됐는지**를 +보여주는 것 — 순수 관찰(passive observation)만으로는 "무엇이 바뀌었다"만 +알 수 있을 뿐 "왜/어디서"는 증발함. **결론: `PropertyChangedSignal` 기반 +교차검증은 (실제로 크로스 컨텍스트에서 작동한다는 전제하에도) 기껏해야 +"quad가 설명 못 하는 변경이 있었다"는 보조 신호 정도이지, 핵심 트레이스 +채널의 대체재가 될 수 없음.** 핵심 채널(어디서/왜)은 여전히 능동적 계측 +(위 2번, 3번의 훅)에서 나와야 함 — 그 계측 데이터를 플러그인까지 실제로 +전달할 수 있는지는 실측으로 확인됨(아래 "데이터 채널" 절), 이 항목 +자체는 채택하더라도 어디까지나 보조 기능으로만 남음(백로그, 아래 "열린 +질문" 참고). + +### 5. 데이터 채널 — Attribute(스냅샷) + BindableEvent(스트림), **BindableEvent 크로스 컨텍스트 확인 완료** + +**상태(2026-08-06): 사용자가 Studio에서 실측 검증 완료 — BindableEvent가 +Plugin↔Play 중인 클라이언트(LocalScript) 경계를 실제로 넘는다.** +`plugin-ignoreme.luau`(Explorer에서 선택한 BindableEvent에 Connect)와 +`game-ignoreme.luau`(2초마다 반복 Fire)로 테스트한 결과, Fire와 플러그인 +수신이 거의 동일한 타임스탬프로 반복 확인됨(`14:10:42.243` Fire / +`14:10:42.243` Plugin 수신 등, 여러 사이클에 걸쳐 안정적). + +**사용자가 정리한 이유**: Play 모드에 들어가도 플러그인이 다루는 `game`은 +edit 모드와 **별도로 복제된 DataModel이 아니라 같은 DataModel**이고(Play +진입 시 "복사된 컨텍스트"라 부른 건 script identity/보안 컨텍스트가 +다르다는 뜻이지 Instance 자체가 별도 메모리에 복제된다는 뜻이 아니었음), +Roblox Luau 샌드박스가 분리하는 건 **스레드/스크립트 컨텍스트**뿐이고 +그 밑의 C++ 구현 userdata(Instance 자체)는 어느 컨텍스트에서 접근하든 +같은 참조를 가리킴 — 그래서 Instance 기반 메커니즘(시그널 Connect/Fire +포함)이 자연스럽게 경계를 넘어 작동함. `require()`가 컨텍스트별로 독립 +모듈 인스턴스를 주는 것(이전 확인 사항)과는 완전히 다른 층위 — 그건 +Lua 모듈 캐시가 컨텍스트별로 분리된다는 것이지 Instance 자체가 +분리된다는 뜻이 아니었음, 이번 실측으로 그 구분이 명확해짐. + +**`BindableFunction`(요청-응답)도 확인 완료**: `Invoke`→`OnInvoke` 실행→ +리턴값 수신까지 왕복이 여러 사이클에 걸쳐 안정적으로 동작(`ok=true, +"plugin-received"`). 이걸로 위 "React DevTools에서 가져올 아이디어" 3번의 +"기본은 얇은 스트림(BindableEvent), 상세는 on-demand 요청-응답 +(BindableFunction)" 구조가 양쪽 다 실측 검증됨. + +**설계 제약으로 반영할 관측(사용자 지적)**: 같은 스크립트 컨텍스트 안에서 +`BindableEvent`/`Function`은 원래 인자를 직렬화 없이 레퍼런스 그대로 +넘기는 게 문서화된 특징(RemoteEvent와 달리 함수/메타테이블도 그대로 +통과 가능)인데, **플러그인 경계를 넘을 때는 RemoteEvent와 비슷하게 +내부적으로 마샬링(직렬화/역직렬화)되는 것으로 보임** — 실측으로 함수 +자체를 못 넘겨본 건 아니지만(테스트는 단순 값만 사용), Instance/Plugin +간 별도 Luau VM 경계라는 점을 고려하면 합리적인 추정. **결론**: +trace 이벤트 페이로드는 처음부터 함수/클로저 없이 **순수 직렬화 가능한 +값(숫자/문자열/불리언/plain 테이블/Instance 참조)만** 담는다는 원칙으로 +설계 — 애초에 "State를 그대로 넘기고 플러그인이 나중에 `.Get()`한다"류의 +설계는 안 되고(State는 클로저를 담은 객체라 직렬화 불가능할 가능성이 +높음), 넘길 값은 항상 quad-debug가 미리 원시 값으로 변환해서 보내야 함. + +**남은 미확인 범위**: 이번 테스트는 (a) 같은 로컬 머신의 Play/Play Solo +클라이언트 컨텍스트, (b) 원시 값 인자만 확인함. 서버 컨텍스트나 복잡한 +중첩 테이블/Instance 배열 전달까지는 실제 구현 단계에서 재확인 권장 — +단, 1차 스코프(클라이언트 한정, 원시 값 위주 trace 이벤트)에서는 이번 +검증만으로 채널 자체의 실현 가능성은 확정됐다고 봐도 됨. + +- **Attribute**: 인스턴스 생성 시점 1회성 정보(생성 위치, "quad가 관리하는 + 인스턴스인가" 마커)에 적합 — DataModel 자체의 일부라 플러그인과 게임이 + 별도 Luau VM이어도 문제없이 공유됨(Selection 서비스로 바로 읽힘). 단점: + 문자열 크기 제약, 그리고 배포된 게임에 실수로 남으면 유저가 F9 콘솔이나 + Explorer로 내부 코드 경로를 볼 수 있는 정보 노출 위험 — `RunService:IsStudio()` + 가드가 필수(quad-debug require 자체가 옵트인이라는 1차 방어선 + IsStudio가 + 2차 방어선, 이중 게이팅). +- **Value 오브젝트(StringValue/ObjectValue 등)는 기각 — 사용자 확정**: + Attribute의 대안으로 자식 Instance로 값을 담는 Value 오브젝트도 검토했으나, + `:GetChildren()`을 호출하면 그대로 드러나 트리를 오염시킴(quad가 실제로 + 마운트한 자식과 섞여버려 `base/slot-plan.md`의 자식 재조정 로직이나 + 사용자 코드의 children 순회를 방해할 위험) — Attribute는 자식이 아니라 + 메타데이터라 이 문제 자체가 없음. **스냅샷성 데이터는 Attribute로 확정, + Value 오브젝트는 후보에서 제외.** +- **BindableEvent+BindableFunction(크로스 컨텍스트 확인 완료, 위치는 + 재검토)**: "지금 이 순간 일어난 일" 스트림은 BindableEvent, 특정 + Instance 선택 시 상세 정보 요청-응답은 BindableFunction — 둘 다 실측 + 확인됨(위 참고). + - **`ReplicatedStorage` 자동 생성 방식은 기각 — 사용자 정정**: 처음 + 구상은 quad-debug-roblox가 초기화 시 `ReplicatedStorage` 밑에 잘 + 알려진 이름으로 Bindable을 만들어 노출하는 것이었으나, **이건 + 개발자가 의도하지 않은 Instance를 게임 트리에 주입하는, 부작용이 + 큰 행위라 기각**. 대신 Bindable을 **quad 모듈 자신의 Instance + 트리 안**(quad가 이미 설치돼 있는 위치 그대로, 새 위치를 따로 + 안 만듦)에 두고, `CollectionService` 태그로 노출 — 플러그인은 + quad가 어디 설치됐는지 몰라도 `CollectionService:GetTagged(tag)`로 + 바로 찾음(`GetDescendants()`로 전체 트리를 훑어 필터링할 필요 + 없음 — 사용자가 "roblox query descendants" 관련해서 짚어준 더 + 저렴한 방법). 태그를 모듈 자신에 달지 Bindable 각각에 달지는 + 취향 차이 — **사용자 확정**("큰 차이는 없는 엔지니어링 선택"). +- **공통 원칙(사용자 확정)**: 어떤 채널이든 **debug를 안 켰을 땐 CPU/메모리 + 영향이 사실상 없어야 함**(패시브하게 가볍게만 들고 있거나, 아예 아무것도 + 안 함) — 무거운 트레이스 함수 실행은 debug를 켠 다음부터만. 이건 위 + "계측 지점 3곳"의 no-op 훅 upvalue 패턴과 정확히 같은 원칙의 재확인. + +### 6. "관측해야 실체화된다" 원칙 — quad-debug 자신이 위반하면 안 됨 + +`bind-system-plan.md`의 전역 원칙: 어떤 파생값도 `.value`/`Get()`으로 직접 +읽히기 전까지 계산되지 않음. quad-debug UI가 트리뷰를 그리면서 모든 State를 +자동으로 펼쳐 값을 미리 읽어버리면, 원래 필요 없었을 계산을 디버그 도구가 +유발하는 부작용이 생김 — **디버그 도구 자체도 lazy해야 함**: 사용자가 UI에서 +노드를 명시적으로 펼칠 때만 그 시점의 값을 읽고, 자동 폴링/자동 전개는 +지양. 이건 UI 설계 시 지켜야 할 제약으로 문서화만 해두고 지금 확정할 필요는 +없음. + +### 7. UUID 기반 on-demand compute — "관측"을 플러그인 클릭으로 명시화 + +debug 모드가 켜지면 quad의 내부 객체(Source/State/handler 등)에 uuid를 +부여해 trace 이벤트와 함께 플러그인에 넘김(위 "데이터 채널" 절의 페이로드 +제약 — uuid 자체는 순수 문자열이라 문제없음). 플러그인 UI에서 특정 +State 노드를 클릭하면 그 uuid로 `BindableFunction`을 통해 "지금 이 값을 +계산해서 보여줘" 요청을 보내고, quad-debug-roblox가 해당 uuid에 +대응하는 실제 State를 찾아 `.Get()`을 호출해 원시 값으로 변환해 돌려줌 — +**사용자 제안**, 위 "6. 관측해야 실체화된다" 원칙과 정확히 맞아떨어짐 +(플러그인 클릭이 곧 명시적 관측 행위). + +**안전 문서화 경고 필요(사용자 지적)**: 이 compute 호출은 여전히 부작용을 +일으킬 수 있음 — `purity-and-effects-plan.md`가 이미 Store는 부작용 +허용이 기본이라고 확정해뒀고, Compute 함수는 원래 "State가 최신 상태를 +요구받는 시점"에만 실행되는 게 전제인데, 플러그인이 임의의 시점(사용자가 +UI를 클릭한 순간)에 강제로 그 계산을 트리거하는 건 이 전제를 벗어남 — +당장 문제를 일으키진 않더라도, quad-debug 문서에는 "State를 눌러보는 +행위 자체가 그 계산과 딸린 부작용을 실행시킨다"는 걸 명확히 경고해야 함. + +**비직렬화 값의 표시**: 함수/클로저처럼 순수 직렬화 불가능한 값은 +`print`류 다른 디버깅 도구들이 흔히 하듯 `"function"` 같은 플레이스홀더 +라벨로 표시(루아 사이드에서 포인터 주소를 얻는 표준적 방법은 없어 보임, +필요하면 재조사). 다만 사용자가 직접 만든 복잡한 값(예: 커스텀 Tween류 +객체)이 그냥 raw 테이블로만 보이면 알아보기 힘드므로, **개발자가 자기 +타입에 대해 "디버거에 어떻게 보여줄지"를 지정할 수 있는 선택적 직렬화 +인터페이스**를 제공하는 것도 검토할 만함(사용자 제안) — 단, 사용자가 +강조한 대로 **오버엔지니어링 경계 주의 — 디버깅 도구는 한정된 규모에서 +도움이 되는 게 목적이지 모든 걸 다 예쁘게 보여주는 게 목적이 아님**, +구현 비용/이점/타당성을 따져서 결정. + +### 8. Element Inspector — 마우스로 UI 요소 피킹 (사용자의 실제 pain point) + +사용자가 직접 겪은 문제: Roblox가 최근 Play 중 라이브 UI 편집 도구를 +꺼버려서, 실제 화면에 보이는 UI 요소의 위치를 찾으려면 Explorer를 계속 +펼치고 접으며 찾거나 검색해야 하는데, quad로 만든 요소는 보통 이름을 +잘 안 지정해서 특히 힘듦. **웹 devtools의 "inspect element"처럼 화면을 +클릭해서 바로 그 자리의 (quad가 관리하는) UI 요소를 선택하는 도구가 +필요** — 최상위에 클릭을 가로채는 투명 레이어를 하나 띄우고 마우스 +위치를 추적, 그 좌표에 있는 요소를 히트테스트해서 quad 요소로 필터링해 +사용자에게 보여주는 방식. Explorer 기반 트리뷰(위 "핵심 설계 방향" 1번)를 +보완하는 별도 진입점 — "무엇을 선택할지도 모르는 상태에서 화면만 보고 +찾아 들어가야 하는" 초기 탐색 단계의 마찰을 없애는 게 목적. 사용자가 +이번 논의에서 원래 요청("Explorer에서 코드 위치를 알려주는 플러그인") +보다 실제로는 더 크게 느낀 pain point로 언급. + +### 9. Explorer ↔ 플러그인 트리 동기화, UI 아키텍처 확인 (2026-08-06) + +**질문**: 플러그인 자체 트리에는 없는 내부 구현 디테일(예: 특수 핸들러가 +자동 생성해 붙인 자식 Instance)을, 사용자가 Roblox 기본 Explorer에서 +직접 선택하면 어떻게 처리할까? + +**사용자 확정 — 두 경우로 분기**: +- 플러그인 트리에 대응 노드가 **없는** 내부 전용 자동 생성물(quad가 + bind/track하지 않고 그냥 만들어 붙여만 둔 것)이면, 플러그인 트리가 + 실제로 알고 있는 가장 가까운 **부모**를 대신 선택/하이라이트. +- 플러그인 트리에 대응 노드가 **있는** 경우(예: 사용자가 어떤 Instance를 + 직접 컴포넌트화해서 quad로 bind한 경우 — 이건 UB가 아니라 충분히 유효한 + 사용법, `base/component-composition-plan.md`의 "컴포넌트 = 플레인 함수" + 원칙과도 맞음) 트리에 있는 그 실제 노드를 정확히 선택. + +**네이밍 컨벤션(사용자 제안)**: 내부 자동 생성 helper Instance는 `_`나 +`QUAD_` 같은 접두어를 붙여서 Explorer에서 직접 봤을 때 헷갈리지 않게 함 — +이름 바꾸는 건 비용이 크지 않음. v1이 이미 `_quad_round`/`_quad_padding`/ +`_quad_scale` 네이밍(`research/ui-shorthand-plan.md` 참고)으로 정확히 +이 관습을 썼던 전례 — quad-v2에서 내부 자동 생성물이 생기면 그대로 +재사용. `research/documentation-plan.md`의 "UI 네이밍 컨벤션 문서" +백로그에도 이 구체적 규칙을 추가해둠. + +**플러그인 UI 아키텍처 확인(사용자 질문에 대한 답 — 맞음)**: 세 개의 +구분된 상호작용면으로 구성됨 — +1. **자기 트리 뷰** — React DevTools 컴포넌트 트리처럼, 플러그인 자체 + `DockWidgetPluginGui` 안에 quad가 관리하는 계층을 보여줌(위 "핵심 + 설계 방향" 1번, "무엇이 무엇에 연결됐는가" 그래프). +2. **리프 클릭 → 상세/상태 패널** — 노드(State 등)를 누르면 그 상세를 + on-demand로 보여줌(위 7번, UUID 기반 compute-on-click). +3. **실제 Instance 선택과의 연동** — Roblox 기본 Explorer에서 직접 + 선택하거나(`Selection` 서비스로 감지, 위 이 절의 동기화 규칙), 또는 + Element Inspector(위 8번)로 화면을 클릭해서 선택하면, 그 실제 + Instance에 대응하는 노드가 1번의 자기 트리 뷰에서 하이라이트/선택됨. + +Explorer(Studio 기본 창)와 플러그인의 트리 뷰(`DockWidgetPluginGui`)는 +**서로 다른 별도 창** — 하나로 합쳐진 UI가 아니라 나란히 떠 있는 도킹 +위젯 두 개고, 3번이 그 둘을 이어주는 동기화 레이어. + +## React DevTools에서 가져올 아이디어 (2026-08-06 조사) + +서브에이전트로 React DevTools 오픈소스(`facebook/react` 내 +`react-devtools-shared` 등)를 조사. 그대로 베낄 순 없지만(브라우저 +익스텐션 ↔ 웹페이지 구조는 Roblox와 다름) 4가지 발상 중 2개는 상당히 +바로 적용 가능: + +### 1. 전역 훅 주입(`__REACT_DEVTOOLS_GLOBAL_HOOK__`) — 참고는 되지만 그대로는 못 씀 + +익스텐션이 React 로드 *전에* `window.__REACT_DEVTOOLS_GLOBAL_HOOK__`을 +먼저 심어두고, React 렌더러가 부팅하며 그걸 찾아 `hook.inject(...)`로 +스스로 등록하는 "로드 순서 무관 레지스트리" 패턴 — Fusion `External.luau`의 +"이미 로드된 모듈의 업밸류를 나중에 스왑"과는 다른 축(React 쪽은 "누가 +먼저 로드되든 상관없게", Fusion 쪽은 "함수 포인터 교체"). quad-debug에도 +개념은 유효하나, Roblox는 플러그인/게임이 애초에 별도 프로세스(VM)라 +"전역"이 그 경계를 못 넘는다는 근본 제약이 있어 그대로 못 씀 — 이미 알고 +있는 문제(위 "데이터 채널" 절)와 동일선상. + +### 2. 소스 위치 캡처는 런타임 스택 트레이스가 아니라 **컴파일타임 주입** — 유력한 대안 후보 + +확인 결과 React DevTools의 "이 컴포넌트가 어디서 정의됐나"는 런타임 +스택 트레이스에 전혀 의존하지 않음 — `@babel/plugin-transform-react-jsx-source`가 +**빌드 타임에** 모든 JSX 생성 호출에 `__source: {fileName, lineNumber, +columnNumber}`를 리터럴로 박아 넣는 컴파일타임 트랜스폼. 런타임엔 이미 +값으로 존재. + +**quad-debug 적용 후보**: 위 "계측 지점 3곳"에서 제안한 +`debug.info(2, "sl")` 런타임 캡처(DI 제네릭 생성자, 호출 시점 caller +위치)의 대안/보완으로, **darklua** 같은 빌드타임 Luau 변환기로 quad +생성자 호출부를 순회하며 파일/라인 리터럴을 인자로 미리 주입하는 방식을 +검토할 만함. `debug.info`가 "호출자(caller)의 정확한 라인"을 항상 +안정적으로 못 주는 경우(꼬리 호출 최적화, 인라인화 등)에 특히 유용 — +런타임 계측보다 신뢰도가 높을 가능성. 단, **darklua를 빌드 파이프라인에 +편입해야 한다는 전제가 새로 생기므로**(지금 프로젝트는 아직 별도 +빌드/번들 단계가 없음, 순수 Rojo 싱크) 실제 채택은 quad-debug 착수 +시점에 비용 대비 검토. + +### 3. 얇은 operation diff + on-demand 상세조회 — 데이터 채널 설계에 바로 적용 가능 + +content script(페이지) ↔ devtools panel은 별도 프로세스라 매 커밋마다 +전체 트리를 보내지 않음 — **압축된 "operation" 배열**(add/remove/reorder +같은 짧은 코드 시퀀스)만 기본으로 보내고, props/state 같은 무거운 데이터는 +사용자가 실제로 그 노드를 선택했을 때만 별도 요청-응답(`inspectElement`)으로 +가져옴. "기본은 얇은 델타, 상세는 온디맨드"라는 원칙. + +**quad-debug 적용**: BindableEvent 크로스 컨텍스트 검증 결과와 무관하게 +(되든 안 되든, 채널이 무엇이든) 이 원칙 자체는 그대로 채택할 만함 — 매 +`process`/`retract` 호출마다 전체 상태를 흘려보내지 않고 "무슨 일이 +있었다"는 최소 메타데이터(대상 id, key, handler id, timestamp)만 기본 +스트림으로 보내고, 플러그인이 실제로 그 Instance를 선택했을 때만 상세 +정보(생성 스택, props 스냅샷)를 별도로 가져오는 구조. 이러면 채널 +대역폭/오버헤드 문제(위 "공통 원칙 — debug 꺼졌을 때 영향 없어야 함"과 +직결)가 크게 완화됨. + +### 4. flash-on-update 오버레이 — 전체 상시 적용은 기각, 범위를 좁혀 채택 + +원안: `getBoundingClientRect()`류(Roblox면 `AbsolutePosition`/ +`AbsoluteSize`, 3D면 바운딩 박스)를 읽어 오버레이 박스를 그리고 매 +커밋마다 갱신 — Studio 플러그인 오버레이(`Highlight`/`SelectionBox`/ +`BoxHandleAdornment`)로 이식 가능한 아이디어 자체는 유효. + +**사용자 정정(2026-08-06)**: 이걸 quad가 관리하는 **모든** Instance의 +**모든** 프로퍼티 변경에 상시 적용하면 안 됨 — "정말 많은 것들이 다 +반짝일 것"(노이즈)이고 추적 비용도 큼. 범위를 좁혀서 채택: + +- **Instance 마운트/언마운트(생성/파괴)는 상시 flash 가능** — 사용자에게 + "뭔가 새로 생겼다/사라졌다"는 notice로 유용하고 빈도도 낮아 비용 문제 + 없음. +- **개별 프로퍼티 변경 flash는 플러그인에서 현재 열어본(inspect 중인) + Instance 한정** — 위 "핵심 설계 방향" 7/8번의 on-demand 상세조회 + 패널을 연 상태에서만 그 Instance의 값 변경을 반짝이게 표시, 나머지는 + 안 함. "얇은 스트림 + on-demand 상세"라는 이미 확정된 프로토콜 모양 + (아래 3번)과도 자연히 맞아떨어짐. +- **백로그(낮은 우선순위, 사용자 확정)**: 선택된 Instance에서 quad가 + 건드리지 않은 프로퍼티 중 기본값이 아닌 것까지 같이 알려주는 기능 — + "쉽다면 있으면 좋겠지만 엄청 중요하진 않다"는 평가, 초기 설계 시 + 가능성 정도만 열어두고 실제 채택은 나중에. + +## quad-mock 백로그와의 관계 + +`architecture.md`의 기존 백로그("범용 렌더 디버깅 도구로서의 quad-mock, +Tween mock 등 동적 동작 포함")와 목적이 다름: + +| | quad-mock 확장판 (기존 백로그) | quad-debug (이 문서) | +|---|---|---| +| 실행 환경 | Studio 불필요, 순수 `luau` CLI, CI | Studio Play 세션, 실제 엔진 | +| 시점 | 오프라인 스냅샷/리플레이 | 실시간 라이브 관찰 | +| 목적 | 렌더 결과 회귀 검증 | 실사용 중 버그 위치 역추적 | + +**공유 가능한 기반**: 둘 다 "quad 내부 이벤트(process/retract 호출, Source +변경)를 관찰 가능한 스트림으로 노출하는 계측 레이어"가 필요하다는 점은 +같음 — 위 "계측 지점 3곳"에서 정의하는 trace 이벤트 스키마를 하나로 +설계해두면, quad-mock(오프라인 검증)과 quad-debug(실시간 스트리밍)가 같은 +이벤트 포맷을 재사용할 수 있음. 지금 당장 통합할 필요는 없고, quad-mock을 +실제로 확장하게 될 때 이 문서를 먼저 참고하라는 정도로만 기록. + +## 패키지 구조 제안 (가칭, 확정 아님) + +기존 `quad-base`/`quad-roblox` 경계 원칙을 그대로 따름 — base는 인터페이스만, +실 구현은 백엔드/애드온이 주입: + +- **`quad-debug`** — 엔진 무관 core. trace 이벤트 스키마 정의, 위 5개 훅 + 지점의 no-op 기본 구현, 이벤트 버퍼/필터링 같은 순수 로직. `quad-base` + 자체에 넣지 않고 별도 패키지로 두는 이유: `quad-base`는 프로덕션 코드가 + 항상 의존하는 코어라 디버그 전용 코드를 섞고 싶지 않음(위 리서치 5번, + 나중에 완전 제거하고 싶을 때도 별도 패키지면 그냥 require 자체를 안 하면 + 끝). +- **`quad-debug-roblox`** — 게임(클라이언트) 쪽에서 require하는 provider. + quad-roblox의 Dispatch/DI에 실제 훅을 꽂고, BindableEvent/Function을 + **quad 모듈 자신의 Instance 트리 안에** 만들어 CollectionService + 태그로 노출(위 "데이터 채널" 절 — `ReplicatedStorage` 등 게임 트리에 + 별도 주입 안 함), `IsStudio` 가드 포함. +- **`quad-debug-roblox-plugin`** — Studio 플러그인. `DockWidgetPluginGui` UI, + `Selection` 서비스로 Explorer 선택 감지, 브릿지 BindableEvent 구독, + 연결 그래프/트리뷰/flash 렌더링. + +## 지금 로드맵에 반영할 것 (최소한만) + +사용자도 동의한 대로 지금 구현할 단계는 아님 — 다만 아래는 관련 마일스톤 +설계 시 "고려는 해두되 지금 확정/구현하지는 않는" 참고용 메모로 남김 +(`ROADMAP.md`의 M2/M3/M5 근처에 훅 확장 지점 존재 가능성만 인지해두는 정도): + +- M2(디스패치 엔진) 구현 시 `process`/`retract` 스캔 루프에 나중에 훅 + 하나를 끼워 넣기 쉬운 모양으로 짜여 있는지 정도만 유의(지금 훅 자체를 + 만들 필요는 없음). +- M3(Source) 구현 시 마찬가지로 나중에 weak-registry 등록 훅을 끼우기 + 쉬운 생성자 모양인지만 유의. +- M5(quad-roblox DI 제네릭 생성자) 구현 시 caller 정보를 나중에 끼워넣기 + 쉬운 단일 진입점(생성자 함수 하나)인지만 유의 — 이건 이미 + `bind-system-plan.md`가 "제네릭 생성자 함수 하나로 통일"이라 확정해둔 + 것과 자연히 맞아떨어짐, 별도 조치 불필요할 가능성이 큼. + +**중요**: 위는 "이런 게 나중에 필요할 수 있으니 지금 설계를 크게 바꾸라"는 +게 아니라, 이미 확정된 설계(단일 디스패치 진입점, 단일 생성자 진입점)가 +우연히도 계측 친화적이라는 걸 확인해두는 것에 가까움 — M0~M11 순서/범위 +자체를 바꿀 이유는 없음. + +## 열린 질문 (`.claude/question.md`에도 취합) + +기술적 실현 가능성은 전부 해소됨(위 핸드오버 요약) — 아래는 우선순위별로만 +분류, 다음 세션 진행을 막는 항목 없음. + +**확인 필요(다음 세션에서 사용자에게 물어볼 것)** + +- "이벤트 함수들이 실제 instance를 읽을 수 있게 self를 건네받는 게 quad의 + 관습"이라는 언급 — 사용자가 이번 논의 중 언급했으나 본인도 "문서화됐는지 + 모르겠다"고 함. 정확히 어떤 함수(이벤트 핸들러? Compute? `describe` 훅?)에 + 어떤 시그니처로 적용되는 관습인지 확인 후 해당 base 문서 + (`bind-system-plan.md` 등)에 반영 필요 — 지금은 추측성 반영 안 함. + +**세부 API 이름 (후순위, 구현 착수 시점에 자연히 정리)** + +- `describe`(가칭) 5번째 핸들러 훅의 정확한 시그니처/이름. +- Attribute 이름 네임스페이싱(`__quadSource`류)과 노출 정보 범위(스크립트 + 전체 경로를 노출해도 되는지, 파일명만 남길지 등 보안/정보노출 고려). + +**백로그(채택 여부만 남음, 핵심 설계와 무관)** + +- 외부 변경 감지(핵심 설계 방향 4번)를 보조 신호로라도 실제로 켤지 — 타이밍 + 매칭 정확도는 프로토타입 단계에서 검증 필요. +- 서버 확장(RemoteEvent) 여부 — 필요성이 실제로 드러나면 그때. +- quad-mock과 trace 이벤트 스키마를 실제로 공유할지, 아니면 별도로 갈지 — + quad-mock 확장 착수 시점에 재검토. +- 인스턴스 변경을 추적하는 전용 새 Source 메커니즘(다크패턴 방지용으로 + 사용자가 제안했다가 스스로 낮게 평가) — quad가 이미 생성 객체에 + `GetPropertyChangedSignal` 역바인딩 옵션을 제공해서 3줄로 되는 것과 큰 + 차별점이 없어 보임, 타당성 조사만 백로그로. + +**범위 밖 — 별도 문서로 분리됨** + +- 이번 논의에서 파생된 문서화 숙제(UI 네이밍 컨벤션, Store 부작용을 게임 + 시스템에서 깔끔하게 쓰는 패턴)는 quad-debug 범위가 아니라 + `research/documentation-plan.md`로 분리해 뼈대만 기록함. diff --git a/.claude/research/documentation-plan.md b/.claude/research/documentation-plan.md new file mode 100644 index 0000000..8778ebc --- /dev/null +++ b/.claude/research/documentation-plan.md @@ -0,0 +1,61 @@ +# 문서화 전략 계획 (뼈대만) + +**상태**: research — 매우 초기. `research/debug-tooling-plan.md` 논의 +(2026-08-06) 도중 파생된 두 아이디어를 별도로 트래킹할 가치가 있다고 +판단해 뼈대만 기록해둠. 세부 설계는 없음, 착수 시점도 미정 — 사용자가 +"간단히 plan 작성은 하는 게 맞을 듯, 아주 뼈대만 있는 것이라 할지라도 +적어줄 필요는 있겠다"고 판단해서 만든 문서. + +## 1. UI 요소 네이밍 컨벤션 문서 + +**배경**: quad-debug 논의 중 사용자가 직접 겪은 pain point로 드러남 — +Roblox가 Play 중 라이브 UI 편집 도구를 꺼버려서 실제 화면의 UI 요소 +위치를 찾으려면 Explorer를 계속 펼치고 접어야 하는데, quad로 만든 요소는 +보통 이름을 잘 안 지정해서 특히 힘들다는 지적(`debug-tooling-plan.md` +"핵심 설계 방향" 8번, Element Inspector로 부분 완화 예정이지만 근본적으로는 +좋은 네이밍 습관이 있어야 함). + +**뼈대(아직 설계 아님, 물음표만)**: +- 어떤 단위에 이름을 붙이게 유도할까 — 모든 Frame? 컴포넌트 루트만? +- 자동 이름 부여 옵션이 있으면 좋을까(예: 컴포넌트 함수 이름을 기본 + `Name`으로 쓰는 관례 — 있으면 편하지만 매직에 가까워 `architecture.md` + 2번의 "마법 안 쓴다" 원칙과 긴장 있을 수 있음, 검토 필요). +- 어디에 문서화할까 — 퀵스타트 튜토리얼? 별도 스타일 가이드? 린트 규칙으로 + 강제할까(과한 선택지, 참고만)? + +**구체적으로 결정된 하위 규칙 하나(2026-08-06)**: quad가 내부적으로 +자동 생성하는 helper Instance(예: `research/ui-shorthand-plan.md`의 +UICorner/UIPadding/UIScale 숏핸드가 만드는 자식)는 `_`나 `QUAD_` 같은 +접두어를 붙여 Explorer에서 직접 볼 때 사용자가 만든 것과 헷갈리지 않게 +함 — v1의 `_quad_round`류 네이밍 재사용(`research/debug-tooling-plan.md` +"핵심 설계 방향" 9번과도 연결, 플러그인이 Explorer 선택을 자기 트리와 +동기화할 때도 이 구분이 필요함). 이건 "사용자가 자기 컴포넌트에 이름을 +잘 붙이게 유도"하는 위 물음표들과는 별개로 이미 결정된 사항. + +## 2. Store 부작용을 게임 시스템에서 깔끔하게 쓰는 패턴 문서 + +**배경**: quad의 Store는 부작용 허용이 기본 설계(`base/architecture.md`, +`base/store-semantics.md`에서 확정)인데, 실제 게임 개발(스킬, 쿨타임, +재화 같은 도메인 상태를 담당하는 store, 부분별 모듈화 등)에서 이 자유도를 +깔끔한 패턴으로 쓰는 법이 아직 문서화 안 됨 — 사용자 원 발언: "원래 +의도했던 편한 부작용 허용을 좀더 더럽지 않은 패턴으로 수행하는 방법도 +적절한 문서화 계획이 있어야겠습니다". + +**뼈대(아직 설계 아님, 물음표만)**: +- 예제 도메인으로 스킬/쿨타임/재화 같은 흔한 게임 시스템을 다룰 것으로 + 보임 — 실제 예제 코드까지 만들지, 원칙만 서술할지 미정. +- `base/purity-and-effects-plan.md`(컴포넌트 "이식성" 경고)와는 성격이 + 다름 — 그쪽은 "이러면 재사용성이 깨진다"는 경고 문서고, 이건 "그래도 + 부작용을 쓸 거면 이렇게 하면 덜 지저분하다"는 처방 문서. 둘을 같은 + 문서에 합칠지 분리할지는 미정. +- 안티패턴 vs 권장 패턴 대조 형식이 적당할지, 다른 형식이 나을지도 미정. + +## 다음 단계 + +둘 다 지금 당장 설계할 필요는 없음(`CLAUDE.md` "지금 할 일" 1번 — +구현 착수가 최우선). 사용자 판단이 필요한 것: +- 이걸 `.claude/question.md`/`CLAUDE.md`에 정식 백로그 항목으로 올릴지, + 아니면 이 파일 하나로 충분한지. +- 착수 시점 — quad-debug처럼 "개발 상당 부분 끝난 뒤"로 볼지, 아니면 + M1(스캐폴딩) 즈음부터 조금씩 곁들일지(네이밍 컨벤션은 특히 초기부터 + 적용하는 게 나중에 소급 적용하는 것보다 쌀 수 있음 — 검토 필요). diff --git a/.claude/research/ui-shorthand-plan.md b/.claude/research/ui-shorthand-plan.md new file mode 100644 index 0000000..928e99c --- /dev/null +++ b/.claude/research/ui-shorthand-plan.md @@ -0,0 +1,102 @@ +# UI 편의 숏핸드 (Corner/Padding/Scale 등) — 인라인 적용 계획 + +**상태**: research — 2026-08-06 세션에서 결론까지 남. `Corner`/ +`PaddingAll`/`Scale` 숏핸드 자체는 **여전히 필요**(사용자 재확정, 아래 +"결론" 절 — 이전에 이 문서가 한 차례 "포팅 불필요"로 잘못 정리했던 걸 +정정함). 패키지 배치는 `quad-roblox` 코어 직접 포함으로 확정. + +## 배경 + +사용자 기억: v1을 쓸 때 "UICorner/UIPadding/UIScale 같은 걸 직접 +`Instance.new`로 만들어 Parent하는 귀찮은 작업 없이, Frame 안에 인라인으로 +넣기만 해도 CSS 스타일처럼 적용됐다 — 코드가 줄고 읽기도 편해서 꽤 +괜찮았다"는 것. 문서 어디에도 기록된 적 없어 v1 소스(`.claude/initreq/quad`)와 +PA님 코드(`.claude/initreq/artworks`)를 서브에이전트로 조사. + +## v1 실제 메커니즘 (조사 완료) + +`class.lua`의 `SetProperty`/`GetProperty`(38~109행)와 `ProcessQuadProperty` +(134~213행)에 하드코딩된 if/elseif 분기로 특수 문자열 키 5종을 지원: + +- `RoundSize = 16` → `ImageLabel`/`ImageButton` 전용, UICorner가 아니라 + 이미지 자체의 9-slice 라운드 처리(`round.SetRound()`) — **UICorner 계열과 + 메커니즘이 다름**. +- `Corner = 8` → 숫자 하나. 기존 `UICorner` 자식이 있으면 재사용, 없으면 + `Instance.new("UICorner", item)`으로 생성(`Name = "_quad_round"`), + `CornerRadius = UDim.new(0, value)` 설정. +- `PaddingAll = UDim.new(...)` / `PaddingAllOffset = 50` → 동일 패턴, + `UIPadding`(`_quad_padding`). +- `Scale = 1.2` → 동일 패턴, `UIScale`(`_quad_scale`). + +값 모양은 항상 **리터럴 하나**(숫자/UDim) — 테이블도 `__type` 태그도 아님. +실사용 예시(`md/kr/tutorial/7_quadProperty.md`): +```lua +Frame "mainFrame" { + PaddingAllOffset = 50; + ImageFrame { RoundSize = 16; ... }; +} +``` + +**`UIListLayout`/`UIGridLayout`/flex는 이런 전용 숏핸드가 v1에 없었음** — +`ProcessQuadProperty`의 범용 자식 나열 분기(배열 인덱스로 놓인 Instance/ +Class 결과를 자동 mount, 207~213행)로 `UIListLayout{...}`을 그냥 직접 +나열했을 뿐, `List = true` 같은 전용 축약 문법은 레포 전체(PA님 코드 +포함)에서 찾지 못함. **quad-v2도 이 부분은 이미 있는 children-array + +인스턴스 생성 문법으로 그대로 커버됨 — 새로 설계할 것 없음.** 사용자 +기억 중 이 부분은 "전용 숏핸드"가 아니라 "선언형 문법 자체가 원래 +간결하다"는 것과 섞였을 가능성이 큼. + +## 결론 (2026-08-06, 한 차례 오해 후 재정정) + +**RoundSize와 Corner는 서로 다른 이유로 존재했던 별개 기능 — 혼동하지 +말 것**: +- **`RoundSize`(이미지 9-slice 라운드)**: `ImageLabel`/`Button`을 + 이미지 트릭으로 둥글게 보이게 하던 것 — **당시 Roblox에 `UICorner` 같은 + 네이티브 구현체가 없었기 때문에** 존재하던 워크어라운드. 지금은 + `UICorner`가 안정적인 네이티브 Instance라 이 이미지 트릭 자체를 그대로 + 포팅할 이유는 없음(이미지에도 그냥 실제 `UICorner`를 쓰면 됨) — + **RoundSize는 포팅 안 함**. +- **`Corner`/`PaddingAll`/`Scale`(UICorner/UIPadding/UIScale 자동 + 생성)**: 이건 워크어라운드가 아니라 **지금도 유효한 편의 기능** — + **사용자 재확정**: "UIScale 같은 건 여전히 별도의 Instance고 부모 + Frame에 영향을 주는 구조, 숏핸드는 여전히 필요하다". `UICorner`가 + 네이티브가 됐다고 해서 "별도 Instance를 만들어 부모에 Parent해야 + 한다"는 구조적 번거로움 자체가 없어지는 게 아니므로, 이 숏핸드의 + 존재 이유는 여전히 유효함 — **이전 정리("포팅 불필요")는 오해였고 + 정정함, `Corner`/`PaddingAll`/`Scale`은 그대로 포팅 대상.** + +**메커니즘 — 새 아키텍처 개념 불필요**: 이미 있는 pluggable Handler로 +그대로 커버됨. `Corner`/`PaddingAll`/`Scale` 같은 특수 키를 인식하는 +Handler(`isHandlable`이 그 키를 매칭)가 "이름 붙은 자식을 찾거나 만들고 +프로퍼티 세팅"을 `process(inst, k, v)`에 구현 — v1의 하드코딩 if/elseif +대신 정식 핸들러 계약(`isHandlable`/`priority`/`process`/`retract`)을 +따르는 것만 다름. `modifier-plan.md`가 이미 예시로 든 +`Modifier.Rounded(8)`은 이 특수 키를 flatten해서 props에 꽂아넣는 사탕 +문법일 뿐, 실제 처리는 이 Handler가 함 — Modifier를 안 거치고 +`Frame { Corner = 8 }`처럼 순수 인라인 키로 직접 써도(v1처럼) 동일하게 +작동함, `architecture.md`의 `[Attribute "Name"]`류 특수 키와 같은 층위. +자동 생성된 자식은 위 "핵심 설계 방향" 관례대로 `_`/`QUAD_` 접두어 +네이밍(`research/debug-tooling-plan.md` 9번, v1의 `_quad_round`류 +그대로 재사용). + +**패키지 배치 — `quad-roblox` 코어에 직접 포함, 확정**: "트윈도 인스턴스 +생성/제어를 직접 구현 가능한 걸 하나로 묶어 쉽게 쓰게 합친 것 — 너무 +잘게 쪼개 오버엔지니어링하기보다 확실히 하나로 코어에 넣어도 충분하다, +opt-out할 이유가 별로 없다"는 게 사용자 판단 — **작고 항상 켜져 있어도 +비용이 무시할 만한 편의 기능은 별도 opt-out 패키지로 쪼개지 말고 +`quad-roblox` 코어에 직접 포함한다**는 원칙으로 확정(이미 계획된 Tween +핸들러가 같은 모양이라는 게 근거). 이 원칙은 일반화해서 재사용 가능 — +앞으로 비슷한 "작은 인스턴스 편의 기능"이 제안되면 `quad-roblox-util` +같은 걸 새로 만들지 않고 이 선례를 따르면 됨. + +**중요도**: 낮음("이건 나중에도 쉽게 구현됨" — 사용자) — 지금 M0 우선순위를 +바꿀 이유는 없음, M10(Handlers/Attribute 등) 전후로 다른 세부 Handler와 +함께 구현하면 충분. + +## 열린 질문 (`.claude/question.md`에도 취합) + +- 이름 그대로 가져올지(`Corner`/`PaddingAll`/`PaddingAllOffset`/`Scale`) + 재검토할지 — 진행 중인 용어 정리(`CLAUDE.md` "지금 할 일" 2번)에 합류 + 대상. +- `RoundSize`(이미지 라운드)를 완전히 드롭할지, 아니면 이미지 대상에도 + 그냥 실제 `UICorner`를 자동 적용하는 것으로 대체할지 — 후순위. diff --git a/CLAUDE.md b/CLAUDE.md index 2b66eb0..39c7f40 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -115,7 +115,14 @@ modifier/Ref의 컴포넌트 경계 통과 방식) 논의도 2026-08-04 세션 4. **[백로그] 범용 렌더 디버깅 도구로서의 quad-mock.** 1번의 quad-base 테스트용 mock과는 별개 — 정적 스냅샷을 넘어 Tween mock 같은 동적 동작까지 지원하는 더 큰 스코프의 디버깅 도구(`architecture.md` "테스트 전략" 절 백로그 참고). - 효용성 봐가며 나중에 검토, 지금 당장 설계할 필요 없음. + 효용성 봐가며 나중에 검토, 지금 당장 설계할 필요 없음. **[백로그, 별개]** + 런타임 디버깅 플러그인 `quad-debug`(실물 Instance→코드 위치 역추적, + `research/debug-tooling-plan.md`)도 2026-08-06 세션에서 설계 수렴 — + 채널 실현 가능성(Studio 플러그인↔Play 중 게임 간 BindableEvent/Function + 통신)까지 실측 검증 완료, 세부 API 이름만 남음. 착수 시점은 여전히 + "quad 개발 상당 부분 끝난 뒤"로 사용자가 못박음. 같은 세션에서 파생된 + 문서화 전략 뼈대(`research/documentation-plan.md`, UI 네이밍 컨벤션 + + Store 부작용 게임 시스템 활용 패턴)도 후순위 백로그로 같이 남김. 5. 자율 작업 루프/스케줄 설정 여부는 사용자 결정 대기 중 (`HUMAN_TODO.md` 2번 항목). @@ -178,3 +185,93 @@ modifier/Ref의 컴포넌트 경계 통과 방식) 논의도 2026-08-04 세션 시작**. 용어 정리 제안 진행 중인 점은 위 "지금 할 일" 2번 참고. + +## 2026-08-06 세션 — quad-debug(런타임 디버깅 플러그인) 설계, 실측 검증까지 완료 + +팀원이 "실물 Frame에서 그걸 만든 코드 위치를 역추적하는 Studio 플러그인이 +있으면 좋겠다"는 피드백을 줬고, 사용자가 이걸 `quad-debug`/ +`quad-debug-roblox-plugin`으로 후순위 설계해두자고 판단해 시작된 세션. +착수는 여전히 "quad 개발이 상당 부분 끝난 뒤"로 못박혀 있음(구현 우선순위는 +안 바뀜) — 대신 base 설계(디스패치/Source/DI 생성자) 시점에 훅 확장 +지점만 고려해두면 나중이 훨씬 싸진다는 문제의식으로 지금 미리 설계만 +해둠. 전체 내용은 `research/debug-tooling-plan.md`, 요지만 요약: + +- **설계를 막던 유일한 기술적 불확실성이 실측으로 해소됨**: Roblox Studio + 플러그인과 Play 중인 게임(LocalScript)이 별도 Luau VM/스크립트 + 컨텍스트라 `BindableEvent`/`BindableFunction`이 그 경계를 실제로 + 넘는지가 문서만으로는 안 갈렸음(공식 문서는 언급 없음, DevForum엔 + 실패 사례도 있었음) — 사용자가 테스트용 플러그인/스크립트 + (`plugin-ignoreme.luau`/`game-ignoreme.luau`, 레포 루트, `*-ignoreme*` + 패턴이라 자동 gitignore)를 직접 Studio에서 돌려 **Fire/Connect, + Invoke/OnInvoke 왕복 둘 다 안정적으로 작동함을 확인**. 원리는 "Play + 진입 시 DataModel이 복제되는 게 아니라 script identity/보안 컨텍스트만 + 분리되고, Instance 자체(C++ userdata)는 어느 컨텍스트에서든 같은 + 참조를 가리킨다"는 것 — 사용자가 실측 도중 직접 정리한 설명. +- **채널은 확인됐지만 처음 구상(quad-debug-roblox가 `ReplicatedStorage`에 + Bindable을 자동 생성)은 기각** — 개발자가 의도 안 한 Instance를 게임 + 트리에 주입하는 부작용이 크다는 사용자 지적. 대신 quad 모듈 자신의 + Instance 트리 안에 두고 `CollectionService` 태그로 노출, 플러그인은 + `GetTagged`로 찾음(`GetDescendants` 전체 순회 불필요). +- Roblox Luau의 `debug` 라이브러리엔 `sethook`류가 없어(확인됨) 엔진이 + 공짜로 주는 동적 트레이싱 방법이 없음 — 대신 Fusion `src/External.luau`에 + 이미 있던 "no-op 업밸류를 나중에 실제 구현으로 통째로 교체" 패턴을 + 재사용하기로 함(quad가 이미 쓰는 "base는 인터페이스, 구현은 팩토리가 + 주입" 원칙과 같은 모양이라 새로 발명할 필요 없음). +- React DevTools 아키텍처도 서브에이전트로 조사 — 그대로 못 베끼는 것도 + 있지만(전역 훅 주입은 프로세스 경계 문제로 안 됨), **컴파일타임 소스 + 위치 주입**(Babel처럼 darklua로 흉내낼 후보)과 **얇은 스트림+on-demand + 상세조회** 원칙은 그대로 채택. +- UX 방향은 사용자가 여러 번 직접 정정: "존재하는 State 목록"이 아니라 + "무엇이 무엇에 연결됐는가" 그래프 중심, flash-on-update는 전체 상시 + 적용이 아니라 마운트/언마운트만 상시+개별 프로퍼티 변경은 현재 열어본 + Instance 한정, PropertyChangedSignal 기반 "외부 변경 감지"는 핵심 + 채널이 아니라 보조 신호일 뿐(어디서/왜 바뀌었는지가 quad-debug의 진짜 + 가치라 순수 관찰만으론 부족). **Element Inspector**(화면 클릭으로 UI + 요소 피킹)가 사용자가 실제로 가장 크게 느낀 pain point로 새로 부상 — + Roblox가 Play 중 라이브 UI 편집 도구를 꺼버려서 Explorer만으로 요소 + 찾기가 힘들다는 실사용 불만. +- 부수적으로 파생된 두 가지(quad-debug 범위 밖) 문서화 아이디어 — + UI 네이밍 컨벤션 문서, 스킬/쿨타임/재화 같은 게임 시스템에서 Store의 + 부작용 허용을 깔끔한 패턴으로 쓰는 법 문서 — 를 `research/ + documentation-plan.md`에 뼈대만 분리해서 남김(위 "지금 할 일" 4번). +- **아직 확인 안 된 것 하나**: 사용자가 "이벤트 함수들이 실제 instance를 + 읽을 수 있게 self를 건네받는 게 quad의 관습"이라고 언급했으나 본인도 + "문서화됐는지 모르겠다"고 함 — 어떤 함수(이벤트 핸들러? Compute? + `describe` 훅?)에 정확히 어떤 시그니처로 적용되는지 다음 세션에서 확인 + 필요(`debug-tooling-plan.md` "열린 질문" 참고), 추측성 반영은 안 해둠. + +**같은 세션 후반, 별개 주제 두 개 추가**(quad-debug와 무관, 사용자가 +"적어는 뒀는데 안 줬는건가" 하며 새로 떠올린 것들): +- **Attribute 특수 키 타입 파라미터화** — `[Attribute<> "name"]` + 제네릭 스타일 vs `[BooleanAttribute "name"]` 타입별 정적 생성자 패밀리. + 기존 문서 어디에도 없던 신규 논의로 확인(`bind-system-plan.md` + "Attribute 특수 키" 절에 새로 추가) — 소견은 DI 인스턴스 생성 때 이미 + 쓴 "제네릭 하나 + 자주 쓰는 타입만 정적 지름길" 패턴 재사용, 확정은 아님. + Roblox Attribute가 이제 Instance 참조 타입도 지원해서 `ObjectValue` + 없이 Ref 용도로도 쓸 수 있다는 점도 확인 — quad-debug 논의의 "Value + 오브젝트 기각, Attribute 우선" 결정을 보강함. +- **UICorner/UIPadding/UIScale 인라인 편의 키** — 사용자가 v1에서 "Frame + 안에 인라인으로 넣기만 해도 CSS처럼 적용됐다"고 기억한 기능, 서브에이전트로 + v1 소스(`class.lua`) 조사해 실체 확인: `Corner`/`PaddingAll(Offset)`/ + `Scale` 3종(+ 별개 메커니즘인 `RoundSize`)이 실제로 있었음(리터럴 값 하나 + → 이름 붙은 UICorner/UIPadding/UIScale 자식을 찾거나 생성). `UIListLayout`/ + Grid/Flex 전용 숏핸드는 없었음 — 그건 이미 quad-v2에 있는 범용 + children-array 메커니즘으로 충분히 커버되므로 새로 설계할 것 없음. + **한 차례 "지금은 UICorner가 네이티브라 포팅 불필요"로 잘못 정리했다가 + 사용자가 재정정**: `RoundSize`(이미지 9-slice 라운드 트릭)만 UICorner + 없던 시절의 워크어라운드라 포팅 불필요고, `Corner`/`PaddingAll`/`Scale` + 자체는 "UIScale 등이 여전히 별도 Instance라 부모에 붙여야 하는 구조는 + 안 바뀌었다"는 이유로 **여전히 필요한 기능으로 재확정**. `research/ + ui-shorthand-plan.md`에 최종 정리 — 메커니즘은 기존 pluggable Handler로 + 그대로 커버(새 아키텍처 개념 불필요), 패키지 배치는 `quad-roblox` 코어에 + 직접 포함으로 확정(별도 `quad-roblox-util` 불필요 — "트윈도 하나로 묶어 + 코어에 넣은 선례처럼, 작고 opt-in 아닌 건 분리 안 한다"는 사용자 판단). +- **quad-debug 플러그인 UI 구조 확정** — Explorer에서 quad 내부 자동 + 생성물(예: 위 UICorner 숏핸드가 만든 것)을 직접 선택했을 때 플러그인 + 트리에 대응 노드가 없으면 부모로 대신 선택, 있으면(사용자가 직접 + bind한 경우 등, UB 아님) 정확히 그 노드 선택. 내부 자동 생성물은 + `_`/`QUAD_` 접두어로 네이밍(v1 `_quad_round`류 재사용, + `documentation-plan.md` 네이밍 컨벤션과 연결). 플러그인 UI는 세 + 상호작용면(자기 트리 뷰/리프 클릭→상세 패널/실제 Explorer 선택과 연동, + Explorer와 플러그인 트리는 별도 도킹 위젯)으로 구성된다는 것도 사용자 + 질문에 확인 응답 — `debug-tooling-plan.md` "핵심 설계 방향" 9번. diff --git a/ROADMAP.md b/ROADMAP.md index 8b48962..f4c9f03 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -112,5 +112,10 @@ Luau 코드로 부딪혀본 적 없는 세 가지**를 던지는 코드로 검 긴장 관계 있음, 재검토 시 그 문서부터 다시 볼 것 - [ ] 범용 렌더 디버깅 도구로서의 quad-mock(Tween mock 등 동적 동작 포함, M1의 quad-base 테스트용 mock과는 별개) +- [ ] `quad-debug`/`quad-debug-roblox-plugin` — 실물 Instance→코드 위치 + 역추적 Studio 플러그인(`research/debug-tooling-plan.md`). 위 + quad-mock과 목적이 다름(오프라인 검증 vs 실시간 라이브 관찰) — + 단 trace 이벤트 스키마를 공유할 여지는 있음, 그 문서 참고. M2/M3/M5 + 구현 시 훅 확장 지점만 고려해두면 이 항목 자체는 지금 착수 불필요. - [ ] v1 마이그레이션 가이드 + `objectListClass.__newIndex` 오타 기능 재현 테스트 - [ ] Slot 형제 순서 보장(다중 백엔드 관점) — Roblox만이면 급하지 않음