ERP-node/popdocs/archive/PHASE3_SUMMARY.md

12 KiB

Phase 3 완료 요약

날짜: 2026-02-04
상태: 완료
버전: v4.0 Phase 3


🎯 달성 목표

Phase 2의 배치 고정 기능 이후, 다음 3가지 핵심 기능 추가:

  1. 모드별 컴포넌트 표시/숨김 (visibility)
  2. 강제 줄바꿈 컴포넌트 (pop-break)
  3. 컴포넌트 오버라이드 병합 (모드별 설정 변경)

📦 구현 내용

1. 타입 정의

파일: frontend/components/pop/designer/types/pop-layout.ts

// pop-break 추가
export type PopComponentType =
  | "pop-field"
  | "pop-button"
  | "pop-list"
  | "pop-indicator"
  | "pop-scanner"
  | "pop-numpad"
  | "pop-spacer"
  | "pop-break";  // 🆕

// visibility 속성 추가
export interface PopComponentDefinitionV4 {
  id: string;
  type: PopComponentType;
  size: PopSizeConstraintV4;
  
  visibility?: {
    tablet_landscape?: boolean;
    tablet_portrait?: boolean;
    mobile_landscape?: boolean;
    mobile_portrait?: boolean;
  };
  
  // ...
}

// 기본 크기
defaultSizes["pop-break"] = {
  width: "fill",   // 100% 너비
  height: "fixed",
  fixedHeight: 0,  // 높이 0
};

2. 렌더러 로직

파일: frontend/components/pop/designer/renderers/PopFlexRenderer.tsx

visibility 체크

const isComponentVisible = (component: PopComponentDefinitionV4): boolean => {
  if (!component.visibility) return true;  // 기본값: 표시
  const modeVisibility = component.visibility[currentMode];
  return modeVisibility !== false;  // undefined도 true로 취급
};

컴포넌트 오버라이드 병합

const getMergedComponent = (baseComponent: PopComponentDefinitionV4) => {
  if (currentMode === "tablet_landscape") return baseComponent;
  
  const override = overrides?.[currentMode]?.components?.[baseComponent.id];
  if (!override) return baseComponent;
  
  // 깊은 병합 (config, size)
  return {
    ...baseComponent,
    ...override,
    size: { ...baseComponent.size, ...override.size },
    config: { ...baseComponent.config, ...override.config },
  };
};

pop-break 렌더링

if (mergedComponent.type === "pop-break") {
  return (
    <div
      style={{ flexBasis: "100%" }}  // 핵심: 100% 너비로 줄바꿈 강제
      className={isDesignMode 
        ? "h-4 border-2 border-dashed border-gray-300" 
        : "h-0"
      }
    >
      {isDesignMode && <span>줄바꿈</span>}
    </div>
  );
}

3. 삭제 함수 개선

파일: frontend/components/pop/designer/types/pop-layout.ts

export const removeComponentFromV4Layout = (
  layout: PopLayoutDataV4,
  componentId: string
): PopLayoutDataV4 => {
  // 1. components에서 삭제
  const { [componentId]: _, ...remainingComponents } = layout.components;
  
  // 2. root.children에서 제거
  const newRoot = removeChildFromContainer(layout.root, componentId);
  
  // 3. 🆕 모든 오버라이드에서 제거
  const newOverrides = cleanupOverridesAfterDelete(layout.overrides, componentId);
  
  return {
    ...layout,
    root: newRoot,
    components: remainingComponents,
    overrides: newOverrides,
  };
};

오버라이드 정리 로직

function cleanupOverridesAfterDelete(
  overrides: PopLayoutDataV4["overrides"],
  componentId: string
) {
  // 각 모드별로:
  // 1. containers.root.children에서 componentId 제거
  // 2. components[componentId] 제거
  // 3. 빈 오버라이드 자동 삭제
}

4. 속성 패널 UI

파일: frontend/components/pop/designer/panels/ComponentEditorPanelV4.tsx

"표시" 탭 추가

<TabsList>
  <TabsTrigger value="size">크기</TabsTrigger>
  <TabsTrigger value="settings">설정</TabsTrigger>
  <TabsTrigger value="visibility">
    <Eye className="h-3 w-3" />
    표시
  </TabsTrigger>
  <TabsTrigger value="data">데이터</TabsTrigger>
</TabsList>

VisibilityForm 컴포넌트

function VisibilityForm({ component, onUpdate }) {
  const modes = [
    { key: "tablet_landscape", label: "태블릿 가로" },
    { key: "tablet_portrait", label: "태블릿 세로" },
    { key: "mobile_landscape", label: "모바일 가로" },
    { key: "mobile_portrait", label: "모바일 세로" },
  ];

  return (
    <div>
      {modes.map(({ key, label }) => (
        <input
          type="checkbox"
          checked={component.visibility?.[key] !== false}
          onChange={(e) => {
            onUpdate?.({
              visibility: {
                ...component.visibility,
                [key]: e.target.checked,
              },
            });
          }}
        />
      ))}
    </div>
  );
}

5. 팔레트 업데이트

파일: frontend/components/pop/designer/panels/ComponentPaletteV4.tsx

const COMPONENT_PALETTE = [
  // ... 기존 컴포넌트들
  {
    type: "pop-break",
    label: "줄바꿈",
    icon: WrapText,
    description: "강제 줄바꿈 (flex-basis: 100%)",
  },
];

🎨 UI 변경사항

컴포넌트 팔레트

컴포넌트
├─ 필드
├─ 버튼
├─ 리스트
├─ 인디케이터
├─ 스캐너
├─ 숫자패드
├─ 스페이서
└─ 줄바꿈 🆕

속성 패널

┌─────────────────────┐
│ 탭: [크기][설정]     │
│     [표시📍][데이터]  │
├─────────────────────┤
│ 모드별 표시 설정     │
│ ☑ 태블릿 가로       │
│ ☑ 태블릿 세로       │
│ ☐ 모바일 가로 (숨김)│
│ ☑ 모바일 세로       │
├─────────────────────┤
│ 반응형 숨김         │
│ [500] px 이하 숨김  │
└─────────────────────┘

📖 사용 예시

예시 1: 모바일 전용 버튼

{
  id: "call-button",
  type: "pop-button",
  label: "전화 걸기",
  visibility: {
    tablet_landscape: false,  // 태블릿: 숨김
    tablet_portrait: false,
    mobile_landscape: true,   // 모바일: 표시
    mobile_portrait: true,
  },
}

결과:

  • 태블릿 화면: "전화 걸기" 버튼 안 보임
  • 모바일 화면: "전화 걸기" 버튼 보임

예시 2: 모드별 줄바꿈

레이아웃: [A] [B] [줄바꿈] [C] [D]

줄바꿈 설정:
{
  id: "break-1",
  type: "pop-break",
  visibility: {
    tablet_landscape: false,  // 태블릿: 줄바꿈 숨김
    mobile_portrait: true,    // 모바일: 줄바꿈 표시
  }
}

결과:

태블릿 가로 (1024px):
┌───────────────────────────┐
│ [A] [B] [C] [D]           │  ← 한 줄
└───────────────────────────┘

모바일 세로 (375px):
┌─────────────────┐
│ [A] [B]         │  ← 첫 줄
│ [C] [D]         │  ← 둘째 줄 (줄바꿈 적용)
└─────────────────┘

예시 3: 리스트 컬럼 수 변경 (확장 가능)

// 기본 (태블릿 가로)
{
  id: "product-list",
  type: "pop-list",
  config: {
    columns: 7,  // 7개 컬럼
  }
}

// 오버라이드 (모바일 세로)
overrides: {
  mobile_portrait: {
    components: {
      "product-list": {
        config: {
          columns: 3,  // 3개 컬럼
        }
      }
    }
  }
}

결과:

  • 태블릿: 7개 컬럼 표시
  • 모바일: 3개 컬럼 표시 (자동 병합)

🧪 테스트 시나리오

테스트 1: 줄바꿈 기본 동작

  1. 팔레트에서 "줄바꿈" 드래그
  2. 컴포넌트 사이에 드롭
  3. 디자인 모드에서 점선 "줄바꿈" 표시 확인
  4. 미리보기에서 줄바꿈이 안 보이는지 확인

테스트 2: 모드별 줄바꿈

  1. 줄바꿈 컴포넌트 추가
  2. "표시" 탭 → 태블릿 모드 체크 해제
  3. 태블릿 가로: 한 줄
  4. 모바일 세로: 두 줄

테스트 3: 삭제 시 오버라이드 정리

  1. 모바일 세로에서 배치 고정
  2. 컴포넌트 삭제
  3. 저장 후 로드
  4. DB 확인: overrides에서도 제거되었는지

테스트 4: 컴포넌트 숨김

  1. 컴포넌트 선택
  2. "표시" 탭 → 태블릿 모드 체크 해제
  3. 태블릿: 컴포넌트 안 보임
  4. 모바일: 컴포넌트 보임

테스트 5: 속성 패널 UI

  1. 컴포넌트 선택
  2. "표시" 탭 클릭
  3. 4개 체크박스 확인
  4. 체크 해제 시 "(숨김)" 표시
  5. 저장 후 로드 → 상태 유지

📝 수정된 파일

코드 파일 (5개)

✅ frontend/components/pop/designer/types/pop-layout.ts
   - PopComponentType 확장 (pop-break)
   - PopComponentDefinitionV4.visibility 추가
   - cleanupOverridesAfterDelete() 추가

✅ frontend/components/pop/designer/renderers/PopFlexRenderer.tsx
   - isComponentVisible() 추가
   - getMergedComponent() 추가
   - pop-break 렌더링 추가
   - ContainerRenderer props 확장

✅ frontend/components/pop/designer/panels/ComponentEditorPanelV4.tsx
   - "표시" 탭 추가
   - VisibilityForm 컴포넌트 추가
   - COMPONENT_TYPE_LABELS 업데이트

✅ frontend/components/pop/designer/panels/ComponentPaletteV4.tsx
   - "줄바꿈" 컴포넌트 추가

✅ frontend/components/pop/designer/PopDesigner.tsx
   - (기존 Phase 2 변경사항 유지)

문서 파일 (6개)

✅ popdocs/CHANGELOG.md
   - Phase 3 완료 기록

✅ popdocs/PLAN.md
   - Phase 3 체크 완료
   - Phase 4 계획 추가

✅ popdocs/V4_UNIFIED_DESIGN_SPEC.md
   - Phase 3 섹션 추가

✅ popdocs/components-spec.md
   - pop-break 상세 스펙 추가
   - Phase 3 업데이트 노트

✅ popdocs/README.md
   - 현재 상태 업데이트
   - Phase 3 요약 추가

✅ popdocs/decisions/002-phase3-visibility-break.md (신규)
   - 상세 설계 문서

✅ popdocs/PHASE3_SUMMARY.md (신규)
   - 이 문서

🎓 핵심 개념

Flexbox 줄바꿈 원리

/* 컨테이너 */
.container {
  display: flex;
  flex-direction: row;
  flex-wrap: wrap;  /* 필수 */
}

/* pop-break */
.pop-break {
  flex-basis: 100%;  /* 전체 너비 차지 → 다음 요소는 새 줄로 */
  height: 0;         /* 실제로는 안 보임 */
}

visibility vs hideBelow

속성 제어 방식 용도
visibility 모드별 명시적 특정 모드에서만 표시 (예: 모바일 전용)
hideBelow 픽셀 기반 자동 화면 너비에 따라 자동 숨김 (예: 500px 이하)

예시:

{
  visibility: {
    tablet_landscape: false,  // 태블릿 가로: 무조건 숨김
  },
  hideBelow: 500,  // 500px 이하: 자동 숨김 (다른 모드에서도)
}

🚀 다음 단계

Phase 4: 실제 컴포넌트 구현

우선순위:
1. pop-field (입력/표시 필드)
2. pop-button (액션 버튼)
3. pop-list (데이터 리스트)
4. pop-indicator (KPI 표시)
5. pop-scanner (바코드/QR)
6. pop-numpad (숫자 입력)

추가 개선 사항

1. 컴포넌트 오버라이드 UI
   - 리스트 컬럼 수 조정 UI
   - 버튼 스타일 변경 UI
   - 필드 표시 형식 변경 UI

2. "모든 모드에 적용" 기능
   - 한 번에 모든 모드 체크/해제

3. 오버라이드 비교 뷰
   - 기본값 vs 오버라이드 차이 시각화

주요 성과

  1. 모드별 컴포넌트 제어: visibility 속성으로 유연한 표시/숨김
  2. Flexbox 줄바꿈 해결: pop-break 컴포넌트로 업계 표준 달성
  3. 확장 가능한 구조: 컴포넌트 오버라이드 병합으로 추후 기능 추가 용이
  4. 데이터 일관성: 삭제 시 오버라이드 자동 정리로 데이터 무결성 유지
  5. 직관적인 UI: 체크박스 기반 visibility 제어

📚 참고 문서


Phase 3 완료 - 2026-02-04 다음: Phase 4 (실제 컴포넌트 구현)