티스토리 뷰

3장 노드 기능 알아보기

 

📌 REPL(:레플)

→ 노드에서 제공하는 콘솔(브라우저의 콘솔과 유사)

→ 코드를 읽고(Read), 해석하고(Eval), 결과물을 반환하고(Print), 종료할 때까지 반복(Loop)

→ 윈도우 명령 프롬프트 또는 VS Code 터미널(Ctrl + ` )를 열고 node를 쳐서 사용! (Ctrl+C를 두번 누르거나 .exit으로 종료)

→ 한두 줄짜리 코드를 테스트해보는 용도로는 좋지만 여러 줄의 코드를 실행하는데는 부적합

 

📌 JS 파일 실행

→ 자바스크립트 파일을 만든 후, 콘솔(cmd, 터미널)에서 node [js 파일 경로] 로 실행

 

📌 모듈 만들기

→ 모듈이란 특정한 기능을 하는 함수나 변수들의 집합 (자체로 하나의 프로그램이면서 다른 프로그램의 부품으로도 사용 가능, 라이브러리와 거의 동일)

→ 모듈은 여러 프로그램에서 재사용 가능

→ 모듈로부터 값을 불러올 때 변수이름을 다르게 지정할 수도 있음

// 모듈 만들기!
const odd = '홀수입니다';
const even = '짝수입니다';

module.exports = {
    odd,
    even,
};
// 모듈 사용하기!
// ES2015: import { odd, even } from './var'
const { odd, even } = require('./var'); // require('파일경로')

function checkOddOrEven(num) {
    if(num % 2) { // 홀수면
        return odd;
    }
    return even;
}
// ES2015: export default checkOddOrEven;
module.exports = checkOddOrEven; // 다른 모듈을 사용하는 파일을 다시 모듈로 만들 수 있음

* 참고) ES2015 모듈

 

3.4 노드 내장 객체 알아보기

 

📌 global

→ 브라우저의 window와 같은 전역 객체 (모든 파일에서 접근 가능)

→ global 객체의 메서드는 global을 생략하고 사용 가능

→ 전역 객체라는 점을 이용해 파일 간에 간단한 데이터를 공유할 때 사용하기도 (남용X)

 

📌 console

→ global 객체 안에 들어있는 객체

→ 보통 디버깅을 위해 사용

console.time('레이블');
console.timeEnd('레이블'); // 레이블: 1.017ms
console.log('평범한 로그를 콘솔에 표시');
console.error('에러를 콘솔에 표시');
console.table([{name: '', birth: ''}, {name: '', birth: ''}]); // 배열 요소로 객체 리터럴을 넣으면, 객체 속성들이 테이블로 표현
console.trace('에러 위치 추적')

 

📌 타이머 함수

→ global 객체 안에 있는 타이머 기능의 함수들 (setTimeout(콜백 함수, 밀리초), setInterval(콜백 함수, 밀리초), setImmediate(콜백 함수))

→ 타이머 함수들은 모두 아이디를 반환

→ 아이디를 사용해 타이머 취소 가능 (clearTimeout(아이디), clearInterval(아이디), clearImmediate(아이디))

→ setImmediate(콜백 함수) 대신에 setTimeout(콜백 함수, 0) 사용하는건 권장 X

// 헷갈렸던 것
const immediate = setImmediate(() => {
	console.log('실행되지 않습니다');
});

clearImmediate(immediate); // 하면 정말 실행 X (이벤트 루프를 잘 생각하기!!)

 

📌 __filename, __dirname 키워드

→ 실행 시 현재 파일명(ex. C:\Users\filename.js)과 파일 경로(ex. C:\Users)에 대한 정보를 제공

→ 경로가 문자열로 반환되는 문제나 경로 구분자(\, /) 문제 때문에 보통 path 모듈과 함께 사용

 

📌 module, exports

→ exports 객체와 module.export 객체가 같은 객체를 참조 (따라서, 둘을 동시에 사용 X)

→ exports 객체를 사용할 때는 객체만 사용 가능 (함수 X)

...

module.exports = {
    odd,
    even,
};

// 위의 코드와 아래 코드는 같은 결과
// exports.odd = '홀수입니다';
// exports.even = '짝수입니다';
참고) 노드에서 최상위 스코프에 존재하는 this는 module.exports를 가리킴 but 함수 선언문 내부의 this는 global 객체를 가리킴

 

📌 require

→ 모듈을 불러오는 함수(객체)

→ require가 반드시 파일 최상단에 위치할 필요가 없고, module.exports도 최하단에 위치할 필요 X

→ require.cache 객체에 require한 파일들이 저장됨 (다음 번에 require할 때 새로 불러오지 않아도 됨!)

→ require.main은 노드 실행 시 첫 모듈을 가리킴 (현재 파일이 첫 모듈일 경우 require.main === module이 true)

→ 첫 모듈의 이름은require.main.filename으로 확인

참고) 두 모듈이 서로를 require하는 것을 순환 참조라고 하며, 순환 참조가 있을 경우 순환 참조되는 대상을 빈 객체로 만듬(에러 발생 X)

 

📌 process

→ process 객체는 현재 실행되고 있는 노드 프로세스에 대한 정보를 담고 있음

process.version // 설치된 노드의 버전
process.arch // 프로세서 아키텍처 정보 (ex. x64)
process.platform // 운영체제 플랫폼 정보 (ex. win32)
process.pid // 현재 프로세스의 아이디
process.uptime // 프로세스가 시작된 후 흐른 시간(초)
process.exePath // 노드의 경로
process.cwd // 현재 프로세스가 실행되는 위치
process.cpuUsage() // 현재 cpu 사용량

 

process.env는 시스템의 환경 변수

// 왼쪽이 환경 변수 이름, 오른쪽이 값
NODE_OPTION=--max-old-space-size=8192 // 노드의 메모리를 8GB까지 사용
UV_THREADPOOL_SIZE=8 // 노드에서 기본적으로 사용하는 스레드풀의 스레드 개수

→ process.env는 서비스의 중요한 키(서버, 데이터베이스 비밀번호, API 키)를 저장하는 공간으로 사용

const secretId = process.env.SECRET_ID;
const secretCode = process.env.SECRET_CODE;
// 이제 process.env에 직접 SECRET_ID와 SECRET_CODE를 넣으면 됨 (dotenv를 이용해서 한 번에 모든 운영체제에 동일하게 넣을 수 있음)

 

→ process.nextTick(콜백)은 이벤트 루프가 다른 콜백 함수들보다 우선으로 처리

→ 다른 콜백들보다 우선시되는 process.nextTick()이나 resolve된 Promise는 마이크로태스크라고 함

→ 만약 비동기 처리를 할 때 process.nextTick()을 사용하게 되면 다른 콜백 함수가 실행되지 않을 수도 있음

 

process.exit(코드)는 실행 중인 노드 프로세스를 종료 (코드가 비어있거나 0이면 정상 종료, 에러가 발생하면 코드가 1)

 

3.5 노드 내장 모듈 사용하기

 

📌 os

→ 운영체제 정보를 담고 있는 모듈

os.arch() // 운영체제 아키텍처 정보 (ex. x64)
os.platform() // 운영체제 플랫폼 정보 (ex. win32)
os.type() // 운영체제 종류
os.uptime() // 운영체제 부팅 이후 흐른 시간(초)
os.hostname() // 컴퓨터 이름
os.release() // 운영체제 버전
os.homedir() // 홈 디렉터리 경로
os.cpus() // 컴퓨터의 코어 정보
os.freemem() // 사용 가능한 메모리(RAM)
os.totalmem() // 전체 메모리 용량
참고) os.constants 객체에는 에러와 신호에 대한 정보가 담겨있음!

 

📌 path

→ 폴더와 파일의 경로를 쉽게 조작하도록 도와주는 모듈

→ 운영체제별로 경로 구분자가 다르기 때문에 꼭 필요!

path.sep // 경로 구분자
path.delimiter // 환경 변수 구분자
path.dirname(경로) // 파일이 위치한 폴더 경로
path.extname(경로) // 파일의 확장자
path.basename(경로, 확장자) // 파일이름(확장자 포함) *확장자를 제외하고 싶을 때 두번째 인수를 넣어줌
path.parse(경로) // 파일 경로를 root, base, ext, name으로 분리
path.format(객체) // path.parse()한 객체를 파일 경로로 합침
path.narmalize(경로) // /sk \를 실수로 여러 번 사용했거나 혼용했을 때 정상적인 경로로 변환
path.isAbsolute(경로) // 파일의 경로가 절대경로인지 상대경로인지를 true나 false로 알림
path.relative(기준경로, 비교경로) // 기준경로에서 비교경로로 가는 방법을 알려줌
path.join(경로, ...) // 여러 인수를 넣으면 하나의 경로로 합침
path.resolve(경로, ...) // 여러 인수를 넣으면 하나의 경로로 합침(/를 만나면 절대경로로 인식)

 

📌 url

→ 인터넷 주소를 쉽게 조작하도록 도와주는 모듈

→ new URL(경로) 방식과 url.parse(경로) 방식이 있음

new URL(): URL {
  href: 'http://www.gilbut.co.kr/book/bookList.aspx?sercate1=001001000#anchor',
  origin: 'http://www.gilbut.co.kr',
  protocol: 'http:',
  username: '',
  password: '',
  host: 'www.gilbut.co.kr',
  hostname: 'www.gilbut.co.kr',
  port: '',
  pathname: '/book/bookList.aspx',
  search: '?sercate1=001001000',
  searchParams: URLSearchParams { 'sercate1' => '001001000' },
  hash: '#anchor'
}

url.parse(): Url {
  protocol: 'http:',
  slashes: true,
  auth: null,
  host: 'www.gilbut.co.kr',
  port: null,
  hostname: 'www.gilbut.co.kr',
  hash: '#anchor',
  search: '?sercate1=001001000',
  query: 'sercate1=001001000',
  pathname: '/book/bookList.aspx',
  path: '/book/bookList.aspx?sercate1=001001000',
  href: 'http://www.gilbut.co.kr/book/bookList.aspx?sercate1=001001000#anchor'
}

// 참고) url.format(객체)는 분해되었던 url 객체를 다시 원래 상태로 조립

→ host부분 없이 pathname 부분만 오는 주소에서는 url.parse()만 사용가능!

→ new URL() 방식은 search부분을 searchParams라는 특수한 객체로 반환

* 보통 search부은 주소를 통해 데이터를 전달할 때 사용

searchParams.getAll(key) // 키에 해당하는 모든 값
searchParams.get(key) // 키에 해당하는 첫 번째 값
searchParams.has(key) // 해당 키가 있는지 없는지 확인
searchParams.keys() // searchParams의 모든 키를 iterator 객체로 반환
searchParams.values() // searchParams의 모든 값을 iterator 객체로 반환
searchParams.append() // 해당 키를 추가
searchParams.set() // 해당 키를 추가 (같은 키 값들을 모두 지움)
searchParams.delete(key) // 해당 키를 제거
searchParams.toString() // searchParams 객체를 다시 문자열로 변환

 

📌 querystring

→ WHATWG 방식(new URL()) 대신 기존 노드의 url(url.parse())을 사용할 때, shearch 부분을 사용하기 쉽게 객체로 만드는 모듈

const query = url.parse('url...').query //  쿼리를 가져오는 방법!
querystring.parse(query) // url의 query 부분을 자바스크립트 객체로 분해
querystring.stringify(query) // 분해된 query 객체를 문자열로 다시 조립

 

📌 crypto

→ 다양한 방식의 암호화를 도와주는 모듈

 

📌 util

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

→ deprecated를 처리할 때나 콜백 패턴을 프로미스 패턴으로 바꿀 때 등에 사용

util.deprecate(함수, '경고 메시지') // 함수가 deprecated 처리되었음을 알림
util.promisify(함수) // 콜백 패턴을 프로미스 패턴으로 바꿈

 

📌 worker_threads

→ 멀티 스레드 작업에 사용하는 모듈

 

📌 child_process

→ 다른 프로그램을 실행하고 싶거나 명령어를 수행하고 싶을 때 사용하는 모듈

 

📌 기타 모듈들

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

 

3.6 파일 시스템 접근하기

 

📌 fs

→ 파일 시스템에 접근하는 모듈 (파일 생성, 삭제, 읽고 쓰기)

→ fs 모듈을 불러와서 사용할 때 파일 경로는 현재 파일 기준이 아니라 node명령어를 실행하는 콘솔 기준!

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

fs.readFile('파일경로') // 파일 읽기 *프로미스가 resolve되면 data,  reject되면 err를 인수로 넘김
fs.writeFile('파일경로', '내용')
fs.access(경로, 옵션, 콜백) // 폴더나 파일에 접근할 수 있는지를 체크
fs.mkdir(경로, 콜백) // 폴더를 만드는 메서드 (이미 폴더가 있으면 error)
fs.open(경로, 옵션, 콜백) // 파일의 아이디를 가져오는 메서드
fs.rename(기존경로, 새경로, 콜백) // 파일의 이름을 바꾸는 메서드
fs.readdir(경로, 콜백) // 폴더 안의 내용물을 확인
fs.unlink(경로, 콜백) // 파일 삭제 (파일이 없으면 error)
fs.rmdir(경로, 콜백) // 폴더를 지울 수 있음
fs.copyFole(복사할 파일 경로, 복사될 경로, 콜백) // 파일 복사
fs.watch(경로, 콜백(eventType, filename)) // 파일/폴더 변경 사항을 감시

→ fs에는 동기 방식으로 변환가능한 비동기 방식의 메서드(ex. readFile())가 많음

동기와 비동기 : 백그라운드 작업 완료 확인 여부
블로킹과 논 블로킹 : 함수가 바로 return되는지 여부

노드에서는 동기-블로킹(백그라운드 작업 완료 여부를 계속 확인하며 호출한 함수가 바로 return되지 않고 백그라운드 작업이 끝나야 return), 비동기-논 블로킹 방식(호출한 함수가 바로 return되어 다음 작업으로 넘어가며, 백그라운드가 작업이 완료되었다고 알려주면 처리)이 대부분

💡 비동기 방식으로 하되 실행 순서를 유지하고 싶으면 이전 함수의 콜백에 다음 함수를 넣으면 됨! (async/await으로 콜백 지옥 해결) 

 

→ fs에서 다루는 data는 Buffer 형식으로 제공 (toString()으로 변환 필요!)

버퍼 : 파일을 읽을 때 메모리에 파일 크기만큼 공간을 마련하고 파일 데이터를 메모리에 저장한 뒤 사용자가 조작
스트림 : 버퍼의 크기를 작게 만든 후 여러 번으로 나눠 보내는 방식
Buffer.from('문자열') // 문자열을 버퍼(메모리에 저장된 데이터)로 변환
Buffer.toString(버퍼) // 버퍼를 다시 문자열로 변환
Buffer.concat(배열) // 배열 안에 든 버퍼들을 하나로 합침
Buffer.alloc(바이트) // 빈 버퍼를 생성
fs.createReadStream('파일경로', 옵션 객체) // 읽기 스트림 생성
fs.createWriteStream('파일경로') // 쓰기 스트림 생성 *WriteStream은 write('문자열'), end() 메서드 제공
readStream.pipe(Other Stream) // 스트림끼리 연결

* 노드에서는 파일을 압축하는 zlib라는 모듈도 제공

스레드풀 덕분에 fs 비동기 메서드를 여러 번 실행해도 백그라운드에서 동시 처리 가능

* fs 외에도 내부적으로 스레드풀을 사용하는 모듈로는 crypto, zlib, dns, lookup 등이 있음
* process.env.UV_THREADPOOL_SIZE를 통해서 스레드풀 갯수 조절 가능!

 

📌 이벤트

→ events 모듈을 통해 이벤트 관리 가능

const myEvent = new EventEmitter(); 

myEvent.on(이벤트명, 콜백) // 이벤트 이름과 이벤트 발생 시의 콜백을 연결
myEvent.addListener(이벤트명, 콜백) // on과 동일
myEvent.emit(이벤트명) // 이벤트를 호출하는 메서드
myEvent.once(이벤트명, 콜백) // 한 번만 실행되는 이벤트
myEvent.removeAllListeners(이벤트명) // 이벤트에 연결된 모든 이벤트 리스너를 제거
myEvent.removeListemer(이벤트명, 리스너) // 이벤트에 연결된 리스너 하나를 제거
myEvent.off(이벤트명, 콜백) // removeListemer와 같음
removeListemer.listenerCount(이벤트명) // 현재 리스너가 몇 개 연결되어 있는지 확인

 

📌 예외 처리

→ 노드에서는 예외 처리가 정말정말정말 중요함

에러가 발생할 것 같은 부분을 try/catch문으로 감싸기!

→ 특히, 에러를 throw하는 경우에는 반드시 try/catch문 사용!

→ 프로미스의 에러는 catch하지 않아도 알아서 처리 (그래도 catch를 붙여주는 것을 권장!)

→ 최후의 수단은 process 객체에 uncaughtException 이벤트 리스너를 설정 (에러 내용을 기록하는 용도로만 권장)

 

공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2025/05   »
1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30 31
글 보관함