디렉토리 탐색 개요

Dart에서 파일 시스템을 탐색하는 방법은 매우 직관적이다. dart:io 패키지를 사용하여 파일과 디렉토리를 처리할 수 있으며, 이를 통해 파일을 읽고 쓰는 작업뿐만 아니라 디렉토리의 내용을 검색하는 작업도 수행할 수 있다.

Dart의 Directory 클래스는 파일 시스템의 디렉토리를 나타내며, 디렉토리 내의 파일과 서브 디렉토리를 쉽게 탐색할 수 있는 기능을 제공한다.

Directory 클래스 사용법

Directory 클래스는 다음과 같이 사용된다.

import 'dart:io';

void listDirectory(String path) {
  var directory = Directory(path);
  directory.list().forEach((entity) {
    print(entity.path);
  });
}

이 코드는 주어진 경로 path에 있는 모든 파일과 디렉토리를 출력한다. directory.list() 메소드는 비동기적으로 Stream을 반환하므로 디렉토리의 내용을 순차적으로 처리할 수 있다.

재귀적으로 디렉토리 탐색

때로는 하위 디렉토리까지 포함하여 재귀적으로 파일을 탐색해야 할 경우가 있다. 이를 위해서는 listSync() 메소드를 사용하여 동기 방식으로 디렉토리를 탐색할 수 있다.

void listAllFiles(String path) {
  var directory = Directory(path);
  directory.listSync(recursive: true).forEach((entity) {
    print(entity.path);
  });
}

위 코드에서 recursive: true 옵션을 주면 하위 디렉토리까지 탐색하여 그 안에 있는 파일 및 디렉토리 경로를 모두 출력할 수 있다.

특정 확장자 파일 검색

디렉토리를 탐색하는 과정에서 특정 확장자의 파일만 검색하고 싶을 때는, 파일의 경로에서 확장자를 검사하는 방법을 사용할 수 있다. Dart에서는 entity.path를 통해 파일의 경로를 확인하고 이를 기반으로 확장자를 추출할 수 있다.

void listSpecificFiles(String path, String extension) {
  var directory = Directory(path);
  directory.listSync().forEach((entity) {
    if (entity.path.endsWith(extension)) {
      print(entity.path);
    }
  });
}

이 코드는 주어진 디렉토리에서 특정 확장자(예: .txt)를 가진 파일만 출력한다.

디렉토리 존재 여부 확인

디렉토리를 탐색하기 전에 해당 경로가 실제로 존재하는지 확인하는 것은 매우 중요하다. 이를 위해 Directory 클래스의 existsSync() 메소드를 사용할 수 있다.

bool checkDirectoryExists(String path) {
  var directory = Directory(path);
  return directory.existsSync();
}

이 코드는 주어진 경로의 디렉토리가 존재하는지 여부를 true 또는 false로 반환한다.

디렉토리 생성

파일 탐색을 하기 전에 원하는 경로에 디렉토리가 존재하지 않는다면 디렉토리를 생성해야 할 경우도 있다. Dart에서는 createSync() 메소드를 사용하여 동기적으로 디렉토리를 생성할 수 있다.

void createDirectory(String path) {
  var directory = Directory(path);
  if (!directory.existsSync()) {
    directory.createSync();
    print('Directory created: $path');
  } else {
    print('Directory already exists.');
  }
}

위 코드는 지정한 경로에 디렉토리가 존재하지 않으면 새 디렉토리를 생성하고, 이미 존재하면 생성하지 않는다.

또한, 비동기 방식으로 디렉토리를 생성하고 싶을 때는 create() 메소드를 사용할 수 있다.

Future<void> createDirectoryAsync(String path) async {
  var directory = Directory(path);
  if (!await directory.exists()) {
    await directory.create();
    print('Directory created: $path');
  } else {
    print('Directory already exists.');
  }
}

비동기 방식은 파일 시스템 작업이 완료되기를 기다리는 동안 다른 작업을 처리할 수 있어 더 효율적이다.

파일과 디렉토리 삭제

디렉토리나 파일을 삭제할 때는 deleteSync() 메소드를 사용한다. 이때, recursive: true 옵션을 주면 디렉토리 내 모든 파일과 서브 디렉토리를 재귀적으로 삭제할 수 있다.

void deleteDirectory(String path) {
  var directory = Directory(path);
  if (directory.existsSync()) {
    directory.deleteSync(recursive: true);
    print('Directory deleted: $path');
  } else {
    print('Directory does not exist.');
  }
}

비동기 방식으로 삭제할 경우에는 delete() 메소드를 사용할 수 있다.

Future<void> deleteDirectoryAsync(String path) async {
  var directory = Directory(path);
  if (await directory.exists()) {
    await directory.delete(recursive: true);
    print('Directory deleted: $path');
  } else {
    print('Directory does not exist.');
  }
}

이 코드들은 파일 시스템 내의 디렉토리나 파일을 안전하게 삭제하는 방법을 보여준다.

디렉토리의 경로 정보

Dart의 Directory 클래스는 여러 가지 유용한 경로 정보를 제공한다. 예를 들어, 사용자의 홈 디렉토리나 임시 디렉토리 경로를 얻을 수 있다.

void getDirectoryPaths() {
  print('Temporary Directory: ${Directory.systemTemp.path}');
  print('Current Directory: ${Directory.current.path}');
}

Directory.systemTemp는 임시 파일을 저장할 수 있는 시스템의 임시 디렉토리 경로를 반환하며, Directory.current는 현재 실행 중인 프로그램의 디렉토리 경로를 반환한다.

파일 시스템의 디렉토리 트리 시각화

여러 디렉토리를 재귀적으로 탐색할 때 그 구조를 시각화하여 보여줄 수 있다. Dart에서는 재귀적인 탐색을 통해 디렉토리 트리를 생성하고, 이를 콘솔에 출력할 수 있다.

void printDirectoryTree(String path, {String prefix = ''}) {
  var directory = Directory(path);
  if (directory.existsSync()) {
    directory.listSync().forEach((entity) {
      print('$prefix${entity.path}');
      if (entity is Directory) {
        printDirectoryTree(entity.path, prefix: '$prefix  ');
      }
    });
  }
}

이 함수는 지정된 경로를 재귀적으로 탐색하며, 디렉토리의 트리 구조를 보기 쉽게 출력해 준다. 이를 통해 파일 시스템의 디렉토리 구조를 시각적으로 이해하기 쉽다.

디렉토리의 메타데이터 접근

디렉토리뿐만 아니라 파일의 메타데이터 정보에도 접근할 수 있다. Dart에서는 파일과 디렉토리의 생성 날짜, 수정 날짜, 크기 등의 정보를 가져올 수 있다. 이를 위해서는 statSync() 또는 비동기적으로 stat() 메소드를 사용할 수 있다.

void printDirectoryMetadata(String path) {
  var directory = Directory(path);
  if (directory.existsSync()) {
    var stats = directory.statSync();
    print('Type: ${stats.type}');
    print('Changed: ${stats.changed}');
    print('Modified: ${stats.modified}');
    print('Accessed: ${stats.accessed}');
  } else {
    print('Directory does not exist.');
  }
}

위 코드에서 statSync() 메소드를 사용하여 디렉토리의 메타데이터에 동기적으로 접근한다. 이를 통해 디렉토리 또는 파일의 마지막 수정 시간, 접근 시간, 파일 유형 등을 확인할 수 있다.

비동기 방식으로 메타데이터를 얻고자 할 때는 다음과 같이 await을 사용하여 처리할 수 있다.

Future<void> printDirectoryMetadataAsync(String path) async {
  var directory = Directory(path);
  if (await directory.exists()) {
    var stats = await directory.stat();
    print('Type: ${stats.type}');
    print('Changed: ${stats.changed}');
    print('Modified: ${stats.modified}');
    print('Accessed: ${stats.accessed}');
  } else {
    print('Directory does not exist.');
  }
}

심볼릭 링크 처리

파일 시스템 탐색 중에는 심볼릭 링크가 포함될 수 있다. 심볼릭 링크는 파일이나 디렉토리의 경로에 대한 참조이다. Dart의 Link 클래스를 사용하여 심볼릭 링크를 처리할 수 있다.

심볼릭 링크를 생성하는 예제는 다음과 같다.

void createSymbolicLink(String targetPath, String linkPath) {
  var link = Link(linkPath);
  link.createSync(targetPath);
  print('Symbolic link created: $linkPath ->$targetPath');
}

이 코드는 targetPath를 참조하는 심볼릭 링크를 linkPath 경로에 생성한다. 비동기 방식으로도 생성할 수 있다.

심볼릭 링크를 탐색할 때는 resolveSymbolicLinks() 메소드를 사용하여 실제 파일 경로를 확인할 수 있다.

void resolveLink(String linkPath) {
  var link = Link(linkPath);
  print('Resolved link: ${link.resolveSymbolicLinksSync()}');
}

비동기 방식의 경우는 resolveSymbolicLinks() 메소드를 await하여 사용한다.

Future<void> resolveLinkAsync(String linkPath) async {
  var link = Link(linkPath);
  print('Resolved link: ${await link.resolveSymbolicLinks()}');
}

경로 조작

디렉토리 탐색을 할 때 경로를 조작하는 방법도 중요하다. Dart에서는 Path 클래스가 존재하지 않지만, Dart의 String 메소드를 사용하여 경로를 쉽게 다룰 수 있다. 파일 경로에서 파일명을 추출하거나 확장자를 제거하는 등의 작업을 String 메소드로 처리할 수 있다.

void manipulatePath(String filePath) {
  print('Directory name: ${filePath.split(Platform.pathSeparator).last}');
  print('File extension: ${filePath.split('.').last}');
}

이 코드는 경로에서 디렉토리 또는 파일명을 추출하고, 파일의 확장자를 가져오는 간단한 예이다.

경로 조작을 위한 패키지

경로를 보다 효율적으로 조작하기 위해 Dart에서는 path 패키지를 사용할 수 있다. 이 패키지를 사용하면 OS에 상관없이 플랫폼 간 호환성을 보장하며 경로를 처리할 수 있다.

import 'package:path/path.dart' as p;

void pathOperations(String filePath) {
  print('Base name: ${p.basename(filePath)}');
  print('Directory name: ${p.dirname(filePath)}');
  print('Extension: ${p.extension(filePath)}');
}

이 코드는 path 패키지를 사용하여 파일의 경로에서 기본 이름, 디렉토리 이름, 확장자를 쉽게 추출한다. 이 패키지는 경로 관련 작업에서 매우 유용하다.