떠도는 섬에 공격 동작과 칼 궤적을 넣어 배포했습니다. 그리고 그 배포본에 크래시가 하나 들어갔습니다.
하나는 렌더 루프 안에서 터졌습니다
영웅이 맞을 때 몸을 잠깐 붉게 빛나게 하려고, 영웅에 붙은 모든 재질을 한 목록에 담아 두고 그 목록을 돌면서 발광색을 바꿨습니다. 한 줄로 끝나는 깔끔한 코드였습니다.
그날 칼 궤적을 새로 넣으면서 궤적용 재질을 그 목록에 같이 담았습니다. 그런데 궤적은 빛 계산을 하지 않는 종류라 발광색이라는 속성 자체가 없습니다. 기사가 처음 맞는 순간 그 줄에서 예외가 났고, 그 코드는 렌더 루프 안에 있었습니다.
프레임이 멈추고 게임이 굳습니다. 전투가 자동으로 도는 게임이라 플레이어가 할 수 있는 일도 없습니다. 0.1.1 배포본에 그대로 나갔습니다.
고친 방식은 목록을 나누는 것이었습니다.
- 버릴 재질 목록 — 화면을 정리할 때 해제해야 하는 전부
- 빛낼 수 있는 재질 목록 — 발광색을 가진 것만
같은 재질이 둘 다에 들어가기도 하지만, 목적이 다르면 목록도 달라야 합니다. 하나의 목록에 종류가 다른 것을 담아 두고 "전부에게 같은 일"을 시키면, 언젠가 그중 하나가 그 일을 할 수 없게 됩니다.
브라우저에서만 나는 일이라 검사로는 못 잡습니다. 그래서 소스를 검사하게 했습니다 — 버릴 재질 목록에 발광색을 쓰는 코드가 있으면 셀프테스트가 실패합니다. 그리고 기사가 398번 맞는 동안 예외 0건을 확인했습니다.
다른 하나는 아무 소리도 내지 않았습니다
같은 날, 허브에 올릴 이미지를 렌더하다가 화면에 하늘 그라디언트 한 장만 남는 것을 봤습니다. 섬도 영웅도 없습니다.
원인은 시야각이었습니다. 창이 아직 그려지기 전이거나 패널이 숨겨진 상태에서는 캔버스 너비와 창 너비가 둘 다 0이 됩니다. 그 값으로 시야각을 계산하니 숫자가 아닌 값이 나왔습니다.
여기서 중요한 건 three.js가 오류를 내지 않는다는 점입니다. 예외도 경고도 없이, 그냥 아무것도 그리지 않습니다. 크래시는 최소한 자기가 났다고 알려 주는데, 이쪽은 알려 주지 않습니다. 화면이 비어 있는 것을 사람이 눈으로 보기 전까지는 아무 일도 없는 것처럼 보입니다.
너비를 구할 때 마지막 기본값을 둬서 0이 나오지 않게 했습니다.
두 오류의 수명이 달랐습니다
| 렌더 루프 크래시 | 시야각이 숫자가 아님 | |
|---|---|---|
| 알려 주나 | 예외를 던진다 | 아무 말도 안 한다 |
| 화면 | 그 자리에서 굳는다 | 하늘만 남는다 |
| 발견 | 배포 뒤 실행하자마자 | 이미지를 렌더하다 우연히 |
| 막는 법 | 소스 검사(목록 오용 금지) | 기본값으로 0을 못 만들게 |
크래시는 시끄러워서 금방 잡힙니다. 조용한 실패가 더 오래 삽니다. 그래서 "예외가 없었다"를 검증의 결론으로 삼으면 안 됩니다 — 이 프로젝트에서 그 두 가지를 하루에 같이 만난 것이 그 이야기의 증거입니다.
같은 결의 이야기를 섬 탐험 쪽에서도 적었습니다. 거기서는 창이 숨겨져 애니메이션 프레임이 멈춘 것이 "기능이 원래 없는 것"과 똑같이 보였습니다. 화면은 무엇이 잘못됐는지 잘 말해 주지 않습니다.