Big Project · AI's EYE 09

삭제 매장의 과거 이력은 남기고 본사 운영 집계에서는 제외한 다매장 경계 설계

매장을 삭제하면 관련 데이터를 모두 지우거나, 반대로 이력이 남아 있으면 계속 집계하는 두 선택밖에 없어 보인다.

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

1장: 매장 삭제: 사라지는 데이터, 남는 그림자

솔라는 손에 든 태블릿 화면의 숫자를 손가락으로 덧그리며 미간을 찌푸렸다. 화면 속에는 ‘본사 운영 KPI 요약’이라는 제목 아래 여러 매장의 실적을 합산한 그래프가 반짝이고 있었다. 언뜻 보기에는 깔끔했다. 전국 매장 총매출, 평균 주문액, 재방문율. 모든 지표가 꾸준히 성장하는 것처럼 보였다.

하지만 솔라는 무언가 석연치 않은 기분을 떨칠 수 없었다. 몇 주 전, 상권 변화로 아쉽게 문을 닫았던 ‘강남점’이 떠올랐기 때문이다. 분명 폐점 절차가 끝났다고 들었는데, 본사 전체 매출 집계는 마치 강남점이 여전히 매일 아침 문을 열고 손님을 맞는 것처럼 보였다.

“언니, 이거 좀 이상해.”

솔라는 생각에 잠겨 있던 루나에게 태블릿을 내밀었다. 루나는 솔라가 가리키는 화면의 총매출액 그래프를 잠시 들여다보았다.

“전체 매출이 너무 부풀려져 있는 것 같아. 강남점은 이제 없는데, 이 숫자에 포함된 거 아닐까? 만약 그렇다면 우리가 현재 운영 중인 매장들의 진짜 실적을 왜곡해서 보고 있는 거잖아.”

솔라의 목소리에는 답답함이 묻어났다.

“그럼 강남점 데이터를 전부 지워버리면 되지 않을까? 주문 기록, 고객 데이터까지 싹. 그러면 현재 집계는 정확해질 테니까.”

스스로 해결책을 찾았다는 듯 솔라가 말을 이었다. 하지만 말을 끝맺기도 전에 자신의 제안이 가진 허점을 깨달았다.

“아니, 잠깐만. 그렇게 하면 안 되지. 작년 이맘때 매출이랑 비교하려면 과거 데이터가 전부 있어야 하잖아. 강남점은 작년 실적에 꽤 큰 부분을 차지했는데, 그 기록을 통째로 날려버리면 과거와의 비교 분석은 다 엉망이 되겠네. 이건 뭐, 어쩌라는 거지?”

솔라는 태블릿을 테이블에 내려놓으며 한숨을 쉬었다. 매장을 없애면 관련된 모든 것을 지우거나, 아니면 기록이 남아있는 한 계속 집계에 포함하거나. 선택지는 둘 중 하나밖에 없어 보였다. 하지만 두 선택지 모두 어딘가 망가져 있었다.

루나는 말없이 솔라의 말을 들은 뒤, 태블릿 옆에 놓인 메모지와 펜을 가져왔다. 그리고 메모지 위에 간단한 표를 그리기 시작했다. 한쪽에는 ‘현재 KPI’, 다른 쪽에는 ‘과거 실적 분석’이라고 적었다.

“네가 방금 했던 말을 이 표에 다시 넣어보자. 먼저 ‘강남점’을 시스템에서 삭제한다고 상상해 봐.”

루나가 말했다. 솔라는 잠시 생각하더니 입을 열었다.

“만약 ‘강남점’의 모든 기록, 그러니까 주문 이력에 남은 ‘store_id’까지 전부 지워버리면… ‘현재 KPI’는 깨끗해지겠지. 지금 운영하는 매장들의 실적만 정확히 보일 테니까. 하지만 ‘과거 실적 분석’ 쪽은 구멍이 뻥 뚫릴 거야. 작년 총매출액이 갑자기 뚝 떨어져서 올해와의 비교가 의미 없어지겠지.”

솔라는 ‘과거 실적 분석’ 칸 아래에 ‘데이터 소실, 비교 불가’라고 적었다.

“좋아. 그럼 반대로 해볼까?”

루나가 반대편 시나리오로 솔라를 이끌었다.

“과거 기록을 보존하기 위해 아무것도 지우지 않는다면? 지금 우리 시스템처럼.”

“그럼 ‘과거 실적 분석’은 완벽하지. 모든 기록이 그대로 있으니까. 하지만 ‘현재 KPI’가 엉망이 돼. 본사 요약 화면은 데이터베이스에 기록된 모든 ‘store_id’를 그대로 긁어모아서 합산하니까. 이미 사라진 강남점의 그림자가 계속 현재의 숫자에 더해지는 거야. 마치 유령처럼.”

솔라는 ‘현재 KPI’ 칸 아래에 ‘삭제된 매장 포함, 실적 왜곡’이라고 썼다.

메모지 위에는 솔라가 방금 겪은 딜레마가 선명하게 그려져 있었다. 한쪽을 해결하면 다른 쪽이 망가지는 시소게임 같았다. 솔라는 자신이 쓴 글씨를 가만히 내려다보았다. ‘데이터 소실’과 ‘실적 왜곡’. 둘 다 피하고 싶은 결과였다.

“결국 문제는 이거네.”

솔라가 입을 열었다.

“우리는 강남점의 과거 기록을 ‘보존해야만’ 해. 동시에, 현재 운영 집계에서는 ‘제외해야만’ 하고. 이 두 가지를 어떻게 동시에 할 수 있지? 데이터가 존재하거나, 존재하지 않거나 둘 중 하나 아니었어?”

문제의 구조가 명확해지자, 처음의 막연한 답답함은 사라지고 날카로운 질문이 그 자리를 대신했다. 더 이상 ‘삭제할까, 말까’의 문제가 아니었다. 운영의 현재와 분석의 과거라는 두 개의 다른 시간대를 하나의 데이터로 어떻게 다뤄야 하는지에 대한 문제였다. 루나는 솔라가 스스로 도달한 질문을 잠자코 기다려주었다. 이제야 진짜 논의를 시작할 준비가 된 것이다.

2장: 활동과 기록: 매장의 두 얼굴

루나는 솔라의 날카로운 질문에 고개를 끄덕였다. 그리고는 솔라가 방금 딜레마를 정리했던 메모지 옆 빈 공간에 펜으로 작은 표를 그리기 시작했다. 복잡한 다이어그램이 아니었다. ‘매장 목록’이라는 제목 아래, 두 개의 열만 있는 단순한 목록이었다.

store_idname
101강남점
102홍대점
103부산서면점

솔라는 루나가 그리는 표를 잠자코 지켜보았다. 어제까지만 해도 이 표에 ‘강남점’이 있다는 사실 자체가 문제의 근원이었다.

“우리가 가진 매장 정보의 핵심 목록, 즉 stores 마스터 테이블이 이렇게 생겼다고 해보자.”

루나가 말했다.

“지난번 문제로 다시 돌아가면, 여기서 101번 행을 완전히 지우면 과거 분석이 불가능해지고, 그냥 두면 현재 KPI 집계에 포함돼서 숫자가 왜곡되지.”

솔라는 고개를 끄덕였다. 바로 자신이 빠졌던 함정이었다. 데이터는 존재하거나, 존재하지 않거나. 그 중간은 없어 보였다.

그러자 루나가 표의 오른쪽에 조용히 세 번째 열을 추가했다. 그리고는 각 행에 무언가를 채워 넣었다.

store_idnameis_active
101강남점false
102홍대점true
103부산서면점true

새로 생긴 is_active라는 열. ‘활성 여부’라는 뜻이었다. 강남점 옆에는 ‘false’가, 다른 운영 중인 매장들 옆에는 ‘true’가 적혔다.

이 작은 변화가 모든 것을 달라 보이게 만들었다. 솔라의 눈이 반짝였다. 그녀는 숨을 잠시 멈췄다가, 마치 안갯속에서 길을 찾은 사람처럼 빠르게 말을 쏟아냈다.

“아! 이런 거구나. 우리는 매장을 ‘삭제’하는 게 아니었어. 그냥 ‘비활성화’하는 거였네!”

솔라는 펜을 들어 is_active 열을 손가락으로 가리켰다.

“그러니까, 본사 KPI 요약 화면을 만들 때는 이 목록에서 is_activetrue인 매장들만 골라서 집계하면 되는 거야. 그러면 강남점은 자연스럽게 빠지니까 현재 운영 실적이 정확해져. ‘실적 왜곡’ 문제가 해결돼.”

그녀는 이어서 말했다.

“동시에, 작년 실적을 분석할 때는? 강남점 데이터는 여전히 데이터베이스 어딘가에 store_id: 101이라는 꼬리표를 달고 남아 있겠지. 이 stores 마스터 테이블에도 행 자체가 사라진 게 아니니까. 그럼 ‘데이터 소실’ 문제도 해결되네!”

솔라의 목소리에는 흥분이 가득했다. ‘존재냐, 삭제냐’의 이분법에서 벗어나자 해답이 보였다. 매장이라는 하나의 대상이 두 개의 다른 얼굴, 즉 ‘운영 집계 대상으로서의 활동 상태’와 ‘과거 기록으로서의 존재’를 가질 수 있다는 사실을 깨달은 것이다. 마치 졸업 앨범에 실린 친구처럼 말이다. 지금 우리 반 명단에는 없지만, 그 친구가 존재했다는 기록과 추억은 사라지지 않는 것과 같았다.

“맞아. 우리는 매장을 하나의 상태로만 보려고 했던 거야.”

솔라는 스스로 깨달은 바를 정리했다.

“‘운영 대상’이라는 관점과 ‘감사나 분석을 위한 기록 보존 대상’이라는 관점을 분리해서 생각해야 했어.”

이 새로운 관점은 강력했다. 더 이상 시소의 양 끝에서 위태로운 저울질을 할 필요가 없었다. 두 가지 요구사항을 모두 만족시키는 길이 열린 것이다. 시스템은 stores 마스터를 기준으로 활성 매장만 골라내 본사 KPI를 만들고, AI에게 분석 데이터를 넘겨주면 되었다.

문제가 해결되었다는 안도감도 잠시, 솔라의 머릿속에 새로운 질문이 떠올랐다. 개념은 명확해졌지만, 현실은 조금 더 복잡했다.

“알겠어. 그러면 stores 마스터 테이블에 is_active라는 깃발을 하나 세우는 거네. 근데 이 깃발 하나로 그 복잡한 시스템 전체를 어떻게 통제하지? 본사 KPI 집계 로직도 바꿔야 하고, 매장 비교 화면도, AI 분석 모델에 데이터를 넣어주는 부분도 전부 이 깃발을 보도록 만들어야 하잖아. 이 ‘활성 매장’이라는 울타리를 실제로 어떻게 치는 거야?”

3장: stores 마스터: 활성 매장의 울타리

솔라의 질문은 허공에 잠시 머물렀다. is_active라는 깃발 하나로 어떻게 거대한 시스템 전체를 제어할 수 있을까? 마치 성벽의 작은 문 하나를 걸어 잠근다고 해서 성 안의 모든 활동이 저절로 통제되지는 않는 것처럼, 개념의 단순함과 구현의 복잡함 사이에는 큰 간격이 있어 보였다.

루나는 말없이 솔라가 내려놓았던 태블릿을 다시 가져왔다. 그리고는 조금 전 stores 마.스터 테이블을 그렸던 메모 앱의 새 페이지를 열었다. 이번에는 표가 아니었다. 루나는 마치 개발자가 데이터베이스에 말을 거는 듯한 문장을 써 내려가기 시작했다.

-- 본사 KPI 요약을 위한 데이터 가져오기

1. 'stores' 마스터 테이블에서 'is_active'가 'true'인 매장 ID 목록을 가져온다.
   (예: [102, 103, ...])

2. 'orders' 테이블에서 주문 기록을 가져올 때,
   주문 기록의 'store_id'가 1번 목록에 포함된 것만 선택한다.

3. 선택된 주문 기록들만 가지고 매출, 주문 건수 등을 집계한다.

화면에 나타난 것은 실제 코드는 아니었지만, 시스템이 따라야 할 명확한 지시사항이었다. 솔라는 처음에는 이것이 본사 KPI 요약 화면 하나만을 위한 설명이라고 생각했다. 하지만 문장을 곱씹어볼수록 그 이상의 의미가 보이기 시작했다.

“아…!”

솔라는 짧은 탄성을 뱉었다. 그녀의 생각은 ‘깃발을 세우면 모든 시스템이 어떻게 알아서 그걸 보지?’에서 ‘모든 시스템이 그 깃발을 보도록 만들면 되는구나’로 바뀌고 있었다.

“하나의 복잡한 시스템을 만드는 게 아니었어. 그냥 원칙을 세우는 거였네. ‘데이터를 쓸 때는, 먼저 stores 마스터에 가서 지금 영업 중인 매장이 맞는지 물어보고 와라.’ 이 규칙 하나만 있으면 되는 거구나.”

솔라는 태블릿 화면의 1번 항목을 손가락으로 가리켰다.

“이게 바로 ‘활성 매장’이라는 울타리 그 자체네. 본사 KPI를 만들 때도 이 울타리 안의 매장만 보고, 매장끼리 실적을 비교하는 화면을 만들 때도 이 울타리를 먼저 확인하고. 심지어 AI한테 분석할 데이터를 줄 때도 이 울타리 안에서 일어난 일만 모아서 주면 되는 거야.”

모든 store_id가 동등한 자격을 가졌던 이전의 세계와는 달랐다. 이제 stores 마스터의 is_active 컬럼이 일종의 출입증 발급처 역할을 하게 된 것이다. 데이터가 아무리 많이 쌓여 있어도, stores 마스터가 발급한 ‘활성’ 출입증이 없는 매장의 데이터는 현재의 운영 집계라는 파티에 입장할 수 없었다.

이것이 바로 중앙 마스터 데이터를 기준으로 시스템의 경계를 설정하는 패턴이었다. 각각의 기능(KPI, 비교, AI 분석)이 제멋대로 데이터를 모으는 것이 아니라, 모두가 단 하나의 진실, 즉 stores 마스터가 정의하는 ‘현재 운영 중인 매장 목록’을 기준으로 삼는 것이다. 이렇게 하면 수백 개의 기능이 있어도 일관된 정책을 유지할 수 있었다.

“맞아. 우리는 stores 마스터를 운영 대상의 유일한 기준으로 삼기로 한 거야. 시스템의 모든 부분이 데이터를 요청할 때마다 참조하는 단 하나의 진실 공급원(Single Source of Truth)이지.”

루나의 차분한 확인에 솔라는 고개를 끄덕였다. 머릿속에 흩어져 있던 퍼즐 조각들이 ‘마스터 데이터’라는 중심축에 맞춰 끼워지는 느낌이었다. 복잡할 것 같았던 통제는 사실 아주 단순한 원칙을 꾸준히 지키는 것에서 비롯되었다.

설계의 명쾌함에 감탄하던 솔라의 얼굴에 다시 새로운 궁금증이 떠올랐다. 데이터의 흐름은 이제 명확해졌다. 하지만 매장이 문을 닫는 현실의 과정은 데이터 처리만으로 끝나지 않았다.

“좋아, 데이터 집계 문제는 해결됐어. 그럼 강남점의 is_activefalse로 바꾸는 바로 그 순간을 상상해 보자. 이제 KPI에는 안 잡히겠지. 그런데… 강남점 홍보용으로 올려둔 사진이나 영상 파일은 어떻게 되는 거야? 그리고 마침 강남점 데이터를 분석하던 AI 작업이 있었다면? 그건 그냥 계속 돌아가나? 멈춰야 하나?”

솔라는 이제 데이터의 세계를 넘어, 그 데이터에 연결된 실질적인 ‘운영 자원’들을 떠올리고 있었다. 비활성화된 매장의 데이터는 보존하되, 더 이상 필요 없는 자원들은 깔끔하게 정리해야 할 것 같았다. 하지만 그 경계는 또 어디에 있을까?

4장: 비활성 매장의 정리와 보존

솔라의 질문이 남긴 정적이 방 안을 채웠다. 데이터의 흐름은 is_active라는 깃발로 정리되었지만, 그 깃발이 흔들리는 순간 현실 세계에 연결된 다른 것들은 어떻게 되는 걸까. 매장의 홍보용 사진, 돌아가고 있던 분석 작업 같은 것들.

루나는 솔라의 말에 대답하는 대신, 다시 한번 메모지를 꺼내 들었다. 하지만 이번에는 표나 코드를 적지 않았다. 그녀는 메모지 중앙에 ‘강남점 폐점’이라고 크게 쓴 뒤, 그 아래로 세 개의 화살표를 나란히 그렸다. 각 화살표 끝에는 빈 상자가 매달려 있었다. 마치 세 개의 다른 서랍장을 그려놓은 것 같았다.

텅 빈 서랍장 세 개. 솔라는 그 의미를 바로 알아채지 못했다. 데이터 집계 문제는 이미 해결했는데, 왜 또다시 그림을 그리는 걸까. 이전의 이분법적 사고방식이 은근히 고개를 들었다. 매장을 비활성화하면, 관련된 것은 모두 동일하게 처리되어야 하는 것 아닌가. 데이터든, 사진이든.

“네가 아까 물었던 것들을 이 서랍장에 한번 나눠 담아보자.”

루나가 조용히 말했다. 그녀는 첫 번째 상자 위에 ‘과거의 기록’이라고 적었다. 두 번째 상자 위에는 ‘운영 자산’, 세 번째 상자 위에는 ‘진행 중인 일’이라고 썼다.

“강남점이 문을 닫았을 때 우리가 처리해야 할 것들을 떠올려 봐. 예를 들면, 작년 매출이 기록된 장부, 매장 전경을 찍은 홍보 사진, 그리고 마침 어제부터 시작된 강남점 고객 분석 작업.”

솔라는 루나가 나열한 것들을 잠시 머릿속에 그렸다. 폐점한 매장의 사무실에 남겨진 물건들을 정리하는 장면이 떠올랐다. 어떤 것은 보관하고, 어떤 것은 버리고, 어떤 것은 진행을 멈춰야 한다. 솔라는 펜을 들어 루나가 그려놓은 서랍장에 항목들을 채워 넣기 시작했다.

  1. 과거의 기록: 작년 매출 장부 (주문, 상태 이력 데이터)
  2. 운영 자산: 매장 전경 사진, 홍보 영상 (미디어 파일)
  3. 진행 중인 일: 고객 재방문율 분석 AI 작업 (실행 중인 프로세스)

항목들을 제자리에 채워 넣고 나자, 솔라는 자신이 그린 메모지를 가만히 내려다보았다. 처음에는 그저 뒤죽박죽 섞여 있던 ‘강남점과 관련된 모든 것’들이 세 개의 다른 성격을 가진 그룹으로 분리되어 있었다.

그 순간, 안개가 걷히는 듯한 깨달음이 찾아왔다.

“아…! 얘네들은 전부 다른 규칙을 따라야 하는 거였어.”

솔라의 목소리에는 놀라움이 섞여 있었다.

“‘과거의 기록’은 사실 그 자체니까 절대 지우면 안 돼. 보존해야 해. 우리가 is_active 플래그를 만든 이유가 바로 이거였지. 이 기록을 지키면서 현재 운영 집계에서는 제외하기 위해서. 그러니까 이 서랍장은 ‘보존’이 원칙이야.”

그녀는 두 번째 상자를 가리켰다.

“하지만 ‘운영 자산’은 달라. 매장 간판 사진이나 홍보 영상은 강남점이 ‘운영 중일 때’만 의미가 있는 거잖아. 이제 문을 닫았으니 더 이상 필요 없는 자원이야. 오히려 스토리지 공간만 차지하고, 다른 관리자가 실수로 이 사진을 쓸 수도 있어. 이 서랍장은 ‘정리’가 원칙이네.”

마지막으로 솔라의 손가락은 세 번째 상자로 향했다.

“‘진행 중인 일’은 더 명확해. 이미 비활성화된 매장의 데이터를 분석하는 건 컴퓨팅 자원 낭비일 뿐이야. 유효하지 않은 결과를 낼 수도 있고. 이건 당장 ‘중단’시켜야 해.”

모든 것을 ‘삭제’하거나, 모든 것을 ‘남기는’ 것이 아니었다. 매장 비활성화라는 하나의 이벤트가 발생했을 때, 그 매장에 연결된 자산의 ‘성격’에 따라 각기 다른 정책—보존, 정리, 중단—이 발동되어야 했다. 주문 이력 데이터와 매장 홍보 사진은 둘 다 ‘데이터’라는 형태를 띠고 있었지만, 그들의 생명주기와 목적은 전혀 달랐던 것이다.

“우리가 하려던 건 ‘삭제’라는 단일 버튼이 아니었어.”

솔라는 스스로 깨달은 바를 명확한 문장으로 정리했다.

“‘매장 비활성화’라는 생명주기의 한 단계에서, 연결된 자산들을 각자의 라이프사이클에 맞게 분리해서 관리하는 절차를 설계하는 거였구나.”

하나의 엔티티가 사라질 때, 그에 딸린 다양한 유형의 자산들을 개별 정책에 따라 처리하는 것. 이것은 복잡하게 얽힌 시스템을 안전하게 관리하는 중요한 패턴이었다. 데이터는 데이터의 보존 정책을, 미디어 파일 같은 운영 자원은 자원의 정리 정책을, 실행 중이던 프로세스는 프로세스의 중단 정책을 따르는 것이다.

설계의 빈틈이 또 하나 메워지는 기분이었다. 이제 시스템은 비활성 매장을 현재 집계에서 제외할 뿐만 아니라, 더 이상 필요 없는 자원들을 깔끔하게 정리하고, 보존해야 할 이력은 안전하게 지킬 수 있게 되었다. 논리적으로는 완벽해 보였다.

하지만 솔라는 문득 현실적인 의구심이 들었다.

“좋아, 이제 규칙은 명확해졌어. 그런데… 우리가 만든 이 복잡한 규칙들이 실제로 시스템의 모든 구석에서 제대로 작동할 거라고 어떻게 보장하지? 만약 어떤 개발자가 이 규칙을 깜빡하고, 그냥 예전 방식대로 stores 테이블 전체를 긁어다가 KPI를 만들면 어떡해? 우리가 세운 이 멋진 울타리가 실제로는 허술한 철조망일 수도 있잖아.”

5장: 검증: 비활성 매장, 본사 집계에서 사라지다

솔라는 지금까지의 논의가 그려진 메모지들을 테이블 위에 펼쳐놓았다. 첫 장의 시소 게임부터, is_active 깃발이 세워진 매장 목록, 그리고 세 개의 서랍장으로 나뉜 자산 정리 정책까지. 아이디어의 흐름은 명확했고, 설계는 우아해 보였다. 모든 퍼즐 조각이 제자리를 찾은 듯했다.

하지만 솔라의 마음 한구석에는 미세한 불안감이 남아 있었다. 그녀는 루나가 그렸던 ‘활성 매장의 울타리’ 그림을 손가락으로 꾹 눌렀다. 논리적으로는 완벽한 울타리였다. 하지만 현실에서 이 울타리는 과연 튼튼할까? 누군가 문을 잠그는 것을 잊거나, 다른 길로 넘어 들어온다면 속수무책으로 뚫리는 허술한 철조망에 불과한 것 아닐까?

“좋아, 이제 규칙은 명확해졌어. 그런데… 우리가 만든 이 복잡한 규칙들이 실제로 시스템의 모든 구석에서 제대로 작동할 거라고 어떻게 보장하지? 만약 어떤 개발자가 이 규칙을 깜빡하고, 그냥 예전 방식대로 stores 테이블 전체를 긁어다가 KPI를 만들면 어떡해?”

설계를 변경했으니 자동으로 문제가 해결될 것이라는 막연한 기대는 위험해 보였다. 솔라의 질문은 더 이상 ‘어떻게 만들까?’가 아니라 ‘만든 것이 제대로 작동하는지 어떻게 믿을 수 있을까?’로 옮겨가 있었다.

루나는 솔라의 불안을 잠자코 듣고 있었다. 그녀는 솔라의 질문에 “테스트를 하면 돼”라고 간단히 답하는 대신, 솔라의 시선을 다른 곳으로 이끌었다.

“네가 만약 이 시스템의 규칙을 어기려는 해커라고 상상해 봐. 혹은, 이 시스템이 정말 튼튼하게 만들어졌는지 검사해야 하는 감사관이거나. 어떻게 이 울타리가 진짜 강철인지, 아니면 그냥 그림인지 증명해 보일 수 있을까?”

해커나 감사관이라니. 역할이 바뀌자 생각의 방향도 달라졌다. 더 이상 설계자의 입장에서 ‘잘 동작하겠지’라고 가정하는 것이 아니었다. 어떻게든 빈틈을 찾아내려는 공격적인 관점이 필요했다. 솔라는 눈을 감고 잠시 생각에 잠겼다.

‘내가 만약 이 시스템의 허점을 찾으려 한다면… 가장 교묘한 상황을 만들어야 해.’

솔라의 머릿속에 구체적인 시나리오가 그려지기 시작했다.

“일단, 이런 매장이 필요해. 과거에는 분명히 존재했고, 상당한 매출 기록도 남겼지만, 지금은 확실히 문을 닫은 매장. 그래, 바로 우리의 ‘강남점’이야.”

솔라는 펜을 들어 빈 메모지에 새로운 시나리오를 써 내려갔다.

<‘활성 경계’ 검증 시나리오>

  1. 준비:

    • 가상 데이터 생성: 작년에 1억 원의 매출을 올린 ‘강남점’(store_id: 101)과, 2억 원의 매출을 올린 ‘홍대점’(store_id: 102)의 주문 이력을 만든다.
    • 상태 설정: stores 마스터 테이블에서 강남점은 is_activefalse로, 홍대점은 is_activetrue로 설정한다.
  2. 실행 및 검증:

    • 테스트 1 (현재 운영 집계): 본사 KPI 요약 화면을 요청한다. 이 화면은 ‘현재 운영 중인’ 매장들의 총매출을 보여줘야 한다.
      • 기대 결과: 총매출은 2억 원이어야 한다. 만약 3억 원이 나온다면, 시스템은 비활성 매장을 걸러내지 못한 것이므로 실패.
    • 테스트 2 (과거 이력 분석): 작년 동분기 대비 매출 분석을 요청한다. 이 분석은 과거의 모든 기록을 참조할 수 있어야 한다.
      • 기대 결과: 분석 시스템은 작년 데이터에서 ‘강남점’의 매출 1억 원을 찾을 수 있어야 한다. 만약 강남점의 기록을 찾지 못한다면, 이력 보존에 실패한 것이므로 실패.

시나리오를 다 쓰고 난 솔라는 숨을 내쉬었다. 이것은 막연한 상상이 아니었다. 시스템이 지켜야 할 규칙을 명확한 성공과 실패 조건으로 번역한, 구체적인 시험 문제였다. 이 테스트를 통과한다면, ‘울타리’는 더 이상 그림이 아니라 실제로 작동하는 방어벽임을 증명하는 셈이었다.

“바로 이거였어.”

솔라가 말했다. 그녀의 목소리에 자신감이 실렸다.

“설계는 규칙을 만드는 것에서 끝나지 않아. 그 규칙이 지켜지고 있는지 감시하는 ‘자동화된 시험’까지 만들어야 비로소 완성되는 거였어. 이 테스트 시나리오가 바로 그 감시카메라인 셈이지. 누군가 실수로 규칙을 어기는 코드를 추가하면, 이 테스트가 즉시 실패하면서 우리에게 알려줄 테니까.”

운영 집계 목적과 감사/분석 목적을 동시에 충족해야 하는 ‘이중 목적 엔티티’의 관리. 그 복잡한 요구사항의 마지막 방점은 바로 이 검증 패턴에 있었다.

더 이상 솔라에게 ‘삭제된 매장의 데이터 처리’는 풀리지 않는 딜레마가 아니었다. 그것은 활성 경계를 정의하고, 자산의 라이프사이클을 분리하며, 마지막으로 이 모든 것이 올바르게 작동하는지 검증하는 시나리오를 통해 관리하는, 명확한 엔지니어링 문제였다.

솔라는 태블릿을 들어 새 문서를 열었다. 제목은 이렇게 적었다.

‘활성 매장 집계 기준 정의서’

그녀는 지금까지 논의한 원칙들을 간결하게 정리했다. 그리고 마지막에 가장 중요한 섹션을 추가했다.


4. 핵심 검증 시나리오

본 설계의 정확성은 아래 시나리오를 만족하는 자동화된 테스트를 통해 보장되어야 한다.

  • 시나리오: ‘이력이 존재하는 비활성 매장’이 현재 집계에서 제외되는지 확인한다.
  • 준비: 과거 매출 이력이 있는 매장 A를 생성하고 is_active: false로 설정한다.
  • 검증:
    1. 현재 KPI 집계 시, 매장 A의 실적은 포함되지 않아야 한다.
    2. 과거 데이터 분석 시, 매장 A의 이력은 조회 가능해야 한다.

문서를 완성한 솔라는 루나를 보았다. 루나는 조용히 고개를 끄덕였다. 길었던 논의의 끝이었다. 솔라는 이제 ‘강남점’과 같은 새로운 폐점 매장이 생겨도 당황하지 않을 것이다. 그녀의 손에는 문제를 분석하고, 원칙을 세우며, 그 원칙이 지켜지는지 검증할 수 있는 명확한 도구가 들려 있었다.