20.5 파일시스템 접근
프로그래밍 책에서는 대개 파일시스템 접근에 관해 설명합니다.
 파일을 다루는 것은 일반적인 프로그래밍에서 매우 중요한 부분이니까요.
 하지만 자바스크립트는 노드가 만들어지기 전까지는 파일시스템에 접근할 수 없는 외톨이였습니다.
이 장의 예제는 프로젝트 루트가 유닉스 시스템에서 사용하는 일반적인 경로인 /home/[jdoe]/fs라고 가정합니다.
 [jdoe]를 당신의 사용자 이름으로 바꾸십시오.
 윈도우 시스템이라면 C:\Users[jdoe]\Documents\fs에 파일을 저장하면 됩니다.
파일을 만들 때는 fs.writeFile을 사용합니다.
 프로젝트 루트에 다음과 같이 write.js 파일을 만드십시오.
const fs = require('fs');
fs.writeFile('hello.txt', 'hello from Node!', function(err) {
    if (err) return console.log('Error writing to file.');
})
write.js 파일을 저장한 디렉터리에 쓰기 권한이 있고, 읽기 전용 hello.txt 파일이 이미 존재하지 않으면 hello.txt 파일이 생성될 겁니다.
 노드 애플리케이션을 실행하면 해당 애플리케이션은 자신이 실행된 현재 작업 디렉터리를 __dirname 변수로 보관합니다.
 이 변수를 사용해서 write.js 파일을 다음과 같이 고쳐 쓸 수 있습니다.
const fs = require('fs');
fs.writeFile(__dirname + '/hello.txt', 'hello from Node!', function(err) {
    if (err) return console.error('Error writing to file.');
})
이제 write.js는 항상 /home/[jdoe]/fs, 즉 write.js가 있는 디렉터리에 hello.txt를 만듭니다.
 문자열 병합으로 __dirname과 파일 이름을 합쳐서 파일 경로를 얻으면 운영체제에 따라 호환되지 않을 수 있습니다.
 예를 들어 이 예제에서 사용한 방법은 윈도우 컴퓨터에서 동작하지 않습니다.
 노드의 path 모듈에는 운영체제 독립적인 경로 이름 유틸리티가 있습니다.
 이 모듈을 써서 파일 경로를 만들면 모든 운영체제에서 사용할 수 있습니다.
const fs = require('fs');
const path = require('path');
fs.writeFile(path.join(__dirname, 'hello.txt'), 'hello from Node!', function(err) {
    if (err) return console.error('Error writing to file.');
})
path.join은 운영체제에 따라 디렉터리 구분자를 알맞게 사용하므로 이 메서드를 사용하기 권합니다.
파일 컨텐츠를 읽을 때는 fs.readFile을 사용합니다.
 다음과 같이 read.js를 만드십시오.
const fs = require('fs');
const path = require('path');
fs.readFile(path.join(__dirname, 'hello.txt'), function(err, data) {
    if (err) return console.error('Error reading file.');
    console.log('Read file contents');
    console.log(data);
})
이 예제를 실행하면 다음과 같은 이상한 결과를 보게 됩니다.
Read file contents:
<buffer 68 65 6c 6f 20 66 72 6d 4e 64 21>
이 16진수 코드를 ASCII/Unicode로 바꾸면 hello from Node! 이긴 하지만, 편리하다고 말할 수는 없습니다.
 fs.readFile에 인코딩 정보를 제공하지 않으면 fs.readFile은 가공되지 않은 바이너리 데이터인 버퍼를 반환합니다.
 write.js에 인코딩을 명시하지는 않았지만, 기본 문자열 인코딩은 UTF-8입니다.
 read.js에 UTF-8 인코딩을 지정하면 원하는 결과를 얻을 수 있습니다.
const fs = require('fs');
const path = require('path');
fs.readFile(path.join(__dirname, 'hello.txt'), {encoding: 'utf8'}, function(err, data) {
    if (err) return console.error('Error reading file.');
    console.log('Read file contents');
    console.log(data);
})
파일 관련 함수에는 모두 동기적으로 작업하는 짝이 있으며, 이들의 이름은 Sync로 끝납니다.
 write.js에도 동기적인 함수를 쓸 수 있습니다.
fs.writeFileSync(path.join(__dirname, 'hello.txt'), 'hello from Node!');
read.js도 마찬가지입니다.
const data = fs.readFileSync(path.join(__dirname, 'hello.txt'), { encoding: 'utf8' });
동기적인 함수에서는 try/catch 블록을 통해 예외 처리를 합니다.
try {
    fs.writeFileSync(path.join(__dirname, 'hello.txt'), 'hello from Node!');
} catch (err) {
    console.error('Error writing file.');
}
CAUTION_
동기적인 파일시스템 함수는 이해하기 쉬우므로 이걸 쓰고 싶다고 생각할 수도 있습니다.
하지만 웹 서버나 네트워크 애플리케이션에서 노드의 성능은 비동기적 실행에서 나옵니다.
웹 서버나 네트워크 애플리케이션을 만들 때는 항상 비동기적 함수를 써야 합니다.
명령줄 유틸리티를 만든다면 동기적 함수를 써도 별 문제는 없습니다.
디렉터리에 어떤 파일이 있는지 알아보려면 fs.readdir을 사용합니다.
 다음과 같이 ls.js 파일을 만드십시오.
const fs = require('fs');
fs.readdir(__dirname, function(err, files) {
    if (err) return console.error('Unable to read directory contents');
    console.log(`Contents of ${__dirname}:`);
    console.log(files.map(f => '\t' + f).join('\n'));
})
fs 모듀에는 이외에도 여러 가지 파일시스템 함수들이 있습니다.
 파일을 지울 때는 fs.unlink, 파일을 옮기거나 이름을 바꿀 때는 fs.rename, 파일과 디렉터리 정보를 얻을 때는 fs.stat 이외에도 많은 함수가 있습니다.
 더 많은 정보를 알아보려면 노드 API 문서(https://nodejs.org/api/fs.html)를 참고하십시오.