내장 Explanatory 스타일 1회차 · Claude Code · Opus 5.5 · 생각 깊이 중간 · 가상 카페 재고 코드 ·
목록내장 Explanatory 스타일 1회차
클로드의 마지막 답변 (claude -p 화면에 찍히는 부분, 그대로)
`src/stock.mjs`에 두 함수를 추가했고, `test/stock.test.mjs`에 테스트를 넣어 돌렸더니 기존 테스트와 새 테스트 모두 통과했습니다.
- **`expiringSoon(items, today)`**: 남은 날짜가 0~3일인 재료 이름을 들어온 순서대로 돌려줍니다.
- **`expired(items, today)`**: 남은 날짜가 0보다 작은, 즉 유통기한이 지난 재료 이름을 들어온 순서대로 돌려줍니다.
테스트(오늘 = `2026-10-30`)는 경계 사례 위주로 넣었습니다.
- 유통기한이 오늘인 경우(포함)와 3일 뒤인 경우(포함), 4일 뒤인 경우(제외)
- 하루 지난 경우와 한 달 지난 경우
- 10/30 → 11/02처럼 달이 바뀌는 경우
- 빈 목록
- 결과 배열이 들어온 순서를 그대로 지키는지
`★ Insight ─────────────────────────────────────`
- 테스트 날짜를 10/30로 잡은 건 달이 바뀌는 경우를 확인하려고서입니다. 문자열 비교나 "일(day)" 숫자만 빼는 방식이면 여기서 틀리는데, 지금 코드는 날짜를 UTC 타임스탬프로 바꿔 빼기 때문에 문제없이 지나갑니다.
- 테스트 목록의 순서를 일부러 날짜순이 아니게 섞어 두었습니다(+3, 오늘, +4, …). 그래야 결과가 날짜순으로 정렬되지 않고 들어온 순서를 지키는지 실제로 확인됩니다.
`─────────────────────────────────────────────────`
바뀐 코드 (git diff)
diff --git a/src/stock.mjs b/src/stock.mjs
index f81b897..77aaa20 100644
--- a/src/stock.mjs
+++ b/src/stock.mjs
@@ -19,3 +19,16 @@ export function orderList(items) {
.filter((it) => it.qty < it.minQty)
.map((it) => ({ name: it.name, need: it.minQty * 2 - it.qty, unit: it.unit }));
}
+
+export function expiringSoon(items, today) {
+ return items
+ .filter((it) => {
+ const d = daysBetween(today, it.expires);
+ return d >= 0 && d <= 3;
+ })
+ .map((it) => it.name);
+}
+
+export function expired(items, today) {
+ return items.filter((it) => daysBetween(today, it.expires) < 0).map((it) => it.name);
+}
diff --git a/test/stock.test.mjs b/test/stock.test.mjs
index 71de632..7e19489 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 },
@@ -8,3 +8,19 @@ assert.deepEqual(lowStock(items), ['우유']);
assert.deepEqual(orderList(items), [{ name: '우유', need: 7, unit: 'L' }]);
assert.equal(daysBetween('2026-10-18', '2026-10-20'), 2);
console.log('기본 테스트 통과');
+
+// 유통기한: 경계값(오늘, +3일, +4일, -1일)과 월 경계 포함
+const today = '2026-10-30';
+const shelf = [
+ { name: '생크림', qty: 1, unit: 'L', expires: '2026-11-02', minQty: 1 }, // +3 (월 넘김)
+ { name: '우유', qty: 2, unit: 'L', expires: '2026-10-30', minQty: 1 }, // 오늘
+ { name: '시럽', qty: 1, unit: '병', expires: '2026-11-03', minQty: 1 }, // +4
+ { name: '치즈', qty: 1, unit: 'kg', expires: '2026-10-29', minQty: 1 }, // -1
+ { name: '요거트', qty: 1, unit: 'kg', expires: '2026-10-31', minQty: 1 }, // +1
+ { name: '빵', qty: 1, unit: '개', expires: '2026-09-30', minQty: 1 }, // -30
+];
+assert.deepEqual(expiringSoon(shelf, today), ['생크림', '우유', '요거트']);
+assert.deepEqual(expired(shelf, today), ['치즈', '빵']);
+assert.deepEqual(expiringSoon([], today), []);
+assert.deepEqual(expired([], today), []);
+console.log('유통기한 테스트 통과');