decide(base): Observer 즉시실행 확정, Effect가 Observer를 조합하도록 확정
- state:Observer(fn)는 등록 즉시 1회 실행되는 것으로 확정 — 초기화 순서 디버깅 문제를 피하고, store-bind 프로퍼티 핸들러가 "초기값 적용"과 "이후 변경 반영"을 같은 코드 경로로 통일할 수 있게 됨. - Effect(fn, state?)로 확정 — state 생략 시 기존 스펙(설치 1회 + 확정 정리) 유지, state 지정 시 내부적으로 state:Observer(...)를 조합해 재실행 + 자동 cleanup 배선(React useEffect와 동형). 다수 의존성은 :With(...)로 묶어서 넘김. 여전히 자유 함수(메소드 아님) — leaf 생명주기 바인딩을 state가 소유하지 않아서. - fn 커링 스타일을 Effect/Observer 공통 모듈화 관용구로 권장, state:Apply 커링 조합기 아이디어는 백로그로만 기록. - question.md 0번의 Effect/Observer 열린 질문 해소. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
parent
8ce3c11213
commit
8fc6dd3b8c
5 changed files with 136 additions and 61 deletions
|
|
@ -584,6 +584,17 @@ Frame {
|
|||
이러면 `observer`는 `Frame`이 살아있는 동안만 유지되고, `Frame`이
|
||||
retract/Destroy되면 자동으로 정리됨.
|
||||
|
||||
- **`fn`은 등록 시점에 즉시 1회 실행된다(2026-08-07 여섯 번째 세션,
|
||||
사용자 확정 — 이전까지 미명시였던 항목).** 근거: (1) 이미 채워진
|
||||
State를 나중에 구독하면 그 값을 반영하는 연산이 아예 한 번도 안
|
||||
일어나는 문제가 생겨 초기화 순서에 디버깅 부담이 생김. (2) 초회
|
||||
실행을 하지 말아야 할 구체적 근거가 약함. (3) **이 결정 덕에
|
||||
Observer 하나로 "초기값 적용"과 "이후 변경 반영"을 같은 코드 경로로
|
||||
통일할 수 있음** — 예: State→프로퍼티 store-bind 핸들러가 그냥
|
||||
`state:Observer(function() inst.SomeProp = state:Get() end)`를 걸어
|
||||
두는 것만으로 최초 적용까지 공짜로 됨(별도의 "설치 시 1회 적용" 코드를
|
||||
따로 안 짜도 됨). `state:Observer()`(인자 없는 "항상 관측" 유틸)도
|
||||
이 규칙을 그대로 따름 — 호출 즉시 한 번 관측이 트리거됨.
|
||||
- **값을 안 실어줌 — 반드시 `Get()`을 다시 해야 함.** 기존 "emit은
|
||||
무효화 신호 하나로 좁혀짐 — 값을 안 실어보내므로 저렴함" 원칙(아래
|
||||
"Store/State/Source 온톨로지" 절)이 그대로 적용됨: `fn`은 "뭔가
|
||||
|
|
@ -593,6 +604,10 @@ retract/Destroy되면 자동으로 정리됨.
|
|||
`:With`한 값에 따라 갈리는 경우가 있어서(위 "포지셔널 인자 지양" 절의
|
||||
`noprint` 예시처럼 계산 자체를 통째로 생략하고 싶을 수 있음) — `Get()`
|
||||
호출 여부를 작성자가 직접 결정하게 열어둔 것.
|
||||
- **`fn`을 커링 스타일로 짜는 것도 모듈화 관용구로 권장(2026-08-07 여섯
|
||||
번째 세션)** — `state:Observer(makeLogger("x"))`처럼 팩토리가 실제
|
||||
`fn`을 만들어 반환하는 패턴, `Modifier`의 `Boldify(10)` 커링(`modifier-plan.md`
|
||||
8번)과 같은 결. `base/effect-plan.md`의 Effect도 동일하게 권장.
|
||||
- **base가 제공하는 것은 `isObserver`류 타입 판별자 하나** — children
|
||||
배열 dispatch가 숫자 슬롯 값을 훑을 때 "이게 Observer인가"를 판별해
|
||||
`CreatedRef`와 같은 방식으로 라이프사이클에 묶어주는 것 말고는 base가
|
||||
|
|
@ -615,7 +630,14 @@ retract/Destroy되면 자동으로 정리됨.
|
|||
이 State가 계속 재계산되게만 강제하고 싶을 때 씀. 문서화만 확실히
|
||||
하면 별문제 없음(사용자 판단).
|
||||
|
||||
### `:Subscribe()`/`:Unsubscribe()` — 리프에 안 붙는 "전역/독립" Observer용 (2026-08-06 후속 세션)
|
||||
### 백로그(미확정) — `state:Apply(...)`: `:With`+`:Compute` 등록을 커링으로 자동화 (2026-08-07 여섯 번째 세션, 사용자 제안)
|
||||
|
||||
Effect/Observer의 `fn` 커링 관용구 논의 중 나온 인접 아이디어 — `Modifier`의
|
||||
`:Apply(factory)`(팩토리 체이닝, `modifier-plan.md` 8번)와 비슷하게, 여러
|
||||
개를 커링으로 받아 알아서 `:With`/`:Compute` 등록을 대신 해주는
|
||||
`state:Apply(...)` 같은 조합기가 있으면 편리할 수 있다는 제안. **지금 결정
|
||||
필요 없음 — 백로그로만 기록.** 구체 시그니처/필요성 검증 없음, 나중에
|
||||
`:With`/`:Compute` 관용구가 실제로 자주 반복되는 게 확인되면 다시 논의.
|
||||
|
||||
**문제**: children 배열에 넣는 자동 라이프사이클 바인딩은 Observer가
|
||||
"어딘가 leaf에 붙어있다"는 걸 전제함. 근데 흔한 실사용 패턴 하나가 이
|
||||
|
|
|
|||
|
|
@ -1,60 +1,83 @@
|
|||
# Effect — leaf 죽음에 확정 정리, 재실행 개념 없음
|
||||
# Effect — 설치 + 확정 정리, `state` 있으면 Observer를 감싸 재실행도 지원
|
||||
|
||||
**상태**: base — `research/additional-primitives-plan.md`(다른 프레임워크
|
||||
대비 갭 분석)에서 갈라져 나온 확정 프리미티브. `base/blocker-plan.md`(같은
|
||||
조사에서 나온 다른 확정 프리미티브)와는 서로 무관 — Effect는 Store/State
|
||||
작업이나 Ref/PreRef와도 파생 관계가 아닌 완전히 독립된 요소라 별도 파일로
|
||||
둔다(2026-08-07 문서 정리에서 한 파일로 합쳤던 걸 다시 분리).
|
||||
조사에서 나온 다른 확정 프리미티브)와는 서로 무관 — Store/State 작업이나
|
||||
Ref/PreRef와 파생 관계는 아니라 별도 파일로 둔다(2026-08-07 문서 정리에서
|
||||
한 파일로 합쳤던 걸 다시 분리). 단 `state` 인자를 받는 형태는 내부적으로
|
||||
Observer를 조합해서 만들어짐(아래 참고, 2026-08-07 여섯 번째 세션 확정) —
|
||||
"Observer와 무관한 완전 독립 프리미티브"였던 이전 서술은 정정됨.
|
||||
|
||||
**Observer와는 별개의, 완전히 새로운 요소로 확정** — Ref/PreRef 같은
|
||||
서로 파생된 관계가 아니라 독립적으로 존재하는 primitive. Roblox엔
|
||||
`task.spawn`으로 코루틴에 반복문/타이머를 돌리는 패턴이 흔하고, Luau
|
||||
테이블엔 `__gc` 같은 GC 시점 훅이 없어서 "이게 진짜 사라지는 순간"을 아는
|
||||
유일한 방법은 `Instance.Destroying`류 명시적 신호뿐 — 이런 케이스(타이머
|
||||
시작 → leaf가 죽을 때 반드시 정지)를 위한 별도 primitive로 합의됨.
|
||||
**Effect와 Observer의 관계 확정(2026-08-07 여섯 번째 세션)**: 별개의
|
||||
독립 프리미티브이되, `state`를 받는 형태의 Effect는 내부적으로 Observer를
|
||||
**조합(compose)**해서 만들어짐 — Ref/PreRef처럼 브랜드 태그만 다른 재사용이
|
||||
아니라, Observer(재실행 신호) 위에 자동 cleanup 배선을 얹은 한 단계 위
|
||||
계층. **자유 함수인 이유는 여전히 유효**: `state` 없이도 성립하는
|
||||
mount/unmount 전용 유스케이스가 있고, 실제 leaf 생명주기 바인딩은 (Observer와
|
||||
마찬가지로) children 배열 위치에 거는 것이라 `state`가 그 바인딩을 소유하지
|
||||
않음 — Roblox엔 `task.spawn`으로 코루틴에 반복문/타이머를 돌리는 패턴이
|
||||
흔하고, Luau 테이블엔 `__gc` 같은 GC 시점 훅이 없어서 "이게 진짜 사라지는
|
||||
순간"을 아는 유일한 방법은 `Instance.Destroying`류 명시적 신호뿐 — 이런
|
||||
케이스(타이머 시작 → leaf가 죽을 때 반드시 정지)를 위한 별도 primitive로
|
||||
합의됨.
|
||||
|
||||
```
|
||||
Effect(fn) -> EffectHandle -- fn을 즉시 1회 실행, 리턴값(nil | () -> ())은
|
||||
-- 이 Effect가 바인드된 leaf가 죽을 때 정확히 1회 호출
|
||||
Effect(fn, state?) -> EffectHandle
|
||||
```
|
||||
|
||||
**재실행 개념이 없다** — 값 변화에 반응해 다시 도는 건 Observer(+클로저로
|
||||
직접 짠 cleanup)의 영역이고, Effect는 순수하게 "설치 + 확정 정리" 페어
|
||||
하나만 담당한다. children 배열에 leaf로 놓는 기존 Observer 바인딩 패턴을
|
||||
그대로 재사용(그 leaf가 살아있는 동안만 유효, leaf가 죽으면 정리 콜백
|
||||
호출). 비용은 leaf당 실제 Destroying 바인딩 하나(공유 weak table로 되는
|
||||
Observer보다 비쌈) — 필요할 때만 쓰는 걸로 충분.
|
||||
**`state` 생략 시**: `fn()`을 즉시 1회 실행, 리턴값(`nil | () -> ()`)은
|
||||
이 Effect가 바인드된 leaf가 죽을 때 정확히 1회 호출. 재실행 없음
|
||||
(mount/unmount 전용, React `useEffect(fn, [])`와 동형).
|
||||
|
||||
**Observer에 cleanup 반환 계약을 추가하는 안은 기각됨** — React `useEffect`류로
|
||||
`fn`이 `nil | () -> ()`를 반환하면 다음 재실행 직전에 그걸 불러주는 안을
|
||||
검토했으나, 클로저 업밸류로 이미 쉽게 되고 잘 작동해서(`local lastConn;
|
||||
state:Observer(function() if lastConn then lastConn:Disconnect() end;
|
||||
lastConn = ... end)`) 프레임워크가 이걸 대신해줄 이유가 약하다는 판단 —
|
||||
`state:Observer(fn)` 자체는 여전히 재실행 계약만 갖고, Effect가 별도로
|
||||
"1회 설치 + 확정 정리"를 담당하는 이 분리 구조가 유지됨.
|
||||
**`state` 지정 시(2026-08-07 여섯 번째 세션 확정)**: Effect는 내부적으로
|
||||
`state:Observer(...)`를 감싸는 걸로 구현 — `fn`은 포지셔널 인자로 `state`를
|
||||
받고(`fn(state)`, `:Compute`의 `fn(self)` 포지셔널-self 패턴 재사용,
|
||||
모듈화 목적 — 클로저 캡처 없이 `fn`을 독립적으로 정의/재사용 가능),
|
||||
Observer가 이제 등록 즉시 1회 실행되므로(아래 Observer 절 참고) 그 첫
|
||||
실행이 "설치"를 겸함. 이후 `state`가 무효화될 때마다 **직전 `fn` 호출이
|
||||
리턴한 cleanup을 먼저 호출한 뒤 `fn`을 재호출**, 그리고 Effect가 바인드된
|
||||
leaf가 죽을 때 **마지막 cleanup을 한 번 더 호출**. 결과적으로 React
|
||||
`useEffect(fn, [dep])`와 동형(설치+재실행 사이/최종 cleanup 전부 같은
|
||||
반환 계약 하나로 처리).
|
||||
|
||||
## ⚠️ 미해결 — Effect와 Observer의 관계, 사용자 확인 필요
|
||||
- **다수 의존성은 `:With(...)`로 먼저 하나의 State로 묶어서 넘길 것** —
|
||||
React식 별도 deps 배열을 새로 만들지 않음, quad가 이미 가진 다중 의존성
|
||||
결합 관용구(`base/bind-system-plan.md` "`:With` + `:Compute`" 절)를
|
||||
그대로 재사용해 같은 일 하는 두 번째 경로를 안 만듦.
|
||||
- **`fn`은 커링 스타일도 권장(2026-08-07 여섯 번째 세션, 사용자 제안)** —
|
||||
`Effect(makeLogger("mount"), state)`처럼 팩토리 함수가 실제 `fn(state)`를
|
||||
만들어 반환하는 패턴, `Modifier`의 `Boldify(10)` 커링 관용구(`modifier-plan.md`
|
||||
8번)와 같은 결. `state:Observer(fn)`도 동일하게 커링 스타일을 권장 대상으로
|
||||
같이 문서화(아래 Observer 절 참고) — 모듈화가 필요하면 둘 다 이 패턴을 쓸 것.
|
||||
- **재실행이 필요 없는 케이스와 혼동하지 말 것**: 값 변화와 무관하게 설치+최종
|
||||
정리만 필요하면 `state` 없이 `Effect(fn)`을 씀 — `state`를 굳이 넘겨서
|
||||
재실행을 유발할 필요 없음.
|
||||
|
||||
**임의로 결론내지 않고 열어둠(2026-08-07 문서 정리 세션)**: 위 스펙은
|
||||
`Effect(fn)`를 State에 종속되지 않는 완전한 자유 함수로 서술하지만, 아래
|
||||
두 가지가 문서상 명확히 확인되지 않음:
|
||||
children 배열에 leaf로 놓는 기존 Observer 바인딩 패턴을 그대로 재사용(그
|
||||
leaf가 살아있는 동안만 유효, leaf가 죽으면 최종 정리 콜백 호출). 비용은
|
||||
leaf당 실제 Destroying 바인딩 하나(공유 weak table로 되는 Observer보다
|
||||
비쌈) — 필요할 때만 쓰는 걸로 충분.
|
||||
|
||||
1. **`Effect`가 실제로는 `state:Effect(fn)`처럼 State의 메소드(=Observer의
|
||||
변형, "재실행 없음 + 확정 정리 추가"만 다른 버전)로 구현/노출되어야
|
||||
하는 것 아닌가?** — 그렇다면 "독립 존재 가능한 프리미티브 vs 원천에
|
||||
종속된 파생 데이터"(`base/store-semantics.md`) 분류상 Effect도
|
||||
Observer처럼 후자(자유 함수 생성자 없음, 항상 `:` 메소드)로 재분류해야
|
||||
함. 지금 이 문서는 이전 조사(`research/additional-primitives-plan.md`)를
|
||||
따라 자유 함수로 서술했지만, 이게 최종 확정인지는 불명확.
|
||||
2. **`state:Observer(fn)`가 생성 시점에 `fn`을 즉시 1회 실행하는지가 문서
|
||||
어디에도 명시돼 있지 않음** — `base/bind-system-plan.md`의 Observer
|
||||
절은 "값을 안 실어줌, `fn` 본문에서 `Get()`을 다시 읽어야 함"만
|
||||
명시할 뿐 "생성 즉시 1회 호출되는지"는 다루지 않는다. Effect는
|
||||
"즉시 1회 실행"이 스펙에 명시돼 있어 이 부분만 보면 둘이 겹쳐
|
||||
보인다.
|
||||
**Observer 자체에 cleanup 반환 계약을 추가하는 안은 여전히 기각** — React
|
||||
`useEffect`류로 `fn`이 `nil | () -> ()`를 반환하면 다음 재실행 직전에 그걸
|
||||
불러주는 안을 검토했으나, 클로저 업밸류로 이미 쉽게 되고 잘 작동해서(`local
|
||||
lastConn; state:Observer(function() if lastConn then lastConn:Disconnect()
|
||||
end; lastConn = ... end)`) **Observer 자체**가 이걸 대신해줄 이유는 여전히
|
||||
약함. **이 기각과 위 Effect 설계는 상충하지 않는다** — 그때 기각한 건
|
||||
"Observer 자체에 이 복잡도를 넣지 말자"였지 "이 패턴 자체가 무용하다"가
|
||||
아니었음. 자동 cleanup 배선이 필요한 사람만 opt-in으로 쓰는 별도 계층
|
||||
(Effect)으로 분리해 얹었을 뿐, Observer의 기본 계약(재실행 신호만, cleanup은
|
||||
클로저로 직접)은 그대로 가볍게 유지됨.
|
||||
|
||||
이 두 질문이 풀리면 Effect가 (a) 완전히 별개인 자유 함수 primitive로
|
||||
남는지, (b) `state:Effect(fn)`로 Observer 계열에 합류하는지가 갈린다.
|
||||
**확인 전까지는 위에 적은 자유 함수 스펙을 잠정 스펙으로 두되, 구현
|
||||
착수(M3~M4 전후) 전에 반드시 사용자와 다시 확인할 것** — `.claude/
|
||||
question.md`에 같은 항목 등재됨.
|
||||
## 해결됨 — Effect/Observer 관계 (2026-08-07 여섯 번째 세션, 이전 미해결 절 대체)
|
||||
|
||||
**과거 미해결이었던 두 질문 모두 확정**:
|
||||
1. Effect는 자유 함수로 확정(`state:Effect(fn)` 메소드 아님) — 위 "Effect와
|
||||
Observer의 관계 확정" 절 참고. `state` 인자가 있어도 실제 leaf 생명주기
|
||||
바인딩을 `state`가 소유하지 않아서 메소드로 만들 필연성이 없었음.
|
||||
2. `state:Observer(fn)`는 등록 즉시 1회 실행되는 것으로 확정(`base/
|
||||
bind-system-plan.md`의 Observer 절 참고) — 이 덕에 Effect가 `state`를
|
||||
받을 때 Observer를 그대로 조합해 재사용할 수 있게 됨(별도 "설치 시
|
||||
1회 실행" 로직을 Effect가 따로 만들 필요 없음).
|
||||
|
||||
`.claude/question.md` 0번의 관련 항목도 해소됨으로 갱신 완료.
|
||||
|
|
|
|||
|
|
@ -30,16 +30,11 @@ context-rejected.md`. 아래는 그중 **아직 실제로 열려있는 것만**
|
|||
필요 — 최종 이름만 미정(아래 "용어 정리" 절에 후보 추가). **사용자가
|
||||
"작업 전에 모든 정의를 마치고 싶다"고 명시** — M0 이전 완전 확정 목표.
|
||||
상세는 `research/additional-primitives-plan.md`(이제 이 주제 전용).
|
||||
- **Effect가 Observer의 변형(`state:Effect()`)인지, 완전히 독립된 free
|
||||
function인지 — 신규, 2026-08-07 문서 정리 세션에서 발견.** `base/
|
||||
effect-plan.md`가 지금까지의 조사대로 Effect를 "재실행 없는
|
||||
독립 free function"으로 서술해뒀지만, 사용자가 직접 `state:Effect()`
|
||||
형태(=Observer에 "확정 정리" 계약만 추가된 변형)로 기억하고 있어서
|
||||
확인이 필요함. 관련 하위 질문: `state:Observer(fn)`가 생성 시점에
|
||||
`fn`을 즉시 1회 실행하는지도 현재 문서 어디에도 명시돼 있지 않음(Effect는
|
||||
"즉시 1회 실행"이 스펙에 있음 — 이 부분만 보면 둘이 겹쳐 보이는 이유).
|
||||
**임의로 결론내지 않고 열어둠** — 구현 착수(M3~M4 전후) 전에 확인 필요,
|
||||
상세는 `base/effect-plan.md`의 "미해결" 절.
|
||||
- **[해소됨, 2026-08-07 여섯 번째 세션]** Effect/Observer 관계 — Effect는
|
||||
자유 함수로 확정(`state` 인자를 받으면 내부적으로 `state:Observer(...)`를
|
||||
조합해 재실행+자동 cleanup 배선, React `useEffect`와 동형). `state:Observer(fn)`도
|
||||
등록 즉시 1회 실행되는 것으로 확정. 상세는 `base/effect-plan.md`의
|
||||
"해결됨" 절과 `base/bind-system-plan.md`의 Observer 절.
|
||||
- Untrack/Suspense/Error Boundary/Readonly는 조사 결과 새 프리미티브 없이
|
||||
기존 설계·Lua 자체 기능으로 이미 충분한 것으로 판단(`research/
|
||||
additional-primitives-plan.md` "빈 자리 아닌 것" 절).
|
||||
|
|
@ -206,7 +201,7 @@ context-rejected.md`. 아래는 그중 **아직 실제로 열려있는 것만**
|
|||
| 컴포넌트화(플레인 함수, State/Source 경계, 컴포넌트 경계 modifier/Ref는 named parameter로 전달, multi-root 개념 폐기, `Modifier.Override`) | `base/component-composition-plan.md` |
|
||||
| 컴포넌트 이식성(전역 store 참조 시 재사용성 문제) | `base/purity-and-effects-plan.md` |
|
||||
| Blocker(값 기반 emit 지연/합치기) | `base/blocker-plan.md` |
|
||||
| Effect(leaf 죽음에 확정 정리 — 단 Observer와의 관계는 위 0번 열린 질문 참고) | `base/effect-plan.md` |
|
||||
| Effect(설치+확정 정리, `state` 있으면 Observer 조합해 재실행도 지원 — 확정) | `base/effect-plan.md` |
|
||||
| UICorner/UIPadding/UIScale 인라인 편의 키 — 이름·메커니즘·store-bind 가능성까지 확정 | `base/ui-shorthand-plan.md` |
|
||||
| Batch(lexical) 기각, Context(+레이어드 Store) 기각 | `archive/batch-rejected.md`, `archive/context-rejected.md` |
|
||||
| Fusion/Vide 비교 리서치(주의: 일부 서술은 이후 라운드에서 뒤집힘, 문서 내 정정 표시 참고) | `reference/comparison-fusion-vide.md` |
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ Vide/v1/artworks 소스 근거 조사, Context 구현 난이도 판정) + 그
|
|||
| 후보 | 판정 | 현재 위치 |
|
||||
|---|---|---|
|
||||
| 키 기반 동적 컬렉션 재조정 | **진짜 빈 자리, 최우선** — 아직 열려있음 | 이 문서(아래) |
|
||||
| Effect(leaf 죽음에 확정 정리) | 채택, 단 Observer와의 관계는 미해결 | `base/effect-plan.md` |
|
||||
| Effect(leaf 죽음에 확정 정리 + `state` 있으면 재실행) | **채택, 확정** — Observer와의 관계도 해소 | `base/effect-plan.md` |
|
||||
| Blocker(값 기반 emit 지연/합치기) | **채택** — Batch의 대안 | `base/blocker-plan.md` |
|
||||
| Batch(함수/코루틴 스코프 lexical block) | **기각** | `archive/batch-rejected.md` |
|
||||
| Context(트리 하위 암묵 전파) + 레이어드 Store | **기각** | `archive/context-rejected.md` |
|
||||
|
|
|
|||
37
CLAUDE.md
37
CLAUDE.md
|
|
@ -851,7 +851,7 @@ setter를 단발로 직접 호출하는 흔한 경로는 여전히 mutable이라
|
|||
하나 추가된 것과, 위 Effect/Observer 미해결 항목은 M3~M4 착수 전에
|
||||
확인해야 함.
|
||||
|
||||
## 2026-08-07 여섯 번째 세션 — Ref/PreRef 메소드 API 확정, 파일 분리, Tween GC 저장 구조 확인
|
||||
## 2026-08-07 여섯 번째 세션 — Ref/PreRef 메소드 API 확정, 파일 분리, Tween GC 저장 구조 확인, Effect/Observer 관계 해소
|
||||
|
||||
사용자가 메모 형태로 두 가지를 던짐: (1) Tween 인스턴스를 per-instance
|
||||
저장소에 담는 구조가 실제로 GC-안전한지, (2) Ref가 이제 충분히 완결된
|
||||
|
|
@ -888,5 +888,40 @@ setter를 단발로 직접 호출하는 흔한 경로는 여전히 mutable이라
|
|||
용어 정리 대상" 항목과 모순 없음(이번 세션은 메소드 이름만 확정, Ref라는
|
||||
타입 이름 자체는 여전히 가칭).
|
||||
|
||||
**같은 세션 후반 — `.claude/question.md` 0번의 마지막 미해결 항목(Effect가
|
||||
`state:Effect()`인지 자유 함수인지) 해소.** 사용자가 직접 "정해볼까" 하고
|
||||
제기해 라이브로 논의, 다음으로 확정(전부 `base/effect-plan.md`/
|
||||
`base/bind-system-plan.md`에 반영):
|
||||
|
||||
- **`state:Observer(fn)`는 등록 즉시 1회 실행되는 것으로 확정** — 근거:
|
||||
(1) 이미 채워진 State를 나중에 구독하면 반영 연산이 아예 한 번도 안
|
||||
일어나는 초기화-순서 디버깅 문제, (2) 초회 실행을 안 해야 할 구체적
|
||||
근거가 약함, (3) 이러면 Observer 하나로 "초기값 적용"과 "이후 변경
|
||||
반영"이 같은 코드 경로로 통일됨(store-bind 프로퍼티 핸들러가 최초
|
||||
적용용 코드를 별도로 안 짜도 됨).
|
||||
- **`Effect(fn, state?) -> EffectHandle`로 확정** — `state` 생략 시 기존
|
||||
스펙 그대로(설치 1회 + leaf 죽을 때 확정 정리, 재실행 없음). `state`
|
||||
지정 시 **내부적으로 `state:Observer(...)`를 조합** — Observer가 이제
|
||||
즉시 1회 실행되므로 그 첫 실행이 설치를 겸하고, 이후 무효화마다
|
||||
직전 cleanup 호출 후 `fn` 재호출, leaf 사망 시 마지막 cleanup 1회 —
|
||||
React `useEffect(fn, [dep])`와 동형. 다수 의존성은 `:With(...)`로 먼저
|
||||
하나의 State로 묶어서 넘기는 쪽으로 확정(React식 별도 deps 배열
|
||||
안 만듦 — 같은 일 하는 두 번째 경로 방지 원칙). Effect는 여전히
|
||||
자유 함수(메소드 아님) — `state` 없이도 성립하는 유스케이스가 있고,
|
||||
있어도 leaf 생명주기 바인딩을 `state`가 소유하지 않아서.
|
||||
- **예전에 기각했던 "Observer에 cleanup 반환 계약 추가"와 안 부딪힘** —
|
||||
그때 기각한 건 "Observer 자체에 이 복잡도를 넣지 말자"였지 패턴 자체가
|
||||
무용하다는 게 아니었음. Effect가 opt-in 상위 계층으로 이 패턴을 제공하는
|
||||
지금 구조가 그 기각과 정확히 양립함.
|
||||
- **`fn`을 커링 스타일(팩토리가 실제 fn을 만들어 반환)로 짜는 것도 Effect/
|
||||
Observer 둘 다 모듈화 관용구로 권장** — `Modifier`의 `Boldify(10)` 커링과
|
||||
같은 결.
|
||||
- **백로그로만 기록, 결정 안 함**: `state:Apply(...)`처럼 여러 개를 커링으로
|
||||
받아 `:With`/`:Compute` 등록을 자동화하는 조합기 아이디어(사용자 제안,
|
||||
`Modifier:Apply`의 State판 대응물) — `base/bind-system-plan.md`에 백로그
|
||||
절로만 남김, 시그니처/필요성 미검증.
|
||||
- 이걸로 `question.md` 0번(추가 프리미티브 논의)의 열린 항목은 "키 기반
|
||||
동적 컬렉션 재조정" 하나만 남음.
|
||||
|
||||
**다음 세션이 할 일**: 안 바뀜(`ROADMAP.md` M0부터) — 이번 세션도 이미
|
||||
설계된 것의 세부 마무리라 M0 착수 우선순위 자체는 그대로.
|
||||
|
|
|
|||
Loading…
Reference in a new issue