[면접] 면접 정리

jm4293·2024년 10월 3일
0

마크업 언어

  • 태그 들을 이용하여 문서나 데이터의 구조를 나타내는 방법이다

HTML

  • Hypertext Markup Language

XML

  • Extensible Markup Language)
  • 마크업 형태를 쓰는 데이터 교환형식

HTML, XML 차이점

  1. HTML의 용도는 데이터를 표시 / XML은 데이터를 저장 및 전송
  2. HTML에는 미리 정의된 태그가 있지만 XML은 사용자가 고유한 태그를 만들고 정의 가능
  3. XML은 대/소문자를 구분하지만 HTML은 구분하지 않는다.

클로저란

  • 자바스크립트는 렉시컬 스코프 규칙을 따름 따라서, 일반적인 방식으로는 함수 선언문 밖에서 함수 스코프에 접근할 수 없음
  • 이를 보완하기 위한 방법이 클로저 함수이며 클로저 함수는 스코프가 상위 함수의 스코프를 참조하는 함수를 return 하는 방식
  • React의 useState, moment 라이브버리에서 사용하는 방식
  • 외부에 굳이 노출될 필요가 없는 정보들은 캡슐화하는 것이 좋은데 이러한 코드구조인 모듈 패턴에 자바스크립트의 클로저는 이 모듈 패턴 구현이 특화가 되어있습니다.

DOM

DOM의 개념을 설명해주세요

  • HTML이나 XML 같은 마크업 언어로 작성된 문서를 자바스크립트와 같은 프로그래밍 언어가 조작할 수 있도록 하는 인터페이스를 의미합니다. DOM은 계층적 구조를 가진 노드 트리로 구성됩니다.

DOM은 왜 필요한가요?

  • 동적인 웹 페이지를 구현하려면 자바스크립트와 같은 프로그래밍 언어가 문서에 접근하고, 제어할 수 있는 수단이 있어야 합니다. 하지만, 마크업 언어로 작성된 문서에는 이러한 수단이 없죠. TV는 있지만, TV를 조작할 수 있는 리모콘이 없는 것과 같은 이치라고 볼 수 있습니다. 따라서, 문서에 접근하고, 제어할 수 있는 수단인 DOM이 필요합니다.

DOM을 통해 어떤 동작을 할 수 있는지 예를 들어주세요

  • button element가 클릭되었을 때, 특정 함수를 호출하도록 Event Handler를 추가할 수 있고, 웹 페이지 내에 새로운 요소를 추가하거나, 삭제, 수정할 수 있습니다.

DOM은 왜 계층적 구조로 표현하는지 설명해주세요

  • 계층적 구조에서는 노드들 간의 관계가 부모, 자식, 형제 등으로 명확하게 정의됩니다. 이는 노드의 추가, 제거, 이동 작업을 쉽게 할 수 있도록 도와줍니다. 또한, 이벤트가 발생한 요소로부터 이벤트가 올라가는 이벤트 버블링, 반대로 이벤트가 내려가는 이벤트 캡처링 동작은 계층적 구조에서 효율적으로 동작하기 때문에, DOM을 계층적 구조로 표현하는 것입니다.

리플로우와 리페인트

리플로우란 무엇인지 설명해주세요

  • 리플로우란, 웹 페이지 내에서 요소의 위치 또는 크기에 변화가 있을 때, 변화된 레이아웃을 다시 계산하여 렌더 트리에 적용하는 과정을 의미합니다. width, height, padding, margin 그리고 border-width와 같은 크기 관련 속성, position, top, left와 같은 위치 관련 속성, display, flex 속성과 같은 레이아웃 관련 속성, font-size, font-weight와 같은 폰트 크기 관련 속성이 리플로우를 유발하는 속성입니다.

리페인트란 무엇인지 설명해주세요

  • 리페인트란, 웹 페이지 내에서 요소의 시각적인 표현에 변화가 있을 때, 변화된 시각적 표현을 다시 계산하여 렌더 트리에 적용하는 과정을 의미합니다. color, background-color 같은 색상 관련 속성, border-color, border-radius 와 같은 테두리 관련 속성이 리페인트를 유발하는 속성입니다.

리플로우와 리페인트의 성능상 차이점을 설명해주세요

  • 부모 노드의 레이아웃 변화는 자식 노드의 레이아웃까지 영향을 미치기 때문에, 리페인트와는 달리, 리플로우가 발생하면 하위 렌더 트리를 다시 계산하고 재구성하는 과정이 필요합니다. 따라서, 리플로우는 그 자체만으로도 부하가 큰 작업입니다. 또한, 리플로우가 발생하면 일반적으로 리페인트도 다시 발생하기 때문에, 성능에 큰 영향을 끼친다고 할 수 있으며, 렌더링 성능을 최적화하기 위해선 리플로우를 최소화해야 합니다. 또한, 리플로우는 주로 CPU를 활용하여 연산하는 반면, 리페인트는 GPU를 활용한다는 차이도 있습니다.

리플로우를 최소화하기 위한 방법은 무엇이 있을까요?

  • 리플로우를 최소화하기 위해, DOM 업데이트를 하나로 묶어 Batch Update하는 방법을 생각해볼 수 있습니다. 또한, offsetHeight, offsetWidth와 같은 자바스크립트의 레이아웃 속성에 여러 번 접근하면 리플로우가 발생할 수 있기 때문에, 이러한 속성들은 변수에 저장해 두고 재사용해야 합니다. 마지막으로, 가급적 레이아웃 변경이 적은 요소를 사용해야 합니다. position 속성을 예로 들면 fixed나 absolute 같은 값들을 사용할 수 있습니다.

브라우저 렌더링 과정

브라우저 렌더링의 순서를 설명해주세요

  • 먼저 서버로부터 HTML 문서를 전달받으면, 브라우저 엔진은 위에서 아래로 순차적으로 파싱하며 태그와 속성을 발견합니다. 이 태그와 속성들은 트리 형태로 변환되어 메모리에 저장되는데, 이를 DOM 트리라고 합니다. HTML 파싱 중 CSS 링크 또는 스타일 태그를 만나면, 이를 파싱하여 CSSOM 트리로 변환합니다. 문서의 파싱이 완료되면, DOM과 CSSOM 트리를 결합하여 렌더 트리를 생성합니다. 렌더 트리는 브라우저 상에서 요소의 위치와 크기를 결정하는 리플로우 과정을 거치며, 마지막으로 요소의 색상, 경계선 등 시각적 요소를 그리는 페인팅 과정이 진행됩니다.

HTML 파싱 중간에 script 태그를 만나면 어떻게 되나요?

  • 파싱 중간에 script 태그를 만나면, 브라우저는 해당 스크립트를 로드하고 실행하기 위해 파싱을 일시 중단합니다. 외부 스크립트의 경우, 스크립트를 로드하고 실행한 후 파싱을 재개하며, 내부 스크립트의 경우, 실행이 완료될 때까지 파싱이 중단됩니다. 이로 인해 파싱 속도가 저하되고, DOM 트리가 완성되기 전에 스크립트가 DOM을 조작할 가능성이 있어, 예기치 못한 상황이 발생할 수 있습니다. 이러한 문제를 방지하기 위해 asyncdefer 속성을 사용하여 파싱에 미치는 영향을 최소화할 수 있습니다.

async와 defer 속성에 대해 설명해주세요

  • asyncdefer는 스크립트를 비동기적으로 로드하는 속성입니다. 이들은 HTML 파싱이 중단되는 현상과 DOM 트리가 완성되기 전에 스크립트가 실행되는 것을 방지하기 위해 사용됩니다. async 속성은 스크립트가 로드되는 즉시 실행하는 반면, defer 속성은 스크립트를 비동기적으로 로드하되, HTML 파싱이 완료된 후 스크립트를 실행합니다. 따라서 async는 스크립트 로드 순서 상관없이 실행되는 경우에 적합하고, defer는 스크립트의 실행 순서가 중요할 때 적합합니다.

CORS와 Preflight의 개념

CORS란 무엇인지 설명해주세요

  • CORS는 Cross Origin Resource Sharing의 약자로, 교차 출처 리소스 공유라고도 부릅니다. 2009년에 HTML5 표준으로 채택된 프로토콜이며, SOP에 의해 제한된 교차 출처 간 리소스 공유를 허용하기 위한 방법입니다. 애플리케이션의 요구 사항이 복잡해지면서, 다른 도메인의 리소스를 활용하는 경우가 많아졌기 때문에 등장한 프로토콜로, 서버에서 CORS 관련 헤더를 설정하여 다른 도메인에서의 리소스 요청을 허용할 수 있습니다. CORS 에러는 CORS 헤더를 적절히 설정하지 않은 상태에서 교차 출처 리소스를 요청하는 경우 발생할 수 있습니다.

SOP란 무엇인지 설명해주세요

  • Same Origin Policy의 약자로, 동일 출처 정책을 의미합니다. 1990년대 후반에 등장한 보안 정책으로, 현재 출처와 동일한 출처의 리소스만 접근할 수 있도록 하는 정책입니다. 여기서 동일 출처란, 도메인과, 프로토콜, 포트 번호가 모두 같은 경우를 의미하며, 하나라도 다를 경우 동일 출처 정책에 의해 리소스 접근이 제한됩니다.

SOP가 없을 경우 가능한 보안 취약점은 무엇인가요?

  • 사용자 인증 정보에 해당하는 세션 ID같은 정보들이 쿠키에 포함되어 있을 수 있기 때문에 이 세션 정보를 탈취하여 Cross Site Scripting 혹은, CSRF와 같은 해킹 공격에 이용할 수 있습니다. SOP 정책을 통해 리소스를 다른 도메인에서 접근하지 못하도록 제한한다면, 이러한 해킹 공격을 어느 정도 완화할 수 있습니다.

CORS 프로토콜이 동작하는 원리를 설명해주세요

  • 서버는 응답 처리 코드에서 CORS 관련 헤더를 설정할 수 있습니다. 이 헤더를 통해 요청을 허용할 도메인과, HTTP 메서드, 그리고 요청 헤더의 종류를 정의할 수 있습니다. 이후, 브라우저에서 서버로 리소스를 요청할 때, 이 헤더에 설정한 정보와 일치하지 않는다면, 브라우저에서 CORS 에러가 발생하는 것입니다. CORS 프로토콜 스펙에서 정의한 비교적 보안적으로 민감하지 않다고 판단되는 요청들이 있는데, 이를 단순 요청이라고 칭하며, 이 요청을 제외한 모든 CORS 요청에는 실제 요청을 전송하기 전, 요청 허가를 위한 preflight 요청이 발생할 수 있습니다.

Preflight 요청이란 무엇인지 설명해주세요

  • preflight 요청은 보안적으로 민감한 CORS 요청에 대해, 요청이 가능한지를 먼저 확인하는 과정입니다. 브라우저에서 자동으로 실행되는 요청으로, OPTIONS 메서드를 사용하며, 서버에서 설정한 CORS 관련 설정들을 Header 값으로 확인할 수 있습니다. 이 과정을 통해, 허용되지 않는 요청에 대한 처리 부하를 낮출 수 있습니다.

모든 CORS 프로토콜마다 Preflight 요청이 일어나나요?

  • 보안적으로 민감하지 않은 단순 요청이거나, 이전에 응답받은 Preflight 응답이 캐싱되어 있는 경우 Preflight 요청이 일어나지 않습니다. 서버에서 Access-Control-Max-Age라는 헤더값을 초 단위로 설정할 수 있고, 이전 요청의 응답값이 아직 캐싱되어 있다면, 같은 요청에 대해서는 Preflight 요청이 일어나지 않습니다.

단순 요청이 무엇인지 설명해주세요

  • 요청의 메서드가 GET, HEAD, POST 중 하나이며, 헤더와 Content-Type이 CORS 프로토콜에서 지정한 값인 경우가 단순 요청에 해당합니다. 이 경우는 Preflight 과정을 통한 권한 조회 과정 없이 CORS 요청이 가능합니다.

자바스크립트 호이스팅

호이스팅이란 무엇인지 설명해주세요

  • 호이스팅이란, 변수와 함수의 선언문이 해당 스코프의 최상단으로 끌어올려지는 현상을 의미합니다. 이러한 현상으로 변수와 함수가 초기화되기 전에 접근할 수 있는 현상이 발생합니다. 단, ES6에서 등장한 방식인 let과 const로 선언한 변수들은 호이스팅은 되지만, 초기화 전에 접근할 수는 없습니다.

변수와 함수 모두 호이스팅이 동일하게 동작하나요?

  • 아닙니다. 변수는 선언만 호이스팅 되지만, 함수는 선언과 초기화 모두 호이스팅됩니다. 이러한 특성으로 인해, 변수는 초기화 전에 참조할 경우, undefined가 나오지만, 함수는 초기화 전에 호출해도 정상적으로 호출이 가능합니다.

왜 변수와 달리, 함수만 초기화 과정까지 호이스팅 되나요?

  • function 키워드로 선언한 함수는 선언과 초기화, 할당 단계가 내부적으로 동시에 진행되기 때문입니다. 반면, 변수는 선언과 초기화를 한 구문으로 코딩했어도, 내부적으로는 두 단계에 걸쳐서 실행됩니다.

왜 let과 const으로 선언한 변수는 초기화 전에 접근할 수 없도록 막아 놓았나요?

  • 호이스팅 현상으로 인해, 초기화되지 않은 변수를 개발자가 참조할 수 없도록 하기 위해서입니다. 즉, 코드의 예측 가능성을 높이기 위해 Temporal Dead Zone (TDZ) 이라는 개념을 도입한 것입니다. 또한, 자바스크립트의 창시자인 브랜든 아이크에 따르면, 호이스팅 현상은 자바스크립트 개발 과정에서 실수로 생긴, 일종의 버그이기 때문에, 이를 ES6 버전에서 해결한 것으로 보입니다.

자바스크립트는 인터프리터 언어인데, 어떻게 호이스팅 현상이 일어날 수 있는건가요?

  • 일반적으로 알고 있는 인터프리터의 개념과는 달리, 자바스크립트 엔진은 코드 실행을 위해 파싱과 실행이라는 두 단계를 거치게 됩니다. 호이스팅이 처리되는 파싱 단계에서는 호이스팅 뿐만 아니라, 구문 트리와 실행 컨텍스트를 생성하는 작업도 함께 수행됩니다.

화살표 함수와 function 키워드의 차이점

화살표 함수와 function 키워드의 차이점을 설명해주세요

  • 먼저, 화살표 함수는 코드 블럭을 사용하지 않을 경우, return 문을 명시할 필요가 없습니다. 또한, function 키워드는 arguments 객체를 통해 함수 인자에 접근할 수 있지만, 화살표 함수는 arguments 객체를 갖지 않기 때문에, 나머지 매개 변수를 통해 함수 인자에 접근할 수 있습니다. 마지막으로, 함수 호출 방식에 따라 this의 참조가 다르게 동작하는 function 키워드와는 달리, 화살표 함수는 this가 항상 상위 스코프의 this를 참조합니다.

왜 화살표 함수에서는 this가 항상 상위 스코프를 참조하도록 하였을까요?

  • function 키워드로 정의한 함수는 호출 방식에 따라 this 바인딩이 다르게 동작하기 때문에, this 값을 예측하기 어렵다는 문제가 있습니다. 따라서, 화살표 함수에서는 this가 항상 상위 스코프를 참조하도록 개선하여, this 바인딩의 일관된 동작을 보장하고, 코드 흐름에 따라 this의 참조값을 예측하기 쉽도록 만든 것입니다.

function 키워드 함수의 this 바인딩은 어떤 식으로 동작하나요?

  • 일반적인 함수 호출의 경우는 this가 전역 객체를 가리키며, 객체의 메서드로 호출되는 경우는 해당 객체를 가리키게 됩니다. 또한, 함수가 이벤트 핸들러로써 호출되는 경우에는 이벤트가 발생한 요소를 가리킵니다. 이처럼, function 키워드로 정의한 함수는 this 바인딩의 일관성이 부족하기 때문에, ES6 이전에는 apply나 call, bind와 같은 함수를 통해 명시적으로 this 바인딩을 수행했습니다.

렉시컬 스코프

스코프란 무엇이며, 자바스크립트의 스코프는 어떻게 동작하나요?

  • 스코프란, 변수나 함수의 유효 범위를 의미합니다. 즉, 특정 변수나 함수를 어느 위치에서 참조할 수 있는 지를 나타내며, 만약 스코프에서 벗어난 위치에서 참조할 경우 Reference Error가 발생합니다. 자바스크립트의 스코프는 크게 전역 스코프와 로컬 스코프로 나눌 수 있으며, 이들 모두 렉시컬 스코프 규칙에 따라 결정됩니다. 로컬 스코프는 다시 함수 스코프와 코드 블록 스코프로 나눌 수 있는데, let과 const로 정의한 변수만 코드 블록 스코프를 가질 수 있습니다.

렉시컬 스코프란 무엇인가요?

  • 렉시컬 스코프란, 변수가 어디에 정의되었는지에 따라 스코프가 결정되는 메커니즘을 의미하며, 정적 스코프라고 부르기도 합니다. 이는 코드가 작성된 위치에 따라 변수의 유효 범위가 결정된다는 뜻입니다. 이러한 이유로, 서로 독립적인 로컬 스코프 A, B가 있을 때, A 내부에 선언된 변수를 B 위치에서, B 내부에 선언된 변수를 A 위치에서 참조할 수 없습니다. (단, 이 때 각 변수는 전역 변수에도 선언되어 있지 않다고 가정) 또한, 함수가 중첩된 경우 내부 함수는 외부 함수의 변수를 참조할 수 있지만, 외부 함수는 내부 함수의 변수를 참조할 수 없습니다.

자바스크립트에서 변수를 참조하는 과정을 설명해주세요

  • 자바스크립트는 함수가 실행될 때마다 메모리 상에 실행 컨텍스트를 생성합니다. 이 실행 컨텍스트 안에는 렉시컬 환경이 존재하는데, 여기에 현재 스코프에서 선언한 모든 변수와 함수들이 저장되어 있는 환경 레코드가 존재합니다. 또한, 현재 환경에서 찾지 못한 변수를 상위 스코프에서 찾기 위해, 상위 스코프를 가리키는 외부 렉시컬 환경 참조도 갖고 있죠. 이를 통해, 코드 상에서 변수나 함수를 참조하면, 먼저 현재의 환경 레코드에서 찾아보고, 없을 경우 외부 렉시컬 환경 참조를 통해 상위 스코프를 참조하게 됩니다. 이런 식으로 모든 상위 스코프를 탐색했는데도 찾지 못할 경우, 전역 스코프까지 올라가며, 여기서도 찾지 못하면 Reference Error를 발생시킵니다.

Promise, async, await

Promise의 개념에 대해 설명해주세요

  • Promise는 비동기 연산의 상태를 나타내는 객체입니다. 비동기 처리가 진행중이면 pending, 성공이면 fulfilled, 실패면 rejected라는 상태값을 가집니다. Promise는 비동기 프로그래밍을 then과 catch의 체이닝을 통해 보다 간결하게 표현할 수 있도록, ES6에서 새로 도입되었습니다.

Promise 등장 이전에는 어떤 방식으로 비동기 처리를 했는지 설명해주세요

  • Promise 등장 이전에는 비동기 작업을 처리하는 함수에 성공 콜백과, 실패 콜백을 각각 넘겨서 완료 상태에 따른 처리를 했습니다. 이런 방식이다 보니, 두 개 이상의 비동기 작업이 순서를 갖고 실행되어야 할 때, 콜백 함수 안에 또다른 콜백 함수가 점점 중첩되는 callback hell 현상이 발생하여, 코드 가독성 및 유지보수성 저하의 요인이 되곤 했습니다.

async-await에 대해 설명해주세요

  • Promise의 완료를 기다리기 위한 문법으로, async 키워드로 정의한 함수 내에서 호출되는 Promise 앞에 await 키워드를 쓰면, 해당 Promise가 완료될 때까지 코드의 실행을 일시정지할 수 있습니다. 이를 통해, 비동기 코드를 마치 동기 코드처럼 쉽게 작성할 수 있습니다.

async-await 를 사용할 때 주의해야할 점을 알려주세요

  • await의 에러 핸들링은 반드시 try-catch 블록에서 해야합니다. 또한, await는 Promise가 완료될 때까지 함수의 실행을 중단하기 때문에, 실행 흐름을 잘 고려하여 적재적소에 써야합니다. 예를 들어, 여러 비동기 작업이 순차적으로 진행될 필요가 없는 경우는, await 대신 Promise.all 함수를 사용하는 것이 바람직합니다.

이벤트 루프와 콜백

비동기 처리는 왜 필요한가?

  • 아시다시피, Javascript는 싱글 스레드 언어입니다. 그 말인 즉슨, 두 개 이상의 연산이나 함수를 동시에 실행할 수 없다는 뜻이죠. 하나의 연산이 실행 중이면, 쓰레드가 block되는 것입니다. 현대 컴퓨터 성능에서, 간단한 함수 실행에 의한 쓰레드 block은 체감하기 힘들지만, 네트워크 요청, DB Query, 파일 시스템 제어 등 시간이 오래 걸리는 작업도 있습니다. 이런 작업들이 메인 쓰레드를 block하고 있으면, 심각한 자원 낭비와 사용성 저하로 이어질 수 있겠죠. 따라서, 메인 쓰레드를 block하지 않고 이런 작업들을 수행할 수 있는 방안이 필요합니다.

비동기 작업이란 무엇인가?

  • 다행히 자바스크립트 엔진은, 긴 시간이 소요되는 작업들을 메인 쓰레드에서 처리하지 않습니다. Node.js의 경우, libuv라는 C++ 라이브러리를 통해, 브라우저의 경우 Fetch와 같은 웹 API를 통해 백그라운드에서 처리하죠. 이렇게 자바스크립트의 메인 쓰레드가 아닌, 백그라운드에서 처리하는 작업을 "비동기 작업"이라고 부르는 것입니다.

왜 C++ 라이브러리를 사용하는가?

  • 그럼 여기서 한 가지 의문이 들 수 있죠. 백그라운드라는 것은 무엇이며, 왜 자바스크립트를 실행하는데 C++ 라이브러리를 사용하는가? 이를 이해하기 위해선, "자바스크립트"와 "자바스크립트 실행 환경"을 구분하는 것이 중요합니다. "자바스크립트 실행 환경"인 Nodejs와 브라우저는 자바스크립트만으로 이루어져 있지 않습니다. 자바스크립트만으로 프로그램의 기본적인 흐름은 잡을 수 있지만, 한계가 있기 때문이죠. 여기에는 single thread blocking 이슈를 비롯하여, 파일 시스템 접근과 OS 수준의 작업이 자바스크립트만으로는 불가능하다는 문제도 있습니다. 이 한계를 보완하기 위하여 자바스크립트 실행 환경 내에 자바스크립트로 접근 가능한 여러 기능들을 구현해 놓은 것입니다. 비동기 처리도 이 기능들 중 하나구요. 집에 TV나 냉장고가 없다고 해서 사람이 살 수 없는 것은 아니지만, 있으면 삶의 질이 확실히 올라가죠? 여기서 자바스크립트의 메인 쓰레드를 사람에, libuv와 웹 API를 가전 제품에 비유해볼 수 있습니다.

Callback 함수란 무엇인가?

  • 다시 본론으로 돌아와, 메인 쓰레드에서 "비동기 함수"를 만나면 실제 처리를 백그라운드로 위임한다고 설명드렸습니다. 당연하게도, 백그라운드에서 작업이 완료되면, 실패가 됐든, 성공이 됐든 메인 쓰레드가 결과를 알 수 있어야합니다. 왜냐하면 메인 쓰레드에서 요청한 작업이니까요. 동시에, 메인 쓰레드 입장에서 비동기 작업이 성공했을 때는 이런 처리를, 실패했을 때는 저런 처리를 하고 싶다는 요구가 있을 수 있습니다. 그래서 우리는 비동기 함수를 호출할 때, 처리 완료 시 메인 쓰레드에서 실행할 함수를 함께 전달하는데, 이를 callback함수라고 합니다. 웬만한 비동기 함수들은 callback 함수를 인자로 받을 수 있게 설계되어 있고, 보통 마지막 인자로 전달하게끔 되어있으니, 각 함수별 스펙을 참고하시면 되겠습니다. 정리하자면, "비동기 작업이 완료되었을 때 메인 쓰레드에서 실행할 함수"를 callback 함수라고 부른다는 것입니다.

이벤트 루프의 필요성

  • callback 함수는 백그라운드로 잠시 전출간 메인 쓰레드 식구입니다. 즉, 언젠가는 다시 메인 쓰레드 안으로 들어와야 한다는 것이죠. 그런데, 메인 쓰레드와 백그라운드는 별도의 프로세스이기 때문에, callback함수가 들어와야 하는 시점이 애매해질 수 있습니다. 메인 쓰레드에서 다른 연산이 진행 중일 수 있기 때문인데요, 따라서, 이 callback함수가 들어올 시점을 정해주는 체계가 필요하며, 이 역할을 담당하는 것이 바로 Event loop입니다.

Event Loop의 동작 원리

  • 메인 쓰레드에서 실행되는 모든 함수들은 Call Stack이라는 공간에 LIFO 형태로 차곡차곡 쌓이게 됩니다. 함수가 호출되면 Call Stack에 push되고, return 문을 만나면 pop되는 단순한 구조이죠. 만약 Call Stack에 비동기 함수가 들어오면 즉시 pop하여 백그라운드로 전달합니다. 비동기 함수가 백그라운드에서 처리되는 한편, 동시에 Call Stack에서는 다음 함수가 계속해서 실행되고 있기 때문에 single thread blocking 문제를 해결할 수 있습니다. 이후, 백그라운드에서 비동기 처리가 완료되면, 호출 시점에 전달한 callback함수가 Event Queue라는 공간에 FIFO 구조로 쌓이게 됩니다. 여기서 이벤트 루프가 등장하는데, 간단히 말하자면 Event Queue에서 Call Stack으로 callback 함수를 이동시켜주는 역할을 합니다. 이벤트 루프는 call stack이 완전히 비어있는지 수시로 확인하며, 비어있는 경우 event queue에서 callback함수를 shift한 다음, call stack에 push해주는 역할을 수행합니다. Event Queue와 Call Stack 사이에 존재하는 일종의 신호등이죠.
  1. 모든 함수 호출들은 call stack에 LIFO구조로 쌓인다.
  2. 비동기 함수는 Call stack에 들어오는 즉시 백그라운드로 보내진다.
  3. 백그라운드에서 처리가 완료되면 callback 함수들은 event queue에 FIFO구조로 쌓인다. 1. Event Loop는 Call Stack이 비었는지 수시로 확인한다.
  4. Call Stack이 비어있을 경우, Event Loop는 Event queue에서 callback 함수를 shift한다.
  5. shift 된 Callback 함수는 Call stack으로 옮겨진 후 실행한다.

React에서 key값

React에서 사용되는 key는 무엇인지 설명하세요

  • Key는 리스트를 매핑하여 동일한 컴포넌트를 여러 개 렌더링할 때 각 컴포넌트에 전달되는 고유한 값입니다. 재조정 단계에서 각 노드의 key 값들을 비교하여 리스트에 추가, 삭제, 혹은 순서가 변경된 노드를 식별하고, 이를 통해 필요한 re-rendering만 수행하기 위해 사용합니다.

배열의 index를 key값으로 하면 안되는 이유가 무엇인가요?

  • 배열의 인덱스를 key 값으로 사용하면 안 되는 이유는 순서가 변경되어도 동일한 key 값이 유지되기 때문입니다. A,B,C 순서로 rendering되어 있던 컴포넌트가 C,B,A 순서로 변경되어도 key값은 동일하기 때문에, 재조정 단계에서 re-rendering 대상으로 식별하지 않을 가능성이 생깁니다. 따라서, 배열 내에서 순서가 변경되어도 각 컴포넌트의 key값이 변경되지 않는 고유한 값으로 설정해야 합니다.

고유한 속성값을 주기 어려울 때는 어떤 값으로 대체할 수 있나요?

  • 컴포넌트에 전달되는 각 props를 적절히 조합하여 서로 겹치지 않는 id를 만드는 것이 효과적입니다. 이런 방식으로도 고유한 id를 할당하기 어려운 경우에는, 랜덤한 UUID를 생성하여 key값으로 전달할 수 있습니다. UUID는 128비트로 이루어져 있기 때문에, 서로 겹칠 가능성이 극히 적기 때문입니다. 하지만, UUID는 16바이트라는 비교적 큰 사이즈를 갖기 때문에, 메모리 공간을 효율적으로 사용할 수 없다는 단점이 있습니다.

Virtual Dom

Virtual DOM의 개념에 대해 설명하세요

  • Virtual DOM은 웹 성능을 최적화하기 위해 사용되는 DOM 관리 방법으로, 웹 어플리케이션의 상태 변경 시, 객체 형태의 가상 DOM을 통해 변경된 부분만 찾아내어 이를 실제 DOM에 적용하는 기능을 합니다. Virtual DOM의 동작 순서는 Diffing과 Reconiliation, 크게 두 가지로 구분할 수 있는데, Diffing이란, Virtual DOM에서 변경점을 찾아내는 과정을 의미하며, Reconciliation이란, 찾아낸 변경점을 실제 DOM에 적용하는 과정을 의미합니다.

Virtual DOM이 동작하는 예시를 간략히 설명해주세요

  • 먼저, 어플리케이션이 제일 처음 rendering 될 때, 어플리케이션의 초기 상태를 담은 Virtual DOM을 메모리 상에 하나 생성합니다. 이후, 어플리케이션이 실행되면서 state나 props가 변경된 부분이 있는 경우, 새로운 버전의 Virtual DOM을 메모리 상에 하나 더 생성합니다. 새로운 버전의 Virtual DOM이 생성된 후, 이전 버전의 Virtual DOM과 비교하는 과정인 Diffing에 돌입하고, 변경점을 찾아냅니다. 이 과정에서 두 Virtual DOM 트리의 각 노드를 비교하여 어떤 부분이 변경되었는지 확인합니다. 변경점을 찾아낸 이후에는, 실제 DOM에 적용하는 과정인 Reconciliation에 돌입합니다. 이 과정에서 변경된 부분만 실제 DOM에 업데이트하기 때문에, 브라우저 성능이 향상될 수 있는 것입니다. Reconciliation이 완료된 이후, 또 다른 변경점이 생기면, 구 버전의 Virtual DOM이 폐기되고, 새로운 변경 사항을 반영한 최신 버전의 Virtual DOM이 다시 생성됩니다.

그럼 state나 props가 변경될 때마다 Diffing과 Reconciliation이 수행되는건가요?

  • React를 비롯하여 Virtual DOM을 사용하는 대부분의 프레임워크에서는 Batch 업데이트를 지원하고 있습니다. 따라서, 짧은 시간 안에 여러 개의 state와 props가 동시에 변경되면, 이를 각각 처리하는 것이 아니라, 한꺼번에 모아서 처리합니다.

Virtual DOM을 사용하는 것이 그렇지 않은 것보다 좋은가요?

  • 항상 그런 것은 아닙니다. 간단한 어플리케이션의 경우에는 Virtual DOM을 사용하는 것이 오히려 오버헤드를 초래할 수 있습니다. 왜냐하면 Virtual DOM 자체도 메모리 공간을 차지하고, Diffing하는 과정 역시 CPU를 활용하기 때문입니다. 다만, DOM 트리가 복잡하고, 상태 변경도 빈번하게 일어나는 대규모 어플리케이션에서 사람의 인지 능력으로는 정확히 어떤 DOM을 업데이트해야 하는지 식별하기 어렵기 때문에, Virtual DOM을 사용하는 것입니다. 따라서, 어플리케이션의 복잡도와 요구 사항에 맞게 Virtual DOM 적용 여부를 결정하는 것이 좋습니다.

Garbage Collection

무엇인가?

  • 더 이상 사용되지 않는 메모리를 해제하는 기법을 의미합니다. 더 이상 사용되지 않는 메모리를 주기적으로 확인하여 지워주는 작업을 진행하게 되는데 이를 Garbage Collection 이라고 합니다.

'메모리가 더 이상 사용되지 않는 메모리이다' 라는 것을 어떻게 판단하는지?

  • 일단 모든 것들의 처음이 되는 글로벌한 루트가 하나 있는데 현재 돌아가는 페이지인지 또는 어떤 환경에서라도 런타임 시발점이 있을 것인데 이것을 루트라고 합니다. 시발점에서 할당되는 모든 메모리는 루트에 연결됩니다.
  1. Refence Count
    • 이 때 각각 노드에 연결하는 되는 선(Reference)이 있을 텐데 노드 중 연결이 안되어있는 노드를 확인하여 주기적으로 삭제합니다. 하지만 치명적인 문제점이 서로가 순환참조가 일어난다면 사용은 안하지만 서로가 연결이 되어있어 Garbage Collection을 할 수가 없습니다.
  2. Mark and Sweep
    • 루트 노드부터 시작하여 순차적으로 노드를 접근하여 루트 노드에서 연결이 되어있는 노드에는 마크를 하고 마크가 안되어있는 노드를 삭제하는 방법

Authorization과 Authentication의 차이점을 이야기해보세요

  • IAM, Identity Access Management, 신원 접근관리
    • 단어 그대로 신원을 확인하여 누가 접근할 수 있고 누가 접근 못하는지 관리하는 개념이다.
  • Authentication
    • 허가/관리자 승인
    • 유저가 이 시스템에 접근할 수 있는지 자체를 결정하는 과정이다.
  • Authorization
    • 입증
    • 유저가 Authentication을 거쳐서 들어온 후에 어떤 작업을 할 수 있는지 판단하는 과정이다.
profile
무언가 만드는 것을 좋아합니다

0개의 댓글

관련 채용 정보