ERP-node/popdocs/sessions/2026-02-11.md

9.6 KiB

세션: 2026-02-11

작업 요약

대시보드 스타일 정리 + 페이지 미리보기 + 차트 디자인 개선 + .next 캐시 이슈 해결


수행한 작업

1. 대시보드 스타일 정리 (글자 크기 제거 + 라벨 정렬만 유지)

이전 작업에서 구현했던 글자 크기 3그룹(라벨/메인값/보조) 커스텀 기능이 @container 반응형 자동 크기 조절과 충돌하여 정보가 잘리는 문제 발생. 분석 후 글자 크기 커스텀을 제거하고 라벨 정렬만 유지하기로 결정.

변경 파일:

  • types.ts: ItemStyleConfig 단순화 (labelAlign만 유지)
  • KpiCard.tsx, StatCard.tsx, GaugeItem.tsx, ChartItem.tsx: FONT_SIZE_PX 제거, 반응형 복원, labelAlignClass 적용
  • PopDashboardConfig.tsx: ItemStyleEditor 단순화 (글자 크기 Select 3개 제거, 접기/펼치기 지원)
  • PopDashboardComponent.tsx: setRenderTick/itemStyleKey 불필요 코드 제거
  • PopDesigner.tsx: handleUpdateComponent stale closure 수정 (함수적 setState)

2. 페이지 미리보기 기능

디자이너 캔버스에서 특정 페이지의 실제 데이터를 렌더링하는 미리보기 기능 추가.

  • PopDashboardConfig.tsx: 각 페이지 옆에 Eye(미리보기) 버튼 추가
  • PopDashboardComponent.tsx: previewPageIndex prop으로 특정 페이지만 단독 렌더링
  • PopDesigner.tsx -> PopCanvas.tsx -> PopRenderer.tsx -> ComponentEditorPanel.tsx: previewPageIndex 전달 체인

3. 차트 디자인 개선

  • CartesianGrid 추가 (얇은 격자선으로 수치 읽기 개선)
  • abbreviateNumber 적용 (큰 숫자 K/M 약어 표시)
  • X축 라벨 7자 이상 시 대각선 표시
  • Y축 숫자 자동 약어 처리
  • 긴 라벨 시 하단 여백 자동 확보

4. .next 빌드 캐시 이슈 해결

라벨 정렬 코드가 브라우저에 반영되지 않는 문제 발생. 디버그 로그 추가로 분석한 결과, ChartItem.tsx의 디버그 로그가 콘솔에 전혀 나타나지 않아 Next.js Turbopack 빌드 캐시 문제로 확인.

Docker 설정에서 .next가 익명 볼륨(/app/.next)으로 분리되어 있어, 호스트의 .next 삭제만으로는 해결 불가. docker-compose down -v로 볼륨까지 제거 후 --build로 재시작하여 해결.

5. 디버그 로그 정리

문제 해결 과정에서 추가한 console.log("[DEBUG]...") 8개를 커밋 전 전부 제거.


커밋

해시 메시지
960b1c99 feat(pop-dashboard): 라벨 정렬 + 페이지 미리보기 + 차트 디자인 개선

발견된 문제 및 해결

문제 원인 해결
글자 크기 커스텀이 반응형과 충돌 절대 px(12~64px)가 유동적 그리드 셀 크기와 충돌 글자 크기 커스텀 제거, @container 반응형 자동 적용 유지
Select 기본값 미발동 Shadcn Select의 onValueChange가 같은 값 선택 시 미발동 글자 크기 제거로 해결 (근본 원인 소멸)
stale closure (handleUpdateComponent) useCallbacklayout state를 직접 참조하여 빠른 연속 변경 시 이전 값 유실 setLayout(prev => ...) 함수적 업데이트로 수정
setRenderTick 불필요 이중 렌더링 글자 크기 강제 반영용이었으나 제거 후 불필요 state + useEffect 삭제
ChartItem.tsx 코드가 브라우저에 반영 안됨 Docker 익명 볼륨에 캐시된 .next가 호스트 삭제와 독립적 docker-compose down -v로 볼륨 포함 삭제 후 재빌드

이번 작업에서 배운 것

새로 알게 된 기술 개념

  • Docker 익명 볼륨과 호스트 파일시스템의 독립성: docker-compose.yml에서 /app/.next처럼 익명 볼륨으로 지정된 경로는 호스트의 동일 경로와 완전히 독립적. 호스트에서 rm -rf .next를 해도 컨테이너 내부 캐시에는 영향 없음. docker-compose down -v로 볼륨까지 제거해야 함.

  • Shadcn/Radix Select 동작: onValueChange는 현재 값과 동일한 값을 선택하면 발동하지 않음. 기본값이 있는 Select에서 이를 인지하지 못하면 "설정이 안 됨" 버그로 보임.

발생했던 에러와 원인 패턴

  • stale closure 패턴: React의 useCallback에서 외부 state를 직접 참조하면 의존성 배열이 변경될 때까지 이전 값이 캡처됨. 빠른 연속 호출(정렬 버튼 클릭 등) 시 이전 state로 덮어씌워져 변경이 유실됨. 해결 패턴: setState(prev => ...) 함수적 업데이트.

  • 빌드 캐시 꼬임 패턴: 파일을 여러 번 수정하고 구조 변경이 많으면 Turbopack/Webpack의 모듈 캐시가 특정 파일의 변경을 인식하지 못하는 경우 발생. 디버그 로그가 콘솔에 안 나오면 캐시 문제를 먼저 의심.

다음에 비슷한 작업할 때 주의할 점

  1. useCallback 안에서 state 직접 참조하지 않기 - 항상 setState(prev => ...)로 최신 state 접근
  2. Docker 익명 볼륨 인지하기 - .next 캐시 초기화가 필요하면 docker-compose down -v 사용
  3. 대시보드 같은 고밀도 정보 컴포넌트에서 절대 크기 지양 - @container 반응형 자동 크기가 더 안정적
  4. 디버그 console.log는 커밋 전 반드시 제거 - Grep으로 [DEBUG] 검색하여 확인


작업 요약 (2차 - Phase 0 공통 인프라 구현)

usePopEvent + useDataSource 공통 훅 구현 + 검수 + 커밋/병합/푸시


수행한 작업 (2차)

1. 프로젝트 현황 파악 + 기존 코드 분석

대시보드의 dataFetcher.ts 조회 로직과 dataApi CRUD를 분석하여 공통 훅 설계의 기반을 마련. 기존 백엔드 API(getTableData, createRecord, updateRecord, deleteRecord, executeQuery)가 모두 완성되어 있어 프론트 래핑만 필요함을 확인.

2. usePopEvent 훅 구현 (STEP 1)

파일: frontend/hooks/pop/usePopEvent.ts (신규)

화면(screenId) 단위로 격리된 이벤트 버스. 전역 Map 2개(screenBuses, sharedDataStore)를 모듈 스코프에 두고, SSR 환경 대응(typeof window !== "undefined" 가드).

  • publish(eventName, payload): 같은 화면의 구독자에게 이벤트 전파
  • subscribe(eventName, callback): 이벤트 구독, unsubscribe 함수 반환
  • getSharedData(key) / setSharedData(key, value): screenId별 격리된 key-value 저장소
  • cleanupScreen(screenId): 화면 언마운트 시 전체 정리 (메모리 누수 방지)

3. popSqlBuilder 유틸 구현 (STEP 2)

파일: frontend/hooks/pop/popSqlBuilder.ts (신규)

dataFetcher.ts에서 SQL 빌더 로직 5개 함수를 추출 (로직 변경 없이 복사):

  • escapeSQL, sanitizeIdentifier, validateDataSourceConfig, buildWhereClause, buildAggregationSQL

대시보드 dataFetcher.ts는 미수정 (안정성 우선, 향후 교체 예정).

4. useDataSource 훅 구현 (STEP 3)

파일: frontend/hooks/pop/useDataSource.ts (신규)

DataSourceConfig 기반 DB 테이블 CRUD 통합 훅:

  • 조회 분기: 집계/조인이면 SQL 빌더 + executeQuery, 단순이면 dataApi.getTableData
  • CRUD: save -> dataApi.createRecord, update -> dataApi.updateRecord, remove -> dataApi.deleteRecord
  • 자동 새로고침: refreshInterval 기반 (최소 5초)
  • refetch 필터 병합: overrideFilters가 config.filters에 추가/덮어쓰기

5. 배럴 파일 (STEP 4)

파일: frontend/hooks/pop/index.ts (신규)

public API re-export: usePopEvent, cleanupScreen, useDataSource, MutationResult, DataSourceResult, buildAggregationSQL, validateDataSourceConfig

6. 종합 검수

  • 린트 에러: 0건
  • 중복 정의: 0건 (20개 함수/타입 전수 Grep)
  • 미사용 import: 0건
  • 누락 import: 0건
  • interface props 불일치: 0건
  • 가상 시뮬레이션 8가지 시나리오: 전부 정상

7. Git 작업

  • ksh-button 브랜치에서 커밋
  • ksh-v2-work로 fast-forward merge
  • origin/ksh-v2-work로 push 완료

커밋 (2차)

해시 메시지
300542d9 feat(pop): usePopEvent, useDataSource 공통 훅 구현

발견된 문제 및 해결 (2차)

이번 작업에서는 코드 품질 문제가 발견되지 않았습니다.

사전 충돌 검사에서 buildAggregationSQLvalidateDataSourceConfigdataFetcher.ts에도 존재하지만, 이는 의도적 복사이며 런타임 충돌은 없음을 확인. 향후 대시보드 교체 시 import 경로만 변경 예정.


이번 작업에서 배운 것 (2차)

새로 알게 된 기술 개념

  • 전역 Map 기반 이벤트 버스: React 외부(모듈 스코프)에 전역 Map을 두면 컴포넌트 마운트/언마운트와 무관하게 이벤트 리스너가 유지됨. 단, cleanupScreen으로 명시적 정리 필요.
  • SSR 가드 패턴: Next.js에서 전역 변수 초기화 시 typeof window !== "undefined" 조건이 필수. 서버 렌더링 시 window 참조 에러 방지.

다음에 비슷한 작업할 때 주의할 점

  1. subscribe는 반드시 useEffect 안에서 호출 - cleanup에서 unsubscribe 반환값을 호출해야 메모리 누수 방지
  2. dataFetcher.ts를 복사할 때 import 경로 주의 - types.ts 경로가 상대적으로 달라짐
  3. useRef로 config 최신값 유지 - useCallback 안에서 config를 직접 참조하면 stale closure 발생, configRef.current 사용

다음 작업

  1. Phase 2: pop-button 컴포넌트 구현 계획 수립
  2. Phase 2: pop-icon 컴포넌트 검토/개선
  3. 브라우저 확인: 대시보드 라벨 정렬, 페이지 미리보기, 차트 디자인