개발기

"어떨 땐 빠르고 어떨 땐 느리다"를 숫자로 가르기

사이트가 어떨 땐 빠르고 어떨 땐 느렸다. 같은 페이지를 열어도 그랬다.

이런 증상은 원인 후보가 너무 많다. 서버가 불안정한 건지, 회선이 문제인지, 캐시가 어떤 상태인지, 아니면 그냥 기분 탓인지 구분이 안 된다. 체감을 숫자로 바꾸는 것부터 시작했다.

재는 장치

브라우저 개발자 도구를 손으로 열어 재면 매번 조건이 달라진다. 캐시를 지웠는지, 네트워크 탭을 언제 열었는지, 다른 탭이 뭘 받고 있었는지가 다 섞인다.

headless Chrome을 CDP로 붙잡고 조건을 고정했다.

await send('Network.setCacheDisabled', { cacheDisabled: true })
await send('Network.emulateNetworkConditions', {
  offline: false,
  latency: 40,
  downloadThroughput: (10 * 1024 * 1024) / 8,  // 10Mbps
  uploadThroughput: 262144,
})
await send('Page.navigate', { url })

이러면 캐시 상태와 회선 속도를 내가 정한다. 매 실행이 같은 조건에서 시작한다.

첫 번째 질문: 서버가 흔들리는가

조건을 완전히 고정하고 네 번 반복했다.

회차 전체 로드
1 2,659ms
2 2,641ms
3 2,652ms
4 2,648ms

편차가 1%도 안 됐다. 서버는 흔들리지 않았다.

두 번째 질문: 그럼 뭐가 흔들렸나

조건만 바꿔 가며 다시 쟀다.

조건 전체 로드
캐시 있음 104ms
10Mbps · 캐시 없음 2,650ms
4Mbps · 캐시 없음 6,214ms

60배 차이가 났다. "어떨 땐 빠르고 어떨 땐 느리다"의 정체가 이것이었다. 서버가 아니라 보는 사람의 캐시 상태와 회선이 전부였다.

여기까지 오니 할 일이 분명해졌다. 서버를 손볼 게 아니라 받는 양을 줄여야 했다.

내 측정이 거짓말을 한 두 번

줄이는 작업을 하고 "개선 후" 수치를 냈다. 그런데 두 번이나 틀린 숫자를 냈다.

로컬에서 잰 것은 아무것도 증명하지 않는다

고친 다음 로컬에서 재서 빨라졌다고 적었다. 돌아온 지적은 이랬다.

로컬 렌더링 속도는 빨라서 별 차이 없는데 말이지

맞는 말이었다. 로컬은 회선 제한이 없다. 내가 줄인 것은 전송량인데, 전송 시간이 거의 0인 환경에서 재면 줄인 효과가 안 나타난다. 빨라 보이는 게 아니라 원래부터 빨랐던 것이다.

유효했던 건 바이트 수뿐이었다. 시간은 같은 스로틀링을 걸어 다시 쟀다.

순서가 뒤집히면 측정이 깨진 것이다

다시 쟀더니 이런 표가 나왔다.

회선 전체 로드
4Mbps 4,280ms
10Mbps 8,805ms
30Mbps 7,901ms

빠른 회선이 더 느렸다. 이건 "의외의 결과"가 아니라 측정이 고장 났다는 신호다. 느린 회선이 빠를 수는 없다.

원인은 측정하는 기기에 있었다. 그 맥북이 도메인을 IPv6로 해석하는데 그 경로가 불안정했다. curl에 IPv4를 직접 지정하면 멀쩡히 붙었다.

같은 배포를 다른 주소로 재니 정상이었다.

회선 고장난 경로 정상 경로
4Mbps 4,280ms 2,312ms
10Mbps 8,805ms 625ms

남은 것

성능 측정에서 배운 게 둘이다.

하나. 변동이 큰 증상은 원인을 좁히기 전에 조건을 하나씩 고정해야 한다. 같은 조건 4회가 1% 안에 들어온 순간 서버는 용의선상에서 빠졌다.

둘. 측정 결과가 상식과 어긋나면 결과를 설명하려 들기 전에 측정을 의심해야 한다. 10Mbps가 4Mbps보다 느린 표를 보고 "엣지 라우팅 때문인가" 하고 넘어갔다면 엉뚱한 곳을 고치고 있었을 것이다.