ERP-node/popdocs/CHANGELOG.md

38 KiB

POP 변경 이력

형식: Keep a Changelog


[미출시]

  • Phase 4: 실제 컴포넌트 구현 (pop-field, pop-button 등)
  • 데이터 바인딩 구현
  • 워크플로우 연동

[2026-02-06] v5.2.1 그리드 셀 크기 강제 고정

배경 (왜 이 작업이 필요했는가)

문제 상황:

  • 4칸 모드에서 특정 행의 가이드 셀이 다른 행보다 작게 표시됨
  • gridAutoRows는 최소 높이만 보장하여, 컴포넌트 콘텐츠가 행 높이를 밀어내면 인접 빈 셀도 영향받음
  • "셀의 크기 = 컴포넌트의 크기"라는 핵심 설계 원칙이 시각적으로 깨짐

Changed

  • gridAutoRows → gridTemplateRows (PopRenderer.tsx)

    // 변경 전: 최소 높이만 보장 (콘텐츠에 따라 늘어남)
    gridAutoRows: `${breakpoint.rowHeight}px`
    
    // 변경 후: 행 높이 강제 고정
    gridTemplateRows: `repeat(${dynamicRowCount}, ${breakpoint.rowHeight}px)`
    gridAutoRows: `${breakpoint.rowHeight}px`  // 동적 추가행 대비 유지
    
  • dynamicRowCount 분리 (PopRenderer.tsx)

    • gridCells 내부 → 독립 useMemo로 분리
    • gridStyle과 gridCells에서 공유
  • 컴포넌트 overflow 변경 (PopRenderer.tsx)

    • overflow-visibleoverflow-hidden
    • 컴포넌트 콘텐츠가 셀 경계를 벗어나지 않도록 강제

Fixed

  • PopRenderer dynamicRowCount에서 숨김 컴포넌트 포함 문제

    • PopCanvas는 숨김 제외하여 높이 계산, PopRenderer는 포함하여 계산 → 기준 불일치
    • PopRenderer에도 숨김 필터 추가, 여유행 +5 → +3으로 통일
  • 디버깅 console.log 잔존 (PopCanvas.tsx)

    • reviewComponents useMemo 내 console.log 2개 삭제
  • 뷰어 viewportWidth 선언 순서 (page.tsx)

    • currentModeKey보다 뒤에 선언되어 있던 viewportWidth를 앞으로 이동

수정 파일

파일 변경 내용
PopRenderer.tsx gridTemplateRows 강제 고정, dynamicRowCount 분리, overflow-hidden, 숨김 필터 추가
PopCanvas.tsx 디버깅 console.log 삭제
page.tsx (뷰어) viewportWidth 선언 순서 수정

[2026-02-06] v5.2 브레이크포인트 재설계 + 세로 자동 확장

배경 (왜 이 작업이 필요했는가)

문제 상황:

  • 뷰어에서 브라우저 수동 리사이즈 시 768~839px 구간에서 모드 불일치
  • useResponsiveMode 훅과 GRID_BREAKPOINTS 상수 간 기준 불일치
  • 기존 브레이크포인트가 실제 기기 뷰포트와 맞지 않음

사용자 요구사항:

  • "현장 모바일 기기 8~14인치, 핸드폰은 아이폰 미니 ~ 갤럭시 울트라"
  • "세로는 신경쓸 필요 없고 무한 스크롤 가능해야 함"

Changed

  • 브레이크포인트 재설계 (pop-layout.ts)

    모드 변경 전 변경 후 근거
    mobile_portrait ~599px ~479px 스마트폰 세로 최대 440px
    mobile_landscape 600~839px 480~767px 스마트폰 가로
    tablet_portrait 840~1023px 768~1023px iPad Mini 768px 포함
    tablet_landscape 1024px+ 동일 -
  • detectGridMode() 조건 수정 (pop-layout.ts)

    if (viewportWidth < 480) return "mobile_portrait";   // was 600
    if (viewportWidth < 768) return "mobile_landscape";  // was 840
    if (viewportWidth < 1024) return "tablet_portrait";
    
  • BREAKPOINTS.TABLET_MIN 변경 (useDeviceOrientation.ts)

    • 768 (was 840)
  • VIEWPORT_PRESETS에서 height 제거 (PopCanvas.tsx)

    • width만 유지, 세로는 무한 스크롤

Added

  • 세로 자동 확장 (PopCanvas.tsx)

    • MIN_CANVAS_HEIGHT = 600: 최소 캔버스 높이
    • CANVAS_EXTRA_ROWS = 3: 항상 유지되는 여유 행 수
    • dynamicCanvasHeight: 컴포넌트 배치 기반 동적 계산
  • 격자 셀 동적 계산 (PopRenderer.tsx)

    • 고정 20행 → maxRowEnd + 5 동적 계산
  • 뷰어 일관성 확보 (page.tsx)

    • 프리뷰 모드: useResponsiveModeWithOverride 유지
    • 일반 모드: detectGridMode(viewportWidth) 직접 사용

Fixed

  • 뷰어 반응형 모드 불일치

    • 768~839px 구간에서 6칸/8칸 모드 불일치 해결
  • hiddenComponentIds 중복 정의 에러

    • 라인 410-412 중복 useMemo 제거

Technical Details

브레이크포인트 재설계 근거 (실제 기기 CSS 뷰포트):

| 기기 | CSS 뷰포트 너비 |
|------|----------------|
| iPhone SE | 375px |
| iPhone 16 Pro | 402px |
| Galaxy S25 Ultra | 440px |
| iPad Mini 7 | 768px |
| iPad Pro 11 | 834px (세로), 1194px (가로) |
| iPad Pro 13 | 1024px (세로), 1366px (가로) |

→ 768px, 1024px가 업계 표준 (Tailwind, Bootstrap 동일)
세로 자동 확장 로직:

const dynamicCanvasHeight = useMemo(() => {
  const maxRowEnd = visibleComps.reduce((max, comp) => {
    return Math.max(max, comp.row + comp.rowSpan);
  }, 1);
  
  const totalRows = maxRowEnd + CANVAS_EXTRA_ROWS;  // +3행 여유
  const height = totalRows * (rowHeight + gap) + padding * 2;
  
  return Math.max(MIN_CANVAS_HEIGHT, height);  // 최소 600px
}, [...]);

수정 파일

파일 변경 내용
pop-layout.ts GRID_BREAKPOINTS 값 수정, detectGridMode() 조건 수정
useDeviceOrientation.ts BREAKPOINTS.TABLET_MIN = 768
PopCanvas.tsx VIEWPORT_PRESETS height 제거, dynamicCanvasHeight 추가
PopRenderer.tsx gridCells 동적 행 수 계산
page.tsx (뷰어) detectGridMode() 사용

[2026-02-06] v5.1 자동 줄바꿈 + 검토 필요 시스템

배경 (왜 이 작업이 필요했는가)

문제 상황:

  • 12칸에서 배치한 컴포넌트가 4칸 모드로 전환하면 "화면 밖" 패널로 이동하여 뷰어에서 안 보임
  • 사용자가 모든 모드를 수동으로 편집해야 하는 부담
  • "화면 밖" 개념이 실제로는 "검토 필요" 알림 역할이었음

해결 방향:

  • 자동 줄바꿈: col > maxCol인 컴포넌트를 자동으로 맨 아래에 배치
  • 정보 손실 방지: 모든 컴포넌트가 항상 그리드 안에 표시됨
  • 검토 필요 알림: 오버라이드 없으면 "검토 필요" 표시 (자동 배치 상태)

Added

  • 자동 줄바꿈 로직 (gridUtils.ts)

    • convertAndResolvePositions() 수정
    • 원본 col 보존 로직 추가
    • 정상 컴포넌트 vs 초과 컴포넌트 분리
    • 초과 컴포넌트를 맨 아래에 순차 배치 (col=1, row=맨아래+1)
    • colSpan 자동 축소 (targetColumns 초과 방지)
  • 검토 필요 판별 함수 (gridUtils.ts)

    • needsReview() 신규 함수
    • 기준: 12칸 아니고 + 오버라이드 없으면 → 검토 필요
    • 간단한 로직: "이 모드에서 편집했냐 안 했냐"
  • 검토 필요 패널 (PopCanvas.tsx)

    • ReviewPanel: "화면 밖" → "검토 필요"로 이름 변경
    • ReviewItem: 클릭 시 해당 컴포넌트 선택 (드래그 없음)
    • 자동 배치 뱃지 표시
    • 파란색 테마 (경고 아닌 안내 느낌)

Changed

  • isOutOfBounds() Deprecated (gridUtils.ts)

    • @deprecated 주석 추가
    • needsReview()로 대체 권장
    • 하위 호환을 위해 함수는 유지
  • "화면 밖" 패널 역할 변경 (PopCanvas.tsx)

    • 기존: col > maxCol → 화면 밖 (드래그로 복원)
    • 변경: 오버라이드 없음 → 검토 필요 (클릭으로 선택)
    • 숨김 기능과 완전히 분리 (별도 유지)

Fixed

  • 정보 손실 문제 해결
    • 모든 컴포넌트가 항상 그리드 안에 배치됨
    • 뷰어에서도 자동 배치가 적용되어 모두 표시됨

Technical Details

자동 줄바꿈 로직:

1. convertAndResolvePositions() 호출
   components: [ {id: "A", position: {col:1, ...}}, {id: "B", position: {col:5, ...}} ]
   targetMode: "mobile_portrait" (4칸)

2. 비율 변환 + 원본 col 보존
   converted: [
     {id: "A", position: {col:1, ...}, originalCol: 1},
     {id: "B", position: {col:2, ...}, originalCol: 5}  // col은 변환됨, 원본은 5
   ]

3. 정상 vs 초과 분리
   normalComponents: [A]  // originalCol ≤ 4
   overflowComponents: [B]  // originalCol > 4

4. 맨 아래 배치
   maxRow = A의 (row + rowSpan - 1) = 1
   B: col=1, row=2 (맨 아래에 자동 배치)

5. 겹침 해결
   resolveOverlaps([A, B], 4)  // 최종 위치 확정

6. 검토 필요 판별
   needsReview("mobile_portrait", false)  // 오버라이드 없음 → true
   → ReviewPanel에 B 표시
검토 필요 vs 숨김:

구분          | 검토 필요              | 숨김
------------- | ---------------------- | -------------------
역할          | 자동 배치 알림         | 의도적 숨김
뷰어에서      | 보임 (자동 배치)       | 안 보임
디자이너에서  | ReviewPanel 표시       | HiddenPanel 표시
판단 기준     | 오버라이드 없음        | hidden 배열에 ID
색상 테마     | 파란색 (안내)          | 회색 (제외)

수정 파일

파일 변경 내용
gridUtils.ts convertAndResolvePositions 자동 줄바꿈, needsReview 추가, isOutOfBounds deprecated
PopCanvas.tsx OutOfBoundsPanel → ReviewPanel 변경, needsReview 필터링
PopRenderer.tsx isOutOfBounds import 제거 (사용 안 함)
README.md v5.1 버전 표시, 최신 기능 요약
CHANGELOG.md v5.1 항목 추가

[2026-02-05 심야] 반응형 레이아웃 + 숨김 기능 완성

배경 (왜 이 작업이 필요했는가)

문제 상황:

  • 12칸 모드에서 배치한 컴포넌트가 4칸 모드에서 초과됨
  • 모드별로 컴포넌트 위치/크기를 다르게 설정할 방법 없음
  • 특정 모드에서만 컴포넌트를 숨길 방법 없음

해결 방향:

  • 모드별 오버라이드 시스템으로 위치/크기 개별 저장
  • 화면 밖 컴포넌트를 별도 패널에 표시하고 드래그로 재배치
  • 숨김 기능으로 특정 모드에서 컴포넌트 제외

Added

  • 모드별 오버라이드 시스템 (PopDesigner.tsx, pop-layout.ts)

    • PopModeOverrideV5.positions: 모드별 컴포넌트 위치 저장
    • PopModeOverrideV5.hidden: 모드별 숨김 컴포넌트 ID 배열
    • getEffectiveComponentPosition(): 오버라이드된 위치 반환
    • 드래그/리사이즈 시 자동으로 오버라이드 저장
  • 화면 밖 컴포넌트 패널 (PopCanvas.tsx)

    • OutOfBoundsPanel: 현재 모드에서 초과하는 컴포넌트 표시
    • OutOfBoundsItem: 드래그 가능한 회색 컴포넌트 카드
    • isOutOfBounds(): 컴포넌트가 현재 모드 칸 수 초과 여부 판단
    • 클릭하면 숨김 패널로 이동
  • 숨김 기능 (PopDesigner.tsx, PopCanvas.tsx)

    • HiddenPanel: 숨김 처리된 컴포넌트 표시
    • HiddenItem: 드래그로 숨김 해제 가능
    • handleHideComponent(): 컴포넌트 숨김 처리
    • handleUnhideComponent(): 숨김 해제 (handleMoveComponent에 통합)
    • 숨김 방법 3가지:
      1. 그리드 → 숨김패널 드래그
      2. H키 단축키
      3. 화면밖 컴포넌트 클릭
  • 리사이즈 겹침 검사 (PopRenderer.tsx)

    • checkResizeOverlap(): 리사이즈 시 다른 컴포넌트와 겹침 검사
    • 겹치면 리사이즈 취소 및 toast 알림
  • 원본으로 되돌리기 (PopDesigner.tsx)

    • handleResetToDefault(): 현재 모드 오버라이드 삭제
    • 자동 위치 계산으로 복원

Fixed

  • 숨김 컴포넌트 드래그 안됨 버그

    • 원인: onUnhideComponentonMoveComponent가 별도로 호출되어 상태 충돌
    • 해결: handleMoveComponent에서 숨김 해제 로직 통합 (단일 상태 업데이트)
  • 그리드 범위 초과 에러

    • 원인: 드롭 위치 + colSpan이 칸 수 초과
    • 해결: 드롭 시 adjustedCol 계산하여 자동으로 왼쪽으로 밀어서 배치
  • getAllEffectivePositions에 숨김 컴포넌트 포함

    • 해결: 숨김 및 화면밖 컴포넌트를 결과에서 제외
  • Expected drag drop context 에러 (뷰어 페이지)

    • 원인: DraggableComponent에서 useDrag 훅이 DndProvider 없이 호출됨
    • 해결: isDesignMode=false일 때 DraggableComponent 대신 일반 div로 렌더링

Changed

  • PopModeOverrideV5 타입 확장

    interface PopModeOverrideV5 {
      positions?: Record<string, Partial<PopGridPosition>>;  // 위치 오버라이드
      hidden?: string[];  // 숨김 컴포넌트 ID 배열
    }
    
  • 12칸 모드(tablet_landscape) 제한

    • 기본 모드이므로 숨김 기능 비활성화
    • 화면밖 패널 표시 안함
    • 위치 변경은 기본 position에 직접 저장
  • 패널 레이아웃 재구성 (PopCanvas.tsx)

    • 오른쪽에 화면밖 패널 + 숨김 패널 세로 배치
    • 12칸 모드에서는 패널 숨김

Technical Details

오버라이드 데이터 흐름:

1. 컴포넌트 드래그/리사이즈
   ↓
2. currentMode 확인
   ↓
3-a. tablet_landscape → layout.components[id].position 직접 수정
3-b. 다른 모드 → layout.overrides[mode].positions[id]에 저장
   ↓
4. getEffectiveComponentPosition()이 우선순위대로 반환
   우선순위: overrides > autoResolved > 기본 position

숨김 기능 흐름:

1. 숨김 요청 (드래그/H키/클릭)
   ↓
2. layout.overrides[mode].hidden 배열에 ID 추가
   ↓
3. PopRenderer에서 hidden 체크 → 렌더링 제외
   ↓
4. HiddenPanel에서 표시
   ↓
5. 드래그로 그리드에 복원 → hidden 배열에서 제거 + 위치 업데이트 (단일 상태 업데이트)

수정 파일

파일 변경 내용
pop-layout.ts PopModeOverrideV5.hidden 추가
PopDesigner.tsx handleHideComponent, handleUnhideComponent 통합, 오버라이드 저장
PopCanvas.tsx OutOfBoundsPanel, HiddenPanel 추가, 드롭 위치 자동 조정
PopRenderer.tsx 숨김 필터링, 리사이즈 겹침 검사
gridUtils.ts getAllEffectivePositions에서 숨김/화면밖 제외, isOutOfBounds 함수

[2026-02-05 저녁] 드래그앤드롭 완전 수정

배경 (왜 좌표 계산이 틀렸는가)

문제 상황:

  • 컴포넌트를 아래로 드래그해도 위로 올라감
  • Row 92 같은 비정상적인 좌표로 배치됨
  • 드래그 이동/리사이즈가 전혀 작동하지 않음

핵심 원인: 캔버스에 transform: scale(0.8) 적용 시 좌표 계산 불일치

문제:
- getBoundingClientRect() → 스케일 적용된 크기 반환 (예: 1024px → 819px)
- getClientOffset() → 뷰포트 기준 실제 마우스 좌표
- 이 둘을 그대로 계산하면 좌표가 완전히 틀림

해결: 단순한 상대 좌표 + 스케일 보정

// 캔버스 내 상대 좌표 (스케일 보정)
const relX = (마우스X - 캔버스left) / canvasScale;
const relY = (마우스Y - 캔버스top) / canvasScale;
calcGridPosition(relX, relY, customWidth, ...);  // 실제 캔버스 크기 사용

Added

  • calcGridPosition() 함수 (PopCanvas.tsx)

    • 캔버스 내 상대 좌표를 그리드 좌표로 변환
    • 패딩, gap, 셀 너비를 고려한 정확한 계산
  • 공통 DND 상수 (constants/dnd.ts)

    • DND_ITEM_TYPES.COMPONENT: 팔레트에서 새 컴포넌트
    • DND_ITEM_TYPES.MOVE_COMPONENT: 기존 컴포넌트 이동
    • 3개 파일에서 중복 정의되던 것을 통합

Fixed

  • 스케일 보정 누락

    • 캔버스 줌(scale)이 적용된 상태에서 좌표 계산 오류
    • (offset - rect.left) / scale로 보정
  • DND 타입 상수 불일치

    • PopCanvas: "component", "MOVE_COMPONENT"
    • PopRenderer: "MOVE_COMPONENT" (하드코딩)
    • ComponentPalette: "component" (로컬 정의)
    • 모두 공통 상수로 통합
  • 컴포넌트 중첩(겹침) 문제

    • 원인: toast import 누락으로 겹침 감지 로직이 실행 안됨
    • 해결: sonner에서 toast import 추가
    • 겹침 시 findNextEmptyPosition()으로 자동 재배치
  • 리사이즈 핸들 작동 안됨

    • 원인: useDrop 훅 2개가 같은 canvasRef에 중복 적용
    • 해결: 단일 useDrop으로 통합 (COMPONENT + MOVE_COMPONENT 모두 처리)
  • 불필요한 toast 메시지 제거

    • "컴포넌트가 이동되었습니다" 알림 삭제

Changed

  • mouseToGridPosition 단순화
    • 복잡한 DOMRect 전달 대신 필요한 값만 직접 전달
    • gridUtils.ts의 함수는 유지 (다른 곳에서 사용)

Technical Details

좌표 변환 흐름 (수정 후):

1. 마우스 드롭
   offset = monitor.getClientOffset()  // 뷰포트 기준 {x: 500, y: 300}

2. 캔버스 위치
   canvasRect = canvasRef.getBoundingClientRect()  // {left: 250, top: 100}

3. 스케일 보정된 상대 좌표
   relX = (500 - 250) / 0.8 = 312.5  // 캔버스 내 실제 X
   relY = (300 - 100) / 0.8 = 250    // 캔버스 내 실제 Y

4. 그리드 좌표 계산
   calcGridPosition(312.5, 250, 1024, 12, 48, 16, 24)
   → { col: 5, row: 4 }

수정 파일

파일 변경 내용
PopCanvas.tsx calcGridPosition 추가, 스케일 보정 적용
PopDesigner.tsx toast 메시지 제거
PopRenderer.tsx DND 상수 import
ComponentPalette.tsx DND 상수 import
constants/dnd.ts 새 파일 (DND 타입 상수)
constants/index.ts 새 파일 (export)

[2026-02-05 오후] 그리드 가이드 CSS Grid 통합

배경 (왜 재설계했는가)

문제 상황:

  • GridGuide.tsx(SVG 기반)와 PopRenderer.tsx(CSS Grid)가 좌표계 불일치
  • 격자선과 컴포넌트가 정렬되지 않음 ("무늬가 따로 논다")
  • 행/열 라벨이 4부터 시작하는 등 오류

핵심 원칙:

"격자선은 컴포넌트와 같은 좌표계에서 태어나야 한다"

결정: SVG 격자 삭제, CSS Grid 기반 통합 → 상세: decisions/004-grid-guide-integration.md

Breaking Changes

  • GridGuide.tsx 삭제 (SVG 기반 격자)

Added

  • CSS Grid 기반 격자 셀 (PopRenderer.tsx)

    • gridCells: 12x20 = 240개 실제 DOM 셀
    • border-dashed border-blue-300/40 스타일
    • 컴포넌트는 z-index:10으로 위에 표시
    • showGridGuide prop으로 ON/OFF
  • 행/열 라벨 (PopCanvas.tsx)

    • 열 라벨: 1~12 (캔버스 상단)
    • 행 라벨: 1~20 (캔버스 좌측)
    • absolute positioning으로 정확한 정렬
    • 줌/패닝에 연동
  • 그리드 토글 버튼 (PopCanvas.tsx)

    • "그리드 ON/OFF" 버튼 추가
    • 격자 표시 상태 관리

Changed

  • 컴포넌트 타입 단순화
    • PopComponentType: pop-sample 1개로 단순화
    • DEFAULT_COMPONENT_GRID_SIZE: pop-sample 전용
    • ComponentPalette.tsx: 샘플 박스 1개만 표시
    • PopRenderer.tsx: 샘플 박스 렌더링으로 단순화

Technical Details

역할 분담:
- PopRenderer: 격자 셀(div) + 컴포넌트 (같은 CSS Grid 좌표계)
- PopCanvas: 라벨 + 줌/패닝 + 토글
- GridGuide: 삭제

격자 셀 구조:
┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐
│1,1│2,1│3,1│4,1│5,1│6,1│7,1│8,1│9,1│10│11│12 │ ← col
├───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┤
│1,2│...                                     │
└───┴───────────────────────────────────────────┘
  ↑ row

[2026-02-05] v5 그리드 시스템 완전 통합

배경 (왜 v5로 전환했는가)

문제 상황:

  • v4 Flexbox로 반응형 구현 시도 → 배치가 예측 불가능
  • 캔버스에 "그리듯이" 배치하면 화면 크기별로 깨짐

상급자 피드백:

"이런 식이면 나중에 문제가 생긴다." "스크린의 픽셀 규격과 마진 간격 규칙을 설정해라. 큰 화면 디자인의 전체 프레임 규격과 사이즈 간격 규칙을 정한 다음에 거기에 컴포넌트를 끼워 맞추듯 우리의 규칙 내로 움직이게 바탕을 잡아라."

연구 내용:

  • Softr: 블록 기반, 제약 기반 레이아웃
  • Ant Design: 24열 그리드, 8px 간격
  • Material Design: 4/8/12열, 반응형 브레이크포인트

결정: CSS Grid 기반 그리드 시스템 (v5) 채택 → 상세: decisions/003-v5-grid-system.md

popdocs 문서 구조 재정비

배경: 문서가 AI 에이전트 진입점 역할을 못함, 컨텍스트 효율화 필요

적용 기법: Progressive Disclosure (점진적 공개), Token as Currency

추가된 파일:

  • SAVE_RULES.md: AI 저장/조회 규칙, 템플릿
  • STATUS.md: 현재 진행 상태, 중단점
  • PROBLEMS.md: 문제-해결 색인
  • INDEX.md: 기능별 색인
  • sessions/: 날짜별 작업 기록

문서 계층:

  • Layer 1 (진입점): README, STATUS, SAVE_RULES
  • Layer 2 (상세): CHANGELOG, PROBLEMS, INDEX, FILES, ARCHITECTURE
  • Layer 3 (심화): decisions/, sessions/, archive/

Breaking Changes

  • v1, v2, v3, v4 레이아웃 완전 삭제
  • 기존 POP 화면 데이터 전체 초기화 필요
  • 레거시 컴포넌트 및 타입 삭제

Added

  • CSS Grid 기반 그리드 시스템 (v5)

    • 4개 모드별 칸 수: 4/6/8/12칸
    • 명시적 위치 지정 (col, row, colSpan, rowSpan)
    • 모드별 오버라이드 지원
    • 자동 위치 변환 (12칸 기준 → 다른 모드)
  • 통합된 파일 구조

    • PopCanvas.tsx: 그리드 캔버스 (DnD + 줌 + 모드 전환)
    • PopRenderer.tsx: 그리드 렌더링
    • ComponentEditorPanel.tsx: 속성 편집
    • pop-layout.ts: v5 전용 타입 정의
    • gridUtils.ts: 그리드 유틸리티 함수

Removed

  • PopCanvasV4.tsx, PopCanvas.tsx (v3)
  • PopFlexRenderer.tsx, PopLayoutRenderer.tsx
  • ComponentEditorPanelV4.tsx, PopPanel.tsx
  • v1, v2, v3, v4 타입 정의 및 유틸리티 함수
  • test-v4 테스트 페이지

Changed

  • screenManagementService.ts: v5 전용으로 단순화
  • screen_layouts_pop 테이블: 기존 데이터 삭제, v5 전용
  • PopDesigner.tsx: v5 전용으로 리팩토링
  • 뷰어 페이지: v5 렌더러 전용

Technical Details

// v5 그리드 모드
type GridMode = "mobile_portrait" | "mobile_landscape" | "tablet_portrait" | "tablet_landscape";

// 그리드 설정
const GRID_BREAKPOINTS = {
  mobile_portrait: { columns: 4, rowHeight: 48, gap: 8, padding: 12 },
  mobile_landscape: { columns: 6, rowHeight: 44, gap: 8, padding: 16 },
  tablet_portrait: { columns: 8, rowHeight: 52, gap: 12, padding: 20 },
  tablet_landscape: { columns: 12, rowHeight: 56, gap: 12, padding: 24 },
};

// 컴포넌트 위치
interface PopGridPosition {
  col: number;      // 시작 열 (1부터)
  row: number;      // 시작 행 (1부터)
  colSpan: number;  // 열 크기
  rowSpan: number;  // 행 크기
}

[2026-02-04] Phase 2.1 완료 - 배치 고정 기능

Added

  • 현재 모드 추적 (PopDesigner.tsx)

    • currentViewportMode 상태 추가
    • PopCanvasV4와 양방향 동기화
    • 모드 변경 시 자동 업데이트
  • 배치 고정 기능

    • "고정" 버튼 추가 (기본 모드 제외)
    • handleLockLayoutV4() - 현재 배치를 오버라이드에 저장
    • 배치 정보: direction, wrap, gap, alignItems, justifyContent, children 순서
  • 오버라이드 초기화 기능

    • handleResetOverrideV4() - 오버라이드 삭제
    • "자동으로 되돌리기" 버튼 (편집된 모드만 표시)
    • 자동 계산으로 되돌림

Changed

  • PopCanvasV4 Props 구조 변경

    • currentMode prop 추가 (외부에서 제어)
    • onModeChange 콜백 추가
    • onLockLayout 콜백 추가
    • 내부 activeViewport 상태 제거 (부모가 관리)
  • 프리셋 버튼 동작

    • 클릭 시 부모 상태 업데이트 (onModeChange)
    • currentMode prop 기반으로 활성 상태 표시

Technical Details

// 고정 로직
const handleLockLayoutV4 = () => {
  const newLayout = {
    ...layoutV4,
    overrides: {
      ...layoutV4.overrides,
      [currentViewportMode]: {
        containers: {
          root: {
            direction: layoutV4.root.direction,
            wrap: layoutV4.root.wrap,
            gap: layoutV4.root.gap,
            children: layoutV4.root.children, // 순서 고정
            // ... 기타 배치 속성
          }
        }
      }
    }
  };
};

// 초기화 로직
const handleResetOverrideV4 = (mode) => {
  const newOverrides = { ...layoutV4.overrides };
  delete newOverrides[mode];
  // overrides가 비면 undefined로 설정
};

UI 변경

툴바:
[모바일↕] [모바일↔] [태블릿↕] [태블릿↔(기본)] [고정] [자동으로 되돌리기]

조건부 표시:
- "고정" 버튼: 기본 모드가 아닐 때
- "자동으로 되돌리기": 오버라이드가 있을 때

주의사항

  • 크기는 고정하지 않음 (여전히 자동 스케일링)
  • 배치만 오버라이드 (순서, 방향, 정렬)
  • 최소/최대값 기능은 별도 구현 필요

[2026-02-04] Phase 2 시작 - 오버라이드 UI 표시

Added

  • 오버라이드 데이터 구조 (pop-layout.ts)

    • PopModeOverride 인터페이스 추가
    • PopLayoutDataV4.overrides 필드 추가
    • 3개 모드 오버라이드 지원 (mobile_portrait, mobile_landscape, tablet_portrait)
  • 프리셋 버튼 상태 표시 (PopCanvasV4.tsx)

    • 기본 모드: "(기본)" 텍스트 표시
    • 편집된 모드: "(편집)" 텍스트 + 노란색 강조
    • 자동 모드: 기본 스타일

Changed

  • hasOverride 함수 구현
    • layout.overrides 필드 체크
    • 컴포넌트/컨테이너 오버라이드 존재 여부 확인

[2026-02-04] 비율 스케일링 시스템 구현

Added

  • 비율 스케일링 시스템 (업계 표준 Scale with Fixed Aspect Ratio)

    • 기준 너비: 1024px (10인치 태블릿 가로)
    • 최대 너비: 1366px (12인치 태블릿)
    • 8~12인치 화면에서 배치 유지, 크기만 비례 조정
  • 뷰포트 감지 (page.tsx)

    • viewportWidth state 추가
    • resize 이벤트 리스너로 실시간 감지
    • Math.min(window.innerWidth, 1366) 최대값 제한

Changed

  • PopFlexRenderer.tsx

    • BASE_VIEWPORT_WIDTH = 1024 상수 추가
    • scale = viewportWidth / BASE_VIEWPORT_WIDTH 계산
    • calculateSizeStyle() 함수에 scale 파라미터 추가
    • 컴포넌트 크기 (fixedWidth, fixedHeight) 스케일 적용
    • 컨테이너 gap, padding 스케일 적용
    • 디자인 모드: scale = 1 (원본), 뷰어 모드: 실제 scale 적용
  • ComponentRendererV4

    • viewportWidth prop 추가
    • 내부에서 scale 계산하여 sizeStyle에 적용
  • ContainerRenderer

    • scaledGap, scaledPadding 계산하여 containerStyle에 적용

Technical Details

비율 스케일링 계산:
scale = 실제 화면 너비 / 기준 너비 (1024px)

예시:
- 800px (8인치): scale = 0.78 → 200px 컴포넌트 → 156px
- 1024px (10인치): scale = 1.00 → 200px 컴포넌트 → 200px (기준)
- 1366px (12인치): scale = 1.33 → 200px 컴포넌트 → 266px
- 1920px (데스크톱): max-width 1366px 적용 → 12인치와 동일 + 여백

Fixed

  • DndProvider 에러 (뷰어 페이지)
    • 원인: isDesignMode=false일 때 useDrag/useDrop 훅 호출
    • 해결: DraggableComponentWrapper에서 isDesignMode 체크 후 early return

[2026-02-04] Flexbox 가로 배치 + Spacer + Undo/Redo 개선

Added

  • Spacer 컴포넌트 (pop-spacer)

    • 빈 공간을 차지하여 레이아웃 정렬에 사용
    • 기본 크기: width: fill, height: 48px
    • 디자인 모드에서 점선 배경으로 표시
    • 실제 모드에서는 투명 (공간만 차지)
  • 컴포넌트 순서 변경 (드래그 앤 드롭)

    • 같은 컨테이너 내에서 컴포넌트 순서 변경 가능
    • 드래그 중인 컴포넌트는 반투명하게 표시
    • 드롭 위치는 파란색 테두리로 표시
    • handleReorderComponentV4 핸들러 추가

Changed

  • 기본 레이아웃 방향 변경 (Flexbox 가로 배치)

    • direction: "vertical"direction: "horizontal"
    • wrap: falsewrap: true (자동 줄바꿈)
    • alignItems: "stretch"alignItems: "start"
    • 컴포넌트가 가로로 나열되고, 공간 부족 시 다음 줄로 이동
  • 컴포넌트 기본 크기 타입별 설정

    • 필드: 200x48px (fixed)
    • 버튼: 120x48px (fixed)
    • 리스트: fill x 200px
    • 인디케이터: 120x80px (fixed)
    • 스캐너: 200x48px (fixed)
    • 숫자패드: 200x280px (fixed)
    • Spacer: fill x 48px
  • Undo/Redo 방식 개선 (데스크탑 모드와 동일)

    • useLayoutHistory 훅 제거
    • 별도 history[], historyIndex 상태로 관리
    • saveToHistoryV4() 함수로 명시적 히스토리 저장
    • 컴포넌트 추가/삭제/수정/순서변경 시 히스토리 저장
  • 디바이스 스크린 스크롤

    • overflow: auto 추가 (컴포넌트가 넘치면 스크롤)
    • heightminHeight 변경 (컨텐츠에 따라 높이 증가)

Technical Details

업계 표준 레이아웃 방식 (Figma, Webflow, FlutterFlow):
1. Flexbox 기반 Row/Column 배치
2. 크기 제어: Fill / Fixed / Hug
3. Spacer 컴포넌트로 정렬 조정
4. 화면 크기별 조건 분기 (반응형)

사용 예시:
[버튼A] [Spacer(fill)] [버튼B]  → 버튼B가 오른쪽 끝으로
[Spacer] [컴포넌트] [Spacer]   → 컴포넌트가 가운데로

[2026-02-04] 드래그 리사이즈 + Undo/Redo 기능

Added

  • useLayoutHistory.ts - Undo/Redo 히스토리 훅

    • 최대 50개 히스토리 저장
    • undo(), redo(), canUndo, canRedo
    • reset() - 새 레이아웃 로드 시 히스토리 초기화
  • 드래그 리사이즈 핸들 (PopFlexRenderer)

    • 오른쪽 핸들: 너비 조정 (cursor: ew-resize)
    • 아래쪽 핸들: 높이 조정 (cursor: ns-resize)
    • 오른쪽 아래 핸들: 너비+높이 동시 조정 (cursor: nwse-resize)
    • 선택된 컴포넌트에만 표시
    • 최소 크기 보장 (너비 48px, 높이 touchTargetMin)

Changed

  • PopDesigner.tsx

    • useLayoutHistory 훅 통합 (v3, v4 각각 독립적)
    • Undo/Redo 버튼 추가 (툴바 오른쪽)
    • 단축키 등록:
      • Ctrl+Z / Cmd+Z: 실행 취소
      • Ctrl+Shift+Z / Cmd+Shift+Z / Ctrl+Y: 다시 실행
    • handleResizeComponentV4 핸들러 추가
  • PopCanvasV4.tsx

    • onResizeComponent prop 추가
    • PopFlexRenderer에 전달
  • PopFlexRenderer.tsx

    • onComponentResize prop 추가
    • ComponentRendererV4에 리사이즈 핸들 추가
    • 드래그 이벤트 처리 (mousemove, mouseup)

단축키 목록

단축키 기능
Delete / Backspace 선택된 컴포넌트 삭제
Ctrl+Z / Cmd+Z 실행 취소 (Undo)
Ctrl+Shift+Z / Ctrl+Y 다시 실행 (Redo)
Space + 드래그 캔버스 패닝
Ctrl + 휠 줌 인/아웃

[2026-02-04] v4 통합 설계 모드 Phase 1 완료

목표

v4를 기본 레이아웃 모드로 통합하고, 새 화면은 자동으로 v4로 시작

Added

  • ComponentPaletteV4.tsx - v4 전용 컴포넌트 팔레트
    • 6개 컴포넌트 (필드, 버튼, 리스트, 인디케이터, 스캐너, 숫자패드)
    • 드래그 앤 드롭 지원

Changed

  • PopDesigner.tsx - v3/v4 통합 디자이너로 리팩토링

    • v3/v4 탭 제거 (자동 판별)
    • 새 화면 → v4로 시작
    • 기존 v3 화면 → v3로 로드 (하위 호환)
    • 빈 레이아웃 → v4로 시작 (컴포넌트 유무로 판별)
    • 레이아웃 버전 텍스트 표시 ("자동 레이아웃 (v4)" / "4모드 레이아웃 (v3)")
  • PopCanvasV4.tsx - 4개 프리셋으로 변경

    • 기존: [모바일] [태블릿] [데스크톱]
    • 변경: [모바일↕] [모바일↔] [태블릿↕] [태블릿↔]
    • 기본 프리셋: 태블릿 가로 (1024x768)
    • 슬라이더 범위: 320~1200px
    • 비율 유지: 슬라이더 조절 시 높이도 비율에 맞게 자동 조정

Fixed

  • 새 화면이 v3로 열리는 문제
    • 원인: 백엔드가 빈 v2 레이아웃 반환 (version 필드 있음)
    • 해결: 컴포넌트 유무로 빈 레이아웃 판별 → v4로 시작

Technical Details

레이아웃 로드 로직:
1. version 필드 확인
2. components 존재 여부 확인
3. version 있고 components 있음 → 해당 버전으로 로드
4. version 없거나 components 없음 → v4로 새로 시작

[2026-02-04] Phase 2.1 완료 - 배치 고정 기능 (버그 수정)

🔥 주요 버그 수정

  • layoutV4.root 오염 문제 해결: 다른 모드에서 편집 시 기본 레이아웃이 변경되던 버그 수정
  • tempLayout 도입: 고정 전 임시 배치를 별도 상태로 관리하여 root를 보호
  • 렌더러 병합 로직: PopFlexRenderer에 오버라이드 자동 병합 기능 추가

데이터 흐름 개선

  1. 기본 모드 (태블릿 가로)

    • 드래그/속성 변경 → layoutV4.root 직접 수정
    • 모든 다른 모드의 기본값으로 사용
  2. 다른 모드 (모바일 세로 등)

    • 드래그 → tempLayout 임시 저장 (화면에만 표시)
    • "고정" 버튼 → layoutV4.overrides[mode]에 저장
    • 속성 패널 → 비활성화 + 안내 메시지
  3. 렌더링

    • tempLayout 있으면 최우선 표시 (고정 전 미리보기)
    • 오버라이드 있으면 root와 병합
    • 없으면 root 그대로 표시

수정 파일

  • PopDesigner.tsx: tempLayout 상태 추가, 핸들러 수정
  • PopFlexRenderer.tsx: 병합 로직 추가 (getMergedRoot)
  • PopCanvasV4.tsx: tempLayout props 전달
  • ComponentEditorPanelV4.tsx: 속성 패널 비활성화 로직

[2026-02-04] Phase 3 완료 - visibility + 줄바꿈 컴포넌트

추가 기능

  • visibility 속성: 모드별 컴포넌트 표시/숨김 제어
  • pop-break 컴포넌트: 강제 줄바꿈 (flex-basis: 100%)
  • 컴포넌트 오버라이드 병합: 모드별 컴포넌트 설정 변경 가능

타입 정의

interface PopComponentDefinitionV4 {
  // 기존 속성...
  
  // 🆕 모드별 표시/숨김
  visibility?: {
    tablet_landscape?: boolean;
    tablet_portrait?: boolean;
    mobile_landscape?: boolean;
    mobile_portrait?: boolean;
  };
}

// 🆕 줄바꿈 컴포넌트
type PopComponentType = 
  | "pop-field"
  | "pop-button"
  | "pop-list"
  | "pop-indicator"
  | "pop-scanner"
  | "pop-numpad"
  | "pop-spacer"
  | "pop-break";  // 새로 추가

렌더러 개선

  • isComponentVisible(): visibility 체크 로직
  • getMergedComponent(): 컴포넌트 오버라이드 병합
  • pop-break 전용 렌더링 (디자인 모드: 점선, 실제: 높이 0)

삭제 함수 개선

  • cleanupOverridesAfterDelete(): 컴포넌트 삭제 시 모든 오버라이드 정리
  • containers.root.children 정리
  • components 오버라이드 정리
  • 빈 오버라이드 자동 제거

UI 개선

  • 속성 패널에 "표시" 탭 추가 (Eye 아이콘)
  • 모드별 체크박스 UI
  • 반응형 숨김 (hideBelow) 유지
  • 팔레트에 "줄바꿈" 컴포넌트 추가

사용 예시

태블릿 가로:
[A] [B] [C] [D] [E]  ← 한 줄

모바일 세로:
[A] [B]
───────  ← 줄바꿈 (visibility: mobile만 true)
[C] [D] [E]

수정 파일

  • pop-layout.ts: 타입 추가, 삭제 함수 수정
  • PopFlexRenderer.tsx: visibility, 병합, pop-break 렌더링
  • ComponentEditorPanelV4.tsx: 표시 탭 추가
  • ComponentPaletteV4.tsx: 줄바꿈 추가

[2026-02-04] v4 타입 및 렌더러

Added

  • v4 타입 정의 (간결 버전)

    • PopLayoutDataV4 - 단일 소스 레이아웃
    • PopContainerV4 - 스택 컨테이너 (direction, wrap, gap, alignItems)
    • PopComponentDefinitionV4 - 크기 제약 기반 (size: fixed/fill/hug)
    • PopSizeConstraintV4 - 크기 규칙
    • PopResponsiveRuleV4 - 반응형 규칙 (breakpoint별 변경)
    • PopGlobalSettingsV4 - 전역 설정
    • createEmptyPopLayoutV4() - 생성 함수
    • isV4Layout() - 타입 가드
    • CRUD 함수들 (add, remove, update, find)
  • PopFlexRenderer.tsx - v4 Flexbox 렌더러

    • 컨테이너 재귀 렌더링
    • 반응형 규칙 적용
    • 크기 제약 → CSS 변환
  • ComponentEditorPanelV4.tsx - v4 속성 편집 패널

    • 크기 제약 편집 UI
    • 컨테이너 설정 UI
  • PopCanvasV4.tsx - v4 전용 캔버스

    • 뷰포트 프리셋
    • 너비 슬라이더
    • 줌/패닝

[2026-02-04] (earlier)

Added

  • 저장/조회 시스템 구축
    • rangraph: AI 장기 기억 (시맨틱 검색, 요약)
    • popdocs: 상세 기록 (파일 기반, 히스토리)
    • 이중 저장 체계로 검색 + 기록 분리

Changed

  • popdocs 문서 구조 정리
    • README.md: 저장/조회 규칙 추가
    • 기존 문서 archive/로 이동
  • 문서 관리 전략 확정
    • 저장 시: 파일 형식 자동 파악 → 형식 맞춰 추가 → rangraph 요약
    • 조회 시: rangraph 시맨틱 검색

Removed

  • .cursorrules 변경 계획 철회 (Git 커밋 영향)

[2026-02-03]

Added

  • v4 제약조건 기반 레이아웃 계획
    • 단일 소스 + 자동 적응
    • 3가지 규칙 (크기, 배치, 반응형)
    • ADR: decisions/001-v4-constraint-based.md

[2026-02-02]

Fixed

  • 캔버스 rowSpan 문제
    • 원인: gridTemplateRows 고정 px
    • 해결: 1fr 사용

[2026-02-01]

Fixed

  • 4모드 자동 전환 문제
    • 해결: useResponsiveMode 훅 추가

[2026-01-31]

Added

  • v3 섹션 제거, 순수 그리드 구조
  • 4개 모드 독립 그리드

[2026-01-30]

Added

  • POP 디자이너 기본 구조
  • PopDesigner, PopCanvas 컴포넌트

[2026-01-29]

Added

  • screen_layouts_pop 테이블
  • POP 레이아웃 API (CRUD)

최신이 위, 시간순 역순