quad/.claude/base/purity-and-effects-plan.md
qwreey 17a2e4f05f
docs(base): bind/store/state 3단계 분할, UI 숏핸드 Tween 지원, existing-instance-bind 기각
세 건을 한 커밋에 처리:

1. ui-shorthand-plan.md — Tween 지원 확정. 숏핸드가 자식 프로퍼티를 직접
   대입하지 않고 Dispatch.process(child, prop, ..., 1)로 위임하면 Tween이
   공짜로 따라옴(해석 코드는 PropertyHandler 하나에만 남음). "process 중
   inst를 바꾸는 건 키를 바꾸는 것과 같은 층위라 UB 아님"을
   dispatch-core-plan.md에 일반 규칙으로 명문화. wrap을 Tween<T>.Value에만
   적용되도록 들어올리는 헬퍼가 새로 필요한 유일한 부품. ROADMAP M10에
   통째로 빠져 있던 UI 숏핸드 항목도 보강.

2. existing-instance-bind — 기각, research/ → archive/. 사유: Length/Offset
   등 quad가 만든 트리를 전제한 부기를 바깥에서 밀고 당기는 버그 표면이
   치명적으로 넓어짐. "열려 있음"을 전제로 쓰인 본문 7곳도 같이 정정
   (architecture.md의 "아직 미정" 절은 유일 항목이었어서 절 자체를 갱신).

3. bind-system-plan.md 3단계 분할 + store-semantics.md 흡수(순수 이동):
   - base/store-plan.md 신설 — Store = 이름 붙은 Source 모음
   - base/source-state-plan.md 신설 — 반응형 코어(Source⊇State, 전파 모델,
     :With/:Compute/:Apply/previous, Observer, 구독·생명주기 게이트)
   - bind-system-plan.md 1238→203줄(인스턴스 생성·이벤트 네이밍 + 색인)
   - store-semantics.md 삭제
   참조 40여 곳 스윕. doc-check.py ERROR 0, WARN 101→84.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-14 05:10:49 +09:00

3.5 KiB

컴포넌트 순수성이 아니라 "이식성" 문제 (재정의됨)

상태: base — 확정됨(2026-08-04 세션에 research/에서 승격). 남은 건 가이드 문서 내 배치 위치 정도로 기술적 결정 사항은 없음. 원본: .claude/initreq/raw-userinput.md "순수함수에 대한 범위를 정할 필요가 있음" / "진짜 부작용은 외부에 만들어버린다" 절.

정정: "순수함수 여부"가 아니라 "이식성(portability)" 문제였다

사용자 확인 완료 — 이전 초안의 프레이밍이 부정확했음. quad는 vdom이 없으므로 컴포넌트(Class 함수)는 딱 한 번만 실행된다. 모든 부작용은 그 한 번의 실행에서 전부 등록됨 — store에 의해 렌더 함수 안 특정 부분이 다시 트리거될 순 있지만, 함수 자체가 반복 실행되는 구조가 아님. 이 전제 위에서 실제로 문제였던 것은 "순수함수냐 아니냐"가 아니라 컴포넌트가 자신이 받은 파라미터(store) 대신 전역(global) store를 직접 참조하는 경우의 이식성이었음.

구체적 문제 상황

컴포넌트가 특정 store를 받아서 렌더하도록 설계되어야 하는데, 그렇게 안 하고 전역 store를 직접 참조해버리는 경우:

  • 그 컴포넌트가 한 게임 안에서 한 번만 쓰이는 존재(예: 특정 페이지에 해당하는 컴포넌트)라면 전혀 문제 없음 — 오히려 그게 자연스러울 수 있음.
  • 하지만 여기저기서 재사용하려고 만들어둔 컴포넌트가 전역을 건드린다면 이식성이 망가짐 — 다른 프로젝트/다른 컨텍스트에 갖다 쓸 수 없게 됨.
  • 라이브러리 내부적으로만 쓰는 공유 값(라이브러리가 의도적으로 내부에서 전역 상태를 만들어 쓰는 경우)은 문제 없을 수도 있음 — 이식성 문제는 "재사용을 의도한 컴포넌트가 자기가 받은 입력 밖의 것에 은밀히 의존하는가"에 국한됨.

결론: 입력받은 store만 처리하는 함수가 좋은 컴포넌트

재사용/이식을 의도하는 컴포넌트는 파라미터로 받은 store만 처리하는 게 좋다는 게 결론 — 다만 이건 기술적으로 막을 문제가 아니라 UB로 두고 사용자에게 경고해야 할 문서화 문제. 라이브러리가 "전역 참조 금지"를 런타임/타입 시스템으로 강제하려는 시도는 좋은 접근이 아니라고 명시적으로 판단함(과도한 엔지니어링, 정당한 유스케이스까지 막을 위험).

문서화 방향

  • base/store-plan.md("Store는 부작용을 허용하는 게 기본 디자인")와 같은 결의 문제 — Store 자체의 부작용 허용 여부와는 별개로, 컴포넌트가 "자기 입력 밖의 상태"에 의존하면 이식성이 깨진다는 원칙을 문서에 별도로 명시.
  • 가이드 문서에 "재사용 가능한 컴포넌트를 만들 땐 store를 파라미터로만 받고 전역을 직접 참조하지 말 것 — 페이지/앱 최상위 컴포넌트처럼 애초에 재사용 의도가 없다면 상관없음"이라는 원칙과, 그 이유(이식성)를 예시와 함께 기술.
  • 린트 규칙이나 런타임 경고 같은 기술적 강제는 하지 않음(확정) — 순수 문서 수준의 권장.

열린 질문

  • 문서에 이 원칙을 얼마나 두드러지게(가이드 최상단 vs 각주 수준) 배치할지 — 급하지 않음, 실제 문서 작성 단계에서 결정.