모의 기술 면접 - 다시 답변 해보기
지난 달에 모의 기술면접을 보고, 나의 답변이 많이 부족했음에 부끄러웠다.
평소에 깊게 공부하지 않았다는 것을 알게 되었고, 이를 보완하고자 코어 자바스크립트 책을 공부하면서 포스팅을 남기기도 했다.
책을 완독한 이 시점에, 조금이라도 더 나은 답변을 할 수 있을까 싶어서 다시 한번 면접 질문에 답해보며 점검해보려고 한다.
* 취소선, 이탤릭체 : 검색해서 작성한 내용
JavaScript
var, let, const 를 중복 선언 허용, 스코프, 호이스팅 관점에서 서로 비교해주세요.
var : 중복 선언이 가능하다. 중복 선언 시 덮어씌워진다. 함수 스코프를 갖는다. 함수 밖에서 쓰이면 전역 스코프를 갖는다. 블록{ }을 무시한다.
변수 선언이 호이스팅되어 변수 선언 전에 접근이 가능하다. (초기화 되어서 undefined 반환)
let, const : ES6에 추가된 변수 선언 키워드. 중복 선언이 불가능하다. 블록 스코프를 갖는다.
let 은 선언 시 초기화 하지 않아도 되지만, const는 선언 시 초기화를 함께 해줘야한다.
변수 선언 전에 접근 시 에러 발생한다. (호이스팅은 일어나지만 초기화가 되지 않음)
호이스팅이란?
코드 맨 위에 변수 선언이 끌어올려지는 현상
실행 컨텍스트의 Lexical Environment (의 environmentRecord)가 식별자(변수, 함수)를 수집하면서 발생하는 현상이다.
동기, 비동기에 대해 설명해주세요
동기는 코드를 위에서부터 순차적으로 실행하는 것
비동기는 처리 시간이 오래 걸리는 코드를 일단 요청해놓고 완료되기 전에 다른 코드를 먼저 실행하는 것
자바스크립트는 싱글 스레드 언어이므로 비동기 처리가 불가능하지만, 이벤트 루프에 의해서 비동기 처리가 가능하다.
콜스택에 추가된 실행 컨텍스트를 차례로 (동기적으로) 실행하다가, 처리 시간이 오래걸리는 비동기 코드(콜백함수, setTimeout, 네트워크 요청 등)를 만나면 web API 나 Node에서 처리하라고 던져놓고 위임하고, 처리가 되면 대기큐태스크 큐(콜백 큐, 이벤트 큐) 나 마이크로태스크 큐에 콜백함수가 추가 된다.
→ 태스크 큐: setTimeout, setInterval, fetch().then().catch() 같은 비동기 작업 완료 후 콜백이 저장됨.
→ 마이크로태스크 큐: Promise.then(), MutationObserver 같은 작업이 저장됨.
콜스택에 모든 코드가 처리되어 빈 상태가 되면 큐에 있는 코드들을 하나씩 실행시킨다.
실제 코드에서 비동기 코드는 어떻게 사용하는가? Promise와 async await 차이점
Promise 생성자 함수 : 파라미터는 resolve와 reject 인자를 받는 콜백함수
then, catch 메서드를 사용
메서드 체이닝이 길어지면 가독성을 해친다.
ES8 에서 async/await가 등장
함수를 async로 선언하고 함수 내부에서 await 키워드를 사용하여 함수의 실행이 완료될 때 까지 기다린다.
비동기 코드를 동기 코드처럼 보이게 한다.
클로저가 무엇인지, JavaScript에서 어떤 경우에 활용하면 좋을지 예시와 함께 설명
클로저 : 외부 함수의 실행 컨텍스트가 사라진 후에도 그 함수의 변수나 함수를 참조하는 대상이 있다면 (참조 카운트가 0이 아니라면) 접근이 가능한 현상
함수가 선언될 당시의 렉시컬 스코프를 기억하여, 함수가 스코프 밖에서 호출되더라도 그 환경에 속한 변수들에 접근할 수 있는 특성
활용 예 :
① 콜백함수 내부에서 외부 데이터 사용 : 이벤트 리스너
② 정보 은닉 : 접근 권한 제어 : public / private / protected
③ 부분 적용 함수 : n 개의 파라미터를 받아 실행하는 함수에 미리 m 개의 파라미터를 할당해놓고, n개의 파라미터와 함께 호출 하는 함수
④ 커링 함수 : 여러개의 파라미터를 받아 실행하는 함수에 1개씩 인자를 전달하여 원하는 시기에 호출되도록 하는 함수
useState 를 클로저로 설명하기
useState로 생성된 상태 변수와 setState 함수는 클로저를 통해 상태 값을 기억합니다. 함수형 컴포넌트가 여러 번 렌더링되더라도, 클로저를 통해 useState의 상태는 항상 최신 상태를 유지합니다.
state로 관리하는 변수를 setState에서 계속 참조하기 때문에 계속해서 접근이 가능하다?
TypeScript
JavaScript만 사용하는것과 비교해 TypeScript를 사용하는 이유에 대해 설명
자바스크립트는 동적 타입 시스템이다. 변수를 선언 시 직접 타입을 정의하지 않고 초기값에 따라 타입 추론이 된다.
코드 작성이 유연하고 간편하다는 장점이 있지만, 반면에 형 변환이 일어나기 때문에 의도치 않은 연산 결과가 나올 수 있다.
이를 보완하기 위해 타입 스크립트를 사용한다. 타입스크립트는 점진적 타입 시스템을 갖고 있어 변수 선언 시 타입을 정의하거나, 또는 초기값에 의해 타입 추론이 일어난다. 컴파일 시 타입 검사를 통해 런타임 에러를 방지한다.
타입스크립트는 컴파일하여 자바스크립트로 변환되고, 자바스크립트가 해석기에 의해 기계어로 변환되어 실행된다.
타입스크립트 컴파일러가 파싱 -> AST -> 타입 검사 -> 자바스크립트
-> 자바스크립트 엔진 파싱하여 AST 생성 -> 바이트 코드 생성 -> 기계어로 변환하여 실행
타입을 선언하는 키워드들은?
type 별칭 : 중복 선언이 안된다.
type A = { ... }
interface : 중복 선언 시 내부 파라미터가 합쳐진다. 객체 타입에 특화. extends로 상속이 가능하다.
interface B { ... }
enum : 열거형
enum C { ... }
const : 리터럴 값을 타입으로 취급하도록 고정
유니언 타입
인터섹션 타입
React
리액트의 장점
가상 DOM 을 사용한다. 유저 인터랙션에 의해 변경된 DOM을 감지한다.
=> 실제 DOM과 비교하여, 최소한의 변경만 모아서 한번에 실제 DOM을 변경 처리 (배치 처리)
바닐라 자바스크립트에서 DOM에 접근하는건 번거로운데 React에서는 DOM을 조작하지 않고 상태 관리를 통해 UI를 업데이트 할 수 있다. 쉽게 DOM을 조작할 수 있다.
- 가상돔과 실제 돔은 어떻게 비교를 하는지?
재조정 Reconilation
1) Dffing Algorithm
- 엘리먼트 타입이 다른 경우 : 완전히 새로운 트리를 구축
- 엘리먼트 타입이 같은 경우 : 변경된 내용만 갱신
2) key prop 으로 DOM 노드의 자식에 대한 재귀적인 처리
- key prop으로 기존 트리와 이후 트리의 자식들이 일치하는지 확인
- 컴포넌트 개발
재사용이 가능하여 개발 효율성을 높인다. 유지보수를 용이하게 한다. (단일 책임 원칙, 캡슐화..)
- 단방향 데이터 흐름 -> 데이터의 흐름을 쉽게 추적할 수 있다.
리액트에서 상태관리를 하는 이유는 무엇일까
리액트가 상태를 감지하고 있으면 상태가 바뀌었을 때 리렌더를 유발해 상태에 맞는 UI로 보여주기 위해서
=> 일관성을 유지한다.
평소에 어떤 방법으로 상태관리를 하는지? 그 이유는?
zustand나 jotai 활용
context API의 경우 프로바이더로 감싼 컴포넌트 트리 중 하나라도 상태 변화가 일어나면 모든 컴포넌트가 리렌더링 된다. (불필요한 리렌더링 유발)
전역상태 라이브러리는 상태만 사용하는 컴포넌트만 리렌더링 하도록 최적화
- 불필요한 리렌더링이란?
context API는 프로바이더로 감싸줌. 프로바이더 내에서 모든 컴포넌트가 리렌더링이 됨
이는 리플로우, 리페인트를 유발하므로 비용이 발생하고, 화면 깜빡임이 발생해 사용자 경험을 악화시킨다.
전역상태 관리 라이브러리 또는 React.memo, useCallback을 사용하여 개선 가능
- 배치처리를 하는데 상관없는거 아닌가?
배치 처리는 동일한 렌더링 사이클 내에서 발생하는 여러 상태 업데이트를 최적화 하는것.
Frontend
브라우저 렌더링 과정 설명
도메인 입력 시 DNS 서버를 거쳐 IP 주소를 받아온다. (또는 캐싱 활용)
3-way hand shaking 과정을 거쳐 TCP/IP 연결을 한다.
TCP연결이 수립되면 브라우저는 서버에 HTTP 요청을 보내 HTML, CSS, JS 등의 리소스를 요청한다.
서버는 이러한 파일을 브라우저에 응답으로 전송한다.
*이 과정에서 display: none, visiblity : hidden으로 설정된 요소는 렌더 트리에 포함되지 않는다.
opacity : 0 인 요소는 포함됨
브라우저가 HTML 파싱하여 DOM tree 생성, CSS 파싱하여 CSSOM tree 생성
이 둘을 조합하여 render tree 생성 : 화면에 실제로 표시할 내용만 담고있다.
layout, 요소의 크기와 위치 등을 계산하는 reflow(또는 layout)와 실제 화면에 그려내는 paint 를 거쳐 유저에게 화면이 표시된다.
브라우저는 HTML 내의 <script>를 만나면 자바스크립트 소스 파일을 파싱하여 DOM과 상호작용하여 동적 요소를 조작한다.
이 과정에서 DOM이 변경되면 브라우저는 해당 부분에 대한 리플로우와 리페인트를 수행한다.
유저에 의해 인터랙션 발생하면 브라우저는 이러한 이벤트를 처리하여 페이지의 상태를 업데이트를 한다.
이러한 변경은 DOM과 렌더트리에 영향을 미쳐 필요에 따라 리플로우와 리페인트를 유발한다.
CSR, SSR 차이점
CSR: 클라이언트(브라우저)에서 빈 HTML, CSS, JS 를 받은 후에 동적으로 콘텐츠를 렌더링. 유저에게 화면이 보여지는데 시간이 소요된다. 이후 화면 전환은 빠르고 부드럽다
주로 SPA에서 사용되며, 페이지 전체를 새로고침 하지 않고 필요한 부분만 동적으로 업데이트
SSR : 서버에서 완성된 HTML을 받아 클라이언트에 전달. 각 페이지마다 완성된 HTML 응답하므로 초기 로딩 속도가 빠르지만 페이지 전환시마다 서버와의 통신이 필요하다
SPA는 항상 CSR인가?
주로 CSR 방식을 사용하여 구현된다.
SSR을 부분적으로 적용하여 초기 로딩 속도를 개선하거나 SEO를 향상시키는 경우도 있다.
(Next.js의 pre-rendering)
하이드레이션이란?
클라이언트에서 SSR 로 정적 HTML을 받아오면 동적으로 활성화 하는 과정
렌더링된 HTML에 React의 이벤트 핸들러와 상태 관리 기능을 연결함
유저가 인터랙션 할 수 있도록 하이드레이션 과정이 필요하다.
JavaScript 를 파싱하여 DOM에 연결하는 과정? : 일반적인 렌더링 과정
=> SSR의 빠른 초기 로딩 + SEO 최적화 + 클라이언트 측에서의 동적 기능 활성화
SSR 로 된 페이지를 받아오는 시간이 느린 경우에, 원인이 무엇일까?
서버에서 처리할 리소스가 많은 경우 : 사진 용량이 크거나, 서버에 네트워크 요청을 보내 응답을 받아야 하는 경우 (네트워크 지연)
자바스크립트 처리 지연
=> 이미지 최적화, 서버 성능 개선, 네트워크 최적화 (CDN을 통해 물리적 거리 감소), 하이드레이션 최적화
자바스크립트 + 타입스크립트 는 그래도 답변하기가 조금 더 수월해진 것 같지만 ! 술술 나오는 수준은 아니라 좀 아쉽다.
분명 책에서 봤는데 (클로저 활용예시 라던가..) 답 못하는게 특히 아쉬웠다.
리액트, 브라우저 관련해서 더 공부할게 많이 보인다.
Next.js도 공부할게 많을텐데.. ㅋㅋ 걱정이 앞서긴 하지만, 모든걸 한번에 이룰 수는 없다. 차근차근 하도록 하자
헷갈리는 개념은 반드시 정확히 알아두도록 해야겠다.