ERP-node/popdocs/CHANGELOG.md

41 KiB

POP 변경 이력

형식: Keep a Changelog


[미출시]

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

[2026-02-09] origin/main 병합 (ksh-v2-work-merge-test)

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

상황:

  • ksh-v2-work 브랜치에서 POP 디자이너 개발이 진행되는 동안, main 브랜치에서 데스크톱 ScreenDesigner 관련 대규모 업데이트(feature/v2-unified-renewal PR #386)가 병합됨
  • 두 브랜치가 ScreenDesigner.tsx를 동시에 수정하여 병합 충돌 발생
  • 안전한 병합을 위해 ksh-v2-work-merge-test 테스트 브랜치에서 작업

병합 통계:

  • 소스: origin/main (86 커밋)
  • 대상: ksh-v2-work-merge-test (ksh-v2-work에서 분기, 14 커밋)
  • 분기점: 3fca677f (feat: V2Media 컴포넌트 추가)
  • 변경 파일: 207개 (43,535줄 추가, 3,547줄 삭제)
  • 충돌 파일: 1개 (ScreenDesigner.tsx)

Merged (origin/main에서 가져온 주요 변경)

  • ScreenDesigner 데스크톱 기능 강화

    • 그룹 정렬/분배/크기 맞춤 (handleGroupAlign, handleGroupDistribute, handleMatchSize)
    • 라벨 토글 (handleToggleAllLabels)
    • 단축키 도움말 모달 (showShortcutsModal)
    • 디버그 console.log 정리
  • 백엔드 신규 기능

    • 스케줄 관리 API (scheduleController, scheduleRoutes, scheduleService)
    • 파일 관리 개선 (fileController, fileRoutes)
    • 테이블 관리 확장 (tableManagementController)
    • 넘버링 규칙 개선 (numberingRuleController)
  • 프론트엔드 신규/수정

    • 레이어 매니저 패널, 레이어 조건 패널
    • 화면 복사 모달, 편집 모달
    • InteractiveDataTable, InteractiveScreenViewer
    • 넘버링 규칙 디자이너
    • 화면 그룹 트리뷰, 화면 관계 플로우

충돌 해결 (ScreenDesigner.tsx)

3건의 충돌을 수동 해결:

충돌 영역 해결 방식
1. 함수 시그니처 isPop, defaultDevicePreview props 추가 ksh-v2-work 유지 (POP 모드 지원), 중복 usePanelState 제거
2. 저장 로직 POP/V2/Legacy 3단계 분기 ksh-v2-work 유지 (3단계 분기), console.log 제거
3. 툴바 props 정렬/분배/크기맞춤/라벨토글/단축키 origin/main 채택 (데스크톱 신규 기능 모두 포함)

검증 결과

항목 결과
충돌 마커 잔존 없음
TypeScript 컴파일 신규 에러 없음 (기존 에러만)
프론트엔드 빌드 성공
백엔드 빌드 신규 에러 없음 (docx/bwip-js 기존 이슈만)
시맨틱 충돌 없음
린트 기능 에러 없음 (들여쓰기 차이만)

주의사항

  • Conflict 3 영역 들여쓰기: origin/main에서 가져온 툴바 JSX(L5745~6631)의 들여쓰기가 ksh-v2-work와 2칸 차이. 기능에는 영향 없으나, 추후 포매팅 정리 권장
  • 기존 타입 에러: GridSettings/GridUtilSettings 불일치, SCREEN_RESOLUTIONS export type 문제 등은 병합 이전부터 존재하던 기술 부채

수정 파일

파일 변경 내용
ScreenDesigner.tsx 3건 충돌 수동 해결 (함수 시그니처, 저장 로직, 툴바 props)
외 207개 파일 origin/main에서 자동 병합

[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)

최신이 위, 시간순 역순