Big Project · AI's EYE 31
평균으로는 알 수 없는 매장 운영의 비밀: 이산사건 시뮬레이션으로 밝히다
방문객 수와 평균 제조시간을 곱하거나 나누면 대기시간을 빠르게 추정할 수 있을 것 같다. 왜 이산사건 simulation이 필요한지 모호하다.
근거 · 프로젝트 문서 · 코드 · 테스트
1장: 평균의 그림자: 왜 단순한 대기 시간 계산은 현실을 놓치나
솔라의 노트 위에는 간단한 계산 몇 줄이 적혀 있었다. 새로 생긴 동네 카페에 대한 생각이었다. 바리스타는 쉴 새 없이 움직였고, 원두 가는 소리와 스팀 밀크 내는 소리가 경쾌하게 공간을 채웠다. 하지만 솔라의 기억에 남은 것은 유독 길게 느껴졌던 대기 줄이었다.
“이상하단 말이야.”
솔라는 혼잣말을 중얼거리며 연필 끝으로 노트를 톡톡 두드렸다. 한 시간에 방문객 20명. 음료 한 잔을 만드는 데 평균 3분. 그렇다면 한 시간이면 60분이니까, 직원 한 명이 20잔을 충분히 만들 수 있다. 이론적으로는 줄을 설 필요가 없어야 했다. 오히려 시간이 남아야 정상이었다. 하지만 솔라가 30분 넘게 기다렸던 그날의 경험은 이 간단한 산수와 전혀 맞지 않았다.
“언니, 이것 좀 봐봐. 내가 계산이 약한가?”
생각에 잠겨 있던 루나가 솔라의 노트를 들여다보았다. 솔라가 방문객 수와 평균 제조 시간을 곱해놓은 것을 보고는 희미하게 미소 지었다.
“계산은 맞는데, 현실이 그 계산대로 움직여주지 않았나 보네.”
“그러니까 말이야. 사장님은 이 계산만 보고 직원을 한 명만 둔 걸까? 그럼 분명 매장이 제대로 안 돌아갈 텐데. 내가 갔을 때만 해도 대기 줄이 너무 길어서 그냥 나가는 사람도 있었어.”
솔라의 목소리에는 순수한 궁금증과 함께 약간의 답답함이 묻어났다. 평균값으로 세상을 이해하려는 시도가 눈앞의 현실 앞에서 힘없이 무너지는 순간이었다. 방문객 수와 평균 시간을 알면 모든 것을 예측하고 통제할 수 있을 것 같았지만, 어딘가 결정적인 구멍이 있었다.
루나는 솔라의 계산을 지적하는 대신, 다른 질문을 던졌다.
“솔라, 그 20명의 손님이 한 시간 동안 어떻게 매장에 들어왔어? 이를테면 3분마다 한 명씩, 시계처럼 정확하게 도착했어?”
“아니? 내가 갔을 땐 점심시간 직후라 그런지, 사람들 여럿이 우르르 들어왔어. 내 뒤로도 금방 대여섯 명이 더 줄을 섰고.”
“그럼 다른 시간은?”
“음… 내가 음료를 받아서 나올 때쯤엔 오히려 매장이 한산했어. 직원이 혼자 서 있더라고.”
루나는 솔라의 노트 빈 곳에 두 개의 시간 축을 나란히 그렸다. 그리고 첫 번째 시간 축 위에 일정한 간격으로 작은 점 20개를 찍었다.
“이게 솔라 네가 처음 생각한 세상이야. 한 시간에 20명, 즉 3분마다 한 명씩 손님이 꾸준히 오는 거지. 이 경우엔 직원이 주문을 받고 3분 안에 음료를 만들면 다음 손님이 도착하기 전에 일이 끝나. 대기 줄이 생길 틈이 없어.”
솔라는 고개를 끄덕였다. 그녀의 산수가 완벽하게 들어맞는 이상적인 상황이었다.
“하지만 현실은 두 번째 그림에 가깝지 않았을까?”
루나는 두 번째 시간 축의 앞부분, 짧은 구간 안에 점 10개를 빽빽하게 몰아서 찍었다. 그리고 나머지 긴 구간에 드문드문 남은 10개의 점을 찍었다.
솔라는 두 번째 그림을 가만히 들여다보았다. 순식간에 머릿속에서 그날의 풍경이 재구성되었다. 12시 10분, 한 무리의 손님들이 쏟아져 들어온다. 첫 번째 손님의 주문을 받는 순간, 이미 뒤에는 아홉 명의 대기 줄이 형성된다. 직원은 첫 번째 음료를 만드는 3분 동안 아홉 개의 주문을 그대로 짊어지고 있어야 한다. 두 번째, 세 번째 손님의 음료를 만드는 동안에도 줄은 쉽게 줄어들지 않는다. 초반 10분의 과부하가 전체 시스템을 마비시키는 것이다. 반면, 손님들의 발길이 뚝 끊긴 시간에는 직원이 할 일 없이 서 있게 된다.
“아…!”
솔라의 입에서 짧은 탄성이 터져 나왔다.
“같은 ‘한 시간에 20명’인데… 언제 오느냐에 따라 완전히 다른 이야기가 되는구나. 내가 한 계산은 모든 손님이 서로 약속이라도 한 듯 일정한 간격으로 도착한다는, 말도 안 되는 가정을 깔고 있었던 거야.”
솔라는 자신의 노트에 적었던 ‘20명 x 3분 = 60분’ 이라는 계산을 연필로 지워버렸다. 그 숫자는 더 이상 현실을 설명하는 근거가 될 수 없었다. 평균값은 마치 모든 굴곡을 매끄럽게 다림질해버린 것처럼, 고객이 한꺼번에 몰려드는 ‘피크 타임’과 주문이 없어 한가한 ‘다운 타임’의 차이를 완전히 지워버렸다. 그 결과 ‘대기열’이라는 현실의 고통은 보이지 않는 유령이 되어버렸다.
평균은 현상의 단면만을 보여줄 뿐, 시간의 흐름 속에서 벌어지는 변화와 상호작용은 담아내지 못한다. 솔라는 이제 평균이라는 숫자의 그림자 뒤에 무엇이 숨어 있는지 어렴풋이 짐작할 수 있었다.
생각을 정리한 솔라가 다시 입을 열었다.
“좋아, 평균 계산이 현실의 운영 흐름을 설명할 수 없다는 건 알겠어. 그럼 이런 ‘몰림’ 현상이나, 손님들이 줄을 서다 지쳐서 포기하고 나가버리는 상황까지 제대로 예측하고 대비하려면 어떻게 해야 해? 단순히 평균보다 더 복잡한 계산식으로 해결할 수 있는 문제일까? 아니면 완전히 다른 방식으로 접근해야 하는 거야?”
2장: 시간 순서에 따른 시스템 이해: 이산사건 모델링의 탄생
솔라는 노트를 펼쳤지만, 어제 썼던 페이지는 그냥 넘겨버렸다. ‘20명 x 3분’이라는 계산에 가차 없이 그어진 취소선이 눈에 들어왔지만, 더 이상 붙들고 있을 문제가 아니었다. 솔라는 새 페이지에 연필을 가져갔다. 평균이라는 뭉뚱그려진 숫자를 버렸으니, 이제 그 너머를 봐야 했다. ‘몰림’과 ‘기다림’과 ‘포기’를 담아낼 다른 방법이 필요했다.
솔라는 카페에서 보았던 한 명의 고객을 떠올리며 그 여정을 시간 순서대로 적기 시작했다.
- 매장에 들어온다.
- 메뉴를 보고 주문한다.
- 자기 순서를 기다린다.
- 음료를 받는다.
- 자리에 앉아 마시거나, 가지고 나간다.
목록은 금세 완성되었지만, 솔라는 만족스럽지 않았다. 어딘가 텅 비어 있는 느낌이었다. 이것 역시 현실을 제대로 담아내지 못하는 것 같았다. 이 목록만으로는 왜 어떤 날은 줄이 길고, 어떤 날은 한가한지 설명할 수 없었다. 그저 ‘손님은 이런 일을 겪는다’는 당연한 사실의 나열일 뿐이었다.
솔라의 고민을 읽기라도 한 듯, 옆에서 조용히 책을 보던 루나가 노트 위로 시선을 옮겼다.
“단순히 순서를 나열하는 것만으로는 부족하다고 느끼는구나.”
“응. 뭔가 중요한 게 빠졌어. 이건 그냥… 고객의 일기 같은 거잖아. 이 목록이 어제 그렸던 ‘몰려드는 손님’ 그림의 문제를 해결해주진 못해. 이건 그냥 또 다른 종류의 단순한 요약일 뿐인걸.”
솔라는 자신이 만든 목록이 여전히 ‘평균의 그림자’에서 벗어나지 못했다고 느꼈다. 고객 한 명의 경험을 시간 순서대로 나열했지만, 그 경험이 다른 고객, 그리고 바리스타의 행동과 어떻게 얽혀 시스템 전체에 영향을 미치는지 전혀 보여주지 못했다.
루나는 솔라의 목록에서 ‘3. 자기 순서를 기다린다’는 항목을 손가락으로 가리켰다.
“이 ‘기다림’은 왜 발생할까? 그리고 이 기다림이 발생하는 동안, 바리스타는 뭘 하고 있을까?”
“바리스타는… 다른 손님의 음료를 만들고 있겠지.”
“바로 그거야. 솔라, 네 목록에는 ‘고객’이라는 배우만 등장해. 하지만 카페라는 무대에는 ‘바리스타’라는 또 다른 중요한 배우가 있잖아. 두 배우의 행동이 시간 속에서 어떻게 맞물리는지를 봐야 해.”
루나는 솔라의 노트 옆에 새로운 칸을 만들었다. 왼쪽에는 ‘고객의 사건’, 오른쪽에는 ‘바리스타의 상태’라고 적었다.
“다시 해보자. 첫 번째 고객, ‘솔라 1호’가 카페에 도착하는 순간부터.”
솔라는 다시 연필을 잡았다. 루나의 말에 따라, 이번에는 한 명의 고객과 한 명의 바리스타가 겪는 시간의 흐름을 쪼개서 따라가 보기로 했다.
- 오전 10:00:00 - 고객 1 도착 (
arrival). 바리스타는 ‘대기중(idle)’. - 오전 10:00:15 - 고객 1 주문 완료 (
order_received). 바리스타는 ‘주문 처리중(busy)’ 상태로 바뀐다. 음료 제조 시작. - 오전 10:01:00 - 고객 2 도착 (
arrival). 바리스타는 여전히 고객 1의 음료를 만드는 중이다. 고객 2는 기다려야 한다. ‘대기열(queue)’이 처음으로 생겼다. - 오전 10:03:15 - 고객 1의 음료 완성 (
ready). 바리스타는 ‘대기중(idle)’ 상태로 돌아온다. 하지만 쉴 틈도 없이, 대기열에 있는 고객 2의 주문을 처리해야 한다. - 오전 10:03:30 - 바리스타는 고객 2의 주문을 받아 다시 ‘주문 처리중(busy)’ 상태가 된다. 대기열은 0이 된다.
- 오전 10:04:00 - 고객 3, 4 동시 도착 (
arrival). 바리스타는 고객 2의 음료를 만드는 중. 대기열 길이는 순식간에 2가 된다.
“아…!”
솔라는 방금 전 자신이 나열한 기록을 보고 깨달음을 얻었다. 이것은 더 이상 단순한 순서 나열이 아니었다. ‘도착(arrival)’, ‘주문(order)’, ‘대기(queued)’, ‘완성(ready)’ 같은 하나하나의 개별적인 **사건(event)**이 발생할 때마다, ‘바리스타의 상태(유휴/바쁨)’나 ‘대기열의 길이’ 같은 시스템의 **상태(state)**가 실시간으로 변하고 있었다.
고객의 행동 하나하나가 시스템 전체에 파장을 일으키는 연쇄 반응의 시작점이었던 것이다. 고객이 도착하는 사건, 주문하는 사건, 바리스타가 음료를 만들기 시작하는 사건, 음료를 완성하는 사건. 이산적(discrete)으로 뚝뚝 끊어져 일어나는 이 사건들이 시간 순서대로 엮이면서, 대기열이라는 동적인 현상을 만들어내고 있었다.
“이제 알겠어. 이건 그냥 고객의 행동을 나열한 게 아니야. 시스템의 상태를 바꾸는 결정적인 순간들을 시간 순서대로 포착하는 거구나. 마치… 각본 같아. ‘고객 1 등장’, ‘바리스타, 커피 머신 사용 시작’ 같은 지시문들이 시간대별로 촘촘히 적힌 각본.”
솔라는 자신의 새로운 목록을 뿌듯하게 바라보았다. ‘고객 도착부터 퇴장까지 시간 순서가 있는 이벤트로 계산한다’는 문장이 이제야 비로소 입체적으로 다가왔다. 그것은 복잡한 수식이 아니라, 현실을 가장 잘 모방하기 위한, 지극히 현실적인 방법이었다. 평균 계산이 뭉개버렸던 시간의 결, 그 안에서 벌어지는 상호작용의 드라마를 ‘이벤트’라는 단위로 복원해낸 것이다.
생각이 여기까지 미치자, 새로운 궁금증이 고개를 들었다.
“좋아, 이렇게 시간 순서대로 모든 사건을 기록하는 각본을 만들면, 평균 계산이 놓쳤던 것들을 볼 수 있다는 건 알겠어. 그런데… 그래서 이 각본이 우리에게 뭘 알려주는 거지? 이 촘촘한 사건 목록이 어떻게 ‘평균 대기 시간은 15분입니다’ 라거나, ‘바리스타 한 명을 더 채용해야 합니다’ 같은 구체적인 답으로 바뀔 수 있는 거야? 이건 그냥… 상황을 더 복잡하게 묘사한 것에 불과한 건 아닐까?”
3장: 운영의 복잡성을 해독하다: 이벤트 모델의 진정한 가치
솔라는 자신이 만든 시간 순서 목록, ‘카페 운영 각본’을 가만히 내려다보았다. 어제와는 다른 뿌듯함이 있었지만, 동시에 풀리지 않는 의문이 머릿속을 맴돌았다. 페이지를 가득 채운 ‘고객 도착’, ‘주문 접수’, ‘대기열 발생’ 같은 사건의 흐름은 분명 평균 계산이 놓쳐버린 현실의 역동성을 담고 있었다. 하지만 그래서 뭘 어떡하라는 것인지 알 수 없었다. 이 촘촘한 사건 기록은 그저 현상을 더 복잡하게 묘사한 사진일 뿐, 미래를 예측하거나 문제를 해결하는 지도가 되지는 못했다.
솔라는 노트의 빈 공간에 볼펜으로 낙서를 시작했다. ‘그래서, 바리스타를 한 명 더 뽑아야 하나?’, ‘그래서, 평균 대기시간은 몇 분이라는 거지?’. 꼬리에 꼬리를 무는 질문들은 결국 하나의 답답함으로 귀결되었다. 이 각본이 저 질문들에 답을 줄 수 있을까? 솔라는 고개를 저었다. 이건 그냥 상황을 더 복잡하게 바라보는 것일 뿐, 근본적인 해결책을 주는 도구는 아닌 것 같았다.
“각본은 완성됐는데, 어떤 결말을 맞을지 알 수가 없네.”
솔라의 혼잣말에 루나가 솔라의 노트를 들여다봤다. 노트 한쪽에는 어제 만들었던, 시간대별로 촘촘히 적힌 사건 목록이 있었고, 다른 한쪽에는 솔라의 답답함이 담긴 낙서 같은 질문들이 흩어져 있었다.
“좋은 각본이야, 솔라. 이제 이 각본으로 연극을 상연해볼 차례네.”
“연극?”
“응. 배우와 무대 장치는 모두 준비됐어. ‘고객’과 ‘바리스타’라는 배우가 있고, ‘커피 머신’이라는 무대 장치도 있지. 하지만 중요한 걸 빠뜨렸어. 이 배우들이 동시에 사용할 수 없는 한정된 자원, 즉 ‘자원(Resource)’이라는 개념 말이야.”
루나는 솔라의 ‘바리스타’라는 단어에 동그라미를 쳤다.
“바리스타는 한 명뿐이야. 동시에 두 명의 고객을 응대할 수 없어. 커피 머신도 한 대뿐이라면, 동시에 두 개의 음료를 만들 수 없지. 이게 바로 ‘자원 경합’이야. 네가 만든 각본 속 대기열은 바로 이 경합 때문에 생겨나는 결과물이고.”
루나는 말을 이으며, ‘매장 내 좌석’이라는 단어를 새로 적었다. “좌석도 마찬가지야. 좌석은 20개뿐인데, 21번째 손님은 앉을 자리가 없어. 이것은 ‘제약(Constraint)’이지.”
자원, 제약. 솔라는 그 단어들을 입안에서 굴려보았다. 지금까지는 그저 ‘상황’이라고만 생각했던 것들에 이름이 붙는 순간이었다. 바리스타의 수, 커피 머신의 개수, 매장의 좌석 수. 이 모든 것이 시스템의 행동을 결정하는 핵심 변수였다.
“자, 그럼 이 각본으로 시뮬레이션, 즉 모의실험을 해보는 거야. 우리가 직접 컴퓨터가 되는 거지.” 루나가 제안했다. “이 각본을 수십, 수백 번 반복해서 상연한다고 상상해봐. 그리고 우리는 연극이 끝날 때마다 중요한 숫자들을 기록하는 거야.”
루나는 솔라의 노트에 새로운 표를 그리기 시작했다.
| 시나리오 | 직원 수 | 완료된 주문 수 | 포기한 고객 수 | 최대 대기열 길이 | 평균 대기 시간 | 직원 활용률 (%) |
|---|---|---|---|---|---|---|
| A (현재) | 1명 | ? | ? | ? | ? | ? |
| B (가설) | 2명 | ? | ? | ? | ? | ? |
“시나리오 A는 솔라 네가 처음 봤던 그 카페의 현실이야. 바리스타 한 명. 우리가 이 이벤트 각본대로 하루 영업을 시뮬레이션하면, ‘음료를 받아서 나간 고객’과 ‘기다리다 지쳐서 나가버린 고객’ 수를 셀 수 있지 않겠어? 대기열이 가장 길었을 때 몇 명이었는지도 기록할 수 있고, 고객들이 평균적으로 얼마나 기다렸는지도 계산할 수 있어. 그리고 바리스타가 하루 종일 얼마나 바쁘게 일했는지, 즉 ‘활용률’도 알 수 있게 돼.”
솔라의 눈이 커졌다. 흩어져 있던 사건들이 ‘자원’과 ‘제약’이라는 규칙 아래 움직이기 시작하자, 그 결과물로 측정 가능한 지표들이 생성되는 모습이 머릿속에 그려졌다. 이것은 더 이상 복잡한 묘사가 아니었다. 원인(직원 수, 고객 도착 패턴)과 결과(대기 시간, 포기율)를 잇는 명확한 인과관계의 사슬이었다.
“그럼… 시나리오 B는?”
“그래, 바로 그게 이 모델의 진짜 가치야.” 루나가 미소 지었다. “우리가 만약 바리스타를 한 명 더 채용해서 두 명이 일하게 한다면 어떻게 될까? 그걸 현실에서 실험해보기 전에, 이 각본으로 먼저 돌려보는 거야. 바리스타라는 자원이 2개로 늘었을 때, 각본의 흐름이 어떻게 바뀌는지 따라가 보는 거지. 아마 대기열이 훨씬 짧아지고, 포기하는 고객도 줄어들 거야. 대신 직원 활용률은 좀 낮아지겠지. 사장님은 그 결과를 보고 비용과 효용을 따져서 결정을 내릴 수 있게 돼.”
솔라는 비로소 깨달았다. 이산사건 시뮬레이션은 단순히 현실을 복잡하게 묘사하는 것이 아니었다. 현실을 움직이는 핵심 규칙(이벤트, 자원, 제약)을 바탕으로 ‘있을 법한 미래’를 수없이 만들어보고, 그 결과를 구체적인 숫자로 비교하여 최적의 의사결정을 내리도록 돕는 강력한 실험 도구였다. 평균값이라는 단 하나의 숫자에 갇혀 있던 시야에서 벗어나, 시스템 전체를 조망하며 다양한 ‘만약에’ 시나리오를 탐색할 수 있게 된 것이다.
“알겠다…! 이건 그냥 상황을 기록한 게 아니라, 일종의 ‘운영 분석 엔진’이었구나.”
솔라는 처음 카페 문제를 고민하며 적었던 노트를 다시 펼쳤다. ‘방문객 20명, 평균 제조시간 3분’. 이제 그 숫자들은 무의미했다. 솔라는 그 옆에 새로운 분석의 틀을 짜기 시작했다. 이것이 진짜 문제를 해결하기 위한 첫걸음이었다.
[솔라의 새로운 카페 분석 모델]
- 이벤트(Events): 고객 도착, 주문, (대기), 음료 제조 시작, 음료 완성, 좌석 착석, 퇴장, (기다리다 포기)
- 자원(Resources):
- 바리스타: 수용량 1명 (경합 대상)
- 에스프레소 머신: 수용량 1대 (경합 대상)
- 제약(Constraints):
- 매장 내 좌석: 12개
- 고객의 최대 인내 시간: 10분 (가정)
- 핵심 성과 지표(KPIs):
- 일일 총 주문 완료 수
- 일일 총 포기 고객 수
- 평균/최대 고객 대기 시간
- 바리스타/머신 자원 활용률
- 시간대별 대기열 길이 변화
자신의 손으로 직접 쓴 목록을 보며 솔라는 확신했다. 이제 더 이상 ‘평균의 그림자’에 갇혀 헤매지 않을 수 있었다. 진짜 문제를 진단하고, 현실적인 해결책을 찾아 나설 준비가 되었다.