Skip to content

Commit c06a31c

Browse files
committed
fix: 수정시 엣지케이스 수정
1 parent ebec191 commit c06a31c

File tree

2 files changed

+92
-32
lines changed

2 files changed

+92
-32
lines changed

src/pages/ProjectEditorPage.tsx

Lines changed: 86 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import { useKeyboardShortcuts } from "../hooks/useKeyboardShortcuts";
1111
import { ResizablePanel } from "../components/ui/ResizablePanel";
1212
import MainHeader from "../components/ui/MainHeader";
1313
import toast from "react-hot-toast";
14-
import { getStackingHint, canDeleteBlockWithStore, getStackedBlocks } from "../utils/stackingRules";
14+
import { getStackingHint, canDeleteBlockWithStore, getStackedBlocks, requiresParent } from "../utils/stackingRules";
1515
import { providerManager, CloudProviderType } from "../providers";
1616

1717
// Zustand 스토어들
@@ -258,8 +258,9 @@ function ProjectEditorPage() {
258258
const ec2Area = ec2SizeX * ec2SizeZ;
259259
const overlapRatio = ec2Area > 0 ? overlapArea / ec2Area : 0;
260260

261-
// Y축 검증
261+
// Y축 검증 + 스태킹 규칙 검증 (VPC 위에 EC2 올라가는 것 방지)
262262
const yValid = validateStacking(newBlock, subnet);
263+
const stackingRuleValid = canStack(newBlock.type, subnet.type);
263264

264265
console.log(`🎯 [NewStacking] Subnet 겹침 분석: ${subnet.type}`, {
265266
subnetId: subnet.id.substring(0, 8),
@@ -268,15 +269,16 @@ function ProjectEditorPage() {
268269
xOverlap: xOverlap.toFixed(2),
269270
zOverlap: zOverlap.toFixed(2),
270271
overlapRatio: (overlapRatio * 100).toFixed(1) + '%',
271-
yValid
272+
yValid,
273+
stackingRuleValid: stackingRuleValid ? '✅' : '❌ (VPC 등 잘못된 타입)'
272274
});
273275

274-
return { subnet, overlapRatio, yValid };
276+
return { subnet, overlapRatio, yValid, stackingRuleValid };
275277
});
276278

277-
// 겹침 면적이 30% 이상이고 Y축 검증 통과한 Subnet 필터링
279+
// 겹침 면적이 30% 이상이고 Y축 검증 + 스태킹 규칙 모두 통과한 Subnet 필터링
278280
const validSubnets = subnetOverlapData
279-
.filter(data => data.overlapRatio >= 0.3 && data.yValid)
281+
.filter(data => data.overlapRatio >= 0.3 && data.yValid && data.stackingRuleValid)
280282
.sort((a, b) => b.overlapRatio - a.overlapRatio);
281283

282284
console.log("✅ [NewStacking] 최종 스태킹 타겟:", {
@@ -439,11 +441,11 @@ function ProjectEditorPage() {
439441
);
440442
};
441443

442-
// 블록 이동 시 스태킹 업데이트
444+
// 블록 이동 시 스태킹 업데이트 (검증 실패 시 false 반환)
443445
const handleStackingForMovedBlock = (
444446
blockId: string,
445447
allBlocks: DroppedBlock[]
446-
) => {
448+
): boolean => {
447449
console.log(
448450
"🔄🔄🔄 [NewStacking] ===== 이동된 블록 스태킹 업데이트 시작 ====="
449451
);
@@ -458,38 +460,62 @@ function ProjectEditorPage() {
458460
const movedBlock = allBlocks.find((block) => block.id === blockId);
459461
console.log("🔍 [NewStacking] 이동된 블록 찾기:", !!movedBlock);
460462

461-
if (movedBlock) {
462-
console.log("🎯 [NewStacking] 새로운 스태킹 처리 호출");
463-
handleStackingForNewBlock(movedBlock, allBlocks);
463+
if (!movedBlock) {
464+
console.log("❌ [NewStacking] 이동된 블록을 찾을 수 없음");
465+
return false;
466+
}
464467

465-
// 즉시 연결 업데이트
466-
console.log("🔗 [NewStacking] 연결 업데이트 시작");
467-
const derivedConnections = deriveConnectionsFromStacking(allBlocks);
468-
console.log(
469-
"🔗 [NewStacking] 파생된 연결 수:",
470-
derivedConnections.length
471-
);
468+
// 규칙 기반 검증: VPC/Virtual Network 같은 Foundation 블록은 부모가 필요없음
469+
const needsParent = requiresParent(movedBlock.type);
472470

473-
const nonStackingConnections = connections.filter(
474-
(conn) => !conn.properties?.stackConnection
475-
);
476-
console.log(
477-
"🔗 [NewStacking] 비스태킹 연결 수:",
478-
nonStackingConnections.length
479-
);
471+
if (!needsParent) {
472+
console.log("✅ [NewStacking] Foundation 블록 (VPC/Virtual Network) - 스태킹 검증 생략");
473+
return true;
474+
}
480475

481-
const allConnections = [...nonStackingConnections, ...derivedConnections];
482-
console.log("🔗 [NewStacking] 총 연결 수:", allConnections.length);
476+
console.log("🎯 [NewStacking] 새로운 스태킹 처리 호출");
477+
handleStackingForNewBlock(movedBlock, allBlocks);
483478

484-
setConnections(allConnections);
479+
// 즉시 연결 업데이트
480+
console.log("🔗 [NewStacking] 연결 업데이트 시작");
481+
const derivedConnections = deriveConnectionsFromStacking(allBlocks);
482+
console.log(
483+
"🔗 [NewStacking] 파생된 연결 수:",
484+
derivedConnections.length
485+
);
485486

486-
console.log("✅ [NewStacking] 이동 후 연결 업데이트 완료");
487-
} else {
488-
console.log("❌ [NewStacking] 이동된 블록을 찾을 수 없음");
487+
// 규칙 기반 검증: 부모가 필요한 블록은 반드시 연결이 있어야 함
488+
if (needsParent) {
489+
const hasValidConnection = derivedConnections.some(conn =>
490+
conn.fromBlockId === blockId || conn.toBlockId === blockId
491+
);
492+
493+
if (!hasValidConnection) {
494+
console.log(`❌ [NewStacking] ${movedBlock.type} 블록이 필수 부모에 연결되지 않음 - 이동 실패`);
495+
console.log(` 필수: ${getStackingHint(movedBlock.type)}`);
496+
return false;
497+
}
498+
console.log(`✅ [NewStacking] ${movedBlock.type} 블록이 올바른 부모에 연결됨`);
489499
}
500+
501+
const nonStackingConnections = connections.filter(
502+
(conn) => !conn.properties?.stackConnection
503+
);
504+
console.log(
505+
"🔗 [NewStacking] 비스태킹 연결 수:",
506+
nonStackingConnections.length
507+
);
508+
509+
const allConnections = [...nonStackingConnections, ...derivedConnections];
510+
console.log("🔗 [NewStacking] 총 연결 수:", allConnections.length);
511+
512+
setConnections(allConnections);
513+
514+
console.log("✅ [NewStacking] 이동 후 연결 업데이트 완료");
490515
console.log(
491516
"🔄🔄🔄 [NewStacking] ===== 이동된 블록 스태킹 업데이트 종료 ====="
492517
);
518+
return true;
493519
};
494520

495521
// 블록 변경 시 HCL 코드 자동 생성 (연결 정보 포함)
@@ -1169,6 +1195,10 @@ terraform {
11691195
finalPosition
11701196
);
11711197

1198+
// 원래 위치 저장 (복원용)
1199+
const originalPosition = movingBlock.position;
1200+
console.log("💾 [APP_MOVE] 원래 위치 저장:", originalPosition);
1201+
11721202
moveBlock(blockId, finalPosition);
11731203

11741204
// 드래그 종료 시 상태 초기화
@@ -1182,7 +1212,31 @@ terraform {
11821212
);
11831213

11841214
console.log("🔄 [APP_MOVE] 업데이트된 블록 배열로 스태킹 처리");
1185-
handleStackingForMovedBlock(blockId, updatedBlocks);
1215+
const stackingSuccess = handleStackingForMovedBlock(blockId, updatedBlocks);
1216+
1217+
// 스태킹 검증 실패 시 원래 위치로 복원
1218+
if (!stackingSuccess) {
1219+
console.log("❌ [APP_MOVE] 스태킹 검증 실패 - 원래 위치로 복원");
1220+
moveBlock(blockId, originalPosition);
1221+
1222+
const hint = getStackingHint(movingBlock.type);
1223+
toast.error(
1224+
`${movingBlock.type} 블록은 ${hint} 올려야 합니다.\n현재 위치에서는 올바른 연결을 찾을 수 없습니다.`,
1225+
{
1226+
id: `stacking-failed-${blockId}`,
1227+
position: "bottom-center",
1228+
duration: 4000,
1229+
style: {
1230+
whiteSpace: 'pre-line',
1231+
maxWidth: '400px'
1232+
}
1233+
}
1234+
);
1235+
1236+
console.log("🔄 [APP_MOVE] 원래 위치로 복원 완료:", originalPosition);
1237+
console.log("🎯 [APP_MOVE] ========== BLOCK MOVE REVERTED ==========");
1238+
return;
1239+
}
11861240

11871241
console.log("🔄 [APP_MOVE] Block moved:", blockId, finalPosition);
11881242
console.log("🎯 [APP_MOVE] ========== BLOCK MOVE END ==========");

src/utils/stackingRules.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,12 @@ export function getStackingHint(blockType: string): string {
8080
return STACKING_HINTS[blockType] || "적절한 블록 위에만";
8181
}
8282

83+
// 블록이 반드시 부모를 가져야 하는지 확인 (VPC/Virtual Network 제외)
84+
export function requiresParent(blockType: string): boolean {
85+
const allowedParents = STACKING_RULES[blockType] || [];
86+
return allowedParents.length > 0;
87+
}
88+
8389
// 블록 위에 스택된 블록들 찾기
8490
export function getStackedBlocks(targetBlockId: string, allBlocks: any[]): any[] {
8591
const targetBlock = allBlocks.find(b => b.id === targetBlockId);

0 commit comments

Comments
 (0)