3.6 버퍼와 스트림 이해하기

Bor·2021년 12월 26일
0

Node.js

목록 보기
4/11

3.6.2 버퍼와 스트림 이해하기

파일을 읽거나 쓰는 방식에는 크게 두 가지 방식, 즉 버퍼를 이용한느 방식과 그트림을 이용하는 방식. 앞에서 readFile 매서드를 사용할 때 읽었던 파일이 버퍼 형식으로 출력되었다. 노드는 파일을 읽을 때 메모리에 파일 크기만큼 공간을 마련해두며 파일 데이터를 메모리에 저장한 뒤 사용자가 조작할 수 있도록 함. 이때 메모리에 저장된 데이터가 바로 버퍼.

여기에 버퍼를 직접 다룰 수 있는 클래스가 있다. 바로 buffer이다.

const buffer = Buffer.from('나를 버퍼로 바꿔보셈!!')
console.log('from():', buffer); //from(): <Buffer eb 82 98 eb a5 bc 20 eb b2 84 ed 8d bc eb a1 9c 20 eb b0 94 ea bf 94 eb b3 b4 ec 85 88 21 21>
console.log('length:', buffer.length); //length: 31
console.log('toString():', buffer.toString()); //toString(): 나를 버퍼로 바꿔보셈!!

const array = [Buffer.from('띄엄 '),Buffer.from('띄엄 '),Buffer.from('띄어쓰기')];
const buffer2 = Buffer.concat(array);
console.log('concat():', buffer2.toString()); // concat(): 띄엄 띄엄 띄어쓰기

const buffer3 = Buffer.alloc(5);
console.log('alloc():', buffer3); //alloc(): <Buffer 00 00 00 00 00>

Buffer 객체는 여러 가지 메서드를 제공한다.

  • from(문자열): 문자열을 버퍼로 바꿀 수 있다. length 속성은 버퍼의 크기를 알린다. 바이트 단위.
  • toString(버퍼): 버퍼를 다시 문자열로 바꿀 수 있다. 이때 base64나 hex를 인수로 넣으면 해당 인코딩으로도 변환가능.
  • concat(배열) : 배열 안에 든 버퍼들을 하나로 합친다.
  • alloc(바이트) : 빈 버퍼를 생성. 바이트를 인수로 넣으면 해당 킈의 버퍼가 생성된다.

readFile 방식의 버퍼가 편리하기는 하지만 문제점도 있다. 만약 용량이 100MB인 파일이 있을 때 메모리레 100MB의 버퍼를 만들어야 한다. 이작업을 동시에 10개만 해도 1GB에 달하는 메모리가 사용된다. 특히 서버처럼 몇 명이 이용할지 모르는 환경에서는 메모리 문제가 발생할 수도.

또한 모든 내용을 버퍼에 다 쓴 후에야 다음 동작으로 넘어가므로 파일 읽기, 압축, 파일 쓰기 등의 조각을 연달아 할 때 매번 전체용량을 버퍼로 처리해야 다음 단계로 넘어갈 수 있다. 그래서 버퍼의 크기를 작게 만든 후 여러 번으로 나눠 보내는 방식이 등장. 예를 들면 버퍼 1MB를 만든 후 100MB 파일을 백번에 걸쳐서 나눠 보내는 것. 이로써 메모리 1MB로 100MB 파일을 전송할 수 있다. 이를 편리하게 만든 것이 스트림.

파일을 읽는 스트림 메서드로는 createReadStream이 있다. 다음과 같이 사용한다.

const fs = require('fs');

const readStream = fs.createReadStream('./readme3.txt', {highWaterMark: 16});
const data = [];

readStream.on('data', (chunck)=>{
    data.push(chunck);
    console.log('data:', chunck, chunck.length);
})

readStream.on('end', ()=>{
    console.log('end:', Buffer.concat(data).toString());
});

readStream.on('error',(err)=>{
    console.log('error:', err);
})

// data: <Buffer eb 82 98 eb 8a 94 20 ec a1 b0 ea b8 88 ec 94 a9> 16
// data: <Buffer 20 ec a1 b0 ea b8 88 ec 94 a9 20 eb 82 98 eb 88> 16
// data: <Buffer a0 ec 84 9c 20 ec a0 84 eb 8b ac eb 90 9c eb 8b> 16
// data: <Buffer a4 2e 20 eb 82 98 eb 88 a0 ec a7 84 20 ec a1 b0> 16
// data: <Buffer ea b0 81 ec 9d 84 20 63 68 75 6e 6b eb 9d bc ea> 16
// data: <Buffer b3 a0 20 eb b6 80 eb a5 b8 eb 8b a4 2e 20> 14
// end: 나는 조금씩 조금씩 나눠서 전달된다. 나눠진 조각을 chunk라고 부른다. 

먼저 createReadStream으로 읽기 스트림을 만든다. 첫 번째 인수로 읽을 파일 경로를 넣는다. 두 번째 인수는 옵션 객체로 highWaterMark라는 옵션이 버퍼의 크기(바이트 단위)를 정할 수 있는 옵션. 기본값은 64KB이지만, 여러번 나눠서 보내는 모습을 보여주기 위해 16B로 낮췄다.

readStream은 이벤트 리스너를 붗여서 사용. 보통 data, end, err 이벤트를 사용. 위 예제의 readSTream.on(data)와 같이 이벤트 리스너를 붙이면 된다. 파일을 읽는 도중 에러가 발생하면 err 이벤트가 호출되고, 파일 읽기가 시작되면 data 이벤트가 발생. 16B씩 읽도록 설정했으므로 파일의 크기가 16B보다 크면 여러번 발생할 수도. 파일을 다 읽으면 end 이벤트가 발생.

예제에서는 미리 data 배열을 만들어 놓고 들어오는 chunck들을 하나씩 push한 뒤에 마지막에 buffer.concat()으로 합쳐서 다시 문자열을 만들었다. 파일의 크기가 99B라 무려 일곱 번에 걸쳐 데이터를 전송. 하지만 기본값으로는 64KB씩 전송하므로 대부분의 txt 파일들은 한 번에 전송.

createWriteStream.js

const fs = require('fs');

const writeStream = fs.createWriteStream('./writeme2.txt');
writeStream.on('finish', () => {
    console.log('파일 쓰기 완료');
});

writeStream.write('이글을 씁니다.\n');
writeStream.write('한 번 더 씁니다.')
writeStream.end();

먼저 createWriteStream 으로 쓰기 스트림을 만든다. 첫 번째 인수로는 출력 파일명을 입력, 두번째는 옵션. finish 이벤트 리스너도 붙였다. 파일 쓰기가 종료되면 콜백 함수가 호출. writeStream에서 제공하는 write 메서드로 넣을 데이터를 쓴다. 여러번 호출될 수 있으며 데이터를 다 썼다면 end 메서드로 종료를 알리고 이 때 finish 이벤트가 발생한다.

createReadStream으로 파일을 읽고 그 스트림을 전달 받아 createWriteStream으로 파일을 쓸 수도 있다. 파일 복사와 비슷, 스트림끼리 연결하는 것을 '파이핑한다'고 표현.

const fs = require('fs');

const readStream = fs.createReadStream('readme4.txt');
const writeStream = fs.createWriteStream('pipe.txt');
readStream.pipe(writeStream);

3.6.3 기타 fs 메서드 알아보기

fs는 파일 시스템을 조작하는 다양한 메서드를 제공함. 지금까지는 단순히 파일 읽기/쓰기만 했지만, 파일을 생성하고 삭제할 수 있으며 폴더를 생성하고 삭제할 수도 있음. 다음 예제를 통해 실습.

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') {
        console.log('폴더없음');
        return fs.mkdir('./folder');
    }
    return Promise.reject(err);
})
.then(()=>{
    console.log('폴더 만들기 성공');
    return fs.open('./folder/file.js', 'w');
})
.then((fd)=>{
    console.log('빈 파일 만들기 성공', fd);
    return fs.rename('./folder/file.js', './folder/newfile.js');
})
.then(()=>{
    console.log('이름바꾸기 성공');
})
.catch((err)=>{
    console.error(err)
})

// 폴더없음
// 폴더 만들기 성공
// 빈 파일 만들기 성공 FileHandle {
//   _events: [Object: null prototype] {},
//   _eventsCount: 0,
//   _maxListeners: undefined,
//   close: [Function: close],
//   [Symbol(kCapture)]: false,
//   [Symbol(kHandle)]: FileHandle {},
//   [Symbol(kFd)]: 3,
//   [Symbol(kRefs)]: 1,
//   [Symbol(kClosePromise)]: null
// }
// 이름바꾸기 성공

모두 비동기 메서드. 한 메서드의 콜백에서 다른 메서드를 호출한다.

  • fs.access(경로, 옵션, 콜백) : 폴더나 파일에 접근할 수 있는지를 체크, 두 번째 인수로 상수들(constants를 통해 가져온다)을 넣었다. F_OK는 파일 존재 여부, R_OK는 읽기 권한 여부, W_OK는 쓰기 권한 여부를 체크. 파일/폴더나 권한이 없다면 에러가 발생하는데 파일/폴더가 없을 때의 에러 코드는 ENOENT
  • fs.mkdir(경로, 콜백): 폴더를 만드는 메서드. 이미 폴더가 있다면 에러가 발생하므로 먼저 access 메서드를 호출해서 확인하는 것이 중요하다.
  • fs.open(경로, 옵션, 콜백) : 파일의 아이디(fd변수)를 가져오는 메서드. 파일이 없다면 파일을 생성한 뒤 아이디를 가져온다. 가져온 아이디를 이용해 fs.read나 fs.write로 읽거나 쓸 수 있다. 두 번째 인수로 어떤 동작을 할 것인지 설정할 수 있다. 쓰려면 w, 읽으려면 r, 기존 파일에 추가하려면 a.
  • fs.rename(기존 경로, 새 갱로, 콜백): 파일의 이름을 바꾸는 메서드. 기존 파일의 위치와 새로운 파일 위치를 적으면 된다. 꼭 같은 파일을 지정할 필요는 없으므로 잘라내기 같은 기능을 할 수도.

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);
    })
    // 폴더내용확인 [ 'newfile.js' ]
    // 파일 삭제 성공
    // 폴더 삭제 성공
  • fs.readdir(경로, 콜백) : 폴더 안의 내용물을 확인할 수 있다. 배열 안에 내부 파일과 폴더 명이 나온다.
  • fs.unlink(경로, 콜백) : 파일을 지울 수 있다. 파일이 없마녀 에러가 발생하므로 맨 먼저 파일이 있는지 꼭 확인해야 한다.
  • fs.rmdir(경로, 콜백): 폴더를 지울 수 있다. 폴더 안에 파일이 있따면 에러가 발생하므로 먼저 내부 파일을 모두 지우고 호출해야 한다.
  • node fsDelete를 한 번 더 실행하면 ENOENT 에러가 발생합니다. 존재하지 않은 파일을 지웠다는 에러.

노드 8.5 버전 이후에는 createReadStream과 createWriteStream을 pipe 하지 않아도 파일을 복사할 수 있다.

copyFile.js

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

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

readme.txt와 동일한 내용의 writeme4.txt가 생성. 첫 번째 인수로 복사할 파일을, 두 번째 인수로 복사될 경로를, 세 번째 인수로 복사 후 실행될 콜백함수를 넣는다. 마지막으로 파일/폴더의 변경 사항을 감시할 수 있는 Watch 메서드. 빈 텍스트 파일인 target.txt를 만들고 watch.js를 작성.

watch.js

const fs = require('fs');

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

내용물을 수정할 때는 change 이벤트가 발생. 파일명을 변경하거나 파일을 삭제하면 rename 이벤트가 발생한다. rename 이벤트가 발생한 후에는 더 이상 watch가 수행되지 않는다. ⚠️ change 이벤츠가 두 번씩 발생하기도 하므로 실무에서 사용할 때는 주의가 필요!⚠️


3.6.4 스레드풀 알아보기

이전 절에서 파일 시스템 실습을 하면서 fs모듈의 비동기 메서드를 사용. 비동기 메서드들은 백그라운드에서 실행되고 실행된 후에는 다시 메인 스레드의 콜백 함수나 프로미스의 then 부분이 실행. 이때 fs 메서드를 여러 번 실행해도 백그라운드에서 동시에 처리되는데, 바로 스레드풀이 있기 때문.

fs 외에도 내부적으로 스레드풀을 사용하는 모듈로는 crypto, zlib, dns, lookup 등이 있다.

스레드풀을 쓰는 crypto.pbkdf2 메서드의 예제로 스레드풀의 존재를 확인해보겠음!

threadpool.js

const crypto = require('crypto');

const pass = 'pass';
const salt = 'salt';
const start = Date.now();

crypto.pbkdf2(pass, salt, 1000000, 128, 'sha512', ()=>{
    console.log('1:', Date.now() - start);
});

crypto.pbkdf2(pass, salt, 1000000, 128, 'sha512', ()=>{
    console.log('2:', Date.now() - start);
})

crypto.pbkdf2(pass, salt, 1000000, 128, 'sha512', ()=>{
    console.log('3:', Date.now() - start);
})

결과 
3: 6401
2: 6422
1: 6451

실행할 때마다 시간과 순서가 달라진다. 스레드풀이 작업을 동시에 처리하므로 여덟 개의 작업 중에서 어느 것이 먼저 처리될지 모른다. 하지만 하나의 규칙을 발견할 수 있다. 1~4, 5~8의 그룹이 묶여져 있고 5~8이 1~4보다 시간이 더 소요된다. 바로 기본적인 스레드풀의 개수가 네 개 이기 때문. 스레드풀이 네 개 이므로 처음 네 작업이 동시 실행. 그것이 종료되면 다음 네 개의 작업이 실행. 컴퓨터의 코어 개수가 4보다 작다면 다른 결과 가능.

우리드는 스레풀을 직접 컨트롤할 수 는 없지만 개수는 조절가능. 윈도라면 명령 프롬프트에 UV_THREADPOOL_SIZE=1을, 입력한 후 다시 node threadpool 명령어를 입력. 순서대로 실행된다. 스레풀 개수를 하나로 제한했으므로 작업이 한 번에 하나씩 밖에 처리되지 않는다.

지금까지 노드로 파일 시스템에 접근하는 방법을 알아봤다. 자바스크립토는 처음 접근해봄! 예제를 반복하고 응용하며 익숙해지자!! 다음 절에서는 스트림에서 사용했던 on을 알아보자!


3.7 이벤트 이해하기

스트림을 배울 때 on('data'. 콜백) 또는 on('end'.콜백)을 사용했다. 바로 data라는 이벤트와 end라는 이벤트가 발생할 때 콜백 함수를 호출하도록 이벤트를 등록한 것. createReadStream 같은 경우는 내부적으로 알아서 data와 end 이벤트를 호출하지만 우리가 직접 이벤트를 만들수도.

event.js

const EventEmitter = require('events');

const myEvent = new EventEmitter();
myEvent.addListener('event1', ()=>{
    console.log('이벤트1');
});
myEvent.on('event2',()=>{
    console.log('이벤트 2');
});
myEvent.once('event3',()=>{
    console.log('이벤트 3');
}); //한 번만 실행된다 

myEvent.emit('event1'); //이벤트 호출 
myEvent.emit('event2'); //이벤트 호출

myEvent.emit('event3'); //이벤트 호출 
myEvent.emit('event3'); //실행 안됨 

myEvent.on('event4', ()=>{
    console.log('이벤트4');
});
myEvent.removeAllListeners('event4');
myEvent.emit('event4'); //실행안됨

const listener = () => {
    console.log('이벤트 5')
};

myEvent.on('event5', listener);
myEvent.removeListener('evnet5',listener);
myEvent.emit('event5'); //실행 안 됨 

console.log(myEvent.listenerCount('event2'));

// 이벤트1
// 이벤트 2
// 이벤트 3
// 이벤트 5
// 1

events 모듈을 사용하면 된다. myEvent 라는 객체를 먼저 만든다. 객체는 이벤트 관리를 위한 메서드를 가지고 있다.

  • on(이벤트, 콜백): 이벤트 이름과 이벤트 발생 시의 콜백을 연결한다. 이렇게 연결하는 동작을 이벤트 리스닝이라고 부른다. event2처럼 이벤트 하나에 이벤트 여러 개를 달아줄 수도 있다.
  • addListener(이벤트명, 콜백) : on과 기능이 동일
  • emit(이벤트명): 이벤트 호출하는 메서드. 이벤트 이름을 인수로 넣으면 미리 등록해뒀던 이벤트 콜백이 실행된다.
  • once(이벤트명, 콜백): 한 번만 실행되는 이벤트. myEvent.emit('event3')을 두 번 연속 호출했지만 콜백이 한 번만 실행된다.
  • removeAllListeners(이벤트명): 이벤트에 연결된 모든 이벤트 리스너를 제거함. event4가 호출되기 전에 리스너를 제거했기 때문에 evnet4의 콜백은 호출되지 않는다.
  • removeListener(이벤트명, 리스너): 이벤트에 연결된 리스너를 하나씩 제거한다. 리스너를 넣어야 한다는 것을 잊지 마세요. 역시 event5의 콜백도 호출되지 않는다.
  • off(이벤트명, 콜백): 노드 10버전에 추가된 메서드 removeListener와 기능이 동일.
  • listenerCount(이벤트명): 현재 리스너가 몇 개 연결되어 있는지 확인

이제는 스트림에서 봤던 on('data')와 on('end')에 대해서도 어느 정도 감이 올 겁니다. 겉으로 이 이벤트를 호출하는 코드는 없지만, 내부적으로는 chunk를 전달할 때마다 data 이벤트를 emit 하고 있습니다. 완료되었을 경우에는 end 이벤트를 emit 한 것임.

이제 직접 이벤트를 만들 수 있으므로 다양한 동작을 구현할 수 있다. 웹 서버를 구축할 때 많이 사용. 지금까지 배운 개념들만으로도 서버를 만들기 충분. 하지만 서버를 운영할 때 코드에 에러가 발생하는 것은 치명적이므로, 마지막으로 에러를 처리하는 방법을 배워보겠음. 다음 절에서 알아봅시다!

3.8 예외 처리하기

노드에서는 예외 처리가 정말 중요. 예외란 보통 처리하지 못한 에러를 가리킨다. 이러한 예외들은 실행중인 노드 프로세스를 멈추게 된다. 멀티 스레드 프로그램에서는 스레드 하나가 멈추면 그 일을 다른 스레드가 대신. 하지만 노드의 메인 스레드는 하나뿐이므로 그 하나를 소중히 보호해야. 메인 스레드가 에러로 인해 멈춘다는 것은 스레드를 갖고 있는 프로세스가 멈춘다는 뜻이며 전체 서버도 멈춘다는 뜻과 같다. 아무리 신중을 기해도 예기치 못한 에러는 발생하는 법.

아래 예제에서는 프로세서가 멈추지 않도록 에러를 잡아보자. 에러가 발생할 것 같은 부분을 try/catch문으로 감싸면 된다.

setInterval(()=>{
    console.log('시작');
    try{
        throw new Error('서버를 고장내주지!!!!!!!!');
    } catch(err) {
        console.error(err);
    }
}, 100);

[결과]
시작
Error: 서버를 고장내주지!!!!!!!!
    at Timeout._onTimeout (c:\coding\TIL\node\node_3장\3.8 예외처리하기\error1.js:4:15)
    at listOnTimeout (node:internal/timers:568:17)
    at processTimers (node:internal/timers:510:7)

setInterval을 사용한 것은 프로세스가 멈추는지 여부를 체크하기 위해서. 프로세스가 에러로 인해 멈추면 setInterval도 멈출 것. setInterval 내부에 throw new Error()를 써서 에러를 강제로 발생.

에러를 발생하지만 try/catch로 잡을 수 있고 setInterval도 직접 멈추기 전까지 계속 실행. 이렇게 에러가 발생할 것 같은 부분을 미리 try/catch로 감싸면 된다. 이번에는 노드 자체에서 잡아주는 에러.

error2.js

// error2 
const fs = require('fs');

setInterval(()=>{ 
    fs.unlink('./abcdefg.js',(err)=>{
        if(err){
            console.error(err);
        }
    });
}, 1000);

fs.unlink로 존재하지 않는 파일을 지우고 있다. 에러가 발생하지만 다행히 노드 내장 모듈의 에러는 실행 중인 프로세스를 멈추지 않는다. 에러 로그를 기록해두고 나중에 원인을 찾아 수정하면 된다.

3.6절의 예제에서는 에러가 발생했을 때 에러를 throw했음. 그런데 throw를 하면 노드 프로세스가 멈춰버린다. 따라서 throw를 하는 경우에는 반드시 try/catch문으로 throw한 에러를 잡아야 한다.

error3.js

const fs = require('fs').promises;
setInterval(()=>{
    fs.unlink('./abcdefg.js')
}, 1000);

프로미스의 에러는 catch하지 않아도 알아서 처리된다. 다만 프로미스의 에러를 알아서 처리하는 동작은 노드 버전이 올라감에 따라 바뀔 수 있다. 따라서 프로미스를 사용할 때는 항상 catch를 붙여주는 것을 권장. 이번에는 정말 예측이 불가능한 에러를 처리하는 방법을 알아보자.

error4.js

process.on('uncaughtException',(err)=>{
    console.log('예기치 못한 에러', err);
});

setInterval(()=>{
    throw new Error('서버를 고장내주지')
},1000);

setTimeout(()=>{
    console.log('실행됩니다');
},2000)

process 객체에 uncaughtException 이벤트 리스너를 달았다. 처리하지 못한 에러가 발생했을 때 이벤트 리스너가 실행되고 프로세스가 유지. 이 부분이 없다면 위 예제에서는 setTimeout이 실행되지 않는다. 실행 후 1초 만에 setInterval에서 에러가 발생하여 프로세스가 멈추기 때문. 하지만 uncaughtException 이벤트 리스너가 연결되어 있으므로 프로세스가 멈추지 않는다.

try/catch로 처리하지 못한 에러가 발생했지만 코드가 제대로 실행됨. 어떻게 보면 uncaughtException 이벤트 리스너로 모든 에러를 처리할 수 있는 것처럼 보인다. 실제로 uncaughtException의 콜백 함수에 에러 발생 시 복구 작업을 하는 코드를 넣어둔 사람을 본 적도 있다. 하지만 노드 공식 문서에서는 uncaughtException 이벤트 최후의 수단으로 사용할 것을 명시. 노드 uncaughtException 이벤트 발생 후 다음 동작이 제대로 동작하는지를 보증하지 않는다. 즉, 복구 작업 코드를 넣어 두었더라도 그것이 동작하는지 확신할 수 없다. 따라서 uncaughtException은 단순히 에러 내용을 기록하는 정도로 사용하고 에러를 기록한 process.exit()으로 프로세스를 종료하는 것이 좋다. 에러가 발생하는 코드를 수정하지 않는 이상, 프로세스가 실행되는 동안 에러는 계속 발생.


3.8.1 자주 발생하는 에러들

서버 운영은 에러와의 싸움. 에러 사황에 대비하는 것이 최선 시간이나 비용, 인력 등의 제약으로 미처 대비하지 못한 에러가 발생할 수 있다. 따라서 에러 발생 시 철저히 기록하는 습관을 들이고 주기적으로 로그를 확인하면서 보완해나가야. 운영 중인 서버가 에러로 인해 종료되었을 때 자동 재시작은 이후 15장에.

이번 절에는 자주 발생하는 에러를 모아둠.

• node: command not found: 노드를 설치했지만 이 에러가 발생하는 경우는 환경 변수가 제대로 설정되지 않은 것입니다. 환경 변수에는 노드가 설치된 경로가 포함되어야 합니다. node 외의 다른 명령어도 마찬가지입니다. 그 명령어를 수행할 수 있는 파일이 환경 변수에 들어 있어야 명령어를 콘솔에서 사용할 수 있습니다.

• ReferenceError: 모듈 is not defined: 모듈을 require했는지 확인합니다.

• Error: Cannot find module 모듈명: 해당 모듈을 require했지만 설치하지 않았습니다. npm i 명령어로 설치하세요.

• Error: Can't set headers after they are sent: 요청에 대한 응답을 보낼 때 응답을 두 번 이상 보냈습니다. 요청에 대한 응답은 한 번만 보내야 합니다. 응답을 보내는 메서드를 두 번 이상 사용하지 않았는지 체크해보세요.

• FATAL ERROR: CALL_AND_RETRY_LAST Allocation failed - JavaScript heap out of memory: 코드를 실행할 때 메모리가 부족하여 스크립트가 정상 작동하지 않는 경우입니다. 코드가 잘못되었을 확률이 높으므로 코드를 점검해보세요. 만약 코드는 정상이지만 노드가 활용할 수 있는 메모리가 부족한 경우라면 노드의 메모리를 늘릴 수 있습니다. 노드를 실행할 때 node --max-old-space-size=4096 파일명과 같은 명령어를 사용하면 됩니다. 4096은 4GB를 의미합니다. 원하는 용량을 적으면 됩니다.

• UnhandledPromiseRejectionWarning: Unhandled promise rejection: 프로미스 사용 시 catch 메서드를 붙이지 않으면 발생합니다. 항상 catch를 붙여 에러가 나는 상황에 대비하세요.

• EADDRINUSE 포트 번호: 해당 포트 번호에 이미 다른 프로세스가 연결되어 있습니다. 그 프로세스는 노드 프로세스일 수도 있고 다른 프로그램일 수도 있습니다. 그 프로세스를 종료하거나 다른 포트 번호를 사용해야 합니다.

• EACCES 또는 EPERM: 노드가 작업을 수행하는 데 권한이 충분하지 않습니다. 파일/폴더 수정, 삭제, 생성 권한을 확인해보는 것이 좋습니다. 맥이나 리눅스 운영체제라면 명령어 앞에 sudo를 붙이는 것도 방법입니다.

• EJSONPARSE: package.json 등의 JSON 파일에 문법 오류가 있을 때 발생합니다. 자바스크립트 객체와는 형식이 조금 다르니 쉼표 같은 게 빠지거나 추가되지는 않았는지 확인해보세요.

• ECONNREFUSED: 요청을 보냈으나 연결이 성립하지 않을 때 발생합니다. 요청을 받는 서버의 주소가 올바른지, 꺼져 있지는 않은지 확인해봐야 합니다.

• ETARGET: package.json에 기록한 패키지 버전이 존재하지 않을 때 발생합니다. 해당 버전이 존재하는지 확인하세요.

• ETIMEOUT: 요청을 보냈으나 응답이 일정 시간 내에 오지 않을 때 발생합니다. 역시 요청을 받는 서버의 상태를 점검해봐야 합니다.

• ENOENT: no such file or directory: 지정한 폴더나 파일이 존재하지 않는 경우입니다. 맥이나 리눅스 운영체제에서는 대소문자도 구별하므로 확인해봐야 합니다.

0개의 댓글