‘대략’이 아닌 ‘정확한’ 데이터로!: 생산성 측정 지표 개발기
SUPER2 어드민은 사장님이 제출한 입점 신청 및 정보 수정 요청을 승인하는 시스템을 제공하고 있어요. (자세한 요청 승인 과정이 궁금하시면 기술블로그 "승인 프로세스 뒤에 숨겨진 일꾼들: 사장님 입점요청 승인툴 고도화 프로젝트 경험담" 을 참고해 주세요.)
그래서 SUPER2 어드민의 주요 사용자 중 하나는 승인 작업자인데요. 이 작업자분들의 관리자는 리소스를 적재적소에 배정해서 업무를 효율적으로 할 수 있도록 하고 장기적으로는 인력 관련 예산을 의미 있는 곳에 투입할 수 있게 해야 해요.
이를 위해 아래와 같은 항목들이 필수로 관리되어야 하는데, 기존에는 이 항목들이 시스템에 내재화되어 있지 않아 수기로 관리할 수밖에 없어 여러 불편을 초래하고 있었죠.
- 업무 배정: 적절한 인원을 적시에 배치하여 요청을 효율적으로 처리
- 업무시간 측정: 각 작업자의 근무시간 및 성과를 정량적으로 관리
- 개인의 업무 생산성 측정: 생산성을 분석해 인원 배치의 효율성을 평가
- 인력 운용 계획: 수요에 따라 인원을 조정하여 비용과 성과를 최적화
이번 과제를 통해 SUPER2 어드민 내에 위 항목과 관련된 기능들을 구축하여, 인력 관리가 보다 효율적이고 체계적으로 이루어질 수 있게 되었는데요. 어떻게 어려움들을 해결했는지 그 과정을 소개해 드릴게요.
어떤 어려움들이 있었을까요
업무 그룹 단위의 생산성 파악의 어려움
업무 그룹 단위로 작업자의 생산성을 파악하는 것은 승인 작업자 운영에서 중요한 과제인데요. 승인 작업자는 사장님이 제출하는 다양한 요청 종류에 따라 각기 다른 업무 그룹에 배정되어 작업을 수행하기 때문에, 각 요청 종류마다 검수 내용의 깊이와 가짓수, 실시간 처리 현황을 고려하여 적절한 인력 배치가 필수로 이루어져야 해요.
예를 들어, 아래와 같이 요청 종류와 그에 따른 업무 그룹 및 필요한 인력이 다른 것을 확인할 수 있어요.
요청 종류 | 검수 항목 | 업무 그룹 | 필요 작업자 수 |
---|---|---|---|
최초 입점 신청 | 사업자 정보, 가게 정보, 계좌 정보, 광고 정보 등 | A 업무그룹 | 30명 |
정산계좌 변경 요청 | 사업자 정보, 계좌 정보 | B 업무그룹 | 10명 |
광고서비스 추가 신청 | 광고 정보 | C 업무그룹 | 5명 |
기존 SUPER2 어드민에서는 업무 그룹과 작업자별로 얼마나 요청 건을 처리했는지 현황 확인하는 기능이 제공되지 않았기 때문에 업무 그룹 단위로 생산성을 종합적으로 파악하거나, 지난 데이터와 비교하여 업무가 현재 원활하게 진행되고 있는지 확인이 어려웠어요.
이로 인해 인력이 긴급하게 추가로 필요한 상황에서도 신속하게 인력 배치가 어려웠고, 적절한 인력 규모를 유지하기 위한 판단 역시 쉽지 않았어요.
적정 인적자원 산정의 어려움
승인 팀을 운영할 때 필요한 적정 인원을 관리 하는 것이 중요한데요. 작업자들이 어느 정도의 업무를 소화할 수 있는지를 파악하고, 이를 바탕으로 필요한 인력을 정확히 산정하는 것이 핵심 과제로 볼 수 있어요. 그러나 기존에는 각 작업자의 요청 처리 건수, 소요 시간, 출퇴근 시간 등을 수기로 엑셀에 기록하고 관리했고, 이 수기 방식은 몇 가지 한계를 지니고 있었어요.
데이터의 신뢰도가 떨어지거나, 실시간으로 정보를 파악하기 어려웠고, 정확한 데이터 없이 어림짐작으로 산출한 예상치를 기반으로 인력을 투입해야 하는 상황이 반복되었죠. 이로 인해 불필요한 인력 투입이나 과도한 비용 발생의 문제가 생기기도 했고요.
이러한 문제들을 해소하기 위해서는, 실시간 데이터에 기반한 정확한 인력 산정과 투입이 가능해야 하며, 비용을 줄일 수 있는 구체적인 지점을 파악하고 실제 성과를 개선할 수 있는 기반을 마련하는 것이 필요했어요.
출퇴근 및 근무 현황 트래킹의 어려움
기존에는 승인 작업자들의 출퇴근 기록을 별도의 시트에 수기로 작성하고, 월말에 이 시트를 기반으로 업무그룹별 적정 인원 파악 및 업무 재분배를 하고 있었어요.
이 방식은 정확한 데이터 확보가 어려워 정합성이 떨어지는 문제가 있었는데요. 특히, 근무 이력에는 휴가나 조퇴 같은 다양한 변수들이 존재하기 때문에 관리자가 미처 파악하지 못하는 여러 사각지대가 존재했죠.
이러한 문제를 해결하기 위해서는 근무 현황을 자동으로 트래킹할 수 있는 시스템 도입이 필요했어요.
이렇게 해결했어요
📍생산성 조회 지면 구축
관리자가 작업자의 업무 처리 현황을 한눈에 볼 수 있도록 승인 생산성 관리 지면을 신설했어요.
이 지면에서는 작업자의 업무 현황을 파악할 수 있는 5가지 지표를 제공하여, 실제 근무 시간 동안 얼마나 많은 요청 건을 처리했는지를 기준으로 최종 생산성을 보여줘요.
지표 종류 | 설명 |
---|---|
할당요청 | 작업자에게 배정된 요청의 수를 의미하며, 작업자가 해야 할 업무의 수를 쉽게 파악할 수 있도록 제공합니다. |
처리요청 | 작업자가 담당 작업자로 적용된 상태에서 처리 완료한 요청의 수를 나타냅니다. 작업자가 직접 승인하거나 취소한 건만 카운팅하고, 요청을 올린 사장님이나 영업담당자가 취소 처리한 경우는 제외하여 정확한 데이터를 제공합니다. |
요청처리율 | 작업자가 처리한 요청 수를 할당된 요청 수로 나누어 보여주며, 작업자가 앞으로 처리해야 할 요청과 처리 완료한 요청 현황을 쉽게 파악할 수 있습니다. |
투입인력 | 작업자가 근무한 시간을 8시간으로 나누어, 총 8시간 중 순수 업무에 투입된 시간을 계산하여 제공합니다. 이는 생산성을 계산하기 위한 분모로 활용됩니다. |
생산성 | 작업자가 처리한 요청 수를 투입인력으로 나눈 수로, 작업자 혹은 업무 그룹 단위로 업무 생산성을 판단하는 최종 지표로 활용됩니다. |
이 지표를 제공하는 것 외에도, 여러 부수적인 고민사항들을 해결하기 위해 추가 기능을 구현했어요.
- 업무그룹 단위 생산성 기록: 업무그룹 단위의 생산성을 기반으로 인력 조정 여부를 판단하기 때문에, 작업자 단위로 생산성을 집계하기보단 업무 그룹단위로 생산성 데이터를 기록하도록 했어요. 동일 작업자가 여러 업무그룹에 속해서 작업을 했다면 각 업무그룹 단위로 생산성이 집계돼요.
- 다중 선택 필터 기능: 한 명의 관리자가 두 개 이상의 업무 그룹을 관리하는 경우가 많은데요. 이를 고려해 특정 업무 그룹을 다중 선택하여 필터링할 수 있는 기능을 제공하여, 관리자가 더 효율적으로 데이터를 분석하고 필요한 정보를 쉽게 찾을 수 있도록 했어요.
- 과거 데이터 다운로드: 최대 1년 치의 생산성을 CSV 파일로 다운로드할 수 있는 기능을 추가했어요. 내려받은 파일은 관리자가 추가로 편집해야 하는 여러 자료들에 활용되고 있어요.
- 기간별 생산성 총합 집계: 검색 기간이 하루 이상인 경우, 하루단위로 생산성을 보여주기보단 검색한 기간 동안의 총합 데이터를 계산하여 보여주도록 했어요. 이를 통해 관리자는 기간별로 생산성을 보다 쉽게 비교할수 있게 되었어요.
📍근무 관리 지면 구축
기존에 작업자가 수기로 기록하던 근무 관리 시트를 폐지할 수 있도록, 시스템으로 작업자의 근무 이력을 기록하고 관리할 수 있는 새로운 지면을 신설했어요. 이 지면에서는 작업자의 출퇴근 시간, 근무 현황, 휴가 및 조퇴 기록 등이 실시간으로 업데이트되며, 관리자는 이를 통해 작업자의 근무 이력을 손쉽게 확인하고 분석할 수 있어요.
이 지면에서는 작업자의 업무 시작부터 종료까지의 다양한 이력들을 자세히 보여주는데요. 이를 통해 관리자는 각 작업자의 근무 이력을 명확하게 파악하고, 생산성 향상을 위한 기초 데이터를 확보할 수 있어요.
이력 종류 | 설명 |
---|---|
ON | 작업자가 최초로 ‘작업 시작’ 버튼을 클릭한 시간으로, 출근 시간을 의미합니다. |
OFF | 작업자가 마지막으로 ‘작업 종료’ 버튼을 클릭한 시간으로, 퇴근 시간을 의미합니다. |
총 근무시간 | 작업자가 업무 그룹에 속해 있던 시작 시간과 종료 시간의 총합을 표시합니다. 업무 그룹 이동이 있었던 경우, 각 업무 그룹에서의 근무 시간을 기록합니다. |
순 근무시간 | 총 근무시간에서 휴식 시간을 제외한 실제 근무 시간을 표시합니다. |
휴식시간 | 휴식 종류에는 식사, 교육, 휴식, 검수 총 4개가 있으며,휴식 종류별로 시간을 표시합니다. |
추가적으로 여러 부수적인 고민을 해결하기 위해 다양한 개선 방안을 마련했는데요.
- 작업자 단위 근무 이력 표시: 작업자가 여러 업무 그룹에서 일하는 경우, 각 작업자 단위로 묶어서 여러 업무그룹에서의 근무시간을 표시했어요. 이를 통해 동일 작업자가 속했던 다양한 업무 그룹의 근무 이력을 신속하고 직관적으로 비교할 수 있게 되었어요.
- 다양한 변수 기록을 위한 메모 시스템: 근무 과정에서 발생하는 반차, 휴가, 조퇴 등 다양한 변수들을 메모 형태로 다양한 기록을 남기고 각 메모마다 카테고리 태그를 지정할 수 있도록 했어요. 이를 통해 관리자는 근무상의 특이사항을 쉽게 파악할 수 있게 되었어요.
- 자동 퇴근 배치 시스템: 생산성 데이터 집계를 위해서 퇴근 시간 기록이 필수인데요. 일부 작업자들이 퇴근 버튼을 누르지 않고 퇴근하는 경우 실제 근무시간 측정이 안되어 생산성이 오집계되는 문제가 발생할 수 있었어요. 이를 해결하기 위해, 저녁 9시에 퇴근 버튼을 누르지 않은 작업자들을 대상으로 자동 퇴근 배치를 개발했어요. 또한 근무 이력에 ‘자동 퇴근’으로 표시해서 관리자가 자동 퇴근 처리된 작업자를 쉽게 확인할 수 있도록 했어요.
📍생산성 데이터와 근무 이력 데이터를 단순하고 효과적으로 집계하기 위한 기술적 고민들과 해결 과정
생산성과 근무 이력 집계 작업을 시작했을 때 가장 처음 고민했던 것은 관련 데이터를 쌓고 있지 않았기 때문에, 기존에 다른 목적으로 수집되는 데이터를 어떻게 선택 및 가공해서 원하는 형태를 만들 수 있을지를 고민했어요.
또한, 이 과정에서 최대한 기존에 운영되는 로직을 건드리지 않고, 작은 작업 범위 하에서 목표를 달성하고자 했어요. 즉, 가성비 높게 개발할지를 중점으로 생각하고 협의하고 개발해 나갔습니다. 자세한 고민 과정을 하나씩 들려드릴게요.
1. 어떤 데이터를 선택할 것인가?
최종적으로 집계하고 싶었던 것은 작업자의 업무그룹별 근무시간(근무, 휴식시간)과 이에 따른 할당 건수, 처리 완료 건수인데요. 저희가 가진 대부분의 테이블은 업무 그룹이라는 단위가 들어가 있지 않아, 업무그룹 별로 집계를 하기 위해선 여러 고민을 해야 했어요.
테이블 | 추출 데이터 | 이슈 | 해결 방법 |
---|---|---|---|
작업요청 테이블(승인된 요청들의 처리상태와 처리완료 일시, 작업자가 포함됨) | 작업 요청들의 상태가 완료 상태이며, completed(완료일시)가 집계일인 모든 요청 | 요청을 누가 처리했는지는 알 수 있지만, 이 작업자가 어떤 업무 그룹에서 처리했는지는 알 수 없음 | 요청의 처리 일시를 기준으로, 요청의 처리가 어느 업무그룹에서 처리 완료되었을지를 추정 |
작업자 변경이력 테이블(작업자의 출퇴근 정보 변경이력이 포함됨) | modifiedAt(변경)이 집계일인 데이터를 모두 가져옴 | – 작업자의 출퇴근 휴식 정보는 알 수 있지만, 이것이 업무 그룹 별로 집계되지 않음 – modifiedAt의 index가 없음 |
– 작업자의 출퇴근 및 휴식 각 상태 변경이 이루어질 때 마다, 현재 배정된 업무 그룹을 확인하여 같이 저장하도록 수정 – modifiedAt index 추가 |
작업 요청 할당 변경이력 테이블(작업자가 특정 요청을 할당/보류한 이력이 포함됨) | createdAt(생성)이 집계일인 데이터를 모두 가져옴(즉, 해당 일에 할당된 요청) | – 어떤 요청이 어떤 작업자에게 할당되었는지는 알수 있지만, 어떤 업무그룹에 할당되었는지 알 수 없음 – createdAt의 index가 없음 |
– 작업자의 모든 작업할당 시점에 현재 배정된 업무그룹을 확인하여 같이 저장하도록 수정 – 작업 할당 테이블은 snowflake로 데이터가 추가 될때마다 timestamp의 id를 채번하는 방식으로 id(pk)를 생성 → createdAt 인덱스를 추가하는 대신 PK 기준의 range 검색 이후 createdAt 필터링하는 방식으로 해결 |
위 3개의 테이블을 활용하기 위해서 각 해결 방법을 통해 필요한 데이터를 활용하고, 동시에 이력성 데이터의 경우 데이터의 양이 많고, 계속 누적된다는 특성을 갖고 있기 때문에, 해당 데이터를 운영환경에서 사용할 수 있을지 검증을 해봤어요.
실시간 호출이 아닌 5분에 1회 집계하는 것이므로, 집계 전체 소요시간을 대략적으로 10초로 잡았고, DB에서 원천 데이터를 조회하는 것이 5초 내로 끝나면 되겠다고 생각 했어요. 그리고 앞으로 데이터가 계속 증가할 것을 대비해서, 현재 기준으로 2초 내로 조회가 가능하다면, 변경 이력 테이블을 사용할 수 있겠다고 생각했어요.
작업자 변경 이력 테이블과 작업 요청 할당 변경 이력 테이블의 누적데이터 수와 추출 데이터 수에 따라, 각각 조회 소요시간이 222ms, 930ms로 2초 내 조회가 가능해서 사용이 가능할것으로 보였고, 지속적인 모니터링으로 평균 조회 속도가 3~5초 사이를 유지한다면 향후 파티셔닝이나 1년 지난 데이터를 파기하는 방식으로 대응해야겠다고 결정했어요.
2. 데이터를 어떻게 가공할 것인가?
생산성 집계에 필요한 데이터는 크게 근무 시간, 할당 건수, 처리 건수인데요. 아래의 과정을 거쳐 생산성 데이터를 가공할 수 있었어요.
- 근무 시간 구하기: 작업자 변경 이력 테이블을 modifiedAt 기준으로 정렬 후, 순회를 하면서 근무상태(근무시작/종료/휴식)가 변경되거나, 업무그룹이 변경되는 시점을 기준으로해서 구간을 만들어요.
- 할당 건수 집계하기: 작업자, 업무그룹의 구조를 만들고, 해당일 할당 데이터 이력에서 중복을 제거하고 최초 작업 할당 건수를 집계해요.
- 처리 건수 집계하기: 마지막으로 요청의 처리 일시를 기준으로, 요청의 처리가 어느 업무그룹에서 처리 완료되었을지를 추정해요.
3. 데이터를 어떻게 집계할 것인가?
데이터 집계 방식을 선택할 때는 ‘내가 짠 모든 코드가 잘못되었다면?’을 상상하며 가장 보수적인 기준으로 접근했는데요. 생산성 데이터를 가장 효율적으로 집계하고 관리하기 위해 ‘배치’와 ‘스케줄러’의 혼합 방식을 선택했어요. 이를 통해 실시간 데이터 수집의 장점을 극대화하면서도 배치 방식의 안정성과 효율성을 확보할 수 있었어요.
집계 방식 | 장점 | 단점 |
---|---|---|
배치 | – 원하는 시점에 원하는 조건으로 집계 로직을 돌릴 수 있고, 배포 이전 시점에 돌려서 데이터를 쌓아둘 수도 있습니다. – 잘못 작성된 로직이 있다면, 로직을 수정해서 과거 데이터를 보정할 수도 있습니다. |
– 실시간성으로 자주 배치 job을 돌리는 데에 드는 리소스가 큽니다. – 워커(10대)에 비해 서버 인스턴스가 한정되어 있어 성능상 제한이 있습니다. |
스케줄러 | – 특정 주기로 반복 호출하는데 유리합니다. – job을 만드는데 드는 리소스가 적고, 분산처리에 용이합니다. |
– 배포 이후 로직을 수동 제어할 수 없습니다. – 과거에 잘못 작성한 로직으로 잘못 집계되었다면 이를 수정할 수 없습니다. |
결과적으로 두 방식의 혼합을 통해 아래와 같은 효과들을 만들어 내고 있어요.
- 리소스 사용 절감: 스케줄러를 오전 6시부터 오후 10시까지만 돌리고, 새벽 1시에 배치로 최종 집계함으로써, 1/3의 리소스를 절약할 수 있어요.(1일 집계 함수 호출 횟수: 288회 → 193회)
- 유연한 데이터 관리: 이슈가 발생하거나 특정 일자의 데이터를 다시 집계해야 할 필요가 있을 경우, 배치 방식을 통해 재집계할 수 있는 유연성을 제공해요. 이는 운영의 효율성을 높이는 데 기여해요.
- 최악의 케이스 대응: 기존에 집계한 로직이 잘못 되었을 때, 과거 시점부터 찾아서 재집계를 할 수 있어요.
추가적으로 이렇게 집계한 결과의 다양한 엣지 케이스들을 검증하기 위해 모니터링 대시보드도 만들어서 트래킹하고 있어요.
4. 집계된 데이터를 어떻게 조회할 것인가?
생산성 데이터의 조회 같은 경우는, 단순히 DB의 데이터를 pagenation으로 조회해서 전달하는 것이 아니고, 조회하면서 집계 데이터를 다시 한번 일자기준으로 sum 하고 평균을 내야하는 작업이 필요해요.
따라서, 얼마만큼의 데이터를 메모리에 올려서 한 번에 처리하는 것이 얼마만큼의 DB 부하를 가져오는지 정확하고, 보수적으로 측정해야 했어요. 이때 세운 기준은 ‘데이터양이 지금보다 5배가 많아진다면?’입니다.
이는 충분히 가능한 시나리오인데요, 그 이유는 작업자 수가 늘어날 수도 있고, 업무방식의 변경으로 한 명의 작업자가 현재는 평균 1.5개의 업무그룹으로 나눠서 일을 하는데 앞으로는 3개~4개의 업무그룹에서 일을 할수도 있기 때문이에요. 그럴 경우 3배가 아니라 5배 이상의 데이터를 한 번에 가져와야 하는 이슈가 생길 수가 있었죠.
실제로 최대 집계 소요 시간은 950ms 가 걸리고 있는데요. 업무방식이 변경되거나 작업자 수가 늘어날 경우 최대 수십만 건도 집계할 수 있어야 했기에, DB 리소스에 부하를 줄일 수 있는 조회하는 방식을 고민해야 했어요. (information_schema의 AVG_ROW_LENGTH 기준 1 row가 150byte 정도 이므로 45mb~75mb입니다.)
그래서 조회 최대 소요시간 목표를 1초로 설정하고, 1회 최대 목표 조회량에 해당하는 일자로 기간을 chunk로 나눠서 아래의 예시코드와 같이 병렬 호출하도록 작업을 하고, 지속적인 모니터링이 가능하도록 구현했어요.
5. 기술적 결과 및 향후 과제
데이터를 선택하고, 사전 작업을 통해 필요한 데이터를 보충하고, 가공 집계한 뒤 조회하는 일련의 과정을 거쳐서 최종적으로 승인 생산성과 근무 이력 집계 로직을 개발할 수 있었어요. 어떻게 보면 간단해 보일 수도 있지만, 각 과정마다 가성비 있는 선택을 하기 위해 위에 작성한 내용 외에도 테이블을 반 정규화를 하거나, 하나의 집계 로직으로 2개의 데이터를 만들어 내는 등 각 과정에서 고민을 많이 했었어요.
그 결과, 1개의 집계 로직으로 승인 생산성 데이터와 근무관리 데이터를 집계하는데 걸리는 시간은 8초 이내이며, 조회는 200ms 이내로 진행되고 있어요. 데이터의 양과 처리의 복잡도를 생각하면 만족할 만한 수준의 결과를 내고 있다고 보는데요. 그렇지만 앞으로도 DB 조회에 걸리는 시간이 기준치 3~5초를 넘어가진 않는지,한 번에 가져오는 데이터 수가 저희의 기준 목표량을 넘어가는지 등을 면밀하게 관찰하면서, 유지 보수를 해 나가는 계획을 세우고 있어요.
이렇게 잘 사용하고 있어요
생산성 지표 지면과 근무 이력 지면 제공을 통해 여러 의미 있는 결과가 만들어지고 있어요.
- 인적 자원 투입 비용 감소
- 생산성 집계 자동화: 기존에 수기로 집계하던 작업자의 생산성을 자동으로 조회할 수 있는 시스템으로 전환함으로써, 연간 약 5명의 인력 비용 절감이 가능해졌어요.
- 근무 기록 관리 자동화: 작업자 근무 기록을 수기로 정리하던 업무를 폐지하여, 연간 약 1명의 인력 비용 절감이 가능해졌어요.
- 실시간 인력 조정
- 실시간으로 각 업무그룹의 생산성을 모니터링하고, 생산성이 평균보다 낮은 업무그룹에서 인력을 재배치하여 승인 처리 지연을 방어하고 있는데요. 하나의 사례로, 9월 12일에 A 작업자는 1번 업무그룹에서 평균 생산성보다 낮은 생산성 77%를 보였으나 2번 업무그룹으로 이동하여 144%의 생산성을 기록했어요. 이 사례를 통해 작업자 개인 그리고 업무그룹 자체의 생산성을 높여 빠른 승인 처리에 기여하고 있는 것을 확인할 수 있어요.
- 데이터 기반의 의사결정
- 업무 레벨 평가 기준 설정: 업무그룹별 생산성 평균치를 확보하여, 이 데이터를 기준으로 업무 레벨을 평가할 수 있게되었어요. 이는 연간 인력 계약 시 계약 적정 인원 산출에 유용하게 사용될 예정이에요.
- 휴식 시간 기록: 다양한 종류의 휴식 시간을 기록함으로써, 업무 시간 외의 관리가 가능해졌어요. 이를 통해 직원의 휴식 패턴을 분석하고, 필요한 가이드를 수립하는 데 활용할 수 있게 되었어요.
이번 과제의 결과처럼 아직 SUPER2 어드민의 사용자 경험 향상과 회사 비용 절약에 기여할 수 있는 많은 부분이 남아있다고 생각해요. 앞으로도 다양한 부분에서 좋은 결과를 만들어 내어 다시 저의 경험담을 공유드릴수 있도록 할게요.