Lottie가 안 움직일 때 — 단계별 디버깅 체크리스트
Lottie 문제의 디버깅은 결국 하나의 질문으로 수렴합니다 — "파일 문제인가, 런타임 문제인가, 내 코드 문제인가?" 이 순서대로 좁혀 가면 대부분 30분 안에 원인을 찾습니다.
1단계 — 파일 자체를 격리 검증
가장 먼저 앱·코드에서 분리해 파일만 검증합니다. 문제의 파일을 Lottie Inspector에 넣어 보세요.
- 여기서도 안 나온다 → 파일 문제입니다. 2단계로.
- 여기서는 잘 나온다 → 파일은 정상. 3단계(런타임/코드)로.
2단계 — 파일이 문제일 때
JSON이 유효한가
의외로 흔합니다. 이메일·메신저를 거치며 파일이 잘리거나, 텍스트 에디터가 인코딩을 바꿔 버린 경우입니다. JSON 파서에 넣어 문법 오류부터 확인하세요.
Lottie 데이터가 맞는가
Lottie JSON은 최상위에 v(버전), fr(프레임레이트), ip/op(시작/끝 프레임), layers 키를 가집니다. 이 구조가 없다면 Lottie 파일이 아닙니다.
미지원 기능을 쓰지 않았는가
특정 레이어만 안 보인다면 십중팔구 지원 경계 문제입니다 — 이펙트, 3D, 루마 매트, 표현식. 내보내기 체크리스트의 표를 참고해 해당 레이어를 다른 방식으로 다시 만들어야 합니다.
3단계 — 런타임/코드가 문제일 때
웹 (lottie-web)
// 로드 실패는 조용히 지나가기 쉽습니다 — 이벤트를 걸어 두세요
anim.addEventListener("data_failed", () => console.error("lottie: load 실패"));
anim.addEventListener("error", (e) => console.error("lottie:", e));
- 컨테이너 크기 0 — 부모가
display:none이거나 높이가 0이면 렌더는 되지만 보이지 않습니다. - CORS — 다른 도메인에서 JSON을 불러올 때 서버에 CORS 헤더가 없으면 로드가 실패합니다.
- 경로 문제 — SPA 라우팅 아래에서 상대 경로가 어긋나는 고전적 실수.
iOS (lottie-ios)
- 번들 타겟 누락 — 파일이 타겟 멤버십에 포함되지 않으면
.named()가 조용히 nil을 반환합니다. - 렌더링 엔진 폴백 — 콘솔에 Core Animation 엔진 관련 경고가 있는지 확인하세요.
Android (lottie-android)
- assets 경로 —
src/main/assets/바깥에 두고 파일명만 넘기는 실수가 흔합니다. - 하드웨어 가속 — 특정 블렌드 모드가 HW 레이어에서 깨지면
RenderMode.SOFTWARE로 비교 테스트.
4단계 — "특정 기기에서만" 문제
- 저사양 기기 프레임 드랍 — 파일이 무거운 것입니다. 레이어 수와 용량을 줄이는 것이 근본 해결. (최적화 글 참고)
- 구형 OS/브라우저 — 런타임 버전을 확인하세요. 최신 스펙(dotLottie 등)은 구버전 런타임이 못 읽습니다.
- 다크모드에서만 이상 — 파일 문제가 아니라 배경과 색이 겹치는 것일 수 있습니다. 배경 전환으로 확인해 보세요.
실전 사례: 30분 디버깅 로그
위 체크리스트를 실제로 어떻게 굴리는지, 제가 최근에 겪은 사례 하나를 시간 순서대로 따라가 보겠습니다. 제가 만드는 CGM 앱의 기록 화면에는 데이터 동기화 중임을 표시하는 작은 로딩 Lottie가 셀마다 들어가는데, QA에서 "스크롤을 내렸다 올리면 일부 셀의 애니메이션만 멈춰 있다"는 리포트가 올라왔습니다. 전부 멈추는 것도 아니고 특정 셀만, 그것도 스크롤 후에만 멈추니 처음 보면 꽤 당황스러운 증상입니다.
0~5분: 1단계, 파일 격리
먼저 해당 JSON을 앱에서 꺼내 뷰어에 넣어 봤습니다. 잘 돕니다. 파일 문제가 아니라는 뜻이므로 2단계는 통째로 건너뛰고 바로 3단계로 갑니다. 여기에 5분을 써서 "파일은 정상"이라는 확신을 얻은 것이 이후 25분을 절약해 줬습니다. 이 확신이 없으면 코드를 보다가도 자꾸 파일을 의심하게 됩니다.
5~20분: 3단계, 코드 추적
번들 타겟 누락이라면 처음부터 아예 안 나와야 하는데, 이 건은 처음엔 나오다가 멈추는 것이므로 로드 문제는 아닙니다. 재현 조건이 "스크롤 후"라는 점에서 셀 재사용을 의심했습니다. lottie-ios의 LottieAnimationView는 뷰가 윈도우에서 떨어져 나가면 — 즉 셀이 화면 밖으로 나가면 — 재생을 멈추는데, 그 셀이 재사용되어 화면에 돌아올 때 play()를 다시 호출하지 않으면 마지막 프레임에 멈춘 채로 보입니다. 제 코드는 cellForItemAt에서 셀을 만들 때 한 번만 play()를 부르고 있었습니다. 재사용된 셀은 이 경로를 다시 타지 않으니 정확히 리포트된 증상이 됩니다.
// 수정: 셀이 화면에 나타날 때마다 재생을 보장
func collectionView(_ cv: UICollectionView,
willDisplay cell: UICollectionViewCell,
forItemAt indexPath: IndexPath) {
(cell as? SyncingCell)?.animationView.play()
}
20~30분: 4단계, 기기 확인
수정 후에는 보급형 테스트 기기와 최신 기기에서 각각 스크롤을 오래 반복하며 멈춘 셀이 다시 생기는지, 프레임 드랍이 새로 생기지는 않는지 확인했습니다. 셀마다 play()를 다시 부르는 비용이 걱정됐지만, 제 프로젝트 기준으로는 체감되는 차이가 없었습니다. 다만 앱을 백그라운드에 보냈다 돌아올 때도 같은 증상이 나면 그건 셀 재사용이 아니라 backgroundBehavior 설정 쪽을 봐야 하는 별개 문제입니다.
돌이켜 보면 이 건이 30분 안에 끝난 이유는 순서를 지켰기 때문입니다. 파일 탓인지 코드 탓인지 모른 채 코드부터 뒤졌다면 애먼 렌더링 엔진 설정을 바꿔 보며 한나절을 썼을 겁니다. 증상이 아무리 이상해 보여도 1단계 격리 검증부터 시작하는 습관이 결국 가장 빠릅니다.