Lottie JSON 구조 뜯어보기 — v·fr·ip·op·layers가 뭔가요
Lottie 파일이 "왜 이렇게 큰지", "왜 안 움직이는지"를 진단하려면 결국 JSON을 열어보게 됩니다. 그런데 처음 열면 v, fr, ip, op 같은 두세 글자 필드가 가득해서 어디를 봐야 할지 막막합니다. 자주 보게 되는 필드만 추리면 사실 열 개가 안 됩니다. 이 글 하나로 Lottie JSON을 "읽을 수 있는" 상태를 만드는 것이 목표입니다.
최상위 필드 — 이것만 알면 절반
| 필드 | 의미 | 진단에서의 쓰임 |
|---|---|---|
v | Bodymovin/Lottie 스펙 버전 | 너무 낮으면(4.x 등) 구형 파일 — 재내보내기 후보 |
fr | 프레임레이트 (fps) | 60이면 30으로 낮출 여지 확인 |
ip / op | 시작/끝 프레임 (in/out point) | 재생 시간 = (op − ip) ÷ fr 초 |
w / h | 캔버스 크기 (px) | 스펙과 규격 일치 여부 |
nm | 이름 | 레이어·컴포지션 식별 (keypath의 재료) |
assets | 이미지·프리컴프 목록 | 용량 폭탄이 숨는 곳 |
layers | 레이어 배열 | 개수 = 렌더링 비용의 대략적 지표 |
markers | 구간 마커 | 토글 인터랙션의 구간 경계 |
예를 들어 fr: 60, ip: 0, op: 417이면 417 ÷ 60 ≈ 6.95초짜리 애니메이션입니다. 뷰어가 카드에 표시하는 재생 시간이 바로 이 계산입니다.
layers — ty 숫자가 레이어의 정체
layers 배열의 각 항목에는 ty(type) 숫자가 있습니다. 이 숫자만 읽어도 파일의 성격이 보입니다.
| ty | 레이어 종류 | 비고 |
|---|---|---|
| 0 | 프리컴프 | 다른 컴포지션 참조 — 중첩 구조의 원인 |
| 1 | 솔리드 | 단색 사각형 |
| 2 | 이미지 | 래스터 — assets와 함께 용량 주범 |
| 3 | 널(null) | 보이지 않는 부모 — 정상적인 리깅 도구 |
| 4 | 셰이프 | 벡터 — Lottie의 본체 |
| 5 | 텍스트 | 폰트 의존 — 아웃라인 변환 권장 |
건강한 UI 모션 파일은 대부분 ty 4(셰이프)와 3(널)로 구성됩니다. ty 2가 보이면 assets를 열어보세요.
assets — 용량 진단의 핵심
assets 항목에 "p": "data:image/png;base64,..."가 있다면 이미지가 파일 안에 통째로 들어 있는 것입니다. 이 한 줄이 수백 KB를 차지하는 경우가 흔하고, 제가 진단한 312KB짜리 파일도 원인의 6할이 여기 있었습니다. base64 문자열 길이 × 0.75가 대략의 원본 바이트 수입니다. 반대로 "u": "images/"처럼 경로 참조만 있다면 dotLottie로 묶을 때 별도 에셋으로 관리되는 구조입니다.
직접 열어볼 때의 순서
- 파일이
.lottie라면 확장자를.zip으로 바꿔 풀면 안에 JSON이 나옵니다. - 에디터에서 열어 최상위 필드로 규격·시간을 확인하고,
assets에서 base64를 검색합니다. - 레이어 이름(
nm)이 정리돼 있는지 보고, 색 교체가 필요하면 keypath 대상 이름을 정합니다. - 수치가 아닌 "실제로 어떻게 보이는지"는 뷰어에 올려 규격선·배경 전환으로 확인합니다 — JSON과 뷰어를 오가면 진단이 가장 빠릅니다.
실전 — 5분 용량 진단 순서
제가 에셋을 받으면 실제로 하는 순서입니다. ① 뷰어에 폴더째 올려 파일 크기순 정렬 → 튀는 파일을 찾고, ② 그 파일의 JSON을 열어 assets에서 base64를 검색하고, ③ 없다면 layers 길이와 패스 포인트 수를 의심합니다. base64가 있는 경우 아래 한 줄이면 이미지가 전체 용량에서 차지하는 비중이 바로 나옵니다.
> python3 - <<'EOF'
import json, sys
d = json.load(open("anim.json"))
img = sum(len(a.get("p","")) for a in d.get("assets",[]) if str(a.get("p","")).startswith("data:"))
total = len(open("anim.json").read())
print(f"base64 이미지 {img*0.75/1024:.0f}KB / 전체 {total/1024:.0f}KB ({img/total*100:.0f}%)")
EOF
이 비중이 절반을 넘으면 최적화의 답은 정해져 있습니다 — 벡터로 다시 그리거나, 최소한 이미지를 dotLottie 별도 에셋으로 빼는 것입니다. 수치로 확인하고 요청하면 디자이너와의 대화도 훨씬 빨라집니다.