본문으로 건너뛰기

dotgimon 제작기 24. 원고 재작성과 디버그 비콘 정리

·7 min read·24 / 32

dotgimon은 Expo(React Native 앱 개발을 돕는 도구 모음)로 만드는 가상 펫 게임이다. 화면에 휴대용 게임기 본체를 그리고, 그 안의 작은 도트 액정(LCD)에서 펫을 키운다. 폰 앱이 있고, 같은 게임 엔진을 QuickJS(앱 안에 내장하는 작은 JavaScript 엔진)로 돌리는 애플워치·Wear OS(구글의 스마트워치 운영체제) 앱이 있다.

이날은 코드보다 글을 더 많이 고쳤다. 저장소의 작업 기록 파일(docs/dev-journal.md)에 빠진 날짜를 채우고, 날짜별 블로그 원고 23편을 코드와 diff(변경 전후 코드의 차이)를 근거로 다시 썼다. 코드 쪽에서는 성능을 재려고 넣어 두었던 디버그 비콘(앱 상태 통계를 주기적으로 밖으로 보내던 진단 코드)을 지웠다.

커밋 메시지를 요약한 원고에는 "왜"가 없었다

블로그 원고는 처음에 커밋 메시지를 요약해서 만들었다. 읽어 보니 "무엇을 했다"는 있는데 "왜, 어떻게"가 없었다.

커밋 메시지만 보고 쓰면 숫자도 흐려진다. "치료 애니메이션을 늘렸다"는 쓸 수 있지만, 펫에게 주사를 놓는 그 장면을 1,700ms에서 2,700ms로 늘렸다는 건 코드를 봐야 안다.

그날의 diff와 코드를 다시 읽고 고쳤다

원고마다 그날 커밋의 diff와 그 시점 코드를 다시 읽고 글을 고쳤다. diff에는 커밋 메시지에 없던 숫자와 이유가 있었다.

  • 숫자와 이름은 코드에서 가져왔다. 기억이나 커밋 메시지로 쓰지 않았다.
  • 작업 주제마다 문제, 고민, 구현, 결과 소제목을 두고 그 순서로 썼다. 쓸 내용이 없는 소제목은 뺐다.
  • 확인하지 못한 것은 확인하지 못했다고 썼다. 자동 테스트만 통과한 애니메이션에는 "실제 기기에서 재생해 보지는 않았다"고 적었다.

그러면서 10월 6일·7일 원고를 새로 썼다. 작업 기록 파일도 7월 16일, 10월 1·2·6·7일 항목이 비어 있어서 채웠다. 9월 30일 워치 스파이크(spike, 되는지 확인하려고 짧게 해 본 시험 구현) 항목은 내용을 보강했다.

원고 23편이 전부 코드 근거를 갖춘 자세한 글이 됐다. 이제 날짜별 원고 하나를 그대로 블로그 글 하나로 올릴 수 있다.

7월 말부터 9월 중순까지의 작업은 9월 30일 커밋에 한꺼번에 들어가 있다. 그래서 그 기간 원고는 작업 순서대로 날짜를 배치했고, 그 사실을 원고 목록(README)에 따로 적었다.

버튼 먹통을 쫓던 진단 코드가 아직 게임 루프에 있었다

9월 말에 "20시간 켜 둔 앱에서 버튼이 안 눌린다"는 문제를 추적하면서 게임 루프(매 프레임 상태를 갱신하고 화면을 다시 그리는 반복 처리)에 진단 코드를 넣었다. 프레임 수, 렌더 횟수, 프레임 간 최대 간격을 모아 3초마다 로컬 주소 127.0.0.1:9999로 POST 요청을 보내는 코드였다. 이 진단 코드를 디버그 비콘이라고 불렀다.

반대로 그 요청의 응답에 버튼 이름이 담겨 오면 앱이 그 버튼을 누른 것처럼 처리하게도 했다. 폰을 만지지 않고 밖에서 버튼을 누르려던 장치였다. 원인을 찾은 뒤에도 이 코드가 그대로 남아 있었다.

__DBG 표시를 달아 둔 덕에 지울 곳이 바로 보였다

진단 코드에는 처음부터 줄 끝에 // __DBG 주석을 표시로 달아 두었다. 그래서 grep(파일 내용에서 문자열을 찾는 명령) 한 번으로 지울 곳을 다 찾았다. 표시가 없었으면 어디까지가 진단 코드인지 다시 읽어야 했다. 게임 루프에서 지운 큰 덩어리는 // __DBG_BEACON_START와 // __DBG_BEACON_END 주석으로 앞뒤를 감싸 두었다.

// src/game/use-game/game-loop.ts (지운 부분)
// __DBG_BEACON_START
const dbg: any = ((globalThis as any).__dbg ??= { deviceRenders: 0, lcdRenders: 0, presses: [] })
let dbgFrames = 0, dbgRenders = 0, dbgSum = 0, dbgMax = 0, dbgMaxGap = 0, dbgPrev = performance.now(), dbgLastSend = Date.now()
// __DBG_BEACON_END

전역 객체(globalThis.__dbg)에 렌더 횟수와 입력 기록을 모으고, 프레임 통계와 마지막 전송 시각을 변수로 들고 있던 부분이다. 게임 루프, 액정 화면 컴포넌트(lcd.tsx), 기기 본체 컴포넌트(device.tsx), 게임 상태 훅(use-game.ts), 버튼 처리(press-actions.ts)까지 다섯 파일에서 28줄을 지웠다.

응답으로 온 버튼을 처리하려고 게임 루프에 넘기던 pressRef(버튼 처리 함수를 담아 둔 useRef)도 같이 빠졌다. 출시 빌드에 로컬 주소로 통계를 보내는 코드가 남아 있으면 안 되니 이날 정리했다.