최근 프로젝트를 구현하던 중 DOM 요소의 text를 바꾸는 데도 innerHTML을 쓰는 것으로 피드백을 받은적이 있습니다.

 

그래서 어떠한 경우에 innerHTML을 써야 하는지 비슷한 대체 기능들은 언제 사용하여야 하는지를 알아 보려 합니다.

 

innerHTML

const content = htmlString;

element.innerHTML = content;

와 같은 방식으로 사용할 수 있습니다. 

위의 코드를 실행하면 element 내부의 모든 자손은 제거되고 문자열 htmlString의 HTML들을 파싱하고 생성된 노드로 대체됩니다.

(HTML 로 파싱되기 때문에 HTML의 양식에 맞아야 합니다)

before
after

 

innerHTML은 보안상의 허점을 가지고 있습니다.

 

스크립트 태그가 추가되는 경우가 있을수 있습니다.

HTML5 에서는 innerHTML과 함께 추가된 <script>태그는 실행하지 않게 하고 있습니다.

코드가 추가는 되었지만 alert의 실행은 되지 않습니다.

 

하지만 <script> 태그 없이 javascript 실행을 할 수 있는 방법이 있습니다.

const name = "<img src='x' onerror='alert(1)'/>"
document.querySelector('#test').innerHTML = name

(위의 방법으로 공식문서 에서 안내해주고 있는데 alert의 실행은 되지 않네요)

 

그래서 일반 텍스트를 변경하는 경우에는 textcontent

요소의 내용을 변경하는 것이 아닌 HTML을 문서내에 삽입하는 것이라면 insertAdjacentHTML을 사용하라고 알려주고 있습니다.

 

 


insertAdjacentHTML

insertAdjacentHTML은 주어진 텍스트를 파싱하고 특정 위치에 node들을 추가할 수 있습니다.

element안에 존재하는 다른 node들은 건드리지 않습니다(innerHTML과 다른 점).

 

네개의 위치에 원하는 요소를 넣어줄 수 있습니다.

beforebegin, afterbegin, beforeend, afterend

 

 


textContent

document.querySelector('#test').textContent = 'test'

textContent는 주어진 string을 파싱하지 않고 raw하게 바로 넣어줍니다.

raw 하게 그대로 들어간 모습

 

파싱을 하지 않으므로 텍스트 컨텐츠만 변경해주어야 하는 경우라면 innerHTML을 쓰는 경우보다 먼저 선택되어야 합니다.

 


innerText

innerText는 textContent와 비슷해 보이지만 차이점을 가지고 있습니다.

 

innerText는 텍스트의 렌더링 후 모습을 인식할 수 있지만 textContent는 인식할 수 없습니다.

=> 화면에 보이는 그대로의 text를 가져올 수 있다는 이야기입니다.

 

dom 요소중 위와 같은 형식의 요소가 있다고 하겠습니다. 

이를 innerText, textContent로 가져오게 되면

아래 사진과 같이 결과값이 나오게 됩니다.

 

source태그는 보이지 않게 되고  <br>태그는 인식되어 /n으로 변경된 모습을 볼 수 있습니다.

 

 

 

4개의 요소(innerHTML, insertAdjacentHTML, textContent, innerText)를 인지하고 적절한 때에 적절한 위치에 사용하는 것이 중요할 것 같습니다.

 

 

 

https://developer.mozilla.org/ko/docs/Web/API/Element/innerHTML

 

Element.innerHTML - Web API | MDN

Element 속성(property) innerHTML 은 요소(element) 내에 포함 된 HTML 또는 XML 마크업을 가져오거나 설정합니다.

developer.mozilla.org

 

via GIPHY

 

Explain event delegation

=>  이벤트 위임은 부모 DOM 요소에 이벤트 핸들러를 등록하여 자식 DOM 요소를 다루는 것을 말합니다.

자식요소에서 이벤트가 발생하고 이것이 이벤트 전파(캡처링 단계, 타깃 단계, 버블링 단계)의 버블링 단계를 통해 부모요소로 전파가 될때 이벤트 핸들러가 동작하게 됩니다.

 

이벤트 위임을 사용하게 되면 두가지 이점을 얻을 수 있습니다.

 

1. 성능을 향상 시킬수 있습니다. 만약 자식요소가 100개라면 100개 요소에 이벤트 핸들러를 등록해주어야 합니다. 이는 사용량의 저하를 초래합니다.

1. 자바스크립트에서 함수는 메모리를 잡아먹는 객체 입니다. 메모리를 많이 사용할수록 성능은 떨어집니다.
2. 이벤트 핸들러를 많이 할당하려면 DOM 접근도 많아집니다. 이는 웹의 접근성을 떨어 뜨릴수 있습니다.

2. 유지보수가 간편해 집니다.

 

고려해야 할 점은 

1. 이벤트 핸들러 내부에서 동작을 수행하고자 하는 특정 DOM요소를 지칭해주어야 합니다. 이벤트 전파 경로에 있는 모든 DOM 요소들에 버블링이 일어나게 되므로 원하지 않는 DOM요소에서 이벤트 핸들러가 동작하게 될 수도 있습니다.

 

 


 

Explain how this works in Javascript

=> this는 자신이 속한 객체나 자신이 생성할 인스턴스를 가리키는 자기참조 변수 입니다.
자바스크립트에서 this는 함수의 호출을 기반으로 이루어 집니다(함수가 선언되는 위치가 아님).

함수가 호출되는 실행컨텍스트가 바로 this의 값이 됩니다. 

 

같은 함수라도 this는 서로 다른 값을 나타낼 수 있습니다.

이는 this가 동적으로 binding 되기 때문입니다.

바인딩은 식별자와 값이 연결되는 과정을 말합니다.

 

1. 일반함수로서 호출

2. 메서드로서 호출

3. 생성자 함수로서 호출

4. Function.protype.call, apply, bind를 통한 호출

5. 이벤트 핸들러 함수에서 this는 이벤트 호출 객체를 가리킵니다(추가된 내용)

var foo = function () {
	console.dir(this);
}

// 일반함수로서 호출되는 this는 window를 가리킵니다. node에서는 global, use strict 모드에서는 undefined
foo() // window

var obj = { foo };

// 메소드로서 호출될 경우 this는 호출한 객체를 가리킵니다.
obj.foo(); // obj { foo }


// 생성자 함수로서 호출될 경우 this는 생성될 인스턴스를 가리킵니다.
// 일반 함수에 new를 붙여서 호출하면 생성자 함수가 됩니다.
new foo() // foo 정보

var bar = { name: 'bar' }

// Function.prototype.call, apply, bind를 통해 명시적으로 this를 binding 할수 있습니다.
foo.call(bar)  // name: 'bar' -> bar 객체에 대한 정보
foo.apply(bar) // name: 'bar' -> bar 객체에 대한 정보
foo.bind(bar)() // name: 'bar' -> bar 객체에 대한 정보

 

es2015(es6) 에서 등장한 화살표함수는 this에 대한 binding이 없습니다.

const Person = {
	age: 0,
    older() {
    	setTimeout(() => {
        	this.age++;
            console.log(this.age);
        }, 100)
    }
}

const People = {
	age: 0,
    older() {
    	setTimeout(function(){
        	this.age++;
            console.log(this.age);
        }, 100)
    }
}

Person.older() // 1
People.older() // NaN

 

People setTimeout의 this는 전역객체를 binding 합니다.(setTimeout 함수는 동기적으로 실행되는 것이 아닌 이벤트 큐에 들어갔다가 콜스택이 비면 이벤트 루프에 의해 큐에서 스택으로 넘어가서 실행이 된다.)

Person setTimeout내에 있는 화살표 함수는 자신의 this가 없습니다. 화살표 함수는 일반 변수의 스코프체인과 같이 this를 찾게 됩니다. 자신의 범위에 this가 없다면 바로 바깥 범위에서 this를 찾게 됩니다. 이때의 this는 메소드를 호출한 Person을 가리키게 될겁니다. 

 


 

Explain how prototypal inheritance works

=> 두개의 코드중 아래의 코드는 프로토타입 상속을 이용하여 같은 기능을 구현한 것입니다.

첫번째 방법은 두개의 인스턴스는 name, say 두개의 property를 가지고 있습니다.

하지만 두번째 방법으로 생성한 두개의 인스턴스는 name property만 가지고 있습니다. 

function Person(name){ 
	this.name = name, 
    this.say = function() {
    	console.log(this.name)
    }
 }
 
 const me = new Person('me');
 const you = new Person('you');
 
 me.say() // me
 you.say() // you
function Person(name){ 
	this.name = name
}

Person.prototype.say = function () {
	console.log(this.name);
}
 
 
const me = new Person('me');
const you = new Person('you');
 
me.say() // me
you.say() // you

그러나 두 코드의 say 함수 호출결과는 같습니다.

이것이 가능한 이유는 Prototype 상속을 하였기 때문입니다.

 

자바스크립트의 객체는 __proto__라는 숨겨진 프로퍼티를 가집니다. 이 프로퍼티는 null 혹은 참조 객체를 가리키고 있습니다. 이 참조 대상을 프로토타입이라고 부릅니다. 

자바스크립트에서는 호출된 메서드가 해당 객체에 존재하지 않을 경우 해당 객체의 프로토타입에 접근하여 메소드를 차례로 검색합니다.

그리고 발견하면 호출, 없으면 undefined를 출력하게 됩니다.

우리는 이것을 프로토타입 체인이라 부릅니다.

 

> new로 생성된 두개의 객체에서 __proto__를 통해 부모 객체에 접근할 수 있습니다.

 

 


 

What do you think of AMD vs CommonJS

=> AMD(Asynchronous Module Definition)와 CommonJs는 둘다 자바스크립트에서 모듈을 선언하는 방법에 대한 명세 입니다.

 

CommonJS는 동기로 동작을 하고 (비동기로 사용을 할 수도 있습니다) 서버 에서 자주 사용합니다. node에서 체택하였습니다.
javascript를 브라우저가 아닌 곳에서도 사용해보자는 취지에서 등장하였습니다.

 

필요한 파일이 모두 로컬 디스크에 있어 바로 불러 쓸수 있는 서버사이드와 같은 상황에서는 commonjs를 주로 사용합니다.

네트워크를 통해 받아와야 하는 브라우저 상황에서는 좀 더 유연한 처리가 필요했습니다. 그래서 commonjs에서 독립되어서 나온것이 AMD(Asynchronous Module Definition)입니다. 

 

AMD에서는 define 함수를 통해 파일단위의 스코프를 제공해 줍니다.

AMD는 비동기로 동작을 하고 브라우저의 비동기 처리를 다루기 위해 commonJS에서 파생되었습니다. 대표적으로 requirejs가 있습니다.

 

 

브라우저에서는 es2015 모듈을 자주 사용하지만 Es2015모듈은 IE와 같은 특정브라우저에서는 동작하지 않아 babel과 같은 transpiler를 이용하여 commonJs로 컴파일 하여 사용합니다.

 

기존의 모듈을 사용하지 않고 script로 일일히 불러오게 되면 전역변수의 관리등 애로사항이 많게 됩니다.

따라서 모듈 시스템이 등장하게 되었습니다.

모듈 시스템은 객체 단위의 스코프를 가지게 됩니다.

 


 

Explain Why the following doesn't work as an IIFE: function foo(){}(); What needs to be changed to properly make it an IIFE??

=> 자바스크립트 엔진에서 function foo(){} 은 함수 선언식으로 인식이 되고 파싱이 됩니다.

()만 남게되어 에러가 발생하게 됩니다. 

 

IIFE(Immediately Invoked Function Expression) 으로서 사용을 하기 위해서는 ()로 전체를 감싸주거나 함수 선언식을 감싸주어 함수 표현식으로 변경해 주어야 합니다. 

즉시 실행 함수에서는 기명함수로도 익명함수로도 동작할 수 있습니다.

즉시 실행 함수를 사용하는 이유는 전역변수에 불필요한 변수를 추가하지 않기 위해 사용합니다.

맨처음 시작시 변수들을 초기화 하거나 와 같을때 사용하곤 합니다

(function(){})() // undefined
(function(){}()) // undefined

(function foo(){})() // undefined
(function foo(){}()) // undefined

 


 

 

What's the difference between a variable that is:null,undefined or undeclared?
How would you go about checking for any of these states?

null은 변수를 선언을 한후에 빈 값으로 처리된 상태입니다.

undefined는 선언은 되었고 초기화까지 된 상태 입니다.

undeclared는 선언조차 되지 않았다는 것을 의미합니다.

 

자바스크립트 엔진은 변수를 해석할때 세개의 단계를 거칩니다.

1. 선언

2. 초기화

3. 할당

 

선언은 변수로 선언할 대상들을 목록화 한것으로 알고 있습니다.

초기화는 변수에 물리적인 메모리 공간을 할당하는 것인데 이때 undefined를 통해 아직 실제 값이 할당되지 않았다는 것을 나타냅니다.

할당은 변수에 실제 값을 할당해주는 것입니다.

// 선언, 초기화
var foo;
console.log(foo) // undefined

// 할당
foo = 3;
console.log(foo) // 3

 

 

아래의 코드로 세개의 상황을 체크 하는 것을 확인할 수 있습니다.

console.log(x) 
// x 변수는 선언된 적이 없으므로 referenceError가 출력되게 됩니다.

var foo;
console.log(foo); // undefined
console.log(foo === undefined) // true
console.log(typeof foo === 'undefined') // true

var foo = null
console.log(foo === null) // true
console.log(typeof foo === 'object') // true

// 주의해야할점
console.log(undefined == null) // true
console.log(undefined === null) // false

== 비교연산자를 사용하게 되면 undefined == null은 true를 리턴합니다.

undefined과 null을 비교해야할시 정확한 결과를 얻고 싶다면 === 를 사용해야 합니다.

 

==는 타입을 친절히 변환해주어서 결과를 리턴해주기 때문에 ===를 통해 타입과 값까지 비교해줄수 있도록 해야합니다.

console.log(0 == []) // true
console.log(0 === []) // false

 


 

What is a closure, and how/why would you use one?

클로저는 이미 생명주기가 끝난 함수의 변수를 참조하는 것입니다.

스코프체인 참조되는 변수 객체가 계속 남아있어서 가비지 컬렉터에 의해 제거되지 않습니다.

아래는 코드로 클로저를 구현한 예시 입니다.

function outerFunc() {
	var count = 0;
    function innerFunc() {
    	count += 1;
        console.log(count);
    }
    
    return innerFunc
}

var innerFunc = outerFunc() // outerFunc의 실행 컨텍스트는 종료
innerFunc() // 1
innerFunc() // 2

 

클로저는 주로 private한 변수를 사용하고자 할때 사용합니다.

외부에서 해당 변수에 접근을 할 수가 없기 때문에 private의 성격을 띌수 있습니다.

 


 

Can you describe the main difference between a.forEach loop and a.map() loop and why you would pick one versus the other?

map과 forEach의 차이점은 새로운 배열을 return 해주는 것에 있습니다.

forEach는 단순히 배열을 순회하고자 할때

map은 원래의 값을 변화시키지 않으면서 새로운 배열을 얻고자 할때 사용합니다.

 

forEach

아래의 코드와 같이 value와 index를 통해 값을 체크하고 배열을 순회할 수 있습니다.

const test = [1,2,3,4,5]

test.forEach((value, index) => { // 액션 실행 })

 

map 

아래의 코드와 같이 새로운 부수 배열을 만들어 낼수 있습니다.

test 배열의 변화 없이 새로운 배열을 뽑아낼 수 있습니다.

const test = [1,2,3,4,5]
const testArray = test.map((el, index) => el % 2 === 0 ? el : 0)
console.log(testArray) // [0, 2, 0, 4, 0]

 


 

 

What's a typical use case for anonymous functions?

익명함수를 사용하는 경우는 크게 3가지 입니다.

1. 즉시 실행 함수

2. 콜백함수

3. 함수형 프로그래밍

 

1. 즉시 실행 함수 

=> 즉시 실행 함수 에서는 익명함수를 사용할 수 있습니다. 물론 기명함수 또한 사용할 수 있습니다.

즉시 실행 함수를 사용함으로써 전역범위의 스코프를 오염시키지 않으면서 원하는 동작을 수행할 수 있습니다.

(function () {})()

 

2. 콜백 함수

=> 콜백함수로서 사용합니다.

콜백함수는 호출될때만 실행되고 다른곳에선 쓰이지 않는다는 특성이 있습니다.

콜백함수에 전역으로 생성된 함수를 전달할 수도 있지만 아래와 같이 익명함수를 넣어줌으로써 전역 범위에 함수를 생성하지 않고 원하는 동작을 수행할 수 있습니다. 

아래의 코드에는 기명함수를 넣어주어도 동작합니다.

setTimeout(function(){
		console.log(this)
    }, 1000)

 

3. 함수형 프로그래밍

함수형 프로그래밍을 사용하기 위해 익명함수를 사용합니다.

함수형 프로그래밍의 작성에 있어 필요한 순수함수를 작성하는데 이용할 수 있습니다.

const numbers = [1,2,3,4,5]
const even = numbers.filter(function(el) { if(el % 2 === 0) { return el} })
console.log(even); // [2,4]

 

함수형 프로그래밍이란 기존의 명령형 프로그래밍이 아닌 선언형 프로그래밍에 집중하는 것입니다.

명령형 프로그래밍이 어떻게 하는 것에 focusing을 두고 있다면 

함수형 프로그래밍은 무엇을 하는 것에 focusing을 두고 있습니다.

 

순수함수란 동일한 입력값에는 같은 결과값을 호출하여 주고 외부의 인자를 참조하지 않는 것입니다.

const add = function(num1, num2){ return num1 + num2 } 

add(1,2) // 3
add(2,3) // 5

 

함수형 프로그래밍과 순수함수에 관하여는 따로 상세히 포스팅 할 수 있도록 하겠습니다.

 


 

How do you organize your code? (module pattern, classical inheritance?)

 

프론트는 주로 React를 사용하며 모듈패턴을 항상 사용합니다. 

백엔드를 구성할때 rails와 javascript로 구성할때가 있는데 이때는 상속패턴을 기반으로 Module을 적용하여 사용합니다.

 

rails 에서는 주로 모델에서 include를 통해 모듈들을 불러와 사용하고 

javascript에서는 es6 방식을 이용하여 모듈을 불러옵니다.

 

javascript에서 nestjs를 사용할때는 모델의 속성을 정의해야 할때가 있습니다.

이때 created_at, updated_at, deleted_at과 같은 공통된 속성들을 가지는 클래스를 생성해 둡니다.

이후 새로 모델을 추가할때마다 이 클래스를 extends 해주어 반복되는 코드량을 줄입니다.

 

cli 도구를 만들어야 했을때는 추상클래스를 이용해 클래스들이 수행해야 하는 메소드를 정의해 두었습니다.

이 추상클래스를 생성해두고 비슷한 역할을 해야하는 클래스들에서 implements를 이용해 상속을 받았습니다.

이렇게 하고 개별 클래스에서 메소드를 override해주었습니다. 

새로 생성되는 클래스들의 메소드를 통일할 수 있었고 코드의 파악이 쉬워졌던 경험이 있습니다.

 

 


 

 

What's the difference between host objects and native objects?

 

네이티브 객체는 Ecma 스크립트에 정의된 객체를 말합니다. 

네이티브 객체는 어플리케이션의 환경과 상관없이 사용이 가능합니다.

Number, Boolean, String, Function, Array, RegExp 등과 같은 것들이 있습니다.

 

호스트 객체는 런타임 환경에서 제공해주는 객체를 의미합니다. 

window, XMLHTTPRequest, HTMLElement등의 DOM 노드 객체와 같은 호스트 환경에 정의된 객체들을 뜻합니다.


 

Difference between:function Person(){}, var person = Person(), and var person = new Person()?

 

function Person() {}

함수를 선언 하는 것입니다. 함수 선언식으로 불리기도 합니다.

호이스팅이 발생하며 전역범위에 함수가 등록됩니다.

함수의 스코프는 선언되는 위치에 따라 결정됩니다.(this의 값은 호출되는 위치에 따라 달라집니다.)

Person()으로 호출할 수 있습니다.

 

var person = Person()

함수 표현식을 나타내려고 한건지 결과값만 저장하려고 한건지 Person에서 내부함수를 리턴해주는 것인지 모르겠습니다.

함수 표현식으로 사용하면 var의 person값만 호이스팅이 되게 됩니다.

선언과 초기화는 이루어지고 할당은 나중에 됩니다.

 

Person에서 내부함수를 리턴해준다면 함수 표현식이 될수도 있고 이러한 경우 클로저의 개념을 사용할 수도 있습니다.

 

var person = new Person()

함수를 생성자 함수로서 호출하는 것입니다.

새로운 인스턴스를 생성해냅니다. 

이때 생성자 함수내부의 this는 생성되는 객체를 가리키게 됩니다.

생성자 함수를 호출할 때 인자를 같이 넣어주고 this.name 과 같이 property를 연결해주곤 합니다.

 


 

What's the difference between .call and .apply?

 

.call과 .apply는 this를 바인딩 할때 사용합니다.

call은 함수의 인자를 하나씩 순서대로 넘겨주며
apply는 함수의 인자를 배열로 넘길수 있다는 차이점이 있습니다.

var foo = function(a, b){
	console.log(this.name);
	console.log(a);
	console.log(b);
}


var name = 'window'

var obj = {
	name: 'test'
}

foo(1,2)     // window, 1, 2
foo.call(obj, 1, 2) // 'test', 1, 2
foo.apply(obj, [1,2]) // 'test', 1, 2

 

 


ExplainFunction.prototype.bind

bind 메소드는 함수내의 this를 같이 넘겨준 인자로 할당해 줍니다.

리턴값은 this가 바인딩된 메소드를 리턴해 줍니다.

따라서 리턴후 즉시 실행을 시켜주려면 ()문을 추가해주어야 합니다.

()안에 인자를 넣어주면 함수에 인자를 넘기는것과 같습니다.

var foo = function(a, b){
	console.log(this.name);
	console.log(a);
	console.log(b);
}


var name = 'window'

var obj = {
	name: 'test'
}

foo.bind(obj) // undefined
foo.bind(obj)() // test, undefined, undefined
foo.bind(obj)(1,2) // test, 1, 2

 

 


 

When would you use document.write()?

document.write는 IE에 대한 호환성 문제나 스크립트의 병렬적인 로딩, 분석코드의 실행이 필요할때 사용합니다.

저는 사용해본 적이 없습니다.

 

document.write를 실행하면 document.open을 통해 새창이 열리고 인자로 받은 내용들이 컨텐츠로 보이게 됩니다.

<script> 태그로 감싼후 사용하게 되면 파라미터 인자만 화면에 출력하게 해줍니다.

함수의 실행으로 실행하게 되면 새창이 열리게 됩니다.


What's the difference between feature detection, feature inference, and using the UA string?

feature detection은 기능이 실제로 있는지 프로그래밍 적으로 테스트하여 사용하는 기술입니다.

아래 코드는 런타임 환경에서 navigator에 geolocation이 있으면 a 없으면 b를 수행하게 할 수 있습니다.

if ('geolocation' in navigator) {
  // do a 
} else {
  // do b
}

위의 코드는 여러 브라우저의 오래된 버전에서 b를 수행하게 될 것입니다.

 

feature inference는 용어 그대로 기능 추론입니다.

만약 a가 b를 가진다면 당연히 c도 가지고 있겠지와 같은 방식입니다.

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

 

ua 문자열은 요청한 브라우저가 어느 브라우저에서 요청했는지에 대한 정보를 가지고 있습니다.

핸드폰인지 tablet인지 또한 알수 있고 브라우저의 종류또한 알수 있습니다.

이 ua 문자열을 통해 안드로이드 폰에서는 애플로그인이 노출 안되게 할 수도 있고 인터넷 익스플로러를 지원하지 않는 서비스라면 지원하지 않는 서비스라는 정보를 띄워줄 수도 있습니다.

 

navigator객체의 userAgent 메소드로 정보를 가져올 수 있습니다.

 


Explain Ajax in as much detail as possible.

Ajax(Asynchronous javascript and Xml)는 javascript를 이용하여 서버와 비동기적으로 통신하기 위한 웹 개발 기술입니다.

ajax를 이용하면 전체 페이지의 로드 없이 요청을 보낸 후에 응답으로 원하는 페이지의 일부분만 받아온 데이터로 보여줄 수 있습니다.

 

XMLHttpRequest 객체를 이용하면 서버에 요청을 보낼수 있습니다.

jquery에서 또한 가능하고 

fetch 함수를 이용하면 쉽게 handling 할수 있습니다.

axios와 같은 라이브러리를 이용하는 것도 좋은 방법입니다.

 

 


What are the advantages and disadvantages of using Ajax?

ajax를 이용하면

전체 페이지의 변경없이 필요한 부분만 최신화된 정보를 보여줄 수 있습니다.

이를 통해서 웹페이지의 속도를 향상시키고, 페이지 새로고침 없이 변경된 정보를 보여줄 수 있습니다.

 

단점은 

브라우저에서 javascript 동작이 불가능하면 동작하지 않습니다.

서버요청이 더 많아질 수 있습니다.

하나의 요청이 실패하게 되면 전체 페이지에 문제가 생길수도 있습니다.

 

 


Explain how JSONP(JSON with Padding) works (and how it's not really Ajax).

 

jsonp는 다른 도메인의 서버로부터 데이터를 요청하기 위해 사용됩니다. (동일출처 정책을 우회하기 위한 꼼수로 이해했습니다.)

get 방식만 가능합니다.

 

웹 브라우저에서 실행되는 자바스크립트는 동일 출처 정책으로 인해 스크립트 태그 내에서 XMLHttpRequest 등으로 다른 도메인의 정보를 받아오는 것이 불가능 합니다.(요청은 가지만 브라우저단에서 해당 요청을 block 시킵니다)

var xmlhttp = new XMLHttpRequest();
xmlhttp.open('GET', 'http://server.example.com/Users/1234', true);
xmlhttp.onload = function () {
  console.log('Retrieved Data: ' + xmlhttp.responseText);
};
xmlhttp.send(); // -> 교차 출처 요청 차단

위의 코드는 앞서 언급한 것과 같이 동일 출처 정책에 따라 오류가 발생합니다.

이는 아래와 같이 변경하면 원하는 대로 사용할 수 있습니다.

<script type="application/javascript"
        src="http://server.example.com/Users/1234?callback=parseResponse">
</script>

 

parseResponse를 빼게 되면 에러가 발생합니다. 

웹 브라우저의 javascript 엔진이 변수, 상수 정의등의 특정한 상황 없이 나오는 중괄호 문법을 block으로 해석하기 때문입니다.

 

외부 서비스 server.example.com은 JSON 데이터를 패딩하여 클라이언트에 보냅니다.

parseResponse({"Name": "Foo", "Id": 1234, "Rank": 7});

 

브라우저에서는 이 응답을 사용할 수 있게 됩니다.

 

JSONP를 직접 사용해본적은 없고 CORS로 대체되고 있는 것으로 알고 있습니다.


Have you ever used JavaScript templating? If so, what libraries have you used?

script에 type 속성에 text/template을 주어 html에 위치 시킨뒤에 document.querySelector를 이용하여 template을 가져온 후

원하는 내용을 replace해주어 화면에 나타낸적이 있습니다.

 

라이브러리는 handlerbar, template7, jsx에서 사용해 본 적이 있습니다.

es6가 나온이후에는 template literal을 활용하여 원하는 데이터를 나타낼 수도 있습니다.

 


Explain "hoisting".

javascript에서 var, let, const, function, class 등 모든 선언자는 자바스크립트 엔진에 의해 자신의 스코프 내에서 최상단으로 끌어올려 집니다.

var는 함수 스코프, let, const는 블록 스코프 입니다.

let,const는 호이스팅이 되면 TDZ(Temperal Dead Zone)에 위치하게 됩니다.

값을 할당하기 전에는 사용이 불가능 합니다.

const는 변수를 선언시 할당또한 같이 해주어야 하며

let은 선언후에 나중에 할당해줄수 있습니다.

위의 이미지는 var가 호이스팅 되었다는 것을 보여줍니다. 

위의 코드에서 people이 호이스팅이 되면

아래와 같이 코드가 구성되게 됩니다.

var people;

console.log(people) // undefined

people = "rick"

console.log(people) // rick

 

함수 선언식 또한 호이스팅 됩니다.

아래의 함수 sum은 호이스팅이 됩니다.

console.log(sum(1,2)) // 3

function sum(a,b) {
	return a + b;
}

console.log(sum(2,3)) // 5

아래는 호이스팅 된 함수 입니다.

function sum(a,b) {
	return a + b;
}

console.log(sum(1,2))

console.log(sum(2,3))

그래서 함수 표현식을 권고합니다.

console.log(sum(1,2)) // error occured => sum is not a function

var sum = function (a,b) {
	return a + b;
}

console.log(sum(2,3))

 

var로 선언된 sum은 호이스팅이 되지만 변수에 할당은 나중에 됩니다.

그래서 함수표현식으로 사용하는 경우 

코드의 하단에서 상위에 정의되어 있는 함수들을 override하지 않을수 있습니다.

 

아래는 const로 선언한 변수 또한 호이스팅 되는 예시 입니다.

만약에 호이스팅이 되지 않았다면 foo는 스코프 체인에 따라 상위 스코프의 foo에 할당된 3을 출력해야 할것입니다.

const foo = 3 
{
	console.log(foo) // cannot access 'foo' before initialization
	const foo = 3;
}

Describe event bubbling.

dom 요소에서 이벤트가 발생하면 해당 이벤트는 상위의 요소들로 계속 전달되어 집니다.

이 전달은 최상위 요소인 body 태그까지 전달됩니다.

 

dom 요소에서 이벤트(click과 같은)가 발생하고 만약에 이벤트 핸들러가 해당 요소에 있다면 이벤트를 핸들링 합니다.

<body>
	<div class="one">
    	<div class="two">
        	<div class="three"></div>
        </div>
    </div>
</body>
var divs = document.querySelectorAll("div")

divs.forEach((div) => {
	div.addEventListener('click', test)
})

function test(event) {
	console.log(event.currentTarget.classList);
}

와 같이 작성한후 three를 클릭하게 되면 

three, two, one 을 차례대로 출력하게 될것입니다.


 

What's the difference between an "attribute" and a "property"?

attribute 속성은 html markup에 정의되는 값이고 property는 dom 객체에서 사용됩니다.

 

위의 image들 처럼 input에 html markup attribute로 value를 추가해주었습니다.

attribute로 가져온 value값과 input의 현재 value 값을 가져오면 다르게 나타납니다.

attibute는 html에 정의되어있고 

property는 dom객체에서 사용되기 때문입니다.

 

attribute는 대문자로 선언을 해도 소문자로 추가되고 값도 문자열만 가능합니다.

property는 대소문자를 식별하며 모든타입의 값이 들어갈수 있습니다.


Why is extending built-in JavaScript objects not a good idea?

built-in javascript 객체를 확장시킨다는 것은 해당 객체를 prototype으로서 사용하는 모든 객체에 영향을 줄수 있습니다.

이것의 의미는 확장된 기능의 적용 범위가 내가 의도한 범위가 아닌 범위에도 적용될 수 있다는 것입니다.

그 범위는 기존에 작성되어 있는 코드일수도 있고 새로 추가되는 코드일 수도 있습니다.

만약 실수로 이미 사용되고 있는 함수를 override 하게 된다면 기존에 동작중인 코드에도 영향을 끼칠수 있어 기능 자체에 문제가 생길수 있습니다.

이러한 위험성 때문에 내장 객체는 함부로 확장하지 않는게 좋은것 같습니다.

 


Difference between document load event and document DOMContentLoaded event?

 

load 이벤트는 dom트리가 그려진 후 필요한 Resource(이미지, stylesheet, script 파일)들이 전부 로드가 끝난후에 이벤트가 발생하게 됩니다.

DOMContentLoaded 이벤트는 dom 트리가 그려진 후 바로 이벤트가 발생합니다. resource들이 다운로드 되기를 기다리지 않습니다. 

 

resource들을 로드하는데 시간이 필요하다면 DOMContentLoaded 이벤트를 사용하여 reource들의 로드를 기다릴 필요없이 이벤트를 발생 시킬수 있습니다.

 


 

What is the difference between == and ===?

 

==는 es6 이전에 비교 연산자로 사용하던 것으로 타입이 다르다면 타입을 변화한후에 값이 같은지를 반환해 줍니다.

===는 es6에 등장한 비교 연산자로 타입까지 비교해주어 유용하게 쓰입니다.

 

아래는 그 예시 입니다.

1 == '1' // true
1 == true // true
1 == [1] // true
null == undefined // true


1 === '1' // false
1 === true // false
1 === [1] // false
null === undefined // false

 

==는 사용하지 않는 것을 선호합니다.


 

Explain the same-origin policy with regards to JavaScript.

동일 출처 정책은 어떤 출처에서 가져온 문서 혹은 스크립트에서 다른 출처에서 가져온 리소스와의 상호작용을 제안 합니다.

이때 출처는 프로토콜, 포트, 호스트가 같아야 합니다.

이를 통해 공격받을 수 있는 경로를 줄여줍니다.

 

ex) http://dkrnfls.tistory.com/dir2/example.html  => 성공

https://dkrnfls.tistory.com/dir2/example.html => 실패 (프로토콜이 다름)

https://www.naver.com/dir2/example.html => 실패 (호스트가 다름)

http://dkrnfls.tistory.com:81/dir2 => 실패 (포트가 다름)

 

쉬운말로 javascript에서 ajax 요청을 보낼때 현재 요청을 보내는 도메인과 다른 도메인에서 응답이 오는 경우 이를 브라우저에서 제한 합합니다.

이걸 해결할 수 있는 것이 cors(cross origin resource sharing) 입니다.

서버에서 응답을 보내줄때 Access-Control-Allow-Origin 헤더에 클라이언트의 도메인을 추가해주거나 wildcard를 추가해주어 교차출처 접근을 허용해줄수 있습니다.

 

일반적인 요청들은 cors에 상관없이 요청과 응답이 가능합니다. 

그러나 이러한 일반적인 요청은 아래의 모든 조건을 충족시켜야 합니다.

* get, post, head 중 하나의 메서드

* 헤더에 사용할 수 있는 값은 아래의 속성 뿐

  * accept

  * accept-language

  * content-language

  * content-type

* content-type에는 아래의 값들만 허용됩니다.

  * application/x-www-form-urlencoded, multipart/form-data, text/plain

 

=> 우리는 json 요청을 주로 보내기 때문에 위의 일반적인 경우에는 해당할 수가 없습니다...

 

서버에서 받는 요청의 헤더에 origin을 통해 어느 클라이언트에서 왔는지 확인이 가능합니다.

아래는 클라이언트의 요청

GET /resources/public-data/ HTTP/1.1
Host: bar.other
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:71.0) Gecko/20100101 Firefox/71.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Connection: keep-alive
Origin: https://foo.example

아래는 서버의 응답

HTTP/1.1 200 OK
Date: Mon, 01 Dec 2008 00:23:53 GMT
Server: Apache/2
Access-Control-Allow-Origin: *
Keep-Alive: timeout=2, max=100
Connection: Keep-Alive
Transfer-Encoding: chunked
Content-Type: application/xml

[…XML Data…]

 

cross-origin 요청은 유저 데이터에 영향을 줄 수 있으므로 preflight 요청을 보냅니다. 

이때 options 메서드를 이용합니다.

미리 안전한지 확인해 본다고 저는 이해하고 있습니다.

아래의 이미지를 보면 xhr 요청 전에 같은 경로로 preflight 요청을 보낸것을 확인할 수 있습니다.

 


Make this work:duplicate([1,2,3,4,5]); // [1,2,3,4,5,1,2,3,4,5]

function duplicate(arr) {
  return arr.concat([...arr])
}

console.log(duplicate([1,2,3,4,5]))

function duplicate(arr) {
  return [...arr, ...arr]
}

console.log(duplicate([1,2,3,4,5]))

function duplicate(arr) {
  return arr.concat(arr)
}

console.log(duplicate([1,2,3,4,5]))

Why is it called a Ternary expression, what does the word "Ternary" indicate?

삼항연산자를 의미합니다.

javascript 에서는 if else 를 ? 로 간단하게 사용할 수 있습니다.

var flag = true

console.log(flag ? "yes" : "no")

What is "use strict";? what are the advantages and disadvantages to using it?

use strict 모드는 말 그대로 엄격한 모드 입니다.

평소에는 에러로 반환하지 않던 것들을 use strict 에서는 에러로 반환해 줍니다.

 

장점

* 전역 변수가 의도치 않게 생기는 것을 방지해 줍니다.

'use strict'

// 전역변수 mistypedVariable가 존재한다고 가정

mistypedVaraible = 17; // 오타로 인해 ReferenceError 발생시킴

* 기존에는 조용히 무시되던 에러들을 throwing 합니다

"use strict"

// 쓸 수 없는 프로퍼티에 할당
var undefined = 5; // TypeError 발생
var Infinity = 5; // TypeError 발생

// 쓸 수 없는 프로퍼티에 할당
var obj1 = {};
Object.defineProperty(obj1, 'x', { value: 42, writable: false });
obj1.x = 9 // TypeError 발생

// getter-only 프로퍼티에 할당
var obj2 = { get x() {return 17;} }
obj2.x = 5 // TypeError 발생

// 확장 불가 객체에 새 프로퍼티 할당
var fixed = {}
Object.preventExtensions(fixed);
fixed.newProp = "ohaio" // TypeError 발생

* 삭제할 수 없는 프로퍼티를 삭제하려 할때 error를 반환합니다.

"use strict"

delete Object.prototype // 에러 발생

* 전역 this는 use strict에서 undefined로 반환 됩니다.

 

단점

* 일부 개발자는 익숙하지 않은 기능이 있을수도 있습니다.

* 서로 다른 엄격한 모드로 작성된 스크립트를 병합하면 문제가 발생할 수 있습니다.

 

 

es6 에서는 strict mode가 default 이기 때문에 따로 선언해줄 필요가 없습니다.

모듈에도 자동적으로 strict mode가 적용됩니다.


Create a for loop that iterates up to 100 while outputting "fizz" at multiples of 3, "buzz" at multiples of 5 and "fizzbuzz" at multiples of 3 and 5

for (let i = 1; i< 100; i++) {
    if (i % 3 === 0 && i % 5 === 0){
        console.log('fizzbuzz')
    }
    else if(i % 3 === 0) {
        console.log('fizz')
    }
    else if(i % 5 === 0) {
        console.log('buzz')
    }
}

Why is it, in general, a good idea to leave the global scope of a website as-is and never touch it?

전역범위에 사용을 하게 되면 모든 범위에서 접근할 수 있다는 장점이 있지만 반대로 모든 범위에서 접근할 수 있다는 단점이 있습니다.

상위에 선언된 전역변수를 스크립트의 하단에서 override 하게 된다면 전역변수의 충돌이 일어날 수 있습니다.

함수 선언식으로 선언된 경우 기존에 해당 함수를 사용하던 곳 또한 영향을 받게 됩니다.

 

따라서 모듈화를 적용하여야 합니다. 클로저나 즉시 실행함수를 이용하거나 파일들을 모듈들로 쪼개어 사용하여야 합니다.

es6의 모듈 방식을 주로 사용합니다.

node에서는 commonjs 방식을 모듈에 적용 하고 있습니다.


 

Why would you use something like the load event? Does this event have disadvantages? Do you know any alternatives, and why would you use those?

load 이벤트는 dom tree와 문서에 필요한 리소스들(script, image)들이 전부 다운로드 되고 나서 스크립트를 실행합니다. 

스크립트 내에서 조작해야 하는 것들이 다운로드된후 존재할때 사용합니다(이미지의 사이즈 등등)

 

대안으로 DOMContentLoaded 메소드가 있습니다.

DOMContentLoaded 메소드는 DOM tree가 그려지고 나서 바로 스크립트를 실행시킬수 있습니다.

모든 이미지 혹은 스크립트들의 다운로드에는 시간이 오래 소요될 수도 있으므로 이들을 다운로드 하기 전에 스크립트를 실행시킬 수 있습니다.


Explain what a single page app is and how to make one SEO-friendly.

SPA(single page app)은 최초 요청시에는 html을 응답받고 이후의 요청은 ajax를 통해 필요한 데이터만 Json 형식으로 전달 받습니다.

필요한 데이터만 서버로부터 전달받으므로 트래픽을 줄일 수 있고 사용자에게 더 빠른 응답속도를 보일수 있습니다.

라우팅에는 html5의 history api를 이용해 페이지가 이동하는 것처럼 보일수 있습니다.

History api에는 history.back(), history.forward(), history.pushState(), history.popState()와 같은 메소드들이 있습니다.

 

최초 렌더링은 느릴수 있습니다. html과 필요한 js를 모두 다운로드 받아야 하기 때문입니다.

하지만 이후의 페이지 이동이나 응답은 필요한 것들을 json으로 받아오기 때문에 상대적으로 빠릅니다.

 

seo에 좀 더 최적화 하려면 ssr 방식을 고려하여야 합니다.

spa에서는 js의 동작으로 사이트의 metadata가 변경되게 됩니다. 

하지만 검색엔진의 크롤러 봇들은 js를 실행시키지 않습니다.(구글의 크롤러 봇은 js를 실행시키는 것으로 알고 있습니다)

따라서 seo를 위해서는 서버에서 즉시 렌더가능한 html 파일을 보내주는 ssr 방식을 고려하여야 합니다.


What is the extent of your experience with Promises and/or their polyfills?

api를 병렬적으로 동시에 이용하고 모든 요청이 성공했을때 처리를 위해 Promise.all을 사용해 본적이 있습니다.

Promise.all에는 promise객체가 배열의 인자로 들어가게 됩니다. 

 

Promise.allSetteled 는 promise의 상태를 반환합니다.
pending, fulfilled, rejected와 value를 반환합니다.

 

Promise는 비동기 작업을 처리하기 위해 es6에서 등장하였습니다.

등장하기 이전에는 비동기 작업을 처리하기 위해 콜백을 이용하였습니다.

콜백이 여러개 쌓이면서 콜백 hell을 경험할 수도 있습니다.

 

es6 이전의 버전에서는 promise의 polyfill을 사용하여야 합니다.

polyfill은 보충하는 솜(보충재)이라는 뜻으로 웹 개발에서 기능을 지원하지 않는 웹 브라우저 상의 기능을 구현하는 코드를 의미합니다.

직접 구현하거나 babel의 polyfill을 이용하여 promise 기능을 지원하지 않는 브라우저에서도 promise의 기능이 동작하도록 할 수 있습니다.

import "@babel/polyfill";

What are the pros and cons of using Promises instead of callbacks?

 

장점

  • callback 지옥을 피할 수 있습니다.
  • then, catch, finally를 통해 연속된 흐름을 가독성 있게 작성할 수 있습니다
  • Promise.all을 통해 여러개의 비동기 코드를 병렬적으로 실행할 수 있습니다.
  • error처리 또한 가독성 있게 처리할 수 있게 됩니다.

단점

  • es6를 지원하지 않는 브라우저에서는 polyfill이 필요합니다.
  • 코드에 익숙치 않으면 복잡하게 느낄 수 있습니다.

 

let a = (cb) => setTimeout(cb, 1000)
let b = (cb) => setTimeout(cb, 1000);
let c = (cb) => setTimeout(cb, 1000);
let d = (cb) => setTimeout(cb, 1000);

let test = () => {
  a(() => {
    console.log('a');
    b(() => {
      console.log('b')
      c(() => {
        console.log('c');
        d(() => {
          console.log('d')
        })
      })
    })
  })
};

test();

위의 코드는 콜백과 setTimeout을 이용한 콜백 hell의 예시입니다.

아래의 코드는 위의 콜백 hell 예시를 promise를 이용하여 가독성 있게 나타낸 코드입니다.

let a = () =>
  new Promise((resolve, reject) => setTimeout(() => resolve("a"), 1000));
let b = () =>
  new Promise((resolve, reject) => setTimeout(() => resolve("b"), 1000));
let c = () =>
  new Promise((resolve, reject) => setTimeout(() => resolve("c"), 1000));
let d = () =>
  new Promise((resolve, reject) => setTimeout(() => resolve("d"), 1000));

a()
  .then((data) => {
    console.log(data);
    return b();
  })
  .then((data) => {
    console.log(data);
    return c();
  })
  .then((data) => {
    console.log(data);
    return d();
  })
  .then((data) => {
    console.log(data);
  });

 

What are some of the advantages/disadvantages of writing JavaScript code in a language that compiles to JavaScript?

javascipt로 컴파일 되는 script는 typescript만 사용해보았습니다. coffeescript, purescript와 같은 것들이 있습니다.

 

장점

  • 코드를 더 짧게 작성할 수 있습니다.(coffeescript의 경우)
  • typescript를 이용하면 유지보수에 더 용이해집니다.(type check를 할 수 있어 컴파일 전에 오류를 체크할 수 있습니다.)
  • javascript의 문제를 해결할 수 있습니다.(ide에서는 컴파일 전에 에러를 띄워줌으로써 런타임의 에러를 방지할 수 있습니다. 변수의 mistyping과 같은 것들)

단점

  • 해당 스크립트 언어에 대한 학습이 추가로 필요하게 됩니다.
  • javascript의 최신버전을 반영하지 않을 수도 있습니다.
  • 빌드나 컴파일 하는 과정이 필요하게 됩니다. typescript 컴파일러와 같은 도구들이 필요할수 있습니다.
  • IDE나 에디터에서 지원하지 않을 수도 있습니다.

What tools and techniques do you use debugging JavaScript code?

React에서는 react developer tools extension을 설치하여 사용하였습니다.

렌더링 될때마다 색깔을 표시해주는 기능을 유용하게 사용했었습니다.

 

React query를 사용했었을때는 react query의 dev tool을 

redux를 사용할때는 redux devtools를 사용하여 어떤 데이터가 dispatch되는지를 확인했었습니다.

 

vanilla js를 사용할때는 console을 찍어서 변수의 값이나 동작을 확인했었습니다.

debugger를 입력하여 크롬의 개발자 도구에서 확인하기도 했었습니다.

 

node에서는 vscode의 debugger를 사용하여 개발의 편의성을 확보하기도 했었습니다.

코드에 breakpoint를 찍으면 vscode 내에서 동작을 javascript의 debugger와 같이 사용할 수 있습니다.


What language constructions do you use for iterating over object properties and array items?

objcet의 속성들을 iterating해야 할때는  Object.keys를 이용하였습니다.

값만 순회하고 싶을때는 Object.values를

key,value를 차례로 알고싶을때는 Object.entries를 사용하였습니다 

 

속성들만 순회하고 싶을때 for-in 문을 이용해서도 값을 가져올 수 있습니다

const testKeys = {a: '1', b: '2', c: '3'}
const keys = Object.keys(testKeys)
console.log(keys) // ['a', 'b', 'c']


const testValues = {...testKeys}
const values = Object.values(testValues)
console.log(values) // ['1', '2', '3']

const testEntries = {...testKeys}
const entries = Object.entries(testEntries)
console.log(entries) // [['a', '1'], ['b', '2'], ['c', '3']]


for (let key in testKeys) {
	console.log(key) // a, b, c
}

 

배열을 순회할때는 for문을 이용하거나 forEach, map, filter 등을 이용하였습니다.


Explain the difference between mutable and immutable objects.

mutable object는 변경가능한 객체, immutable object는 변경이 불가능한 객체를 의미합니다.

함수형 프로그래밍에서 immutable object는 핵심 개념이라고 알고 있습니다.

 

javascript에서 원시 type의 객체들은 immutable한 객체들입니다.

var a = 'a'
var b = a;
b = 'b'

console.log(a) // 'a'
console.log(b) // 'b'

위와 같은 경우 b는 a의 값을 복제하여 새로운 메모리 주소에 할당합니다. 

a의 값을 b로 인해 바뀌지 않습니다.

 

값에 의한 참조로 선언된 변수는 값이 변할수 있습니다.

let c = { a: 'a', b: 'b' }
let d = c
d.a = 'd'

c // {a: 'd', b: 'b'}
d // {a: 'd', b: 'b'}

 

javascript에서는 아래와 같은 메소드들로 immutable한 객체를 제공할 수 있습니다.

Object.defineProperty에서 writable 옵션을 false

Object.preventExtensions, Object.seal, Object.freeze

let a = {}
Object.defineProperty(a, 'b', {value: 42, writable: false, configurable: true });
a // {b: 42}
a.b = 43
a // {b: 42}

Object.preventExtensions(a)
a.c = 44
a // {b: 42}


a = { b: 'b' }
Object.seal(a) 
a.c = 'c'
a // { b: 'b' }

a = { b: 'b' }
Object.freeze(a) 
a.c = 'c'
a // { b: 'b' }

 

immutable한 객체를 사용함으로써 

* 변화를 감지하기 쉽습니다(react에서 변수의 상태를 감시할때 사용하기 편리합니다.)

* 멀티 스레드 환경에서 값이 오염될 상황을 방지할 수 있습니다.

* 할당을 잘못하게 되면 매번 값이 생성되어 메모리를 비효율적으로 사용할 수도 있습니다.


Explain the difference between synchronous and asynchronous functions.

synchronous 함수와 asynchronous 함수는 동기, 비동기 함수를 각각 의미합니다.

동기 함수는 해당 함수의 실행이 끝날때까지 기다립니다.

하지만 비동기 함수는 해당 함수의 실행이 끝날때까지 기다리지 않습니다.

 

웹 api를 이용하거나 setTimeout과 같은 함수를 실행하면 함수들은 task queue에 push 됩니다.

이후 call stack이 비게되면 event loop에 의해 하나씩 콜 스택으로 이동하여 실행되게 됩니다.

 

api 요청을 하거나 많은 시간이 필요한 함수의 실행을 동기적으로 실행했을때 main thread에서는 해당 함수의 실행이 끝나기까지 기다려야 하므로 ui를 그리지 못하거나 하게 됩니다. 따라서 비동기 적으로 실행하는 것이 좋습니다.

https://html.spec.whatwg.org/multipage/webappapis.html#task-queue

 

HTML Standard

 

html.spec.whatwg.org


 

What is event loop? What is the difference between call stack and task queue?

이벤트 루프는 call stack과 task queue를 계속 확인하면서 call stack이 비게 되면 

task queue에서 꺼내와 call stack에서 실행할 수 있게 해줍니다.

 

call stack은 자바스크립트 엔진에 위치합니다. memory heap또한 같이 구성되어 있습니다.

함수의 실행이 호출되면 콜스택에 push 되고 하나씩 실행되게 됩니다.

 

web api와 task queue는 html에서 제공됩니다. 

html은 마크업 언어 뿐만 아니라 많은 api들을 의미합니다.

fetch, setTimeout, setInterval 등등 web api가 호출되면 (만약 timer가 있다면 timer수행후에) task queue에 하나씩 넣어줍니다.

이후 event loop가 call stack과 task queue를 주기적으로 확인하여 call stack이 비게되면 queue에서 하나씩 꺼내와 실행하게 해줍니다.


Explain the differences on the usage of foo between function foo() {} and var foo = function() {}

function foo() {} 는 함수 선언식 입니다. 이러한 형태로 사용하게 되면 함수 전체가 호이스팅이 일어나게 됩니다.

var foo = function(){} 는 함수 표현식 입니다. 이 형태는 foo 변수만 호이스팅이 일어나게 됩니다.

 

함수 선언식으로 사용하게 될 경우 오버라이딩이 일어날 수 있게 됩니다.

상위에서 function sum 으로 정의하여 사용하고 있는 경우 아래에서 function sum으로 override하게 되면 

이미 선언한 sum을 이용하고 있던 코드도 영향을 받게 됩니다.

function sum(a, b){
	return a + b;
}

sum(1,2) // 7

function sum(a,b) {
	return a + b + 4;
}

sum(1,2)  // 7

 


What are the differences between variables created using let, var or const?

let과 const는 블록 스코프, var는 함수 스코프를 가지게 됩니다.

또한 세가지 선언자를 이용해 선언한 변수들은 전부 호이스팅 됩니다.

다만 let과 const는 TDZ(Temperal Dead Zone)에 위치하게 되어 선언전에 사용하고자 하면 에러가 발생됩니다.

한가지 유의할 점은 const는 선언과 할당을 꼭 동시에 해주어야 합니다.

var 변수는 선언 전에 사용하더라도 undefined를 출력하게 됩니다.

 

함수 스코프와 블록 스코프를 확인하기 위한 코드는 아래와 같습니다.

{
	let a = 'a'
}
console.log(a) // undefined




{
	var b = 'b'
}
console.log(b) // 'b'

function test() {
	var c = 'c'
}
console.log(c) // undefined

 


What are the differences between ES6 class and ES5 function constructors?

클래스는 프로토타입 기반 상속을 사용합니다.

같은 클래스를 재선언하게 되면 에러 발생합니다.

클래스 선언은 호이스팅이 되지 않습니다

function Person(name){
  this.name = name;
}

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

아래 이미지는 class를 babel을 이용하여 es5로 나타낸 것입니다.

class를 이용하게 되면 생성자 함수의 prototype 성질이 writable: false가 되는것을 볼 수 있습니다.

 

가장 큰 차이는 상속에 있습니다.

학생이 사람을 상속받는 것을 가정해 보겠습니다.

class Person {
  constructor(name){
    this.name = name
  }
}

class Student extends Person {
  constructor(name, school) {
    super(name)
    this.school = school;
  }
}

const student = new Student('홍길동', '동국')
console.log(student);
function Person(name) {
  this.name = name;
}

function Student(name, school) {
  Person.call(this, name);
  this.school = school;
}

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

console.dir(Student)

const student = new Student('홍길동', '동국')
console.log(student);

class 문법을 이용하면 상속을 훨씬 편하게 사용할 수 있습니다. 이때 python과 같이 다중상속은 되지 않습니다.


Can you offer a use case for the new arrow => function syntax? How does this new syntax differ from other functions?

콜백함수 내부의 this가 외부 함수의 this와 다르기 때문에 발생하는 문제를 해결하기 위해 의도적으로 설계함

const sum = (a,b) => a + b;

console.log(sum(1,2)) // 3

화살표 함수는 위와 같이 사용합니다.

화살표 함수는 this를 가지지 않습니다. 그래서 콜백함수, 비동기적인 함수의 실행, 메소드에서의 사용에서는 this가 상위 스코프의 this를 가리킵니다.

함수의 스코프는 선언되는 위치에 따라 설정됩니다. 이를 렉시컬 스코프 혹은 정적스코프 라고 합니다.

 

화살표 함수는 생성자 함수로 사용할수 없습니다. prototype 속성을 가지고 있지 않기 때문입니다.

 


What advantage is there for using the arrow syntax for a method in a constructor?

생성자 함수에서 mehtod에 화살표 함수를 사용하게 되면 this는 생성된 객체를 가리키지 않습니다.

화살표 함수가 생성된 상위 스코프의 this를 가리키게 됩니다.

 

클래스형 컴포너트를 사용하는 리액트에서 생성자의 메소드에 화살표 함수를 사용하면 좋다는데 해당 예시는 잘 모르겠습니다.

아래의 코드는 메소드에서 화살표 함수를 사용한 예시입니다.

const Person = function (firstName) {
  this.firstName = firstName;
  this.sayName1 = function () {
    console.log(this.firstName);
  };
  this.sayName2 = () => {
    console.log(this.firstName);
  }
}

const john = new Person('John')
john.sayName1(); // John
john.sayName2(); // John

const dave = new Person('dave')

// The regular function can have its 'this' value changed, but the arrow function cannot
john.sayName1.call(dave); // Dave (because "this" is now the dave object)
john.sayName2.call(dave); // John

john.sayName1.apply(dave); // Dave (because 'this' is now the dave object)
john.sayName2.apply(dave); // John

john.sayName1.bind(dave)(); // Dave (because 'this' is now the dave object)
john.sayName2.bind(dave)(); // John

var sayNameFromWindow1 = john.sayName1;
sayNameFromWindow1(); // undefined (because 'this' is now the window object)

var sayNameFromWindow2 = john.sayName2;
sayNameFromWindow2(); // John

 


What is the definition of a higher-order function?

고차함수는 하나 이상의 함수를 인자로 받거나 함수를 결과값으로 반환하는 함수를 말합니다.

고차 함수는 일반적인 함수인데 함수를 인자로 받고 함수를 반환할 수 있는 추가적인 기능을 가진 것이라고 이해하시면 될 것 같습니다.

 

대표적인 예시로 map, filter, reduce 등이 있습니다.

이들은 원본데이터의 변형을 초래하지 않아서 순수함수에 자주쓰이고 순수함수는 함수형 프로그래밍에 쓰입니다.

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

const mulitplyTwo = arr.map((el) => el * 2); // [2, 4, 6, 8, 10]
const underThree = arr.filter((el) => el < 3); // [1, 2]
const totalSum = arr.reduce((prev, next) => prev + next, 0); // 15

console.log(mulitplyTwo, underThree, totalSum);

 

 


Can you give an example for destructuring an object or an array?

구조 분해 할당은 Es6 부터 가능합니다!

const arr = [1,2,3]
const [one, two, three] = arr
console.log(one, two, three) //  1, 2, 3

const test = { a: 'a', b: 'b', c: 'c' }
const { a, b, c } = test
console.log(a, b, c) // a, b, c

ES6 Template Literals offer a lot of flexibility in generating strings, can you give an example?

const students = ['a', 'b', 'c', 'd', 'e']

const getStudentTemplate = '<li>name: {name}</li>';

const studentBoard = document.querySelector("ul")
students.forEach((student) => {
  studentBoard.insertAdjacentHtml("beforeend", getStudentTemplate.replace('{name}', student))
})
const students = ['a', 'b', 'c', 'd', 'e']

const getStudentTemplate = (name) => `<li>name: ${name}</li>`;

const studentBoard = document.querySelector("ul")
students.forEach((student) => {
  studentBoard.insertAdjacentHtml("beforeend", getStudentTemplate(student))
})

 

template literal을 사용하면 생성되는 DOM node에 eventHandler를 달기 애매하다.

이럴때는 이벤트 위임을 이용하거나 template tag를 front에 적재해두고 사용할 것 같다.


Can you give an example of a curry function and why this syntax offers an advantage?

const sum = (x) => (y) => x + y

sum(2)(5) // 7

const addFive = sum(5)
addFive(2) // 7

const sum = (a,b) => a + b;
sum.length // 2 함수의 length는 받는 인자의 수
sum(2,5) // 7

커링 함수는 복수개의 파라미터를 받는 함수에서 파라미터를 누적시킬 수 있습니다

커링 함수는 함수의 확장성을 활용하기에 좋습니다.

 

또한 확장된 함수를 사용함으로써 중복된 인자를 피할 수 있습니다.

const add = (a) => (b, c) => a + b + c

const addFive = add(5)

addFive(1,2) // 8
addFive(2,3) // 10
addFive(3,4) // 12

add(1,2,5)
add(2,3,5)
add(3,4,5)



const addSix = add(6)

addSix(1,2) // 9
addSix(2,3) // 11
addSix(3,4) // 13

add(1,2,6) // 9
add(2,3,6) // 11
add(3,4,6) // 13

What are the benefits of using spread syntax and how is it different from rest syntax?

es6에서 등장한 스프레드 문법은 객체나 배열을 복사해야할때 편하게 사용할 수 있습니다.

이때 1 depth에서는 깊은 복사, 2 depth 부터는 얕은복사가 이루어지게 됩니다.

const putDookieInAnyArray = (arr) => [...arr, 'Dookie']

const result = putDookieInAnyArray(["i", "really", "don't"])

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

const copyOfTodd = {...person}

rest syntax는 spread 문법과 반대처럼 느껴집니다.

rest syntax는 함수의 인자나 구조 분해 할당에서 활용할 수 있습니다.

객체나 배열로부터 데이터들을 꺼내오는데 전체를 꺼내오는 것이 아닌 일부만 꺼내올수 있는 것처럼 느껴집니다.

const addFiveToBunchOfNumbers = (...numbers) => numbers.map(x => x + 5);

const result = addFiveToBunchOfNumbers(4,5,6,7,8,9,10) // 9 10 11 12 13 14 15

const [a, b, ...rest] = [1,2,3,4] // a: 1, b: 2, rest: [3,4]

const {e, f, ...others} = {
  e: 1,
  f: 2,
  g: 3,
  h: 4,
} // e: 1, f: 2, others: { g: 3, h: 4 }

 


How can you share code between files?

es6의 모듈 방식을 사용합니다. 이후에는 babel을 통해 es5 문법으로 Transpiling한 후에 웹팩으로 번들하여 하나의 파일을 배포합니다.

저는 코드를 기능별로 구분 하여 위치시키는 것을 좋아합니다.

하지만 도메인에 따라 기능이 아닌 같은 주제에 따라 위치 시킬때도 있습니다.


Why you might want to create static class members?

공통된 기능을 하는 함수나 요소가 있을때 static 요소를 고려합니다.

static 속성이나 메소드는 인스턴스의 상태에 의존하지 않는 순수함수를 보통 할당하곤 합니다.


 

 

 

https://www.frontendinterviewhandbook.com/javascript-questions#why-is-it-called-a-ternary-expression-what-does-the-word-ternary-indicate

 

JavaScript questions | Front End Interview Handbook

Answers to Front-end Job Interview Questions - JS Questions. Pull requests for suggestions and corrections are welcome!

www.frontendinterviewhandbook.com

 

이 글은 아래의 포스트를 번역한 글입니다.(오역이 있을수 있습니다.)

 

 

https://itnext.io/system-design-concepts-that-helped-me-get-sr-frontend-offers-from-amazon-linkedin-9e100f3ce7d2

 

🔥 System Design Concepts that Helped Me Get Sr Frontend Offers From Amazon & LinkedIn

If you just started your System Design journey, the compilation of this concept will help you to kickstart with the basics.

itnext.io

 

 

Content

  • Intro
  • Client-server model
  • Network Protocols
  • Storage
  • Latency and Throughput
  • Availability
  • Cache
  • Proxy
  • Load Balancer
  • Hashing
  • Database
  • Key-value store
  • Specialized storage paradigms
  • Replication and sharding
  • Leader Election
  • Peer to Peer
  • Polling and Streaming
  • Configuration
  • Rate limiting
  • Logging
  • Pub/sub pattern -- Stream
  • MapReduce
  • Security
  • Conclusion
  • Learn More

 


 

Intro

이 글은 Frontend Interview Cheatsheet의 후속편입니다. 만약 아직 보지않았다면 먼저 읽어보세요👇

https://itnext.io/frontend-interview-cheatsheet-that-helped-me-to-get-offer-on-amazon-and-linkedin-cba9584e33c7

 

🔥 Frontend Interview Cheatsheet That Helped Me Get Offers From Amazon & LinkedIn

If you are preparing for a frontend interview & want to quickly refresh your domain knowledge, this cheatsheet will save you a lot of time.

itnext.io

 

여기에 나와있는 것들은 당신이 master해야할 시스템 디자인 면접 개념에 도움이 될만한 것들을 줄것입니다.

재밋게 읽고 나서 medium and twitter 에서 팔로우 하는것을 잊지 말아주세요

 

만약에 1000개의 👏, 박수를 받게된다면 인터뷰 준비에 도움을 줄 System Design Interview Template을 포스트 할게요

 

 


 

Client-server model

  • DNS  Domain Name System은 IP 주소를 domain 이름으로 redirect 시켜줍니다. client는 DNS에 쿼리를 날리고 IP 주소를 전달받습니다.
  • IP address — 각각의 기기가 인터넷에 연결된 숫자로 된 주소입니다. a.b.c.d(0 - 255). 192.0.0.1 - localhost.192.169.c.d
  • Port — 복수의 프로세스가 충돌없이 동작하기 위한 것 0-65525. 2^16. 0-1023 포트 들은 시스템에서 관리합니다. (22: Secure Shell, 53: DNS lookup, 80: HTTP, 443: HTTPS)

 


 

 

Network Protocols

  • IP —  인터넷 프로토콜 -- IP 패킷을 이용해서 인터넷 상에서 기기간에 통신하기 위한 규칙
  • TCP — 패킷들은 순서대로 전달되는것을 보장하고, 오류가 발생했는지 여부를 알수 있습니다. 추가적인 TCP 헤더(전달 순서에 관한 정보)를 가지고 IP 전달과정의 위에 존재하게 됩니다. 
  • IP Packet — 기기간에 통신을 위한 가장 작은 데이터 단위 입니다. 헤더(source, recipient, size of the packet, version of protocol IPv4)와 페이로드로 구성되어있습니다. 패킷의 크기는 ²¹⁶ bytes 입니다. 모든 패킷이 전달되었는지 보장할수 없고 순서는 HTTP -> TCP -> IP 순으로 전달됩니다.
 

 

Storage

  • Database — 데이터를 디스크 혹은 메모리에 저장하는 서버 입니다. Record and query
  • Disk — 프로세스가 죽으면 데이터를 저장하고 유지합니다. HDD는 느리지만 비싸지 않습니다. SSD는 빠르지만 더 비싸고 비휘발성 입니다.
  • Memory — RAM, 프로세스가 죽으면 데이터를 지우고 빠른접근이 가능하고 휘발성 입니다.
  • Persistent data — 프로세스가 죽으면 데이터를 유지합니다.

 


 

Latency and Throughput

  • Latency  작업을 완료하는데 소요되는 시간(ms) :

1) RAM 으로부터 1 Mb를 읽는데 - 0.25 ms;

2) SSD 로 부터 1Mb를 읽는데 - 1ms;

 

3) 네트워크(1 Gb/s)를 통해 1Mb를 전송하는데  - 10ms;

 

4) HDD로부터 1Mb를 읽는데 - 20ms;

 

5) 대륙간에 1Mb를 전송하는데 - 150ms

  • Throughput  1초 단위로 수행할 수 있는 연산 동작의 수 (RPS, QPS)
 

 

Availability — reliability

  • Availability — 1년 단위로 서비스에 접근할수 있는 시간 1) 99% (87.8 hours); 2) 99.9% (8.8 hours); 3) 99.99% (52.6 min) 4) 99.999% (5.3 min) (5 nines);
  • Redundancy — availability를 증가하기 위한 시스템의 복제 (단일 실패 지점이 없는지 확인 - 이중화 사용) Passive redundancy (2개의 기기중 하나가 죽으면 다른 하나가 동작), active redundancy (만약 하나가 죽으면 다른 하나가 기능을 수행한다.)
  • SLA (service — level — agreement) 복수개의 SLO로 구성되어있고 서비스의 실행을 보장해준다.
  • SLO (service — level — objective) 서비스의 실행을 보장 (availability)

 


 

Cache

  • What can be cached — Client, Server, In Between(서버-데이터베이스), API, 하드웨어(CPU), 자주 사용되는 연산들, 무거운 동작들과 계산들
  • Cache — 빠르게 제공하기 위한 데이터를 가지고 있는 하드웨어 혹은 소프트웨어(무거운 계산의 결과, API 반응들, API 요청들)
  • Write through cache — 캐시와 메모리에서 동시에 캐시가 업데이트 될때 프로세스가 죽으면 데이터를 복구합니다. 장점 -- 간단하고 신뢰할 수 있습니다. 단점 -- 캐시가 자주 write되지 않을때 수월하게 동작합니다.
  • Write back cache — 캐시와 메모리에 있는 캐시가 동시에 업데이트 될때(캐시만 먼저 업데이트 되고 나중에 메모리에 있는 정보가 업데이트 되는 방식)
  • 만약 캐시 데이터의 지속성을 신경써야 한다면 -> local 캐시를 사용합니다. 만약 정확성이 중요하다면(views, likes) - 서드파티 캐시를 사용합니다(Redis)
  • Cache Hit—  캐시에서 요청된 데이터를 찾는 것
  • Cache miss — 설계로 인하여 저장된 캐시를 찾지 못하는것
  • Cache eviction policy — 캐시가 제거되는 규칙(LRU (recent), LFU(frequent), FIFO(queue))
  • Content Delivery Network (CDN) — 지역에 따라 서버를 캐시해주는 third party 서비스, 대기시간이 더 짧아진다. PoP (Point of Presence). Cloudflare, Google Cloud CDN. 


 

Proxy — security, reliability

  • Forward proxy  client-side (hide IP) VPN; (클라이언트를 숨기는 것)
  • Reverse proxy  server interceptor (load balancing, leader election) (logging, caching, filter requests, load balancing, security shield); (서버를 숨기는 것)
  • Proxy tool — Nginx;

 


 
 

Load Balancer — security, reliability, performance

  • 서버들간에 트래픽을 분배해주는것. 서로 다른 선택 전략을 가진 여러가지의 로드밸런서 들이 있습니다.
  • Server selection strategy — (weight-if some servers more powerful) round-robin, random selection, performance-based, IP based, API path based; (라운드 로빈, 랜덤 선택, 퍼포먼스 기반, Ip 기반, API 경로 기반)
  • Hot-spot — 하나의 기기에 너무 많이 몰린 트래픽을 분산 시켜주는것, reason suboptimal sharding key/hashing function
  • LB Library — Nginx;
 
 
 

 

Hashing

  • Hashing  데이터를 해시로 바꿔주는 것 -- 보통 interger로
  • Consistent hashing — 해시 테이블이 resize 되어져야 할때 재위치되어야 할 key들의 숫자를 최소화 하는 것 (load balancer during adding or removing server) (Rendezvous — highest random hash);
  • SHA  secure hash algorithm, hash 함수들을 모아둔 것. Popular SHA-3. 

 


Database

  • ACID  database (of database transaction):
    ATOMICITY(원자성) — 트랜잭션의 모든 연산들은 실패하거나 성공한다. 중간상태를 가지지 않는다.
    CONSISTENCY(일관성) — 각각의 트랜잭션은 데이터가 최신화된 상태임을 보장한다. (strong consistency), (eventual consistency) will be updated after some time; 
    ISOLATION(독립성) — 여러 트랜잭션들의 실행은 그들이 연속적으로 실행되는 것과 같다. (서로 관련되어 있지 않아야 한다 라는 뜻으로 이해)
    DURABILITY(지속성) — 트랜잭션들은 비휘발성 상태를 가지며 서로 충돌할 수 없다.
  • Relational database — 테이블, 열, 행으로 구성된 데이터 베이스, 그들간에 관계성을 가진다
  • Nonrelational database — 표 형식이 아닌 특정한 목적을 위해 구성되어 있다.
  • SQL — structured query language;
  • SQL database — SQL을 지원하는 관계형 데이터 베이스;
  • NoSQL — SQL을 지원하지 않는 데이터 베이스
  • Database index   데이터 검색을 빠르게 해주고 데이터 write를 느리게 하는(columns들의 인덱스를 생성해야 하기 때문) 데이터 구조
  • Strong consistency — ACID와 연관된(모든 연산들은 최신의 데이터를 보장해야 한다) -- PostgreeSQL
  • Eventual consistency — 트랜잭션은 나중에 최신화된 데이터를 가지게 된다. (transaction will eventually (later) know the updated data); (미국에서 좋아요가 100개인 상품을 누르면 101개가 됨, 한국에서 같은 상품을 확인했을때 좋아요는 100개로 노출됨); 

 


Key-value store

  • NoSQL 데이터베이스가 캐싱과 동적 환경설정을 위해 사용한다. (Etcd — strong consistency, used for leader election), (Redis — caching, rate limiter), Zookeeper (strong consistency, leader election, highly available);

 


 

Specialized storage paradigms

  • Blob Storage — Blob(binary large object)로 구성된 key-value 저장소이다. 보통 media: 이미지, 오디오, 비디오에 사용한다.(Google Cloud Storage, Amazon S3)
  • Time Series Database — 시간 인덱스를 가진 데이터를 분석하고 저장할때, 연속적으로 생성되어야 할때(logging, internet of things, stocks ): Prometheus, InfluxDB, Graphite;
  • Graph Database — 데이터가 복잡한 관계 형성을 하고 있을때 (social network) (Neo4j)
  • Cypher — graph query language created by Neo4j, simpler than SQL 
  • Spatial database — 위치, 지도, 빠른 쿼리를 위해 quadtree를 사용함 (the region/location); (hotel, map)
 

 

Replication and sharding

  • Replication — 한 서버에서 다른 서버로 데이터를 복제하는 것(단일 실패 지점을 줄이고 대기시간을 줄인다), 주 데이터베이스와 복제가 업데이트 되어 진다(sync, async)
  • Sharding — 데이터 분할(데이터베이스가 거대할때), 처리량을 늘리기 위해 데이터 베이스를 파편(shard)으로 분리한다. (based on client region, based on the type of data (payment), based on the hash of column). 차선의 sharding 전략이 있다면 Hot Spot을 가질수 있다.

 


 

Leader Election — reliability

  • 주요 연산을 수행할때 서버들 중에 리더를 뽑는 과정(system important operations). And other nodes know who is the leader also after reelection;
  • Consensus algorithms — 리더를 뽑고 모든 서버간에 동기화를 한다. Paxos, Raft. Etcd and Zookeeper — key-value databases that implement leader election

 


 

Peer to Peer

  • 기기들이 시스템 처리량을 올리기 위해 작업량을 나누는 것(file distribution) 
  • Gossip Protocol — 업무량을 기기들간에 나누기 위해 소통하는 과정. 파일을 데이터 덩어리로 나누고 누가 어느 덩어리를 소유할지 알려주는 hash map을 만든다. Distributed Hash Table. Kraken

 


 

Polling and Streaming

  • Use cases: 채팅, 주식, 온도
  • Polling — 주기적으로 데이터를 호출한다(API)
  • Stream  지속적으로 데이터(API)를 가져오는 연결을 만든다 웹소켓

 


Configuration

  • JSON or YAML
  • Can be static — 일정하다,  장점 -- 일정하다, 단점 -- rebuiling을 요구한다
  • Dynamic (stored in KeyValue — Redis) 장점 - 유연하다, 적용하기 쉽다. 단점 -- 테스트가 없으면 시스템을 망가뜨리기 쉽다

 


 

Rate limiting — security, reliability

  • 스팸 시스템을 줄인다. Dos와 DDos(Distributed Denial of Service attack)을 피할수 있다. 
  • 캐시에 의해 수행되어진다. 로드밸런서가 어떻게 redirect 해야할지 알지 못하기 때문에 서버캐시를 사용하기 어렵다.
  • reverse proxy(Load Balancer)에서 key-value 데이터베이스를 사용한다(Redis)
  • Rate limiting 시스템에 따라 규칙은 단순하거나 더 복잡해질수 있다 
 

Logging — reliability, growing

  • Logging — 이벤트들의 정보를 수집한다.  모든 이벤트들은 STDOUT, STDERR에서 기록된다. 시간연속성 데이터 베이스에 그것들을 저장한다.InfluxDB, Prometheus 
  • Monitor — Grafana로 시각화 한 것;
  • Alert — 모니터링 하고 있는 시스템에 이벤트가 발생하면 alert를 생성한다. 예를 들면 slack의 post와 같이
  • 시스템의 성장에 있어서 logging은 중요하다.(에러 잡을때 무조건 필수)
 

Pub/sub pattern — Stream

  • Pub/Sub model; (하나를 구독하고 이벤트가 발생하면 캐치하는 모델)
  • Use cases: 채팅 메시지, 뉴스 피드, 주식 정보, 알림
  • Guarantees: at-least-one-delivery, persistent storage, ordering, replayability;(적어도 하나의 배달, 영구 저장소, 순서보장, 재실행성)
  • Publisher Server (P) — Topic Channel (T) — Message — Subscriber (P) -> (T) — m1 — m2 -> (S)
  • Idempotent operation — 몇번 호출되었나 상관없이 같은 결과를 가진다
  • Apache Kafka — 링크드인, 구글 클라우드 pub/sub과 같은 스트리밍 시스템은 적어도 하나의 전달을 보장한다.

 


 

MapReduce — Scalability Big Data

  • File System — 데이터의 구조를 추상화 하는 것(hierarchy, tree — Unix file system). 
    Distributed File System — 기기간에도 데이터를 분리하는 것 — Google File System, Hadoop Distributed File System; 
  • MapReduce — 데이터 분배: 빠르고, 효율성 있고, 실패에 관대한; 데이터셋은 복수개의 기기로 분리되고 -> (Map) 각각의 덩어리들이 key:value로 구성됨 -> (Shuffle) 재구성되고 -> (Reduce) 의미있는 데이터 들로 변환 된다.
  • Prerequisite — 우리는 파일 분배 시스템을 가지고 있다. 데이터셋을 덩어리로 분리하고, 여러 기기들간에 나누어 주며 중앙(map/reduce 워커들간에 어떻게 소통해야 하고 오고가야하는 지를 알고 있는)에서 관리하게 됩니다. 서버들은 매핑된 데이터를 보냅니다.
  • 만약 실패가 발생하고 중앙제어가 map/reduce를 다시 실행합니다.(같은 연산 결과)

 


Security

  • Man in the middle attack — 클라이언트에서 서버로 개인 IP 패킷을 가로채고 변경합니다. 암호화하고 HTTPS를 사용해야함
  • Symmetric Encryption — 데이터를 암호화 하고 복호화 하기 위해 하나의 키를 사용합니다. 비대칭키 방식보다 빠릅니다. Advanced Encryption Standard (AES)중의 알고리즘들을 사용합니다;
  • Advanced Encryption Standard  표준 대칭키 암호화 방식 알고리즘 들(AES — 128, AES — 192, AES — 256);
  • Asymmetric Encryption — 암호화, 복호화를 위해 공용키와 개인키를 사용합니다. 데이터는 공용키로 암호화 되고 개인키로만 복호화를 합니다(안전하게 보호되어 있어야 함). 대칭키 방식보다 느립니다;
  • HTTPS — 안전한 연결. 서버는 SSL 인증서를 가지고 있어야 하고 서버와 클라이언트 간에 통신을 위해 TLS를 사용해야 합니다. 
  • TLS — TCP의 위에 설계된 transport layer security 프로토콜;
  • SSL Certificate — 서버는 Certificate Authority로부터 전자 인증서를 부여 받습니다. TLS handshake 와 HTTPS connection 을 수행하기 위해 서버는 공용키를 가지고 있습니다.
  • Certificate Authority  공용키의 인증된 출처를 확인해줌
  • TLS Handshake — 클라이언트와 서버간에 보장된 연결을 수행해줌. 
    클라이언트는 random bytes를 전달하고(client hello) ->
    서버는 또 다른 random bytes(server hello) + 공용키와 함께 SSL 인증서를 보냅니다. ->
    클라이언트는 인증서를 Certificate Authority와 함게 확인하고 공용키로 암호화된 premaster secret을 보냅니다. -> 
    클라이언트와 서버는 client hello와 server hello, premaster secret을 세션키를 생성하기위해 사용합니다. (대칭키 방식을 사용하여), 그리고 통신간에 모든 데이터들을 암호화 합니다.

Conclusion

기초들을 refresh하는 것은 시스템 디자인 인터뷰 준비를 위한 첫번째 단계일 뿐입니다. The next is to learn a System Design Interview Template, which I am going to share if you share a little bit of your love ❤️ that looks like 1000 👏 claps.

'Frontend > Javascript' 카테고리의 다른 글

innerHTML, insertAdjacentHTML, textContent, innerText  (0) 2022.03.01
🔥Javascript questions  (2) 2022.02.28
js + cypress  (0) 2022.02.23
Frontend Interview Cheatsheet  (0) 2022.02.15
var, let, const  (0) 2021.11.10

상태관리 라이브러리 없이 만들어본 js 프로젝트에서 간단한 tdd를 적용해 보았습니다. 

라이브러리는 cypress를 사용하였습니다.

 

저는 cypress를 테스트 도구로서만 알고 있었습니다. 

좀 더 자세히 알아봐야겠습니다.

cypress 공식문서에서는 'A complete end-to-end testing experience' 라고 소개하고 있습니다.

Ent to End test가 뭘까요??

 

End To End test는 사용자 관점에서 테스트 하는 방법입니다.

의도하는 텍스트가 출력이 되었는지, 삭제 버튼을 누른후에 해당 컴포넌트가 삭제되었는지 등등 

사용자 관점에서 테스트를 하는 방법입니다.

 

cypress등장 이전에 테스트를 해본적은 없지만 이전에는 selenium등 가상의 브라우저를 띄워서 동작을 수행하였다고 합니다.

이전에 selenium을 브라우저의 쿠키 정보를 가져오기위해 사용해 봤는데 리눅스 환경에서 크롬드라이버를 설치하느라 애먹었던 기억이 나네요...

두서가 길었네요 이제 한번 cypress를 사용해 보겠습니다.

 


요약 순서

1. cypress 라이브러리 설치
2. package.json 설정
3. test 파일 설정
4. test 실행

 

1. Cypress 라이브러리 설치

yarn 을 통해 cypress를 설치해줍니다.

yarn add -D cypress

npm을 통해 설치해 준다면 아래와 같이 되겠네요

npm install cypress --save-dev

 

2. package.json 설정

package.json에 test script를 추가해주겠습니다

"scripts": {
    // ...
    "test": "cypress open"
    // ...
}

test를 추가해주고 바로 yarn test를 실행해 줍니다.

그러면 가상의 브라우저가 실행되고 

폴더 내에 cypress 폴더, cypress.json이 생성된 것을 확인할 수 있습니다.

변경된 폴더 구조
실행된 브라우저

 

 

3. test 파일 설정

저는 moonbucks.spec.js 이름으로 파일을 생성해주었습니다.

 

describe("moonbucks test", () => {
  beforeEach(() => {
    cy.visit("http://127.0.0.1:5500/client");
  });

  it("add americano in espresso tab", () => {
    cy.get("#espresso-menu-name").type("아메리카노");
    cy.get("#espresso-menu-submit-button").click();
    cy.get(".menu-name").first().should("have.text", "아메리카노");
  });

  it("edit americano in espresso tab", () => {
    cy.window().then((win) => {
      cy.stub(win, "prompt").returns("americano");
      cy.get(".menu-edit-button").first().click();
      cy.get(".menu-name").first().should("have.text", "americano");
    });
  });

  it("sold out americano in espresso tab", () => {
    cy.get(".menu-sold-out-button").first().click();
    cy.get(".menu-name")
      .first()
      .should("have.css", "text-decoration")
      .and("match", /line/);
  });

  it("delete americano in espresso tab", () => {
    cy.get(".menu-remove-button").first().click();
    cy.get(".menu-name").should("not.exist");
  });
});

 

코드의 내용을 보면

테스트 코드의 실행 전에 개발환경에서 구현 주소에 방문합니다.

이후 테스트 메소드를 하나씩 실행합니다.

그 다음부터는 메소드의 이름이 직관적이라 내용을 파악하기 쉬우실겁니다.

 

주어진 예시코드와 공식문서를 통해 원하는 유형의 테스트를 작성하면 됩니다.

https://docs.cypress.io/guides/getting-started/writing-your-first-test

 

Writing Your First Test | Cypress Documentation

What you'll learn How to start testing a new project in Cypress. What passing and failing tests look like. Testing web navigation, DOM querying, and

docs.cypress.io

 

테스트 코드의 내용중 prompt의 내용을 테스트 하는 방법에 대해 간략히 설명하겠습니다.

핵심 flow만 먼저 살펴보면 

 

1. cy.window()를 통해 현재 page에서 활성화 되어있는 window 객체를 가져옵니다.

2. window 의 prompt 메소드를 재정의 해줍니다.

 

=> stub메소드를 통해  built-in 함수들이나 커스텀 메소드들을 재정의 하고 제어할 수 있습니다.

 

 

https://docs.cypress.io/api/commands/window

 

window | Cypress Documentation

Get the window object of the page that is currently active. Syntax Usage Correct Usage Arguments options (Object) Pass in an options object to change the

docs.cypress.io

https://docs.cypress.io/api/commands/stub#Method

 

stub | Cypress Documentation

Replace a function, record its usage and control its behavior. Note: .stub() assumes you are already familiar with our guide: Stubs, Spies, and Clocks

docs.cypress.io

 

 

4. test 실행

이제 test 파일을 실행해줍시다.

위 화면에서 테스트하고자 하는 파일을 더블 클릭하면 가상의 브라우저가 켜질 것입니다.

가상의 브라우저에 동작과정과 결과까지 같이 보여주는 모습을 확인할 수 있습니다.

 

테스트 동작 과정

이 글은 아래에 첨부되어 있는 글의 한글 번역입니다. 오역이 들어 있을수 있습니다.

만약 프론트엔드 면접을 준비하고 있고 프론트엔드 도메인 지식을 빠르게 refresh 하고 싶다면 이 cheatsheet는 많은 시간을 아껴줄 것입니다.

 

목차

 

Intro

Web Knowledge

  1.Caching

  2.HTTP/2

  3.Security

 

Web Performance

  1.Critical rendering Path

  2.Reflow

  3.preload,preconnect,prefetch,prerender

  4.Rendering Performance

  5.Workers

  6.Image Optimization

 

DOM

  1.Elements

  2.Manipulation

  3.Document Fragment

  4.Event delegation and bubbling

 

HTML

  1.Semantic Elements

  2.Accessibility

  3.Responsive web

 

Javascript

  1.this

  2.Closure

  3.Inheritance

  4.Asynchronous Javascript

  5.Hoisting

  6.Currying

  7.Higher-order functions

 

Design patterns

  1.Mixin

  2.Factory

  3.Singleton

  4.Facade

  5.MVC,MVVM

  6.Server vs Client-Side Rendering

 

Conclusion

You are Awesome ❤️

Learn More


Intro

물론 모든 프론트엔드 지식을 하나의 글에 담기에 공간은 충분하지 않습니다. 그리고 이것은 이 cheatsheet가 이루고자 하는 바가 아닙니다. 이것은 단지 시니어 프론트엔드 엔지니어들은 친숙해야하는 frontend 주제의 지름길일 뿐입니다. 이것들은 인터뷰에서 자주 언급되었고 아마존과 링크드인으로부터 오퍼를 받을수 있게 도와주었습니다. 재밋게 읽으시고 주제의 링크를 클릭함으로써 깊게 들어가는 것에 주저하지 마세요🙌

 

 

Web Knowledge

 

1. Caching

  • Cache-Control: 요청 및 응답 캐시 명령
  • Etag: <cache_id>  를 비교하여  리소스가 업데이트 되었는지 확인합니다. 업데이트 되지 않은 경우 cached된 버전을 업데이트 합니다.

 

2. HTTP/2

 

장점

  • 복수의 HTTP 연결 요청(HTTP/1 은 오직 6개만 지원함)
  • 서버에서 클라이언트에게 이벤트를 보낼수 있다.(SSE:Server Sent Event)
  • 압축된 headers
  • 더 안전함

 

단점

  • 서버 요청이 남용될수 있다.
  • LoadBalancer가 HTTP/1을 지원하고 server는 HTTP/2를 지원하면 느려질 수 있다.

 

3. Security

  • Transfer-Encoding - body를 어떻게 암호화할건지 정한다: chunked, gzip => (헤더의 한종류)
  • Access-Control-Allow-Origin(Cross-Origin Resource Sharing -- CORS) : origin domain의 API에 접근할수 있는 도메인 리스트들을 정의해 놓는다
  • JSONP - 도메인 간 데이터에 접근하기 위한 스크립트 실행
  • X-Frame-Options - iframe으로부터 clickjacking을 예방한다.
  • Cross-Site Request Forgery(CSRF) - 사용자는 세션을 가지고 있고, 공격자는 link를 생성한다, 사용자는 링크를 클릭하고 요청을 수행한다, 공격자는 사용자의 세션을 강탈한다. 예방법: captcha, 방문한 사이트로부터의 로그아웃 
  • Content-Security-Policy - 해로운 코드의 실행을 예방한다.
  • X-XSS-Protection - 오래된 사이트를 위해 XSS 보호가 가능하게 한다.
  • Feature-Policy - 브라우저 기능에 필요하지 않은것들은 disable 한다
  • Referrer-Policy - 다른 사이트로의 링크가 있을때 링크를 클릭함으로써 중요한 데이터를 포함할수 있는 URL의 origin을 보낼 것이다.
  • 사용자에게 HTML 넣는것을 허락하지 않는다 (innerHtml)
  • UI 프레임워크를 사용하고 node_module들의 업데이트를 계속 유지하고 서드파티의 사용을 제한한다.

 


 

Web Performance

 

1. Critical Rendering Path - 브라우저가 페이지를 그리는 단계. 그 단계들은 

  • DOM - 브라우저는 Document Object Model을 컴파일 합니다.
  • CSSOM - 브라우저는 CSS Object Model을 컴파일 합니다.
  • Render Tree - 브라우저가 DOM 과 CSSOM을 결합하여 tree를 렌더합니다. 
  • Layout - 브라우저가 각 object들의 위치와 크기를 계산합니다.
  • Paint - 브라우저가 tree를 화면의 pixel로 변환합니다.

 

CRP 최적화

  • 소스 순서를 최적화 합니다. -- 중요한 리소스를 가능한 빨리 로드합니다.
  • 소스의 수를 최소화 합니다. -- 숫자를 줄이고 비동기를 사용합니다.

 


 

2. Reflow - 브라우저는 렌더후에 각 요소들의 위치와 구조를 다시 계산합니다.

 

Reflow 최적화

  • DOM의 뎁스를 줄입니다.
  • 긴 CSS 선택자를 피하고 규칙을 최소화 합니다

 


 

3. preload, preconnect, prefetch, prerender

  • preload - 더 빠르게 로드되어져야할 필요가 있는 우선순위가 높은 소스들을 load 합니다. <link rel="preload">
  • preconnect - handshake를 가속화 해야할 필요가 있는 리소스들이 있다면 대기시간을 줄이기 위하여 <link rel="preconnect">를 사용합니다.
  • prefetch - 낮은 우선순위의 리소스들을 불러오고 캐시를 사용합니다. <link rel="prefetch">
  • dns-prefetch - 리소스를 요청하기 전에 도메인 이름 확인하는 대기시간을 줄이기 위해 사용합니다.<link rel="dns-prefetch">
  • perrender -  prefetch와 비슷하고 전체 페이지를 cache 하기위해 사용합니다. <link rel="prerender">

 


 

4. Rendering Performance

 

JS:

 

Style:

  • 선택자의 복잡성을 줄입니다.
  • 계산을 필요로 하는 스타일을 사용하는 요소들을 줄입니다.

 

Layout: (어떻게 요소들이 위치되어지고 크기를 이루는지)

  • layout의 변경을 피합니다.
  • flexbox를 사용합니다
  • css-grid를 사용합니다.

 

Paint: (픽셀 그리기: 색상, 그림자, 레이아웃의 변경은 repaint를 불러온다)

  • layout repaint를 최적화하기 위해 will-change를 사용합니다.

 


 

5. Workers

  • Service Worker - 오프라인 app을 만들기 위한 도구
  • Web Worker - 백그라운드에서 무거운 동작들을 수행한다.

 


 

6. Image Optimization

 

Format:

  • 만약 animation이 있다면 - gif대신 <video>태그를 사용하세요
  • 만약 높은 디테일과 해상도가 있다면 - PNG
  • 기하학적 형태가 필요하다면 - SVG
  • text 로고가 있다면 - font text
  • 사진이 있다면 - JPEG

 

Compression:

  • SVG - SVGO와 같은 최적화 도구를 사용하고 gzip을 사용하세요
  • WebP - 웹을 위해 최적화된 image format을 사용하세요
  • SVG 태그로부터 metadata 속성을 지우세요
  • Sprites를 사용하세요

 

 

Cache and Lazy Load:

  • 정적 데이터의 제공을 위해 CDN을 사용하세요
  • image와 비디오의 Lazy Load를 사용하세요 - <img loading="lazy" />을 사용하거나 lazysizes 같은 라이브러리를 사용하세요

 

 

DOM

 

1. Elements:

  • selector: getElementById, getElementByTagName, querySelector, querySelectorAll
  • navigation: children(elements), childNodes(nodes), firstElementChild, lastElementChild, parentElement, previousElementSibling, nextElementSibling;
  • attributes: classList, clientHeight, clientWidth, childElementCount, setAttribute(attrName, value), removeAttribute(attrName)

 


 

2. Manipulation:

    createElement('div'), append, prepend, el.cloneNode(true), remove(), insertBefore(newNode, beforeNode), insertAfter(newNode, afterNode);

 


 

3. Document Fragment - 복수의 요소를 가질수 있는 document의 가상의 복제본을 만든다. document fragment를 DOM에 넣어줌으로써 단 한번의 reflow만 실행시킬수 있다.

 

 


 

4. Event delegation and bubbling:

  • 클릭과 같은 이벤트를 발생시킬때 parentElement를 통해 <html>까지 bubbling 되어진다.
    - html (bubble click)
        -body (bubble click)
        	-div (bubble click)
            	-p
                -p (click)​
  • 위임은 성능의 증가를 위해 사용한다. 아래와 같은 구조가 있다고 가정해보자
-div.parent  
     -p.child
     -p.child
     -p.child

우리는 .childaddEventListener을 할당하고 싶다. 이러한 경우 3개의 요소에 이벤트를 붙여야 하는데, 대신에 .parent에 이벤트를 붙여서 로직을 해결할 수 있다

document.querySelector(".parent").addEventListener("click", 
	function(event) {
    	if(event.target.classList.contains("child")){
        	// your logic is here
        };
    }
);

 


 

HTML

 

1. Semantic Elements - 이름과 함께 의미를 명확하게 개발자와 브라우저에게 나타낼수 있다: article, aside, details, figcaption, figure, footer, header, main, mark, nav, section, summary, time 

 


 

2. Accessibility

  • 헤더태그를 사용한다 <h1>, <h2>, <h3>
  • <img alt="" 를 사용한다
  • Tab키를 이용해 focus를 이동시킬때를 위해 tabindex="index_position" 속성을 사용한다.
  • roles를 사용한다. <ul role="list"><li role="listitem">, <dialog role="dialog"와 같이. 모든 리스트는 이곳에서 찾을수 있습니다.
  • accesskey="key"를 사용한다. 키보드 단축기 생성을 위해
  • 요소들을 나타내기 위한 속성들을 사용한다: aria-label="label_text" 혹은 aria-labelledby="text_id", aria-describedby="text_id" 그리고 <label id="text_id">label_text</label>
  • 색깔의 대비와 질감을 사용한다.
  • text size를 사용한다
  • video에서 caption을 사용한다

 


 

3. Responsive web

  • <meta viewport name="viewport" content="width=device-width, initial-scale=1.0">을 추가한다. 브라우저의 확장 방향을 알려주기 위해
  • <img max-width="100%">를 사용하면 이미지는 해당 이미지의 크기보다 더 커지지 않을 것이다.
  • <picture><source srcset="" media="">를 사용한다. 화면 크기 별로 다른 이미지를 명확히 해주기 위해서
  • 반응형 font size들을 사용한다: emrem
  • media query들을 사용한다

 


 

Javascript

 

1. this

  • this는 함수가 호출되는 객체의 참조입니다.
  • this의 default는 window 입니다.
  • 함수가 호출된 곳에서 context를 가져옵니다.
  • 화살표 함수 -> 는 함수가 정의된 곳으로 부터 context를 가져옵니다.
  • this는 다른 함수 안에서 함수를 호출할때 context를 잃어버립니다.
    function Foo() {
    	console.log(this);
    }
    Foo(); // at this line the context is 'window'
    // output 'window'
    
    var foo1 = new Foo(); // at this line the context binds to 'foo1'
    // output 'Foo {}'​
  • this context의 명확한 할당: foo1.apply(context, arrayOfArgs), foo1.call(context, arg1, arg2, ...), var foo2 = foo1.bind(context, arg1, arg2, ..) --- 주어진 context와 함께 함수를 반환해 줍니다.

 


 

2. Closure - 다른 scope에서 호출되었을 지라도 scope를 기억하고 접근할 수 있는 기능(함수가 block scope 내에서 function을 반환한 경우)

function a(arg1) { // arg1 scoped
	var arg2 = 0;  // arg2 scoped
     
    return function b() {
    	++arg2;
        return arg1 + arg2
    }
}

var foo = a(2);
foo(); // 2
foo(); // 3
var foo2 = a(4);
foo(); // 4
foo(); // 5

 


 

3. Inheritance

  • obj2로 부터 ob1을 상속받기 위해 object와 다른 object를 연결지을수 있다. var obj1 = Object.create(obj2);
  • JS는 프로토타입 상속을 사용한다. 각각의 Object는 __proto__ 를 가진다. 만약 우리가 object의 속성에 접근한다면 Js 엔진은 해당 객체가 속성을 가지고 있는지 체크하고 만약 가지고 있지 않다면 프로토타입을 확인한다. 해당 속성을 찾으려고 계속 __proto__ 연결을 통해 접근하고 만약 정의되어 있지 않다면 undefined를 반환한다.

 


 

 

4. Asynchronous Javascript

  • Event loop: JS에는 세 형태의 메모리가 있다: 함수 호출을 위한 stack, 객체들을 위한 heap, setTimeout을 위한 queue, Js 엔진은 stack의 함수를 첫번째로 실행하고 만약 stack이 비었다면 queue로 부터 이벤트를 pop 한다. 만약 queue의 이벤트가 함수의 호출이 필요하다면 이것을 stack에 담고 이 과정을 queue가 비어질때까지 실행한다. 이것을 event loop라 부른다.
  • Js는 callback, promise, async-await을 비동기 동작을 수행하기 위해 사용한다. 아래의 글에서 좀 더 많은것을 확인할 수 있다.

https://itnext.io/the-evolution-of-asynchronous-patterns-in-javascript-64efc8938b16

 

🔥 The Evolution of Asynchronous Patterns in JavaScript

Let’s talk about async patterns used in JavaScript

itnext.io

 

 


 

 

5. Hoisting

  • Js 컴파일에서 function 정의는 block scope의 최상단으로 이동한다, 그 후에 var가 위치된다.
  • letconst도 hoisting 된다 하지만 temporary dead zone에 위치한다
// Code example                       // After hoisting
1. console.log('hoisting');           1. function foo() {
2. var a;                             2.     return null;
3. function foo() {                   3. }
4.    return null;                    4. var a;
5. }                                  5. console.log('hoisting');
6. a = 5;                             6. a = 5;

 


 

 

6. Currying - 중첩된 함수들

function calcABC(a) {
	return function(b) {
    		return function(c) {
            	return a + b + c;
            }
    }
}

console.log(calcABC(1)(2)(3)); // 6

 


 

7. Higher-order functions

  • map, reduce, filter, find
  • Higher-order 함수들을 연결지을수 있다.
    [1,2,3,4,5].map((num) => ({age: num})).filter((el) => el.age < 4);
    // [{age: 1}, {age: 2}, {age: 3}]​

 

Design patterns

 

1. Mixin - 함수의 기능을 확장한다.

// Option 1
class Person {}

let Mixin = {foo(){}};

Object.assign(Person.prototype, Mixin);

let person = new Person();

person.foo();

// Option 2

let Mixin1 = {foo1(){}};

let Mixin2 = {__proto__: Mixin1, foo2(){}};

 


 

2. Factory - 하나 혹은 다른 여러개의 객체들을 생성할 수 있는 class(단위 테스트에서 다른 mock 데이터를 생성하고 싶을때 유용하다)

personFactory.one({name: 'John'}); -> Person{name: 'John', age: 5}

personFactory.many(); -> [Person{name: 'Bill', age: 7}, Person{name: 'Anna', age: 3}]

 


 

3. Singleton -- 객체의 생성 없이 함수를 직접 불러올수 있는 class

let mySingleton = (function(){
	let instance = null;
    
    function init() {
    	return {
        	// list all the methods
            method(){};
        }
    }
    
    if (instance === null) {
    	instance = init();
    }
    
    return instance
})();

mySingleton.method();

 


 

4. Facade - 복잡한 로직을 추상화 시키고 이걸 class로 감싼다, 예를들면 component와 API layer 사이에 있는 서비스

ui component - Facade service (complex state object) - API layer(Redux);

 


 

5. MVC, MVVM -- Model Viw Controller, Model View ViewModel

 

React는 MVC

  • state - Model
  • JSX - View
  • actions(위반시 view와 섞일수도 있다) - Controller

 

Angular는 MVVM

  • component -- ModelView
  • template -- View (위반시 재사용 불가)
  • properties -- Model

 


 

 

6. Server vs Client-Side Rendering

 

SSR - 사이트가 안정적이고, 정적이고, SEO에 초점을 두고 있고 추가 서버를 결제할 수 있다면 SSR을 사용하라

 

장점

  • 빠른 page 로드(보이는 건 가능하지만 동작은 안한다)
  • 검색엔진에 더 좋다(빠른 인덱싱)
  • 정적 콘텐츠가 많은 사이트에 더 좋다(블로그들)

 

단점

  • 서버 요청이 더 많다
  • interact를 위한 render가 느리다
  • 전체 페이지가 reload 된다.

 

CSR - 사이트가 개발중에 있고 동적이라면 CSR을 사용하라

 

장점 

  • 초기 로드 후에 렌더가 빠르다
  • web app에 좋다

 

단점

  • 초기 로드에 시간이 많이 소요된다

 


 

Conclusion

 

big tech 대기업의 프론트엔드 면접을 통과하기 위한 자신감을 가지기위해 개발자가 알아야할 필요가 있는 지식은 매우 많다. 

비록 코딩 문제와 시스템 디자인으로 구성된 더 복잡한 라운드가 있을지라도 웹 관련 질문들을 해결하기 위해 프론트엔드 도메인 지식은 중요합니다.

 

 

 

 

https://itnext.io/frontend-interview-cheatsheet-that-helped-me-to-get-offer-on-amazon-and-linkedin-cba9584e33c7

 

🔥 Frontend Interview Cheatsheet That Helped Me Get Offers From Amazon & LinkedIn

If you are preparing for a frontend interview & want to quickly refresh your domain knowledge, this cheatsheet will save you a lot of time.

itnext.io

 

var는 javascript에서 es5 이전에 등장한 변수 선언 키워드 입니다.

let, const는 es6에서 등장한 변수 선언 키워드 입니다.

 

es6 이상에서는 대부분의 책, 블로그 들에서 var를 쓰지 말라고들 합니다. 

그 이유에 대해서 알아볼게요

 

  • 스코프

var는 함수 레벨 스코프를 가집니다.

let, const는 블록 레벨 스코프를 가집니다.

 

// var 
var a = 1;
{
	var a = 3;
    console.log(a) // 3
}
console.log(a) // 3

// let
let b = 1 // 전역변수
{
	let b = 2;  // 지역변수
    console.log(b) // 2
}
console.log(b) // 1

// const
const c = 1; // 전역변수
{
	const c = 2; // 지역변수
    console.log(c) // 2
}
console.log(c) // 1

 

let은 수정이 가능한 변수지만, const는 상수의 개념이기 때문에 수정이 불가능 해요.

둘 다 물론 중복 선언은 불가능합니다.(var는 가능해요)

 

 

  • 호이스팅

호이스팅은 변수들의 선언을 코드 최상단으로 옮깁니다.

호이스팅 덕분에 이러한 것들이 가능합니다.

dogName("test")
function dogName(name) {
	return "내 강아지 이름은 " + test + "입니다"
}

자바스크립트에서 인터프리터가 변수와 함수의 메모리 공간을 선언 전에 미리 할당하는 것이지요.

호이스팅에서는 var로 선언된 변수는 undefined로 변수를 초기화 시켜버리고 let,const로 선언된 변수는 초기화를 시키지 않아요.

 

함수 선언의 호이스팅이 되는 경우 문제가 발생할 수 있어요. 

협업의 경우에서 생각해 볼게요

console.log(sum(1,2))
function sum(a, b) {
	return a + b;
}	

console.log(sum(2,3))

function sum(a, b) {
	return "a + b = " + (a + b);
}	

console.log(sum(3,4))

이전의 개발자는 sum을 결과값을 반환하게 했지만 이후의 개발자는 텍스트도 같이 반환하게 했습니다.

호이스팅으로 인해 이전의 코드에 영향이 갈수도 있어요.(매우 좋지 않네요)

 

함수 선언의 호이스팅을 방지하기 위해 함수 표현식을 사용하라고 권장하곤 합니다.

var sum = function (a, b) {
	return a + b;
}	

console.log(sum(2,3)) // 5

var sum = function (a, b) {
	return "a + b = " + (a + b); 
}	

console.log(sum(3,4)) //  a + b = 7

(이런.. 중복 선언또한 var는 가능하네요)

 

var, let, const는 모두 호이스팅이 되어집니다.

console.log(x) // undefined
var x = 1


var x;
console.log(x)
x = 1;

위의 함수는 에러를 발생시키지 않아요. 아래와 같이 변수 선언 들이 스코프 내에서 최상단으로 끌어올려져 버립니다. 

이게 왜 문제가 될까요??

 

var i = 20

for (var i = 0; i < 10; i++) {
	console.log(i)
}

console.log(i);

이와 같이 예전에 선언한 변수들에게 영향이 끼쳐 버리게 됩니다. 
의도치 않게 변수값이 계속 바뀌어 버릴수 있어요

 

let과 const둘다 호이스팅이 되어 집니다. 하지만 이들은 호이스팅시 임시로 참조할수 없는 구역 TDZ(Temporal Dead Zone)에 위치하게 됩니다.

만약 호이스팅이 되지 않는다면

let foo = "foo"
{
	console.log(foo); // 에러 발생
    let foo = "bar";
}

// 호이스팅이 되지 않는다면 3번째 줄의 코드라인은 전역 객체의 foo를 참조해야만 합니다.
// 블록 스코프 내에서 foo가 호이스팅이 되고 TDZ에 존재하지 때문에 3번째 줄에서 에러가 나버려요

 

> TDZ: 스코프의 시작 지점부터 초기화 시작 지점까지 변수를 참조할 수 없는 구간 (일시적 사각지대)

 

* const는 변수의 선언과 할당을 동시에 해주어야만 합니다

let a;
a = 1;

// 아래 코드는 불가
const b; // error 발생
const b = 2


const c = 3;

 

* const는 상수의 개념이지만 mutable 객체의 경우 변경이 가능해요. 배열이나 객체와 같은 참조에 의한 전달이 이루어지는 경우 값의 변경이 가능해 집니다. => const 키워드는 재할당을 금지할뿐 '불변'은 아니다!!!

 

const building = {
	floor: 4
}

building.floor = 5;
console.log(building)

 

> 자바스크립트는 es6에 도입된 모든 선언(var, let, const, function, function*, class)를 호이스팅 합니다. 단 es6에서 도입된 let, const, class를 사용한 선언문은 호스팅이 발생하지 않는 것처럼 동작합니다(TDZ개념 이용)

 

 

아래 3가지 권고사항만 잘 지킵시다

  • es6를 사용한다면 var 키워드는 x
  • 재할당이 필요한 경우에는 let키워드를 사용합시다! 이때 변수의 스코프는 최대한 작게 만들어 주도록
  • 변경이 발생하지 않고 읽기 전용으로 사용하는 원시값과 객체에는 const 키워드를 사용합시다. const 키워드는 재할당을 금지하므로 var, let보다 안전합니다

 

변수 선언할때는 일단 const 쓰고 이후에 let으로 필요한 경우에 바꾸더라도 충분히 늦지 않아요!

 

 

 

 

 

출처 : https://developer.mozilla.org/ko/docs/Glossary/Hoisting

DeepDive 책

+ Recent posts