플러그인 없이 1회차 · 요청 3: 요청 2 + "핵심 부분은 직접 짜 보고 싶어" · Claude Code · Opus 5.5 · 생각 깊이 중간 · 목록

플러그인 없이 1회차

요청 3: 요청 2 + "핵심 부분은 직접 짜 보고 싶어"

요청문: src/stock.mjs에 todayTasks(items, today) 함수를 만들어 줘. 아침에 카페 사장님이 보는 '오늘 할 일' 목록이야. 유통기한이 지난 것, 3일 안에 끝나는 것, 수량이 minQty보다 적은 것을 한 목록으로 합쳐서 급한 것부터 보여 주고 싶어. today는 'YYYY-MM-DD' 문자열이야. test/stock.test.mjs에 테스트도 추가하고 돌려 봐. 나는 코딩을 배우는 중이라 핵심 부분은 직접 짜 보고 싶어.

클로드의 마지막 답변 (그대로)

`todayTasks`의 틀은 만들어 두었고, 순서를 정하는 핵심 부분 `compareTasks`는 직접 짜 보시라고 비워 뒀어요. 지금 테스트를 돌리면 이렇게 나옵니다. ``` 기본 테스트 통과 todayTasks 항목 테스트 통과 AssertionError: 지난 것이 맨 앞 ← compareTasks가 아직 0을 돌려줘서 실패 ``` 순서 테스트가 실패하는 건 정상이에요. `compareTasks`를 채우면 통과해야 합니다. ## 제가 만든 부분 (`src/stock.mjs`) - `todayTasks(items, today)`는 재료를 하나씩 보면서 할 일을 `{ name, kind, days }` 형태로 모으고, 마지막에 `tasks.sort(compareTasks)`로 정렬해요. - `kind`는 셋 중 하나예요: `'expired'`(유통기한 지남), `'expiring'`(3일 안에 끝남), `'low'`(minQty 미만) - `days`는 유통기한까지 남은 날이에요. 오늘이 마감이면 0, 이미 지났으면 음수예요. - 경계는 이렇게 정했어요. 바꾸고 싶으면 말씀해 주세요. - 오늘이 마감(생크림)이면 아직 "지난 것"은 아니라서 `expiring`으로 분류돼요. - 딱 3일 남은 것(바닐라 시럽)도 목록에 들어가요. - 한 재료가 두 조건에 걸리면 할 일도 두 건이 돼요. 예를 들어 치즈케이크는 "곧 끝남"(버리기)과 "부족"(주문하기)이 따로 들어가요. ## 직접 짜 볼 부분: `compareTasks(a, b)` a가 앞에 와야 하면 음수, b가 앞이면 양수, 같으면 0을 돌려주면 돼요. 코드보다 먼저 정해야 할 건 "사장님 입장에서 뭐가 더 급한가"예요. 1. **종류끼리의 순서:** 지난 것이 제일 먼저인 건 테스트로 정해 뒀어요. 그럼 "곧 끝남"과 "부족" 중엔 뭐가 먼저일까요? 버리는 건 바로 할 수 있고, 주문은 배송 시간이 걸린다는 점을 생각해 보세요. 2. **같은 종류 안에서의 순서:** `expiring`은 남은 날이 적은 게 먼저여야 해요(테스트에 있어요). `low`끼리는 이름순으로 할지, 부족한 정도로 할지 정해 보세요. 부족한 정도로 하려면 할 일 객체에 정보를 더 넣어야 해서 `todayTasks`도 고쳐야 해요. 힌트를 하나 드리면, 종류마다 순위 숫자를 붙여 두고(`{ expired: 0, ... }`) 순위를 먼저 비교한 다음, 같으면 `a.days - b.days`로 비교하는 방식이 흔해요. 다 짜면 `node test/stock.test.mjs`로 확인해 보세요. 세 줄이 모두 "통과"로 나오면 성공이에요. 막히거나 코드를 봐 달라고 하시면 리뷰해 드릴게요.

바뀐 코드 (git diff)

diff --git a/src/stock.mjs b/src/stock.mjs
index f81b897..ac6088b 100644
--- a/src/stock.mjs
+++ b/src/stock.mjs
@@ -19,3 +19,25 @@ export function orderList(items) {
     .filter((it) => it.qty < it.minQty)
     .map((it) => ({ name: it.name, need: it.minQty * 2 - it.qty, unit: it.unit }));
 }
+
+// 오늘 할 일 한 건: { name, kind, days }
+//   kind: 'expired'(유통기한 지남) | 'expiring'(3일 안에 끝남) | 'low'(minQty 미만)
+//   days: 유통기한까지 남은 날 (지났으면 음수, 오늘이면 0)
+// 한 재료가 여러 조건에 걸리면 할 일도 여러 건 (예: 버리기 + 주문하기)
+export function todayTasks(items, today) {
+  const tasks = [];
+  for (const it of items) {
+    const days = daysBetween(today, it.expires);
+    if (days < 0) tasks.push({ name: it.name, kind: 'expired', days });
+    else if (days <= 3) tasks.push({ name: it.name, kind: 'expiring', days });
+    if (it.qty < it.minQty) tasks.push({ name: it.name, kind: 'low', days });
+  }
+  return tasks.sort(compareTasks);
+}
+
+// 급한 할 일이 앞에 오도록 두 할 일을 비교한다.
+// a가 먼저면 음수, b가 먼저면 양수, 순서 상관없으면 0.
+export function compareTasks(a, b) {
+  // TODO(직접 작성): 무엇이 "급한" 것인지 정해서 구현해 보세요.
+  return 0;
+}
diff --git a/test/stock.test.mjs b/test/stock.test.mjs
index 71de632..2cb7abd 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, todayTasks } 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,35 @@ 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('기본 테스트 통과');
+
+// todayTasks — data.json과 같은 재고, 오늘은 2026-10-18
+const cafe = [
+  { name: '우유', qty: 3, unit: 'L', expires: '2026-10-20', minQty: 5 },
+  { name: '생크림', qty: 2, unit: '팩', expires: '2026-10-18', minQty: 2 },
+  { name: '딸기', qty: 1, unit: 'kg', expires: '2026-10-17', minQty: 1 },
+  { name: '원두', qty: 4, unit: 'kg', expires: '2027-01-10', minQty: 2 },
+  { name: '바닐라 시럽', qty: 1, unit: '병', expires: '2026-10-21', minQty: 1 },
+  { name: '치즈케이크', qty: 0, unit: '판', expires: '2026-10-19', minQty: 1 },
+];
+const tasks = todayTasks(cafe, '2026-10-18');
+const key = (t) => `${t.name}:${t.kind}`;
+
+// 1) 무엇이 목록에 들어가는지 (순서 무관)
+assert.deepEqual(tasks.map(key).sort(), [
+  '딸기:expired', // 어제 지남
+  '바닐라 시럽:expiring', // 딱 3일 남음 → 포함
+  '생크림:expiring', // 오늘까지 → 아직 지난 건 아님
+  '우유:expiring',
+  '우유:low',
+  '치즈케이크:expiring',
+  '치즈케이크:low', // 한 재료가 두 가지 할 일
+].sort());
+assert.ok(!tasks.some((t) => t.name === '원두'), '여유 있는 재료는 빠져야 함');
+assert.deepEqual(todayTasks([], '2026-10-18'), []);
+console.log('todayTasks 항목 테스트 통과');
+
+// 2) 급한 순서 (compareTasks를 구현하면 통과해야 함)
+assert.equal(key(tasks[0]), '딸기:expired', '지난 것이 맨 앞');
+const expiring = tasks.filter((t) => t.kind === 'expiring').map((t) => t.name);
+assert.deepEqual(expiring, ['생크림', '치즈케이크', '우유', '바닐라 시럽'], '남은 날이 적은 것부터');
+console.log('todayTasks 순서 테스트 통과');