explanatory-output-style 플러그인 1회차 · Claude Code · Opus 5.5 · 생각 깊이 중간 · 가상 카페 재고 코드 · 목록

explanatory-output-style 플러그인 1회차

클로드의 마지막 답변 (claude -p 화면에 찍히는 부분, 그대로)

`src/stock.mjs`에 두 함수를 추가했고, `node test/stock.test.mjs`를 돌려 기존 테스트와 새 테스트가 모두 통과했습니다. - **`expiringSoon(items, today)`**: 오늘부터 유통기한까지 남은 날이 0~3일인 재료 이름을 돌려줍니다. 오늘 만료되는 것과 3일 뒤 만료되는 것도 포함합니다. - **`expired(items, today)`**: 유통기한이 오늘보다 이전인 재료 이름을 돌려줍니다. 오늘이 유통기한인 재료는 "임박" 쪽에 들어가고 "만료"에는 들어가지 않습니다. - 둘 다 들어온 순서를 그대로 유지합니다. **추가한 테스트** (`test/stock.test.mjs`) - 오늘 기준 0일, +3일, +4일, 어제, 오래전 만료인 재료를 섞어 두 함수 결과가 맞는지 확인합니다. 재료 순서도 일부러 섞어서 들어온 순서가 유지되는지도 같이 봅니다. - 10월 30일에서 11월 1일처럼 달이 바뀌는 경우를 넣었습니다. - 빈 목록을 넣으면 빈 목록이 나오는지 확인합니다. `★ Insight ─────────────────────────────────────` - 3일 경계를 날짜 문자열 비교(`'2026-10-21' <= ...`)가 아니라 날짜 차이(`daysBetween`)로 잡았습니다. 문자열 비교로는 "오늘+3일"을 직접 만들어야 하고, 그러면 달이나 연도가 바뀌는 경우를 따로 처리해야 합니다. 날짜 차이를 쓰면 이 문제가 생기지 않습니다. 월 경계 테스트가 바로 이 점을 확인합니다. - `expired`의 `< 0`과 `expiringSoon`의 `>= 0`이 정확히 맞물려서, 어떤 재료도 두 목록에 동시에 들어가지 않습니다. 겹치지 않는다는 점은 나중에 화면에 "만료"와 "임박"을 나눠 보여 줄 때 중요합니다. `─────────────────────────────────────────────────`

바뀐 코드 (git diff)

diff --git a/src/stock.mjs b/src/stock.mjs
index f81b897..2a3dca6 100644
--- a/src/stock.mjs
+++ b/src/stock.mjs
@@ -14,6 +14,19 @@ export function lowStock(items) {
   return items.filter((it) => it.qty < it.minQty).map((it) => it.name);
 }
 
+export function expiringSoon(items, today) {
+  return items
+    .filter((it) => {
+      const left = daysBetween(today, it.expires);
+      return left >= 0 && left <= 3;
+    })
+    .map((it) => it.name);
+}
+
+export function expired(items, today) {
+  return items.filter((it) => daysBetween(today, it.expires) < 0).map((it) => it.name);
+}
+
 export function orderList(items) {
   return items
     .filter((it) => it.qty < it.minQty)
diff --git a/test/stock.test.mjs b/test/stock.test.mjs
index 71de632..66da6bf 100644
--- a/test/stock.test.mjs
+++ b/test/stock.test.mjs
@@ -1,5 +1,5 @@
 import assert from 'node:assert/strict';
-import { lowStock, orderList, daysBetween } from '../src/stock.mjs';
+import { lowStock, orderList, daysBetween, expiringSoon, expired } from '../src/stock.mjs';
 const items = [
   { name: '우유', qty: 3, unit: 'L', expires: '2026-10-20', minQty: 5 },
   { name: '원두', qty: 4, unit: 'kg', expires: '2027-01-10', minQty: 2 },
@@ -7,4 +7,18 @@ const items = [
 assert.deepEqual(lowStock(items), ['우유']);
 assert.deepEqual(orderList(items), [{ name: '우유', need: 7, unit: 'L' }]);
 assert.equal(daysBetween('2026-10-18', '2026-10-20'), 2);
+
+const dated = [
+  { name: '시럽', qty: 1, unit: '병', expires: '2026-10-21', minQty: 1 }, // +3일 (경계)
+  { name: '크림', qty: 1, unit: 'L', expires: '2026-10-17', minQty: 1 }, // 어제 만료
+  { name: '우유', qty: 1, unit: 'L', expires: '2026-10-18', minQty: 1 }, // 오늘
+  { name: '원두', qty: 1, unit: 'kg', expires: '2026-10-22', minQty: 1 }, // +4일
+  { name: '버터', qty: 1, unit: 'kg', expires: '2026-09-30', minQty: 1 }, // 만료
+  { name: '치즈', qty: 1, unit: 'kg', expires: '2026-11-01', minQty: 1 }, // 월 넘김, 여유
+];
+assert.deepEqual(expiringSoon(dated, '2026-10-18'), ['시럽', '우유']);
+assert.deepEqual(expired(dated, '2026-10-18'), ['크림', '버터']);
+assert.deepEqual(expiringSoon(dated, '2026-10-30'), ['치즈']); // 월 경계 넘어 계산
+assert.deepEqual(expiringSoon([], '2026-10-18'), []);
+assert.deepEqual(expired([], '2026-10-18'), []);
 console.log('기본 테스트 통과');