Node js 면접준비

00_8_3·2021년 1월 21일
13

Node js 기술 면접 준비

var, let, const의 차이점

  • var : 함수 스코프를 갖는다. 변수를 한번 더 선언해도 에러가 발생하지 않는다. 그래서 es6 업데이트로 let과 const 도입
  • let : 블록 스코프를 갖는다다. 예를들어 if문 for문 while문 중괄호{}안에 선언 되었을 경우, 변수가 그 안에서 생성되고 사라진다.
  • const : 블록 스코프, 상수를 선언 할 때 사용한다. 선언과 동시에 할당 되어야합니다. (재할당하면 오류발생)

ES6 업데이트 이후 let, const 사용

기본적인 변수 선언에는 const를 사용하여 재할당을 방지하고 재할당이 필요한 경우 let을 사용하자

hoisting이란?

호이스트란, 변수의 정의가 그 범위에 따라 선언(declaration)/초기화(initialization)/할당 분리되는 것을 의미한다.

함수 안에 있는 선언들을 모두 끌어올려서 해당 함수 유효 범위의 최상단에 선언하는 것을 말한다.

  • 자바스크립트 함수는 실행되기 전에 함수 안에 필요한 변수값들을 모두 모아서 유효 범위의 최상단에 선언한다.
    • 자바스크립트 Parser가 함수 실행 전 해당 함수를 한 번 훑는다.
    • 함수 안에 존재하는 변수/함수선언에 대한 정보를 기억하고 있다가 실행시킨다.
    • 유효 범위: 함수 블록 {} 안에서 유효
  • 즉, 함수 내에서 아래쪽에 존재하는 내용 중 필요한 값들을 끌어올리는 것이다.
    • 실제로 코드가 끌어올려지는 건 아니며, 자바스크립트 Parser 내부적으로 끌어올려서 처리하는 것이다.
    • 실제 메모리에서는 변화가 없다.

예-1) var hoisting

var는 함수 스코프

function counter () {
  for(var i=0; i<10; i++) {
    console.log('i', i)
  }
}
counter()
console.log('after loop i is', i) // ReferenceError: i is not defined

예-2) IIFE

(immediately-invoked function expression)

// IIFE를 사용하면
// i is not defined가 뜬다.
(function() {
  // var 변수는 여기까지 hoisting이 된다.
  for(var i=0; i<10; i++) {
    console.log('i', i)
  }
})()
console.log('after loop i is', i) // ReferenceError: i is not defined

//////////
// var i 전역변수화 됨 IIFE
(function() {
  for(i=0; i<10; i++) {
    console.log('i', i)
  }
})()
console.log('after loop i is', i) // after loop i is 10

IIFE는 함수 스코프처럼 보여지지만 결과는 같지 않다.

에러없이 샐행되는 이유는 i가 hoisiting 되어 global-variable이 되었기 때문

이것을 막기위해 use-strict사용

// 아까랑 다르게 실행하면 i is not defined라는 에러가 발생한다.
(function() {
  'use strict'
  for(i=0; i<10; i++) {
    console.log('i', i)
  }
})()
console.log('after loop i is', i) // ReferenceError: i is not defined

Temporal Dead Zone(TDZ)

const와 let은 범위가 지정된 변수를 갖는다.
이 둘의 호이스팅이 실행 되기전 까지 액세스 할수 없는 것을 TDZ라 한다.

즉, let/const선언 변수는 호이스팅되지 않는 것이 아니다. 스코프에 진입할 때 변수가 만들어지고 TDZ(Temporal Dead Zone)가 생성되지만, 코드 실행이 변수가 실제 있는 위치에 도달할 때까지 액세스할 수 없는 것이다. let/const변수가 선언된 시점에서 제어흐름은 TDZ를 떠난 상태가 되며, 변수를 사용할 수 있게 된다.

NodeJS 프로젝트의 javascript 버전

ecma 6 (es6)(ECMAScript 2015) 이상의 버전에서 사용

NodeJS란

크롬 V8 JavaScript 엔진으로 빌드된 JS 런타임기

Chrome의 V8엔진을 이용하여 브라우저에서 JavaScript를 해석하듯이 서버에서 JavaScript를 동작할 수 있도록 하는 환경(플랫폼)

말 그대로 환경이기 때문에 NodeJS만으로는 할 수 없다.

  • Node.js 라이브러리 내의 API는 모두 비동기. (None-Blocking) API를 실행하고 완료되기까지 기다리지않고 바로 다음 API를 실행합니다.
    그리고 예전에 실행한 API가 값을 반환하면 이벤트 루프가 이를 확인하고 값을 받아옵니다.

  • 단일 쓰레드 모델을 사용하고, 이벤트 메커니즘을 통해 서버가 멈추지 않고 반응할 수 있어 확장성을 키워줍니다.

  • 그리고 Chrome의 V8 JavaScript 엔진을 사용하여 빠른 코드 실행을 제공하며, 버퍼링이 없습니다.

  • 위와 같은 특징을 가지고 있어 데이터를 실시간으로 다루는 애플리케이션이나 싱글페이지 애플리케이션(SPA), 입출력이 잦은 애플리케이션을 개발할 때 뛰어난 효율성을 발휘합니다.

  • 단, 싱글 쓰레드 모델이기 때문에 하나의 작업 자체가 시간이 많이 걸리면 전체 시스템의 성능이 아주 급격하게 나빠지므로 CPU 사용률이 높은 애플리케이션에선 Node.js 사용을 권장하지 않습니다.

동기와 비동기의 차이점

  • 동기 : 요청(Req) 후 응답(Res) 기다림
  • 비동기 : 기다리지 않음

비동기 처리의 대표적인 예

  • fetch, axios, ajax로 JSON 통신
  • setTimeout()
// #1
console.log('Hello');
// #2
setTimeout(function() {
	console.log('Bye');
}, 3000);
// #3
console.log('Hello Again');

결과
‘Hello’ 출력
‘Hello Again’ 출력
3초 있다가 ‘Bye’ 출력

콜백 지옥의 문제점

비동기 콜백패턴으로 중첩하여 사용하면 가독성 및 디버깅이 힘들어짐.
그래서 Promise를 씀.

await async ?

foreach()와 map()함수의 차이점

.foreach()

const a = [1, 2, 3]
const doubled = a.forEach((num, index) => {
  // num나 index로 무언가 합니다.
  return arr[index] = num * 2;
})

// doubled = undefined
// arr = [2, 4, 6]
  • 값을 반환 하지 않는다.
  • 각 요소에 대해 콜백을 실행합니다.(현재 배열을 변경해서 반환)

.map()

const a = [1, 2, 3]
const doubled = a.map(num => {
  return num * 2
})

// doubled = [2, 4, 6]
  • 값을 반환 한다.
  • 각 요소에서 함수를 호출하여 결과로 새 배열을 작성하여 각 요소를 새 요소에 매핑합니다.

.map()이 새로운 배열을 반환한다. 즉 원본 배열의 변경을 피하려면 .map()을 단순히 배열을 반복한다면 .foreach()를 추천합니다.

속도 비교

Closure 함수

“A closure is the combination of a function and the lexical environment within which that function was declared.”
클로저는 함수와 그 함수가 선언됐을 때의 렉시컬 환경(Lexical environment)과의 조합이다. -MDN-

위 정의에서 말하는 “함수” 란 반환된 내부함수를 의미하고 “그 함수가 선언될 때의 렉시컬 환경(Lexical environment)”란 내부 함수가 선언됐을 때의 스코프를 의미한다.

즉, 클로저는 반환된 내부함수가 자신이 선언됐을 때의 환경(Lexical environment)인 스코프를 기억하여 자신이 선언됐을 때의 환경(스코프) 밖에서 호출되어도 그 환경(스코프)에 접근할 수 있는 함수를 말한다.
이를 조금 더 간단히 말하면 클로저는 자신이 생성될 때의 환경(Lexical environment)을 기억하는 함수다 라고 말할 수 있다.

예-1

function outerFunc() {
  var x = 10;
  var innerFunc = function () { console.log(x); };
  return innerFunc;
}

/**
 *  함수 outerFunc를 호출하면 내부 함수 innerFunc가 반환된다.
 *  그리고 함수 outerFunc의 실행 컨텍스트는 소멸한다.
 */
var inner = outerFunc();
inner(); // 10

예-2

// 함수를 인자로 전달받고 함수를 반환하는 고차 함수
// 이 함수가 반환하는 함수는 클로저로서 카운트 상태를 유지하기 위한 자유 변수 counter을 기억한다.
function makeCounter(predicate) {
  // 카운트 상태를 유지하기 위한 자유 변수
  var counter = 0;
  // 클로저를 반환
  return function () {
    counter = predicate(counter);
    return counter;
  };
}

// 보조 함수
function increase(n) {
  return ++n;
}

// 보조 함수
function decrease(n) {
  return --n;
}

// 함수로 함수를 생성한다.
// makeCounter 함수는 보조 함수를 인자로 전달받아 함수를 반환한다
const increaser = makeCounter(increase);
console.log(increaser()); // 1
console.log(increaser()); // 2

// increaser 함수와는 별개의 독립된 렉시컬 환경을 갖기 때문에 카운터 상태가 연동하지 않는다.
const decreaser = makeCounter(decrease);
console.log(decreaser()); // -1
console.log(decreaser()); // -2

출처

Prototype

1개의 댓글

comment-user-thumbnail
2022년 5월 23일

도일 멋져요!

답글 달기