JavaScript는 싱글 스레드로 동작한다. 비동기 작업은 큐에 들어가며, 콜스택이 비면 마이크로태스크 → 매크로태스크 순서로 실행된다. Promise가 setTimeout보다 먼저 실행되는 이유다.
싱글 스레드 = JavaScript는 기본적으로 한 번에 하나의 코드만 실행한다.
Promise는 마이크로태스크 큐로 간다.
1. 동기 코드는 바로 실행된다.
console.log("1");
console.log("4");
그래서 먼저 1, 4가 출력된다.
2. Promise는 마이크로태스크 큐로 간다.
Promise.resolve().then(...)
Promise의 then은 바로 실행되지 않고, 마이크로태스크 큐에 등록된다.
3. setTimeout은 매크로태스크 큐로 간다.
setTimeout(...)
setTimeout은 시간이 0초여도 바로 실행되지 않는다.
콜스택이 비워진 뒤 매크로태스크 큐에서 실행 대기한다.
4. 콜스택이 비면 마이크로태스크를 먼저 처리한다.
JavaScript는 동기 코드 실행이 끝난 뒤,
먼저 마이크로태스크 큐를 전부 비운다.
그 다음 매크로태스크 큐를 처리한다.
그래서 Promise가 setTimeout보다 먼저 실행된다.
var 재선언 O / 재할당 O | 선언 + 초기화 자동 → 할당
let 재선언 X /재할당 O | 선언 → (자동 초기화) → 할당
const 재선언 X / 재할당 불가능X | 선언 + 초기화 + 할당 동시에 해야 함
const는 값을 고정하는 게 아니라 참조를 고정한다.
const obj = { a: 1 };
// 가능 (객체 내부 값 변경)
obj.a = 2;
// 가능 (속성 추가)
obj.b = 3;
// 불가능 (새 객체로 교체)
obj = { a: 10 }; // 에러
함수가 선언될 당시의 외부 스코프를 기억하는 구조다. 함수 실행이 끝난 이후에도 외부 변수에 접근할 수 있다. 상태 유지나 캡슐화에 활용된다.
function outer() {
let x = 10; // outer의 지역 변수
function inner() {
console.log(x); // inner는 x를 사용하고 있음 (참조 중)
}
return inner;
// inner 함수만 반환하는 것처럼 보이지만,
// 실제로는 x를 기억한 상태로 같이 나감 (클로저 형성)
}
const fn = outer();
// outer 실행 끝났지만,
// inner가 x를 참조하고 있어서 x가 사라지지 않고 유지됨
fn();
// inner 실행
// inner가 기억하고 있던 x 사용 → 10 출력
클로저 - 함수 + 그 함수가 기억하고 있는 외부 변수(x)가 같이 묶인 상태
this는 선언 위치가 아니라 호출 방식에 따라 결정된다. 일반 함수는 전역 객체, 메서드는 해당 객체, 생성자는 인스턴스를 가리킨다. bind/call/apply로 명시적 바인딩이 가능하다.
async/await은 Promise 기반의 문법적 설탕이다. 비동기 코드를 동기처럼 작성할 수 있어 가독성이 좋다. 에러 처리는 try/catch로 수행한다.
HTML 파싱 → DOM 생성
CSS 파싱 → CSSOM 생성
DOM + CSSOM → Render Tree 생성
Layout (Reflow) → 요소의 위치/크기 계산
Paint (Repaint) → 화면에 그리기
Repaint는 색상이나 스타일 변경으로 다시 그리는 작업이다. Reflow는 요소의 위치나 크기 변경으로 레이아웃 재계산이 발생하는 경우다. Reflow가 비용이 더 크다.
브라우저의 렌더링 타이밍에 맞춰 실행된다. 일반 타이머보다 자연스럽고 성능 효율적인 애니메이션 구현이 가능하다.
CSR은 클라이언트에서 렌더링을 수행한다. SSR은 서버에서 HTML을 생성한다. SSR이 초기 로딩과 SEO에 유리하다.
Q10. 호이스팅
변수와 함수 선언이 실행 전에 메모리에 먼저 올라가는 동작이다. var는 undefined로 초기화되고, let/const는 TDZ로 인해 선언 전 접근이 불가능하다.
Q11. 실행 컨텍스트
코드 실행 시 생성되는 환경으로 변수, 스코프, this 정보 등을 포함한다. 함수 호출 시마다 생성되며 콜스택에 쌓여 실행된다.
Q12. 얕은 깊은 복사 비교
얕은 복사는 겉의 객체의 값만 복사되고 내부 객체 값은 공유된다. 깊은 복사는 내부까지 모두 복사되어 완전히 독립된 값을 가진다.
얕은복사
const user = {
name: "Kim",
info: {
age: 30
}
};
const copy = { ...user };
copy.name = "Lee";
copy.info.age = 40;
console.log(user.name); // Kim
console.log(user.info.age); // 40
깊은복사
const user = {
name: "Kim",
info: {
age: 30
}
};
const copy = structuredClone(user);
copy.info.age = 40;
console.log(user.info.age); // 30
console.log(copy.info.age); // 40
얕은 비교는 값이 아니라 참조(주소)를 비교한다.
// 얕은비교
const a = { x: 1 };
const b = { x: 1 };
console.log(a === b); // false
const c = 1;
const d = 1;
console.log(c === d); // true
// 깊은비교
const e = { x: 1 };
const f = { x: 1 };
JSON.stringify(e) === JSON.stringify(f); // true
React의 setState는 얕은 비교로 변경 여부를 판단한다. 객체의 값만 수정하면 메모리 주소가 변경되지 않기 때문에 같은 값이라고 판단한다
Q13. 프로토타입이란
객체가 다른 객체의 속성과 메서드를 상속받을 수 있게 하는 구조다. 모든 객체는 프로토타입 체인을 통해 상위 객체를 참조한다.
Q14. 디바운스와 쓰로틀링
디바운스는 이벤트가 끝난 후 한 번 실행된다. 쓰로틀링은 일정 시간 간격으로 한 번씩 실행된다. 이벤트 과도 실행을 제어하기 위해 사용된다.
Q15. 메모리 관리
메모리는 크게 두 영역으로 나뉜다
Stack (스택) → 원시값, 실행 컨텍스트 const a = 10;
Heap (힙) → 객체, 배열, 함수 const obj = { x: 1 }
가비지 컬렉션 - 더 이상 참조되지 않는 값은 제거한다
JavaScript는 가비지 컬렉터(GC)를 통해 사용하지 않는 메모리를 자동으로 해제한다. 대표적으로 mark-and-sweep 방식이 사용된다.
mark (표시)
전역 객체부터 시작해서 접근 가능한 값들을 모두 표시
sweep (정리)
표시되지 않은 값들을 메모리에서 제거
Q16. undefined vs null
undefined는 값이 할당되지 않은 상태다. null은 의도적으로 비어있음을 나타내는 값이다.
Q17. call stack overflow
함수 호출이 과도하게 쌓여 콜스택 한계를 초과한 상태다. 주로 재귀 호출 종료 조건이 없을 때 발생한다.
await a();
await b();
await Promise.all([a(), b()]);
import vs require
import는 정적, require는 동적
undefined vs null
Q. 차이
undefined: 값 없음
null: 의도적 비어있음
Q. == vs ===
→ 타입 변환 여부
Q. 차이
→ key 타입, 순서, 성능
Q. 언제 발생하나
→ 재귀 무한 호출
코드 스플리팅, lazy loading, 이미지 최적화, 캐싱 등을 활용한다. 불필요한 렌더링을 줄이고 이벤트 실행을 제어하는 것이 핵심이다.
debounce는 이벤트 종료 후 한 번 실행된다. throttle은 일정 시간 간격으로 실행된다. 이벤트 과도 실행을 방지하는 목적이다.
불필요한 객체 참조가 유지되어 GC가 회수하지 못하는 상황이다. 이벤트 리스너 미제거, 클로저, DOM 참조 유지 등이 주요 원인이다.
200은 정상 응답, 304는 캐시 사용, 404는 리소스 없음이다. 상태 코드에 따라 클라이언트 처리 방식이 달라진다.
브라우저가 다른 출처 요청을 제한하는 보안 정책이다. 서버에서 허용 헤더 설정이 필요하다.
쿠키는 서버로 자동 전송되며 용량이 작다. 로컬스토리지는 클라이언트에만 저장되며 용량이 크다. 보안이 필요한 경우 쿠키(HttpOnly)를 사용한다.
배열은 인덱스를 통한 접근이 빠르다. 연결 리스트는 삽입과 삭제가 효율적이다. 탐색 성능은 배열이 우수하다.
대표적으로 병합 정렬과 퀵 정렬이 해당된다. 대량 데이터 정렬에서 효율적인 시간복잡도다.
렌더링 이후 실행된다. 의존성 배열에 따라 실행 조건이 결정된다. cleanup 함수로 정리 작업을 수행한다.
여러 상태 업데이트를 한 번에 처리하는 batching 때문이다. 즉시 값이 반영되지 않는 이유다.
리스트 렌더링 시 요소 식별을 위한 값이다. diffing 최적화에 사용된다. 잘못된 key는 불필요한 렌더링을 유발한다.
Canvas는 픽셀 기반 렌더링이다. DOM은 요소 기반 렌더링이다. Canvas가 성능 측면에서 게임에 적합하다.
GPU를 활용한 그래픽 처리 방식이다. 고성능 2D/3D 렌더링에 사용된다.
과도한 렌더링, 무거운 JavaScript 연산, 잦은 Reflow 등이 원인이다. 레이아웃 변경이 많을수록 성능 저하가 발생한다.
Q18. React 렌더링이란
React에서 렌더링은 컴포넌트 함수를 다시 실행해 UI 결과를 계산하는 과정이다. 상태나 props가 바뀌면 렌더링이 발생하고, 실제 DOM 변경은 React가 비교 후 필요한 부분만 반영한다.
Q19. 리렌더링이 발생하는 경우
state 변경, props 변경, 부모 컴포넌트 리렌더링 시 자식도 함께 리렌더링될 수 있다. 불필요한 리렌더링은 성능 저하 원인이 될 수 있다.
Q20. React.memo
props가 바뀌지 않으면 컴포넌트 리렌더링을 막는 최적화 기능이다. 내부적으로 얕은 비교를 사용하므로 객체, 배열, 함수 props는 주의해야 한다.
Q21. useMemo
계산 비용이 큰 값을 메모이제이션하는 Hook이다. 의존성 배열 값이 바뀌지 않으면 이전 계산 결과를 재사용한다.
Q22. useCallback
함수 자체를 메모이제이션하는 Hook이다. 자식 컴포넌트에 함수를 props로 넘길 때 불필요한 리렌더링을 줄이는 데 사용한다.
Q23. Next.js란
React 기반의 프레임워크로 라우팅, SSR, SSG, API Routes, 이미지 최적화 등을 제공한다. React만 사용할 때보다 프로덕션 웹 애플리케이션 구성이 쉽다.
Q24. CSR vs SSR vs SSG vs ISR
CSR은 브라우저에서 렌더링하고, SSR은 요청 시 서버에서 HTML을 생성한다. SSG는 빌드 시점에 HTML을 미리 생성해 빠르게 제공한다.
ISRT 은 SSG처럼 미리 HTML을 생성해두지만, 일정 시간 이후에 다시 생성할 수 있는 방식이다.
Q25. Hydration
서버에서 만들어진 HTML에 React 이벤트와 상태를 연결하는 과정이다. SSR/SSG 페이지가 브라우저에서 동작 가능한 React 앱이 되는 단계다.
Q26. Next.js App Router
Next.js의 새로운 라우팅 구조로 app 디렉터리를 기반으로 동작한다. Server Component, Layout, Loading, Error UI 등을 라우트 단위로 구성할 수 있다.
Q27. Server Component
서버에서만 실행되는 컴포넌트다. 클라이언트 번들에 포함되지 않아 번들 사이즈를 줄일 수 있고, DB나 서버 리소스에 직접 접근할 수 있다.
Q28. Client Component
브라우저에서 실행되는 컴포넌트다. useState, useEffect, 이벤트 핸들러처럼 클라이언트 기능이 필요할 때 사용한다.
Q29. 번들링이란
여러 JavaScript, CSS, 이미지 등의 파일을 브라우저가 효율적으로 받을 수 있게 묶고 최적화하는 과정이다. 번들링 결과가 크면 초기 로딩 속도가 느려질 수 있다.
Q30. 번들 사이즈란
브라우저가 다운로드해야 하는 JavaScript 파일 크기다. 번들 사이즈가 크면 다운로드, 파싱, 실행 시간이 늘어나 초기 로딩 성능이 나빠진다.
결론:
둘 다 본다. (빌드 + 브라우저)
Next.js 기준:
npm run build
하면 이런 거 나온다:
Route (pages) Size First Load JS
/index 2 kB 80 kB
/about 3 kB 82 kB
Size: 해당 페이지 코드
First Load JS: 실제 브라우저가 처음에 받는 JS
👉 이게 “번들 사이즈”의 핵심 지표
크롬 → Network → JS 필터
여기서:
실제 다운로드된 파일 크기 확인 가능
gzip / brotli 압축된 크기도 확인 가능
👉 실제 사용자 기준 성능
Next에서 많이 씀:
npm install @next/bundle-analyzer
→ 어떤 라이브러리가 용량 큰지 시각화
정리
빌드 → 구조/이론적 크기
네트워크 → 실제 사용자 다운로드 크기
Q31. 번들 사이즈 줄이는 방법
코드 스플리팅, dynamic import, tree shaking, 불필요한 라이브러리 제거, Server Component 활용 등이 있다. Next.js에서는 페이지나 라우트 단위 코드 스플리팅이 기본적으로 적용된다.
Q32. Code Splitting
하나의 큰 번들을 여러 작은 파일로 나누는 방식이다. 필요한 시점에 필요한 코드만 로드해 초기 로딩 속도를 개선한다.
Q33. Lazy Loading
초기 화면에 필요 없는 리소스를 나중에 불러오는 방식이다. 이미지, 모달, 차트, 에디터 같은 무거운 컴포넌트에 자주 사용한다.
Q34. Tree Shaking
사용하지 않는 코드를 최종 번들에서 제거하는 최적화 방식이다. ES Module의 정적 import/export 구조에서 효과적으로 동작한다.
Q35. Webpack / Vite / Turbopack
Webpack은 오래 쓰인 대표 번들러고, Vite는 개발 서버 속도가 빠른 빌드 도구다. Turbopack은 Rust 기반의 Next.js 내장 번들러로, 개발 환경에서 빠른 증분 빌드를 목표로 한다.
Q36. Vercel이란
Next.js를 만든 회사에서 제공하는 프론트엔드 배포 플랫폼이다. Git 저장소와 연결하면 빌드, 배포, 프리뷰 URL 생성, CDN 제공 등을 자동화할 수 있다.
Q37. Vercel 빌드 과정
코드를 가져온 뒤 의존성을 설치하고 빌드 명령을 실행해 배포 산출물을 만든다. Vercel의 vercel build는 결과물을 .vercel/output에 생성할 수 있다.
Q38. 환경 변수
API URL, 토큰, 배포 환경별 설정값을 코드 밖에서 관리하기 위한 값이다. Next.js에서는 서버 전용 환경 변수와 NEXTPUBLIC이 붙은 클라이언트 노출 환경 변수를 구분해야 한다.
Q39. CDN
정적 파일을 사용자와 가까운 서버에서 제공하는 네트워크다. 이미지, JS, CSS 같은 리소스를 빠르게 전달해 로딩 성능을 개선한다.
서버 한국 → 유저 미국 → 느림
미국 CDN 서버 → 빠름
즉 가까운 서버에서 빠르게 전달하는 것
Q40. Lighthouse
웹 성능, 접근성, SEO, Best Practices 등을 측정하는 도구다. 프론트엔드 성능 개선 전후를 비교할 때 자주 사용한다.
Q41. HTTP란
클라이언트와 서버 간 데이터를 주고받는 애플리케이션 계층 프로토콜이다. 요청(Request)과 응답(Response) 구조로 동작한다.
Q42. HTTP vs HTTPS
HTTP는 평문 통신이고, HTTPS는 TLS를 사용해 데이터를 암호화한다. HTTPS는 중간자 공격 방지와 데이터 보호에 필수적이다.
Q43. TCP vs UDP
TCP는 연결 기반으로 신뢰성이 높고 순서를 보장한다. UDP는 비연결 방식으로 빠르지만 데이터 손실 가능성이 있다.
Q44. 3-way handshake
TCP 연결을 맺는 과정이다. SYN → SYN+ACK → ACK 순으로 진행되며, 클라이언트와 서버가 연결을 확립한다.
Q45. 4-way handshake
TCP 연결을 종료하는 과정이다. FIN → ACK → FIN → ACK 순으로 진행되며 양쪽 연결을 안전하게 종료한다.
Q46. DNS 동작 과정
도메인을 IP 주소로 변환하는 과정이다. 브라우저 → DNS 서버 → IP 조회 → 서버 요청 순으로 진행된다.
Q47. 브라우저에 URL 입력 시 과정
DNS 조회 → TCP 연결 → TLS handshake → HTTP 요청 → 서버 응답 → 렌더링 순으로 진행된다.
Q48. HTTP 메서드
GET은 조회, POST는 생성, PUT은 전체 수정, PATCH는 부분 수정, DELETE는 삭제를 의미한다.
Q49. GET vs POST
GET은 URL에 데이터 포함, 캐싱 가능, 길이 제한 있음. POST는 body에 데이터 포함, 캐싱 기본적으로 안 됨.
Q50. 상태 코드 (Status Code)
2xx는 성공, 3xx는 리다이렉션, 4xx는 클라이언트 오류, 5xx는 서버 오류를 의미한다.
200: 조회 성공
201: 생성 성공
204: 삭제 성공, 응답 없음
304: 캐시 사용
400: 잘못된 요청
401: 인증 필요
403: 권한 없음
404: 리소스 없음
409: 충돌
422: 유효성 실패
429: 요청 과다
500: 서버 오류
503: 서버 일시 장애
Q51. 캐싱 (Cache)
서버 응답을 저장해 재사용하는 방식이다. 네트워크 요청을 줄이고 성능을 개선한다.
Q52. Cache-Control
캐시 정책을 정의하는 헤더다. max-age, no-cache, no-store 등을 통해 캐시 동작을 제어한다.
Q53. ETag / Last-Modified
리소스 변경 여부를 확인하는 방식이다. 변경되지 않았다면 304 응답으로 네트워크 비용을 줄인다.
Q54. 쿠키 vs 세션
쿠키는 클라이언트 저장, 세션은 서버 저장이다. 인증 상태 유지에 사용된다.
Q55. JWT (JSON Web Token)
서버가 상태를 저장하지 않고 인증 정보를 토큰으로 전달하는 방식이다. 클라이언트에 저장해 인증에 사용한다.
Q56. CORS
다른 출처 간 요청을 제한하는 브라우저 보안 정책이다. 서버에서 허용 헤더를 설정해야 요청 가능하다.
Q57. Preflight 요청
CORS에서 실제 요청 전에 OPTIONS 요청을 보내 서버 허용 여부를 확인하는 과정이다.
Q58. REST API란
자원을 URI로 표현하고, HTTP 메서드를 사용해 자원에 대한 행위를 정의하는 API 설계 방식입니다.
Q59. GraphQL
클라이언트가 필요한 데이터만 요청할 수 있는 API 방식이다. over-fetching, under-fetching 문제를 해결한다.
Q60. WebSocket
클라이언트와 서버 간 양방향 통신을 가능하게 하는 프로토콜이다. 실시간 채팅, 게임 등에 사용된다.
Q61. Polling vs Long Polling vs WebSocket
Polling은 주기적 요청, Long Polling은 응답 지연 방식, WebSocket은 지속 연결로 실시간 통신을 지원한다.
Q62. HTTP/1.1 vs HTTP/2
HTTP/1.1은 요청을 순차 처리하고, HTTP/2는 하나의 연결에서 여러 요청을 병렬 처리(multiplexing)할 수 있다.
Q63. HTTP/3
UDP 기반으로 동작하며 지연 시간을 줄이고 연결 성능을 개선한 프로토콜이다.
Q64. Keep-Alive
하나의 TCP 연결을 재사용하는 방식이다. 연결 비용을 줄이고 성능을 개선한다.
Q65. 압축 (Compression)
gzip, brotli 등을 사용해 응답 데이터를 줄여 전송 속도를 개선한다.
Q66. CDN
정적 리소스를 사용자와 가까운 서버에서 제공해 네트워크 지연을 줄이는 시스템이다.
브라우저 통신이 5분 정도 걸릴대 최적화 방법
먼저 Chrome DevTools의 Network, Performance 탭, Lighthouse를 통해 병목이 네트워크, JavaScript 실행, 렌더링 중 어디인지 파악한다.
네트워크 영역에서는 API 요청을 최적화한다.
불필요한 요청을 제거하고, 독립적인 요청은 Promise.all을 통해 병렬 처리한다.
또한 서버에서 모든 데이터를 가져오는 것이 아니라 필요한 데이터만 가져오도록 API를 수정한다.
JavaScript 영역에서는 번들 사이즈를 확인하고, 코드 스플리팅과 tree shaking을 통해 초기 로딩 비용을 줄인다.
렌더링 영역에서는 한 번에 모든 데이터를 그리지 않고 lazy loading, 페이지네이션, 무한 스크롤을 적용해 렌더링 부담을 줄인다.
XSS
React의 Hidration
IPV6 브로드캐스트 멀티캐스트 애니캐스트
IPV6는 멀티캐스트 씀