Please enable JavaScript to view the comments powered by Disqus.

프론트엔드 면접 핸드북 - 자바스크립트(1)

이 인터뷰 핸드북은 front-end-interview-handbook/javascript-questions을 기반으로 정리한 것입니다.


1. 이벤트 위임(event delegation)에 대해서 설명하라

한줄 답변: 부모 요소에 이벤트 리스너를 붙이는 것

이벤트 위임이란 이벤트 리스너를 부모 요소(element)에 붙이는 것을 말한다. 하위 요소에 이벤트가 발생하면 이벤트 버블링 때문에 부모 요소에 연결된 리스너가 실행된다. 이 테크닉의 장점은

  • 사용하는 메모리의 양(memory footprint)이 감소한다. 이벤트가 발생하는 모든 요소마다 리스너를 추가할 필요가 없고 부모에 하나만 추가하면 되기 때문이다.
  • 이벤트 발생하는 요소가 추가되고 제거될때마다 리스너를 추가하고 제거할 필요가 없다.

관련 자료


2. this 키워드는 어떻게 동작하는지 설명하라

한줄 답변: this가 가리키는 것은 코드의 실행 위치, 실행 방법에 따라 달라진다

this에 대한 간단한 설명은 없다. 자바스크립트에서 가장 헷갈리는 개념 중 하나라고 할 수 있다. 그래도 설명하자면 this의 값은 함수가 어떻게 실행되느냐에 따라 달라진다. 그리고 가장 깔끔하다고 생각하는 Arnav Aggrawal의 글에 의하면 다음의 규칙들로 정리할 수 있다.

  1. 함수 실행에 new 키워드를 사용하면, 그 함수 안에서 this 키워드는 새로운 객체를 가리킨다.
  2. apply, call, bind를 사용해 함수를 호출/생성 했다면, 함수 안에서 this 키워드는 apply, call, bind 호출시 전달된 객체를 가리킨다.
  3. 함수가 객체의 메소드로서 호출되었다면, this는 그 함수를 속성(property)으로 추가한 객체를 가리킨다.
  4. 함수가 단순히 실행되었다면, 다시말해 위의 3가지 사례를 제외한 방식으로 호출되었다면, this는 전역 객체를 가리킨다. 브라우저에서는 window, node에서는 global이 된다. 만약 strict 모드(‘use strict’)에서는 this는 전역 객체 대신 undefined가 된다.
  5. 만약 위의 4가지 조건이 중첩된다면, 먼저 제시한 규칙이 적용된다. (1번 규칙부터 우선함)
  6. 함수가 ES2015의 화살표 함수라면, 위의 규칙을 모두 무시하고, 함수가 만들어진 시점에서 그 함수를 둘러싼 scope의 this를 가리킨다.

관련 자료


3. 프로토타입 상속이 어떻게 동작하는지 설명하라

한줄 답변: 어떤 속성이 내 객체에 있으면 사용하고, 없으면 프로토타입 객체에 있는 것을 사용한다

자바스크립트 인터뷰에서 무척 자주 나오는 질문이다. 모든 자바스크립트 객체는 __proto__라는 속성을 가지고 있다. 그것은 다른 객체를 가리키는 레퍼런스며, 객체의 “prototype”이라고 불린다. 객체에서 어떤 속성(property)을 사용하려 하는데 그 객체에 없다면 자바스크립트 엔진은 __proto__ 객체에 그 속성이 있는지 확인한다. 만약 거기에도 없다면 다시 __proto____proto__ 객체에 있는지 확인하는 식으로 반복 확인하면서 프로토타입 체인의 끝까지 탐색한다. 이 동작은 전통적인 상속(inheritance)처럼 보이지만 상속 보다는 위임에 더 가깝다.

예제

Object.create(지정된 프로토타입 객체 및 속성을 갖는 새 객체를 생성)의 polyfill 코드.

if (typeof Object.create !== "function") {
  Object.create = function (parent) {
    function Tmp() {}
    Tmp.prototype = parent;
    return new Tmp();
  };
}

const Parent = function () {
  this.name = "Parent";
};

Parent.prototype.greet = function () {
  console.log("hello from Parent");
};

const child = Object.create(Parent.prototype);

child.cry = function () {
  console.log("waaaaaahhhh!");
};

child.cry();
// Outputs: waaaaaahhhh!

child.greet();
// Outputs: hello from Parent

관련 자료


4. AMD vs CommonJS에 대해 어떻게 생각하는가?

한줄 답변: 비동기 vs 동기 모듈. 이제는 Javascript module에 의해 대통합.

둘다 모듈 시스템을 구현하는 방식이다. ES2015 전까지 자바스크립트는 모듈 시스템을 직접 지원하지 않았다. CommonJS는 동기적인데 비해 AMD는 이름 그대로(Asynchronous Module Definition) 비동기적이다. CommonJS는 서버사이드 개발에 맞춰 디자인되었지만 AMD는 비동기적인 모듈 로딩을 지원하므로 보다 브라우저를 위한 시스템이라고 할 수 있다.

나는 AMD의 문법이 다소 장황하며 CommonJS의 문법이 다른 언어에서 사용하는 import 문법과 비슷하다고 본다. 현재 사용하고 있는 개발 시스템에서 나는 AMD가 필요없어 보인다. 왜냐면 모든 소스가 babel로 컴파일되고 하나의 파일로 번들링된 후 앱이 시작하기 때문이다. 여기서는 비동기 로딩이라는 AMD의 장점을 취할 수 없다. 그리고 CommonJS는 Node의 모듈 작성 방식과 비슷하기 때문에 클라이언트 사이드와 서버 사이드 개발 사이에 모듈 작성 방식의 차이에서 발생하는 비용이 크지 않다.

나는 ES2015의 모듈이 만족스럽다. 동기적, 비동기적 모듈 로딩을 모두 지원한다. 물론 ES2015가 브라우저와 노드에 완전히 탑재되지는 않았다. 하지만 Babel이라는 트랜스파일러가 있으므로 우리는 드디어 하나의 방법만 사용할 수 있게 되었다.

관련 자료


5. 다음의 코드 function foo(){ }();는 왜 IIFE처럼 실행되지 않는가? IIFE가 되게 하려면 어떤 부분을 수정해야 하나?

한줄 답변: 함수 선언부를 소괄호로 둘러싸야 즉시 호출이 가능하다

IIFE 는 Immediately Invoked Function Expressions 의 약자다.

자바스트립트 엔진은 function foo(){ }();function foo(){}();으로 읽는다. 첫번째는 함수 선언이고, 두번째는 함수 호출이다. 하지만 호출하려는 함수 이름이 없어서 Uncaught SyntaxError: Unexpected token ) 에러가 발생한다.

IIFE처럼 작동하게 하려면 아래처럼 작성해야 한다.

(function foo() {})();

function 키워드로 시작하면 함수 선언으로 인식하기 때문이다. 하지만 소괄호로 둘러싸면 함수 표현식으로 인식하고 뒤따르는 ()로 실행할 수 있다. 저렇게 실행한 함수는 전역 스쿠프에 노출되지 않으며 필요하지 않다면 함수 이름도 생략해서 작성할 수 있다.

> (function () {console.log(1)})();
1

소괄호로 둘러싸는 대신 void 키워드를 사용할 수도 있다. 하지만 리턴값을 받을 수는 없다. void 키워드는 항상 undefined를 리턴하기 때문이다.

> const foo = void function bar() { console.log(1); return 'foo'; }();
> console.log(foo)
undefined

관련 자료


6. null, undefined, 그리고 선언되지 않은 변수에는 어떤 차이가 있는가?

한줄 답변: null은 명시적인 값, undefined는 문자 그대로의 의미, 그리고 선언하지 않은 변수는 쓰지 말아야 한다.

선언되지 않은 변수(undeclared variable)

var, let, const를 붙여서 선언하지 않은 변수를 코드에서 사용하는 것을 말한다. strict 모드에서는 레퍼렌스 에러가 발생한다. strict 모드가 아니면 전역에 자동으로 추가된다. 사용하지 말아야 한다.

undefined 변수

선언은 되었지만 값이 할당되지 않은 상태. 함수가 값을 리턴하지 않는다면 undefined를 반환한다. falsy한 값이기 때문에 확실히 undefined인지 확인하기 위해서는 strict equality 연산자(===)를 사용해야 한다. loose equality(==)를 사용하면 undefined와 null 이 같다고 할 수 있으니 주의.

// 값 할당 안함
var foo;
console.log(foo); // undefined
console.log(foo === undefined); // true

// 리턴값 없음
function bar() {}
var baz = bar();
console.log(baz); // undefined

// strict equality
console.log(undefined == null); // true
console.log(undefined === null); // false

나는 변수를 undeclared 또는 unassigned 상태로 두지 않으려 한다. 당분간 사용하지 않을 변수라면 null 값을 명시적으로 할당해 둔다.

관련 자료


7. 클로져(closure)란 무엇이며 어떻게/왜 사용하는가?

한줄 답변: 접근 불가능한 영역에 통로를 만드는 것

클로져는 함수의 실행이 끝난 뒤에도 함수에 선언된 변수의 값을 접근할 수 있도록 만든 함수를 말한다.

함수는 블럭으로 둘러싸여 있고 하나의 스쿠프를 형성한다. 함수 안에 선언된 변수는 함수 바깥에서 참조할 수 없으며, 함수가 실행된 후에는 더 이상 사용할 수 없다.

하지만 자바스크립트에서는 함수 리턴값을 통해 함수 안에 선언된 변수를 노출시킴으로서 클로져를 형성할 수 있다.

자바스크립트에서 스쿠프(Scope)는 현재 코드 특정 변수에 대한 접근 가능 여부를 결정하는 환경(context)를 가리킨다.

스쿠프는 두가지 타입이 있다. 지역(local)과 전역(global)이다. 전역 변수는 어떤 블럭에도 속하지 않은 위치에서 선언된 것이며, 로컬 변수는 블럭 안에서 선언된 것이다.

사용하는 이유

  • 데이터 프라이버시 / 모듈 패턴에서 private 메소드를 구현하기 위해 사용됨.
  • 함수형 프로그래밍에서 partial application, currying 구현

예제

함수 안의 변수 업데이트

function counter() {
  let num = 0;
  function increase() {
    num += 1;
    return num;
  }
  return increase;
}

const c = counter();
console.log(c()); // 1
console.log(c()); // 2
console.log(c()); // 3

간단한 curry

function curry(fn, a) {
  return function (b) {
    return fn(a, b);
  };
}

var add3 = curry((num1, num2) => num1 + num2, 3);

add3(4); // 7

관련 자료


8. forEach와 map의 차이점이 무엇이며, 어떤 메소드를 사용해야 하는가?

한줄 답변: 콜백 리턴값으로 맵핑을 하느냐, 하지 않느냐

Array.prototype.forEach

  • 배열의 요소을 순환한다
  • 각각의 요소를 파라미터로 전달받는 콜백 함수가 실행된다.
  • 값을 리턴하지 않는다.

Array.prototype.map

  • 배열의 요소을 순환한다
  • 새로운 배열을 리턴한다. 그리고 각각의 요소마다 콜백함수를 실행시키며, 콜백의 리턴값을 새로운 배열의 요소로 mapping(사상)한다.

차이점과 선택

두 메소드 모두 원본 배열의 값을 바꾸지 않는다. 차이점은 forEach는 값을 리턴하지 않고, map은 값을 리턴한다는 것이다. 결과가 필요하다면 map을, 아니라면 forEach를 사용하면 된다.

관련 자료


9. 익명 함수를 사용하는 사례에는 어떤 것들이 있나?

한줄 답변: IIFE, callback ⇒ 다른 곳에서 참조할 일이 없는 것들

IIFE

코드를 둘러싸서 실행 코드와 변수를 전역 스쿠프에 노출하지 않을 목적으로 사용한다. 다른 곳에서 사용하지 않을 것이므로 IIFE 패턴에 사용할 함수는 이름이 필요 없다.

(function () {
  // 실행할 코드 작성
})();

콜백 함수

콜백으로서의 함수는 재사용하지 않는 경우가 대부분이다. 콜백이 사용되는 위치에 직접 선언하는 편이 더 가독성이 높다.

setTimeout(function () {
  console.log(new Date().getSeconds());
}, 1000);

// vs

function onEveryTick() {
  console.log(new Date().getSeconds());
}

setTimeout(onEveryTick);

관련 자료


10. 호스트 객체와 네이티브 객체의 차이점

한줄 답변: 표준 객체와 런타임 환경 객체

네이티브 객체는 자바스크립트 언어의 일부로서 ECMAScript 표준에 정의된 것이다. 예를 들면 String, Math, RegExp, Object, Function 등이다.

호스트(Host) 객체는 런타임 환경(브라우저, Node)에서 제공하는 객체로서 window, XMLHTTPRequest 등이 있다.


11. function Person(){}이 있을 때, var person = Person()var person = new Person()의 차이점은?

한줄 답변: 함수를 생성자로 사용하느냐, 일반 호출로 사용하느냐의 차이.

이 질문은 다소 모호하다. 내 생각에 이 질문의 의도는 생성자(constructor)에 대해서 묻고 있는 것으로 보인다. 기술적으로 말해서 function Person(){}은 그냥 일반적인 함수 선언이다. 함수의 이름으로 파스칼 케이스(PascalCase)를 사용하는 것은 이 함수가 생성자 함수임을 나타내기 위한 규칙(convention)이다.

var person = Person()Person을 생성자가 아닌 함수로서 호출한다. 생성자 역할을 해야 하는 함수를 저렇게 호출하는 것은 흔히 볼 수 있는 실수 중 하나다. 일반적으로 생성자는 아무것도 리턴하지 않는다. 인스턴스를 리턴받으려는 의도였겠지만 실제로는 undefined 값이 할당될 뿐이다.

var person = new Person()new 연산자를 사용해 Person 객체의 인스턴스를 만든다. 그 인스턴스는 Person.prototype을 상속한다. 프로토타입을 상속하는 다른 방법에는 Object.create가 있다.

var person = Object.create(Person.prototype);

정리하면 아래와 같다.

// 생성자 함수
function Person(name) {
  this.name = name;
}

var person = Person("John");
console.log(person); // undefined
console.log(person.name); // Uncaught TypeError: Cannot read property 'name' of undefined

var person = new Person("John");
console.log(person); // Person { name: "John" }
console.log(person.name); // "john"

관련 자료


12. Function.prototype.callFunction.prototype.apply 의 차이점은?

한줄 답변: this 객체를 지정한다는 것은 같지만, 함수 파라미터 전달방식에 차이가 있다.

call, apply 모두 함수를 호출하는데 사용하며, 첫번째 파라미터는 함수 안에서 this 값으로 사용된다. 두 번째 파라미터부터는 함수의 파라미터로 전달된다.

call 은 콤마로 구분된 파라미터를 사용하지만, apply는 배열을 사용한다.

  • callc ⇒ comma separated arguments
  • applya ⇒ array of arguments
function add(a, b) {
  return a + b;
}

console.log(add.call(null, 1, 2)); // 3
console.log(add.apply(null, [1, 2])); // 3

13. Function.prototype.bind에 대해 설명하라

한줄 답변: this 키워드가 지정된 새 함수를 만든다.

bind 메소드는 새로운 함수를 만든다. 새로 만들어진 함수 안에서 this 키워드의 값은 bind를 호출할 때 첫 번째로 전달된 값이 된다. Function.prototype.call처럼 두번째 파라미터부터 차례대로 원래 함수의 파라미터로 전달된다. (Taken word-for-word from MDN)

React에서 컴포넌트 클래스의 메소드에 this를 바인딩하는 것이 유용한 사용 방법 중 하나다.

클래스의 메소드가 비동기적으로 실행되거나, DOM의 이벤트 핸들러(ex. onClick)로 할당되면 this가 유지되지 않고 사라진다. bind를 사용해서 this를 명시적으로 바인딩하거나 화살표 함수를 사용해야 한다.

bind를 사용하는 예제

setTimeout 은 비동기적으로 실행되므로 함수가 호출될 때 this를 유지하지 않는다. 그래서 this를 바인딩하지 않으면 this가 클래스 인스턴스가 아닌 다른 값(전역 객체)을 가리키게 된다.

function debounce(fn, to) {
  setTimeout(fn);
}

class Foo {
  constructor() {
    this.fullName = "Bar";
  }

  speak() {
    console.log("My name is", this.fullName);
  }

  test() {
    debounce(this.speak, 1000);
    debounce(this.speak.bind(this), 2000);
  }
}

let foo = new Foo();

foo.test();
// My name is undefined
// My name is Bar

메소드를 선언할 때 화살표 함수를 사용하면 this를 바인딩하지 않아도 된다. 화살표 함수는 자신의 this를 바인딩하지 않으며, this를 lexical하게 바인딩하기 때문이다. 즉 다른 규칙 다 무시하고, 화살표 함수 코드가 작성된 곳의 this를 사용한다. 개인적으로는 이것이 화살표 함수의 탄생 목적 중 하나로 보이기도 한다.

class Foo {
  constructor() {
    this.fullName = "Bar";
  }

  speak = () => {
    console.log("My name is", this.fullName);
  };
}

관련 자료


14. document.write를 사용하겠는가?

한줄 답변: 아니오. 기존의 html, body 모두 날아감

document.write는 문자열을 document stream으로 작성한다. 웹페이지가 로딩된 후에 document.write가 호출되면 그것은 document.open을 호출하고, 로딩된 페이지가 모두 지워진다. (head, body 태그가 제거된다!) 그리고 컨텐츠를 전달된 파라미터로 교체한다.

<html>
  <head>
    <title>write example</title>
    <script>
      function newContent() {
        document.open();
        document.write("<h1>Out with the old - in with the new!</h1>");
        document.close();
      }
    </script>
  </head>

  <body onload="newContent();">
    <p>Some original document content.</p>
  </body>
</html>

위의 코드를 실행하면 html이 아래처럼 바뀐다.

document.write 실행 결과

document.write가 애널리틱스 코드에서 사용되거나, 자바스크립트가 허용된 환경에서만 스타일을 추가하고 싶을때 사용된다는 설명이 웹상에 존재한다. 심지어 HTML5 bolilerplate 코드에서 스크립트를 병렬적으로 로딩하고 실행 순서를 유지하는데 사용된다고 하기도 한다.

하지만, 나는 저런 테크닉들이 요즘에 와서는 다소 철이 지난 오래된 것들이라 생각한다. 그리고 document.write 를 사용하지 않아도 구현할 수 있다.


15. feature detection, feature inference, 그리고 UA 문자열에 대해서 설명하라

한줄 답변: 브라우저가 어떤 기능을 지원하는지 확인하기 위한 테크닉들이다.

Feature Detection

기능 탐지(feature detection)는 브라우저에서 어떤 기능을 포함한 코드 블럭을 사용할 수 있는지 확인하는 것, 그리고 지원 여부에 따라 다른 코드를 실행해서 오류를 발생하지 않게 하여 일관적인 사용자 경험을 제공하는 것을 포함한다.

if ("geolocation" in navigator) {
  // Can use navigator.geolocation
} else {
  // Handle lack of feature
}

Modernizr 는 feature detection을 처리하는데 사용할 수 있는 훌륭한 라이브러리다.

Feature inference

기능 추론(Feature inference)은 feature detection처럼 기능을 확인하지만, 그 기능은 사용하지 않고 다른 기능을 사용한다. 탐지한 기능이 있으면 관련된 다른 기능도 있을 것이라 추정하는 것이다.

if (document.getElementsByTagName) {
  element = document.getElementById(id);
}

하지만 이런 코드는 별로 권장하지 않는다. Feature detection이 더 확실한 방법이다.

UA 문자열

브라우저가 제공하는 문자열. UA를 통해 네트워크 프로토콜이 리퀘스트하는 대상의 어플리케이션 타입, 운영체제, 소프트웨어 벤더(vendor), 소프트웨어 버전을 확인할 수 있다. 브라우저에서 navigator.userAgent 로 가져올 수 있다. 하지만 이 문자열은 파싱하기가 까다롭고 잘못되었을 수도 있다. 예를 들어 macOS에서 실행한 Chrome 브라우저는 “Chrome”과 “Safari”라는 문자열을 모두 포함한다.

// chrome에서 확인한 userAgent.
// 뒤쪽에 보면 Safari도 포함하고 있다.
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.132 Safari/537.36";

그래서 Safari임을 확인하려면 Safari 문자열이 있는지, 그리고 Chrome 문자열이 없는지도 확인해야 한다. 이 방법은 사용하지 말아야 한다.

관련 자료


16. Ajax에 대해 가능한 자세하게 설명하라

한줄 답변: 비동기 데이터 통신, 동적인 웹 컨텐츠 제공을 위한 기술

Ajax(asynchronous JavaScript and XML)는 비동기적인 웹 어플리케이션을 만들기 위해 클라이언트 사이드에서 사용하는 웹 개발 기술의 모음이다.

Ajax를 사용하면 웹 어플리케이션은 현재 표시중인 있는 화면에 영향을 주지 않고 서버에 비동기적으로(백그라운드에서) 데이터를 보내고 받을 수 있다. 데이터 교환 계층과 프레젠테이션 레이어를 분리함으로 Ajax는 웹 어플리케이션이 화면 새로고침 없이도 동적으로 컨텐츠를 변경하는 것이 가능하도록 만들었다.

실제로는 XML을 JSON으로 대체해서 구현하고 있다. 왜냐하면 자바스크립트 엔진에 JSON 처리 기능이 탑재되어 있어 데이터를 바로 사용할 수 있다는 이점이 때문이다.

XMLHttpRequest API는 비동기 통신에 많이 사용되는데, 최근에는 fetch API를 많이 사용한다.

관련 자료


17. Ajax의 장점과 단점은 무엇인가?

한줄 답변: SPA의 그것과 거의 같다. 동적인 웹 구현을 가능하게 하지만 그만큼 댓가도 따른다.

장점

  • 개선된 상호작용. 서버에서 가져온 컨텐츠를 페이지 새로고침 없이 보여줄 수 있다.
  • 어플리케이션 스크립트와 스타일시트를 한번만 가져와도 된다.
  • 페이지에서 상태를 유지할 수 있다. 페이지 새로고침이 되지 않을 것이기 때문에 자바스크립트 변수와 DOM 상태가 초기화되지 않는다.
  • 기본적으로 SPA(single page application)가 가지는 대부분의 장점과 일치한다.

단점

  • 동적인 웹페이지는 북마크하기 더 어렵다.
  • 자바스크립트가 허용되지 않은 페이지에서는 동작하지 않는다.
  • 어떤 웹 크롤러는 자바스크립트를 실행하지 않기 때문에 동적으로 로딩되는 컨텐츠가 표시되지 않을 수 있다.
  • Ajax를 이용해 데이터 가져오는 웹페이지는 클라이언트 사이드에서 템플릿과 원격 데이터를 조합해 DOM을 업데이트해야 한다. 이를 위해선 자바스크립트가 클라이언트 사이드에서 파싱되고 실행되어야 한다. 느린 접속 속도와 낮은 스펙을 가진 모바일 기기에서는 페이지 표시에 어려움을 겪을 수 있다.
  • 기본적으로 SPA가 가지는 대부분의 단점과 일치한다.

18. JSONP가 어떻게 동작하는지 설명

한줄 답변: 다른 도메인에 있는 서버에서 클라이언트의 전역 함수를 호출하는 테크닉이다.

JSONP(JSON with Padding)는 크로스 도메인 정책을 우회하기 위해서 주로 사용된다. 웹 브라우저는 Ajax 리퀘스트를 다른 도메인에 요청하는 것을 허용하지 않기 때문이다.

JSONP는 script 태그로 리퀘스트를 보내며 보통 쿼리 파라미터에 callback 을 붙인다.

https://example.com?callback=printData

서버에서는 데이터를 callback 파라미터로 전달된 printData 라는 함수에 담아서 실행한다.

<!-- https://mydomain.com -->
<script>
  function printData(data) {
    console.log(`My name is ${data.name}!`);
  }
</script>

<script src="https://example.com?callback=printData"></script>
// File loaded from https://example.com?callback=printData
printData({ name: "user name" });

클라이언트에는 printData라는 함수가 전역 scope에 선언되어 있어야 한다. 다른 도메인의 스크립트가 클라이언트에서 실행되면서 클라이언트의 함수를 실행하고, 파라미터까지 전달할 수 있다.

JSONP 구현을 위해서는 클라이언트와 서버가 printData 라는 이름을 가진 함수를 사용할 것이고 어떻게 데이터를 보낼지 사전에 조율되어야 한다.

JSONP는 안전하지 않으며 보안에 문제를 가져올 수 있다. JSONP는 자바스크립트이기에 자바스크립트로 할 수 있는 다른 모든 것들을 할 수 있다. 그렇기에 JSONP를 통해 데이터를 제공하는 상대를 신뢰해야만 사용할 수 있다.

이제 JSONP는 해킹으로 간주되며, CORS를 사용한 구현이 더 권장된다.

관련 자료


19. 자바스크립트 templating을 사용해 본 적이 있는가? 어떤 라이브러리로?

한줄 답변: Angular, React, template literal(XSS에 유의)

Angular, React를 사용한다. Lodash도 _.template 메소드로 템플릿 작업을 할 수 있지만 거의 사용하지 않았다.

React로 JSX를 가장 많이 사용했으며 좋아한다. 자바스크립트에 가깝고 JSX를 위해 별도로 공부할 것이 거의 없기 때문이다. 주의해야 할 점이라면 DOM attribute 키 값을 JSX에서는 모두 camel 케이스로 사용해야 한다는 것 정도가 있다.

최근에는 ES2015에서 표준이 된 template literal을 사용해 라이브러리 도움 없이도 템플릿을 만들 수 있다.

const template = `<div>My name is: ${name}</div>`;

하지만 템플릿 라이브러리의 도움 없이 template literal을 사용할 때는 XSS(cross site scripting) 공격을 염두에 두고 필요하면 문자열을 모두 이스케이프 처리해야 한다.

React에서는 동적으로 HTML을 추가하기 위한 attribute로 dangerouslySetInnerHTML 을 제공한다. 그 이름에서 HTML을 직접 추가할 때는 XSS에 주의해야 한다는 사실을 상기시키고 있다.


20. hoisting에 대해서 설명하라

한줄 답변: 같은 스쿠프라면 아래쪽 줄에 있는 변수를 윗줄에서 참조 가능하도록 하는 자바스크립트의 특징. 하지만 변수에 할당된 값은 알려주지 않는다.

Hoisting(이하 호이스팅)은 자바스크립트 변수 선언의 동작 방식을 설명하기 위한 용어다.

var 키워드로 선언되거나 초기화된 변수는 그 선언이 자바스크립트 엔진에 의해 모듈/함수 스쿠프의 최상단으로 옮겨진다. 이것을 호이스팅이라고 부른다. 하지만 선언(declaration)만 호이스팅될 뿐 값 할당(assignment)은 여전히 거기에 머무른다. 다시말해 값이 할당된 변수라도 호이스팅을 통 통해 접근하면 undefined 값을 얻게 된다.

console.log(foo); // undefined
var foo = 1;
console.log(foo); // 1

선언이 실제로 옮겨진것이 아니라는 것임을 주지해야 한다. 자바스크립트 엔진은 컴파일 과정에서 코드를 파싱해서 변수 선언을 인지하고 어느 스쿠프에 속하는지 알게 된다. 스쿠프 맨 위로 호이스팅되는 것은 시각화를 통해 더 쉽게 이해할 수 있다.

본체(body)가 있는 함수 선언은 호이스팅되지만 함수 표현식(변수에 할당하는 식으로 선언한 함수)은 호이스팅되지 않는다.

// 함수 선언
console.log(foo); // [Function: foo]
foo(); // 'FOOOOO'
function foo() {
  console.log("FOOOOO");
}
console.log(foo); // [Function: foo]

// 함수 표현식
console.log(bar); // undefined
bar(); // Uncaught TypeError: bar is not a function
var bar = function () {
  var a = 1;
  console.log("BARRRR");
};
console.log(bar); // [Function: bar]

let, const 로 선언된 변수도 호이스팅된다. 하지만 코드에서 선언한 부분 위에서 접근하면 var와는 다르게 값이 할당되어 있지 않으면 undefined가 출력되는 것이 아니라, ReferenceError exception 오류를 발생시킨다. 그런 변수는 코드 시작점부터 변수 선언부까지 ”temporal dead zone(TDZ)“을 가진다.

// -- 변수 y의 TDZ 시작
x; // undefined.
y; // Reference error: y is not defined

var x = "local";
let y = "local";
// -- 변수 y의 TDZ 종료

관련 자료