입주 전, 우리 집을 먼저 걸어보는 사이버 모델하우스입니다. 평면 타입을 고르면 가구까지 갖춘 3D 모델하우스로 들어가고, 유상옵션·인테리어 스타일·바닥재를 바꿔 보며 추가금을 바로 확인합니다. 한국 아파트의 전형적인 평면(59㎡·84㎡)과 벽식구조, 발코니 확장 같은 분양 옵션을 반영했고, 직접 벽을 그려 "내 평면"을 만들 수도 있습니다. 한국어가 기본이고 영어로 전환할 수 있습니다.
평면과 가격은 국내 아파트 일반형과 분양 사례를 참고한 예시입니다. 특정 단지의 평면이 아니며, 실제 분양가·옵션가와 다릅니다.
타입 선택 — 첫 화면에서 평면 타입을 고릅니다.
| 타입 | 구조 | 전용 / 공급 | 특징 |
|---|---|---|---|
| 59A | 3Bay 판상형 | 59.85㎡ / 80.9㎡ (24평형) | 맞통풍, 효율 설계 |
| 84A | 4Bay 판상형 | 84.95㎡ / 112.4㎡ (34평형) | 맞통풍, 알파룸, 워크인 드레스룸 |
| 84B | 3Bay 타워형 | 84.80㎡ / 113.2㎡ (34평형) | 남·동 2면 개방 코너 거실, 팬트리 |
| 내 평면 | 직접 그림 | 벽으로 둘러싼 영역에서 자동 계산 | 빈 도면 또는 분양 타입 복사 |
면적은 한국 전용면적 산정 기준처럼 벽 중심선으로 계산합니다(서비스 면적인 발코니·다용도실 제외).
3D 둘러보기
- 2D 평면에서 벽이 솟아오르며 3D로 전환, 조감 보기
- 방 탭(거실·주방·안방…)을 누르면 그 방의 눈높이 시점으로 이동해 제자리에서 둘러보기, 미니맵에 현재 위치 표시
- 모델하우스 투어: 현관부터 방마다 자동으로 이동하며 면적·바닥재·그 방에 적용된 옵션을 설명
- 걸어보기: 데스크톱은 WASD + 마우스, 터치 기기는 가상 조이스틱. 벽·가구·유리문 충돌, 현관 단차, 문 열고 닫기
- 일조 시간 슬라이더, 야경, 단면(벽 자르기), 가구·이름 표시 켜고 끄기
꾸미기 패널
- 유상옵션: 발코니 확장, 시스템에어컨, 빌트인 가전 패키지(인덕션·식기세척기·김치냉장고), 드레스룸 시스템장, 현관 중문, 침실 2·3 통합 — 고르면 2D·3D 평면이 실제로 바뀝니다
- 스타일·마감: 인테리어 스타일 패키지 3종(내추럴 우드 · 모던 화이트 · 호텔 그레이지), 벽지 6색, 방마다 바닥재(강마루·강화마루·원목마루·포세린 타일·타일·논슬립 타일·장판·대리석) — 분양 기본 사양 대비 차액을 원화로 계산
- 가구: 한국 가정 가구·가전 60여 종(4인 소파, 안마의자, 김치냉장고, 워시타워 등)을 끌어 놓거나 눌러 추가, 선택해서 크기·색·회전 수정
2D 평면 — 벽 중심선 치수 체인, 구조(내력벽) 표시, 거리 측정, 가구 배치, 1:50 축척 표시
견적 요약 — 면적(전용·공급·서비스·실사용), 스타일, 선택한 옵션과 바닥재 변경 내역, 예상 추가금 합계. 인쇄·이미지 저장
평면 편집기(내 평면)
- 벽 그리기: 클릭으로 연속 그리기, 끝점·벽선·50mm 격자에 붙기, 숫자 입력 + Enter로 정확한 길이
- 문·창·미닫이·개구부를 벽 위에 놓기, 선택해서 폭·열림 방향·현관문 지정
- 벽 끝점으로 길이 조절, 가운데 손잡이로 평행 이동(모서리로 이어진 벽도 함께 늘어남)
- 벽으로 둘러싸인 영역을 자동으로 방으로 인식, 방 종류·이름 지정
- 분양 타입을 복사해 고치거나 빈 도면에서 시작. 만든 평면도 3D 둘러보기·걸어보기·견적에 그대로 쓰입니다
그 밖에 — 실행 취소/다시 실행, 브라우저에 자동 저장(다시 열면 보던 모델하우스로), 저장 파일 내보내기·불러오기(형식 검증), 이미지 저장, 한국어/영어, 태블릿·휴대폰 화면 대응
Node.js 20 이상이 필요합니다.
npm install
npm run dev # 개발 서버 (http://localhost:5173)빌드:
npm run build # dist/ — 정적 호스팅용. 3D 엔진은 처음 3D로 들어갈 때 별도 파일로 로드
npm run preview # dist/ 미리 보기
npm run build:single # dist-single/index.html 한 파일 — 서버 없이 더블클릭으로 열기용 (시스템 한글 폰트 사용)
npm run typecheck # 타입 검사npm test # 단위 테스트 (Vitest)
npm run test:e2e # 브라우저 테스트 (빌드 → 미리보기 서버 → Playwright)- 단위 테스트 (
tests/*.test.ts): 평면 데이터 검증 — 전용면적 범위, 공간끼리 겹침, 현관에서 모든 공간까지 동선(격자 탐색), 가구·설비가 벽에 박히지 않는지, 방 시점 카메라 여유, 벽 그래프에서 방 찾기 - 브라우저 테스트 (
tests/e2e/*.py): 첫 화면 → 3D 입장 → 방 시점·투어 → 옵션·스타일·추가금 → 타입 전환 → 2D·가구 → 견적 → 걸어보기 → 영어 → 필요할 때만 그리기 확인 → 저장·복원 → 가져오기 검증 → WebGL 불가 환경, 평면 편집기 전 과정- 필요:
python3,pip install playwright,playwright install chromium(또는CHROMIUM_PATH지정). 파이썬 경로는E2E_PYTHON으로 바꿀 수 있습니다
- 필요:
| 키 | 동작 |
|---|---|
T |
2D / 3D 전환 |
V / M |
선택 / 측정 (2D) |
R / Shift+R |
선택한 가구 90° 회전 |
Delete |
선택한 가구 삭제 |
Ctrl/⌘ + D |
선택한 가구 복제 |
Ctrl/⌘ + Z, Ctrl/⌘ + Shift + Z |
실행 취소, 다시 실행 |
방향키 (Shift) |
선택한 가구 10mm (100mm) 이동 |
F, + / - |
화면에 맞춤, 확대 / 축소 (2D) |
Shift + F |
전체 화면 |
Esc |
선택 해제 · 그리기 끝내기 |
걸어보기: WASD / 방향키, Shift, E |
이동, 빠르게, 문 열고 닫기 |
평면 편집: V W D N |
선택 · 벽 · 문 · 창 도구 |
Vite + TypeScript, UI 프레임워크 없이 DOM을 직접 다룹니다. 2D 평면도는 SVG, 3D는 Three.js r160입니다.
src/
main.ts 시작점: 렌더링 연결, 이벤트 바인딩
core/ 상태·실행 취소·저장(state), 현재 평면·견적(plan), 편집 동작(actions), 이름 표시
data/apt/ 아파트 타입 스키마, 평면 빌더(벽 중심선 → 형상), 59A·84A·84B, 벽 그래프 방 찾기, 내 평면
data/ 바닥재 단가(materials), 스타일·벽지(styles), 유상옵션(options), 가구 목록(library)
i18n/ 한국어(ko)·영어(en) 사전 — 키가 두 사전에 모두 있어야 타입 검사 통과
plan2d/ SVG 평면도, 확대·이동, 붙이기, 포인터 조작, 평면 편집기, 작은 평면 그림
ui/ 첫 화면, 상단 바, 꾸미기 패널, 방 탭·미니맵, 추가금·견적, 투어, 보기 모드, 편집 패널
view3d/ 3D 장면(index), 가구·설비 모델(furniture), 기본 도형(prims), 재질(materials)
tests/ 평면·방 찾기 단위 테스트, e2e/ 브라우저 테스트
도면 상태는 브라우저 localStorage(wmh:state-v3)에 저장됩니다.
- 필요할 때만 그리기: 카메라·애니메이션·문·걸어보기가 움직일 때만 렌더링하고, 가만히 있으면(걸어보기에서 서 있을 때 포함) GPU를 쓰지 않습니다
- 화면 갱신 한 번에 최대 한 번: 다음 프레임 예약은 늘 하나뿐이고, 120·144·165Hz 모니터에서는 일정한 간격으로 건너뛰어 초당 60번 안팎만 그립니다
- 카메라 관성은 시간 기준으로 줄여 느린 컴퓨터에서도 1.5초 안팎에 멈추고, 방 시점·전체 보기로 이동할 때 남은 관성을 버립니다
- 캔버스 픽셀 상한(약 420만): 레티나·5K 모니터의 큰 창에서도 칠할 픽셀이 4배로 늘지 않습니다
- 미리 준비: 첫 화면에 머무는 동안 WebGL·환경 반사맵·장면을 만들고 셰이더를 미리 컴파일해, 처음 3D로 들어갈 때 멈추지 않습니다. 이후 2D↔3D 전환에서는 바뀐 것만 다시 만듭니다
- 전환 화면 안정: 카메라가 높을수록 근평면을 멀리 둬 깊이 정밀도를 확보하고(눌린 벽·가구가 바닥과 번갈아 보이는 떨림 방지), 천장은 처음부터 제 높이에 둬 그림자가 전환 도중 한 번에 바뀌지 않습니다
- 그림자는 장면이 바뀔 때만 다시 계산합니다
- 병합: 가구 하나를 재질별 메시 몇 개로, 벽·창틀·유리·천장·바닥은 재질별로 한 번에 합칩니다 (84A 기준 그림 호출 약 1,800 → 약 340)
- 증분 갱신: 가구는 바뀐 것만 다시 만들고, 옮기기만 했으면 위치만 바꿉니다
- 도형 재사용: 같은 치수의 가구 부품(상자·둥근 상자·원기둥·구)은 한 번만 계산해 스타일·옵션을 바꿀 때 다시 쓰고, 조명 원판은 재질 하나를 같이 씁니다
- 점광원은 야경일 때만 두고 개수를 바꾸지 않아 셰이더 재컴파일을 피합니다
src/data/apt/schema.ts의 AptType을 따라 src/data/apt/에 파일을 만들고 index.ts의 TYPES에 등록합니다.
walls: 벽 중심선(수평·수직), 두께, 종류(e외벽 ·b내력벽 ·n경량벽 ·low), 개구부(door·entry·window·slide·gap, 위치는 벽 방향 절대 좌표).when: 'ext'처럼 옵션 조건을 달 수 있습니다rooms: 중심선 다각형, 종류, 기본 바닥재, 이름 위치, 서비스 면적 여부, 옵션에 따른 합치기(join), 방 시점(view)fixtures: 주방 가구·욕실 도기·신발장·옵션 품목 같은 붙박이,furniture: 기본 연출 가구(스타일 색 역할role)options/price/tags/dims/entry
npm test가 전용면적·겹침·동선·충돌·시점을 검사하므로, 새 타입을 넣고 테스트를 통과시키면 됩니다.