Notice
Recent Posts
Recent Comments
Link
소파에서 개발하기
[SEB TIL] 26일차 Node.js 모듈 + promise + async 실습 본문
과제 Part 1 - 타이머 API
1) 타이머 관련 API
- setTimeout(callback, millisecond) : 일정 시간 후에 함수를 실행
- 매개변수 : 실행할 콜백 함수, 실행 전 기다릴 시간(0.001초)
- return값 : 임의의 타이머 ID
setTimeout(function () { console.log('1초 후 실행'); }, 1000); // 123
- clearTimeout(timerId) : setTimeout 타이머를 종료
- 매개변수 : 타이머 ID
- return값 : 없음
const timer = setTimeout(function () { console.log('10초 후 실행'); }, 10000); //timer에 id를 반환받아 저장 clearTimeout(timer); // setTimeout이 종료됨
- setInterval(callback, millisecond) : 일정 시간의 간격을 가지고 함수를 반복적으로 실행
- 매개변수 : 콜백함수, 반복적으로 실행시킬 시간 간격(0.001초)
- return값 : 임의의 타이머 ID
- clearInterval(timerId) : setInterval 타이머를 종료
- 매개변수 : 타이머 ID
- return값 : 없음
- **bind() 는 뭘까?
- 기본적으로는 setTimeout에 어떤 객체 arr 안에 있던 메서드를 전달할 때 this가 맥락을 잃어 arr → window로 바뀌어 현상을 막고 this를 arr로 고정하는 메서드
- sleep.bind(null, 2000) ⇒ 여기서는 bind 메서드를 이용해서 sleep에 전달하는 인자 값을 2000으로 고정(참고)
2) Promise 이해하기
참고 : https://ko.javascript.info/promise-basics
- promise 인스턴스의 맥락
- '제작 코드(producing code)'
- promise에 전달되는 함수(callback)
- 시간이 걸리는 일을 하는 함수
- 시간이 얼마나 걸리든 성공/실패 중 1가지의 결과를 만들어 냄.
- '소비 코드(consuming code)'
- '제작 코드’의 결과를 기다렸다가 이를 소비하는 함수
- (메소드 쓰듯) 여러 개가 줄줄이 사용될 수 있음.
- 프라미스(promise)
- '제작 코드’와 '소비 코드’를 연결해 주는 특별한 자바스크립트 객체
- ‘제작 코드’가 준비되었을 때 그 결과를 소비 코드가 사용할 수 있도록 전달함.
- '제작 코드(producing code)'
- promise 인스턴스 만들기
let promise = new Promise(function(resolve, reject) { // executor (resolve와 reject를 인수로 받는 제작 코드) });
- Promise 클래스에 executor(제작 함수)를 전달해 만듦
- new Promise 시 생성자가 반환하는(상속하는) promise 객체의 내부 프로퍼티 : state, result
- executor는 new Promise가 만들어질 때 즉각적으로 호출됨 ⇒ 그게 싫다면 실행 조건을 만들거나 setTimeout의 callback으로 주기
- executor의 인수 resolve와 reject
- 자바스크립트에서 자체 제공하는 콜백이므로 직접 작성하지 않아도 됨
- 인수로 넘겨준 콜백 중 하나를 반드시 호출
- Promise 클래스에 executor(제작 함수)를 전달해 만듦
- resolve, reject 의미
- promise의 executor 함수가 성공했을 때 실행할 callback / 실패했을 때 실행할 callback
- resolve(’hello’)
: 성공했을 때 [PromiseState] 값을 "pending" → "fulfilled"로, [PromiseResult] 값을 undefined → "hello"로 바꿈 - reject(error)
: 실패했을 때 [PromiseState] 값을 "pending" → "failed"로, [PromiseResult] 값을 undefined → error로 바꿈
- resolve(’hello’)
- status: "rejected"인 것을 .catch()로 잡으면 status:"fulfilled"로 바뀌어 더 이상 오류가 나지 않음
<= 어떤 경로든 state 속성값이 한 번 fulfilled로 바뀌면 더 이상 변경되지 않음 - PromiseResult 속성값을 전달인자로 활용 : .then( [성공result], [실패result] ) 메서드의 callback의 인자로 전달됨
** promise와 then이 value를 리턴하는 게 아니라, 속성값이 value로 바뀐 promise 객체를 리턴하면 뒤이은 메서드에서 이 속성값을 받아다 쓰는 것!
- promise의 executor 함수가 성공했을 때 실행할 callback / 실패했을 때 실행할 callback
- promise 인스턴스의 메서드
- .then( resultFunc, errorFunc )
promise.then( function(result) { /* 결과(result)를 다룹니다 */ }, function(error) { /* 에러(error)를 다룹니다 */ } );
- 첫 번째 인자로 resolve 시 전달된 value를 처리할 함수를, 두 번째 인자 자리에 rejected 시 전달될 error를 처리할 함수를 받는다
- resolve만 있고 rejected는 없는 경우 : then( resultFunc )만 적어도 된다
- rejected만 있고 resolve는 없는 경우 : then( null, errorFunc )과 같이 첫 번째 자리를 채우고 두번째 자리에 errorFunc값을 전달해야 한다
- .catch(error)
- 체인을 진행하다가 에러가 발생하면 발생지점과 catch 사이의 then은 건너뛰고 catch로 가서 에러를 처리
- .then( null, errorFunc )과 동일
- .finally(someFunc)
- 실행 결과가 resolve인지 rejected인지 상관없이 someFunc를 실행 => 그 다음 then이나 catch로 실행 결과를 전달
- .then(someFunc, someFunc)와 비슷
- .then( resultFunc, errorFunc )
- 잊지말자! POINT!
- 배열의 .filter()나 .map() 메소드 등이 실행 후 배열을 리턴하기 때문에 계속 덧붙여 활용할 수 있는 것처럼, promise 객체와 then()도 실행 후 Promise 객체를 리턴하기 때문에 메서드를 chaining할 수 있음 (then은 리졸브된 promise 객체 반환)
- then에서 연산한 결과를 'return'하면 Promise객체의 [[PromiseResult]]에 return 값이 담김. return 안하면 undefined가 됨 => undefined가 리턴되어도 체이닝은 계속 이루어짐
- await로 실행을 기다리지 않고 promise(arg)를 생으로 변수에 할당하면 resolved data를 할당받는 게 아니라 promise 인스턴스를 할당받게 될 것
3) async / await 이해하기
- async 키워드
- await 키워드 다음에 등장하는 함수 실행은 promise를 리턴해야만 의미가 있다!
- function 앞에 async를 붙이면 해당 함수는 항상 promise 인스턴스를 반환
- promise가 아닌 값을 반환하는 함수라도 그걸 resolved promise로 감싸서 반환
async function f() { return 1; } f().then(alert); // 1 (결과가 1인 resolved promise) //명시적으로 프라미스를 반환한 것과 결과가 동일 async function f() { return Promise.resolve(1); }
- await 키워드
// await는 async 함수 안에서만 동작합니다. async function f() { let promise = new Promise((resolve, reject) => { setTimeout(() => resolve("완료!"), 1000) }); let result = await promise; // 프라미스가 이행될 때까지 기다림 (*) alert(result); // "완료!" } f();
- **await는 async가 붙은 함수 안에서만 동작** ⇒ 안 지키면 syntax error
- 자바스크립트는 await 키워드를 만나면 promise가 처리될 때까지 멈춰서 기다림 ⇒ 그 이후에 결과를 반환하고 실행 재개
promise(arg)를 생으로 변수에 할당할 때와 다르게 await promise(arg) 를 할당하면 resolve 결과값이 담김 - promise 처리를 기다리는 동안에 엔진이 다른 일(다른 스크립트 실행, 이벤트 처리 등)을 할 수 있음
- promise.then보다 세련되게 promise의 result 값을 얻을 수 있는 문법
- 주의사항
- await는 최상위 레벨 스코프에서 작동하지 않는다 ⇒ 익명 async 함수로 코드를 감싸면 사용 가능
3 - Node.js
3-1. Node.js 모듈 사용법
- 개발자는 자신이 이해하는 범위만큼 모듈을 사용할 수 있다
- Node.js 내장 모듈 목록 : https://nodejs.org/dist/latest-v16.x/docs/api/
- 모든 모듈은 '모듈을 사용하기 위해 불러오는 과정'이 필요
- 브라우저에서 다른 파일을 불러올 때에는 <script> 태그를 이용
- Node.js 에서는 JavaScript 코드 가장 상단에 require 구문을 이용하여 다른 파일을 불러옴
const fs = require('fs'); // 파일 시스템 모듈을 불러옵니다 const dns = require('dns'); // DNS 모듈을 불러옵니다 // 이제 fs.readFile 메서드 등을 사용할 수 있습니다!
- 3rd-party 모듈 사용하는 법
- 프로그래밍 언어에서 공식적으로 제공하는 빌트인 모듈(built-in module)이 아닌 모든 외부 모듈
- 서드 파티 모듈을 다운로드하기 위해서는 npm을 사용
npm install underscore
- node_modules에 underscore가 설치되면 내장 모듈을 사용하듯 require 구문을 통해 사용할 수 있음
const _ = require('underscore');
3-2. Node.js 공식 문서 가이드
fs.readFile을 통해 알아보기 (fs.readFile의 공식 API 문서)
메서드 fs.readFile 은 비동기적 으로 파일 내용 전체를 읽으며 전달인자를 3개 받음.
fs.readFile(path[, options], callback)
- path \<string> | \<Buffer> | \<URL> | \<integer>
filename or file descriptor
-
- path 자리에는 파일 이름을 받음. 네 가지 타입이 가능하지만 일반적으로 문자열(<string>)을 받음.
//'etc/passwd' 라는 파일을 불러오는 경우 fs.readFile('/etc/passwd', ..., ...)
- path 자리에는 파일 이름을 받음. 네 가지 타입이 가능하지만 일반적으로 문자열(<string>)을 받음.
- options \<Object> | \<string>
If options is a string, then it specifies the encoding:
-
- options는 선택적 전달인자로 문자열 또는 객체 형태. 문자열이면 인코딩 정보.
//options에 객체를 전달한 경우 let options = { encoding: 'utf8', // utf8 인코딩 방식으로 엽니다 flag: 'r' // 읽기 위해 엽니다 } // /etc/passwd 파일을 options를 사용하여 읽습니다. fs.readFile('/etc/passwd', options, ...)
// options에 문자열을 전달한 경우 // /etc/passwd 파일을 'utf8'을 사용하여 읽습니다. fs.readFile('/etc/passwd', 'utf8', ...);
- options는 선택적 전달인자로 문자열 또는 객체 형태. 문자열이면 인코딩 정보.
callback \<Function>
- err \<Error> | \<AggregateError>
- data \<string> | \<Buffer>
If no encoding is specified, then the raw buffer is returned.
- 파일을 읽고 난 후에 비동기적으로 실행될 콜백함수를 전달.
- 콜백 함수에는 두 가지 매개변수 err와 data가 존재. 에러가 발생하지 않으면 err 는 null 이 되며, data(파일의 내용)에 string 또는 Buffer 라는 객체가 전달됨.
- option에서 인코딩이 특정되지 않으면 Buffer가 리턴됨.
- 실습 : JavaScript 파일이 실행되는 디렉토리, 적당한 텍스트 파일(test.txt)을 새롭게 생성해 읽어오기.
fs.readFile('test.txt', 'utf8', (err, data) => {
if (err) {
throw err; // 에러를 던집니다.
}
console.log(data);
});
이것은 nodejs의 fs라는 모듈을 테스트하기 위해 만든 text.txt 파일의 내용이다.
js파일을 만든 뒤 터미널 창에서 node test.js를 입력하면
터미널창에 이 파일의 내용이 뜰 것이다.
과제. Part 2 - fs 모듈과 promise
- fs 모듈 활용하기
- path 읽어오기 오류
- ‘utf-8’ 인코딩을 안해서 데이터를 못 읽어옴
- 파일 경로가 js 파일 기준이 아니라 실행되는 환경 기준으로 적혀야 함
- promise로 표현하기
- 함수에서 promise를 리턴하고 바로 fs.readFIle을 실행해, err가 있을 때 reject(err), 없을 때 resolve(data)를 전달
- path 읽어오기 오류
- promise 생성자
- 체이닝 기초
- chaining 문제
// HINT: getDataFromFilePromise(user1Path) 및 getDataFromFilePromise(user2Path)를 이용해 작성합니다
const readAllUsersChaining = () => {
// TODO: 여러개의 Promise를 then으로 연결하여 작성합니다
return getDataFromFilePromise(user1Path)
//1레벨의 1번째 then
//파라미터로 data1을 받고, return 값은 (2레벨 then에서 리턴한) 배열
.then((data1)=> {
return getDataFromFilePromise(user2Path)
.then((data2) => {
return `[${data1}, ${data2}]`
})
})
//2레벨의 2번째 then
.then((result) => JSON.parse(result))
}
- Promise.all 로 리팩토링
- async / await 로 리팩토링