왕: 칠일이 또 지났다. 그간 무엇을 찾았느냐?
영의정: 전하, 지난번 병(丙)의 방식을 연구해보았사옵니다.
왕: 그래서?
영의정: 병이 인공지를 쪼개서 부린다는 것은 알겠사옵니다. 그런데... 어떤 순서로 쪼개야 하는지가 관건인 것 같사옵니다.
왕: 오, 순서라. 좌의정은?
좌의정: 전하, 신도 같은 생각이옵니다. 쪼개는 것까지는 알겠는데, 무엇을 먼저 시키고 무엇을 나중에 시켜야 하는지... 그것이 핵심인 것 같사옵니다.
왕: 좋다. 오늘은 그것을 보겠다. 술사들을 부르라.
왕: 오늘은 다른 문제를 내겠다. (두루마리를 펼치며)
왕: 새로운 기능을 만들어야 한다. 유저가 상품을 장바구니에 담고, 수량을 조절하고, 쿠폰을 적용하고, 최종 가격을 계산하는 기능이다.
왕: 단, 기존 시스템과 연동되어야 한다. 상품 정보는 상품방(商品房)에서 가져오고, 쿠폰 정보는 쿠폰방(券房)에서 가져오고, 재고 확인은 창고방(倉庫房)에서 가져와야 한다.
왕: 인공지를 써서 만들어라. 과인이 지켜보겠다.
왕: 갑, 먼저 하라.
갑: (지난번 교훈을 떠올리며) 이번에는 용어를 제대로 써야겠다.
갑: (인공지에게) 장바구니 기능 만들어라. 리액쿼리로 서버 상태 관리하고, 주스탄드로 클라이언트 상태 관리해라.
갑: 상품 담기는 옵티미스틱 업데이트로 처리하고, 쿠폰 적용은 유즈멤모로 메모이제이션해라. 가격 계산 로직은 퓨어 펑션으로 분리하고.
갑: 에이피아이 호출은 어댑터 패턴으로 추상화하고, 에러 핸들링은 에러 바운더리로 잡아라.
인공지: (한참 작업 후 결과를 내놓는다)
갑: (뿌듯하게) 전하, 완료되었사옵니다. 이번에는 용어를 제대로 썼사옵니다.
왕: 오, 그래? 어디 보자. (코드를 살펴보며) 이 코드가 몇 줄이냐?
내관: 천이백 줄이옵니다, 전하.
왕: 천이백 줄... 갑, 이 코드가 어떻게 동작하는지 설명해보아라.
갑: 전하, 이 부분이 리액쿼리로 상품 정보를 가져오는 부분이고... (코드를 보다가 멈칫)
왕: 왜 그러느냐?
갑: 잠시만요, 전하. (코드를 더 살피며) 이 부분이... 소인이 옵티미스틱 업데이트라 했는데...
갑: (한참 들여다보다가) 이상하옵니다. 어댑터 패턴으로 추상화하라 했는데, 여기는 직접 호출하고 있고...
갑: (다른 부분을 보며) 여기는 또... 주스탄드랑 리액쿼리가 같은 데이터를 따로 관리하고 있사옵니다. 이러면 안 맞을 텐데...
왕: 그대가 시킨 대로 안 된 것이냐?
갑: 전하, 용어는 다 썼는데... 어떻게 엮이는지를 안 정해줘서 인공지가 제 나름대로 엮은 것 같사옵니다.
왕: 그래서 지금 어찌 동작하는지 아느냐?
갑: (고개를 숙이며) 솔직히... 잘 모르겠사옵니다.
왕: 흠... 지난번보다는 나아졌다. 용어를 쓰니 인공지가 알아듣기는 했구나. 그런데 그대가 결과를 모르면 무슨 소용이냐?
왕: 을, 네 차례다.
을: (생각하며) 지난번에 한꺼번에 시켜서 문제였다. 이번엔 쪼개서 시키자. 용어도 쓰고.
을: (인공지에게) 먼저 장바구니에 상품 담는 기능 만들어라. 주스탄드로 상태 관리해라.
인공지: (결과를 내놓는다)
을: (확인하며) 좋아, 잘 나왔다. 다음, 수량 조절 기능 추가해라. 옵티미스틱 업데이트로.
인공지: (결과를 내놓는다)
을: 다음, 쿠폰 적용 기능 추가해라. 쿠폰방 에이피아이 연동하고.
인공지: (결과를 내놓는다)
을: 다음, 가격 계산 기능 추가해라. 퓨어 펑션으로.
인공지: (결과를 내놓는다)
을: 다음, 상품방 에이피아이 연동해라. 리액쿼리로.
인공지: (결과를 내놓는다)
을: 다음, 창고방 에이피아이 연동해라. 재고 확인용으로.
인공지: (결과를 내놓는다)
을: (뿌듯하게) 전하, 완료되었사옵니다. 이번에는 용어도 쓰고, 쪼개서 시켰사옵니다.
왕: 오, 지난번 교훈을 잘 새겼구나. 어디 실행해보아라.
을: (실행한다) ...어?
왕: 왜 그러느냐?
을: 전하, 에러가 나옵니다...
왕: 무슨 에러냐?
을: (에러를 보며) 쿠폰을 적용하는데... 상품 카테고리 정보가 없다고...
왕: 왜 카테고리가 필요하냐?
을: 아... 쿠폰이 특정 카테고리에만 적용되는 경우가 있사옵니다. 그런데 소인이 그걸 미리 생각 못 했사옵니다.
왕: 또 다른 문제는 없느냐?
을: (더 살펴보며) 전하, 재고 확인하는 시점이... 이상하옵니다. 장바구니에 담을 때 확인해야 하는데, 지금은 마지막에 확인하고 있사옵니다.
왕: 왜 그리 되었느냐?
을: 소인이 창고방 연동을 마지막에 시켜서... 인공지가 마지막에 넣어버린 것 같사옵니다.
왕: 또 있느냐?
을: (더 살펴보며) 전하, 이상한 것이 있사옵니다. 소인이 시키지 않은 것들이 들어가 있사옵니다.
왕: 시키지 않은 것?
을: 위시리스트 연동이 들어가 있사옵니다. 소인은 장바구니만 시켰는데...
을: 그리고 최근 본 상품 기능도 들어가 있사옵니다. 소인은 이것도 안 시켰사옵니다.
왕: 인공지가 알아서 넣은 것이냐?
을: 그런 것 같사옵니다. "장바구니 기능"이라 하니까 관련된 것을 알아서...
을: (급히) 소인이 고쳐보겠사옵니다. (인공지에게) 위시리스트 연동 빼라.
인공지: (결과를 내놓는다)
을: (확인하며) ...전하, 위시리스트를 빼니까 장바구니 담기가 안 되옵니다.
왕: 왜 그러느냐?
을: 인공지가 위시리스트랑 장바구니를 엮어놔서... 하나를 빼니까 다른 게 깨진 것 같사옵니다.
을: (다시 시도하며) 장바구니 담기 다시 고쳐라.
인공지: (결과를 내놓는다)
을: (확인하며) ...이번에는 수량 조절이 안 되옵니다.
을: (한숨) 전하, 고치면 고칠수록 더 꼬이옵니다...
왕: 을, 그대는 지난번 교훈대로 용어도 쓰고 쪼개서 시켰다. 그런데 왜 문제가 생겼느냐?
을: (생각하다가) 전하, 소인이 쪼개기는 했는데... 순서가 잘못된 것 같사옵니다.
왕: 순서?
을: 기능 순서대로 시켰사옵니다. 담기, 수량 조절, 쿠폰, 가격 계산... 그런데 전체 그림 없이 하나씩 시키니까, 나중에 안 맞는 것이옵니다.
왕: 흠... 쪼개긴 쪼갰는데, 뭔가 빠진 것이 있구나.
왕: 병, 네 차례다.
병: (인공지에게) 잠깐. 만들기 전에 먼저 정리하자.
병: 장바구니 기능에서 외부 의존성이 세 개다. 상품방, 쿠폰방, 창고방. 각각에서 어떤 데이터를 가져와야 하는지 정리해라.
인공지: 정리 결과입니다.
인공지: 상품방: 상품 ID, 이름, 가격, 카테고리, 이미지 URL.
인공지: 쿠폰방: 쿠폰 코드, 할인율, 적용 가능 카테고리, 최소 주문 금액, 유효기간.
인공지: 창고방: 상품별 재고 수량, 재고 상태.
병: 빠진 거 있다. 상품방에서 할인가 정보도 필요하고, 창고방에서 예약 수량도 확인해야 한다. 추가해라.
인공지: 수정합니다.
병: 좋다. 이제 핵심 흐름을 정리해봐라. 유저 관점에서 어떤 순서로 동작해야 하냐?
인공지: 핵심 흐름입니다.
인공지: 1) 유저가 상품 선택 → 2) 재고 확인 → 3) 장바구니에 담기 → 4) 수량 변경 시 재고 재확인 → 5) 쿠폰 선택 → 6) 쿠폰 적용 가능 여부 확인 → 7) 최종 가격 계산.
병: 결제 직전에 최종 재고 재확인도 해야 하지 않냐?
인공지: 맞습니다. 8) 결제 진행 전 최종 재고 재확인 추가.
병: 좋다. 이 흐름에서 엣지 케이스를 생각해봐라. 뭐가 잘못될 수 있냐?
인공지: 엣지 케이스입니다.
인공지: 1) 담는 중 재고 소진 2) 수량 변경 시 재고 부족 3) 쿠폰 적용 불가 카테고리 4) 쿠폰 최소 금액 미달 5) 쿠폰 유효기간 만료 6) 결제 전 재고 변동 7) 동시에 여러 유저가 같은 상품 담기.
병: 일곱 번째, 동시성 문제는 이번 범위에서 빼자. 복잡도가 올라간다. 나머지 여섯 개만 처리하자.
인공지: 알겠습니다. 동시성 문제 제외.
병: 이제 컴포넌특 구조를 어떻게 나눌지 제안해봐라.
인공지: 컴포넌특 구조입니다.
인공지: 최상위 CartPage, 하위에 CartItemList, CartItem, CouponSelector, PriceSummary.
인공지: 훅으로는 useCart, useProduct, useCoupon, useInventory.
인공지: 추가로 위시리스트 연동을 위한 useWishlist, 최근 본 상품을 위한 useRecentProducts도 필요합니다.
병: 잠깐. 위시리스트랑 최근 본 상품은 어디서 나온 것이냐?
인공지: 장바구니 기능에서 일반적으로 함께 제공되는 기능입니다.
병: 아니다. 이번 범위에 없다. 빼라.
인공지: 수정합니다. useWishlist, useRecentProducts 제외.
왕: (끼어들며) 병, 잠깐. 인공지가 알아서 넣으려 한 것이 아까 을한테 일어난 일 아니냐?
병: 전하, 정확하옵니다. 범위를 안 쳐주면 인공지가 알아서 판단하옵니다. 그래서 미리 잡아준 것이옵니다.
왕: 오... 계속해보라.
병: 그리고 훅 분리가 이상하다. useProduct랑 useInventory가 따로 있으면 상품 담을 때 호출이 두 번이다. 다시 생각해봐라.
인공지: 수정합니다. useProductWithStock으로 합쳐서 상품 정보와 재고를 같이 가져오게 합니다.
병: 좋다. 이 구조로 타입 정의부터 해라. 구현은 하지 마라. 인터페이스만.
인공지: (타입 정의를 내놓는다)
병: CartItem 타입에 쿠폰 적용 후 가격 필드가 빠졌다. 추가해라.
인공지: (수정)
병: 좋다. 이제 useProductWithStock부터 구현해라. 리액쿼리 패턴대로. 끝나면 테스트 시나리오 세 가지만 작성해라.
인공지: (결과를 내놓는다)
병: (결과 확인) 재고 부족 케이스에서 에러 메시지가 하드코딩이다. 상수로 빼라.
인공지: (수정)
병: 좋다. 다음 useCart 구현해라.
왕: 됐다. 과인이 충분히 보았다.
왕: 셋 다 지난번 교훈을 새겼구나. 용어를 쓰려고 노력했다.
왕: 갑은 리액쿼리, 주스탄드, 옵티미스틱 업데이트, 어댑터 패턴... 용어를 많이 썼다.
왕: 을도 주스탄드, 옵티미스틱 업데이트, 퓨어 펑션... 용어를 썼고, 쪼개서 시켰다.
왕: 그런데 왜 병만 제대로 됐느냐?
왕: 갑, 그대는 왜 실패했느냐?
갑: 전하, 소인은 용어는 썼으나... 한꺼번에 시켰사옵니다. 그래서 결과가 나오긴 했는데, 어떻게 엮여 있는지 소인도 모르겠사옵니다.
왕: 을, 그대는 쪼개서 시켰는데 왜 실패했느냐?
을: 전하, 소인이 생각해보니... 쪼개는 방식이 달랐사옵니다.
왕: 쪼개는 방식?
을: 지난번에 병이 한 것은... "이게 맞나?" 확인하면서 쪼갠 것이었사옵니다.
을: 소인이 오늘 한 것은... 그냥 일을 나눠서 시킨 것이었사옵니다.
왕: 오. 그 차이가 뭐냐?
을: (생각하며) 병은 "책임이 몇 개냐?" 물어보고 "아니다, 네 개다" 하고 잡아줬사옵니다. 맥락을 맞춰가며 쪼갠 것이옵니다.
을: 소인은 그냥 "담기 해라", "수량 조절 해라", "쿠폰 해라"... 기능 순서대로 쪼갠 것이옵니다.
왕: 아... 같은 "쪼개기"인데 다른 것이구나.
을: 그러하옵니다, 전하. 소인이 헷갈렸사옵니다.
왕: 병, 그대가 설명해보라. 이 둘이 어찌 다르냐?
병: 전하, 쪼개기에는 두 가지가 있사옵니다.
병: 하나는 확인하며 쪼개기이옵니다. "이게 맞나?" "이 방향이 맞나?" 중간중간 확인하는 것이옵니다. 지난번에 소인이 한 것이 이것이옵니다.
병: 다른 하나는 일을 나눠서 쪼개기이옵니다. 큰 일을 작은 일로 나누는 것이옵니다. 을이 오늘 한 것이 이것이옵니다.
왕: 둘 다 필요한 것 아니냐?
병: 그러하옵니다, 전하. 그런데 순서가 있사옵니다.
왕: 순서?
병: 일을 나누기 전에, 먼저 전체 그림을 그려야 하옵니다. 그 그림을 그릴 때 확인하며 쪼개기를 하는 것이옵니다.
병: 그림이 완성되면, 그제야 일을 나눠서 쪼개는 것이옵니다.
왕: 을은 그림 없이 바로 일을 나눴다?
병: 그러하옵니다. 그래서 나중에 "어? 카테고리가 필요했네?" "어? 재고 확인 시점이 틀렸네?" 하게 된 것이옵니다.
왕: 그러면 그대는 오늘 무엇을 먼저 했느냐?
병: 전하, 소인은 구현하기 전에 여섯 단계를 거쳤사옵니다.
병: 하나, 외부 데이터 정리. 어떤 데이터가 필요한지.
병: 둘, 핵심 흐름 정리. 유저 관점에서 어떤 순서로 동작하는지.
병: 셋, 엣지 케이스 정리. 뭐가 잘못될 수 있는지.
병: 넷, 범위 설정. 이번에 할 것과 안 할 것.
병: 다섯, 구조 설계. 컴포넌특과 훅을 어떻게 나눌지.
병: 여섯, 인터페이스 정의. 타입을 먼저 정의.
병: 그제야 구현을 시켰사옵니다.
왕: 과인이 세어보니, 구현 전에 여섯 단계를 거쳤다.
왕: 을은 몇 단계를 거쳤느냐?
을: (머쓱하게) 전하, 소인은... 바로 구현을 시켰사옵니다.
왕: 왜 이 순서여야 하느냐?
병: 전하, 앞 단계가 틀리면 뒤가 다 틀리기 때문이옵니다.
병: 데이터 정리가 잘못되면, 나중에 "이 필드가 없네?" 하고 처음부터 다시 해야 하옵니다. 을이 카테고리를 빠뜨린 것처럼요.
병: 흐름 정리가 잘못되면, 나중에 "재고 확인 시점이 이상하네?" 하고 구조를 갈아엎어야 하옵니다. 을이 재고 확인 순서가 틀린 것처럼요.
병: 범위를 안 치면, 작업하다가 "이것도 해야 하나? 저것도 해야 하나?" 계속 늘어나옵니다. 동시성 문제처럼 복잡한 게 들어오면 끝이 안 나옵니다.
왕: 그래서 설계가 먼저라는 것이구나.
병: 그러하옵니다, 전하.
왕: 집을 지을 때 터를 닦고 기둥을 세우듯, 코드도 순서가 있다.
왕: 설계도 없이 벽돌부터 쌓으면, 아무리 좋은 벽돌이어도 무너진다.
왕: 사상누각(沙上樓閣)이로구나. 모래 위에 집을 지은 것이다.
병: 명언이시옵니다, 전하.
왕: 갑, 그대도 마찬가지다. 용어를 잘 썼으나, 설계 없이 한꺼번에 시켰다.
왕: 그래서 결과가 나오긴 했는데, 그대가 그것을 이해 못 한다. 내가 이해 못 하는 집에서 어찌 살겠느냐?
갑: (고개를 숙이며) 명심하겠사옵니다, 전하.
왕: 그런데 과인이 이상한 게 있다. 이것은 인공지 쓰는 법인가, 일하는 법인가?
병: (잠시 생각하다가) 전하, 똑같사옵니다.
왕: 똑같다?
병: 사람한테 일 시킬 때도 이래야 하옵니다.
병: 신입한테 "장바구니 만들어"하고 던지면, 갑처럼 되옵니다.
왕: 허허...
병: "담기부터 해, 수량 조절 해, 쿠폰 해"하고 쪼개서 시켜도, 전체 설계 없이 시키면 을처럼 되옵니다.
왕: 그러면?
병: "어떤 데이터 필요한지 정리해봐", "흐름 그려봐", "엣지 케이스 뽑아봐"...
병: 이렇게 시킨 다음, 검토하고, 틀린 부분 잡아주고, 그 다음에 구현 시키는 것이옵니다.
왕: 사람이나 인공지나 같다는 말이구나.
병: 그러하옵니다, 전하.
왕: 을, 그대는 오늘 무엇을 깨달았느냐?
을: 전하, 소인이 지난번에 "쪼개서 시켜야 한다"는 것을 배웠사옵니다.
을: 그래서 오늘 쪼개서 시켰사옵니다. 담기, 수량 조절, 쿠폰, 가격 계산...
을: 그런데 병을 보니 다르옵니다.
을: 병은 바로 만들라 하지 않았사옵니다. "정리해봐라", "흐름 그려봐라", "엣지 케이스 뽑아봐라"...
을: 인공지가 뭔가 내놓으면 "이건 아니다", "이건 빠졌다" 하며 잡아줬사옵니다.
을: 그렇게 설계를 같이 만들어간 다음에야 구현을 시켰사옵니다.
왕: 그래서?
을: 소인은 설계를 건너뛰고 바로 "만들어라"만 시킨 것이옵니다.
을: 그러니 인공지가 제멋대로 한 것이옵니다. 위시리스트도 넣고, 최근 본 상품도 넣고...
왕: 아... 설계를 같이 잡아가지 않으니, 인공지가 알아서 판단해버린 것이구나.
을: 그러하옵니다, 전하. 확인하면서 가야 했는데, 소인은 그냥 시키기만 한 것이옵니다.
을: 그리고 한 가지 더 깨달은 것이 있사옵니다.
왕: 말해보라.
을: 병이 엣지 케이스를 뽑으라 했을 때, 인공지가 일곱 개를 뽑았사옵니다.
을: 그런데 병이 "일곱 번째는 빼자"라고 했사옵니다.
을: 소인이라면 "인공지가 뽑았으니 다 해야지" 했을 것이옵니다.
을: 병은 할 것과 안 할 것을 정해줬사옵니다. 그래서 인공지가 딴 길로 안 샌 것이옵니다.
왕: (미소 지으며) 오늘 제대로 배웠구나.
을: 전하의 가르침 덕이옵니다.
왕: 오늘의 깨달음을 기록하라.
내관: (붓을 들며) 예, 전하.
왕: 첫째, 터를 닦기 전에 기와를 올리지 마라. 구현 전에 설계가 먼저다.
내관: (받아 적으며)
왕: 을이 그랬다. 데이터가 뭐가 필요한지, 흐름이 어찌 되는지 정리도 않고 바로 "만들어라"부터 시켰다. 그래서 나중에 안 맞은 것이다.
왕: 둘째, 주춧돌, 기둥, 서까래 순서가 있듯, 설계에도 순서가 있다.
왕: 병이 보여줬다. 데이터 정리, 흐름 정리, 엣지 케이스, 범위, 구조, 인터페이스. 이 순서대로 해야 앞 단계가 뒤 단계를 받쳐준다.
왕: 셋째, 장수가 머릿속으로만 진을 짜면 병졸은 모른다. 내 설계를 인공지도 알아들었는지 확인하라.
왕: 을도 나름 순서를 생각은 했을 것이다. 그러나 인공지와 맞춰보지 않았다. 그래서 인공지가 제멋대로 위시리스트도 넣고 딴 길로 샌 것이다.
왕: 넷째, 이것은 인공지를 부리는 법이 아니라 일을 시키는 법이다. 기본기는 시대가 바뀌어도 변하지 않는다.
왕: 사람한테 일 시킬 때도 마찬가지다. 설계 없이 던지면 갑처럼 되고, 확인 없이 쪼개서 시키면 을처럼 된다.
영의정·좌의정: 명심하겠사옵니다, 전하.
왕: 다음 칠일 후에 다시 보겠다. 오늘 본 것을 바탕으로, 또 무엇을 알아야 하는지 찾아오라.
- 제2화 끝 -
다음 화: 제3화 - 천리마의 역설