Node.js 주니어 기술면접
새 면접Q. Node.js에서 Buffer 객체의 용도와 필요성을 설명하고, 문자열과 Buffer의 차이점을 설명해주세요. 어떤 상황에서 Buffer를 사용해야 하나요?
바이너리 데이터 처리와 문자 인코딩 관점에서 생각해보세요.
Buffer는 Node.js에서 바이너리 데이터를 다루기 위한 객체입니다. 문자열은 텍스트 데이터만 다루지만, Buffer는 이미지, 비디오, 파일 등 원시 바이너리 데이터를 메모리에 저장하고 조작할 수 있습니다. 파일 시스템 작업, 네트워크 통신, 암호화 작업 등에서 Buffer를 사용합니다. Buffer.from()으로 문자열을 Buffer로 변환할 수 있으며, toString() 메서드로 다시 문자열로 변환할 수 있습니다. 스트림 처리나 TCP 소켓 통신에서 데이터를 주고받을 때 필수적으로 사용됩니다.
- • 바이너리 데이터 처리를 위한 객체
- • 문자열은 텍스트만 다루지만 Buffer는 원시 데이터 처리
- • 파일, 네트워크, 암호화 작업에서 필수
이미지 업로드 API에서 multipart 데이터를 처리하거나 파일을 암호화할 때 사용됩니다.
Buffer의 메모리 할당 방식과 Buffer Pool에 대해 설명해주세요.
Q. 데이터베이스에서 트랜잭션(Transaction)의 ACID 속성을 각각 설명하고, Node.js에서 여러 테이블에 데이터를 저장할 때 트랜잭션을 사용해야 하는 이유를 설명해주세요.
원자성, 일관성, 격리성, 지속성의 의미와 데이터 무결성 관점에서 생각해보세요.
ACID는 트랜잭션의 4가지 핵심 속성입니다. Atomicity(원자성)은 모든 작업이 완전히 수행되거나 아예 수행되지 않음을 보장합니다. Consistency(일관성)은 트랜잭션 전후로 데이터베이스가 일관된 상태를 유지함을 의미합니다. Isolation(격리성)은 동시에 실행되는 트랜잭션들이 서로 영향을 주지 않도록 격리됩니다. Durability(지속성)은 커밋된 트랜잭션은 시스템 장애가 발생해도 영구적으로 반영됨을 보장합니다. 예를 들어 주문 생성 시 주문 테이블과 재고 테이블을 동시에 업데이트할 때, 한쪽만 성공하면 데이터 불일치가 발생하므로 트랜잭션으로 묶어 처리해야 합니다.
- • Atomicity: 전부 성공 또는 전부 실패
- • Consistency: 데이터 일관성 유지
- • Isolation: 트랜잭션 간 격리
- • Durability: 영구적 반영
- • 여러 테이블 작업 시 데이터 무결성 보장
결제 시스템에서 주문 생성, 재고 차감, 포인트 적립을 하나의 트랜잭션으로 처리합니다.
Node.js에서 Sequelize나 TypeORM을 사용할 때 트랜잭션을 어떻게 구현하나요?
Q. Node.js API 서버에서 응답 시간이 평소보다 3배 느려졌다는 모니터링 알림을 받았습니다. 어떤 순서로 원인을 진단하고 해결할 것인지 단계별로 설명해주세요.
로그 분석, 데이터베이스 쿼리, 외부 API 호출, 서버 리소스 순서로 접근해보세요.
먼저 애플리케이션 로그와 APM 도구를 확인하여 어떤 엔드포인트에서 지연이 발생하는지 파악합니다. 해당 엔드포인트의 데이터베이스 쿼리 실행 시간을 확인하고 Slow Query Log를 분석합니다. 외부 API 호출이 있다면 타임아웃이나 응답 지연 여부를 확인합니다. 서버의 CPU, 메모리, 디스크 I/O 사용률을 모니터링하여 리소스 부족 여부를 점검합니다. 데이터베이스 커넥션 풀이 고갈되었는지 확인하고, 필요하다면 인덱스 추가나 쿼리 최적화를 진행합니다. 근본 원인을 찾은 후 재발 방지를 위해 모니터링 알림 임계값을 조정하고 부하 테스트를 수행합니다.
- • 로그와 APM으로 병목 지점 파악
- • 데이터베이스 쿼리 성능 분석
- • 외부 API 응답 시간 확인
- • 서버 리소스 사용률 점검
- • 커넥션 풀 상태 확인
트래픽이 급증하는 프로모션 기간에 성능 저하를 빠르게 진단하고 대응해야 합니다.
데이터베이스 쿼리가 원인이라면 어떤 최적화 방법들을 적용할 수 있나요?
Q. JavaScript에서 null과 undefined의 차이점을 설명하고, 각각 어떤 상황에서 발생하는지 예시를 들어주세요. typeof 연산자로 확인했을 때 결과는 어떻게 다른가요?
의도적 부재와 선언만 된 상태의 차이를 생각해보세요.
undefined는 변수가 선언되었지만 값이 할당되지 않았을 때 자동으로 할당되는 값입니다. 함수에서 return 문이 없거나 객체에 존재하지 않는 속성에 접근할 때도 undefined가 반환됩니다. null은 개발자가 의도적으로 값이 없음을 명시할 때 사용하는 값입니다. typeof undefined는 'undefined'를 반환하지만, typeof null은 'object'를 반환하는 JavaScript의 오래된 버그가 있습니다. 변수 초기화 시 명시적으로 빈 값을 표현하고 싶다면 null을 사용하는 것이 좋습니다.
- • undefined는 자동 할당, null은 의도적 할당
- • 선언만 된 변수는 undefined
- • typeof null은 'object' 반환
- • 빈 값 명시는 null 사용 권장
API 응답에서 선택적 필드가 없을 때 null로 표현하여 명시적으로 처리합니다.
null 체크를 할 때 ==와 ===의 동작 차이는 무엇인가요?
Q. Express.js에서 라우터(Router)를 분리하여 모듈화하는 이유와 방법을 설명하고, express.Router()를 사용하여 /api/users 경로를 처리하는 라우터를 구성할 때 어떤 구조로 작성해야 하는지 설명해주세요.
코드 재사용성과 유지보수성 관점에서 생각해보세요.
라우터를 분리하면 코드의 가독성과 유지보수성이 향상되고, 도메인별로 코드를 구조화할 수 있습니다. express.Router()를 사용하여 독립적인 라우터 인스턴스를 생성하고, 각 경로에 대한 핸들러를 정의합니다. 예를 들어 users.js 파일에서 router.get('/', ...), router.post('/', ...)로 라우트를 정의한 후 module.exports로 내보냅니다. 메인 app.js에서는 app.use('/api/users', userRouter)로 마운트하여 /api/users 경로에 연결합니다. 이렇게 하면 각 도메인의 라우팅 로직을 별도 파일로 관리할 수 있고, 미들웨어도 라우터별로 적용할 수 있습니다.
- • 코드 모듈화와 유지보수성 향상
- • express.Router()로 독립적 라우터 생성
- • 도메인별 파일 분리
- • app.use()로 라우터 마운트
대규모 API 서버에서 사용자, 상품, 주문 등 도메인별로 라우터를 분리하여 관리합니다.
라우터별로 다른 인증 미들웨어를 적용하려면 어떻게 해야 하나요?
Q. Node.js에서 단위 테스트(Unit Test)와 통합 테스트(Integration Test)의 차이를 설명하고, Express.js API를 테스트할 때 각각 어떤 것을 테스트해야 하는지 예시를 들어주세요.
테스트 범위와 의존성 격리 관점에서 생각해보세요.
단위 테스트는 개별 함수나 모듈을 독립적으로 테스트하며, 외부 의존성을 모킹(Mocking)하여 격리된 환경에서 실행합니다. 예를 들어 비즈니스 로직 함수가 올바른 값을 반환하는지 테스트합니다. 통합 테스트는 여러 모듈이 함께 동작하는지 확인하며, 실제 데이터베이스나 외부 서비스와 연동하여 테스트합니다. Express API에서는 컨트롤러 함수의 로직을 단위 테스트하고, supertest 같은 라이브러리로 HTTP 요청부터 데이터베이스 저장까지 전체 플로우를 통합 테스트합니다. 단위 테스트는 빠르고 안정적이며, 통합 테스트는 실제 환경과 유사한 시나리오를 검증합니다.
- • 단위 테스트: 개별 함수 독립 테스트, 의존성 모킹
- • 통합 테스트: 여러 모듈 연동 테스트
- • 단위는 빠르고 안정적, 통합은 실제 시나리오 검증
CI/CD 파이프라인에서 단위 테스트로 빠른 피드백을 받고, 통합 테스트로 배포 전 최종 검증합니다.
테스트 커버리지는 어떻게 측정하고, 몇 퍼센트를 목표로 해야 하나요?
Q. Node.js에서 비밀번호를 데이터베이스에 저장할 때 평문으로 저장하면 안 되는 이유를 설명하고, bcrypt 같은 해싱 라이브러리를 사용하는 이유와 salt의 역할을 설명해주세요.
단방향 암호화와 레인보우 테이블 공격 방어를 생각해보세요.
비밀번호를 평문으로 저장하면 데이터베이스가 유출될 경우 모든 사용자 계정이 즉시 노출됩니다. bcrypt는 단방향 해싱 알고리즘으로, 해시값에서 원본 비밀번호를 복원할 수 없습니다. salt는 각 비밀번호마다 무작위 값을 추가하여 같은 비밀번호라도 다른 해시값을 생성하게 만듭니다. 이를 통해 레인보우 테이블 공격(미리 계산된 해시값 테이블)을 방어할 수 있습니다. bcrypt는 의도적으로 느린 알고리즘을 사용하여 brute-force 공격을 어렵게 만듭니다. 회원가입 시 bcrypt.hash()로 해싱하고, 로그인 시 bcrypt.compare()로 비밀번호를 검증합니다.
- • 평문 저장 시 유출 위험
- • bcrypt는 단방향 해싱
- • salt로 같은 비밀번호도 다른 해시값 생성
- • 레인보우 테이블 공격 방어
- • 의도적으로 느린 알고리즘
회원가입 API에서 사용자 비밀번호를 bcrypt로 해싱하여 안전하게 저장합니다.
bcrypt의 cost factor(rounds)는 무엇이며, 어떻게 설정해야 하나요?
Q. HTTP 상태 코드 200, 201, 400, 401, 404, 500의 의미를 각각 설명하고, Node.js API에서 각 상태 코드를 반환해야 하는 상황을 예시로 들어주세요.
성공, 클라이언트 오류, 서버 오류로 구분하여 생각해보세요.
200 OK는 요청이 성공적으로 처리되었음을 의미하며, 데이터 조회 성공 시 사용합니다. 201 Created는 새로운 리소스가 생성되었음을 나타내며, 회원가입이나 게시글 작성 성공 시 반환합니다. 400 Bad Request는 잘못된 요청 형식이나 유효성 검증 실패 시 사용합니다. 401 Unauthorized는 인증이 필요하거나 인증 정보가 잘못되었을 때 반환합니다. 404 Not Found는 요청한 리소스가 존재하지 않을 때 사용하며, 존재하지 않는 사용자 조회 시 반환합니다. 500 Internal Server Error는 서버 내부 오류가 발생했을 때 사용하며, 예상치 못한 예외 상황에서 반환합니다.
- • 200: 성공
- • 201: 리소스 생성
- • 400: 잘못된 요청
- • 401: 인증 실패
- • 404: 리소스 없음
- • 500: 서버 오류
RESTful API 설계 시 적절한 상태 코드를 반환하여 클라이언트가 상황을 정확히 파악하게 합니다.
403 Forbidden과 401 Unauthorized의 차이는 무엇인가요?
Q. Node.js 애플리케이션을 프로덕션 환경에서 실행할 때 node app.js 대신 PM2 같은 프로세스 매니저를 사용하는 이유를 설명하고, PM2의 주요 기능을 설명해주세요.
프로세스 관리, 재시작, 로그, 클러스터링 관점에서 생각해보세요.
PM2는 Node.js 애플리케이션의 프로세스를 관리하는 도구로, 애플리케이션이 예기치 않게 종료되면 자동으로 재시작합니다. 서버 재부팅 시에도 애플리케이션이 자동으로 시작되도록 설정할 수 있습니다. 로그를 자동으로 수집하고 관리하여 디버깅을 용이하게 합니다. 클러스터 모드를 지원하여 CPU 코어 수만큼 프로세스를 생성하고 로드 밸런싱을 수행합니다. 무중단 배포(reload)를 지원하여 서비스 중단 없이 새 버전을 배포할 수 있습니다. 모니터링 대시보드를 제공하여 CPU, 메모리 사용량을 실시간으로 확인할 수 있습니다.
- • 자동 재시작으로 가용성 향상
- • 클러스터 모드로 멀티 코어 활용
- • 무중단 배포 지원
- • 로그 관리 및 모니터링
프로덕션 서버에서 PM2로 애플리케이션을 관리하여 안정성과 성능을 확보합니다.
PM2의 클러스터 모드와 fork 모드의 차이는 무엇인가요?
Q. 개발 중 기술적으로 어려운 문제에 부딪혀 혼자 해결하기 어려웠던 경험을 말씀해주세요. 어떤 방법으로 문제를 해결했으며, 그 과정에서 무엇을 배웠나요?
문제 상황, 시도한 방법, 도움 요청 과정, 결과와 배운 점을 구조화하여 답변하세요.
STAR 기법으로 답변을 구성합니다. Situation: 어떤 프로젝트에서 어떤 기술적 문제가 발생했는지 구체적으로 설명합니다. Task: 본인이 맡은 역할과 해결해야 할 과제를 명확히 합니다. Action: 공식 문서 검색, 스택오버플로우 조사, 동료나 시니어에게 도움 요청, 코드 리뷰 등 시도한 방법들을 순서대로 설명합니다. Result: 문제를 어떻게 해결했고, 프로젝트에 어떤 긍정적 영향을 주었는지 설명합니다. 배운 점으로는 문제 해결 접근법, 커뮤니케이션의 중요성, 새로운 기술 지식 등을 언급합니다. 비슷한 문제를 예방하기 위해 어떤 조치를 취했는지도 추가하면 좋습니다.
- • STAR 기법으로 구조화
- • 구체적인 문제 상황 설명
- • 시도한 해결 방법 나열
- • 결과와 배운 점 명확히
- • 재발 방지 노력 언급
실무에서는 혼자 모든 것을 해결할 수 없으며, 적절한 타이밍에 도움을 요청하는 것이 중요합니다.
그 경험 이후 비슷한 문제에 더 효과적으로 대응할 수 있게 되었나요?
아직 댓글이 없습니다. 첫 번째 댓글을 남겨보세요!