1. REPL 사용하기
자바스크립트는 스크립트 언어이므로 미리 컴파일을 하지 않아도 즉석에서 코드를 실행 할 수 있음. 노드도 비슷한 콘솔을 제공하는데, 입력한 코드를 읽고(Read), 해석하고(Eval), 결과물을 반환하고(Print), 종료할 때까지 반복한다(Loop) 라고해서 REPL라고 부름(Read Eval Print Loop).
읽고, 해석한 뒤 바로 결과물을 출력했다. REPL은 한 두줄짜리를 테스트하기엔 좋지만 여러줄을 실행하기에는 불편함.
2. JS 파일 실행하기
코드가 길 경우에는 코드자체를 자바스크립트 파일로 작성하여 파일을 통쨰로 실행하는 것이 좋다.
ex) node [자바스크립트 파일 경로]
3. 모듈로 만들기
모듈 : 특정한 기능을 하는 함수나 변수들의 집합. 모듈은 하나의 프로그램이면서 다른 프로그램의 부품으로 사용 가능
//var.js
const odd = '홀수입니다';
const even = '짝수입니다';
module.exports = {
odd,
even,
};
//noduel.exports는 변수들을 모아둔 변수의 역할을 하게됨.
//func.js
const { odd, even } = require('./var');
function checkOddOrEven(num) {
if (num % 2) { //홀수면}
return odd;
}
return even;
}
module.exports = checkOddOrEven;
//index.js
const { odd, even} = require('./var');
const checkNumber = require('./func');
function checkStringOddOrEven(str) {
if (str.length % 2) { //홀수면}
return odd;
}
return even;
}
console.log(checkNumber(10));
console.log(checkStringOddOrEven('hello'));
예제를 봐도 알 수 있듯이 여러 파일에 걸쳐 재사용되는 함수나 변수를 모듈로 만들어두면 편리함. 단, 모듈이 많아지고 모듈 간의 관계가 얽히게 되면 구조를 파악하기 어려움.
예제에서 선언한 require 함수나 module 객체는 따로 선언하지 않았음에도 사용 할 수 있었다. 그 이유는 노드에서 기본적으로 제공하는 내장 객체이기 때문.
4. 노드 내장 객체 알아보기
노드에서는 기본적으로 내장 객체와 내장 모듈을 제공함.
1) global
전역 객체이므로 모든 파일에 접근 가능. → 파일 간에 간단한 데이터를 공유할 때 사용
//globalA.js
module.exports = () => global.message;
//globalB.js
const A = require('.globalA');
global.message = '안녕하세요';
console.log(A());
2) console
보통 디버깅을 위해서 사용. 대표적으로 사용하는것이 바로 cosole.log 메소드.
- console.time(레이블) : console.timeEnd(레이블)과 대응되어 같은 레이블을 가진 time과 timeEnd 사이의 시간을 측정.
- console.log(내용) : 평범한 로그를 콘솔에 표시. console.log(내용, 내용, ...) 처럼 여러 내용 동시에 표현 가능
- console.error(에러 내용) : 에러를 콘솔에 표시
- console.table(배열) : 배열의 요소로 객체 리터럴을 넣으면, 객체의 속성들이 테이블 형식으로 표현.
- console.dir(객체, 옵션) : 객체를 콘솔에 표시할 때 사용. 첫 번째 인수로 표시할 객체를 넣고, 두 번째 인수로 옵션을 넣음. 옵션의 colors를 true로 하면 콘솔에 색이 추가되어 보기가 편함. depth는 객체 안의 객체를 몇 단계까지 보여줄지를 결정. 기본값 2
- console.trace(레이블) : 에러가 어디서 발생했는지 추적할 수 있게함. 일반적으로 에러 발생 시 에러 위치를 알려주므로 자주 사용하지 않지만, 위치가 나오지 않는다면 사용할만함.
const string = 'abc';
const number = 2;
const boolean = true;
const obj = {
outside: {
inside: {
key: 'value',
},
},
};
console.time('전체 시간');
console.log('평범한 로그입니다 쉼표를 구분해 여러 값을 찍을 수 있습니다.');
console.log(string, number, boolean);
console.error('에러 메시지는 console.error에 담아주세요');
console.table([{name : '제로', birth : 1994}, {name : 'hero', birth : 1988}]);
console.dir(obj, { colors: false, depth: 2});
console.dir(obj, { colors: true, depth: 1});
console.time('시간 측정');
for (let i = 0; i < 100000; i++) {}
console.timeEnd('시간 측정');
function b() {
console.trace('에러 위치 추적');
}
function a() {
b();
}
a();
console.timeEnd('전체 시간');
3) 타이머
타이머 기능을 제공하는 함수 setTimeout, setInterval, setImmediate는 노드에서 window대신 global 객체안에 들어있음
- setTimeout(콜백 함수, 밀리초) : 주어진 밀리초(1,000분의 1초) 이후에 콜백 함수를 실행
- setInterval(콜백 함수, 밀리초) : 주어진 밀리초마다 콜백 함수를 반복 실행
- setImmediate(콜백 함수) : 콜백 함수를 즉시 실행
이 타이머 함수들을 모두 아이디를 반환하며, 아이디를 사용하여 타이머를 취소 할 수 있음
- clearTimeout(아이디) : setTimeout을 취소
- clearInterval(아이디) : setInterval)을 취소
- clearInmmediate(아이디) : setImmediate를 취소
const timeout = setTimeout(() => {
console.log('1.5초 후 실행');
}, 1500);
const interval = setInterval(() => {
console.log('1초마다 실행');
}, 1000);
const timeout2 = setTimeout(() => {
console.log('실행되지 않습니다');
}, 3000);
setTimeout(() => {
clearTimeout(timeout2);
clearInterval(interval);
}, 2500);
const immediate = setImmediate(() => {
console.log('즉시 실행');
});
const immediate2 = setImmediate(() => {
console.log('실행되지 않습니다');
});
clearImmediate(immediate2);
※ setImmediate(콜백)과 setImeout(콜백, 0)의 차이
둘다 이벤트 루프를 거친 뒤 즉시 실행되지만 특수한 경우에 setImmediate는 setTimeout(콜백, 0)보다 먼저 실행. 파일 시스템 접근, 네트워킹 같은 I/O 작업의 콜백 함수 안에서 타이머를 호출하는 경우. 하지만 setImmediate가 항상 setTimeout(콜백, 0)보다 먼저 호출되지는 않다. 보통 setTimeout(콜백, 0)은 사용하지 않는것을 권장.
4) __filename, __dirname
노드는 __filename, __dirname이라는 키워드로 경로에 대한 정보를 제공. 파일에 이 2가지를 넣어두면 실행 시 현재 파일명과 현재 파일경로로 바뀜.
5) moduel, exports, require
module말고도 다른 객체로도 모듈을 만들어서 사용 할 수가 있음.
6) process
process 객체는 현재 실행되고 있는 노드 프로세스에 대한 정보를 담고 있음. 사용빈도는 많지않지만 보통 운영체제나 실행환경별로 다른 동작을 하고싶을때 사용.
※ 중요한 녀석들
[1] process.env
process.env를 입력 할 경우 많은 정보가 출력됨. 이 정보들은 시스템의 환경변수이다. 서비스의 중요한 키를 저장하는 공간으로도 사용되며, 서버나 데이터베이스의 비밀번호와 각종 API키를 코드에 직접 입력하는것은 위험. 따라서 중요한 정보는 process.env속성으로 대체 가능하다
ex)
const secretId = process.env.SECRET_ID;
const secretCode = process.env.SECRET_CODE;
process.env에 secretId와 secretCode를 넣으면 사용 가능!
[2] process.nextTick
이벤트 루프가 다른 콜백 함수들보다 nextTick의 콜백 함수를 우선으로 처리하도록 만듬. setImmedate나 setTimeout보다 먼저 실행된다.
setImmediate(() => {
console.log('immediate');
});
proccess.nextTick(() => {
console.log('nextTick');
});
setTimeout(() => {
console.log('timeout');
}, 0);
Promise.resolve().then(() => console.log('promise'));
맨 아래에 Promise를 넣은것은 resolve된 Promise도 nextTick처럼 다른 콜백들보다 우선시되기 때문.
[3] process.exit()
실행중인 노드 프로세스를 종료시키는 녀석. 서버 환경에서 이 함수를 사용하면 서버가 멈추기 때문에 특수한 경우를 제외하곤 서버에서 잘 사용하지 않는다. 주로 서버 외의 독립적인 프로그램에서는 수동으로 노드를 멈추기 위해 사용.
5. 노드 내장 모듈 사용하기
1) os모듈
: 웹 브라우저에서 사용되는 자바스크립트는 운영체제의 정보를 가져올 수 없지만, 노드는 OS모듈에 정보가 담겨 있어 정보를 가져올 수 있음.
- os.arch() : process.arch와 동일
- os.platform() : process.platform과 동일
- os.type() : 운영체제의 종류를 보여줌
- os.uptime() : 운영체제 부팅 이후 흐른 시간(초)를 보여줌. process.uptime()은 노드의 실행시간
- os.hostname() : 컴퓨터의 이름을 보여줌
- os.release() : 운영체제의 버전을 보여줌
- os.homedir() : 홈 디렉터리 경로를 보여줌
- os.tmpdir() : 임시 파일 저장 경로를 보여줌
- os.cpus() : 컴퓨터의 코어 정보를 보여줌
- os.freemem() : 사용 가능한 메모리(RAM)를 보여줌
- os.totalmem() : 전체 메모리 용량을 보여줌
2) path
: 폴더와 파일의 경로를 쉽게 조작하도록 도와주는 모듈. 운영체제마다 경로 구분자가 다르다. 크게 윈도와 POSIX타입으로 구별되며, POSIX는 유닉스 기반의 운영체제들을 의미한다. ex) 맥과 리눅스.
• 윈도 : C:\User\로춘남\로 구분
• POSIX : /home/로춘남/로 구분
- path.sep : 경로의 구분자. 윈도는 \, POSIX는 /
- path.delimiter : 환경 변수의 구분자. process.env.PATH를 입력하면 여러 개의 경로가 이 구분자로 구분. 윈도는 세미콜론(;), POSIX는 콜론(:)
- path.dirname(경로) : 파일이 위치한 폴더 경로를 보여줌
- path.extname(경로) : 파일의 확장자를 보여줌
- path.basename(경로, 확장자) : 파일의 이름(확장자 포함)을 표시. 파일의 이름만 표시하고싶으면 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()과 비슷하지만 차이가 있음.
3) url
인터넷 주소를 쉽게 조작하도록 도와주는 모듈.
- url.parse(주소) : 주소를 분해. WHATWG 방식과 비교하면 username과 password 대신 auth 속성이 있고, searchParams 대신 query가 있음.
- url.format(객체) : WHATWG 방식 url과 기존 노드의 url을 모두 사용 할 수 있음. 분해되었던 url 객체를 다시 원래 상태로 조립.
const { URL } = require('url');
const myURL = new URL('http://www.gilbut.co.kr/?page=3&limit=10&category=nodejs&category=javascript');
console.log('searchParams:', myURL.searchParams);
console.log('searchParams.getAll():', myURL.searchParams.getAll('category'));
console.log('searchParams.get():', myURL.searchParams.get('limit'));
console.log('searchParams.has():', myURL.searchParams.has('page'));
console.log('searchParams.keys():', myURL.searchParams.keys());
console.log('searchParams.values():', myURL.searchParams.values());
myURL.searchParams.append('filter', 'es3');
myURL.searchParams.append('filter', 'es5');
console.log(myURL.searchParams.getAll('filter'));
myURL.searchParams.set('filter', 'es6');
console.log(myURL.searchParams.getAll('filter'));
myURL.searchParams.delete('filter');
console.log(myURL.searchParams.getAll('filter'));
console.log('searchParams.toString():', myURL.searchParams.toString());
myURL.search = myURL.searchParams.toString();
- getAll(키) : 키에 해당하는 모든 값들을 가져옴.
- get(키) : 키에 해당하는 첫 번째 값만 가져옴
- has(키) : 해당 키가 있는지 없는지 검사
- keys() : searchParams의 모든 키를 반복기(iterator)(ES2015 문법) 객체로 가져옴
- values() : searchParams의 모든 값을 반복기 객체로 가져옴.
- append(키, 값) : 해당 키를 추가. 같은 키의 값이 있다면 유지하고 하나 더 추가
- set(키, 값) : append와 비슷하지만, 같은 키의 값들을 지우고 새로 추가
- delete(키) : 해당 키 제거
- toString() : 조작한 searchParams 객체를 다시 문자열로 만듬. 이 문자열을 search에 대입하면 주소 객체에 반영
4) querystring
: WHATWG 방식의 url 대신 기존 노드의 url을 사용할 때, search 부분을 사용하기 쉽게 객체로 만드는 모듈
- querystring.parse(쿼리) : url의 query 부분을 자바스크립트 객체로 분해
- querystring.stringify(객체) : 분해된 query 객체를 문자열로 다시 조림.
5) crypto
다양한 방식의 암호화를 도와주는 모듈.
• 단방향 암호화 : 복호화할 수 없는 암호화 방식. 복호화는 암호화된 문자열을 원래 문자열로 되돌려놓는것을 의미.
- createHash(알고리즘) : 사용할 해시 알고리즘을 넣음. md5, sha1, sha256, sha512 등이 가능하지만 md5와 sha1은 취약점이 있음. 현재는 sha512로도 충분하지만 이것마져 취약해지면 더 강력한 알고리즘이 필요함.
- update(문자열) : 변환할 문자열을 넣음
- disgest(인코딩) : 인코딩할 알고리즘을 넣음. base64, hex, latin1이 주로 사용. 주로 base64가 애용됨.
• 양방향 암호화 : 대칭형 암호화라고 부르며, 암호화된 문자열을 복호화 할 수 있고, 키를 사용함. 대칭형 암호화에서 암호를 복호화하려면 사용한 키와 같은 키를 사용해야함.
- crypto.createCipheriv(알고리즘, 키, iv) : 암호화 알고리즘과 키, iv를 넣는다.
- cipher.update(문자열, 인코딩, 출력 인코딩) : 암호화할 대상과 대상의 인코딩. 출력 결과물의 인코딩을 넣는다.
- cipher.final(출력 인코딩) : 출력 결과물의 인코딩을 넣으면 옴화화가 완료됨
- crypto.createDecipheriv(알고리즘, 키, iv) : 복호화 할 때 사용. 암호화 할 때 사용했던 알고리즘과 키, iv를 그대로 넣어야함.
- decipher.update(문자열, 인코딩, 출력 인코딩) : 암호화된 문장. 그 문장의 인코딩, 복호화할 인코딩을 넣음.
- decipher.final(출력 인코딩) : 복호화 결과물의 인코딩을 넣음.
6) util
각종 편의 기능을 모아둔 모듈.
- util.deprecate : 함수가 deprecated 처리되었음을 알림. '중요도가 떨어져 더 이상 사용되지 않고 앞으로 사라지게 될 것'
- util.promisify : 콜백 패턴을 프로미스 패턴으로 바꿈.
7) worker_threads
노드에서 멀티 스레드 방식으로 작업하는 방법.
8) child_process
다른 프로그램을 실행하고 싶거나 명령어를 수행하고 싶을 때 사용하는 모듈.
9) 그외 기타 모듈
- assert : 값을 비교하여 프로그램이 제대로 동작하는지 테스트하는데 사용
- dns : 도메인 이름에 대한 IP주소를 얻어내는 데 사용
- net : HTTP보다 로우 레벨인 TCP나 IPC 통신을 할 때 사용
- string_decoder : 버퍼 데이터를 문자열로 바꾸는 데 사용
- tls : TLS와 SSL에 관련된 작업을 할 때 사용
- tty : 터미널과 관련된 작업을 할 때 사용
- dgram : UDP와 관련된 작업을 할 때 사용
- v8 : V8 엔진에 직접 접근 할 때 사용
- vm : 가상 머신에 직접 접근 할 때 사용
6. 파일 시스템 접근하기
fs 모듈은 파일 시스템에 접근하는 모듈. 파일을 생성하거나 삭제하고, 읽거나 쓸 수 있음. 폴더도 만들거나 지울 수 있음.
• 동기 메서드와 비동기 메서드
- 동기와 비동기 : 백그라운드 작업 완료 확인 여부
- 블로킹과 논 블로킹 : 함수가 바로 return 되는지 여부
- 동기 - 블로킹 방식 : 백그라운드 작업 완료 여부를 계속 확인하며, 호출한 함수가 바로 return 되지 않고 백그라운드 작업이 끝나야 return 됨.
- 비동기 - 논 블로킹 방식 : 호출한 함수가 바로 return 되어 다음 작업으로 넘어가며, 백그라운드 작업 완료 여부는 신경 쓰지 않고 나중에 백그라운드가 알림을 줄 때 비로소 처리
※ 동기- 논블로킹이나 비동기-블로킹은 없다고 봐도 무방
• 버퍼와 스트림 이해하기
- from(문자열) : 문자열을 버퍼로 바꿀 수 있음.
- toString(버퍼) : 버퍼를 다시 문자열로 바꿈
- concat(배열) : 배열 안에 든 버퍼들을 하나로 합침
- alloc(바이트) : 빈 버퍼를 생성
→ 서버처럼 몇 명이 이용할지 모르는 환경에서 버퍼는 메모리 문제를 발생시킬 수 있다. 또한 모든 버퍼를 다 쓴 이후에 다음 동작으로 넘어가기 때문에 파일 읽기, 압추그 파일 쓰기 등의 조작을 연달아 할 때 매번 전체 용량을 버퍼로 처리해야 다음단계로 넘어감. 그래서 버퍼의 크기를 작게 만든 후 여러 번으로 나눠 보내는 방식이 등장했는데, 이것이 바로 스트림이다.
• 기타 메서드
- fs.access(경로, 옵션, 콜백) : 폴더나 파일에 접근 할 수 있는지를 체크
- fs.mkdir(경로, 콜백) : 폴더를 만드는 메서드
- fs.open(경로, 옵션, 콜백) : 파일의 아이디(fd 변수)를 가져오는 메서드. 파일이 없다면 파일을 생성한 뒤 그 아이디를 가져옴.
- fs.rename(기존 경로, 새 경로, 콜백) : 파일의 이름을 바꾸는 메서드.
- fs.readdir(경로, 콜백) : 폴더 안의 내용물을 확인 할 수 있음.
- fs.unlink(경로, 콜백) : 파일을 지울 수 있음. 파일이 없다면 에러가 발생한다.
- fs.rmdir(경로, 콜백) : 폴더를 지울 수 있음. 폴더 안에 파일들이 있다면 에러가 발생. 먼저 파일을 모두 지워야함.
7. 이벤트 이해하기
- on(이벤트명, 콜백) : 이벤트 이름과 이벤트 발생 시의 콜백을 연결. 이렇게 연결하는 동작을 이벤트 리스닝이라고 한다.
- addListener(이벤트명, 콜백) : on과 기능이 같음
- emit(이벤트명) : 이벤트를 호출하는 메서드. 이벤트 이름을 인수로 넣으면 미리 등록해뒀던 이벤트 콜백이 실행
- once(이벤트명, 콜백) : 한 번만 실행되는 이벤트.
- removeAllListeners(이벤트명) : 이벤트에 연결된 모든 이벤트 리스너를 제거
- removeListener(이벤트명, 리스너) : 이벤트에 연결된 리스너를 하나씩 제거.
- off(이벤트명, 콜백) : 노드 10버젼에 추가된 메서드로, removeListener와 기능이 같다.
- listenerCount(이벤트명) : 현재 리스너가 몇 개 연결되어 있는지 확인.
8. 예외 처리하기
//1.
setInterval(() => {
console.log('시작');
try {
throw new Error ('서버를 고장내주마!');
} catch (err) {
console.error(err);
}
}, 1000);
//2.
const fs = require('fs');
setInterval(() => {
fs.unlink('./abcdefg.js', (err) => {
if (err) {
console.error(err);
}
});
}, 1000);
//3.
const fs = require('fs').promises;
setInterval(() => {
fs.unlink('./abcdefg.js')
}, 1000);
//4.
process.on('uncaughtException', (err) => {
console.error('예기치 못한 에러', err);
});
setInterval(() => {
throw new Error('서버를 고장내주마!');
}, 1000);
setTimeout(() => {
console.log('실행됩니다');
}, 2000);
프로세스가 멈추지 않게 에러를 잡기 예제.
• 그외 자주 발생되는 에러
- node : command not found : 환경변수가 제대로 설정되지 않았을때. 환경변수에는 노드가 설치된 경로가 포함되어 있어야한다.
- 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 : 코드를 실행할 때 메모리가 부족하여 스크립트가 정상 작동하지 않는 경우.
- UnhandledPromiseRejectionWarning: Unhandled promise rejection : 프로미스 사용시 catch 메서드를 붙이지 않으면 발생. → 항상 catch를 붙이는거 추천
- EADDRINUSE 포트 번호 : 해당 포트 번호에 이미 다른 프로세스가 연결되어 있음.
- EACCES 또는 EPERM : 노드가 작업을 수행하는 데 권한이 충분하지 않음. → 파일/폴더 수정, 삭제, 생성 권한을 확인해보는것이 좋음.
- EJSONPARSE : package.json 등의 JSON 파일에 문법 오류가 있을 때 발생.
- ECONNREFUSED : 요청을 보냈으나 연결이 성립하지 않을 때 발생. → 요청을 받는 서버의 주소가 올바른지, 꺼져 있지는 않은지 확인해볼 것
- ETARGET : package.json에 기록한 패키지 버전이 존재하지 않을 때 발생.
- ETIMEOUT : 요청을 보냈으나 응답이 일정 시간 내에 오지 않을 때 발생.
- ENOENT : no such file of directory : 지정한 폴더나 파일이 존재하지 않는 경우.
참 조 : Node.js 교과서(개정 2판)
'node.js' 카테고리의 다른 글
[Node.js] #6 익스프레스 웹 서버 만들기 (0) | 2021.11.15 |
---|---|
[Node.js] #5 패키지매니저 (0) | 2021.11.15 |
[Node.js] #4 http 모듈로 서버 만들기(2) (0) | 2021.11.12 |
[Node.js] #3 http 모듈로 서버 만들기(1) (0) | 2021.11.12 |
[Node.js] #1 node.js 첫 시작. 노드(node.js) 란? (1) | 2021.11.11 |