Async Await이 Promises 및 콜백을 사용하는 것과 비교했을 때 코드의 가독성과 유지 보수성을 어떻게 향상시키는지 설명해 주세요.
- Async/Await은 비동기 코드를 동기 코드와 유사하게 작성할 수 있도록 도와줍니다. 이로써 코드가 더 직관적이고 읽기 쉽습니다.
- 에러 처리가 간편하며, try-catch 블록을 사용하여 에러를 처리할 수 있어 예외 처리 코드가 명확하게 구분됩니다.
- 비동기 작업의 순서를 나타내는 데 도움을 줍니다. 비동기 작업을 연달아 수행하거나 병렬로 실행할 때 코드의 흐름이 명확하게 드러납니다.
제너레이터 함수의 실행 과정을 next, yield를 포함해 설명하세요
- 제너레이터 함수는 일반 함수와 다르게 코드 실행을 일시 중지하고 재개할 수 있는 특수한 종류의 함수
- 제너레이터 함수를 호출하면 실행이 시작되고 제너레이터 객체가 반환
- .next() 메서드를 호출하면 함수 실행이 시작되고 yield 문을 만날 때까지 실행이 진행됨
- yield 문을 만나면 해당 값을 반환하고 실행이 일시 중지됩니다.
- .next()를 호출하면 다음 yield 문까지 실행이 진행됨
제너레이터란 무엇이며 어떤 상황에서 사용되나요?
- 제너레이터는 주로 비동기 작업을 제어하고 순서를 조절하는 데 사용됩니다. 예를 들어, 비동기 작업을 순차적으로 실행하고 중간에 값을 반환하거나 상태를 저장할 수 있습니다.
제너레이터와 일반 함수의 차이는 무엇인가요?
- 일반 함수는 실행 중에 중지하거나 일시 중지할 수 없고, 코드를 한 번 실행하면 종료됩니다. 반면 제너레이터 함수는 실행 중에 중지 및 재개가 가능하며 값을 반환하고 중지된 상태에서 재개할 수 있습니다.
await 키워드 사용 조건 두 가지를 말해 주세요.
- await는 반드시 async 함수 내에서 사용되어야 합니다. await가 사용된 함수가 비동기적인 작업을 수행하는 중요한 표시입니다.
- await는 Promise 객체 앞에 사용되어야 합니다. 이 키워드는 해당 Promise가 처리되고 값을 반환할 때까지 현재 함수의 실행을 일시 중지합니다.
useEffect, useCallback, useMemo과 같은 React Hook들은 의존성 배열 (dependencies)를 활용합니다. 이 의존성 배열에 들어가는 값들이 변경될 때마다 상위 Hook들이 실행됩니다. 그렇다면 의존성 배열에는 객체를 가능한 넣지 말아야 한다고 합니다. 왜 그럴까요?
- 의존성 배열은 해당 배열 내의 값이 변경될 때에만 특정 Hook 또는 함수가 재실행되도록 관리합니다.
- 객체를 의존성 배열에 직접 넣을 경우, 레퍼런스(메모리 주소)만을 비교하기 때문에 객체의 내부 속성이 변경되더라도 레퍼런스가 동일하면 해당 객체는 변경사항을 감지하지 못합니다. 따라서 의존성 배열에 객체를 직접 넣으면 의도치 않은 부작용이 발생할 수 있습니다. 대신 객체의 변경을 감지하고 싶다면 해당 객체 내의 속성 중 값이 변경되는 속성들을 의존성 배열에 추가해야 합니다.
개발 시, 얕은/깊은 복사 쓰이는 예시와 하는 방법을 각각 설명하세요
- 얕은 복사는 객체를 복사할 때 원본 객체와 복사본 객체가 동일한 레벨의 속성을 공유합니다. 즉, 복사본 객체는 원본 객체의 레퍼런스와 같은 객체를 가지며, 내부 객체는 복사되지 않습니다. 얕은 복사는 JavaScript에서 기본적으로 Object.assign() 또는 전개 연산자(...)를 사용하여 수행할 수 있습니다.
- 깊은 복사는 원본 객체와 복사본 객체가 서로 독립적인 객체로 복사됩니다. 이는 원본 객체와 그 내부 객체의 모든 레벨에 걸쳐 복사가 이루어집니다. 깊은 복사를 수행하려면 라이브러리나 직접 구현해야 합니다.
원시 값과 객체의 차이점은 무엇인가요?
- 원시 값(Primitive Values): 원시 데이터 유형은 JavaScript에서 불변(Immutable)합니다. 원시 값은 숫자, 문자열, 불리언, null, undefined, Symbol과 같은 기본 데이터 유형을 의미합니다. 이러한 값은 변수에 할당되거나 함수에 전달될 때 값 자체가 복사됩니다.
- 객체(Objects): 객체는 복합 데이터 유형이며 여러 속성과 값들을 포함할 수 있습니다. 객체는 변경 가능하며 레퍼런스로 전달되므로 여러 변수가 동일한 객체를 가리킬 수 있습니다.
얕은 복사와 깊은 복사에 대해 설명해주세요.
- 얕은 복사는 객체의 최상위 수준의 속성을 복사하고 내부 객체들은 원본 객체와 복사본 객체가 동일한 레퍼런스를 가집니다. 즉, 내부 객체가 복사되지 않습니다.
- 깊은 복사는 원본 객체와 복사본 객체가 모두 독립적이며 내부 객체까지 복사됩니다. 모든 레벨의 속성과 내부 객체가 복제됩니다. 깊은 복사를 구현하려면 반복문이나 재귀 함수를 사용하거나, 외부 라이브러리(예: lodash의_.cloneDeep())를 활용할 수 있습니다.
불변성에 대해서 설명하고 불변성이 가져다 주는 효과에 대해서 말해주세요.
- 불변성(Immutability)은 컴퓨터 과학과 프로그래밍에서 중요한 개념으로, 한 번 생성된 데이터나 객체가 변경되지 않음을 의미합니다. 이것은 데이터 구조나 객체의 상태가 생성된 후에 수정될 수 없다는 것을 의미합니다. 반면에 가변성(Mutability)은 데이터나 객체의 상태가 변경될 수 있는 것을 나타냅니다.
- 불변성은 데이터나 객체가 생성된 후에 변경되지 않는 개념을 나타냅니다. 불변성은 안전성, 다중 스레드 환경에서의 안정성, 함수형 프로그래밍, 캐싱, 디버깅 및 테스트 용이성, 복사 및 비교 오버헤드 감소와 같은 다양한 이점을 제공합니다. 이는 코드의 예측 가능성, 안정성, 성능 향상 및 복잡성 감소를 도와줍니다.
- 안전성과 예측 가능성: 불변 데이터는 변하지 않으므로 코드 실행 중 예기치 않은 상태 변경이 발생할 가능성이 줄어듭니다. 이는 프로그램의 예측 가능성과 안전성을 향상시킵니다. 다른 부분에서 데이터에 대한 변경이 발생하더라도 해당 데이터에 접근하는 다른 부분에 영향을 미치지 않습니다.
- 다중 스레드 환경에서의 안정성: 불변 데이터는 여러 스레드 또는 프로세스에서 안전하게 공유될 수 있습니다. 병렬 처리 환경에서 데이터 무결성 문제가 발생할 가능성이 줄어들어 다중 스레드 환경에서의 코드를 더 안정적으로 만들어줍니다.
- 함수형 프로그래밍: 함수형 프로그래밍 언어 및 개념은 불변성을 중시합니다. 불변 데이터는 함수형 프로그래밍 패러다임을 따르는 코드 작성을 촉진하며, 이를 통해 코드를 간결하고 읽기 쉽게 만들어줍니다.
- 캐싱: 불변 데이터는 캐싱에 적합하며, 동일한 데이터에 대한 연산 결과를 캐시하여 계산 비용을 줄일 수 있습니다.
- 디버깅 및 테스트: 불변 데이터는 디버깅 및 테스트를 쉽게 만들어줍니다. 왜냐하면 데이터 상태가 변경되지 않으므로 프로그램의 여러 부분에서 데이터 상태를 추적하고 문제를 식별하기가 더 쉽기 때문입니다.
- 복사 및 비교 오버헤드 감소: 불변 데이터는 새로운 데이터를 생성할 필요가 없으므로 복사 및 비교 오버헤드가 감소합니다. 이는 성능 향상을 가져올 수 있습니다.
- 불변성은 프로그램을 더 안정적이고 효율적으로 만들어주며, 특히 병렬 및 분산 시스템에서 중요한 역할을 합니다. 이러한 이점을 고려하여 적절한 상황에서 불변성을 사용하면 프로그램의 품질과 성능을 향상시킬 수 있습니다.
스코프 체인을 통한 식별자 탐색 과정은 어떻게 진행되는 지 설명해주세요.
- 스코프 체인은 JavaScript에서 변수 및 함수 식별자를 검색하는 프로세스입니다. 코드가 실행될 때, JavaScript 엔진은 현재 실행 중인 함수 또는 블록의 스코프에서 시작하여 해당 스코프에서 식별자를 찾습니다.
- 만약 식별자를 현재 스코프에서 찾지 못하면, 엔진은 바로 상위 스코프로 이동하고 해당 스코프에서 식별자를 찾으려 시도합니다. 이러한 과정을 상위 스코프로 계속 이동하거나 전역 스코프까지 이동하면서 식별자를 찾을 때까지 반복됩니다. 이런 방식으로 중첩된 함수나 블록 내에서 외부 스코프에 정의된 변수에 접근할 수 있습니다.
스코프가 필요한 이유와 함수/블록 레벨 스코프의 차이를 알려주세요
- 스코프의 필요성: 스코프는 변수와 함수의 식별자를 격리시켜 충돌을 방지하고 변수의 가시성과 생명 주기를 관리하는 데 도움을 줍니다.
- 함수 레벨 스코프: 변수는 함수 내에서 선언된 경우 해당 함수 내에서만 유효합니다.
- 블록 레벨 스코프: 변수는 블록 내에서 선언된 경우 해당 블록 내에서만 유효합니다. ES6에서 let과 const를 사용하면 블록 레벨 스코프를 갖게 됩니다.
스코프와 스코프 체인에 대해 설명해 주세요.
- 스코프는 변수 및 함수의 식별자에 대한 유효 범위를 정의합니다.
- 스코프 체인은 현재 스코프에서 식별자를 찾지 못할 때 상위 스코프로 이동하여 검색하는 메커니즘입니다. 이것은 중첩된 스코프에서 식별자에 접근 가능하게 합니다.
var과 let, const의 스코프에 대해 설명해주세요.
- var로 선언된 변수는 함수 스코프를 따르며 함수 내에서 정의된 경우 해당 함수 내 어디에서든 접근할 수 있습니다.
- let과 const로 선언된 변수는 블록 스코프를 따르며 블록 내에서 정의된 경우 해당 블록 내 어디에서든 접근할 수 있습니다.
동적 스코프와 렉시컬 스코프를 설명하고 JS는 어떤 방식을 따르는지 말해주세요.
- 동적 스코프: 변수의 스코프가 실행 시점에 결정됩니다. 이는 현재 실행 중인 함수를 호출한 위치에 따라 변수의 스코프가 결정됨을 의미합니다. JavaScript는 렉시컬 스코프를 따르므로 동적 스코프는 JavaScript에서 기본적으로 지원하지 않습니다.
- 렉시컬 스코프: 변수의 스코프는 코드를 작성하는 시점에 정적으로 결정됩니다. 즉, 변수의 스코프는 해당 변수가 어디에서 선언되었는지에 의해 결정됩니다. JavaScript는 대부분의 상황에서 렉시컬 스코프를 따릅니다.