Big Project · AI's EYE 32

직원 수 효과 측정을 위한 시뮬레이션 조건 통제

직원 1명 조건과 2명 조건을 각각 여러 번 simulation해 평균을 비교하면 공정해 보인다. 왜 같은 고객 trace를 재사용해야 하는지 궁금하다.

근거 · 프로젝트 문서 · 코드 · 테스트

1장: 무작위성은 친구인가, 적인가? 시뮬레이션 비교의 함정

솔라는 모니터에 떠 있는 두 개의 숫자 덩어리를 번갈아 보며 미간을 찌푸렸다. 하나는 ‘직원 1명’ 시나리오의 결과였고, 다른 하나는 ‘직원 2명’ 시나리오의 결과였다. 직원 한 명을 더 늘리면 고객 대기 시간이 얼마나 줄어들지 예측해보는 시뮬레이션이었다.

“이상하네….”

결과는 예상대로였다. 직원이 두 명일 때 고객의 평균 대기 시간이 훨씬 짧았다. 하지만 솔라는 어쩐지 개운하지 않은 표정으로 의자에 등을 기댔다. 혼잣말이 불쑥 튀어나왔다.

“결과 차이가 이렇게 많이 나는 게 맞나?”

옆에서 조용히 책을 읽던 루나가 고개를 들었다. 솔라는 기다렸다는 듯이 화면을 가리키며 말했다.

“언니, 이것 좀 봐. 직원 한 명일 때랑 두 명일 때 시뮬레이션을 각각 백 번씩 돌려서 평균을 낸 거야. 당연히 두 명일 때가 대기 시간이 짧게 나오긴 했는데, 이 결과가 정말 ‘직원 수’ 때문에 생긴 차이라고 말할 수 있을까? 그냥 궁금해서.”

“왜 그런 생각이 들어?”

“음… 시뮬레이션을 돌릴 때마다 고객이 얼마나 올지는 무작위로 정해지잖아. 내가 백 번씩이나 돌린 건, 혹시 한두 번의 시뮬레이션에서 우연히 손님이 몰리거나 너무 적게 와서 결과가 왜곡되는 걸 막고 싶어서였어. 많이 반복하면 그런 ‘운’적인 요소는 평균 속에서 희석될 거라고 생각했지. 소위 말하는 ‘대수의 법칙’처럼 말이야.”

솔라의 설명은 지극히 합리적으로 들렸다. 무작위적인 사건도 횟수를 늘리면 특정 확률에 수렴한다는 것은 상식에 가깝다. 솔라는 바로 그 점 때문에 자신의 결과에 의심을 품고 있었다.

“그런데 봐봐. 직원 1명 시나리오의 결과 중에는 유난히 고객 대기 시간이 짧게 나온 게 있어. 반대로 직원 2명 시나리오인데도 대기 시간이 길게 나온 것도 있고. 물론 평균을 내면 직원 2명 쪽이 훨씬 좋지만, 어쩌면… 직원 1명 시나리오를 돌릴 땐 우연히 한가한 날이 많이 걸리고, 직원 2명 시나리오를 돌릴 땐 바쁜 날이 많이 걸렸을 수도 있는 거 아닐까? 내가 백 번이나 돌렸다고 해서 그 ‘운’이 공평하게 배분되었다고 어떻게 확신하지?”

솔라의 질문은 더 이상 ‘평균’이라는 방패 뒤에 숨을 수 없었다. 루나는 책을 덮고 잠시 생각하더니, 책상 서랍에서 작은 주사위 두 개를 꺼내 솔라 앞에 놓았다.

“재미있는 생각인데. 우리 아주 간단한 게임을 한번 해볼까?”

루나는 하얀 메모지 두 장을 가져와 각각 ‘가게 A (직원 1명)’, ‘가게 B (직원 2명)’이라고 썼다.

“주사위를 던져서 나오는 눈의 수가 그 시간대에 도착하는 고객의 수라고 해보자. 솔라 네가 먼저 가게 A를 운영해봐. 주사위를 다섯 번 던져서 오늘 하루 동안 올 고객들을 정하는 거야.”

솔라는 고개를 갸웃하며 주사위를 손에 쥐었다. 첫 번째 던짐. 숫자 2가 나왔다. 두 번째는 1, 세 번째도 2. 이어서 3, 1이 나왔다.

가게 A (직원 1명): 고객 수 -> 2, 1, 2, 3, 1

“음, 별로 안 바빴네. 이 정도는 혼자서도 충분하지.” 솔라가 어깨를 으쓱하며 말했다.

“좋아. 이제 가게 B를 운영할 차례야. 똑같이 주사위를 다섯 번 던져서 고객 수를 정해봐.”

솔라는 다시 주사위를 던졌다. 이번에는 전혀 다른 숫자들이 나왔다. 6, 5, 6, 4, 5.

가게 B (직원 2명): 고객 수 -> 6, 5, 6, 4, 5

솔라는 두 메모지에 적힌 숫자를 비교해보고는 순간 할 말을 잃었다.

”…이건,”

“어때? 가게 B는 직원이 두 명이나 있었는데도 꽤 힘들었을 것 같지 않아?” 루나가 조용히 물었다.

솔라의 얼굴에 아까의 의구심이 명확한 깨달음으로 바뀌는 순간이 스쳐 지나갔다.

“잠깐만. 이건 비교가 안 되잖아. 가게 A는 한가한 날이었고, 가게 B는 역대급으로 바쁜 날이었네. 가게 B의 대기 시간이 가게 A보다 길게 나왔다고 해서 ‘직원이 두 명인데도 운영을 못하네’라고 말할 수는 없어. 애초에 비교하는 조건이 다르니까. 출발선이 달라.”

솔라는 자신의 시뮬레이션 결과가 떠 있는 모니터를 다시 쳐다보았다. 백 번의 시뮬레이션, 수많은 숫자들의 평균. 그 복잡한 과정의 본질이 방금 던진 몇 번의 주사위 놀이와 다르지 않았다.

“알겠다. 내가 한 실수가 뭔지. 나는 직원 1명일 때와 2명일 때라는 두 ‘조건’을 비교하고 싶었던 건데, 실제로는 ‘한가한 날의 직원 1명’과 ‘바쁜 날의 직원 2명’을 비교하고 있었을 수도 있는 거구나. 고객 수요라는, 내가 통제하지 않은 변수가 멋대로 끼어들어서 결과를 흐리고 있었던 거야.”

솔라는 방금 자신이 뱉은 말을 곱씹었다. 무작위성은 현실을 반영하는 중요한 도구지만, 두 가지 조건을 ‘공정하게’ 비교하는 실험에서는 통제해야 할 적이 될 수도 있었다. 솔라의 머릿속에 새로운 질문이 떠올랐다.

“그렇다면, 이 ‘운’을 어떻게 제거하지? 직원 한 명의 효과를 정확히 보려면… 두 시나리오가 완전히 똑같은 날, 똑같은 고객들을 상대하게 만들어야 해. 어떻게 하면 시뮬레이션이 매번 똑같은 고객들을 보내주게 할 수 있을까?“

2장: 과학적 비교를 위한 선택: 동일 고객 트레이스 재사용

솔라는 전날의 깨달음이 남긴 두 장의 메모지와 주사위들을 물끄러미 바라보았다. ‘가게 A (직원 1명)’와 ‘가게 B (직원 2명)’. 그리고 그 아래에 적힌, 서로 완전히 달랐던 고객들의 숫자. 이 종잇조각들은 불공정한 비교의 명백한 증거였다.

솔라는 잠시 생각에 잠기더니, 주사위들을 손에 쥐었다. 하지만 던지는 대신, 옆에 있던 깨끗한 노트 한 페이지를 펼쳤다. 그리고는 결심한 듯, 주사위를 딱 한 번만 던져 나온 숫자 ‘6’을 노트 첫 줄에 적었다. 다시 던져 ‘5’를, 그 다음엔 ‘6’, ‘4’, ‘5’를 차례로 적어 내려갔다. 이것은 지난번 가게 B를 혼란에 빠뜨렸던 바로 그 ‘역대급으로 바쁜 날’의 고객 기록이었다.

솔라는 다섯 개의 숫자로 이루어진 이 목록 옆에 두 개의 빈칸을 만들었다. 하나는 ‘직원 1명일 때’, 다른 하나는 ‘직원 2명일 때’라고 이름 붙였다. 이제 두 가게는 같은 출발선에 서 있었다. 더 이상 서로 다른 날씨를 탓할 수 없는, 완벽히 통제된 경기장이었다.

“언니, 지난번엔 완전히 잘못 생각했어. 가게 A와 가게 B가 완전히 다른 날을 겪게 했으니까. 그래서 이번엔 이렇게 해보려고.”

솔라는 자신만만하게 노트를 루나에게 보여주었다.

“오늘 우리 가게에 올 손님 목록을 아예 이렇게 미리 만들어버리는 거야. 그리고 이 똑같은 손님 목록을 가지고, 직원 한 명일 때랑 두 명일 때 각각 어떻게 되는지 지켜보는 거지. 이래야 공평하잖아.”

루나는 솔라의 노트를 흥미롭게 들여다보았다. 솔라가 직접 만든, 세상에 하나뿐인 ‘오늘의 고객 시나리오’였다.

“네가 방금 한 게, 바로 우리가 하려던 이야기의 핵심이야, 솔라. 시뮬레이션에서 ‘공정한 비교’를 하기 위한 아주 중요한 첫걸음이지.”

루나는 솔라의 노트에 적힌 숫자 목록을 손가락으로 가리켰다.

“컴퓨터 시뮬레이션에서는 이 고객 목록을 ‘수요 트레이스(Demand Trace)’라고 불러. 특정 기간 동안 어떤 고객이, 언제 도착해서, 무엇을 주문하는지에 대한 모든 정보가 담긴 하나의 시나리오 스크립트 같은 거지. 그리고 중요한 건, 방금 네가 한 것처럼, 비교하고 싶은 모든 조건—예를 들면 직원 한 명 시나리오와 직원 두 명 시나리오—에 바로 이 동일한 수요 트레이스를 재사용하는 거야.”

솔라는 ‘수요 트레이스’라는 단어를 입안에서 굴려보았다. 막연했던 자신의 아이디어에 이름이 붙는 순간이었다. 하지만 이내 새로운 의문이 고개를 들었다.

“잠깐만. 그렇게 고객이 오는 순서랑 시간을 완전히 고정해버리면… 너무 인위적인 거 아니야? 현실의 가게는 매일매일이 다르잖아. 시뮬레이션이 현실을 최대한 비슷하게 흉내 내야 의미가 있는 거 아니었어?”

솔라의 지적은 날카로웠다. 무작위성을 제거하는 것이 오히려 시뮬레이션의 현실성을 해치는 것처럼 보일 수 있었다.

“목표가 무엇인지 다시 생각해보자.” 루나가 차분하게 말했다. “우리는 ‘내일 우리 가게에 정확히 몇 명이 올까?‘를 예측하려는 게 아니야. 우리는 ‘직원을 한 명 늘리는 결정이, 고객 대기 시간에 어떤 영향을 미칠까?‘라는 아주 구체적인 질문의 답을 찾고 있어. 이 질문에 답하려면, 우리가 바꾸는 딱 한 가지 변수, 즉 ‘직원 수’를 제외한 다른 모든 조건은 변하지 않아야 해. 그래야 결과의 차이가 오롯이 그 변수 때문이라고 말할 수 있으니까.”

루나는 비유를 들어 설명했다.

“새로운 비료의 효과를 실험하는 과학자를 생각해봐. 똑같은 씨앗으로 심은 두 개의 화분을 준비하고, 하나에만 새 비료를 주겠지. 이때 두 화분에 똑같은 양의 햇빛과 물을 주지 않으면 어떻게 될까? 식물이 더 잘 자란 게 비료 덕분인지, 햇빛을 더 받아서인지 알 수 없게 돼. 여기서 햇빛과 물이 바로 시뮬레이션의 ‘수요 트레이스’ 같은 거야. 비교의 바탕이 되는, 고정되어야 할 조건이지.”

‘통제된 실험 환경.’ 솔라의 머릿속에 단어가 떠올랐다.

“아…! 알겠다. 내가 만든 이 고객 목록은 ‘현실의 어떤 하루’를 그대로 복사한 게 아니라, 비교 실험을 위한 ‘표준 시험지’ 같은 거구나. 수많은 가능한 ‘하루’들 중에서, 가령 ‘꽤 바쁜 날’이라는 시나리오를 하나 정하고, 그 동일한 시험지로 직원 한 명짜리 가게와 두 명짜리 가게를 각각 테스트하는 거네. 그래야 점수 차이가 순수하게 실력 차이, 즉 직원 수의 효과라고 말할 수 있으니까.”

솔라는 자신의 노트를 다시 보았다. 이제 숫자 목록은 단순한 주사위 눈의 나열이 아니었다. 그것은 직원 수의 효과를 측정하기 위해 세심하게 설계된 ‘동일 수요 추적 설계’의 축소판이었다. 무작위라는 안개를 걷어내고, 비교하려는 대상의 민낯을 드러내기 위한 과학적 장치.

문득 솔라의 표정이 다시 진지해졌다. 이해는 했지만, 현실적인 문제가 남았다.

“좋아, 원칙은 알겠어. 그런데… 이건 주사위랑 종이니까 내 눈으로 확인할 수 있잖아. 복잡한 컴퓨터 시뮬레이션에서 이 방법이 제대로 적용됐는지는 어떻게 알지? 내가 ‘직원 1명’ 시나리오와 ‘직원 2명’ 시나리오를 각각 돌렸을 때, 시스템이 정말로 같은 고객 목록, 같은 ‘수요 트레이스’를 사용했다는 걸 어떻게 믿을 수 있어?“

3장: 직원 수 효과, 그 순수한 결과: 검증과 해석

솔라는 두 개의 시뮬레이션 결과 로그가 떠 있는 모니터 앞에서 손가락을 멈췄다. 왼쪽은 ‘직원 1명’ 시나리오, 오른쪽은 ‘직원 2명’ 시나리오. 어제 루나와 이야기하며 깨달은 ‘동일 수요 추적 설계’ 원칙에 따라, 두 시뮬레이션은 같은 고객 시나리오, 즉 같은 ‘수요 트레이스’를 바탕으로 실행되었다. 적어도, 솔라는 그렇게 되도록 설정했다.

문제는 신뢰였다. 솔라는 자신의 설정을, 그리고 시스템을 온전히 믿을 수 없었다. 그녀는 눈으로 두 로그 파일을 비교하며 스크롤을 내리고 있었다. 첫 번째 고객의 도착 시간, 두 번째 고객의 도착 시간… 수백 개의 이벤트 로그 속에서 두 시나리오의 ‘고객 도착’ 이벤트가 정말로 동일한지 확인하려는 시도였다. 하지만 몇 줄 내려가지 않아 눈이 아찔해졌다.

“이건… 거의 똑같아 보이는데. 하지만 수천 줄이나 되는 로그에서 내가 뭔가를 놓쳤다면? 딱 한 명의 고객 정보만 달라도, 내 비교는 또다시 불공평해지는 거잖아.”

솔라는 결국 비교를 포기하고 의자에 등을 기댔다. 눈에 보이지 않는 컴퓨터의 내부 동작을 어떻게 믿어야 할지 막막했다. ‘동일한 시험지’라는 원칙은 완벽하게 이해했지만, 두 응시자가 정말 같은 시험지를 받았는지 검사하는 방법을 몰랐다.

솔라의 고민을 지켜보던 루나가 조용히 다가와 화면의 한 부분을 가리켰다. 솔라가 무의미한 식별자라고 여기며 지나쳤던 텍스트였다.

demand_trace_id: trace__XYZ_123

루나는 아무 말 없이 솔라가 보고 있던 다른 쪽 로그 파일에서도 같은 부분을 손가락으로 짚었다. 거기에도 같은 문자열이 적혀 있었다.

demand_trace_id: trace__XYZ_123

“이 ID들이 단지 로그를 정리하기 위한 꼬리표라고 생각해?” 루나가 처음으로 입을 열었다.

솔라는 잠시 생각에 잠겼다. “음… 그냥 각 시뮬레이션 실행에 부여된 고유한 이름 같은 거라고 생각했어. 두 시뮬레이션에 같은 수요 트레이스를 쓰라고 했으니까, 당연히 같은 ID가 붙었겠지. 하지만 이건 그냥 이름표일 뿐, 실제 내용물까지 같다는 증거가 되진 않잖아.”

“그럴까? 그 이름표가 내용물을 보증하는 도장이라면 어떨까?” 루나가 질문을 던졌다. “눈으로 모든 줄을 비교하는 대신, 그 도장을 믿고 확인하는 방법은 없을까?”

루나는 키보드를 끌어당겨 간단한 명령어 하나를 입력했다. 두 개의 로그 파일에서 ‘order_received(주문 접수)’ 이벤트이면서, 동시에 특정 ‘demand_trace_id’를 포함하는 줄만 걸러내는 명령어였다. 엔터 키를 누르자, 화면에 새로운 결과가 나타났다.

[직원 1명 시뮬레이션 결과]

... event_type: order_received, order_id: 1, arrival_time: 09:00:03, event_demand_trace_id: trace_XYZ_123 ...
... event_type: order_received, order_id: 2, arrival_time: 09:01:15, event_demand_trace_id: trace_XYZ_123 ...
... event_type: order_received, order_id: 3, arrival_time: 09:01:48, event_demand_trace_id: trace_XYZ_123 ...

[직원 2명 시뮬레이션 결과]

... event_type: order_received, order_id: 1, arrival_time: 09:00:03, event_demand_trace_id: trace_XYZ_123 ...
... event_type: order_received, order_id: 2, arrival_time: 09:01:15, event_demand_trace_id: trace_XYZ_123 ...
... event_type: order_received, order_id: 3, arrival_time: 09:01:48, event_demand_trace_id: trace_XYZ_123 ...

솔라는 숨을 죽이고 두 결과를 비교했다. 스크롤을 끝까지 내려도, 두 목록은 토씨 하나 다르지 않고 완벽하게 일치했다. 똑같은 주문 ID가, 똑같은 시간에, 똑같은 수요 트레이스 ID를 달고 시스템에 접수되었다. 의심의 여지 없이, 두 가게는 완전히 동일한 손님들을 맞이한 것이었다.

“아…!”

솔라의 입에서 짧은 탄성이 터져 나왔다. demand_trace_id는 단순한 꼬리표가 아니었다. 그것은 이 실험의 공정성을 보증하는 ‘공증된 인감’이었다. 시스템은 이 ID를 통해 모든 ‘주문 접수’ 이벤트가 사전에 정의된 시나리오와 일치하는지 스스로 검증하고 있었던 것이다.

“이제 알겠어. 이 ID는 ‘우리는 이 시나리오 대본을 따랐습니다’라는 시스템의 선서 같은 거구나. 내가 할 일은 모든 대사를 일일이 확인하는 게 아니라, 두 배우가 같은 대본을 들고 연기했는지, 그 대본의 ID만 확인하면 되는 거였어.”

솔라는 이번에는 ‘order_completed(주문 완료)’ 이벤트를 같은 방식으로 필터링했다. 예상대로, 결과는 달랐다. 같은 1번 주문이라도, 직원이 두 명인 가게에서 훨씬 빨리 처리되어 ‘주문 완료’ 이벤트가 더 일찍 발생했다.

이 명확한 차이 앞에서 솔라는 미소를 지었다. 마침내 무작위라는 안개를 걷어내고, 비교하고 싶었던 대상의 순수한 모습을 마주한 순간이었다.

“이제야 확실히 말할 수 있겠네.”

솔라는 첫날, 무작위성에 대한 찝찝함 속에서 만들었던 결과 보고서를 열었다. ‘독립적인 랜덤 시뮬레이션 100회 평균’이라는 제목 아래 그려진, 신뢰할 수 없었던 그래프. 솔라는 그 그래프를 망설임 없이 삭제했다.

그리고 방금 확인한, ‘trace_XYZ_123’이라는 동일한 시험지로 치른 두 시뮬레이션의 결과를 입력했다. 고객 대기 시간, 주문 완료율, 포기율… 모든 지표의 차이가 이제는 ‘운’ 때문이 아닌, 오직 ‘직원 수 한 명’의 차이에서 비롯된 결과임이 명백했다.

새로운 그래프가 화면에 그려졌다. 전보다 차이는 더 명확했고, 그 의미는 더 단단했다.

“이제 이 결과는 믿을 수 있어. 직원 한 명을 더 늘리는 것은, 이런 ‘꽤 바쁜 날’을 기준으로 고객 대기 시간을 평균 3분 12초 단축시키는 효과가 있다.”

솔라는 자신의 마지막 문장을 나지막이 읽어보았다. 그것은 더 이상 추측이나 평균에 기댄 막연한 예상이 아니었다. 통제된 조건 아래에서 과학적으로 검증된, 하나의 단단한 사실이었다.