트래픽 비용은 비싸다 - JSON key 길이 줄이는 건 도움이 된다
트래픽 비용은 생각보다 훨씬 비싸다.
이 사이트는 원래 친구들이랑 같이 놀려고 만든 거였다. 처음부터 많은 사람이 들어오는 서비스를 상정하고 설계한게 아니었다. 데이터 전송 구조도 사람 수가 늘수록 꽤 가파르게 비용이 커지는 형태였지만, 애초에 3~4명, 많아야 10명 정도가 플레이할 거라고 생각했기 때문에 그 정도면 충분히 합리적인 선택이었다.
실제로도 그렇게 쓸 줄 알았다. 친구들이랑 잠깐 플레이하고, 나머지 시간에는 서버가 조용히 놀고 있는, 딱 그런 취미 프로젝트였다.
그런데 예상은 아주 빠르게 빗나갔다.
친구들이 하는 시간 외에 서버가 놀고 있으니, 혹시 하고 싶은 사람이 있으면 해보라고 몇 군데 사이트에 내가 만든 게임 주소를 올렸는데, 며칠 뒤 동시접속자 수가 1000명을 넘겼다. 하필 그날은 하루종일 미팅이 있는 날이어서 밖에 있었다. 오전에 동접이 1000명을 넘었다는 알람이 왔을 때까지만 해도 그냥 잠깐 생긴 해프닝이라고 생각했는데, 어쩌다 한 번 반짝하는 정도겠지 싶었다.
그런데 오후가 되니까 숫자가 두 배가 되어 있었다.
그때부터 서버가 느려지기 시작했고, 트래픽 비용도 같이 치솟았다. 원래 취미용으로 쓰던 서버에 올려둔 상태였는데, 급하게 더 큰 서버로 바꿔야 했다. 서버 인스턴스 비용만 해도 내 한달 취미비용을 넘었고, 트래픽 비용은 더 비쌌다. 광고 하나 없는 사이트에서 감당하기 힘들 정도로... 최대한 효율적으로 바꾸지 않으면 계속 유지하는 것이 불가능할 것 같았다.
지금은 ECS 위에 여러 마이크로서비스로 쪼개서 배포하고 있다. 각각은 스케줄 기반 오토스케일링이 걸려 있다. 그런데 이것도 막상 운영해보면 생각보다 까다롭다. 내 사이트 트래픽은 예쁘게 서서히 올라가지 않는다. 어느 순간 갑자기 피크를 친다. 그래서 오토스케일링만 믿고 있으면 이미 늦는 경우가 생긴다. 결국 어느 정도는 버퍼를 넉넉하게 띄워놔야 한다.
구조를 바꾸고, 서버를 키우고, 오토스케일링도 붙였지만 문제는 거기서 끝나지 않았다. 매일같이 수만 명이 만들어내는 게임 데이터 트래픽은 생각보다 훨씬 많았다. 그리고 그 트래픽은 생각보다 훨씬 비쌌다.
예전에 나는 실시간 금융 데이터를 처리하는 일을 했었다. websocket이나 FIX로 수십 수백 마이크로초 단위의 데이터를 다루던 일이었다. 그때는 클라이언트 사이드에서 초고속 데이터 스트림을 받아 처리하는 쪽이었다. 이번에는 완전히 반대다. 이번에는 서버사이드에서 데이터를 보내는 쪽이 되었다.
예전에 그 일을 할 때는 데이터 포맷이 굉장히 공격적으로 최적화되어 있었다. key 이름은 최대한 짧았고, 한 글자만 쓰는 경우도 흔했다. 심지어 t와 T를 서로 다른 의미의 key로 쓰기도 했다. 당시에는 솔직히 왜 이렇게까지 하나 싶었다. 읽기도 불편하고, 처음 보는 사람 입장에서는 헷갈리기만 하는 것처럼 보였다.
그런데 이번에 직접 트래픽 비용을 내는 쪽이 되어보니 그 이유를 아주 빨리 이해하게 됐다.
커뮤니티나 LLM에 이런 걸 물어보면 대체로 '이른 최적화'라고 답한다. 물론 많은 경우 그 말이 맞다. 아직 병목이 확인되지도 않았는데 미리 복잡한 최적화를 하는 건 보통 좋은 선택이 아니다. 그런데 JSON key 길이를 줄이는 건 내가 운영하는 종류의 서비스에서는 조금 다른 이야기였다. 이건 리스크가 거의 없고, 효과는 꽤 분명했다.
개발을 하다 보면 '겨우 5%'라는 말을 쉽게 한다. 5% 줄여서 뭐 하냐는 식으로 넘기기 쉽다. 그런데 제조업에서 제조원가 5%를 줄인다고 생각해보면 그건 전혀 작은 숫자가 아니다. 서비스 운영도 마찬가지다. 특히 트래픽처럼 매일 반복해서 쌓이는 비용에서는 더 그렇다.
그리고 실제로 해보니 결과는 생각보다 더 컸다. key 값을 줄이는 것만으로 트래픽 양이 20% 이상 줄었다. 처음엔 나도 이 정도까지 체감될 줄은 몰랐다.
물론 trade-off가 없는 건 아니다. 사람이 읽을 때 덜 직관적일 수 있고, 디버깅할 때 헷갈렸던 순간도 있었다. timestamp 같은 이름보다 t가 불친절한 건 사실이다. 하지만 실제 운영 비용이 눈에 띄게 줄어드는 걸 보면, 적어도 내 상황에서는 충분히 감수할 만한 불편이었다.
재미있는 건 예전에 하던 최적화와 지금 하는 최적화가 거의 정반대라는 점이다. 예전에는 10마이크로초를 줄이기 위해 더 많은 비용과 비효율을 감수했다. 하이퍼스레딩을 끄고 코어를 절반만 쓴다던가, 캐시라인을 맞추려고 데이터 주변에 패딩을 붙이고, 그런 식의 작업을 했다. 그때는 속도가 가장 중요한 문제였기 때문이다.
지금은 반대로 생각한다. 마음 같아서는 같이 플레이하는 유저가 다른 유저의 상태를 즉시 받았으면 좋겠다. 하지만 실제 운영에서는 '얼마 정도까지 늦춰도 유저가 크게 불편함을 느끼지 않을까'를 실험하게 된다. 무조건 빠른 것이 답도 아니고, 무조건 보기 좋은 것이 답도 아니다. 어떤 상황에서는 사람이 읽기 편한 구조보다 네트워크 비용이 더 중요한 문제가 된다.
결국 silver bullet은 없고 상황에 따라 적절한 방법이 있을 뿐이다. 예전에는 10마이크로초를 줄이기 위해 돈을 더 쓰는 게 맞았고, 지금은 몇 바이트를 줄이기 위해 가독성을 조금 포기하는 게 맞다. 나는 이런게 개발의 묘미라고 생각한다.
전체 글