Q1. call by value와 call by reference에 대해 설명
A: 함수 호출 시 매개변수를 전달하는 방법 두 가지
자세한 설명 참조: https://velog.io/@elma98/TIL-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8%EC%97%90%EC%84%9C%EC%9D%98-Call-by-value-Call-by-reference
Q2. message queue란 무엇인가
A: 메세지 큐(Message Queue)란 프로세스 간에 데이터를 교환할 때 사용되는 통신 방법 중에 하나이다. 더 큰 개념으로는 MOM(Message Oriented Middleware: 메세지 지향 미들웨어)를 의미한다. MOM이란 비동기 메세지를 사용하는 프로그램 간 데이터 송수신을 의미하며, MOM을 구현한 서비스를 MQ라고 한다.
메시지 큐는 메시지를 임시로 저장하는 간단한 버퍼라고 생각하면 된다. 메시지를 전송 및 수신하기 위해 중간에 메시지 큐를 두는 것이다.(+답변 보충 필요)
Q3. 자바스크립트의 이벤트 루프에 대해 아는대로 설명
A: 자바스크립트 엔진에는 호출스택과 힙이 존재한다. 힙은 런타임 중 메모리 할당이 일어나는 곳이고 호출스택은 런타임 중 실행될 함수들이 쌓이는 곳이며 LIFO구조로 후입선출 구조이다.
함수가 호출된 순서대로 아래에서 위로 차곡차곡 쌓이고 위에서부터 아래로 하나씩 순차적으로 실행된다.
그러나 비동기적으로 실행되는 함수(ex: setTimeOut)의 경우 브라우저의 webapi로 보내지고 여기서 호출된 순서대로 태스크큐로 실행시킬 콜백함수를 이동시킨다. 그리고 호출스택이 비면 태스크큐에 담긴 함수를 호출스택으로 올리고 호출스택에 올라간 함수가 실행되게 된다. 이렇게 코드가 실행되는 동안 계속 일어나는 자바스크립트의 비동기 처리 동작방식을 이벤트 루프라고 한다.
+) 자바스크립트의 태스크큐는 여러개가 존재하는데, 여러개의 태스크큐가 백그라운드에서부터 옮겨진 함수들의 실행 우선순위를 결정하여 호출스택으로 올려주는 역할을 한다.
Q3-1. 자바스크립트에서 blocking과 non-blocking이 어떤 의미인지
A: 느린 동작이 스택에 남아있는 것을 blocking이라고 한다. 만약 자바스크립트에 이벤트루프가 존재하지 않는다면 AJAX 요청과 같은 네트워크 요청을 보낸 후 응답이 올 때까지 다른 동작들을 실행하지 못한채 해당 요청이 끝날 때까지 기다리는 수밖에 없을 것이다. 그러나 자바스크립트와 node.js에는 이러한 blocking 현상이 일어나지 않도록 비동기적 작업을 수행하는 동작방식을 지원하며 이것이 이벤트 루프이다.
Q4. node.js가 왜 이런 이벤트 루프를 적용해서 복잡하게 비동기처리를 할 수밖에 없었는지
A: 자바스크립트가 싱글스레드 언어이기 때문이다. 싱글스레드란 호출 스택이 하나만 존재하는 실행환경을 의미하고 호출 스택이 하나면 한 번에 하나의 동작만 실행할 수 있다는 뜻이다. 자바스크립트는 따라서 기본적으로 동기적으로 실행되는 언어이지만 한 번에 여러 동작을 비동기적으로 수행해야 하는 경우에는 자바스크립트 엔진의 백그라운드에서 이벤트 루프를 동작시켜 비동기적인 작업들을 수행할 수 있게 한다.
Q5. node.js가 싱글스레드이고 이벤트 루프를 신경써야 한다는 점에서 개발자가 개발을 할 때 신경써야했던 부분이 있는지?
A: 블로킹되는 코드를 최대한 피하는 식으로 코딩했다. 어쩔 수 없이 블로킹이 된다고 하더라도 최대한 로직을 분리해서 하나의 연산이 지나치게 오래 걸리지 않도록 코딩하려 했다.
싱글스레드 에러가 나지 않도록 에러 처리를 철저히 했다.
Q6. node.js에 대해 아는대로 설명
A: 노드는 Chrome V8 Javascript 엔진으로 빌드된 자바스크립트 런타임이다. 런타임이란 특정 언어로 만든 프로그램들을 실행할 수 있는 환경이다.(+답변 추가 필요)
Q7. RESTful api로 회원가입을 설계한다면 어떻게 설계할 것인가? 그리고 그 이유는?
A: POST 도메인 이름/member(또는 account), RESTful api는 엔드포인트에 행위를 서술하지 않고 메소드+명사로 된 엔드포인트
만 가지고 어떤 동작을 수행하는 api인지 유추할 수 있어야 한다는 규칙을 가진 api 설계 방법이기 때문에 이와 같이 설계한다. POST 메소드는 데이터를 생성할 때 사용하는 메소드이기 때문에 엔드포인트의 member(또는 account)와 합쳐졌을 때 새로운 member를 생성한다는 뜻으로 유추가 가능하게 된다.
Q8. dependency inversion(의존관계 역전 원칙)이란 무엇인가
A:
Q1. “예측 정확도 300% 개선”에서 어떤 기능을 어떻게 개선하게 되었는지?
A: 카*
앱에는 차량관리탭이 존재하고 차량관리탭에는 고객 차량의 과거 엔진오일 교환시기, 차량 정기 점검시기 등을 바탕으로 현재 및 다음 소모품 교환 시기의 예상 주행거리를 계산하여 제공하고 다음 소모품 권장 교환 시기를 보여주는 기능이 존재합니다. 그 중에서도 다음 소모품 권장 교환 시기를 알려주고 해당 시기 차량의 예상 주행거리를 예측하는 기능이 기존에는 앞선 시점의 해당 차량의 과거 데이터들이 데이터베이스에 쌓이면 그 데이터만을 바탕으로 교환 예정 시기 알림에 필요한 데이터를 데이터베이스에서 생성하여 저장하고 있었기 때문에 중간에 고객이 엔진오일 등 소모품을 서버 로직이 예상할 수 없는 시기에 새롭게 교체를 하게 되면 해당 정보는 이미 생성되어 있는 교환 예정 시기에 관련한 데이터를 새롭게 업데이트하여 저장할 수 있도록 구성되어있지 않아서 교환 예정 시기에 대한 데이터가 정확하지 않고 변동 가능성이 높은 데이터인 반면에 실시간으로 데이터를 관리할 수 없어 아쉬움이 많은 기능이었습니다. 그러나 새롭게 재구성한 로직에서는 교환 예정 시기에 관한 데이터를 실시간으로 업데이트하고 관리할 수 있도록 데이터베이스에 따로 저장하지 않고 조회 api를 호출할 때 서버가 자체적으로 계산로직을 태워 매번 새롭게 업데이트된 교환 권장 시기에 대한 데이터를 보여줄 수 있도록 재구성하였습니다. 교환 권장 시기에 대한 데이터는 그 자체를 수정하거나 삭제할 일이 없는 데이터이며 단순히 batch를 활용하여 앱 푸시알림만 사용자에게 보내기만 하면 되는 데이터였기 때문에 이와 같이 로직을 구성하는 것이 가능하였습니다. 재구성한 로직은 실시간으로 변화하며 쌓이는 데이터를 모두 반영하여 주행거리 및 소모품 교환 권장 시기를 예측할 수 있기 때문에 기존보다 약 3배 정도 정확도를 높일 수 있었습니다.
Q1-1. 디비에서 매번 셀렉트해서 계산로직을 태우는 것이 서버에 부담을 많이 줄수도 있을 것 같은데 그런 점에서 문제점은 없었는지?
A: 사실 지금까지는 리뉴얼 앱이 출시된지 얼마 안되었기 때문에 각 사용자마다 차량관리 목록의 데이터 양 자체가 그리 많지 않다.(많아야 row 10개 정도) 그래서 현재로써는 서버 성능에 크게 영향을 주지 않는 수준이지만 향후 유지 및 보수를 하는 차원에서 각 사용자마다 데이터가 많아지게 되면 지금처럼 조회시마다 모든 배열을 순회하며 계산 로직을 태우는 것이 비효율적인 로직이 될 수 있기 때문에 교환 권장 시기에 대한 데이터(편의상 ‘미래카드’라 지칭)를 새롭게 쌓이게 되는 과거 이력에 대한 데이터가 생성될 때마다 데이터베이스에 함께 생성하여 저장하고 앞선 히스토리 데이터들이 추가로 쌓이고 수정될 때마다 ‘미래카드’까지 같이 수정할 수 있게 해당 로직을 따로 분리하여 해당 로직이 필요한 api에 추가로 삽입하고 조회시에는 기존처럼 계산로직을 태우는 방식이 아닌 select 쿼리 한 번으로 조회 가능하도록 구현하는 식으로 리팩토링을 진행해야 할 것 같다.
Q2. nestjs 내에서 트랜잭션 관리(데이터의 일관성)를 어떻게 했는지
A: 트랜잭션을 실행시키는(queryRunner.startTransaction()
) 최상단 함수에서 트랜잭션의 시작부터 끝까지 책임지도록 하는 디자인 패턴으로 구현이 되어 있었으며, 최상단 함수에서 필요에 의해 호출하는 함수들은 각각 옵셔널 파라미터로 엔티티매니저를 가지고 있도록 하여 만약 이 옵셔널 파라미터로 엔티티매니저의 트랜잭션이 시작된 상태로 들어오게 되면 현재 한 트랜잭션 내에서 함께 쿼리가 실행되고 있는 것으로 간주하게 되고 그렇지 않으면 단독으로 실행되는 것으로 간주하여 트랜잭션을 관리하였다. 최상단 함수에서 트랜잭션의 시작부터 끝까지 관장하기 때문에 트랜잭션 중간에 발생하는 db exception 또는 에러를 try catch 문을 사용하여 catch 블록에서 예외 분기처리 및 트랜잭션 롤백을 수행하였고 finally 블록에서 release하였다.
Q2-1. 트랜잭션 release는 왜 하는지 알고 있나?
A: db 커넥션을 생성하면 미리 생성된 커넥션풀에서 하나의 접근권한을 가져오고 다른 커넥션에서 접근할 수 없게 된다. 커넥션을 사용하면 다른사람이 사용할 수 있게 반납을 해야 한다. 단일 쿼리를 실행하는 경우 자동으로 커넥션을 가져와서 pool.query()
실행 후 커넥션을 반납한다.하지만 하나의 커넥션에서 여러 쿼리를 수행하고자 할때는 독립된 커넥션을 수동으로 생성 후 여러쿼리 실행 후 커넥션을 수동으로 반납해야 한다.
수동으로 커넥션을 가져왔는데 반납하지 않는다면 커넥션풀은 고갈되고 곧 사용되지는 않지만 사용할 수 없는 idle
커넥션만 남게 되어 deadlock
이 발생한다.
Q3. nest.js에서 데코레이터의 역할과 기능
A:
@
뒤의 expression
은 함수로, 데코레이터가 실행된다는 것은 데코레이터로 구현된 함수가 실행된다는 의미이다.Q4. nest.js를 사용할 때 요청으로 들어오는 데이터의 검증은 어떻게 했는지?
A: class-validator 데코레이터를 dto마다 적절히 적용하여 validation 하였다.
Q5. 데이터 검증시 클라이언트에서 잘못된 요청을 보내면 클라이언트는 어떤 에러를 받았는지?
A: 클라이언트로부터 알맞은 형태의 데이터가 들어오지 않으면 class-validator가 돌려주는 400 에러 객체를 반환하였다.