dart:io

dart:io

Dart의 dart:io 라이브러리가 제공하는 주요 기능을 소개해 드릴게요. 파일, 디렉터리, 프로세스, 소켓, WebSocket, HTTP 클라이언트와 서버를 다루는 API를 이 라이브러리에서 제공해요.

출처: dart:io

본문

dart:io 라이브러리는 파일, 디렉터리, 프로세스, 소켓, WebSocket, HTTP 클라이언트·서버를 다루는 API를 제공해요.

중요: dart:io는 웹이 아닌(non-web) Flutter 앱, 커맨드라인 스크립트, 서버에서만 import해서 사용할 수 있어요. 웹 앱에서는 쓸 수 없어요.

일반적으로 dart:io 라이브러리는 비동기(asynchronous) API를 구현하고 장려해요. 동기 메서드는 애플리케이션을 쉽게 차단할 수 있어서 확장하기 어려워지지요. 그래서 대부분의 연산은 FutureStream 객체로 결과를 반환해요. 이는 Node.js 같은 현대 서버 플랫폼에서 흔한 패턴이에요.

dart:io 라이브러리에 있는 몇 안 되는 동기 메서드는 메서드 이름에 Sync 접미사가 명확하게 표시되어 있어요. 동기 메서드는 여기서 다루지 않을게요.

dart:io 라이브러리를 사용하려면 import 해야 해요.

import 'dart:io';

파일과 디렉터리 (Files and directories)

I/O 라이브러리는 커맨드라인 앱이 파일을 읽고 쓰고, 디렉터리를 탐색할 수 있게 해줘요. 파일 내용을 읽는 방법은 두 가지예요: 한 번에 모두 읽기, 또는 스트리밍으로 읽기. 파일을 한 번에 모두 읽으려면 파일 내용 전체를 저장할 충분한 메모리가 필요해요. 파일이 아주 크거나 읽으면서 처리하고 싶다면, 파일 내용 스트리밍에서 설명하는 것처럼 Stream을 사용해야 해요.

파일을 텍스트로 읽기 (Reading a file as text)

UTF-8로 인코딩된 텍스트 파일을 읽을 때는 readAsString()으로 파일 내용 전체를 읽을 수 있어요. 개별 줄이 중요하다면 readAsLines()을 사용할 수 있어요. 두 경우 모두 파일 내용을 문자열 하나 또는 여러 개로 제공하는 Future 객체가 반환돼요.

void main() async {
  var config = File('config.txt');

  // Put the whole file in a single string.
  var stringContents = await config.readAsString();
  print('The file is ${stringContents.length} characters long.');

  // Put each line of the file into its own string.
  var lines = await config.readAsLines();
  print('The file is ${lines.length} lines long.');
}

파일을 바이너리로 읽기 (Reading a file as binary)

다음 코드는 파일 전체를 바이트 단위로 읽어서 int 목록으로 만든답니다. readAsBytes() 호출은 Future를 반환하고, 그 결과가 준비되면 값을 제공해요.

void main() async {
  var config = File('config.txt');

  var contents = await config.readAsBytes();
  print('The file is ${contents.length} bytes long.');
}

오류 처리하기 (Handling errors)

오류를 잡아서 포착되지 않은 예외(uncaught exception)로 이어지지 않게 하려면, FuturecatchError 핸들러를 등록하거나, (async 함수 안에서) try-catch를 사용하면 돼요.

void main() async {
  var config = File('config.txt');
  try {
    var contents = await config.readAsString();
    print(contents);
  } catch (e) {
    print(e);
  }
}

파일 내용 스트리밍하기 (Streaming file contents)

Stream을 사용하면 파일을 조금씩 읽을 수 있어요. Stream API를 쓰거나, Dart의 비동기 지원 기능의 일부인 await for를 쓰면 돼요.

import 'dart:io';
import 'dart:convert';

void main() async {
  var config = File('config.txt');
  Stream<List<int>> inputStream = config.openRead();

  var lines = utf8.decoder.bind(inputStream).transform(const LineSplitter());
  try {
    await for (final line in lines) {
      print('Got ${line.length} characters from stream');
    }
    print('file is now closed');
  } catch (e) {
    print(e);
  }
}

파일 내용 쓰기 (Writing file contents)

IOSink를 사용해서 파일에 데이터를 쓸 수 있어요. FileopenWrite() 메서드를 사용하면 쓸 수 있는 IOSink를 얻어요. 기본 모드인 FileMode.write는 파일에 있는 기존 데이터를 완전히 덮어써요.

var logFile = File('log.txt');
var sink = logFile.openWrite();
sink.write('FILE ACCESSED ${DateTime.now()}\n');
await sink.flush();
await sink.close();

파일 끝에 내용을 추가하려면 선택적 mode 매개변수로 FileMode.append를 지정하세요.

var sink = logFile.openWrite(mode: FileMode.append);

바이너리 데이터를 쓰려면 add(List<int> data)를 사용하세요.

디렉터리의 파일 나열하기 (Listing files in a directory)

디렉터리의 모든 파일과 하위 디렉터리를 찾는 것은 비동기 연산이에요. list() 메서드는 파일이나 디렉터리를 만날 때마다 객체를 내보내는 Stream을 반환해요.

void main() async {
  var dir = Directory('tmp');

  try {
    var dirList = dir.list();
    await for (final FileSystemEntity f in dirList) {
      if (f is File) {
        print('Found file ${f.path}');
      } else if (f is Directory) {
        print('Found dir ${f.path}');
      }
    }
  } catch (e) {
    print(e.toString());
  }
}

기타 흔한 기능 (Other common functionality)

FileDirectory 클래스에는 그 외에도 여러 기능이 있는데, 그중에는 다음과 같은 것들이 있어요.

  • 파일이나 디렉터리 만들기: FileDirectorycreate()
  • 파일이나 디렉터리 삭제하기: FileDirectorydelete()
  • 파일 길이 구하기: Filelength()
  • 파일에 무작위 접근(random access)하기: Fileopen()

전체 메서드 목록은 FileDirectory의 API 문서를 참고하세요.

HTTP 클라이언트와 서버 (HTTP clients and servers)

dart:io 라이브러리는 커맨드라인 앱이 HTTP 리소스에 접근하고 HTTP 서버를 실행하는 데 쓸 수 있는 클래스들을 제공해요.

HTTP 서버 (HTTP server)

HttpServer 클래스는 웹 서버를 구축하기 위한 저수준 기능을 제공해요. 요청 핸들러를 매칭하고, 헤더를 설정하고, 데이터를 스트리밍하는 등 다양한 작업을 할 수 있어요.

다음 샘플 웹 서버는 간단한 텍스트 정보를 반환해요. 이 서버는 포트 8888, 주소 127.0.0.1(localhost)에서 수신하면서 /dart 경로의 요청에 응답해요. 다른 경로에 대해서는 상태 코드 404(페이지를 찾을 수 없음)로 응답해요.

void main() async {
  final requests = await HttpServer.bind('localhost', 8888);
  await for (final request in requests) {
    processRequest(request);
  }
}

void processRequest(HttpRequest request) {
  print('Got request for ${request.uri.path}');
  final response = request.response;
  if (request.uri.path == '/dart') {
    response
      ..headers.contentType = ContentType('text', 'plain')
      ..write('Hello from the server');
  } else {
    response.statusCode = HttpStatus.notFound;
  }
  response.close();
}

HTTP 클라이언트 (HTTP client)

HTTP 요청을 할 때 dart:io를 직접 사용하는 것은 피하는 게 좋아요. dart:ioHttpClient 클래스는 플랫폼에 의존적이고 단일 구현에 묶여 있거든요. 대신 package:http 같은 상위 수준의 라이브러리를 사용하세요.

Fetch data from the internet 튜토리얼에서 package:http로 HTTP 요청을 하는 방법을 설명해요.

더 알아보기

  • 이 페이지에서 dart:io 라이브러리의 주요 기능을 다뤘어요. 이 밖에도 dart:io는 프로세스, 소켓, 웹 소켓을 위한 API도 제공해요.
  • 서버 사이드 및 커맨드라인 앱 개발에 대해 더 알고 싶다면 서버 사이드 Dart 개요를 참고하세요.