[프론트엔드 면접 준비] JavaScript 질문 목록 (feat. TypeScript)

Rachel·2024년 8월 6일
1

실제 면접에서 받은 질문들 앞에는 ⭐️ 별을 붙였습니다.

JavaScript

자바스크립트는 어떤 언어일까요?

자바스크립트는 웹 페이지의 동적인 동작을 구현하는데 주로 사용되는 인터프리터 언어입니다. (코드가 한 줄씩 순차적으로 실행) JS의 특징으로는 동적 타입을 사용하고 싱글 스레드이면서 논 블록킹 언어입니다.

자바스크립트 데이터 타입

자바스크립트 원시 타입은 number, string, boolean, null, undefined, symbol이 있습니다. 원시 타입은 값이 저장되는 위치가 메모리 상에 직접적으로 저장됩니다.

자바스크립트 참조 타입은 객체, 배열, 함수가 있습니다. 참조 타입은 값이 저장되는 위치가 메모리 상의 주소이며, 해당 주소를 통해 실제 값에 접근합니다.

이벤트 루프란?

이벤트 루프는 콜 스택을 모니터하고 태스크 큐에서 수행할 작업이 있는지 확인하는 싱글 스레드 루프입니다. 콜 스택이 비어 있고 태스크 큐에 콜백 함수가 있는 경우, 함수는 큐에서 제거되고 실행될 콜 스택으로 푸시됩니다.

싱글 스레드란?

싱글 스레드(single thread)는 한 번에 하나의 작업을 순차적으로 처리하는 방식입니다. 즉, 한 시점에 하나의 코드 실행만 가능합니다. 이는 프로그램 흐름이 단순하고 예측 가능해 디버깅과 테스트가 상대적으로 쉽고, 다중 스레드 환경에서 발생할 수 있는 동기화 문제, 데이터 경합 등을 신경쓸 필요가 없는 장점이 있습니다. 단점으로는 한 번에 하나의 작업만 처리하므로 성능 제약이 있고, 긴 작업이 실행 중일때 다른 작업이 대기 상태가 되어 반응성이 떨어질 수 있습니다.

콜 스택이란?

JS 엔진이 함수 호출을 관리하는 방식 중 하나로, 함수 호출과 실행 순서를 추적하는 자료 구조입니다. 콜 스택은 기본적으로 LIFO 후입선출 방식으로 작동합니다. 즉 마지막에 추가된 항목이 가장 먼저 제거됩니다.

재귀 호출
함수가 자기 자신을 호출할 때마다 새로운 실행 컨텍스트가 콜 스택에 푸시됩니다.
재귀 호출이 너무 깊어지면 콜 스택의 크기를 초과하여 “스택 오버플로우(Stack Overflow)“가 발생할 수 있습니다.

자바스크립트가 싱글스레드인데 비동기 처리가 어떻게 가능한가요?

자바스크립트가 싱글 스레드인데 비동기 처리가 가능한 이유는 이벤트 루프와 콜백 큐를 통해 가능합니다.
비동기 작업은 브라우저 또는 Node.js 환경에서 실행됩니다. 자바스크립트 엔진의 콜스택에 비동기 작업이 발생하면 해당 작업을 백그라운드로 보냅니다. 백그라운드에서 비동기 작업이 완료되면, 해당 작업의 콜백 함수를 태스크큐로 보냅니다. 이벤트루프는 계속해서 콜스택과 태스트큐를 확인하고 콜스택이 비었을때 태스크큐에 콜백 함수가 있다면 이를 콜스택으로 이동시켜 실행합니다. 이러한 과정을 통해 JS는 싱글 스레드이지만 비동기 처리가 가능합니다.

자바스크립트는 왜 싱글 스레드로 설계되었나요?

JS는 1995년 처음 개발되었고, 당시 웹 앱은 지금처럼 복잡하지 않아 사용자 인터랙션 처리가 싱글 스레드 환경에서도 충분했습니다.

마이크로태스크 큐 & 태스크 큐 차이점

마이크로태스크 큐가 태스크 큐보다 우선순위가 더 높아서 해당 큐에 있는 콜백이 먼저 실행됩니다.
태스크 큐는 setTimeout, setInterval, I/O 작업 등 일반적인 비동기 작업의 콜백을 저장하는 용도이고,
마이크로태스크 큐는 Promise 관련 작업이나 async/await의 후속 작업 등의 용도로 보다 짧은 지연 시간의 비동기 작업을 저장하는 용도입니다.

⭐️ 클로저에 대해 설명해주세요.

클로저란 함수와 그 함수가 선언된 렉시컬 환경의 조합입니다. 클로저는 외부 함수가 반환된 후에도 외부 함수의 변수 범위의 체인에 접근할 수 있는 함수입니다. 예를 들어 외부와 내부 함수가 있을때, 외부함수 실행이 끝나더라도 내부함수는 외부함수의 변수를 참조할 수 있습니다.

렉시컬 스코프란?

렉시컬 스코프란 함수를 선언한 시점에 상위 스코프가 결정되는 것을 말합니다.

스코프란?

스코프는 변수가 접근할 수 있는 유효 범위를 나타냅니다.

스코프 체인에 대해 설명해주세요.

스코프 체인은 현재 스코프에서 변수를 찾지 못할 경우, 상위 스코프로 이동하여 변수를 찾는 과정입니다. JS에서 함수가 중첩될 때 내부 함수는 외부 함수의 스코프에 접근할 수 있습니다. 스코프 체인은 전역 스코프까지 올라가면서 변수를 검색합니다.

실행 문맥(컨텍스트)에 대해 설명해주세요.

실행 컨텍스트는 실행할 코드에 제공할 환경 정보들을 모아놓은 객체입니다. 실행 컨텍스트는 동일한 환경에 있는 코드들을 실행할 때 필요한 환경 정보들을 모아 객체를 구성하고, 이를 콜 스택에 쌓아올렸다가, 가장 위에 쌓여있는 컨텍스트와 관련 있는 코드들을 실행하는 식으로 전체 코드의 환경과 순서를 보장합니다. 어떤 실행 컨텍스트가 활성화되는 시점에 선언된 변수를 위로 끌어올리고 외부 환경 정보를 구성하고, this 값을 설정하는 등의 동작을 수행합니다. 실행 컨텍스트는 자바스크립트의 동적 언어로서의 성격을 가장 잘 파악할 수 있는 개념입니다. 만약 실행 컨텍스트를 구성하고 싶다면, 함수를 실행해보면 됩니다.

  1. 전역 실행 컨텍스트 생성/소스코드 실행

    • var로 선언한 전역 변수는 객체 환경 레코드에 저장됨.
    • const, let으로 선언한 전역 변수는 선언적 환경 레코드에 저장됨.
    • edit 함수 실행 컨텍스트 생성/소스코드 실행
  2. 전역 환경 레코드와 달리 함수 환경 레코드는 분리되지 않고, 한 장소에서 var,const,let 모두를 처리한다.

    • 추가로 알아야 하는 것들:
      실행 컨텍스트들은 실행 컨텍스트 스택에 하나씩 쌓이고 사라진다. 소스코드 평가 과정에서는 선언문이 실행되고, 스코프에 등록된다. 소스코드 실행 과정에서는 변수에 값이 할당되고 함수가 호출된다.

⭐️ this가 JavaScript에서 어떻게 작동하는지 설명하세요.

this 키워드는 함수가 호출될 때 함수의 실행 문맥을 참조하는 특별한 객체입니다.
this의 값은 함수가 어떻게 호출되었는지에 따라 동적으로 결정됩니다.

  • 생성자 함수 호출(new)을 할 경우, 미래에 생성할 인스턴스가 바인딩됩니다.
  • 메서드 호출을 할 경우, 마침표 연산자 앞에 기술한 객체가 바인딩됩니다.
  • 일반 함수 호출시, 기본적으로 전역 객체가 바인딩됩니다. 브라우저에서는 window 객체, 엄격 모드('use strict')에서는 undefined이 됩니다.
    위의 규칙 중 다수가 적용되면 더 상위 규칙이 승리하고 this값을 설정합니다. (우선순위는 생성자 함수 > 명시적 바인딩 (call, apply, bind) > 암시적 바인딩 (메서드 호출) > 기본 바인딩 (전역 객체 또는 undefined) > 화살표 함수 (외부 스코프의 this) 순)

함수가 ES2015 화살표 함수인 경우 위의 모든 규칙을 무시하고 생성된 시점에서 주변 스코프의 this값을 받습니다.

  • 화살표 함수는 자신만의 this 바인딩을 가지지 않고, 선언될 시점에서의 상위 스코프가 바인딩됩니다.

call, apply, bind에 대해 설명해주세요.

call 메서드는 함수의 this 값을 명시적으로 설정하고, 이후의 인수들을 개별적으로 전달하여 함수를 즉시 호출합니다.
apply 메서드는 call과 비슷하지만 함수의 인수를 배열로 전달합니다. 이 역시 함수를 즉시 호출합니다.
bind 메서드는 함수의 this값을 역시 명시적으로 설정하고, 주어진 인수들로 새로운 함수를 반환합니다. 반환된 함수는 나중에 호출될 수 있습니다.

⭐️ 호이스팅에 대해 설명하세요.

호이스팅은 JS에서 변수나 함수 선언이 그 범위 내에서 최상단으로 끌어올려지는 동작을 의미합니다. 이로 인해 코드에서 변수를 선언하기 전에 참조할 수 있는 것처럼 보이는 현상이 발생합니다. 호이스팅은 변수와 함수의 선언만 끌어올려지며 초기화는 끌어올려지지 않습니다.

⭐️ var, let, const 키워드 관점에서 호이스팅 설명해주세요.

var은 함수 스코프로 선언과 초기화가 동시에 이루어집니다.
let, const는 블록 스코프로 선언 단계에서만 호이스팅이 됩니다.

따라서 var로 선언하기 전에 해당 변수를 console.log 해보면 할당된 undefined가 출력되고,
let, const로 선언하기 전 해당 변수를 consoel.log 해보면 TDZ에 의해 Reference Error가 출력됩니다.

TDZ(Temporal Dead Zone_일시적 사각지대)이란?

let, const로 선언한 변수는 선언과 동시에 초기화되지 않고 선언만 호이스팅이 됩니다. 따라서 초기화가 되기 전에는 참조가 불가능한데 이 상태를 표현하기 위해 TDZ(Temporal Dead Zone) 개념이 사용됩니다. 선언 전에 변수를 사용하는 것을 허용하지 않는 개념상의 공간입니다.

TDZ에 있는 식별자에 접근하는 것은 ReferenceError를 발생시킵니다.

⭐️ 추가적인 var, let, const의 차이점

var, let은 재할당이 가능하지만 const는 상수로 재할당이 불가합니다.

✏️ 정리
var: 함수 스코프, 재할당 가능, 호이스팅 시 undefined로 초기화
let: 블록 스코프, 재할당 가능, 호이스팅 시 Temporal Dead Zone 적용
const: 블록 스코프, 재할당 불가, 호이스팅 시 Temporal Dead Zone 적용

블록 스코프란?

ES6에서 도입된 개념으로, 중괄호 {}로 둘러싸인 코드 블록 내에서 선언된 변수는 해당 블록 스코프에서만 유효하다는 의미입니다.

함수 스코프란?

함수 스코프란 함수 내에서 선언된 변수들이 해당 함수 내에서만 접근 가능하다는 것을 의미합니다. 블록({}) 내부에서 선언된 변수도 함수 스코프 내에 포함됩니다.

이벤트 위임에 대해서 설명하세요.

이벤트 위임은 이벤트 리스너를 하위 요소에 추가하는 대신 상위 요소에 추가하는 기법입니다. 이 기법은 DOM의 이벤트 버블링(event bubbling) 특성을 활용하여, 하위 요소에서 이벤트가 발생될 때마다 상위 요소의 리스너가 실행되도록 합니다.

장점으로는 각 하위 항목에 이벤트 핸들러를 연결하지 않고, 상위 요소에 하나의 단일 핸들러만 필요하기 때문에 메모리 사용 공간이 줄어듭니다.
또 제거된 요소에서 핸들러를 해제하고 새 요소에 대해 이벤트를 바인딩할 필요가 없습니다.

이벤트 버블링과 캡처링에 대해서 설명해주세요.

이벤트 버블링은 한 요소에서 이벤트가 발생하면, 이 요소에 할당된 핸들러가 동작하고 이어서 부모 요소의 핸들러가 동작하고, 이러한 과정이 최상단 요소를 만날때까지 반복되면서 이벤트가 전파되는 것을 말합니다.

이벤트 캡쳐링은 이벤트 버블링과 반대로 한 요소에서 이벤트가 발생하면, 이 요소에 할당된 핸들러가 동작하고 자식요소의 핸들러가 동작, 이러한 과정을 반복하며 최하위 요소를 만날 때까지 반복되며 전파되는 것을 말합니다.
addEventListener() 메서드의 세 번째 매개변수로 true로 설정하면 이벤트 캡처링이 발생합니다.

이벤트 버블링, 캡처링을 방지하려면 어떻게 해야 하나요?

이벤트 전파를 막는 방법은 event.preventDefault()를 사용하면 이벤트에 대한 구현체의 기본 동작을 실행하지 않습니다. 예를 들어 a 태그 클릭시 href로 이동하지 않게 하거나 form 안에 submit 역할을 하는 버튼을 눌렀어도 새로고침하지 않게 하는 경우 사용합니다.
event.stopPropagation()은 현재 이벤트가 버블링되거나 캡쳐링 되는 것을 막습니다.
event.stopImmediatePropagation()은 버블링, 캡쳐링뿐만 아니라 해당 요소에 등록된 다른 이벤트 리스너들도 실행되지 않도록 합니다.

Ajax에 대해 설명해주세요.

ajax는 서버와 비동기적으로 데이터를 교환하고 웹페이지를 업데이트할 수 있는 기술입니다. 비동기적으로 처리되기 때문에 페이지 전체를 새로고침하지 않고도 수행될 수 있습니다. XMLHttpRequset 객체, fetch() API가 사용됩니다.

Ajax를 사용하는 것의 장단점

장점: 페이지 전체를 새로고침하지 않고도 서버와 통신할 수 있어 사용자 경험을 향상시킵니다. 또 부분적인 데이터 갱신을 통해 응답 시간이 단축되고 네트워크 대역폭을 절약할 수 있습니다. JSON, XML, HTML 등 다양한 형식의 데이터를 처리할 수 있습니다.

단점: SEO에 불리할 수 있습니다. 일부 웹 크롤러는 JS를 실행하지 않아 JS에 의해 로드된 컨텐츠를 볼 수 없습니다.

프로토타입에 대해 설명해주세요.

프로토타입은 객체가 다른 객체로부터 상속받을 수 있도록 해주는 객체입니다. 자바스크립트는 프로토타입 기반 언어로서 객체를 생성하고 상속하는 메커니즘을 제공합니다. 모든 JS 객체는 다른 객체에 기초하여 생성됩니다. 이를 위해 각 객체는 내부적으로 ‘proto’라는 내부 속성을 가지고 있는데, 이 속성은 객체를 생성한 생성자 함수의 프로토타입 객체를 가리킵니다.

프로토타입을 사용하는 이유는 여러 객체가 동일한 프로토타입을 공유함으로써 메모리 사용을 줄일 수 있고, 프로토타입 체인을 통해 상속을 자연스럽게 구현할 수 있기 때문입니다. 또 실행 중에 프로토타입 객체를 수정하여 모든 객체에 즉시 적용 가능합니다(동적 프로퍼티 및 메서드 추가 가능).

프로토타입 상속이 어떻게 작동하는지 설명하세요.

프로토타입 상속은 객체가 다른 객체의 프로퍼티와 메서드를 상속받는 메커니즘을 말합니다. 예를 들어, 어떤 객체가 특정 메서드를 호출할 때 그 객체 자신의 해당 메서드가 없다면, JS는 이 객체의 프로토타입 체인을 따라 올라가며 해당 메서드를 찾습니다. 이 과정에서 상위 프로토 타입 객체에 정의된 메서드가 실행되어 해당 객체에 상속됩니다. 만약 최상위 프로토 타입 객체에서도 해당 메서드가 없다면 null 에서는 더 이상 메서드를 찾을 수 없어 (TypeError: Cannot read property 'method' of undefined) 오류가 발생합니다.

attribute와 property의 차이점에 대해 설명하세요.

attribute는 HTML 마크업에 정의되지만 property는 DOM에 정의됩니다.

attributeHTML 태그 내에서 정의된 키-값 쌍으로 객체의 데이터를 설명하거나 특정 메타 데이터를 지정합니다. 예를 들어HTML 태그에서 class, type, id 등을 의미합니다.

propertyDOM 객체의 속성으로, JavaScript를 통해 동적으로 읽고 쓸 수 있습니다.

HTML attribute는 초기 DOM property의 값을 설정하지만, 이후 JavaScript로 DOM property를 변경해도 HTML attribute는 그대로 남아 있습니다.

참고

⭐️ ES6에서 달라진 점 말해주세요.

ES6에서는 let, const, 화살표 함수, 클래스, 템플릿 리터럴, 구조 분해 할당, 기본 매개변수 기본값 설정, 나머지 매개변수와 전개 연산자, import 키워드, 프로미스, 심볼, 향샹된 객체 리터럴이 추가되었습니다.

자세한 설명

  1. let과 const
    let: 블록 스코프 변수를 선언합니다. 함수 스코프가 아닌 블록 스코프를 가지므로, var의 호이스팅 문제를 해결합니다.
    const: 블록 스코프 상수로, 선언과 동시에 초기화되어야 하며, 이후 재할당이 불가능합니다.

  2. 화살표 함수 (Arrow Functions)
    구문: () => {} 형태의 간결한 함수 선언 방식.
    this 바인딩: 화살표 함수는 자신만의 this를 가지지 않고, 외부 스코프의 this를 유지합니다.

  3. 클래스 (Classes)
    클래스 문법: ES5의 프로토타입 기반 상속을 간단하고 명확하게 표현하는 구문.

  4. 템플릿 리터럴 (Template Literals)
    문자열 템플릿: 백틱(`)을 사용하여 문자열 내에서 변수를 쉽게 포함할 수 있습니다.

  5. 구조 분해 할당 (Destructuring Assignment)
    배열 및 객체 분해: 배열이나 객체의 속성을 개별 변수로 쉽게 분해할 수 있습니다.

  6. 기본 매개변수 (Default Parameters)
    기본값 설정: 함수 매개변수에 기본값을 설정할 수 있습니다.

  7. 나머지 매개변수와 전개 연산자 (Rest and Spread Operators)
    나머지 매개변수: 가변 인수를 배열 형태로 받을 수 있습니다. 함수의 매개변수 수가 정해져 있지 않고 호출할 때마다 다른 수의 인수를 전달할 수 있습니다.
    전개 연산자: 배열이나 객체를 개별 요소로 분해할 수 있습니다.

  8. 모듈 (Modules)
    모듈화: import와 export 키워드를 사용하여 코드의 모듈화를 지원합니다.

  9. 프로미스 (Promises)
    비동기 처리: 콜백 헬을 방지하고, 비동기 작업을 더 간결하게 작성할 수 있습니다.

  10. 심볼 (Symbols)
    고유 식별자: 객체 속성의 고유 식별자로 사용됩니다.

  11. 향상된 객체 리터럴 (Enhanced Object Literals)
    개선된 문법: 객체 리터럴의 속성을 정의할 때 변수명을 직접 사용할 수 있고, 메서드 축약 구문을 사용할 수 있습니다.

ES6에서 Arrow 함수를 언제, 왜 쓸까요?

Arrow 함수는 함수 본연의 입출력 기능을 직관적으로 표현해줍니다. 파라미터가 하나일 때 소괄호를, return 한 줄만 있을 때 중괄호와 return을 생략할 수 있어 코드가 간결해집니다.
또한, 내부의 this 값이 함수가 선언된 곳의 this 값을 그대로 사용하므로, this의 변경을 피할 수 있습니다. 단, 일반 함수와 용도가 완전히 같지 않아 항상 대체할 수는 없습니다.

참고

클래스가 뭔지 설명해주세요.

클래스는 객체 지향 프로그래밍에서 사용되는 개념으로 객체를 생성하기 위한 템플릿을 말합니다. 클래스를 사용하면 코드의 재사용성을 높이고, 복잡한 시스템을 더 쉽게 설계할 수 있습니다. 객체 지향 프로그래밍의 주요 원칙으로는 캡슐화, 상속, 다형성이 있습니다.

  1. 캡슐화 (Encapsulation):
  • 객체의 속성과 메서드를 하나로 묶고, 외부로부터 내부의 상태를 숨기는 것입니다. 이를 통해 데이터의 무결성을 유지하고, 객체의 사용 방법을 단순화할 수 있습니다.
  1. 상속 (Inheritance):
  • 하나의 클래스가 다른 클래스의 속성과 메서드를 물려받는 것입니다. 이를 통해 코드의 재사용성을 높이고, 계층 구조를 형성할 수 있습니다.
  1. 다형성 (Polymorphism):
  • 동일한 메서드나 속성 이름이 클래스에 따라 다르게 동작하는 것입니다. 이를 통해 객체 간의 상호작용을 단순화하고 유연성을 높일 수 있습니다.

생성자란?

JS의 생성자 함수는 새로운 객체를 생성하고 초기화 하는데 사용합니다. 객체를 생성할때 new라는 키워드와 대문자로 시작해서 선언합니다. 생성자 함수는 this를 사용하여 새로운 객체의 속성을 정의할 수 있습니다.

익명 함수란?

익명 함수는 이름이 없는 함수로, 일반적으로 함수 리터럴(function literal)을 사용하여 정의됩니다. 익명 함수는 주로 함수 표현식(function expression)으로 사용되며, 변수에 할당하거나 직접 호출할 수 있습니다.

익명 함수의 일반적인 사용 사례는 무엇인가요?

익명 함수는 다음과 같은 경우에 주로 사용됩니다:

  1. 일회성 함수: 한 번만 사용되고 재사용되지 않는 함수.
  2. 콜백 함수: 다른 함수에 인수로 전달되어 특정 이벤트나 작업이 완료된 후 호출되는 함수.
  3. 즉시 실행 함수 (IIFE): 정의와 동시에 즉시 실행되는 함수로, 변수나 코드 블록을 캡슐화하여 전역 범위로의 누출을 방지합니다.

익명 함수는 코드의 캡슐화와 일회성 작업, 이벤트 핸들러와 콜백 함수 등에서 유용하게 사용됩니다.

함수 선언문과 함수 표현식의 차이?

함수 선언문으로 정의된 함수는 호이스팅됩니다. 이는 함수 선언이 코드의 최상단으로 끌어올려지기 때문에, 함수가 정의되기 전에도 호출할 수 있음을 의미합니다. 또한 같은 스코프 내에서 여러 번 정의할 수 있습니다.

greet(); // 출력: "Hello, World!"

function greet() {
  console.log("Hello, World!");
}

함수 표현문은 함수 표현식은 익명 함수일 수도 있으며, 변수에 할당됩니다. 변수에 할당된 함수는 변수 이름을 통해 호출할 수 있습니다.
함수 표현식은 호이스팅되지 않습니다. 함수 표현식이 정의되기 전에 호출하면 undefined 오류가 발생합니다.
변수에 할당된 함수는 해당 변수로 재정의할 수 있지만, 변수 이름을 통해 호출해야 합니다.

console.log(greet); // 출력: undefined

const greet = function () {
  console.log("Hello, World!");
};

greet(); // 출력: "Hello, World!"

require와 import 차이점

둘 다 모듈 키워드로 외부 파일이나 라이브러리를 불러올 때 사용합니다. CommonJS 모듈 시스템에서는 require을 사용하고, import는 ES6부터 새로 도입된 키워드로 export한 파일을 import로 불러옵니다. require는 프로그램의 어느 지점에서나 호출 가능하지만, imports는 파일의 시작 부분에서만 실행할 수 있습니다.

일반적으로 import는 사용자가 필요한 모듈 부분만 선택하고 로드할 수 있기 때문에 더 선호됩니다. import는 require보다 성능이 우수하며 메모리를 절약합니다.

콜백에 대해 설명해주세요.

콜백 함수는 다른 함수의 인자로 전달되어 특정 작업이 완료되면 호출되는 함수입니다. 주로 비동기 작업의 결과나 에러 처리를 위해 사용됩니다.

콜백 함수를 호출하는 방법에는 동기, 비동기 두 가지 방식이 있습니다. 동기식 콜백은 중간에 비동기 작업 없이 외부 함수 호출 직후에 호출되고, 비동기식 콜백은 asynchronous 작업이 완료된 후 나중에 호출됩니다.

동기식 콜백의 예는 Array.prototype.map(), Array.prototype.forEach()에 전달된 콜백 함수가 있습니다.
비동기식 콜백의 예는 setTimeOut(), Promise.prototype.then()이 있습니다.

map, forEach의 차이점

forEach는 map과 달리 원본 객체를 변형하고, return 값을 반환하지 않습니다.
map은 원본 객체를 변형하지 않고, 변형된 새로운 배열을 반환합니다.

⭐️ 콜백 지옥이란?

여러 개의 비동기 작업이 연달아 발생할 때 콜백 함수가 중첩되어 코드가 복잡해지는 현상입니다. ES6에서 도입된 Promise나 async awiat을 사용하면 보다 간단하고 직관적인 구조로 비동기 코드를 작성할 수 있어 콜백 지옥을 방지할 수 있습니다.

⭐️ Promise에 대해 설명해주세요.

Promise는 ES6에 등장한 비동기 작업을 관리하기 위해 사용되는 객체로, 비동기 작업의 완료 또는 실패 여부를 추적하고 이를 처리하는 코드를 제공합니다.

Promise는 대기 중(Pending), 완료(Fullfiled), 실패(Rejected) 3가지 상태를 가지며 이를 이용해 비동기 작업의 흐름을 제어할 수 있습니다.
Promise의 then 메서드는 또 다른 Promise를 반환하므로 여러 비동기 작업을 순차적으로 처리할 수 있습니다.

Promise.all()에 대해서 설명해주세요.

Promise.all()은 여러 개의 Promise가 모두 이행될 때까지 기다리며, 하나라도 거부되면 전체가 거부됩니다.

Promise.race()에 대해서 설명해주세요.

Promise.race는 가장 먼저 이행되거나 거부된 Promise의 결과를 반환합니다.

Promise와 Callback를 비교 설명해주세요.

Promise와 콜백(callback)은 모두 JavaScript에서 비동기 작업을 처리하는 데 사용되는 방법입니다.

Promise를 사용하면 콜백 지옥을 방지하고 코드를 구조화하여 가독성을 높일 수 있습니다.
Promise는 catch 메서드를 통해 에러를 한 번에 처리할 수 있지만 콜백은 각각의 비동기 함수에서 별도로 에러를 처리해야 합니다. 또한, Promise는 then 메서드를 사용하여 비동기 작업을 순차적으로 처리할 수 있어 코드를 직관적으로 작성할 수 있습니다. 콜백은 보통 중첩된 구조로 순서를 보장하기 어렵습니다.
Promise는 async/await와 함께 사용될 수 있어 더 간편하게 작성할 수 있습니다.

Promise와 Async, Await를 비교 설명해주세요.

Promise는 비동기 작업의 완료 또는 실패를 나타내는 객체이고, async/await는 Promise를 더 간단하고 동기 코드처럼 작성할 수 있게 해주는 문법입니다.

async 키워드를 사용하여 함수를 비동기 함수로 선언하고 async는 항상 Promise를 반환합니다. 이 함수 내에서 await 키워드를 만나면, 해당 Promise가 해결될 때까지 비동기적으로 기다립니다. await은 Promise가 해결될 때까지 함수 실행을 일시 중단하고, 해결된 후에는 마이크로태스크 큐에 다음 작업을 넣어 실행을 재개합니다.

동기와 비동기의 차이점

동기는 말 그대로 ‘동시에 일어난다’는 뜻 입니다. 코드 순서대로 실행되고 여러 가지 요청을 동시에 처리할 수 없습니다.

비동기는 ‘동시에 일어나지 않는다’를 의미합니다. 하나의 요청에 따른 응답을 즉시 처리하지 않아도, 그 대기 시간 동안 또 다른 요청에 대해 처리가 가능한 방식입니다. JavaScript에서는 이벤트 루프를 사용하여 비동기 작업을 처리하는 작동 방식 덕에 이러한 처리가 가능합니다.

예를 들어 쉽게 말하면 카페에서 커피를 줄서서 주문할때 앞에 사람이 커피를 받아야 다음 사람이 주문할 수 있는 것은 동기 처리이고, 커피를 주문한 사람마다 진동벨을 주고 음료가 완료되면 손님을 부르는 방식이 비동기 처리입니다.

Blocking과 Non-Blocking에 대해 설명해주세요.

블로킹(blocking)은 코드의 실행이 다른 코드의 실행을 막는다는 것을 의미합니다.
반면, 논블로킹(Non-blocking)은 코드의 실행이 다른 코드의 실행을 막지 않는다는 것을 의미합니다.

깊은 복사와 얕은 복사에 대해 설명해주세요.

얕은 복사는 객체를 참조하고 있어서 값을 변경하면 참조하고 있던 객체의 값도 변경됩니다. 깊은 복사는 다른 객체로 복사하는 것이라서 값을 변경하면 원본 객체는 변하지 않습니다.

원본을 변경불가를 위해 어떻게 해야 할까요?

원본 데이터를 변경할 수 없도록 하려면 JavaScript에서는 주로 객체나 배열의 복사본을 만들거나, 객체를 동결(immutable)시키는 방법을 사용합니다.

  1. 객체 동결 (Object.freeze)
    Object.freeze 메서드를 사용하면 객체를 동결할 수 있습니다. 동결된 객체는 변경이 불가능해집니다.

  2. 깊은 복사 (Deep Copy)
    깊은 복사를 통해 원본 객체의 복사본을 만들어 원본 객체를 변경하지 않도록 할 수 있습니다. 이는 특히 중첩된 객체나 배열을 다룰 때 유용합니다.

    1. JSON.parse와 JSON.stringify 사용
    		const copy = JSON.parse(JSON.stringify(original));
    1. 라이브러리 사용 (예: lodash)
    2. 상수 사용 (const)
      객체나 배열의 레퍼런스를 const로 선언하면, 변수 자체의 재할당은 불가능하지만 객체의 속성은 여전히 변경할 수 있습니다. 따라서 const만으로는 원본 데이터 변경을 완전히 막을 수는 없습니다.
    3. 함수 내부에서 객체 변경 방지
      함수 내에서 원본 객체를 변경하지 않도록 복사본을 만들어 사용하는 방법입니다.

Object.Assign 과 spread 등 객체를 복사하는 방법들이 가진 문제점이 무엇인가요?

  • Object.assign: 중첩된 객체의 경우 얕은 복사(shallow copy)만 수행하므로, 중첩된 객체가 있는 경우 원본 객체가 변경될 수 있습니다.
  • Spread 연산자: 역시 얕은 복사만을 수행하므로 중첩된 객체에 대해서는 동일한 문제가 발생합니다.
  • JSON.parse(JSON.stringify(obj)): 이 방법은 객체를 완전히 복사하지만, 함수나 undefined 같은 값들을 복사할 수 없습니다.

어떤 단계까지 immutable(불변)한가요?

  1. 원시 데이터 유형 (Primitive Types): 원시 데이터 유형인 숫자(Number), 문자열(String), 불린(Boolean), null, undefined는 불변성을 가지고 있습니다. 이들은 값 자체가 변경되지 않고 새로운 값이 할당되어야만 변경됩니다.

  2. 객체 (Objects): 객체는 참조 타입이며 불변성을 가지고 있지 않습니다. 객체의 프로퍼티는 변경될 수 있고, 객체 자체의 참조가 변경될 수 있습니다. 그러나 객체를 불변하게 유지하기 위해 Immutable.js와 같은 라이브러리를 사용하여 변경 불가능한 객체를 생성할 수 있습니다.

  3. 배열 (Arrays): 배열 역시 객체와 마찬가지로 참조 타입이며 불변성을 가지고 있지 않습니다. 배열의 요소는 변경될 수 있고, 배열 자체의 참조가 변경될 수 있습니다. 마찬가지로 Immutable.js와 같은 라이브러리를 사용하여 변경 불가능한 배열을 생성할 수 있습니다.

자바스크립트 메모리 관리에 대해 아는 대로 말해주세요.

JS의 메모리 관리는 자동으로 수행되며, 이를 가비지 컬렉션이라고 합니다. JS엔진은 메모리를 자동으로 할당하고 해제합니다. 가비지 컬렉션은 더 이상 사용되지 않는 객체를 자동으로 메모리에서 해제하여 메모리 누수를 방지하는 메커니즘입니다.

가비지 컬렉터가 어떻게 작동하는지 설명해주세요.

가비지 컬렉터는 Mark and Sweep 알고리즘을 사용합니다. 마크 단계에서 루트 객체에서 시작해 도달 가능한 모든 객체를 마크합니다. 스위프 단계에서 마크되지 않은 객체를 메모리에서 해제하는 방식으로 동작합니다.

제너레이터란?

제네레이터(Generator)는 자바스크립트의 함수 중 하나로, 일반 함수와 달리 실행 도중에 여러 번 멈추고 재개할 수 있는 특성을 가지고 있습니다. 제네레이터는 function* 키워드로 정의되며, yield 키워드를 사용해 실행을 일시 중지하고 값을 반환합니다. 제네레이터 함수는 호출 시 즉시 실행되지 않고, 대신 이터레이터 객체를 반환합니다. 이 이터레이터 객체를 통해 제네레이터의 실행을 제어할 수 있습니다.


TypeScript

⭐️ 타입스크립트 잘 아시나요? 사용 경험

여러 프로젝트에서 타입스크립트를 사용한 경험이 있습니다. 해당 경험으로 타입 상속, 유틸리티 타입을 통해 중복을 줄이고 재사용하는 법을 배웠습니다. 또한 타입스크립트 사용으로 타입 체크를 통해 오류를 방지할 수 있어 타입스크립트 사용을 지향하는 편입니다.

자바스크립트와 타입스크립트의 차이를 설명해주세요

자바스크립트는 동적 타입 언어로 변수의 타입을 런타임 시점에 결정합니다.
타입스크립트는 정적 타입 언어로, 변수의 타입을 코드 작성 시점에 명시하여 타입 오류를 미리 방지합니다.

타입스크립트에서 타입을 지정할 때 인터페이스와 타입은 뭐가 다른지 설명해주세요.

  • 인터페이스: 주로 객체의 구조를 정의할 때 사용되며, extends 키워드를 사용해 상속이 가능합니다.
  • 타입(Type): 객체뿐만 아니라 원시 타입, 유니온 타입(| 연산자), 교차 타입(& 연산자), 튜플 등을 정의할 수 있습니다.

인터페이스는 동일한 이름으로 여러 번 선언되면 TS가 이를 병합합니다. 타입은 선언 병합을 지원하지 않습니다.

any, unknown의 공통점과 차이점을 설명해주세요.

공통점은 any와 unknown은 모두 모든 타입을 할당할 수 있는 타입입니다.

차이점은 any는 타입 안전성을 무시하며 자유롭게 사용할 수 있는 반면, unknown은 타입 안전성을 보장하고, 사용하기 전에 반드시 타입 확인이 필요합니다. unknown은 any보다 더 안전한 타입으로 간주됩니다.


면접 질문 목록을 모두 외우려고 하는 것은 추천드리지 않고, 질문을 받았을 때 간단하게 핵심을 말할 수 있도록 연습하는 것을 추천합니다. 또 꼬리 질문이 들어왔을때 대답할 수 있게 원리에 대해 이해하는 것이 중요합니다. 질문 목록이 굉장히 많지만 회바회 질문이 다르고 ⭐️이 실제 면접 질문이 나온 부분임을 확인해주세요! 또한 답변은 개인의 경험에 따라 달라질 수 있습니다.

추가하면 좋은 부분이나 잘못된 점이 있다면 댓글 남겨주세요. 감사합니다 :)


📌 참고 자료

깃허브 - 프론트엔드 면접질문 중요도별 정리

프론트엔드 면접 질문 - JS 질문

profile
기존 블로그: https://hi-rachel.tistory.com

0개의 댓글