1. JavaScript 동작 원리
var / let / const 차이
모범답안
var는 함수 스코프이고, let과 const는 블록 스코프입니다.
또 var는 재선언이 가능하고 호이스팅 시 undefined로 초기화되지만, let과 const는 호이스팅은 되지만 TDZ 때문에 선언 전에 접근하면 에러가 납니다.
const는 재할당이 불가능하다는 의미이지, 객체의 내부 값까지 불변이라는 뜻은 아닙니다.
실무에서는 의도치 않은 재할당을 막기 위해 기본적으로 const를 쓰고, 값이 바뀌는 경우에만 let을 사용합니다. var는 예측하기 어려운 스코프 문제 때문에 거의 사용하지 않습니다.
호이스팅이 뭔가요?
모범답안
호이스팅은 변수나 함수 선언이 코드 실행 전에 스코프의 최상단으로 끌어올려진 것처럼 동작하는 현상입니다.
다만 실제 코드가 물리적으로 이동하는 것은 아니고, 자바스크립트 엔진이 실행 컨텍스트를 만들 때 변수와 함수 선언을 먼저 메모리에 등록하기 때문에 그렇게 보입니다.
var는 선언과 동시에 undefined로 초기화되지만, let과 const는 선언은 인식되지만 초기화 전까지 TDZ에 있기 때문에 선언 전에 접근하면 에러가 납니다.
클로저가 뭔가요?
모범답안
클로저는 함수가 자신이 선언된 렉시컬 스코프를 기억해서, 외부 함수의 실행이 끝난 뒤에도 그 스코프의 변수에 접근할 수 있는 구조입니다.
예를 들어 어떤 함수 안에서 내부 함수를 반환하면, 외부 함수는 종료됐더라도 내부 함수가 외부 함수의 변수를 계속 참조할 수 있습니다.
실무에서는 상태를 은닉하거나, 특정 값을 기억하는 함수, debounce/throttle, React hook 내부 동작 같은 곳에서 클로저 개념이 많이 쓰입니다.
this 바인딩은 어떻게 결정되나요?
모범답안
자바스크립트의 this는 함수가 어디서 선언됐는지보다 어떻게 호출됐는지에 따라 결정됩니다.
일반 함수 호출에서는 전역 객체 또는 strict mode에서는 undefined가 되고, 객체의 메서드로 호출하면 해당 객체가 this가 됩니다. call, apply, bind를 쓰면 명시적으로 바꿀 수 있습니다.
반면 화살표 함수는 자기만의 this를 만들지 않고, 선언 당시의 상위 스코프 this를 그대로 사용합니다.
React나 이벤트 핸들러에서 this 문제가 생기는 이유도 결국 호출 방식이 바뀌기 때문입니다.
이벤트 루프가 뭔가요?
모범답안
자바스크립트는 기본적으로 싱글 스레드로 실행되지만, 비동기 작업을 처리하기 위해 이벤트 루프를 사용합니다.
동기 코드는 콜스택에서 바로 실행되고, setTimeout, 네트워크 요청, 이벤트 같은 비동기 작업은 브라우저나 런타임에 맡겨집니다. 이후 콜백은 task queue나 microtask queue에 들어가고, 콜스택이 비면 이벤트 루프가 큐에서 작업을 꺼내 실행합니다.
중요한 점은 Promise.then, queueMicrotask 같은 microtask가 setTimeout 같은 macrotask보다 먼저 처리된다는 점입니다.
microtask / macrotask 차이
모범답안
microtask는 현재 실행 중인 동기 코드가 끝난 직후, 다음 macrotask로 넘어가기 전에 처리되는 작업입니다. 대표적으로 Promise.then, queueMicrotask, MutationObserver가 있습니다.
macrotask는 이벤트 루프의 한 사이클에서 처리되는 큰 작업 단위이고, setTimeout, setInterval, DOM 이벤트, 네트워크 콜백 등이 해당합니다.
그래서 setTimeout(..., 0)보다 Promise.then()이 먼저 실행됩니다. 이 차이를 알아야 비동기 코드의 실행 순서를 정확히 예측할 수 있습니다.
Promise와 async/await 차이
모범답안
async/await는 Promise를 더 동기 코드처럼 읽기 쉽게 작성하기 위한 문법입니다. 내부적으로는 Promise 기반으로 동작합니다.
async 함수는 항상 Promise를 반환하고, await는 Promise가 처리될 때까지 해당 async 함수의 이후 실행을 잠시 멈춥니다. 하지만 전체 자바스크립트 스레드가 멈추는 것은 아니고, 나머지 작업은 이벤트 루프를 통해 계속 처리됩니다.
실무에서는 복잡한 Promise 체인을 줄이고 에러 처리를 try/catch로 명확하게 하기 위해 async/await를 많이 사용합니다.
얕은 복사 / 깊은 복사 차이
모범답안
얕은 복사는 객체의 1단계 속성만 복사하고, 중첩된 객체는 참조를 공유합니다. 깊은 복사는 중첩된 객체까지 모두 새로운 객체로 복사하는 방식입니다.
예를 들어 spread 문법이나 Object.assign은 기본적으로 얕은 복사입니다. 그래서 중첩 객체를 수정하면 원본에도 영향이 갈 수 있습니다.
React 상태를 업데이트할 때 이 차이가 중요합니다. 상태를 불변으로 관리하려면 변경되는 깊이까지 새 객체를 만들어야 React가 변경을 감지할 수 있습니다.
prototype과 class의 관계
모범답안
자바스크립트는 프로토타입 기반 언어입니다. 객체가 어떤 속성이나 메서드를 직접 가지고 있지 않으면 자신의 프로토타입 체인을 따라 올라가며 찾습니다.
class 문법은 이 프로토타입 기반 상속을 더 익숙한 문법으로 감싼 syntactic sugar에 가깝습니다. 내부적으로는 여전히 prototype을 사용합니다.
즉, Java나 C++의 클래스 기반 상속과 완전히 같은 개념이라기보다는, 자바스크립트의 프로토타입 상속을 class 문법으로 표현한 것이라고 이해하면 됩니다.
2. 브라우저 / 웹 기본기
브라우저 렌더링 과정
모범답안
브라우저는 HTML을 파싱해서 DOM을 만들고, CSS를 파싱해서 CSSOM을 만듭니다. 그다음 DOM과 CSSOM을 합쳐 Render Tree를 만들고, 각 요소의 위치와 크기를 계산하는 Layout 단계를 거칩니다.
이후 실제 픽셀을 그리는 Paint가 일어나고, 레이어를 합성하는 Composite 단계가 진행됩니다.
프론트엔드 성능 최적화에서는 이 과정 중 Layout과 Paint를 불필요하게 많이 발생시키지 않는 것이 중요합니다. 예를 들어 DOM 크기를 줄이거나, 잦은 스타일 변경을 줄이고, transform/opacity 같은 composite 중심 속성을 활용할 수 있습니다.
reflow / repaint 차이
모범답안
reflow는 요소의 크기나 위치가 바뀌어서 레이아웃을 다시 계산하는 작업입니다. 예를 들어 width, height, margin, display 변경 등이 reflow를 유발할 수 있습니다.
repaint는 레이아웃은 그대로인데 색상, 배경, 그림자처럼 시각적인 표현만 다시 그리는 작업입니다.
reflow는 주변 요소의 위치 계산까지 다시 해야 할 수 있어서 repaint보다 비용이 큽니다. 그래서 성능이 중요한 화면에서는 DOM 읽기와 쓰기를 섞지 않고, 변경을 한 번에 모아서 처리하는 방식이 좋습니다.
debounce / throttle 차이
모범답안
debounce는 이벤트가 연속으로 발생할 때 마지막 이벤트 이후 일정 시간이 지나면 한 번만 실행하는 방식입니다. 검색창 자동완성처럼 사용자가 입력을 멈춘 뒤 요청을 보내는 경우에 적합합니다.
throttle은 이벤트가 계속 발생하더라도 일정 시간 간격마다 최대 한 번만 실행하는 방식입니다. 스크롤 위치 계산이나 resize 이벤트 처리처럼 지속적으로 발생하는 이벤트에 적합합니다.
둘 다 과도한 이벤트 호출을 줄여 성능을 개선하기 위한 기법입니다.
이벤트 버블링 / 캡처링 / 위임
모범답안
DOM 이벤트는 크게 캡처링, 타깃, 버블링 단계를 거칩니다.
캡처링은 이벤트가 상위 요소에서 하위 요소로 내려가는 단계이고, 버블링은 이벤트가 실제 타깃에서 시작해서 상위 요소로 올라가는 단계입니다.
이벤트 위임은 이 버블링 특성을 이용해서 여러 자식 요소에 각각 이벤트를 붙이지 않고, 부모 요소 하나에 이벤트를 걸어 처리하는 방식입니다.
실무에서는 리스트 아이템이 많거나 동적으로 생성되는 요소가 있을 때 이벤트 위임을 사용하면 메모리 사용과 이벤트 등록 비용을 줄일 수 있습니다.
CORS가 뭔가요?
모범답안
CORS는 Cross-Origin Resource Sharing의 약자로, 브라우저에서 다른 origin의 리소스에 접근할 때 적용되는 보안 정책입니다.
기본적으로 브라우저는 보안상 다른 origin으로의 요청을 제한합니다. 서버가 Access-Control-Allow-Origin 같은 응답 헤더를 통해 허용한 경우에만 브라우저가 응답을 사용할 수 있습니다.
중요한 점은 CORS는 서버 간 통신 제한이 아니라 브라우저 보안 정책이라는 점입니다. 서버에서 서버로 요청할 때는 보통 CORS 제한을 받지 않습니다.
쿠키 / 세션 / localStorage 차이
모범답안
쿠키는 브라우저에 저장되는 작은 데이터이고, 요청 시 자동으로 서버에 함께 전송될 수 있습니다. 인증이나 사용자 추적에 자주 사용됩니다.
세션은 보통 서버 쪽에 사용자 상태를 저장하고, 브라우저에는 세션 ID만 쿠키로 저장하는 방식입니다.
localStorage는 브라우저에 데이터를 저장하지만 요청에 자동으로 포함되지 않습니다. 그래서 단순 사용자 설정이나 임시 데이터 저장에는 쓸 수 있지만, 보안이 중요한 인증 정보 저장에는 조심해야 합니다.
특히 XSS 위험 때문에 access token 같은 민감한 값은 localStorage보다 HttpOnly 쿠키를 쓰는 방식이 더 안전한 경우가 많습니다.
HTTP 캐시
모범답안
HTTP 캐시는 브라우저나 CDN이 응답을 저장해두고, 같은 리소스를 다시 요청할 때 네트워크 비용을 줄이는 기능입니다.
Cache-Control로 캐시 정책을 정하고, ETag나 Last-Modified를 통해 리소스가 변경됐는지 검증할 수 있습니다.
예를 들어 리소스가 변경되지 않았다면 서버가 304 Not Modified를 응답하고, 브라우저는 기존 캐시를 사용합니다.
실무에서는 JS/CSS 파일처럼 해시가 붙은 정적 리소스는 길게 캐시하고, HTML 문서는 짧게 캐시하거나 항상 재검증하도록 설정하는 경우가 많습니다.
SSR / CSR 차이
모범답안
CSR은 브라우저가 빈 HTML을 받고 JavaScript를 실행해서 화면을 그리는 방식입니다. 초기 로딩 시 JS 번들 다운로드와 실행이 필요하지만, 이후 페이지 전환은 빠르게 처리할 수 있습니다.
SSR은 서버에서 HTML을 먼저 생성해서 내려주는 방식입니다. 사용자는 초기 화면을 더 빨리 볼 수 있고 SEO에도 유리합니다. 다만 서버 부하가 늘 수 있고, 클라이언트에서 hydration 과정이 필요합니다.
실무에서는 서비스 성격에 따라 CSR, SSR, SSG, ISR 같은 방식을 섞어서 사용합니다. 예를 들어 SEO가 중요한 기사나 랜딩 페이지는 SSR/SSG가 유리하고, 로그인 이후 복잡한 대시보드는 CSR도 충분할 수 있습니다.
3. 자료구조 / 알고리즘 기본
배열 / 객체 / Map / Set 차이
모범답안
배열은 순서가 있는 데이터 목록을 다룰 때 사용합니다. 인덱스로 접근할 수 있고, 순회나 정렬에 적합합니다.
객체는 문자열 또는 심볼 키를 기반으로 값을 저장하는 구조입니다. 일반적인 key-value 데이터에 많이 씁니다.
Map은 객체보다 key 타입이 자유롭고, 삽입 순서가 보장되며, 데이터 추가/삭제/조회에 명확한 API를 제공합니다.
Set은 중복 없는 값의 집합입니다. 중복 제거, 존재 여부 확인에 적합합니다.
예를 들어 리스트에서 중복 ID를 제거해야 한다면 Set을 사용할 수 있고, ID 기준으로 데이터를 빠르게 찾고 싶다면 Map이나 객체를 사용할 수 있습니다.
Stack / Queue 사용 사례
모범답안
Stack은 Last In First Out 구조입니다. 마지막에 들어간 데이터가 먼저 나옵니다. 예를 들어 브라우저 히스토리, undo/redo, 괄호 검사, DFS 같은 곳에서 사용됩니다.
Queue는 First In First Out 구조입니다. 먼저 들어간 데이터가 먼저 나옵니다. 예를 들어 작업 대기열, 이벤트 큐, BFS, 메시지 처리 등에 사용됩니다.
프론트엔드에서도 토스트 메시지를 순서대로 보여주거나, 비동기 작업을 순차 처리할 때 queue 개념을 사용할 수 있습니다.
시간복잡도 Big-O
모범답안
Big-O는 입력 크기가 커질 때 알고리즘의 실행 시간이나 메모리 사용량이 어떻게 증가하는지를 표현하는 방식입니다.
예를 들어 배열을 처음부터 끝까지 한 번 순회하면 O(n)이고, 중첩 반복문으로 모든 쌍을 비교하면 O(n²)입니다. 해시 기반 조회는 평균적으로 O(1)에 가깝습니다.
실무에서는 데이터가 작을 때는 큰 차이가 없어도, 리스트가 수천 개, 수만 개로 늘어나면 시간복잡도가 성능에 큰 영향을 줍니다. 그래서 중복 검사나 검색이 많다면 배열 순회보다 Set이나 Map을 고려할 수 있습니다.
정렬 / 탐색 기본
모범답안
정렬은 데이터를 특정 기준으로 순서대로 배치하는 작업입니다. JavaScript의 Array.prototype.sort를 사용할 수 있지만, 숫자 정렬에서는 비교 함수를 명시해야 합니다.
탐색은 원하는 데이터를 찾는 과정입니다. 단순 배열에서 순차 탐색을 하면 O(n)이고, 정렬된 데이터에서 이진 탐색을 하면 O(log n)입니다.
다만 이진 탐색은 데이터가 정렬되어 있어야 하고, 삽입/삭제가 잦은 데이터에는 유지 비용이 있습니다. 그래서 실무에서는 데이터 특성에 따라 배열, Map, 트리 구조 등을 선택해야 합니다.
재귀 개념
모범답안
재귀는 함수가 자기 자신을 다시 호출하는 방식입니다. 트리 구조나 중첩된 데이터처럼 같은 구조가 반복되는 문제를 풀 때 유용합니다.
재귀를 사용할 때는 반드시 종료 조건이 있어야 합니다. 종료 조건이 없으면 무한 호출이 발생해서 콜스택이 초과됩니다.
프론트엔드에서는 메뉴 트리, 댓글 대댓글 구조, JSON 트리 렌더링 같은 곳에서 재귀를 사용할 수 있습니다.
특정 상황에서 자료구조 선택
질문 예시: 중복 제거를 어떻게 할 건가요?
모범답안
단순 값 배열의 중복 제거라면 Set을 사용할 것 같습니다. Set은 중복을 허용하지 않기 때문에 배열을 Set으로 변환한 뒤 다시 배열로 만들면 됩니다.
객체 배열이라면 어떤 값을 기준으로 중복을 판단할지 먼저 정해야 합니다. 예를 들어 id 기준이라면 Map을 사용해서 id를 key로 저장하면 중복을 제거하면서 마지막 값이나 첫 번째 값을 유지할 수 있습니다.
중요한 건 단순히 중복 제거 방식만 보는 게 아니라, 데이터 크기와 기준값, 순서 유지 여부까지 같이 고려하는 것입니다.
4. 네트워크 / HTTP
HTTP / HTTPS 차이
모범답안
HTTP는 클라이언트와 서버가 데이터를 주고받기 위한 프로토콜이고, HTTPS는 HTTP에 TLS 암호화를 추가한 방식입니다.
HTTPS를 사용하면 통신 내용이 암호화되고, 서버 인증서를 통해 접속한 서버가 신뢰할 수 있는 서버인지 검증할 수 있습니다. 또한 중간자 공격을 줄일 수 있습니다.
실무에서는 로그인, 결제, 개인정보뿐만 아니라 대부분의 웹서비스에서 HTTPS를 기본으로 사용합니다. 최신 브라우저 기능 중 일부는 HTTPS 환경에서만 동작하기도 합니다.
GET / POST / PUT / PATCH / DELETE 차이
모범답안
GET은 데이터를 조회할 때 사용하고, 일반적으로 서버 상태를 변경하지 않아야 합니다.
POST는 새로운 리소스를 생성하거나 특정 동작을 수행할 때 사용합니다.
PUT은 리소스 전체를 교체하는 의미가 강하고, PATCH는 리소스의 일부를 수정하는 의미가 강합니다.
DELETE는 리소스를 삭제할 때 사용합니다.
실무에서는 메서드의 의미를 지키면 API 의도를 이해하기 쉽고, 캐시나 재시도 정책을 설계할 때도 유리합니다.
주요 HTTP 상태코드
모범답안
200은 요청이 성공했다는 의미입니다.
301은 영구 리다이렉트, 302는 임시 리다이렉트입니다.
304는 리소스가 변경되지 않았으니 캐시를 사용해도 된다는 의미입니다.
400은 잘못된 요청, 401은 인증이 필요하거나 인증 실패, 403은 인증은 됐지만 권한이 없는 경우에 가깝습니다.
404는 리소스를 찾을 수 없다는 의미이고, 500은 서버 내부 오류입니다.
프론트엔드에서는 상태코드에 따라 로그인 페이지로 보낼지, 권한 없음 메시지를 보여줄지, 재시도할지 등을 다르게 처리해야 합니다.
TCP 3-way handshake
모범답안
TCP 3-way handshake는 클라이언트와 서버가 데이터를 주고받기 전에 연결을 수립하는 과정입니다.
클라이언트가 SYN을 보내고, 서버가 SYN-ACK로 응답하고, 클라이언트가 다시 ACK를 보내면 연결이 성립됩니다.
이 과정을 통해 양쪽이 통신 가능한 상태인지 확인하고, 이후 신뢰성 있는 데이터 전송을 시작합니다.
프론트엔드 입장에서는 직접 구현할 일은 거의 없지만, 네트워크 지연이나 연결 비용을 이해할 때 중요한 기본 개념입니다.
DNS가 하는 일
모범답안
DNS는 사람이 읽기 쉬운 도메인 이름을 실제 서버 IP 주소로 변환해주는 시스템입니다.
브라우저에서 도메인에 접속하면 먼저 DNS 조회를 통해 IP를 찾고, 그 IP로 서버에 연결합니다.
DNS 조회에도 시간이 걸리기 때문에 성능 최적화에서는 DNS prefetch 같은 기법을 사용할 수 있습니다. 또 장애 상황에서는 서버 문제가 아니라 DNS 설정이나 전파 문제일 수도 있습니다.
CORS preflight
모범답안
CORS preflight는 브라우저가 실제 요청을 보내기 전에 OPTIONS 요청으로 서버가 해당 요청을 허용하는지 먼저 확인하는 과정입니다.
단순 요청이 아닌 경우, 예를 들어 커스텀 헤더를 사용하거나 PUT, DELETE 같은 메서드를 쓰거나 특정 Content-Type을 사용할 때 preflight가 발생할 수 있습니다.
서버가 Access-Control-Allow-Origin, Access-Control-Allow-Methods, Access-Control-Allow-Headers 등을 적절히 응답해야 실제 요청이 진행됩니다.
실무에서는 preflight 때문에 요청이 한 번 더 발생하므로 성능이나 서버 설정 문제를 같이 봐야 합니다.
SameSite / Secure / HttpOnly
모범답안
SameSite는 쿠키가 cross-site 요청에 포함될지 제어하는 속성입니다.
Strict는 같은 사이트 요청에만 쿠키를 보내고, Lax는 일부 안전한 top-level navigation에서는 허용합니다. None은 cross-site에서도 보내지만 반드시 Secure가 필요합니다.
Secure는 HTTPS 연결에서만 쿠키를 전송하게 하는 속성입니다.
HttpOnly는 JavaScript에서 쿠키에 접근하지 못하게 하는 속성입니다. XSS 공격으로 쿠키가 탈취되는 위험을 줄일 수 있습니다.
인증 쿠키는 보통 HttpOnly, Secure, 적절한 SameSite 설정을 함께 고려해야 합니다.
5. React 기본 원리
React에서 key가 필요한 이유
모범답안
React에서 key는 리스트를 렌더링할 때 각 요소를 식별하기 위한 값입니다.
React는 렌더링 전후의 Virtual DOM을 비교해서 어떤 요소가 추가, 삭제, 변경됐는지 판단하는데, 이때 key가 없거나 불안정하면 요소를 잘못 재사용할 수 있습니다.
특히 배열 index를 key로 쓰면 리스트의 순서가 바뀌거나 중간에 항목이 추가/삭제될 때 상태가 엉뚱한 항목에 붙을 수 있습니다. 그래서 가능하면 고유하고 안정적인 ID를 key로 사용하는 것이 좋습니다.
상태 변경 시 렌더링이 일어나는 방식
모범답안
React에서 state나 props가 변경되면 해당 컴포넌트가 다시 렌더링됩니다. 렌더링된 결과는 Virtual DOM 형태로 만들어지고, 이전 결과와 비교해서 실제 DOM에 필요한 변경만 반영합니다.
중요한 점은 렌더링이 곧바로 DOM 전체를 다시 그린다는 뜻은 아니라는 것입니다. React는 변경된 부분을 계산해서 최소한의 DOM 업데이트를 하려고 합니다.
또 상태 업데이트는 비동기적으로 batch 처리될 수 있기 때문에, 이전 상태를 기반으로 업데이트할 때는 함수형 업데이트를 사용하는 것이 안전합니다.
Virtual DOM이 왜 필요한가요?
모범답안
Virtual DOM은 실제 DOM을 직접 매번 조작하는 대신, 메모리상의 가벼운 객체 트리로 UI 상태를 표현하는 방식입니다.
상태가 변경되면 React는 새로운 Virtual DOM과 이전 Virtual DOM을 비교해서 실제 DOM에 필요한 변경만 적용합니다.
이 방식의 장점은 개발자가 직접 DOM 업데이트를 세밀하게 관리하지 않아도 선언적으로 UI를 작성할 수 있다는 점입니다. 다만 Virtual DOM이 항상 무조건 빠르다는 의미는 아니고, 복잡한 UI 변경을 예측 가능하게 관리하기 위한 추상화라고 보는 게 맞습니다.
useEffect 실행 타이밍
모범답안
useEffect는 렌더링이 끝나고 브라우저가 화면을 그린 이후에 실행됩니다. 그래서 데이터 fetching, 이벤트 구독, 타이머 설정처럼 렌더링 이후에 필요한 side effect를 처리할 때 사용합니다.
의존성 배열이 없으면 매 렌더링마다 실행되고, 빈 배열이면 마운트 시 한 번 실행됩니다. 특정 값이 들어가 있으면 그 값이 바뀔 때 다시 실행됩니다.
cleanup 함수는 컴포넌트가 언마운트되거나 effect가 다시 실행되기 전에 호출됩니다. 그래서 이벤트 리스너 제거, 타이머 해제, 구독 해제 같은 작업에 사용합니다.
memoization이 필요한 상황
모범답안
memoization은 동일한 입력에 대해 이전 계산 결과나 함수 참조를 재사용하는 기법입니다.
React에서는 useMemo, useCallback, React.memo 등을 사용합니다.
비싼 계산을 매 렌더링마다 반복하지 않거나, 자식 컴포넌트에 전달되는 props의 참조가 불필요하게 바뀌는 것을 막을 때 사용합니다.
다만 모든 곳에 memoization을 쓰는 것이 좋은 것은 아닙니다. memoization 자체도 비용이 있기 때문에, 실제로 렌더링 비용이 크거나 참조 안정성이 필요한 경우에 선택적으로 사용하는 것이 좋습니다.
상태관리 라이브러리를 왜 쓰나요?
모범답안
React의 기본 state만으로도 많은 상태를 관리할 수 있지만, 여러 컴포넌트가 공유하는 상태가 많아지면 props drilling이나 상태 동기화 문제가 생길 수 있습니다.
상태관리 라이브러리는 전역 상태를 더 명확하게 관리하고, 컴포넌트 간 상태 공유를 쉽게 하기 위해 사용합니다.
다만 모든 상태를 전역으로 빼는 것은 좋지 않습니다. 폼 입력값이나 모달 열림 상태처럼 특정 컴포넌트에만 필요한 상태는 로컬 state로 두고, 로그인 사용자 정보, UI 전역 설정, 여러 화면에서 공유하는 상태만 전역으로 관리하는 것이 좋습니다.
서버에서 가져온 데이터는 React Query 같은 서버 상태 관리 도구를 쓰고, 클라이언트 UI 상태는 jotai, Zustand, Redux 같은 도구를 쓰는 식으로 역할을 나누는 것도 중요합니다.
React Query의 stale / cache 개념
모범답안
React Query는 서버 데이터를 클라이언트에서 캐싱하고 동기화하기 위한 라이브러리입니다.
staleTime은 데이터가 fresh하다고 간주되는 시간입니다. 이 시간 안에는 같은 queryKey로 다시 접근해도 불필요한 refetch를 하지 않습니다.
gcTime은 사용하지 않는 inactive query가 캐시에 남아 있는 시간입니다. 컴포넌트에서 더 이상 사용하지 않더라도 일정 시간 동안 캐시에 남아 있다가 제거됩니다.
중요한 점은 stale과 cache가 다르다는 것입니다. stale은 데이터가 오래됐는지에 대한 개념이고, cache는 데이터를 메모리에 보관하는 개념입니다.
실무에서는 사용자 경험과 최신성 요구에 따라 staleTime을 조정합니다. 예를 들어 자주 바뀌지 않는 메뉴 정보는 staleTime을 길게 잡을 수 있고, 실시간성이 중요한 데이터는 짧게 잡거나 refetch 정책을 강화할 수 있습니다.
6. 실무형 꼬리질문 대비 답변
“브라우저에서 화면이 느려졌을 때 어떻게 접근하나요?”
모범답안
먼저 문제가 네트워크인지, JavaScript 실행 비용인지, 렌더링 비용인지 나눠서 봅니다.
DevTools Performance 탭으로 long task, layout, paint 비용을 확인하고, Network 탭에서 API 응답 시간과 리소스 크기를 봅니다.
프론트 코드에서는 불필요한 렌더링, 큰 리스트 렌더링, 이미지 최적화, debounce/throttle 적용 여부, layout thrashing 여부를 확인합니다.
React라면 컴포넌트 렌더링 범위가 너무 넓지 않은지, key가 안정적인지, memoization이 필요한 구간인지도 봅니다.
“무한 스크롤을 구현한다면 어떤 점을 고려하나요?”
모범답안
무한 스크롤에서는 데이터를 페이지 단위로 가져오고, 현재 로딩 상태와 다음 페이지 존재 여부를 관리해야 합니다.
스크롤 이벤트를 직접 감지할 수도 있지만, 가능하면 Intersection Observer를 사용해서 sentinel 요소가 보일 때 다음 페이지를 요청하는 방식이 좋습니다.
성능 측면에서는 너무 많은 DOM이 쌓이지 않도록 virtualization을 고려할 수 있고, 중복 요청 방지, 요청 취소, 로딩 중 재요청 방지, 에러 처리도 필요합니다.
React Query를 사용한다면 useInfiniteQuery로 pageParam과 hasNextPage를 관리할 수 있습니다.
“이벤트가 예상과 다르게 동작하면 어떻게 디버깅하나요?”
모범답안
먼저 이벤트가 실제로 어느 요소에서 발생하는지, 캡처링/버블링 중 어느 단계에서 처리되는지 확인합니다.
event.target과 event.currentTarget 차이를 보고, 중간에 stopPropagation이나 preventDefault가 호출되는지도 확인합니다.
모바일에서는 touch 이벤트의 passive 옵션이나 브라우저별 스크롤 처리 차이도 봐야 합니다. 특히 iOS Safari는 스크롤, viewport, input focus 관련 동작이 Chrome과 다를 수 있어서 실제 기기 테스트가 중요합니다.
“서버 응답이 403이면 프론트에서는 어떻게 판단하나요?”
모범답안
403은 보통 인증은 되었지만 권한이 없거나, 서버가 요청을 거부한 경우로 봅니다.
다만 실무에서는 WAF, CDN, robots.txt, CORS, 쿠키 누락, referer 정책 등 다양한 이유로 403이 발생할 수 있어서 단순히 권한 문제로만 보지는 않습니다.
프론트에서는 먼저 요청 URL, method, header, cookie 포함 여부, credentials 설정, 서버 응답 body를 확인합니다. 이후 서버 로그나 CDN 로그와 함께 봐야 정확한 원인을 찾을 수 있습니다.
“CSR과 SSR 중 어떤 걸 선택하겠습니까?”
모범답안
서비스 성격에 따라 선택하겠습니다.
SEO가 중요하거나 초기 HTML 노출이 중요한 페이지라면 SSR이나 SSG를 우선 고려합니다. 예를 들어 기사 페이지, 랜딩 페이지, 공유가 중요한 콘텐츠 페이지는 SSR/SSG가 유리합니다.
반면 로그인 이후 사용자별 데이터가 많고 상호작용이 복잡한 관리자 페이지나 대시보드는 CSR도 적합할 수 있습니다.
또 SSR을 쓰더라도 모든 데이터를 서버에서 처리하기보다, 초기 노출에 필요한 데이터만 서버에서 내려주고 이후 상호작용 데이터는 클라이언트에서 가져오는 식으로 혼합할 수 있습니다.
7. 30분 스크리닝에서 특히 잘 먹히는 답변 방식
면접에서 이렇게 말하면 안정적입니다.
패턴 1
“정의만 말하지 말고 실무 예시까지 붙이기”
예를 들어 CORS 질문이 나오면:
CORS는 브라우저의 same-origin 정책 때문에 다른 origin 요청을 제한하는 보안 정책입니다. 서버가 허용 헤더를 내려줘야 브라우저가 응답을 사용할 수 있습니다. 실무에서는 API 서버와 프론트 도메인이 다를 때 자주 만나고, credentials를 쓰는 경우에는 Access-Control-Allow-Credentials와 Origin 설정을 같이 봐야 합니다.
이렇게 말하면 단순 암기보다 훨씬 실무자처럼 보입니다.
패턴 2
“무조건이라고 말하지 않기”
좋은 표현:
보통은 이렇게 접근합니다. 상황에 따라 다르지만, 일반적으로는 이 기준으로 판단합니다. 데이터 크기와 변경 빈도에 따라 선택이 달라질 수 있습니다.
안 좋은 표현:
이건 무조건 이렇게 해야 합니다. 이게 항상 제일 빠릅니다. 이 방식이 정답입니다.
프론트엔드 실무에서는 트레이드오프를 이해하는 사람이 더 좋아 보입니다.
패턴 3
“모르면 범위를 좁혀서 말하기”
예를 들어 OS나 DB 쪽 질문이 나와서 정확히 모르면:
그 부분은 깊게 다뤄본 경험은 많지 않지만, 제가 이해한 수준에서는 이런 개념으로 알고 있습니다. 프론트엔드 실무에서는 주로 이 부분과 연결해서 경험했습니다.
이렇게 답하면 모르는 걸 숨기는 것보다 훨씬 낫습니다.
8. 우선 암기해야 할 압축 답변 TOP 10
진짜 시간이 없으면 이것부터 외우면 됩니다.
1. 이벤트 루프
자바스크립트는 싱글 스레드로 동작하지만 비동기 처리를 위해 이벤트 루프를 사용합니다. 동기 코드는 콜스택에서 실행되고, 비동기 콜백은 큐에 들어갔다가 콜스택이 비면 실행됩니다. Promise 같은 microtask는 setTimeout 같은 macrotask보다 먼저 처리됩니다.
2. 클로저
클로저는 함수가 선언 당시의 렉시컬 스코프를 기억해서 외부 함수 실행이 끝난 뒤에도 그 변수에 접근할 수 있는 구조입니다. 상태 은닉, debounce, React hook 같은 곳과 연결됩니다.
3. CORS
CORS는 브라우저가 다른 origin의 응답을 사용할 수 있는지 제한하는 보안 정책입니다. 서버가 허용 헤더를 내려줘야 하고, credentials가 있으면 Origin과 Credential 설정을 더 엄격하게 맞춰야 합니다.
4. 브라우저 렌더링
HTML로 DOM, CSS로 CSSOM을 만들고 Render Tree를 구성한 뒤 Layout, Paint, Composite 과정을 거쳐 화면에 그립니다. 성능 최적화에서는 불필요한 Layout과 Paint를 줄이는 게 중요합니다.
5. reflow / repaint
reflow는 요소의 위치나 크기 계산을 다시 하는 것이고, repaint는 색상 같은 시각적 표현을 다시 그리는 것입니다. reflow가 더 비용이 크기 때문에 잦은 DOM 측정과 변경을 섞지 않는 것이 좋습니다.
6. HTTP 캐시
HTTP 캐시는 리소스를 저장해 재요청 비용을 줄이는 기능입니다. Cache-Control로 정책을 정하고, ETag나 Last-Modified로 변경 여부를 검증합니다. 변경이 없으면 304를 받고 기존 캐시를 사용합니다.
7. React key
key는 리스트에서 각 요소를 안정적으로 식별하기 위한 값입니다. key가 불안정하면 React가 요소를 잘못 재사용해서 상태가 엉뚱한 항목에 붙을 수 있습니다.
8. Virtual DOM
Virtual DOM은 실제 DOM을 직접 조작하기 전에 메모리상의 객체 트리로 UI를 표현하는 방식입니다. 변경 전후를 비교해 필요한 DOM 업데이트만 적용하고, 개발자가 선언적으로 UI를 작성하게 해줍니다.
9. staleTime / gcTime
staleTime은 데이터가 fresh하다고 보는 시간이고, gcTime은 사용하지 않는 query cache가 메모리에 남아 있는 시간입니다. stale은 최신성 개념이고 cache는 보관 개념입니다.
10. Big-O
Big-O는 입력 크기가 커질 때 실행 시간이나 메모리 사용량이 어떻게 증가하는지 나타내는 표현입니다. 예를 들어 배열 순회는 O(n), 중첩 반복문은 O(n²), 해시 조회는 평균적으로 O(1)에 가깝습니다.