=> 이벤트 위임은 부모 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 객체에 대한 정보
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) 으로서 사용을 하기 위해서는 ()로 전체를 감싸주거나 함수 선언식을 감싸주어 함수 표현식으로 변경해 주어야 합니다.
즉시 실행 함수에서는 기명함수로도 익명함수로도 동작할 수 있습니다.
즉시 실행 함수를 사용하는 이유는 전역변수에 불필요한 변수를 추가하지 않기 위해 사용합니다.
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?
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);
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 등이 있습니다.
이들은 원본데이터의 변형을 초래하지 않아서 순수함수에 자주쓰이고 순수함수는 함수형 프로그래밍에 쓰입니다.
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
만약에 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, 프로세스가 죽으면 데이터를 지우고 빠른접근이 가능하고 휘발성 입니다.
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를 증가하기 위한 시스템의 복제 (단일 실패 지점이 없는지 확인 - 이중화 사용)Passiveredundancy (2개의 기기중 하나가 죽으면 다른 하나가 동작),activeredundancy (만약 하나가 죽으면 다른 하나가 기능을 수행한다.)
서버들간에 트래픽을 분배해주는것. 서로 다른 선택 전략을 가진 여러가지의 로드밸런서 들이 있습니다.
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
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(지속성) — 트랜잭션들은 비휘발성 상태를 가지며 서로 충돌할 수 없다.
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
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,HadoopDistributed File System;
MapReduce— 데이터 분배:빠르고, 효율성 있고, 실패에 관대한; 데이터셋은 복수개의 기기로 분리되고 -> (Map) 각각의 덩어리들이 key:value로 구성됨 -> (Shuffle) 재구성되고 -> (Reduce) 의미있는 데이터 들로 변환 된다.
Prerequisite— 우리는 파일 분배 시스템을 가지고 있다. 데이터셋을 덩어리로 분리하고, 여러 기기들간에 나누어 주며 중앙(map/reduce 워커들간에 어떻게 소통해야 하고 오고가야하는 지를 알고 있는)에서 관리하게 됩니다. 서버들은 매핑된 데이터를 보냅니다.
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 like1000 👏 claps.
만약 프론트엔드 면접을 준비하고 있고 프론트엔드 도메인 지식을 빠르게 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 주제의 지름길일 뿐입니다. 이것들은 인터뷰에서 자주 언급되었고 아마존과 링크드인으로부터 오퍼를 받을수 있게 도와주었습니다. 재밋게 읽으시고 주제의 링크를 클릭함으로써 깊게 들어가는 것에 주저하지 마세요🙌
우리는 .child에 addEventListener을 할당하고 싶다. 이러한 경우 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="">를 사용한다. 화면 크기 별로 다른 이미지를 명확히 해주기 위해서
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
obj2로 부터 ob1을 상속받기 위해 object와 다른 object를 연결지을수 있다. var obj1 = Object.create(obj2);
JS는 프로토타입 상속을 사용한다. 각각의 Object는 __proto__ 를 가진다. 만약 우리가 object의 속성에 접근한다면 Js 엔진은 해당 객체가 속성을 가지고 있는지 체크하고 만약 가지고 있지 않다면 프로토타입을 확인한다. 해당 속성을 찾으려고 계속 __proto__ 연결을 통해 접근하고 만약 정의되어 있지 않다면 undefined를 반환한다.
Event loop: JS에는 세 형태의 메모리가 있다: 함수 호출을 위한 stack, 객체들을 위한 heap, setTimeout을 위한 queue, Js 엔진은 stack의 함수를 첫번째로 실행하고 만약 stack이 비었다면 queue로 부터 이벤트를 pop 한다. 만약 queue의 이벤트가 함수의 호출이 필요하다면 이것을 stack에 담고 이 과정을 queue가 비어질때까지 실행한다. 이것을 event loop라 부른다.
Js는 callback, promise, async-await을 비동기 동작을 수행하기 위해 사용한다. 아래의 글에서 좀 더 많은것을 확인할 수 있다.
// 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 데이터를 생성하고 싶을때 유용하다)
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);
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 키워드는 재할당을 금지할뿐 '불변'은 아니다!!!
> 자바스크립트는 es6에 도입된 모든 선언(var, let, const, function, function*, class)를 호이스팅 합니다. 단 es6에서 도입된 let, const, class를 사용한 선언문은 호스팅이 발생하지 않는 것처럼 동작합니다(TDZ개념 이용)
아래 3가지 권고사항만 잘 지킵시다
es6를 사용한다면 var 키워드는 x
재할당이 필요한 경우에는 let키워드를 사용합시다! 이때 변수의 스코프는 최대한 작게 만들어 주도록
변경이 발생하지 않고 읽기 전용으로 사용하는 원시값과 객체에는 const 키워드를 사용합시다. const 키워드는 재할당을 금지하므로 var, let보다 안전합니다
변수 선언할때는 일단 const 쓰고 이후에 let으로 필요한 경우에 바꾸더라도 충분히 늦지 않아요!