잡담

ERP 중 매출액 현황 계산 최적화

True or False 2023. 5. 29. 17:08

서론

기존 ERP를 기반으로 해서 새롭게 ERP 를 만들게 된 경험이 있다.

 

그때 기술 스택을 React + Django 로 개발 진행을 하게되었고

그때 내 밑으로 있었던 신입 개발자와 나는 사수로서 함께 작업을 하게 되었다.

 

그때 신입 개발자가 따로 어디쪽으로 개발을 진행한 경험이 없고 전공자이기만 해서

내가 프론트로 React를 해보는 게 어떻겠냐라고 제안을 했다.

왜냐하면 그전에 작업물이 그래도 node.js 기반으로 진행을 했기 때문에 그쪽이 더 적합할 것으로 판단했다.

제안하고 동생도 승낙을 해서 React(개발자 신입) + Django(본인) 이렇게 개발을 진행했다.

 

기한은 넉넉하게 잡아 2달이었고, 물론 기한내에 하기위해 내가 백엔드 작업을 빨리 끝내고 프론트를 도와주는 형태로 작업했다.

 

문제 발생

그러면서 막바지에 문제가 하나 생겼다.

 

처음에는 기존 테스트 데이터들만을 가지고 진행을 하다보니 데이터의 수가 그렇게 많지 않아 전체 주문에 대한 금액 계산이 오래 걸리지가 않았다.

 

그런데 기존에 있던 데이터베이스의 양이 많았고 옮기고 테스트를 돌려보니 시간이 너무 오래 걸리는 것이였다.

 

그때의 DB가 간추려서

주문내역(날짜, 구매자, 수금금액, 할인율, 부가세) - 아이템(수량, 금액, 이름, 입고일, 출고일, 입고금액) 이렇게 있었다.

 

계산방식

주문내역의 총액은 해당 주문내역을 부모키로 가진 아이템을 가져와 sum(아이템 별(수량 * 금액)) 이런 형태로 가져왔고(subquery)

 

다시 여기에 할인율, 부가세를 가져와 계산을 진행 후 수금이 되어진 금액을 통해서 만약에 수금이 안되었으면 -, 수금이 되었으면 + 로

나타내었다.(앱 내에서 처리)

 

그리고 주문금액의 총합이 전체 매출액으로 계산이 되어졌다.

 

그런데 당초 진행을 했을 때의 데이터는 100개 정도로만 하다가 갑자기

주문내역이 200,000건 정도로 늘어나니 총액 계산에 약 1분 30초정도가 소요되었다.

 

임시방편

그래서 내가 여기에서 먼저 진행한 것은 API 분리를 먼저 했다.

 

총매출액 계산이 포함된 API 호출이 되어지는 페이지가 로그인이 되면 제일 처음에 마주하는 대시보드 페이지였다.

그 페이지에서 GET API 호출을 했는데 다른 항목들은 준비가 되었지만

총매출액 계산에서 저렇게 시간이 오래걸리니 다른 곳에 영향을 끼쳐 렌더링이 되어지지않은 문제가 발생하니

API 를 따로 먼저 분리를 진행하고 개선을 시도했다.

 

일단은 그때 당시에 저 부분의 경우에는 먼저 subquery를 통해서 주문내역에 들어있는 총합은 계산이 되어져서 나오고

그다음에 계산들은 앱내에서 계산을 진행했다.

 

그렇게 하게 된 이유는 Subquery로 전체적으로 계산하기에는 일단 query가 너무 길어져 보기가 힘들기도 했고 속도개선이 그렇게 되어지지는 않았다.

 

그렇다보니 그냥 절대적인 양에 따른 시간이 문제임을 깨달았고 다른 방식으로 이 문제를 해결을 진행했다.

 

문제 해결

일단 절대적인 양 때문에 시간을 큰 폭으로 개선을 할 수 없음을 깨닫고 진행한 방식은

Redis 를 이용해 처음에 계산한 전체 매출액을 저장했다가
전체 매출액에 영향을 끼칠만한 행동이 있을 때 업데이트를 진행하는 방식으로 개선했다.

 

그렇게 될 경우에 처음에 1분30초 정도로 걸리고 다음에 호출을 할 때에는

조회에서는 당연히 Redis 에 접근하는 시간만큼 들 것으로 예상했고, 다른 작업에서는 조회 + 계산 + 저장 만큼의 시간으로 들것으로 판단했다.

 

예시로

만약에 주문 내역이 발생하면 주문내역의 금액 계산 진행 -> Redis 에 있는 전체 매출액을 가져오고 그 값을 계산 후 Redis 에 저장

기존에 (+)10,000 매출액이 있고 주문내역으로 (+)2,000이 발생한 경우 (+)10,000 + (+)2,000 = (+)12,000을 Redis 에 저장

 

이렇게 Update, Create, Delete 부분에 해당 로직을 적용시켰다.

 

결과

일단 결과는 매우 만족스러웠다.

 

예상했던대로

기존 1분 30초가 걸리는 API가 사실상 100ms 이내로 동작을 하게되었다.

(해당 부분 Redis에 정보만 가져오는 것만 했으면 더 짧았을 것이다.)

 

GET API 가 하는 것은 진짜 별것이 없었다. 그냥 Redis 에서 데이터만 가져오는 것이면 되었기 때문이다.

 

그리고 주기적으로 사람이 이용을 안하는 때(새벽)에 Cron을 이용해서 Redis 에 있는 값이 맞는지 검증하는 로직도 돌렸었다.

만약에 맞지않을 경우에는 업데이트 후 알림을 보내도록 했었는데 딱히 알림이 오는 일은 없었다.

 

이게 절대적인 데이터의 양이 많아지면 어쩔 수 없이 시간이 걸리는 문제가 발생한다.

뭔가 offset pagination 같은 느낌이다.

그냥 읽기만 해도 시간이 걸리는 느낌?

그런데 거기에 계산을 해? -> 더 걸린다. 이 느낌이다.

 

그래도 이때 프로젝트가 의미있었던 시간이었던게 해당문제를 처리하는 방법이 많이 있었겠지만

빠르게 문제 요인을 파악하고 그때 당시에 가장 적절한 방법을 취했던 것 같았다.

그리고 그것을 검증하는 로직을 통해서 이상이 있을 경우에는 해당 부분을 다시 검토 후 저장하다보니

문제가 발생할 수 있는 순간도 줄였던 것 같다.

 

그리고 처음으로 누군가를 가르치면서 했던 경험이라서 내가 새롭게 배운 느낌도 있었다.

뭔가 내가 처음에 React를 했을 때에는 경험했던 부분들에 대해 의문이 안 들었던 부분이 있었는데

신입 개발자가 의문을 가지고 질문을 했을 때 일단 내가 생각했던 느낌을 말하고 그래도 확실하지는 않기에

문서를 찾아서 다시 재확증하는? 경험을 많이 했는데 그러면서 다시 재각인하는 시간이 되었던 것 같다.