프론트엔드 면접 질문 리스트 - JS

Javascript 인터뷰 질문 리스트 2

면접!

프론트엔드 면접 준비를 위한 질문 리스트 정리

Javascript

아래질문 리스트를 기반으로 면접 질문 & 답변 목록을 작성할 예정이고, 지속적으로 보충 or 보수할 계획입니다.

업데이트 날짜

  • 2020-10-26
  • 2021-07-02
    • 질문 목록 수정
  • 2021-07-22
  • 2021-07-26
  • 2021-07-27
    • 질문 목록 및 답변 수정

질문 목록

  1. ES6

    • ES6 이상의 버전을 브라우저에서 인식하지 못한다면 어떻게 해결해야 하는지?
    • ES6 에서 추가된 스펙에 대해 아는대로 다 말해달라
    • Babel이란? babel은 컴파일러 인가 ? 트랜스파일러인가
    • Class 는 무엇이고, Prototype, fucntion의 ES5 스펙만으로 Class를 구현할수 있는가
  2. ES6에서 화살표 함수와 일반 함수의 차이는 무엇인가

  3. 구조 분해 할당(destructuring) 이란 무엇인가

  4. spread 문법과 장점과 rest 문법은 무엇이며 차이점은 무엇인가

  5. Javascript의 디자인 패턴에 대해 아는대로 설명해주세요.

  6. 모듈 패턴과 전통적 상속, 각각의 장단점을 설명해보시오.

  7. 호스트 객체(Host Objects)와 네이티브 객체(Native Objects)의 차이점은 무엇인가요?

  8. 이벤트 버블링, 이벤트 위임(delegation)에 대해서 설명해달라

    • DOM (Document Object Model) 이벤트에 대한 질문
    1. ``document.write()` 는 언제 사용하나요 ?
  9. load 이벤트와 같은 것을 사용하나요? 이 이벤트에는 단점이 있나요? 다른 대안을 알고 있나요? 알고 있다면 왜 그것을 사용할 건가요?

    • document load event와 DOMContentLoaded event의 차이
  10. attribute와 property

  11. use strict 은 무엇이고, 사용했을 때 장단점에 관해서 설명해주세요.

  12. Functional Programming 이란 무엇인지 설명해주세요

  13. 고차함수 (High-Order Function)란 무엇인지 설명해주세요.

  14. 자바스크립트의 배열(Array)이 실제 자료구조 배열이 아닌데 그 이유는?

  15. 자바스크립트의 순환참조란? 어떤게 문제이고 해결방법은?

  16. 자바스크립트와 관련한 same-origin 정책에 대해 설명

  17. 동일 출처 원칙을 회피하기 위한 방법으로는 무엇인 있으며 구체적으로 설정해달라.


1. ES6

ES6 이상의 버전을 브라우저에서 인식하지 못한다면 어떻게 해결해야 하는지?

최신 자바스크립트의 호환성 문제를 해결하는 방법에 대한 질문

브라우저 별로 구동원리 또는 JS엔진 등의 차이가 있기 때문에 브라우저 호환성 이슈가 발생하는데, 이러한 차이를 최소화해 런타임 환경에 맞게 최적화 하는 작업을 크로스 브라우징이라고 하는데, Javascript의 호환성에 따른 충돌이나 부작용을 줄이기 위한 방법으로는 Babel이라는 도구를 사용하면 됩니다. 바벨은 주로 ES5 이상 버전의 코드를 현재 및 과거의 브라우저와 같은 환경에서 호환되는 버전으로 변환하는데 사용되는 도구로, IE나 다른 브라우저에서 동일한 기능을 제공하고 side-effect를 최소한으로 줄일수 있습니다.

바벨 공식 웹사이트에서는 바벨을 다음과 같이 정의한다.

Babel is a toolchain that is mainly used to convert ECMAScript 2015+ code into a backwards compatible version of JavaScript in current and older browsers or environments.

바벨은 주로 ECMAScript 2015+ 코드를 현재 및 과거의 브라우저와 같은 환경에서 호환되는 버전으로 변환하는데 주로 사용되는 도구입니다.

babel은 컴파일러 인가 ? 트랜스파일러인가

Babel은 빌드 단계에서 Javascript를 다른 언어로 변환하는 것이 아닌 어플리케이션에서 설정된 환경에 맞게 또는 현재 사용하고 있는 대중적인 브라우저 스펙 또는 호환성에 맞는 하위 버전의 Javascript로 변환한다는 의미에서 Babel은 트랜스파일러라고 할 수 있습니다.

컴파일러와 트랜스파일러에 대해 설명하자면,

  • 컴파일러 의 경우에는 한 언어도 작성된 코드를 다른 언어로 변환하는 것으로 C 코드로 개발된 코드를 Assembly 코드로 변환하는 것과 같고

  • 트랜스파일러 란 한 언어로 작성된 소스 코드를 비슷한 수준의 추상화를 가진 언어로 변환하는 것을 의미합니다.

바벨의 빌드 단계를 설명하자면, 파싱, 변환, 출력의 단계로 코드를 변환하는데,

  1. 파싱: 코드를 읽고, 추상 구문 트리(AST)로 변환하는 단계
  2. 변환: 추상 구문 트리(AST)를 변경
  3. 출력: 변경된 결과물을 출력

위와 같은 빌드 단계를 통해 Babel은 Javascript의 호환성 이슈를 해결합니다.

그렇다면 바벨에서 코드를 변환하기 위한 규칙은 어떻게 설정하나

바벨을 사용한다고 코드를 변환할 수 있다고 할수는 없으며 변환하기 위한 규칙이나 브라우저 버전을 설정해야 하는데 이 작업은 바벨 플러그인 이 담당합니다. 바벨 플러그인 이란, 바벨이 변환할 코드에 대한 규칙으로 custom으로 Javascript의 기능에 따라 각각 설정하기에는 까다롭고 매우 많기 때문에, preset 이라는 플러그인을 사용해 손쉽게 변환규칙을 설정해줄 수 있습니다. preset 이란 필요한 플러그인들을 목적에 따라 세트로 묶여 있는 플러그인으로 대표적인 프리셋 플러그인으로 preset-env 이 있으며 프로젝트가 지원하고자 하는 환경에 기반해 빌드 타임에 동적으로 결정하는 프리셋으로 ES6 이상의 코드를 변환합니다.

크로스 브라우징 이슈를 해결하기 위해 플러그인을 설정해 호환가능한 버전으로 변환하지만 빌트인 메서드나 JS 기능이 없는 경우에는 어떻게 해결할 수 있는가?

빌드 과정에서 Babel을 통해 트랜스파일링 과정을 거쳤더라도 Promise와 같은 빌트인 또는 Array.prototype.includes 등의 인스턴스 메소드가 코드에 남아있을 수 있다. 해당 빌트인 또는 메서드를 지원하지 않는 환경에서는 에러를 유발하거나 어플리케이션이 멈추는 이슈가 발생하는데, 이를 해결하기 위한 기술이 폴리필 (Polyfill) 입니다. Babel 설정에서 폴리필을 설정하면, 최신 ECMAScript 환경을 만들기 위해 코드가 실행되는 환경에 존재하지 않는 빌트인 메서드 등을 추가해줍니다.

폴리필 전체를 빌드에 포함하면 번들 사이즈가 너무 커질수 있기 때문에, preset-envuseBuiltIns 옵션을 사용하면 빌드 타임에 babel-polyfill import를 꼭 필요한 폴리필 import로 대체해 번들의 사이즈를 줄일 수 있습니다.

ES6 에서 추가된 스펙에 대해 아는대로 다 말해달라

  • ES5 (2009)
    • 일반적으로 (구형 브라우저를 제외한) 모든 런타임에서 지원한다고 가정할 수 있는 Javascript의 기본 버전
    • Added forEach, map, filter, reduce, some, every in Array Methods
    • Object getter & setter
    • Added stricter standards to strict mode
    • JSON
  • ES6 / ES2015
    • Standard Modules — import and export
    • Standardised Promises
    • Classes & Inheritance
    • Block-scoped variables — letand const
    • Template Literals
    • Object destructing into variables
    • Generator functions
    • Map and Set data structures
    • Internationalisation for Strings, Numbers and Dates via Intl API
    • Arrow Function
  • ES7 / ES2016
    • Array.prototype.includes()
    • Numeric exponent (power of) operator **
  • ES8 / ES2017
    • Async / Await Functions
    • Object.entries, Object.values Methods
    • String padding functions
    • Allow Trailing commas to function expression arguement
  • ES9 / ES2018
    • Object Rest/Spread const obj = { ...props };
    • Asynchronous Iteration for await (...) {
    • Promise finally() function
    • Regular expression enhancements (lookbehind, named groups)
  • ES10 / ES2019
    • Object.fromEntries
    • flat, flatMap
    • Symbol.description
    • optional catch

ES2015/ES6 를 기준으로 수많은 편리한 메서드와 문법들이 추가되었고 개발자들은 보다 점차 높은 수준의 Javascript를 구사할 수 있게 되었으며 사용자들 또한 높은 수준의 기능들을 경험할 수 있게 되었다.

Class 는 무엇이고, Prototype, fucntion의 ES5 스펙만으로 Class를 구현할수 있는가

둘 다 생성자 역할을 하지만 상속을 구현하는 방법에서 큰 차이가 있다.

1
2
3
4
5
6
7
8
9
10
11
// ES5 Function Constructor
function Person(name) {
this.name = name;
}

// ES6 Class
class Person {
constructor(name) {
this.name = name;
}
}

아래는 상속을 구현한 코드. ES5 문법이 훨씬 더 길고 복잡하며 클래스 문법을 사용하면 간단하다.

ES5에서는 생성자 함수를 상속받기 위해서 prototype에 새 객체를 복사하는 등 여러가지 작업이 필요하지만 클래스는 그런 과정이 필요없다. extends 키워드로 상속받을 함수를 명시하고, constructor 메소드에서 super(this) 만 추가하면 된다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// ES5 Function Constructor
function Student(name, studentId) {
// Call constructor of superclass to initialize superclass-derived members.
Person.call(this, name);

// Initialize subclass's own members.
this.studentId = studentId;
}

Student.prototype = Object.create(Person.prototype);
Student.prototype.constructor = Student;

// ES6 Class
class Student extends Person {
constructor(name, studentId) {
super(name);
this.studentId = studentId;
}
}

2. ES6에서 화살표 함수와 일반 함수의 차이는 무엇인가

  • 일반 함수 는 함수를 선언할 때 this 에 바인딩할 객체가 정적으로 결정되는 것이 아니고, 함수를 호출할 때 함수가 어떻게 호출되었는지에 따라 this 에 바인딩할 객체가 동적으로 결정된다.
  • 화살표 함수 는 함수를 선언할 때 this 에 바인딩할 객체가 정적으로 결정된다. 화살표 함수 안에서의 this 는 화살표 함수를 포함한 스코프의 this를 가리킨다.

3. 구조 분해 할당(destructuring) 이란 무엇인가

ES6에서 도입된 객체, 배열의 값을 추출해서 변수에 바로 할당할 수 있는 편리한 문법입니다

디스트럭처링(destructuring) 할당이란 무엇인가요?

디스트럭처링을 이용하면 배열의 요소나 객체의 속성을 배열 리터럴(literal)이나 객체 리터럴과 비슷한 문법을 이용해서 변수에 할당할 수 있습니다. 아주 간결한 문법입니다. 그러면서도 훨씬 더 명확합니다.

배열 Destructuring

1
2
3
4
5
6
7
// 배열 내 요소의 직접 접근 변수 할당
var first = someArray[0];
var second = someArray[1];
var third = someArray[2];

// 디스트럭처링을 이용한 변수 할당
var [first, second, third] = someArray;

위와 같이 배열의 각 요소를 외부 변수에 할당할 수 있으며 배열과 이터러블(iterable) 을 이용해 디스트럭처링을 하면 특정 멤버 이외의 멤버들은 하나의 변수에 할당할 수도 있으며 또는 마지막 멤버만을 외부 변수에 할당할 수도 있습니다.

1
2
3
4
5
6
7
let [ first, second, ...others ] = [1, 2, 3, 4, 5];
// first: 1
// second: 2
// others: 3, 4, 5
var [,,third] = ["foo", "bar", "baz"];
console.log(third);
// "baz"

객체 Destructuring

객체를 디스트럭처링하면 변수에 객체의 속성값을 할당할 수 있습니다. 할당할 속성을 지정하고 그 속성값을 할당할 변수를 지정합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 기본 객체 디스트럭처링
var robotA = { name: "Bender" };
var robotB = { name: "Flexo" };

var { name: nameA } = robotA;
var { name: nameB } = robotB;

console.log(nameA, nameB);
// "Bender", "Flexo"

// 중첩 객체 디스트럭처링
var complicatedObj = {
arrayProp: [
"Zapp",
{ second: "Brannigan" }
]
};

var { arrayProp: [first, { second }] } = complicatedObj;

console.log(first, second);
// "Zapp", "Brannigan"

한가지 주의할 것은 객체를 디스트럭처링해서 선언되지 않은 변수에 할당하는 경우입니다 (let, const, 또는 var를 함께 쓰지 않는 경우입니다).

1
2
{ blowUp } = { blowUp: 10 };
// Syntax error

이 구문이 에러를 일으키는 이유는 JavaScript 문법에 따라 엔진이 {로 시작하는 모든 구문을 블록(block) 구문으로 해석하기 때문입니다. (예를 들어, { console } 은 유효한 블록 구문입니다). 구문 에러를 해결하는 방법은 문장 전체를 괄호로 감싸는 것입니다.

1
2
({ safe } = {});
// No errors

사용사례

  • 함수의 인자 정의
  • 설정 객체의 파라메터
  • 다중 결과값을 반환하기

함수의 인자 정의

개발자로서 API를 만들 때, 여러개의 속성을 가진 객체를 파라메터로 전달 받는 것이 좀 더 나은 접근입니다. 디스트럭처링을 이용하면 API 함수 안에서 파라메터 속성을 참조할 때마다 파라메터 객체를 반복해서 사용하는 것을 피할 수 있습니다.

1
2
3
function removeBreakpoint({ url, line, column }) {
// ...
}

설정 객체의 파라메터

디스트럭처링하려는 객체의 속성에 디폴트 값을 줄 수도 있습니다. 이것은 설정값을 관리하는 객체가 있고, 각 설정값에 적절한 디폴트 값이 존재할 때 특히 도움이 됩니다.

다중 결과값을 반환하기

비록 여러개의 값을 리턴하는 기능이 랭귀지에 추가되지는 않았지만, 배열을 리턴하고 그 결과를 디스트럭처링할 수 있기 때문에 여러개의 값을 리턴하는 기능을 대신할 수 있습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function returnMultipleValues() {
return [1, 2];
}
var [foo, bar] = returnMultipleValues();
// foo: 1, bar: 2

function returnMultipleValues() {
return {
foo: 3,
bar: 4
};
}
var { foo, bar } = returnMultipleValues();
// foo: 3, bar: 4

4. spread 문법과 장점과 rest 문법은 무엇이며 차이점은 무엇인가

spread ⇒ 데이터를 풀어놓는다, rest ⇒ 전달받은 데이터를 배열, 객체 안에 채워넣는다

ES6의 전개(spread) 문법은 함수형 패러다임으로 코드를 작성할 때 매우 유용하다. 배열이나 객체의 복제본을 Object.createArray.prototype.slice , 또는 라이브러리 함수를 사용하지 않고도 간단하게 만들 수 있기 때문이다. 이 문법은 Redux나 RxJS 를 사용하는 프로젝트에서 자주 사용된다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function putDookieInAnyArray(arr) {
return [...arr, "dookie"];
}

const result = putDookieInAnyArray(["I", "really", "don't", "like"]);
// ["I", "really", "don't", "like", "dookie"]

const person = {
name: "Todd",
age: 29,
};

const copyOfTodd = { ...person };
// {name: "Todd", age: 29}

ES6의 rest 문법은 함수에 전달된 파라미터를 배열로 만들 수 있는 간단한 문법이다. 이것은 마치 spread 문법을 반대로 해놓은 것 같다. spread가 배열 안에 있는 데이터를 펼쳐놓는다면, rest 문법은 데이터를 받아서 배열에 채워넣기 때문이다.

이 문법은 함수 파라미터, 배열와 객체 destructuring에도 사용할 수 있다. destructuring에서 배열 데이터는 새로운 배열로, 객체 데이터는 새로운 객체로 만들어진다.

5. Javascript의 디자인 패턴에 대해 아는대로 설명해주세요.

디자인 패턴

디자인 패턴이란 무엇일까? 소프트웨어 개발을 하면서 발생하는 다양한 이슈들을 해결하는데 도움을 주는 일종의 증명된 기술들이다. 이미 많은 개발자들이 자바스크립트를 개발하면서 겪은 다양한 경험들을 바탕으로 만들어진 것들이다. 즉, 이런 상황에서는 이런 패턴을 사용하면 좋을거라는 일종의 방향성을 제시해준다. 패턴들은 정확한 해결책을 제공해주는 것이 아니다. 프레임워크나 라이브러리, 패턴 등은 그저 우리가 자바스크립트로 개발하는데 있어서 도움을 주는 도구일 뿐이고 이 패턴들을 어떻게 활용해서 어떤 식으로 개발한 것인지는 순전히 개발자의 역량에 달려있는 것이다.

디자인 패턴 종류

모듈 패턴, 싱글톤 패턴, 팩토리 패턴, Iterator 패턴, Decorator 패턴

6. 모듈 패턴과 전통적 상속, 각각의 장단점을 설명해보시오.

모듈 패턴

모듈 패턴(Module pattern)은 Javascript 디자인 패턴 중 하나로, 클로저 개념을 이용해 구현할 수 있으며 객체를 public/private 개념으로 나누는 캡슐화 및 은닉화가 핵심 이다. 매우 기본적인 방법으로는 모든 코드를 익명함수로 감싸 내부 스코프를 생성하고, private method는 코드 접근을 제한할 수 있을뿐만 아니라 추가적인 자바스크립트가 다른 스크립트와 이름이 충돌하는 것을 막을 수 있다.

하지만 이렇게 하면 코드를 재사용하기 불편해지기 때문에 별도의 네임스페이스(namespace) 를 적용해야 한다. 이는 global 영역에 객체 고유의 영역을 지정하고 변수와 함수 할당에 해당 네임스페이스(namespace)의 하위에 둬 중복된 name으로 인한 오류를 피할 수 있다.

모듈 패턴을 작성함에 있어서 return 구문을 이용하여 공개/비공개 영역과 내부적으로 처리할 영역을 구분하여 공개여부를 선택할 수 있습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
// namespace 패턴
var myApp = myApp || {}; // 네임 스페이스 선언

myApp.insanehong = function() {
return 'insanehong';
};

myApp.helloworld = function() {
return 'hello world';
}

// 모듈 페턴


var Messages = {h : 'hello', w : 'world', insane:'insanehong'};

var myApp = (function(msg) {
var helloworld = msg.h+' '+msg.w;
var helloinsanehong = msg.h+' '+msg.insane;

var printInsane = function () {
return helloinsanehong;
};

var printhello = function() {
return helloworld;
};

return {
foo1 : printInsane,
foo2 : printhello
};

})(Messages);


console.log(myApp.foo1());
> hello insanehong
console.log(myApp.helloworld);
> undefined

모듈 패턴은 전역 영역에서 특정 변수영역을 보호하기 위해 단일 객체 안의 public/private의 변수를 포함할 수 있는 각 클래스 형식의 개념을 구현하는데 사용된다. 이 패턴으로 추가적인 자바스크립트 객체가 다른 스크립트의 객체와 충돌하는 것을 줄여줄 수 있다.

Before module pattern

  • 모듈 패턴을 사용하지 않으면 해당 객체가 모두 전역에서 인스턴스화 되어 다른 코드와 충돌 가능성이 높아진다.
  • 객체 간의 연관성을 알기 어려워 코드관리가 어려우며 가용성이 떨어진다.
1
2
3
4
5
6
7
8
9
const count = 3
const publicMethod = function () {
console.log('Public Method : ', count);
}
const publicMethod2 = function () {
console.log('Public Method2 : ', count);
}
publicMethod();
publicMethod2();

After module pattern

  • 은닉화, 다형성, 상속을 통해 객체지향적으로 코드를 구성할 수 있다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Module {
private count: number = 3
private privateMethod() {
console.log('Private Method : ', this.count);
}
publicMethod() {
console.log('Public Method : ', this.count);
}
public publicMethod2() {
console.log('Public Method2 : ', this.count);
}
}
const mod = new Module();
// mod.privateMethod(); // coudn't access
mod.publicMethod();
mod.publicMethod2();

7. 네이티브 객체(Native Objects), 호스트 객체(Host Objects), Built-in 객체의 차이점은 무엇인가요?

  • 네이티브 객체 는 ECMAScript 명세에 정의된 브라우저 혹은 구동 엔진에 내장되어 있는 자바스크립트의 모든 엔진에 구현된 표준객체이다. 네이티브 객체는 built-in 객체와 달리 자바스크립트 엔진이 구성하고 있는 기본객체라고 하기보단 브라우저 혹은 사용되는 자바스크립트 엔진에 영향을 많이 받게 된다. BOM 이라는 브라우저 객체 모델과 DOM 이라는 문서 객체 모델이 네이티브 객체에 포함되는데 이 객체의 사용성이 이를 구현한 구동엔진에 따라 각기 다르게 존재하는 경우가 있기 때문에 크로스 브라우징에 문제를 발생시키기도 한다.

  • 호스트 객체 는 빌트인 객체와 네이티브객체에 포함되지 않은 사용자에 의해 생성된 객체를 의미한다. 자바스크립트 엔진은 빌트인 객체와 네이티브 객체를 구성한 이후 호스트객체를 해석하게 된다.

  • Built-in 객체는 자바스크립트 엔진을 구성하는 기본 객체들을 포함한다. Number, String, Array, Date 등의 내장객체들이 있다

요약

  • 네이티브 객체 : 브라우저 혹은 구동 엔진에 내장되어 있는 객체 DOM이 이에 해당 ⇒ 구동앤잔 별 사용성이 달라서 크로스 브라우징 문제를 바랭시키기도 함
  • 호스트 객체 : 빌트인 또는 네이티브 객체에 포함되지 않은 사용자에 의해 생성된 객체 ⇒ js는 빌트인 및 네이티브 객체 구성 후 호스트 객체를 해석

8. 이벤트 버블링, 이벤트 캡쳐링, 이벤트 위임(delegation, 딜리게이션)에 대해서 설명해달라

  • 이벤트 버블링은 특정 화면 요소에서 이벤트가 발생했을 때 더 상위 요소들로 전달되어 가는 특성을 의미합니다.
  • 이벤트 캡처링은 이벤트 버블링과 반대로 상위 요소에서 하위 요소로 탐색하며 이벤트를 전파하는 방식입니다.
  • 이벤트 위임은 이벤트 리스너를 하위 요소에 추가하는 대신 상위 요소에 추가하는 기법입니다. 리스너는 DOM의 event bubbling으로 인해 하위 요소에서 이벤트가 발생될 때마다 실행됩니다. 이 기술의 이점은 다음과 같습니다.
    • 이 방법은 동적인 요소들에 대한 처리가 수월하며
    • 이벤트 핸들러를 더 적게 등록해 주기 때문에 메모리도 절약할 수 있다.
    • 각 하위 항목에 이벤트 핸들러를 연결하지 않고, 상위 요소에 하나의 단일 핸들러만 필요하기 때문에 제거된 요소에서 핸들러를 해제하고 새 요소에 대해 이벤트를 바인딩할 필요가 없습니다.

이벤트 전파 방식

  1. 이벤트 발생(사용자 이벤트 혹은 강제 이벤트 할당:trigger)
  2. 이벤트 발생 객체를 찾기 위하여 하위 탐색(캡쳐)
  3. 이벤트 발생 객체 도달
  4. 하위 탐색의 역순으로 복귀(버블링)

IE의 경우 위와 같은 탐색에서 캡션단계를 지원하지 않음으로 이벤트 핸들링은 버블링을 기준으로 작성되어야 한다.

어찌 되었건 이벤트가 발생한 타겟에서 시작하여 상위로 해당 이벤트가 계속 해서 전파되어 버린다. 이를 버블링이라하며 이와 같은 버블링으로 인한 오작동을 방지 하기 위해서는 stopPropagation() 을 이용하여 이벤트 전파를 차단해 주어야 한다.

이벤트 흐름(Event flow)

HTML 요소가 다른 요소의 내부에 중첩되어 있을 때 자식 요소를 클릭하면 부모 요소를 클릭한 셈이 된다. 이처럼 이벤트는 흐름을 가지고 있으며, 이것을 이벤트 흐름이라고 부른다. 이벤트 흐름에는 두 가지 방식이 있다. 먼저 이벤트 버블링(Event bubbling)은 이벤트가 직접적으로 발생한 노드로부터 시작해 바깥 노드로 이벤트가 퍼져 나가는 방식을 말한다. 대부분의 브라우저가 기본적으로 이 방식을 지원한다. 반대로 이벤트 캡쳐링(Event capturing)은 바깥 노드부터 시작해서 안쪽으로 퍼지는 방식이다. IE8 혹은 그 이전 버전에서는 지원되지 않는다.

이벤트 위임 Case

IE까지 고려한 코드다. 하나의 이벤트 리스너로 요소 3개를 제어하고 있다다. jQuery는 보다 편하게 이벤트를 바인딩할 수 있도록 .delegate() 메소드를 제공하고 있다.

1
2
3
4
5
<ul id="todoList">
<li>TODO: A</li>
<li>TODO: B</li>
<li>TODO: C</li>
</ul>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
function getTarget(e) {
if(!e) { // event 객체가 존재하지 않으면
e = window.event; // IE의 event 객체를 사용
}

return e.target || e.srcElement; // 이벤트가 발생한 요소를 가져옴
}

function itemDone(e) {
var elTarget, elParent;

elTarget = getTarget(e); // 이벤트가 발생한 요소 가져옴 (li)
elParent = target.parentNode; // 해당 요소의 부모 요소를 가져옴 (ul)
elParent.removeChild(elTarget); // 이벤트가 발생한 요소를 제거함 (li)
}

(function(){
var el = document.getElementById('todoList');

if(el.addEventListener) { // 이벤트 리스너가 지원되면
el.addEventListener('click', function(e) { // 클릭 이벤트에 리스너를 지정
itemDone(e);
}, false); // 이벤트 버블링을 사용
} else { // 이벤트 리스너가 지원되지 않으면
el.attachEvent('onclick', function(e) { // IE의 onclick 이벤트를 사용
itemDone(e);
}
});
})();

9. document.write() 는 언제 사용하나요 ?

document.write()document.open()에 의해 열린 문서 스트림에 텍스트 문자열을 씁니다. 페이지가 로드된 후에 document.write()가 실행되면 document.open을 호출하여 문서 전체를 지우고 (<head><body>를 지웁니다!). 문자열로 주어진 매개 변수 값으로 대체합니다. 그러므로 일반적으로 위험하고 오용되기 쉽습니다.

document.write()가 코드분석이나 JavaScript가 활성화된 경우에만 작동하는 스타일을 포함하고 싶을 때 사용되는 경우를 설명하는 온라인 답변이 몇 가지 있습니다. 심지어 HTML5 보일러 플레이트에서 스크립트를 병렬로 로드하고 실행 순서를 보존할 때도 사용됩니다! 그러나, 저는 그 이유가 시대에 뒤떨어진 것으로 생각하고 있으며, 현재는 document.write()를 사용하지 않고도 할 수 있습니다.

10. 왜 load 이벤트와 같은 것을 사용하나요? 이 이벤트에는 단점이 있나요? 다른 대안을 알고 있나요? 알고 있다면 왜 그것을 사용할 건가요?

  • document load eventDOMContentLoaded event에 대한 질문

두 Event 모두 DOM 을 다루기 위한 이벤트 이다. 하지만 두 이벤트에는 큰 차이가 있다. DOM을 제어하는 스크립트는 마크업의 모든 요소에 대한 처리가 끝난 뒤에 로드되어야 한다. 그래서 보통 <body> 태그 최하단에서 스크립트를 불러오도록한다. 또 다른 방법은 이벤트를 이용하는 것이다.

document load event

  • 페이지의 모든 리소스가 로드된 이후에 실행된다.

  • 이 시점에서 문서의 모든 객체가 DOM에 있고, 모든 이미지, 스크립트, 링크 및 하위 프레임로딩이 완료됩니다.

  • 때문에 구동이 지연되어 사용자 경험을 저하할 수 있다.

DOMContentLoaded event

  • 스크립트 로드를 마치고 실행이 가능한 시점에 바로 실행된다.

  • DOM 이벤트 DOMContentLoaded는 페이지의 DOM이 생성된 후에 발생하지만 다른 리소스가 로딩되기를 기다리지 않습니다.

  • 이것은 초기화되기 전에 전체 페이지가 로드될 필요가 없는 경우에 선호됩니다.

11. attribute와 property

  • attribute : HTML 마크업에서 정의
  • property : DOM에서 정의

Attributes

HTML 요소의 추가적인 정보를 전달하고 이름=“값” 이렇게 쌍으로 온다. 예를 들어 <div class="my-class"></div> 를 보면 div 태그가 class 라는 값이 ‘my-class’인 attribute를 가지고 있다.

Property

attribute에 대한 HTML DOM 트리안에서의 표현이다. 그래서 위 예시에서 attribute는 값이 ‘my-class’이며 이름이 ‘className’인 property를 가진다.

attribute 와 property의 차이는 무엇인가?

Attributes는 HTML 텍스트 문서에 있는 것이고 properties는 HTML DOM 트리에 있는 것이다. 이것은 attribute는 변하지 않고 초기 (default)값을 전달한다는 것은 의미한다. 반면에 HTML properties는 변할 수 있다. 예를 들어 사용자가 체크박스를 체크했거나 input 박스에 텍스트를 넣었거나 JavaScript로 값을 변경하면 property의 값은 변한다.

Input 박스에 value를 타이핑한다는 시나리오를 예로 들면, property는 DOM안에 존재하고 동적이기 때문에 property의 값만 변경되었다. 그러나 attribute는 HTML 문서안에 존재하고 결코 변하지 않는다.

12. use strict 은 무엇이고, 사용했을 때 장단점에 관해서 설명해주세요.

‘use strict’는 전체 스크립트나 개별 함수에 엄격 모드를 사용하는데 사용되는 명령문입니다. Strict 모드는 JavaScript 다양한 자바스크립트를 제한하는 방법입니다.

장점:

  • 실수로 전역변수를 만드는 것이 불가능합니다.
  • 암묵적으로 실패한 예외를 throw하지 못하는 할당을 만듭니다.
  • 삭제할 수 없는 속성을 삭제하려고 시도합니다. (시도 효과가 없을 때까지)
  • 함수의 매개변수 이름은 고유해야합니다.
  • this는 전역 컨텍스트에서 undefined입니다.
  • 예외를 발생시키는 몇 가지 일반적인 코딩을 잡아냅니다.
  • 헷갈리거나 잘 모르는 기능을 사용할 수 없게 합니다.

단점:

  • 일부 개발자는 익숙하지 않은 기능이 많습니다.
  • function.callerfunction.arguments에 더 이상 접근할 수 없습니다.
  • 서로 다른 엄격한 모드로 작성된 스크립트를 병합하면 문제가 발생할 수 있습니다.

전반적으로 장점이 단점보다 중요하다고 생각합니다. 엄격 모드가 차단하는 기능에 의존하지 않아도 됩니다. 엄격한 모드를 사용하는 것을 추천합니다.

13. Functional Programming 이란 무엇인지 설명해주세요

객체 지향 프로그래밍에서는 멤버변수의 상태를 공유하고, 상태를 변경함으로써 예상치 못한 버그를 일으킬 수 있습니다. 이를 보안한 함수형 프로그래밍은 불변성 으로 선언한 값을 복사해 변경하므로, 반환되는 값이 예측이 가능하다는 장점이 있습니다. 또한, 상태를 공유하기보다는 반환되는 값을 이용해 함수를 사용하기에 프로그램의 실행에 있어 영향을 끼치지 않습니다.
이것을 고차 함수(High-Order Function), 순수 함수(Pure Function) 라고 합니다. 고차 함수1급 함수의 부분집합입니다. 따라서 함수를 변수에 할당할 수 있으며, 매개변수로 전달 할 수 있고 함수를 반환할 수도 있습니다. 이처럼 어떤 방법 으로 해야하는지를 나타내기보다 무엇 과 같은지 생각하는 것을 우리는 선언형 이라고 합니다.

14. 고차함수 (High-Order Function)란 무엇인지 설명해주세요.

고차 함수는 함수를 파라미터로 받거나 함수를 리턴하는 함수를 말한다. 반복적으로 실행되는 어떤 작업을 추상화시키는 수단으로 사용한다.

고차 함수의 고전적인 예제는 Array.prototype.map 함수다. 배열을 기반으로 새로운 배열을 만들 때 사용하는데, 이런저런 과정을 생략하고 맵핑 로직만 전달하면 되기 때문에 코드가 무척 간결해진다.

배열에 문자열로 구성되어 있고, 모든 요소에 toUpperCase를 적용해서 새 배열을 만들어야 한다고 하자. map 없이 구현하려면 아래와 같지만

1
2
3
4
5
6
7
8
9
10
const names = ["irish", "daisy", "anna"];

const transformNamesToUppercase = function (names) {
const results = [];
for (let i = 0; i < names.length; i++) {
results.push(names[i].toUpperCase());
}
return results;
};
transformNamesToUppercase(names); // ['IRISH', 'DAISY', 'ANNA']

.map(transformerFn) 함수를 사용한다면 무척 간단해진다.

1
2
3
4
const transformNamesToUppercase = function (names) {
return names.map((name) => name.toUpperCase());
};
transformNamesToUppercase(names); // ['IRISH', 'DAISY', 'ANNA']

Array의 forEach, filter, reduce 등도 모두 고차 함수다.

15. 자바스크립트의 배열(Array)이 실제 자료구조 배열이 아닌데 그 이유는?

참조: 자바스크립트 배열은 배열이 아니다 (https://poiemaweb.com/js-array-is-not-arrray)

16. 자바스크립트의 순환참조란? 어떤게 문제이고 해결방법은?

참조: 순환 참조 (https://jeonghwan-kim.github.io/dev/2020/03/24/circular-dependancy.html)

17. AJAX에 대해 설명해달라

정의

Asynchronous Javascript And XML의 약자로 브라우저가 가지고 있는 XMLHttpRequest 객체를 이용해서 서버와 브라우저가 비동기 방식으로 데이터를 교환할 수 있는 통신 기능으로, 데이터를 받아와 전체 페이지를 새로 고치지 않고도 페이지의 일부만을 위한 데이터를 불러올 수 있습니다.

장점

  • 웹페이지의 속도향상
  • XMLHttpRequest 를 통해 필요로 하는 일부 데이터만 JSON이나 XML형태로 갱신하기 때문에 그만큼의 시간과 자원을 아낄 수 있다.
  • 서버의 처리가 완료될 때까지 기다리지 않고 처리가 가능하다. => 비동기 처리 방식 지원

단점

  • Ajax로는 바이너리 데이터를 보내거나 받을 수 없다.
  • Ajax는 클라이언트가 서버에 데이터를 요청하는 클라이언트 풀링 방식을 사용하므로, 서버 푸시 방식의 실시간 서비스는 만들 수 없다.
  • XMLHttpRequest 를 통해 통신하는 경우, 사용자에게 아무런 진행 정보가 주어지지 않는다. (요청이 완료되지 않았는데 사용자가 페이지를 떠나거나 오작동할 우려가 발생하게 된다.)

동작원리

Ajax를 이용한 웹 응용 프로그램은 자바스크립트 코드를 통해 웹 서버와 통신을 하며 사용자의 동작에 영향을 주지 않으면서 백그라운드에서 지속해서 서버와 통신할 수 있다.

  1. 사용자에 의한 요청 이벤트 발생
  2. JS에서 XMLHttpRequest 객체를 사용해 서버로 요청 전송
    • XMLHttpRequest 는 비동기로 처리되기 때문에 요청을 전송하고 나서 웹 브라우저는 기다리지 않고 다른 작업을 처리할 수 있다.
  3. 서버는 전달받은 XMLHttpRequst 객체를 기반으로 AJAX 요청을 처리
  4. 처리된 HTML, XML 또는 JSON 형태의 데이터를 클라이언트측으로 전송
  5. 서버로부터 전달받은 데이터를 가지고 사용자 요청 이벤트를 처리하기 위한 Javascript Callback 함수 호출
  6. 사용자에 의한 요청 이벤트 완료

18. Same-Origin Policy 정책에 대해 설명해달라

Same-Origin Policy 정의

웹 어플리케이션 및 브라우저에서 중요한 보안 개념으로, 동일 출처의 리소스에 한해서만 데이터(DOM)에 접근할 수 있도록 하는 웹 브라우저 보안 정책으로, 동일 출처인지 판단하는 기준은 두 URL의 프로토콜, 포트 (명시한 경우), 호스트가 모두 같아야 동일 출처라고 말합니다.

동일 출처 판단 기준

URI 를 결정하는 알고리즘은 RFC 6454 에 명시되어 있으며 출처(Origin)는 Protocol, Host, Port 번호 등으로 구성된 정보를 의미합니다. 만약 이 정보들 중 하나라도 다를 경우, 동일 출처 원칙에 위배된 것으로 판단합니다.

아래 표는 URL http://store.company.com/dir/page.html의 출처를 비교한 예시입니다.

URL 결과 이유
http://store.company.com/dir2/other.html 성공 경로만 다름
http://store.company.com/dir/inner/another.html 성공 경로만 다름
https://store.company.com/secure.html 실패 프로토콜 다름
http://store.company.com:81/dir/etc.html 실패 포트 다름 (http://는 80이 기본값)
http://news.company.com/dir/other.html 실패 호스트 다름

만일 동일 출처 원칙에 위배되는 AJAX 요청을 전송했을 경우, 아래와 같이 전송된 요청에서 Access-Control-Allow-Origin 헤더가 없다는 에러 메시지지를 받게 됩니다.

XMLHttpRequest cannot load ‘https://store.company.com'. No ‘Access-Control-Allow-Origin’ header is present on the requested resource. Origin ‘http://store.company.com' is therefore not allowed access.

동일 출처 원칙을 사용하는 이유

가장 큰 이유는 브라우저 측면에서의 웹 어플리케이션 보안 강화입니다.

  1. XSS 와 같은 스크립트 삽입 공격을 통해서 출처가 다른 웹 어플리케이션에 접근을 방지
  2. 웹 어플리케이션은 인증된 사용자 세션정보를 HTTP Cookie에 담아서 광범위하게 사용하곤 하는데, 출처(Origin)가 다른 페이지에서 스크립트를 이용해 해당 쿠키정보를 추출할 수 있기 때문
  3. 데이터의 기밀성 또는 일관성을 유지하기 위해서 클라이언트 측에서 관계 없는 사이트에서 제공된 컨탠츠를 분리해야함

동일 출처 원칙 회피방법

  1. document.domain 속성 수정
    일부 제약이 있으나 페이지의 출처를 변경 가능. 스크립트로 document.domain 의 값을 현재 도메인이나 현재 도메인의 상위 도메인으로 변경할 수 있는데, 상위 도메인으로 설정한 경우 (더 짧은) 상위 도메인을 동일 출처 검사에 사용합니다.
  2. CORS(Cross-Orgin Resource Sharing)
    CORS는 Cross-Orgin Resource Sharing(상호 출처 자원 공유)의 약자로, 현재 접속중인 도메인의 출처와 다른 출처에 리소스를 요청 및 접근을 허가하는 웹 브라우저 보안 정책의 일환입니다. 이 방법은 서버단에서 처리해줘야 하는데, 요청된 리소스를 전송할때 HTTP 헤더에 Access-Control-Allow-Origin 헤더와 접근을 허가하는 값를 추가해 응답(Response) 메시지와 함께 전송하면 브라우저에서도 상호 출처 접근을 허가해 요청한 응답 리소스를 스크립트에서 사용할 수 있습니다.

19. 동일 출처 원칙을 회피하기 위한 방법으로는 무엇이 있으며 구체적으로 설정해달라.

상호 출처 공유 원칙(Cross-Origin Resource Policy) 이란

동일 출처 원칙 (Same-Origin Policy) 란 웹 어플리케이션 및 브라우저에서 중요한 보안 개념으로 동일 출처의 리소스에 한해서면 데이터에 접근할 수 있도록 하는 웹 브라우저 보안 정책입니다. 동일 출처 판단 기준은 Protocol, Host, Port 번호 등으로 구성된 출저가 모두 같아야 동일 출처라고 판단합니다.

  • Same-Origin: https://www.same.com => https://www.same.com
  • Cross-Origin: https://www.same.com => https://www.cross.com

그렇지면 현대의 웹 어플리케이션들은 동일 출처의 Resource 뿐만 아니고 상호 출처의 Resource에도 접근하며 뿐만 아니라 Client-Side와 Server-Side가 분리된 어플리케이션 구조를 띄고 있는 경우도 있습니다. 이럴 경우, 출처가 다르며 Same-Origin Policy 에러를 만나게 되는데, 이를 해결하기 위해 등장한 출처 원칙이 상호 출처 공유 원칙(Cross-Origin Resource Policy) 입니다.

상호 출처 공유 원칙(Cross-Origin Resource Policy) 이란

현재 접속중인 도메인의 출차와 다른 상호 출처에 리소스를 요청할때 상호 출처의 Server-side 응답에 추가된 Header Access-Control-Allow-Origin 헤더를 사용해 웹 브라우저측에서 상호 출처의 요청이 웹 어플리케이션에 접근을 허가하는 보안 정책의 일환으로, 웹 어플리케이션은 리소스가 자신의 출처(domain, protocol, port)와 다를때 Cross-Origin HTTP 요청 을 실행합니다.

CORS가 작동하는 방식

CORS가 작동하는 방식가 작동하는 방식으로는 총 3가지로, Simple requests, Preflighted requests, Requests with credentials이 있습니다.

Simple requests

아래의 조건을 모두 충족하는 요청의 경우, CORS Preflight를 요청하지 않으며 이를 Simple request라고 칭합니다.

  • 다음 중 하나의 메서드: GET, HEAD, POST
  • POST 방식일 경우 conte-type이 아래 셋 중 하나여야 한다.
    • application/x-www-form-unlencoded
    • multipart/form-data
    • text/plain
  • 유저 에이전트가 자동으로 설정 한 헤더 외의 다음과 같은 수동으로 설정할 수 있는 헤더만 설정한 경우,
    • Accept
    • Accept-Language
    • Content-Language
    • Content-Type (아래의 추가 요구 사항에 유의하세요.)
    • DPR
    • Downlink (en-US)
    • Save-Data
    • Viewport-Width
    • Width

Simple requests 예시)

한 웹 어플리케이션에서 다른 출처의 리소스에 대해 접근 및 요청시 클라이언트와 서버간에 간단한 통신을 하고 CORS 헤더를 사용하여 권한을 처리합니다.

이 경우 브라우저가 서버로 전송하는 내용중 요청 헤더의 Origin 이 해당 상호 출처에 접근할 수 있음을 나타내기 위해 서버는 이에 대한 응답으로 Access-Control-Allow-Origin 헤더를 다시 전송합니다.

가장 간단한 접근 제어 프로토콜은 Origin 헤더와 Access-Control-Allow-Origin 을 사용하는 것입니다. 이 경우 서버는 Access-Control-Allow-Origin: *, 으로 응답해야 하며 이는 모든 도메인에서 접근할 수 있음을 의미하고, Access-Control-Allow-Origin: https://foo.example 와 같이 응답할 경우 https://bar.other 의 리소스 소유자가 https://foo.example 의 리소스에 대한 접근 요청 허용을 의미합니다.

Preflighted requests

Simple Request 가 아닌 요청의 경우 Preflight Request 에 해당하며 클라이언트에서 상호 출처의 서버로 실제 요청을 보내기 전에 미리 보내는(preflight) 요청으로, OPTIONS HTTP 메서드로 실제 요청이 안전한지 확인하기 위한 목적을 가지고 있습니다.

Preflight 요청을 받은 상호 출처(Cross-Origin)는 OPTIONS 메서드를 통해 전달된 Origin 값에 따라서 접근을 허용할지 또는 특정 작업을 수행할 리소스 서버에 대한 접근 허용을 판별하며 접근 허용시 응답 헤더에 Access-Control-Request-* 헤더를 추가해 요청을 전송한 출처(Origin)에게 응답을 전송합니다.

1
2
3
4
Access-Control-Allow-Origin: http://same.com
Access-Control-Allow-Methods: POST, GET, OPTIONS
Access-Control-Allow-Headers: X-CUSTOM-HEADER, Content-Type
Access-Control-Max-Age: 86400

위 예시는 Preflight Request의 응답 헤더의 예로, Access-Control-Allow-Originhttp://same.com 의 접근 허가를 의미하고, Access-Control-Allow-Methods 는 상호 출처의 리소스에 접근할 수 있는 Methods를 의미합니다. 그리고 Access-Control-Allow-Headers 는 실제 요청에서 전송 가능한 사용자 정의 헤더로 대부분 필수로 포함해 전송해야하는 사용자 정의 헤더를 의미하고, Access-Control-Max-Age 는 Preflight Request에 대한 응답을 캐시할 수 있는 시간(초)을 제공합니다.

만약 Preflight Request로 허용되지 않는 요청임이 판명된다면 405 Method Not Allowed 에러를 반환하며 실제 요청은 상호 출처 서버로 전송되지 않습니다.

Requests with credentials (인증이 필요한 요청)

CORS Request는 기본적으로 Non-Credential 기반의 요청이지만 Cross-Origin에 인증 정보와 함께 리소스 접근 허가를 받아야 하는 경우를 Requests with credentials 이라고 합니다. 이 Request를 위해서는 인증 정보를 갖고 있는 쿠키를 실제 Request의 헤더에 담아 전송해야하며 총 두 가지 작업을 해줘야하고, 그 이후 쿠키와 함께 실제 Request를 전송할 수 있습니다.

  1. withCredentials: true 옵션을 추가
    Cross-Origin에 요청을 전송하는 Origin에서

    • Simple Request 의 경우에는 실제 Request의 헤더에 담아 Cross-Origin 측으로 전송하며 Cross-Origin 에서는 전송된 쿠키를 기반으로 인증 여부를 확인해 요청한 정보를 전달합니다.
    • Preflighted Request 의 경우에는 Preflight Request의 헤더에 담아 Cross-Origin 측으로 전송하며 2 번 으로 넘어갑니다.
  2. (Preflight의 경우에만) Cross-Origin 에서 응답 헤더에 Access-Control-Allow-Crendentials: true 헤더를 추가해 전송
    1 번을 성공한 Preflighted Request의 경우 Cross-Origin 에서 응답 헤더에 Access-Control-Allow-Crendentials: true 를 추가해 요청한 Origin 에게 응답을 전송하며 Origin 은 쿠키 정보를 담은 헤더와 함께 실제 Request를 보낼 수 있습니다.

“Access-Control-Allow-Origin’ header in the response must not be the wildcard ‘*’ when the request’s credentials mode is ‘include”

주의사항으로는 Preflighted Requests 의 경우, Preflight Request의 응답헤더에 Access-Control-Allow-Origin : "*" 으로 헤더에 추가 및 설정할 경우 위와 같은 에러를 유발하므로 요청한 Origin 으로 또는 와일드카드가 아닌 명시적 값으로 반드시 설정해야 합니다.


참조