· case-study · 2 min read
웹 브라우저에서 3D 블록을 조립한다 — Geometree 서비스 개발기
종이 설명서로 따라하던 블록 조립을 브라우저 안으로 옮긴 풀스택 프로젝트. 가장 어려웠던 건 그래픽이 아니라 “단계”라는 데이터를 어떻게 잡느냐였습니다.
무엇을 만들었나
종이 설명서로 따라가던 블록 조립을 브라우저 탭 안에서 그대로 할 수 있게 만든 서비스입니다. 다운로드 없이, 회원가입 한 번이면 바로 시작합니다.
기능은 단순합니다.
- 마우스/터치로 회전·확대하면서 단계별로 따라가기
- 다음 조립할 부품은 강조, 이미 조립한 부품은 고정, 미래 부품은 흐릿하게
- 단일 부품 조립 모드 / 큐브(전체) 조립 모드
- 진행 상태 자동 저장 → “여기서부터 다시” 가능
- 완성된 결과물 PNG 스크린샷 저장
가구 조립 가이드, 산업 부품 트레이닝, 교육용 키트, DIY — “설명서가 너무 두꺼운 모든 것”의 후속자가 될 수 있는 도구입니다.
왜 굳이 웹3D였나
이 카테고리의 보통 경쟁자는 유니티 빌드나 모바일 앱입니다. 둘 다 “먼저 받으세요”가 첫 단계예요. 웹3D는 링크 한 줄이면 끝납니다. 학습 곡선의 첫 30초가 사라집니다.
대신 웹3D에는 두 개의 함정이 있어요.
- 모델 로딩이 길면 “즉시성”이 무너진다
- 폴리곤 수가 많으면 모바일이 죽는다
이 두 함정을 피하는 게 프로젝트의 절반이었습니다.
어려웠던 한 가지 — “단계”라는 데이터
처음에는 만만해 보였습니다. “순서에 따라 부품 상태가 바뀐다.” 한 줄짜리 요구사항이었거든요.
부품이 100개라면 0~99까지 인덱스를 매겨 step별로 보여주면 끝일 것 같았는데 — 실제로는 그렇지 않았습니다.
“단계”의 정의가 비대칭이다
같은 모델 안에서도 어떤 단계는 부품 한 개를 다루고, 어떤 단계는 부품 여러 개를 동시에 다룹니다. 예를 들면 “좌·우 다리를 동시에 끼운다” 같은. 처음에 1 step = 1 part로 짰다가, 며칠 만에 데이터 모델을 다시 잡았습니다.
type Step = {
id: number;
parts: PartId[]; // 이 단계에서 조립되는 부품들
preview: PartId[]; // 다음 단계에서 들어올 미리보기 부품
pose: 'open' | 'closed' | 'exploded';
};
이걸 잘못 잡으면 코드가 if 문으로 도배되기 시작합니다. “이 부품은 step 5에 들어가지만 step 4부터 미리 보여야 하고, step 7에서는 색이 바뀌고…” — 이런 예외가 데이터 안에 흡수되지 않으면 코드가 못 버팁니다.
카메라가 부품을 “안 보이게” 만든다
부품 시각화의 절반은 카메라가 어디에 있느냐입니다. 어떤 단계는 위에서 봐야 하고, 어떤 단계는 옆에서 봐야 합니다. 단계 데이터에 권장 카메라 포즈를 함께 저장해 두고, 단계 변경 시 부드럽게 보간했습니다.
사용자 자유 vs 가이드의 균형
자동으로 보여주기만 하면 영상이랑 다를 게 없습니다. 그래서 이런 규칙을 지켰어요.
- 사용자가 직접 회전·확대할 수 있다
- 단계 변경 시 카메라가 “이쪽이 좋아요” 라고 부드럽게 추천 한다
- 사용자가 무시해도 된다
가르치되 강요하지 않는다. 한 줄짜리 룰이지만, 이게 UX의 절반이었습니다.
풀스택 구조
기획·UI·프론트·백을 한 팀에서 진행했습니다. 그래서 “이거 가능해요?”와 “네/아니오” 사이의 거리가 보통 한 시간 미만이었어요. 분업 구조였다면 같은 결정에 며칠이 들었을 겁니다.
[브라우저]
React + Three.js
↓ REST/JSON
[Node.js API]
express / prisma
↓
[PostgreSQL]
사용자, 조립 진행도, 스크린샷 메타
[AWS S3]
3D 모델(GLB), 사용자 스크린샷
[Docker / AWS ECS]
무중단 배포
백엔드의 1차 역할은 “진행도 저장”
- 어느 단계까지 갔는지
- 어떤 모드(단일 부품 / 큐브)에서 멈췄는지
- 마지막 카메라 포지션까지
이걸 다 저장하면 다음에 들어왔을 때 “여기서부터”가 자연스럽게 작동합니다. 조립 게임의 가장 큰 좌절은 “처음부터 다시”니까요.
스크린샷은 클라이언트가 직접 만든다
canvas.toBlob() 으로 PNG를 만들어서 presigned URL로 S3에 직접 업로드합니다. 서버를 거치지 않으니 트래픽이 가볍고 빠릅니다.
모바일 60fps을 어떻게 지켰나
부품이 많은 모델은 모바일에서 가장 먼저 죽습니다. 그래서 네 가지를 적용했습니다.
- 부품 단위 인스턴싱: 같은 부품이 여러 개일 때
InstancedMesh로 GPU 한 번만 호출 - LOD: 카메라가 멀어지면 자동으로 저폴리 모델로 교체
- KTX2 텍스처 압축: 같은 비주얼에서 텍스처 메모리 1/3
- PBR 머티리얼 캐시: 같은 재질 부품은 머티리얼 인스턴스를 공유
가장 효과가 컸던 건 인스턴싱이었습니다. 같은 모양의 작은 블록 50개를 한 번에 그릴 때, 호출이 50번에서 1번으로 줄면서 모바일 fps가 24 → 58로 올랐어요.
끝나고 보니 배운 것
3D 조립은 결국 데이터 모델링이다
조립 순서, 카메라, 강조 상태, 단계 그룹 — 데이터를 잘 잡으면 코드가 짧아지고, 못 잡으면 if 문으로 도배됩니다. 단계 데이터 정의에 시간을 쓰는 게 가장 효율적이었습니다.
“놓을 수 있는 곳”을 보여주는 게 절반
블록 조립의 본질은 “이걸 어디에 놓느냐”입니다. 다음 부품이 들어갈 위치를 점선 와이어프레임으로 미리 보여주면 사용자는 도움말 없이도 다음 단계를 이해합니다. 모든 안내 텍스트보다 효과적이었어요.
풀스택 한 팀이 빠르다
기획·UI·프론트·백 — 다 같은 회의실에 있는 게 가장 빠릅니다. 모든 결정이 “물어볼게요”가 아니라 “지금 정합시다”가 됐어요.
자주 받는 질문
어떤 영역에 적용 가능한가요? 가구·산업 장비 조립 매뉴얼, 교육용 키트, 군 장비 트레이닝, 자동차 부품 학습, DIY 가이드 — “순서가 있는 조립”이라면 카테고리에 거의 제한이 없습니다.
우리 회사 모델로 만들려면 무엇이 필요한가요?
- GLB 또는 OBJ 형식의 3D 모델 (없으면 CAD 파일에서 변환 가능)
- 조립 순서 (스프레드시트 한 장이면 충분)
- 회사 톤·컬러가 정리된 디자인 가이드
오프라인에서도 동작하나요? PWA로 만들면 첫 방문 후 캐시되어 오프라인에서도 동작합니다. 진행도 저장만 다음 온라인 접속 때 동기화됩니다.
도입까지 얼마나 걸리나요? 부품 50개 미만, 단계 30개 정도라면 6~10주. 부품 수가 많거나(예: 자동차 1/24 모델) 단계가 100개를 넘으면 단계 데이터를 만드는 시간이 개발보다 길어질 수 있습니다.
조립 매뉴얼을 PDF에서 3D 웹 서비스로 옮길 계획이라면, 프로젝트 문의 로 부품 수와 단계 수를 알려주세요. 보통 한 시간이면 “3D로 옮길 가치가 있는가”에 대한 답을 같이 만들 수 있습니다.
Hammergrid Lab은 WebGPU/Three.js 기반 3D 웹과 풀스택 서비스를 만드는 크리에이티브 개발 스튜디오입니다.
English version: Assembling 3D Blocks Inside a Browser — Building Geometree