JavaScript 미드레벨 기술면접
새 면접Q. JavaScript의 클로저(Closure)가 무엇인지 설명하고, 클로저를 사용할 때 발생할 수 있는 메모리 관련 문제와 이를 해결하는 방법을 실무 관점에서 설명해주세요. 또한 클로저가 실제로 유용하게 사용되는 패턴 2가지를 예시와 함께 설명해주세요.
클로저는 함수가 선언될 당시의 렉시컬 환경을 기억하여 외부 함수의 변수에 접근할 수 있는 메커니즘입니다. 클로저는 불필요한 외부 변수 참조를 유지하면 가비지 컬렉션이 되지 않아 메모리 누수가 발생할 수 있으므로, 더 이상 필요 없는 참조는 null로 명시적으로 해제해야 합니다. 실무에서는 데이터 은닉을 위한 private 변수 구현, 커링(Currying)을 통한 함수 재사용성 향상, 이벤트 핸들러에서 특정 컨텍스트 유지 등에 활용됩니다. 특히 모듈 패턴에서 public/private 인터페이스를 구분할 때나, 함수형 프로그래밍에서 부분 적용된 함수를 생성할 때 유용합니다.
- • 렉시컬 스코프를 기억하는 함수의 특성
- • 메모리 누수 방지를 위한 참조 해제 필요성
- • 데이터 은닉과 모듈 패턴 구현
- • 커링과 부분 적용 함수 생성
클로저를 사용한 모듈 패턴과 ES6 Module의 차이점은 무엇이며, 각각 어떤 상황에서 더 적합한가요?
Q. 여러 개의 API를 순차적으로 호출해야 하는 상황과 병렬로 호출해야 하는 상황을 구분하고, 각각의 경우 async/await를 사용하여 구현할 때의 성능 차이와 주의사항을 설명해주세요. 또한 일부 API 호출이 실패해도 전체 프로세스를 계속 진행해야 할 때의 에러 핸들링 전략을 제시해주세요.
순차 호출은 이전 API 응답값이 다음 API의 입력으로 필요할 때 사용하며 await를 연속적으로 사용하지만, 병렬 호출은 독립적인 API들을 Promise.all로 동시에 실행하여 총 소요 시간을 단축할 수 있습니다. 순차 호출 시 불필요하게 await를 남발하면 성능 저하가 발생하므로, 의존성이 없는 작업은 Promise를 먼저 생성한 후 나중에 await하는 방식으로 최적화할 수 있습니다. 일부 실패를 허용해야 할 때는 Promise.allSettled를 사용하거나, 각 API 호출을 try-catch로 감싸서 실패한 항목만 기본값으로 처리하는 방식을 사용합니다. 중요한 API와 부가적인 API를 구분하여, 핵심 API 실패 시에만 전체 프로세스를 중단하는 선택적 에러 핸들링이 실무에서 효과적입니다.
- • 순차 vs 병렬 호출의 의존성 기준 구분
- • 불필요한 await로 인한 성능 저하 방지
- • Promise.allSettled를 통한 부분 실패 허용
- • 중요도에 따른 선택적 에러 핸들링
10개의 API를 호출해야 하는데 동시 요청 수를 3개로 제한해야 한다면 어떻게 구현하시겠습니까?
Q. JavaScript에서 객체와 배열의 불변성을 유지해야 하는 이유를 설명하고, 깊은 복사(Deep Copy)를 구현하는 여러 방법들(JSON.parse/stringify, 재귀 함수, structuredClone, 라이브러리)의 장단점과 성능을 비교해주세요. 또한 React 같은 프레임워크에서 불변성이 중요한 이유를 설명해주세요.
불변성 유지는 예측 가능한 상태 관리, 변경 감지 최적화, 사이드 이펙트 방지를 위해 필수적입니다. JSON.parse/stringify는 간단하지만 함수, undefined, Symbol, 순환 참조를 처리할 수 없고, 재귀 함수는 모든 타입을 처리 가능하지만 구현이 복잡하며, structuredClone은 최신 브라우저에서 대부분의 타입을 지원하지만 함수는 복사할 수 없습니다. React에서는 불변성을 기반으로 얕은 비교(Shallow Comparison)로 리렌더링 여부를 판단하므로, 객체를 직접 수정하면 참조가 동일하여 변경을 감지하지 못해 UI가 업데이트되지 않습니다. 실무에서는 Immer 같은 라이브러리로 가독성과 성능을 모두 확보하는 것이 일반적입니다.
- • 예측 가능한 상태 관리와 변경 감지 최적화
- • 각 복사 방법의 타입 지원 범위와 한계
- • React의 얕은 비교 메커니즘과 불변성의 관계
- • 실무에서 Immer 같은 라이브러리 활용
대용량 객체 배열을 다룰 때 불변성을 유지하면서도 성능을 최적화하는 방법은 무엇인가요?
Q. JavaScript의 이벤트 위임(Event Delegation) 패턴이 무엇인지 설명하고, 언제 사용하는 것이 효과적인지 실무 예시와 함께 설명해주세요. 또한 이벤트 위임을 사용할 때 발생할 수 있는 문제점과 e.target과 e.currentTarget의 차이를 활용한 해결 방법을 제시해주세요.
이벤트 위임은 부모 요소에 단일 이벤트 리스너를 등록하여 자식 요소들의 이벤트를 처리하는 패턴으로, 이벤트 버블링을 활용합니다. 동적으로 추가되는 대량의 리스트 아이템, 테이블 셀, 버튼 등을 처리할 때 메모리 사용을 줄이고 성능을 개선할 수 있습니다. e.target은 실제 클릭된 요소를, e.currentTarget은 리스너가 등록된 요소를 가리키므로, 중첩된 구조에서 e.target.closest() 메서드로 원하는 부모 요소를 찾거나 dataset 속성으로 구분하여 정확한 이벤트 처리가 가능합니다. 단, 모든 이벤트가 버블링되는 것은 아니므로(focus, blur 등) 필요시 capture 단계를 사용하거나 focusin/focusout 같은 대체 이벤트를 활용해야 합니다.
- • 이벤트 버블링을 활용한 단일 리스너 등록
- • 동적 요소 처리와 메모리 최적화
- • e.target과 e.currentTarget의 차이와 활용
- • 버블링되지 않는 이벤트 처리 방법
이벤트 위임을 사용할 때 성능상 주의해야 할 점과 너무 상위 요소에 위임했을 때의 문제점은 무엇인가요?
Q. JavaScript에서 순수 함수(Pure Function)의 개념과 장점을 설명하고, 배열 메서드 중 순수 함수인 것과 아닌 것을 구분해주세요. 또한 실무에서 순수 함수 원칙을 지키기 어려운 상황(API 호출, DOM 조작, 날짜 처리 등)을 어떻게 관리하는지 함수형 프로그래밍 관점에서 설명해주세요.
순수 함수는 동일한 입력에 항상 동일한 출력을 반환하고 사이드 이펙트가 없는 함수로, 테스트 용이성, 예측 가능성, 병렬 처리 안전성을 제공합니다. map, filter, reduce, slice는 순수 함수이지만, push, pop, splice, sort는 원본 배열을 변경하므로 순수하지 않습니다. 실무에서는 사이드 이펙트를 완전히 제거할 수 없으므로, 순수한 비즈니스 로직과 불순한 I/O 작업을 분리하는 전략을 사용합니다. 예를 들어 API 호출은 별도 레이어로 분리하고, 순수 함수는 데이터 변환만 담당하게 하며, 의존성 주입을 통해 테스트 시 사이드 이펙트를 모킹할 수 있도록 설계합니다.
- • 동일 입력에 동일 출력, 사이드 이펙트 없음
- • 배열 메서드의 순수성 구분
- • 순수 로직과 사이드 이펙트 분리 전략
- • 의존성 주입을 통한 테스트 가능성 확보
Redux나 Zustand 같은 상태 관리 라이브러리에서 순수 함수 원칙이 어떻게 적용되며, 이것이 디버깅과 테스트에 어떤 이점을 제공하나요?
아직 댓글이 없습니다. 첫 번째 댓글을 남겨보세요!