JavaScript
목차
- JavaScript란
- JavaScript의 핵심 특징
- 스코프와 클로저
- 비동기 모델과 이벤트 루프
- Promise와 async/await
- 백엔드 개발 관점에서 중요한 포인트
- 코드 예시
- 면접 포인트
- 참고 자료
JavaScript란
JavaScript 는 웹 브라우저와 서버 환경(Node.js)에서 널리 사용되는 범용 프로그래밍 언어입니다. 문법은 비교적 가볍지만, 프로토타입 기반 객체 모델, 클로저, 이벤트 루프, 비동기 프로그래밍 모델까지 함께 이해해야 실제로 제대로 다룰 수 있습니다.12
백엔드 면접에서는 “프론트엔드 언어”라고만 설명하기보다, 이벤트 루프 기반의 비동기 처리 모델을 가진 언어로 설명하는 편이 좋습니다.
JavaScript의 핵심 특징
- 동적 타입: 변수 선언 시 타입을 고정하지 않으며, 런타임에 값의 형태가 결정됩니다.
- 함수 일급 객체: 함수를 값처럼 전달하고 반환할 수 있습니다.
- 프로토타입 기반 객체 모델: 클래스 문법이 있어도 내부적으로는 프로토타입 체계를 사용합니다.2
- 단일 스레드 실행 모델: 하나의 실행 흐름 위에서 이벤트 루프와 비동기 API를 조합해 동시성을 다룹니다.3
- 광범위한 실행 환경: 브라우저, Node.js, 서버리스 런타임 등 다양한 환경에서 사용됩니다.
JavaScript의 장점은 생산성과 생태계이지만, 그만큼 암묵적 형변환, 실행 순서, 비동기 흐름을 정확히 이해하는 것이 중요합니다.
스코프와 클로저
JavaScript는 렉시컬 스코프(Lexical Scope) 를 사용합니다. 즉, 함수가 어디서 호출되었는지가 아니라 어디서 선언되었는지에 따라 접근 가능한 변수가 결정됩니다.
var: 함수 스코프let,const: 블록 스코프- 클로저: 함수가 선언될 당시의 렉시컬 환경을 함께 유지하는 특성4
클로저는 단순 면접 정의보다, “상태를 캡슐화하는 데 쓰이는 함수”로 연결해서 설명하면 좋습니다.
function createCounter() {
let count = 0;
return function () {
count += 1;
return count;
};
}
const next = createCounter();
console.log(next()); // 1
console.log(next()); // 2
위 예시에서 내부 함수는 외부 함수의 count를 계속 참조합니다. 이것이 클로저의 대표 예시입니다.
비동기 모델과 이벤트 루프
JavaScript는 보통 단일 스레드로 코드를 실행하지만, I/O나 타이머 같은 비동기 작업은 런타임이 처리한 뒤 콜백이나 태스크를 다시 실행 큐로 넘깁니다.3
이때 중요한 개념은 다음과 같습니다.
- Call Stack: 현재 실행 중인 함수 스택
- Task Queue / Callback Queue: 타이머, I/O 완료 후 실행될 작업 대기열
- Microtask Queue: Promise 후속 처리처럼 더 우선순위가 높은 작업 대기열
- Event Loop: 스택이 비면 큐의 작업을 실행 흐름으로 넣는 메커니즘
면접에서는 “JavaScript는 싱글 스레드인데 어떻게 비동기를 처리하나요?” 같은 질문이 자주 나옵니다. 이때 런타임 + 큐 + 이벤트 루프 구조로 답하면 됩니다.
Promise와 async/await
콜백만으로 비동기 흐름을 관리하면 중첩이 깊어지고 에러 처리도 불편해지기 쉽습니다. Promise는 비동기 작업의 완료 상태를 표현하고, async/await는 이를 동기 코드처럼 읽기 쉽게 만들어 줍니다.56
- Promise: 성공(
fulfilled), 실패(rejected), 대기(pending) 상태를 가집니다. then/catch: 후속 처리와 에러 처리를 연결합니다.async/await: Promise 기반 비동기 코드를 더 읽기 쉽게 표현합니다.
중요한 점은 await가 “블로킹”이 아니라, 현재 async 함수의 실행을 일시 중단하고 Promise가 끝난 뒤 이어서 실행하는 문법이라는 것입니다.
백엔드 개발 관점에서 중요한 포인트
- I/O 중심 작업에 유리: 파일, 네트워크, API 호출처럼 I/O 비중이 큰 서버 작업에 잘 맞습니다.
- CPU 바운드 작업은 주의: 무거운 계산은 이벤트 루프를 오래 점유해 전체 응답성을 떨어뜨릴 수 있습니다.
- 에러 흐름 관리: 비동기 호출 체인이 길어질수록 에러 전파 전략이 중요합니다.
- 타입 안정성 보완: 규모가 커질수록 TypeScript 같은 보완 수단을 함께 쓰는 경우가 많습니다.
- 실행 순서 이해: Promise,
setTimeout,process.nextTick같은 동작 차이를 이해해야 운영 중 디버깅이 수월합니다.
즉, 백엔드에서 JavaScript를 설명할 때는 문법보다 이벤트 루프, 비동기 I/O, CPU 작업 한계를 같이 묶어 말하는 편이 좋습니다.
코드 예시
function delay(ms) {
return new Promise((resolve) => {
setTimeout(resolve, ms);
});
}
async function processJob(name) {
console.log(`start: ${name}`);
await delay(100);
console.log(`done: ${name}`);
}
async function main() {
await Promise.all([processJob("email"), processJob("billing")]);
console.log("all jobs completed");
}
main().catch((error) => {
console.error("job failed", error);
});
이 코드는 Promise와 async/await를 사용해 비동기 작업을 병렬로 처리하는 기본 예시입니다.
면접 포인트
- JavaScript는 동적 타입, 함수 일급 객체, 프로토타입 기반 객체 모델을 가진 언어입니다.
- 백엔드 관점에서는 이벤트 루프와 비동기 I/O 모델을 이해하고 설명하는 것이 중요합니다.
- 클로저는 함수가 선언될 당시의 렉시컬 환경을 유지하는 특성으로 설명하면 됩니다.
- Promise와
async/await는 비동기 흐름을 구조화하는 도구이며, CPU 작업을 빠르게 만들어 주는 기능은 아닙니다. - Node.js 서버에서는 긴 CPU 작업이 이벤트 루프를 막을 수 있다는 점까지 같이 말하면 답변이 좋아집니다.