BabylonJS 20회차 : 에셋 관리 전략(폴더/네이밍/버전)



BabylonJS 20회차 : 에셋 관리 전략(폴더/네이밍/버전)

목표: 프로젝트가 커져도 관리 가능한 구조 만들기
핵심 개념: assets/, scenes/, materials/, prefabs 개념
실습: 기본 템플릿 구조 확정
산출물: “내 Babylon 프로젝트 템플릿 레포”


요약

초반에는 파일이 몇 개 없어서 폴더 규칙이 없어도 잘 굴러갑니다. 하지만 모델(GLB), 텍스처, 머티리얼, 씬이 늘어나면 “어디에 뭐가 있는지”와 “어떤 게 최신인지”가 바로 생산성을 갉아먹습니다. 이번 회차에서는 폴더 구조, 네이밍 규칙, 버전 관리를 미리 고정하고, 이를 그대로 복사해 쓸 수 있는 “Babylon 프로젝트 템플릿 레포” 형태로 정리합니다.


목차


1) 핵심 포인트(오늘 고정할 규칙)

  • “소스”와 “런타임”을 분리: Blender 원본(.blend)과 엔진에서 로딩하는 GLB/텍스처를 같은 폴더에 섞지 않습니다.
  • 폴더 역할을 고정: assets/(리소스), materials/(머티리얼 팩토리), prefabs/(조립 단위), scenes/(씬 엔트리)로 나눕니다.
  • 파일명 규칙은 “보는 즉시” 판단 가능해야: 타입/용도/버전이 드러나게 하고, 케이스는 단일 규칙(kebab-case 등)으로 통일합니다.
  • 버전은 2트랙으로 관리: 코드(semver)와 에셋(리비전/리비전+체인지로그)을 분리해 생각합니다.
  • 최종 목표는 템플릿화: 매 프로젝트마다 고민하지 않고 “복사 → 시작”이 되도록 레포 구조를 확정합니다.


2) 폴더 전략: assets/, scenes/, materials/, prefabs

폴더는 “파일을 담는 상자”가 아니라 “팀/미래의 내가 사고하는 방식”입니다. 따라서 폴더 이름은 기능과 책임이 분명해야 합니다. 아래는 초보자에게도 확장성이 좋은 기본 구조입니다.

폴더 역할 예시 파일 운영 규칙
public/assets/ 런타임 로딩 대상(GLB/텍스처/환경맵) models/, textures/, env/ 여기 있는 파일은 “브라우저가 직접 접근”하는 정적 리소스만 둡니다.
source-assets/ 원본 제작물(Blender/PSD 등) .blend, .psd 배포 대상이 아니며, 내보내기(export) 결과만 public/assets로 이동합니다.
src/scenes/ 씬 엔트리(장면 시작점) MainScene.ts, ViewerScene.ts “게임/뷰어 흐름”의 진입점만 두고, 조립 로직은 prefabs로 내려보냅니다.
src/materials/ 머티리얼 팩토리(재사용 단위) createCarPaint.ts “재사용” 가능한 머티리얼만 둡니다(씬에서 즉석 생성 금지 규칙을 추천).
src/prefabs/ 프리팹(조립 단위: 모델+머티리얼+애니메이션) CarPrefab.ts, RobotArmPrefab.ts 엔진에서 “조립 후 반환”하는 단위를 표준화합니다(부모자식/피벗 포함).

2-1) 왜 public/assets 아래로 모으나요?

  • 브라우저에서 로딩하는 파일은 URL로 접근 가능한 정적 경로에 있어야 합니다.
  • 빌드 도구(Vite/Webpack 등)에서도 public은 “그대로 복사”되는 영역으로 취급되는 경우가 많아 관리가 단순합니다.
  • “어디가 런타임 경로인지”가 명확해져, Import/SceneLoader 경로 문제를 줄일 수 있습니다.

2-2) assets 내부 세부 구조(권장)

에셋 폴더도 ‘성격’에 따라 하위 폴더를 고정하면 찾기 쉬워집니다.

public/
  assets/
    models/
      vehicles/
      characters/
      props/
    textures/
      vehicles/
      characters/
      props/
      shared/
    env/
      studio-hdr/
    audio/
    manifest/

초보자 단계에서는 models/textures/만 먼저 시작해도 충분합니다. 프로젝트가 커질 때 env/, audio/를 확장하면 됩니다.


3) 네이밍 규칙: 파일명만 봐도 용도를 알게 만들기

네이밍 규칙의 목표는 “검색/정렬/충돌 방지”입니다. 특히 GLB/텍스처는 파일 개수가 빠르게 늘어나기 때문에, 초반에 규칙을 잡지 않으면 금방 정리가 어려워집니다.

3-1) 기본 규칙(권장)

  • kebab-case(소문자-하이픈) 통일: 운영체제/호스팅 환경 차이를 줄이고, URL에서도 안전합니다.
  • 의미 단위로 구분: [카테고리]-[이름]-[변형]-[버전] 같은 패턴을 고정합니다.
  • 약어는 최소화: 팀원이 늘어날수록 약어는 해석 비용이 됩니다(orm, normal 같은 표준 약어만 예외).
종류 권장 패턴 예시 설명
GLB 모델 [name]-v###.glb car-sedan-v003.glb 버전은 정렬을 위해 3자리 숫자 권장
텍스처(Base Color) [name]__basecolor.png car-sedan__basecolor.png 채널 의미가 명확해야 합니다
텍스처(Normal) [name]__normal.png car-sedan__normal.png 노멀은 포맷/색공간 관리가 중요합니다
텍스처(ORM) [name]__orm.png car-sedan__orm.png Occlusion/Roughness/Metallic 패킹 텍스처
Babylon 코드(씬) PascalCase.ts ViewerScene.ts TS/JS는 관례적으로 PascalCase를 자주 사용합니다

3-2) “latest”는 파일명으로 관리하지 말고, 코드에서 선택

초보자 때 흔한 실수는 car-sedan-final.glb, car-sedan-final2.glb처럼 ‘감정’이 담긴 파일명이 쌓이는 것입니다. 대신 다음 방식 중 하나를 추천합니다.

  • 버전 파일만 유지: car-sedan-v001.glb, car-sedan-v002.glb처럼 숫자로만 쌓습니다.
  • manifest에서 “현재 사용 버전”을 지정: 코드는 manifest를 읽어 최신을 선택합니다.
  • 릴리스 폴더 분리: 검증된 버전만 public/assets/models/release/로 복사합니다.


4) 버전 전략: “최신/호환/변경”을 추적하는 방법

버전 관리는 크게 두 종류입니다.

  • 코드 버전: semver(예: 1.2.0)처럼 의미 있는 규칙으로 릴리스합니다.
  • 에셋 버전: GLB/텍스처는 “작업 과정상” 자주 바뀌므로, 숫자 리비전(v001, v002)과 변경 기록이 중요합니다.

4-1) 에셋 버전이 필요한 순간

  • 같은 이름의 모델이 스케일/피벗/UV가 달라져서, 엔진 쪽 코드(피벗/애니메이션)가 갑자기 깨집니다.
  • 텍스처가 교체되어 색공간/노멀 방향 등이 달라져 렌더링이 달라집니다.
  • 최적화(폴리곤 감소/텍스처 압축)로 파일이 바뀌었는데, 품질 이슈가 생겨 이전 버전으로 롤백이 필요합니다.

4-2) 추천: 에셋 매니페스트(manifest)로 “사용 중인 버전”을 고정

프로젝트가 커지면 “현재 씬이 어떤 GLB를 쓰는지”가 코드 곳곳에 하드코딩되기 쉽습니다. 이를 줄이려면 간단한 manifest를 두고, 씬/프리팹이 그 값을 참조하도록 만들면 안정적입니다.

{
  "models": {
    "car_sedan": {
      "path": "/assets/models/vehicles/car-sedan-v003.glb",
      "rev": 3,
      "note": "pivot fixed, wheel hierarchy separated"
    },
    "robot_arm": {
      "path": "/assets/models/props/robot-arm-v002.glb",
      "rev": 2,
      "note": "scale applied, materials updated"
    }
  },
  "env": {
    "studio_hdr": {
      "path": "/assets/env/studio-hdr/studio-1k.env"
    }
  }
}

이 방식의 장점은 간단합니다. 파일 교체(버전 업) 시 수정 지점이 한 곳으로 모입니다. 또한 note를 남기면 “왜 바뀌었는지”도 함께 남습니다.

4-3) 변경 기록(Changelog) 최소 구성

  • 에셋별로 note를 남기기: 위 manifest의 note처럼 “무엇이 바뀌었는지”를 1줄로 남깁니다.
  • 큰 변경은 별도 문서로: 예: docs/assets-changelog.md에 주간 단위로 기록합니다.
  • 호환성 파손은 표시: 피벗/본 구조가 바뀌면 프리팹 코드가 바뀌어야 하므로, “Breaking” 표시를 권장합니다.


5) 실습: 기본 템플릿 구조 확정(레포 뼈대)

이제 “내 Babylon 프로젝트 템플릿 레포”를 목표로, 복사해서 바로 시작할 수 있는 구조를 확정합니다. 빌드 도구는 무엇이든 가능하지만, 여기서는 “폴더/규칙” 자체가 핵심이므로 도구에 종속되지 않는 형태로 제시합니다.

5-1) 템플릿 폴더 트리(최종안)

my-babylon-template/
  public/
    assets/
      manifest/
        assets-manifest.json
      models/
        vehicles/
        characters/
        props/
      textures/
        shared/
      env/
  source-assets/
    blender/
    textures/
  src/
    app/
      bootstrap.ts
    scenes/
      MainScene.ts
      ViewerScene.ts
    prefabs/
      CarPrefab.ts
    materials/
      createDefaultMaterial.ts
    loaders/
      assetRegistry.ts
  docs/
    export-checklist.md
    naming-rules.md
  .gitignore
  README.md

5-2) prefabs는 “조립 단위”로 생각합니다

prefab은 게임엔진에서 흔히 “재사용 가능한 오브젝트 조립품”을 의미합니다. BabylonJS는 유니티처럼 프리팹 파일이 고정된 형태로 존재하는 것은 아니지만, 코드 레벨에서 프리팹 개념을 만들 수 있습니다.

  • CarPrefab은 “차체 GLB 로딩 + 바퀴 피벗 연결 + 머티리얼 적용”을 한 번에 수행하고, 루트 노드를 반환합니다.
  • 씬은 CarPrefab을 호출해 배치만 합니다(조립 로직이 씬으로 새지 않게).
  • 이렇게 분리하면 씬이 많아져도 구조가 유지됩니다.

5-3) 실습 코드: assetRegistry(매니페스트 기반 경로 관리)

아래 코드는 “경로 하드코딩”을 줄이는 최소 골격입니다. 초보자 단계에서는 이 정도만 있어도 폴더 구조의 장점이 바로 체감됩니다.

// src/loaders/assetRegistry.ts
export type AssetManifest = {
  models: Record<string, { path: string; rev?: number; note?: string }>;
  env?: Record<string, { path: string }>;
};

let cached: AssetManifest | null = null;

export async function loadManifest(url = "/assets/manifest/assets-manifest.json") {
  if (cached) return cached;
  const res = await fetch(url);
  if (!res.ok) throw new Error("Failed to load manifest: " + res.status);
  cached = await res.json();
  return cached;
}

export async function getModelPath(modelId: string) {
  const manifest = await loadManifest();
  const item = manifest.models[modelId];
  if (!item) throw new Error("Unknown modelId: " + modelId);
  return item.path;
}

위 코드의 포인트는 단순합니다. 씬/프리팹 어디에서도 /assets/models/...를 직접 쓰지 않고, modelId로만 접근하게 만드는 것입니다. 프로젝트가 커질수록 이 차이가 누적됩니다.

5-4) 템플릿 README에 넣을 “규칙 요약” 예시

# My Babylon Template Rules

* Runtime assets live in: public/assets/
* Source files live in: source-assets/ (never loaded by the app)
* Model files: kebab-case + -v###.glb (e.g. car-sedan-v003.glb)
* Textures: [name]__basecolor / __normal / __orm
* Don't hardcode paths in scenes/prefabs. Use assets-manifest.json + assetRegistry.
* Prefabs assemble (load + hierarchy + materials) and return a root node.

  


6) 운영 체크리스트

템플릿이 있어도, 운영 중 규칙이 깨지면 다시 혼란이 생깁니다. 아래 체크리스트는 “늘어나는 프로젝트”를 안정적으로 유지하기 위한 최소 점검 항목입니다.

구분 체크 항목 권장 기준 놓치면 생기는 일
폴더 런타임/소스 분리 public/assets vs source-assets 경로 혼란, 배포 파일 누락/과포함
네이밍 kebab-case 통일 소문자+하이픈, 의미 있는 접미사 OS/호스팅에 따라 경로가 깨짐
버전 모델 리비전 관리 -v### + 변경 노트 최신 추적 불가, 롤백 어려움
로딩 경로 하드코딩 금지 manifest 기반 로딩 씬마다 수정, 버그 발생 확률 증가
조립 prefab로 조립 로직 격리 씬은 배치만 담당 씬이 커지고 복잡해져 유지보수 어려움


7) 추가로 생각해볼 점

  • 압축/최적화 파이프라인: 프로젝트가 커지면 GLB 최적화(메시 압축, 텍스처 압축, 환경맵 크기) 단계가 필요해집니다. 이때도 “source-assets → public/assets” 흐름을 유지하면 작업이 안전합니다.
  • 에셋 ID 설계: manifest의 키(car_sedan 같은 modelId)는 “프로젝트 내부 API”가 됩니다. 한 번 정한 ID는 가급적 바꾸지 않는 규칙이 장기적으로 유리합니다.
  • 팀 합류를 대비한 문서: docs/naming-rules.md, docs/export-checklist.md 같은 문서는 초보자에게 특히 도움이 됩니다. 규칙이 글로 남아 있으면 품질 편차가 줄어듭니다.
  • 프리팹 테스트: prefabs가 늘어나면 “프리팹만 단독으로 로딩해보는 ViewerScene”이 품질 게이트 역할을 합니다(이전 회차의 로딩 UI 뷰어와 연결하면 효과가 큽니다).

이 포스팅은 쿠팡 파트너스 활동의 일환으로, 이에 따른 일정액의 수수료를 제공받습니다.

Reactions

댓글 쓰기

0 댓글