0523 - 0529

0524

JS 복습 및 면접 준비 2

실행 컨텍스트

소스코드를 실행하는 데 필요한 환경을 제공하는 코드의 결과를 실제로 관리하는 영역

식별자를 등록하고 관리하는 스코프와 코드 실행 순서 관리를 구현한 내부 메커니즘으로, 모든 코드는 실행 컨텍스트를 통해 실행되고 관리된다. 식별자와 스코프는 실행 컨텍스트의 렉시컬 환경으로 관리하고, 코드 실행 순서는 실행 컨텍스트 스택으로 관리한다.

렉시컬 환경

렉시컬 환경은 식별자와 식별자에 바인딩된 값, 그리고 상위 스코프에 대한 참조를 기록하는 자료구조로 실행 컨텍스트를 구성하는 컴포넌트

실행 순서

  1. 전역 객체 생성 - 빌트인 객체와 호스트 객체 포함
  2. 전역 코드 평가
    1. 전역 실행 컨텍스트 생성
    2. 전역 렉시컬 환경 생성
      1. 전역 환경 레코드 생성 (객체 환경 레코드, 선언적 환경 레코드)
      2. this 바인딩
      3. 외부 렉시컬 환경에 대한 참조 결정
  3. 전역 코드 실행

클로저란?

중첩 함수가 자유 변수를 참조하고 있고, 중첩 함수가 외부 함수보다 더 오래 유지되는 경우, 이 중첩 함수를 클로저라고 한다.

외부 함수를 즉시 실행함수로 만들어 한 번만 실행하고 내부에서 외부 함수의 변수를 참조하는 함수를 반환하면 그것이 클로저다.

클로저 활용

클로저는 상태를 안전하게 변경하고 유지하기 위해 사용한다.
상태가 의도치 않게 변경되지 않도록 은닉하고 특정 함수에게만 상태 변경을 허용하여 상태를 안전하게 변경하고 유지하기 위해 사용한다.

클래스

클래스와 생성자 함수 차이점

  1. 클래스를 new 연산자 없이 호출하면 에러가 발생한다.
  2. 상속을 지원하는 extends와 super 키워드를 제공한다.
  3. 클래스는 호이스팅이 발생하지 않는 것처럼 동작한다.
  4. 클래스 내의 모든 코드에는 암묵적으로 strict mode가 지정되어 실행된다.
  5. 클래스의 constructor, 프로토타입 메서드, 정적 메서드는 모두 프로퍼티 어트리뷰트 enumerable 값이 false이다.

상속 클래스의 인스턴스 생성 과정

  1. 서브 클래스의 super 호출
  2. 수퍼 클래스의 인스턴스 생성과 this 바인딩
  3. 수퍼클래스의 인스턴스 초기화
  4. 서브클래스의 constructor로의 복귀와 this 바인딩
  5. 서브 클래스의 인스턴스 초기화
  6. 인스턴스 반환

ES6에서 생긴 문법

  • let / const
  • 화살표 함수, 클래스
  • 디스트럭처링 할당, 스프레드 문법, rest 파라미터
  • 프로미스
  • 모듈 import / export

화살표 함수와 일반 함수의 차이

  • 일반 함수는 constructor이지만 메서드와 화살표 함수는 non-constructor이다. 인스턴스를 생성할 수 없으므로 prototype 프로퍼티가 없고, 프로토타입도 생성하지 않는다.
  • 중복된 매개변수 이름을 선언할 수 없다.
  • 함수 자체의 this 바인딩을 갖지 않는다. 화살표 함수 내부에서 this를 참조하면 상위 스코프의 this를 그대로 참조한다.

JS 배열

자료구조에서 말하는 배열은 동일한 크기의 메모리 공간이 빈틈없이 연속적으로 나열된 자료구조
즉, 배열의 요소는 하나의 데이터 타입으로 통일되어 있으며 서로 연속적으로 인접해 있다. 이러한 배열을 밀집 배열이라고 한다.

JS의 배열은 요소를 위한 각각의 메모리 공간이 동일한 크기를 갖지 않아도 되며, 연속적으로 이어져 있지 않을 수도 있다. 배열의 요소가 연속적으로 이어져 있지 않은 배열을 희소 배열이라 한다. JS 배열은 일반적인 배열의 동작을 흉내 낸 특수한 객체다.

  • 일반적 배열은 인덱스로 요소에 빠르게 접근할 수 있다. 하지만 특정 요소 검색, 삽입, 삭제 시 효율적이지 않다.
  • JS 배열은 해시 테이블로 구현된 객체이므로 인덱스로 요소에 접근하는 경우 일반적인 배열보다 성능적인 면에서 느리다. 하지만 특정 요소 검색, 삽입, 삭제 시 빠른 성능을 기대할 수 있다.

-> 희소 배열을 문법적으로는 허용하지만, 사용하지 않는 것이 좋다. 배열에는 같은 타입의 요소를 연속적으로 위치시키는 것이 최선이다.

이터러블

이터레이션 프로토콜

순회 가능한 데이터 컬렉션을 만들기 위해 ECMAScript 사양에 정의하여 미리 약속한 규칙

  • 이터러블 프로토콜
    • Symbol.iterator 메서드를 호출하면 이터레이터를 반환하는 객체를 이터러블이라고 한다. 이터러블은 for of문, 스프레드 문법, 배열 디스트럭처링 할당의 대상이 될 수 있다.
  • 이터레이터 프로토콜
    • next 메서드를 소유하며 next 메서드를 호출하면 value와 done을 프로퍼티로 갖는 이터레이터 리절트 객체를 반환한다.

이터레이션 프로토콜은 데이터 소비자와 데이터 공급자를 연결하는 인터페이스 역할을 한다.

대표 빌트인 이터러블

  • 배열
  • 스트링
  • Map
  • Set
  • DOM 컬렉션




0525

JS 복습 및 면접 준비 3

브라우저 렌더링 과정

  1. 브라우저가 HTML을 파싱하여 DOM을 생성하고, HTML을 파싱하는 도중 link 태그를 만나면 CSS 파일을 받아와서 파싱하여 CSSOM을 생성한다.
  2. DOM과 CSSOM을 결합하여 렌더 트리를 생성한다. 렌더 트리는 렌더링을 위한 트리 구조의 자료 구조다.
  3. 이후 렌더 트리에서 각 노드의 위치와 크기를 계산하는 레이아웃 과정과 노드를 화면상의 실제 픽셀로 변환하고, 레이어를 만드는 페인트 과정을 거친다.
  4. 마지막으로 레이어를 합성하여 실제 화면에 나타내는 컴포지트 과정을 통해 사용자에게 화면을 보여준다. 이 단계는, 컴포지터 스레드에서 발생하기에 메인 스레드와 별개로 작동한다. 이것이 합성만 하는 애니메이션이 성능상 가장 부드럽다고 보는 이유이다.

HTTP 1.1 / HTTP 2.0

HTTP/1.1은 다중 요청/응답이 불가능하다는 단점이 있지만 HTTP/2는 커넥션당 여러 개의 요청과 응답, 즉 다중 요청/응답이 가능하다. 따라서 리소스 동시 전송이 가능하므로 HTTP/1.1에 비해 페이지 로드 속도가 약 50% 정도 빠르다고 알려져 있다.

리플로우 발생 최적화

리플로우는 다음과 같은 경우 발생한다.

  • JS에 의한 노드 추가 및 삭제
  • 브라우저 창의 리사이징에 의한 뷰포트 크기 변경
  • HTML 요소 레이아웃에 변경을 발생시키는 width/height 등의 스타일 변경

최적화 방법

  • JS를 통해 스타일 변화를 줄 경우, style 객체를 여러 번 호출해 스타일 변화를 주기보다는, 클래스를 사용해 한 번에 처리하기
  • 인라인 스타일 사용을 배제하여 reflow 비용 줄이기
  • 가상돔
  • 인터섹션 옵저버 API
  • 애니메이션 최적화
    • 컴포지트 레이어만 애니메이션 작업을 수행하도록 transform 속성을 사용하여 리플로우와 리페인트를 적게 발생시키고 메인스레드를 자유롭게 유지할 수 있도록 함
    • setInterval보다 requestAnimationFrame을 사용하여 프레임 생성 초기 단계에 맞춰 애니메이션 코드를 실행시킴으로써 애니메이션을 더 부드럽게 동작시킨다. setInterval이나 setTimeout은 프레임을 신경쓰지 않고 동작한다.

requestAnimationFrame vs setTimeout

  • rAF와 setTimeout의 가장 큰 차이점은 1프레임 당 호출이 보장되느냐 않느냐에 차이가 있다.
  • rAF는 렌더링 파이프라인과 붙어 동작하기 때문에 1프레임 당 1번의 호출이 보장되는 반면, setTimeout은 지연되어 버벅거리는 애니메이션을 제공할 수 있다.

이벤트 전파

이벤트가 발생하면 이벤트 객체가 생성되고, 이벤트를 발생시킨 DOM 요소인 이벤트 타깃을 중심으로 DOM 트리를 통해 전파된다.
이 때, 이벤트가 상위 요소에서 하위 요소 방향으로 전파되는 캡처링이 발생하고, 이벤트가 이벤트 타깃에 도달하고, 이후 이벤트가 하위 요소에서 상위 요소 방향으로 전파되는 버블링이 발생한다.

이벤트 위임

이벤트는 이벤트를 발생시킨 타깃은 물론 상위 DOM 요소에서도 캐치할 수 있는데, 이를 활용하면 예를 들어 하위 요소가 100개인 아이템에 모두 이벤트를 등록하는 것이 아닌 상위 요소에만 이벤트 핸들러를 등록하여 이벤트 최적화를 할 수 있습니다.

이벤트 전파를 방지하는 방법

event.stopPropagation() 메소드를 사용하거나, event.cancelBubble = true;를 사용한다.
stopImmediatePropagation()는 현재 이벤트가 전파되지 않은 뿐더러 같은 레벨에 걸린 다른 이벤트들도 작동하지 않는다.

Ajax

자바스크립트를 사용해서 브라우저가 서버에게 비동기 방식으로 데이터를 요청하고, 서버가 응답한 데이터를 수신하여 웹페이지를 동적으로 갱신하는 프로그래밍 방식

장점

  • 필요한 데이터만 서버로부터 전송받기 때문에 불필요한 데이터 통신이 발생하지 않는다.
  • 화면이 순간적으로 깜박이는 현상이 발생하지 않는다.
  • 클라이언트와 서버와의 통신이 비동기 방식으로 동작하기 때문에 서버에게 요청을 보낸 이후 블로킹이 발생하지 않는다.

REST API

REST

HTTP를 기반으로 클라이언트가 서버의 리소스에 접근하는 방식을 규정한 아키텍처. REST API는 REST를 기반으로 서비스 API를 구현한 것

REST API 구성

자원, 행위, 표현의 3가지 요소로 구성

자원은 URI, 행위는 HTTP 요청 메서드, 표현은 페이로드로 표현한다.

설계 원칙

  1. URI는 리소스를 표현해야 한다.
  2. 리소스에 대한 행위는 HTTP 요청 메서드로 표현한다.

PUT vs PATCH

PUT은 리소스의 전체 교체, PATCH는 일부 수정

자바스크립트 비동기 처리

자바스크립트 엔진은 한 번에 하나의 태스크만 실행할 수 있는 싱글 스레드 방식으로 동작한다.
싱글 스레드 방식은 한 번에 하나의 태스크만 실행할 수 있기 때문에 처리에 시간이 걸리는 태스크를 실행하는 경우 블로킹이 발생한다.

동기 처리 - 실행 중인 태스크가 종료할 때까지 다음에 실행될 태스크가 대기하는 방식. 실행 순서가 보장된다는 장점이 있지만, 앞선 태스크가 종료할 때까지 이후 태스크들이 블로킹되는 단점이 존재

비동기 처리 - 현재 실행 중인 태스크가 종료되지 않은 상태라 해도 다음 태스크를 곧바로 실행하는 방식.
블로킹이 발생하지 않는다는 장점이 있지만, 태스크의 실행 순서가 보장되지 않는 단점이 있다.

setTimeout, setInterval, HTTP 요청, 이벤트 핸들러는 비동기 처리 방식으로 동작함

이벤트 루프란?

브라우저 메인 스레드 동작 타이밍을 관리하는 기능이다.

메인 스레드란 JS 코드 실행이나 브라우저 렌더링을 맡는 등 브라우저의 주된 동작이 수행되는 곳이다. 메인 스레드는 싱글 스레드로 동작하는데, 하나의 작업을 하고 있다면 다른 작업을 시연시킨다. 즉, 메인 스레드에서 블로킹이 발생하면 브라우저는 먹통이 되는 현상이 발생한다. 사용자 이벤트도 메인 스레드에서 동작하기 때문에 키보드 입력이나 마우스 클릭도 동작하지 않는다.

메인 스레드는 이벤트 루프에 의해 관리된다. 메인 스레드와 같이 싱글 스레드에서 하나의 작업이 오랫동안 실행되어서도 안 되고, 여러 작업 중 어떤 작업을 우선으로 동작시킬 것인가를 결정하는 것도 매우 중요하다. 또한 작업 간 전환 속도를 빠르게 하여 한 번에 하나의 작업씩 수행하지만 마치 동시에 수행하는 것처럼 동작해야 한다. 이러한 컨트롤을 이벤트 루프가 해주기에 이를 정확히 이해하는 것이 중요하다.

이벤트 루프의 우선순위

  1. 초기 콜스택에 쌓여있는 작업을 모두 처리한다.
  2. Promise 후속 처리 메서드의 콜백 함수가 마이크로태스크 큐에 등록되어 있다면 모든 콜백이 처리될 때까지 계속 실행한다.
  3. 화면 갱신이 필요하다면 렌더링 파이프라인으로 이동
    • 사용자가 스크롤 이동을 했거나, 어떤 요소를 클릭했거나 등등 화면을 갱신할 필요가 있을 때
    • requestAnimationFrame을 사용하면 이벤트 루프가 모니터 주사율에 맞춰 렌더링 파이프라인으로 들어가려고 노력한다.
  4. 비동기 함수의 콜백 함수 또는 이벤트 핸들러가 일시적으로 보관되는 영역인 태스크 큐에 있는 콜백을 하나씩 실행

프로미스

프로미스는 전통적인 콜백 패턴이 가진 단점을 보완하며 비동기 처리 시점을 명확하게 표현할 수 있다는 장점을 가진 비동기 처리 상태와 결과를 관리하는 객체입니다.

콜백 패턴의 단점

  • 콜백 헬
  • 에러는 호출자 방향으로 전파되어 에러 처리가 곤란함

프로미스는 비동기 처리 상태와 처리 결과를 관리하는 객체로, 비동기 처리를 수행할 콜백 함수를 인수로 전달받는다. 전달받은 콜백 함수 내부에서 비동기 처리를 수행하고, 비동기 처리 결과에 따라 resolve나 reject를 호출하여 프로미스의 상태를 변경시킨다.

프로미스의 상태가 변경되면 후속 처리 메서드가 실행되어 비동기 처리 시점을 명확하게 표현하며 콜백 패턴의 문제점도 해결할 수 있다.

async/await

가독성 좋게 비동기 처리를 동기 처리처럼 동작하도록 구현할 수 있는 방식
이는 프로미스를 기반으로 동작하는데, async와 await를 사용하면 마치 동기 처리처럼 프로미스를 사용할 수 있다.

await 키워드는 반드시 async 함수 내부에서 사용해야 한다.
await 키워드는 프로미스가 settled 상태가 되면 프로미스가 resolve한 처리 결과를 반환한다. await 키워드는 반드시 프로미스 앞에서 사용해야 한다.




0529

React 복습 및 면접 준비 4

리액트 불변성

리액트 컴포넌트에서 상태를 업데이트할 때 불변성을 지키는 것은 매우 중요하다.
업데이트가 필요한 곳에서는 아예 새로운 배열 혹은 객체를 만들기 때문에, 기존의 값을 수정하지 않으면서 새로운 값을 만들어 내는 것을 불변성을 지킨다고 한다.
불변성이 지켜지지 않으면 객체 내부의 값이 새로워져도 바뀐 것을 감지하지 못한다.

  • 일반적으로 객체가 참조를 통해 공유되어 있다면 상태가 어디든지 변경 될 가능성도 커져 문제가 된다.
  • 변경이 일어난 객체의 프로퍼티만 비교함으로써 React에서 최적화가 가능하다.
    • React는 실제 DOM을 제어하는 것이 아니라 중간에 가상 DOM을 두고 관리하기 때문에 빠르게 UI 변화에 대한 관리가 가능하다.
    • 일부 side-effect로 인해 원하지 않은 시점에 컴포넌트가 다시 렌더링되는 경우 shouldComponentUpdate로 최적화가 가능하다.
  • 상태값이 변경 불가성이라면 간단한 비교 연산자를 통해 변화를 감지할 수 있지만, 깊은 깊이에 있는 내부 프로퍼티는 비교가 힘들 수도 있다.
    • 일일이 비교하기 힘들기 때문에 비용이 좀 들더라도 객체를 복사하여 새로운 객체를 생성한 후 변경하는 작업을 한다.
    • 이렇게 하면 메모리 주소가 바뀔때마다 데이터가 변경되었다고 판단할 수 있기 때문이다.

리액트에서 리렌더링 되는 조건

부모 컴포넌트 리렌더링

부모 컴포넌트가 렌더링되면 자식 컴포넌트들 모두 리렌더링된다. 그래서 코드양이 많아지면 최적화가 필수적이다.

state 변경

리액트는 상태를 사용해서 동적인 데이터를 저장한다.
이 상태의 변경이 감지되면 리렌더링을 해주게 된다.

새로운 props

전달받은 props가 변경됐다면 리렌더링한다.

forceUpdate가 실행될 때

props나 state가 아닌 다른 값이 변경되었을 때 리렌더링을 하고 싶다면 사용할 수 있는 메서드이다.

클래스 컴포넌트 vs 함수 컴포넌트

클래스 컴포넌트는 state 기능 및 라이프 사이클 기능을 사용할 수 있다.
또한 render 함수가 꼭 있어야 하고, 그 안에서 JSX를 반환해야 한다.

함수 컴포넌트는 클래스 컴포넌트보다 선언하기가 편하고, 메모리 자원도 덜 사용한다.
그리고 빌드한 후 배포할 때도 결과물의 크기가 더 작다.
함수 컴포넌트의 단점은 state와 라이프사이클 API의 사용이 불가능하다는 점인데, Hooks를 도입하며 이러한 문제점을 해결했다.

CSS in JS 장단점

장점

  • css 확장자를 가진 스타일 파일을 따로 만들지 않아도 되고, JS 파일 하나에 스타일까지 작성하여 응집도를 높일 수 있다.
  • 컴포넌트 props 값으로 전달해 주는 값을 쉽게 스타일에 적용할 수 있다.

단점

  • 자바스크립트 해석 과정이 있기 때문에 CSS-in-CSS에 비해 속도가 느리다.

개발 효율성에 중접을 둔 컴포넌트 위주의 프로젝트라면 CSS-in-JS를, 사용자 편의에 방점을 둔 인터렉티브한 웹 프로젝트라면 CSS-in-CSS 방식이 좋을 것 같다.

react 성능 개선 방법

  • useState 지연된 초기화
  • useCallback, useMemo, React.memo
  • shouldComponentUpdate
  • input에 타이핑 시마다 렌더링 되는 것을 최적화하기 위해 디바운스 사용
  • 자식 컴포넌트의 props로 객체를 넘겨줄 경우, 함수나 객체 리터럴로 객체를 생성해서 하위 컴포넌트로 넘겨주는 방식이 아닌, state를 그대로 하위 컴포넌트에 넘겨주어 필요한 데이터 가공을 하위 컴포넌트에서 해주는 것이 좋다.
  • 가상화된 List - 만약 거대한 list data를 렌더링한다면 브라우저의 viewport에 보이는 부분만 렌더링하고 나머지는 스크롤 할 때 보여지도록 한다.

setState가 비동기 동작을 취했을 때 얻을 수 있는 이점

setState() 메서드는 이벤트 핸들러 내에서 비동기 방식으로 처리된다.
부모와 자식이 모두 click 이벤트에서 setState를 호출한다면 자식은 두 번 렌더링되지 않는다. 대신 브라우저 이벤트가 끝날 시점에 state를 일괄적으로 업데이트한다. 불필요한 렌더링을 최대한 피하려 React에서 처리하기 때문이다.
즉, 불필요한 렌더링을 하지 않고 최적화 할 수 있다.

Virtual DOM

가상 돔을 사용하면 실제 돔에 접근하여 조작하는 대신, 이를 추상화한 JS 객체를 구성하여 사용한다.

  1. 데이터를 업데이트하면 전체 UI를 가상 돔에 리렌더링한다.
  2. 이전 가상 돔에 있던 내용과 현재 내용을 비교한다.
  3. 바뀐 부분만 실제 돔에 적용한다.

지속적으로 데이터가 변화하는 대규모 애플리케이션 구축에 진가를 발휘한다.

태그:

카테고리:

업데이트: