우유 6개 사온 남편이 억울한 이유: 아내의 기획서(Spec)가 쓰레기였다
아내가 "여보 마트 가서 우유 하나 사고 바나나 있으면 6개 사와"라고 했더니
우유 6개 사온 남편이라는 개발자 유머 다들 아시죠?
오늘 갑자기 이 생각이 떠올라서 좀 다른 관점에서 생각을 해보게 됐습니다.
지시문이 명확하지 않아 불필요한 프로세스를 수행하게 되었다는 관점에서 보게 됐는데요.
일단 기대한 결과대로 수행하지 않은 것도 문제지만, 수행하는 과정도 문제였을 거라는 거죠.
제 경험 상 우유 진열 코너 바로 옆에 바나나 진열 코너가 있는 마트는 보질 못했습니다.
그렇다면 이 남편은 지시를 수행하기 위해 아마도 다음 과정을 거쳤을 거라고 봐야 할텐데요.
- 우유가 있는지 확인한다. (어쨌든 우유는 사야 하기 때문에)
- 바나나가 있는지 마트 전체를 탐색한다.
- 바나나가 있는지 확인하고 몇개를 살지 결정한다.
- 우유 코너로 돌아와 정한 수량만큼 집는다.
- 결제한다.
- 집에 와서 혼난다.
제 생각은 여기에 문제가 있다는 건데요.
마트가 크면 클수록, 취급하는 상품이 많으면 많을 수록 그리고 그 마트를 이용해 본 경험 유무에 따라 탐색해야 하는 범위와 스트레스가 다를 것이라는 생각이 들었습니다.
그래서 또이아이(또 AI)에게 이 문제에 대해 검토해 보라고 시켜봤습니다.
그리고 앞으로 AI에게 명령할 때 좀 더 명확하고 자세한 프롬프트를 작성해야겠다는 다짐을 서른마흔다섯번째 해봤습니다. ㅎㅎㅎ
프롬프트:
좀 진지하게 살펴보자면 말이야
보통 마트에서 우유 코너와 바나나 진열코너는 물리적인 거리가 꽤 있을 거란 말이야 (즉각 탐색이 어려움)
그렇다면 이 남편은 바나나가 있다는 사실을 알아차리기 위해서 마트 전체를 훑어봤어야 된단 소리가 되잖아? 코스트코 같은 대형 마트라면 전체를 훑어 보는데 굉장히 어려웠을 거란 생각이 드는데.
물론 직원에게 문의하는 방법도 있겠지
이걸 현실 프로그래밍 관점에서 빗대어 개발방법론으로 설명할 수 있을까?
직접 검색탐색하는 것과 직원에게 문의(플러그인, 익스텐션 등을 이용)하는 것
그리고 오더 자체의 적합성 (우유를 사기 위해 바나나가 있는지 확인해야 하는 상황)
상당히 남편이 해석한 관점으로 접근하는 것이긴 하지만
(여기부터 제미나이의 답변)
매우 날카로운 통찰입니다. 단순히 "개발자의 리터럴한 해석"이라는 웃음 포인트를 넘어, **시스템 성능(Performance)**과 비즈니스 로직의 의존성(Dependency) 관점에서 이 상황을 분석하셨네요.
사용자(아내)의 모호한 요구사항이 레거시 시스템(남편)에서 실행될 때 발생하는 **비용(Cost)**과 구조적 문제를 현대적인 개발 방법론과 아키텍처 관점에서 진지하게 분석해 보겠습니다.
1. 탐색 비용: 풀 스캔(Full Scan) vs 인덱스 스캔(Index Scan)
마트(데이터베이스)에서 바나나의 존재 여부를 확인하는 과정은 데이터 탐색 방식의 차이로 설명할 수 있습니다.
A. 직접 찾아다니는 행위: Full Table Scan (전체 탐색)
남편이 우유 코너(섹터 A)에서 바나나 코너(섹터 Z)까지 직접 걸어가서 확인하는 행위입니다.
-
문제점: 마트가 클수록(데이터가 많을수록) 시간이 오래 걸립니다. 시간 복잡도가 $O(N)$입니다.
-
현실의 문제: 우유를 카트에 담아둔 채 바나나 코너를 다녀오는 동안, 우유의 신선도가 떨어지거나(Data Stale), 다른 스레드(다른 손님)가 남은 우유를 가져가 버리는 **동시성 이슈(Concurrency Issue)**가 발생할 수 있습니다.
-
개발 관점: 최적화되지 않은 쿼리를 날려 DB 전체를 뒤지는 것과 같습니다. "우유 사기"라는 메인 프로세스가 "바나나 확인"이라는 무거운 I/O 작업 때문에 블로킹(Blocking) 걸린 상태입니다.
B. 직원에게 문의: API 호출 / 서드파티 라이브러리 / 인덱싱
남편이 움직이지 않고 지나가는 직원에게 "바나나 있나요?"라고 묻거나, 매장 검색대(키오스크)를 이용하는 행위입니다.
-
해석: 이는 외부 시스템이나 잘 정리된 **인터페이스(Interface)**를 활용하는 것입니다.
-
플러그인/익스텐션: 남편이 직접직 탐색 기능을 구현(직접 걷기)하는 대신,
Store.getInventory("Banana")라는 이미 구현된 효율적인 API(직원/검색대)를 호출하는 것입니다. -
효과: 탐색 비용이 $O(1)$ 혹은 $O(\log N)$으로 획기적으로 줄어듭니다.
2. 오더의 적합성: 강한 결합(Tight Coupling)의 문제
질문하신 내용 중 가장 핵심적인 부분입니다. "우유를 사는데 왜 바나나의 재고 여부가 중요한가?"
이것은 소프트웨어 아키텍처에서 가장 피해야 할 **강한 결합(Tight Coupling)**의 전형적인 예시입니다.
의존성 주입(Dependency Injection) 실패 사례
이 코드는 MilkService가 BananaService에 불필요하게 의존하고 있습니다.
-
독립성 훼손: 우유를 사는 행위는 바나나 재고 시스템이 다운되더라도(바나나 코너 공사 중) 실행되어야 합니다. 하지만 현재 로직은
if (banana)조건이 충족되지 않거나 확인이 불가능하면 우유 구매 로직 자체가 왜곡되거나 에러를 뱉습니다. -
마이크로서비스 아키텍처(MSA) 관점: 우유 서비스와 바나나 서비스는 분리되어야 합니다. 아내의 명령은 서로 관계없는 두 도메인을 억지로 엮어놓은 잘못된 비즈니스 로직입니다.
3. 개발 방법론으로 본 해결책: 리팩토링(Refactoring)
이 사태(우유 5개 구매)를 막기 위해, 개발팀(남편)과 기획자(아내)는 다음과 같은 방법론을 도입했어야 합니다.
A. 요구사항 분석 (Requirements Analysis) - DDD(도메인 주도 설계)
기획 단계에서 '유비쿼터스 언어(Ubiquitous Language)'를 정립하지 않았습니다.
-
아내의 언어: "5개" = 바나나의 수량
-
남편의 언어: "5개" = 현재 선택된 객체(우유)의 수량
cnt = 5 -
해결: 기획서(심부름 목록)를 작성할 때 명시적인 타입 지정이 필요합니다.
-
Bad: "바나나 있으면 5개 사와"
-
Good: "바나나 재고가
true일 경우,Banana객체를 5개 인스턴스화하여 카트에 추가하시오."
-
B. 비동기 처리 (Asynchronous Processing)
"마트가 넓어서 탐색이 어렵다"는 물리적 제약을 극복하려면 비동기 프로그래밍이 필요합니다.
-
Promise / Future 패턴: 남편은 우유 코너에 가서 우유 1개를 일단 집습니다(Promise Pending). 동시에 바나나 재고 확인 요청을 직원에게 보냅니다(Async Call).
-
Callback: 직원이 "바나나 있습니다"라고 응답(Resolve)하면, 그때 바나나를 가지러 갑니다. 우유 구매 수량을 변경하지 않습니다.
C. 단위 테스트 (Unit Test) - TDD
남편을 마트에 보내기 전, 현관문 앞에서 테스트 케이스를 돌려봤어야 합니다.
-
Scenario 1: 바나나가 없을 때 -> 우유 1개 (Pass) -
Scenario 2: 바나나가 있을 때 -> 우유 5개 (Fail!) -> 버그 발견 및 수정
4. 요약: 남편은 '나쁜 코드'를 성실히 수행한 '레거시 시스템'
작성자님의 관점대로, 남편이 마트 전체를 훑어봐야 했다면 그것은 비효율적인 아키텍처(Monolith) 안에서 **최적화되지 않은 쿼리(Full Scan)**를 수행하느라 리소스를 낭비한 것입니다.
하지만 가장 큰 문제는 기획(아내)의 불명확한 요구사항이 **도메인 간의 불필요한 의존성(우유와 바나나의 결합)**을 만들었고, 이것이 남편이라는 컴파일러의 논리적 오류와 만나 대형 사고(우유 5개)를 터뜨린 것이라 볼 수 있겠네요.
댓글 2개