배민셀프서비스는 가게를 운영하는 파트너가 가게 정보, 메뉴, 할인, 배달지역을 직접 관리하는 서비스입니다.
이 중 배달지역은 한번 정해 두면 자주 바꿀 일이 없습니다. 그런데 기상이 나빠지면 이야기가 달라집니다. 폭우나 폭설이 오면 주문은 평소처럼 들어오는데 배달할 라이더가 부족해져, 취소되는 주문이 급격히 늘어납니다. 취소를 겪은 고객은 다시 주문하지 않을 가능성이 높아지고, 파트너는 가게 운영에 타격을 입습니다.
이 문제를 줄이려고 셀프서비스에 임시배달지역 조정 기능을 만들었습니다. 기상악화 같은 사유가 생기면 배달 반경을 일시적으로 좁혀, 지금 배달 여건으로 감당할 수 있는 가까운 주문 위주로 받는 기능입니다. 받을 수 있는 만큼만 받으니 취소를 줄일 수 있고, 기상이 좋아지면 원래 배달지역으로 되돌립니다. 다만 기능을 만들었다고 끝이 아니었습니다.
임시배달지역 조정 기능이 제 역할을 하려면 파트너가 "지금이 기상악화 상황이다"라는 사실을 제때 알아야 합니다. 기상악화는 비나 눈이 오는지만 보는 게 아니라, 외부 팀이 지역별 날씨와 주문 취소율 같은 걸 종합해 단계별로 판단합니다. 셀프서비스는 외부 팀이 이벤트로 발행하는 기상악화 단계를 받아 파트너에게 전달해야 합니다.
하지만 기존 셀프서비스에는 서버가 파트너에게 먼저 무언가를 보낼 방법이 없었습니다. 지금까지 셀프서비스의 통신은 파트너가 화면을 열거나 버튼을 누를 때 시작됐고, 서버는 그 요청에 응답만 하면 됐기 때문입니다. 파트너가 화면을 열 때까지 기다려서는 기상악화를 제때 알릴 수 없으니, 서버가 먼저 통신을 시작해야 했습니다.
그래서 셀프서비스에 SSE(Server-Sent Events)를 도입했습니다. 기상악화 이벤트는 다음과 같은 흐름으로 파트너 화면까지 전달됩니다.
셀프서비스 서버는 쿠버네티스 위에서 동일한 애플리케이션을 여러 개의 파드로 띄워 운영합니다. 파드는 애플리케이션이 실행되는 단위 하나를 가리킵니다. 파트너의 연결은 그중 한 파드에만 붙고, 어느 파드에 붙었는지는 서버가 따로 알지 못합니다.
기상악화 이벤트 한 건은 Kafka를 거쳐 모든 파드에 뿌려지고, 그중 파트너가 연결되어 있는 파드에서만 프론트로 전달됩니다. 프론트가 받는 건 뭔가 바뀌었다는 신호뿐이라, 최신 기상악화 상태를 다시 조회합니다.
연결 하나를 여는 건 어렵지 않았지만, 여러 파드에 흩어진 연결을 관리하는 일은 쉽지 않았습니다.
서버에서 클라이언트로 실시간 푸시를 보내는 방식을 검토 중인데, SSE가 자신의 서비스에 맞을지 판단이 서지 않는 분께 이 글이 도움이 되면 좋겠습니다.
1. 서버가 파트너 화면에 먼저 알리는 네 가지 방법
폴링으로 실시간처럼 보이게 하면 요청이 얼마나 늘어나는지 따져 봅니다. 그다음 SSE가 무엇인지, 롱 폴링과 WebSocket은 왜 안 썼는지, 왜 SSE를 골랐는지 차례로 봅니다.
폴링
개발 시점에 서버가 프론트에 제공하는 통신 수단은 REST API뿐이었습니다.
그래서 남는 방법은 프론트가 계속 물어보는 것이었습니다. 이걸 폴링이라고 합니다. 몇 초마다 "지금 기상악화인가요?"를 묻고, 답이 바뀌면 화면을 고쳐서 실시간처럼 보이게 하는 방식입니다.
여기서 걸리는 게 있습니다. 기상악화 알림을 받아야 하는 파트너는 전체 중 일부인데, 폴링은 묻는 쪽이 프론트라 서버가 그 일부만 골라낼 수가 없습니다. 누가 대상인지는 이벤트를 받아야 알 수 있으니까요. 결국 셀프서비스를 열어 둔 파트너 모두가 몇 초마다 "우리 지역 날씨가 어때?"를 묻게 됩니다.
주기를 정하면 요청 수는 바로 따라 나옵니다. 30초 주기면 접속한 파트너 수의 30분의 1이 매초 서버로 들어옵니다. 접속이 몰리는 저녁 피크에는 그만큼 더 커집니다.
그리고 그 요청의 거의 전부가 "기상 상태 좋음"이라는 답을 받습니다. 이벤트 한 건을 제때 전달하려고, 아무 일도 없


