티스토리 뷰
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; // 다른 모듈을 사용하는 파일을 다시 모듈로 만들 수 있음
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 이벤트 리스너를 설정 (에러 내용을 기록하는 용도로만 권장)
'백엔드 > Nodejs' 카테고리의 다른 글
[Nodejs] 익스프레스 웹 서버 만들기 (0) | 2021.11.16 |
---|---|
[Nodejs] 패키지 매니저 (0) | 2021.11.15 |
[Nodejs] http 모듈로 서버 만들기 (0) | 2021.11.15 |
[Nodejs] 알아두어야 할 자바스크립트 (0) | 2021.11.11 |
[Nodejs] 노드 시작하기 (0) | 2021.11.10 |