이벤트기반,싱글쓰레드,non blocking I/O
response 객체는 서버로 웹브라우저나 또는 앱으로 부터 어떤 요청이 있을 때 요청한 사용자 측으로 값을 반환해 줄 때 사용하는 객체다. 반대로 request 객체는 사용자가 요청한 내용이 담겨있는 객체라고 볼 수 있다. 뭐 URL을 치면 서버가 HTML을 요청한다던가... 이런 것들은 보통 callback함수로 지정되어서 이벤트리스너로부터 호출된다.
그렇게 되면 callback함수들은 테스크큐에 담기게 되고 이벤트 루프는 우선순위에 따라 큐에 담겨있는 callback함수를 콜스택으로 옮기면서 실행한다. 이런 걸 event driven이라고 한다.
non blocking I/O
non blocking : 테스크큐로 보내는 동작을 말함. 즉 눈에 보이는 순서가 다를 때
I/O : 파일시스템,네트워크에서 요청하고 받고 하는 걸 말함. 이런 애들은 자체적으로 non blocking I/O 로 돌림.
싱글스레드 : 팔이 하나라서 한번에 한가지 일만 함(js,node.js)
멀티 스레드 : 팔이 여러개라서 한번에 어려가지 일을 함. 파일시스템 I/O에서 주로 사용됨.
왜 그러면 멀티스레드 안함? 어렵거든... 제어권같은 거 조정이 어려움. 그러면 node에서는 어케 멀티스레드 써요? 꼼수를 쓴다. 똑같은 일을 하는 왼팔만 여러개 만드는 식으로...
callback함수라고 해서 무조건 테스크큐에 들어가는 건 아님.
const와 let
ES는 표준, JS는 구현을 맡음
변수를 선언할 때 const와 let을 쓰도록 하고 var는 자제할 것.
그럼 이 차이는 뭐냐?
const와 let은 블록 바깥으로 빠져나갈 수 없음(지역변수)
but var는 블록 바깥으로 나가도 사용가능(전역변수)
const는 한번 값을 할당하면 바꿀 수 없음. 그리고 선언과 동시에 값을 할당해야 함.
let은 값을 바꿀 수 있음.
만약 const가 객체라면 그 객체 자체는 못바꾸지만 그 안의 attribute의 값은 바꿀 수 있음.
예를 들어서 const list 자체는 못바꾸지만 list안의 값은 바꿀 수 있음.
템플릿 문자열(백틱,')
문자와 변수들을 더할 때 보통 +를 쓴다. 파이썬처럼... 근데 사실 좀 뭔가 햇갈릴 수 있다.
그래서 백틱을 쓰는데 작은 따옴표가 약간 꺾여있는 모양이다.
이런 식으로 백틱 안에서 ${} 안에 변수를 넣고 하면 멀쩡하게 잘 돌아간다. 물론 가독성도 당연히 더 좋고 말이다. 그래서 이걸 템플릿 문자열이라고 함. 그리고 만약 안에 따옴표를 쓰고 싶으면 원래는 이스케이핑을 해야하는데 템플릿 문자열에서는 그런 거 안해줘도 된다. 개꿀
객체 리터럴의 변화
중괄호로 표현하는 객체를 객체 리터럴이라고 함.
const obj = {a:1, b:2} 이런식으로
위에가 예전 스타일의 객체 리터럴이라면 ES2018에서는 아래처럼 쓸 것을 권장함. 훨씬 더 간결하다.
그리고 동적 속성 할당을 리터럴 안에 표현이 가능함 {[변수]:값} 그리고 메소드에 :function()을 안붙여도 되도록 만듦.
화살표함수
이 세 함수가 모두 같은 역할을 하는 함수다. 참고로 마지막 함수같은 경우에는 결과값은 중괄호 없이 쓰면 그대로 리턴한다고 보면 됨. 근데 그러면 이 세 함수의차이가 뭐냐?
this!
function을 썼을 때 결과
아 이 좆망언어 이거를 왜 굳이 that으로 받아오나 싶었는데 위에서는 외부의 this와 저 함수 내부에서 작동하는 this가 달라서 받아온 다음에 써야했는데 화살표 함수를 사용할 경우 그럴 필요가 없다... 와 이딴 쓰레기 언어가 다 있네..ㄷㄷ
아 근데 솔직히 익명함수에 대한 언급을 좀 해줬으면 좋겠다. pl할 때 저거때문에 아주 지옥을 맛봤는데 설명이 꽤 부족했다.
비구조화 할당(destructuring)
만약 변수명에 객체의 변수를 꺼내올 때 일일이 다 한줄씩 한다면 어때?
해도 안되는 건 아니지만 좀 번거롭다... 그러면 어떤 식으로 할당을 해야할까?
a,b라는 속성들은 당연히 candyMachine에 없는 속성이니까 없고 getCandy()를 실행시킨 경우에 왜 저렇게 되는지 한번 알아보자. candyMachine.getCandy()를 실행을 하면 5에서 1이 줄어든 4를 return 한다. 근데 왜 그냥 getCandy()를 하면 undefined가 뜰까???? 우선 candyMachine.getCandy()를 호출했을 때에는 getCandy()함수의 this는 candyMachine이 된다. 근데 getCandy는 어떤 걸 찾는 지 모른다. this를 못찾는다는 소리임. 그래서 함수는 destructuring을 하면 떨어져나와 더이상 candyMachine하고 관련이 없게 된다.??? 어 근데 이러면 변수 건드리면 클래스 내 변수에 영향이 가나???? 이거 잘 모르겠다.
여담으로 위에랑 아래랑 똑같은 코드다. 이런 것도 된다. 역시 노근본 언어다. 암튼 객체 리터럴을 destructuring 할때는 {} 을 씌워줘야 한다고 생각하면 된다.
만약에 여기서 왜 bool이라는 변수에 10은 안들어가고 true만 들어가냐! 난 10도 넣고싶다! 하면은 ...을 앞에 써주면 10도 들어가게 된다. 마찬가지로 함수의 파라미터 같은 경우에도 동일하게 사용을 할 수 있다. 이를 rest라고 하는데 이거는 배열으로 취급한다. 원래 함수의 arguments는 배열이 아닌데 이 경우엔 배열이 된다.
객체참조에 대한 내용 : 그냥 y=x를 하면 똑같은 주소를 가지게 되어 하나만 바꿔도 둘 다 바뀌게 된다. 뭐 다른 언어에서도 저렇게 되니까 ㅋㅋㅋㅋ
callback과 promise 비교
일반적인 callback함수의 구성 (만약 db가 있고 한사람을 찾아오는 함수라고 치지)
callback을 쓰는 이유? 이게 비동기적으로(그니까 non blocking) 구조로 작동하기 때문.
만약 뒤에 console.log를 뒤에 쓴다면 그 메시지가 나중에 뜬다. 근데 정말 어려운게 실행순서를 알기가 어렵다.
뭐 예를 들어서 callback이 연달아서 이어지는 경우... 비동기적이기 때문에 다른 쿼리들도 그 callback함수 안에 넣어줘야 하니까 실행 순서가 정말 애매해진다.
이렇게 callback이 깊어지는 걸 callback hell이라고 함... 예전에는 그래서 callback을 변수로 빼서 하는 경우가 많았는데 이런 경우에는 가독성이 너무 안좋았다.
그래서 이걸 어찌 못하나...? Promise등장!
아까 코드처럼 callback 깊이가 길어지지도 않고 에러처리를 마지막에 한번만 해줘도 되서 코드도 훨씬 간결해졌다. 그리고 순서도 위에서 아래로부터 순차적으로 흘러간다.
Promise 만드는 법 :
new Promise를 만든 후, resolve와 reject가 매개변수가 되는 함수를 넣어줌. 안에서 조건을 차례대로 단 후 resolve와 reject의 조건을 만들어줌. 그래서 resolve가 되면 success, reject가 되면 catch로 넘어가게 된다. then과 catch는 내부적으로 promise가 지원해주는 메소드이기 때문에 사용 가능. 여튼 내부적으로 promise를 짤 수 있으면 callback보다는 promise로 많이 쓰는 추세이다.
.then 같은 경우에는 리턴값이 있으면 다음 then으로 넘어간다. promise를 return하면 resolve나 reject되어 넘어간다.
만약 첫번째 then에서 실패했다? 그러면 바로 catch로 넘어간다.
이렇게 then 내부의 함수로직을 then의 파라미터로 받아서 사용할 수도 있다. 가독성은 이게 조금 더 좋은 편이다.
그리고 이런식으로 무조건 성공하거나 실패하는 promise는 저렇게 한줄로 줄일 수 있다. 당연하게도 성공하는 코드에 catch를 쓰거나 실패하는 코드에 then을 쓸 이유가 없다.
이런 식으로 여러가지 프로미스를 돌릴 수도 있다. 단 하나라도 실패한다면 Promise.all()은 catch로 넘어가게 된다. Promise는 결과를 가지고 있는데 아직 표현을 하지 않은 식이라고 생각하면 명쾌하게 정리가 된다. 그리고 then이나 catch를 만났을 때 결과를 가지고 있다. 이게 좋은 점은 원할 때 꺼내쓸 수 있다는 거다. 개꿀
위 코드의 callback에서는 결과 나오면 그 결과를 바로 써야 하는데 promise에서는 그럴 필요 없이 원할 때 그 결과를 쓸 수 있다. 이런 점도 callback보다는 훨씬 유용하게 Promise를 사용할 수 있는 점이다.
async/await
이 흐름에서 하나라도 실패하면 바로 catch로 간다. 근데 그러면 그 뒤에 console.log는 어떻게 되는걸까?
실제로는 console.log가 첫번째 then보다 먼저 호출됨. 결국은 코드 순서대로 호출이 되지는 않는다. 그래서 yield라는 걸 이용해서 순서대로 나오는 것처럼 눈속임을 하다가 async/await가 나옴
이런 식으로 바꾸었더니 코드가 엄청 짧아지고 깔끔해졌다. 이 코드는 그리고 순서대로 돌아가기 때문에 저 사이에 console.log를 넣어주면 된다. 즉 async/await에서는 동기적으로 코드가 돌아가게 된다. 다만 예외처리를 하기 위해서는 try{} catch{} 문으로 따로 감싸줘야 한다. 그리고 async/await을 쓸려면 함수에서 써야하고 async를 써야만 await을 사용할 수 있다.
만약 개별적으로 error 처리를 할려면 try catch를 다 따로 붙여주면 된다. 이건 이거 나름대로 불편한 점이긴 하다.
이 강의자료들은 osam.kr의 node.js 기본부터 프로젝트 실습까지(조현영님) 라는 강의를 토대로 작성하였습니다. 사실 제가 받아들인 대로 쓰는거라서 틀린 내용이 있을 수 있으니 혹시 사실과 다른 부분이 있으면 피드백 해주시면 감사하겠습니다.
node.js를 공부해보자 (4 : express web server 만들기) (0) | 2021.10.08 |
---|---|
node.js를 공부해보자(2:html 모듈로 웹 서버 만들기) (0) | 2021.10.08 |
야구 시뮬레이션 개발일기 (1. 왜 내가 하던 게임은 망겜소리를 듣나) (0) | 2021.09.18 |
시간 날 때 할만한 TOY Project 목록들 (0) | 2020.12.01 |
react-native 개발자료 메모장 (0) | 2020.09.29 |