尚未检测到更新。GitHub 发布版本后会显示在这里。
个人 GitHub 账号下公开仓库的所有者可以登录并验证所有权,自行管理介绍,无需管理员批准。
登录并生成验证文件,在 30 分钟内将其提交到此仓库的默认分支。其他分支或分叉仓库中的文件无效。然后在下方检查文件,即可无需管理员批准直接管理项目。请保留文件以维持管理权限。
如需登录,将前往登录页面。登录后请回到此处,再次点击所选的验证按钮。
在项目文件夹中运行 /nomorevibe verify,即可验证所有权并自行管理介绍。
一分钟完成验证正在加载完整 README。 以下为已保存的摘录。
건물 옥상에 태양광 모듈 배치를 설계하고 발전 시뮬레이션 입력을 받는 웹 애플리케이션입니다. Google Maps 위성 이미지에서 대상 영역을 크롭하고, 크롭 팝업의 캔버스 에디터에서 지붕 면과 개구부를 그리면 자동으로 최적 배치를 계산합니다. 일본어/영어 인터페이스를 지원합니다.查看完整 README ↗
건물 옥상에 태양광 모듈 배치를 설계하고 발전 시뮬레이션 입력을 받는 웹 애플리케이션입니다. Google Maps 위성 이미지에서 대상 영역을 크롭하고, 크롭 팝업의 캔버스 에디터에서 지붕 면과 개구부를 그리면 자동으로 최적 배치를 계산합니다. 일본어/영어 인터페이스를 지원합니다.
html2canvas) → 크롭 팝업 진입select 선택/이동, drawRoof 지붕면, drawOpening 개구부(제외 영역), flowSetting 처마(흐름방향), mergeSelected 지붕면 병합, editRoof 꼭짓점 편집, deleteSelected, deleteAll, undoflowSetting으로 지정한 처마 변(eave)에 평행하도록 그리드 정렬<html lang> 속성 자동 동기화| 기술 | 버전 | 용도 |
|---|---|---|
| Next.js | 16.2.0 | App Router, output: "standalone", React Compiler 활성화 |
| React | 19.2.4 | React Compiler |
| TypeScript | ^5 | strict mode |
| Tailwind CSS | v4 | @tailwindcss/postcss (CSS 커스텀 프로퍼티 기반) |
| @vis.gl/react-google-maps | ^1.7.1 | Google Maps 통합 |
| html2canvas | ^1.4.1 | 지도 영역 캡처 |
| lucide-react | ^0.577.0 | 아이콘 |
| polygon-clipping | ^0.15.7 | 지붕면 병합용 폴리곤 boolean 연산 (union / intersection) |
| OpenRouter API | — | 지붕 자동 감지 추론 (Chat Completions). 전용 SDK 없이 fetch 로 직접 호출 |
| zod | ^4.3.6 | AI 응답·요청 스키마 검증 (최종 SSOT) |
| babel-plugin-react-compiler | 1.0.0 | React Compiler |
| ESLint | ^9 | flat config (eslint-config-next) |
pnpm install
.env.local 파일에 Google Maps API 키를 설정합니다:
NEXT_PUBLIC_GOOGLE_MAPS_API_KEY=your_api_key_here
OPENROUTER_API_KEY=your_openrouter_api_key_here
OPENROUTER_MODEL=openai/gpt-5.6-sol
QSP/MUSBI BFF API(모듈 목록 조회 등)와 API 문서(/reference)를 로컬에서 호출하려면 dev 전용 키(QSP_API_HOST, MUSBI_API_HOST, ENABLE_API_DOCS)도 필요합니다. 이 키들은 .env.dev에 있지만 next dev는 .env.dev 파일명을 자동 로드하지 않습니다 — Next.js가 자동 로드하는 파일은 .env · .env.local · .env.development 등으로 한정되며, .env.dev/.env.prod는 배포 시 Jenkins가 .env로 병합하는 용도입니다. 따라서 로컬에서는 .env.dev를 .env.local로 복사해 사용합니다:
cp .env.dev .env.local # 기존 .env.local 이 있으면 append: cat .env.dev >> .env.local
.env.local은.gitignore대상이라 커밋되지 않습니다.- env 파일은 서버 시작 시점에만 로드되므로, 복사 후
pnpm dev를 재시작해야 적용됩니다.
개발 서버 실행:
pnpm dev
http://localhost:3000에서 확인합니다.
pnpm build # next build (standalone output)
pnpm start # 프로덕션 서버
pnpm lint # ESLint
npx tsc --noEmit # 타입 체크
.env 파일에 API 키를 설정한 후:
docker compose up --build # 빌드 및 실행
docker compose up --build -d # 백그라운드
docker compose down # 중지
단독 빌드:
docker build --build-arg NEXT_PUBLIC_GOOGLE_MAPS_API_KEY=your_api_key_here -t solar-pv-system .
docker run -p 3000:3000 \
-e OPENROUTER_API_KEY=your_openrouter_api_key_here \
-e OPENROUTER_MODEL=openai/gpt-5.6-sol \
solar-pv-system
좌측 사이드바(설계/시뮬레이션 탭)와 우측 Google Maps 메인 영역으로 구성된 SPA입니다. 크롭 후에는 메인 영역 위에 CropPopup(폴리곤 편집/패널 배치 캔버스)이 오버레이됩니다.
src/app/
├── components/
│ ├── AiDetectControls.tsx # AI 지붕 분석 트리거 (분석 시작/취소, prod 프로파일에서는 미렌더)
│ ├── CropPopup.tsx # 크롭 이미지 위 캔버스 폴리곤 에디터 + 패널 렌더링
│ ├── MapView.tsx # Google Maps + 크롭 영역 선택 오버레이 (html2canvas 캡처)
│ ├── RoofEditToolbar.tsx # 지붕 편집 툴바 (지도 위 플로팅)
│ └── lnb/ # 좌측 사이드바 (Lnb / LnbDesign / LnbSim / address-input-lnb)
├── utils/
│ ├── i18n.ts # 일/영 번역 사전 + `t(key, lang)` 함수
│ ├── mergePolygons.ts # 지붕면 병합 (인접 판정 + 폴리곤 union)
│ └── panelPlacement.ts # 좌표 변환 및 패널 배치 계산 엔진
├── types/index.ts # 도메인 타입
├── globals.css # CSS 커스텀 프로퍼티 테마
├── layout.tsx # 루트 레이아웃 (Server Component)
└── page.tsx # 메인 페이지 (Client Component, 전역 상태 소유)
page.tsx가 모든 상태(언어, 탭, 크롭 데이터, 폴리곤 영역, 픽셀 영역, 배치된 패널, 시뮬레이션 입력 등)를 소유하고 자식 컴포넌트에 props로 전달하는 Props-Down / Callbacks-Up 패턴을 사용합니다. RoofEditToolbar의 활성 도구에서 CropPopup의 drawingMode(install/exclude/null)를 파생합니다.
utils/panelPlacement.ts)세 가지 진입점을 제공합니다:
| 함수 | 좌표계 | 단위 | 호출 위치 |
|---|---|---|---|
placePanels | lat/lng | mm | 지도 직접 배치 (크롭 미사용 경로) |
placePanelsOnCanvas | 픽셀 | mm | 내부 구현 |
placePanelsOnCanvasCm | 픽셀 | cm | UI 진입점 (크롭 팝업) |
공통 처리 단계:
metersPerPixel 스케일) 변환MARGIN_CM만큼 설치 폴리곤은 내부로 축소, 제외(개구) 폴리곤은 바깥으로 확장(개구 주변 이격)eaveEdgeIndex) 또는 폴리곤의 가장 긴 변을 기준축으로 그리드 생성page.tsx는 패널 긴 변을 처마와 평행하게(landscape 고정) 호출하며, 경사(寸)를 cos 투영 보정해 전달합니다. 배치 엔진은 처마선 기준으로 x·y 시작 위상을 스캔해 가장 많이 채워지는 결과를 채택하고, 오목 폴리곤·장애물은 패널 변 교차 검사로 제외합니다.
page.tsx에 고정값으로 선언되어 있습니다 (UI 입력 아님):
| 상수 | 값 | 의미 |
|---|---|---|
GAP_X_CM | 0.3 | 모듈 좌우 간격 (3 mm, 처마 평행) |
GAP_Y_CM | 3 | 모듈 상하 간격 (30 mm, 처마 수직) |
MARGIN_CM | 30 | 외주 여백 (300 mm) |
globals.css의 CSS 커스텀 프로퍼티(--bg-primary, --accent-blue, --radius-md 등)를 인라인 스타일로 적용합니다. Tailwind 유틸리티 클래스는 사용하지 않습니다.
src/app/types/index.ts)| 타입 | 설명 |
|---|---|
LatLng | 위도/경도 좌표 |
PanelSize | 모듈 크기 (label, width/height mm) |
PanelOrientation | "portrait" | "landscape" |
DrawingMode | "install" | "exclude" | null (RoofEditToolbar에서 파생) |
PolygonArea | lat/lng 폴리곤 (id, type, paths, eaveEdgeIndex?) |
PlacedPanel | lat/lng로 표현된 배치 패널 (polygonId, 4 corners) |
CropBounds | 크롭 영역의 SW/NE lat/lng |
CropData | 크롭 결과 (이미지 dataURL, bounds, address, zoom, sizeMeters) |
PixelPoint | 캔버스 픽셀 좌표 |
PixelPolygon | 픽셀 폴리곤 (id, type, points, eaveEdgeIndex?) |
PixelPanel | 픽셀로 표현된 배치 패널 (polygonId, 4 corners) |
drawRoof로 지붕면 폴리곤을 그립니다 (3점 이상 → 시작점 클릭으로 닫기)drawOpening으로 환기구 등 개구부(제외 영역)를 그립니다flowSetting으로 각 지붕면의 처마(흐름방향) 변을 지정하면 모듈이 그 변과 평행하게 배치됩니다editRoof로 꼭짓점을 드래그하거나 더블클릭으로 삭제undo는 지붕면·개구부를 그리는 중 마지막으로 찍은 점을 되돌립니다 (되돌릴 점이 있을 때만 활성화)| 프리셋 | 폭 (mm) | 높이 (mm) |
|---|---|---|
| Standard 60-Cell | 991 | 1,650 |
| Standard 72-Cell | 991 | 1,960 |
| Large Format | 1,134 | 2,278 |
| Custom | 사용자 지정 | 사용자 지정 |
| 변수 | 필수 | 설명 |
|---|---|---|
NEXT_PUBLIC_GOOGLE_MAPS_API_KEY | ✅ | Google Maps API 키 (Maps JS / Places / Geometry) |
OPENROUTER_API_KEY | ✅ | OpenRouter API 키 (지붕 자동 감지). 서버 라우트에서만 사용하므로 NEXT_PUBLIC_ 접두사 금지. 미설정 시 detect-roof 가 500 |
OPENROUTER_MODEL | ✅ | 추론 모델 슬러그 (현행 openai/gpt-5.6-sol). 기본값이 없어 미설정 시 detect-roof 가 500 |
GEMINI_API_KEY / GEMINI_MODEL | — | OpenRouter 전환 후 미사용. 안정화 관측 기간 롤백 대비로 남겨둔 잔존 키 |