Moove의 성능을 59% 개선한 방법
Moove 사용자 대부분은 서버와 멀리 떨어져 있습니다. 서버를 옮기지 않고 첫 응답까지의 시간을 59% 줄인 방법과, 예상보다 어려웠던 부분을 소개합니다.거리가 가장 느린 의존성이었습니다
Moove app을 여는 사람 대부분은 나이지리아와 인도네시아의 모바일 네트워크에 있으며, 자신의 요청에 응답하는 서버와는 수천 킬로미터 떨어져 있습니다. 이번 달 우리는 그들의 요청이 우리에게 도달하는 방식을 바꾸었고, 휴대폰이 요청을 보낸 시점부터 응답의 첫 조각이 도착하기까지의 시간인 **첫 바이트 시간(TTFB)**이 0.74초에서 0.31초로 59% 줄었습니다. 이를 위해 서버를 단 한 대도 옮기지 않았습니다.
이 글에서는 거리가 예상보다 더 큰 비용을 치르게 한 이유, 우리가 바꾼 것, 변경 자체보다 더 어려웠던 부분, 그리고 같은 일을 하려는 다른 팀에게 해 주고 싶은 조언을 설명합니다.
한 번의 긴 여정이 세 번이 되는 이유
엔지니어들은 무언가를 보낸 시점부터 그것이 도착하기까지의 지연을 **지연 시간(latency)**이라 부르고, 메시지가 서버에 닿고 응답이 돌아오기까지 걸리는 시간을 **왕복 시간(RTT)**이라 부릅니다. 라고스에서 우리 서버까지의 RTT는 200~300밀리초입니다. 이 정도만으로는 견딜 만합니다. 문제는 요청이 홀로 이동하는 경우가 드물다는 것입니다.
휴대폰이 보안 연결로 무언가를 요청하려면 먼저 두 번의 핸드셰이크(handshake), 즉 연결을 설정하는 짧은 교환을 마쳐야 합니다. TCP 핸드셰이크가 연결을 열고, 이어서 TLS 핸드셰이크가 연결을 비공개로 유지하는 암호화 키에 합의합니다. 각각 한 번의 전체 왕복이 듭니다. 그런 다음에야 요청 자체가 전송되며, 이것이 세 번째 왕복입니다. 한 번에 250밀리초라면, 우리 서버가 아무 작업도 하기 전에 사용자는 약 4분의 3초를 기다리게 됩니다.
모바일 네트워크에서는 이 비용이 바라는 것보다 훨씬 자주 발생합니다. 휴대폰은 기지국 사이를 이동하고, 배터리를 아끼려고 무선 장치가 절전 상태에 들어가며, 앱은 백그라운드로 밀려납니다. 이런 일이 생길 때마다 연결이 끊길 수 있고, 다음 요청은 콜드 커넥션(cold connection), 즉 아직 핸드셰이크가 하나도 끝나지 않은 연결에서 시작해 전체 설정 비용을 다시 치러야 합니다. 이미 열려 있고 보안이 설정된 **웜 커넥션(warm connection)**은 곧바로 요청으로 넘어갑니다.
라고스를 우리 서버 가까이로 옮길 수는 없었습니다. 하지만 연결 설정을 라고스 가까이로 옮길 수는 있었습니다.
서버가 아니라 핸드셰이크를 옮기다
**콘텐츠 전송 네트워크(CDN)**는 보통 캐시로 설명됩니다. 전 세계 곳곳에 파일의 사본을 보관해 가까운 곳에서 제공하는 위치들의 집합입니다. 우리는 이것을 다른 이유로 사용했습니다. 앱이 모든 동작을 위해 통신하는 인터페이스인 우리의 API에서는 캐싱을 완전히 꺼 두었습니다. 모든 요청은 여전히 오리진(origin), 즉 CDN이 그 뒤의 서버들을 부르는 이름까지 이동합니다.
달라지는 것은 연결이 시작되는 위치입니다. 이제 휴대폰은 가장 가까운 엣지 로케이션(edge location), 다른 이름으로 **접속 지점(PoP)**에 연결을 엽니다. 이는 전 세계에 흩어진 CDN의 거점 중 하나로, 수백 밀리초가 아니라 수십 밀리초 거리에 있는 경우가 많습니다. 엣지는 **TLS 종료(TLS termination)**를 수행하는데, 휴대폰에서 오는 보안 연결이 그곳에서 끝난다는 뜻입니다. 그런 다음 엣지는 우리 오리진으로 향하는 웜 커넥션 풀을 재사용하며, 이 연결들은 요청 사이에 연결을 닫지 않고 열어 두는 설정인 keep-alive로 유지됩니다. 비용이 큰 두 번의 설정 왕복은 짧은 구간에서 이루어지고, 요청 자체만 이미 따뜻하게 유지된 연결을 타고 바다를 건넙니다.
세 번의 긴 여정이 한 번의 긴 여정과 두 번의 짧은 여정이 됩니다. 이것이 전부입니다.
나머지는 의도적으로 평범하게 유지했습니다.
- 클라이언트는 아무것도 눈치채지 못했습니다. 앱과 웹사이트는 같은 주소를 유지했으므로 앱 릴리스가 필요 없었고 누구도 아무것도 업데이트할 필요가 없었습니다.
- 실패 동작은 그대로입니다. 타임아웃은 이전과 같아서, 전에 실패하던 것은 같은 방식으로 실패합니다. 새로운 종류의 오류는 원하지 않았습니다.
- 유휴 연결은 엣지가 먼저 닫습니다. 모든 연결에는 유휴 타임아웃(idle timeout), 즉 사용되지 않은 채 닫히기 전까지 둘 수 있는 시간이 있습니다. 엣지는 아직 열려 있다고 생각하는데 우리 서버가 조용한 연결을 닫아 버리면 다음 요청이 실패합니다. 엣지의 타임아웃을 더 짧게 설정하면 항상 엣지가 먼저 연결을 포기하고 새 연결을 엽니다.
- 휴대폰을 위한 최신 프로토콜. 휴대폰은 웹 요청 프로토콜의 최신 버전인 HTTP/3로 연결할 수 있습니다. HTTP/3는 QUIC 위에서 동작하며, QUIC은 두 번의 핸드셰이크를 한 번의 왕복으로 합치고 모바일 네트워크에서 흔한 패킷 손실에도 더 잘 대응합니다.
- 빠진 지역이 없습니다. 엣지 네트워크의 저렴한 요금제는 우리 사용자가 사는 바로 그 지역의 거점을 빼놓는 경우가 많아서, 전체 커버리지를 선택했습니다.
측정한 결과
우리는 시드니에서 측정했습니다. 시드니는 우리 서버에서 라고스만큼 멀리 떨어져 있으며, 기존 경로와 새 경로를 비교했습니다. 각 경로마다 모바일 네트워크에서 가장 중요한 경우인 새 콜드 커넥션을 다섯 번 열고 중앙값(median), 다른 표기로 p50을 취했습니다. 중앙값은 중간 결과로, 절반의 요청은 이보다 빠르고 절반은 느립니다. 같은 프로젝트에서 우리 웹사이트 스크립트에 **압축(compression)**도 켰는데, 그동안 스크립트는 원래 크기 그대로 전송되고 있었습니다. 압축은 파일을 보내기 전에 줄이고 브라우저가 도착한 뒤 다시 펼치는 방식이며, 우리는 웹을 위해 만들어진 압축 형식인 Brotli를 사용했습니다.
| 측정 항목 | 이전 | 이후 | 개선율 |
|---|---|---|---|
| TCP 및 TLS 핸드셰이크 완료 (p50) | 490 ms | 59 ms | 88% 빨라짐 |
| 첫 바이트 시간 (p50) | 743 ms | 307 ms | 59% 빨라짐 |
| 홈페이지 스크립트 용량 | 1.76 MB | 560 KB | 68% 줄어듦 |
59%는 첫 바이트 시간에서 나온 수치입니다. 설정에 반 초가 걸리던 연결이 이제 0.1초도 안 되어 준비되며, 웹사이트를 처음 방문할 때 내려받는 양이 3분의 2 줄었습니다. 이는 많은 사람이 메가바이트마다 비용을 내는 연결에서 특히 의미가 큽니다.
이 수치는 전 세계 벤치마크가 아니라 한 도시에서 얻은 것입니다. 그래도 계산은 라고스와 자카르타에도 그대로 적용되며, 우리 사용자가 있는 곳이 바로 그곳입니다.
어려운 부분은 호출자가 누구인지 아는 것이었습니다
서버 앞에 무언가를 두는 것은 쉽습니다. 그 뒤에서 보호 장치가 계속 작동하게 하는 데 대부분의 노력이 들어갔습니다.
대부분의 결제 플랫폼과 마찬가지로 Moove는 속도 제한(rate limiting), 즉 한 호출자가 주어진 시간 동안 보낼 수 있는 요청 수의 상한을 사용하며, 이 제한은 호출자의 IP 주소, 인터넷에서 기기를 식별하는 번호를 기준으로 걸립니다. 엣지 네트워크 뒤에서는 모든 요청이 사용자의 주소가 아니라 엣지 로케이션의 주소에서 도착합니다. 다른 것이 바뀌지 않았다면 라고스의 모든 사람이 하나의 한도를 공유하게 되었을 것이고, 바쁜 오후 한때 아무 잘못도 없는 사람들을 돌려보내기 시작했을 것입니다.
해법은 두 가지 아이디어에 기반합니다.
가지고 있는 계층만 정확히 신뢰하세요. CDN은 일종의 리버스 프록시(reverse proxy), 즉 서버 앞에 놓여 요청을 전달하는 서버입니다. 각 프록시는 요청을 받은 주소를 X-Forwarded-For라는 표준 헤더에 추가하므로, 실제 호출자는 그 목록에서 정해진 수의 단계, 즉 **홉(hop)**만큼 뒤에 있습니다. 이 숫자가 틀리면 양쪽 방향 모두 버그가 됩니다. 계층을 너무 적게 세면 모든 사용자가 자신의 엣지 로케이션처럼 보입니다. 너무 많이 세면 호출자가 직접 쓴 항목을 신뢰하기 시작합니다. 그래서 우리는 이 숫자를 코드가 아니라 인프라의 속성으로 만들었습니다. 네트워크가 바뀌는 바로 그 순간에 함께 바뀌며, 값이 말이 되지 않으면 서비스는 추측하는 대신 시작을 거부합니다.
엣지가 호출자를 보증하게 하고, 엣지만 믿으세요. 이제 엣지는 모든 요청에 실제 호출자의 주소를 기록하고, 호출자가 보낸 값이 있으면 그것을 덮어씁니다. 우리의 보호 장치는 요청이 정말로 우리 엣지를 거쳐 왔음을 검증할 수 있을 때만 이 값을 믿습니다. 이전에 네트워크 주소를 기준으로 걸던 모든 제한은 이제 엣지 트래픽에 대해 실제 호출자를 기준으로 합니다. 우리는 이 규칙들을 기존 규칙을 바꾸는 대신 나란히 추가했으며, 그래서 롤아웃의 모든 변경은 누구나 한 번에 검토할 수 있는 순수한 추가였습니다.
출시 전에 잡은 버그
Moove Profiles는 판매자의 자체 도메인에서 운영될 수 있습니다. 이러한 도메인에 대한 요청은 판매자의 도메인이 붙은 채로 우리 웹사이트를 통과했고, 덕분에 사이트는 어떤 프로필을 보여줄지 알 수 있었습니다.
웹사이트 앞에 놓인 새 엣지는 들어오는 요청에서 이런 종류의 정보를 제거하는데, 방문자가 이를 설정할 수 있어서는 안 되므로 이는 완전히 올바른 동작입니다. 하지만 그 결과 웹사이트가 엣지 뒤로 옮겨지면 모든 판매자 도메인이 조용히 판매자의 프로필 대신 Moove 홈페이지를 보여주게 될 상황이었습니다.
우리는 리뷰에서 이를 발견하고, 판매자 도메인 경로를 먼저 별도의 라우트로 옮긴 뒤, 그 이동이 이루어지지 않은 곳에는 웹사이트 변경을 적용하지 못하도록 인프라가 거부하게 만들었습니다. 예전에는 리뷰어의 머릿속에만 있던 실수가 이제는 빌드가 막아 주는 것이 되었습니다.
일반적인 교훈은 이것입니다. 입력을 정리하는 계층을 추가할 때는 정리하려는 그 입력에 의존하던 모든 것을 찾아보세요.
롤아웃 방법
결제 인프라에서는 "배포하고 지켜보기"가 통하지 않으므로, 스테이징을 먼저 바꾸고 프로덕션을 나중에 바꿨으며, 몇 가지 선택으로 전환을 무탈하게 만들었습니다. 우리는 moove.xyz 같은 이름을 서버 주소로 바꿔 주는 인터넷의 주소록인 DNS 레코드의 **TTL(time to live)**을 낮췄습니다. TTL은 인터넷의 다른 곳이 그 응답을 캐시해도 되는 시간이며, 우리는 이를 한 시간에서 5분으로 줄였습니다. 아무것도 옮기기 한 시간 전에 줄였기 때문에 이전 응답은 모든 곳에서 만료되었고, 롤백도 몇 분 안에 적용될 수 있었습니다. 주소 레코드는 제자리에서 전환할 수 있는 형태로 유지했는데, 삭제하고 다시 만들면 공백이 생기고 공백은 곧 장애이기 때문입니다. 위험한 단계는 각각 별도의 작은 변경으로 나누었고, 되돌리는 절차를 순서대로 적어 두었습니다.
다른 팀에게 해 주고 싶은 조언
사용자가 서버와 멀리 떨어져 있다면, 아무것도 캐시하지 않더라도 엣지 네트워크는 가질 만한 가치가 있습니다. 전환하기 전에 다음을 살펴보세요.
- 호출자의 주소를 읽는 모든 것을 나열하세요. 속도 제한, 허용 목록, 사기 신호, 감사 로그, 보안 규칙이 포함됩니다. 바꾸지 않으면 이들 모두 엣지의 주소를 보게 됩니다.
- 신뢰하는 계층의 수를 인프라의 일부로 만들고, 값이 틀리면 시작을 거부하세요.
- 엣지가 실제 호출자를 기록하게 하고, 엣지에서 왔음을 검증할 수 있는 트래픽에서만 그 기록을 신뢰하세요.
- 서버가 엣지를 통한 트래픽만 받도록 하고, 강제하기 전에 먼저 지켜보세요.
- 페이지를 캐시한다면, 응답이 실제로 달라지는 모든 요소로 캐시 키(cache key), 즉 두 요청이 저장된 응답 하나를 공유할 수 있는지 캐시가 판단하는 데 쓰는 요청 세부 정보의 집합을 만드세요. 서버는 이러한 세부 정보를 Vary 헤더에 나열하므로, 기억에 의존하지 말고 그것을 읽으세요.
- 전후를 측정하고, 핸드셰이크 시간과 첫 바이트 시간을 따로 확인하세요. 그러면 연결 설정과 서버가 작업하는 데 쓰는 시간을 구분할 수 있습니다.
앞으로의 계획
우리 페이지 대부분은 개인화되어 있어서 웹사이트의 엣지 캐시는 당분간 대체로 놀고 있습니다. 연결 개선은 모든 페이지에 적용되며, 이제 개별 페이지는 공유해도 안전할 때 캐싱을 선택할 수 있습니다. API 응답을 캐시하려면 별도의 설계와 검토가 필요하며, 이것이 우리가 다음으로 살펴보고 싶은 일입니다.
돈은 바다를 건너는 빛의 속도가 아니라 네트워크의 속도로 움직여야 합니다.
Moove 위에서 개발한다면 Web3 결제 API와 SDK 소개와 Moove Developer Program을 읽어 보세요.
moove.xyz 엔지니어링 팀 작성.
moove.xyz 소개
moove.xyz는 가치의 무허가적이고 손쉬운 이동을 위해 구축된 글로벌 Web3 핀테크 플랫폼입니다. 우리는 전 세계 비즈니스와 소비자가 하나의 플랫폼에서 모든 블록체인에 걸쳐 모든 암호화폐를 송수신, 스테이킹, 스왑할 수 있도록 지원합니다.
우리는 멀티 체인 지갑 접근, 개인화된 지갑 핸들, 크로스 체인 토큰 스왑, 임베디드 크로스 체인 거래, 분산형 소셜 금융 네트워크에 걸쳐 통합되고 포괄적인 커버리지를 가능하게 하는 풀스택 암호 결제 및 분산형 금융 인프라를 혁신하고 구축하는 세계 최초의 Web3 핀테크 기업 중 하나입니다. 주요 제품으로는 Moove Profile, Moove Send, Moove Receive, Moove Stake, Moove Swap, Moove Rewards, Moove Discover 등이 있습니다.
우리의 미션은 간단합니다——다음 10억 Web3 사용자를 위한 무허가적이고 손쉬운 금융 기술을 창조하고 배포하는 것. 우리는 돈과 가치 이동의 미래가 비용 없이, 국경 없이, 무허가로, 손쉽게, 그리고 모든 사람을 위해 구축될 것이라고 근본적으로 믿습니다——그리고 그 미래를 현실로 만들기 위한 궁극의 Web3 핀테크 플랫폼을 구축하고 있습니다.
내 돈. 내 결정.






