JavaScript 미드레벨 코딩 면접
새 면접Q. 여러 개의 비동기 작업 중 가장 먼저 완료되는 결과만 필요한 경우 Promise.race를 사용할 수 있습니다. Promise.race의 동작 원리를 설명하고, 실무에서 이를 활용할 수 있는 시나리오 2가지를 제시한 뒤, Promise.race 사용 시 주의해야 할 점과 reject 처리 방법을 설명해주세요.
Promise.race는 전달된 Promise 배열 중 가장 먼저 settle(resolve 또는 reject)되는 Promise의 결과를 반환합니다. 실무에서는 API 타임아웃 구현(API 호출과 setTimeout을 race로 처리), 여러 CDN 중 가장 빠른 응답 선택 등에 활용됩니다. 주의할 점은 가장 먼저 reject되면 전체가 실패하므로, 타임아웃 구현 시 reject를 적절히 처리해야 하며, race가 완료되어도 나머지 Promise는 계속 실행되므로 리소스 정리가 필요한 경우 각 Promise에서 cleanup 로직을 구현해야 합니다. 또한 race에 빈 배열을 전달하면 영원히 pending 상태로 남는 점도 유의해야 합니다.
- • 가장 먼저 settle되는 Promise의 결과를 반환하며, resolve/reject 구분 없음
- • API 타임아웃, 다중 소스 경쟁 로딩 등의 실무 활용 사례
- • reject 처리, 나머지 Promise 계속 실행, 빈 배열 처리 등 주의사항
Promise.any와 Promise.race의 차이점을 설명하고, 각각 어떤 상황에서 더 적합한지 비교해주세요.
Q. JavaScript에서 커링(Currying)과 부분 적용(Partial Application)의 개념을 설명하고, 두 개념의 차이점을 명확히 구분해주세요. 또한 커링을 구현하는 범용 curry 함수를 만들 때 고려해야 할 핵심 사항과, 실무에서 커링이 유용한 구체적인 사례를 제시해주세요.
커링은 n개의 인자를 받는 함수를 1개의 인자를 받는 n개의 함수로 변환하는 기법이고, 부분 적용은 함수의 일부 인자를 고정하여 새로운 함수를 만드는 기법입니다. 커링은 항상 단일 인자를 받지만, 부분 적용은 여러 인자를 한번에 고정할 수 있습니다. 범용 curry 함수 구현 시 원본 함수의 arity(매개변수 개수)를 확인하고, 누적된 인자가 충분할 때만 원본 함수를 실행하며, this 바인딩을 고려해야 합니다. 실무에서는 로깅 함수 설정(log level 고정), 이벤트 핸들러 팩토리(공통 로직 재사용), API 클라이언트 생성(baseURL, headers 고정) 등에 활용됩니다.
- • 커링은 단일 인자 함수 체인, 부분 적용은 여러 인자 고정 가능
- • curry 함수 구현 시 arity 확인, 인자 누적, this 바인딩 처리 필요
- • 설정 함수, 이벤트 핸들러, API 클라이언트 등 실무 활용 사례
함수 합성(Function Composition)과 커링을 함께 사용하여 복잡한 데이터 변환 파이프라인을 구축하는 패턴을 설명해주세요.
Q. JavaScript에서 대용량 데이터를 렌더링할 때 사용하는 가상 스크롤링(Virtual Scrolling) 또는 윈도잉(Windowing) 기법의 핵심 원리를 설명하고, 이를 직접 구현할 때 고려해야 할 주요 이슈들(동적 높이, 스크롤 위치 계산, 버퍼 영역)을 설명해주세요. 또한 react-window 같은 라이브러리를 사용하지 않고 직접 구현하는 것이 필요한 경우는 언제인지 판단 기준을 제시해주세요.
가상 스크롤링은 전체 데이터 중 현재 뷰포트에 보이는 영역과 그 주변 버퍼 영역의 아이템만 실제 DOM으로 렌더링하고, 스크롤 시 동적으로 아이템을 교체하는 기법입니다. 구현 시 스크롤 위치로부터 시작/끝 인덱스를 계산하고, 각 아이템의 높이가 동적인 경우 측정 및 캐싱이 필요하며, 급격한 스크롤 시 빈 공간이 보이지 않도록 상하 버퍼를 두고, 전체 스크롤 높이를 유지하기 위한 spacer 요소가 필요합니다. 동적 높이 처리는 실제 렌더링 후 높이를 측정하여 캐시하고 offset을 재계산하는 방식으로 구현합니다. 라이브러리 대신 직접 구현이 필요한 경우는 복잡한 레이아웃(그리드, 마소니 레이아웃), 특수한 스크롤 동작(무한 스크롤과 결합, 양방향 스크롤), 극한의 성능 최적화가 필요한 경우입니다.
- • 뷰포트 영역만 렌더링하고 스크롤 시 동적 교체하는 원리
- • 동적 높이 측정 및 캐싱, 버퍼 영역, spacer 요소 등 구현 이슈
- • 복잡한 레이아웃이나 특수 요구사항이 있을 때 직접 구현 고려
가상 스크롤링에서 아이템 높이가 비동기로 결정되는 경우(이미지 로딩 등) 어떻게 처리할 수 있는지 설명해주세요.
Q. JavaScript에서 Proxy 객체를 활용한 데이터 검증(Validation) 패턴을 설명하고, getter/setter를 사용한 전통적인 방식과 비교했을 때의 장단점을 제시해주세요. 또한 Proxy의 트랩(trap) 중 set, get 외에 실무에서 유용하게 활용할 수 있는 트랩 2가지와 그 활용 사례를 설명해주세요.
Proxy를 사용하면 객체의 모든 속성에 대한 접근을 가로채서 검증 로직을 적용할 수 있으며, 새로운 속성이 추가되어도 자동으로 검증이 적용됩니다. getter/setter는 각 속성마다 명시적으로 정의해야 하지만, Proxy는 동적으로 모든 속성을 처리할 수 있습니다. 단점으로는 Proxy가 더 낮은 성능을 보이고, 디버깅이 어려우며, Proxy 자체를 직접 비교하면 항상 false가 나옵니다. has 트랩은 in 연산자를 가로채서 속성 존재 여부를 커스터마이징할 수 있어 권한 기반 속성 필터링에 유용하고, deleteProperty 트랩은 delete 연산을 가로채서 중요 속성의 삭제를 방지하거나 삭제 시 로깅을 추가하는데 활용할 수 있습니다.
- • Proxy는 동적으로 모든 속성 접근을 가로채며, 새 속성에도 자동 적용
- • getter/setter 대비 유연하지만 성능과 디버깅에서 단점 존재
- • has 트랩으로 권한 필터링, deleteProperty 트랩으로 삭제 제어 가능
Reflect API와 Proxy를 함께 사용하는 이유와 패턴을 설명하고, Reflect를 사용하지 않았을 때 발생할 수 있는 문제를 제시해주세요.
Q. JavaScript에서 깊은 객체 비교(Deep Equality Check) 함수를 구현할 때 고려해야 할 핵심 케이스들을 설명하고, 순환 참조(Circular Reference) 문제를 어떻게 해결할 수 있는지 알고리즘 접근법을 제시해주세요. 또한 배열의 순서를 무시하고 비교해야 하는 경우와 Date, RegExp, Map, Set 같은 특수 객체를 비교하는 방법을 설명해주세요.
깊은 객체 비교 시 primitive 타입 비교, null과 undefined 처리, 배열과 객체 구분, NaN 비교(Object.is 사용), 프로토타입 체인 고려 등이 필요합니다. 순환 참조는 WeakMap을 사용하여 이미 방문한 객체 쌍을 저장하고, 재방문 시 true를 반환하는 방식으로 해결합니다. 배열 순서를 무시하려면 먼저 정렬하거나, 각 요소의 출현 횟수를 카운트하여 비교하는 방법을 사용합니다. Date는 getTime()으로 타임스탬프 비교, RegExp는 source와 flags 비교, Map과 Set은 크기 확인 후 각 요소를 재귀적으로 비교하되, Set의 경우 순서가 없으므로 배열로 변환 후 정렬하여 비교합니다.
- • primitive, null, NaN, 배열/객체 구분 등 다양한 타입 처리
- • WeakMap을 사용한 순환 참조 감지 및 처리
- • Date, RegExp, Map, Set 등 특수 객체별 비교 전략
대용량 객체를 비교할 때 성능을 최적화하기 위한 전략(조기 종료, 얕은 비교 우선 등)을 설명하고, lodash의 isEqual 같은 라이브러리를 사용하는 것과 직접 구현하는 것의 트레이드오프를 분석해주세요.
아직 댓글이 없습니다. 첫 번째 댓글을 남겨보세요!