[실전 성능 16] 계속 빠르게 유지하기

드디어 이 시리즈의 마지막 편이다. 지금까지 측정부터 시작해 핵심 지표, 이미지와 폰트, 렌더링과 번들, 캐시와 서버 렌더링, 봇과 측정 도구까지 두루 다뤘다. 그런데 이 모든 걸 한 번 적용했다고 끝이 아니었다. 성능은 방심하면 다시 나빠졌다. 이번 편은 성능을 계속 좋은 상태로 유지하는 루틴에 대한 이야기다.


1. 성능은 한 번으로 끝나지 않는다

내가 크게 착각했던 건 성능 최적화를 일회성 작업으로 여긴 것이었다. 한바탕 고쳐서 지표를 통과선 안에 넣고 나면 다 됐다고 생각했다. 그런데 몇 주 지나 다시 재보면 성능이 슬금슬금 나빠져 있었다. 새 기능이 추가되고, 코드가 늘고, 이미지가 쌓이면서 그동안의 개선이 조금씩 잠식됐다. 성능은 가만두면 나빠지는 것이었다.


이 퇴화는 눈에 잘 안 띄게 진행됐다. 한 번에 확 나빠지면 알아챘겠지만, 기능 하나 추가할 때마다 아주 조금씩 무거워지니 그때그때는 몰랐다. 그러다 어느 순간 다시 통과선 밖으로 나가 있었다. 각각은 사소한 변화였는데 쌓이니 큰 퇴보였다. 나는 이 조용한 퇴화를 막을 장치가 필요하다는 걸 깨달았다.


그래서 성능을 상태가 아니라 과정으로 보기 시작했다. 한 번 달성하는 목표가 아니라, 계속 유지해야 하는 상태로 여긴 것이다. 마치 정리된 방이 가만두면 어질러지듯, 성능도 지속적으로 관리하지 않으면 무너졌다. 최적화는 이벤트가 아니라 습관이어야 했다. 이 관점의 전환이 이번 편의 핵심이다.


이 관점을 받아들이니 접근이 달라졌다. 대대적으로 한 번 고치는 것보다, 나빠지지 않게 계속 지키는 쪽에 무게를 뒀다. 나빠진 걸 나중에 크게 고치는 것보다, 나빠지기 전에 조금씩 막는 게 훨씬 쉬웠다. 예방이 치료보다 값쌌다. 한참 방치해 여러 문제가 뒤엉킨 뒤에 손대면 원인을 가려내기도 힘들고 되돌릴 지점도 없었지만, 그때그때 막으면 원인이 명확해 작은 손질로 끝났다. 나는 성능을 지키는 일상의 절차를 만드는 데 집중했다.


2. 변화를 감시하는 습관

가장 먼저 만든 습관은 성능 지표를 정기적으로 확인하는 것이었다. 매번 대대적으로 재는 게 아니라, 핵심 지표 몇 개를 자주 힐끗 보는 식이었다. 앞서 지표를 세 개로 좁게 유지한 이유가 여기서 빛을 발했다. 적으니 자주 볼 수 있었고, 자주 보니 나빠지는 걸 일찍 알아챘다. 관찰의 빈도가 퇴화를 막는 첫 방어선이었다.


배포할 때마다 확인하는 절차도 뒀다. 새 코드를 올리기 전후로 주요 지표와 번들 크기를 비교해서, 이번 변경이 성능을 나쁘게 하지 않았는지 봤다. 갑자기 번들이 커졌으면 무엇이 들어왔는지 추적했다. 이 확인 하나로 성능을 해치는 변경이 조용히 스며드는 걸 막았다. 나빠지는 순간 바로 잡으니 쌓일 틈이 없었다.


감시는 자동화할수록 좋았다. 매번 손으로 재는 건 잊기 쉬우니, 가능한 부분은 절차에 녹여서 자동으로 확인되게 했다. 사람이 기억에 의존하면 반드시 빠뜨리는 때가 왔다. 절차가 대신 기억해주면 놓치지 않았다. 나는 반복되는 확인을 최대한 자동으로 돌아가게 만들어서, 내가 잊어도 시스템이 챙기게 했다.


감시의 목적은 조기 발견이었다. 문제가 작을 때 발견하면 고치기 쉬웠다. 크게 자란 뒤 발견하면 원인을 찾기도 어렵고 고치기도 힘들었다. 그래서 나는 자주, 일찍 보는 데 집중했다. 완벽한 측정을 가끔 하는 것보다 간단한 측정을 자주 하는 게 유지에는 나았다. 빈도가 정밀함보다 중요할 때가 있었다. 정밀한 측정은 준비가 번거로워 자꾸 미루게 되지만, 간단한 확인은 부담이 없어 매번 하게 되었고, 결국 놓치지 않고 자주 보는 것이 가끔 완벽하게 보는 것보다 퇴화를 잘 막았다.


3. 예산이라는 방어선

퇴화를 막는 강력한 장치는 예산이었다. 앞서 이미지 편에서 나온 개념인데, 이걸 성능 전반으로 넓혔다. 페이지가 쓸 수 있는 자원의 총량, 번들의 크기, 지표의 통과선 같은 것에 한도를 정해두고, 그 한도를 넘지 않도록 관리하는 것이다. 예산이 있으니 무심코 무거워지는 걸 막는 경계선이 생겼다.


예산의 좋은 점은 판단을 단순하게 만드는 것이었다. 새 기능이나 이미지를 추가할 때, 그것이 예산 안에 드는지만 보면 됐다. 예산을 넘으면 무언가를 덜어내거나 가볍게 만들어야 했다. 이 명확한 기준이 끝없이 무거워지려는 경향에 제동을 걸었다. 무제한이라 여기면 한없이 늘었지만, 한도가 있으니 절제가 강제됐다.


예산은 팀이나 미래의 나와 소통하는 언어이기도 했다. 성능을 지키자는 막연한 다짐은 쉽게 흐지부지됐지만, 구체적인 예산은 명확한 약속이 됐다. 이 한도를 넘기지 않는다는 합의가 있으면, 누가 작업하든 그 선을 지켰다. 나 혼자 만들 때도 과거의 내가 정한 예산이 현재의 나를 붙잡아줬다. 규칙이 의지를 대신했다. 의지는 바쁘거나 급할 때 가장 먼저 무너지지만, 명시적으로 적어둔 예산은 그럴 때도 그 자리에 남아 나를 멈춰 세웠으니, 나는 지켜야 할 것일수록 의지가 아니라 규칙에 맡겼다.


다만 예산은 현실적이어야 했다. 지나치게 빡빡하면 지키기 어려워 무시됐고, 너무 헐거우면 방어선 역할을 못 했다. 나는 실제 측정을 바탕으로 지킬 만하면서도 의미 있는 선에서 예산을 정했다. 그리고 상황이 바뀌면 예산도 재검토했다. 예산은 고정된 족쇄가 아니라 관리의 도구였다. 지키되 맹목적이지는 않았다.


4. 구조에 성능을 녹이기

가장 지속 가능한 유지법은 성능을 구조에 녹이는 것이었다. 개별 페이지마다 성능을 챙기면 어딘가는 반드시 빠뜨렸다. 대신 공통으로 쓰는 부분에 성능 규칙을 심어두면, 그걸 쓰는 모든 곳이 자동으로 그 혜택을 받았다. 이미지 자리 예약이나 캐시 규칙을 공통 부품에 넣어두니, 새 페이지도 알아서 성능을 지켰다.


이 방식은 실수의 여지를 줄였다. 사람이 매번 기억해서 적용하는 것에 의존하면 반드시 빠뜨리는 때가 왔다. 하지만 구조가 기본으로 성능을 챙기면, 특별히 노력하지 않아도 성능이 유지됐다. 잘하는 것을 쉽게 만들고 못하는 것을 어렵게 만드는 설계였다. 나는 성능을 지키는 게 기본값이 되도록 구조를 짰다.


구조에 녹인 성능은 사이트가 커져도 잘 버텼다. 기능이 늘고 사람이 바뀌어도, 구조가 지키는 성능은 흔들리지 않았다. 반면 개인의 주의력에 의존한 성능은 시간이 지나며 무너졌다. 나는 중요한 성능 원칙일수록 개인의 노력이 아니라 구조로 강제되게 했다. 오래 가는 성능은 습관보다 구조에서 나왔다.


물론 모든 걸 구조로 만들 수는 없었다. 새로운 상황은 늘 사람의 판단을 요구했다. 그래도 반복되는 성능 원칙을 구조로 굳혀두면, 사람은 정말 판단이 필요한 곳에 집중할 수 있었다. 뻔한 것은 구조가 챙기고, 사람은 새로운 문제에 힘을 쏟는 분업이었다. 이 분업이 지속 가능한 성능 관리의 뼈대였다.


5. 시리즈를 닫으며

이 시리즈를 관통한 하나의 원칙이 있다면 측정이었다. 첫 편에서 감이 아니라 숫자로 고치자고 했고, 마지막까지 그 원칙을 지켰다. 무엇을 최적화하든 재보고 판단했고, 고친 뒤 다시 재서 확인했다. 측정 없는 최적화는 어둠 속의 더듬거림이라는 첫 편의 말을 나는 시리즈 내내 실천했다.


또 하나의 원칙은 사용자였다. 모든 지표와 기법은 결국 사용자의 경험을 위한 것이었다. 나는 늘 가장 불리한 사용자, 느린 기기와 회선을 쓰는 사람을 기준으로 삼았다. 그들이 쾌적하면 나머지는 당연히 쾌적했다. 성능 최적화는 기술의 문제이자 배려의 문제였다. 숫자 뒤의 사람을 잊지 않으려 애썼다.


그리고 성능은 균형이었다. 로딩과 안정성과 반응성이 서로 당기고, 성능과 디자인과 편의가 부딪혔다. 어느 하나를 극단으로 밀지 않고 전체의 균형을 잡는 것이 좋은 성능이었다. 획일적인 정답 대신 상황에 맞는 판단을 택했다. 이 균형 감각은 재고 고치기를 반복하며 경험으로 자랐다. 지름길은 없었다.


이렇게 열여섯 편에 걸쳐 내가 커뮤니티 사이트를 만들며 성능과 씨름한 경험을 풀어냈다. 대단한 비법은 없었다. 재보고, 무거운 것부터 고치고, 나빠지지 않게 지키는 평범한 반복이 전부였다. 그 평범한 반복이 사이트를 빠르고 쾌적하게 만들었다. 이 시리즈가 비슷한 고민을 하는 누군가에게 작은 이정표가 되었으면 한다. 긴 여정을 함께해줘서 고맙다.