사장님용 통계 데이터 서빙하기

Jul.04.2022 한진욱

Backend

안녕하세요, 배민상품시스템팀에서 서버 개발을 맡고 있는 한진욱입니다.
작년에 저희 팀은 ‘우리가게NOW’라는, 사장님들을 위한 통계 서비스를 만드는 프로젝트에 참여했습니다.
기존에 하던 업무에서 보기 힘들었던 통계 데이터를 다루면서 겪었던 어려움과 문제 해결의 과정에 대해서 소개하고자 합니다.

이름도 낯선 프로젝트, ‘우리가게NOW’이름도 낯선 프로젝트, ‘우리가게NOW’

우리가게NOW 서비스는 작년 6월에 출시한 통계 서비스입니다.


[출처: 배민사장님광장]

사장님들께 주문 접수율, 주문 접수시간, 조리시간, 조리시간 준수율 등의 지표를 공개하여, 사장님이 직접 가게 운영과 고객만족에 큰 영향을 미치는 지표들을 살펴보고 개선할 수 있도록 하는 통계 서비스입니다.

사장님에게 새로운 통계 화면을 제공하기 위해, 다음과 같은 요구사항을 구현해야 했습니다.

  • 주문/배달 데이터로부터 주문 접수율, 주문 접수시간, 조리시간 준수율 등의 통계 데이터를 만들 수 있어야 합니다.
  • 통계 데이터를 바탕으로 상대평가를 해야 합니다. 각 지표별로 모든 가게들 중 상위 몇 퍼센트인지 수치로 나타낼 수 있어야 합니다.
  • 매일 아침 9시에 통계/상대평가 데이터 최신본을 업데이트해야 합니다.

이 프로젝트는 기존에 팀에서 주로 하던 작업과 비교해 생소한 부분이 많았는데, 다음과 같은 점이 생소했습니다.

  • 통계 데이터를 다뤄야 했습니다. 실시간 데이터가 아니라는 점이 정말 생소했습니다. 과거 데이터를 처리하는 통계 작업을 진행하는 것은 처음이었습니다.
  • 처리하는 데이터 크기가 컸습니다. 주문 데이터를 바탕으로 통계 데이터를 계산해야 하는데, 주문 데이터 개수는 이전에 팀에서 취급해왔던 데이터 수(몇 십만 개 수준)의 몇 배를 뛰어넘는 수준이었습니다.
  • Airflow, SparkSQL 기술을 사용해야 했습니다. 데이터 레이크에서 다른 도메인(주문) 데이터를 바탕으로 작업해야 했기 때문입니다.

낯선 작업이었지만, Airflow+SparkSQL을 통해서 외부 데이터를 사용해야 한다는 점은 분명해 보였습니다.

아래 3가지 틀 하에서, 세부적인 구조를 만들며 나아갔습니다.

  1. Airflow 스케줄러를 통해 주기적으로 외부에서 필요한 정보를 가져온다.
  2. 데이터 레이크(Data Lake)에서 SparkSQL 쿼리문으로 데이터 추출한다.
  3. 데이터를 팀 내 RDB로 저장한다.

프로젝트를 위한 첫 구조

팀원과 함께 정한 첫 구조는 이렇습니다.

먼저, 사내에서 데이터서비스실이 관리하고 있는 테이블을 모두 ‘데이터 레이크(Data Lake)’라고 하겠습니다.

  1. 데이터 레이크에서 주문 데이터를 뽑은 뒤 팀 내 RDB로 적재합니다.
  2. 적재한 주문 데이터를 바탕으로 주문 접수율, 주문 접수시간, 조리시간, 조리시간 준수율 등의 통계 데이터를 계산합니다.
  3. 계산한 결과를 다시 팀 내 RDB로 적재합니다.

통계 데이터를 배치 애플리케이션으로 계산한다는 점이 핵심입니다.
위 구조의 장점은 다음과 같습니다.

  • 테스트가 쉽습니다.
  • 변경사항에 유연하게 대응할 수 있습니다.
  • 통계를 처리하는 부분을 가독성 있게 코드로 표현할 수 있습니다.

세 가지 장점 모두 애플리케이션 코드라는 특성에서 나옵니다. Java 코드로 계산 로직을 표현하기 때문에, 테스트를 할 수 있고 따라서 유연성 및 가독성이 증가한다는 것입니다.

첫 구조의 문제점

하지만 이 구조는 곧 문제에 부딪혔습니다.

팀에서 겪어보지 못한 엄청난 크기의 데이터 때문이었습니다.
몇 백만 건의 주문 데이터양 때문에 두 가지 우려가 생겼습니다.

  1. 3시간에 안에 배치를 완료할 수 있을지에 대한 확신이 없었습니다.
    1. 적어도 3시간 안에는 통계 계산이 끝나야 합니다. 전날 주문 데이터가 쌓이는 6시 이후부터 시작해서, 사장님에게 통계 데이터가 오픈되는 9시까지 통계 데이터를 생성해야 했기 때문입니다.
    2. 하지만 14일간의 주문 건을 바탕으로 통계를 내기 때문에, 계산 대상 데이터가 몇 백만 건이 될 수 있습니다.
    3. 팀에서 평소에 배치가 다루는 데이터가 몇십만 건 수준임을 생각하면, 팀 내 평균 배치 실행 시간보다는 훨씬 오래 걸릴 것으로 추정했습니다.
  2. 팀 내 RDB로 모든 주문 데이터를 적재하는 부담이 컸습니다. 몇 백만 건의 주문 데이터가 팀 내 RDB로 저장됩니다. 활용도가 제한적인 주문 데이터를 관리해야 하는 부담이 있습니다.

적재하는 과정에서 계산하기로 결정, 하지만…

몇 차례의 팀 내 논의를 거친 후, 애플리케이션이 아닌 적재하는 과정에서 통계 데이터를 함께 계산하는 것으로 결정했습니다.
즉, 데이터 레이크에서 팀 내 RDB로 데이터를 적재하는 과정에서 통계 데이터를 계산하기로 했습니다.

이를 통해, 다음 두 가지 장점을 얻을 것으로 기대했습니다.

  1. 성능상 유리하다고 생각했습니다. 데이터 레이크에서 팀 내 RDB로 적재할 때 SparkSQL를 이용하고 있습니다. 분산 환경에서 동작하는 Spark 특성을 이용하여, executor 수를 조절하여 저희가 원하는 만큼 성능을 올릴 수 있을 것이라고 기대했습니다.
  2. 주문데이터를 팀내 RDB로 적재하는 부담을 없앨 수 있습니다.

하지만 아직 SparkSQL로 통계 데이터를 생성하는 구조에 대해 자신이 없었습니다.
평소에 쿼리 형태의 코드를 작성한 적이 드물었고, 쿼리 형태의 코드가 앞으로의 요구사항 변경을 과연 충족할 수 있을지에 대해 의문이 들었기 때문입니다.

여기에 대해 동료 개발자 한 분은 다음과 같은 질문을 할 것을 조언했습니다.

나에게 일주일의 시간이 있다고 상상해 보기

‘나에게 일주일의 빈 시간이 있을 때, 새로운 기술의 불안감을 해소하기 위해 나는 무엇을 할 것인가?’
‘SparkSQL의 불안감을 해소하기 위해 나는 어떤 점을 찾아볼 것인가?’

스스로에게 이 질문을 던졌을 때, 업주 간 상대평가를 하는 구현하는 부분이 제일 자신이 없었습니다.
그래서 SparkSQL 쿼리로 구현할 수 있을지 직접 확인했습니다.

업주 조리시간 준수율 순위
A 12% 1
B 9% 2
C 9% 2
D 8% 4

현재 요구사항은 위 표와 같이 순위가 나오도록 하는 것입니다. 동점자는 같은 순위가 되고, 그다음 순위는 직전 동점자들 수가 반영되어 매겨집니다.

찾아보니 SparkSQL에도 RANK() 함수가 있었습니다.
다음과 같이 쿼리문을 만드니 상대평가를 하는 로직이 간단히 해결됐습니다.

RANK() OVER (ORDER BY cooking_time_rate DESC)

SparkSQL에 대한 불안감을 일정 부분 해소했습니다.

물론 더 복잡한 요구사항이 들어온다면, 쿼리로 구현하는 게 어려울 수도 있습니다.
하지만 더 복잡한 요구사항이 들어올 가능성은 적을 것이라고 생각했습니다. 또한 들어온다고 하더라도, 그때 상대평가 부분만 배치로 계산하도록 바꿔도 됩니다.

그렇게 해서 만들었습니다, 최종 구조

최종 구조는 다음과 같습니다.

중간 통계 결과를 저장하는 hive 테이블을 두고, 중간 결과를 합친 최종 결과만 팀 내 RDB로 저장하도록 만들었습니다.
통계 데이터를 계산하는 로직은 데이터 레이크에서 데이터를 추출하는 SparkSQL에 있습니다.
이를 통해, 우리가게NOW 오픈 첫날 30분 안쪽으로 데이터 처리를 완료할 수 있었습니다.
지금까지도 평균적으로 30분 처리 시간을 유지하고 있습니다.

돌이켜보면

우리가게NOW 통계를 계산하는 작업은 몇백만 건이 넘는 과거 주문 데이터를 처리하는 빅데이터 성격의 작업이었습니다.

하지만 당시에 제가 작업했을 때 빅데이터를 다루고 있다는 인식조차 희박했습니다.

그래서 처음에는 실시간성 데이터를 보정하는 배치 애플리케이션으로 접근하는 등의 시행착오를 겪었습니다.

지식이 부족한 상태였지만, 당시로서 가장 나은 대안을 찾을 수 있었던 이유는 다음 요인이었던 것 같습니다.

  • 가치의 우선순위 파악, 기술의 장단점 분석 후 우선순위에 따라 기술 선택
    • 당시 아침 9시까지 업주에게 최신화된 통계 화면을 보여주는 것을 최우선 우선순위로 두었습니다.
    • 따라서 수행 시간 축소를 최우선 순위로 둘 수 있었습니다.
  • 생소한 기술에 대한 불안감을 질문으로 해소하기
    • SparkSQL에 대한 불안감은 말로 표현하기 전까지는 막연한 상태였습니다.
    • 일주일의 시간이 있으면 어떤 부분을 공부할 것인가? 질문을 통해 불안감의 원인을 구체화할 수 있었습니다.

이번 우리가게Now 프로젝트를 통해 기술에 대한 막연한 불안감을 해소하는 방법을 배울 수 있었습니다. 더불어 데이터에도 실시간 데이터, 통계 데이터 등 여러 가지 유형이 있다는 점을 배웠습니다.

통계 데이터를 서빙할 때 이 글이 도움이 되기를 바라며, 이상으로 글을 마치도록 하겠습니다.

[추신!]

배민상품시스템에서 함께할 개발자를 적극적으로 모집하고 있습니다(지원 공고).
저희와 함께 더 나은 서비스를 위한 기술적인 고민을 할 수 있으면 좋겠습니다.
감사합니다.