LHJ

I'm a FE developer.

20.5 파일시스템 접근

04 Jun 2020 » js_lj

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)를 참고하십시오.