diff --git a/.env.development b/.env.development index 357a2f7f..8f653e38 100644 --- a/.env.development +++ b/.env.development @@ -33,4 +33,7 @@ NEXT_PUBLIC_AWS_S3_BASE_URL="//files.hanasys.jp" S3_PROFILE="dev" #logging -NEXT_PUBLIC_ENABLE_LOGGING=true \ No newline at end of file +NEXT_PUBLIC_ENABLE_LOGGING=true + +#Gemini API Key +GEMINI_API_KEY="AIzaSyDDmMkTkNt12QTj7I3SUygYkOoeG_Sc28Q" \ No newline at end of file diff --git a/.env.production b/.env.production index 6e2981d9..a436dafe 100644 --- a/.env.production +++ b/.env.production @@ -32,4 +32,7 @@ NEXT_PUBLIC_AWS_S3_BASE_URL="//files.hanasys.jp" S3_PROFILE="prd" #logging -NEXT_PUBLIC_ENABLE_LOGGING=false \ No newline at end of file +NEXT_PUBLIC_ENABLE_LOGGING=false + +#Gemini API Key +GEMINI_API_KEY="AIzaSyDDmMkTkNt12QTj7I3SUygYkOoeG_Sc28Q" \ No newline at end of file diff --git a/docs/PDF Gemini 평면도 분석 기능 구현 계획.html b/docs/PDF Gemini 평면도 분석 기능 구현 계획.html new file mode 100644 index 00000000..a401e541 --- /dev/null +++ b/docs/PDF Gemini 평면도 분석 기능 구현 계획.html @@ -0,0 +1,1071 @@ + + + + + +PDF 평면도 → Gemini 분석 → 캔버스 반영 · 구현 계획 + + + + + + + + +
+
+ Q.CAST + docs / floor-plan / pdf-import + + v0.1 · draft + 2026 · 05 · 18 + +
+
+ +
+
Engineering Spec · Floor Plan / PDF Import
+

PDF 평면도 Gemini 분석 캔버스 반영 구현 계획

+

+ 사용자가 업로드한 평면도 PDF 를 Gemini 멀티모달 모델로 분석해 + 건물 외곽선 폴리곤 좌표(mm)를 얻고, Fabric.js 기반 floor-plan + 캔버스에 자동으로 반영한다. 업로드 파일은 분석 종료와 동시에 + Gemini File API 측에서 폐기한다. +

+ +
+
+ 모델 + gemini-3.1-pro-preview +
+
+ 좌표 단위 + mm · clockwise +
+
+ 진입 위치 + floor-plan toolbar +
+
+ 브랜치 + claude/lucid-shaw-91e51f +
+
+
+ +
+ + + +
+ + +
+
+
01
+
+
Requirements
+

기능 요구 사항

+
+
+
+
REQ-01

기본 프로세스 상에서 특정 버튼 클릭으로 사용자가 PDF 문서를 업로드한다.

+
REQ-02

Gemini API 로 해당 문서의 내용 중 평면도를 분석한다.

+
REQ-03

분석 내용을 응답으로 전달받고 현재 프로세스(캔버스)로 전달한다.

+
REQ-04

분석이 성공적으로 마무리되면 업로드한 파일은 폐기 처리한다.

+
+
+ + +
+
+
02
+
+
Decisions
+

결정사항

+
+
+
+
워크트리
현재 워크트리 lucid-shaw-91e51f 에서 진행
+
캔버스 반영 형태
외곽선 폴리곤 좌표(JSON)
+
버튼 위치
floor-plan 상단 툴바
+
API 호출 위치
서버 라우트 /api/gemini/floor-plan
+
Gemini 모델
gemini-3.1-pro-preview
+
좌표 단위
mm
+
툴바 아이콘
신규 PDF 아이콘 추가
+
+
+ + +
+
+
03
+
+
End-to-end Flow
+

전체 흐름

+
+
+ +
+
+
+ Client · UI + 툴바 버튼 클릭 +
+
+
+
+
+ Client · Modal + PdfImportModal — 파일 선택 및 업로드 +
+
+
+
+
+ Server · Route + POST /api/gemini/floor-plan (multipart) +
+
+
+
+
+ Gemini · File API + PDF 업로드 → fileUri 획득 +
+
+
+
+
+ Gemini · gemini-3.1-pro-preview + 외곽선 좌표 JSON 응답 (mm 단위) +
+
+
+
+
+ Server · finally + Gemini 파일 삭제 (성공 / 실패 무관) +
+
+
+
+
+ Client · Canvas + 좌표 검증 → outerLinePointsState + QLine / QPolygon 생성 +
+
+
+

∗ 클라이언트는 서버 라우트만 호출하며 Gemini API 키와 직접 통신하지 않는다.

+
+ + +
+
+
04
+
+
Files
+

핵심 파일 (수정 / 신규)

+
+
+ +
+
+
영역
경로
변경 내용
구분
+
+
+
서버 라우트
+
src/app/api/gemini/floor-plan/route.js
+
multipart 수신 + Gemini 호출 + finally 폐기
+
신규
+
+
+
환경설정
+
src/config/config.*.js
+
GEMINI_API_KEY, GEMINI_MODEL 추가
+
수정
+
+
+
의존성
+
package.json
+
@google/generative-ai 추가
+
수정
+
+
+
메뉴 등록
+
src/store/menuAtom.js (menusState)
+
{ type: 'pdf-import', name, icon } 항목
+
수정
+
+
+
툴바 분기
+
src/components/floor-plan/CanvasMenu.jsx (onClickNav)
+
case 'pdf-import' 추가
+
수정
+
+
+
업로드 모달
+
src/components/floor-plan/modal/pdfImport/PdfImportModal.jsx
+
파일 선택 / 진행 / 에러 상태머신
+
신규
+
+
+
캔버스 반영 hook
+
src/hooks/pdf-import/usePdfImport.js
+
좌표 검증 + 단위 변환 + 캔버스 반영
+
신규
+
+
+
다국어 메시지
+
메시지 키 추가 — pdf.import.*
+
ja / ko 양쪽
+
수정
+
+
+
아이콘
+
public/ 또는 기존 아이콘 디렉토리
+
PDF 임포트용 SVG 추가
+
신규
+
+
+
+ + +
+
+
05
+
+
Server Route · /api/gemini/floor-plan
+

서버 라우트 설계

+
+
+ +
+
+
5 · 1

입력 (Input)

+
    +
  • Content-Type · multipart/form-data
  • +
  • file · PDF (≤ 20MB, MIME application/pdf)
  • +
  • unitHint (옵션) · mm · auto
  • +
+
+ +
+
5 · 2

처리 (Pipeline)

+
    +
  • formData.get('file') → 사이즈 / MIME 검증
  • +
  • GoogleGenerativeAI(GEMINI_API_KEY) 클라이언트 생성
  • +
  • Gemini File API 업로드 → fileUri 획득
  • +
  • gemini-3.1-pro-preview 호출 · 외곽선 추출 프롬프트 + JSON 강제
  • +
  • 응답 JSON 파싱 + 좌표 검증 (vertex ≥ 3, 자기교차 / 0면적 거부)
  • +
  • finally · genai.files.delete(uploadedFile.name)
  • +
+
+ +
+
5 · 3

출력 (Output)

+
    +
  • 성공 · { outerline:[{x,y},…], unit:'mm', confidence }
  • +
  • 실패 · { error:{ code, message } }
  • +
  • 응답 스키마: { outerline, unit:'mm', scale?, confidence:0..1, notes? }
  • +
  • response_mime_type=application/json · response_schema 강제
  • +
+
+ +
+
5 · 4

보안 / 운영 (Ops)

+
    +
  • GEMINI_API_KEY 는 서버 env 만 사용 · 클라이언트 노출 금지
  • +
  • S3 등 영구 저장소에 절대 저장하지 않음 (REQ-04 폐기)
  • +
  • 로깅은 src/util/logger.js 만 사용
  • +
  • raw 응답 출력은 NEXT_PUBLIC_ENABLE_LOGGING=true 일 때만
  • +
+
+
+
+ + +
+
+
06
+
+
Client Flow
+

클라이언트 흐름

+
+
+ +
+
+

6 · 1모달 PdfImportModal

+
    +
  • 상태머신 · idle → uploading → analyzing → success | error
  • +
  • 호출 · fetch('/api/gemini/floor-plan', { method:'POST', body: formData })
  • +
  • 성공 시 usePdfImport.applyOuterline(points, unit) 호출 후 모달 닫기
  • +
  • 실패 시 useSwal 로 에러 메시지 표시
  • +
+
+
+

6 · 2캔버스 반영 hook usePdfImport

+
    +
  • 단위 변환 · unit === 'mm' → canvas px/mm 비율로 변환
  • +
  • 정규화 · 시계방향 정렬 + bounding box 중앙 정렬(translate)
  • +
  • 기존 외곽선 존재 시 useSwal confirm — 덮어쓰기 분기
  • +
  • 점 → name === 'outerLine' QLine 객체 add
  • +
  • setOuterLinePoints(points)addPolygonByLines(lines) 재사용
  • +
  • canvas.renderAll()
  • +
+
+
+
+ + +
+
+
07
+
+
Edge Cases
+

검증 / 엣지 케이스

+
+
+ +
+
PDF 가 아닌 파일
라우트에서 MIME 거부
+
평면도가 아닌 PDF
모델이 빈 outerline 반환 → 친화적 메시지
+
좌표 ≤ 2개 / 자기교차 / 0면적
라우트에서 거부 (SizeSetting 가드와 일관)
+
모델 JSON 파싱 실패
retry 1회 후 실패 메시지
+
Gemini 호출 실패
finally 에서 File API 삭제 보장
+
캔버스 기존 외곽선 존재
confirm 후 덮어쓰기
+
응답이 거대한 폴리곤(> 500 vertex)
Douglas–Peucker 단순화 또는 거부
+
업로드 파일 크기 초과
클라이언트 / 서버 양쪽 가드
+
네트워크 실패
모달에 재시도 버튼 + 파일 폐기 보장
+
+
+ + +
+
+
08
+
+
Conventions · Ops
+

컨벤션 / 운영

+
+
+ +
+
+

Formatting

+
    +
  • Prettier · single quote · no semicolons
  • +
  • tabWidth: 2 · printWidth: 150
  • +
  • 경로 alias @/... 우선 (상대경로 체이닝 지양)
  • +
+
+
+

Messaging / I18n

+
    +
  • 모든 알림 · 다이얼로그는 useSwal + useMessage
  • +
  • raw alert() 금지
  • +
  • 다국어 키 pdf.import.* ja / ko 양쪽 추가
  • +
+
+
+

Logging

+
    +
  • console.log 직접 사용 금지
  • +
  • src/util/logger.js · NEXT_PUBLIC_ENABLE_LOGGING 게이트
  • +
  • Gemini raw 응답은 로깅 플래그 ON 시에만 출력
  • +
+
+
+

Verification

+
    +
  • 마지막에 lint / type / build subagent 점검
  • +
  • dev 에서 실제 평면도 PDF 1~2건 manual test
  • +
+
+
+ +
+
[xxxx] feat(api) — Gemini 평면도 분석 라우트 신설
+
[xxxx] feat(floor-plan) — PDF 임포트 모달 / 툴바 진입
+
[xxxx] feat(floor-plan) — 분석 결과 외곽선 캔버스 반영
+
[xxxx] chore — 다국어 메시지 / lint 정리
+
+
+ + +
+
+
09
+
+
Work Order
+

작업 순서

+
+
+ +
+
+
@google/generative-ai 의존성 추가
+
GEMINI_API_KEY · GEMINI_MODEL env / config 매핑
+
+
+
서버 라우트 src/app/api/gemini/floor-plan/route.js 작성
+
Gemini 호출 + 파일 폐기 (try / finally)
+
+
+
menusStatepdf-import 항목 + PDF 아이콘 SVG 추가
+
기존 menusState 아이콘 컨벤션에 맞춤
+
+
+
CanvasMenu.jsx onClickNav switch 에 케이스 추가
+
modalAtom 으로 PdfImportModal 오픈
+
+
+
PdfImportModal.jsx 작성
+
idle → uploading → analyzing → success / error 상태머신
+
+
+
usePdfImport.js 작성
+
단위 / 방향 정규화 · 외곽선 덮어쓰기 분기 · QLine / QPolygon 반영
+
+
+
다국어 메시지 키 추가
+
pdf.import.* · ja / ko 양쪽
+
+
+
yarn lint + dev 환경 manual test
+
실제 평면도 PDF 1~2건으로 회귀 확인
+
+
+
의미 단위로 커밋
+
§ 8 의 commit 단위 표 참고
+
+
+
+ +
+
+ + + + + + +