행인로 개인 작업실

격자가 스스로를 다시 짓고 있었다

매일 쓰는 이미지 브라우저가 있다. 빠르게 스크롤하면 빈 칸이 번쩍인다. 뻔한 용의자는 전부 이미지 쪽이다 — 지연 로딩이 충분히 일찍 걸리지 않거나, 디코딩이 칸을 그린 뒤에 도착하거나, 화면 바깥에 미리 그려두는 양이 모자라거나. 가설 셋을 적어두고(격자 재생성, 선행 렌더 부족, 지연 로딩·디코딩 지연) 한 번에 하나씩만 바꾸기로 했다. 끝에 가서 답이 읽혔던 이유는 그것 하나다.

이미지 쪽은 전부 아니었다. 목록이 늘어날 때마다 격자가 통째로 부서지고 처음부터 다시 지어지고 있었다.

주석이 코드와 정반대를 말하고 있었다

벽돌식 배치 컴포넌트가 데이터 묶음에서 계산한 값을 키로 쓰고 있었는데, 그 값에 마지막 로드 표식과 목록 길이가 둘 다 들어 있었다. 무한 스크롤은 한 번에 60개를 덧붙인다. 덧붙일 때마다 키의 두 부분이 모두 바뀌었고, 컴포넌트는 통째로 내려갔다 다시 올라왔다. 바로 그 줄 위에 붙은 주석은 이렇게 말하고 있었다 — 목록이 길어진 것만으로는 재배치가 일어나면 안 된다. 그 키는 필터 변경을 감지하려고 쓴 것이었고, 무한 스크롤은 필터 변경과 정확히 같은 조건을 만족시킨다.

312초 동안 약 4,300개 자산·18,742프레임에 계측을 붙여 재보니 상관관계는 조금도 미묘하지 않았다. 페이지 덧붙임 71회에 재생성 71회, 정확히 일대일. 기록된 빈 칸 304건 중 72.9%가 재생성 후 0.3초 안에 발생했고 중앙값은 146밀리초, 1초 안으로 넓히면 89.6%였다.

키를 갈라서 목록의 길이가 아니라 정체성에 반응하게 만드는 데 아홉 줄이 들었다. 같은 방식으로 다시 재보니 페이지 로드 40회 동안 재생성 0회, API 호출당 빈 칸은 4.28에서 1.35로 68% 줄었다. 나머지는 아무것도 건드리지 않았다 — 선행 렌더도, 지연 로딩도, 썸네일도, 무한 스크롤도 그대로.

측정 환경이 하루를 통째로 날릴 뻔했다

남의 글에서 읽고 싶은 부분이 바로 이 대목이다. 내 처음 몇 번의 측정은 완전히 인위적인 환경에서 이뤄졌고, 그 숫자들은 가설이 틀렸다고 말했다.

탭이 백그라운드로 내려가면 브라우저는 지연 이미지의 우선순위를 낮추는 정도가 아니라 아예 시작하지 않는다. 그래서 이미지 주소 속성이 빈 채로 남는다. 가상 스크롤 라이브러리의 스크롤 추적이 멈추니, 낡은 숫자로 보이는 영역을 계산한다. 렌더 콜백이 아예 불리지 않으니 무한 스크롤이 완전히 정지한다. 하나하나는 전부 합리적인 동작이다. 합쳐지면 격자가 0번 재생성되고 완벽하게 건강해 보이는 기록이 나온다.

그 0은 가설을 반박하는 증거가 아니었다. 목록이 한 번도 늘어나지 않아서 방아쇠가 당겨진 적이 없다는 증거였다. 진짜 숫자는 탭을 앞으로 꺼내 사람이 직접 스크롤하고 나서야 나왔다. 아무도 보고 있지 않은 페이지를 재는 자동 장치는, 다른 페이지를 재고 있는 것이다.

닿자마자 녹아버린 곁가지

큰 자산일수록 더 많이 빈다는 게 내 인상이었다. 아니었다. 파일 크기와 그려지기까지의 시간 사이 상관은 −0.012였다. 서버가 썸네일을 전부 같은 폭으로 맞추고, 그래서 전부 비슷한 수십 킬로바이트에 떨어지기 때문이다. 실제로 참인 것은 세로로 긴 칸은 비어 있는 동안 더 큰 구멍을 남긴다는 것이다. 같은 빈 칸이 더 나쁘게 느껴진다. 최적화하기 전에 확인할 것 — 체감 심각도와 측정된 빈도는 서로 다른 축이다.

그리고 같은 날 넣었다가 지운 기능 하나

별개로, 같은 앱에 수집 대기열의 대기 건수를 보여주는 작은 읽기 전용 패널을 붙였다. 값이 이미 기존 상태 응답에 실려 오고 있어서 쌌다 — 새 요청도, 백엔드 변경도, 쓰기 경로에 손대는 일도 없었다.

그리고 그게 무엇을 보여주는지 봤다. 대기 0, 중복 0, 실패 0. 누적 이벤트는 6만 건이 넘지만 항목이 도착하는 즉시 처리되니 대기열에 쌓이는 일이 없다. 그 패널이 보여줄 수 있는 건 최근 활동 목록뿐인데, 그건 대기열 상태와 다른 물건이고, 질문을 먼저 했다면 만들지 않았을 물건이다. 붙인 지 한 시간 만에 되돌렸다.

통과시킨 논리는 “읽기 전용이니까 공짜다”였다. 사실이었고, 상관없는 사실이었다. 공짜라는 건 비용에 대한 말이지 값어치에 대한 말이 아닌데, 나는 앞쪽만 확인했다.