Skip to Content
WebJavaScriptECMAScript11. 정규식으로 검색 명령 만들기

이번 편의 결과물: todo search <패턴>이 정규식으로 할 일 제목을 검색해 일치하는 항목만 출력하고, 일치한 부분을 대괄호로 표시합니다. · 다루는 개념: 정규식 리터럴/플래그, test/match/replace

이 편에서 만드는 파일

todo-cli/ └── src/ └── commands/ └── search.js (+)

src/index.js에도 search 명령 분기가 하나 추가됩니다.

개념 정리

정규식 리터럴과 플래그

정규식(regular expression)은 문자열에서 특정 패턴을 찾는 문법입니다. 슬래시(/) 사이에 패턴을 쓰고, 뒤에 플래그를 붙입니다.

표기의미
/제출/”제출”이라는 문자열을 포함하는지 검사
/제출/i대소문자 구분 없이 검사(i = ignore case)
/제출/g문자열 전체에서 모든 일치를 찾음(g = global)
new RegExp(문자열, 플래그)문자열로 정규식을 동적으로 생성

검색어를 사용자가 명령행에서 입력하는 todo-cli는 패턴을 코드에 미리 적어둘 수 없으므로, 리터럴(/.../) 대신 new RegExp() 생성자로 동적 정규식을 만듭니다.

정규식 메서드 3가지

메서드호출 주체반환값용도
regex.test(문자열)정규식boolean일치 여부만 확인
문자열.match(regex)문자열일치 정보 배열 또는 null일치한 내용 자체를 확인
문자열.replace(regex, 대체값)문자열치환된 새 문자열일치한 부분을 바꾸거나 표시

replace의 두 번째 인자로 함수를 넘기면, 일치한 문자열을 인자로 받아 원하는 형태로 가공할 수 있습니다. todo-cli 검색은 test로 일치 항목을 걸러내고, replace로 일치한 부분을 대괄호로 감싸 표시합니다.

잘못된 패턴 처리

사용자가 [처럼 정규식 문법으로는 불완전한 문자열을 입력하면 new RegExp()SyntaxError를 던집니다. try/catch로 감싸 사용자에게 읽을 수 있는 메시지를 보여줘야 합니다. 에러 처리 자체는 14편에서 더 다루고, 여기서는 최소한의 방어만 합니다.

실습

1. src/commands/search.js 파일 만들기

src/commands/ 폴더에 search.js를 새로 만듭니다.

2. 코드 작성

// src/commands/search.js export function runSearch(store, positional) { const [pattern] = positional; if (!pattern) { console.log('검색어를 입력하세요. 예: node src/index.js search 제출'); return; } let regex; try { regex = new RegExp(pattern, 'i'); } catch { console.log('올바르지 않은 정규식입니다:', pattern); return; } const matched = store.toArray().filter((todo) => regex.test(todo.title)); if (matched.length === 0) { console.log('일치하는 할 일이 없습니다.'); return; } matched.forEach((todo) => { const highlighted = todo.title.replace(regex, (match) => `[${match}]`); console.log(`#${todo.id} ${highlighted}`); }); }

regex.test(todo.title)은 제목에 패턴이 포함되어 있는지만 확인하므로 filter의 조건 함수로 바로 쓸 수 있습니다. title.replace(regex, 콜백)은 콜백이 돌려준 대괄호 문자열로 실제로 일치한 부분(대소문자가 섞여 있어도 원래 표기 그대로)을 대괄호로 감쌉니다. i 플래그를 고정으로 붙여 대소문자를 구분하지 않고 검색합니다.

src/index.jssearch 명령 분기를 추가합니다.

// src/index.js import { parseArgs } from './cli/parseArgs.js'; import { TodoStore } from './models/TodoStore.js'; import { loadTodos, saveTodos } from './storage/fileStore.js'; import { runAdd } from './commands/add.js'; import { runList } from './commands/list.js'; import { runTags } from './commands/tags.js'; import { runSearch } from './commands/search.js'; const argv = process.argv.slice(2); const { command, positional, flags } = parseArgs(argv); const rawTodos = await loadTodos(); const store = TodoStore.fromArray(rawTodos); if (command === 'add') { await runAdd(store, positional, flags); await saveTodos(store.toArray().map((todo) => todo.toJSON())); } else if (command === 'list') { runList(store, flags); } else if (command === 'tags') { runTags(store); } else if (command === 'search') { runSearch(store, positional); } else { console.log('알 수 없는 명령입니다:', command); }

3. 실행

node src/index.js search cli
node src/index.js search 카드

확인

  • search cli 결과(제목에 “cli”가 들어간 항목, 대소문자 구분 없이 “todo-cli”의 “cli”와 일치):
#3 todo-[cli]에 검색 명령 추가
  • search 카드 결과(제목에 “카드”가 들어간 항목):
#2 포트폴리오 프로젝트 [카드] 6개 채우기
  • search만 입력하고 검색어를 생략하면 “검색어를 입력하세요. 예: node src/index.js search 제출”가 출력됩니다.
  • search [처럼 정규식 문법이 불완전한 값을 넣으면 “올바르지 않은 정규식입니다: [“이 출력되고 프로그램이 죽지 않습니다.

직접 해보기

  1. search 명령에 --exact 플래그가 있을 때만 대소문자를 구분하도록(i 플래그를 빼도록) runSearch를 고쳐봅니다.
  2. 문자열.match(regex)를 콘솔에서 직접 실행해 일치한 부분과 인덱스 정보가 담긴 배열이 반환되는 것을 확인합니다.

정답 보기

// src/commands/search.js export function runSearch(store, positional, flags = {}) { const [pattern] = positional; if (!pattern) { console.log('검색어를 입력하세요. 예: node src/index.js search 제출'); return; } let regex; try { regex = new RegExp(pattern, flags.exact ? '' : 'i'); } catch { console.log('올바르지 않은 정규식입니다:', pattern); return; } const matched = store.toArray().filter((todo) => regex.test(todo.title)); if (matched.length === 0) { console.log('일치하는 할 일이 없습니다.'); return; } matched.forEach((todo) => { const highlighted = todo.title.replace(regex, (match) => `[${match}]`); console.log(`#${todo.id} ${highlighted}`); }); }

index.js의 호출부도 runSearch(store, positional, flags)로 세 번째 인자를 넘기도록 함께 고쳐야 합니다.

자주 하는 실수

증상원인고치는 법
대소문자가 다르면 검색이 안 됨i 플래그를 빼먹음new RegExp(pattern, 'i')처럼 플래그를 붙인다
특수문자가 든 검색어에서 오류 발생(, [ 같은 정규식 전용 문자를 그대로 패턴으로 씀try/catch로 감싸 사용자에게 안내 메시지를 보여준다
replace로 표시가 안 됨정규식에 g 플래그가 없어 첫 일치만 바뀜(여러 번 나오는 경우)모든 일치를 바꾸려면 g 플래그를 추가한다

확인 문제

문제 14지선다
regex.test(문자열)의 반환값 타입은?
문제 24지선다
new RegExp(pattern, 'i')에서 'i' 플래그의 의미는?
문제 34지선다
사용자가 입력한 검색어 문자열을 new RegExp()로 넘길 때 try/catch로 감싸는 이유는?
문제 44지선다
title.replace(regex, (match) => [${match}])에서 콜백 함수의 match 매개변수가 받는 값은?

참고 자료

Last updated on