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:handleUpdateComponentstale closure 수정 (함수적 setState)
2. 페이지 미리보기 기능
디자이너 캔버스에서 특정 페이지의 실제 데이터를 렌더링하는 미리보기 기능 추가.
PopDashboardConfig.tsx: 각 페이지 옆에 Eye(미리보기) 버튼 추가PopDashboardComponent.tsx:previewPageIndexprop으로 특정 페이지만 단독 렌더링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) | useCallback이 layout 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의 모듈 캐시가 특정 파일의 변경을 인식하지 못하는 경우 발생. 디버그 로그가 콘솔에 안 나오면 캐시 문제를 먼저 의심.
다음에 비슷한 작업할 때 주의할 점
useCallback안에서 state 직접 참조하지 않기 - 항상setState(prev => ...)로 최신 state 접근- Docker 익명 볼륨 인지하기 -
.next캐시 초기화가 필요하면docker-compose down -v사용 - 대시보드 같은 고밀도 정보 컴포넌트에서 절대 크기 지양 -
@container반응형 자동 크기가 더 안정적 - 디버그 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 mergeorigin/ksh-v2-work로 push 완료
커밋 (2차)
| 해시 | 메시지 |
|---|---|
300542d9 |
feat(pop): usePopEvent, useDataSource 공통 훅 구현 |
발견된 문제 및 해결 (2차)
이번 작업에서는 코드 품질 문제가 발견되지 않았습니다.
사전 충돌 검사에서 buildAggregationSQL과 validateDataSourceConfig이 dataFetcher.ts에도 존재하지만, 이는 의도적 복사이며 런타임 충돌은 없음을 확인. 향후 대시보드 교체 시 import 경로만 변경 예정.
이번 작업에서 배운 것 (2차)
새로 알게 된 기술 개념
- 전역 Map 기반 이벤트 버스: React 외부(모듈 스코프)에 전역 Map을 두면 컴포넌트 마운트/언마운트와 무관하게 이벤트 리스너가 유지됨. 단,
cleanupScreen으로 명시적 정리 필요. - SSR 가드 패턴: Next.js에서 전역 변수 초기화 시
typeof window !== "undefined"조건이 필수. 서버 렌더링 시 window 참조 에러 방지.
다음에 비슷한 작업할 때 주의할 점
- subscribe는 반드시 useEffect 안에서 호출 - cleanup에서 unsubscribe 반환값을 호출해야 메모리 누수 방지
- dataFetcher.ts를 복사할 때 import 경로 주의 - types.ts 경로가 상대적으로 달라짐
- useRef로 config 최신값 유지 - useCallback 안에서 config를 직접 참조하면 stale closure 발생, configRef.current 사용
다음 작업
- Phase 2: pop-button 컴포넌트 구현 계획 수립
- Phase 2: pop-icon 컴포넌트 검토/개선
- 브라우저 확인: 대시보드 라벨 정렬, 페이지 미리보기, 차트 디자인