Node.jsを使用したサーバーサイド開発やCLIツールの作成において、ファイルシステム上のディレクトリ操作は避けて通れない要素の一つです。
特にフォルダの削除処理は、一時的な作業ディレクトリのクリーンアップや不要なデータの整理など、多くの場面で必要とされます。
以前のNode.jsでは fs.rmdir が主に使用されていましたが、現在のバージョンではより強力な fs.rm が推奨されるようになっています。
この記事では、Node.jsでフォルダを効率的かつ安全に削除するための最新の手法と、使い分けのポイントを整理して紹介します。
Node.jsにおけるフォルダ削除の変遷
Node.jsの初期から存在する fs.rmdir は、元々「空のディレクトリを削除する」ための関数として設計されていました。
しかし、開発現場からは「中身が含まれるディレクトリも一括で削除したい」という強い要望が常にありました。
これに応える形で、かつては recursive: true というオプションが fs.rmdir に追加された時期もありました。
現在では、その機能はより汎用的な fs.rm メソッドに集約 されています。
Node.js v14.14.0以降、ファイルとディレクトリの両方を削除できる fs.rm が導入されたことで、フォルダ削除のベストプラクティスが大きく変わりました。
fs.rmメソッドによる最新のフォルダ削除
現在のNode.js開発において、ディレクトリを削除する際の第一選択肢は fs.rm です。
このメソッドは、ファイル、ディレクトリ、シンボリックリンクのいずれも削除できる汎用性を持っています。
非同期(Promise版)での削除方法
モダンなJavaScript開発では、コールバック地獄を避けるために fs/promises モジュールを使用するのが一般的です。
import { rm } from 'fs/promises';
async function deleteDirectory(path) {
try {
// recursive: true で中身ごと削除、force: true で存在しなくてもエラーにしない
await rm(path, { recursive: true, force: true });
console.log(`削除に成功しました: ${path}`);
} catch (err) {
console.error(`削除に失敗しました: ${err.message}`);
}
}
deleteDirectory('./temp_data');
削除に成功しました: ./temp_data
主要なオプションの解説
fs.rm を使用する際には、第2引数のオプションオブジェクトが非常に重要な役割を果たします。
- recursive:
trueに設定すると、指定したフォルダ内のすべてのファイルとサブフォルダを再帰的に削除します。 - force:
trueに設定すると、指定したパスが存在しない場合でも例外(エラー)をスローしません。 - retryDelay: 削除に失敗した際のリトライ間隔をミリ秒単位で指定できます。
- maxRetries: Windows環境などでファイルがロックされている場合に備え、削除を試行する最大回数を指定できます。
中身があるフォルダを消す場合は、必ず recursive: true を指定してください。
fs.rmdirが使用される限定的なケース
fs.rm が推奨される一方で、fs.rmdir も依然として存在していますが、その役割は限定的です。
現在、fs.rmdir は 「空のディレクトリのみを削除する」 という用途に特化しています。
fs.rmdirの使用例
import { rmdir } from 'fs/promises';
async function removeEmptyDir(path) {
try {
// 空でない場合はエラーが発生する
await rmdir(path);
console.log('空のフォルダを削除しました');
} catch (err) {
if (err.code === 'ENOTEMPTY') {
console.error('エラー:フォルダは空ではありません');
} else {
console.error(`エラー:${err.code}`);
}
}
}
removeEmptyDir('./empty_folder');
このように、「対象が空であること」を保証したいビジネスロジック がある場合には、あえて fs.rmdir を使う意味があります。
ただし、再帰的な削除オプションは非推奨(Deprecated)となっているため、将来的な互換性を考慮すると使用すべきではありません。
fs.rmとfs.rmdirの比較表
それぞれのメソッドの違いを理解するために、以下の比較表を参考にしてください。
| 機能 | fs.rm | fs.rmdir |
|---|---|---|
| 空のフォルダ削除 | 可能 | 可能 |
| 中身のあるフォルダ削除 | 可能(recursiveオプション) | 非推奨(将来削除予定) |
| ファイルの削除 | 可能 | 不可能 |
| 存在しない場合の無視 | 可能(forceオプション) | 不可能(エラーになる) |
同期処理(Sync)と非同期処理の選び方
Node.jsには、fs.rmSync のような同期実行版のメソッドも用意されています。
同期版はスクリプトが完了するまで次の行の実行をブロックするため、パフォーマンスに影響を与える可能性があります。
しかし、プログラムの起動時に初期化として特定のフォルダを消す場合など、実行順序を確実に保証したいシンプルなスクリプトでは重宝されます。
import { rmSync } from 'fs';
// 起動時にキャッシュフォルダをクリアする例
try {
rmSync('./cache', { recursive: true, force: true });
console.log('キャッシュをクリアしました');
} catch (err) {
console.error('キャッシュのクリアに失敗しました');
}
高トラフィックなWebサーバー内でのリクエスト処理中には、非同期版の rm を使用すべきです。
安全にフォルダを削除するための注意点
フォルダの削除は取り返しのつかない操作であるため、実装時にはいくつかのリスク対策が必要です。
まず、削除対象のパスが意図しない場所(ルートディレクトリなど)になっていないかを事前にチェックしてください。
特にユーザーからの入力をパスに含める場合は、パスの正規化を行い、ディレクトリトラバーサル攻撃を防ぐ必要があります。
また、Windows環境では他のプロセスがファイルを開いていると、削除が拒否される EBUSY エラーが発生することがよくあります。
このような不安定な環境では、先述の maxRetries オプションを適切に設定することで、エラーの発生率を下げることができます。
エラーハンドリングの実践
フォルダ削除において発生しやすいエラーコードを知っておくことで、堅牢なプログラムを作成できます。
代表的なエラーコードには以下のものがあります。
- ENOENT: 指定されたパスにファイルやフォルダが存在しません。
- EACCES: 削除に必要な権限がありません。
- ENOTEMPTY: フォルダが空ではないため、
fs.rmdirでの削除に失敗しました。 - EBUSY: ファイルが他のプロセスによってロックされています。
これらのエラーを try-catch ブロックで適切に捕捉し、ログ出力やリトライ処理を行うことがプロフェッショナルな実装への第一歩です。
まとめ
Node.jsでフォルダを削除する方法は、時代とともに進化し、現在の主流は fs.rm メソッド になっています。
fs.rm を使用すれば、中身のあるディレクトリの再帰的な削除から、ファイル単体の削除まで柔軟に対応可能です。
一方で、fs.rmdir は「空のディレクトリのみを削除する」という特定の目的がある場合にのみ使用を検討してください。
非同期処理が推奨されるモダンな環境では fs/promises を活用し、安全で効率的なファイル操作を実現しましょう。
今回の内容を参考に、プロジェクトの要件に合わせた最適なフォルダ削除処理を実装してみてください。
