웹 프론트엔드 기술면접 질문 이해 - 1

chu·2021년 5월 14일
2

웹 프론트엔드 기술면접 최대한 이해 하자 #1

틀린 부분이 있으면 피드백 부탁드립니다! (꾸벅)

DOM (Document Object Model)

문서 객체 모델(The Document Object Model, 이하 DOM) 은 HTML, XML 문서의 프로그래밍 interface 이다. DOM은 문서의 구조화된 표현(structured representation)을 제공하며 프로그래밍 언어가 DOM 구조에 접근할 수 있는 방법을 제공하여 그들이 문서 구조, 스타일, 내용 등을 변경할 수 있게 돕는다.

출처 MDN

간단하게 HTML, CSS를 Javascript에게 보여준다면 무슨 내용인지 정확히 의미를 알 수 없다. 그렇기 때문에 Document 안에 있는 HTML 태그, 속성 등을 구조화된 문서를 제공해서 Javascript로 제어할 수 있도록 도와주는 역할을 하고 있다.

궁금하다면 console.dir(document)로 직접 어떻게 구조가 되어있는지 DOM 객체를 확인하면 좋을 것 같다. 흔히 아는 내용들이 많다.

DOM vs Virtual DOM(가상DOM)

가상 DOM은 DOM 요소와 더 쉽고 성능이 뛰어난 방식으로 인터페이스 할 수있는 도구입니다. 필요한만큼 자주 수정할 수있는 DOM의 Javascript 객체 표현입니다. 그런 다음이 개체에 대한 변경 사항이 수집되고 실제 DOM에 대한 수정 사항이 대상으로 지정되고 덜 자주 수행됩니다.

출처 : https://bitsofco.de/understanding-the-virtual-dom/

위 내용은 아래 출처를 번역한 내용입니다. 위 내용을 좀 더 간단하게 풀이하자면,

가상DOM은 실제DOM을 복사한 DOM이라고 생각할 수 있다.(생각일 뿐이다. 사람이 편하게 이해할 수 있도록) 2개의 가상DOM 존재하며 변화가 일어날 경우 가상DOM끼리 비교를 한 후 실제DOM에 변경된 부분만 업데이트를 처리한다. 반대로 가상DOM이 없는 경우에는 실제DOM에 변경이 있을 때 변경된 내용을 모두 바꾸고나서 한번에 업데이트를 진행한다.

이때 Diff 알고리즘을 통해 비교 분석하여, UI 업데이트를 한다.

Element Type을 비교한다고 한다.

var , let , const

현재는 var 변수 선언문을 억제하는 것이 룰이다. 그렇게 때문에 let, const를 사용힌다.
왜 그런지 알아볼 필요가 있다.

var

var는 ES5까지 변수를 선언하는 방법이다. 하지만 아래와 같은 단점으로 인해 사용을 억제한다.

1. 함수 레벨 스코프

  • 함수의 코드 블록만을 스코프로 인정한다. 따라서 전역 함수 외부에서 생성한 변수는 모두 전역 변수이다. 이는 전역 변수를 남발할 가능성을 높인다.

  • for 문의 변수 선언문에서 선언한 변수를 for 문의 코드 블록 외부에서 참조할 수 있다.

2. var 키워드 생략 허용

  • 암묵적 전역 변수를 양산할 가능성이 크다.

3. 변수 중복 선언 허용

  • 의도하지 않은 변수값의 변경이 일어날 가능성이 크다.

4. 호이스팅

  • 변수를 선언하기 이전에 참조할 수 있다.

    호이스팅(Hoisting)이란, var 선언문이나 function 선언문 등을 해당 스코프의 선두로 옮긴 것처럼 동작하는 특성을 말한다.

// var
console.log(arr); // undefind
var arr;

// let, const
console.log(hello); // ReferenceError

let hello;

참고로, 변수는 선언 단계 > 초기화 단계 > 할당 단계 에 걸쳐 생성되는데
var로 선언된 변수는 선언 단계와 초기화 단계가 한번에 이루어진다. 하지만,

// 스코프의 선두에서 선언 단계와 초기화 단계가 실행된다.
// 따라서 변수 선언문 이전에 변수를 참조할 수 있다.

console.log(foo); // undefined

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

foo = 1; // 할당문에서 할당 단계가 실행된다.
console.log(foo); // 1

let, const 로 선언된 변수는 선언 단계와 초기화 단계가 분리되어 진행된다.

// 스코프의 선두에서 선언 단계가 실행된다.
// 아직 변수가 초기화(메모리 공간 확보와 undefined로 초기화)되지 않았다.
// 따라서 변수 선언문 이전에 변수를 참조할 수 없다.

console.log(foo); // ReferenceError

let foo; // 변수 선언문에서 초기화 단계가 실행된다.
console.log(foo); // undefined

foo = 1; // 할당문에서 할당 단계가 실행된다.
console.log(foo); // 1

const

const는 let과 동일한 부분이 많다. 그 중 const, let은 블록 레벨 스코프이다. 그러면 다른 점만 정리 하겠다.

1. 상수 저장
const는 상수를 저장하는 변수이다. (상수 - 변하지 않는 값)
하지만 아래와 같은 예시에서는 저장된 정보를 변경할 수 있다.

const hello = { name: 'bye' };
hello = { name: 'bye2' }; // Uncaught TypeError: Assignment ...

hello.name = 'bye2'; // OK
console.log(hello.name); // bye2

즉, 객체 리터럴을 감싸는 {} 자체를 대체하려고 한다면 에러가 발생하지만, {} 내부의 프로퍼티를 변경하는 것은 가능하다. 간단하게 재할당만 금지된다.

2. 선언만 하면 에러
변수를 선언하고 값을 할당하지 않으면 아래와 같은 에러가 발생한다.

const obj; // SyntaxError: Missing initializer ...

출처 : https://poiemaweb.com/es6-block-scope

OPP & FP

면접 후기를 보았을 때 매번 등장하는 문제인 것 같다. OPP와 FP의 차이 점!

OPP (Object-Oriented Programming)

객체 지향 프로그래밍 이라고 불린다. 정확히 객체 지향이라는 개념은 명확하게 정해진 것은 없다고 한다. 객체지향 프로그래밍은 보다 유연하고 유지보수하기 쉬우며 확장성 측면에서서도 유리한 프로그래밍을 하도록 의도되었다. 현재 대규모 애플리케이션은 이런 객체 지향 프로그래밍으로 구현 되어있다.

OPP 출처 : https://poiemaweb.com/js-object-oriented-programming

OPP 특징

  • 상속 : 클래스개념에서 상위 클래스(부모)로 부터 하위 클래스(자식)이 유산을 물려받는것과 같이, 부모의 메소드나 변수를 사용할 수 있는 것을 말함.

  • 다형성 : 같은 함수가 있다고 칠대 그 함수가 매개변수에 따라 다른 역할을 할 수 도 있다.

  • 캡슐화 : 보통 데이터를 은닉시킨다고 표현하는데, 외부에서 쉽게 데이터를 접근할 수 없게 만들기도하고, 데이터 구조와 데이터를 다루는 방법들을 한데다 묶는것.

  • 추상화 : 공통적인 속성이나 기능을 묶어서 이름을 붙이는 것 ( a b d 이런게있다고 치면 이런건 알파벳이라고 묶을 수 있다)

OPP 특징 출처 : https://sunnykim91.tistory.com/121

FP (Functional Programming)

함수형 프로그래밍은 순수함수와 보조 함수의 조합을 통해 로직내에 존재하는 조건문과 반복문을 제거하여 복잡성을 해결하고 변수의 사용을 억제하여 상태 변경을 피하려는 프로그래밍 패러다임이다.

조건문 제거

우리가 흔히 하는 조건문은 if else, switch case 등 있다.
이것을 사용하지 않고 조건문을 작성하라구욧!!? 그래야 한다고 하더라... 예를 들자면

// 조건문 O
function impureFunc(num){
  if (typeof num === 'number') {
  	return console.log('number');
  }
  return console.log('Not a number');
}

// 조건문 X
type NumberOrNull = number | null;

function pureFunc(num: NumberOrNull) {
  return num;
}

조건이 필요할 경우가 분명히 생길 수 있다. 그럼 위 처럼 if문이 아닌 타입을 생성해서 체크하는 것도 필터링하는 방법이 될 수 있다.

반복문 제거

// for문 X
function impureFunc(arr) {
  for (let i = 1; i <= arr.length; i++) {
    console.log(i);
  }
}
impureFunc([1,2,3,4,5]);

// map O
function pureFunc(arr) {
  arr.map((el) => console.log(el));
}
pureFunc([1,2,3,4,5]);

순수함수 란

순수함수는 같은 입력이 주어지면, 같은 출력을 반환해야하고, side effect가 없어야한다.
결국, 함수형 프로그래밍은 순수함수를 통해 side effect를 최대한 억제하여 오류를 피하고 프로그램의 안정성을 높이려는 노력의 한 방법

const c = 100;

function pureFunc(a, b) { // 순수함수 O
  console.log(a + b);
}
pureFunc(1, 2); // 3

function impureFunc(a, b) { // 순수함수 X
  console.log(a + b + c);
}
impureFunc(1, 2); // 103

(조건문, 반복문, 순수함수 등 개인의 의견으로 만든 함수이며, 틀리다면 피드백 부탁드립니다!)

위 처럼 외부의 변수를 참조하거나, 입력에 맞는 출력이 다를 경우 순수함수에 맞지 않는다.

또한 변수의 값을 직접 변경해서도 안된다. 만약 변수의 값을 변경해야하는 상황이라면, 새로운 변수를 생성에서 그 안에 넣거나, 새로운 값을 리턴해서 사용해야 한다.

이 밖에도 함수형 프로그래밍은 Monad, 고차함수(Higher-order function), 모든 식은 값을 반환하는 등... 여러 조건이 있다. 한 마디로 이해를 한다해서 쉽게 적용하거나 할 수 없는 영역같다...

OOP와 함수형 프로그래밍의 가장 큰 차이점

객체지향은 객체 안에 상태를 저장하고, 이 상태를 이용해서 메소드를 추가하고 상태변화를 설정하고, 조정하기위해 다양한 기능을 사용한다. 이에 반해 함수형 프로그래밍은 상태를 제어하는것보다 상태를 저장하지 않고 없애는데 주력한다.

예를들면, 객체 지향은 상태를 저장하는 필드와 그 필드들을 이용해 기능을 제공하는 메소드를 만들고 클래스를 만듭니다. 반면 함수형은 몇몇 자료구조(list, map, set) 등을 이용해 최적화된 동작을 만들어낸다.

출처 : https://sunnykim91.tistory.com/121

함수와 화살표 함수

이 내용은 차이점을 중점으로만 소개하려고 한다.

  1. => 붙혀서 사용되어 화살표함수(Arrow Function)이라 불린다.

  2. 화살표 함수는 익명함수이며, 따라서 함수 표현식을 사용한다.

// 함수
function func() { ... }

// 화살표 함수
const func = () => { ... }
  1. 일반 함수와 가장 큰 차이점 this가 어디를 가르키고 있는가?

자바스크립트 함수

함수를 선언할 때 this에 바인딩할 객체가 정적으로 결정되는 것이 아니고, 함수를 호출할 때 함수가 어떻게 호출되었는지에 따라 this에 바인딩할 객체가 동적으로 결정된다.

함수 호출 방법

  • 함수 호출
  • 메소드 호출
  • 생성자 함수 호출
  • apply/call/bind 호출(전역을 가리키는 this를 회피하는 방법)

화살표 함수 this

화살표 함수는 함수를 선언할 때 this에 바인딩할 객체가 정적으로 결정된다. 동적으로 결정되는 일반 함수와는 달리 화살표 함수의 this는 언제나 상위 스코프의 this를 가리킨다. 이를 Lexical this라 한다.

출처
https://poiemaweb.com/js-this
https://poiemaweb.com/es6-arrow-function

동기 & 비동기

동기식 처리 모델

동기식 처리 모델은 직렬 태스트(Task)로 순차적 실행을 한다.

아래 코드는 순차적으로 실행되는 예제이다.

a() 실행 및 콘솔 출력 후 b 호출
b() 실행 및 콘솔 출력 후 c 호출
c() 실행 및 콘솔 출력 후 끝

function a() {
  console.log('a');
  b();
}

function b() {
  console.log('b');
  c();
}

function c() {
  console.log('c');
}

a();

순차적으로 실행되어 문제가 없다. 하지만 b()에서 서버에 데이터를 요청해서 받아오거나, setTimeout같은 시간과 관련된 함수를 사용을 하면 c()는 b() 처리가 끝나야 실행 할 수 있다.
이와 같은 상황을 블로킹(Blocking)이라고 한다.

비동기식 처리 모델

비동기는 병렬적으로 태스크(Task)를 실행할 수 있도록 한다.

아래 예시 코드를 보면 a 실행 후 b가 실행되는데 setTimeout으로 0초 뒤에 콘솔을 출력한다.
이렇게 본다면 콘솔에 a -> b -> c가 될 것 같지만, a -> c -> b가 출력이 된다.

function a() {
  console.log('a'); // 1 - a
  b();
}

function b() {
  setTimeout(function(){
  	console.log('b'); // 3 - b
  }, 0);
  c();
}

function c() {
  console.log('c'); // 2 - c
}

a();

setTimeout은 비동기식 처리 모델이기 때문이다. 0초를 해도 실제로는 실행 순서에서 빠지게되어
동기 이벤트가 다 처리되고 난 후에 b가 출력된다. 예시로는 서버에 데이터를 처리 요청 및 응답을 받을 때 비동기식 처리 모델로 처리한다.

자바스크립트의 대부분의 DOM 이벤트 핸들러와 Timer 함수(setTimeout, setInterval), Ajax 요청은 비동기식 처리 모델로 동작한다.

출처 : https://poiemaweb.com/js-async

Ajax(Asynchronous JavaScript and XML)

자바스크립트를 이용해서 비동기적으로 서버와 브라우저가 데이터를 교환할 수 있는 통신 방식을 의미한다. 기존에는 페이지 내부에 데이터가 바뀌어도 모든 페이지를 서버로 부터 요청하여 받아왔다. Ajax는 일부 변경된 데이터만 받아와서 업데이트 하기 때문에 좋은 성능을 보일 수 있다.

브라우저가 가지고있는 XMLHttpRequest 객체를 이용해서 전체 페이지를 새로 고치지 않고도 페이지의 일부만을 위한 데이터를 로드하는 기법 이며, JavaScript를 사용한 비동기 통신, 클라이언트와 서버간에 XML 데이터를 주고받는 기술이다. 정리하자면, 자바스크립트를 통해서 서버에 데이터를 요청하는 것이다.

아래는 사용 예제 코드이다.

// 클라이언트-사이드 스크립트

// Ajax 요청을 초기화합니다
var xhr = new XMLHttpRequest();
xhr.open('get', 'send-ajax-data.php');

// 요청의 상태 변화를 추적합니다
xhr.onreadystatechange = function(){
  if(xhr.readyState !== 4) return;
  // readyState 4: 완료

  if(xhr.status === 200) {
    // status 200: 성공
    console.log(xhr.responseText); // '반환된 텍스트'
  } else {
    console.log('에러: ' + xhr.status); // 요청 도중 에러 발생
  }
}

출처 : https://poiemaweb.com/js-ajax

Promise

자바스크립트는 비동기 처리를 위한 하나의 패턴으로 콜백 함수를 사용한다. 하지만 전통적인 콜백 패턴은 콜백 헬로 인해 가독성이 나쁘고 비동기 처리 중 발생한 에러의 처리가 곤란하며 여러 개의 비동기 처리를 한번에 처리하는 데도 한계가 있다.

ES6에서는 비동기 처리를 위한 또 다른 패턴으로 프로미스(Promise)를 도입했다. 프로미스는 흔히 알고있는 콜백헬과 에러처리 한계 등 을 보안하여 비동기 처리 시점을 명확하게 표현할 수 있다는 장점이 있다.

프로미스는 Promise 생성자 함수를 통해 인스턴스화한다. Promise 생성자 함수는 비동기 작업을 수행할 콜백 함수를 인자로 전달받는데 이 콜백 함수는 resolve와 reject 함수를 인자로 전달받는다.

// Promise 객체의 생성
const promise = new Promise((resolve, reject) => {
  // 비동기 작업을 수행한다.
  if (/* 비동기 작업 수행 성공 */) {
    resolve('result');
  }
  else { /* 비동기 작업 수행 실패 */
    reject('failure reason');
  }
});

Promise method

Promise로 구현된 비동기 함수는 Promise 객체를 반환하여야 한다. Promise로 구현된 비동기 함수를 호출하는 측(promise consumer)에서는 Promise 객체의 후속 처리 메소드(then, catch)를 통해 비동기 처리 결과 또는 에러 메시지를 전달받아 처리한다. Promise 객체는 상태를 갖는다고 하였다. 이 상태에 따라 후속 처리 메소드를 체이닝 방식으로 호출한다.

Promise의 후속 처리 메소드는 아래와 같다.

then
then 메소드는 두 개의 콜백 함수를 인자로 전달 받는다. 첫 번째 콜백 함수는 성공(fulfilled, resolve 함수가 호출된 상태) 시 호출되고 두 번째 함수는 실패(rejected, reject 함수가 호출된 상태) 시 호출된다.
then 메소드는 Promise를 반환한다.

catch
예외(비동기 처리에서 발생한 에러와 then 메소드에서 발생한 에러)가 발생하면 호출된다. catch 메소드는 Promise를 반환한다.

const $result = document.querySelector('.result'); // div
const render = content => { $result.textContent = JSON.stringify(content, null, 2); };

const promiseAjax = (method, url, payload) => {
  return new Promise((resolve, reject) => {
    // Ajax 코드 ...
  });
};

// 비동기 함수 promiseAjax은 Promise 객체를 반환한다.
// Promise 객체의 후속 메소드를 사용하여 비동기 처리 결과에 대한 후속 처리를 수행한다.
promiseAjax('GET', 'http://jsonplaceholder.typicode.com/posts/1')
  .then(JSON.parse)
  .then(
  // 첫 번째 콜백 함수는 성공(fulfilled, resolve 함수가 호출된 상태) 시 호출된다.
  render,
  // 두 번째 함수는 실패(rejected, reject 함수가 호출된 상태) 시 호출된다.
  console.error
);     

출처 : https://poiemaweb.com/es6-promise

Async await

Promise기반 코드를 좀 더 쓰기 쉽고 읽기 쉽게 만들어줍니다. 이 기능을 사용하면 비동기 코드를 구식 동기 코드처럼 보여주기 때문에 충분히 배울 가치가 있습니다. - MDN -

async를 사용하기 위해서는 function 앞에 async를 붙혀야 된다.

// async x
function foo() { ... }

// async
async function foo() { ... }

// arrow function async
const foo = async () => { ... }

awaitasync로 감싸진 함수 내부에서만 사용할 수 있다.

const foo = async () => {
  try {
    const response = await get(...url);
    return response.data
  } catch(error) {
    // ...
  }
}

위 처럼 await은 변수안에 반환된 값을 할당할 수 있고, JavaScript 런타임이 이 라인에서 비동기 코드를 일시 중지하여 비동기 함수 호출이 결과를 반환할 때 까지 기다리게 합니다.

출처 : https://developer.mozilla.org/ko/docs/Learn/JavaScript/Asynchronous/Async_await

promise 소개 부분에서 then 메소드를 사용해서 후속처리를 할 수 있는데, 이 부분도 여러 처리를 해야하는 상황이 온다면 콜백헬에 비해 깔끔하지만, 아쉬운 부분이 있다. 그 부분을 해소시켜주는 방법 중 async/await 이다.

fetch

브라우저에서 제공하는 API이며, fetch() 함수는 첫번째 인자(필수값)로 URL, 두번째 인자로 옵션 객체를 받고, Promise 타입의 객체를 반환합니다.

두번째 인자로 오는 옵션은 다양합니다. 여기서 확인해보면 좋습니다.

특징

  1. fetch()로 부터 반환되는 Promise 객체는 HTTP error 상태를 reject하지 않습니다. HTTP Statue Code가 404나 500을 반환하더라도요. 대신 ok 상태가 false인 resolve가 반환되며, 네트워크 장애나 요청이 완료되지 못한 상태에는 reject가 반환됩니다.

  2. 보통 fetch는 쿠키를 보내거나 받지 않습니다. 사이트에서 사용자 세션을 유지 관리해야하는 경우 인증되지 않는 요청이 발생합니다. 쿠키를 전송하기 위해서는 자격증명(credentials) 옵션을 반드시 설정해야 합니다.

출처는 위 '여기서'를 클릭하면 확인 가능합니다.

또한 여기는 쉽게 이해할 수 있도록 소개해주는 페이지라 공유합니다.

Axios

fetch와 마찬가지로 서버와 데이터를 주고 받을 때 대표적으로 많이 쓰이며 통신 기법이며, 브라우저, Node.js를 위한 Promise API를 활용하는 HTTP 비동기 통신 라이브러리이다.

(개인 프로젝트에서는 axios를 사용해서 백 서버와 통신을 했다.)

특징

  • 운영 환경에 따라 브라우저의 XMLHttpRequest 객체 또는 Node.js의 http api 사용
  • Promise(ES6) API 사용
  • 요청과 응답 데이터의 변형
  • HTTP 요청 취소
  • HTTP 요청과 응답을 JSON 형태로 자동 변경

출처 : 여기

profile
한 걸음 한걸음 / 현재는 알고리즘 공부 중!

0개의 댓글