Devlog: I Built a 3D Library in Three.js Without a Level Editor — So I Made My Own
Three.js로 3D 도서관을 만들다가 직접 편집 도구를 만들었습니다
Oniria에서 DEV.to 글을 책으로 배치하는 3D 도서관을 만들며, 좌표를 코드로 고치는 대신 공간 안에서 위치를 지정하는 마커 도구를 개발했습니다. 마커를 Sanity 문서로 저장하고 내보내면서 별도 레벨 편집기 없이도 배치를 이어갈 수 있는 작업 흐름을 마련했습니다.
- 주제
AI 요약
개발자는 Oniria 안에 DEV.to 글을 책으로 배치한 3D 도서관을 만들고 있습니다. 처음에는 네온 구조물과 고층 건물이 있는 사이버 도시를 구상했지만, 글이 공간에 놓여도 그 위치가 정보를 설명하지는 않는다는 점을 깨달았습니다. 그래서 공간 자체가 콘텐츠를 정리하는 도서관으로 방향을 바꿨습니다. 방은 카테고리, 책장은 컬렉션, 책은 글을 나타냅니다.
좌표를 코드로 편집하는 작업의 한계
기존에는 GLB 에셋을 복제해 위치·크기·회전을 적용하는 Three.js 함수 placeAsset을 만들고, 호출할 때마다 좌표와 회전값을 직접 입력했습니다. 배치를 바꾸려면 코드를 수정하고 저장한 뒤 브라우저를 새로고침해야 했습니다. 그다음 도서관 안을 걸어가 물체가 원하는 자리에 있는지 확인하고, 틀리면 다시 숫자를 고쳤습니다.
배치 대상은 벽과 건축 요소, 책장과 가구뿐 아니라 표지판, 조명, 출입구, 충돌 경계, 아직 만들지 않은 책장을 위한 빈 공간까지 늘었습니다. 테이블 하나의 위치를 조금 바꾸는 일도 방을 가로질러 가서 책장과 겹치는지 확인해야 했습니다. 저자는 전통적인 레벨 편집기로 프로젝트를 옮기기보다, DEV 데이터에서 구역·방·책장 슬롯을 거쳐 Three.js 배치로 이어지는 기존 구조 안에서 해결책을 찾고자 했습니다.
공간에서 위치를 기록하는 마커
저자는 Oniria에 인월드 레이아웃 마커 도구를 만들었습니다. 배치할 곳에 직접 서서 핀을 놓으면 도구가 위치와 회전값을 기록합니다. 예를 들어 마커에는 label, roomSlot, districtId, x, y, z, yaw가 들어갑니다. 눈으로 확인한 자리를 숫자로 짐작해 입력하는 대신, 공간에서 원하는 위치를 정하고 그 상태를 좌표로 가져오는 방식입니다.
기록한 값은 실제 에셋 배치에 다시 사용합니다. 책장이나 파빌리온을 놓을 곳을 표시하고, 표지판끼리 겹치는 문제를 확인하거나 조명을 특정 위치에 맞추는 식입니다. 마커를 Sanity 문서로 저장해 브라우저를 새로고침해도 남게 했으며, 안정적인 ID를 부여하고 JSON으로 내보내는 기능도 마련했습니다. 예시 내보내기 형식은 oniria-library-layout-pins-v1이며, 마커 목록에 방 슬롯과 좌표를 담습니다.
별도 편집기 대신 프로젝트 안에 도구를 둔 이유
마커 도구는 디버깅 보조 기능에서 출발했지만, 지속 저장과 내보내기를 더하면서 조사 도구이자 작은 레벨 편집기 역할을 하게 됐습니다. 저자는 별도 애플리케이션을 배치 정보의 새로운 기준 데이터로 만들고 싶지 않았습니다. 글 데이터에서 공간 구조와 에셋 배치까지 연결된 기존 흐름을 유지하면서, 필요한 편집 기능을 프로젝트 안에 추가했습니다. 현재 도서관에는 공간 배치 규칙, 저장되는 마커, 검색 가능한 책장, 동적으로 생성되는 슬롯이 들어가며, 저자는 방 단위로 작업을 이어가고 있습니다.
dev.to 반응
- @sinarezaei — 흥미로운 대목은 3D 도서관 자체보다 문제가 “이 물체를 어떻게 놓지?”에서 “왜 아직도 좌표를 손으로 고치고 있지?”로 바뀌는 순간입니다. 마커 방식이 잘 맞는 이유는 환경 자체를 저작 인터페이스로 쓰기 때문입니다. 눈으로 본 위치를 x/y/z 값으로 머릿속에서 바꾸지 않고, 물체가 들어갈 자리에 서서 도구가 상태를 기록하게 합니다. 별도 편집기를 데이터 기준의 또 다른 출처로 추가하지 않은 점도 좋습니다. 도서관이 이미 DEV 데이터 → 구역 → 방 → 책장 구조를 갖췄으니, 배치 정보를 그 안에 두면 동기화할 파이프라인이 하나 더 생기지 않습니다. Sanity에 저장하는 것도 좋은 선택입니다. 마커에 ID를 붙이고 내보낼 수 있게 되면 임시 디버깅 도구를 넘어 실제 레이아웃 데이터가 됩니다. 작업 흐름이 프로젝트와 맞지 않을 때는 흐름 자체를 바꾸기보다 빠진 도구를 만드는 편이 나을 때가 있다는 좋은 사례입니다.
- @mikachu — 만드는 동안 실제로 그런 변화가 일어났습니다. 처음에는 위치를 더 잘 어림잡거나 값을 정리하면 된다고 생각해 좌표를 문제로 여겼습니다. 그러다 공간 환경을 환경 자체가 아니라 숫자로 작성하려는 게 진짜 문제라는 걸 깨달았습니다. 직접 서서 마커를 놓고 상태를 저장할 수 있게 되자 작업 흐름이 훨씬 자연스러워졌습니다. 꼼꼼히 읽어주셔서 감사합니다! 💚
- @plastikelectrik — 잘 만들었네요. 작동하는 모습을 기다리겠습니다 😃
- @mikachu — 감사합니다! 잘 진행되고 있습니다. 3D 배치를 설계하는 과정이 지금까지 가장 즐거웠습니다 :)
- @plastikelectrik — 얼마나 즐거운지 완전히 이해합니다!!!
- @pavel_kkkkazantsev — 잘 만들었습니다! 행운을 빕니다!
- @dylnd0g — 마커를 안정적인 ID가 있는 Sanity 문서로 만든 점이 좋은 선택입니다. 디버그 상태를 실제 레이아웃 데이터로 바꿔줍니다. 위치와 yaw를 격자에 맞추고 마커를 놓을 때 바운딩 박스를 검사하면 대부분의 겹침을 걸러낼 수 있습니다. 핀은 실행 시 Sanity에서 가져오나요, 아니면 빌드 시점에 포함하나요?
- @georgekobaidze — “도서관 아이디어는 간단합니다...”라고 하더니 게임 엔진 전체를 만들고 있네요 😄 보통 그렇게 되죠. 접근 방식이 흥미롭습니다. 엔진을 재사용할 수 있다는 점이 큰 장점이라, 그 위에 다른 재미있는 것도 만들 수 있겠네요. 그리고 다른 재미있는 얘기가 나와서 말인데, 사이버 도시 아이디어도 좋습니다. 제가 정말 좋아하는 Cyberpunk와 Night City가 떠오릅니다. City Center나 Badlands에서 DEV 글을 읽는 모습을 상상해보세요. 정말 멋진 경험이겠네요. 다시 현실로 돌아와서, 도서관을 꼭 체험해보고 싶습니다.
- @kyisaiah47 — 인월드 편집기에서 변환값을 코드가 관리하는 씬 그래프로 어떻게 저장하나요? 책장 슬롯과 충돌 경계가 같은 배치 데이터를 공유할 때도 저장 결과를 diff하기 쉽게 유지하나요?
- @brianainews — 코드 우선 방식의 한계에 부딪힌 뒤 편집기를 만든 것은 좋은 제품 선택입니다. 이제 시각적 편집 계층에서도 씬 데이터를 살펴보고 내보낼 수 있는지가 중요합니다. 그래야 편리함을 얻는 대신 새로운 종속성이 생기지 않습니다.
원문: dev.to / 번역·요약: Trawling