20240416

귤금·2024년 4월 16일

Node.js 4기 TIL

목록 보기
78/86

모의면접

첫번째 질문

Q) 호이스팅이란 무엇일까요?

A) 호이스팅(hoisting)은 JavaScript에서 변수 및 함수 선언을 현재 범위(scope)의 맨 위로 끌어올리는 것을 의미합니다.

console.log(x); // undefined
var x = 5;

위 코드에서 var x = 5; 라인 이전에 console.log(x);를 호출하고 있지만, 오류가 발생하지 않습니다. 이것은 JavaScript 엔진이 변수 선언(var x)을 현재 범위의 맨 위로 끌어올리기 때문이에요!
이러한 동작이 호이스팅이라고 불리며, 주로 var로 선언된 변수와 함수 선언문에 적용됩니다.

T : 그렇다면 let과 const에서는 호이스팅이 일어나지 않나요?
: 네, let과 const 키워드를 사용한 변수 선언은 호이스팅이 발생하지 않는 것으로 알고 있습니다.
T : 아닙니다. 실제로 let과 const로 선언된 변수에도 호이스팅이 발생합니다. 하지만 동작 방식이 var로 선언된 변수와 다르게 나타나기 때문에, 호이스팅이 발생하지 않는 것처럼 보일 수 있습니다.

var vs let/const

  • var: var로 선언된 변수는 호이스팅되며, 선언이 스코프의 최상단으로 끌어올려집니다. 초기화가 선언과 동시에 이루어지기 때문에, 변수에 접근하면 undefined가 반환됩니다.
  • let 및 const: 이 두 키워드로 선언된 변수도 호이스팅됩니다. 그러나 var와 달리, 초기화가 선언과 분리되어 실행됩니다. let과 const로 선언된 변수는 선언된 지점 이전에 접근하려고 하면 ReferenceError가 발생합니다. 일시적 사각지대(Temporal Dead Zone, TDZ)라고 불리는 기간 동안 변수가 초기화되지 않아 접근할 수 없기 때문입니다.

즉, let과 const 변수는 호이스팅이 일어나지만, TDZ로 인해 초기화 이전에는 접근할 수 없습니다. 이 특성 때문에 일부 개발자들은 let과 const가 호이스팅되지 않는 것처럼 착각할 수 있지만, 실제로는 스코프의 최상단으로 선언 자체가 끌어올려지는 호이스팅이 발생합니다. 이 점을 이해하는 것은 JavaScript의 변수 스코프와 선언 생명 주기를 정확하게 파악하는 데 매우 중요합니다.


두 번째 질문

Q) 자바스크립트는 싱글 스레드 언어입니다. 그러나 node.js에서 작업하면 두세 개 작업을 동시에 할 수 있는데, 이 원리가 뭘까요?

A) Node.js는 파일 시스템 작업, 네트워크 요청 등 대부분의 작업을 비동기로 처리합니다. 콜백(callback), 프로미스(promises), async/await 등의 기능으로 한 번에 여러 요청을 보내더라도 꼬이지 않고 순차적 처리가 되는데요, 비동기 작업을 통해서 동시에 여러 작업을 거의 동시에 처리할 수 있습니다.

T : 순차적 처리라고 하셨는데, 동시에 작업하는 것과는 약간 개념이 다른 것 같습니다. 그렇다면, 실행에 1초가 걸리는 함수를 100번 호출하면 100초의 시간이 걸리는 걸까요?
: 헉 그러게요...
T : 이벤트 루프에 대해 공부해보시면 좋을 것 같습니다! 아마 실제로 이렇게 100번 함수를 호출하면 1초 조금 넘는 시간밖에 걸리지 않을 거예요.

이벤트 루프(Event Loop)

  • Node.js의 핵심!
  • Node.js는 libuv 라이브러리를 기반으로 구현되어 있으며, libuv는 비동기 I/O 작업을 처리하기 위한 이벤트 루프(event loop)를 제공합니다. 이 이벤트 루프는 비동기 작업들(예: 파일 I/O, 네트워크 요청 등)을 관리하고, 작업이 완료되면 해당 작업에 연결된 콜백 함수를 실행시킵니다. 이벤트 루프는 싱글 스레드에서 실행되지만, I/O 작업은 실제로는 Node.js의 백그라운드에서 비동기적으로 처리됩니다. 이렇게 함으로써, 이벤트 루프가 일어나는 동안에 다른 작업을 동시에 계속 진행할 수 있습니다.

세 번째 질문

Nest.js에서 ORM을 왜 쓰는 걸까요?

A) ORM은 데이터베이스와 객체 간의 매핑을 자동으로 처리해준다고 알고 있습니다. 그래서 객체 지향적인 코드를 작성할 때 유용합니다. 그리고 ORM에서 가장 중요한 점은 캡슐화라고 알고 있는데요, DB 관련 로직을 캡슐화해서 조금 더 유연하고 재사용성 높은 코드를 짤 수 있다는 게 장점 같습니다.

T : 그럼 express와 같은 프레임워크를 사용한다면 매핑이 자동으로 이루어지지 않을까요?
: 네! express를 사용할 땐 DB와 매핑하는 코드를 직접 작성해줘야 합니다.
T : ORM에서 캡슐화가 중요하다는 말은 좋은 접근입니다. 그럼, TypeORM에서 쿼리 빌더를 사용해보셨나요? 쿼리 빌더를 사용할 때와 아닐 때의 차이점은 무엇일까요?

A) 쿼리 빌더를 사용한 적도 있고, TypeORM의 메소드를 사용하여 데이터베이스를 조작한 적도 있습니다. ORM의 메소드를 사용하면 간단하고 직관적으로 데이터베이스에 접근할 수 있습니다. 다만 복잡한 쿼리를 수행하기 어렵기 때문에, 이런 경우 쿼리 빌더를 사용하여 조금 더 세부적으로 데이터를 컨트롤하도록 합니다.

T : 대체로 맞습니다. 메소드를 사용해서 복잡한 쿼리를 수행하는 것도 가능은 합니다. 다만 성능과 가독성이 몹시 떨어집니다. 쿼리 빌더는 메소드보단 복잡한 쿼리를 수행하기 용이하지만, 이것도 역시 너무 복잡한 쿼리를 설계할 때 쓰기에는 좋지 않기 때문에 복잡한 로직이 필요한 경우 RAW 쿼리를 사용하는 경우가 많습니다.

네 번째 질문

nest.js에서의 DI가 뭘까요?

A) 의존성 주입이라고 알고 있습니다. 객체가 직접 필요한 의존성을 만들어서 사용하는 게 아니라, 외부에서 주입된 의존성을 사용하는 패턴입니다.

T : 우리가 Express를 사용할 땐 의존성 주입을 외부에서 해 주지 않았었죠. 굳이 DI를 사용하지 않아도 되는데 왜 이런 방식을 사용할까요? 장점이 있나요?

A) 외부에서 주입받은 의존성을 사용하기 때문에 동일한 클래스나 모듈을 다른 곳에서도 재사용할 수 있습니다. 코드를 좀 더 유연하고 독립적으로 관리할 수 있어서 유지보수하기 편합니다.

T : 유연성을 향상시킨다는 대답이 좋습니다.

0개의 댓글