프론트엔드 면접 질문들 모음
4월 말부터 현재까지 서류를 70군데 정도 넣었고, 면접을 10회 가까이 봤다. 대부분 기술 면접에서 떨어졌다. 그래서 기술 면접에서 받았던 질문들을 모아봤다. 좀만 더 화이팅..나 자신..
#CS 관련
Q. 브라우저에 URL을 입력하면 어떤 일이 일어날까요?
A. 내 답변
브라우저는 입력된 URL의 도메인 이름을 DNS 서버에 요청하여 해당 도메인 이름에 대한 IP 주소를 얻습니다(DNS 캐쉬). 이 과정에서 도메인 이름이 IP 주소로 변환됩니다. 브라우저는 얻은 IP 주소를 사용하여 해당 서버와 TCP 연결을 설정합니다. TCP 연결이 설정되면, 브라우저는 서버에 HTTP 요청을 보내고, 서버는 요청을 처리한 후, 브라우저에 HTTP 응답을 보냅니다. 이 응답에는 요청한 리소스(HTML, CSS, 자바스크립트 파일 등)가 포함됩니다. 브라우저는 서버로부터 받은 HTML 문서를 파싱하여 DOM트리를 생성하고, CSS파일을 파싱해서 CSSOM 트리를 생성합니다. 이 두 트리를 결합하여 렌더 트리를 생성합니다. 생성된 렌더트리를 기반으로 각 요소의 레이아웃을 계산하고, 화면에 표시될 위치와 크기를 정합니다. 이를 리플로우(reflow) 과정이라고도 합니다. 레이아웃 계산이 완료되면, 페인팅(Painting) 과정을 통해 각 요소를 화면에 픽셀 단위로 그립니다. 마지막으로, 페인팅된 여러 레이어를 합성(Compositing)하여 최종적인 화면을 구성합니다. 이 과정에서 GPU를 사용해 성능을 최적화할 수 있습니다.
Q. Stack
과 Queue
의 차이점은 무엇인가요?
A. 스택은 LIFO(Last In First Out) 방식으로 데이터를 저장하는 자료구조입니다. 그에 반해 큐는 FIFO(First In First Out) 방식으로 데이터를 저장하는 자료구조입니다.
Q. 본인이 웹 서비스를 개발할때 웹 서비스에서 Queue
를 사용한다면 어디에서 사용할 수 있을까요?
A. 잘 모르겠어서.. 알림 alert 같은 게 쌓이는 형식이라면 Queue
를 사용할 수 있을 것 같다고 답했음.. 대답하면서 생각해보니 이건 스택이 아닌가?
생각이 들었음(집에 와서 찾아봤지만 어떤 대답을 원하셨는지 잘 모르겠음 ㅠ..)
사용자가 빠르게 여러 개의 입력을 제공할 때(예: 빠르게 연속으로 키를 누르는 경우), 큐를 사용해서 순차적으로 처리??
여러 개의 API 호출을 순서대로 보내야 할 때?? 사용할 수 있을지도
Q. CSS에서 left, right 속성 대신 transform 속성을 사용하는 이유는 무엇인가요?
A. left
, right
는 레이아웃 단계에서 다시 계산을 해야하지만 transform
은 렌더링 단계에서만 처리하고 GPU를 사용하기 때문에 빠르다라고 답했음 (틀린 답변)
맞는 답변
left, right 속성은 레이아웃 단계에서 다시 계산을 해야 하므로 성능 저하가 발생할 수 있지만, transform 속성은 컴포지팅 단계에서 처리되고 GPU 가속을 활용할 수 있기 때문에 더 빠르다.
Q. 알고있는 알고리즘이나 데이터구조에 대해 알고있는게 있다면 말해주세요.
A. Stack, Queue, Heap을 설명했음
Q. 쿠키와 세션, 그리고 스토리지(로컬, 세션)에 대해서 설명해주세요.
A. 내 답변
쿠키
, 세션
, 그리고 스토리지
는 웹 애플리케이션에서 데이터를 저장하고 관리하는 데 사용되는 것들입니다.
쿠키
는 주로 클라이언트와 서버 간의 상태 정보를 저장하고 전달하는 작은 데이터 파일입니다. 클라이언트에서도 생성이 가능하지만
일반적으로 서버에서 생성하고, 사용자 인증이나 세션 유지, 사용자 설정 저장 등에 활용됩니다.
쿠키
는 모든 HTTP 요청 헤더에 자동으로 전송되며, 브라우저에 저장됩니다.
반면, 세션
은 서버에서 관리되는 사용자 상태 정보입니다.
사용자가 웹사이트에 접속하면 서버는 세션 ID를 생성하고, 이를 쿠키
에 저장하여 클라이언트와 서버 간의 상태를 유지합니다.
스토리지
는 클라이언트 측에 데이터를 저장하는 방법으로, 로컬 스토리지
와 세션 스토리지
가 있습니다. 로컬 스토리지
는 브라우저를 닫아도 데이터가 유지되며,
쿠키
와 마찬가지로 브라우저 단위로 저장됩니다. 반면, 세션 스토리지
는 브라우저 탭이 닫히면 데이터가 삭제되며,
각 브라우저 탭마다 독립적으로 데이터가 저장됩니다.
두 스토리지
모두 쿠키
보다 용량이 비교적 크며, 도메인 단위로 데이터를 저장합니다.
결론적으로, 쿠키
는 클라이언트와 서버 간의 통신을 위해, 세션
은 서버 측에서 사용자 상태를 관리하기 위해,
그리고 스토리지
는 클라이언트 측에서 데이터를 영구적 또는 일시적으로 저장하기 위해 사용됩니다.
Q. 쿠키 용량을 알고계신가요?
A. 4KB
Q. (CS는 아니지만) 브라우저 개발자 도구에 어떤 탭들을 사용해봤나요?
A. 요소, 소스, 콘솔, 네트워크, 어플리케이션, 라이트 하우스 탭을 사용해봤다고 답했음
Q. Application
탭에서 어떤 정보를 확인할 수 있나요?
A. Application
탭은 Storage
, Cookies
등의 정보를 확인할 때 사용했다고 답했음
#JS 관련
Q. 자바스크립트의 특성을 알고있는대로 말해주세요.
한 줄씩 해석하는 인터프리터 언어이고, 타입이 동적이고 프로토타입 기반 언어로 객체 지향 프로그래밍이 가능하고 함수형 프로그래밍도 가능합니다. 그리고 싱글 스레드 기반 언어입니다. 라고 답변
Q. var
, let
, const
의 차이점은 무엇인가요?
A. var
은 함수 스코프, let
, const
는 블록 스코프이며, let
은 재할당이 가능하고, const
는 재할당이 불가능하다고 답했음
Q. 함수스코프와 블록스코프의 차이점은 무엇인가요?
A. 딱 뭐다 라고 정의를 내리려고 하니까 어렵다고 답하고, 함수 스코프는 함수 내에서만 유효한 스코프이고, 블록 스코프는 블록 내에서만 유효한 스코프라고 답했음
var
은 함수 스코프이고, let
과 const
는 블록 스코프라고 답함
Q. 스코프 체이닝에 대해서 알고계신가요? 그렇다면 설명해주세요.
A. 스코프 체이닝은 변수를 찾을 때, 현재 스코프에서 찾지 못하면 상위 스코프로 올라가서 찾는 것을 말한다고 답했음(글로벌 까지)
Q. this
키워드에 대해 설명해주세요.
A. this
는 실행 컨텍스트에 따라 달라지는 녀석인데, 자바스크립트에서는 함수가 호출되는 방식에 따라 this
가 가리키는 대상이 달라진다고 답했음
전역 컨텍스트에서는 window
를 가리키고, 메소드로 호출하면 메소드를 호출한 객체를 가리키고, 생성자 함수로 호출하면 생성자 함수가 생성한 인스턴스를 가리킨다고 답함
(call
, apply
, bind
를 사용하면 this
를 바인딩할 수 있다는 것도 말하면 좋았을듯?)
Q. 콜스택과 이벤트루프에 대해 설명해주세요. 그리고 태스크 큐가 있는데 마이크로 태스크 큐와 매크로 태스크 큐의 차이점은 무엇인가요?
A. 콜스택은 함수 호출을 기록하는 자료구조이고,
이벤트 루프는 계속 돌면서 콜스택과 태스크 큐를 감시하고, 콜스택이 비어있을 때 태스크 큐에 있는 함수를 콜스택으로 옮기는 역할을 한다고 답했음
매크로 태스크 큐(면접 당시에는 그냥 태스크큐, 마이크로 태스크 큐로 알고있어서 태스크 큐는 이라고 답함)는 setTimeout
, setInterval
같은 비동기 함수들이 들어가고,
마이크로 태스크 큐는 Promise
, MutationObserver
같은 비동기 함수들이 들어간다고 답하고, 매크로 태스크를 처리하기전에 쌓여있는 마이크로 태스크 큐를 모두 수행한 뒤에
매크로 태스크 큐를 수행한다고 답함
Q. 일반 객체와 Map
의 차이를 설명해주세요.
일반 객체는 키로 문자열만 사용할 수 있고(숫자를 사용해도 문자열로 변환되고, 심볼이라는 것도 사용할 수 있는걸로 알고있다고 부연설명 했음) 키의 삽입 순서가 보장되지않고
Map
은 어떤 타입이든 키로 사용할 수 있고, 순서가 보장된다고 답했음
(ECMAScript 2015 (ES6) 부터 일반 객체도 순서가 보장되는 글을 봤다고 했는데 보장되는게 아니었음 ㅎ..)
Q. for..of
문을 사용할 수 있는 객체를 뭐라고 부르나요?
A. 유사배열 객체입니다.(틀린 답변, 정답은 이터러블 객체임..)
-> 이터러블 객체죠, 혹시 이터러블 객체가 되기 위한 조건이 두 가지 있는데 어떤건지 알고 계신가요?
A. "Symbol.iterator
속성이 있어야합니다." 는 대답했는데 나머지 하나는 잘 모르겠다고 답변했음
맞는 답변
이터러블 객체가 되기 위한 조건은
- 객체에
Symbol.iterator
속성이 존재해야 함, 이 속성은 이터레이터(iterator
) 객체를 반환하는 함수여야 합니다. - 이터레이터 객체:
Symbol.iterator
메서드가 반환하는 이터레이터 객체는next
메서드를 가져야 합니다.next
메서드는 호출될 때마다{ value: ..., done: ... }
{ value: ..., done: ... }
형태의 객체를 반환해야 합니다.value
: 현재 이터레이션의 값.done
: 이터레이션이 끝났는지 여부를 나타내는 불리언 값.
이러한 조건을 만족하는 객체는 for..of
문을 통해 순회할 수 있습니다. 예를 들어, 배열, 문자열, Map
, Set
등의 내장 객체는 모두 이터러블 객체입니다.
Q. 이벤트 버블링과 캡쳐링, 이벤트 위임에 대해서 설명해주세요.
먼저 캡처링 단계에 대해 설명드리겠습니다. 이 단계는 이벤트가 최상위 요소인 document
에서 시작해서 이벤트가 발생한 요소까지 내려가는 과정입니다.
기본적으로 캡처링 단계에서는 이벤트를 잡아내지 않지만, addEventListener
메서드의 세 번째 인자로 true
를 전달하면 캡처링 단계에서 이벤트를 처리할 수 있습니다.
다음은 타깃 단계입니다. 이 단계에서는 이벤트가 실제로 발생한 요소에서 이벤트가 처리됩니다. 예를 들어, 버튼을 클릭하면 그 버튼이 타깃 요소가 됩니다.
마지막으로 버블링 단계는 이벤트가 발생한 요소에서 시작해서 최상위 요소인 document
까지 전파되는 과정입니다. 이 단계에서 이벤트가 하위 요소에서 상위 요소로 전파됩니다. 기본적으로 이벤트 리스너는 버블링 단계에서 동작합니다.
이제 이벤트 위임에 대해 설명드리겠습니다. 이벤트 위임은 이벤트 버블링을 활용하여 공통 조상 요소에 이벤트 리스너를 설정하여 여러 하위 요소들의 이벤트를 처리하는 기법입니다. 이렇게 하면 많은 요소에 각각 이벤트 리스너를 설정하는 대신, 하나의 상위 요소에 이벤트 리스너를 설정하여 자식 요소들의 이벤트를 효율적으로 처리할 수 있습니다. 이는 성능을 향상시키고, 동적으로 생성된 요소에도 이벤트를 처리할 수 있게 합니다.
외에 클로저의 개념을 아는지 확인해보는 퀴즈와 이벤트 루프와 태스크 큐의 동작(코드 실행순서)퀴즈 하나씩 있었음
퀴즈 복기
Quiz1) 결과값은 무엇인가요? 이유는 무엇인가요?
function count() {
var i;
for (i = 0; i < 3; i++) {
setTimeout(function() {
console.log(i);
}, 0);
}
}
count();
function count() {
var i;
for (i = 0; i < 3; i++) {
setTimeout(function() {
console.log(i);
}, 0);
}
}
count();
A. for 루프가 0부터 2까지 반복하면서 setTimeout 함수를 3번 설정합니다. 이 때 setTimeout의 콜백 함수는 비동기적으로 실행됩니다. setTimeout 함수에 전달된 콜백 함수가 실행될 때, i의 값은 이미 3이 됩니다. 이는 for 루프가 종료된 후에 setTimeout의 콜백 함수가 실행되기 때문입니다. var로 선언된 변수 i는 함수 스코프를 가지므로, for 루프 안에서의 i는 전역 변수처럼 동작합니다. 따라서 모든 setTimeout 함수는 동일한 i 값을 참조하게 됩니다.
해결방법) let으로 변수 i를 선언하면 블록 스코프를 가지므로, for 루프가 실행될 때마다 새로운 i 변수가 생성됩니다. (다른 방법으로는 즉시 실행 함수를 사용하여 클로저를 생성하는 방법이 있습니다. 까지 말했으면 좋았을 듯)
Quiz2) 결과값은 무엇인가요? 이유는 무엇인가요?
console.log('script start');
setTimeout(() => {
console.log('setTimeout');
}, 0);
Promise.resolve().then(() => {
console.log('promise1');
}).then(() => {
console.log('promise2');
});
console.log('script end');
console.log('script start');
setTimeout(() => {
console.log('setTimeout');
}, 0);
Promise.resolve().then(() => {
console.log('promise1');
}).then(() => {
console.log('promise2');
});
console.log('script end');
A. 코드 실행 순서는 다음과 같습니다.
script start
script end
promise1
promise2
setTimeout
script start
script end
promise1
promise2
setTimeout
- 'script start'가 출력됩니다.
setTimeout
콜백이 매크로태스크 큐에 추가됩니다.- 첫 번째 then 블록이 마이크로태스크 큐에 추가됩니다.
- 'script end'가 출력됩니다.
- 동기 코드가 모두 실행된 후, 마이크로태스크 큐가 실행됩니다:
- 첫 번째 then 블록이 실행되어 'promise1'이 출력됩니다.
- 두 번째 then 블록이 마이크로태스크 큐에 추가됩니다.
- 두 번째 then 블록이 실행되어 'promise2'가 출력됩니다.
- 모든 마이크로태스크가 완료된 후, 매크로태스크 큐가 실행됩니다:
- setTimeout 콜백이 실행되어 'setTimeout'이 출력됩니다.
#React 관련
Q. React
에서 key
의 역할은 무엇인가요?
A. key
는 파이버 노드에 컴포넌트에 관련된 여러 정보 중 하나입니다. key
는 리액트가 컴포넌트를 식별하는 데 도움을 줍니다.
동적으로 생성되는 컴포넌트 리스트의 각 아이템을 식별하는 데 사용되거나 key
를 이용해서 상태값을 유지하거나 초기화할 수 있습니다. 라고 답변함
(개인적으로 조금 미흡했던거 같음..)
Q. 리액트 파이버에 대해 알고계신가요? 그렇다면 재조정이란 무엇인가요?
A. 위 key
답변에서 파이버를 언급해버렸다... 그래서 파이버에 대해 물어봤는데, 파이버는 리액트의 내부 알고리즘으로, 컴포넌트 트리를 재조정하는 알고리즘이라고 답했음
(재조정은 공부한적 있지만 기억이 휘발돼서 모르겠다고 답변했음 ㅠ)
Q. React
에서 값을 다룰 때, 불변성을 중요하게 생각하는데 불변성을 왜 유지해야 하는지 설명해주세요.
A. 버그 발생이 줄어들고, 성능 최적화에 도움이 된다고 답했음... (면접이 끝나고 가장 후회한 답변 중 하나... 알고있었는데 왜 이렇게 대답했을까..)
다시 답변한다면
- 객체가 불변성을 유지하면, 리액트는 객체의 참조만 비교하여 변경 여부를 빠르게 알 수 있습니다.
리액트는 상태와 props가 변경될 때 컴포넌트를 다시 렌더링합니다.
이 과정에서 리액트는 변경사항을 감지하기 위해 객체의 얕은 비교를 수행하는데,
이를 위해
Object.is
를 기반으로한shallowEqual
이라는 함수를 사용하여 두 객체의 최상위 속성을 비교합니다.
Q. setState
를 여러 번 호출해도 state
가 바로 업데이트 되지 않는 이유는 무엇인가요?
A. 스냅샷 비교를 통해 최적화를 하기 때문이라고 답했음. 면접관님이 원하셨던 대답은 "성능 최적화를 위해 상태 업데이트가 큐(queue)에 저장되고, 나중에 배치(batch) 처리되기 때문" 이었던듯
Q. 여러 번 호출했을 때 state
를 바로 업데이트 하기 위한 방법은 무엇인가요?
A. setState
에 함수를 넘기는 방법을 말했음 함수형 업데이트를 사용하면 각 상태 업데이트가 이전 상태를 기반으로 이루어진다고 답함.
Q. React Query
에서 staleTime
과 gcTime(cacheTime)
옵션 의 차이점은 무엇인가요?
A. 잘 모르겠다고 답함
staleTime과 gcTime(cacheTime)
캐시 관련 공식문서staleTime
- 정의: staleTime은 데이터가 "신선한(fresh)" 상태로 간주되는 기간입니다.
- 동작: staleTime 내에서는 캐시된 데이터가 신선한 것으로 간주되어, 서버에 재요청하지 않고 캐시된 데이터를 사용합니다. 이 시간이 지나면 데이터는 "stale" 상태가 되어, 다음번 요청 시 자동으로 다시 데이터를 가져오게 됩니다.
- 용도: 네트워크 요청을 줄이고 자주 변하지 않는 데이터를 일정 기간 동안 신선하게 유지하여 사용자 경험을 개선합니다.
cacheTime
- 정의: gcTime(cacheTime)은 데이터가 "비활성(inactive)" 상태로 간주되어 캐시에서 유지되는 전체 기간입니다.
- 동작: gcTime(cacheTime)이 지나면 캐시된 데이터는 메모리에서 제거됩니다. 이 시간이 지나면 데이터는 캐시에서 완전히 사라지게 되며, 다음번 요청 시 서버에서 다시 데이터를 가져오게 됩니다.
- 용도: 메모리 관리와 불필요한 데이터 유지 시간을 줄이기 위해 사용합니다. 기본값은 5분입니다.
- 관계와 설정 기준
staleTime < gcTime(cacheTime)
: 항상staleTime
은gcTime(cacheTime)
보다 짧아야 합니다. 이는 데이터가 먼저 "stale" 상태가 된 후, 나중에 캐시에서 제거되도록 하기 위함입니다. 이 규칙을 지키면 데이터가 신선하지 않은 상태에서도 캐시에 남아있는 시간을 설정할 수 있습니다.
Q. Vanilla Extract
를 사용하셨던데 왜 사용하셨는지 설명해주세요.
A. 새로운 기술을 학습해보고자 하는 생각으로 사용했고, 서버 사이드 렌더링환경에서 별도의 설정이 필요하지 않았기 때문에 적합하다고 생각해서 사용했다고 답함 타입스크립트를 지원해서 스타일 작성 시 발생할 수 있는 오류를 줄일 수 있었고, CSS-in-JS 방식으로 스타일을 작성하지만 런타임 비용이 없다고 답함
Q. 제로 런타임
방식이랑 Styled Component
처럼 런타임에 스타일을 적용하는 방식이랑 차이점을 질문
A. 제로 런타임 방식은 스타일을 컴파일 타임에 처리하여 런타임에는 스타일을 적용하는 데 추가적인 비용이 들지 않는 방식입니다. 스타일이 빌드 시점에 미리 컴파일되어, 최종 번들에 CSS 파일로 포함됩니다.
반면 Styled Component
는 런타임에 스타일을 적용하는 방식으로, 컴포넌트가 렌더링될 때마다 스타일이 적용됩니다. 이는 런타임 비용이 발생할 수 있습니다.
(이 부분은 좀 더 공부해야겠다고 생각함 CSSOM에 직접 적용한다 하더라..) 관련 링크
Q. 런타임에 스타일을 적용하는 방식이나 CSS파일을 읽어서 적용하는 방식의 속도차이가 있냐고 물어봄
잘 모르겠다고 답변..
Q. (과제) 에서 응답으로 받은 jwt를 왜 localStorage에 저장했는지 설명해주세요.
A. 과제에서 제공된 토큰 API는 응답 바디로 JWT를 응답해줬기 때문에 어느 곳에 저장하든 보안에 취약하다고 생각했고, 구현이 편리한 localStorage
에 저장했습니다.
localStorage
에 저장된 JWT는 XSS 공격에 취약할 수 있는 건 알고있습니다. 좀 더 안전한 대안으로는 httpOnly
쿠키를 사용하는 방법이 있지만, 이는 서버 측 설정이 필요해서 불가능 했다고 답변
Q. (과제) 모달을 구현 할 때, 특이하게 useState가 아니라 useSearchParams를 사용한 이유, 그리고 직접 돔을 조작하던데 왜 그렇게 했는지 설명해주세요.
A. useSearchParams
를 이용해서 쿼리 스트링으로 한건 새로고침 했을 때 useState
를 사용하면 상태가 초기화되기 때문에 그렇게 했다고 답했음
직접 돔을 조작한 부분은 전혀 문제가 없을 것이라고 생각해서 ??? 표정으로 모르겠다고 답하니 면접관님이 가상돔을 사용하는 리액트에서 직접 돔을 조작하는 것은 좋지 않잖아요?
라고 하셔서 그렇다고 답했음..
내 생각
아직도 저 부분에 대해서는 잘 모르겠음..
useEffect
내부에서 body
에 스타일을 추가해서 배경이 스크롤이 되지않도록 했었는데 리액트는 body
내부에 id
가 root
인 <div/>
아래부터 관리하는게 아닌가? 라는 생각이 들어서
구글링을 해봤지만 정확한 답변을 찾지 못했음.. (이 부분은 좀 더 공부해야겠다고 생각함)
function Modal({ children, isOpen, onClose, className }: ModalProps) {
const bodyRef = useRef<HTMLElement | null>(null);
function getBodyRef() {
if (bodyRef.current !== null) {
return bodyRef.current;
}
const body = document.body;
bodyRef.current = body;
return body;
}
useEffect(
function closeOnEscapeEffect() {
const closeOnEscapeKey = (e: KeyboardEvent) => (e.key === 'Escape' ? onClose() : null);
const body = getBodyRef();
body.addEventListener('keydown', closeOnEscapeKey);
return () => {
body.removeEventListener('keydown', closeOnEscapeKey);
};
},
[onClose]
);
// ...
function Modal({ children, isOpen, onClose, className }: ModalProps) {
const bodyRef = useRef<HTMLElement | null>(null);
function getBodyRef() {
if (bodyRef.current !== null) {
return bodyRef.current;
}
const body = document.body;
bodyRef.current = body;
return body;
}
useEffect(
function closeOnEscapeEffect() {
const closeOnEscapeKey = (e: KeyboardEvent) => (e.key === 'Escape' ? onClose() : null);
const body = getBodyRef();
body.addEventListener('keydown', closeOnEscapeKey);
return () => {
body.removeEventListener('keydown', closeOnEscapeKey);
};
},
[onClose]
);
// ...
그래서 후에 document.body 가 아니라 위 방식으로 ref를 이용했음
#일반적인 질문
1분 자기소개(안하는 곳은 20% 정도)
싸피에 관한 질문
본인의 성격의 장단점
프론트엔드 개발자가 되고싶은 이유
나이가 많은데 어린 개발자들과 일할 때 괜찮을지
취미
, 최근에 본 책
, 프로젝트 중 어려웠던 점
최근 가장 열심히 했던 기간은 언제인지
어떤 회사가 좋은 회사라고 생각하는지, 성공한 회사라고 생각하는지, 어떤 회사를 원하는지
갈등은 없었는지, 있었다면 어떻게 해결했는지, 없다면 앞으로 생겼을 때 어떻게 대처할건지
회사에 들어온다면 어떤 업무를 하고싶은지?