Skip to Content
WebJavaScriptECMAScript04. 함수·클로저·고차 함수로 필터링·정렬

이번 편의 결과물: list --filter done|pending, list --sort created|due가 동작합니다. · 다루는 개념: 함수 선언 방식과 화살표 함수, 클로저, filter/sort/map

이 편에서 만드는 파일

todo-cli/ └── src/ └── index.js (~)

개념 정리

함수를 선언하는 세 가지 방식

방식예시특징
함수 선언function add(a, b) { return a + b; }호이스팅됨(선언 전에 호출 가능)
함수 표현식const add = function (a, b) { return a + b; };변수에 담긴 뒤에만 호출 가능
화살표 함수const add = (a, b) => a + b;this를 새로 만들지 않음, 간결한 표기

이 프로젝트는 화살표 함수와 함수 선언을 함께 씁니다. 짧은 콜백은 화살표 함수, 이름이 필요한 로직은 함수 선언을 사용합니다.

클로저

클로저(closure)는 함수가 자신이 정의될 때의 스코프를 기억해, 함수 밖으로 나간 뒤에도 그 스코프의 변수에 접근할 수 있는 것을 말합니다. 아래 makeStatusFilterstatus 값을 기억하는 함수를 반환합니다.

function makeStatusFilter(status) { return (todo) => todo.done === (status === 'done'); } const isDone = makeStatusFilter('done'); isDone({ done: true }); // true, status는 makeStatusFilter 실행이 끝난 뒤에도 기억됨

makeStatusFilter 실행은 끝났지만 반환된 화살표 함수는 status를 계속 참조합니다. 이것이 클로저입니다.

고차 함수: filter, sort

고차 함수(higher-order function)는 함수를 인자로 받거나 함수를 반환하는 함수입니다. filtersort는 조건 함수(비교 함수)를 인자로 받는 대표적인 배열 고차 함수입니다.

메서드인자로 받는 함수반환값원본 배열
filter(fn)(item) => boolean조건을 만족하는 항목만 담긴 새 배열변경 안 함
sort(fn)(a, b) => number정렬된 배열(원본과 같은 참조)변경함

sort는 원본 배열을 직접 바꾸는 유일한 메서드입니다. 원본을 보존하는 toSorted는 16편(최신 문법)에서 다룹니다. 비교 함수는 ab보다 앞에 와야 하면 음수, 뒤에 와야 하면 양수를 반환해야 합니다.

실습

1. src/index.js 열기

03편의 todos 배열과 addTodo, printList는 그대로 두고 필터·정렬 함수를 추가합니다.

2. 코드 작성

// src/index.js import { parseArgs } from './cli/parseArgs.js'; const todos = [ { id: 1, title: '리포트 제출', done: false, tags: ['work'], due: '2026-09-20', createdAt: '2026-09-15T09:00:00.000Z', }, { id: 2, title: '운동하기', done: true, tags: ['health'], due: null, createdAt: '2026-09-15T10:00:00.000Z', }, { id: 3, title: 'todo-cli에 검색 명령 추가', done: false, tags: ['study', 'node'], due: '2026-09-22', createdAt: '2026-09-16T08:10:00.000Z', }, ]; 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 makeStatusFilter(status) { if (status === 'done') return (todo) => todo.done; if (status === 'pending') return (todo) => !todo.done; return () => true; } function makeComparator(sortKey) { if (sortKey === 'due') { return (a, b) => { if (a.due === null && b.due === null) return 0; if (a.due === null) return 1; if (b.due === null) return -1; return a.due.localeCompare(b.due); }; } return (a, b) => a.createdAt.localeCompare(b.createdAt); } 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') { const filterFn = makeStatusFilter(flags.filter); const compareFn = makeComparator(flags.sort); const result = todos.filter(filterFn).sort(compareFn); printList(result); } else { console.log('알 수 없는 명령입니다:', command); }

makeStatusFiltermakeComparator는 둘 다 함수를 반환하는 고차 함수이고, 반환된 함수는 클로저로 바깥 매개변수를 기억합니다. flags.filterflags.sort가 없으면 각각 “전부 통과”와 “생성일순” 기본 동작으로 떨어집니다.

3. 실행

node src/index.js list --filter pending
node src/index.js list --sort due

확인

  • --filter pending 결과(완료되지 않은 항목만):
[ ] #1 리포트 제출 (태그: work, 마감: 2026-09-20) [ ] #3 todo-cli에 검색 명령 추가 (태그: study, node, 마감: 2026-09-22)
  • --sort due 결과(마감일 빠른 순, null은 맨 뒤):
[ ] #1 리포트 제출 (태그: work, 마감: 2026-09-20) [ ] #3 todo-cli에 검색 명령 추가 (태그: study, node, 마감: 2026-09-22) [x] #2 운동하기 (태그: health, 마감: 없음)

직접 해보기

  1. flags.filter'done''pending'도 아닌 값을 넣고 실행해 makeStatusFilter가 기본값(전부 통과)으로 동작하는지 확인합니다.
  2. title 기준 정렬을 지원하는 --sort titlemakeComparator에 추가해봅니다.

정답 보기

function makeComparator(sortKey) { if (sortKey === 'due') { return (a, b) => { if (a.due === null && b.due === null) return 0; if (a.due === null) return 1; if (b.due === null) return -1; return a.due.localeCompare(b.due); }; } if (sortKey === 'title') { return (a, b) => a.title.localeCompare(b.title); } return (a, b) => a.createdAt.localeCompare(b.createdAt); }

자주 하는 실수

증상원인고치는 법
정렬 결과가 뒤죽박죽 나옴비교 함수가 boolean을 반환함(a.due > b.due만 반환)반드시 음수/0/양수를 반환하는 비교 함수를 만든다
list 두 번 실행하면 순서가 계속 바뀜sort가 원본 배열을 직접 변경함원본을 유지하려면 [...todos].sort(...)처럼 복사 후 정렬한다
filter 결과가 항상 빈 배열조건 함수가 undefined를 반환(화살표 함수 중괄호 안에서 return 누락)화살표 함수 본문에 중괄호를 쓰면 return을 명시한다

확인 문제

문제 14지선다
클로저에 대한 설명으로 옳은 것은?
문제 24지선다
filter와 sort의 차이로 옳은 것은?
문제 34지선다
makeStatusFilter('done')이 반환하는 함수가 status 값을 계속 기억하는 이유는?
문제 44지선다
비교 함수 a.due.localeCompare(b.due)를 반환하는 sort 콜백에서, 그 반환값의 의미로 옳은 것은?

참고 자료

Last updated on