쌓고 쌓다

[node] 'fs 모듈' 파일 시스템 접근하기 본문

프로그래밍/node.js

[node] 'fs 모듈' 파일 시스템 접근하기

승민아 2023. 1. 27. 14:38

fs 모듈

fs 모듈은 파일 시스템에 접근하는 모듈이다. 이 모듈로 파일의 생성과 삭제, 읽기, 폴더 생성도 가능하다.

 

1. 파일 읽기 - readFile

readme.txt

리드미입니다.

 

readFile.js

const fs = require('fs')

fs.readFile('./readme.txt', (err, data) => {
    if(err) {
        throw err
    }
    console.log(data)
    console.log(data.toString())
})

실행 결과

 

* 읽은 파일의 경로가 현재 파일 기준이 아니라 node 명령어를 실행하는 콘솔 기준이다.

  C:\ 디렉토리에서 node folder/readFile.js를 실행하면 C:\folder\readme.js가 실행되는 것이 아니라

  C:\readme.txt.가 실행된다.

 

위의 예제에서 forNode 폴더 안에 readme.txt가 있는데

만약 하나의 부모 디렉토리(..)로 이동하여 node ./forNode/readFile.js를 실행하면

현재 노드 명령어를 실행하는 콘솔의 위치(Desktop)에 readme.txt를 찾는다. (없으니 에러 발생)

파일 경로 유의

 

실행 결과

<Buffer eb a6 ac eb 93 9c eb af b8 ec 9e 85 eb 8b 88 eb 8b a4 2e>
리드미입니다.

readFile의 결과물은 버퍼 형식으로 반환된다. 그래서 toString으로 읽을 수 있는 문자열로 변환한다.

 

fs 모듈 - 프로미스 형식

fs는 기본적으로 콜백 형식의 모듈이다. 이것을 프로미스 형식으로 바꿀 수 있다.

fs모듈의 promises 속성을 사용한다.

const fs = require('fs').promises

fs.readFile('./readme.txt')
  .then((data) => {
    console.log(data)
    console.log(data.toString())
  })
  .catch((err) => {
    console.error(err)
  })

 

2. 파일 쓰기 - writeFile

writeFile.js

const fs = require('fs')

fs.writeFile('./writeme.txt', '라이트미', (err) => {
    if(err) {
        throw err
    }

    /* 파일 읽기 */
    fs.readFile('./writeme.txt', (err, data) => {
        if(err) {
            throw err
        }
        console.log(data.toString())
    })
    
})

writeFile 메소드에 생성될 파일의 경로와 내용을 입력한다.

 

동기 메소드와 비동기 메소드

출처 : Node,js 교과서 - 조현영

동기 메소드를 실행하면 이 메소드가 반환될 때까지 다음 일을 못하고 대기해야 한다.

 

비동기 메소드는 실행과 동시에 콜백 함수를 백그라운드로 보낸다.

백그라운드에 보낸 것들이 완료가 된다면 태스크 큐를 거쳐 호출 스택으로 올라온다.

이때 실행은 어떤 순서로 될지 모르지만 비동기 메소드는 다음 일을 수행이 가능하다.

 

async.js

const fs = require('fs')

console.log('시작')
fs.readFile('./readme.txt', (err, data) => {
    if(err) {
        throw err
    }
    console.log('1번', data.toString())
})
fs.readFile('./readme.txt', (err, data) => {
    if(err) {
        throw err
    }
    console.log('2번', data.toString())
})
fs.readFile('./readme.txt', (err, data) => {
    if(err) {
        throw err
    }
    console.log('3번', data.toString())
})
console.log('끝')

실행 결과

 

시작, 끝 외에 출력 결과 순서는 실행할 때마다 달라진다.

readFile은 비동기 함수이기에 콜백 함수들을 백그라운드로 보낸다.

백그라운드에 온 비동기 함수들은 동시에 실행된다. 백그라운드에서 또 태스크 큐로 보내 실행하기에

순서를 예측할 수 없다.

 

setTimeout, process.nextTick 등 메소드는 비동기 방식으로 처리한다.

비동기 메소드를 동기식으로 사용이 불가능한 것은 아니다. 몇몇 메소드는 동기 방법을 제공한다.

fs 모듈 또한 비동기 식으로 처리를 하나 동기 방법으로 처리가 가능하다.

 

3. fs 동기 메소드 - readFileSync

syns.js

const fs = require('fs')

console.log('시작')
let data = fs.readFileSync('./readme.txt')
console.log('1번', data.toString())
data = fs.readFileSync('./readme.txt')
console.log('2번', data.toString())
data = fs.readFileSync('./readme.txt')
console.log('3번', data.toString())
console.log('끝')

실행 결과

동기 메소드를 사용하여 결과의 순서를 맞출 수 있다.

동기 메소드는 처음 프로그램을 실행할 때 초기화 용도로 사용하는 것이 좋다.

이전 작업이 완료되어야 다음 작업을 하기에 성능에 문제가 생기기 때문이다.

 

비동기 방식으로 순서 유지하기

const fs = require('fs')

console.log('시작')
fs.readFile('./readme.txt', (err, data) => {
    if(err) {
        throw err
    }
    console.log('1번', data.toString())
    
    fs.readFile('./readme.txt', (err, data) => {
        if(err) {
            throw err
        }
        console.log('2번', data.toString())

        fs.readFile('./readme.txt', (err, data) => {
            if(err) {
                throw err
            }
            console.log('3번', data.toString())
            console.log('끝')
        })
    })
})

readFile은 비동기 메소드이다.

1번readFile가 readme.txt를 읽고 출력을 하고 2번readFile을 호출한다.

2번readFile이 readme.txt를 읽고 출력을 하고 3번readFile을 호출한다.

3번 readFile은 readme.txt.를 읽고 출력하고 반환된다.

이렇게 비동기 메서드인 콜백 함수에서 또 다른 콜백함수를 호출하는 방식으로 구현이 가능하다.

코드를 보면 "콜백 지옥"이 일어난 게 보인다...~

 

콜백 지옥 해결

Promise나 async/await으로 해결이 가능하다.

 

1. promise로 해결

const fs = require('fs').promises

console.log('시작')
fs.readFile('./readme.txt')
  .then((data) => {
    console.log('1번', data.toString())
    return fs.readFile('./readme.txt')
  })
  .then((data) => {
    console.log('2번', data.toString())
    return fs.readFile('./readme.txt')
  })
  .then((data) => {
    console.log('3번', data.toString())
    console.log('끝')
  })
  .catch((err) => {
    console.error(err)
  })

 

2. async/await로 해결

const fs = require('fs').promises

async function solve() {
    console.log('시작')
    let data = await fs.readFile('./readme.txt')
    console.log('1번', data.toString())
    data = await fs.readFile('./readme.txt')
    console.log('2번', data.toString())
    data = await fs.readFile('./readme.txt')
    console.log('3번', data.toString())
    console.log('끝')
}
solve()

 

추가 주요 메소드1

  • fs.access(경로, 옵션, 콜백) : 폴더, 파일에 접근할 수 있는지 체크한다.
    위의 예시에서 두 번째 인수로 상수(constant를  통해 가져옴) 넣었다. F_OK(파일 존재 여부), R_OK(읽기 권한 여부) , W_OK(쓰기 권한 여부)로 체크함. 파일/폴더 또는 권한이 없다면 에러가 발생하는데 파일/폴더가 없을 때 에러 코드는 ENOENT이다.
  • fs.mkdir(경로, 콜백) : 폴더를 만든다. 이미 폴더가 존재하면 에러를 발생하므로 aceess로 확인하는 것이 중요함.
  • fs.open(경로, 옵션, 콜백) : 파일의 아이디(fd 변수)를 가져온다. 파일이 없다면 파일을 생성한 뒤 그 아이디를 가져온다. 아이디와 fs.read 또는 fs.write로 읽고 쓰기가 가능하다. 두 번째 인수로 어떤 동작을 할지 설정한다. 'w'는 쓰기, 'r'는 읽기, 기존 파일에 추가하려면 a이다. 아래의 예제에서는 'w'를 사용했으니 파일이 없을 때 새로 만들 수 있었지 'r'이였다면 에러가 발생할 것이다.
  • fs.rename(기존 경로, 새 경로, 콜백) : 파일의 이름을 바꾼다. 같은 경로를 작성하지 않는다면 잘라내기 같은 기능으로 작동한다.

fsCreate.js

const fs = require('fs').promises
const constants = require('fs').constants

fs.access('./folder', constants.F_OK | constants.W_OK | constants.R_OK)
  .then(() => {
    return Promise.reject('이미 폴더 있음')
  })
  .catch((err) => { // 폴더가 있거나 없거나가 들어온다.
    if(err.code === 'ENOENT') { // 폴더가 없다면
        return fs.mkdir('./folder') // 폴더 생성
    }
    return Promise.reject(err) // 폴더가 있다면 '이미 폴더 있음' 반환
  })
  .then(() => { // 폴더가 생성된 경우에만
    console.log('폴더 생성 완료.')
    return fs.open('./folder/file.js', 'w') // 파일 생성
  })
  .then((fd) => { // 파일 아이디(fd)
    console.log('파일 생성 완료.', fd)
    return fs.rename('./folder/file.js', './folder/newfile.js') // 파일명 변경
  })
  .then(() => {
    console.log('이름 변경 완료.')
  })
  .catch((err) => {
    console.error(err)
  })

 

추가 주요 메소드2

  • fs.readdir(경로, 콜백) : 폴더 안의 내용물을 확인할 수 있다. 배열 안에 내부 파일과 폴더명이 나옴.
  • fs.unlink(경로, 콜백) : 파일을 지울 수 있다. 파일이 없다면 에러가 나니 주의하자.
  • fs.rmdir(경로, 콜백) : 폴더를 지울 수 있다. 폴더 안에 파일이 있다면 에러가 발생한다.

fsDelete.js

const fs = require('fs').promises

fs.readdir('./folder')
  .then((dir) => {
    console.log('폴더 내용', dir)
    return fs.unlink('./folder/newfile.js')
  })
  .then(() => {
    console.log('파일 삭제 완료.')
    return fs.rmdir('./folder')
  })
  .then(() => {
    console.log('폴더 삭제 완료')
  })
  .catch((err) => {
    console.error(err)
  })

 

추가 주요 메소드3

1. 파일 복사

fs.copy('복사할 파일', '복사될 경로', '복사후 실행될 콜백함수') : 파일을 복사한다.

const fs = require('fs').promises

fs.copyFile('readme.txt', 'readme2.txt')
  .then(() => {
    console.log('복사 완료')
  })
  .catch((err) => {
    console.error(err)
  })

 

2. 파일/폴더 변경 사항 감지

const fs = require('fs')

fs.watch('./target.txt', (eventType, filename) => {
    console.log(eventType, filename)
})

이 스크립트를 실행한 상태에서 파일을 다루면 콘솔에 이벤트가 발생할 때마다 출력이 된다.

내용물의 수정이 있다면 change 이벤트가, 파일명을 변경하거나 삭제하면 rename 이벤트가 발생한다.

rename 이벤트가 발생했다면 더 이상 watch는 수행되지 않는다.

 

'프로그래밍 > node.js' 카테고리의 다른 글

[Node.js] REST와 REST API  (0) 2023.02.01
[Node.js] http 모듈로 서버 만들기  (2) 2023.01.30
[node] 버퍼와 스트림 / pipe  (0) 2023.01.29
[node] path 내장 모듈  (0) 2023.01.23
[node] CommonJS 모듈, ECMASciprt 모듈  (0) 2023.01.18
Comments