파일 크기가 큰 프로젝트 최적화 (feat. GLTF 파일)
목차
구현한 것
three.js를 이용한 3D 미로 탈출 게임
문제상황
완전히 로딩이 되기 까지 1분이나 걸렸다.
gltf 모델을 불러오는것 자체가 오래걸려 네트워크를 열어보니 png 파일 로드가 엄청나게 느렸다. 뒷부분이 잘렸지만 파란색 바가 끝없이 늘어났다...
모델 하나 크기도 큰데 모델을 무려 11개나 사용하니 느릴 수 밖에 없었다.
처음에는 다음 블로그처럼 LOD나 merging 방법을 써보려고 했는데 나는 이미 누군가 만들어둔 3D 모델을 갖다 쓰는거라 mesh 속성에 접근할 수 없었다.
https://joong-sunny.github.io/graphics/graphics2/
[그래픽스] three.js에서의 다양한 렌더링 최적화 batching과 LOD
🚀Introduction
joong-sunny.github.io
시도해본 방법
1. html에 link 태그로 리소스 우선순위 높이기 → 실패
모든 gltf의 우선순위를 높이는게 의미가 없었고, 로딩 속도가 너무 길어 효과적이지 않았다.
우리 프로젝트는 랜딩페이지에서 시작 버튼을 누르면 게임페이지로 넘어오는 구조였는데, 랜딩페이지에서 머무는 시간은 10초도 안될 것이기 때문에 랜딩페이지에서 미리 로딩한다고 해도 소용이 없었던 것이다.
2. 텍스쳐 png 파일들 webp로 변경 (1차 최적화, 로딩속도 1분 → 15초)
3. gltf 파일 glb 파일로 변환 & 3D 모델 draco 압축 (2차 최적화, 로딩속도 15초 → 2초 이내)
4. 빌드시 파일 gzip 압축 (3차 최적화, 로딩속도 2초 이내 → 1초 이내)
1차 최적화
용량이 큰 jpg, png, jpeg 파일들을 webp로 바꿨다. 친절하게 이미지 확장자를 한번에 변환할 수 있는 방법을 올려두셔서 빠르게 할 수 있었다. 이 때 주의해야할 점은, 파일 이름에 공백이 들어가면 에러가 뜨고 webp으로 바뀌지 않는다.
https://velog.io/@cnsrn1874/WebP%EB%A1%9C-%EC%84%B1%EB%8A%A5-%EA%B0%9C%EC%84%A0
WebP로 성능 개선
이미지의 확장자를 .webp로 변환해서 웹 성능을 개선해본다.
velog.io
참고로 특정 디렉토리 위치에서 바로 cmd를 여는 방법은 다음과 같다.
텍스쳐 디렉토리에 있던 기존 파일을 삭제하고 변경된 파일들을 넣어준다.
그리고 .gltf 파일을 열고 기존 확장자명을 webp로 바꿔준다. 나같은 경우는 사진 파일들이 gltf 모델에 쓰였기 때문에 이 과정을 거치는 것이다. 웹에 업로드하는 용도라면 변환한 파일 교체까지만 하면 된다.
같은 파일이라는게 믿어지지 않을 정도로 크기가 줄어들었다!!!!
완전 로드될 때까지 1분이 넘던 파일들이 이제 15초 이내로 다 로드된다.
하지만 여전히 bin, gltf, 용량이 큰 webp는 10초 정도 걸린다.
* 참고
webp란?
구글에서 만든 이미지 포맷으로, 이미지 용량을 30%정도 줄여준다. gif, png, jpeg으로된 이미지 포맷은 모두 webp로 대체될 수 있다. 구글에서는 웹 브라우저를 확인한 후 webp가 지원되는 브라우저면 webp로 보여준다고 한다. 이미지 크기를 줄이면 트래픽도 줄어들고, 인터넷 속도도 빨라지기 때문이다.
대표적인 예시로 유튜브 썸네일이 있다. webp로 썸네일 사진을 보여준다고 한다.
다만, 호환성 문제가 있다. 대부분의 브라우저에서 지원이 가능하지만 여전히 일부 사이트에서는 호환이 되지 않는다.
https://caniuse.com/?search=webp
"webp" | Can I use... Support tables for HTML5, CSS3, etc
Video element Method of playing videos on webpages (without requiring a plug-in). Includes support for the following media properties: `currentSrc`, `currentTime`, `paused`, `playbackRate`, `buffered`, `duration`, `played`, `seekable`, `ended`, `autoplay`,
caniuse.com
2차 최적화
이제 2차 최적화를 해보겠다.
draco 압축과 동시에 gltf 파일을 glb로 바꿔줬다.
> draco 압축하는법
https://github.com/CesiumGS/gltf-pipeline
GitHub - CesiumGS/gltf-pipeline: Content pipeline tools for optimizing glTF assets. :globe_with_meridians:
Content pipeline tools for optimizing glTF assets. :globe_with_meridians: - CesiumGS/gltf-pipeline
github.com
gltf-pipeline을 이용하면 다음과 같은 작업을 할 수 있다.
- gltf를 glb로 변환, 또는 glb를 gltf로 변환
- gltf 1.0 모델을 gltf 2.0 모델로 변환
- Draco 압축
우선 gltf-pipeline을 설치한다.
npm install -g gltf-pipeline
다음으로는 변환하려는 파일(gltf/glb)이 있는 위치에서 터미널을 연다.
그리고 옵션을 확인 후 명령어를 입력한다. 위 깃허브 주소를 들어가면 더 많은 옵션을 확인할 수 있다.
glb를 glTF로 변환
gltf-pipeline -i model.glb -o model.gltf
gltf-pipeline -i model.glb -j
나에게 필요한 옵션은 다음과 같았다.
1. gltf를 glb로 변경하기
2. draco 압축을 최대 레벨로 하기

따라서 다음과 같은 명령어를 사용하였다.
gltf-pipeline -i model.gltf --draco.compressionLevel 10 --draco.compressMeshes -o model.glb
gltf 파일을 glb로 바꿔준 이유는 gltf는 json 파일이고 glb는 바이너리 파일이기 때문이다. json 파일은 다음과 같이 공백, 개행이 많은 파일이다. 이런 공백 역시 파일 용량에 포함되기 때문에 바이너리 파일로 바꾸어 불필요한 용량을 줄였다. json 파일의 형식을 바꾸지 않으면서도 파일 용량을 줄이고 싶다면 json minifier을 이용하여 불필요한 공백, 개행, 주석을 삭제해주면 된다.
나는 glb 파일이 gltf + bin + texture 인줄 알았는데 그냥 json 형식으로 된 gltf를 바이너리 형식으로 바꿔주는 것이었다. 그래서 사용자가 모델을 직접 커스텀하기 어렵다. 나는 기존 gltf를 커스텀하긴 했지만 더 수정할 부분이 없어 일부 파일을 제외하고 모두 glb로 바꿔줬다. bin이랑 texture 파일을 지우지 말고 그대로 둬야한다. 바이너리 파일로 바꿨더니 용량이 50KB → 34KB로 줄었다.
파일 용량을 줄인 후 draco 압축을 해주니 2초만에 로드가 됐다. cardsoldier의 경우 실험삼아 draco 압축을 안하고 단순히 glb로 변경했는데 용량이 좀 줄었을 뿐 별 다른 개선이 없었다.
그럼 이제 cardsoldier도 압축해보겠다. 속도가 엄청나게 빨라진걸 볼 수 있다. (14ms → 2ms)
최종 결과
400ms 안에 모든 파일이 들어왔고, 사용자가 서비스를 완전히 이용할 수 있을 때 까지 걸린 시간은 2초였다.
3차 최적화
여전히 사이트가 약간 버벅거린다는 느낌이 들어서 텍스트 데이터를 gzip으로 압축해봤다.
> gzip이란? gzip을 사용하는 이유 (feat. content encoding)
> gzip 압축 방법
nginx(서버)에서 압축하는 방법이 있고 모듈 번들러 빌드시 미리 압축한 후 서버에 업로드하는 방법이 있다.
나는 vite 환경에서 gzip 압축하는 방식을 선택했다.
1. 플러그인 설치
npm install vite-plugin-compression --save-dev
2. Vite 설정 파일(vite.config.js) 수정
- threshold를 설정하는 이유는, 클라이언트 브라우저가 압축을 해제하는 과정이 있으므로 크기가 작은 파일의 경우 압축을 안 하는 것이 오히려 효율적일 수 있기 때문이다.
import { defineConfig } from 'vite';
import compression from 'vite-plugin-compression';
export default defineConfig({
plugins: [
compression({
// 압축 설정
verbose: true, // 압축 프로세스의 로그를 출력
threshold: 10240, // 10KB 미만의 파일은 압축하지 않음
algorithm: 'gzip', // 사용할 압축 알고리즘
ext: '.gz', // 생성되는 파일의 확장자
}),
],
});
3. Nginx 설정으로 더 효율적으로 압축파일 서빙하기 (생략 가능)
- gzip_static을 on으로 설정
- 이 설정으로 인해 Nginx는 요청에 해당하는 .gz 파일이 존재할 경우, 자동으로 이 압축된 버전을 제공한다. 이는 불필요한 서버 측 압축 작업을 방지하고, 성능을 개선하는 데 도움이 된다.
- 이 설정으로 인해 클라이언트가 압축을 지원하는 경우에만 압축된 파일을 제공하도록 설정할 수 있다. Nginx는 Accept-Encoding 헤더를 확인하여 이를 처리한다.
server {
listen 80;
server_name example.com;
location / {
root /path/to/your/dist;
try_files $uri $uri/ $uri.gz =404;
gzip_static on;
expires max;
}
}
로딩속도가 절반으로 줄어들었다.
이렇게 response Header을 열어서 Content-Encoding: gzip이 있는지 확인해보면 된다.
index.html 파일
gltf와 glb 파일 용량도 엄청나게 작아진걸 확인할 수 있다.
참고 블로그
[GLTF 최적화 시키기]
정말 빛과 같은 블로그였다...
https://velog.io/@juunini/GLTF-%EC%B5%9C%EC%A0%81%ED%99%94-%EC%8B%9C%ED%82%A4%EA%B8%B0
GLTF 최적화 시키기
저는 퍼리충이 아닙니다
velog.io
[gzip으로 리소스 압축]
나의 경우 vite 환경이라 조금 달랐지만 참고하기 좋았다.
https://wonsss.github.io/%EC%B5%9C%EC%A0%81%ED%99%94/gzip-compression/
gzip으로 리소스 압축(웹서버 또는 Webpack 방법)
Encoding 관련 헤더 필드 Content-Encoding (엔티티 헤더 필드) 은 의 일종이다. 엔티티 헤더 필드 엔티티 헤더 필드는 request 메시지와 response 메시지에 포함된 엔티티에 사용되는 헤더이다. 콘텐츠의 갱
wonsss.github.io