프론트엔드 면접 질문 - CS (2)

프론트엔드 면접 시 CS 면접 질문들입니다.

면접
--

CS 기초 면접 질문 정리 (네트워크/알고리즘/OS 중심)

🌐 1. 네트워크 (Network) - 8문항

Q1. OSI 7계층과 각 계층의 역할은?

답변:

  1. 물리 계층: 비트 전송 (케이블, 허브)
  2. 데이터링크 계층: 프레임 전송, 오류 검출 (스위치, MAC)
  3. 네트워크 계층: 패킷 라우팅 (IP, 라우터)
  4. 전송 계층: 종단 간 통신, 흐름 제어 (TCP, UDP)
  5. 세션 계층: 세션 관리, 동기화
  6. 표현 계층: 데이터 인코딩, 암호화
  7. 응용 계층: 사용자 인터페이스 (HTTP, FTP, DNS)

개념 설명: 각 계층은 독립적으로 동작하며, 하위 계층의 서비스를 이용합니다. 문제 발생 시 해당 계층만 수정하면 되어 유지보수가 용이합니다.


Q2. TCP와 UDP의 차이점과 실제 사용 사례는?

답변:

특징TCPUDP
연결연결 지향비연결
신뢰성보장 (재전송)보장 안 됨
순서보장보장 안 됨
속도느림빠름
헤더 크기20바이트8바이트

사용 사례:

  • TCP: HTTP/HTTPS, 이메일(SMTP), 파일 전송(FTP), SSH
  • UDP: 실시간 스트리밍, 온라인 게임, DNS, VoIP

개념 설명: TCP는 3-way handshake로 연결을 수립하고, 확인 응답(ACK)을 통해 신뢰성을 보장합니다. UDP는 오버헤드가 적어 실시간 통신에 적합합니다.


Q3. HTTP와 HTTPS의 차이, SSL/TLS 동작 원리는?

답변:

  • HTTP: 평문 통신, 포트 80, 중간자 공격 취약
  • HTTPS: SSL/TLS 암호화, 포트 443, 데이터 기밀성/무결성 보장

SSL/TLS Handshake 과정:

  1. 클라이언트 → 서버: Client Hello (지원 암호화 방식)
  2. 서버 → 클라이언트: Server Hello (인증서, 공개키)
  3. 클라이언트: 인증서 검증 후 대칭키 생성, 공개키로 암호화하여 전송
  4. 서버: 비밀키로 복호화하여 대칭키 획득
  5. 이후 대칭키로 데이터 암호화 통신

개념 설명: 비대칭키(공개키/비밀키)는 대칭키 교환에만 사용하고, 실제 데이터는 빠른 대칭키로 암호화합니다.


Q4. HTTP 상태 코드의 종류와 의미는?

답변:

  • 1xx (정보): 요청 처리 중
    • 100 Continue
  • 2xx (성공): 요청 성공
    • 200 OK, 201 Created, 204 No Content
  • 3xx (리다이렉션): 다른 위치로 이동
    • 301 Moved Permanently, 302 Found, 304 Not Modified
  • 4xx (클라이언트 오류): 클라이언트 요청 오류
    • 400 Bad Request, 401 Unauthorized, 403 Forbidden, 404 Not Found, 429 Too Many Requests
  • 5xx (서버 오류): 서버 처리 오류
    • 500 Internal Server Error, 502 Bad Gateway, 503 Service Unavailable

개념 설명: 프론트엔드 개발 시 상태 코드에 따라 적절한 에러 핸들링과 사용자 피드백을 제공해야 합니다.


Q5. REST API와 RESTful 설계 원칙은?

답변: REST 6가지 원칙:

  1. Stateless: 각 요청은 독립적, 세션 정보를 서버에 저장하지 않음
  2. Client-Server: 클라이언트와 서버 분리
  3. Cacheable: 응답은 캐시 가능 여부 명시
  4. Uniform Interface: 일관된 인터페이스
  5. Layered System: 계층 구조
  6. Code on Demand (선택): 서버가 코드 전송 가능

RESTful 설계:

  • 리소스는 명사로 표현 (동사 X)
  • URI는 계층 구조로 표현
  • HTTP 메서드로 행위 표현
GET /users          # 사용자 목록 조회
GET /users/123      # 특정 사용자 조회
POST /users         # 사용자 생성
PUT /users/123      # 사용자 전체 수정
PATCH /users/123    # 사용자 부분 수정
DELETE /users/123   # 사용자 삭제

개념 설명: RESTful API는 직관적이고 이해하기 쉬워 프론트엔드-백엔드 협업에 효과적입니다.


Q6. 쿠키(Cookie), 세션(Session), 토큰(JWT)의 차이는?

답변: 쿠키:

  • 클라이언트에 저장되는 작은 데이터 (4KB 제한)
  • 만료 기간 설정 가능
  • HttpOnly, Secure, SameSite 속성으로 보안 강화

세션:

  • 서버에 저장, 세션 ID를 쿠키로 전달
  • 서버 메모리 사용, 확장성 제한
  • 보안성 높음

토큰(JWT):

  • 클라이언트에 저장, Stateless
  • Header.Payload.Signature 구조
  • 서버 확장에 유리, 토큰 크기가 큼
  • 한번 발급하면 만료 전까지 무효화 어려움 (Refresh Token으로 해결)

개념 설명: 프론트엔드에서는 JWT를 localStorage나 sessionStorage에 저장하기보다 HttpOnly 쿠키에 저장하는 것이 XSS 공격 방지에 유리합니다.


Q7. CORS(Cross-Origin Resource Sharing)란?

답변: 브라우저가 다른 출처(origin)의 리소스 요청을 제한하는 보안 정책입니다.

출처(Origin): 프로토콜 + 도메인 + 포트

  • http://example.com:80https://example.com:443

동작 방식:

  1. Simple Request: GET, HEAD, POST (특정 조건)
  2. Preflight Request: OPTIONS 메서드로 사전 확인 후 본 요청

서버 설정:

Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
Access-Control-Allow-Headers: Content-Type
Access-Control-Allow-Credentials: true

개념 설명: 프론트엔드 개발 시 자주 마주치는 이슈입니다. 개발 환경에서는 프록시 설정으로 우회 가능합니다.


Q8. DNS(Domain Name System) 동작 원리는?

답변: 도메인 이름을 IP 주소로 변환하는 시스템입니다.

DNS 조회 과정:

  1. 브라우저 캐시 확인
  2. OS 캐시 확인
  3. 로컬 DNS 서버(ISP)에 질의
  4. Root DNS 서버 → TLD DNS 서버(.com) → 권한 있는 DNS 서버 순으로 재귀 질의
  5. IP 주소 반환 및 캐싱

DNS 레코드 타입:

  • A: 도메인 → IPv4
  • AAAA: 도메인 → IPv6
  • CNAME: 도메인 별칭
  • MX: 메일 서버
  • TXT: 텍스트 정보 (SPF, DKIM)

개념 설명: DNS 캐싱으로 반복 조회를 줄여 성능을 향상시킵니다. TTL(Time To Live)로 캐시 유효 기간을 설정합니다.


🎯 2. 알고리즘 (Algorithm) - 8문항

Q1. 시간 복잡도와 Big-O 표기법이란?

답변: 알고리즘의 실행 시간을 입력 크기 n의 함수로 표현하는 방법입니다.

주요 시간 복잡도:

  • O(1): 상수 시간 - 배열 인덱스 접근
  • O(log n): 로그 시간 - 이진 탐색
  • O(n): 선형 시간 - 배열 순회
  • O(n log n): - 병합 정렬, 퀵 정렬
  • O(n²): 이차 시간 - 이중 for문, 버블 정렬
  • O(2ⁿ): 지수 시간 - 피보나치 재귀
  • O(n!): 팩토리얼 시간 - 순열 생성

개념 설명: Big-O는 최악의 경우를 나타내며, 입력이 커질수록 성능 차이가 극명해집니다. 실무에서는 O(n²) 이상은 피해야 합니다.


Q2. 정렬 알고리즘 비교 (버블, 선택, 삽입, 병합, 퀵, 힙)

답변:

알고리즘평균최악공간안정성
버블 정렬O(n²)O(n²)O(1)O
선택 정렬O(n²)O(n²)O(1)X
삽입 정렬O(n²)O(n²)O(1)O
병합 정렬O(n log n)O(n log n)O(n)O
퀵 정렬O(n log n)O(n²)O(log n)X
힙 정렬O(n log n)O(n log n)O(1)X

특징:

  • 삽입 정렬: 거의 정렬된 데이터에 효율적
  • 병합 정렬: 안정 정렬 필요 시 사용
  • 퀵 정렬: 평균적으로 가장 빠름, 실무에서 많이 사용
  • 힙 정렬: 추가 메모리 불필요

개념 설명: JavaScript의 Array.sort()는 엔진마다 다르지만, V8은 TimSort(삽입+병합 정렬)를 사용합니다.


Q3. 이진 탐색(Binary Search) 알고리즘과 구현은?

답변: 정렬된 배열에서 중간값과 비교하여 탐색 범위를 절반씩 줄이는 알고리즘입니다.

시간 복잡도: O(log n)

구현 (JavaScript):

function binarySearch(arr, target) {
  let left = 0;
  let right = arr.length - 1;

  while (left <= right) {
    const mid = Math.floor((left + right) / 2);

    if (arr[mid] === target) {
      return mid;
    } else if (arr[mid] < target) {
      left = mid + 1;
    } else {
      right = mid - 1;
    }
  }

  return -1; // 찾지 못함
}

개념 설명: 선형 탐색 O(n)보다 훨씬 빠르지만, 반드시 정렬된 배열이어야 합니다. 재귀로도 구현 가능합니다.


Q4. DFS와 BFS의 구현과 차이점은?

답변: DFS (깊이 우선 탐색):

// 재귀 방식
function dfs(graph, node, visited = new Set()) {
  visited.add(node);
  console.log(node);

  for (const neighbor of graph[node]) {
    if (!visited.has(neighbor)) {
      dfs(graph, neighbor, visited);
    }
  }
}

// 스택 방식
function dfsIterative(graph, start) {
  const visited = new Set();
  const stack = [start];

  while (stack.length > 0) {
    const node = stack.pop();

    if (!visited.has(node)) {
      visited.add(node);
      console.log(node);

      for (const neighbor of graph[node].reverse()) {
        if (!visited.has(neighbor)) {
          stack.push(neighbor);
        }
      }
    }
  }
}

BFS (너비 우선 탐색):

function bfs(graph, start) {
  const visited = new Set();
  const queue = [start];
  visited.add(start);

  while (queue.length > 0) {
    const node = queue.shift();
    console.log(node);

    for (const neighbor of graph[node]) {
      if (!visited.has(neighbor)) {
        visited.add(neighbor);
        queue.push(neighbor);
      }
    }
  }
}

비교:

  • DFS: 스택/재귀, 경로 찾기, 사이클 검출, 백트래킹
  • BFS: 큐, 최단 경로, 레벨별 탐색

개념 설명: 그래프 탐색의 기본입니다. 프론트엔드에서 DOM 트리 탐색, 상태 관리 등에 활용됩니다.


Q5. 동적 계획법(DP) vs 분할 정복 vs 탐욕 알고리즘

답변: 동적 계획법(DP):

  • 중복되는 부분 문제를 한 번만 계산하여 저장(메모이제이션)
  • 예: 피보나치, 배낭 문제, LCS
// 피보나치 DP
function fibonacci(n, memo = {}) {
  if (n <= 1) return n;
  if (memo[n]) return memo[n];

  memo[n] = fibonacci(n - 1, memo) + fibonacci(n - 2, memo);
  return memo[n];
}

분할 정복:

  • 문제를 독립적인 부분 문제로 분할하여 해결
  • 예: 병합 정렬, 퀵 정렬, 이진 탐색

탐욕 알고리즘:

  • 각 단계에서 최선의 선택, 항상 최적해 보장은 아님
  • 예: 거스름돈, 다익스트라, 크루스칼

개념 설명: DP는 중복 부분 문제가 있을 때, 분할 정복은 독립적인 부분 문제, 탐욕은 지역 최적해가 전역 최적해인 경우 사용합니다.


Q6. 투 포인터(Two Pointer) 알고리즘이란?

답변: 배열에서 두 개의 포인터를 이용하여 O(n) 시간에 문제를 해결하는 기법입니다.

예시 1: 두 수의 합 (정렬된 배열)

function twoSum(arr, target) {
  let left = 0;
  let right = arr.length - 1;

  while (left < right) {
    const sum = arr[left] + arr[right];

    if (sum === target) {
      return [left, right];
    } else if (sum < target) {
      left++;
    } else {
      right--;
    }
  }

  return [-1, -1];
}

예시 2: 중복 제거

function removeDuplicates(arr) {
  if (arr.length === 0) return 0;

  let slow = 0;
  for (let fast = 1; fast < arr.length; fast++) {
    if (arr[fast] !== arr[slow]) {
      slow++;
      arr[slow] = arr[fast];
    }
  }

  return slow + 1;
}

개념 설명: O(n²)를 O(n)으로 최적화할 수 있는 강력한 기법입니다. 슬라이딩 윈도우 기법과 함께 자주 사용됩니다.


Q7. 해시맵을 활용한 문제 해결 (Two Sum)

답변: 해시맵을 사용하면 O(n²)를 O(n)으로 개선할 수 있습니다.

Two Sum 문제:

// O(n²) 방법 - 이중 for문
function twoSumBrute(nums, target) {
  for (let i = 0; i < nums.length; i++) {
    for (let j = i + 1; j < nums.length; j++) {
      if (nums[i] + nums[j] === target) {
        return [i, j];
      }
    }
  }
  return [];
}

// O(n) 방법 - 해시맵
function twoSum(nums, target) {
  const map = new Map();

  for (let i = 0; i < nums.length; i++) {
    const complement = target - nums[i];

    if (map.has(complement)) {
      return [map.get(complement), i];
    }

    map.set(nums[i], i);
  }

  return [];
}

개념 설명: 해시맵은 키-값 쌍을 O(1)에 조회할 수 있어 많은 알고리즘 문제에서 최적화의 핵심입니다.


Q8. 재귀(Recursion)와 꼬리 재귀 최적화란?

답변: 재귀: 함수가 자기 자신을 호출하는 방식

일반 재귀:

// 팩토리얼
function factorial(n) {
  if (n <= 1) return 1;
  return n * factorial(n - 1); // 호출 후 연산 필요
}

꼬리 재귀 (Tail Recursion):

function factorialTail(n, acc = 1) {
  if (n <= 1) return acc;
  return factorialTail(n - 1, n * acc); // 마지막이 재귀 호출
}

장점:

  • 꼬리 재귀는 컴파일러가 반복문으로 최적화 가능
  • 스택 오버플로우 방지

단점:

  • JavaScript는 꼬리 재귀 최적화가 일부 엔진에서만 지원됨

개념 설명: 재귀는 코드가 간결하지만 스택 메모리를 많이 사용합니다. 깊은 재귀는 반복문으로 변환하는 것이 안전합니다.


💻 3. 운영체제 (Operating System) - 8문항

Q1. 프로세스와 스레드의 차이, 멀티프로세스 vs 멀티스레드

답변: 프로세스:

  • 실행 중인 프로그램
  • 독립적인 메모리 공간 (Code, Data, Heap, Stack)
  • 프로세스 간 통신(IPC) 필요
  • 안정적이지만 무거움

스레드:

  • 프로세스 내 실행 단위
  • Code, Data, Heap 공유, Stack만 독립적
  • 직접 통신 가능
  • 가볍지만 동기화 문제

비교:

특징멀티프로세스멀티스레드
메모리독립적공유
통신IPC (복잡)간단
생성 비용작음
안정성높음 (격리)낮음 (공유)
Context Switching느림빠름

개념 설명: Chrome은 탭마다 별도 프로세스 사용(안정성), Node.js는 단일 스레드 이벤트 루프 사용(효율성). 프론트엔드에서는 Web Worker로 멀티스레드 활용 가능합니다.


Q2. 동기(Synchronous)와 비동기(Asynchronous), 블로킹과 논블로킹

답변: 동기 vs 비동기:

  • 동기: 작업 완료까지 대기, 순차적 실행
  • 비동기: 작업 완료를 기다리지 않고 다음 작업 실행

블로킹 vs 논블로킹:

  • 블로킹: 제어권을 넘기고 대기
  • 논블로킹: 제어권을 바로 반환

조합:

  1. 동기 + 블로킹: 일반적인 함수 호출
  2. 비동기 + 논블로킹: fetch, setTimeout (가장 많이 사용)
  3. 동기 + 논블로킹: 폴링(주기적 확인)
  4. 비동기 + 블로킹: 드물게 사용
// 동기 블로킹
const result = syncFunction();
console.log(result);

// 비동기 논블로킹
fetch("/api/data")
  .then((response) => response.json())
  .then((data) => console.log(data));
console.log("바로 실행됨");

개념 설명: 프론트엔드 개발의 핵심 개념입니다. JavaScript는 싱글 스레드이지만 이벤트 루프를 통해 비동기 처리를 효율적으로 수행합니다.


Q3. 교착상태(Deadlock)의 발생 조건과 해결 방법

답변: 발생 조건 (4가지 모두 충족 시):

  1. 상호 배제(Mutual Exclusion): 자원을 한 번에 하나의 프로세스만 사용
  2. 점유와 대기(Hold and Wait): 자원을 가진 채로 다른 자원 대기
  3. 비선점(No Preemption): 강제로 자원을 빼앗을 수 없음
  4. 순환 대기(Circular Wait): 프로세스들이 원형으로 대기

해결 방법:

  1. 예방(Prevention): 4가지 조건 중 하나를 제거
    • 자원 순서화로 순환 대기 방지
  2. 회피(Avoidance): 안전 상태 유지 (은행원 알고리즘)
  3. 탐지 및 회복(Detection & Recovery): 주기적 탐지 후 프로세스 종료
  4. 무시: 발생 확률이 낮으면 무시 (UNIX/Linux 방식)

실제 예시:

// JavaScript에서의 Deadlock
let lock1 = new Promise((resolve) => {});
let lock2 = new Promise((resolve) => {});

// Thread 1
async function task1() {
  await lock1;
  await lock2; // 영원히 대기
}

// Thread 2
async function task2() {
  await lock2;
  await lock1; // 영원히 대기
}

개념 설명: 데이터베이스 트랜잭션, 멀티스레드 프로그래밍에서 자주 발생합니다. 자원 획득 순서를 일관되게 유지하면 예방 가능합니다.


Q4. 컨텍스트 스위칭(Context Switching)이란?

답변: CPU가 실행 중인 프로세스/스레드를 전환하는 과정입니다.

과정:

  1. 현재 프로세스 상태 저장
    • PC(Program Counter), 레지스터, 스택 포인터를 PCB에 저장
  2. 다음 프로세스 상태 복원
    • PCB에서 상태 정보를 CPU 레지스터로 복원
  3. CPU 제어권 이전
    • 새 프로세스 실행 시작

발생 시점:

  • 인터럽트 발생 (I/O 요청, 타이머)
  • 시스템 콜
  • 스케줄러에 의한 CPU 재할당

오버헤드:

  • 레지스터 저장/복원 시간
  • 캐시 무효화 (Cache Miss 증가)
  • TLB 플러시

프로세스 vs 스레드 Context Switching:

  • 프로세스: 메모리 맵 전환 필요 (느림)
  • 스레드: 같은 주소 공간 사용 (빠름)

개념 설명: 너무 자주 발생하면 오버헤드로 인해 전체 성능이 저하됩니다. 타임 슬라이스를 적절히 설정해야 합니다.


Q5. 메모리 관리: 페이징과 세그멘테이션

답변: 페이징(Paging):

  • 프로세스를 고정 크기 페이지로 분할
  • 물리 메모리를 같은 크기의 프레임으로 분할
  • 페이지 테이블로 논리 주소 → 물리 주소 변환
  • 장점: 외부 단편화 없음
  • 단점: 내부 단편화 발생 가능

세그멘테이션(Segmentation):

  • 프로세스를 논리적 단위(코드, 데이터, 스택)로 분할
  • 가변 크기 세그먼트
  • 장점: 논리적 구분 명확
  • 단점: 외부 단편화 발생

페이지 크기:

  • 너무 작으면: 페이지 테이블 커짐
  • 너무 크면: 내부 단편화 증가
  • 일반적으로 4KB 사용

개념 설명: 현대 OS는 페이징과 세그멘테이션을 결합한 세그멘테이션-페이징 기법을 사용합니다.


Q6. 가상 메모리와 페이지 교체 알고리즘

답변: 가상 메모리:

  • 물리 메모리보다 큰 프로그램 실행 가능
  • 디스크를 메모리처럼 사용
  • 페이지 폴트 발생 시 디스크에서 로드

페이지 교체 알고리즘:

  1. FIFO (First In First Out)

    • 가장 먼저 들어온 페이지 교체
    • 구현 간단, 성능 낮음
  2. LRU (Least Recently Used)

    • 가장 오래 사용되지 않은 페이지 교체
    • 좋은 성능, 구현 복잡 (연결 리스트 + 해시맵)
  3. LFU (Least Frequently Used)

    • 사용 빈도가 가장 낮은 페이지 교체
  4. Optimal (최적)

    • 미래에 가장 오래 사용되지 않을 페이지 교체
    • 이론상 최적, 실제 구현 불가능
  5. Clock (Second Chance)

    • FIFO + 참조 비트
    • LRU의 근사 알고리즘

Thrashing (스래싱): 페이지 폴트가 너무 자주 발생하여 실제 작업보다 페이지 교체에 시간을 더 쓰는 현상

개념 설명: LRU는 웹 브라우저 캐시, Redis 등에서 널리 사용됩니다. JavaScript의 Map/Set과 유사한 자료구조로 구현 가능합니다.


Q7. CPU 스케줄링 알고리즘의 종류

답변: 비선점형(Non-preemptive):

  1. FCFS (First Come First Served)

    • 먼저 온 프로세스 먼저 처리
    • Convoy Effect: 긴 프로세스 뒤에 짧은 프로세스들이 대기
  2. SJF (Shortest Job First)

    • 실행 시간이 짧은 프로세스 우선
    • 최소 평균 대기 시간
    • 실행 시간 예측 어려움, 기아(Starvation) 발생 가능

선점형(Preemptive):

  1. SRTF (Shortest Remaining Time First)

    • SJF의 선점형 버전
  2. Round Robin (RR)

    • 타임 퀀텀(Time Quantum) 단위로 순환
    • 공평하지만 컨텍스트 스위칭 오버헤드
  3. Priority Scheduling

    • 우선순위가 높은 프로세스 먼저 실행
    • 기아 문제 → Aging으로 해결
  4. Multilevel Queue

    • 여러 큐를 우선순위별로 관리
    • 각 큐는 다른 스케줄링 알고리즘 사용

성능 지표:

  • CPU 이용률: CPU가 일하는 시간 비율
  • 처리량: 단위 시간당 완료된 프로세스 수
  • 대기 시간: Ready Queue에서 대기한 시간
  • 응답 시간: 첫 응답까지 걸린 시간
  • 반환 시간: 완료까지 총 시간

개념 설명: 현대 OS는 대부분 Priority + Round Robin 조합을 사용합니다. 프론트엔드에서는 브라우저의 Task Queue 스케줄링과 유사한 개념입니다.


Q8. 동기화 문제: Race Condition, 세마포어, 뮤텍스

답변: Race Condition (경쟁 상태): 여러 프로세스/스레드가 공유 자원에 동시 접근할 때 실행 순서에 따라 결과가 달라지는 문제

임계 영역(Critical Section): 공유 자원에 접근하는 코드 영역

해결 방법:

  1. 뮤텍스(Mutex)
    • 상호 배제(Mutual Exclusion)
    • 한 번에 하나의 스레드만 접근
    • Lock/Unlock 사용
// JavaScript 예시 (Atomic operation)
let lock = false;

function criticalSection() {
  while (lock) {
    /* 대기 */
  }
  lock = true;

  // 임계 영역
  sharedResource++;

  lock = false;
}
  1. 세마포어(Semaphore)

    • 카운터 기반 (S = 2이면 2개까지 동시 접근)
    • P(wait): S--, V(signal): S++
    • 이진 세마포어(S=1)는 뮤텍스와 유사
  2. 모니터(Monitor)

    • 고수준 동기화 도구
    • Java의 synchronized, wait(), notify()

뮤텍스 vs 세마포어:

  • 뮤텍스: 소유권 개념, 락을 건 스레드만 해제 가능
  • 세마포어: 소유권 없음, 다른 스레드도 해제 가능

개념 설명: 프론트엔드에서는 Web Worker 간 통신, SharedArrayBuffer 사용 시 동기화가 필요합니다. Atomics API로 원자적 연산 가능합니다.


📚 4. 자료구조 (Data Structure) - 3문항

Q1. 배열 vs 연결 리스트 vs 해시 테이블 비교

답변:

연산배열연결 리스트해시 테이블
접근O(1)O(n)O(1) 평균
검색O(n)O(n)O(1) 평균
삽입O(n)O(1)O(1) 평균
삭제O(n)O(1)O(1) 평균
메모리연속적비연속적비연속적

JavaScript에서:

// 배열
const arr = [1, 2, 3];
arr.push(4); // O(1)
arr.unshift(0); // O(n)

// 연결 리스트 (구현 필요)
class Node {
  constructor(value) {
    this.value = value;
    this.next = null;
  }
}

// 해시 테이블
const map = new Map();
map.set("key", "value"); // O(1)
const obj = {}; // Object도 해시 테이블

개념 설명: 상황에 따라 적절한 자료구조를 선택해야 합니다. 빠른 접근이 필요하면 배열, 잦은 삽입/삭제는 연결 리스트, 키-값 매핑은 해시 테이블을 사용합니다.


Q2. 스택과 큐의 활용 사례 및 구현

답변: 스택 (LIFO):

  • 함수 호출 스택
  • 브라우저 뒤로가기
  • Undo/Redo 기능
  • 괄호 짝 맞추기
  • DFS
class Stack {
  constructor() {
    this.items = [];
  }

  push(item) {
    this.items.push(item);
  }

  pop() {
    return this.items.pop();
  }

  peek() {
    return this.items[this.items.length - 1];
  }

  isEmpty() {
    return this.items.length === 0;
  }
}

큐 (FIFO):

  • BFS
  • 프린터 대기열
  • 콜백 큐 (이벤트 루프)
  • 메시지 큐
class Queue {
  constructor() {
    this.items = [];
  }

  enqueue(item) {
    this.items.push(item);
  }

  dequeue() {
    return this.items.shift(); // O(n), 개선 필요 시 연결 리스트 사용
  }

  front() {
    return this.items[0];
  }

  isEmpty() {
    return this.items.length === 0;
  }
}

개념 설명: 프론트엔드에서 이벤트 루프의 Task Queue가 대표적인 큐 구조입니다.


Q3. 트리 구조 (이진 트리, BST, 힙)

답변: 이진 트리 (Binary Tree):

  • 각 노드가 최대 2개의 자식

이진 탐색 트리 (BST):

  • 왼쪽 < 부모 < 오른쪽
  • 평균 O(log n), 최악 O(n)

트리 순회:

// 전위 순회 (Preorder): 부모 → 왼쪽 → 오른쪽
function preorder(node) {
  if (!node) return;
  console.log(node.value);
  preorder(node.left);
  preorder(node.right);
}

// 중위 순회 (Inorder): 왼쪽 → 부모 → 오른쪽
function inorder(node) {
  if (!node) return;
  inorder(node.left);
  console.log(node.value);
  inorder(node.right);
}

// 후위 순회 (Postorder): 왼쪽 → 오른쪽 → 부모
function postorder(node) {
  if (!node) return;
  postorder(node.left);
  postorder(node.right);
  console.log(node.value);
}

힙 (Heap):

  • 완전 이진 트리
  • 최대 힙: 부모 ≥ 자식
  • 최소 힙: 부모 ≤ 자식
  • 우선순위 큐 구현에 사용

개념 설명: React의 Virtual DOM은 트리 구조입니다. DOM 조작 최적화를 위해 트리 순회와 비교 알고리즘을 사용합니다.


🗄️ 5. 데이터베이스 (Database) - 3문항

Q1. 인덱스의 동작 원리와 B-Tree 구조

답변: 인덱스: 테이블의 검색 속도를 향상시키는 자료구조 (책의 색인과 유사)

B-Tree (Balanced Tree):

  • 대부분의 DBMS가 사용하는 구조
  • 노드에 여러 개의 키와 자식 포인터
  • 균형 유지로 항상 O(log n) 보장

장점:

  • SELECT WHERE 절 성능 향상
  • ORDER BY, GROUP BY 성능 향상
  • JOIN 성능 향상

단점:

  • INSERT, UPDATE, DELETE 시 인덱스도 갱신 (오버헤드)
  • 추가 저장 공간 (테이블 크기의 10%)
  • 너무 많으면 오히려 성능 저하

인덱스 설계 원칙:

  • WHERE 절에 자주 사용되는 컬럼
  • 카디널리티가 높은 컬럼 (중복도가 낮음)
  • 복합 인덱스는 순서 중요 (선택도가 높은 컬럼을 앞에)

개념 설명: 프론트엔드 개발자도 쿼리 최적화를 위해 인덱스 개념을 이해해야 합니다.


Q2. 트랜잭션 ACID와 격리 수준

답변: ACID:

  • Atomicity: 전체 성공 or 전체 실패
  • Consistency: 일관성 유지
  • Isolation: 독립적 실행
  • Durability: 영구 저장

격리 수준 문제:

  • Dirty Read: 커밋 안 된 데이터 읽기
  • Non-Repeatable Read: 같은 쿼리의 다른 결과
  • Phantom Read: 새로운 행이 나타남

격리 수준:

수준Dirty ReadNon-RepeatablePhantom
READ UNCOMMITTEDOOO
READ COMMITTEDXOO
REPEATABLE READXXO
SERIALIZABLEXXX

개념 설명: MySQL InnoDB는 기본적으로 REPEATABLE READ를 사용합니다. 동시성과 일관성의 트레이드오프를 고려해야 합니다.


Q3. SQL JOIN의 종류와 성능 최적화

답변: JOIN 종류:

-- INNER JOIN: 양쪽 모두 존재
SELECT * FROM users u
INNER JOIN orders o ON u.id = o.user_id;

-- LEFT JOIN: 왼쪽 전체 + 오른쪽 매칭
SELECT * FROM users u
LEFT JOIN orders o ON u.id = o.user_id;

-- RIGHT JOIN
SELECT * FROM users u
RIGHT JOIN orders o ON u.id = o.user_id;

-- FULL OUTER JOIN: 양쪽 전체
SELECT * FROM users u
FULL OUTER JOIN orders o ON u.id = o.user_id;

성능 최적화:

  1. 인덱스 활용: JOIN 키에 인덱스 생성
  2. 작은 테이블을 먼저: Driving Table 선택
  3. 불필요한 컬럼 제거: SELECT * 지양
  4. 서브쿼리 대신 JOIN: 대부분 JOIN이 빠름
  5. EXIST 사용: IN보다 효율적인 경우 많음

개념 설명: N+1 문제를 피하기 위해 Eager Loading을 사용하거나, GraphQL DataLoader로 해결할 수 있습니다.


프론트엔드 개발자로서 네트워크(HTTP, REST API, CORS), 알고리즘(시간 복잡도, 정렬, 해시맵), OS(프로세스/스레드, 동기/비동기) 개념을 잘 이해하시면 백엔드와의 협업과 성능 최적화에 큰 도움이 됩니다!

추가로 궁금하신 부분이나 더 깊이 알고 싶은 주제가 있으면 말씀해주세요! 🚀

댓글

0/2000
Newsletter

이 글이 도움이 되셨나요?

새로운 글이 발행되면 이메일로 알려드립니다.

뉴스레터 구독하기