[TIL] Node.js, fs모듈 과제

ㅜㅜ·2022년 9월 26일
1

Today I learn

목록 보기
23/77
post-thumbnail

Node.js

  • node.js는 비동기 이벤트 기반 javascript 런타임.

  • 모듈 : 어떤 기능을 조립할 수 있는 형태로 만든 부분
    모든 모듈은 모듈을 사용하기 위해 불러오는 과정이 필요한데, node.js는 가장 상단에 require 구문을 이용해 다른 파일을 불러올 수 있음.

	const fs = require('fs')
  • 3rd-party 모듈 : 해당 프로그래밍 언어에서 공식적으로 제공하는 빌트인 모듈이 아닌 모든 외부 모듈들을 말함. 서드 파티 모듈을 다운로드 할 때 npm을 사용함.
	npm install underscore;//터미널에 이렇게 입력해서 설치 후 
	const _ = require('underscore')// require 구문 이용해 사용 가능. 
  • file system 모듈(fs모듈) : pc의 파일을 읽거나 저장하는 등의 일을 할 수 있게 도와줌.




Promise

프로미스의 3가지 상태(처리 과정)

  1. Pending (대기) : 이행하지도 실패하지도 않은 상태로 비동기 처리가 아직 완료되지 않은 상태.
  2. Fulfilled (이행) : 연산을 성공적으로 완료한 상태.
  3. Rejected (실패) : 연산에 실패하거나 오류가 발생한 상태.
let a = new Promise((resolve, reject) => {});

a // "Pending"상태, Promise result는 undefined.

let a = new Promise((resolve, reject) =>{
  resolve("hi")
});

a//"fulfilled"상태, Promise result는 "hi"


let a = new Promise((resolve, reject) =>{
  reject("bye")
});

a//"rejected"상태, Promise result는 "bye"




프로미스에 존재하는 매서드

1. Promise.prototype.catch()
: reject 함수가 실행될 경우 실행되며, reject 함수에 인자로 넘겨준 값을 받음. 거부되었을 때 실행되는 함수가 catch의 인자.
catch를 사용하는 이유는 오류가 떴을 때 오류를 띄우는 화면을 catch 안에서 실행해줄 수 있고, 이런 오류 처리를 하지 않았을 경우 앱이 강제 종료 되어 버리는 경우가 생기기 때문이다.

2. Promise.prototype.then()
: resolve 함수가 실행될 경우 실행됨. resolve 함수에 인자로 넘겨준 값을 받게 됨. 첫번째 인수는 이행될 때 실행되는 함수(실행 결과), 두번째 임수는 거부되었을 때 실행되는 함수(에러). 두번째 인수 생략 가능.

3. Promise.prototype.finally()
: 프로미스의 이행과 거부 여부에 상관 없이 처리가 되었다면 항상 호출되는 콜백을 추가하고, 이행한 값 그대로 이행하는 새로운 프로미스 반환함.


=> 위 매서드들은 프로미스를 반환하며 then 마지막에 return 해준 값은 Promise result로 들어간다. (Promise result 값은 아래와 같이 콘솔창에서 확인 가능함)

a.then()// fulfilled 상태 + Promise result는 'hi'가 나옴. 

a.then(()=>{return "code"}).then((el) => {return el + "states"}) 
//매개변수로 code를 받고, el이 code를 받는다. 
//fulfilled 상태
//Promise result : 'codestates'
let b = new Promise((resolve,reject) => {
  resoleve(1)
})

b//fulfilled상태, Promise result는 1

b.then((num)=> num+1).then((num)=> num+1).then((num)=>num+1)
//첫 then에서 Promise result는 2이고, 이 2가 두번째 then의 num값이 되어 두번째 then P.r 값은 3
//마지막 then의 Promise result 값은 4가 된다. 

화살표 함수 외에도 then 안에 콜백 함수가 들어갈 수 있는데, 이때 콜백 '함수'가 들어가야한다. (함수가 실행된 값이 들어가는 게 아님)

funciton plus10(num){
  return num + 10
}


b.then(plus10)//ok, Promise result는 11 
~~b.then(plus10())//no ~~



+@ resolve나 reject로 넘어가는 조건은 조건문을 사용해 지정해줄 수 있다.

+@@ resolve, reject라고 꼭 쓰지 않아도 되고, 앞에 있는 게 fulfilled 상태, 뒤에 있는 게 rejected 상태일 때로 각각 사용할 수 있다. 다만 코드는 여러 사람들이 같이 보기 때문에 약속된/예상 가능한 형태로 써주는 것이 좋다.





file system module 과제

fs에서 사용하는 다양한 매서드들이 있지만 이번 과제에서 중점적으로 사용한 매서드는 fs.readFIle이라는 매서드로 비동기적으로 파일 내용 전체를 읽는다.

fs.readFile공식 API문서

Buffer 이해에 참고한 블로그

😀 part 1.

과제의 첫번째 파트에서는 callback을 사용해 비동기를 동기적으로 처리한 함수 delay와 promise를 사용해 처리한 sleep이라는 함수의 쓰임을 비교해보는 것이었다.
(사실 part1은 과제라기 보다는 그냥 실습이어서 어려울 건 없었다.)

실습을 해보는 과정에서 bind라는 매서드를 만나게 되었다.

bind() 메소드가 호출되면 새로운 함수를 생성합니다. 받게되는 첫 인자의 value로는 this 키워드를 설정하고, 이어지는 인자들은 바인드된 함수의 인수에 제공됩니다.

반환값 : 지정한 this 값 및 초기 인수를 사용하여 변경한 원본 함수의 복제본.

MDN의 설명에 의하면 이런 기능을 하는 매서드이다.
bind에 대한 내용을 좀 더 공부해서 정리해본다.

.bind(this키워드 설정, 바인드된 함수의 인수에 제공될 인자)

<바인딩 된 함수 생성>

객체로 부터 매서드를 추출한 뒤, 그 함수를 호출할 때 원본 객체가 그 함수의 this로 사용될 것이라고 기대할 수 있지만, 특별한 조치가 없으면 원본 객체는 손실된다.
그래서 원본 객체가 바인딩 된 함수를 생성하면 이런 문제를 해결할 수 있다.

아래 예시에서는 module이라는 객체에서 getX의 값인 함수 속의 this는 원래 module을 뜻하는 것이었기 때문에 module.getX()는 81이 반환되지만,
module.getX만을 따로 할당한 retrieveX에서는 this가 전역 스코프에서 호출되기 때문에 retrieveX()에서는 9가 반환된다.

이런 간극을 없애기 위해서 bind 매서드를 사용해서 원본 객체와 바인딩된 함수 boundGetX를 만들어주면 값이 달라지지 않는다!

this.x = 9;
var module = {
  x: 81,
  getX: function() { return this.x; }
};

module.getX(); // 81

var retrieveX = module.getX;
retrieveX();
// 9 반환 - 함수가 전역 스코프에서 호출됐음

// module과 바인딩된 'this'가 있는 새로운 함수 생성
// 신입 프로그래머는 전역 변수 x와
// module의 속성 x를 혼동할 수 있음
var boundGetX = retrieveX.bind(module);
boundGetX(); // 81


<부분 적용 함수>

그리고 아래 예시에서 볼 수 있듯이 bind는 원본 함수에 특정 인자를 전달 받은 채인 새로운 함수를 반환할 수도 있다.
addThirtySeven은 addArguments의 첫번째 인자로 37을 전달 받은 상태인 새로운 함수이다. 그래서 addThirtySeven에 5를 전달인자로 주게 되면 37 + 5인 42가 반환된다.

    function addArguments(arg1, arg2) {
        return arg1 + arg2
    }

    var result1 = addArguments(1, 2); // 3

    // 첫 번째 인수를 지정하여 함수를 생성합니다.
    var addThirtySeven = addArguments.bind(null, 37);

    var result2 = addThirtySeven(5); // 37 + 5 = 42

    // 두 번째 인수는 무시됩니다.
    var result3 = addThirtySeven(5, 10); // 37 + 5 = 42




🤢 part 2.

두번째 과제는 실제로 callback, promise, promise.al, async await와 fs module을 사용해 특정 데이터를 받아오는 함수를 완성해야했다. 어제는 겨우 개념만 이해한 정도였기 때문에 나는 part2부터 급속도로 낡고 지쳐가기 시작했다...🤯

문제를 풀면서 막혔던 부분, 새롭게 알게된 부분만 정리해보도록 하겠다.


< callback >

테스트 통과 조건
1. 파일을 읽고 나서 callback이 실행되어야 함
2. 에러가 발생할 경우, callback 첫번째 인자에 에러 객체가 전달되어야 함
3. callback 두번째 인자에 파일 내용이 전달되어야 함

  • 메서드 fs.readFile 은 비동기적으로 파일 내용 전체를 읽고 파일에 저장된 내용/데이터를 반환하거나 오류가 있으면 반환한다.

  • fs.readFile(파일 이름(경로), 인코딩(기본값 utf8), callback)

  • 세번째 인자인 콜백 함수는 파일을 읽고 난 후에 비동기적으로 실행되는 함수이다. 콜백 함수에는 두 가지 매개변수가 필요한데 err와 data이다.

  • 에러가 발생하지 않으면 err 는 null 이 되며, data 에는 문자열이나 Buffer 라는 객체가 전달된다. (data 는 readFile이 읽은 파일의 내용을 말한다.)

If no encoding is specified, then the raw buffer is returned.
If options is a string, then it specifies the encoding:

  • 위 인용에서 알 수 있듯이 문자열을 얻기 위해서는 옵션에 utf-8을 지정해주어야 버퍼가 아닌 문자열을 얻을 수 있었다.
    (해당 내용을 제대로 알지 못해서 한참 헤맸다 🫠)

  • 콜백 함수 내부에는 조건문을 만들어서 err에 전달인자가 전달된 경우를 기준으로 두 가지 경우를 만들었다.
    err가 전달될 때는 콜백함수의 첫번째 인자(에러 객체가 전달 될 곳)에 err가 담기고, 두번째 data가 들어올 자리에는 null을 담았다.
    err가 전달되지 않을 경우에는 콜백함수의 첫번째 인자에는 null, 두번째 인자에는 data가 담기도록 했다. (아래 그림 usage 참고)

테스트 코드를 보고 2,3번이 무슨 의미인지 해석하지 못해서 오래 걸리기도 한 것 같다.


< promise, promise chaining, promise.all >

  • 프로미스 체이닝을 하면서 then 속의 함수를 적어줄 때 화살표 함수(화살표 함수는 항상 익명 함수)로 처리해줄 때가 많았다.
    화살표 함수는 매개변수가 하나일 때는 괄호를 생략해줄 수 있고, 화살표 함수의 유일한 문장이 'return'일 때 'return'과 중괄호({})를 생략해줄 수 있었다.
  • promise.all을 사용하면 여러 개의 프로미스를 한 번에 처리할 수 있다.
    다만 주어진 프로미스들 중 어느 하나라도 거부하면 자신(promise.all)도 거부하고, catch문을 실행한다.

  • part 3을 풀 때 promise.all 뒤에 then을 여러개 붙여서 사용해보려고 했는데 잘 안 되어서 promise.all은 then을 여러 번 사용할 수 없나? 하는 의문이 들었는데, 질문을 해보니 그건 아닌 것 같았다. 그저 내가 코드를 이상하게 짜서 안 돌아갔던 것 같다..^^...


< async await >

  • async await를 남발하면 안 되는 이유 : 병렬적으로 실행할 수 없게 되기 때문에
async function foo(){
  let a = await new Promise(resolve=>
 setTimeout(()=>resolve(1),3000))
  console.log("hello")
  let b = await new Promise(resolve=>
 setTimeout(()=>resolve(2),2000))
  console.log("code")
  let c = await new Promise(resolve=>
 setTimeout(()=>resolve(3),1000))
  console.log("states")
  
  console.log("foo",[a,b,c])
}

foo()
//콘솔에 hello, code, states, foo, [1,2,3]이 나올 때까지 6초가 걸리는데 
//사실 a,b,c는 서로 연관되어 있지 않기 때문에 병렬적으로 처리할 수도 있다.
//병렬적으로 처리하면 3초 밖에 걸리지 않음. 
//동기적으로 일하는 카페가 되어버리지 않도록 주의! 
async function bar(){
  let a = new Promise(resolve=>setTimeout(()=>resolve(1),3000));
  let b = new Promise(resolve=>setTimeout(()=>resolve(2),2000));
  let c = new Promise(resolve=>setTimeout(()=>resolve(3),1000));
  let d = await Promise.all([a,b,c])
  
  console.log("bar",d)}
  
  bar()//bar, [1,2,3]
  //이렇게 만들면 6초가 아닌 3초만에 결과가 나온다. 




😶 part 3.

이상하게도 part3이 part2를 풀 때보다 더 쉬웠다면...?
part2에서 그나마 callback, promise, async await를 사용해보고 나니 part3은 part2에서 풀었던 것을 토대로 풀 수 있었다.

  • JSON.parse()와 json()매서드의 차이점

json() is asynchronous and returns a Promise object that resolves to a JavaScript object. JSON. parse() is synchronous can parse a string to (a) JavaScript object(s).stack overflow

json 매서드는 비동기적이고 프로미스 객체를 반환하는데, JSON.parse()는 동기적이고 문자열을 자바스크립트 객체로 반환한다.

JSON() is used with fetch, whereas JSON.parse is used with AJAX.

json()매서드는 fetch에서 사용되고, JSON.parse는 ajax와 함께 사용된다.


  • 이렇게 데이터들을 주고 받을 때에도 여러 형식으로 주고 받을 수 있기 때문에 현업에서 프로젝트를 할 때 백엔드와 서버에서 어떤 형식으로 데이터를 받아올 것인지 등도 잘 이야기 해야한다고 한다.
profile
다시 일어나는 중

0개의 댓글