프론트엔드 인터뷰 예상 질문 (1) - JS

서동경·2023년 11월 6일
0

인터뷰 준비

목록 보기
1/3
post-thumbnail

💬 자바스크립트

  • HTML,CSS와 함께 주로 웹 브라우저에서 동작하는 프로그래밍 언어이다. HTML은 '브라우저의 구조와 정보'를, CSS는 '브라우저 스타일링'을, 자바스크립트는 '브라우저의 움직임'을 담당한다.

◽ 자바스크립트가 다른 언어에 비해 가지는 장단점

  • 비교적 간단한 문법을 가지고, 클라이언트 개발은 물론 서버 개발에도 사용할 수 있어 범용성이 뛰어나다. 다만 유연한 언어이기 때문에 코드에 문제가 있어도 실행되는 경우가 있어 디버깅이 어려워질 수 있다. (Strict Mode, TypeScript로 보완 가능)

◽ 자바스크립트 엔진의 구성

  • 자바스크립트 엔진은 Call Stack과 Memory Heap으로 구성되어 있다. Call Stack은 원시 타입의 데이터와 실행 컨텍스트를 저장하는 곳이고, Memory Heap은 참조 타입의 데이터을 저장하는 곳이다.

💬 ECMAScript

  • 자바스크립트 표준 사양을 정의하는 규약이다.

◽ ES6

  • 2015넨에 발표된 자바스크립트 표준 사양을 정의하는 규약의 여섯 번째 버전이다. 이 버전은 자바스크립트에 여러 중요 기술들을 도입한 중요한 버전이다.

    ES6 주요 변경점

    • let, const
    • 화살표 함수
    • Promise
    • 클래스
    • import, export를 사용하는 모듈 시스템
    • 각종 리터럴 (문자열 리터럴, 객체 리터럴, 템플릿 리터럴)
    • 전개 연산자, 구조 분해 할당
    • Symbol, Map, Set
    • ...

    require와 import

    모듈을 불러올 때 사용하는 키워드이다.

    • require: (NodeJS에서 사용되고 있는)CommonJS 키워드. 동기적으로 모듈을 불러옴. 내보낼 때는 module.exports 혹은 exports 사용.
    • import: ES6에 도입된 키워드. 비동기적으로 모듈을 불러옴. 내보낼 때는 export default 혹은 export 사용.

💬 변수, 식별자

  • 변수는 값을 저장하는 메모리 공간을 의미하고 식별자는 그 메모리 공간의 이름을 의미한다.

💬 선언, 할당, 초기화

  • 선언은 메모리 공간을 확보하고 해당 메모리 공간의 주소와 식별자를 연결하는 작업이고, 할당은 메모리 공간에 데이터을 저장하는 작업이다. 선언과 할당이 동시에 일어나는 작업을 초기화하고 한다.

💬 ⭐ 호이스팅

  • 호이스팅은 렉시컬 스코프의 선언이 스코프 최상단으로 끌어올려지는 현상이다. 자바스크립트 엔진실행 컨텍스트를 생성 단계에서 실행에 필요한 변수와 함수들을 메모리 공간에 등록하는데, 이 작업으로 인해 선언 위치에 상관없이 해당 변수와 함수에 접근이 가능해진다.

💬 ⭐ var, let, const

  • var은 ES6 이전부터 변수를 선언하기 위해 사용되던 키워드이고 let과 const는 ES6에서 var의 단점을 보완하고자 등장한 키워드이다. / var는 자바스크립트 엔진에 의해 undefined로 자동으로 초기화되므로 호이스팅 시 undefined가 출력되지만, let과 const는 초기화 작업을 거치지 않아 호이스팅 시 ReferenceError(undeclared)가 출력된다. 또한 var은 함수만을 지역 스코프로 인정하는 함수 레벨 스코프를 가지지만 let과 const는 함수를 포함한 모든 블록을 지역 스코프로 인정하는 블록 레벨 스코프를 가진다. 그리고 var은 재선언이 가능하지만 let과 const는 재선언이 불가능하다. / 이러한 특징을 보았을 때, 코드를 명시적으로 작성하기 위해서 var보다 let과 const를 사용하는 것이 유리하다.
  • let과 const의 차이는, let의 경우 재할당이 가능(초기화하지 않아도 OK)하고, const는 재할당이 불가능(초기화가 필수)하다

💬 ⭐ 데이터 타입

  • 자바스크립트의 데이터 타입으로는 원시 타입과 참조 타입(객체 타입)이 있다. / 원시 타입은 변경 불가능한 값으로 immutable 특성을 가지며 number, string, boolean, null, undefined, symbol 등이 있다. 참조 타입은 변경 가능한 값으로 mutable 특성을 가지며 원시 타입을 제외한 모든 것(object, array, function 등)이 참조 타입이다.

    immutable/mutable의 개념 vs 재할당의 개념

    immutable/mutable은 메모리 공간에 있는 데이터의 안에 들어있는 값이 변경될 수 있는지에 대한 개념이고, 재할당은 메모리 공간에 있는 데이터가 변경되는 개념이다.

    null, undefined, undeclared, NaN

    null은 '빈 값', undefined는 '정의되지 않음', undeclared는 '선언되지 않음', NaN은 '표현 불가능한 수치 결과'를 의미한다.

    symbol(ES6)

    객체의 키로 사용되는 자료형이다. 같은 문자열로 생성하더라도 각각의 고유성을 보장하여 이름 충돌을 방지할 수 있고, 문자열을 은폐할 수 있어 키 값의 외부 노출을 방지할 수 있다. (for key in Object, Object.keys, Object.entries에서도 출력되지 않음!)

💬 ⭐ 타입 결정 방식

  • 타입 결정 방식에는 정적 타이핑과 동적 타이핑이 있다. / 정적 타이핑이란 변수 선언 시 타입을 결정하는 방식이다. (해당 타입의 값만 할당할 수 있음) 한편 동적 타이핑이란 변수 선언이 아닌 변수에 값이 할당될 때 타입을 결정하는 방식이다. (값과 타입이 변할 수 있음)
  • 자바스크립트는 동적 타이핑 언어이지만 타입스크립트를 사용하여 정적 타이핑 방식을 사용할 수 있다.

💬 타입 변환 방식

  • 타입 변환 방식에는 명시적 타입 변환과 암묵적 타입 변환이 있다. / 명시적 타입 변환은 Number(), String() 등의 메서드나 NOT 연산자 등을 통해 개발자가 직접 타입을 변환하는 방식이다. 한편 암묵적 타입 변환은 자바스크립트 엔진에 의해 자동으로 타입이 변환되는 방식이다.

    암묵적 타입 변환의 예시

    문자열과 숫자 타입의 값에 더하기 연산자를 사용했을 때는 문자열로, 빼기/곱하기/나누기 연산자를 사용했을 때 숫자로 변환된다.

💬 truthy와 falsy

  • 조건문이나 논리 연산에서 true로 평가되는 값은 truthy, false로 평가되는 값은 falsy라고 한다. / falsy에는 false, undefined, null, 0, -0, NaN, 빈 문자열('') 등이 속하고 나머지는 truthy에 속한다.

💬 배열

  • 여러 값을 index를 통해 저장하는 데이터이다.
  • 배열의 종류에는 크기와 타입이 동일한 값이 연속적으로 나열되는 밀집 배열크기와 타입이 달라도 되고 연속적이지 않아도 되는 희소 배열이 있는데, 자바스크립트의 배열은 희소 배열이다.

◽ 배열 메소드

  • 원본 배열을 변경하는 메서드로 push, pop, unshift, shift, splice, sort, reverse 등이 있고, 원본 배열을 변경하지 않는 메서드로 '고차 함수 배열 메서드', slice, concat 등이 있다. 고차 함수인 배열 매서드는 forEach, map, every, some, find, reduce처럼 콜백 함수를 갖는 메서드이다.

    split과 join

    split은 문자열을 특정 구분자를 기준으로 나누어 배열로 반환하고, join은 배열의 요소들을 특정 구분자로 연결하여 문자열로 반환한다.

◽ forEach와 map

  • 두 메서드 모두 배열을 순회하며 각 요소에 대해 콜백 함수를 실행하는 메서드이다. forEach는 undefined를 반환하지만, map은 콜백 함수의 반환값으로 이루어진 배열을 반환한다.

    for in vs for of

    for in 루프 - enumerable한, 열거 가능한 데이터를 순회하는 반복문
    for of 루프 - iterable한, 반복 가능한 데이터를 순회하는 반복문

💬 객체

  • 키와 값으로 구성된 프로퍼티를 저장하는 데이터이다.
  • 자바스크립트에서 원시 타입을 제외한 모든 것이 객체이다.

◽ 객체 생성 방법

  • 객체 리터럴 (ES6)
  • 생성자 함수
  • 클래스 (ES6)
  • Object 빌트인 생성자 함수
  • Object.create

◽ 전역 객체

  • 자바스크립트 엔진에 의해 생성되는 어디에서든지 접근 가능한 특수 객체이다.
  • 전역 객체는 표준 빌트인 객체, 호스트 객체, 사용자 정의 객체프로퍼티로 가진다.
  • 브라우저 환경에서는 window가, NodeJS 환경에서는 global이 전역 객체이다.

    표준 빌트인 객체와 호스트 객체

    표준 빌트인 객체(Object, Map, Set, Promise, Math, Date, RegExp...)는 ECMAScript 사양으로 정의된 객체이고, 호스트 객체(window, global, document...)는 자바스크립트 실행 환경에서 제공하는 객체이다.

💬 래퍼 객체

  • 문자열의 길이에 접근할 때처럼 원시 타입의 데이터를 객체 타입처럼 사용해야 하는 경우, 자바스크립트 엔진은 원시 타입의 데이터를 일시적으로 연관된 객체로 변경하여 사용하는데, 이 때 사용하는 객체를 래퍼 객체라고 한다.

💬 Map, Set

  • Map과 Set은 모두 iterable한 특성을 가지는 Keyed Collection이다. / Map은 문자열, 심볼뿐만 아니라 다양한 타입의 key를 허용하는 Collection이고, Set은 value만을 저장하며 중복값을 허용하지 않는 Collection이다. (key를 따로 저장하지는 않지만 value값이 곧 key값이 된다.)

💬 ⭐ Call by ~

  • 자바스크립트에서, 파라미터에 원시 타입을 할당하여 함수를 호출한다면 데이터의 원본값(값이 '담긴' 주소값)을 복사하여 전달하고, 이를 'call by value(값에 의한 호출)'라고 한다. 원시 타입은 immutable한 특성을 가지기 때문에, 함수가 전달받은 데이터를 변경한다고 하더라도 기존 데이터는 변경되지 않는다. / 반면 참조 타입을 할당하여 함수를 호출한다면 데이터의 주소값(값이 '담긴 곳을 가르키는' 주소값)을 복사하여 전달하는데, 엄밀히 말하자면 이는 데이터의 주소값을 그대로 전달하는 'call by reference(참조에 의한 호출)'와는 다르기 때문에 자바스크립트는 call by value의 개념만 존재한다고 할 수 있다. (참조 타입의 경우, call by sharing이라는 용어를 사용해서 call by reference와 구분한다.) 한편 참조 타입은 mutable한 특성을 가지기 때문에, 함수가 전달받은 데이터를 변경(ex: 프로퍼티에 접근하여 값을 변경하는 방식)한다면 기존 데이터가 변경된다. 다만 새로운 참조 데이터를 생성하여 값을 할당하는 방식으로 전달받은 데이터를 변경한다면 새로운 주소값을 사용하기 때문에 기존 데이터가 유지된다.

💬 함수

  • 특정 작업을 수행하는 재사용 가능한 코드 블록이다.
    • 프로퍼티로 저장된 함수 → '메서드'
    • 함수의 매개변수로 들어오는 함수 → '콜백 함수'
      • 주로 비동기 작업이나 이벤트를 처리하기 위한 목적으로 사용한다.
    • 콜백 함수를 갖는 함수 → '고차 함수'

◽ 함수 정의 방법

  • 함수 선언식
  • 함수 표현식
  • 화살표 함수(ES6)
  • Function 빌트인 생성자 함수

◽ 화살표 함수(ES6)

  • 함수 본연의 입출력 기능을 직관적으로 보여주면서 간단하게 정의할 수 있는 함수 정의 방법이다.
  • 화살표 함수의 this는 언제나 함수가 선언될 때의 렉시컬 환경, 즉 상위 스코프에서의 this 바인딩 규칙을 따른다. 이를 렉시컬 this라고 한다.
  • (파라미터가 하나라면 소괄호를, 반환문 한 줄만 존재한다면 중괄호를 생략할 수 있다.)

◽ ⭐ 함수 호이스팅 (aka. 함수 선언식과 함수 표현식의 차이)

  • 함수 선언식은 function 키워드를 맨 앞에 바로 선언하는 방식으로, 이렇게 선언된 함수는 실행 컨택스트 생성 단계에서 메모리 공간에 등록된다. 이에 따라 함수 호이스팅이 일어나기 때문에, 선언부 이전 호출 시 함수가 실행된다. / 반면 함수 표현식은 함수를 생성하여 변수에 할당하며 선언하는 방식으로, 함수 호이스팅이 일어나지 않고 변수 호이스팅이 일어나기 때문에, 함수 선언부 이전 호출 시 TypeError 혹은 undefined가 출력된다.
  • (이러한 특징은 클래스 선언식, 클래스 표현식에서도 마찬가지이다.)

◽ 즉시 실행 함수

  • 선언과 동시에 단 한번만 실행되는 함수이다.
  • 전역 변수 생성을 줄이고자 할 때, 변수를 은닉하고자 할 때 사용할 수 있다.
  • (함수 선언식이 괄호로 감싸진 형태를 가진다.)

💬 ⭐ 스코프

  • 식별자가 유효한 범위이다.

◽ 스코프의 종류

  • 스코프는 전역 스코프와 지역 스코프로 구분할 수 있다. / 전역 스코프는 가장 바깥 영역의 스코프어떤 환경에서든지 접근할 수 있고, 지역 스코프는 함수나 블록의 스코프로, 해당 함수나 블록 내부에서만 접근할 수 있다.

    전역 변수 선언의 문제점

    전역 변수를 선언한다는 것은 전역 스코프를 사용한다는 것인데, 전역 스코프를 사용하면 어디서든 참조될 수 있고, 생명 주기가 길기 때문에 오랫동안 변경될 수 있다. 또한 스코프 체인의 끝에 있으므로 속도가 느리다.

◽ 스코프 결정 방식

  • 스코프 결정 방식은 정적 스코핑과 동적 스코핑으로 나뉜다. / 정적(렉시컬) 스코핑은 함수 선언 시 스코프를 결정하는 방식이고, 동적 스코핑은 함수 호출 시 스코프를 결정하는 방식이다. / 자바스크립트는 정적 스코핑을 통해 스코프를 결정한다.

💬 ⭐ 클로저

  • 클로저는 함수와 그 함수가 선언될 때의 렉시컬 환경의 조합으로, 자신이 선언될 때의 렉시컬 환경을 기억하는 함수를 클로저 함수라고 한다. / 클로저는 함수 내부에서 또 다른 함수를 정의하고 해당 함수를 리턴하는 형태를 가진다. 이렇게 되면, 외부 함수가 종료되더라도 내부 함수는 렉시컬 환경에 계속 접근할 수 있다. / 이를 통해 변수의 캡슐화상태 유지를 구현할 수 있다.

  • (클로저는 메모리를 계속 차지한다는 단점이 있다.)

    클로저를 사용한 캡슐화 예시

    function getPassword() {
      const password = "mySuperSecretPassword";
    
      function getPasswordClosure() {
        return password;
      }
    
      return getPasswordClosure;
    }
    
    console.log(getPassword()); // [Function: getPasswordClosure]
    
    const getPasswordFunction = getPassword();
    console.log(password); // ReferenceError: password is not defined
    console.log(getPasswordFunction()); // "mySuperSecretPassword"

    클로저를 사용한 캡슐화 예시

    function createCounter() {
        let count = 0; // 카운트 상태 유지
    
        return function () {
            count++; // 카운트 증가
            return count; // 현재 카운트 값 반환
        };
    }
    
    const counter1 = createCounter();
    console.log(counter1()); // 1
    console.log(counter1()); // 2
    console.log(counter1()); // 3
    
    const counter2 = createCounter();
    console.log(counter2()); // 1
    console.log(counter2()); // 2
    console.log(counter2()); // 3

💬 ⭐ 생성자 함수

  • new 키워드로 호출하는 함수이다. / String, Number, Boolean 등의 빌트인 생성자 함수를 사용할 수도 있고 사용자 정의 함수를 사용할 수도 있다.

◽ 객체 리터럴 & 생성자 함수 & 클래스

  • 객체 리터럴은 하나의 객체를 간편하게 생성할 수 있다. / 생성자 함수는 동일한 구조의 여러 객체를 생성하는데 유리하다. / ES6의 클래스를 사용하면 동일한 구조의 여러 객체를 생성할 수 있는 것은 물론 extends를 사용하여 쉽게 상속을 구현할 수 있다. (가독성 측면에서도 유리)

◽ 생성자 함수의 인스턴스 생성 과정

  1. 생성자 함수를 선언
  2. new 키워드를 통해 생성자 함수를 호출하여 this를 바인딩하고 인스턴스를 초기화
  3. 생성자 함수가 실행되며 인스턴스 반환

    생성 예시

    function Workout(name, sets, reps) {
      this.name = name;
      this.sets = sets;
      this.reps = reps;
      this.getInfo = function () {
        console.log(
          `오늘의 운동은 ${this.name}이고 ${this.sets}세트, 각 ${this.reps}회가 목표입니다!`
        );
      };
    }
    
    let squat = new Workout("Squat", 3, 5);
    let legPress = new Workout("Leg Press", 5, 10);
    
    squat.getInfo(); // 오늘의 운동은 Squat이고 3세트, 각 5회가 목표입니다!
    console.log(squat.constuctor) // Workout
    legPress.getInfo(); // 오늘의 운동은 Leg Press이고 5세트, 각 10회가 목표입니다!
    console.log(legPress.constuctor) // Workout

💬 ⭐ this

  • 자신이 속한 객체 혹은 생성할 인스턴스를 가리키는 자기 참조 변수이다.
  • this와 this가 가르킬 객체를 연결하는 작업을 this 바인딩이라고 한다.

◽ 상황에 따른 this 바인딩

  • 암시적 바인딩 ('일종의 바인딩 규칙')
    - 전역이나 일반 함수에서의 this는 전역 객체를 가르킨다.
    - 메서드에서의 this는 자신이 속한 객체를 가르킨다.
    - 생성자 함수에서의 this는 생성할 인스턴스를 가르킨다.
    - (이벤트 리스너의 콜백 함수에서 사용되는 경우는 이벤트가 발생한 요소를 가르킨다.)

  • 명시적 바인딩 ('사용자가 직접 바인딩')
    - call, apply, bind 라는 메서드를 통해 this가 메서드의 첫 번째 인자로 들어온 객체를 가르키도록 할 수 있다. / (call은 매개변수를 함께 전달하여 사용하고 apply는 매개변수를 배열 형태로 함께 전달하여 사용한다. bind는 해당 객체로 바인딩된 함수를 리턴하므로, 반환된 함수에 매개변수를 전달하여 사용한다.)

  • 기타
    - 화살표 함수의 this는 언제나 함수가 선언될 때의 환경, 즉 상위 스코프에서의 this 바인딩 규칙을 따른다. 이를 렉시컬 this라고 한다.

    명시적 바인딩 메서드 사용법

    const workout = {
     name: "Squat",
     todayWorkout: function () {
       return `오늘의 운동: ${this.name} / `;
     },
    };
    
    function getInfo(kg, sets, reps) {
     return this.todayWorkout() + `${kg}KG ${sets}SETS ${reps}REPS`;
    }
    
    const strengthTraining = getInfo.call(workout, 140, 3, 5);
    console.log(strengthTraining);
    // 첫 번째 매개변수에는 "this가 가리킬 객체"
    // 두 번째 매개변수부터 "함수의 매개변수"를 입력 !
    // 오늘의 운동: Squat / 140KG 3SETS 5REPS
    
    const hypertrophyTraining = getInfo.apply(workout, [100, 3, 12]);
    console.log(hypertrophyTraining);
    // 첫 번째 매개변수에는 "this가 가리킬 객체"
    // 두 번째 매개변수에는 "함수의 매개변수를 배열로" 입력 !
    // 오늘의 운동: Squat / 100KG 3SETS 12REPS
    
    const endurance = getInfo.bind(workout);
    const enduranceTraining = endurance(80, 3, 20);
    console.log(enduranceTraining);
    // 매개변수로 "this가 가리킬 객체"를 입력하고
    // 함수의 매개변수는 리턴받은 함수를 호출할 때 입력 !
    // 오늘의 운동: Squat / 80KG 3SETS 20REPS
    

    바인딩 우선순위

    생성할 인스턴스로 바인딩(in 생성자 함수) > 명시적 바인딩 > 자신이 속한 객체에 바인딩(in 메서드)

    💬 ⭐ 함수형 프로그래밍

  • 함수를 일급 객체 취급하고 외부 상태에 의존하지 않는 순수 함수를 사용하여 사이드 이펙트를 줄이고 참조 투명성을 지키는 프로그래밍 페러다임이다.

◽ '함수를 일급 객체 취급한다'의 의미

  1. 이름없는 리터럴로 생성 가능
  2. 변수나 자료 구조에 저장 가능
  3. 다른 함수의 매개변수나 반환값으로 사용 가능

    함수형 프로그래밍의 등장 배경

    1. 전역 변수 선언(전역 스코프 사용)의 '어디에서든지 참조될 수 있고, 오랫동안 변경될 수 있으며, 느리다는 단점'이 부각됨.
    2. 스코프의 범위를 클래스로 좁히는 객체 지향 프로그래밍 등장. (공통으로 사용하는 변수를 자기들끼리만 사용하는 특징을 가짐.)
    3. 초기 자바스크립트에서는 클래스의 개념이 없었기 때문에 OOP 구현이 제한적. 따라서 스코프의 범위를 함수로 좁히는 함수형 프로그래밍 등장. (공통으로 사용하는 변수를 아예 사용하지 않는 특징을 가짐.)

💬 ⭐ 객체 지향 프로그래밍

  • 프로그래밍에 필요한 개념을 객체라는 개념으로 추상화하고 객체들의 상호작용을 통해 프로그래밍하는 프로그래밍 패러다임이다.

◽ 객체 지향 프로그래밍의 특징

  1. 추상화: 복잡한 개념을 공통된 핵심 기능으로 묶어 단순화하는 것
  2. 상속: 기존 객체의 기능을 다른 객체가 사용할 수 있도록 전달하는 것
  3. 다형성: 같은 이름의 메서드가 다양한 형태로 동작할 수 있도록 하는 것
  4. 캡슐화: 데이터를 외부로부터 보호하는 것

◽ 자바스크립트는 객체 지향 프로그래밍 언어인가?

  • 전통적인 객체 지향 프로그래밍 언어라기보단 멀티 패러다임 프로그래밍 언어이다. ES6 이후 클래스가 도입되어 클래스를 이용한 객체 지향 프로그래밍이 가능해졌지만 자바스크립트는 여전히 클래스 기반이 아닌 프로토타입 기반 언어이고, 여러가지 프로그래밍 패러다임을 지향하기 때문이다.

◽ 자바스크립트에서 객체 지향 프로그래밍을 어떻게 구현?

  • (ES5의) 생성자 함수와 프로토타입 메서드를 사용하거나 (ES6의) 클래스를 통해 구현할 수 있다.

💬 ⭐ 프로토타입

  • 자바스크립트는 프로토타입 기반 언어로, 모든 객체는 프로토타입이라는 쌍둥이 객체를 가지고 있다. 원본 객체와 프로토타입 객체는 각각 constructor와 prototype이라는 프로퍼티를 통해 상호 참조하여 연결되어 있고, 원본 객체는 __proto__라는 프로퍼티를 통해 프로토타입 링크를 타고 상위의 프로토타입 객체와 연결된다. 이러한 연결고리를 '프로토타입 체인'이라고 하고, 이를 통해 객체는 상위 객체에서도 필요한 프로퍼티를 찾을 수 있다.

💬 ⭐ 클래스(ES6)

  • 자바스크립트에서 OOP를 구현하기 위해 ES6에 추가된 문법이다. 클래스를 사용하면 여러 객체를 쉽게 찍어낼 수 있고, extends 키워드를 통해 상속을 구현할 수 있다.
  • (클래스 내부에 변수도 선언할 수 있다. (ES2022))
  • (클래스의 메서드는 열거되지 않는 특성(Enumerable: false)을 지닌다.)

◽ 클래스 문법의 특징

  • class 키워드를 사용하여 선언하고, constructor 내부에서 프로퍼티를 정의하고 constructor 외부에서 메서드를 정의한다. 그리고 new 키워드를 통해 인스턴스를 생성한다.

💬 Strict Mode(ES5)

  • 유연한 언어인 자바스크립트를 엄격한 환경에서 실행하는 모드로, 자바스크립트에서 에러가 더 잘 발생되도록 한다.
  • 전역 변수 암묵적 선언 불가, 재선언 금지, 함수 내부 this에 직접 값 할당 불가
  • (ES6에 추가된 클래스를 사용하면 클래스 내부의 body는 Strict Mode로 실행된다.)

💬 ⭐ 실행 컨텍스트

  • 자바스크립트 엔진이 생성코드 실행에 필요한 정보를 모아놓은 환경이다.

◽ 실행 컨텍스트의 종류

  • 실행 컨텍스트의 종류에는 전역 실행 컨텍스트(GEC)와 함수 실행 컨텍스트(FEC)가 있다. / 전역 실행 컨텍스트는 애플리케이션이 시작할 때 한번만 생성되고 애플리케이션이 종료될 때까지 유지된다. 이는 자바스크립트가 싱글 스레드 언어라는 것을 의미한다. 함수 실행 컨텍스트는 함수가 호출될 때마다 생성되고 해당 함수의 실행이 완료될 때까지 유지된다.
  • (전역 실행 컨텍스트는 전역 객체를, 함수 실행 컨텍스트는 활성 객체를 참조한다.)

◽ 실행 컨텍스트의 단계(작동 방식)

  • 실행 컨텍스트의 단계는 생성 단계와 실행 단계로 나뉜다. / 생성 단계는 함수를 호출했지만 아직 실행되지 않은 단계이고, 실행 단계는 함수가 실행되는 단계이다. / 생성 단계에서는 변수 환경과 렉시컬 환경이 생성되며 this 바인딩이 일어난다.

◽ 렉시컬 환경과 변수 환경

  • 변수 환경과 렉시컬 환경은 환경 레코드(Environment Record)외부 환경 참조(Outer environment reference)라는 동일한 프로퍼티를 가진다. / 환경 레코드에는 실행에 필요한 변수와 함수를 등록하여 호이스팅이 일어날 수 있는 환경을 형성하고, 외부 환경 참조에는 외부 스코프에 대한 참조 정보를 등록하여 스코프 체인을 형성한다. / 다만 변수 환경의 환경 레코드에는 초기화까지 이루어지는 var, 함수선언식으로 선언된 식별자가 등록되고, 렉시컬 환경의 환경 레코드에는 초기화되지 않는 let, const로 선언된 식별자가 등록된다. / (let과 const가 등장함에 따라 관련 정보를 저장하기 위해 렉시컬 환경이라는 쌍둥이 프로퍼티가 등장한 것이라는 추측성 글을 본 적이 있다.)

💬 '=='와 '==='의 차이

  • '=='는 동등 연산자로, 자동으로 형변환을 수행하여 타입을 고려하지 않고 값을 비교한다. 반면 '==='는 일치연산자로 형변환을 수행하지 않아 타입을 고려하여 값을 비교한다.

💬 스프레드 연산자, 전개 연산자(ES6)

  • iterable 특성을 가지는(=반복 가능한) 데이터를 펼쳐놓는 문법이다. / 배열, 문자열, Map, Set 등에 사용할 수 있고, 예외적으로 객체 리터럴에도 적용할 수 있다. / 리액트에서 state 불변성을 유지를 위해 참조 타입의 데이터를 복사할 때 자주 사용한다.

    const initialState = { count: 0 };
    
    const newState = { ...initialState, count: initialState.count + 1 };
    console.log(newState); // { count: 1 }

💬 디스트럭처링 할당, 구조 분해 할당(ES6)

  • 배열이나 객체 등의 데이터를 분해하여 새로운 배열이나 객체에 할당하는 문법이다. / 리액트에서 props를 전달받아서 데이터를 추출할 때 자주 사용한다.

    // 부모 컴포넌트
    const ParentComponent = () => {
     const userData = { username: 'john', email: 'john@example.com', isAdmin: true };
    
     return (
       <div>
         <UserInfo userData={userData} />
       </div>
     );
    }
    
    // 자식 컴포넌트
    const UserInfo = ({ userData }) => {
     const { username } = userData;
    
     return (
       <div>
         <p>Username: {username}</p>
       </div>
     );
    }

💬 Rest 문법

  • 나머지 요소를 추출하기 위한 문법이다. / 구조 분해 할당 시 스프레드 연산자를 같이 사용하면 추출하지 않은 나머지 요소를 한번에 추출할 수 있다.

    // 부모 컴포넌트
    const ParentComponent = () => {
     const userData = { username: 'john', email: 'john@example.com', isAdmin: true };
    
     return (
       <div>
         <UserInfo userData={userData} />
       </div>
     );
    }
    
    // 자식 컴포넌트
    const UserInfo = ({ userData, ...rest }) => {
     const { username } = userData;
    
     return (
       <div>
         <p>Username: {username}</p>
         {/* 나머지 속성들을 출력 */}
         <p>Other Data: {JSON.stringify(rest)}</p>
       </div>
     );
    }

💬 Node.js

  • 자바스크립트 v8 엔진으로 빌드된 자바스크립트 런타임(실행환경)이다. 단일 스레드(단일한 콜스택)를 가지면서 논블로킹 I/O를 지원하여 비동기 처리가 가능하다는 특징이 있다.

◽ npm(node package manager)

  • Node.js 환경에서 자바스크립트 라이브러리를 설치 및 관리하는 패키지 매니저이다. 패키지를 순차적으로 처리하고, 패키지에 포함된 다른 패키지를 자동으로 설치하는 특징을 가진다.

    yarn

    페이스북에서 개발한 자바스크립트 라이브러리를 설치 및 관리하는 패키지 매니저이다. 패키지를 병렬적으로 처리(속도가 더 빠름)하고, yarn.lock 또는 package.json 파일에 있는 패키지만을 설치(안정성 및 보안성 개선)하는 특징을 가진다.

💬 이벤트

  • 웹 페이지에서 발생하는 상황이나 상호작용을 의미한다.

◽ 이벤트 핸들러를 등록하는 방법

  1. HTML의 Attribute 조작
    • onClick, onSubmit, onFocus...
  2. DOM API로 Property 조작
    • querySelector, getElementById...등의 메서드로 요소를 선택하고 addEventListener로 이벤트 핸들러 등록

◽ ⭐ 이벤트 전파

  • 어떤 요소에 이벤트가 발생할 때, 이벤트가 DOM 트리를 통해 다른 요소로 전파되는 현상이다. / 이벤트 전파는 캡처링 단계 - 타겟 단계 - 버블링 단계를 거친다. / 캡처링 단계는 상위 요소에서부터 타겟 요소로 이벤트가 전파되는 단계이고, 타겟 단계는 이벤트가 타겟 요소에 도달하여 이벤트 핸들러가 실행되는 단계이고, 버블링 단계는 타겟 요소에서부터 다시 상위 요소로 이벤트가 전파되는 단계이다.

    event.target과 event.currentTarget 차이점

    이벤트가 발생한 요소가 event.target이고, 이벤트 핸들러가 등록된 요소가 event.currentTarget이다.

◽ 이벤트 버블링과 이벤트 캡처링을 적용하는 방법과 중단하는 방법

  • 기본적으로 이벤트 버블링 단계에서 이벤트가 처리되도록 설정되어 있고, addEventListener의 세 번째 인자에 true를 입력하면 버블링이 아닌 캡처링 단계로 설정된다. 한편 이벤트 핸들러에 event.stopPropagation을 추가하면 이벤트 전파를 중단할 수 있다.

    event.preventDefault

    이벤트의 기본 동작을 막는 메서드이다.

◽ 이벤트 위임

  • 이벤트 버블링을 활용하여 여러 요소에 이벤트 핸들러를 등록할 때 공통 부모 요소에만 등록하여 메모리 낭비를 방지하는 방법이다.

◽ ⭐ debounce와 throttle

  • 연속적으로 일어나는 이벤트의 과도한 호출을 방지하기 위한 기술이다. / debounce는 일정 시간동안 이벤트가 발생하지 않으면 마지막 이벤트를 처리하는 기술이고, throttle은 일정 시간동안 일어난 이벤트를 1번만 처리하는 기술이다.

💬 ⭐ 동기와 비동기

  • 동기는 순차적으로 작업을 처리하는 것이고, 비동기는 이전 작업이 끝나지 않더라도 다음 작업을 병렬적으로 처리하는 것을 말한다.

◽ 비동기 함수

  • 함수의 실행 결과가 즉시 반영되지 않고 특정 조건이 충족될 때까지 기다리는 함수이다. 비동기 동작을 위해 타이머 함수처럼 콜백 함수를 사용할 수도 있고, fetch, axios와 같은 AJAX 통신 혹은 async/await 키워드처럼 Promise를 반환할 수도 있다.

◽ 이벤트 루프 (aka. 동기 작업과 비동기 작업의 작동 방식)

  • 동기와 비동기의 작동 방식을 이해하기 위해서는, 자바스크립트 런타임의 이벤트 루프를 이해해야 한다. / 자바스크립트 런타임은 Call Stack과 Memory Heap으로 구성된 자바스크립트 엔진과 Web API, Task Queue로 구성된다. / 함수가 호출되면 해당 함수는 Call Stack에 쌓이고 후입선출(LIFO, push&pop) 방식으로 처리된다. 그러다가 비동기 함수를 만나면 Call Stack에서 Web API에 위임되고, Web API는 비동기 처리가 완료되면 비동기 함수의 콜백 함수를 Task Queue에 등록한다. Task Queue에 등록된 콜백 함수들은 Call Stack이 비게 되면 그제서야 선입선출(FIFO, Enqueue&Dequeue) 방식으로 (Call Stack에서) 처리된다.

◽ Task Queue

  • Task Queue는 Microtask QueueMacrotask Queue로 구분할 수 있는데, Microtask Queue가 Macrotask Queue보다 높은 우선 순위를 갖는다. / Microtask Queue에서는 Promise를 반환하는 fetch, axios, async/await 등이 처리되고, Macrotask Queue에서는 XMLHttpRequest, 타이머 함수 등이 처리된다.

💬 타이머 함수

  • setTimeout, setInterval은 일정 시간 이후 혹은 일정 시간 간격으로 콜백 함수를 호출하는 비동기 함수이다. 타이머 함수는 clearTimeout 혹은 clearInterval를 통해 종료시킬 수 있다.

💬 ⭐ AJAX(Asynchronous JavaScript and XML)

  • 비동기 통신을 이용한 클라이언트와 서버 간의 데이터 교환 기술이다. / 데이터를 주고 받을 때 새로고침이 발생하지 않기 때문에 특정 부분만 렌더링하는 방식의 개발이 가능해졌다.

◽ AJAX 종류

  • XMLHttpRequestXML(요즘에는 JSON)과 HTTP 프로토콜을 이용하는 전통적인 데이터 교환 방식이다. 이는 콜백 지옥이 형성되어 가독성이 떨어진다는 단점이 있다. / 반면 fetch와 axiosPromise 기반으로, then-catch 메서드 혹은 async/await의 try-catch문를 통해 콜백 지옥을 피할 수 있다. / 그러나 fetch는응답값을 json 메서드를 통해 JSON 포멧으로 변환주는 작업이 필요하고, 네트워크 장애 상태만을 Rejected 상태로 보기 때문에 에러 처리가 까다롭다. 이에 반해 axios는 응답값을 JSON 포멧으로 바로 제공하여 변환 작업이 필요없고, 거의 모든 장애 상태를 Rejected 상태로 보기 때문에 에러 처리도 수월하다.

💬 JSON(JavaScript Object Notation)

  • HTTP 통신을 위한 데이터 포멧으로, 자바스크립트 객체의 형태와 비슷하다. / 다만 key를 쌍따옴표로 표현하고 value에 특정 타입(함수, 정규표현식, undefined 등)은 사용할 수 없다는 차이가 있다.

◽ JSON 처리를 위한 메서드

  • 자바스크립트는 JSON 처리를 위한 표준 빌트인 객체인 JSON 객체를 가지고 있다. JSON 객체에는 JSON.stringify 메서드와 JSON.parse 메서드가 있는데, JSON.stringify는 자바스크립트 객체를 JSON 문자열로 변환하는 직렬화 메서드이고, JSON.parse는 JSON 문자열을 자바스크립트 객체로 변환하는 역직렬화 메서드이다. / JSON.stringify는 요청을 보낼 때, JSON.parse는 응답을 받을 때 많이 사용한다.

💬 ⭐ Callback

  • 함수의 매개변수로 들어오는 함수로, 비동기 처리를 위해 사용할 수 있다.

💬 ⭐ Promise(ES6)

  • 비동기 처리를 위해 사용되는, 내용은 실행되었지만 결과를 반환하지 않은 특수 객체이다.

  • resolve, reject라는 콜백 함수를 가지는 함수를 전달받아, 비동기 작업 성공 시 resolve, 실패 시 reject를 호출한다. resolve가 실행되면 Fullfilled State가 되고 reject가 실행되면 Rejected State가 된다. Fullfilled State일 때는 then 메서드를 통해 결과를 반환받을 수 있고, Rejected State일 때는 catch 메서드를 통해 결과를 반환받을 수 있다.

    const myPromise = new Promise((resolve, reject) => {
      setTimeout(() => {
        if (success) {
          resolve('작업이 완료되었습니다.');
        } else {
          reject('작업을 처리하는 중 오류가 발생했습니다.');
        }
      }, 1000); 
    });
    
    myPromise.then((result) => {
      console.log('성공:', result);
    }).catch((error) => {
      console.error('오류:', error);
    });

💬 ⭐ async/await(ES8)

  • 비동기 함수를 만드는 키워드로, async를 사용하면 함수가 항상 Promise를 반환하도록 하여 비동기적으로 동작하도록 만들고, await를 사용하면 해당 라인의 Promise가 결과를 반환할 때까지 대기시켜서 비동기 코드를 동기적으로 동작하도록 만든다. / 이를 활용하면 비동기 코드를 더 직관적으로 작성할 수 있고 try-catch문을 통해 then 지옥을 해결할 수 있다.

💬 Generator ( function* )

  • 코드 블록의 실행을 중지했다가 필요한 시점에 다시 실행하는 특수 객체이다.

  • (제너레이터 함수를 호출하면 함수가 바로 실행되지는 않고 제너레이터 객체를 반환하는데, 이 객체의 next() 메서드를 사용하면 제너레이터 함수를 실행할 수 있다. 제네레이터 함수가 실행되면 다음 yield 키워드를 찾을 때까지 실행을 지속하고 (yield 뒤에 위치하는 값인) value를 반환한다. `다음 yield가 없으면 ({ done: true }가 되면서) 제너레이터 함수는 종료되는데, 이 때 만약 return문이 있으면 반환값이 value가 된다.)

    function* simpleGeneratorWithReturn() {
      yield 1;
      yield 2;
      yield 3;
      return 'Generator 실행 완료';
    }
    
    const generator = simpleGeneratorWithReturn();
    
    console.log(generator.next().value); // 1
    console.log(generator.next().value); // 2
    console.log(generator.next().value); // 3
    
    const result = generator.next();
    if (result.done) {
      console.log(result.value); // 'Generator 실행 완료'
    }

💬 Proxy

  • 특정 객체의 특정 작업을 가로채서 다른 작업을 수행하도록 하는 특수 객체이다.

  • (두 개의 객체 파라미터를 가지는데, 첫 번째 파라미터에는 타겟 객체이고, 두 번째 파라미터에는 핸들러 객체이다. 핸들러 객체에는 내장 메서드의 동작을 가로채 어떠한 작업을 수행하는 트랩을 정의한다. (get, set 매서드를 사용하면 읽기/할당 동작을 가로챌 수 있다.))

    const targetObject = {
      name: 'DONGKYEONG',
      age: 20
    };
    
    const handler = {
      get: function (target, property) {
        console.log(`속성을 읽는 중: ${property}`);
        return target[property];
      },
      set: function (target, property, value) {
        console.log(`속성을 설정하는 중: ${property} = ${value}`);
        target[property] = value; 
      }
      // get(){}, set(){} 형태도 OK
    };
    
    const proxy = new Proxy(targetObject, handler);
    
    proxy.name; // "속성을 읽는 중: name", 결과: 'DONGKYEONG'
    proxy.age = 30; // "속성을 설정하는 중: age = 30"
    console.log(proxy.age); // "속성을 읽는 중: age", 결과: 30
profile
개발 공부💪🏼

0개의 댓글