Node.js fsPromises.glob 가이드: 파일 탐색 코드를 단순하게 만드는 법
빌드 스크립트나 콘텐츠 자동화 작업을 만들다 보면, 결국 해야 하는 일은 단순합니다. “어떤 패턴에 맞는 파일을 안전하게 찾고, 그 목록을 다음 단계로 넘기는 것”입니다.
문제는 이 단순한 요구를 구현할 때 readdir() 재귀 순회, 경로 필터링, 확장자 체크가 뒤섞이면서 코드가 빠르게 복잡해진다는 점입니다.
fsPromises.glob()은 이런 파일 탐색 코드를 훨씬 짧고 읽기 좋게 정리할 때 꽤 유용합니다.
fsPromises.glob가 필요한 이유
H3. 파일 탐색 로직은 생각보다 금방 중복된다
스크립트를 몇 개만 만들어도 이런 코드가 반복됩니다.
_posts에서 특정 날짜 파일만 찾기src아래에서 확장자별 파일 수집하기- 로그 폴더에서 패턴에 맞는 파일만 모으기
- 배포 전 생성 산출물만 따로 훑기
이때 디렉터리 순회를 직접 구현하면, 파일 탐색 자체보다 예외 처리와 경로 조합 코드가 더 많아지기 쉽습니다.
H3. 패턴 기반 탐색은 의도를 더 직접적으로 드러낸다
glob의 장점은 분명합니다.
- 찾고 싶은 대상을 패턴으로 바로 표현할 수 있다
- 재귀 순회를 직접 구현할 필요가 줄어든다
- 후속 필터링 로직이 더 단순해진다
특히 ESM 기반 스크립트에서 경로 기준점을 분명히 잡으려면 Node.js import.meta.dirname 가이드: ESM에서 현재 파일 경로를 안전하게 다루는 법과 함께 보는 편이 좋습니다.
fsPromises.glob 기본 사용법
H3. 특정 패턴의 파일 목록을 모은다
import { glob } from 'node:fs/promises';
const files = [];
for await (const file of glob('src/**/*.js')) {
files.push(file);
}
console.log(files);
핵심은 glob()이 비동기 iterable을 돌려준다는 점입니다.
그래서 한꺼번에 배열로 모을 수도 있고, 필요하면 순차 처리도 할 수 있습니다.
H3. 배열로 모아 다음 단계에 넘긴다
import { glob } from 'node:fs/promises';
const files = await Array.fromAsync(glob('content/**/*.md'));
for (const file of files) {
console.log(file);
}
파일 개수가 아주 많지 않다면 이런 방식이 읽기 쉽습니다. 비동기 iterable을 배열로 수집하는 감각은 Node.js Array.fromAsync 가이드: 비동기 iterable을 배열로 안전하게 모으는 법과도 자연스럽게 이어집니다.
실무에서 유용한 패턴
H3. Jekyll 포스트 후보 파일만 골라낸다
import { glob } from 'node:fs/promises';
const posts = await Array.fromAsync(
glob('_posts/2026-05-*.md')
);
console.log(posts);
콘텐츠 운영 자동화에서는 날짜 규칙에 맞는 파일만 찾는 일이 자주 있습니다. 이 패턴을 쓰면 포스트 생성 여부 점검, 누락 검사, 후처리 스크립트를 단순하게 만들 수 있습니다.
H3. 순차 처리로 메모리와 제어 흐름을 단순하게 유지한다
import { glob } from 'node:fs/promises';
import { readFile } from 'node:fs/promises';
for await (const file of glob('logs/**/*.log')) {
const content = await readFile(file, 'utf8');
console.log(file, content.length);
}
파일이 많을 때는 전부 배열에 담은 뒤 한꺼번에 읽기보다, 이렇게 찾는 즉시 처리하는 흐름이 더 안전할 때가 많습니다. 큰 로그 파일을 다룰 때는 Node.js FileHandle.readLines 가이드: 대용량 로그를 메모리 부담 없이 처리하는 법처럼 읽기 단계도 스트리밍 관점으로 이어서 설계하면 좋습니다.
H3. 찾기와 검증을 분리한다
import { glob } from 'node:fs/promises';
import path from 'node:path';
const files = await Array.fromAsync(glob('src/**/*.*'));
const jsFiles = files.filter((file) =>
['.js', '.mjs', '.cjs'].includes(path.extname(file))
);
glob 패턴 하나로 모든 정책을 해결하려 하기보다, 탐색은 탐색대로 두고 검증은 다음 단계로 분리하는 편이 유지보수에 유리합니다.
나중에 허용 확장자나 제외 규칙이 바뀌어도 수정 지점이 분명해집니다.
fsPromises.glob를 쓸 때 주의할 점
H3. 파일이 너무 많으면 무조건 배열화하지 않는 편이 낫다
Array.fromAsync(glob(...))는 편하지만, 결과 개수가 커지면 메모리 사용량이 늘어납니다.
작업 목적이 “모두 수집”이 아니라 “하나씩 처리”라면 for await...of가 더 나은 기본값입니다.
H3. 패턴이 넓을수록 의도하지 않은 파일이 섞일 수 있다
예를 들어 **/* 같은 패턴은 편하지만, 캐시 파일·빌드 산출물·숨김 파일까지 함께 잡을 수 있습니다.
실무에서는 아래처럼 생각하는 편이 안전합니다.
- 먼저 탐색 범위를 가능한 한 좁힌다
- 그다음 확장자나 파일명 규칙을 검증한다
- 마지막으로 처리 대상만 후속 단계에 넘긴다
H3. 상대 경로 기준점을 애매하게 두면 CI에서 흔들린다
로컬에서는 되는데 CI에서 경로가 달라지는 경우가 꽤 많습니다.
특히 스크립트 실행 위치가 매번 같지 않다면, 현재 작업 디렉터리에 기대기보다 기준 경로를 명시하는 편이 낫습니다.
경로 기준을 정리하는 방법은 앞서 연결한 import.meta.dirname 글과 함께 보면 더 실전적으로 감이 잡힙니다.
추천 적용 체크리스트
H3. 도입 전에 이 다섯 가지를 먼저 확인한다
- 파일 탐색 결과를 배열로 모아야 하는가, 바로 순차 처리하면 되는가?
- 패턴 범위가 너무 넓어서 불필요한 파일이 섞이지 않는가?
- 탐색 단계와 검증 단계를 분리했는가?
- CI와 로컬에서 같은 기준 경로를 쓰는가?
- 대용량 파일은 읽기 단계도 별도로 최적화했는가?
이 다섯 가지만 점검해도 파일 자동화 스크립트의 안정성이 꽤 올라갑니다.
마무리
fsPromises.glob()은 화려한 기능이라기보다, 반복되는 파일 탐색 코드를 의도 중심으로 줄여 주는 실용 도구에 가깝습니다.
직접 재귀 순회를 짜는 것보다 짧고, 나중에 읽었을 때도 “무엇을 찾는 코드인지”가 더 분명하게 보입니다.
정리하면 이렇게 기억하면 됩니다.
- 파일 탐색 의도는 패턴으로 먼저 표현한다
- 결과가 많으면 배열화보다 순차 처리를 우선한다
- 탐색, 검증, 읽기 단계를 분리하면 운영이 쉬워진다
함께 보면 좋은 글
이 글은 AI가 작성/정리하고, 오너가 방향을 결정한 프로젝트 운영 로그입니다.