@g1cloud/entity-modeler-next 5.1.1 → 5.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -76,6 +76,43 @@ export type ResolveError = {
76
76
  opIndex: number;
77
77
  handle: AssocHandle;
78
78
  }
79
+ /**
80
+ * `association.add` 의 end 중 하나가 `JPA_EMBEDDABLE` — 임베더블은 독립 참조 대상이 아니라 owner 테이블로
81
+ * 평탄화되는 값이므로 그 관계는 **EMBED 여야** 한다(owner 의 `EMBED_OWN` 표시속성 + `end.composition` +
82
+ * `end.attributeRef` 셋이 EMBED 판정의 유일한 근거다 — `core/sharedColumn.sharedColumnScopeEntities`·
83
+ * `ddl.ts` 평탄화·코드젠 `@Embedded` 가 전부 그것을 본다).
84
+ *
85
+ * ★resolver 에는 **EMBED 관계 생성 어휘가 없다**(AA-5 S3 미구현). 그래서 통과시키면 *항상* 「스테레오타입은
86
+ * 임베더블인데 관계는 일반」 형상이 만들어지고, 그 형상은 **아무도 신호하지 않는다**: 공유 통화 스코프가
87
+ * 자기 자신으로 좁아져 owner 통화 컬럼을 `SHARED_REF` 로 고를 수 없고(GUI picker 후보 0 = 사용자에겐
88
+ * 「리졸빙 안 됨」으로만 보인다) DDL 은 컬럼을 평탄화하지 않으며 코드젠은 `@Embedded` 를 내지 않는다.
89
+ * 실측(2026-08-25 프로덕션): 이 형상 4건 중 1건이 증상으로 드러나고 3건은 잠복이었다.
90
+ *
91
+ * ⇒ GUI 는 이 조작을 애초에 EMBED 로 분기한다(`DiagramCanvas.vue` 연결 핸들러·`editor/association.ts`).
92
+ * 같은 불변식을 op 경로에서도 지킨다 — 어휘가 생기면(AA-5 S3) 거부 대신 EMBED 방출로 바뀔 자리다.
93
+ */
94
+ | {
95
+ code: 'embeddable-relation-unsupported';
96
+ opIndex: number;
97
+ handle: AssocHandle;
98
+ }
99
+ /**
100
+ * `entity.update` patch 의 `stereotype` 이 **`JPA_EMBEDDABLE` 경계를 넘음** — 이 전환은 단일 필드 쓰기가
101
+ * 아니라 **동반 명령을 가진 composite** 다(`editor/entity.ts` becomingEmbeddable/leavingEmbeddable):
102
+ * - 들어갈 때: 자기 PK 해제(임베더블은 `@Id` 불가 = `CHK-JPA-2`) + 보유 FK 제거·자식 cascade reconcile +
103
+ * 이 엔티티를 가리키는 일반 관계를 EMBED 로 복원(`enteringEmbeddableCommands`).
104
+ * - 나올 때: PK 자동 부여 + EMBED 관계를 일반 관계로 정규화(`leavingEmbeddableCommands`).
105
+ * 맨 patch 만 흘리면 PK·FK 잔재(`CHK-JPA-2` error)와 「임베더블인데 일반 연관」 형상이 동시에 남는다.
106
+ * ★`navigable-toggle-unsupported` 와 같은 근거의 구조 거부다 — *동반 작업이 GUI 소유*라 op 경로엔 표현이 없다.
107
+ * 경계를 넘지 않는 전환(예: `JPA_ENTITY`↔`JPA_MULTI_LANGUAGE_ENTITY`)은 그대로 통과한다.
108
+ */
109
+ | {
110
+ code: 'stereotype-embeddable-transition-unsupported';
111
+ opIndex: number;
112
+ entity: Handle;
113
+ from: string;
114
+ to: string;
115
+ }
79
116
  /** `type:'GroupCodeEnum'` 인데 `groupCode` 미동반 — groupCode 바인딩 없는 깨진 파생 타입 방지(B3). */
80
117
  | {
81
118
  code: 'groupcode-required';
@@ -1,4 +1,53 @@
1
- import { Attribute, DbColumn, EmbeddableCatalog, Entity, LogicalModel } from './types';
1
+ import { Attribute, DbColumn, EmbeddableCatalog, Entity, LogicalModel, ModelId } from './types';
2
+ /**
3
+ * 임베더블이 **선언하는 슬롯** 하나 — 배열 인덱스가 곧 사용처(owner) 슬롯 위치다.
4
+ *
5
+ * ★이 목록이 「임베더블이 선언한 컬럼」의 **단일 출처**다. 종전엔 같은 개념이 다섯 곳에 흩어져 있었고
6
+ * 그중 코드젠만 다른 정의를 써서(DIVIDER·빈 물리명 제외) 위치 페어링이 밀렸다 — 실측 손채움 157건.
7
+ * 기준을 **전 `dbAttrs`** 로 잡는 이유: 소비처 5 중 4가 이미 그것이고, 특히 **상속 해소가 인덱스
8
+ * 대응**이라 이 축을 바꾸면 표시·DDL·코드젠·검증이 동시에 흔들린다. DIVIDER 는 `dbAttrs` 가 없어
9
+ * 정의상 0 기여이므로 «DIVIDER 를 제외한다» 는 조작이 애초에 불필요하다.
10
+ *
11
+ * 배치: `embedSlots.ts` 가 아니라 여기 두는 이유는 **순환 회피**다(`embedSlots → columnResolve` 의존이
12
+ * 이미 있다) — 그리고 인덱스 대응 시맨틱의 소유자가 이 모듈이다(`inheritedSlotOf`).
13
+ */
14
+ export interface DeclaredEmbedSlot {
15
+ /** 선언 컬럼(임베더블 소유). */
16
+ col: DbColumn;
17
+ /**
18
+ * 임베더블 루트에서 이 슬롯의 **basic 속성까지의 JPA 프로퍼티 경로** — 코드젠 `@AttributeOverride(name=…)` 의 값.
19
+ *
20
+ * 단일 컬럼 속성이면 속성명 하나(`buyerName`)지만, 그 속성이 **자기도 임베더블**이면 JPA 는 basic 까지의
21
+ * 경로를 요구하므로 dotted 가 된다(`totalBaseSellingAmt.amount`). 실물 `Cancellation.before` 는 40/40 이
22
+ * dotted 다. ★종전엔 속성명만 실어서 **Money 를 품은 임베더블의 사용처가 매핑되지 않았다** — 게다가
23
+ * 한 속성의 두 컬럼이 **같은 이름**을 받아 `@AttributeOverrides` 안에 name 중복이 생겼다(실측 5블록·46라인).
24
+ *
25
+ * **해소 못 하면 빈 문자열**이다(모르는 값 규약 — 발명하지 않는다). 소비처는 빈값을 손채움으로 표면화한다.
26
+ */
27
+ propertyPath: string;
28
+ /** 선언 기본 물리명. 공유 투영·미물질화면 빈 문자열. */
29
+ defaultPhysical: string;
30
+ /** 선언이 지정한 공유 타깃 — 사용처 바인딩과 **비교**하는 기준. */
31
+ defaultSharedRef?: ModelId;
32
+ /**
33
+ * 이 자리에 **자체 물리 컬럼**이 생기는가 — 공유 투영(`sharedColumnRef`)과 미물질화(빈 이름)는 false.
34
+ * ⚠️**방출 게이트가 아니다** — 코드젠 방출은 「사용처 실효 바인딩이 선언 기본과 다른가」가 정한다.
35
+ * 이 플래그는 규칙의 규모 분해(`CHK-JPA-21` 부족분 중 실제 컬럼 수)와 형상 판정에 쓴다.
36
+ */
37
+ ownColumn: boolean;
38
+ }
39
+ /**
40
+ * 선언 슬롯의 **프로퍼티 경로 해소 컨텍스트** — 경로 축에만 쓰인다(`col`·`defaultPhysical`·`ownColumn` 은
41
+ * 컨텍스트 없이도 종전과 동일). 미주입이면 중첩 축만 graceful degrade 하고 나머지는 그대로다.
42
+ */
43
+ export interface DeclaredSlotContext {
44
+ /** 중첩 임베더블(`EMBED_OWN`) 해소용 — 모델 엔티티 목록. */
45
+ entities?: readonly Entity[];
46
+ /** `EMBED_PREDEF` 서브필드명 해소용(호스트 주입). */
47
+ embeddableCatalog?: EmbeddableCatalog;
48
+ }
49
+ /** 임베더블 선언 슬롯 목록 — 연결·규칙·보충·상속·코드젠의 단일 출처(`DeclaredEmbedSlot`). */
50
+ export declare function declaredEmbedSlots(embeddable: Entity | undefined, ctx?: DeclaredSlotContext): DeclaredEmbedSlot[];
2
51
  /** 상속 해소 컨텍스트 — `ValidationContext`·`DdlContext`와 동형(카탈로그는 호스트 주입). */
3
52
  export interface ColumnResolveContext {
4
53
  /** EMBED_PREDEF 서브컬럼의 선언값 해석용. 미주입 시 그 축만 graceful degrade(빈 값). */
@@ -57,3 +57,122 @@ export interface EmbedSlotReorder {
57
57
  * @param nextAttributeOrder 재정렬 후 임베더블 속성 순서(전체 — `reorderedSequence` 산출).
58
58
  */
59
59
  export declare function planEmbedSlotReorder(model: LogicalModel, embeddableRef: ModelId, nextAttributeOrder: readonly ModelId[]): EmbedSlotReorder[];
60
+ /** 한 사용처의 **슬롯 제거** 계획 — 임베더블 속성이 삭제되면 그 자리 슬롯을 걷어낸다. */
61
+ export interface EmbedSlotRemoval {
62
+ /** 표시 속성을 보유한 소유자 엔티티. */
63
+ entityId: ModelId;
64
+ /** `EMBED_OWN` 표시 속성. */
65
+ attributeId: ModelId;
66
+ /** 제거 후 전체 슬롯 목록(남는 슬롯의 **순서·정체성 보존** — 신규 슬롯 없음). */
67
+ dbAttrs: DbColumn[];
68
+ /** 걷어낸 슬롯 — **호출자가 무엇이 사라지는지 볼 수 있게** 실어 보낸다(S5 `dropped` 동형). */
69
+ dropped: DbColumn[];
70
+ }
71
+ /**
72
+ * 임베드 슬롯 **삭제 전파** — 임베더블 속성이 삭제되면 사용처(owner)의 그 자리 슬롯을 함께 걷어낸다.
73
+ * `planEmbedSlotFill`(꼬리 보충)의 **역방향 짝**이고, 게이트는 `planEmbedSlotReorder`(순열)와
74
+ * `planEmbedSlotShapeNormalization`(형상)에서 각각 하나씩 물려받는다.
75
+ *
76
+ * ## 왜 필요한가 — 삭제는 «남은 슬롯의 뜻»을 바꾼다
77
+ * 슬롯↔소스 대응은 **위치(인덱스)뿐**이라, 선언 속성 하나가 사라지면 그 뒤 슬롯이 **한 칸씩 밀려**
78
+ * 각자 남의 컬럼을 상속한다. 보충 부재가 *"컬럼이 모자란다"*(양의 문제)라면 이쪽은
79
+ * *"남은 컬럼이 다른 것을 가리킨다"*(뜻의 문제)라 조용하고 더 나쁘다.
80
+ * ★실측(프로덕션 `OrderItemOriginalAmt` · 사용처 62슬롯): 선언 속성 **하나만** 지우면
81
+ * `CHK-NAME-2` 가 **+30**(사용처 2 → 34) 된다.
82
+ *
83
+ * ## 왜 가능한가 — reorder 와 같은 근거
84
+ * *사후 복원은 불가지만 조작 시점엔 근거가 있다.* 삭제를 실행하는 시점에는 어느 선언 컬럼
85
+ * (`DbColumn.modelId`)이 사라지는지 알 수 있으므로, **남는 컬럼이 이전 평탄 목록의 어느 위치였는지**로
86
+ * 남길 슬롯이 유일하게 결정된다. `planEmbedSlotReorder` 는 **재사용할 수 없다** — 그쪽 게이트 1이
87
+ * *"순열일 때만"* 이라 집합이 줄면 즉시 빈 계획이 된다.
88
+ *
89
+ * ## 게이트 셋 — 확실할 때만 걷어낸다
90
+ * 1. **삭제만**: 지울 것이 실제로 있고(집합이 줄고) 그 속성이 컬럼을 가질 때. 추가가 섞였으면 위치
91
+ * 대응이 이번 조작 안에서 흔들려 근거가 없다(호출자가 가른다 — reorder 게이트 1과 같은 규율).
92
+ * 2. **대응이 성립하던 사용처만**: 슬롯 수가 삭제 전 선언 컬럼 수와 같아야 한다. 이미 뒤처진/앞선
93
+ * 사용처는 인덱스 대응 자체가 깨져 있어 걷어내면 **더 나빠진다**(reorder 게이트 2 동형).
94
+ * 3. ★**사라지는 자리가 진짜 override 를 쥐지 않았을 때만**: 슬롯이 **선언과 다른** 물리명·공유참조를
95
+ * 들고 있으면 그건 사람이 넣은 `@AttributeOverride` 라 도구가 버릴 수 없다(S5 게이트 2 동형).
96
+ * ⚠️★**「빈 슬롯인가」로 재면 안 된다 — 「선언과 다른가」로 재야 한다.** 연결 시점
97
+ * `flattenEmbeddableColumns` 가 소스 값을 **verbatim 복사**하므로 방금 연결한 사용처의 슬롯은 전부
98
+ * 물리명을 «명시 보유»한 형태고, 저장·재로드하면 `embedColTo` 가 source diff 로 환원해 빈 순수
99
+ * 참조가 된다 — **같은 논리 상태가 세션에 따라 다른 표현**을 갖는다. «빈 슬롯»으로 재면 그 표현
100
+ * 차이에 걸려 전파가 죽는다(프로덕션 실측: 전파 **75 → 2** · 보류 17 → **90**). 선언과 대조하면
101
+ * **버릴 것이 실제로 있는지**만 본다.
102
+ * ⚠️**「선언과 같으니 버려도 무해」가 아니다** — 삭제 축에서는 선언 자체가 사라지므로 그 값을 다시
103
+ * 구할 데가 없다. 버릴 수 있는 근거는 *복원 가능성*이 아니라 **그 슬롯이 독자적으로 아는 정보가
104
+ * 없다**는 것이다: 컬럼이 사라지는 것은 선언을 지운 **정당한 결과**이고, 남기면 오히려 그 슬롯이
105
+ * 밀려 남의 컬럼을 상속한다. 반대로 선언과 다른 값은 슬롯만 아는 정보라 사라지면 복구 불가다.
106
+ *
107
+ * ## 왜 충돌 게이트가 없는가 (S5 와 다른 점)
108
+ * S5(형상 정규화)는 **빈 슬롯을 삽입**하므로 새 이름이 생겨 충돌을 만들 수 있고, 그래서 정규화 후
109
+ * 이름 다중집합을 실제로 계산하는 게이트를 뒀다. 삭제는 반대로 **이름이 늘 수 없다**:
110
+ * - 남는 슬롯의 **상속원이 바뀌지 않는다** — 새 선언 목록은 옛 목록의 **부분수열**이고 남길 슬롯을
111
+ * 그 부분수열로 골랐으므로, 남는 슬롯 j 의 짝은 삭제 전 그 슬롯의 짝과 **같은 컬럼**이다.
112
+ * - 사라지는 슬롯은 게이트 3에 의해 **빈 슬롯이거나 선언과 같은 값**뿐이라, 그 이름은 사라지는 선언
113
+ * 컬럼의 이름과 같고 함께 사라진다.
114
+ * ⇒ 실효 이름 다중집합은 **엄격히 줄거나 같다**(`CHK-NAME-2` 는 감소 방향) — reorder 가 *"순열은 슬롯
115
+ * 집합을 보존한다"* 로 게이트를 뺀 것과 같은 자리다.
116
+ * ⚠️애초 설계에는 S5 대칭으로 게이트 4(산출물 대조)를 두려 했으나 **구현 중 기각**했다 — 위 논증으로
117
+ * 발동이 불가능한 데다, 판정에 쓸 `duplicateNameCount` 가 **삭제 전 model** 을 보므로 남는 슬롯이
118
+ * *옛* 선언 j 를 상속한 것으로 계산해 **틀린 상태로 판정**한다(정확히 재려면 삭제 반영 future 가 필요한데
119
+ * 그 비용을 발동 0 인 게이트에 치를 이유가 없다). 대신 그 불변식을 테스트가 단언한다.
120
+ *
121
+ * @param model 삭제 **이전** 상태(현재 모델) — 사라지는 슬롯의 정체를 알아야 한다.
122
+ * @param embeddableRef 속성이 삭제된 임베더블(사용처가 `embedded.embeddableRef`로 가리키는 그 엔티티).
123
+ * @param removedAttributeIds 삭제되는 임베더블 속성 id 집합.
124
+ */
125
+ export declare function planEmbedSlotRemoval(model: LogicalModel, embeddableRef: ModelId, removedAttributeIds: readonly ModelId[]): EmbedSlotRemoval[];
126
+ /** 한 사용처의 **형상 정규화** 계획 — legacy(1 속성 = 1 슬롯) → fresh(1 컬럼 = 1 슬롯). */
127
+ export interface EmbedSlotShapeNormalization {
128
+ /** 표시 속성을 보유한 소유자 엔티티. */
129
+ entityId: ModelId;
130
+ /** `EMBED_OWN` 표시 속성. */
131
+ attributeId: ModelId;
132
+ /** 정규화 후 전체 슬롯 목록 — 기존 슬롯이 **제자리로 이동** + 빈 슬롯 삽입 + 잉여 제거. */
133
+ dbAttrs: DbColumn[];
134
+ /** 자리를 옮긴 기존 슬롯 수(로그·테스트 가독용). */
135
+ moved: number;
136
+ /** 새로 삽입한 빈 슬롯 수(선언 다중 컬럼 속성의 2번째 이후 자리). */
137
+ inserted: number;
138
+ /** 대응할 선언 컬럼이 없어 빠진 슬롯 — **호출자가 무엇이 사라지는지 볼 수 있게** 실어 보낸다. */
139
+ dropped: DbColumn[];
140
+ }
141
+ /**
142
+ * 임베드 슬롯 **형상 정규화** — v1 유래 legacy 사용처를 선언 형상(fresh)에 맞춘다.
143
+ * `planEmbedSlotFill`(꼬리 보충)·`planEmbedSlotReorder`(순열)에 이어지는 **세 번째 축**이고,
144
+ * 앞의 둘이 손대지 못하던 «중간이 어긋난» 형상을 다룬다.
145
+ *
146
+ * ## 왜 여기서는 대응을 복원할 수 있는가 — 이 파일 헤더의 한계에 대한 예외
147
+ * 헤더는 *"어느 슬롯이 어느 컬럼이었는지 복원할 근거가 모델에 없다(소스 링크 부재)"* 라고 적었고
148
+ * 그건 **임의 드리프트**에 대해 참이다. legacy 형상은 임의가 아니라 **체계적 산물**이라 다르다:
149
+ * - `migrateLegacyMoney` 가 v1→v2 에서 레거시 1컬럼 `CustomMoneyType` 을 amount(**verbatim 보존**) +
150
+ * currency(빈 슬롯) 2컬럼으로 승격하는데, 그 판정이 `attrType === 'NORMAL'` 이라 **임베더블 선언에만
151
+ * 적용되고 owner 의 `EMBED_OWN` 사용처 슬롯은 따라가지 않는다**.
152
+ * - v1 의 DIVIDER 는 이름이 대시인 가짜 `NORMAL` 속성이었고 사용처 평탄화에 **한 칸**을 차지했는데,
153
+ * 로드 시 `DIVIDER` 로 승격되며 선언 측에서 `dbAttrs: []` 가 됐다(`types.ts`).
154
+ * ⇒ 남는 형상이 정확히 **「1 속성 = 1 슬롯」**이고, 그 슬롯은 선언 속성의 **첫 컬럼**(Money 면 amount)이다.
155
+ * 즉 대응의 근거는 슬롯이 아니라 **형상 규칙**이 들고 있다.
156
+ *
157
+ * ★실측이 이 산식을 확증한다(프로덕션 v2 24문서 · EMBED_OWN 사용처 100):
158
+ * fresh **70** · legacy **27** · other 3 이고, legacy 27 은 **27/27 이 `슬롯 수 == 선언 속성 수`**다
159
+ * (경쟁 가설 «물질화 컬럼 수»는 단독 성립 **0**). legacy 임베더블의 다중 컬럼 선언 속성은
160
+ * **전부 `Money:2`**(175건)이라 «첫 컬럼» 이 모호해질 자리가 없다.
161
+ *
162
+ * ## 왜 삭제가 아니라 정렬인가 (S4 → S5 흡수)
163
+ * DIVIDER 자리 슬롯만 **지우는** 안은 시뮬레이션이 기각했다 — 그 빈 슬롯이 인덱스 상속으로 이름을 얻어
164
+ * 어떤 컬럼의 *유일 방출자*가 되어 있어 실 컬럼 2개를 잃었다. 삭제는 **재인덱싱**이라 현재 상태로
165
+ * 계산한 어떤 기준도 삭제 후를 예측하지 못한다. 정렬은 반대로 **선언 컬럼마다 슬롯이 하나씩** 생기므로
166
+ * 컬럼 집합이 구성상 보존되고, 그 뒤에야 잉여가 «대응 없음»으로 명확히 식별된다.
167
+ *
168
+ * ## 게이트 셋
169
+ * 1. **legacy 형상일 때만** — 슬롯 수가 선언 **속성 수**와 같고 선언 **컬럼 수**와는 다를 때.
170
+ * 둘이 같으면(다중 컬럼·DIVIDER 없는 임베더블) 이미 정렬돼 있어 할 일이 없다.
171
+ * 2. **잉여는 빈 슬롯일 때만 뺀다** — 대응 자리가 없는 슬롯이 물리명·공유참조를 **명시 보유**하면
172
+ * 그건 사람이 넣은 값이라 도구가 버릴 수 없다(실측 22/22 가 빈 슬롯이지만 규칙으로 박아 둔다).
173
+ * 3. **이름 충돌을 만들지 않을 때만** — 삽입 슬롯은 선언 기본명을 상속하므로, 같은 임베더블을 두 번 쓰는
174
+ * 엔티티에서 서로 같은 이름이 될 수 있다. 정규화 **후** 실효 이름 다중집합을 실제로 계산해
175
+ * 중복이 늘면 그 사용처는 보류한다(`CHK-JPA-21` 이 계속 지목하므로 무신호가 아니다).
176
+ * ★기준 계산이 아니라 **산출물 대조**다 — S4 가 기준 계산으로 두 개의 다른 답을 얻은 자리다.
177
+ */
178
+ export declare function planEmbedSlotShapeNormalization(model: LogicalModel, embeddableRef: ModelId, genId?: () => ModelId): EmbedSlotShapeNormalization[];
@@ -78,6 +78,17 @@ export declare function cascadeCommands(futureLogical: LogicalModel, parentId: M
78
78
  * 어긋날 수 없다(여기서 splice를 다시 쓰면 그 지점이 드리프트 후보가 된다).
79
79
  */
80
80
  export declare function embedSlotReorderCommands(logical: LogicalModel, entityId: ModelId, attrIds: ModelId[], toIndex: number): Command[];
81
+ /**
82
+ * 임베더블 속성 **삭제**가 사용처 슬롯으로 번지는 서브명령(`core/embedSlots.planEmbedSlotRemoval`).
83
+ * 위 재정렬 짝과 같은 형태다 — 삭제 명령과 **같은 composite**에 실어 undo 를 원자적으로 만든다.
84
+ *
85
+ * ★`cascadeCommands`(보충 축이 사는 자리)에 얹지 않고 **전용 seam**으로 둔 이유: 그쪽은 조작이 **반영된
86
+ * 미래 모델**을 받아 «무엇이 사라졌는가»를 알 수 없다. 삭제 전파는 사라지는 슬롯의 정체가 입력이라
87
+ * **삭제 전 `logical` + 지울 id 목록**이 필요하고, 그 시그니처가 정확히 재정렬 짝과 같다.
88
+ *
89
+ * `logical`은 삭제 **전** 상태여야 한다(인덱스 정리 `indexCleanupCommands`와 같은 계약).
90
+ */
91
+ export declare function embedSlotRemovalCommands(logical: LogicalModel, entityId: ModelId, removedAttributeIds: readonly ModelId[]): Command[];
81
92
  /**
82
93
  * "자식의 FK 집합이 직접 바뀌는" 트리거(관계 삭제·identifying 토글)용 연쇄 서브명령.
83
94
  * 1) seed 자식을 직접 reconcile(FK 증감/플립), 2) 그 변화를 적용한 상태로 후손 cascade.