이번 편의 결과물: add로 메모리 배열에 할 일 객체를 추가하고, list로 전체 목록을 출력합니다. · 다루는 개념: 배열 메서드(push/find), 객체 리터럴, 구조 분해, 스프레드/레스트
이 편에서 만드는 파일
todo-cli/
└── src/
└── index.js (~)개념 정리
할 일 객체의 모양
todo-cli가 다루는 할 일 하나는 아래 필드를 가진 객체입니다. 이 모양은 08편에서 파일로 저장할 때도 그대로 씁니다.
| 필드 | 타입 | 설명 |
|---|---|---|
id | number | 순번 |
title | string | 할 일 내용 |
done | boolean | 완료 여부 |
tags | array of string | 태그 목록 |
due | string 또는 null | 마감일 문자열, 없으면 null |
createdAt | string | 생성 시각(ISO 문자열) |
배열 메서드
| 메서드 | 역할 | 원본 배열 변경 |
|---|---|---|
push(item) | 끝에 항목 추가 | 변경함 |
find(fn) | 조건에 맞는 첫 항목 반환 | 변경 안 함 |
forEach(fn) | 항목마다 함수 실행(반환값 없음) | 변경 안 함 |
filter, sort, map은 04편에서 다룹니다.
구조 분해와 스프레드
구조 분해(destructuring)는 배열이나 객체에서 값을 꺼내 변수에 바로 담는 문법입니다.
const { tag, due } = flags; // 객체 구조 분해
const [first, ...rest] = todos; // 배열 구조 분해 + 레스트스프레드(...)는 반대로 배열이나 객체를 펼쳐 새 값을 만듭니다. 원본을 바꾸지 않고 복사본을 만들 때 자주 씁니다.
const updated = { ...todo, done: true }; // todo는 그대로, updated만 done이 true이 편에서 만드는 todos 배열은 프로그램이 끝나면 사라지는 메모리 배열입니다. 파일에 저장해 다음 실행에도 남기는 방법은 08편(비동기 파일 저장)에서 다룹니다.
실습
1. src/index.js 열기
02편에서 만든 인자 파싱 코드에 할 일 배열과 명령 처리 로직을 추가합니다.
2. 코드 작성
// src/index.js
import { parseArgs } from './cli/parseArgs.js';
const todos = [
{
id: 1,
title: '리포트 제출',
done: false,
tags: ['work'],
due: '2026-09-20',
createdAt: new Date().toISOString(),
},
{
id: 2,
title: '운동하기',
done: true,
tags: ['health'],
due: null,
createdAt: new Date().toISOString(),
},
];
function nextId(list) {
const last = list[list.length - 1];
return last ? last.id + 1 : 1;
}
function addTodo(list, title, { tag, due } = {}) {
const todo = {
id: nextId(list),
title,
done: false,
tags: tag ? [tag] : [],
due: due ?? null,
createdAt: new Date().toISOString(),
};
list.push(todo);
return todo;
}
function printList(list) {
list.forEach(({ id, title, done, tags, due }) => {
const mark = done ? '[x]' : '[ ]';
const tagText = tags.length > 0 ? tags.join(', ') : '없음';
const dueText = due ?? '없음';
console.log(`${mark} #${id} ${title} (태그: ${tagText}, 마감: ${dueText})`);
});
}
const argv = process.argv.slice(2);
const { command, positional, flags } = parseArgs(argv);
if (command === 'add') {
const [title] = positional;
const created = addTodo(todos, title, flags);
console.log('추가됨:', created);
printList(todos);
} else if (command === 'list') {
printList(todos);
} else {
console.log('알 수 없는 명령입니다:', command);
}addTodo의 두 번째 매개변수 { tag, due } = {}는 객체 구조 분해와 기본값을 함께 씁니다. flags에 tag나 due가 없어도 undefined로 처리되어 오류가 나지 않습니다. printList에서도 배열 원소를 바로 { id, title, done, tags, due }로 구조 분해해서 씁니다.
3. 실행
node src/index.js listnode src/index.js add "포트폴리오 프로젝트 카드 6개 채우기" --tag portfolio확인
list실행 결과:
[ ] #1 리포트 제출 (태그: work, 마감: 2026-09-20)
[x] #2 운동하기 (태그: health, 마감: 없음)add실행 결과(뒤 3줄은 방금 추가한 항목까지 포함한 전체 목록,createdAt값은 실행 시각에 따라 달라집니다):
추가됨: {
id: 3,
title: '포트폴리오 프로젝트 카드 6개 채우기',
done: false,
tags: [ 'portfolio' ],
due: null,
createdAt: '(실행 시각)'
}
[ ] #1 리포트 제출 (태그: work, 마감: 2026-09-20)
[x] #2 운동하기 (태그: health, 마감: 없음)
[ ] #3 포트폴리오 프로젝트 카드 6개 채우기 (태그: portfolio, 마감: 없음)add를 한 번 더 실행해도 처음 두 항목만 다시 나타납니다. 메모리 배열이 프로세스가 끝날 때마다 초기화되기 때문입니다.
직접 해보기
find로id가 2인 할 일을 찾아 콘솔에 출력하는 코드를todos.find(...)형태로 추가해봅니다.- 배열 구조 분해로
todos의 첫 번째 항목과 나머지 항목을const [first, ...rest] = todos;로 나눠 각각 출력해봅니다.
정답 보기
const found = todos.find((todo) => todo.id === 2);
console.log('찾음:', found);
const [first, ...rest] = todos;
console.log('첫 항목:', first);
console.log('나머지:', rest);자주 하는 실수
| 증상 | 원인 | 고치는 법 |
|---|---|---|
add 실행 후 list로 다시 봐도 추가한 항목이 없음 | 각 실행이 별도 프로세스라 메모리 배열이 초기화됨 | 08편에서 파일 저장을 배우기 전까지는 정상 동작이다 |
tags.join에서 오류 발생 | tag 플래그를 안 줘서 tags가 undefined가 됨 | tag ? [tag] : []처럼 기본값을 명시한다 |
구조 분해 시 Cannot destructure property of undefined | flags 자체가 undefined인 상태에서 분해 시도 | 매개변수에 = {} 기본값을 붙인다 |
확인 문제
문제 14지선다
배열의 push 메서드에 대한 설명으로 옳은 것은?
문제 24지선다
const { tag, due } = flags; 코드에서 사용된 문법은?
문제 34지선다
const updated = { ...todo, done: true }; 의 결과로 옳은 것은?
문제 44지선다
addTodo 함수에서 { tag, due } = {} 처럼 기본값을 준 이유는?
참고 자료
Last updated on