qcast-front/CLAUDE.md
sangwook yoo dbaebf9e2b feat(pdf-import): 외벽선 자동 확정 토글 동작 연결 — ON 확정·지붕형상 직행 / OFF 미확정 유지
- usePdfImport.applyOuterline 에 autoFix 옵션 추가 — outerLineFix 확정 여부를 토글에 따라 분기
- PlacementShapeSetting.handleSaveBtn 에서 토글값으로 메뉴 분기(ON→ROOF_SHAPE_SETTINGS / OFF→EXTERIOR_WALL_LINE); 모달은 MenuDepth01 이 currentMenu 변화로 자동 오픈하므로 직접 addPopup 제거
- fix: basicSettingSave 의 setMenuByRoofSize 를 skipSideEffects 가드 안으로 이동 — ON 인데도 async 저장(await post) 직후 메뉴가 EXTERIOR_WALL_LINE 로 덮어써져 외벽선 작성 팝업으로 되돌아가며 확정이 풀리던 문제 수정
- docs(claude): PDF 임포트 절에 자동 확정 분기·MenuDepth01 메뉴 연동·skipSideEffects 주의 반영

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 23:53:25 +09:00

10 KiB
Raw Blame History

CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

루트 AGENTS.md@CLAUDE.md 한 줄짜리 alias. Codex 등 다른 코딩 에이전트도 이 문서를 그대로 따른다.

README.md 는 사실상 create-next-app 보일러플레이트 그대로다 — 신뢰하지 말 것. 스택·디렉토리·env·라우팅·트러블슈팅의 단일 진실 공급원은 이 파일과 docs/ PDF 들이다.

Project at a glance

Q.CAST Front — a Next.js 14 (App Router) frontend for solar-panel layout planning, quoting, and management, targeted at the Japanese (Q.CELLS) market with ja/ko i18n. JavaScript + TypeScript (allowJs, strict: false). Path alias @/* → ./src/*.

Common commands

All scripts use env-cmd to inject the right .env.<mode> file. There is no test runner wired into package.json.

yarn dev                # local dev (.env.localhost)
yarn dev:local-api      # local dev pinned to http://localhost:8080 API
yarn local:dev          # .env.local.dev mode
yarn build              # production build (.env.production)
yarn build:dev          # development build (.env.development)
yarn start:cluster1     # next start -p 5000 (.env.production) — used by PM2
yarn start:dev          # next start -p 5010 (.env.development)
yarn serve              # custom HTTP:3000 + HTTPS:3001 (needs ./certificates/*.pem)
yarn lint               # next lint

PM2 entrypoints (ecosystem.config.js, prd1/prd2/dev/dev.local.ecosystem.config.js) wrap startscript*.js, which child_process.exec the yarn scripts above.

Architecture — the parts that span many files

Mode routing through src/config/

Runtime config is selected at module load by src/config/config.export.js, which switches on process.env.NEXT_PUBLIC_RUN_MODE between config.local.js, config.local.dev.js, config.development.js, config.production.js. Never read env values directly in feature code — import from here so mode switches stay in one place.

Session / auth gate

iron-session cookies are the session source of truth (src/lib/session.js, src/lib/authActions.js). src/app/layout.js checks the session at the root and redirects every non-login route to /login when missing. SESSION_SECRET must be set (≥32 bytes). The session also carries storeLvl (1차/2차 판매점) which drives quote-render branches, builder number, and group ID. Q-Order / Q-Musubi support autoLogin flows via NEXT_PUBLIC_Q_ORDER_AUTO_LOGIN_URL / NEXT_PUBLIC_Q_MUSUBI_AUTO_LOGIN_URL.

src/middleware.js injects x-pathname into request headers so the root layout can branch on the active route.

Floor Plan canvas (the heart of the app)

src/app/floor-plan/** is built on Fabric.js 5.5.2. The model has three layered concerns:

  1. Roof covering (hooks/roofcover, components/floor-plan/modal/roofShape / outerlinesetting / eavesGable …): outer wall lines, auto/manual roof-shape detection, guides, eaves & kerabs, flow up/down, outer-line edit & offset, roof-face assignment.
  2. Placement surface (hooks/surface, components/floor-plan/modal/placementShape …): slope, placement/shape drawing, object placement, bulk delete.
  3. Module / circuit (hooks/module, components/floor-plan/modal/panelBatch …): basic settings, circuit & rack config, orientation.

A modal under components/floor-plan/modal/<feature> pairs with a hook under hooks/<roofcover|surface|module|object|option|...>. Keep that pairing when adding new tools.

Fabric extensions live in src/components/fabric/QLine.js, QPolygon.js and src/util/fabric-extensions.js. Prototype patches must be defined in one place and loaded together — patching elsewhere causes hard-to-debug shape issues.

Context menus are unified through useContextMenu + the contextMenu Recoil atom. The per-target menu inventory is documented in docs/Canvas Status.md — match that list when changing menu wiring.

Plan persistence (serialize/restore) is in src/lib/canvas.js and src/util/canvas-util.js. Recent commits (a5e794b3, 2aef32e0) added a three-layer guard (entry / cache / save) and SizeSetting input guards against 0-area apertures/shadows/dormers — when touching plan load/save or size inputs, do not weaken or remove these guards without understanding the failure mode (InvalidStateError from corrupted plans).

PDF 도면 임포트 — 배치면 초기설정 모달의 PDF 입력 모드 → src/app/api/gemini/floor-plan/route.js(Gemini 분석, 세션 인증 + 사용자별 분당 5회 레이트리밋) → src/hooks/pdf-import/usePdfImport.js. 임포트는 외벽선 자동 확정(autoOuterLineFixState, 기본 ON) 옵션에 따라 분기한다(PlacementShapeSetting.handleSaveBtnapplyOuterline(mmPoints, { autoFix })). ON: 수동 외벽선 확정(useOuterLineWall.handleFix) 직후와 동일한 상태를 만든다 — useLine.addLine 경유 outerLine 생성(attributes/planeSize/fontSize), outerLinePoint 정점 circle, canvas.outerLineFix = true + outerLineFixState. OFF: 외벽선만 그려둔 미확정(outerLineFix=false) 상태로 둔다. 메뉴/모달 전환: handleSaveBtnsetSelectedMenu('outline') + setCurrentMenu(ON→ROOF_SHAPE_SETTINGS / OFF→EXTERIOR_WALL_LINE) 만 하면 된다 — MenuDepth01currentMenu 변화를 감지해 handleMenu 로 해당 모달(RoofShapeSetting / WallLineSetting)을 자동으로 띄운다. 직접 addPopup 하지 말 것(MenuDepth01 의 closeAll 과 충돌). ⚠️ basicSettingSave({ skipSideEffects:true }) 는 메뉴를 건드리지 않아야 한다 — setMenuByRoofSizeif (!skipSideEffects) 밖에 있으면 async 저장(await post) 직후 currentMenuEXTERIOR_WALL_LINE 로 덮어써, ON 인데도 WallLineSetting 으로 되돌아가 useOuterLineWallouterLineFix 를 풀어버린다(2026-06-21 버그). 좌표에 축소 scale 을 곱하지 말 것 — 'QLine 길이×10 = 표시 mm' 불변식이 깨진다. 화면맞춤은 viewportTransform 줌 + canvasZoomState 동기화로만 처리한다.

State — Recoil, split by domain

Atoms live under src/store/ and are split by domain (canvasAtom, roofAtom, estimateAtom, modalAtom, menuAtom, settingAtom, …). When adding state, find the existing domain atom rather than introducing a new global. RecoilWrapper.js mounts the root; QcastProvider.js composes the rest of the global providers (session, global data, global loading) for the App Router root layout.

Build / runtime quirks

  • next.config.mjs sets resolve.fallback = { fs: false } and externalizes canvas, utf-8-validate, bufferutil as commonjs. Adding a node-only dep usually means adding it to that externals list.
  • experimental.staleTimes are all 0 on purpose — App Router cache is disabled so plan/estimate pages always refetch. Don't re-enable without checking the plan-stale failure mode.
  • reactStrictMode: false. Don't assume double-render semantics.
  • HTTPS local dev via yarn serve needs ./certificates/key.pem and cert.pem (gitignored).

Conventions that are easy to violate

  • Prettier: single quote, no semicolons, tabWidth 2, printWidth 150, trailingComma all (.prettierrc). Run yarn lint before declaring done.

  • No raw alert() — always go through useSwal / useMessage + sweetalert2. See docs/메세지 처리 가이드.pdf.

  • Path alias — prefer @/... over relative ../../.. chains.

  • Logging — never console.log directly in shipped code. Use src/util/logger.js, which no-ops unless NEXT_PUBLIC_ENABLE_LOGGING=true. src/util/debugCapture.js POSTs to /api/debug for server-side capture (also gated by the same flag).

  • Commit messages follow .gitmessage.txt:

    [일감번호] : [제목]
    
    [작업내용] :
    

    Recent commits use a slightly tighter [2204] <subject> — <detail> form; mirror neighboring commits on the branch.

  • .env.* can contain real secrets — never paste contents into PR bodies, issues, or chat. .env.*.local is gitignored for machine-local overrides.

Reference docs in docs/

  • Canvas Status.md — canvas feature status and right-click menu inventory per target type.
  • Qcast coding convention.pdf, Qcast development guilde.pdf — full conventions / dev guide.
  • 메세지 처리 가이드.pdf — modal & alert message rules.
  • git commit message Convention.pdf — commit format.
  • Nextjs 14 컴포넌트에 대해서….pdf — App Router server/client component notes.
  • dictionary.txt — domain vocabulary (roof / panel terms in JP/KR).

Always Do

  • All answers and reasoning processes must be written in Korean.
  • Once the task is complete, use a sub-agent to perform lint checks, and build checks.
  • When performing a lint check, ensure that any errors are resolved before proceeding, and make an effort to resolve any warnings that arise.
  • When committing, write the prefix in English, while the rest of the title and content in Korean.
  • Upon task completion, update the CLAUDE.md and README.md documents if necessary.
  • When working, commit in meaningful units that can be explained in a single sentence.

.agents/ skill 문서

.agents/skills/{next-best-practices,vercel-react-best-practices}/** 에 Vercel·Next.js 모범사례 룰북이 들어 있음(총 95개 .md). Next/React 패턴 관련 결정을 내릴 때(렌더링 최적화, RSC 경계, 번들 분할, hydration 이슈, 서버/클라이언트 캐싱 등) 우선 참조.

  • Skill 진입점: 각 디렉토리의 SKILL.md 가 인덱스, 룰은 rules/ 하위 토픽별 파일.
  • 이 문서들은 git 추적 대상이므로 변경하면 PR 에 함께 올릴 것.

graphify (선택)

src/ 지식 그래프를 graphify-out/ 에 캐싱해두면 cross-module 질문(A 가 B 에 어떻게 연결되는가, X 의 의존성 등)을 grep 보다 빠르게 답할 수 있다. 캐시는 로컬 전용이며 git 추적 대상이 아니다.

  • 부재 시(현재 워크트리 포함): /graphify src 로 풀 빌드, 이후엔 graphify update . 로 incremental.
  • src/ 편집 후 architectural 질문에 답하기 전에 graphify update . 1회 실행 (AST-only, 수 초).
  • JS import-기반 calls edge 는 INFERRED 로 마크됨 — 호출 의미가 답의 핵심이면 소스로 한 번 더 확인.