From 34a0e1dcce34b429d2562a214284f4b109d9e497 Mon Sep 17 00:00:00 2001 From: qwreey Date: Thu, 6 Aug 2026 20:10:15 +0900 Subject: [PATCH] =?UTF-8?q?v1=20=ED=95=98=EC=9C=84=ED=98=B8=ED=99=98(compa?= =?UTF-8?q?t)=20=EB=A0=88=EC=9D=B4=EC=96=B4=20=ED=83=80=EB=8B=B9=EC=84=B1?= =?UTF-8?q?=20=EA=B2=80=ED=86=A0=20=EB=A6=AC=EC=84=9C=EC=B9=98=20=EB=AC=B8?= =?UTF-8?q?=EC=84=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 사용자 질문(v2가 v1에 얇은 compat 래퍼를 제공할 수 있는가)에 답하기 위한 조사. quad2-try의 quad-compat 서브패키지는 빈 폴더로 실제 시도된 적 없었음을 확인 — 반복 조사 금지 대상이 아니라 새로 검토 가능한 주제. 결론: 이벤트 self 관습 등 표면 문법은 opt-in 패키지로 얇게 재현 가능하지만, Class.Extend()의 자동-store+자동재렌더 같은 핵심 런타임은 v1/v2가 컴포넌트 정체성 모델 자체를 다르게 정의해서 얇게 안 됨. 방향 결정(부분 compat vs 마이그레이션 가이드)은 question.md에 열린 질문으로 반영. Co-Authored-By: Claude Sonnet 5 --- .claude/README.md | 1 + .claude/question.md | 7 ++ .claude/research/v1-compat-plan.md | 119 +++++++++++++++++++++++++++++ 3 files changed, 127 insertions(+) create mode 100644 .claude/research/v1-compat-plan.md diff --git a/.claude/README.md b/.claude/README.md index b37aa38..ca6d0a2 100644 --- a/.claude/README.md +++ b/.claude/README.md @@ -45,6 +45,7 @@ | `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 전후 구현하면 됨 | +| `v1-compat-plan.md` | v1 하위호환(compat) 레이어 타당성 검토 — 표면 문법(이벤트 self 등)은 opt-in 서브패키지로 얇게 가능, 핵심 런타임(`Class.Extend()` 자동-store+자동재렌더)은 컴포넌트 정체성 모델 충돌로 얇게 안 됨. quad2-try의 `quad-compat`은 빈 폴더로 실제 시도된 적 없었음을 확인 | 하 — 방향(부분 compat vs 마이그레이션 가이드) 사용자 결정 대기 | ## 참고 diff --git a/.claude/question.md b/.claude/question.md index 5edd49c..eb82b80 100644 --- a/.claude/question.md +++ b/.claude/question.md @@ -86,6 +86,13 @@ ui-shorthand-plan.md`. 기능 필요 여부(여전히 필요로 재확정)·메커니즘 (Handler)·패키지 배치(quad-roblox 코어)는 확정, 남은 건 이름 재검토 (용어 정리 합류)와 `RoundSize`(이미지 라운드) 대체 방식뿐. +- **v1 하위호환(compat) 레이어 방향** — `research/v1-compat-plan.md`(신규, + 2026-08-06). 타당성 평가 결론: 이벤트 self 관습 등 표면 문법은 opt-in + 서브패키지로 얇게 가능, `Class.Extend()` 자동-store+자동재렌더 같은 + 핵심 런타임은 컴포넌트 정체성 모델(v1 mutate vs v2 선언적 재구성) 자체가 + 달라 얇게 안 됨. 사용자 결정 필요: 목표가 "일부 문법만 익숙하게" 인지 + "v1 코드를 거의 무수정으로 돌리는 것"인지 — 후자면 compat보다 마이그레이션 + 가이드가 더 현실적이라는 게 문서의 권장. ## 참고: 지금까지 확정된 것 (요약) diff --git a/.claude/research/v1-compat-plan.md b/.claude/research/v1-compat-plan.md new file mode 100644 index 0000000..3481afe --- /dev/null +++ b/.claude/research/v1-compat-plan.md @@ -0,0 +1,119 @@ +# v1 하위호환(compat) 레이어 타당성 검토 + +**상태**: research — 신규 조사(2026-08-06 세션, 사용자 질문으로 착수). 설계 +확정 아님, "얇은 래퍼가 가능한가"에 대한 타당성 평가만 담음. + +**배경**: 사용자가 "quad v1에 대한 하위호환 레이어를 v2가 얇은 래퍼로 제공할 +수 있을지" 질문. 폐기된 재작성 시도 `quad2-try`에 `quad-compat`이라는 +서브패키지가 있어서 "이미 한 번 시도했다 실패한 것"으로 짐작했으나, 조사 +결과 아래처럼 사실이 아니었음 — 완전히 새로 검토할 만한 주제. + +## 1. 선행 조사: quad2-try의 `quad-compat`은 실제로 시도된 적 없음 + +`base/bind-system-plan.md:715`에서 quad2-try의 서브패키지 9개(`quad-docs`, +`quad-debug`, `quad-compat`, `quad-2`, `quad-roblox`, `quad-lang`, `quad-gtk`, +`quad-core` 등)를 나열하며 "`quad-core` 밖엔 참고할 게 없다"고 기록돼있는데, +직접 확인한 결과 `out/quad-compat/`은 **파일이 0개인 완전히 빈 디렉토리**. +compat.lua나 어댑터 코드는 전혀 없고, README/커밋 메시지에도 "왜 포기했는지" +단서가 없음 — 애초에 착수된 적이 없다는 뜻. + +→ `question.md:110`이 "OOP 상속/커스텀 파서/Slot 스텁/`Pipe` COW는 확인된 +죽은 접근"이라고 명시한 목록에 compat은 **포함돼 있지 않음**(정확한 서술). +즉 CLAUDE.md의 "반복 조사 금지"는 compat에는 적용되지 않는다 — 이 문서를 +쓰는 게 규칙 위반이 아님. + +## 2. v1 공개 API 표면 — 두 계층으로 나뉨 + +v1(`.claude/initreq/quad/src`) 조사 결과, API는 성격이 다른 두 계층으로 +나뉜다: + +**(a) 표면 문법** — 개별 함수/헬퍼로 비교적 독립적: +- 이벤트 핸들러가 첫 인자로 `self`(or `this`)를 받는 관습(`event.lua:81-83`) +- 프로퍼티 테이블의 특수 키(`RoundSize`/`Corner`/`PaddingAll`/`Scale`, + `class.lua:134-213`) +- `target()`을 통한 양방향 프로퍼티 바인딩(Linker, `class.lua:352-358`) +- `store.GetObjects("a,b&c")` 쿼리 문법의 오브젝트 태그 저장소(`store.lua:103-190`) + +**(b) 핵심 런타임** — v1 컴포넌트 모델 그 자체: +- `Class.Extend()`가 반환하는 단일 메타테이블이 상속 체인을 대신 (`class.lua:361`) +- 인스턴스화 시 생성자 인자를 자동으로 store로 감싸고(`class.lua:367`), + 이후 `comp.Text = "hi"`처럼 프로퍼티를 재대입하면 `__newindex`가 자동으로 + 내부 store에 위임 + `UpdateTriggers`에 걸리면 자동 재렌더까지 발생 + (`class.lua:524-566`) — CLAUDE.md에 이미 "이 자동 위임/재렌더 매직은 + v2에서 폐기하기로 확정"이라 기록된 바로 그 메커니즘. + +## 3. 계층별 실현 가능성 + +### 3-1. (a)는 얇게 재현 가능 — opt-in 서브패키지로 격리하면 근거 문제도 해소됨 + +- **이벤트 self 관습**: 클로저 한 겹으로 재현 가능. `base/bind-system-plan.md` + "이벤트 핸들러는 self를 받지 않는다" 절이 든 반대 근거 4가지(Ref 중복 + 채널, Modifier 정적 flatten과 경쟁, quad-debug 추적 밖 mutate 경로, 클로저 + 비용)는 **코어에 넣을 때** 문제가 되는 것들 — 별도 opt-in 패키지 + (`quad-compat` 부활)로 격리하면 비용은 compat 사용자만 부담하고 코어 KV + 핸들러 분기도 안 생김. 단, "quad-debug 추적 밖 mutate 경로가 생긴다"는 + 근거(4번)는 격리해도 남는 문제라 별도 검토 필요. +- **RoundSize 등 특수 키**: `Corner`/`PaddingAll`/`Scale`은 이미 + `research/ui-shorthand-plan.md`에서 네이티브 포팅 확정됨 — 별도 compat + 작업 불필요. `RoundSize`(이미지 9-slice 라운드 트릭)만 UICorner 없던 + 시절 워크어라운드라 재현 자체가 불필요하다고 이미 결론남. +- **`target()` 양방향 바인딩**: v2 State가 이미 get/set 가능한 핸들이라 + "두 State를 서로 미러링하는 어댑터 함수" 정도는 가능해 보임 — 정확한 + 구현은 미검증, 착수 시점에 확인 필요. +- **오브젝트 태그 조회**: v2엔 대응 개념이 아예 없음 — `CollectionService` + 태그로 유사 구현은 가능하나 새 서브시스템에 가까워 "얇다"고 하기 애매. + +### 3-2. (b)는 얇게 안 됨 — 컴포넌트 정체성 모델 자체가 충돌 + +`Class.Extend()` 자동-store 위임 + 자동 재렌더는 v1 컴포넌트 작성 경험의 +본질인데, v2는 정확히 이 매직("자기 store 자동 소유")을 이미 폐기하기로 +확정한 상태(`base/component-composition-plan.md` §1, 사용자 확정 발언 +"마법 안쓴다 그것도 동의함"). 이유는 이름 문제가 아니라 **컴포넌트 +정체성을 다르게 정의**하기 때문: + +- v1: 컴포넌트는 렌더 후에도 "살아있는 오브젝트"로 남아 `.Text = ...` + 재대입을 전제 — mutate 기반. +- v2: 반응형 소스(Store/State)를 갈아끼우는 방식, 컴포넌트는 "특정 상태의 + store를 받는 함수"(`architecture.md`) — 만들어진 후의 컴포넌트 인스턴스를 + 밖에서 mutate하는 접점 자체가 없음. + +이 격차를 메우려면 compat 레이어가 컴포넌트마다 "가짜 OOP 인스턴스"를 +만들어 내부적으로 v2 Store/State를 대신 조작해주는 shim을 새로 설계해야 +함 — 몇 줄짜리 어댑터가 아니라 사실상 v1 런타임을 v2 위에 재구현하는 것. +참고 사례로 Vue 2→3의 `@vue/compat`이 있으나, 그것도 별도 빌드 모드 + +다수의 호환 플래그 + 성능 오버헤드 경고가 딸린 규모라 "얇다"고 부르기 +어려움. + +## 4. 결론 / 권장 + +- **"v1 코드를 무수정으로 돌리는 완전 하위호환"은 사실상 불가능하거나, + 적어도 "얇은 래퍼"라는 표현에 맞지 않음** — 근본 원인은 API 이름 + 불일치가 아니라 컴포넌트 정체성 모델(OOP mutate vs 선언적 재구성) 충돌. +- **문법 설탕 수준의 부분 compat은 시도할 가치 있음** — 이벤트 self 관습, + 양방향 바인딩 헬퍼 정도는 opt-in 서브패키지로 저비용 제공 가능. 단 이건 + "v1 코드가 그대로 돈다"가 아니라 "v1에서 넘어온 사람이 익숙한 문법 + 일부를 v2 컴포넌트 함수 안에서 부분적으로 쓸 수 있다"는 수준. +- **"v1 코드베이스 자체를 옮기는 것"이 목표라면 compat보다 마이그레이션 + (자동 변환 스크립트 또는 수동 포팅 가이드)이 더 현실적** — `question.md`에 + 이미 있는 "v1 `objectListClass.__newIndex` 오타 기능 재현 테스트, 마이그레이션 + 가이드 작성 시점에 필요" 항목도 이 방향을 전제하고 있어 자연스럽게 합류 + 가능. +- 확정된 소스 트리(`base/architecture.md` "구현 착수" 절)는 `quad-base`/ + `quad-roblox` 두 패키지뿐 — `quad-compat`은 그 목록에 없음. 부분 + compat을 실제로 만든다면 세 번째 패키지를 새로 추가하는 결정이 필요함. + +## 5. 열린 질문 (사용자 판단 필요) + +- 목표가 "v1 문법 일부를 v2에서도 익숙하게 쓰게" 인지, "v1 코드를 무수정 + 또는 최소 수정으로 돌리는 것"인지 — 답에 따라 부분 compat vs 마이그레이션 + 가이드 중 방향이 갈림. +- 부분 compat으로 간다면 `quad-compat`을 정식 세 번째 서브패키지로 추가할지. +- 이벤트 self 관습을 compat에서 되살릴 때, `base/bind-system-plan.md`가 + 이미 명시한 반대 근거 4번(quad-debug 추적 밖 mutate 경로)을 어떻게 + 처리할지(quad-debug는 어차피 후순위라 지금 결정 불필요할 수도 있음). + +## 착수 시점 + +지금 당장 설계/착수 불필요 — `CLAUDE.md` "지금 할 일" 1번(구현 착수, +ROADMAP M0)이 최우선. 이 문서는 타당성 평가 결과만 남겨두고, 실제 설계는 +사용자가 방향(부분 compat vs 마이그레이션 가이드)을 정한 뒤 진행.