본문 바로가기

Coding/내일배움캠프

[내일배움캠프] Node.js 4기 TIL | Day 21 | 24.01.18.(목)

// 콜백 함수

setTimeout(function(){
   console.log("hello");
}, 1000);


const numbers = [1, 2, 3, 4, 5];

numbers.forEach(function(number){
   console.log(number);
});

콜백함수의 여러 가지 특징 ..

콜백 지옥 .. 

동기 vs 비동기의 개념

비동기적인 것을 동기적으로 보이게끔 만드는 것도 필요하다 ..



(1) 콜백함수란

- 1. 다른 코드의 인자로 넘겨주는 함수 .. 콜백함수를 넘겨받는 코드도 있겠다 .. forEach, setTimeout 등등 ..
- ☆ 2. 콜백함수를 넘겨받은 위와 같은 코드 forEach, setTimeout 등은 이 콜백 함수를 필요에 따라 적절한 시점에 실행하게 된다. ("제어권"이 그들에게 있는 거죠)

ex)

수시로 시간을 구하는 함수를 직접 호출 (제어권 : 스펀지밥)

-  시간을 구하는 함수 : 눈 뜨고 → 일어나고 → 시계 보고 → 아직 안 됐네? → 다시 눕고 → 눈 감고 → 잠들고

현명한 알람을 맞춘 스밥

1. 알람시계 세팅 ~ 꿀잠
2. 6시에 알람시계 듣고 ~ 
3. 시계의 알람을 설정하는 함수(알람시계)를 호출했고, 그 함수(알람시계)가 '알아서' 스펀지밥이 정해놓은 시간이 됐을 때 ' 알람이 울리는 결과'를 반환했어요 (제어권 : 함수 즉, 알람시계) → action에 대한 제어권은 함수에게 있었다 .. 
4. ☆ callback = call(부르다) + back(되돌아오다) = 되돌아와서 호출해줘!


다시 말하면, 제어권을 넘겨줄테니 너가 알고 있는 그 로직으로 처리해줘!

5. 콜백함수는 다른 코드(함수 또는 메서드)에게 "인자로 넘겨줌"으로써 그

 제어권도 함께 "위임한 함수". 콜백함수를 위임받은 코드는 "자체적"으로 "내부 로직"에 의해 이 콜백 함수를 적절한 시점에 실행

-------------

무슨 제어권?

1. 호출 시점에 대한 제어권을 갖는다.

setInterval()

- setInterval() 메서드는 각 호출 사이에 고정된 시간 지연으로 함수를 반복적으로 호출하거나 코드 스니펫을 실행합니다.

setInterval : 반복해서 매개변수로 받은 콜백함수의 로직을 수행

var count = 0;

var timer = setInterval(function() {
   console.log(count);
   if (++count > 4) clearInterval(timer);
}, 300);



setInterval의 반환값이 timer!

timer를 .. 고유값을 없애줘 .. 


---

var count = 0;
var cbFunc = function () {
   console.log(count);
   if (++count > 4) clearInterval(timer);
};

cbFunc();


var timer = setInterval(cbFunc, 300);

제어권을 누가 갖고 있냐?

매개변수로 넣었을 때 .. 호출 시점에 대한 제어권 우리한테 없고 setInterval에게 있다..


원래 cbFunc()를 수행한다면 그 "호출주체"와 "제어권"은 모두 사용자가 되죠.
setInterval로 넘겨주게 되면 그 "호출주체"와 "제어권"은 모두 setInterval이 돼요.


-----


무슨 제어권? 2번째
인자에 대한 제어권을 갖는다.
map 함수입니다.

index : 사람이 이해할 수 있는 변수명일 뿐
currentValue : 사람이 이해할 수 있는 변수명일 뿐

var newArr = [10, 20, 30].map(function (index, currentValue) {
   console.log(index, currentValue);
   return currentValue + 5;
});

// 결과는 뭐가 될까?
console.log(newArr);


무조건 왼쪽이 currentValue이고 오른쪽이 index이다.

map / filter / reduce .. 원하는 결과를 얻기 위해서는 명세서에 나온대로 사용해야 한다 ..

사람은 제어권을 가질 수 없다. map 만이 콜백함수에 대한 인자의 제어권을 갖고 있는다 .. 

제어권이 넘어갈 map 함수의 규칙에 맞춰서 해야 한다 ..


-----

" 콜백 함수도 함수이기 때문에 기본적으로는 this가 전역객체를 참조한다 " 

☆☆☆ " 제어권을 넘겨받을 코드에서 콜백 함수에 별도로 this가 될 대상을 지정하누 경우에는 그 대상을 참조한다. "

map은 항상 콜백, this를 받는다. 결론적으로 새로운 객체를 리턴한다 ..

Array.prototype.map123 = function (callback, thisArg) {

   // map 함수에서 return할 결과 배열
   var mappedArr = [];

   for (var i = 0 ; i < this.length; i++) {
      var mappedValue = callback.call(thisArg || global, this[i]);
      mappedArr[i] = mappedValue;
   }

};

var neArr = [1, 2, 3].map123(function(number){
   return number * 2
});

console.log(newArr);

콜백함수 내부에서 this를 명시적으로 binding하기 때문에 이런 게 가능하구나 ~~

아하, 그러니까 바로 제어권을 넘겨받을 코드에서 call / apply 메서드의 첫 번째 인자에서
콜백 함수 내부에서 사용될 this를 명시적으로 binding하기 때문에 this에 다른 값이 담길 수 있는 거군요!

☆ (다시 한번) 제어권을 넘겨받을 코드에서 콜백 함수에 별도로 this가 될 대상을 지정한 경우에는 그 대상을 참조한다.

1. 호출 시점에 대한 제어권
2. 인자에 대한 제어권
3. this에 대한 제어권 .. 


-------------------------------------------

콜백 함수는 함수다.

콜백 함수는 함수이기 때문에 함수로써의 호출로 인식이 돼서 this binding을 global로써 한다 .. 

global로 하기 때문에 맥락을 잃어버리게 된다 ..

내부적인 맥락이 아니라 항상 전역 객체를 바라보게 된다 ~



// obj
// 2가지 속성

var obj = {
   vals : [1, 2, 3],
   logValues: function (v, i) {
      console.log(this, v, i);
   },
};

// method로서 호출 | obj를 잘 잡아주고 있다 .. 
obj.logValues();


콜백함수로 들어가보자.

함수를 매개변수로 넣는 것과 .. 함수를 실행한 결과를 매개변수로 넣는 것은 다르다 ..

[4, 5, 6].forEach(obj.logValues(1, 4));

forEach의 매개변수로 .. obj.logValues를 했기 때문에 .. 함수 자체를 넣은 게 아니라 메서드를 넣었다고 생각할 수도 있다.

그럼에도 불구하고 .. 메서드의 형태처럼 보이나 .. 결국 나는 if .. else .. console.log .. 이걸 넣은 거다.

메서드로서의 호출 .. 호출을 해줘야 하는 거지 매개변수로 함수를 넘겨주어서는 안 된다.

호출 .. 값을 넣어서 전달하는 것 ..

var obj = { 
   vals: [1, 2, 3],
   logValues: function (v, i) {
      console.log(">>> test starts");
      if (this === global) {
         console.log("this가 global입니다. 원하지 않는 결과!");
      } else {
         console.log(this, v, i);
      }

   console.log(">>> test ends");
   },
};

// forEach, map, filter
[4, 5, 6].forEach(obj.logValues);

콜백함수는 함수다 ~~

this에 어떻게 하면 다른 값을 binding할 수 있을까?
call, apply, bind
self <- this

------------------------------------------------

[콜백 함수 내부의 this에 다른 값 바인딩하기]
콜백 함수 내부에서 this가 문맥에 맞는 객체를 바라보게 할 수는 없을까요?
콜백 함수 내부의 this에 다른 값을 바인딩하는 방법


밖에서 참조하고 있는 self가 살아있다 .. ? closure 

setTimeout(obj1.func, 1000);

결과만을 위한 코딩 => 하드코딩 ... 완전 10점/100점짜리 코딩


개발 하실 때에도 .. 잘 이해가 안 되면 직접 대입해보시는 걸 권장드립니다~


call과 apply는 call, apply 괄호 열고 닫자마자 즉시 실행 되는 반면 ..
bind는 this를 리턴해서 새로운 함수를 바인딩해준다

bind .. 함수를 만들 때 유용하다. . 

var obj1 = {
   name: "obj1",
   func: function () {
      console.log(this.name);
   },
};

setTimeout(obj1.func.bind(obj1), 1000);

this가 obj1로 묶이게 된다 .. 

var obj2 = { name : "obj2" };
setTimeout(obj1.func.bind(obj2), 1500);

새로운 this를 바인드하게 된 것이다 ~

--------------------------

콜백지옥이란

a. 콜백 함수를 익명 함수로 전달하는 과정이 반복되어 코드의 "들여쓰기 수준이 헬 수준"인 경우를 말해요!
b. 주로 이벤트 처리 및 서버 통신과 같은 비동기적 작업을 수행할 때 발생하죠.
c. 뭐가 문제일까요? 가독성이 정말 지옥(hell)이구요. 오랜 상태로 이렇게 짜여왔기 때문에, 수정도 어렵습니다.



동기 vs 비동기

- 동기 : 주문한 후에 커피가 나올 때까지 기다려주세요!
- 비동기 : 주문한 후에 진동벨이 울리면 커피를 가지러 오세요!


동기 : synchronous

- 현재 실행 중인 코드가 끝나야 다음 코드를 실행하는 방식을 말해요!
- CPU의 계산에 의해 즉시 처리가 가능한 대부분의 코드는 동기적 코드구요.
- 계산이 복잡해서 CPU가 계산하는 데에 오래 걸리는 코드 역시도 동기적 코드예요

비동기 : a + synchronous => async라고들 흔히 부르죠

- 실행 중인 코드의 "완료 여부와 무관하게" 즉시 다음 코드로 넘어가는 방식
- setTimeout, addEventListener 등
- 별도의 요청, 실행 대기, 보류 등과 관련된 코드는 모두 비동기적 코드

코드

// 비동기적 코드의 이해!

setTimeout(function () {
   console.log("여기가 먼저 실행될까?!?!");
}, 1000);

console.log("여기 좀 봐주세요!!!!");

실행 결과

여기 좀 봐주세요!!!!
여기가 먼저 실행될까?!?!

- 복잡도가 올라갈수록 비동기적 코드의 비중이 늘어난다.

-----------

비동기 작업
=> 순서를 보장하지 X .


☆ 비동기 작업의 동기적 표현이 필요하다.

Promise, Generator (ES6), async/await(ES7)

☆ Promise는 비동기 처리에 대해, 처리가 끝나면 알려달라는 '약속'이에요.

- new 연산자로 호출한 Promise의 인자로 넘어가는 콜백은 바로 실행돼요.
- 그 내부의 resolve(또는 reject) 함수를 호출하는 구문이 있을 경우 resolve (또는 reject) 둘 중 하나가 실행되기 전까지는 다음(then), 오류(catch)로 넘어가지 않아요.
- 따라서, 비동기작업이 완료될 때 비로소 resolve, reject 호출해요.

우리는 이 방법으로 비동기 → 동기적 표현을 구현할 수 있습니다.

--------------

리팩토링 .. 

---------------

이터러블 객체 (Iterable)

스페인어사전 단어

iterable
[형용사] 반복될 수 있는, 반복할 수 있는

제너레이터 문법이 등장해요. 생소하시죠?

*가 붙은 함수가 제너레이터 함수입니다. 제너레이터 함수는 실행하면, Iterator 객체가 반환(next()를 가지고 있음)돼요.

iterator은 객체는 next 메서드로 순환할 수 있는 객체구요. next 메서드 호출 시, Generator 함수 내부에서 가장 먼저 등장하는 yield에서 stop 이후 다시 next 메서드를 호출하면 멈췄던 부분 → 그 다음의 yield까지 실행 후 stop

즉, 비동기 작업이 완료되는 시점마다 next 메서드를 호출해주면 Generator 함수 내부소스가 위 → 아래 "순차적으로" 진행돼요

-------------------

Document Object Model

(1) DOM의 기본 개념

1. javascript가 왜 생겼는데?

a. 브라우저에서 쓰려고 만들어진 JS예요!

본연의 역할 : 웹 페이지를 동적으로 만들기 위해! 즉, HTMl 문서를 조작해서 생명력을 불어넣어주기 위해 만들어진 언어예요.

b. 웹 페이지가 뜨는 과정

사용자 = 브라우저 = 클라이언트

- 클라이언트가 서버에게 '요청(request)
- 네이버 서버는 클라이언트에게 '응답(response)'를 보낸다 .. 
- 그 응답은 HTML 문서(document)이다.

c. 브라우저가 HTML 파일을 해석(parsing 파싱)

- 브라우저에는 기본적으로 렌더링 엔진이 있다. 서버가 클라이언트에게 준 HTML문서를 렌더링 한다는 것

해석은 어제 필요할까요?

어떠한 것을 이해하지 못할 때, 우리는 해석을 해야 합니다. 마찬가지로 서버로부터 받아온 그 문서를
javascript는 이해할 수 없기 때문에 javascript가 알아들을 수 있는 방법으로 '해석'하는 과정이 필요해요.



d. JavaScript가 알아들을 수 있는 방식으로 해석한 내용을 토대로 DOM Tree를 구성한다.

e. DOM Tree랑 CSSOM Tree를 묶어서 Render Tree를 구성

렌더 트리(Render Tree)는 HTML, CSS 및 JavaScript 문서를 파싱하여 브라우저에서
실제로 렌더링되는 최종 문서 모델을 나타내는 객체의 계층 구조이다.

결국은, 브라우저 화면에 그리기 위한 최종 버전을 만들어낸다는 것이죠.
그리고 나서 브라우저에 그림을 그리기 위한 "레이아웃 계산" → "페인팅 과정"이 시작돼요.

--------------------

API가 뭘까요?

API는 무언가를 주문할 때 메뉴판과 같은 것
메뉴판은 고객과 카페 사이의 인터페이스
API는 시스템과 사용자 간의 인터페이스 역할

API는 다른 시스템에서 제공하는 기능을 사용할 수 있도록 도와주는 중간자 역할

DOM 객체에 접근할 수 있도록 도와준다.

DOM은 브라우저 환경에서 돌아간다.

DOM이 브라우저에 내장되어 있기 때문에 우리는 HTML의 내용을 javascript로

i. 접근할 수 있어요
ii. 제어할 수 있어요

모든 DOM의 node들은 '속성'과 '메서드'를 갖고 있어요.


DOM의 node?

DOM에서 Node란 웹 페이지를 구성하는 모든 HTML 태그와 텍스트, 그리고 속성 등을 하나의 블록으로 취급하는 것


DOM에 접근 및 제어해보기

1. 항상 돔트리의 최상단 노드는 document

2. childNodes, parentNode를 이요해 기어다녀보기

---------

javascript의 클래스 문법은 ES6에서야 도입됐습니다.

클래스 - 설계도
인스탄서 - 실제 책상

Constructor : Class의 생성자 함수


상속(Inheritance)

Class 는 상속을 통해 다른 Class의 기능을 물려받을 수 있습니다.
상속을 받는 Class를 subclass, 또는 derived class라고 하며, 상속을 하는 Class를 superclass 또는 base class라고 합니다.

--

Static Method

CLass에서는 static 키워드를 사용하여 Class 레벨의 메소드를 정의할 수 있습니다.

유틸리티 함수, 정적 속성인 경우

--------------

클로저

클로저에 대한 정의

A closure is the combination of a function and the lexcial environment within which that
function was declared(클로저 함수는 그 함수가 선언된 렉시컬 환경과의 조화입니다.)

1. 렉시컬 스코트

- JS엔진은 함수를 어디서 '호출했는지'가 아니라 함수를 어디에 '정의했는지'에 따라 상위 스코프를 결정한다.
- 다시 말하면, "외부 렉시컬 환경에 대한 참조"에 저장할 참조값, 즉, 스코프에 대한 참조는 함수 정의가 평가되는 시점에 함수가 정의된 환경(위치)에 의해 결정된다.

2. 정의된 환경에 대한 정보를 저장하는 곳 : outer

3. 클로저와 렉시컬 환경 (LexicalEnvironment)

a. 외부 함수보닫 중첩 함수가 더 오래 유지되는 경우, 중첩 함수는 이미 생명 주기가 종료한 외부 함수의 변수를 "여전히" 참조할 수 있다. ← 이 개념에서 중첩 함수가 바로 클로저에요


클로저의 활용

- '상태를 안전하게 변경하고 유지하기 위해 사용'
-  은닉한다(특정 함수에게만 상태 변경을 허용한다)

'중첩 함수' '종료' '여전히

--

결론

클로저는 상태(state)가 의도치 않게 변경되지 않도록 은닉(information hiding)하고
특정 함수에게만 상태 변경을 허용하여 상태를 안전하게 변경하고 유지하기 위해 사용합니다.


--

캡슐화

- 프로퍼티와 메서드를 하나로 묶는 것 .. 
- 자바스크립트는 제공 안 해요 : 즉, 별도로 조치를 취하지 않으면 기본적으로 외부 공개가 된다는 의미!