상세 컨텐츠

본문 제목

[Node.js] 3장 기능 알아보기(2)

21-22/21-22 Node.js

by juserh 2021. 10. 30. 21:34

본문

728x90

5. 노드 내장 모듈 사용하기

(1) os 

os모듈에 운영체제의 정보가 담겨 있어 해당 정보를 가져올 수 있음
process 객체와 겹치는 부분이 있음
일반적인 웹 서비스에는 사용빈도가 높지 않으나 운영체제별로 다른 서비스를 제공하고자 할 때 유용

 

  • os.arch(): 운영체제 아키텍처, process.arch와 동일
  • os.platform(): 운영체제 플랫폼, process.arch와 동일
  • os.type(): 운영체제 종류
  • os.uptime(): 운영체제 부팅 이후 흐른 시간(초)        cf)process.uptime(): 노드의 실행시간
  • os.hostname(): 컴퓨터의 이름
  • os.release(): 운영체제의 버전
  • os.homedir(): 홈 디렉터리의 경로

os.tmpdir(): 임시 파일 저장 경로

  • os.cpus(): 컴퓨터의 코어 정보(os.cpus().length: 코어의 개수)
  • os.freemem(): 사용 가능한 메모리(RAM)
  • os.totalmem(): 전체 메모리 용량
  • os.constants: 각종 에러와 신호에 대한 정보가 담긴 객체

(2) path

폴더와 파일의 경로 조작을 도움.
운영체제별로 경로 구분자가 다르기 때문에 필요함. (윈도우: \(역슬래시), POSIX: /)

 

  • path.sep: 경로의 구분자(윈도우: \(역슬래시), POSIX: /)
  • path.delimiter: 환경변수의 구분자(윈도우: 세미콜론(;), POSIX: 콜론(:))
  • path.dirname(경로): 파일이 위치한 폴더 경로
  • path.extname(경로): 파일의 확장자
  • path.basename(경로, 확장자): 파일의 이름(확장자 포함)
  • path.parse(경로): 파일 경로를 root, dir, base, ext, name으로 분리
  • path.format(객체): path.parse()한 객체를 파일 경로로 합침
  • path.normalize(경로): /나 \를 실수로 여러 번 사용하거나 혼용한 경우 정상적인 경로로 반환
  • path.isAbsolute(경로): 파일의 경로가 절대경로인지 상대경로인지를 true, false로 반환
  • path.relative(기준경로, 비교경로): 첫 번째 경로에서 두 번째 경로로 가는 방법
  • path.join(경로, ...): 여러 인수를 넣으면 하나의 경로로 합침(상대경로 ..과 .도 처리 가능). /을 상대경로로 처리.
  • path.resolve(경로, ...): path.join()과 비슷하지만 다름. /을 절대 경로로 처리.
path.join()과 path.resolve()의 차이
path.join('/a', '/b', 'c');      ->결과: /a/b/c
path.resolve('/a', '/b', 'c');  ->결과: /b/c    

(3) url

인터넷 주소 조작을 돕는 모듈

WHATWG과 기존 노드의 주소 체계

  • WHATWG 주소 체계

url.js(WHATWG 방식)

url 모듈 안에 URL 생성자가 존재함. 생성자 안에 주소를 넣어 객체로 만들면 주소가 부분별로 정리됨.

출력결과

이 중 origin, username, password, searchParams 속성은 WHATWG에만 있는 속성

searchParams: search
URL.searchParams.getAll(키): 키에 해당하는 모든 값
URL.searchParams.get(키): 키에 해당하는 첫 번째 값만
URL.searchParams.has(키): 해당 키 존재 여부(true, false)
URL.searchParams.keys(): searchParams의 모든 키를 반복(ES2015 문법) 객체로 가져옴
URL.searchParams.values(): searchParams의 모든 값을 반복기 객체로 가져옴
URL.searchParams.append(키, 값): 해당 키를 추가. (같은 키의 값이 있다면 유지하고 하나 더 추가)
URL.searchParams.set(키, 값): 같은 키의 값들을 모두 지우고 새로 추가
URL.searchParams.delete(키): 해당 키 제거
URL.searchParams.toString(): 조작한 searchParams를 다시 문자열 형태 저장. 이 문자열을 search 속성에 대입하면 주소 객체에 반영됨.
  • 기존 노드 주소 체계

url.js
출력 결과

url.parse(주소): WHATWG의 username과 password속성 -> auth속성

                    WHATWG의 searchParams -> query속성

                    search 속성

url.format(객체): WHATWG와 기존 노드의 url 모두 사용 가능. 분해된 url 객체를 원래 상태로 조합

기존 노드의 url형식
host부분 없이 pathname부분만 오는 주소 사용하는 경우 ex)/book/bookList.apsx
search
querystring.js
querystring: search부분을 사용하기 쉽게 객체로 만드는 모듈
querystring.parse(쿼리): url의 query부분을 자바스크립트 객체로 분해
querystring.stringify(객체): 분해된 query객체를 문자열로 다시 조립

(5) crypto

암호화를 돕는 모듈

1. 단방향 암호화: 복호화 불가. 해시함수(출력 문자열의 길이 고정)  ex.비밀번호

hash.js

  • crypto.createHash(알고리즘): 사용할 해시 알고리즘 입력( md5, sha1, sha256, sha512 등)
  • crypto.update(문자열): 변환할 문자열
  • crypto.digest(인코딩): 인코딩할 알고리즘 입력(base64, hex, latin1 등). 결과물로 변화된 문자열 반환.
    pbkdf2
기존의 문자열에 salt 문자열 붙인 후, 해시 알고리즘 반복 적용
pbkdf2
pbkdf2.js

crypto.pbkdf2(비밀번호, salt, 반복 횟수, 출력 바이트, 해시 알고리즘, callback)

2. 양방향 암호화: 복호화 가능, 키 사용

cipher.js

  • crypto.createCipheriv(암호화 알고리즘, 키, iv): 알고리즘마다 키와 iv에 요구되는 길이가 존재. (iv: 암호화 초기화 벡터)
  • cipher.update(문자열, 대상의 인코딩, 출력 인코딩): 암호화할 대상과 그의 인코딩, 암호의 인코딩
  • cipher.final(출력 인코딩): 출력 결과물 인코딩->암호화 완료
  • crypto.createDeCipheriv(암호화 알고리즘, 키, iv): 암호화할 때 사용한 알고리즘과 키, iv 그대로 입력
  • decipher.update(문자열, 인코딩, 출력 인코딩): 암호화된 문장과 그의 인코딩, 복호화할 인코딩
  • decipher.final(출력 인코딩): 복호화 결과물 인코딩
  • crypto.getCiphers(): 사용 가능한 알고리즘 목록 출력

(6) util

각종 편의 기능을 모아둔 모듈

util.js

  • util.deprecate: 함수가 deprecated 처리되었음을 알림. 첫 번째 인수로 넣은 함수를 사용했을 때 경고 메시지 출력, 두 번째 인수가 경고 메시지 내용.
  • util.primisify: 콜백 패턴을 프로미스 패턴으로 변환. async/await 패턴 사용 가능.    cf)pbkdf2.js의 randomBytes와 비교)

(7) worker_threads

멀티 스레드 방식 작업 관련 모듈

worker_threads.js

  • new Worker(__filename): 현재 파일(__filename)에서 워커 스레드 실행
  • worker.postMessage(메시지): 워커에 데이터 전송
  • parentPort.on('message'): 부모로부터 메시지 받음
  • parentPort.postMessage(메시지): 부모에 메시지 전송
  • parentPort.close(): 워커에서 on메서드 사용할 때는 직접 워커를 종료해야 함. 부모와의 연결 종료. worker.on('exit') 실행
  • worker.on('message'): 워커로부터 메시지 받음

worker_data.js

  • new Worker(__filename, {workerData: {start: 1}, }): new Worker 호출. 두 번째 인수의 workerData 속성으로 원하는 데이터 전송 가능.

prime.js(워커 스레드 없이)

const { Worker, isMainThread, parentPort, workerData } = require('worker_threads');

const min = 2;
let primes = []; //소수 배열

function findPrimes(start, range) {  //소수 검사
    let isPrime = true;
    const end = start + range;
    for (let i = start; i < end; i++) {
        for (let j = min; j < Math.sqrt(end); j++) {
            if (i !== j && i % j === 0) {
                isPrime = false;
                break;
            }
        }
        if (isPrime) {
            primes.push(i);
        }
        isPrime = true;
    }
}

if (isMainThread) {
    const max = 10000000;
    const threadCount = 8;  //워커 스레드 개수
    const threads = new Set();  //워커 스레드 집합
    const range = Math.ceil((max - min) / threadCount);
    let start = min;
    console.time('prime');  //시간 측정 시작
    for (let i = 0; i < threadCount - 1; i++) {  //워커 스레드에 수 분배
        const wStart = start;
        threads.add(new Worker(__filename, { workerData: { start: wStart, range } }));
        start += range;
    }
    threads.add(new Worker(__filename, { workerData: { start, range: range + ((max - min + 1) % threadCount) } }));
    for (let worker of threads) {  //스레드 집합에 있는 모든 워커에 대해서
        worker.on('error', (err) => {  //에러 발생 시
        throw err;
        });
        worker.on('exit', () => {  //워커 종료 시
            threads.delete(worker);  //해당 워커 삭제
            if (threads.size === 0) {  //남은 워커가 없는 경우
                console.timeEnd('prime');  //시간 측정 종료
                console.log(primes.length);  //배열에 저장된 소수의 개수 출력
            }
        });
        worker.on('message', (msg) => {  //워커에게서 데이터(소수) 수신 시
            primes = primes.concat(msg);  //소수 배열에 해당 데이터 추가
        });
    }
} else { //워커 스레드 코드
    findPrimes(workerData.start, workerData.range);  //워커스레드 소수 검사 실행
    parentPort.postMessage(primes);  //부모(메인) 스레드에 소수 데이터 전송
}

prime.js(워커 스레드X)와 prime_worker.js(워크스레드O) 소요 시간 비교

     스레드를 생성하고 스레드 사이에서 통신하는 데 상당한 비용 발생->이를 고려하여 멀티 스레딩 해야 함!!

(8) child_process

다른 프로그램을 실행하거나 명령어를 수행하고 싶은 경우 사용하는 모듈
다른 언어의 코드를 실행하고 결괏값을 받을 수 있음
현재 노드 프로세스 외에 시로운 프로세스를 띄워서 명령을 수행하고 노드 프로세스에 결과를 알려줌

exec.js

  • exec(명령어): 위 코드에선 dir명령(현재 폴더의 파일 목록 출력)        +)리눅스나 맥의 경우 'ls'
  • stdout: 표준 출력, data이벤트 리스너에 버퍼 형태로 전달
  • stderr: 표준 에러, data이벤트 리스너에 버퍼 형태로 전달

spawn.js(파이썬 프로그램 실행)

  • spawn(명령어, 옵션배열)

(9) 기타 모듈들

  • assert: 값을 비교하여 프로그램이 제대로 동작하는지 테스트하는 데 사용
  • dns: 도메인 이름에 대한 IP주소를 얻어내는 데 사용
  • net: HTTP보다 로우 레벨인 TCP나 IPC 통신을 할 때 사용
  • string_decoder: 버퍼 데이터를 문자열로 바꾸는 데 사용
  • tls: TLS와 SSL 관련된 작업을 할 때 사용
  • tty: 터미널과 관련된 작업을 할 때 사용
  • dgram: UDP 관련 작업을 할 때 사용
  • v8: V8 엔진에 직접 접근할 때 사용
  • vm: 가상 머신에 직접 접근할 때 사용

 

6. 파일 시스템 접근하기

fs 모듈
파일 시스템에 접근하는 모듈
파일 생성, 삭제, 읽기, 쓰기
폴더 생성, 삭제

readFile.js

  • fs.readFile(파일 경로, 콜백함수): 읽을 파일 경로 지정, 콜백형식의 모듈. 결과물을 buffer형태로 출력하므로 data에 toString()을 붙여러 출력.

readFilePromise.js

  • .promises: 프로미스 기반의 fs 모듈 사용 가능

writeFile.js

  • fs.writeFile(파일 경로, 내용, 콜백함수)

(1) 동기 메서드와 비동기 메서드

노드는 대부분의 메서드를 비동기 방식으로 처리
fs모듈은 동기와 비동기 메서드 모두 존재

async.js
async.js 출력결과

  • 비동기: 백그라운드에 해당 파일을 읽으라고만 요청한 후 다음 작업으로 넘어감. 위 코드의 경우, 파일 읽기 요청만 세 번 보내고 console.log('끝') 실행, 파일 읽기가 완료되면 백그라운드가 다시 메인 스레드에 알리고 난 후 콜백함수 실행.
동기/비동기: 백그라운드 작업 완료 확인 여부
블로킹/논 블로킹: 함수가 바로 return 되는지 여부

동기-블로킹: 백그라운드 작업 완료 여부 계속 확인, 호출한 함수 바로 return 되지 않고 백그라운드 작업 끝나야 return
비동기-논블로킹: 호출된 함수 바로 return 되어 다음 작업으로 넘어가며, 백그라운드 작업 완료 여부는 신경 쓰지 않고 나중에 백그라운드가 알림을 줄 때 처리

sync.js

  • 동기: 순서대로 실행. 백그라운드가 작업하는 동안 메인 스레드는 대기->비효율적. 초기화 용도 사용 권장.

asyncOrderPromise.js(비동기 메소드로 동기식 출력-프로미스 사용)

(2) 버퍼와 스트림 이해하기

파일을 읽기, 쓰기 방식: 버퍼/스트림

1. 버퍼: 파일 데이터를 메모리에 저장한 뒤, 사용자 조작. 용량 관련 문제 발생.

버퍼
buffer.js

  • Buffer: 버퍼를 직접 다룰 수 있는 클래스
  • buffer.from(문자열): 문자열을 버퍼로 변환. length 속성은 버퍼의 크기(바이트 단위)
  • buffer.toString(인수): 버퍼를 다시 문자열로 변환. 인수에 특정 인코딩 지정 가능.
  • buffer.concat(배열): 배열 안에 든 버퍼를 합침
  • buffer.alloc(바이트): 빈 버퍼 생성

2. 스트림: 버퍼의 크기를 작게 만든 후, 여러 번으로 나눠 보내는 방식

스트림
createReadStream.js
출력 결과

  • fs.createReadStream(파일 경로, {highWaterMark: 버퍼의 크기}): 버퍼의 크기는 바이트 단위(기본값 64KB)
  • 이벤트 리스너: data(파일 일기 시작), end(파일 읽기 완료 시), error(에러 발생 시)

createWriteStream.js

  • fs.createWriteStream(출력 파일명): 두 번째 인수로 옵션 추가 가능
  • 이벤트 리스너: finish(파일 쓰기 종료 시)
  • writeStream.write(데이터)
  • writeSTream.end(): 종료 알림->finish 이벤트 발생

pipe.js

  • readStream.pipe(writeStream): 두 스트림 사이를 연결하여 데이터 전달

gzip.js

  • zlib.createGzip(): 파일 압축. 스트림 지원. readStream과 writeStream 사이에서 파이핑 가능. 버퍼 데어터가 전달되다가 gzip 압축을 거친 후 파일로 써짐. (readme4.txt.gz 파일 생성)

buffer-memory.js(버퍼를 이용한 파일 복사)
stream-memory.js(스트림을 이용한 파일 복사)

(3) 기타 fs 메서드 알아보기

fsCreate.js

  • fs.access(경로, 옵션, 콜백): 폴더나 파일 접근 가능 여부 확인. 옵션은 상수(constants)로 설정 
    • 옵션 F_OK: 파일의 존재 여부
    • 옵션 R_OK: 읽기 권한 여부
    • 옵션 W_OK: 쓰기 권한 여부
    • 에러코드 ENOENT
  • fs.mkdir(경로, 콜백): 폴더 생성. 이미 폴더가 있다면 에러 발생
  • fs.open(경로, 옵션, 콜백): 파일의 아이디(fd 변수 가져옴)
    • 옵션 w: 쓰기(파일 없는 경우 생성)
    • 옵션 r: 읽기(파일 없는 경우 에러 발생)
    • 옵션 a: 기존 파일에 추가
  • fs.rename(기존 경로, 새 경로, 콜백): 파일 이름 변경 메소드. 잘라내기 기능으로도 사용 가능

fsDelete.js

  • fs.readdir(경로, 콜백): 폴더 안의 내용물 확인. 배열로 반환.
  • fs.unlink(경로, 콜백): 파일 삭제. 파일이 없는 경우 에러 발생->파일 존재 여부 확인
  • fs.rmdir(경로, 콜백): 폴더 삭제. 폴더 안에 파일이 있는 경우 에러 발생->내부 파일을 먼저 지우고 호출

copyFile.js

  • fs.copyFile(복사할 파일, 복사될 경로, 콜백): 노드 8.5버전 이후에는 pipe를 사용하지 않아도 파일 복사 가능

watch.js

  • fs.watch(대상 파일, 콜백): 파일/폴더의 변경사항 감시. rename 이벤트가 발생한 후에는 watch가 수행되지 않음. 실무 사용에 주의 필요.

(4) 스레드풀 알아보기

스레드풀: fs 메서드 여러번 실행해도 백그라운드에서 동시에 처리되도록 함

fs 메서드에서 스레드풀을 사용하는 모듈: crypto, zlib, dns, lookup 등

threadpool.js
실행결과

기본적인 스레드풀의 개수: 4개->처음 4개의 작업 동시에 실행->종료->다음 4개의 작업 실행  (컴퓨터의 코어 개수에 따라 다른 결과가 생길 수 있음)

  • 윈도우의 경우, 명령프롬프트에 SET UV_THREADPOOL_SIZE=개수
  • 맥의 경우, 터미널에 THREADPOOL=개수

      입력하면 스레드풀 개수 조절 가능(숫자를 크게 할 때는 컴퓨터 코어 개수와 같거나 많게 두어야 뚜렷한 효과 발생)

7. 이벤트 이해하기

event 모듈

event.js
실행결과

  • on(이벤트명, 콜백): 이벤트 리스닝(이벤트 이름과 이벤트 발생 시의 콜백 연결). 이벤트 하나에 여러 개의 콜백 가능.
  • addListener(이벤트명, 콜백): on과 같은 기능
  • emit(이벤트명): 이벤트 호출 메서드. 해당 이벤트의 콜백 실행.
  • once(이벤트명, 콜백): 한번만 실행되는 이벤트
  • removeAllListener(이벤트명): 이벤트에 연결된 모든 이벤트 리스너 제거.
  • removeListener(이벤트명, 리스너): 이벤트에 연결된 리스너를 하나씩 제거.
  • off(이벤트병, 콜백): 노드 10버전에 추가된 메서드. removeListener와 같은 기능.
  • listenerCount(이벤트명): 현재 연결되어 있는 리스너의 개수 확인

9. 예외 처리하기

노드에서는 예외 처리 매우 중요
노드의 메인 스레드는 하나뿐이므로 메인 스레드가 멈추면 스레드를 갖는 프로세스가 멈추고 전체 서버가 멈추게 됨.
  • 에러 발생 가능성이 있는 부분을 try/catch문으로 감싸기

error1.js

  •  노드 내장 모듈의 에러: 프로세스 계속 진행->에러 로그 기록

error2.js

  • 프로미스의 에러: catch하지 않아도 알아서 처리. 그러나 노드 버전이 올라감에 따라 바뀔 수 있으므로 catch 사용 권장.

error3.js

  • 예측 불가능한 에러 처리: unCaughtException 이벤트 리스너(에러 기록 용도로 사용)

error4.js

(1) 자주 발생하는 에러들

  • node: command not found: 환경변수가 제대로 설정되지 않음
  • ReferenceError: 모듈 is not defined: 모듈 require 확인
  • Error: Cannot find modul 모듈명: 해당 모듈을 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: 메모리 부족으로 스크립트가 정상 작동하지 않는 경우->코드 점검
  • UnhandledPromiseRejectionWarning: Unhandled promise rejection: 프로미스 사용 시 catch 메서드를 붙이지 않은 경우
  • EADDRINUSE 포트번호: 해당 포트번호에 이미 다른 프로세스가 연결되 경우->프로세스 종료/다른 포트번호 사용
윈도우에서 프로세스 종료하기
netstat -ano | findstr 포트
taskkill /pid 프로세스아이디 /f
맥/리눅스에서 프로세스 종료하기
lsof -i tcp:포트
kill -9 프로세스 아이디
  • EACCES 또는 EPERM: 노드의 작업 수행에 권한이 충분하지 않은 경우->파일이나 폴더의 권한 확인/명령어 앞에 sudo
  • EJSONPARSE: package.json 등의 JSON파일에 문법 오류가 있는 경우
  • ECONNREFUSED: 요청을 보냈으나 연결이 성립하지 않은 경우->요청 받는 서버 확인
  • ETARGET: package.json에 기록한 패키지 버전이 존재하지 않는 경우->해당 버전 존재 여부 확인
  • ETIMEOUT: 요청을 보냈으나 응답이 일정 시간 내에 오지 않은 경우->요청 받는 서버의 상태 점검
  • ENOENT: no such file or directory: 지정한 폴더나 파일이 존재하지 않는 경우
728x90

관련글 더보기