프론트엔드 질문

JS

Webpack,Babel,Polyfill

  • Webpack

모듈을 번들시켜주는 역할, 빌드라는 과정을 통해 하나의 파일로 만들어주는데, 빌드란 소스코드 파일을 실행가능한 소프트웨어의 산출물로 만드는 과정

  • Babel

자바스크립트 컴파일러로 새로운 방식의 자바스크립트로 개발한 뒤에 배포 시에는 예전 방식의 자바스크립트로 변환해서 배포하기 위해 사용

왜 사용하느냐? 최신 버전의 자바스크립트가 실행이 안되는 구버전 브라우저를 대응하기 위해 사용

  • Polyfill

최신 ECMAScript 환경을 만들어준다고 한다. 바벨은 ES6=>ES5로 변호나할 수 있는 것들만 변환하는데, ES6에서 비동기 처리를 위해 Promise와 같이 ES5에서 변환할 수 있는 대상이 없는 경우 에러가 발생한다. 이럴 때 Polyfill을 이용하여 이슈 해결이 가능하다.

이벤트 버블링, 이벤트 캡처링, 이벤트 위임

(캡틴판교님 블로그를 참조하였습니다.)

  • 이벤트 버블링: 특정 화면 요소에서 이벤트가 발생했을 때 해당 이벤트가 더 상위의 화면 요소들로 전달되어 가는 특성을 의미한다.

currentTarget: Event Handler가 연결된 것을 가리킴. 즉 이벤트가 부착된 부모의 위치를 반환한다.
target: 부모로부터 이벤트가 위임되어 발생하는 자식의 위치, 내가 클릭한 자식 요소를 반환한다.

  • 이벤트 캡처링: 이벤트 버블링과 반대 방향으로 진행되는 이벤트 전파 방식

  • 이벤트 위임: 하위 요소에 각각 이벤트를 붙이지 않고 상위 요소에서 하위 요소의 이벤트들을 제어하는 방식

브라우저의 기본 구조

1.사용자 인터페이스 : 요청한 페이지를 보여주는 창 제외한 나머지 모든 부분 (예 : 주소 표시줄, 북마크 메뉴
2.브라우저 엔진 : 사용자 인터페이스와 렌더링 엔진 사이의 동작 제어
3.렌더링 엔진 : 요청한 컨텐츠 표시 - HTML을 요청하면 HTML과 CSS를 파싱하여 표시
통신

  • HTTP 요청과 같은 네트워크 호출에 사용.
  • 플랫폼 독립적
  • 각 플랫폼 하부에서 실행
    4.UI 백엔드 : 콤보 박스와 창 같은 기본적인 장치를 그림. 플랫폼에서 명시하지 않은 일반적인 인터페이스로서, OS 사용자 인터페이스 체계를 사용.
    5.자료저장소 : 모든 종류의 자원을 하드 디스크에 저장.
    6.자바스크립트 해석기

브라우저의 렌더링 과정

  1. HTML 마크업을 처리하여 DOM 트리 생성
  2. CSS 마크업을 처리하여 CSSOM 트리 생성
  3. DOM 트리와 CSSOM 트리를 결합하여 렌더링 트리 생성
  4. 렌더링 트리 배치 : 각 노드에 대해 화면에서의 정확한 위치와 크기를 계산
  5. 렌더 트리 그리기: UI 백엔드에서 렌더링 트리의 각 노드를 가로지르며 렌더링한다.

호이스팅

호이스팅이란 마치 함수 안에있는 선언들을 모두 끌어올려서 해당 함수 유효 범위의 최상단에 선언되는 것을 말한다.

  var foo2; // [Hoisting] 함수표현식의 변수값 "선언"

  function foo() { // [Hoisting] 함수선언문
          console.log("hello");
  }

  foo();
  foo2(); // ERROR!! 

  foo2 = function() { 
          console.log("hello2");
  }

let

{
  {
    {
      var name = 'hello'
    }
  }
}
console.log(name) // hello

{
  {
    {
      let name2 = 'hi'
    }
  }
}
console.log(name2) // ReferenceError: name2 is not defined

letconst는 모두 블록 수준의 스코프를 사용하기 때문에 블록 내부에서 선언한 변수는 모두 지역 변수로 취급된다. 따라서 블록 내부에서 선언한 변수들을 참고할 수 없게 된다.

const

const는 초기화와 동시에 선언이 이루어져야 한다.

let hello
hello = 'hello'

const hi //SyntaxError: Missing initializer in const declaration
hi = 'hi'

const 자체가 값을 불변으로 만드는 것은 아니다.

Closure

function outerFunc() {
  var x = 10;
  var innerFunc = function () { console.log(x); };
  innerFunc();
}

outerFunc(); // 10

스코프는 함수를 호출할 때가 아니라 함수를 어디에 선언하였는지에 따라 결정된다.
이를 렉시컬 스코핑이라 한다.

MDN의 정의에 따르면,

"클로저는 함수와 그 함수가 선언됐을 때의 렉시컬 환경과의 조합이다."라고 나와있다.

여기서 말하는 "함수"는 반환된 내부함수를 의미하고 "그 함수가 선언될 때의 렉시컬 환경"은 내부 함수가 선언됐을 때의 스코프를 의미한다.

즉 클로저는 반환된 내부함수가 자신이 선언됐을 때의 환경(Lexical environment)인 스코프를 기억하여 자신이 선언됐을 때의 환경(스코프) 밖에서 호출되어도 그 환경(스코프)에 접근할 수 있는 함수를 말한다.

상태 유지

클로저가 가장 유용하게 쓰이는 때는 현재 상태를 기억하고 변경된 최신 상태를 유지하는 것이다.

<!DOCTYPE html>
<html>
<body>
  <button class="toggle">toggle</button>
  <div class="box" style="width: 100px; height: 100px; background: red;"></div>

  <script>
    var box = document.querySelector('.box');
    var toggleBtn = document.querySelector('.toggle');

    var toggle = (function () {
      var isShow = false;

      // ① 클로저를 반환
      return function () {
        box.style.display = isShow ? 'block' : 'none';
        // ③ 상태 변경
        isShow = !isShow;
      };
    })();

    // ② 이벤트 프로퍼티에 클로저를 할당
    toggleBtn.onclick = toggle;
  </script>
</body>
</html>

전역 변수의 사용 억제

<!DOCTYPE html>
<html>
<body>
  <p>전역 변수를 사용한 Counting</p>
  <button id="inclease">+</button>
  <p id="count">0</p>
  <script>
    var incleaseBtn = document.getElementById('inclease');
    var count = document.getElementById('count');

    // 카운트 상태를 유지하기 위한 전역 변수
    var counter = 0;

    function increase() {
      return ++counter;
    }

    incleaseBtn.onclick = function () {
      count.innerHTML = increase();
    };
  </script>
</body>
</html>

코드는 잘 동작하지만 increase 함수는 호출 직전에 전역 변수 counter의 값이 항상 0이어야 제대로 동작하며, 전역 변수이기 때문에 언제든지 누구나 접근이 가능하고 의도치 않게 변경이될 수 있다.

<html>
<body>
  <p>지역 변수를 사용한 Counting</p>
  <button id="inclease">+</button>
  <p id="count">0</p>
  <script>
    var incleaseBtn = document.getElementById('inclease');
    var count = document.getElementById('count');

    function increase() {
      // 카운트 상태를 유지하기 위한 지역 변수
      var counter = 0;
      return ++counter;
    }

    incleaseBtn.onclick = function () {
      count.innerHTML = increase();
    };
  </script>
</body>
</html>

전역 변수를 지역 변수로 변경하여 의도치 않은 상태 변경은 방지했지만 함수 호출을 할 때마다 counter를 0으로 초기화 하기 때문에 언제나 1이 표시된다. 다시 말해 변경된 이전 상태를 기억하지 못한다.

<!DOCTYPE html>
<html>
  <body>
  <p>클로저를 사용한 Counting</p>
  <button id="inclease">+</button>
  <p id="count">0</p>
  <script>
    var incleaseBtn = document.getElementById('inclease');
    var count = document.getElementById('count');

    var increase = (function () {
      // 카운트 상태를 유지하기 위한 자유 변수
      var counter = 0;
      // 클로저를 반환
      return function () {
        return ++counter;
      };
    }());

    incleaseBtn.onclick = function () {
      count.innerHTML = increase();
    };
  </script>
</body>
</html>

스크립트가 실행되면 즉시실행함수가 호출되고 변수 increase에는 함수 function () { return ++counter;}; 가 할당된다. 이 함수는 자신이 생성됐을 때의 렉시컬 환경을 기억하는 클로저다.

즉시실행함수는 호출된 이후 소멸되지만 즉시실행함수가 반환한 함수는 변수 increase에 할당되어 increase 버튼을 클릭하면 클릭 이벤트 핸들러 내부에서 호출된다.

즉시실행함수는 한번만 실행되므로 increase가 호출될 때마다 변수 counter가 다시 초기화 될 일은 없을것이다. 변수 counter는 외부에서 직접 접근할 수 없는 private 변수이므로 전역 변수를 사용했을 때와 같이 의도치 않은 변경을 걱정할 필요도 없다.

정보은닉

function Counter() {
  // 카운트를 유지하기 위한 자유 변수
  var counter = 0;

  // 클로저
  this.increase = function () {
    return ++counter;
  };

  // 클로저
  this.decrease = function () {
    return --counter;
  };
}

const counter = new Counter();

console.log(counter.increase()); // 1
console.log(counter.decrease()); // 0

생성자 함수 Counterincrease, decrease 메소드를 갖는 인스턴스를 생성한다. 이 메소드들은 모두 자신이 생성됐을 때의 렉시컬 환경인 생성자 함수 Counter의 스코프에 속한 변수 counter를 기억하는 클로저이며 렉시컬 환경을 공유한다.
생성자 함수가 생성한 객체의 메소드는 객체의 프로퍼티에만 접근할 수 있는 것이 아니며 자신이 기억하는 렉시컬 환경의 변수에도 접근할 수 있다.

비동기 프로그래밍

AJAX

자바스크립트를 이용해 비동기적으로 서버와 브라우저가 데이터를 교환할 수 있는 통신 방식

Promise와 Callback의 차이점

둘 다 자바스크립트에서 비동기처리를 위해서 사용되는 패턴

Callback 같은 경우 함수의 처리 순서를 보장하기 위해 함수를 중첩하게 사용되는 경우가 발생하기 때문에 콜백 지옥이 발생한다는 점과 에러 처리가 힘들다는 단점이 있다.

실행 컨텍스트

자바스크립트의 코드들이 실행되기 위한 환경으로, 전역 컨텍스트,함수 컨텍스트 2가지가 존재한다.
전역 컨텍스트 하나 생성 후에 함수 호출 때마다 함수 컨텍스트가 생성되고 이 때 변수 객체, 스코프 체인,this가 생성된다.

이벤트루프

자바스크립트는 싱글 스레드 언어이다.

일반적으로는 함수 실행 👉 함수가 콜스택에 순차적으로 쌓임

하지만 자바스크립트에서는 이벤트루프라는 것을 통해 동시성을 지원한다. 이벤트 루프는 콜 스택에서 실행 중인 게 있는지 확인하고 이벤트 큐에 작업이 있는지 확인해서 콜 스택이 비어있다면 이벤트 큐 내의 작업이 콜스택으로 이동되어서 실행된다.

출처

React

Virtual DOM

애플리케이션의 UI를 구성하는 HTML element를 메모리에 내에서 구현한 것.
업데이트할 요소의 목록을 만들기 위해 기존의 DOM 모델에서 변경사항을 비교함.
DOM 전체를 다시 렌더링 하지 않고 실제 DOM에 필요한 최소한만 변경하기 때문에 효율성이 높다.

JSX

HTML처럼 보이는 코드를 작성할 수 있게 해주는 자바스크립트 문법의 확장

Key

리액트에서 렌더링 시에 element와 데이터 사이의 관계를 추적하기 쉽도록 구별해주는 것

State and Props 그리고 Component

props는 부모 컴포넌트에서 자식 컴포넌트로 전달되는 데이터이고 수정할 수 없으며 표시되거나 다른 값을 계산하는 데만 사용된다.
state는 컴포넌트의 생명 주기 동안 수정될 수 있는 내부 데이터, 다시 렌더링해도 유지된다.

Component는 리액트로 만들어진 앱을 이루는 최소한의 단위

UseMemo,Usecallback

useMemo

Component는 State 값이 변경될 때마다 리렌더링된다.

만약 값이 변경이 되지 않았는데, 값을 재설정하는 것은 불필요한 작업일 것이다.

  • 사용법
const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);

useMemo를 사용하여 변경하고자 하는 props 값만 새롭게 연산할 수 있다.

useCallback

  • 사용법
const memoizedCallback = useCallback(
  () => {
    doSomething(a, b);
  },
  [a, b],
);

useMemo 는 특정 결과값을 재사용 할 때 사용하는 반면, useCallback 은 특정 함수를 새로 만들지 않고 재사용하고 싶을때 사용한다.

State를 직접 변경하지 않고 setState를 쓰는 이유

state를 직접 변경하려고 한다면 리액트는 컴포넌트를 다시 렌더링 해야하는지 알 방법이 없다.

// Wrong
this.state.comment = 'Hello';
// Correct
this.setState({comment: 'Hello'});

React 애플리케이션 스타일링

  • Inline Styling: 프로토타입을 만들 떄 유용하지만 한계가 많다.(예를 들어 pseudo selector 사용 불가능)
  • CSS In JS: 컴포넌트 안에서 스타일을 자바스크립트로 선언하여 스타일링 할 수 있게 해줌
    예를 들어 styled-component

생명 주기 메소드

순서

초기화 단계 : constructor() 👉 getDerivedStateFromProps() 👉 render() 👉 componentDidMount()

업데이트 단계: getDerivedStateFromProps() 👉 shouldComponentUpdate() 👉 render() 👉 getSnapshotBeforeUpdate() 👉 componentDidupdate()

소멸 단계: componentWillUnmount()

클래스 기반 컴포넌트들이 mount(DOM에 렌더링) 되었을 때나 unmount 될 때 등과 같이 이것들의 생명 주기 중 특정한 시점에 호출되는 특별한 메소드를 선언할 수 있다.

  • componentWillMount: 컴포넌트가 생성된 후 DOM에 렌더링되기 전에 호출됩니다.
  • componentDidMount: 처음으로 렌더링이 끝나고 컴포넌트의 DOM 엘리먼트가 사용 가능할 때 호출됩니다.
  • componentWillReceiveProps: props가 업데이트 될 때 호출됩니다.
  • shouldComponentUpdate: 새로운 props를 받았을 때 호출되며, 성능 최적화를 위해 리랜더링을 막을 수 있습니다.
  • componentWillUpdate: 새로운 props를 받았고 shouldComponentUpdate가 true를 리턴할 때 호출됩니다.
  • componentDidUpdate: 컴포넌트가 업데이트된 후에 호출됩니다.
  • omponentWillUnmount: 컴포넌트가 DOM에서 제거되기 전에 호출되어 이벤트리스너 등을 정리할 수 있게 해줍니다.

React Hooks

클래스 기반 컴포넌트의 장점을 함수형 컴포넌트로 가져오려는 리액트의 시도.

장점

  • 클래스 기반 컴포넌트, life cycle, this의 필요성이 사라진다.
  • 공통 기능을 커스텀 hook으로 만들어서 로직을 재사용하기 쉬워진다.
  • 컴포넌트 자체에서 로직을 분리할 수 있어서 읽기 쉽고 테스트하기 쉬운 코드를 작성할 수 있다.

빌드 과정

리액트는 build 시에 내가 짠 모든 리액트 코드를 하나의 자바스크립트 파일로 만들어준다.
(babel로 transpiling, webpack으로 bundling)

기타

CSR과 SSR

CSR: 서버가 브라우저에게 응답(HTML,CSS) 보냄 👉 브라우저가 JS를 다운받음 👉 브라우저는 리액트를 실행 👉 페이지가 보여지고 상호작용

SSR: 브라우저에게 HTML 응답을 렌더링 하기 위한 준비가 되었다고 보냄 👉 브라우저가 페이지 렌더링하고 페이지가 보여진 후에 브라우저는 JS를 다운 받음. 👉 브라우저가 리액트 실행 👉 페이지 상호작용 가능

CSR은 마지막 단계 전까지 화면에 보여지지 않고 로딩중이지만 SSR은 미리 페이지가 보여진다.
즉 CSR은 초기 로딩 속도가 느리긴 하지만 화면 전환에 있어서 클라이언트에서 이루어져서 빠른 전환이 가능
SSR은 초기 로딩속도가 빨라서 사용자가 느끼기엔 좋지만, 동작은 하지 않는다. 그리고 화면전환에 있어서 서버에 요청해야 하므로 서버에 부담을 줄 수 있다.

CSR : SEO에 불리하고 초기 구동 속도는 느리지만 이후 인터랙션 시에는 속도가 빠름
SSR : SEO에 유리하고 초기 구동 속도가 빠르지만 페이지 이동 시에 서버에 요청을 보내야 하기 때문에 서버에 부담이 많이 갈 수 있다는 단점이 있음.

null vs undefined

기본적으로 둘 다 값이 없을을 나타내지만
undefined는 데이터 타입이자 값을 나타내는데 정의되지 않았음을 뜻하고
null은 명시적으로 값이 비어있음을 나타낸다.

undefined는 변수를 선언만 하더라도 할당되지만, null은 변수를 선언한 후에 null로 값을 바꾼다.

localStorage vs sessionStorage, cookie

localStorage

  • 사용자 세션 데이터를 유지할 수 있다.
  • 브라우저를 닫았다가 열어도 지속된다.
  • 명시적으로 삭제될 때까지 지속된다.

sessionStorage

  • 탭이나 창을 닫을때 삭제된다.
  • 새로고침을 해도 유지된다.

cookie

  • 웹사이트에서 쿠키를 설정하면 이후 모든 웹 요청은 쿠키 정보를 포함하여 서버로 전송된다.
  • 하나의 사이트에서 저장할 수 있는 최대 쿠키는 20개이며, 최대 쿠키 크기는 4KB로 제한되어 있다.

장점: 대부분의 브라우저에 지원이 된다.
단점: api가 한번 더 호출되므로 서버에 부담이 증가된다.

RESTFUL API

HTTP Method

  • GET: 요청받은 URI의 정보를 검색하여 응답
  • HEAD: GET 방식과 동일하지만 응답에 Body가 없고 응답코드와 HEAD만 응답한다.
  • POST: 요청된 source를 생성한다. 새로 작성된 리소스인 경우 HTTP Header 항목 Location:URI 주소를 포함하여 응답
  • PUT: 요청된 source를 update한다. 내용 갱신을 위주로 Location:URI를 보내지 않아도 된다. 클라이언트 측은 요청된 URI를 그대로 사용하는 것으로 간주
  • PATCH: PUT과 유사하게 요청된 자원을 수정할 때 사용한다. PUT의 경우에는 자원 전체를 갱신하는 의미지만, PATCH는 해당 자원의 일부를 교체하는 의미로 사용
  • DELETE: 요청된 source를 삭제할 것을 요청함.
  • OPTIONS: 웹서버에서 지원되는 메소드의 종류를 확인할 경우 사용

OOP

객체의 관점에서 프로그래밍을 하는 것으로 사람의 사고방식에 좀 더 가깝다.
모든 데이터를 객체로 취급하며 처리하는 프로그래밍 방법에 따라 독립적으로 동작할 수도 있고 다른 객체의 부품으로도 사용될 수 있습니다.

특징

  • 상속: 클래스개념에서 상위 클래스(부모)로부터 하위 클래스(자식)이 유산을 물려받는 것과 같이, 부모의 메소드나 변수를 사용할 수 있는 것
  • 다형성: 같은 함수가 있다고 했을 때 그 함수가 매개변수에 따라 다른 역할을 할 수도 있다.
    - 오버로딩: 메소드의 이름은 같지만 매개변수의 개수나 타입이 다른 함수를 정의할 때 (확장)
    - 오버라이딩: 메소드의 이름은 물론 매개변수 개수나 타입도 동일해야 하며, 주로 상위 클래스의 동작을 상속받은 하위 클래스에서 변경하기 위해 사용된다. (재정의)

  • 캡슐화: 외부에서 쉽게 데이터를 접근할 수 없게 만들기도 하고, 데이터 구조와 데이터를 다루는 방법들을 한데다 묶는 것
  • 추상화: 공통적인 속성이나 기능을 묶어서 이름을 붙이는 것

프로세스와 스레드

프로그램 : 어떤 작업을 위해 실행할 수 있는 파일

프로세스 : 운영체제로부터 자원을 할당받는 작업의 단위

프로세스는 이렇게 각각 독립된 메모리 영역(Code,Data,Stack,Heap의 구조)을 할당받는다.
기본적으로 프로세스 당 최소 1개의 스레드를 가지고 있다.
한 프로세스가 다른 프로세스의 자원에 접근하려면 프로세스 간의 통신(IPC)이 이루어져야 한다.

스레드: 프로세스 내에서 실행되는 여러 흐름의 단위

스레드는 프로세스 내에서 각각 Stack만 따로 할당받고, Code,Data,Heap 영역만 공유한다.

멀티 프로세스와 멀티 스레드

멀티 프로세스 : 하나의 프로그램을 여러개의 프로세스로 구성하여 각 프로세스로 구성하여 각 프로세스가 하나의 작업을 처리하는 것

-장점: 하나의 프로세스가 잘못되어도 프로그램은 동작함
-단점: context switching 비용 발생

멀티 스레드: 프로그램을 여러개의 스레드로 구성하고 각 스레드가 작업을 처리

-장점: context switching 비용 감소, 스레드 간 자원 공유
-단점: 하나의 스레드가 오류 나면 전체 프로세스에 영향이 간다.

Context Switching

CPU에서 여러 프로세스를 돌아가면서 작업을 처리하는데 이 과정을 Context Switching이라 한다.
동작 중인 프로세스가 대기를 하면서 해당 프로세스의 상태를 보관하고 대기상태에 있다가 다시 실행 시 복구하는 비용.

CORS

CORS를 살펴보기 전에 SOP가 무엇인지부터 알아보자.

SOP(Same Origin Policy): 다른 출처의 리소스를 사용하는 것에 제한하는 보안 방식

여기서 출처란?
https://company.com:81/index.html로 예를 들면,

https:// : Protocol, company: Host, :81: Port

URL의 Protocol, Host, Port 이 3가지가 모두 같아야만 같은 출처라고 한다.

페이스북을 예로 들면 사용자는 해당 인증 토큰을 가지고 있는 상태고 중간에 해커가 자극적인 내용의 링크를 줬다고 가정하자. 이 때 사용자가 클릭을 하면 해당 주소로 이동을 하면 해커는 페이스북에게 자극적인 내용을 게시하라고 명령한다. 이 때 SOP가 위력을 발휘하는데 해커가 준 요청을 페이스북이 다른 출처로 판단하여 차단시킨다.

하지만 다른 출처의 리소스가 필요하다면 어떻게 해야할까? 바로 이 때 CORS가 필요하다.

좋은 웹페이지 즐겨찾기